mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Compare commits
22 Commits
experiment
...
overloaded
Author | SHA1 | Date | |
---|---|---|---|
016614e714 | |||
9e7e76a609 | |||
b1e4e44f77 | |||
f4f31b654b | |||
799dfecdd9 | |||
1415e2492a | |||
51cf6817b1 | |||
fd04894a7b | |||
d2d6a59c72 | |||
788ee9a008 | |||
246c1776df | |||
2670e81668 | |||
e29aa2f337 | |||
9b3b45258a | |||
ca4debbf08 | |||
03f98ee0a9 | |||
70368ea653 | |||
21ccd90147 | |||
0a9a45a126 | |||
88937f1785 | |||
e5a783cb9e | |||
70fb9bbdfd |
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\build\update_registry_annotations;
|
||||
|
||||
use pocketmine\utils\OverloadedRegistryMember;
|
||||
use pocketmine\utils\Utils;
|
||||
use function basename;
|
||||
use function class_exists;
|
||||
@ -34,6 +35,7 @@ use function fwrite;
|
||||
use function implode;
|
||||
use function is_dir;
|
||||
use function ksort;
|
||||
use function lcfirst;
|
||||
use function mb_strtoupper;
|
||||
use function preg_match;
|
||||
use function sprintf;
|
||||
@ -47,11 +49,19 @@ if(count($argv) !== 2){
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param \ReflectionClass<*> $class
|
||||
*/
|
||||
function makeTypehint(string $namespaceName, \ReflectionClass $class) : string{
|
||||
return $class->getNamespaceName() === $namespaceName ? $class->getShortName() : '\\' . $class->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object[] $members
|
||||
* @phpstan-param array<string, object> $members
|
||||
* @phpstan-param array<string, OverloadedRegistryMember> $overloadedMembers
|
||||
*/
|
||||
function generateMethodAnnotations(string $namespaceName, array $members) : string{
|
||||
function generateMethodAnnotations(string $namespaceName, array $members, array $overloadedMembers) : string{
|
||||
$selfName = basename(__FILE__);
|
||||
$lines = ["/**"];
|
||||
$lines[] = " * This doc-block is generated automatically, do not modify it manually.";
|
||||
@ -69,14 +79,20 @@ function generateMethodAnnotations(string $namespaceName, array $members) : stri
|
||||
}
|
||||
if($reflect === false){
|
||||
$typehint = "object";
|
||||
}elseif($reflect->getNamespaceName() === $namespaceName){
|
||||
$typehint = $reflect->getShortName();
|
||||
}else{
|
||||
$typehint = '\\' . $reflect->getName();
|
||||
$typehint = makeTypehint($namespaceName, $reflect);
|
||||
}
|
||||
$accessor = mb_strtoupper($name);
|
||||
$memberLines[$accessor] = sprintf($lineTmpl, $accessor, $typehint);
|
||||
}
|
||||
foreach(Utils::stringifyKeys($overloadedMembers) as $baseName => $member){
|
||||
$accessor = mb_strtoupper($baseName);
|
||||
$returnTypehint = makeTypehint($namespaceName, new \ReflectionClass($member->memberClass));
|
||||
$enumReflect = new \ReflectionClass($member->enumClass);
|
||||
$paramTypehint = makeTypehint($namespaceName, $enumReflect);
|
||||
|
||||
$memberLines[] = sprintf(" * @method static %s %s(%s \$%s)", $returnTypehint, $accessor, $paramTypehint, lcfirst($enumReflect->getShortName()));
|
||||
}
|
||||
ksort($memberLines, SORT_STRING);
|
||||
|
||||
foreach($memberLines as $line){
|
||||
@ -107,7 +123,7 @@ function processFile(string $file) : void{
|
||||
}
|
||||
echo "Found registry in $file\n";
|
||||
|
||||
$replacement = generateMethodAnnotations($matches[1], $className::getAll());
|
||||
$replacement = generateMethodAnnotations($matches[1], $className::getAll(), $className::getAllOverloaded());
|
||||
|
||||
$newContents = str_replace($docComment, $replacement, $contents);
|
||||
if($newContents !== $contents){
|
||||
|
@ -44,7 +44,7 @@
|
||||
"pocketmine/locale-data": "~2.24.0",
|
||||
"pocketmine/log": "^0.4.0",
|
||||
"pocketmine/math": "~1.0.0",
|
||||
"pocketmine/nbt": "~1.0.0",
|
||||
"pocketmine/nbt": "~1.1.0",
|
||||
"pocketmine/raklib": "~1.1.0",
|
||||
"pocketmine/raklib-ipc": "~1.0.0",
|
||||
"pocketmine/snooze": "^0.5.0",
|
||||
|
14
composer.lock
generated
14
composer.lock
generated
@ -4,7 +4,7 @@
|
||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "af7547291a131bfac6d7087957601325",
|
||||
"content-hash": "663122b8f03ef5ec6718a419923a0851",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/json-comment",
|
||||
@ -576,16 +576,16 @@
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/nbt",
|
||||
"version": "1.0.1",
|
||||
"version": "1.1.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/pmmp/NBT.git",
|
||||
"reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d"
|
||||
"reference": "cfd53a86166b851786967fc560cdb372e66fde96"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/53db37487bc5ddbfbd84247966e1a073bdcfdb7d",
|
||||
"reference": "53db37487bc5ddbfbd84247966e1a073bdcfdb7d",
|
||||
"url": "https://api.github.com/repos/pmmp/NBT/zipball/cfd53a86166b851786967fc560cdb372e66fde96",
|
||||
"reference": "cfd53a86166b851786967fc560cdb372e66fde96",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@ -612,9 +612,9 @@
|
||||
"description": "PHP library for working with Named Binary Tags",
|
||||
"support": {
|
||||
"issues": "https://github.com/pmmp/NBT/issues",
|
||||
"source": "https://github.com/pmmp/NBT/tree/1.0.1"
|
||||
"source": "https://github.com/pmmp/NBT/tree/1.1.0"
|
||||
},
|
||||
"time": "2025-01-07T22:47:46+00:00"
|
||||
"time": "2025-02-01T21:20:26+00:00"
|
||||
},
|
||||
{
|
||||
"name": "pocketmine/raklib",
|
||||
|
@ -34,11 +34,16 @@ use function mt_rand;
|
||||
final class ChorusPlant extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
/**
|
||||
* @var true[]
|
||||
* @phpstan-var array<int, true>
|
||||
*/
|
||||
protected array $connections = [];
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$bb = AxisAlignedBB::one();
|
||||
foreach($this->getAllSides() as $facing => $block){
|
||||
$id = $block->getTypeId();
|
||||
if($id !== BlockTypeIds::END_STONE && $id !== BlockTypeIds::CHORUS_FLOWER && !$block->hasSameTypeId($this)){
|
||||
foreach(Facing::ALL as $facing){
|
||||
if(!isset($this->connections[$facing])){
|
||||
$bb->trim($facing, 2 / 16);
|
||||
}
|
||||
}
|
||||
@ -46,6 +51,26 @@ final class ChorusPlant extends Flowable{
|
||||
return [$bb];
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
|
||||
$this->collisionBoxes = null;
|
||||
|
||||
foreach(Facing::ALL as $facing){
|
||||
$block = $this->getSide($facing);
|
||||
if(match($block->getTypeId()){
|
||||
BlockTypeIds::END_STONE, BlockTypeIds::CHORUS_FLOWER, $this->getTypeId() => true,
|
||||
default => false
|
||||
}){
|
||||
$this->connections[$facing] = true;
|
||||
}else{
|
||||
unset($this->connections[$facing]);
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ use pocketmine\block\BlockIdentifier as BID;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\world\light\LightUpdate;
|
||||
use function count;
|
||||
use function min;
|
||||
|
||||
/**
|
||||
@ -40,6 +41,11 @@ use function min;
|
||||
class RuntimeBlockStateRegistry{
|
||||
use SingletonTrait;
|
||||
|
||||
public const COLLISION_CUSTOM = 0;
|
||||
public const COLLISION_CUBE = 1;
|
||||
public const COLLISION_NONE = 2;
|
||||
public const COLLISION_MAY_OVERFLOW = 3;
|
||||
|
||||
/**
|
||||
* @var Block[]
|
||||
* @phpstan-var array<int, Block>
|
||||
@ -74,6 +80,13 @@ class RuntimeBlockStateRegistry{
|
||||
*/
|
||||
public array $blastResistance = [];
|
||||
|
||||
/**
|
||||
* Map of state ID -> useful AABB info to avoid unnecessary block allocations
|
||||
* @var int[]
|
||||
* @phpstan-var array<int, int>
|
||||
*/
|
||||
public array $collisionInfo = [];
|
||||
|
||||
public function __construct(){
|
||||
foreach(VanillaBlocks::getAll() as $block){
|
||||
$this->register($block);
|
||||
@ -100,6 +113,70 @@ class RuntimeBlockStateRegistry{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the given class method overrides a method in Block.
|
||||
* Used to determine if a block might need to disable fast path optimizations.
|
||||
*
|
||||
* @phpstan-param anyClosure $closure
|
||||
*/
|
||||
private static function overridesBlockMethod(\Closure $closure) : bool{
|
||||
$declarer = (new \ReflectionFunction($closure))->getClosureScopeClass();
|
||||
return $declarer !== null && $declarer->getName() !== Block::class;
|
||||
}
|
||||
|
||||
/**
|
||||
* A big ugly hack to set up fast paths for handling collisions on blocks with common shapes.
|
||||
* The information returned here is stored in RuntimeBlockStateRegistry->collisionInfo, and is used during entity
|
||||
* collision box calculations to avoid complex logic and unnecessary block object allocations.
|
||||
* This hack allows significant performance improvements.
|
||||
*
|
||||
* TODO: We'll want to redesign block collision box handling and block shapes in the future, but that's a job for a
|
||||
* major version. For now, this hack nets major performance wins.
|
||||
*/
|
||||
private static function calculateCollisionInfo(Block $block) : int{
|
||||
if(
|
||||
self::overridesBlockMethod($block->getModelPositionOffset(...)) ||
|
||||
self::overridesBlockMethod($block->readStateFromWorld(...))
|
||||
){
|
||||
//getModelPositionOffset() might cause AABBs to shift outside the cell
|
||||
//readStateFromWorld() might cause overflow in ways we can't predict just by looking at known states
|
||||
//TODO: excluding overriders of readStateFromWorld() also excludes blocks with tiles that don't do anything
|
||||
//weird with their AABBs, but for now this is the best we can do.
|
||||
return self::COLLISION_MAY_OVERFLOW;
|
||||
}
|
||||
|
||||
//TODO: this could blow up if any recalculateCollisionBoxes() uses the world
|
||||
//it shouldn't, but that doesn't mean that custom blocks won't...
|
||||
$boxes = $block->getCollisionBoxes();
|
||||
if(count($boxes) === 0){
|
||||
return self::COLLISION_NONE;
|
||||
}
|
||||
|
||||
if(
|
||||
count($boxes) === 1 &&
|
||||
$boxes[0]->minX === 0.0 &&
|
||||
$boxes[0]->minY === 0.0 &&
|
||||
$boxes[0]->minZ === 0.0 &&
|
||||
$boxes[0]->maxX === 1.0 &&
|
||||
$boxes[0]->maxY === 1.0 &&
|
||||
$boxes[0]->maxZ === 1.0
|
||||
){
|
||||
return self::COLLISION_CUBE;
|
||||
}
|
||||
|
||||
foreach($boxes as $box){
|
||||
if(
|
||||
$box->minX < 0 || $box->maxX > 1 ||
|
||||
$box->minY < 0 || $box->maxY > 1 ||
|
||||
$box->minZ < 0 || $box->maxZ > 1
|
||||
){
|
||||
return self::COLLISION_MAY_OVERFLOW;
|
||||
}
|
||||
}
|
||||
|
||||
return self::COLLISION_CUSTOM;
|
||||
}
|
||||
|
||||
private function fillStaticArrays(int $index, Block $block) : void{
|
||||
$fullId = $block->getStateId();
|
||||
if($index !== $fullId){
|
||||
@ -112,6 +189,8 @@ class RuntimeBlockStateRegistry{
|
||||
if($block->blocksDirectSkyLight()){
|
||||
$this->blocksDirectSkyLight[$index] = true;
|
||||
}
|
||||
|
||||
$this->collisionInfo[$index] = self::calculateCollisionInfo($block);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -79,6 +79,10 @@ use function strtolower;
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static Sapling SAPLING(\pocketmine\block\utils\SaplingType $saplingType)
|
||||
* @method static Leaves LEAVES(\pocketmine\block\utils\LeavesType $leavesType)
|
||||
* @method static FloorSign SIGN(\pocketmine\block\utils\WoodType $woodType)
|
||||
* @method static WallSign WALL_SIGN(\pocketmine\block\utils\WoodType $woodType)
|
||||
* @method static WoodenButton ACACIA_BUTTON()
|
||||
* @method static WoodenDoor ACACIA_DOOR()
|
||||
* @method static WoodenFence ACACIA_FENCE()
|
||||
@ -1231,10 +1235,12 @@ final class VanillaBlocks{
|
||||
$name = $saplingType->getDisplayName();
|
||||
self::register(strtolower($saplingType->name) . "_sapling", fn(BID $id) => new Sapling($id, $name . " Sapling", $saplingTypeInfo, $saplingType));
|
||||
}
|
||||
self::registerOverloaded("sapling", SaplingType::class, Sapling::class);
|
||||
foreach(LeavesType::cases() as $leavesType){
|
||||
$name = $leavesType->getDisplayName();
|
||||
self::register(strtolower($leavesType->name) . "_leaves", fn(BID $id) => new Leaves($id, $name . " Leaves", $leavesBreakInfo, $leavesType));
|
||||
}
|
||||
self::registerOverloaded("leaves", LeavesType::class, Leaves::class);
|
||||
|
||||
$sandstoneBreakInfo = new Info(BreakInfo::pickaxe(0.8, ToolTier::WOOD));
|
||||
self::register("red_sandstone_stairs", fn(BID $id) => new Stair($id, "Red Sandstone Stairs", $sandstoneBreakInfo));
|
||||
@ -1370,22 +1376,12 @@ final class VanillaBlocks{
|
||||
self::register($idName("pressure_plate"), fn(BID $id) => new WoodenPressurePlate($id, $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20));
|
||||
self::register($idName("trapdoor"), fn(BID $id) => new WoodenTrapdoor($id, $name . " Trapdoor", $woodenDoorBreakInfo, $woodType));
|
||||
|
||||
$signAsItem = match($woodType){
|
||||
WoodType::OAK => VanillaItems::OAK_SIGN(...),
|
||||
WoodType::SPRUCE => VanillaItems::SPRUCE_SIGN(...),
|
||||
WoodType::BIRCH => VanillaItems::BIRCH_SIGN(...),
|
||||
WoodType::JUNGLE => VanillaItems::JUNGLE_SIGN(...),
|
||||
WoodType::ACACIA => VanillaItems::ACACIA_SIGN(...),
|
||||
WoodType::DARK_OAK => VanillaItems::DARK_OAK_SIGN(...),
|
||||
WoodType::MANGROVE => VanillaItems::MANGROVE_SIGN(...),
|
||||
WoodType::CRIMSON => VanillaItems::CRIMSON_SIGN(...),
|
||||
WoodType::WARPED => VanillaItems::WARPED_SIGN(...),
|
||||
WoodType::CHERRY => VanillaItems::CHERRY_SIGN(...),
|
||||
WoodType::PALE_OAK => VanillaItems::PALE_OAK_SIGN(...),
|
||||
};
|
||||
$signAsItem = fn() => VanillaItems::SIGN($woodType);
|
||||
self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
|
||||
self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class);
|
||||
}
|
||||
self::registerOverloaded("sign", WoodType::class, FloorSign::class);
|
||||
self::registerOverloaded("wall_sign", WoodType::class, WallSign::class);
|
||||
}
|
||||
|
||||
private static function registerMushroomBlocks() : void{
|
||||
|
@ -103,10 +103,18 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{
|
||||
|
||||
/** @phpstan-param \Closure(Reader) : Block $c */
|
||||
public function map(string $id, \Closure $c) : void{
|
||||
if(array_key_exists($id, $this->deserializeFuncs)){
|
||||
throw new \InvalidArgumentException("Deserializer is already assigned for \"$id\"");
|
||||
}
|
||||
$this->deserializeFuncs[$id] = $c;
|
||||
$this->simpleCache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the existing data deserializer for the given ID, or null if none exists.
|
||||
* This may be useful if you need to override a deserializer, but still want to be able to fall back to the original.
|
||||
*
|
||||
* @phpstan-return ?\Closure(Reader) : Block
|
||||
*/
|
||||
public function getDeserializerForId(string $id) : ?\Closure{
|
||||
return $this->deserializeFuncs[$id] ?? null;
|
||||
}
|
||||
|
||||
/** @phpstan-param \Closure() : Block $getBlock */
|
||||
|
@ -51,12 +51,19 @@ final class ItemDeserializer{
|
||||
* @phpstan-param \Closure(Data) : Item $deserializer
|
||||
*/
|
||||
public function map(string $id, \Closure $deserializer) : void{
|
||||
if(isset($this->deserializers[$id])){
|
||||
throw new \InvalidArgumentException("Deserializer is already assigned for \"$id\"");
|
||||
}
|
||||
$this->deserializers[$id] = $deserializer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the existing data deserializer for the given ID, or null if none exists.
|
||||
* This may be useful if you need to override a deserializer, but still want to be able to fall back to the original.
|
||||
*
|
||||
* @phpstan-return ?\Closure(Data) : Item
|
||||
*/
|
||||
public function getDeserializerForId(string $id) : ?\Closure{
|
||||
return $this->deserializers[$id] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-param \Closure(Data) : Block $deserializer
|
||||
*/
|
||||
|
@ -144,8 +144,9 @@ class InventoryTransaction{
|
||||
$needItems = [];
|
||||
$haveItems = [];
|
||||
foreach($this->actions as $key => $action){
|
||||
if(!$action->getTargetItem()->isNull()){
|
||||
$needItems[] = $action->getTargetItem();
|
||||
$targetItem = $action->getTargetItem();
|
||||
if(!$targetItem->isNull()){
|
||||
$needItems[] = $targetItem;
|
||||
}
|
||||
|
||||
try{
|
||||
@ -154,8 +155,9 @@ class InventoryTransaction{
|
||||
throw new TransactionValidationException(get_class($action) . "#" . spl_object_id($action) . ": " . $e->getMessage(), 0, $e);
|
||||
}
|
||||
|
||||
if(!$action->getSourceItem()->isNull()){
|
||||
$haveItems[] = $action->getSourceItem();
|
||||
$sourceItem = $action->getSourceItem();
|
||||
if(!$sourceItem->isNull()){
|
||||
$haveItems[] = $sourceItem;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\item;
|
||||
|
||||
use pocketmine\block\utils\RecordType;
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\block\VanillaBlocks as Blocks;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Location;
|
||||
@ -47,6 +48,8 @@ use function strtolower;
|
||||
* @see build/generate-registry-annotations.php
|
||||
* @generate-registry-docblock
|
||||
*
|
||||
* @method static Boat BOAT(BoatType $boatType)
|
||||
* @method static ItemBlockWallOrFloor SIGN(\pocketmine\block\utils\WoodType $woodType)
|
||||
* @method static Boat ACACIA_BOAT()
|
||||
* @method static ItemBlockWallOrFloor ACACIA_SIGN()
|
||||
* @method static ItemBlock AIR()
|
||||
@ -396,7 +399,6 @@ final class VanillaItems{
|
||||
//in the future we'll probably want to dissociate this from the air block and make a proper null item
|
||||
self::_registryRegister("air", Blocks::AIR()->asItem()->setCount(0));
|
||||
|
||||
self::register("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN()));
|
||||
self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard"));
|
||||
self::register("apple", fn(IID $id) => new Apple($id, "Apple"));
|
||||
self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow"));
|
||||
@ -406,7 +408,6 @@ final class VanillaItems{
|
||||
self::register("beetroot", fn(IID $id) => new Beetroot($id, "Beetroot"));
|
||||
self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds"));
|
||||
self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup"));
|
||||
self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN()));
|
||||
self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder"));
|
||||
self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod"));
|
||||
self::register("bleach", fn(IID $id) => new Item($id, "Bleach"));
|
||||
@ -420,7 +421,6 @@ final class VanillaItems{
|
||||
self::register("bucket", fn(IID $id) => new Bucket($id, "Bucket"));
|
||||
self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot"));
|
||||
self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal"));
|
||||
self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN()));
|
||||
self::register("chemical_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide"));
|
||||
self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia"));
|
||||
self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate"));
|
||||
@ -475,8 +475,6 @@ final class VanillaItems{
|
||||
self::register("cookie", fn(IID $id) => new Cookie($id, "Cookie"));
|
||||
self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot"));
|
||||
self::register("coral_fan", fn(IID $id) => new CoralFan($id));
|
||||
self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN()));
|
||||
self::register("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN()));
|
||||
self::register("diamond", fn(IID $id) => new Item($id, "Diamond"));
|
||||
self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)"));
|
||||
self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath"));
|
||||
@ -516,12 +514,10 @@ final class VanillaItems{
|
||||
self::register("ink_sac", fn(IID $id) => new Item($id, "Ink Sac"));
|
||||
self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot"));
|
||||
self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget"));
|
||||
self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN()));
|
||||
self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli"));
|
||||
self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA()));
|
||||
self::register("leather", fn(IID $id) => new Item($id, "Leather"));
|
||||
self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream"));
|
||||
self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN()));
|
||||
self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine"));
|
||||
self::register("melon", fn(IID $id) => new Melon($id, "Melon"));
|
||||
self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds"));
|
||||
@ -539,9 +535,7 @@ final class VanillaItems{
|
||||
self::register("netherite_scrap", fn(IID $id) => new class($id, "Netherite Scrap") extends Item{
|
||||
public function isFireProof() : bool{ return true; }
|
||||
});
|
||||
self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN()));
|
||||
self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting"));
|
||||
self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN()));
|
||||
self::register("paper", fn(IID $id) => new Item($id, "Paper"));
|
||||
self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane"));
|
||||
self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod"));
|
||||
@ -597,7 +591,6 @@ final class VanillaItems{
|
||||
self::register("snowball", fn(IID $id) => new Snowball($id, "Snowball"));
|
||||
self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye"));
|
||||
self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion"));
|
||||
self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN()));
|
||||
self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass"));
|
||||
self::register("steak", fn(IID $id) => new Steak($id, "Steak"));
|
||||
self::register("stick", fn(IID $id) => new Stick($id, "Stick"));
|
||||
@ -607,7 +600,6 @@ final class VanillaItems{
|
||||
self::register("sweet_berries", fn(IID $id) => new SweetBerries($id, "Sweet Berries"));
|
||||
self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds"));
|
||||
self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying"));
|
||||
self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN()));
|
||||
self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER()));
|
||||
self::register("wheat", fn(IID $id) => new Item($id, "Wheat"));
|
||||
self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds"));
|
||||
@ -618,6 +610,12 @@ final class VanillaItems{
|
||||
//boat type is static, because different types of wood may have different properties
|
||||
self::register(strtolower($type->name) . "_boat", fn(IID $id) => new Boat($id, $type->getDisplayName() . " Boat", $type));
|
||||
}
|
||||
self::registerOverloaded("boat", BoatType::class, Boat::class);
|
||||
|
||||
foreach(WoodType::cases() as $woodType){
|
||||
self::register("{$woodType->name}_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SIGN($woodType), Blocks::WALL_SIGN($woodType)));
|
||||
}
|
||||
self::registerOverloaded("sign", WoodType::class, ItemBlockWallOrFloor::class);
|
||||
}
|
||||
|
||||
private static function registerSpawnEggs() : void{
|
||||
|
8
src/network/mcpe/cache/ChunkCache.php
vendored
8
src/network/mcpe/cache/ChunkCache.php
vendored
@ -88,9 +88,13 @@ class ChunkCache implements ChunkListener{
|
||||
private int $hits = 0;
|
||||
private int $misses = 0;
|
||||
|
||||
/**
|
||||
* @phpstan-param DimensionIds::* $dimensionId
|
||||
*/
|
||||
private function __construct(
|
||||
private World $world,
|
||||
private Compressor $compressor
|
||||
private Compressor $compressor,
|
||||
private int $dimensionId = DimensionIds::OVERWORLD
|
||||
){}
|
||||
|
||||
private function prepareChunkAsync(int $chunkX, int $chunkZ, int $chunkHash) : CompressBatchPromise{
|
||||
@ -109,7 +113,7 @@ class ChunkCache implements ChunkListener{
|
||||
new ChunkRequestTask(
|
||||
$chunkX,
|
||||
$chunkZ,
|
||||
DimensionIds::OVERWORLD, //TODO: not hardcode this
|
||||
$this->dimensionId,
|
||||
$chunk,
|
||||
$promise,
|
||||
$this->compressor
|
||||
|
@ -25,6 +25,8 @@ namespace pocketmine\player;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\animation\ArmSwingAnimation;
|
||||
use pocketmine\entity\effect\VanillaEffects;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||
@ -65,11 +67,29 @@ final class SurvivalBlockBreakHandler{
|
||||
if(!$this->block->getBreakInfo()->isBreakable()){
|
||||
return 0.0;
|
||||
}
|
||||
//TODO: improve this to take stuff like swimming, ladders, enchanted tools into account, fix wrong tool break time calculations for bad tools (pmmp/PocketMine-MP#211)
|
||||
$breakTimePerTick = $this->block->getBreakInfo()->getBreakTime($this->player->getInventory()->getItemInHand()) * 20;
|
||||
|
||||
if(!$this->player->isOnGround() && !$this->player->isFlying()){
|
||||
$breakTimePerTick *= 5;
|
||||
}
|
||||
if($this->player->isUnderwater() && !$this->player->getArmorInventory()->getHelmet()->hasEnchantment(VanillaEnchantments::AQUA_AFFINITY())){
|
||||
$breakTimePerTick *= 5;
|
||||
}
|
||||
if($breakTimePerTick > 0){
|
||||
return 1 / $breakTimePerTick;
|
||||
$progressPerTick = 1 / $breakTimePerTick;
|
||||
|
||||
$haste = $this->player->getEffects()->get(VanillaEffects::HASTE());
|
||||
if($haste !== null){
|
||||
$hasteLevel = $haste->getEffectLevel();
|
||||
$progressPerTick *= (1 + 0.2 * $hasteLevel) * (1.2 ** $hasteLevel);
|
||||
}
|
||||
|
||||
$miningFatigue = $this->player->getEffects()->get(VanillaEffects::MINING_FATIGUE());
|
||||
if($miningFatigue !== null){
|
||||
$miningFatigueLevel = $miningFatigue->getEffectLevel();
|
||||
$progressPerTick *= 0.21 ** $miningFatigueLevel;
|
||||
}
|
||||
|
||||
return $progressPerTick;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
@ -82,7 +102,10 @@ final class SurvivalBlockBreakHandler{
|
||||
$newBreakSpeed = $this->calculateBreakProgressPerTick();
|
||||
if(abs($newBreakSpeed - $this->breakSpeed) > 0.0001){
|
||||
$this->breakSpeed = $newBreakSpeed;
|
||||
//TODO: sync with client
|
||||
$this->player->getWorld()->broadcastPacketToViewers(
|
||||
$this->blockPos,
|
||||
LevelEventPacket::create(LevelEvent::BLOCK_BREAK_SPEED, (int) (65535 * $this->breakSpeed), $this->blockPos)
|
||||
);
|
||||
}
|
||||
|
||||
$this->breakProgress += $this->breakSpeed;
|
||||
|
39
src/utils/OverloadedRegistryMember.php
Normal file
39
src/utils/OverloadedRegistryMember.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
final class OverloadedRegistryMember{
|
||||
|
||||
/**
|
||||
* @phpstan-template TMember of object
|
||||
* @phpstan-param class-string<covariant \UnitEnum> $enumClass
|
||||
* @phpstan-param class-string<TMember> $memberClass
|
||||
* @phpstan-param array<string, string> $enumToMemberMap
|
||||
*/
|
||||
public function __construct(
|
||||
public readonly string $enumClass,
|
||||
public readonly string $memberClass,
|
||||
public readonly array $enumToMemberMap
|
||||
){}
|
||||
}
|
@ -24,7 +24,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use function array_map;
|
||||
use function assert;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function mb_strtoupper;
|
||||
use function preg_match;
|
||||
|
||||
@ -42,6 +44,12 @@ trait RegistryTrait{
|
||||
*/
|
||||
private static $members = null;
|
||||
|
||||
/**
|
||||
* @var OverloadedRegistryMember[]
|
||||
* @phpstan-var array<string, OverloadedRegistryMember>
|
||||
*/
|
||||
private static $overloadedMembers = [];
|
||||
|
||||
private static function verifyName(string $name) : void{
|
||||
if(preg_match('/^(?!\d)[A-Za-z\d_]+$/u', $name) === 0){
|
||||
throw new \InvalidArgumentException("Invalid member name \"$name\", should only contain letters, numbers and underscores, and must not start with a number");
|
||||
@ -59,12 +67,37 @@ trait RegistryTrait{
|
||||
}
|
||||
self::verifyName($name);
|
||||
$upperName = mb_strtoupper($name);
|
||||
if(isset(self::$members[$upperName])){
|
||||
if(isset(self::$members[$upperName]) || isset(self::$overloadedMembers[$upperName])){
|
||||
throw new \InvalidArgumentException("\"$upperName\" is already reserved");
|
||||
}
|
||||
self::$members[$upperName] = $member;
|
||||
}
|
||||
|
||||
/**
|
||||
* @phpstan-template TEnum of \UnitEnum
|
||||
* @phpstan-param class-string<TEnum> $enumClass
|
||||
* @phpstan-param class-string $returnClass
|
||||
*/
|
||||
private static function registerOverloaded(string $baseName, string $enumClass, string $returnClass) : void{
|
||||
self::verifyName($baseName);
|
||||
$upperName = mb_strtoupper($baseName);
|
||||
if(isset(self::$members[$upperName]) || isset(self::$overloadedMembers[$upperName])){
|
||||
throw new \InvalidArgumentException("\"$upperName\" is already reserved");
|
||||
}
|
||||
$enumToMemberMap = [];
|
||||
foreach($enumClass::cases() as $case){
|
||||
$memberName = mb_strtoupper($case->name . "_" . $baseName);
|
||||
if(!isset(self::$members[$memberName])){
|
||||
throw new \LogicException("\"$memberName\" needs to be registered to define overloaded member with enum $enumClass");
|
||||
}
|
||||
if(!self::$members[$memberName] instanceof $returnClass){
|
||||
throw new \LogicException("\"$memberName\" doesn't satisfy the desired type $returnClass");
|
||||
}
|
||||
$enumToMemberMap[$case->name] = $memberName;
|
||||
}
|
||||
self::$overloadedMembers[$upperName] = new OverloadedRegistryMember($enumClass, $returnClass, $enumToMemberMap);
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts default entries into the registry.
|
||||
*
|
||||
@ -111,20 +144,36 @@ trait RegistryTrait{
|
||||
* @return object
|
||||
*/
|
||||
public static function __callStatic($name, $arguments){
|
||||
if(count($arguments) > 0){
|
||||
throw new \ArgumentCountError("Expected exactly 0 arguments, " . count($arguments) . " passed");
|
||||
}
|
||||
if(count($arguments) === 0){
|
||||
//fast path
|
||||
if(self::$members !== null && isset(self::$members[$name])){
|
||||
return self::preprocessMember(self::$members[$name]);
|
||||
}
|
||||
|
||||
//fast path
|
||||
if(self::$members !== null && isset(self::$members[$name])){
|
||||
return self::preprocessMember(self::$members[$name]);
|
||||
}
|
||||
//fallback
|
||||
try{
|
||||
return self::_registryFromString($name);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
throw new \Error($e->getMessage(), 0, $e);
|
||||
}
|
||||
}elseif(count($arguments) === 1 && $arguments[0] instanceof \UnitEnum){
|
||||
$enum = $arguments[0];
|
||||
self::checkInit();
|
||||
|
||||
//fallback
|
||||
try{
|
||||
return self::_registryFromString($name);
|
||||
}catch(\InvalidArgumentException $e){
|
||||
throw new \Error($e->getMessage(), 0, $e);
|
||||
$overloadInfo = self::$overloadedMembers[$name] ?? self::$overloadedMembers[mb_strtoupper($name)] ?? null;
|
||||
|
||||
if($overloadInfo !== null){
|
||||
if($enum::class !== $overloadInfo->enumClass){
|
||||
throw new \Error("Wrong enum type for overloaded registry member " . self::class . "::" . mb_strtoupper($name) . "($overloadInfo->enumClass)");
|
||||
}
|
||||
$memberName = $overloadInfo->enumToMemberMap[$enum->name];
|
||||
assert(self::$members !== null);
|
||||
return self::preprocessMember(self::$members[$memberName]);
|
||||
}
|
||||
|
||||
throw new \Error("No such overloaded registry member: " . self::class . "::" . mb_strtoupper($name) . "(" . get_class($enum) . ")");
|
||||
}else{
|
||||
throw new \LogicException("Incorrect arguments passed to overloaded registry member: " . self::class . "::" . mb_strtoupper($name));
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,4 +185,12 @@ trait RegistryTrait{
|
||||
self::checkInit();
|
||||
return array_map(self::preprocessMember(...), self::$members ?? throw new AssumptionFailedError(self::class . "::checkInit() did not initialize self::\$members correctly"));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
* @phpstan-return array<string, OverloadedRegistryMember>
|
||||
*/
|
||||
public static function getAllOverloaded() : array{
|
||||
return self::$overloadedMembers;
|
||||
}
|
||||
}
|
||||
|
@ -69,6 +69,7 @@ abstract class Terminal{
|
||||
public static string $COLOR_MATERIAL_DIAMOND = "";
|
||||
public static string $COLOR_MATERIAL_LAPIS = "";
|
||||
public static string $COLOR_MATERIAL_AMETHYST = "";
|
||||
public static string $COLOR_MATERIAL_RESIN = "";
|
||||
|
||||
private static ?bool $formattingCodes = null;
|
||||
|
||||
@ -131,6 +132,7 @@ abstract class Terminal{
|
||||
self::$COLOR_MATERIAL_DIAMOND = $color(37);
|
||||
self::$COLOR_MATERIAL_LAPIS = $color(24);
|
||||
self::$COLOR_MATERIAL_AMETHYST = $color(98);
|
||||
self::$COLOR_MATERIAL_RESIN = $color(208);
|
||||
}
|
||||
|
||||
protected static function getEscapeCodes() : void{
|
||||
@ -174,11 +176,12 @@ abstract class Terminal{
|
||||
self::$COLOR_MATERIAL_DIAMOND = $colors >= 256 ? $setaf(37) : $setaf(14);
|
||||
self::$COLOR_MATERIAL_LAPIS = $colors >= 256 ? $setaf(24) : $setaf(12);
|
||||
self::$COLOR_MATERIAL_AMETHYST = $colors >= 256 ? $setaf(98) : $setaf(13);
|
||||
self::$COLOR_MATERIAL_RESIN = $colors >= 256 ? $setaf(208) : $setaf(11);
|
||||
}else{
|
||||
self::$COLOR_BLACK = self::$COLOR_DARK_GRAY = self::$COLOR_MATERIAL_NETHERITE = $setaf(0);
|
||||
self::$COLOR_RED = self::$COLOR_DARK_RED = self::$COLOR_MATERIAL_REDSTONE = self::$COLOR_MATERIAL_COPPER = $setaf(1);
|
||||
self::$COLOR_GREEN = self::$COLOR_DARK_GREEN = self::$COLOR_MATERIAL_EMERALD = $setaf(2);
|
||||
self::$COLOR_YELLOW = self::$COLOR_GOLD = self::$COLOR_MINECOIN_GOLD = self::$COLOR_MATERIAL_GOLD = $setaf(3);
|
||||
self::$COLOR_YELLOW = self::$COLOR_GOLD = self::$COLOR_MINECOIN_GOLD = self::$COLOR_MATERIAL_GOLD = self::$COLOR_MATERIAL_RESIN = $setaf(3);
|
||||
self::$COLOR_BLUE = self::$COLOR_DARK_BLUE = self::$COLOR_MATERIAL_LAPIS = $setaf(4);
|
||||
self::$COLOR_LIGHT_PURPLE = self::$COLOR_PURPLE = self::$COLOR_MATERIAL_AMETHYST = $setaf(5);
|
||||
self::$COLOR_AQUA = self::$COLOR_DARK_AQUA = self::$COLOR_MATERIAL_DIAMOND = $setaf(6);
|
||||
@ -253,6 +256,7 @@ abstract class Terminal{
|
||||
TextFormat::MATERIAL_DIAMOND => Terminal::$COLOR_MATERIAL_DIAMOND,
|
||||
TextFormat::MATERIAL_LAPIS => Terminal::$COLOR_MATERIAL_LAPIS,
|
||||
TextFormat::MATERIAL_AMETHYST => Terminal::$COLOR_MATERIAL_AMETHYST,
|
||||
TextFormat::MATERIAL_RESIN => Terminal::$COLOR_MATERIAL_RESIN,
|
||||
default => $token,
|
||||
};
|
||||
}
|
||||
|
@ -73,6 +73,7 @@ abstract class TextFormat{
|
||||
public const MATERIAL_DIAMOND = TextFormat::ESCAPE . "s";
|
||||
public const MATERIAL_LAPIS = TextFormat::ESCAPE . "t";
|
||||
public const MATERIAL_AMETHYST = TextFormat::ESCAPE . "u";
|
||||
public const MATERIAL_RESIN = TextFormat::ESCAPE . "v";
|
||||
|
||||
public const COLORS = [
|
||||
self::BLACK => self::BLACK,
|
||||
@ -102,6 +103,7 @@ abstract class TextFormat{
|
||||
self::MATERIAL_DIAMOND => self::MATERIAL_DIAMOND,
|
||||
self::MATERIAL_LAPIS => self::MATERIAL_LAPIS,
|
||||
self::MATERIAL_AMETHYST => self::MATERIAL_AMETHYST,
|
||||
self::MATERIAL_RESIN => self::MATERIAL_RESIN,
|
||||
];
|
||||
|
||||
public const OBFUSCATED = TextFormat::ESCAPE . "k";
|
||||
@ -150,7 +152,7 @@ abstract class TextFormat{
|
||||
* @return string[]
|
||||
*/
|
||||
public static function tokenize(string $string) : array{
|
||||
$result = preg_split("/(" . TextFormat::ESCAPE . "[0-9a-u])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
$result = preg_split("/(" . TextFormat::ESCAPE . "[0-9a-v])/u", $string, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);
|
||||
if($result === false) throw self::makePcreError();
|
||||
return $result;
|
||||
}
|
||||
@ -164,7 +166,7 @@ abstract class TextFormat{
|
||||
$string = mb_scrub($string, 'UTF-8');
|
||||
$string = self::preg_replace("/[\x{E000}-\x{F8FF}]/u", "", $string); //remove unicode private-use-area characters (they might break the console)
|
||||
if($removeFormat){
|
||||
$string = str_replace(TextFormat::ESCAPE, "", self::preg_replace("/" . TextFormat::ESCAPE . "[0-9a-u]/u", "", $string));
|
||||
$string = str_replace(TextFormat::ESCAPE, "", self::preg_replace("/" . TextFormat::ESCAPE . "[0-9a-v]/u", "", $string));
|
||||
}
|
||||
return str_replace("\x1b", "", self::preg_replace("/\x1b[\\(\\][[0-9;\\[\\(]+[Bm]/u", "", $string));
|
||||
}
|
||||
@ -175,7 +177,7 @@ abstract class TextFormat{
|
||||
* @param string $placeholder default "&"
|
||||
*/
|
||||
public static function colorize(string $string, string $placeholder = "&") : string{
|
||||
return self::preg_replace('/' . preg_quote($placeholder, "/") . '([0-9a-u])/u', TextFormat::ESCAPE . '$1', $string);
|
||||
return self::preg_replace('/' . preg_quote($placeholder, "/") . '([0-9a-v])/u', TextFormat::ESCAPE . '$1', $string);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -252,6 +254,7 @@ abstract class TextFormat{
|
||||
TextFormat::MATERIAL_DIAMOND => "color:#2cb9a8",
|
||||
TextFormat::MATERIAL_LAPIS => "color:#20487a",
|
||||
TextFormat::MATERIAL_AMETHYST => "color:#9a5cc5",
|
||||
TextFormat::MATERIAL_RESIN => "color:#fc7812",
|
||||
TextFormat::BOLD => "font-weight:bold",
|
||||
TextFormat::ITALIC => "font-style:italic",
|
||||
default => null
|
||||
|
@ -375,6 +375,8 @@ class World implements ChunkManager{
|
||||
|
||||
private \Logger $logger;
|
||||
|
||||
private RuntimeBlockStateRegistry $blockStateRegistry;
|
||||
|
||||
/**
|
||||
* @phpstan-return ChunkPosHash
|
||||
*/
|
||||
@ -488,6 +490,7 @@ class World implements ChunkManager{
|
||||
$this->displayName = $this->provider->getWorldData()->getName();
|
||||
$this->logger = new \PrefixedLogger($server->getLogger(), "World: $this->displayName");
|
||||
|
||||
$this->blockStateRegistry = RuntimeBlockStateRegistry::getInstance();
|
||||
$this->minY = $this->provider->getWorldMinY();
|
||||
$this->maxY = $this->provider->getWorldMaxY();
|
||||
|
||||
@ -559,7 +562,7 @@ class World implements ChunkManager{
|
||||
}catch(BlockStateDeserializeException){
|
||||
continue;
|
||||
}
|
||||
$block = RuntimeBlockStateRegistry::getInstance()->fromStateId(GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData));
|
||||
$block = $this->blockStateRegistry->fromStateId(GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData));
|
||||
}else{
|
||||
//TODO: we probably ought to log an error here
|
||||
continue;
|
||||
@ -570,7 +573,7 @@ class World implements ChunkManager{
|
||||
}
|
||||
}
|
||||
|
||||
foreach(RuntimeBlockStateRegistry::getInstance()->getAllKnownStates() as $state){
|
||||
foreach($this->blockStateRegistry->getAllKnownStates() as $state){
|
||||
$dontTickName = $dontTickBlocks[$state->getTypeId()] ?? null;
|
||||
if($dontTickName === null && $state->ticksRandomly()){
|
||||
$this->randomTickBlocks[$state->getStateId()] = true;
|
||||
@ -1394,7 +1397,7 @@ class World implements ChunkManager{
|
||||
$entity->onRandomUpdate();
|
||||
}
|
||||
|
||||
$blockFactory = RuntimeBlockStateRegistry::getInstance();
|
||||
$blockFactory = $this->blockStateRegistry;
|
||||
foreach($chunk->getSubChunks() as $Y => $subChunk){
|
||||
if(!$subChunk->isEmptyFast()){
|
||||
$k = 0;
|
||||
@ -1528,24 +1531,48 @@ class World implements ChunkManager{
|
||||
|
||||
$collides = [];
|
||||
|
||||
$collisionInfo = $this->blockStateRegistry->collisionInfo;
|
||||
if($targetFirst){
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
$zOverflow = $z === $minZ || $z === $maxZ;
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
$zxOverflow = $zOverflow || $x === $minX || $x === $maxX;
|
||||
for($y = $minY; $y <= $maxY; ++$y){
|
||||
$block = $this->getBlockAt($x, $y, $z);
|
||||
if($block->collidesWithBB($bb)){
|
||||
return [$block];
|
||||
$overflow = $zxOverflow || $y === $minY || $y === $maxY;
|
||||
|
||||
$stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo);
|
||||
if($overflow ?
|
||||
$stateCollisionInfo === RuntimeBlockStateRegistry::COLLISION_MAY_OVERFLOW && $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) :
|
||||
match ($stateCollisionInfo) {
|
||||
RuntimeBlockStateRegistry::COLLISION_CUBE => true,
|
||||
RuntimeBlockStateRegistry::COLLISION_NONE => false,
|
||||
default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb)
|
||||
}
|
||||
){
|
||||
return [$this->getBlockAt($x, $y, $z)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}else{
|
||||
//TODO: duplicated code :( this way is better for performance though
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
$zOverflow = $z === $minZ || $z === $maxZ;
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
$zxOverflow = $zOverflow || $x === $minX || $x === $maxX;
|
||||
for($y = $minY; $y <= $maxY; ++$y){
|
||||
$block = $this->getBlockAt($x, $y, $z);
|
||||
if($block->collidesWithBB($bb)){
|
||||
$collides[] = $block;
|
||||
$overflow = $zxOverflow || $y === $minY || $y === $maxY;
|
||||
|
||||
$stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo);
|
||||
if($overflow ?
|
||||
$stateCollisionInfo === RuntimeBlockStateRegistry::COLLISION_MAY_OVERFLOW && $this->getBlockAt($x, $y, $z)->collidesWithBB($bb) :
|
||||
match ($stateCollisionInfo) {
|
||||
RuntimeBlockStateRegistry::COLLISION_CUBE => true,
|
||||
RuntimeBlockStateRegistry::COLLISION_NONE => false,
|
||||
default => $this->getBlockAt($x, $y, $z)->collidesWithBB($bb)
|
||||
}
|
||||
){
|
||||
$collides[] = $this->getBlockAt($x, $y, $z);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1555,24 +1582,64 @@ class World implements ChunkManager{
|
||||
return $collides;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int[] $collisionInfo
|
||||
* @phpstan-param array<int, int> $collisionInfo
|
||||
*/
|
||||
private function getBlockCollisionInfo(int $x, int $y, int $z, array $collisionInfo) : int{
|
||||
if(!$this->isInWorld($x, $y, $z)){
|
||||
return RuntimeBlockStateRegistry::COLLISION_NONE;
|
||||
}
|
||||
$chunk = $this->getChunk($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE);
|
||||
if($chunk === null){
|
||||
return RuntimeBlockStateRegistry::COLLISION_NONE;
|
||||
}
|
||||
$stateId = $chunk
|
||||
->getSubChunk($y >> SubChunk::COORD_BIT_SIZE)
|
||||
->getBlockStateId(
|
||||
$x & SubChunk::COORD_MASK,
|
||||
$y & SubChunk::COORD_MASK,
|
||||
$z & SubChunk::COORD_MASK
|
||||
);
|
||||
return $collisionInfo[$stateId];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of all block AABBs which overlap the full block area at the given coordinates.
|
||||
* This checks a padding of 1 block around the coordinates to account for oversized AABBs of blocks like fences.
|
||||
* Larger AABBs (>= 2 blocks on any axis) are not accounted for.
|
||||
*
|
||||
* @param int[] $collisionInfo
|
||||
* @phpstan-param array<int, int> $collisionInfo
|
||||
*
|
||||
* @return AxisAlignedBB[]
|
||||
* @phpstan-return list<AxisAlignedBB>
|
||||
*/
|
||||
private function getBlockCollisionBoxesForCell(int $x, int $y, int $z) : array{
|
||||
$block = $this->getBlockAt($x, $y, $z);
|
||||
$boxes = $block->getCollisionBoxes();
|
||||
private function getBlockCollisionBoxesForCell(int $x, int $y, int $z, array $collisionInfo) : array{
|
||||
$stateCollisionInfo = $this->getBlockCollisionInfo($x, $y, $z, $collisionInfo);
|
||||
$boxes = match($stateCollisionInfo){
|
||||
RuntimeBlockStateRegistry::COLLISION_NONE => [],
|
||||
RuntimeBlockStateRegistry::COLLISION_CUBE => [AxisAlignedBB::one()->offset($x, $y, $z)],
|
||||
default => $this->getBlockAt($x, $y, $z)->getCollisionBoxes()
|
||||
};
|
||||
|
||||
$cellBB = AxisAlignedBB::one()->offset($x, $y, $z);
|
||||
foreach(Facing::OFFSET as [$dx, $dy, $dz]){
|
||||
$extraBoxes = $this->getBlockAt($x + $dx, $y + $dy, $z + $dz)->getCollisionBoxes();
|
||||
foreach($extraBoxes as $extraBox){
|
||||
if($extraBox->intersectsWith($cellBB)){
|
||||
$boxes[] = $extraBox;
|
||||
//overlapping AABBs can't make any difference if this is a cube, so we can save some CPU cycles in this common case
|
||||
if($stateCollisionInfo !== RuntimeBlockStateRegistry::COLLISION_CUBE){
|
||||
$cellBB = null;
|
||||
foreach(Facing::OFFSET as [$dx, $dy, $dz]){
|
||||
$offsetX = $x + $dx;
|
||||
$offsetY = $y + $dy;
|
||||
$offsetZ = $z + $dz;
|
||||
$stateCollisionInfo = $this->getBlockCollisionInfo($offsetX, $offsetY, $offsetZ, $collisionInfo);
|
||||
if($stateCollisionInfo === RuntimeBlockStateRegistry::COLLISION_MAY_OVERFLOW){
|
||||
//avoid allocating this unless it's needed
|
||||
$cellBB ??= AxisAlignedBB::one()->offset($x, $y, $z);
|
||||
$extraBoxes = $this->getBlockAt($offsetX, $offsetY, $offsetZ)->getCollisionBoxes();
|
||||
foreach($extraBoxes as $extraBox){
|
||||
if($extraBox->intersectsWith($cellBB)){
|
||||
$boxes[] = $extraBox;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1594,13 +1661,15 @@ class World implements ChunkManager{
|
||||
|
||||
$collides = [];
|
||||
|
||||
$collisionInfo = $this->blockStateRegistry->collisionInfo;
|
||||
|
||||
for($z = $minZ; $z <= $maxZ; ++$z){
|
||||
for($x = $minX; $x <= $maxX; ++$x){
|
||||
$chunkPosHash = World::chunkHash($x >> Chunk::COORD_BIT_SIZE, $z >> Chunk::COORD_BIT_SIZE);
|
||||
for($y = $minY; $y <= $maxY; ++$y){
|
||||
$relativeBlockHash = World::chunkBlockHash($x, $y, $z);
|
||||
|
||||
$boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getBlockCollisionBoxesForCell($x, $y, $z);
|
||||
$boxes = $this->blockCollisionBoxCache[$chunkPosHash][$relativeBlockHash] ??= $this->getBlockCollisionBoxesForCell($x, $y, $z, $collisionInfo);
|
||||
|
||||
foreach($boxes as $blockBB){
|
||||
if($blockBB->intersectsWith($bb)){
|
||||
@ -1795,7 +1864,7 @@ class World implements ChunkManager{
|
||||
return;
|
||||
}
|
||||
|
||||
$blockFactory = RuntimeBlockStateRegistry::getInstance();
|
||||
$blockFactory = $this->blockStateRegistry;
|
||||
$this->timings->doBlockSkyLightUpdates->startTiming();
|
||||
if($this->skyLightUpdate === null){
|
||||
$this->skyLightUpdate = new SkyLightUpdate(new SubChunkExplorer($this), $blockFactory->lightFilter, $blockFactory->blocksDirectSkyLight);
|
||||
@ -1914,7 +1983,7 @@ class World implements ChunkManager{
|
||||
|
||||
$chunk = $this->chunks[$chunkHash] ?? null;
|
||||
if($chunk !== null){
|
||||
$block = RuntimeBlockStateRegistry::getInstance()->fromStateId($chunk->getBlockStateId($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK));
|
||||
$block = $this->blockStateRegistry->fromStateId($chunk->getBlockStateId($x & Chunk::COORD_MASK, $y, $z & Chunk::COORD_MASK));
|
||||
}else{
|
||||
$addToCache = false;
|
||||
$block = VanillaBlocks::AIR();
|
||||
@ -2573,7 +2642,7 @@ class World implements ChunkManager{
|
||||
$localY = $tilePosition->getFloorY();
|
||||
$localZ = $tilePosition->getFloorZ() & Chunk::COORD_MASK;
|
||||
|
||||
$newBlock = RuntimeBlockStateRegistry::getInstance()->fromStateId($chunk->getBlockStateId($localX, $localY, $localZ));
|
||||
$newBlock = $this->blockStateRegistry->fromStateId($chunk->getBlockStateId($localX, $localY, $localZ));
|
||||
$expectedTileClass = $newBlock->getIdInfo()->getTileClass();
|
||||
if(
|
||||
$expectedTileClass === null || //new block doesn't expect a tile
|
||||
|
Reference in New Issue
Block a user