From 944dd7d3e4cca0fe9ceb79548293c56c92ed8da1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Dec 2023 15:19:44 +0000 Subject: [PATCH 01/34] BaseBanner: remove unnecessary array_filter() usage --- src/block/BaseBanner.php | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index c2bbaf737a6..6b9e493d19e 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -34,7 +34,6 @@ use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; -use function array_filter; use function assert; use function count; @@ -89,11 +88,12 @@ public function getPatterns() : array{ * @return $this */ public function setPatterns(array $patterns) : self{ - $checked = array_filter($patterns, fn($v) => $v instanceof BannerPatternLayer); - if(count($checked) !== count($patterns)){ - throw new \TypeError("Deque must only contain " . BannerPatternLayer::class . " objects"); + foreach($patterns as $pattern){ + if(!$pattern instanceof BannerPatternLayer){ + throw new \TypeError("Array must only contain " . BannerPatternLayer::class . " objects"); + } } - $this->patterns = $checked; + $this->patterns = $patterns; return $this; } From d3d7f24015626442ccd73f92697ed9dd3999b565 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Dec 2023 15:32:54 +0000 Subject: [PATCH 02/34] Noise: make calls with many parameters less nauseating to read --- src/world/generator/noise/Noise.php | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/src/world/generator/noise/Noise.php b/src/world/generator/noise/Noise.php index af9cefe14e0..54d3c2759fe 100644 --- a/src/world/generator/noise/Noise.php +++ b/src/world/generator/noise/Noise.php @@ -217,7 +217,13 @@ public function getFastNoise1D(int $xSize, int $samplingRate, int $x, int $y, in for($xx = 0; $xx < $xSize; ++$xx){ if($xx % $samplingRate !== 0){ $nx = (int) ($xx / $samplingRate) * $samplingRate; - $noiseArray[$xx] = self::linearLerp($xx, $nx, $nx + $samplingRate, $noiseArray[$nx], $noiseArray[$nx + $samplingRate]); + $noiseArray[$xx] = self::linearLerp( + x: $xx, + x1: $nx, + x2: $nx + $samplingRate, + q0: $noiseArray[$nx], + q1: $noiseArray[$nx + $samplingRate] + ); } } @@ -253,9 +259,16 @@ public function getFastNoise2D(int $xSize, int $zSize, int $samplingRate, int $x $nx = (int) ($xx / $samplingRate) * $samplingRate; $nz = (int) ($zz / $samplingRate) * $samplingRate; $noiseArray[$xx][$zz] = Noise::bilinearLerp( - $xx, $zz, $noiseArray[$nx][$nz], $noiseArray[$nx][$nz + $samplingRate], - $noiseArray[$nx + $samplingRate][$nz], $noiseArray[$nx + $samplingRate][$nz + $samplingRate], - $nx, $nx + $samplingRate, $nz, $nz + $samplingRate + x: $xx, + y: $zz, + q00: $noiseArray[$nx][$nz], + q01: $noiseArray[$nx][$nz + $samplingRate], + q10: $noiseArray[$nx + $samplingRate][$nz], + q11: $noiseArray[$nx + $samplingRate][$nz + $samplingRate], + x1: $nx, + x2: $nx + $samplingRate, + y1: $nz, + y2: $nz + $samplingRate ); } } From e1f4fd3048c3504d5af957705084f124dedb2824 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Dec 2023 16:01:43 +0000 Subject: [PATCH 03/34] ProcessLoginTask: remove dead comments This is no longer an issue since b2df405cc0355dcffe202bc4040fae316940d427. --- src/network/mcpe/auth/ProcessLoginTask.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/network/mcpe/auth/ProcessLoginTask.php b/src/network/mcpe/auth/ProcessLoginTask.php index 607b75c89e2..c396338da94 100644 --- a/src/network/mcpe/auth/ProcessLoginTask.php +++ b/src/network/mcpe/auth/ProcessLoginTask.php @@ -128,7 +128,6 @@ private function validateToken(string $jwt, ?string &$currentPublicKey, bool $fi try{ [$headersArray, $claimsArray, ] = JwtUtils::parse($jwt); }catch(JwtException $e){ - //TODO: we shouldn't be showing internal information like this to the client throw new VerifyLoginException("Failed to parse JWT: " . $e->getMessage(), null, 0, $e); } @@ -141,13 +140,11 @@ private function validateToken(string $jwt, ?string &$currentPublicKey, bool $fi /** @var JwtHeader $headers */ $headers = $mapper->map($headersArray, new JwtHeader()); }catch(\JsonMapper_Exception $e){ - //TODO: we shouldn't be showing internal information like this to the client throw new VerifyLoginException("Invalid JWT header: " . $e->getMessage(), null, 0, $e); } $headerDerKey = base64_decode($headers->x5u, true); if($headerDerKey === false){ - //TODO: we shouldn't be showing internal information like this to the client throw new VerifyLoginException("Invalid JWT public key: base64 decoding error decoding x5u"); } @@ -163,7 +160,6 @@ private function validateToken(string $jwt, ?string &$currentPublicKey, bool $fi try{ $signingKeyOpenSSL = JwtUtils::parseDerPublicKey($headerDerKey); }catch(JwtException $e){ - //TODO: we shouldn't be showing this internal information to the client throw new VerifyLoginException("Invalid JWT public key: " . $e->getMessage(), null, 0, $e); } try{ From c8da9dea9545c917fcbf5fd1514e83186af3cc17 Mon Sep 17 00:00:00 2001 From: DiamondStrider1 <62265561+Swift-Strider@users.noreply.github.com> Date: Wed, 20 Dec 2023 11:38:00 +0100 Subject: [PATCH 04/34] WorldManager: Remove unused if in unloadWorld() (#6203) --- src/world/WorldManager.php | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/world/WorldManager.php b/src/world/WorldManager.php index bd968f9042c..ff603a2dfcb 100644 --- a/src/world/WorldManager.php +++ b/src/world/WorldManager.php @@ -129,10 +129,6 @@ public function unloadWorld(World $world, bool $forceUnload = false) : bool{ } $ev = new WorldUnloadEvent($world); - if($world === $this->defaultWorld && !$forceUnload){ - $ev->cancel(); - } - $ev->call(); if(!$forceUnload && $ev->isCancelled()){ From 74cb0be868b5a56023bd6a17d91833a184df831e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Dec 2023 14:32:05 +0000 Subject: [PATCH 05/34] Noise: give PHPStan some help understanding SplFixedArray --- src/world/generator/noise/Noise.php | 2 ++ tests/phpstan/configs/actual-problems.neon | 15 +++++++++++++++ 2 files changed, 17 insertions(+) diff --git a/src/world/generator/noise/Noise.php b/src/world/generator/noise/Noise.php index 54d3c2759fe..d91a58350ad 100644 --- a/src/world/generator/noise/Noise.php +++ b/src/world/generator/noise/Noise.php @@ -208,6 +208,7 @@ public function getFastNoise1D(int $xSize, int $samplingRate, int $x, int $y, in throw new \InvalidArgumentException("xSize % samplingRate must return 0"); } + /** @phpstan-var \SplFixedArray $noiseArray */ $noiseArray = new \SplFixedArray($xSize + 1); for($xx = 0; $xx <= $xSize; $xx += $samplingRate){ @@ -240,6 +241,7 @@ public function getFastNoise2D(int $xSize, int $zSize, int $samplingRate, int $x assert($xSize % $samplingRate === 0, new \InvalidArgumentException("xSize % samplingRate must return 0")); assert($zSize % $samplingRate === 0, new \InvalidArgumentException("zSize % samplingRate must return 0")); + /** @phpstan-var \SplFixedArray<\SplFixedArray> $noiseArray */ $noiseArray = new \SplFixedArray($xSize + 1); for($xx = 0; $xx <= $xSize; $xx += $samplingRate){ diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 51454a70837..9fea3803d8a 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -1070,6 +1070,21 @@ parameters: count: 1 path: ../../../src/world/generator/hell/Nether.php + - + message: "#^Offset int does not exist on SplFixedArray\\\\|null\\.$#" + count: 4 + path: ../../../src/world/generator/noise/Noise.php + + - + message: "#^Parameter \\$q0 of static method pocketmine\\\\world\\\\generator\\\\noise\\\\Noise\\:\\:linearLerp\\(\\) expects float, float\\|null given\\.$#" + count: 1 + path: ../../../src/world/generator/noise/Noise.php + + - + message: "#^Parameter \\$q1 of static method pocketmine\\\\world\\\\generator\\\\noise\\\\Noise\\:\\:linearLerp\\(\\) expects float, float\\|null given\\.$#" + count: 1 + path: ../../../src/world/generator/noise/Noise.php + - message: "#^Cannot call method getBiomeId\\(\\) on pocketmine\\\\world\\\\format\\\\Chunk\\|null\\.$#" count: 1 From 58ce746ae10771279e553fdb2ac39202f850407d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Dec 2023 14:44:24 +0000 Subject: [PATCH 06/34] Remove dead PHPStan ignored error --- tests/phpstan/configs/phpstan-bugs.neon | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index b3bf3dadd86..af048661144 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -1,10 +1,5 @@ parameters: ignoreErrors: - - - message: "#^Instanceof between pocketmine\\\\block\\\\utils\\\\BannerPatternLayer and pocketmine\\\\block\\\\utils\\\\BannerPatternLayer will always evaluate to true\\.$#" - count: 1 - path: ../../../src/block/BaseBanner.php - - message: "#^Method pocketmine\\\\block\\\\CakeWithCandle\\:\\:onInteractCandle\\(\\) has parameter \\$returnedItems with no value type specified in iterable type array\\.$#" count: 1 From 8dc28b7ea8e7337783dd85c91f73662f9b6283ee Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Dec 2023 15:15:43 +0000 Subject: [PATCH 07/34] RuntimeDataDescriber: remove useless template parameter --- src/data/runtime/LegacyRuntimeEnumDescriberTrait.php | 5 ----- src/data/runtime/RuntimeDataDescriber.php | 4 ---- 2 files changed, 9 deletions(-) diff --git a/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php b/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php index dd35fabfbf1..3c540f7fa75 100644 --- a/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php +++ b/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php @@ -29,11 +29,6 @@ * @deprecated */ trait LegacyRuntimeEnumDescriberTrait{ - - /** - * @phpstan-template T of \UnitEnum - * @phpstan-param T $case - */ abstract protected function enum(\UnitEnum &$case) : void; public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{ diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 04217f5df56..6f1d35b9838 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -89,10 +89,6 @@ public function railShape(int &$railShape) : void; public function straightOnlyRailShape(int &$railShape) : void; - /** - * @phpstan-template T of \UnitEnum - * @phpstan-param T $case - */ public function enum(\UnitEnum &$case) : void; /** From 80125f9b193a453f6d13df1cca5a0e911f044123 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Dec 2023 15:20:28 +0000 Subject: [PATCH 08/34] Modernize single-use phpstan-template tags --- src/event/HandlerList.php | 3 +-- src/event/HandlerListManager.php | 6 ++---- src/timings/Timings.php | 6 ++---- 3 files changed, 5 insertions(+), 10 deletions(-) diff --git a/src/event/HandlerList.php b/src/event/HandlerList.php index 74eedf3a4a2..2072cd5226f 100644 --- a/src/event/HandlerList.php +++ b/src/event/HandlerList.php @@ -37,8 +37,7 @@ class HandlerList{ private array $affectedHandlerCaches = []; /** - * @phpstan-template TEvent of Event - * @phpstan-param class-string $class + * @phpstan-param class-string $class */ public function __construct( private string $class, diff --git a/src/event/HandlerListManager.php b/src/event/HandlerListManager.php index 047632f5483..605a3874789 100644 --- a/src/event/HandlerListManager.php +++ b/src/event/HandlerListManager.php @@ -86,8 +86,7 @@ private static function resolveNearestHandleableParent(\ReflectionClass $class) * * Calling this method also lazily initializes the $classMap inheritance tree of handler lists. * - * @phpstan-template TEvent of Event - * @phpstan-param class-string $event + * @phpstan-param class-string $event * * @throws \ReflectionException * @throws \InvalidArgumentException @@ -113,8 +112,7 @@ public function getListFor(string $event) : HandlerList{ } /** - * @phpstan-template TEvent of Event - * @phpstan-param class-string $event + * @phpstan-param class-string $event * * @return RegisteredListener[] */ diff --git a/src/timings/Timings.php b/src/timings/Timings.php index 61a8bbc927d..563af69bff6 100644 --- a/src/timings/Timings.php +++ b/src/timings/Timings.php @@ -210,8 +210,7 @@ public static function getScheduledTaskTimings(TaskHandler $task, int $period) : } /** - * @phpstan-template T of object - * @phpstan-param class-string $class + * @phpstan-param class-string $class */ private static function shortenCoreClassName(string $class, string $prefix) : string{ if(str_starts_with($class, $prefix)){ @@ -302,8 +301,7 @@ public static function getEventTimings(Event $event) : TimingsHandler{ } /** - * @phpstan-template TEvent of Event - * @phpstan-param class-string $event + * @phpstan-param class-string $event */ public static function getEventHandlerTimings(string $event, string $handlerName, string $group) : TimingsHandler{ if(!isset(self::$eventHandlers[$event][$handlerName])){ From c51b1b28125a12f5c874ed9aeee4ac9f331cc43a Mon Sep 17 00:00:00 2001 From: Hugo_ <55756021+Dhaiven@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:21:11 +0100 Subject: [PATCH 09/34] Create LightableTrait and remove repetitive code (#6111) --- src/block/Furnace.php | 16 ++--------- src/block/RedstoneOre.php | 20 ++----------- src/block/RedstoneTorch.php | 20 +++++-------- src/block/utils/CandleTrait.php | 15 +--------- src/block/utils/LightableTrait.php | 46 ++++++++++++++++++++++++++++++ 5 files changed, 58 insertions(+), 59 deletions(-) create mode 100644 src/block/utils/LightableTrait.php diff --git a/src/block/Furnace.php b/src/block/Furnace.php index fbff73c9398..7a64e3cd3e5 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -25,6 +25,7 @@ use pocketmine\block\tile\Furnace as TileFurnace; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; +use pocketmine\block\utils\LightableTrait; use pocketmine\crafting\FurnaceType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -34,11 +35,10 @@ class Furnace extends Opaque{ use FacesOppositePlacingPlayerTrait; + use LightableTrait; protected FurnaceType $furnaceType; - protected bool $lit = false; - public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, FurnaceType $furnaceType){ $this->furnaceType = $furnaceType; parent::__construct($idInfo, $name, $typeInfo); @@ -57,18 +57,6 @@ public function getLightLevel() : int{ return $this->lit ? 13 : 0; } - public function isLit() : bool{ - return $this->lit; - } - - /** - * @return $this - */ - public function setLit(bool $lit = true) : self{ - $this->lit = $lit; - return $this; - } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $furnace = $this->position->getWorld()->getTile($this->position); diff --git a/src/block/RedstoneOre.php b/src/block/RedstoneOre.php index 9e537bd279a..10e701a6f4c 100644 --- a/src/block/RedstoneOre.php +++ b/src/block/RedstoneOre.php @@ -24,7 +24,7 @@ namespace pocketmine\block; use pocketmine\block\utils\FortuneDropHelper; -use pocketmine\data\runtime\RuntimeDataDescriber; +use pocketmine\block\utils\LightableTrait; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\Vector3; @@ -32,23 +32,7 @@ use function mt_rand; class RedstoneOre extends Opaque{ - protected bool $lit = false; - - protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->bool($this->lit); - } - - public function isLit() : bool{ - return $this->lit; - } - - /** - * @return $this - */ - public function setLit(bool $lit = true) : self{ - $this->lit = $lit; - return $this; - } + use LightableTrait; public function getLightLevel() : int{ return $this->lit ? 9 : 0; diff --git a/src/block/RedstoneTorch.php b/src/block/RedstoneTorch.php index b30c011d4d8..26c86038b95 100644 --- a/src/block/RedstoneTorch.php +++ b/src/block/RedstoneTorch.php @@ -23,28 +23,22 @@ namespace pocketmine\block; +use pocketmine\block\utils\LightableTrait; use pocketmine\data\runtime\RuntimeDataDescriber; class RedstoneTorch extends Torch{ - protected bool $lit = true; + use LightableTrait; + + public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){ + $this->lit = true; + parent::__construct($idInfo, $name, $typeInfo); + } protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ parent::describeBlockOnlyState($w); $w->bool($this->lit); } - public function isLit() : bool{ - return $this->lit; - } - - /** - * @return $this - */ - public function setLit(bool $lit = true) : self{ - $this->lit = $lit; - return $this; - } - public function getLightLevel() : int{ return $this->lit ? 7 : 0; } diff --git a/src/block/utils/CandleTrait.php b/src/block/utils/CandleTrait.php index 58a7443a3ce..c9da97ee0cc 100644 --- a/src/block/utils/CandleTrait.php +++ b/src/block/utils/CandleTrait.php @@ -24,7 +24,6 @@ namespace pocketmine\block\utils; use pocketmine\block\Block; -use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\projectile\Projectile; use pocketmine\item\Durable; use pocketmine\item\enchantment\VanillaEnchantments; @@ -38,24 +37,12 @@ use pocketmine\world\sound\FlintSteelSound; trait CandleTrait{ - private bool $lit = false; - - protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->bool($this->lit); - } + use LightableTrait; public function getLightLevel() : int{ return $this->lit ? 3 : 0; } - public function isLit() : bool{ return $this->lit; } - - /** @return $this */ - public function setLit(bool $lit) : self{ - $this->lit = $lit; - return $this; - } - /** @see Block::onInteract() */ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item->getTypeId() === ItemTypeIds::FIRE_CHARGE || $item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){ diff --git a/src/block/utils/LightableTrait.php b/src/block/utils/LightableTrait.php new file mode 100644 index 00000000000..51ce54f42c1 --- /dev/null +++ b/src/block/utils/LightableTrait.php @@ -0,0 +1,46 @@ +bool($this->lit); + } + + public function isLit() : bool{ + return $this->lit; + } + + /** + * @return $this + */ + public function setLit(bool $lit = true) : self{ + $this->lit = $lit; + return $this; + } +} From 57f3a04bc50700c3e0727277b59ce436604e429a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Dec 2023 16:06:54 +0000 Subject: [PATCH 10/34] data: Use statically analyzable ways of ensuring all cases are registered PHPStan will verify that these matches cover all cases, which guarantees that all cases will be covered. In addition, if PHPStan is not used, the constructors will immediately bail out when they hit a case that isn't covered. The only downside is the extra indentation :( --- src/data/bedrock/BannerPatternTypeIdMap.php | 80 +++++++++-------- src/data/bedrock/DyeColorIdMap.php | 38 ++++---- src/data/bedrock/MedicineTypeIdMap.php | 12 ++- src/data/bedrock/MobHeadTypeIdMap.php | 18 ++-- src/data/bedrock/MushroomBlockTypeIdMap.php | 26 +++--- src/data/bedrock/NoteInstrumentIdMap.php | 36 ++++---- src/data/bedrock/PotionTypeIdMap.php | 90 ++++++++++--------- src/data/bedrock/SuspiciousStewTypeIdMap.php | 25 +++--- src/data/java/GameModeIdMap.php | 12 ++- .../data/bedrock/DyeColorIdMapTest.php | 38 -------- 10 files changed, 188 insertions(+), 187 deletions(-) delete mode 100644 tests/phpunit/data/bedrock/DyeColorIdMapTest.php diff --git a/src/data/bedrock/BannerPatternTypeIdMap.php b/src/data/bedrock/BannerPatternTypeIdMap.php index 064844e199f..87f9b8f5713 100644 --- a/src/data/bedrock/BannerPatternTypeIdMap.php +++ b/src/data/bedrock/BannerPatternTypeIdMap.php @@ -43,44 +43,48 @@ final class BannerPatternTypeIdMap{ private array $enumToId = []; public function __construct(){ - $this->register("bo", BannerPatternType::BORDER); - $this->register("bri", BannerPatternType::BRICKS); - $this->register("mc", BannerPatternType::CIRCLE); - $this->register("cre", BannerPatternType::CREEPER); - $this->register("cr", BannerPatternType::CROSS); - $this->register("cbo", BannerPatternType::CURLY_BORDER); - $this->register("lud", BannerPatternType::DIAGONAL_LEFT); - $this->register("rd", BannerPatternType::DIAGONAL_RIGHT); - $this->register("ld", BannerPatternType::DIAGONAL_UP_LEFT); - $this->register("rud", BannerPatternType::DIAGONAL_UP_RIGHT); - $this->register("flo", BannerPatternType::FLOWER); - $this->register("gra", BannerPatternType::GRADIENT); - $this->register("gru", BannerPatternType::GRADIENT_UP); - $this->register("hh", BannerPatternType::HALF_HORIZONTAL); - $this->register("hhb", BannerPatternType::HALF_HORIZONTAL_BOTTOM); - $this->register("vh", BannerPatternType::HALF_VERTICAL); - $this->register("vhr", BannerPatternType::HALF_VERTICAL_RIGHT); - $this->register("moj", BannerPatternType::MOJANG); - $this->register("mr", BannerPatternType::RHOMBUS); - $this->register("sku", BannerPatternType::SKULL); - $this->register("ss", BannerPatternType::SMALL_STRIPES); - $this->register("bl", BannerPatternType::SQUARE_BOTTOM_LEFT); - $this->register("br", BannerPatternType::SQUARE_BOTTOM_RIGHT); - $this->register("tl", BannerPatternType::SQUARE_TOP_LEFT); - $this->register("tr", BannerPatternType::SQUARE_TOP_RIGHT); - $this->register("sc", BannerPatternType::STRAIGHT_CROSS); - $this->register("bs", BannerPatternType::STRIPE_BOTTOM); - $this->register("cs", BannerPatternType::STRIPE_CENTER); - $this->register("dls", BannerPatternType::STRIPE_DOWNLEFT); - $this->register("drs", BannerPatternType::STRIPE_DOWNRIGHT); - $this->register("ls", BannerPatternType::STRIPE_LEFT); - $this->register("ms", BannerPatternType::STRIPE_MIDDLE); - $this->register("rs", BannerPatternType::STRIPE_RIGHT); - $this->register("ts", BannerPatternType::STRIPE_TOP); - $this->register("bt", BannerPatternType::TRIANGLE_BOTTOM); - $this->register("tt", BannerPatternType::TRIANGLE_TOP); - $this->register("bts", BannerPatternType::TRIANGLES_BOTTOM); - $this->register("tts", BannerPatternType::TRIANGLES_TOP); + foreach(BannerPatternType::cases() as $case){ + $this->register(match($case){ + BannerPatternType::BORDER => "bo", + BannerPatternType::BRICKS => "bri", + BannerPatternType::CIRCLE => "mc", + BannerPatternType::CREEPER => "cre", + BannerPatternType::CROSS => "cr", + BannerPatternType::CURLY_BORDER => "cbo", + BannerPatternType::DIAGONAL_LEFT => "lud", + BannerPatternType::DIAGONAL_RIGHT => "rd", + BannerPatternType::DIAGONAL_UP_LEFT => "ld", + BannerPatternType::DIAGONAL_UP_RIGHT => "rud", + BannerPatternType::FLOWER => "flo", + BannerPatternType::GRADIENT => "gra", + BannerPatternType::GRADIENT_UP => "gru", + BannerPatternType::HALF_HORIZONTAL => "hh", + BannerPatternType::HALF_HORIZONTAL_BOTTOM => "hhb", + BannerPatternType::HALF_VERTICAL => "vh", + BannerPatternType::HALF_VERTICAL_RIGHT => "vhr", + BannerPatternType::MOJANG => "moj", + BannerPatternType::RHOMBUS => "mr", + BannerPatternType::SKULL => "sku", + BannerPatternType::SMALL_STRIPES => "ss", + BannerPatternType::SQUARE_BOTTOM_LEFT => "bl", + BannerPatternType::SQUARE_BOTTOM_RIGHT => "br", + BannerPatternType::SQUARE_TOP_LEFT => "tl", + BannerPatternType::SQUARE_TOP_RIGHT => "tr", + BannerPatternType::STRAIGHT_CROSS => "sc", + BannerPatternType::STRIPE_BOTTOM => "bs", + BannerPatternType::STRIPE_CENTER => "cs", + BannerPatternType::STRIPE_DOWNLEFT => "dls", + BannerPatternType::STRIPE_DOWNRIGHT => "drs", + BannerPatternType::STRIPE_LEFT => "ls", + BannerPatternType::STRIPE_MIDDLE => "ms", + BannerPatternType::STRIPE_RIGHT => "rs", + BannerPatternType::STRIPE_TOP => "ts", + BannerPatternType::TRIANGLE_BOTTOM => "bt", + BannerPatternType::TRIANGLE_TOP => "tt", + BannerPatternType::TRIANGLES_BOTTOM => "bts", + BannerPatternType::TRIANGLES_TOP => "tts", + }, $case); + } } public function register(string $stringId, BannerPatternType $type) : void{ diff --git a/src/data/bedrock/DyeColorIdMap.php b/src/data/bedrock/DyeColorIdMap.php index a360e4f910a..60c50970595 100644 --- a/src/data/bedrock/DyeColorIdMap.php +++ b/src/data/bedrock/DyeColorIdMap.php @@ -48,22 +48,28 @@ final class DyeColorIdMap{ private array $enumToItemId = []; private function __construct(){ - $this->register(0, ItemTypeNames::WHITE_DYE, DyeColor::WHITE); - $this->register(1, ItemTypeNames::ORANGE_DYE, DyeColor::ORANGE); - $this->register(2, ItemTypeNames::MAGENTA_DYE, DyeColor::MAGENTA); - $this->register(3, ItemTypeNames::LIGHT_BLUE_DYE, DyeColor::LIGHT_BLUE); - $this->register(4, ItemTypeNames::YELLOW_DYE, DyeColor::YELLOW); - $this->register(5, ItemTypeNames::LIME_DYE, DyeColor::LIME); - $this->register(6, ItemTypeNames::PINK_DYE, DyeColor::PINK); - $this->register(7, ItemTypeNames::GRAY_DYE, DyeColor::GRAY); - $this->register(8, ItemTypeNames::LIGHT_GRAY_DYE, DyeColor::LIGHT_GRAY); - $this->register(9, ItemTypeNames::CYAN_DYE, DyeColor::CYAN); - $this->register(10, ItemTypeNames::PURPLE_DYE, DyeColor::PURPLE); - $this->register(11, ItemTypeNames::BLUE_DYE, DyeColor::BLUE); - $this->register(12, ItemTypeNames::BROWN_DYE, DyeColor::BROWN); - $this->register(13, ItemTypeNames::GREEN_DYE, DyeColor::GREEN); - $this->register(14, ItemTypeNames::RED_DYE, DyeColor::RED); - $this->register(15, ItemTypeNames::BLACK_DYE, DyeColor::BLACK); + foreach(DyeColor::cases() as $case){ + [$colorId, $dyeItemId] = match($case){ + DyeColor::WHITE => [0, ItemTypeNames::WHITE_DYE], + DyeColor::ORANGE => [1, ItemTypeNames::ORANGE_DYE], + DyeColor::MAGENTA => [2, ItemTypeNames::MAGENTA_DYE], + DyeColor::LIGHT_BLUE => [3, ItemTypeNames::LIGHT_BLUE_DYE], + DyeColor::YELLOW => [4, ItemTypeNames::YELLOW_DYE], + DyeColor::LIME => [5, ItemTypeNames::LIME_DYE], + DyeColor::PINK => [6, ItemTypeNames::PINK_DYE], + DyeColor::GRAY => [7, ItemTypeNames::GRAY_DYE], + DyeColor::LIGHT_GRAY => [8, ItemTypeNames::LIGHT_GRAY_DYE], + DyeColor::CYAN => [9, ItemTypeNames::CYAN_DYE], + DyeColor::PURPLE => [10, ItemTypeNames::PURPLE_DYE], + DyeColor::BLUE => [11, ItemTypeNames::BLUE_DYE], + DyeColor::BROWN => [12, ItemTypeNames::BROWN_DYE], + DyeColor::GREEN => [13, ItemTypeNames::GREEN_DYE], + DyeColor::RED => [14, ItemTypeNames::RED_DYE], + DyeColor::BLACK => [15, ItemTypeNames::BLACK_DYE], + }; + + $this->register($colorId, $dyeItemId, $case); + } } private function register(int $id, string $itemId, DyeColor $color) : void{ diff --git a/src/data/bedrock/MedicineTypeIdMap.php b/src/data/bedrock/MedicineTypeIdMap.php index 00d1f27a866..90fd835509d 100644 --- a/src/data/bedrock/MedicineTypeIdMap.php +++ b/src/data/bedrock/MedicineTypeIdMap.php @@ -32,9 +32,13 @@ final class MedicineTypeIdMap{ use IntSaveIdMapTrait; private function __construct(){ - $this->register(MedicineTypeIds::ANTIDOTE, MedicineType::ANTIDOTE); - $this->register(MedicineTypeIds::ELIXIR, MedicineType::ELIXIR); - $this->register(MedicineTypeIds::EYE_DROPS, MedicineType::EYE_DROPS); - $this->register(MedicineTypeIds::TONIC, MedicineType::TONIC); + foreach(MedicineType::cases() as $case){ + $this->register(match($case){ + MedicineType::ANTIDOTE => MedicineTypeIds::ANTIDOTE, + MedicineType::ELIXIR => MedicineTypeIds::ELIXIR, + MedicineType::EYE_DROPS => MedicineTypeIds::EYE_DROPS, + MedicineType::TONIC => MedicineTypeIds::TONIC, + }, $case); + } } } diff --git a/src/data/bedrock/MobHeadTypeIdMap.php b/src/data/bedrock/MobHeadTypeIdMap.php index ec678b19241..bf16e6eba0a 100644 --- a/src/data/bedrock/MobHeadTypeIdMap.php +++ b/src/data/bedrock/MobHeadTypeIdMap.php @@ -32,12 +32,16 @@ final class MobHeadTypeIdMap{ use IntSaveIdMapTrait; private function __construct(){ - $this->register(0, MobHeadType::SKELETON); - $this->register(1, MobHeadType::WITHER_SKELETON); - $this->register(2, MobHeadType::ZOMBIE); - $this->register(3, MobHeadType::PLAYER); - $this->register(4, MobHeadType::CREEPER); - $this->register(5, MobHeadType::DRAGON); - $this->register(6, MobHeadType::PIGLIN); + foreach(MobHeadType::cases() as $case){ + $this->register(match($case){ + MobHeadType::SKELETON => 0, + MobHeadType::WITHER_SKELETON => 1, + MobHeadType::ZOMBIE => 2, + MobHeadType::PLAYER => 3, + MobHeadType::CREEPER => 4, + MobHeadType::DRAGON => 5, + MobHeadType::PIGLIN => 6, + }, $case); + } } } diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php index 92edef4b23f..a25336d897d 100644 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ b/src/data/bedrock/MushroomBlockTypeIdMap.php @@ -33,16 +33,20 @@ final class MushroomBlockTypeIdMap{ use IntSaveIdMapTrait; public function __construct(){ - $this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, MushroomBlockType::PORES); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, MushroomBlockType::CAP_NORTHWEST); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, MushroomBlockType::CAP_NORTH); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, MushroomBlockType::CAP_NORTHEAST); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, MushroomBlockType::CAP_WEST); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, MushroomBlockType::CAP_MIDDLE); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, MushroomBlockType::CAP_EAST); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, MushroomBlockType::CAP_SOUTHWEST); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, MushroomBlockType::CAP_SOUTH); - $this->register(LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, MushroomBlockType::CAP_SOUTHEAST); - $this->register(LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, MushroomBlockType::ALL_CAP); + foreach(MushroomBlockType::cases() as $case){ + $this->register(match($case){ + MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, + MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, + MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, + MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, + MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, + MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, + MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, + MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, + MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, + MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, + MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, + }, $case); + } } } diff --git a/src/data/bedrock/NoteInstrumentIdMap.php b/src/data/bedrock/NoteInstrumentIdMap.php index e721b881d2e..c847ecd98c0 100644 --- a/src/data/bedrock/NoteInstrumentIdMap.php +++ b/src/data/bedrock/NoteInstrumentIdMap.php @@ -32,21 +32,25 @@ final class NoteInstrumentIdMap{ use IntSaveIdMapTrait; private function __construct(){ - $this->register(0, NoteInstrument::PIANO); - $this->register(1, NoteInstrument::BASS_DRUM); - $this->register(2, NoteInstrument::SNARE); - $this->register(3, NoteInstrument::CLICKS_AND_STICKS); - $this->register(4, NoteInstrument::DOUBLE_BASS); - $this->register(5, NoteInstrument::BELL); - $this->register(6, NoteInstrument::FLUTE); - $this->register(7, NoteInstrument::CHIME); - $this->register(8, NoteInstrument::GUITAR); - $this->register(9, NoteInstrument::XYLOPHONE); - $this->register(10, NoteInstrument::IRON_XYLOPHONE); - $this->register(11, NoteInstrument::COW_BELL); - $this->register(12, NoteInstrument::DIDGERIDOO); - $this->register(13, NoteInstrument::BIT); - $this->register(14, NoteInstrument::BANJO); - $this->register(15, NoteInstrument::PLING); + foreach(NoteInstrument::cases() as $case){ + $this->register(match($case){ + NoteInstrument::PIANO => 0, + NoteInstrument::BASS_DRUM => 1, + NoteInstrument::SNARE => 2, + NoteInstrument::CLICKS_AND_STICKS => 3, + NoteInstrument::DOUBLE_BASS => 4, + NoteInstrument::BELL => 5, + NoteInstrument::FLUTE => 6, + NoteInstrument::CHIME => 7, + NoteInstrument::GUITAR => 8, + NoteInstrument::XYLOPHONE => 9, + NoteInstrument::IRON_XYLOPHONE => 10, + NoteInstrument::COW_BELL => 11, + NoteInstrument::DIDGERIDOO => 12, + NoteInstrument::BIT => 13, + NoteInstrument::BANJO => 14, + NoteInstrument::PLING => 15, + }, $case); + } } } diff --git a/src/data/bedrock/PotionTypeIdMap.php b/src/data/bedrock/PotionTypeIdMap.php index 3fef20f68ce..4e7d8d4c79f 100644 --- a/src/data/bedrock/PotionTypeIdMap.php +++ b/src/data/bedrock/PotionTypeIdMap.php @@ -32,48 +32,52 @@ final class PotionTypeIdMap{ use IntSaveIdMapTrait; private function __construct(){ - $this->register(PotionTypeIds::WATER, PotionType::WATER); - $this->register(PotionTypeIds::MUNDANE, PotionType::MUNDANE); - $this->register(PotionTypeIds::LONG_MUNDANE, PotionType::LONG_MUNDANE); - $this->register(PotionTypeIds::THICK, PotionType::THICK); - $this->register(PotionTypeIds::AWKWARD, PotionType::AWKWARD); - $this->register(PotionTypeIds::NIGHT_VISION, PotionType::NIGHT_VISION); - $this->register(PotionTypeIds::LONG_NIGHT_VISION, PotionType::LONG_NIGHT_VISION); - $this->register(PotionTypeIds::INVISIBILITY, PotionType::INVISIBILITY); - $this->register(PotionTypeIds::LONG_INVISIBILITY, PotionType::LONG_INVISIBILITY); - $this->register(PotionTypeIds::LEAPING, PotionType::LEAPING); - $this->register(PotionTypeIds::LONG_LEAPING, PotionType::LONG_LEAPING); - $this->register(PotionTypeIds::STRONG_LEAPING, PotionType::STRONG_LEAPING); - $this->register(PotionTypeIds::FIRE_RESISTANCE, PotionType::FIRE_RESISTANCE); - $this->register(PotionTypeIds::LONG_FIRE_RESISTANCE, PotionType::LONG_FIRE_RESISTANCE); - $this->register(PotionTypeIds::SWIFTNESS, PotionType::SWIFTNESS); - $this->register(PotionTypeIds::LONG_SWIFTNESS, PotionType::LONG_SWIFTNESS); - $this->register(PotionTypeIds::STRONG_SWIFTNESS, PotionType::STRONG_SWIFTNESS); - $this->register(PotionTypeIds::SLOWNESS, PotionType::SLOWNESS); - $this->register(PotionTypeIds::LONG_SLOWNESS, PotionType::LONG_SLOWNESS); - $this->register(PotionTypeIds::WATER_BREATHING, PotionType::WATER_BREATHING); - $this->register(PotionTypeIds::LONG_WATER_BREATHING, PotionType::LONG_WATER_BREATHING); - $this->register(PotionTypeIds::HEALING, PotionType::HEALING); - $this->register(PotionTypeIds::STRONG_HEALING, PotionType::STRONG_HEALING); - $this->register(PotionTypeIds::HARMING, PotionType::HARMING); - $this->register(PotionTypeIds::STRONG_HARMING, PotionType::STRONG_HARMING); - $this->register(PotionTypeIds::POISON, PotionType::POISON); - $this->register(PotionTypeIds::LONG_POISON, PotionType::LONG_POISON); - $this->register(PotionTypeIds::STRONG_POISON, PotionType::STRONG_POISON); - $this->register(PotionTypeIds::REGENERATION, PotionType::REGENERATION); - $this->register(PotionTypeIds::LONG_REGENERATION, PotionType::LONG_REGENERATION); - $this->register(PotionTypeIds::STRONG_REGENERATION, PotionType::STRONG_REGENERATION); - $this->register(PotionTypeIds::STRENGTH, PotionType::STRENGTH); - $this->register(PotionTypeIds::LONG_STRENGTH, PotionType::LONG_STRENGTH); - $this->register(PotionTypeIds::STRONG_STRENGTH, PotionType::STRONG_STRENGTH); - $this->register(PotionTypeIds::WEAKNESS, PotionType::WEAKNESS); - $this->register(PotionTypeIds::LONG_WEAKNESS, PotionType::LONG_WEAKNESS); - $this->register(PotionTypeIds::WITHER, PotionType::WITHER); - $this->register(PotionTypeIds::TURTLE_MASTER, PotionType::TURTLE_MASTER); - $this->register(PotionTypeIds::LONG_TURTLE_MASTER, PotionType::LONG_TURTLE_MASTER); - $this->register(PotionTypeIds::STRONG_TURTLE_MASTER, PotionType::STRONG_TURTLE_MASTER); - $this->register(PotionTypeIds::SLOW_FALLING, PotionType::SLOW_FALLING); - $this->register(PotionTypeIds::LONG_SLOW_FALLING, PotionType::LONG_SLOW_FALLING); - $this->register(PotionTypeIds::STRONG_SLOWNESS, PotionType::STRONG_SLOWNESS); + foreach(PotionType::cases() as $case){ + $this->register(match($case){ + PotionType::WATER => PotionTypeIds::WATER, + PotionType::MUNDANE => PotionTypeIds::MUNDANE, + PotionType::LONG_MUNDANE => PotionTypeIds::LONG_MUNDANE, + PotionType::THICK => PotionTypeIds::THICK, + PotionType::AWKWARD => PotionTypeIds::AWKWARD, + PotionType::NIGHT_VISION => PotionTypeIds::NIGHT_VISION, + PotionType::LONG_NIGHT_VISION => PotionTypeIds::LONG_NIGHT_VISION, + PotionType::INVISIBILITY => PotionTypeIds::INVISIBILITY, + PotionType::LONG_INVISIBILITY => PotionTypeIds::LONG_INVISIBILITY, + PotionType::LEAPING => PotionTypeIds::LEAPING, + PotionType::LONG_LEAPING => PotionTypeIds::LONG_LEAPING, + PotionType::STRONG_LEAPING => PotionTypeIds::STRONG_LEAPING, + PotionType::FIRE_RESISTANCE => PotionTypeIds::FIRE_RESISTANCE, + PotionType::LONG_FIRE_RESISTANCE => PotionTypeIds::LONG_FIRE_RESISTANCE, + PotionType::SWIFTNESS => PotionTypeIds::SWIFTNESS, + PotionType::LONG_SWIFTNESS => PotionTypeIds::LONG_SWIFTNESS, + PotionType::STRONG_SWIFTNESS => PotionTypeIds::STRONG_SWIFTNESS, + PotionType::SLOWNESS => PotionTypeIds::SLOWNESS, + PotionType::LONG_SLOWNESS => PotionTypeIds::LONG_SLOWNESS, + PotionType::WATER_BREATHING => PotionTypeIds::WATER_BREATHING, + PotionType::LONG_WATER_BREATHING => PotionTypeIds::LONG_WATER_BREATHING, + PotionType::HEALING => PotionTypeIds::HEALING, + PotionType::STRONG_HEALING => PotionTypeIds::STRONG_HEALING, + PotionType::HARMING => PotionTypeIds::HARMING, + PotionType::STRONG_HARMING => PotionTypeIds::STRONG_HARMING, + PotionType::POISON => PotionTypeIds::POISON, + PotionType::LONG_POISON => PotionTypeIds::LONG_POISON, + PotionType::STRONG_POISON => PotionTypeIds::STRONG_POISON, + PotionType::REGENERATION => PotionTypeIds::REGENERATION, + PotionType::LONG_REGENERATION => PotionTypeIds::LONG_REGENERATION, + PotionType::STRONG_REGENERATION => PotionTypeIds::STRONG_REGENERATION, + PotionType::STRENGTH => PotionTypeIds::STRENGTH, + PotionType::LONG_STRENGTH => PotionTypeIds::LONG_STRENGTH, + PotionType::STRONG_STRENGTH => PotionTypeIds::STRONG_STRENGTH, + PotionType::WEAKNESS => PotionTypeIds::WEAKNESS, + PotionType::LONG_WEAKNESS => PotionTypeIds::LONG_WEAKNESS, + PotionType::WITHER => PotionTypeIds::WITHER, + PotionType::TURTLE_MASTER => PotionTypeIds::TURTLE_MASTER, + PotionType::LONG_TURTLE_MASTER => PotionTypeIds::LONG_TURTLE_MASTER, + PotionType::STRONG_TURTLE_MASTER => PotionTypeIds::STRONG_TURTLE_MASTER, + PotionType::SLOW_FALLING => PotionTypeIds::SLOW_FALLING, + PotionType::LONG_SLOW_FALLING => PotionTypeIds::LONG_SLOW_FALLING, + PotionType::STRONG_SLOWNESS => PotionTypeIds::STRONG_SLOWNESS, + }, $case); + } } } diff --git a/src/data/bedrock/SuspiciousStewTypeIdMap.php b/src/data/bedrock/SuspiciousStewTypeIdMap.php index c4de4b742d5..d6a31f0ea79 100644 --- a/src/data/bedrock/SuspiciousStewTypeIdMap.php +++ b/src/data/bedrock/SuspiciousStewTypeIdMap.php @@ -32,15 +32,20 @@ final class SuspiciousStewTypeIdMap{ use IntSaveIdMapTrait; private function __construct(){ - $this->register(SuspiciousStewTypeIds::POPPY, SuspiciousStewType::POPPY); - $this->register(SuspiciousStewTypeIds::CORNFLOWER, SuspiciousStewType::CORNFLOWER); - $this->register(SuspiciousStewTypeIds::TULIP, SuspiciousStewType::TULIP); - $this->register(SuspiciousStewTypeIds::AZURE_BLUET, SuspiciousStewType::AZURE_BLUET); - $this->register(SuspiciousStewTypeIds::LILY_OF_THE_VALLEY, SuspiciousStewType::LILY_OF_THE_VALLEY); - $this->register(SuspiciousStewTypeIds::DANDELION, SuspiciousStewType::DANDELION); - $this->register(SuspiciousStewTypeIds::BLUE_ORCHID, SuspiciousStewType::BLUE_ORCHID); - $this->register(SuspiciousStewTypeIds::ALLIUM, SuspiciousStewType::ALLIUM); - $this->register(SuspiciousStewTypeIds::OXEYE_DAISY, SuspiciousStewType::OXEYE_DAISY); - $this->register(SuspiciousStewTypeIds::WITHER_ROSE, SuspiciousStewType::WITHER_ROSE); + foreach(SuspiciousStewType::cases() as $case){ + $this->register(match($case){ + SuspiciousStewType::POPPY => SuspiciousStewTypeIds::POPPY, + SuspiciousStewType::CORNFLOWER => SuspiciousStewTypeIds::CORNFLOWER, + SuspiciousStewType::TULIP => SuspiciousStewTypeIds::TULIP, + SuspiciousStewType::AZURE_BLUET => SuspiciousStewTypeIds::AZURE_BLUET, + SuspiciousStewType::LILY_OF_THE_VALLEY => SuspiciousStewTypeIds::LILY_OF_THE_VALLEY, + SuspiciousStewType::DANDELION => SuspiciousStewTypeIds::DANDELION, + SuspiciousStewType::BLUE_ORCHID => SuspiciousStewTypeIds::BLUE_ORCHID, + SuspiciousStewType::ALLIUM => SuspiciousStewTypeIds::ALLIUM, + SuspiciousStewType::OXEYE_DAISY => SuspiciousStewTypeIds::OXEYE_DAISY, + SuspiciousStewType::WITHER_ROSE => SuspiciousStewTypeIds::WITHER_ROSE, + }, $case); + } + } } diff --git a/src/data/java/GameModeIdMap.php b/src/data/java/GameModeIdMap.php index 41258dd66e0..9262723ed87 100644 --- a/src/data/java/GameModeIdMap.php +++ b/src/data/java/GameModeIdMap.php @@ -44,10 +44,14 @@ final class GameModeIdMap{ private array $enumToId = []; public function __construct(){ - $this->register(0, GameMode::SURVIVAL); - $this->register(1, GameMode::CREATIVE); - $this->register(2, GameMode::ADVENTURE); - $this->register(3, GameMode::SPECTATOR); + foreach(GameMode::cases() as $case){ + $this->register(match($case){ + GameMode::SURVIVAL => 0, + GameMode::CREATIVE => 1, + GameMode::ADVENTURE => 2, + GameMode::SPECTATOR => 3, + }, $case); + } } private function register(int $id, GameMode $type) : void{ diff --git a/tests/phpunit/data/bedrock/DyeColorIdMapTest.php b/tests/phpunit/data/bedrock/DyeColorIdMapTest.php deleted file mode 100644 index a3ca6db25ce..00000000000 --- a/tests/phpunit/data/bedrock/DyeColorIdMapTest.php +++ /dev/null @@ -1,38 +0,0 @@ -toId($color); - $color2 = DyeColorIdMap::getInstance()->fromId($id); - self::assertTrue($color === $color2); - } - } -} From bf99917f2a6404ad5dff61874adbf283d9060936 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 20 Dec 2023 17:01:20 +0000 Subject: [PATCH 11/34] ThreadSafeClassLoader: add native property types --- src/thread/ThreadSafeClassLoader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/thread/ThreadSafeClassLoader.php b/src/thread/ThreadSafeClassLoader.php index 95b983dc1b8..fd9e6afed6e 100644 --- a/src/thread/ThreadSafeClassLoader.php +++ b/src/thread/ThreadSafeClassLoader.php @@ -51,12 +51,12 @@ class ThreadSafeClassLoader extends ThreadSafe{ * @var ThreadSafeArray|string[] * @phpstan-var ThreadSafeArray */ - private $fallbackLookup; + private ThreadSafeArray $fallbackLookup; /** * @var ThreadSafeArray|string[][] * @phpstan-var ThreadSafeArray> */ - private $psr4Lookup; + private ThreadSafeArray $psr4Lookup; public function __construct(){ $this->fallbackLookup = new ThreadSafeArray(); From c05116849ad47d08bd2d1708a3b8d9e05df27368 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Dec 2023 12:38:51 +0000 Subject: [PATCH 12/34] AsyncWorker: clean up nonsensical sleeper notifier handling code --- src/scheduler/AsyncTask.php | 6 +----- src/scheduler/AsyncWorker.php | 13 ++++++------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/scheduler/AsyncTask.php b/src/scheduler/AsyncTask.php index b4c3ce20db6..b0b64347adc 100644 --- a/src/scheduler/AsyncTask.php +++ b/src/scheduler/AsyncTask.php @@ -24,12 +24,10 @@ namespace pocketmine\scheduler; use pmmp\thread\Runnable; -use pmmp\thread\Thread as NativeThread; use pmmp\thread\ThreadSafe; use pmmp\thread\ThreadSafeArray; use pocketmine\thread\NonThreadSafeValue; use function array_key_exists; -use function assert; use function igbinary_serialize; use function igbinary_unserialize; use function is_null; @@ -83,9 +81,7 @@ public function run() : void{ $this->onRun(); $this->finished = true; - $worker = NativeThread::getCurrentThread(); - assert($worker instanceof AsyncWorker); - $worker->getNotifier()->wakeupSleeper(); + AsyncWorker::getNotifier()->wakeupSleeper(); } /** diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php index 919e3eedcf9..0f05dd89695 100644 --- a/src/scheduler/AsyncWorker.php +++ b/src/scheduler/AsyncWorker.php @@ -36,7 +36,7 @@ class AsyncWorker extends Worker{ /** @var mixed[] */ private static array $store = []; - private const TLS_KEY_NOTIFIER = self::class . "::notifier"; + private static ?SleeperNotifier $notifier = null; public function __construct( private ThreadSafeLogger $logger, @@ -45,12 +45,11 @@ public function __construct( private SleeperHandlerEntry $sleeperEntry ){} - public function getNotifier() : SleeperNotifier{ - $notifier = $this->getFromThreadStore(self::TLS_KEY_NOTIFIER); - if(!$notifier instanceof SleeperNotifier){ - throw new AssumptionFailedError("SleeperNotifier not found in thread-local storage"); + public static function getNotifier() : SleeperNotifier{ + if(self::$notifier !== null){ + return self::$notifier; } - return $notifier; + throw new AssumptionFailedError("SleeperNotifier not found in thread-local storage"); } protected function onRun() : void{ @@ -66,7 +65,7 @@ protected function onRun() : void{ $this->logger->debug("No memory limit set"); } - $this->saveToThreadStore(self::TLS_KEY_NOTIFIER, $this->sleeperEntry->createNotifier()); + self::$notifier = $this->sleeperEntry->createNotifier(); } public function getLogger() : ThreadSafeLogger{ From fd1bc1b8456907e8449087e5550ea08a1fd35e5c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Dec 2023 12:39:55 +0000 Subject: [PATCH 13/34] AsyncWorker: deprecate ThreadStore methods these are inconvenient and don't make any sense. It's far easier and more static-analysis-friendly to just use static properties. --- src/scheduler/AsyncWorker.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/scheduler/AsyncWorker.php b/src/scheduler/AsyncWorker.php index 0f05dd89695..5fdfb1ebb73 100644 --- a/src/scheduler/AsyncWorker.php +++ b/src/scheduler/AsyncWorker.php @@ -83,6 +83,8 @@ public function getAsyncWorkerId() : int{ /** * Saves mixed data into the worker's thread-local object store. This can be used to store objects which you * want to use on this worker thread from multiple AsyncTasks. + * + * @deprecated Use static class properties instead. */ public function saveToThreadStore(string $identifier, mixed $value) : void{ if(NativeThread::getCurrentThread() !== $this){ @@ -98,6 +100,8 @@ public function saveToThreadStore(string $identifier, mixed $value) : void{ * account for the possibility that what you're trying to retrieve might not exist. * * Objects stored in this storage may ONLY be retrieved while the task is running. + * + * @deprecated Use static class properties instead. */ public function getFromThreadStore(string $identifier) : mixed{ if(NativeThread::getCurrentThread() !== $this){ @@ -108,6 +112,8 @@ public function getFromThreadStore(string $identifier) : mixed{ /** * Removes previously-stored mixed data from the worker's thread-local object store. + * + * @deprecated Use static class properties instead. */ public function removeFromThreadStore(string $identifier) : void{ if(NativeThread::getCurrentThread() !== $this){ From 03619ebca91387113e749555463c306b33754b0f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Dec 2023 12:44:03 +0000 Subject: [PATCH 14/34] Thread/Worker: Remove duplicated code Despite the comments, there doesn't seem to be an obvious reason for these to be copy-pasted. Perhaps there was some legacy reason for this with legacy pthreads. In fact, it looks likely that quit() will probably be able to be traitified too. --- src/thread/CommonThreadPartsTrait.php | 10 ++++++++++ src/thread/Thread.php | 10 ---------- src/thread/Worker.php | 11 ----------- 3 files changed, 10 insertions(+), 21 deletions(-) diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index de2ecbde81d..f01a979b09a 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -23,6 +23,7 @@ namespace pocketmine\thread; +use pmmp\thread\Thread as NativeThread; use pmmp\thread\ThreadSafeArray; use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\Server; @@ -96,6 +97,15 @@ public function registerClassLoaders() : void{ public function getCrashInfo() : ?ThreadCrashInfo{ return $this->crashInfo; } + public function start(int $options = NativeThread::INHERIT_NONE) : bool{ + ThreadManager::getInstance()->add($this); + + if($this->getClassLoaders() === null){ + $this->setClassLoaders(); + } + return parent::start($options); + } + final public function run() : void{ error_reporting(-1); $this->registerClassLoaders(); diff --git a/src/thread/Thread.php b/src/thread/Thread.php index 706f964298f..2f020402292 100644 --- a/src/thread/Thread.php +++ b/src/thread/Thread.php @@ -38,16 +38,6 @@ abstract class Thread extends NativeThread{ use CommonThreadPartsTrait; - public function start(int $options = NativeThread::INHERIT_NONE) : bool{ - //this is intentionally not traitified - ThreadManager::getInstance()->add($this); - - if($this->getClassLoaders() === null){ - $this->setClassLoaders(); - } - return parent::start($options); - } - /** * Stops the thread using the best way possible. Try to stop it yourself before calling this. */ diff --git a/src/thread/Worker.php b/src/thread/Worker.php index 3bc5cda97aa..7d9ca72c3b0 100644 --- a/src/thread/Worker.php +++ b/src/thread/Worker.php @@ -23,7 +23,6 @@ namespace pocketmine\thread; -use pmmp\thread\Thread as NativeThread; use pmmp\thread\Worker as NativeWorker; use pocketmine\scheduler\AsyncTask; @@ -40,16 +39,6 @@ abstract class Worker extends NativeWorker{ use CommonThreadPartsTrait; - public function start(int $options = NativeThread::INHERIT_NONE) : bool{ - //this is intentionally not traitified - ThreadManager::getInstance()->add($this); - - if($this->getClassLoaders() === null){ - $this->setClassLoaders(); - } - return parent::start($options); - } - /** * Stops the thread using the best way possible. Try to stop it yourself before calling this. */ From b69843a8bd2e72f16ee2f82a2842cfc1a0ca0b7a Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Dec 2023 12:56:51 +0000 Subject: [PATCH 15/34] CommonThreadPartsTrait: add common implementation of quit() there's no need for the worker specialization here (isShutdown and shutdown are aliased to isJoined and join respectively), and the unstacking is not really desirable either as we previously learned with AsyncPool. --- src/thread/CommonThreadPartsTrait.php | 14 ++++++++++++++ src/thread/Thread.php | 14 -------------- src/thread/Worker.php | 17 ----------------- 3 files changed, 14 insertions(+), 31 deletions(-) diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index f01a979b09a..9cd2b68f9a0 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -120,6 +120,20 @@ final public function run() : void{ $this->isKilled = true; } + /** + * Stops the thread using the best way possible. Try to stop it yourself before calling this. + */ + public function quit() : void{ + $this->isKilled = true; + + if(!$this->isJoined()){ + $this->notify(); + $this->join(); + } + + ThreadManager::getInstance()->remove($this); + } + /** * Called by set_exception_handler() when an uncaught exception is thrown. */ diff --git a/src/thread/Thread.php b/src/thread/Thread.php index 2f020402292..a6c36e14c76 100644 --- a/src/thread/Thread.php +++ b/src/thread/Thread.php @@ -37,18 +37,4 @@ */ abstract class Thread extends NativeThread{ use CommonThreadPartsTrait; - - /** - * Stops the thread using the best way possible. Try to stop it yourself before calling this. - */ - public function quit() : void{ - $this->isKilled = true; - - if(!$this->isJoined()){ - $this->notify(); - $this->join(); - } - - ThreadManager::getInstance()->remove($this); - } } diff --git a/src/thread/Worker.php b/src/thread/Worker.php index 7d9ca72c3b0..cc0b56046a4 100644 --- a/src/thread/Worker.php +++ b/src/thread/Worker.php @@ -38,21 +38,4 @@ */ abstract class Worker extends NativeWorker{ use CommonThreadPartsTrait; - - /** - * Stops the thread using the best way possible. Try to stop it yourself before calling this. - */ - public function quit() : void{ - $this->isKilled = true; - - if(!$this->isShutdown()){ - $this->synchronized(function() : void{ - while($this->unstack() !== null); - }); - $this->notify(); - $this->shutdown(); - } - - ThreadManager::getInstance()->remove($this); - } } From 1b0ef468f389832bf598b029548e199bd435776d Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 21 Dec 2023 13:09:05 +0000 Subject: [PATCH 16/34] CommonThreadPartsTrait: remove outdated documentation This is now automatically called by the final run(), and the user now only needs to implement onRun(), so they have no business calling this function. --- src/thread/CommonThreadPartsTrait.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index 9cd2b68f9a0..9a14b23454b 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -78,9 +78,7 @@ public function setClassLoaders(?array $autoloaders = null) : void{ /** * Registers the class loaders for this thread. * - * WARNING: This method MUST be called from any descendent threads' run() method to make autoloading usable. - * If you do not do this, you will not be able to use new classes that were not loaded when the thread was started - * (unless you are using a custom autoloader). + * @internal */ public function registerClassLoaders() : void{ if($this->composerAutoloaderPath !== null){ From 5386e8607977583767a62d8f4eba2011f9a050ea Mon Sep 17 00:00:00 2001 From: GameParrot <85067619+GameParrot@users.noreply.github.com> Date: Wed, 3 Jan 2024 07:50:05 -0500 Subject: [PATCH 17/34] ProcessLoginTask: remove old root key (#6211) --- src/network/mcpe/auth/ProcessLoginTask.php | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/network/mcpe/auth/ProcessLoginTask.php b/src/network/mcpe/auth/ProcessLoginTask.php index c396338da94..3bd8d62b866 100644 --- a/src/network/mcpe/auth/ProcessLoginTask.php +++ b/src/network/mcpe/auth/ProcessLoginTask.php @@ -39,16 +39,6 @@ class ProcessLoginTask extends AsyncTask{ private const TLS_KEY_ON_COMPLETION = "completion"; - /** - * Old Mojang root auth key. This was used since the introduction of Xbox Live authentication in 0.15.0. - * This key is expected to be replaced by the key below in the future, but this has not yet happened as of - * 2023-07-01. - * Ideally we would place a time expiry on this key, but since Mojang have not given a hard date for the key change, - * and one bad guess has already caused a major outage, we can't do this. - * TODO: This needs to be removed as soon as the new key is deployed by Mojang's authentication servers. - */ - public const MOJANG_OLD_ROOT_PUBLIC_KEY = "MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAE8ELkixyLcwlZryUQcu1TvPOmI2B7vX83ndnWRUaXm74wFfa5f/lwQNTfrLVHa2PmenpGI6JhIMUJaWZrjmMj90NoKNFSNBuKdm8rYiXsfaz3K36x/1U26HpG0ZxK/V1V"; - /** * New Mojang root auth key. Mojang notified third-party developers of this change prior to the release of 1.20.0. * Expectations were that this would be used starting a "couple of weeks" after the release, but as of 2023-07-01, @@ -170,7 +160,7 @@ private function validateToken(string $jwt, ?string &$currentPublicKey, bool $fi throw new VerifyLoginException($e->getMessage(), null, 0, $e); } - if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY || $headers->x5u === self::MOJANG_OLD_ROOT_PUBLIC_KEY){ + if($headers->x5u === self::MOJANG_ROOT_PUBLIC_KEY){ $this->authenticated = true; //we're signed into xbox live } From db3bb55a2bf9daa658af6c119d2da9335d6a23bf Mon Sep 17 00:00:00 2001 From: Ayesh Karunaratne Date: Tue, 9 Jan 2024 12:28:20 +0000 Subject: [PATCH 18/34] Change `PHP_DEBUG` constant usage to `ZEND_DEBUG_BUILD` In PHP 8.4, the type of `PHP_DEBUG` changes from `int` to `bool`. See [PHP.Watch: PHP 8.4: `PHP_ZTS` and `PHP_DEBUG` constant value type changed from `int` to `bool`](https://php.watch/versions/8.4/PHP_ZTS-PHP_DEBUG-const-type-change). This changes the constants to `ZEND_DEBUG_BUILD`, which contains the same value but as a `bool` across all PHP versions. closes #6222 --- src/PocketMine.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/PocketMine.php b/src/PocketMine.php index 72d72506d32..1209b28f02b 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -166,7 +166,7 @@ function check_platform_dependencies(){ * @return void */ function emit_performance_warnings(\Logger $logger){ - if(PHP_DEBUG !== 0){ + if(ZEND_DEBUG_BUILD){ $logger->warning("This PHP binary was compiled in debug mode. This has a major impact on performance."); } if(extension_loaded("xdebug") && (!function_exists('xdebug_info') || count(xdebug_info('mode')) !== 0)){ From 9b03b082ab4b2b18a194419fb49066198d24ad1b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 9 Jan 2024 13:04:14 +0000 Subject: [PATCH 19/34] Added --version option --- src/BootstrapOptions.php | 2 ++ src/PocketMine.php | 9 +++++++++ 2 files changed, 11 insertions(+) diff --git a/src/BootstrapOptions.php b/src/BootstrapOptions.php index 879c502a69d..c34dda94b99 100644 --- a/src/BootstrapOptions.php +++ b/src/BootstrapOptions.php @@ -45,4 +45,6 @@ private function __construct(){ public const PLUGINS = "plugins"; /** Path to store and load server data */ public const DATA = "data"; + /** Shows basic server version information and exits */ + public const VERSION = "version"; } diff --git a/src/PocketMine.php b/src/PocketMine.php index 1209b28f02b..d13cf33a1a9 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -25,6 +25,7 @@ use Composer\InstalledVersions; use pocketmine\errorhandler\ErrorToExceptionHandler; + use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\thread\ThreadManager; use pocketmine\thread\ThreadSafeClassLoader; use pocketmine\utils\Filesystem; @@ -40,14 +41,17 @@ use function extension_loaded; use function function_exists; use function getcwd; + use function getopt; use function is_dir; use function mkdir; use function phpversion; use function preg_match; use function preg_quote; + use function printf; use function realpath; use function version_compare; use const DIRECTORY_SEPARATOR; + use const PHP_EOL; require_once __DIR__ . '/VersionInfo.php'; @@ -273,6 +277,11 @@ function server(){ ErrorToExceptionHandler::set(); + if(count(getopt("", [BootstrapOptions::VERSION])) > 0){ + printf("%s %s (git hash %s) for Minecraft: Bedrock Edition %s\n", VersionInfo::NAME, VersionInfo::VERSION()->getFullVersion(true), VersionInfo::GIT_HASH(), ProtocolInfo::MINECRAFT_VERSION); + exit(0); + } + $cwd = Utils::assumeNotFalse(realpath(Utils::assumeNotFalse(getcwd()))); $dataPath = getopt_string(BootstrapOptions::DATA) ?? $cwd; $pluginPath = getopt_string(BootstrapOptions::PLUGINS) ?? $cwd . DIRECTORY_SEPARATOR . "plugins"; From 288bd4018baf6c92ce7e3f2264fb8d4e7f6e1bc5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 9 Jan 2024 13:35:10 +0000 Subject: [PATCH 20/34] Block: deprecate isSolid() As discussed many years ago in #2551, no one actually knows what this property actually means. It definitely isn't the conventionally expected definition of 'solid' found in the real world, as signs are solid but flower pots are not. --- src/block/Block.php | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/block/Block.php b/src/block/Block.php index b2847bb3583..d6158ea9478 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -576,6 +576,15 @@ public function isTransparent() : bool{ return false; } + /** + * @deprecated TL;DR: Don't use this function. Its results are confusing and inconsistent. + * + * No one is sure what the meaning of this property actually is. It's borrowed from Minecraft Java Edition, and is + * used by various blocks for support checks. + * + * Things like signs and banners are considered "solid" despite having no collision box, and things like skulls and + * flower pots are considered non-solid despite obviously being "solid" in the conventional, real-world sense. + */ public function isSolid() : bool{ return true; } From a459e3c1a9c3f485c7073aa06a2c09b3ff884570 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 9 Jan 2024 13:35:36 +0000 Subject: [PATCH 21/34] Block: improve some documentation --- src/block/Block.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/block/Block.php b/src/block/Block.php index d6158ea9478..dbc269c6301 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -402,7 +402,7 @@ public function writeStateToWorld() : void{ } /** - * AKA: Block->isPlaceable + * Returns whether this block can be placed when obtained as an item. */ public function canBePlaced() : bool{ return true; @@ -572,6 +572,9 @@ public function blocksDirectSkyLight() : bool{ return $this->getLightFilter() > 0; } + /** + * Returns whether this block allows any light to pass through it. + */ public function isTransparent() : bool{ return false; } @@ -590,7 +593,7 @@ public function isSolid() : bool{ } /** - * AKA: Block->isFlowable + * Returns whether this block can be destroyed by liquid flowing into its cell. */ public function canBeFlowedInto() : bool{ return false; From c7c20d4d7906674cee099c191165935075af4f8f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 9 Jan 2024 16:43:11 +0000 Subject: [PATCH 22/34] tools/generate-block-palette-spec: fixed sorting --- tools/generate-block-palette-spec.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tools/generate-block-palette-spec.php b/tools/generate-block-palette-spec.php index 6217d543783..879ffd6b486 100644 --- a/tools/generate-block-palette-spec.php +++ b/tools/generate-block-palette-spec.php @@ -40,6 +40,7 @@ use function json_encode; use function ksort; use const JSON_PRETTY_PRINT; +use const SORT_NATURAL; use const SORT_STRING; use const STDERR; @@ -82,7 +83,7 @@ foreach(Utils::stringifyKeys($reportMap) as $blockName => $propertyList){ foreach(Utils::stringifyKeys($propertyList) as $propertyName => $propertyValues){ - ksort($reportMap[$blockName][$propertyName]); + ksort($propertyValues, SORT_NATURAL); $reportMap[$blockName][$propertyName] = array_values($propertyValues); } } From f207d1bbf244636de69a05d468926e530f60bec1 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Mon, 5 Feb 2024 07:36:09 -0500 Subject: [PATCH 23/34] Make CocoaBlock Flowable (#6218) --- src/block/CocoaBlock.php | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index 5cec4b933a2..6d8ce1adc9c 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -26,7 +26,6 @@ use pocketmine\block\utils\AgeableTrait; use pocketmine\block\utils\BlockEventHelper; use pocketmine\block\utils\HorizontalFacingTrait; -use pocketmine\block\utils\SupportType; use pocketmine\block\utils\WoodType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Fertilizer; @@ -40,7 +39,7 @@ use pocketmine\world\BlockTransaction; use function mt_rand; -class CocoaBlock extends Transparent{ +class CocoaBlock extends Flowable{ use HorizontalFacingTrait; use AgeableTrait; @@ -65,10 +64,6 @@ protected function recalculateCollisionBoxes() : array{ ]; } - public function getSupportType(int $facing) : SupportType{ - return SupportType::NONE; - } - private function canAttachTo(Block $block) : bool{ return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE; } From 6bb84bc46cbf8352824628c270b57bb037c591d2 Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 Date: Tue, 6 Feb 2024 13:42:24 +0100 Subject: [PATCH 24/34] Add `Promise::all` (#6152) --- src/promise/Promise.php | 50 +++++++++++ tests/phpstan/configs/phpstan-bugs.neon | 15 ++++ tests/phpunit/promise/PromiseTest.php | 106 ++++++++++++++++++++++++ 3 files changed, 171 insertions(+) diff --git a/src/promise/Promise.php b/src/promise/Promise.php index bafec097978..0def7e60523 100644 --- a/src/promise/Promise.php +++ b/src/promise/Promise.php @@ -23,6 +23,7 @@ namespace pocketmine\promise; +use function count; use function spl_object_id; /** @@ -57,4 +58,53 @@ public function isResolved() : bool{ //rejected or just hasn't been resolved yet return $this->shared->state === true; } + + /** + * Returns a promise that will resolve only once all the Promises in + * `$promises` have resolved. The resolution value of the returned promise + * will be an array containing the resolution values of each Promises in + * `$promises` indexed by the respective Promises' array keys. + * + * @param Promise[] $promises + * + * @phpstan-template TPromiseValue + * @phpstan-template TKey of array-key + * @phpstan-param non-empty-array> $promises + * + * @phpstan-return Promise> + */ + public static function all(array $promises) : Promise{ + if(count($promises) === 0){ + throw new \InvalidArgumentException("At least one promise must be provided"); + } + /** @phpstan-var PromiseResolver> $resolver */ + $resolver = new PromiseResolver(); + $values = []; + $toResolve = count($promises); + $continue = true; + + foreach($promises as $key => $promise){ + $promise->onCompletion( + function(mixed $value) use ($resolver, $key, $toResolve, &$values) : void{ + $values[$key] = $value; + + if(count($values) === $toResolve){ + $resolver->resolve($values); + } + }, + function() use ($resolver, &$continue) : void{ + if($continue){ + $continue = false; + $resolver->reject(); + } + } + ); + + if(!$continue){ + break; + } + } + + return $resolver->getPromise(); + } } diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index af048661144..de38903bd39 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -35,3 +35,18 @@ parameters: count: 1 path: ../../../src/world/generator/normal/Normal.php + - + message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertFalse\\(\\) with false will always evaluate to true\\.$#" + count: 1 + path: ../../phpunit/promise/PromiseTest.php + + - + message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false and 'All promise should…' will always evaluate to false\\.$#" + count: 1 + path: ../../phpunit/promise/PromiseTest.php + + - + message: "#^Call to static method PHPUnit\\\\Framework\\\\Assert\\:\\:assertTrue\\(\\) with false will always evaluate to false\\.$#" + count: 2 + path: ../../phpunit/promise/PromiseTest.php + diff --git a/tests/phpunit/promise/PromiseTest.php b/tests/phpunit/promise/PromiseTest.php index 7198f4f619e..682ee007035 100644 --- a/tests/phpunit/promise/PromiseTest.php +++ b/tests/phpunit/promise/PromiseTest.php @@ -39,4 +39,110 @@ function() : void{ } ); } + + public function testAllPreResolved() : void{ + $resolver = new PromiseResolver(); + $resolver->resolve(1); + + $allPromise = Promise::all([$resolver->getPromise()]); + $done = false; + $allPromise->onCompletion( + function($value) use (&$done) : void{ + $done = true; + self::assertEquals([1], $value); + }, + function() use (&$done) : void{ + $done = true; + self::fail("Promise was rejected"); + } + ); + self::assertTrue($done); + } + + public function testAllPostResolved() : void{ + $resolver = new PromiseResolver(); + + $allPromise = Promise::all([$resolver->getPromise()]); + $done = false; + $allPromise->onCompletion( + function($value) use (&$done) : void{ + $done = true; + self::assertEquals([1], $value); + }, + function() use (&$done) : void{ + $done = true; + self::fail("Promise was rejected"); + } + ); + self::assertFalse($done); + $resolver->resolve(1); + self::assertTrue($done); + } + + public function testAllResolve() : void{ + $resolver1 = new PromiseResolver(); + $resolver2 = new PromiseResolver(); + + $allPromise = Promise::all([$resolver1->getPromise(), $resolver2->getPromise()]); + $done = false; + $allPromise->onCompletion( + function($value) use (&$done) : void{ + $done = true; + self::assertEquals([1, 2], $value); + }, + function() use (&$done) : void{ + $done = true; + self::fail("Promise was rejected"); + } + ); + self::assertFalse($done); + $resolver1->resolve(1); + self::assertFalse($done); + $resolver2->resolve(2); + self::assertTrue($done); + } + + public function testAllPartialReject() : void{ + $resolver1 = new PromiseResolver(); + $resolver2 = new PromiseResolver(); + + $allPromise = Promise::all([$resolver1->getPromise(), $resolver2->getPromise()]); + $done = false; + $allPromise->onCompletion( + function($value) use (&$done) : void{ + $done = true; + self::fail("Promise was unexpectedly resolved"); + }, + function() use (&$done) : void{ + $done = true; + } + ); + self::assertFalse($done); + $resolver2->reject(); + self::assertTrue($done, "All promise should be rejected immediately after the first constituent rejection"); + $resolver1->resolve(1); + } + + /** + * Promise::all() should return a rejected promise if any of the input promises were rejected at the call time + */ + public function testAllPartialPreReject() : void{ + $resolver1 = new PromiseResolver(); + $resolver2 = new PromiseResolver(); + $resolver2->reject(); + + $allPromise = Promise::all([$resolver1->getPromise(), $resolver2->getPromise()]); + $done = false; + $allPromise->onCompletion( + function($value) use (&$done) : void{ + $done = true; + self::fail("Promise was unexpectedly resolved"); + }, + function() use (&$done) : void{ + $done = true; + } + ); + self::assertTrue($done, "All promise should be rejected immediately after the first constituent rejection"); + $resolver1->resolve(1); + } } From 61d0181bfd7122d76109f8ff390675eaf35f3da3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 19 Feb 2024 16:10:22 +0000 Subject: [PATCH 25/34] Added script to generate biome IDs this has been sitting in my local workspace for a very long time --- build/generate-biome-ids.php | 133 +++++++++++++++++++++++++++++++++++ 1 file changed, 133 insertions(+) create mode 100644 build/generate-biome-ids.php diff --git a/build/generate-biome-ids.php b/build/generate-biome-ids.php new file mode 100644 index 00000000000..f36591fe4de --- /dev/null +++ b/build/generate-biome-ids.php @@ -0,0 +1,133 @@ + $map + */ +function generate(array $map, string $outputFile) : void{ + $file = safe_fopen($outputFile, 'wb'); + fwrite($file, HEADER); + fwrite($file, <<<'CLASSHEADER' +namespace pocketmine\data\bedrock; + +final class BiomeIds{ + + private function __construct(){ + //NOOP + } + + +CLASSHEADER +); + $list = $map; + asort($list, SORT_NUMERIC); + $lastId = -1; + foreach(Utils::stringifyKeys($list) as $name => $id){ + if($name === ""){ + continue; + } + if($id !== $lastId + 1){ + fwrite($file, "\n"); + } + $lastId = $id; + fwrite($file, "\tpublic const " . make_const_name($name) . ' = ' . $id . ';' . "\n"); + } + fwrite($file, "}\n"); + fclose($file); +} + +$ids = json_decode(Filesystem::fileGetContents(BedrockDataFiles::BIOME_ID_MAP_JSON), true); +if(!is_array($ids)){ + throw new \RuntimeException("Invalid biome ID map, expected array for root JSON object"); +} +$cleanedIds = []; +foreach($ids as $name => $id){ + if(!is_string($name) || !is_int($id)){ + throw new \RuntimeException("Invalid biome ID map, expected string => int map"); + } + $cleanedIds[$name] = $id; +} +generate($cleanedIds, dirname(__DIR__) . '/src/data/bedrock/BiomeIds.php'); + +echo "Done. Don't forget to run CS fixup after generating code.\n"; From 2616d8c5adacff29de6a8db17c8e13a075ff5ea9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 19 Feb 2024 16:10:46 +0000 Subject: [PATCH 26/34] New biome IDs, courtesy of build/generate-biome-ids --- src/data/bedrock/BiomeIds.php | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/data/bedrock/BiomeIds.php b/src/data/bedrock/BiomeIds.php index ac955527805..1169a51eaee 100644 --- a/src/data/bedrock/BiomeIds.php +++ b/src/data/bedrock/BiomeIds.php @@ -111,4 +111,15 @@ private function __construct(){ public const CRIMSON_FOREST = 179; public const WARPED_FOREST = 180; public const BASALT_DELTAS = 181; + public const JAGGED_PEAKS = 182; + public const FROZEN_PEAKS = 183; + public const SNOWY_SLOPES = 184; + public const GROVE = 185; + public const MEADOW = 186; + public const LUSH_CAVES = 187; + public const DRIPSTONE_CAVES = 188; + public const STONY_PEAKS = 189; + public const DEEP_DARK = 190; + public const MANGROVE_SWAMP = 191; + public const CHERRY_GROVE = 192; } From 4fab518384b1525f2f126643c57f1df6446557df Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 19 Feb 2024 16:53:53 +0000 Subject: [PATCH 27/34] PluginManager: do not accept generator functions as event handlers closes #4912 I didn't merge the original PR because this needs to be checked for explicitly registered handlers as well as auto-detected ones from listeners. --- src/plugin/PluginManager.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/plugin/PluginManager.php b/src/plugin/PluginManager.php index 301c668547a..198e4e893bf 100644 --- a/src/plugin/PluginManager.php +++ b/src/plugin/PluginManager.php @@ -650,6 +650,11 @@ public function registerEvent(string $event, \Closure $handler, int $priority, P $handlerName = Utils::getNiceClosureName($handler); + $reflect = new \ReflectionFunction($handler); + if($reflect->isGenerator()){ + throw new PluginException("Generator function $handlerName cannot be used as an event handler"); + } + if(!$plugin->isEnabled()){ throw new PluginException("Plugin attempted to register event handler " . $handlerName . "() to event " . $event . " while not enabled"); } From 920341668f92a1043be2f74b44970542d59e12c4 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Mon, 19 Feb 2024 13:46:48 -0500 Subject: [PATCH 28/34] Implemented working Name tag (#5209) --- .../ItemSerializerDeserializerRegistrar.php | 1 + src/entity/Entity.php | 8 ++++ src/entity/Living.php | 4 ++ src/item/ItemTypeIds.php | 3 +- src/item/NameTag.php | 40 +++++++++++++++++++ src/item/StringToItemParser.php | 1 + src/item/VanillaItems.php | 2 + src/player/Player.php | 4 ++ 8 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 src/item/NameTag.php diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index bbc295b7251..7a720559eaa 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -305,6 +305,7 @@ private function register1to1ItemMappings() : void{ $this->map1to1Item(Ids::MUSIC_DISC_WAIT, Items::RECORD_WAIT()); $this->map1to1Item(Ids::MUSIC_DISC_WARD, Items::RECORD_WARD()); $this->map1to1Item(Ids::MUTTON, Items::RAW_MUTTON()); + $this->map1to1Item(Ids::NAME_TAG, Items::NAME_TAG()); $this->map1to1Item(Ids::NAUTILUS_SHELL, Items::NAUTILUS_SHELL()); $this->map1to1Item(Ids::NETHER_STAR, Items::NETHER_STAR()); $this->map1to1Item(Ids::NETHERBRICK, Items::NETHER_BRICK()); diff --git a/src/entity/Entity.php b/src/entity/Entity.php index d26a06d92b3..5fc2168d518 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -252,6 +252,14 @@ public function isNameTagAlwaysVisible() : bool{ return $this->alwaysShowNameTag; } + /** + * Returns whether players can rename this entity using a name tag. + * Note that plugins can still name entities using setNameTag(). + */ + public function canBeRenamed() : bool{ + return false; + } + public function setNameTag(string $name) : void{ $this->nameTag = $name; $this->networkPropertiesDirty = true; diff --git a/src/entity/Living.php b/src/entity/Living.php index 4d5e10cb363..e7d669fda7e 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -132,6 +132,10 @@ protected function getInitialGravity() : float{ return 0.08; } abstract public function getName() : string; + public function canBeRenamed() : bool{ + return true; + } + protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index bf688118bc9..66eebed744c 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -323,8 +323,9 @@ private function __construct(){ public const EYE_ARMOR_TRIM_SMITHING_TEMPLATE = 20284; public const SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE = 20285; public const PITCHER_POD = 20286; + public const NAME_TAG = 20287; - public const FIRST_UNUSED_ITEM_ID = 20287; + public const FIRST_UNUSED_ITEM_ID = 20288; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/NameTag.php b/src/item/NameTag.php new file mode 100644 index 00000000000..8c7113e1dee --- /dev/null +++ b/src/item/NameTag.php @@ -0,0 +1,40 @@ +canBeRenamed() && $this->hasCustomName()){ + $entity->setNameTag($this->getCustomName()); + $this->pop(); + return true; + } + return false; + } +} diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index ca73dded81a..91fd2ab4a43 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1384,6 +1384,7 @@ private static function registerItems(self $result) : void{ $result->register("mutton_raw", fn() => Items::RAW_MUTTON()); $result->register("muttoncooked", fn() => Items::COOKED_MUTTON()); $result->register("muttonraw", fn() => Items::RAW_MUTTON()); + $result->register("name_tag", fn() => Items::NAME_TAG()); $result->register("nautilus_shell", fn() => Items::NAUTILUS_SHELL()); $result->register("nether_brick", fn() => Items::NETHER_BRICK()); $result->register("nether_quartz", fn() => Items::NETHER_QUARTZ()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index f9aca9a767f..bbd0dfc01b3 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -219,6 +219,7 @@ * @method static MilkBucket MILK_BUCKET() * @method static Minecart MINECART() * @method static MushroomStew MUSHROOM_STEW() + * @method static NameTag NAME_TAG() * @method static Item NAUTILUS_SHELL() * @method static Axe NETHERITE_AXE() * @method static Armor NETHERITE_BOOTS() @@ -490,6 +491,7 @@ protected static function setup() : void{ self::register("milk_bucket", new MilkBucket(new IID(Ids::MILK_BUCKET), "Milk Bucket")); self::register("minecart", new Minecart(new IID(Ids::MINECART), "Minecart")); self::register("mushroom_stew", new MushroomStew(new IID(Ids::MUSHROOM_STEW), "Mushroom Stew")); + self::register("name_tag", new NameTag(new IID(Ids::NAME_TAG), "Name Tag")); self::register("nautilus_shell", new Item(new IID(Ids::NAUTILUS_SHELL), "Nautilus Shell")); self::register("nether_brick", new Item(new IID(Ids::NETHER_BRICK), "Nether Brick")); self::register("nether_quartz", new Item(new IID(Ids::NETHER_QUARTZ), "Nether Quartz")); diff --git a/src/player/Player.php b/src/player/Player.php index 92e15ddd380..7a717dacd9c 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -630,6 +630,10 @@ public function setDisplayName(string $name) : void{ $this->displayName = $ev->getNewName(); } + public function canBeRenamed() : bool{ + return false; + } + /** * Returns the player's locale, e.g. en_US. */ From efd113bdc88a9b8382e6f1a47b70cb39493a0790 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 26 Feb 2024 17:09:09 +0000 Subject: [PATCH 29/34] Integrate pmmp/BedrockProtocol@65b3d0b341afc5c0aa952f1d71a4109d78bb0a59 --- composer.json | 2 +- composer.lock | 22 ++++++------ src/Server.php | 11 +++--- src/network/mcpe/ChunkRequestTask.php | 6 ++-- src/network/mcpe/NetworkSession.php | 8 ++--- .../mcpe/StandardPacketBroadcaster.php | 10 ++---- src/network/mcpe/convert/TypeConverter.php | 29 +++++++++++---- src/network/mcpe/raklib/RakLibInterface.php | 5 --- .../mcpe/serializer/ChunkSerializer.php | 5 ++- tools/generate-bedrock-data-from-packets.php | 35 ++++++++++--------- 10 files changed, 67 insertions(+), 66 deletions(-) diff --git a/composer.json b/composer.json index 47e798b1643..01b4c75b17b 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "pocketmine/bedrock-block-upgrade-schema": "~3.5.0+bedrock-1.20.60", "pocketmine/bedrock-data": "~2.8.0+bedrock-1.20.60", "pocketmine/bedrock-item-upgrade-schema": "~1.7.0+bedrock-1.20.60", - "pocketmine/bedrock-protocol": "~27.0.0+bedrock-1.20.60", + "pocketmine/bedrock-protocol": "dev-master", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index 47aeeaecab0..eb6faf8b26f 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "d923f5fd75f0d33eb5198268a74a58b4", + "content-hash": "1786511a89ee1a1e932ecc68834ec476", "packages": [ { "name": "adhocore/json-comment", @@ -200,21 +200,20 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "27.0.1+bedrock-1.20.60", + "version": "dev-master", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", - "reference": "0cebb55f6e904f722b14d420f6b2c84c7fa69f10" + "reference": "6e73f21cdc7433f0674dfcdf4817f478aa5528e6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/0cebb55f6e904f722b14d420f6b2c84c7fa69f10", - "reference": "0cebb55f6e904f722b14d420f6b2c84c7fa69f10", + "url": "https://api.github.com/repos/pmmp/BedrockProtocol/zipball/6e73f21cdc7433f0674dfcdf4817f478aa5528e6", + "reference": "6e73f21cdc7433f0674dfcdf4817f478aa5528e6", "shasum": "" }, "require": { "ext-json": "*", - "netresearch/jsonmapper": "^4.0", "php": "^8.1", "pocketmine/binaryutils": "^0.2.0", "pocketmine/color": "^0.2.0 || ^0.3.0", @@ -223,11 +222,12 @@ "ramsey/uuid": "^4.1" }, "require-dev": { - "phpstan/phpstan": "1.10.39", + "phpstan/phpstan": "1.10.59", "phpstan/phpstan-phpunit": "^1.0.0", "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5 || ^10.0" }, + "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -241,9 +241,9 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/27.0.1+bedrock-1.20.60" + "source": "https://github.com/pmmp/BedrockProtocol/tree/master" }, - "time": "2024-02-07T11:53:50+00:00" + "time": "2024-02-26T16:18:34+00:00" }, { "name": "pocketmine/binaryutils", @@ -2931,7 +2931,9 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "pocketmine/bedrock-protocol": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/Server.php b/src/Server.php index 73bfdb85a24..ed7f52d63ea 100644 --- a/src/Server.php +++ b/src/Server.php @@ -59,7 +59,6 @@ use pocketmine\network\mcpe\NetworkSession; use pocketmine\network\mcpe\PacketBroadcaster; use pocketmine\network\mcpe\protocol\ProtocolInfo; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\mcpe\protocol\types\CompressionAlgorithm; use pocketmine\network\mcpe\raklib\RakLibInterface; use pocketmine\network\mcpe\StandardEntityEventBroadcaster; @@ -1187,12 +1186,11 @@ private function startupPrepareConnectableNetworkInterfaces( bool $useQuery, PacketBroadcaster $packetBroadcaster, EntityEventBroadcaster $entityEventBroadcaster, - PacketSerializerContext $packetSerializerContext, TypeConverter $typeConverter ) : bool{ $prettyIp = $ipV6 ? "[$ip]" : $ip; try{ - $rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter)); + $rakLibRegistered = $this->network->registerInterface(new RakLibInterface($this, $ip, $port, $ipV6, $packetBroadcaster, $entityEventBroadcaster, $typeConverter)); }catch(NetworkInterfaceStartException $e){ $this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_server_networkStartFailed( $ip, @@ -1219,15 +1217,14 @@ private function startupPrepareNetworkInterfaces() : bool{ $useQuery = $this->configGroup->getConfigBool(ServerProperties::ENABLE_QUERY, true); $typeConverter = TypeConverter::getInstance(); - $packetSerializerContext = new PacketSerializerContext($typeConverter->getItemTypeDictionary()); - $packetBroadcaster = new StandardPacketBroadcaster($this, $packetSerializerContext); + $packetBroadcaster = new StandardPacketBroadcaster($this); $entityEventBroadcaster = new StandardEntityEventBroadcaster($packetBroadcaster, $typeConverter); if( - !$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) || + !$this->startupPrepareConnectableNetworkInterfaces($this->getIp(), $this->getPort(), false, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter) || ( $this->configGroup->getConfigBool(ServerProperties::ENABLE_IPV6, true) && - !$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $packetSerializerContext, $typeConverter) + !$this->startupPrepareConnectableNetworkInterfaces($this->getIpV6(), $this->getPortV6(), true, $useQuery, $packetBroadcaster, $entityEventBroadcaster, $typeConverter) ) ){ return false; diff --git a/src/network/mcpe/ChunkRequestTask.php b/src/network/mcpe/ChunkRequestTask.php index 05a4f20c109..13b5db5b795 100644 --- a/src/network/mcpe/ChunkRequestTask.php +++ b/src/network/mcpe/ChunkRequestTask.php @@ -28,7 +28,6 @@ use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\protocol\LevelChunkPacket; use pocketmine\network\mcpe\protocol\serializer\PacketBatch; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\mcpe\protocol\types\ChunkPosition; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\network\mcpe\serializer\ChunkSerializer; @@ -72,11 +71,10 @@ public function onRun() : void{ $subCount = ChunkSerializer::getSubChunkCount($chunk, $dimensionId); $converter = TypeConverter::getInstance(); - $encoderContext = new PacketSerializerContext($converter->getItemTypeDictionary()); - $payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter->getBlockTranslator(), $encoderContext, $this->tiles); + $payload = ChunkSerializer::serializeFullChunk($chunk, $dimensionId, $converter->getBlockTranslator(), $this->tiles); $stream = new BinaryStream(); - PacketBatch::encodePackets($stream, $encoderContext, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]); + PacketBatch::encodePackets($stream, [LevelChunkPacket::create(new ChunkPosition($this->chunkX, $this->chunkZ), $dimensionId, $subCount, false, null, $payload)]); $compressor = $this->compressor->deserialize(); $this->setResult(chr($compressor->getNetworkId()) . $compressor->compress($stream->getBuffer())); diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 8ac4db98fc7..63fab278eb2 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -67,7 +67,6 @@ use pocketmine\network\mcpe\protocol\ProtocolInfo; use pocketmine\network\mcpe\protocol\serializer\PacketBatch; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\mcpe\protocol\ServerboundPacket; use pocketmine\network\mcpe\protocol\ServerToClientHandshakePacket; use pocketmine\network\mcpe\protocol\SetDifficultyPacket; @@ -180,7 +179,6 @@ public function __construct( private Server $server, private NetworkSessionManager $manager, private PacketPool $packetPool, - private PacketSerializerContext $packetSerializerContext, private PacketSender $sender, private PacketBroadcaster $broadcaster, private EntityEventBroadcaster $entityEventBroadcaster, @@ -435,7 +433,7 @@ public function handleDataPacket(Packet $packet, string $buffer) : void{ $decodeTimings = Timings::getDecodeDataPacketTimings($packet); $decodeTimings->startTiming(); try{ - $stream = PacketSerializer::decoder($buffer, 0, $this->packetSerializerContext); + $stream = PacketSerializer::decoder($buffer, 0); try{ $packet->decode($stream); }catch(PacketDecodeException $e){ @@ -494,7 +492,7 @@ public function sendDataPacket(ClientboundPacket $packet, bool $immediate = fals } foreach($packets as $evPacket){ - $this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder($this->packetSerializerContext), $evPacket)); + $this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder(), $evPacket)); } if($immediate){ $this->flushSendBuffer(true); @@ -554,8 +552,6 @@ private function flushSendBuffer(bool $immediate = false) : void{ } } - public function getPacketSerializerContext() : PacketSerializerContext{ return $this->packetSerializerContext; } - public function getBroadcaster() : PacketBroadcaster{ return $this->broadcaster; } public function getEntityEventBroadcaster() : EntityEventBroadcaster{ return $this->entityEventBroadcaster; } diff --git a/src/network/mcpe/StandardPacketBroadcaster.php b/src/network/mcpe/StandardPacketBroadcaster.php index 1de6f80fe81..32afdeeb7c2 100644 --- a/src/network/mcpe/StandardPacketBroadcaster.php +++ b/src/network/mcpe/StandardPacketBroadcaster.php @@ -26,7 +26,6 @@ use pocketmine\event\server\DataPacketSendEvent; use pocketmine\network\mcpe\protocol\serializer\PacketBatch; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\Server; use pocketmine\timings\Timings; use pocketmine\utils\BinaryStream; @@ -37,8 +36,7 @@ final class StandardPacketBroadcaster implements PacketBroadcaster{ public function __construct( - private Server $server, - private PacketSerializerContext $protocolContext + private Server $server ){} public function broadcastPackets(array $recipients, array $packets) : void{ @@ -58,10 +56,6 @@ public function broadcastPackets(array $recipients, array $packets) : void{ /** @var NetworkSession[][] $targetsByCompressor */ $targetsByCompressor = []; foreach($recipients as $recipient){ - if($recipient->getPacketSerializerContext() !== $this->protocolContext){ - throw new \InvalidArgumentException("Only recipients with the same protocol context as the broadcaster can be broadcast to by this broadcaster"); - } - //TODO: different compressors might be compatible, it might not be necessary to split them up by object $compressor = $recipient->getCompressor(); $compressors[spl_object_id($compressor)] = $compressor; @@ -72,7 +66,7 @@ public function broadcastPackets(array $recipients, array $packets) : void{ $totalLength = 0; $packetBuffers = []; foreach($packets as $packet){ - $buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder($this->protocolContext), $packet); + $buffer = NetworkSession::encodePacketTimed(PacketSerializer::encoder(), $packet); //varint length prefix + packet buffer $totalLength += (((int) log(strlen($buffer), 128)) + 1) + strlen($buffer); $packetBuffers[] = $buffer; diff --git a/src/network/mcpe/convert/TypeConverter.php b/src/network/mcpe/convert/TypeConverter.php index 53ce6e98a88..0f9789de797 100644 --- a/src/network/mcpe/convert/TypeConverter.php +++ b/src/network/mcpe/convert/TypeConverter.php @@ -30,13 +30,17 @@ use pocketmine\crafting\TagWildcardRecipeIngredient; use pocketmine\data\bedrock\BedrockDataFiles; use pocketmine\data\bedrock\item\BlockItemIdMap; +use pocketmine\data\bedrock\item\ItemTypeNames; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\nbt\NbtException; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; +use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; use pocketmine\network\mcpe\protocol\types\GameMode as ProtocolGameMode; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraDataShield; use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor; use pocketmine\network\mcpe\protocol\types\recipe\RecipeIngredient as ProtocolRecipeIngredient; use pocketmine\network\mcpe\protocol\types\recipe\StringIdMetaItemDescriptor; @@ -76,7 +80,7 @@ public function __construct(){ ); $this->itemTypeDictionary = ItemTypeDictionaryFromDataHelper::loadFromString(Filesystem::fileGetContents(BedrockDataFiles::REQUIRED_ITEM_LIST_JSON)); - $this->shieldRuntimeId = $this->itemTypeDictionary->fromStringId("minecraft:shield"); + $this->shieldRuntimeId = $this->itemTypeDictionary->fromStringId(ItemTypeNames::SHIELD); $this->itemTranslator = new ItemTranslator( $this->itemTypeDictionary, @@ -217,26 +221,39 @@ public function coreItemStackToNet(Item $itemStack) : ItemStack{ [$id, $meta, $blockRuntimeId] = $idMeta; } + $extraData = $id === $this->shieldRuntimeId ? + new ItemStackExtraDataShield($nbt, canPlaceOn: [], canDestroy: [], blockingTick: 0) : + new ItemStackExtraData($nbt, canPlaceOn: [], canDestroy: []); + $extraDataSerializer = PacketSerializer::encoder(); + $extraData->write($extraDataSerializer); + return new ItemStack( $id, $meta, $itemStack->getCount(), $blockRuntimeId ?? ItemTranslator::NO_BLOCK_RUNTIME_ID, - $nbt, - [], - [], - $id === $this->shieldRuntimeId ? 0 : null + $extraDataSerializer->getBuffer(), ); } /** + * WARNING: Avoid this in server-side code. If you need to compare ItemStacks provided by the client to the + * server, prefer encoding the server's itemstack and comparing the serialized ItemStack, instead of converting + * the client's ItemStack to a core Item. + * This method will fully decode the item's extra data, which can be very costly if the item has a lot of NBT data. + * * @throws TypeConversionException */ public function netItemStackToCore(ItemStack $itemStack) : Item{ if($itemStack->getId() === 0){ return VanillaItems::AIR(); } - $compound = $itemStack->getNbt(); + $extraDataDeserializer = PacketSerializer::decoder($itemStack->getRawExtraData(), 0); + $extraData = $itemStack->getId() === $this->shieldRuntimeId ? + ItemStackExtraDataShield::read($extraDataDeserializer) : + ItemStackExtraData::read($extraDataDeserializer); + + $compound = $extraData->getNbt(); $itemResult = $this->itemTranslator->fromNetworkId($itemStack->getId(), $itemStack->getMeta(), $itemStack->getBlockRuntimeId()); diff --git a/src/network/mcpe/raklib/RakLibInterface.php b/src/network/mcpe/raklib/RakLibInterface.php index 17aa87e8390..b2325f5698f 100644 --- a/src/network/mcpe/raklib/RakLibInterface.php +++ b/src/network/mcpe/raklib/RakLibInterface.php @@ -33,7 +33,6 @@ use pocketmine\network\mcpe\PacketBroadcaster; use pocketmine\network\mcpe\protocol\PacketPool; use pocketmine\network\mcpe\protocol\ProtocolInfo; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\Network; use pocketmine\network\NetworkInterfaceStartException; use pocketmine\network\PacketHandlingException; @@ -84,7 +83,6 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ private PacketBroadcaster $packetBroadcaster; private EntityEventBroadcaster $entityEventBroadcaster; - private PacketSerializerContext $packetSerializerContext; private TypeConverter $typeConverter; public function __construct( @@ -94,12 +92,10 @@ public function __construct( bool $ipV6, PacketBroadcaster $packetBroadcaster, EntityEventBroadcaster $entityEventBroadcaster, - PacketSerializerContext $packetSerializerContext, TypeConverter $typeConverter ){ $this->server = $server; $this->packetBroadcaster = $packetBroadcaster; - $this->packetSerializerContext = $packetSerializerContext; $this->entityEventBroadcaster = $entityEventBroadcaster; $this->typeConverter = $typeConverter; @@ -192,7 +188,6 @@ public function onClientConnect(int $sessionId, string $address, int $port, int $this->server, $this->network->getSessionManager(), PacketPool::getInstance(), - $this->packetSerializerContext, new RakLibPacketSender($sessionId, $this), $this->packetBroadcaster, $this->entityEventBroadcaster, diff --git a/src/network/mcpe/serializer/ChunkSerializer.php b/src/network/mcpe/serializer/ChunkSerializer.php index e089daef569..9120f34a7e0 100644 --- a/src/network/mcpe/serializer/ChunkSerializer.php +++ b/src/network/mcpe/serializer/ChunkSerializer.php @@ -30,7 +30,6 @@ use pocketmine\network\mcpe\convert\BlockTranslator; use pocketmine\network\mcpe\protocol\serializer\NetworkNbtSerializer; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\mcpe\protocol\types\DimensionIds; use pocketmine\utils\Binary; use pocketmine\utils\BinaryStream; @@ -84,8 +83,8 @@ public static function getSubChunkCount(Chunk $chunk, int $dimensionId) : int{ /** * @phpstan-param DimensionIds::* $dimensionId */ - public static function serializeFullChunk(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, PacketSerializerContext $encoderContext, ?string $tiles = null) : string{ - $stream = PacketSerializer::encoder($encoderContext); + public static function serializeFullChunk(Chunk $chunk, int $dimensionId, BlockTranslator $blockTranslator, ?string $tiles = null) : string{ + $stream = PacketSerializer::encoder(); $subChunkCount = self::getSubChunkCount($chunk, $dimensionId); $writtenCount = 0; diff --git a/tools/generate-bedrock-data-from-packets.php b/tools/generate-bedrock-data-from-packets.php index 77d2b9d93f9..ca15bd8bec0 100644 --- a/tools/generate-bedrock-data-from-packets.php +++ b/tools/generate-bedrock-data-from-packets.php @@ -34,6 +34,7 @@ use pocketmine\crafting\json\SmithingTrimRecipeData; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\item\BlockItemIdMap; +use pocketmine\data\bedrock\item\ItemTypeNames; use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NBT; use pocketmine\nbt\tag\CompoundTag; @@ -50,12 +51,12 @@ use pocketmine\network\mcpe\protocol\PacketPool; use pocketmine\network\mcpe\protocol\serializer\ItemTypeDictionary; use pocketmine\network\mcpe\protocol\serializer\PacketSerializer; -use pocketmine\network\mcpe\protocol\serializer\PacketSerializerContext; use pocketmine\network\mcpe\protocol\StartGamePacket; use pocketmine\network\mcpe\protocol\types\CacheableNbt; use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry; use pocketmine\network\mcpe\protocol\types\inventory\ItemStack; -use pocketmine\network\mcpe\protocol\types\ItemTypeEntry; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraData; +use pocketmine\network\mcpe\protocol\types\inventory\ItemStackExtraDataShield; use pocketmine\network\mcpe\protocol\types\recipe\ComplexAliasItemDescriptor; use pocketmine\network\mcpe\protocol\types\recipe\FurnaceRecipe; use pocketmine\network\mcpe\protocol\types\recipe\IntIdMetaItemDescriptor; @@ -170,16 +171,21 @@ private function itemStackToJson(ItemStack $itemStack) : ItemStackData{ $data->meta = $meta; } - $nbt = $itemStack->getNbt(); - if($nbt !== null && count($nbt) > 0){ - $data->nbt = base64_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($nbt))); - } + $rawExtraData = $itemStack->getRawExtraData(); + if($rawExtraData !== ""){ + $decoder = PacketSerializer::decoder($rawExtraData, 0); + $extraData = $itemStringId === ItemTypeNames::SHIELD ? ItemStackExtraDataShield::read($decoder) : ItemStackExtraData::read($decoder); + $nbt = $extraData->getNbt(); + if($nbt !== null && count($nbt) > 0){ + $data->nbt = base64_encode((new LittleEndianNbtSerializer())->write(new TreeRoot($nbt))); + } - if(count($itemStack->getCanPlaceOn()) > 0){ - $data->can_place_on = $itemStack->getCanPlaceOn(); - } - if(count($itemStack->getCanDestroy()) > 0){ - $data->can_destroy = $itemStack->getCanDestroy(); + if(count($extraData->getCanPlaceOn()) > 0){ + $data->can_place_on = $extraData->getCanPlaceOn(); + } + if(count($extraData->getCanDestroy()) > 0){ + $data->can_destroy = $extraData->getCanDestroy(); + } } return $data; @@ -421,7 +427,7 @@ public function handleCraftingData(CraftingDataPacket $packet) : bool{ $recipes["potion_type"][] = new PotionTypeRecipeData( $this->recipeIngredientToJson(new RecipeIngredient(new IntIdMetaItemDescriptor($recipe->getInputItemId(), $recipe->getInputItemMeta()), 1)), $this->recipeIngredientToJson(new RecipeIngredient(new IntIdMetaItemDescriptor($recipe->getIngredientItemId(), $recipe->getIngredientItemMeta()), 1)), - $this->itemStackToJson(new ItemStack($recipe->getOutputItemId(), $recipe->getOutputItemMeta(), 1, 0, null, [], [], null)), + $this->itemStackToJson(new ItemStack($recipe->getOutputItemId(), $recipe->getOutputItemMeta(), 1, 0, "")), ); } @@ -571,10 +577,7 @@ function main(array $argv) : int{ fwrite(STDERR, "Unknown packet on line " . ($lineNum + 1) . ": " . $parts[1]); continue; } - $serializer = PacketSerializer::decoder($raw, 0, new PacketSerializerContext( - $handler->itemTypeDictionary ?? - new ItemTypeDictionary([new ItemTypeEntry("minecraft:shield", 0, false)])) - ); + $serializer = PacketSerializer::decoder($raw, 0); $pk->decode($serializer); $pk->handle($handler); From 687211835591a63ba1a282307463059778ee76ef Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 27 Feb 2024 14:15:31 +0000 Subject: [PATCH 30/34] Update BedrockProtocol to release version --- composer.json | 2 +- composer.lock | 11 ++++------- 2 files changed, 5 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index 01b4c75b17b..6b7893bc73b 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ "pocketmine/bedrock-block-upgrade-schema": "~3.5.0+bedrock-1.20.60", "pocketmine/bedrock-data": "~2.8.0+bedrock-1.20.60", "pocketmine/bedrock-item-upgrade-schema": "~1.7.0+bedrock-1.20.60", - "pocketmine/bedrock-protocol": "dev-master", + "pocketmine/bedrock-protocol": "~28.0.0+bedrock-1.20.60", "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", diff --git a/composer.lock b/composer.lock index eb6faf8b26f..8f5af610ce5 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "1786511a89ee1a1e932ecc68834ec476", + "content-hash": "8b860493cc27ba81e717153651a786b6", "packages": [ { "name": "adhocore/json-comment", @@ -200,7 +200,7 @@ }, { "name": "pocketmine/bedrock-protocol", - "version": "dev-master", + "version": "28.0.0+bedrock-1.20.60", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockProtocol.git", @@ -227,7 +227,6 @@ "phpstan/phpstan-strict-rules": "^1.0.0", "phpunit/phpunit": "^9.5 || ^10.0" }, - "default-branch": true, "type": "library", "autoload": { "psr-4": { @@ -241,7 +240,7 @@ "description": "An implementation of the Minecraft: Bedrock Edition protocol in PHP", "support": { "issues": "https://github.com/pmmp/BedrockProtocol/issues", - "source": "https://github.com/pmmp/BedrockProtocol/tree/master" + "source": "https://github.com/pmmp/BedrockProtocol/tree/28.0.0+bedrock-1.20.60" }, "time": "2024-02-26T16:18:34+00:00" }, @@ -2931,9 +2930,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "pocketmine/bedrock-protocol": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { From a0cca53f52c614fc39a1285eb39cd0ebb7c3862f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 27 Feb 2024 16:07:43 +0000 Subject: [PATCH 31/34] Fixed mismatched predictions due to NBT key order differences this is a pain :( It appears the client always sorts the keys in alphabetical order due to use of std::map. However I'm not sure of the exact ordering behaviour, so it needs to be investigated. --- src/network/mcpe/InventoryManager.php | 37 ++++++++++++++++++- src/network/mcpe/convert/TypeConverter.php | 12 ++++-- .../mcpe/handler/InGamePacketHandler.php | 15 ++++++-- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 938b5c82c29..e2b71ab3106 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -423,6 +423,41 @@ public function onClientRemoveWindow(int $id) : void{ } } + /** + * Compares itemstack extra data for equality. This is used to verify legacy InventoryTransaction slot predictions. + * + * TODO: It would be preferable if we didn't have to deserialize this, to improve performance and reduce attack + * surface. However, the raw data may not match due to differences in ordering. Investigate whether the + * client-provided NBT is consistently sorted. + */ + private function itemStackExtraDataEqual(ItemStack $left, ItemStack $right) : bool{ + if($left->getRawExtraData() === $right->getRawExtraData()){ + return true; + } + + $typeConverter = $this->session->getTypeConverter(); + $leftExtraData = $typeConverter->deserializeItemStackExtraData($left->getRawExtraData(), $left->getId()); + $rightExtraData = $typeConverter->deserializeItemStackExtraData($right->getRawExtraData(), $right->getId()); + + $leftNbt = $leftExtraData->getNbt(); + $rightNbt = $rightExtraData->getNbt(); + return + $leftExtraData->getCanPlaceOn() === $rightExtraData->getCanPlaceOn() && + $leftExtraData->getCanDestroy() === $rightExtraData->getCanDestroy() && ( + $leftNbt === $rightNbt || //this covers null === null and fast object identity + ($leftNbt !== null && $rightNbt !== null && $leftNbt->equals($rightNbt)) + ); + } + + private function itemStacksEqual(ItemStack $left, ItemStack $right) : bool{ + return + $left->getId() === $right->getId() && + $left->getMeta() === $right->getMeta() && + $left->getBlockRuntimeId() === $right->getBlockRuntimeId() && + $left->getCount() === $right->getCount() && + $this->itemStackExtraDataEqual($left, $right); + } + public function onSlotChange(Inventory $inventory, int $slot) : void{ $inventoryEntry = $this->inventories[spl_object_id($inventory)] ?? null; if($inventoryEntry === null){ @@ -432,7 +467,7 @@ public function onSlotChange(Inventory $inventory, int $slot) : void{ } $currentItem = $this->session->getTypeConverter()->coreItemStackToNet($inventory->getItem($slot)); $clientSideItem = $inventoryEntry->predictions[$slot] ?? null; - if($clientSideItem === null || !$clientSideItem->equals($currentItem)){ + if($clientSideItem === null || !$this->itemStacksEqual($currentItem, $clientSideItem)){ //no prediction or incorrect - do not associate this with the currently active itemstack request $this->trackItemStack($inventoryEntry, $slot, $currentItem, null); $inventoryEntry->pendingSyncs[$slot] = $currentItem; diff --git a/src/network/mcpe/convert/TypeConverter.php b/src/network/mcpe/convert/TypeConverter.php index 0f9789de797..e886b2b8be0 100644 --- a/src/network/mcpe/convert/TypeConverter.php +++ b/src/network/mcpe/convert/TypeConverter.php @@ -248,10 +248,7 @@ public function netItemStackToCore(ItemStack $itemStack) : Item{ if($itemStack->getId() === 0){ return VanillaItems::AIR(); } - $extraDataDeserializer = PacketSerializer::decoder($itemStack->getRawExtraData(), 0); - $extraData = $itemStack->getId() === $this->shieldRuntimeId ? - ItemStackExtraDataShield::read($extraDataDeserializer) : - ItemStackExtraData::read($extraDataDeserializer); + $extraData = $this->deserializeItemStackExtraData($itemStack->getRawExtraData(), $itemStack->getId()); $compound = $extraData->getNbt(); @@ -272,4 +269,11 @@ public function netItemStackToCore(ItemStack $itemStack) : Item{ return $itemResult; } + + public function deserializeItemStackExtraData(string $extraData, int $id) : ItemStackExtraData{ + $extraDataDeserializer = PacketSerializer::decoder($extraData, 0); + return $id === $this->shieldRuntimeId ? + ItemStackExtraDataShield::read($extraDataDeserializer) : + ItemStackExtraData::read($extraDataDeserializer); + } } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 8c3449d415b..750502172a5 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -444,9 +444,18 @@ private function handleNormalTransaction(NormalTransactionData $data, int $itemS return false; } $serverItemStack = $this->session->getTypeConverter()->coreItemStackToNet($sourceSlotItem); - //because the client doesn't tell us the expected itemstack ID, we have to deep-compare our known - //itemstack info with the one the client sent. This is costly, but we don't have any other option :( - if(!$serverItemStack->equals($clientItemStack)){ + //Sadly we don't have itemstack IDs here, so we have to compare the basic item properties to ensure that we're + //dropping the item the client expects (inventory might be out of sync with the client). + if( + $serverItemStack->getId() !== $clientItemStack->getId() || + $serverItemStack->getMeta() !== $clientItemStack->getMeta() || + $serverItemStack->getCount() !== $clientItemStack->getCount() || + $serverItemStack->getBlockRuntimeId() !== $clientItemStack->getBlockRuntimeId() + //Raw extraData may not match because of TAG_Compound key ordering differences, and decoding it to compare + //is costly. Assume that we're in sync if id+meta+count+runtimeId match. + //NB: Make sure $clientItemStack isn't used to create the dropped item, as that would allow the client + //to change the item NBT since we're not validating it. + ){ return false; } From a35c3406a8c602cb02c412173bd88534a1a418dd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 Feb 2024 18:14:37 +0000 Subject: [PATCH 32/34] Release 5.12.0 --- changelogs/5.12.md | 54 +++++++++++++++++++++++++++++++++++++++++++++ src/VersionInfo.php | 4 ++-- 2 files changed, 56 insertions(+), 2 deletions(-) create mode 100644 changelogs/5.12.md diff --git a/changelogs/5.12.md b/changelogs/5.12.md new file mode 100644 index 00000000000..f1857efa4e9 --- /dev/null +++ b/changelogs/5.12.md @@ -0,0 +1,54 @@ +# 5.12.0 +Released 28th February 2024 + +**For Minecraft: Bedrock Edition 1.20.60** + +This is a minor feature release, with a few new features and improvements. + +**Plugin compatibility:** Plugins for previous 5.x versions will run unchanged on this release, unless they use internal APIs, reflection, or packages like the `pocketmine\network\mcpe` or `pocketmine\data` namespace. +Do not update plugin minimum API versions unless you need new features added in this release. + +**WARNING: If your plugin uses the `pocketmine\network\mcpe` namespace, you're not shielded by API change constraints.** +Consider using the `mcpe-protocol` directive in `plugin.yml` as a constraint if you're using packets directly. + +## General +- Added a `--version` command-line option to display the server version and exit. + +## Tools +- Added `tools/generate-biome-ids.php` to generate `pocketmine\data\bedrock\BiomeIds`. +- Fixed ordering of property values generated by `tools/generate-block-palette-spec.php`. + +## API +### `pocketmine\block` +- The following new classes have been added: + - `utils\LightableTrait` - used by blocks with `getLit()` and `setLit()` methods +- The following methods have been deprecated: + - `Block->isSolid()` - this method returns confusing results which don't match expectations and no one really knows what it actually means +- `CocoaBlock` now extends `Flowable` to match vanilla Minecraft behaviour. + +### `pocketmine\plugin` +- `PluginManager->registerEvent()` now throws an exception when given a generator function for the event handler. +- `PluginManager->registerEvents()` now throws an exception if any of the detected event handlers are generator functions. Use `@notHandler` to have the function ignored if intended. + +### `pocketmine\promise` +- The following methods have been added: + - `public static Promise::all(list $promises) : Promise` - returns a promise that is resolved once all given promises are resolved, or is rejected if any of the promises are rejected. + +### `pocketmine\scheduler` +- The following methods have been deprecated: + - `AsyncWorker->getFromThreadStore()` - use class static properties for thread-local storage + - `AsyncWorker->removeFromThreadStore()` + - `AsyncWorker->saveToThreadStore()` + +## Documentation +- Improved documentation of various methods in `Block`. + +## Gameplay +- The following new items have been added: + - Name Tag + +## Internals +- Removed specialization of shutdown logic for `Thread` vs `Worker` (no specialization is required). +- Authentication system no longer accepts logins signed with the old Mojang root public key. +- ID to enum mappings in `pocketmine\data` now use a new `match` convention to allow static analysis to ensure that all enum cases are handled. +- Updated version of `pocketmine/bedrock-protocol` allows avoiding decoding of some itemstack data from the client in most cases, improving performance. diff --git a/src/VersionInfo.php b/src/VersionInfo.php index 946e53a2328..c54d241f224 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.11.3"; - public const IS_DEVELOPMENT_BUILD = true; + public const BASE_VERSION = "5.12.0"; + public const IS_DEVELOPMENT_BUILD = false; public const BUILD_CHANNEL = "stable"; /** From b2c97cf2f1fe63f6e2c4ea283eb929956f0ef0cc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 28 Feb 2024 18:14:41 +0000 Subject: [PATCH 33/34] 5.12.1 is next --- src/VersionInfo.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/VersionInfo.php b/src/VersionInfo.php index c54d241f224..794ed17d71a 100644 --- a/src/VersionInfo.php +++ b/src/VersionInfo.php @@ -31,8 +31,8 @@ final class VersionInfo{ public const NAME = "PocketMine-MP"; - public const BASE_VERSION = "5.12.0"; - public const IS_DEVELOPMENT_BUILD = false; + public const BASE_VERSION = "5.12.1"; + public const IS_DEVELOPMENT_BUILD = true; public const BUILD_CHANNEL = "stable"; /** From 72f3c0b4b9a4c6b5d1bc79a3b31d0568ccc2baa9 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 1 Mar 2024 17:36:40 +0000 Subject: [PATCH 34/34] NetworkSession: fixed timings not being stopped when handling uncompressed packets --- src/network/mcpe/NetworkSession.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 63fab278eb2..7aa804370e2 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -360,12 +360,12 @@ public function handleEncoded(string $payload) : void{ } if($this->enableCompression){ - Timings::$playerNetworkReceiveDecompress->startTiming(); $compressionType = ord($payload[0]); $compressed = substr($payload, 1); if($compressionType === CompressionAlgorithm::NONE){ $decompressed = $compressed; }elseif($compressionType === $this->compressor->getNetworkId()){ + Timings::$playerNetworkReceiveDecompress->startTiming(); try{ $decompressed = $this->compressor->decompress($compressed); }catch(DecompressionException $e){