From bc2abf4b158889313d19a9795f115ce14ad4b946 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 1 Mar 2024 14:41:53 +0000 Subject: [PATCH 01/35] First shot at packet ack receipt support this will be useful for preventing resource pack sending from overloading the network. it's not the best solution for that (since it means the RTT will limit the pack download speed), but it's easier than implementing congestion control and will work fine in most cases. --- src/network/mcpe/NetworkSession.php | 107 +++++++++++++++--- src/network/mcpe/PacketSender.php | 2 +- src/network/mcpe/raklib/RakLibInterface.php | 7 +- .../mcpe/raklib/RakLibPacketSender.php | 4 +- 4 files changed, 100 insertions(+), 20 deletions(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 63fab278e..edb7f2549 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -100,6 +100,8 @@ use pocketmine\player\Player; use pocketmine\player\PlayerInfo; use pocketmine\player\UsedChunkStatus; use pocketmine\player\XboxLivePlayerInfo; +use pocketmine\promise\Promise; +use pocketmine\promise\PromiseResolver; use pocketmine\Server; use pocketmine\timings\Timings; use pocketmine\utils\AssumptionFailedError; @@ -158,15 +160,24 @@ class NetworkSession{ /** @var string[] */ private array $sendBuffer = []; - /** - * @var \SplQueue|CompressBatchPromise[]|string[] - * @phpstan-var \SplQueue + * @var PromiseResolver[] + * @phpstan-var list> */ + private array $sendBufferAckPromises = []; + + /** @phpstan-var \SplQueue>}> */ private \SplQueue $compressedQueue; private bool $forceAsyncCompression = true; private bool $enableCompression = false; //disabled until handshake completed + private int $nextAckReceiptId = 0; + /** + * @var PromiseResolver[][] + * @phpstan-var array>> + */ + private array $ackPromisesByReceiptId = []; + private ?InventoryManager $invManager = null; /** @@ -468,7 +479,23 @@ class NetworkSession{ } } - public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{ + public function handleAckReceipt(int $receiptId) : void{ + if(!$this->connected){ + return; + } + if(isset($this->ackPromisesByReceiptId[$receiptId])){ + $promises = $this->ackPromisesByReceiptId[$receiptId]; + unset($this->ackPromisesByReceiptId[$receiptId]); + foreach($promises as $promise){ + $promise->resolve(true); + } + } + } + + /** + * @phpstan-param PromiseResolver|null $ackReceiptResolver + */ + private function sendDataPacketInternal(ClientboundPacket $packet, bool $immediate, ?PromiseResolver $ackReceiptResolver) : bool{ if(!$this->connected){ return false; } @@ -491,6 +518,9 @@ class NetworkSession{ $packets = [$packet]; } + if($ackReceiptResolver !== null){ + $this->sendBufferAckPromises[] = $ackReceiptResolver; + } foreach($packets as $evPacket){ $this->addToSendBuffer(self::encodePacketTimed(PacketSerializer::encoder(), $evPacket)); } @@ -504,6 +534,23 @@ class NetworkSession{ } } + public function sendDataPacket(ClientboundPacket $packet, bool $immediate = false) : bool{ + return $this->sendDataPacketInternal($packet, $immediate, null); + } + + /** + * @phpstan-return Promise + */ + public function sendDataPacketWithReceipt(ClientboundPacket $packet, bool $immediate = false) : Promise{ + $resolver = new PromiseResolver(); + + if(!$this->sendDataPacketInternal($packet, $immediate, $resolver)){ + $resolver->reject(); + } + + return $resolver->getPromise(); + } + /** * @internal */ @@ -545,7 +592,9 @@ class NetworkSession{ $batch = $stream->getBuffer(); } $this->sendBuffer = []; - $this->queueCompressedNoBufferFlush($batch, $immediate); + $ackPromises = $this->sendBufferAckPromises; + $this->sendBufferAckPromises = []; + $this->queueCompressedNoBufferFlush($batch, $immediate, $ackPromises); }finally{ Timings::$playerNetworkSend->stopTiming(); } @@ -572,22 +621,27 @@ class NetworkSession{ } } - private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false) : void{ + /** + * @param PromiseResolver[] $ackPromises + * + * @phpstan-param list> $ackPromises + */ + private function queueCompressedNoBufferFlush(CompressBatchPromise|string $batch, bool $immediate = false, array $ackPromises = []) : void{ Timings::$playerNetworkSend->startTiming(); try{ if(is_string($batch)){ if($immediate){ //Skips all queues - $this->sendEncoded($batch, true); + $this->sendEncoded($batch, true, $ackPromises); }else{ - $this->compressedQueue->enqueue($batch); + $this->compressedQueue->enqueue([$batch, $ackPromises]); $this->flushCompressedQueue(); } }elseif($immediate){ //Skips all queues - $this->sendEncoded($batch->getResult(), true); + $this->sendEncoded($batch->getResult(), true, $ackPromises); }else{ - $this->compressedQueue->enqueue($batch); + $this->compressedQueue->enqueue([$batch, $ackPromises]); $batch->onResolve(function() : void{ if($this->connected){ $this->flushCompressedQueue(); @@ -604,14 +658,14 @@ class NetworkSession{ try{ while(!$this->compressedQueue->isEmpty()){ /** @var CompressBatchPromise|string $current */ - $current = $this->compressedQueue->bottom(); + [$current, $ackPromises] = $this->compressedQueue->bottom(); if(is_string($current)){ $this->compressedQueue->dequeue(); - $this->sendEncoded($current); + $this->sendEncoded($current, false, $ackPromises); }elseif($current->hasResult()){ $this->compressedQueue->dequeue(); - $this->sendEncoded($current->getResult()); + $this->sendEncoded($current->getResult(), false, $ackPromises); }else{ //can't send any more queued until this one is ready @@ -623,13 +677,24 @@ class NetworkSession{ } } - private function sendEncoded(string $payload, bool $immediate = false) : void{ + /** + * @param PromiseResolver[] $ackPromises + * @phpstan-param list> $ackPromises + */ + private function sendEncoded(string $payload, bool $immediate, array $ackPromises) : void{ if($this->cipher !== null){ Timings::$playerNetworkSendEncrypt->startTiming(); $payload = $this->cipher->encrypt($payload); Timings::$playerNetworkSendEncrypt->stopTiming(); } - $this->sender->send($payload, $immediate); + + if(count($ackPromises) > 0){ + $ackReceiptId = $this->nextAckReceiptId++; + $this->ackPromisesByReceiptId[$ackReceiptId] = $ackPromises; + }else{ + $ackReceiptId = null; + } + $this->sender->send($payload, $immediate, $ackReceiptId); } /** @@ -642,6 +707,18 @@ class NetworkSession{ $this->disconnectGuard = false; $this->flushSendBuffer(true); $this->sender->close(""); + + foreach($this->ackPromisesByReceiptId as $resolvers){ + foreach($resolvers as $resolver){ + $resolver->reject(); + } + } + $this->ackPromisesByReceiptId = []; + foreach($this->sendBufferAckPromises as $resolver){ + $resolver->reject(); + } + $this->sendBufferAckPromises = []; + foreach($this->disposeHooks as $callback){ $callback(); } diff --git a/src/network/mcpe/PacketSender.php b/src/network/mcpe/PacketSender.php index 4842ea93e..36e556fe4 100644 --- a/src/network/mcpe/PacketSender.php +++ b/src/network/mcpe/PacketSender.php @@ -28,7 +28,7 @@ interface PacketSender{ /** * Pushes a packet into the channel to be processed. */ - public function send(string $payload, bool $immediate) : void; + public function send(string $payload, bool $immediate, ?int $receiptId) : void; /** * Closes the channel, terminating the connection. diff --git a/src/network/mcpe/raklib/RakLibInterface.php b/src/network/mcpe/raklib/RakLibInterface.php index b2325f569..44328f8f3 100644 --- a/src/network/mcpe/raklib/RakLibInterface.php +++ b/src/network/mcpe/raklib/RakLibInterface.php @@ -252,7 +252,9 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ } public function onPacketAck(int $sessionId, int $identifierACK) : void{ - + if(isset($this->sessions[$sessionId])){ + $this->sessions[$sessionId]->handleAckReceipt($identifierACK); + } } public function setName(string $name) : void{ @@ -289,12 +291,13 @@ class RakLibInterface implements ServerEventListener, AdvancedNetworkInterface{ $this->network->getBandwidthTracker()->add($bytesSentDiff, $bytesReceivedDiff); } - public function putPacket(int $sessionId, string $payload, bool $immediate = true) : void{ + public function putPacket(int $sessionId, string $payload, bool $immediate = true, ?int $receiptId = null) : void{ if(isset($this->sessions[$sessionId])){ $pk = new EncapsulatedPacket(); $pk->buffer = self::MCPE_RAKNET_PACKET_ID . $payload; $pk->reliability = PacketReliability::RELIABLE_ORDERED; $pk->orderChannel = 0; + $pk->identifierACK = $receiptId; $this->interface->sendEncapsulated($sessionId, $pk, $immediate); } diff --git a/src/network/mcpe/raklib/RakLibPacketSender.php b/src/network/mcpe/raklib/RakLibPacketSender.php index d940c282b..df8cf9a00 100644 --- a/src/network/mcpe/raklib/RakLibPacketSender.php +++ b/src/network/mcpe/raklib/RakLibPacketSender.php @@ -33,9 +33,9 @@ class RakLibPacketSender implements PacketSender{ private RakLibInterface $handler ){} - public function send(string $payload, bool $immediate) : void{ + public function send(string $payload, bool $immediate, ?int $receiptId) : void{ if(!$this->closed){ - $this->handler->putPacket($this->sessionId, $payload, $immediate); + $this->handler->putPacket($this->sessionId, $payload, $immediate, $receiptId); } } From 90409b50d130fc24edc895a1a5ce6b868089d0a7 Mon Sep 17 00:00:00 2001 From: Jason Wynn Date: Fri, 1 Mar 2024 09:53:59 -0500 Subject: [PATCH 02/35] Allow offering different resource packs to different players (#6249) closes #6248 --- .../player/PlayerResourcePackOfferEvent.php | 106 ++++++++++++++++++ src/network/mcpe/NetworkSession.php | 15 ++- .../handler/ResourcePacksPacketHandler.php | 49 +++++--- tests/phpstan/configs/actual-problems.neon | 5 + 4 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 src/event/player/PlayerResourcePackOfferEvent.php diff --git a/src/event/player/PlayerResourcePackOfferEvent.php b/src/event/player/PlayerResourcePackOfferEvent.php new file mode 100644 index 000000000..df00cf474 --- /dev/null +++ b/src/event/player/PlayerResourcePackOfferEvent.php @@ -0,0 +1,106 @@ + key, leave unset for any packs that are not encrypted + * + * @phpstan-param list $resourcePacks + * @phpstan-param array $encryptionKeys + */ + public function __construct( + private readonly PlayerInfo $playerInfo, + private array $resourcePacks, + private array $encryptionKeys, + private bool $mustAccept + ){} + + public function getPlayerInfo() : PlayerInfo{ + return $this->playerInfo; + } + + /** + * Adds a resource pack to the top of the stack. + * The resources in this pack will be applied over the top of any existing packs. + */ + public function addResourcePack(ResourcePack $entry, ?string $encryptionKey = null) : void{ + array_unshift($this->resourcePacks, $entry); + if($encryptionKey !== null){ + $this->encryptionKeys[$entry->getPackId()] = $encryptionKey; + } + } + + /** + * Sets the resource packs to offer. Packs are applied from the highest key to the lowest, with each pack + * overwriting any resources from the previous pack. This means that the pack at index 0 gets the final say on which + * resources are used. + * + * @param ResourcePack[] $resourcePacks + * @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted + * + * @phpstan-param list $resourcePacks + * @phpstan-param array $encryptionKeys + */ + public function setResourcePacks(array $resourcePacks, array $encryptionKeys) : void{ + $this->resourcePacks = $resourcePacks; + $this->encryptionKeys = $encryptionKeys; + } + + /** + * @return ResourcePack[] + * @phpstan-return list + */ + public function getResourcePacks() : array{ + return $this->resourcePacks; + } + + /** + * @return string[] + * @phpstan-return array + */ + public function getEncryptionKeys() : array{ + return $this->encryptionKeys; + } + + public function setMustAccept(bool $mustAccept) : void{ + $this->mustAccept = $mustAccept; + } + + public function mustAccept() : bool{ + return $this->mustAccept; + } +} diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index 63fab278e..5a369ba33 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -25,6 +25,7 @@ namespace pocketmine\network\mcpe; use pocketmine\entity\effect\EffectInstance; use pocketmine\event\player\PlayerDuplicateLoginEvent; +use pocketmine\event\player\PlayerResourcePackOfferEvent; use pocketmine\event\server\DataPacketDecodeEvent; use pocketmine\event\server\DataPacketReceiveEvent; use pocketmine\event\server\DataPacketSendEvent; @@ -844,7 +845,19 @@ class NetworkSession{ $this->sendDataPacket(PlayStatusPacket::create(PlayStatusPacket::LOGIN_SUCCESS)); $this->logger->debug("Initiating resource packs phase"); - $this->setHandler(new ResourcePacksPacketHandler($this, $this->server->getResourcePackManager(), function() : void{ + + $packManager = $this->server->getResourcePackManager(); + $resourcePacks = $packManager->getResourceStack(); + $keys = []; + foreach($resourcePacks as $resourcePack){ + $key = $packManager->getPackEncryptionKey($resourcePack->getPackId()); + if($key !== null){ + $keys[$resourcePack->getPackId()] = $key; + } + } + $event = new PlayerResourcePackOfferEvent($this->info, $resourcePacks, $keys, $packManager->resourcePacksRequired()); + $event->call(); + $this->setHandler(new ResourcePacksPacketHandler($this, $event->getResourcePacks(), $event->getEncryptionKeys(), $event->mustAccept(), function() : void{ $this->createPlayer(); })); } diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index 3d413ee5a..c5a459613 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -37,12 +37,13 @@ use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackInfoEntry; use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackStackEntry; use pocketmine\network\mcpe\protocol\types\resourcepacks\ResourcePackType; use pocketmine\resourcepacks\ResourcePack; -use pocketmine\resourcepacks\ResourcePackManager; +use function array_keys; use function array_map; use function ceil; use function count; use function implode; use function strpos; +use function strtolower; use function substr; /** @@ -52,35 +53,55 @@ use function substr; class ResourcePacksPacketHandler extends PacketHandler{ private const PACK_CHUNK_SIZE = 128 * 1024; //128KB + /** + * @var ResourcePack[] + * @phpstan-var array + */ + private array $resourcePacksById = []; + /** @var bool[][] uuid => [chunk index => hasSent] */ private array $downloadedChunks = []; /** - * @phpstan-param \Closure() : void $completionCallback + * @param ResourcePack[] $resourcePackStack + * @param string[] $encryptionKeys pack UUID => key, leave unset for any packs that are not encrypted + * + * @phpstan-param list $resourcePackStack + * @phpstan-param array $encryptionKeys + * @phpstan-param \Closure() : void $completionCallback */ public function __construct( private NetworkSession $session, - private ResourcePackManager $resourcePackManager, + private array $resourcePackStack, + private array $encryptionKeys, + private bool $mustAccept, private \Closure $completionCallback - ){} + ){ + foreach($resourcePackStack as $pack){ + $this->resourcePacksById[$pack->getPackId()] = $pack; + } + } + + private function getPackById(string $id) : ?ResourcePack{ + return $this->resourcePacksById[strtolower($id)] ?? null; + } public function setUp() : void{ $resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{ //TODO: more stuff - $encryptionKey = $this->resourcePackManager->getPackEncryptionKey($pack->getPackId()); return new ResourcePackInfoEntry( $pack->getPackId(), $pack->getPackVersion(), $pack->getPackSize(), - $encryptionKey ?? "", + $this->encryptionKeys[$pack->getPackId()] ?? "", "", $pack->getPackId(), false ); - }, $this->resourcePackManager->getResourceStack()); - //TODO: support forcing server packs - $this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->resourcePackManager->resourcePacksRequired(), false, false, [])); + }, $this->resourcePackStack); + // TODO: support forcing server packs + $this->session->sendDataPacket(ResourcePacksInfoPacket::create($resourcePackEntries, [], $this->mustAccept, false, false, [])); $this->session->getLogger()->debug("Waiting for client to accept resource packs"); } @@ -104,11 +125,11 @@ class ResourcePacksPacketHandler extends PacketHandler{ if($splitPos !== false){ $uuid = substr($uuid, 0, $splitPos); } - $pack = $this->resourcePackManager->getPackById($uuid); + $pack = $this->getPackById($uuid); if(!($pack instanceof ResourcePack)){ //Client requested a resource pack but we don't have it available on the server - $this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList())); + $this->disconnectWithError("Unknown pack $uuid requested, available packs: " . implode(", ", array_keys($this->resourcePacksById))); return false; } @@ -128,7 +149,7 @@ class ResourcePacksPacketHandler extends PacketHandler{ case ResourcePackClientResponsePacket::STATUS_HAVE_ALL_PACKS: $stack = array_map(static function(ResourcePack $pack) : ResourcePackStackEntry{ return new ResourcePackStackEntry($pack->getPackId(), $pack->getPackVersion(), ""); //TODO: subpacks - }, $this->resourcePackManager->getResourceStack()); + }, $this->resourcePackStack); //we support chemistry blocks by default, the client should already have this installed $stack[] = new ResourcePackStackEntry("0fba4063-dba1-4281-9b89-ff9390653530", "1.0.0", ""); @@ -151,9 +172,9 @@ class ResourcePacksPacketHandler extends PacketHandler{ } public function handleResourcePackChunkRequest(ResourcePackChunkRequestPacket $packet) : bool{ - $pack = $this->resourcePackManager->getPackById($packet->packId); + $pack = $this->getPackById($packet->packId); if(!($pack instanceof ResourcePack)){ - $this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", $this->resourcePackManager->getPackIdList())); + $this->disconnectWithError("Invalid request for chunk $packet->chunkIndex of unknown pack $packet->packId, available packs: " . implode(", ", array_keys($this->resourcePacksById))); return false; } diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 9fea3803d..cc647da80 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -650,6 +650,11 @@ parameters: count: 2 path: ../../../src/network/mcpe/NetworkSession.php + - + message: "#^Parameter \\#1 \\$playerInfo of class pocketmine\\\\event\\\\player\\\\PlayerResourcePackOfferEvent constructor expects pocketmine\\\\player\\\\PlayerInfo, pocketmine\\\\player\\\\PlayerInfo\\|null given\\.$#" + count: 1 + path: ../../../src/network/mcpe/NetworkSession.php + - message: "#^Parameter \\#1 \\$target of method pocketmine\\\\command\\\\Command\\:\\:testPermissionSilent\\(\\) expects pocketmine\\\\command\\\\CommandSender, pocketmine\\\\player\\\\Player\\|null given\\.$#" count: 1 From 4b5ac532769036bdf3d6b9b8599f44ba7b906985 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 1 Mar 2024 17:01:32 +0000 Subject: [PATCH 03/35] Fixes --- composer.json | 4 ++-- composer.lock | 34 ++++++++++++++--------------- src/network/mcpe/NetworkSession.php | 24 ++++++++++---------- 3 files changed, 32 insertions(+), 30 deletions(-) diff --git a/composer.json b/composer.json index 4ba554594..80ad483a3 100644 --- a/composer.json +++ b/composer.json @@ -45,8 +45,8 @@ "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.0.0", - "pocketmine/raklib": "^0.15.0", - "pocketmine/raklib-ipc": "^0.2.0", + "pocketmine/raklib": "~1.0.0", + "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", "ramsey/uuid": "~4.7.0", "symfony/filesystem": "~6.4.0" diff --git a/composer.lock b/composer.lock index c06fd4c78..d654c06c0 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": "f43d60f8c44393f5c42b7ba27cd8ccc0", + "content-hash": "651b773ac69b4a68e59f32b7a72ab357", "packages": [ { "name": "adhocore/json-comment", @@ -616,28 +616,28 @@ }, { "name": "pocketmine/raklib", - "version": "0.15.1", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/RakLib.git", - "reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca" + "reference": "fd74ba254d2cfaed1369ae5684cf85773edaaae9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/RakLib/zipball/79b7b4d1d7516dc6e322514453645ad9452b20ca", - "reference": "79b7b4d1d7516dc6e322514453645ad9452b20ca", + "url": "https://api.github.com/repos/pmmp/RakLib/zipball/fd74ba254d2cfaed1369ae5684cf85773edaaae9", + "reference": "fd74ba254d2cfaed1369ae5684cf85773edaaae9", "shasum": "" }, "require": { "ext-sockets": "*", - "php": "^8.0", + "php": "^8.1", "php-64bit": "*", "php-ipv6": "*", "pocketmine/binaryutils": "^0.2.0", "pocketmine/log": "^0.3.0 || ^0.4.0" }, "require-dev": { - "phpstan/phpstan": "1.9.17", + "phpstan/phpstan": "1.10.1", "phpstan/phpstan-strict-rules": "^1.0" }, "type": "library", @@ -653,32 +653,32 @@ "description": "A RakNet server implementation written in PHP", "support": { "issues": "https://github.com/pmmp/RakLib/issues", - "source": "https://github.com/pmmp/RakLib/tree/0.15.1" + "source": "https://github.com/pmmp/RakLib/tree/1.0.0" }, - "time": "2023-03-07T15:10:34+00:00" + "time": "2024-03-01T15:38:54+00:00" }, { "name": "pocketmine/raklib-ipc", - "version": "0.2.0", + "version": "1.0.1", "source": { "type": "git", "url": "https://github.com/pmmp/RakLibIpc.git", - "reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c" + "reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c", - "reference": "26ed56fa9db06e4ca6e8920c0ede2e01e219bb9c", + "url": "https://api.github.com/repos/pmmp/RakLibIpc/zipball/ce632ef2c6743e71eddb5dc329c49af6555f90bc", + "reference": "ce632ef2c6743e71eddb5dc329c49af6555f90bc", "shasum": "" }, "require": { "php": "^8.0", "php-64bit": "*", "pocketmine/binaryutils": "^0.2.0", - "pocketmine/raklib": "^0.15.0" + "pocketmine/raklib": "^0.15.0 || ^1.0.0" }, "require-dev": { - "phpstan/phpstan": "1.9.17", + "phpstan/phpstan": "1.10.1", "phpstan/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -694,9 +694,9 @@ "description": "Channel-based protocols for inter-thread/inter-process communication with RakLib", "support": { "issues": "https://github.com/pmmp/RakLibIpc/issues", - "source": "https://github.com/pmmp/RakLibIpc/tree/0.2.0" + "source": "https://github.com/pmmp/RakLibIpc/tree/1.0.1" }, - "time": "2023-02-13T13:40:40+00:00" + "time": "2024-03-01T15:55:05+00:00" }, { "name": "pocketmine/snooze", diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index edb7f2549..fb369855d 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -708,17 +708,6 @@ class NetworkSession{ $this->flushSendBuffer(true); $this->sender->close(""); - foreach($this->ackPromisesByReceiptId as $resolvers){ - foreach($resolvers as $resolver){ - $resolver->reject(); - } - } - $this->ackPromisesByReceiptId = []; - foreach($this->sendBufferAckPromises as $resolver){ - $resolver->reject(); - } - $this->sendBufferAckPromises = []; - foreach($this->disposeHooks as $callback){ $callback(); } @@ -726,6 +715,19 @@ class NetworkSession{ $this->setHandler(null); $this->connected = false; + $ackPromisesByReceiptId = $this->ackPromisesByReceiptId; + $this->ackPromisesByReceiptId = []; + foreach($ackPromisesByReceiptId as $resolvers){ + foreach($resolvers as $resolver){ + $resolver->reject(); + } + } + $sendBufferAckPromises = $this->sendBufferAckPromises; + $this->sendBufferAckPromises = []; + foreach($sendBufferAckPromises as $resolver){ + $resolver->reject(); + } + $this->logger->info($this->server->getLanguage()->translate(KnownTranslationFactory::pocketmine_network_session_close($reason))); } } From 4abc36275cebcf4a74e667080f94ee459905df30 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 1 Mar 2024 17:02:44 +0000 Subject: [PATCH 04/35] Remove newline --- src/network/mcpe/NetworkSession.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/network/mcpe/NetworkSession.php b/src/network/mcpe/NetworkSession.php index fb369855d..ae90f3e64 100644 --- a/src/network/mcpe/NetworkSession.php +++ b/src/network/mcpe/NetworkSession.php @@ -707,7 +707,6 @@ class NetworkSession{ $this->disconnectGuard = false; $this->flushSendBuffer(true); $this->sender->close(""); - foreach($this->disposeHooks as $callback){ $callback(); } From b9a1ef1357e6b784f5f2de15e5a0b7bc31e296a5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 1 Mar 2024 17:07:19 +0000 Subject: [PATCH 05/35] Throttle resource pack sending using ack receipts this isn't the best solution, as it limits the download speed somewhat, but it's relatively simple and works quite well. closes #3127 --- .../handler/ResourcePacksPacketHandler.php | 49 +++++++++++++++++-- 1 file changed, 46 insertions(+), 3 deletions(-) diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index 3d413ee5a..b9089d146 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -50,11 +50,22 @@ use function substr; * packs to the client. */ class ResourcePacksPacketHandler extends PacketHandler{ - private const PACK_CHUNK_SIZE = 128 * 1024; //128KB + private const PACK_CHUNK_SIZE = 256 * 1024; //256KB + + /** + * Larger values allow downloading more chunks at the same time, increasing download speed, but the client may choke + * and cause the download speed to drop (due to ACKs taking too long to arrive). + */ + private const MAX_CONCURRENT_CHUNK_REQUESTS = 1; /** @var bool[][] uuid => [chunk index => hasSent] */ private array $downloadedChunks = []; + /** @phpstan-var \SplQueue */ + private \SplQueue $requestQueue; + + private int $activeRequests = 0; + /** * @phpstan-param \Closure() : void $completionCallback */ @@ -62,7 +73,9 @@ class ResourcePacksPacketHandler extends PacketHandler{ private NetworkSession $session, private ResourcePackManager $resourcePackManager, private \Closure $completionCallback - ){} + ){ + $this->requestQueue = new \SplQueue(); + } public function setUp() : void{ $resourcePackEntries = array_map(function(ResourcePack $pack) : ResourcePackInfoEntry{ @@ -176,8 +189,38 @@ class ResourcePacksPacketHandler extends PacketHandler{ $this->downloadedChunks[$packId][$packet->chunkIndex] = true; } - $this->session->sendDataPacket(ResourcePackChunkDataPacket::create($packId, $packet->chunkIndex, $offset, $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE))); + $this->requestQueue->enqueue([$pack, $packet->chunkIndex]); + $this->processChunkRequestQueue(); return true; } + + private function processChunkRequestQueue() : void{ + if($this->activeRequests >= self::MAX_CONCURRENT_CHUNK_REQUESTS || $this->requestQueue->isEmpty()){ + return; + } + /** + * @var ResourcePack $pack + * @var int $chunkIndex + */ + [$pack, $chunkIndex] = $this->requestQueue->dequeue(); + + $packId = $pack->getPackId(); + $offset = $chunkIndex * self::PACK_CHUNK_SIZE; + $chunkData = $pack->getPackChunk($offset, self::PACK_CHUNK_SIZE); + $this->activeRequests++; + $this->session + ->sendDataPacketWithReceipt(ResourcePackChunkDataPacket::create($packId, $chunkIndex, $offset, $chunkData)) + ->onCompletion( + function() use ($packId, $chunkIndex) : void{ + $this->activeRequests--; + $this->processChunkRequestQueue(); + }, + function() : void{ + //this may have been rejected because of a disconnection - this will do nothing in that case + $this->disconnectWithError("Plugin interrupted sending of resource packs"); + } + ); + $this->session->getLogger()->debug("Sent resource pack $packId chunk $chunkIndex"); + } } From f13eaaab05b3ff289d3bfd1ce285102221cf63cc Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 2 Mar 2024 00:08:49 +0000 Subject: [PATCH 06/35] Update RakLib --- composer.json | 2 +- composer.lock | 38 +++++++++++++++++++------------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/composer.json b/composer.json index 80ad483a3..ecd7cf026 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.0.0", - "pocketmine/raklib": "~1.0.0", + "pocketmine/raklib": "dev-client-reliable-buffer as 1.0.0", "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", "ramsey/uuid": "~4.7.0", diff --git a/composer.lock b/composer.lock index d654c06c0..ef1aabe65 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": "651b773ac69b4a68e59f32b7a72ab357", + "content-hash": "665baa60fbb39de57c133c35b26eb7c3", "packages": [ { "name": "adhocore/json-comment", @@ -616,17 +616,11 @@ }, { "name": "pocketmine/raklib", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/pmmp/RakLib.git", - "reference": "fd74ba254d2cfaed1369ae5684cf85773edaaae9" - }, + "version": "dev-client-reliable-buffer", "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pmmp/RakLib/zipball/fd74ba254d2cfaed1369ae5684cf85773edaaae9", - "reference": "fd74ba254d2cfaed1369ae5684cf85773edaaae9", - "shasum": "" + "type": "path", + "url": "../deps/RakLib", + "reference": "90d8ce314ac59244d738d20313cb3525ab5d63d8" }, "require": { "ext-sockets": "*", @@ -646,16 +640,13 @@ "raklib\\": "src/" } }, - "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-3.0" ], "description": "A RakNet server implementation written in PHP", - "support": { - "issues": "https://github.com/pmmp/RakLib/issues", - "source": "https://github.com/pmmp/RakLib/tree/1.0.0" - }, - "time": "2024-03-01T15:38:54+00:00" + "transport-options": { + "relative": true + } }, { "name": "pocketmine/raklib-ipc", @@ -2928,9 +2919,18 @@ "time": "2023-11-20T00:12:19+00:00" } ], - "aliases": [], + "aliases": [ + { + "package": "pocketmine/raklib", + "version": "dev-client-reliable-buffer", + "alias": "1.0.0", + "alias_normalized": "1.0.0.0" + } + ], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": { + "pocketmine/raklib": 20 + }, "prefer-stable": false, "prefer-lowest": false, "platform": { From 5ad63f27bbd2b2f2601033520f58fadd0bb5a868 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 2 Mar 2024 01:02:57 +0000 Subject: [PATCH 07/35] Update RakLib (again) --- composer.lock | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/composer.lock b/composer.lock index ef1aabe65..e12dc5439 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": "665baa60fbb39de57c133c35b26eb7c3", + "content-hash": "4e1a278e68755522f194fba9b29cd3e2", "packages": [ { "name": "adhocore/json-comment", @@ -617,10 +617,16 @@ { "name": "pocketmine/raklib", "version": "dev-client-reliable-buffer", + "source": { + "type": "git", + "url": "https://github.com/pmmp/RakLib.git", + "reference": "b1a31e87f0aadeb5894939ded01f60f1259657c5" + }, "dist": { - "type": "path", - "url": "../deps/RakLib", - "reference": "90d8ce314ac59244d738d20313cb3525ab5d63d8" + "type": "zip", + "url": "https://api.github.com/repos/pmmp/RakLib/zipball/b1a31e87f0aadeb5894939ded01f60f1259657c5", + "reference": "b1a31e87f0aadeb5894939ded01f60f1259657c5", + "shasum": "" }, "require": { "ext-sockets": "*", @@ -640,13 +646,16 @@ "raklib\\": "src/" } }, + "notification-url": "https://packagist.org/downloads/", "license": [ "GPL-3.0" ], "description": "A RakNet server implementation written in PHP", - "transport-options": { - "relative": true - } + "support": { + "issues": "https://github.com/pmmp/RakLib/issues", + "source": "https://github.com/pmmp/RakLib/tree/client-reliable-buffer" + }, + "time": "2024-03-02T01:02:00+00:00" }, { "name": "pocketmine/raklib-ipc", From 781e3643dde7f7bc0313cdc1a276d208b2c0e89f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 4 Mar 2024 14:25:47 +0000 Subject: [PATCH 08/35] Clean up --- composer.json | 2 +- composer.lock | 27 +++++++------------ .../handler/ResourcePacksPacketHandler.php | 3 +-- 3 files changed, 11 insertions(+), 21 deletions(-) diff --git a/composer.json b/composer.json index ecd7cf026..16811f7e1 100644 --- a/composer.json +++ b/composer.json @@ -45,7 +45,7 @@ "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", "pocketmine/nbt": "~1.0.0", - "pocketmine/raklib": "dev-client-reliable-buffer as 1.0.0", + "pocketmine/raklib": "~1.1.0", "pocketmine/raklib-ipc": "~1.0.0", "pocketmine/snooze": "^0.5.0", "ramsey/uuid": "~4.7.0", diff --git a/composer.lock b/composer.lock index e12dc5439..e76cfcd04 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": "4e1a278e68755522f194fba9b29cd3e2", + "content-hash": "22ebdaff13a226edba97ff5405eac16c", "packages": [ { "name": "adhocore/json-comment", @@ -616,16 +616,16 @@ }, { "name": "pocketmine/raklib", - "version": "dev-client-reliable-buffer", + "version": "1.1.1", "source": { "type": "git", "url": "https://github.com/pmmp/RakLib.git", - "reference": "b1a31e87f0aadeb5894939ded01f60f1259657c5" + "reference": "be2783be516bf6e2872ff5c81fb9048596617b97" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/RakLib/zipball/b1a31e87f0aadeb5894939ded01f60f1259657c5", - "reference": "b1a31e87f0aadeb5894939ded01f60f1259657c5", + "url": "https://api.github.com/repos/pmmp/RakLib/zipball/be2783be516bf6e2872ff5c81fb9048596617b97", + "reference": "be2783be516bf6e2872ff5c81fb9048596617b97", "shasum": "" }, "require": { @@ -653,9 +653,9 @@ "description": "A RakNet server implementation written in PHP", "support": { "issues": "https://github.com/pmmp/RakLib/issues", - "source": "https://github.com/pmmp/RakLib/tree/client-reliable-buffer" + "source": "https://github.com/pmmp/RakLib/tree/1.1.1" }, - "time": "2024-03-02T01:02:00+00:00" + "time": "2024-03-04T14:02:14+00:00" }, { "name": "pocketmine/raklib-ipc", @@ -2928,18 +2928,9 @@ "time": "2023-11-20T00:12:19+00:00" } ], - "aliases": [ - { - "package": "pocketmine/raklib", - "version": "dev-client-reliable-buffer", - "alias": "1.0.0", - "alias_normalized": "1.0.0.0" - } - ], + "aliases": [], "minimum-stability": "stable", - "stability-flags": { - "pocketmine/raklib": 20 - }, + "stability-flags": [], "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/src/network/mcpe/handler/ResourcePacksPacketHandler.php b/src/network/mcpe/handler/ResourcePacksPacketHandler.php index b9089d146..14ab3280d 100644 --- a/src/network/mcpe/handler/ResourcePacksPacketHandler.php +++ b/src/network/mcpe/handler/ResourcePacksPacketHandler.php @@ -212,7 +212,7 @@ class ResourcePacksPacketHandler extends PacketHandler{ $this->session ->sendDataPacketWithReceipt(ResourcePackChunkDataPacket::create($packId, $chunkIndex, $offset, $chunkData)) ->onCompletion( - function() use ($packId, $chunkIndex) : void{ + function() : void{ $this->activeRequests--; $this->processChunkRequestQueue(); }, @@ -221,6 +221,5 @@ class ResourcePacksPacketHandler extends PacketHandler{ $this->disconnectWithError("Plugin interrupted sending of resource packs"); } ); - $this->session->getLogger()->debug("Sent resource pack $packId chunk $chunkIndex"); } } From ac8dbf864033504c59de3cddf6016710c06af7c1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 6 Mar 2024 12:56:49 +0000 Subject: [PATCH 09/35] BlockStateUpgrader: extract state remap to its own function --- .../block/upgrade/BlockStateUpgrader.php | 85 +++++++++++-------- 1 file changed, 49 insertions(+), 36 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index 582039305..f8574d6a2 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -69,44 +69,14 @@ final class BlockStateUpgrader{ //didn't always bump the blockstate version when changing it :( continue; } - $oldName = $blockStateData->getName(); - $oldState = $blockStateData->getStates(); - if(isset($schema->remappedStates[$oldName])){ - foreach($schema->remappedStates[$oldName] as $remap){ - if(count($remap->oldState) > count($oldState)){ - //match criteria has more requirements than we have state properties - continue; //try next state - } - foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){ - if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){ - continue 2; //try next state - } - } - if(is_string($remap->newName)){ - $newName = $remap->newName; - }else{ - $flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null; - if($flattenedValue instanceof StringTag){ - $newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix); - unset($oldState[$remap->newName->flattenedProperty]); - }else{ - //flattened property is not a TAG_String, so this transformation is not applicable - continue; - } - } - - $newState = $remap->newState; - foreach($remap->copiedState as $stateName){ - if(isset($oldState[$stateName])){ - $newState[$stateName] = $oldState[$stateName]; - } - } - - $blockStateData = new BlockStateData($newName, $newState, $resultVersion); - continue 2; //try next schema - } + $newStateData = $this->applyStateRemapped($schema, $blockStateData); + if($newStateData !== null){ + $blockStateData = $newStateData; + continue; } + + $oldName = $blockStateData->getName(); $newName = $schema->renamedIds[$oldName] ?? null; $stateChanges = 0; @@ -131,6 +101,49 @@ final class BlockStateUpgrader{ return $blockStateData; } + private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{ + $oldName = $blockStateData->getName(); + $oldState = $blockStateData->getStates(); + + if(isset($schema->remappedStates[$oldName])){ + foreach($schema->remappedStates[$oldName] as $remap){ + if(count($remap->oldState) > count($oldState)){ + //match criteria has more requirements than we have state properties + continue; //try next state + } + foreach(Utils::stringifyKeys($remap->oldState) as $k => $v){ + if(!isset($oldState[$k]) || !$oldState[$k]->equals($v)){ + continue 2; //try next state + } + } + + if(is_string($remap->newName)){ + $newName = $remap->newName; + }else{ + $flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null; + if($flattenedValue instanceof StringTag){ + $newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix); + unset($oldState[$remap->newName->flattenedProperty]); + }else{ + //flattened property is not a TAG_String, so this transformation is not applicable + continue; + } + } + + $newState = $remap->newState; + foreach($remap->copiedState as $stateName){ + if(isset($oldState[$stateName])){ + $newState[$stateName] = $oldState[$stateName]; + } + } + + return new BlockStateData($newName, $newState, $schema->getVersionId()); + } + } + + return null; + } + /** * @param Tag[] $states * @phpstan-param array $states From 8ec304e66e489b1dd4d4c2f7b6493e70212a01e3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Mar 2024 11:45:08 +0000 Subject: [PATCH 10/35] BlockStateUpgradeSchema: avoid unnecessary property access and calculation this was costing a surprisingly large 5-10% of the processing time for blockstate data. --- .../block/upgrade/BlockStateUpgradeSchema.php | 16 ++++++++++------ .../bedrock/block/upgrade/BlockStateUpgrader.php | 2 +- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php index 36960383e..6d280ecf7 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchema.php @@ -64,20 +64,24 @@ final class BlockStateUpgradeSchema{ */ public array $remappedStates = []; + public readonly int $versionId; + public function __construct( - public int $maxVersionMajor, - public int $maxVersionMinor, - public int $maxVersionPatch, - public int $maxVersionRevision, + public readonly int $maxVersionMajor, + public readonly int $maxVersionMinor, + public readonly int $maxVersionPatch, + public readonly int $maxVersionRevision, private int $schemaId - ){} + ){ + $this->versionId = ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision; + } /** * @deprecated This is defined by Mojang, and therefore cannot be relied on. Use getSchemaId() instead for * internal version management. */ public function getVersionId() : int{ - return ($this->maxVersionMajor << 24) | ($this->maxVersionMinor << 16) | ($this->maxVersionPatch << 8) | $this->maxVersionRevision; + return $this->versionId; } public function getSchemaId() : int{ return $this->schemaId; } diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index f8574d6a2..e91a5cf60 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -62,7 +62,7 @@ final class BlockStateUpgrader{ $version = $blockStateData->getVersion(); $highestVersion = $version; foreach($this->upgradeSchemas as $schema){ - $resultVersion = $schema->getVersionId(); + $resultVersion = $schema->versionId; $highestVersion = max($highestVersion, $resultVersion); if($version > $resultVersion){ //even if this is actually the same version, we have to apply it anyway because mojang are dumb and From a6202d0442029ffe628484cb9ac1135e400229d4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 12 Mar 2024 11:48:48 +0000 Subject: [PATCH 11/35] BlockStateUpgrader: calculate output version ID in a less stupid way this improves the performance by a conservative 10%. --- src/data/bedrock/block/upgrade/BlockStateUpgrader.php | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index e91a5cf60..501ca750a 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -38,6 +38,8 @@ final class BlockStateUpgrader{ /** @var BlockStateUpgradeSchema[] */ private array $upgradeSchemas = []; + private int $outputVersion = 0; + /** * @param BlockStateUpgradeSchema[] $upgradeSchemas * @phpstan-param array $upgradeSchemas @@ -56,14 +58,14 @@ final class BlockStateUpgrader{ $this->upgradeSchemas[$schemaId] = $schema; ksort($this->upgradeSchemas, SORT_NUMERIC); + + $this->outputVersion = max($this->outputVersion, $schema->getVersionId()); } public function upgrade(BlockStateData $blockStateData) : BlockStateData{ $version = $blockStateData->getVersion(); - $highestVersion = $version; foreach($this->upgradeSchemas as $schema){ $resultVersion = $schema->versionId; - $highestVersion = max($highestVersion, $resultVersion); if($version > $resultVersion){ //even if this is actually the same version, we have to apply it anyway because mojang are dumb and //didn't always bump the blockstate version when changing it :( @@ -93,10 +95,10 @@ final class BlockStateUpgrader{ } } - if($highestVersion > $version){ + if($this->outputVersion > $version){ //always update the version number of the blockstate, even if it didn't change - this is needed for //external tools - $blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $highestVersion); + $blockStateData = new BlockStateData($blockStateData->getName(), $blockStateData->getStates(), $this->outputVersion); } return $blockStateData; } From 9f3533d870c8c70fc3600166c3d6f950961b0ad5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 13 Mar 2024 16:42:23 +0000 Subject: [PATCH 12/35] Improved logging for block decode errors this is still noisy, but less so than before. this also adds logging to places where it was previously missing. --- src/world/format/io/BaseWorldProvider.php | 32 ++++++++++++------- src/world/format/io/leveldb/LevelDB.php | 19 ++++++++--- src/world/format/io/region/Anvil.php | 5 +-- .../io/region/LegacyAnvilChunkTrait.php | 7 ++-- src/world/format/io/region/McRegion.php | 9 ++++-- src/world/format/io/region/PMAnvil.php | 5 +-- .../format/io/region/RegionWorldProvider.php | 4 +-- 7 files changed, 55 insertions(+), 26 deletions(-) diff --git a/src/world/format/io/BaseWorldProvider.php b/src/world/format/io/BaseWorldProvider.php index a4d8651a6..f863fdf74 100644 --- a/src/world/format/io/BaseWorldProvider.php +++ b/src/world/format/io/BaseWorldProvider.php @@ -31,7 +31,9 @@ use pocketmine\world\format\io\exception\CorruptedWorldException; use pocketmine\world\format\io\exception\UnsupportedWorldFormatException; use pocketmine\world\format\PalettedBlockArray; use pocketmine\world\WorldException; +use function count; use function file_exists; +use function implode; abstract class BaseWorldProvider implements WorldProvider{ protected WorldData $worldData; @@ -62,27 +64,35 @@ abstract class BaseWorldProvider implements WorldProvider{ */ abstract protected function loadLevelData() : WorldData; - private function translatePalette(PalettedBlockArray $blockArray) : PalettedBlockArray{ + private function translatePalette(PalettedBlockArray $blockArray, \Logger $logger) : PalettedBlockArray{ $palette = $blockArray->getPalette(); $newPalette = []; + $blockDecodeErrors = []; foreach($palette as $k => $legacyIdMeta){ //TODO: remember data for unknown states so we can implement them later + $id = $legacyIdMeta >> 4; + $meta = $legacyIdMeta & 0xf; try{ - $newStateData = $this->blockDataUpgrader->upgradeIntIdMeta($legacyIdMeta >> 4, $legacyIdMeta & 0xf); + $newStateData = $this->blockDataUpgrader->upgradeIntIdMeta($id, $meta); }catch(BlockStateDeserializeException $e){ + $blockDecodeErrors[] = "Palette offset $k / Failed to upgrade legacy ID/meta $id:$meta: " . $e->getMessage(); $newStateData = GlobalBlockStateHandlers::getUnknownBlockStateData(); } try{ $newPalette[$k] = $this->blockStateDeserializer->deserialize($newStateData); - }catch(BlockStateDeserializeException){ - //TODO: this needs to be logged - //TODO: maybe we can remember unknown states for later saving instead of discarding them and destroying maps... + }catch(BlockStateDeserializeException $e){ + //this should never happen anyway - if the upgrader returned an invalid state, we have bigger problems + $blockDecodeErrors[] = "Palette offset $k / Failed to deserialize upgraded state $id:$meta: " . $e->getMessage(); $newPalette[$k] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); } } + if(count($blockDecodeErrors) > 0){ + $logger->error("Errors decoding/upgrading blocks:\n - " . implode("\n - ", $blockDecodeErrors)); + } + //TODO: this is sub-optimal since it reallocates the offset table multiple times return PalettedBlockArray::fromData( $blockArray->getBitsPerBlock(), @@ -91,16 +101,16 @@ abstract class BaseWorldProvider implements WorldProvider{ ); } - protected function palettizeLegacySubChunkXZY(string $idArray, string $metaArray) : PalettedBlockArray{ - return $this->translatePalette(SubChunkConverter::convertSubChunkXZY($idArray, $metaArray)); + protected function palettizeLegacySubChunkXZY(string $idArray, string $metaArray, \Logger $logger) : PalettedBlockArray{ + return $this->translatePalette(SubChunkConverter::convertSubChunkXZY($idArray, $metaArray), $logger); } - protected function palettizeLegacySubChunkYZX(string $idArray, string $metaArray) : PalettedBlockArray{ - return $this->translatePalette(SubChunkConverter::convertSubChunkYZX($idArray, $metaArray)); + protected function palettizeLegacySubChunkYZX(string $idArray, string $metaArray, \Logger $logger) : PalettedBlockArray{ + return $this->translatePalette(SubChunkConverter::convertSubChunkYZX($idArray, $metaArray), $logger); } - protected function palettizeLegacySubChunkFromColumn(string $idArray, string $metaArray, int $yOffset) : PalettedBlockArray{ - return $this->translatePalette(SubChunkConverter::convertSubChunkFromLegacyColumn($idArray, $metaArray, $yOffset)); + protected function palettizeLegacySubChunkFromColumn(string $idArray, string $metaArray, int $yOffset, \Logger $logger) : PalettedBlockArray{ + return $this->translatePalette(SubChunkConverter::convertSubChunkFromLegacyColumn($idArray, $metaArray, $yOffset), $logger); } public function getPath() : string{ diff --git a/src/world/format/io/leveldb/LevelDB.php b/src/world/format/io/leveldb/LevelDB.php index 3d4f577a1..20b55922c 100644 --- a/src/world/format/io/leveldb/LevelDB.php +++ b/src/world/format/io/leveldb/LevelDB.php @@ -26,6 +26,7 @@ namespace pocketmine\world\format\io\leveldb; use pocketmine\block\Block; use pocketmine\data\bedrock\BiomeIds; use pocketmine\data\bedrock\block\BlockStateDeserializeException; +use pocketmine\data\bedrock\block\convert\UnsupportedBlockStateException; use pocketmine\nbt\LittleEndianNbtSerializer; use pocketmine\nbt\NBT; use pocketmine\nbt\NbtDataException; @@ -58,6 +59,7 @@ use function count; use function defined; use function extension_loaded; use function file_exists; +use function implode; use function is_dir; use function mkdir; use function ord; @@ -184,6 +186,8 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ $paletteSize = $stream->getLInt(); } + $blockDecodeErrors = []; + for($i = 0; $i < $paletteSize; ++$i){ try{ $offset = $stream->getOffset(); @@ -199,18 +203,25 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ $blockStateData = $this->blockDataUpgrader->upgradeBlockStateNbt($blockStateNbt); }catch(BlockStateDeserializeException $e){ //while not ideal, this is not a fatal error - $logger->error("Failed to upgrade blockstate: " . $e->getMessage() . " offset $i in palette, blockstate NBT: " . $blockStateNbt->toString()); + $blockDecodeErrors[] = "Palette offset $i / Upgrade error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); continue; } try{ $palette[] = $this->blockStateDeserializer->deserialize($blockStateData); + }catch(UnsupportedBlockStateException $e){ + $blockDecodeErrors[] = "Palette offset $i / " . $e->getMessage(); + $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); }catch(BlockStateDeserializeException $e){ - $logger->error("Failed to deserialize blockstate: " . $e->getMessage() . " offset $i in palette, blockstate NBT: " . $blockStateNbt->toString()); + $blockDecodeErrors[] = "Palette offset $i / Deserialize error: " . $e->getMessage() . ", NBT: " . $blockStateNbt->toString(); $palette[] = $this->blockStateDeserializer->deserialize(GlobalBlockStateHandlers::getUnknownBlockStateData()); } } + if(count($blockDecodeErrors) > 0){ + $logger->error("Errors decoding blocks:\n - " . implode("\n - ", $blockDecodeErrors)); + } + //TODO: exceptions return PalettedBlockArray::fromData($bitsPerBlock, $words, $palette); } @@ -443,7 +454,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ $subChunks = []; for($yy = 0; $yy < 8; ++$yy){ - $storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy)]; + $storages = [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $yy, new \PrefixedLogger($logger, "Subchunk y=$yy"))]; if(isset($convertedLegacyExtraData[$yy])){ $storages[] = $convertedLegacyExtraData[$yy]; } @@ -482,7 +493,7 @@ class LevelDB extends BaseWorldProvider implements WritableWorldProvider{ } } - $storages = [$this->palettizeLegacySubChunkXZY($blocks, $blockData)]; + $storages = [$this->palettizeLegacySubChunkXZY($blocks, $blockData, $logger)]; if($convertedLegacyExtraData !== null){ $storages[] = $convertedLegacyExtraData; } diff --git a/src/world/format/io/region/Anvil.php b/src/world/format/io/region/Anvil.php index abefc9f25..2c14b54e8 100644 --- a/src/world/format/io/region/Anvil.php +++ b/src/world/format/io/region/Anvil.php @@ -31,10 +31,11 @@ use pocketmine\world\format\SubChunk; class Anvil extends RegionWorldProvider{ use LegacyAnvilChunkTrait; - protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk{ + protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{ return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkYZX( self::readFixedSizeByteArray($subChunk, "Blocks", 4096), - self::readFixedSizeByteArray($subChunk, "Data", 2048) + self::readFixedSizeByteArray($subChunk, "Data", 2048), + $logger )], $biomes3d); //ignore legacy light information } diff --git a/src/world/format/io/region/LegacyAnvilChunkTrait.php b/src/world/format/io/region/LegacyAnvilChunkTrait.php index ba97d43b5..6e2f4c8f8 100644 --- a/src/world/format/io/region/LegacyAnvilChunkTrait.php +++ b/src/world/format/io/region/LegacyAnvilChunkTrait.php @@ -54,7 +54,7 @@ trait LegacyAnvilChunkTrait{ /** * @throws CorruptedChunkException */ - protected function deserializeChunk(string $data) : ?LoadedChunkData{ + protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData{ $decompressed = @zlib_decode($data); if($decompressed === false){ throw new CorruptedChunkException("Failed to decompress chunk NBT"); @@ -90,7 +90,8 @@ trait LegacyAnvilChunkTrait{ $subChunksTag = $chunk->getListTag("Sections") ?? []; foreach($subChunksTag as $subChunk){ if($subChunk instanceof CompoundTag){ - $subChunks[$subChunk->getByte("Y")] = $this->deserializeSubChunk($subChunk, clone $biomes3d); + $y = $subChunk->getByte("Y"); + $subChunks[$y] = $this->deserializeSubChunk($subChunk, clone $biomes3d, new \PrefixedLogger($logger, "Subchunk y=$y")); } } for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){ @@ -111,6 +112,6 @@ trait LegacyAnvilChunkTrait{ ); } - abstract protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk; + abstract protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk; } diff --git a/src/world/format/io/region/McRegion.php b/src/world/format/io/region/McRegion.php index 878c84df4..ad6a2d7f2 100644 --- a/src/world/format/io/region/McRegion.php +++ b/src/world/format/io/region/McRegion.php @@ -46,7 +46,7 @@ class McRegion extends RegionWorldProvider{ /** * @throws CorruptedChunkException */ - protected function deserializeChunk(string $data) : ?LoadedChunkData{ + protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData{ $decompressed = @zlib_decode($data); if($decompressed === false){ throw new CorruptedChunkException("Failed to decompress chunk NBT"); @@ -90,7 +90,12 @@ class McRegion extends RegionWorldProvider{ $fullData = self::readFixedSizeByteArray($chunk, "Data", 16384); for($y = 0; $y < 8; ++$y){ - $subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn($fullIds, $fullData, $y)], clone $biomes3d); + $subChunks[$y] = new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkFromColumn( + $fullIds, + $fullData, + $y, + new \PrefixedLogger($logger, "Subchunk y=$y"), + )], clone $biomes3d); } for($y = Chunk::MIN_SUBCHUNK_INDEX; $y <= Chunk::MAX_SUBCHUNK_INDEX; ++$y){ if(!isset($subChunks[$y])){ diff --git a/src/world/format/io/region/PMAnvil.php b/src/world/format/io/region/PMAnvil.php index 41dc0fa80..4fb463124 100644 --- a/src/world/format/io/region/PMAnvil.php +++ b/src/world/format/io/region/PMAnvil.php @@ -35,10 +35,11 @@ use pocketmine\world\format\SubChunk; class PMAnvil extends RegionWorldProvider{ use LegacyAnvilChunkTrait; - protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d) : SubChunk{ + protected function deserializeSubChunk(CompoundTag $subChunk, PalettedBlockArray $biomes3d, \Logger $logger) : SubChunk{ return new SubChunk(Block::EMPTY_STATE_ID, [$this->palettizeLegacySubChunkXZY( self::readFixedSizeByteArray($subChunk, "Blocks", 4096), - self::readFixedSizeByteArray($subChunk, "Data", 2048) + self::readFixedSizeByteArray($subChunk, "Data", 2048), + $logger )], $biomes3d); } diff --git a/src/world/format/io/region/RegionWorldProvider.php b/src/world/format/io/region/RegionWorldProvider.php index a4a3055c6..1feca61be 100644 --- a/src/world/format/io/region/RegionWorldProvider.php +++ b/src/world/format/io/region/RegionWorldProvider.php @@ -147,7 +147,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ /** * @throws CorruptedChunkException */ - abstract protected function deserializeChunk(string $data) : ?LoadedChunkData; + abstract protected function deserializeChunk(string $data, \Logger $logger) : ?LoadedChunkData; /** * @return CompoundTag[] @@ -200,7 +200,7 @@ abstract class RegionWorldProvider extends BaseWorldProvider{ $chunkData = $this->loadRegion($regionX, $regionZ)->readChunk($chunkX & 0x1f, $chunkZ & 0x1f); if($chunkData !== null){ - return $this->deserializeChunk($chunkData); + return $this->deserializeChunk($chunkData, new \PrefixedLogger($this->logger, "Loading chunk x=$chunkX z=$chunkZ")); } return null; From 1de66cb0de7296686327921241117b05b33435f8 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 13 Mar 2024 17:11:06 +0000 Subject: [PATCH 13/35] RegistryTrait: added fast path optimization this reduces VanillaBlocks access time from 360 ns to 230 ns on my machine - an improvement of about 35%. --- src/utils/RegistryTrait.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/utils/RegistryTrait.php b/src/utils/RegistryTrait.php index cf231bf21..f1972b518 100644 --- a/src/utils/RegistryTrait.php +++ b/src/utils/RegistryTrait.php @@ -114,6 +114,13 @@ trait RegistryTrait{ if(count($arguments) > 0){ throw new \ArgumentCountError("Expected exactly 0 arguments, " . count($arguments) . " passed"); } + + //fast path + if(self::$members !== null && isset(self::$members[$name])){ + return self::preprocessMember(self::$members[$name]); + } + + //fallback try{ return self::_registryFromString($name); }catch(\InvalidArgumentException $e){ From 7ff0ae19d65e3b3029246b51de0e361cee6a1a01 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 13 Mar 2024 18:19:51 +0000 Subject: [PATCH 14/35] BlockStateUpgrader: a simple yet hard-to-explain optimization Prior to this commit, upgrade schemas would be applied to blockstates with the same version, as there wasn't any way to know if they should be applied or not given Mojang's tendency to forget to bump the version. However, it occurred to me that we only need to do this if there are multiple schemas present for the same version ID, which is rarely the case. This allows skipping costly logic for blockstates on the newest version (the common case), reducing the time taken to process the blockstate for upgrading by over 30%. Overall, this translates into less than 10% real performance improvement for chunk loading, but it's still a worthwhile improvement. --- .../block/upgrade/BlockStateUpgrader.php | 76 ++++++++++++------- 1 file changed, 47 insertions(+), 29 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index 501ca750a..f4a5b6e93 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -35,7 +35,10 @@ use function sprintf; use const SORT_NUMERIC; final class BlockStateUpgrader{ - /** @var BlockStateUpgradeSchema[] */ + /** + * @var BlockStateUpgradeSchema[][] versionId => [schemaId => schema] + * @phpstan-var array> + */ private array $upgradeSchemas = []; private int $outputVersion = 0; @@ -52,46 +55,37 @@ final class BlockStateUpgrader{ public function addSchema(BlockStateUpgradeSchema $schema) : void{ $schemaId = $schema->getSchemaId(); - if(isset($this->upgradeSchemas[$schemaId])){ - throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID"); + $versionId = $schema->getVersionId(); + if(isset($this->upgradeSchemas[$versionId][$schemaId])){ + throw new \InvalidArgumentException("Cannot add two schemas with the same schema ID and version ID"); } - $this->upgradeSchemas[$schemaId] = $schema; + + //schema ID tells us the order when multiple schemas use the same version ID + $this->upgradeSchemas[$versionId][$schemaId] = $schema; ksort($this->upgradeSchemas, SORT_NUMERIC); + ksort($this->upgradeSchemas[$versionId], SORT_NUMERIC); $this->outputVersion = max($this->outputVersion, $schema->getVersionId()); } public function upgrade(BlockStateData $blockStateData) : BlockStateData{ $version = $blockStateData->getVersion(); - foreach($this->upgradeSchemas as $schema){ - $resultVersion = $schema->versionId; - if($version > $resultVersion){ - //even if this is actually the same version, we have to apply it anyway because mojang are dumb and - //didn't always bump the blockstate version when changing it :( + foreach($this->upgradeSchemas as $resultVersion => $schemaList){ + /* + * Sometimes Mojang made changes without bumping the version ID. + * A notable example is 0131_1.18.20.27_beta_to_1.18.30.json, which renamed a bunch of blockIDs. + * When this happens, all the schemas must be applied even if the version is the same, because the input + * version doesn't tell us which of the schemas have already been applied. + * If there's only one schema for a version (the norm), we can safely assume it's already been applied if + * the version is the same, and skip over it. + */ + if($version > $resultVersion || (count($schemaList) === 1 && $version === $resultVersion)){ continue; } - $newStateData = $this->applyStateRemapped($schema, $blockStateData); - if($newStateData !== null){ - $blockStateData = $newStateData; - continue; - } - - $oldName = $blockStateData->getName(); - $newName = $schema->renamedIds[$oldName] ?? null; - - $stateChanges = 0; - $states = $blockStateData->getStates(); - - $states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges); - $states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges); - $states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges); - $states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges); - - if($newName !== null || $stateChanges > 0){ - $blockStateData = new BlockStateData($newName ?? $oldName, $states, $resultVersion); - //don't break out; we may need to further upgrade the state + foreach($schemaList as $schema){ + $blockStateData = $this->applySchema($schema, $blockStateData); } } @@ -103,6 +97,30 @@ final class BlockStateUpgrader{ return $blockStateData; } + private function applySchema(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : BlockStateData{ + $newStateData = $this->applyStateRemapped($schema, $blockStateData); + if($newStateData !== null){ + return $newStateData; + } + + $oldName = $blockStateData->getName(); + $newName = $schema->renamedIds[$oldName] ?? null; + + $stateChanges = 0; + $states = $blockStateData->getStates(); + + $states = $this->applyPropertyAdded($schema, $oldName, $states, $stateChanges); + $states = $this->applyPropertyRemoved($schema, $oldName, $states, $stateChanges); + $states = $this->applyPropertyRenamedOrValueChanged($schema, $oldName, $states, $stateChanges); + $states = $this->applyPropertyValueChanged($schema, $oldName, $states, $stateChanges); + + if($newName !== null || $stateChanges > 0){ + return new BlockStateData($newName ?? $oldName, $states, $schema->getVersionId()); + } + + return $blockStateData; + } + private function applyStateRemapped(BlockStateUpgradeSchema $schema, BlockStateData $blockStateData) : ?BlockStateData{ $oldName = $blockStateData->getName(); $oldState = $blockStateData->getStates(); From 8d7f1a8557303296d4a5cdbfb710efafb61ae0a3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 13 Mar 2024 18:35:07 +0000 Subject: [PATCH 15/35] BlockStateUpgraderTest: fixed tests for 7ff0ae19d65e3b3029246b51de0e361cee6a1a01 --- .../block/upgrade/BlockStateUpgraderTest.php | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php index efd22212f..4d4d321ec 100644 --- a/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php +++ b/tests/phpunit/data/bedrock/block/upgrade/BlockStateUpgraderTest.php @@ -47,11 +47,11 @@ class BlockStateUpgraderTest extends TestCase{ } private function getNewSchema() : BlockStateUpgradeSchema{ - return $this->getNewSchemaVersion(PHP_INT_MAX); + return $this->getNewSchemaVersion(PHP_INT_MAX, 0); } - private function getNewSchemaVersion(int $versionId) : BlockStateUpgradeSchema{ - $schema = new BlockStateUpgradeSchema(($versionId >> 24) & 0xff, ($versionId >> 16) & 0xff, ($versionId >> 8) & 0xff, $versionId & 0xff, 0); + private function getNewSchemaVersion(int $versionId, int $schemaId) : BlockStateUpgradeSchema{ + $schema = new BlockStateUpgradeSchema(($versionId >> 24) & 0xff, ($versionId >> 16) & 0xff, ($versionId >> 8) & 0xff, $versionId & 0xff, $schemaId); $this->upgrader->addSchema($schema); return $schema; } @@ -211,20 +211,23 @@ class BlockStateUpgraderTest extends TestCase{ } /** - * @phpstan-return \Generator + * @phpstan-return \Generator */ public static function upgraderVersionCompatibilityProvider() : \Generator{ - yield [0x1_00_00_00, 0x1_00_00_00, true]; //Same version: must be altered - this may be a backwards-compatible change that Mojang didn't bother to bump for - yield [0x1_00_01_00, 0x1_00_00_00, true]; //Schema newer than block: must be altered - yield [0x1_00_00_00, 0x1_00_01_00, false]; //Block newer than schema: block must NOT be altered + yield [0x1_00_00_00, 0x1_00_00_00, true, 2]; //Same version, multiple schemas targeting version - must be altered, we don't know which schemas are applicable + yield [0x1_00_00_00, 0x1_00_00_00, false, 1]; //Same version, one schema targeting version - do not change + yield [0x1_00_01_00, 0x1_00_00_00, true, 1]; //Schema newer than block: must be altered + yield [0x1_00_00_00, 0x1_00_01_00, false, 1]; //Block newer than schema: block must NOT be altered } /** * @dataProvider upgraderVersionCompatibilityProvider */ - public function testUpgraderVersionCompatibility(int $schemaVersion, int $stateVersion, bool $shouldChange) : void{ - $schema = $this->getNewSchemaVersion($schemaVersion); - $schema->renamedIds[self::TEST_BLOCK] = self::TEST_BLOCK_2; + public function testUpgraderVersionCompatibility(int $schemaVersion, int $stateVersion, bool $shouldChange, int $schemaCount) : void{ + for($i = 0; $i < $schemaCount; $i++){ + $schema = $this->getNewSchemaVersion($schemaVersion, $i); + $schema->renamedIds[self::TEST_BLOCK] = self::TEST_BLOCK_2; + } $getStateData = fn() => new BlockStateData( self::TEST_BLOCK, From 11ca208d934f54d7c403783888f052ccdbbf5d00 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 14 Mar 2024 12:32:26 +0000 Subject: [PATCH 16/35] RakLib: Allow larger number of split packet parts some persona skins are insanely bloated and get split into hundreds of parts. it's quite annoying that we have to accommodate this, but we can't keep allowing players to experience login timeouts without an obvious indication what's wrong. --- src/network/mcpe/raklib/RakLibServer.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/network/mcpe/raklib/RakLibServer.php b/src/network/mcpe/raklib/RakLibServer.php index e2d6d9a66..5137b94ba 100644 --- a/src/network/mcpe/raklib/RakLibServer.php +++ b/src/network/mcpe/raklib/RakLibServer.php @@ -96,7 +96,8 @@ class RakLibServer extends Thread{ new SimpleProtocolAcceptor($this->protocolVersion), new UserToRakLibThreadMessageReceiver(new PthreadsChannelReader($this->mainToThreadBuffer)), new RakLibToUserThreadMessageSender(new SnoozeAwarePthreadsChannelWriter($this->threadToMainBuffer, $this->sleeperEntry->createNotifier())), - new ExceptionTraceCleaner($this->mainPath) + new ExceptionTraceCleaner($this->mainPath), + recvMaxSplitParts: 512 ); $this->synchronized(function() : void{ $this->ready = true; From b77193b9876fe44bfde9eb6adc30fa48ae494a23 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 14 Mar 2024 12:34:30 +0000 Subject: [PATCH 17/35] ZlibCompressor: Increase max decompression size to accommodate larger skins again, very annoying that we have to account for this (it makes it easier for attackers to waste CPU time and memory), but we don't really have much of a choice. --- src/network/mcpe/compression/ZlibCompressor.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/network/mcpe/compression/ZlibCompressor.php b/src/network/mcpe/compression/ZlibCompressor.php index a6000e781..e908721c7 100644 --- a/src/network/mcpe/compression/ZlibCompressor.php +++ b/src/network/mcpe/compression/ZlibCompressor.php @@ -38,7 +38,7 @@ final class ZlibCompressor implements Compressor{ public const DEFAULT_LEVEL = 7; public const DEFAULT_THRESHOLD = 256; - public const DEFAULT_MAX_DECOMPRESSION_SIZE = 2 * 1024 * 1024; + public const DEFAULT_MAX_DECOMPRESSION_SIZE = 8 * 1024 * 1024; /** * @see SingletonTrait::make() From e31fd122d9fb590bfee189cefb972fcf3f3a1a7b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 14 Mar 2024 17:54:26 +0000 Subject: [PATCH 18/35] BlockStateReader: micro optimize unread properties check this has a pretty much insignificant performance impact, but reduces the cost of this check to basically 0. --- .../block/convert/BlockStateReader.php | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/data/bedrock/block/convert/BlockStateReader.php b/src/data/bedrock/block/convert/BlockStateReader.php index ea44e90b4..07effd4f9 100644 --- a/src/data/bedrock/block/convert/BlockStateReader.php +++ b/src/data/bedrock/block/convert/BlockStateReader.php @@ -38,20 +38,24 @@ use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\Tag; -use pocketmine\utils\Utils; +use function array_keys; +use function count; use function get_class; +use function implode; final class BlockStateReader{ /** - * @var true[] - * @phpstan-var array + * @var Tag[] + * @phpstan-var array */ - private array $usedStates = []; + private array $unusedStates; public function __construct( private BlockStateData $data - ){} + ){ + $this->unusedStates = $this->data->getStates(); + } public function missingOrWrongTypeException(string $name, ?Tag $tag) : BlockStateDeserializeException{ return new BlockStateDeserializeException("Property \"$name\" " . ($tag !== null ? "has unexpected type " . get_class($tag) : "is missing")); @@ -66,7 +70,7 @@ final class BlockStateReader{ /** @throws BlockStateDeserializeException */ public function readBool(string $name) : bool{ - $this->usedStates[$name] = true; + unset($this->unusedStates[$name]); $tag = $this->data->getState($name); if($tag instanceof ByteTag){ switch($tag->getValue()){ @@ -80,7 +84,7 @@ final class BlockStateReader{ /** @throws BlockStateDeserializeException */ public function readInt(string $name) : int{ - $this->usedStates[$name] = true; + unset($this->unusedStates[$name]); $tag = $this->data->getState($name); if($tag instanceof IntTag){ return $tag->getValue(); @@ -99,7 +103,7 @@ final class BlockStateReader{ /** @throws BlockStateDeserializeException */ public function readString(string $name) : string{ - $this->usedStates[$name] = true; + unset($this->unusedStates[$name]); //TODO: only allow a specific set of values (strings are primarily used for enums) $tag = $this->data->getState($name); if($tag instanceof StringTag){ @@ -346,7 +350,7 @@ final class BlockStateReader{ */ public function ignored(string $name) : void{ if($this->data->getState($name) !== null){ - $this->usedStates[$name] = true; + unset($this->unusedStates[$name]); }else{ throw $this->missingOrWrongTypeException($name, null); } @@ -363,10 +367,8 @@ final class BlockStateReader{ * @throws BlockStateDeserializeException */ public function checkUnreadProperties() : void{ - foreach(Utils::stringifyKeys($this->data->getStates()) as $name => $tag){ - if(!isset($this->usedStates[$name])){ - throw new BlockStateDeserializeException("Unread property \"$name\""); - } + if(count($this->unusedStates) > 0){ + throw new BlockStateDeserializeException("Unread properties: " . implode(", ", array_keys($this->unusedStates))); } } } From 7148c7a22234fad45d43132770d1fd8c8f495562 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Fri, 15 Mar 2024 16:44:37 +0000 Subject: [PATCH 19/35] Log rotate (#4032) `server.log` is not rotated by default and grows unmanageably large, to the point where it's so huge that it's not possible to read it with any standard text editor anymore. This PR implements automatic log rotation. - When the `server.log` reaches 32MB in size, it's moved to the `log_archive/` folder of the server's data folder. - The archive's file name will look something like this: `server.2024-03-15T15.26.24.0.log` - The file's name contains the date and time when the file was archived. This may be useful if you're trying to find logs from a particular time frame. This has several benefits: - Much more easily find logs from a particular time frame without scrolling through GBs of logs - Free up space without stopping the server - Archived log files in `log_archive/` can be safely deleted and/or modified while the server is running If you want to automatically compress or clean up the log files, I suggest an external cron job or disk watcher. Closes #4029. --- src/PocketMine.php | 3 +- src/utils/MainLogger.php | 4 +- src/utils/MainLoggerThread.php | 86 +++++++++++++++++++++-- tests/phpunit/scheduler/AsyncPoolTest.php | 2 +- 4 files changed, 84 insertions(+), 11 deletions(-) diff --git a/src/PocketMine.php b/src/PocketMine.php index d13cf33a1..d6e0ae506 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -326,7 +326,8 @@ JIT_WARNING Terminal::init(); } - $logger = new MainLogger(Path::join($dataPath, "server.log"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get())); + $logger = new MainLogger(Path::join($dataPath, "server.log"), Path::join($dataPath, "log_archive"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get())); + \GlobalLogger::set($logger); emit_performance_warnings($logger); diff --git a/src/utils/MainLogger.php b/src/utils/MainLogger.php index ffd56b041..20f9fb6e7 100644 --- a/src/utils/MainLogger.php +++ b/src/utils/MainLogger.php @@ -44,7 +44,7 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ /** * @throws \RuntimeException */ - public function __construct(string $logFile, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false){ + public function __construct(string $logFile, string $logArchiveDir, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false){ parent::__construct(); $this->logDebug = $logDebug; @@ -52,7 +52,7 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ $this->mainThreadName = $mainThreadName; $this->timezone = $timezone->getName(); - $this->logWriterThread = new MainLoggerThread($logFile); + $this->logWriterThread = new MainLoggerThread($logFile, $logArchiveDir); $this->logWriterThread->start(NativeThread::INHERIT_NONE); } diff --git a/src/utils/MainLoggerThread.php b/src/utils/MainLoggerThread.php index 548e23a4f..e7acf9737 100644 --- a/src/utils/MainLoggerThread.php +++ b/src/utils/MainLoggerThread.php @@ -25,23 +25,42 @@ namespace pocketmine\utils; use pmmp\thread\Thread; use pmmp\thread\ThreadSafeArray; +use function clearstatcache; +use function date; use function fclose; +use function file_exists; use function fopen; +use function fstat; use function fwrite; +use function is_dir; +use function is_file; use function is_resource; +use function mkdir; +use function pathinfo; +use function rename; +use function strlen; use function touch; +use const PATHINFO_EXTENSION; +use const PATHINFO_FILENAME; final class MainLoggerThread extends Thread{ + /** @phpstan-var ThreadSafeArray */ private ThreadSafeArray $buffer; private bool $syncFlush = false; private bool $shutdown = false; public function __construct( - private string $logFile + private string $logFile, + private string $archiveDir, + private readonly int $maxFileSize = 32 * 1024 * 1024 //32 MB ){ $this->buffer = new ThreadSafeArray(); touch($this->logFile); + if(!@mkdir($this->archiveDir) && !is_dir($this->archiveDir)){ + throw new \RuntimeException("Unable to create archive directory: " . ( + is_file($this->archiveDir) ? "it already exists and is not a directory" : "permission denied")); + } } public function write(string $line) : void{ @@ -71,12 +90,64 @@ final class MainLoggerThread extends Thread{ $this->join(); } + /** @return resource */ + private function openLogFile(string $file, int &$size){ + $logResource = fopen($file, "ab"); + if(!is_resource($logResource)){ + throw new \RuntimeException("Couldn't open log file"); + } + $stat = fstat($logResource); + if($stat === false){ + throw new AssumptionFailedError("fstat() should not fail here"); + } + $size = $stat['size']; + return $logResource; + } + + /** + * @param resource $logResource + * @return resource + */ + private function archiveLogFile($logResource, int &$size){ + fclose($logResource); + + clearstatcache(); + + $i = 0; + $date = date("Y-m-d\TH.i.s"); + $baseName = pathinfo($this->logFile, PATHINFO_FILENAME); + $extension = pathinfo($this->logFile, PATHINFO_EXTENSION); + do{ + //this shouldn't be necessary, but in case the user messes with the system time for some reason ... + $fileName = "$baseName.$date.$i.$extension"; + $out = $this->archiveDir . "/" . $fileName; + $i++; + }while(file_exists($out)); + + //the user may have externally deleted the whole directory - make sure it exists before we do anything + @mkdir($this->archiveDir); + rename($this->logFile, $out); + + $logResource = $this->openLogFile($this->logFile, $size); + fwrite($logResource, "--- Starting new log file - old log file archived as $fileName ---\n"); + + return $logResource; + } + + private function logFileReadyToArchive(int $size) : bool{ + return $size >= $this->maxFileSize; + } + /** * @param resource $logResource */ - private function writeLogStream($logResource) : void{ + private function writeLogStream(&$logResource, int &$size) : void{ while(($chunk = $this->buffer->shift()) !== null){ fwrite($logResource, $chunk); + $size += strlen($chunk); + if($this->logFileReadyToArchive($size)){ + $logResource = $this->archiveLogFile($logResource, $size); + } } $this->synchronized(function() : void{ @@ -88,13 +159,14 @@ final class MainLoggerThread extends Thread{ } public function run() : void{ - $logResource = fopen($this->logFile, "ab"); - if(!is_resource($logResource)){ - throw new \RuntimeException("Couldn't open log file"); + $size = 0; + $logResource = $this->openLogFile($this->logFile, $size); + if($this->logFileReadyToArchive($size)){ + $logResource = $this->archiveLogFile($logResource, $size); } while(!$this->shutdown){ - $this->writeLogStream($logResource); + $this->writeLogStream($logResource, $size); $this->synchronized(function() : void{ if(!$this->shutdown && !$this->syncFlush){ $this->wait(); @@ -102,7 +174,7 @@ final class MainLoggerThread extends Thread{ }); } - $this->writeLogStream($logResource); + $this->writeLogStream($logResource, $size); fclose($logResource); } diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 54c8ccafd..51622dd22 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -45,7 +45,7 @@ class AsyncPoolTest extends TestCase{ public function setUp() : void{ @define('pocketmine\\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 3) . '/vendor/autoload.php'); - $this->mainLogger = new MainLogger(tempnam(sys_get_temp_dir(), "pmlog"), false, "Main", new \DateTimeZone('UTC')); + $this->mainLogger = new MainLogger(tempnam(sys_get_temp_dir(), "pmlog"), sys_get_temp_dir(), false, "Main", new \DateTimeZone('UTC')); $this->pool = new AsyncPool(2, 1024, new ThreadSafeClassLoader(), $this->mainLogger, new SleeperHandler()); } From f527a4c8fe3787c7e4b187f0ed6d174c6dd5e5a7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Mar 2024 17:53:50 +0000 Subject: [PATCH 20/35] Added --no-log-file command line option while this would be more user-friendly as a config option, configs are a pain because they aren't initialized until after the server log has already been set up. In any case, I foresee that people will likely want to bake this into Dockerfiles directly anyway. --- src/BootstrapOptions.php | 2 ++ src/PocketMine.php | 8 +++++-- src/utils/MainLogger.php | 28 ++++++++++++++--------- tests/phpunit/scheduler/AsyncPoolTest.php | 2 +- 4 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/BootstrapOptions.php b/src/BootstrapOptions.php index c34dda94b..6a1469625 100644 --- a/src/BootstrapOptions.php +++ b/src/BootstrapOptions.php @@ -47,4 +47,6 @@ final class BootstrapOptions{ public const DATA = "data"; /** Shows basic server version information and exits */ public const VERSION = "version"; + /** Disables writing logs to server.log */ + public const NO_LOG_FILE = "no-log-file"; } diff --git a/src/PocketMine.php b/src/PocketMine.php index d6e0ae506..f0fba9dcb 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -317,7 +317,7 @@ JIT_WARNING //Logger has a dependency on timezone Timezone::init(); - $opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI]); + $opts = getopt("", [BootstrapOptions::NO_WIZARD, BootstrapOptions::ENABLE_ANSI, BootstrapOptions::DISABLE_ANSI, BootstrapOptions::NO_LOG_FILE]); if(isset($opts[BootstrapOptions::ENABLE_ANSI])){ Terminal::init(true); }elseif(isset($opts[BootstrapOptions::DISABLE_ANSI])){ @@ -325,8 +325,12 @@ JIT_WARNING }else{ Terminal::init(); } + $logFile = isset($opts[BootstrapOptions::NO_LOG_FILE]) ? null : Path::join($dataPath, "server.log"); - $logger = new MainLogger(Path::join($dataPath, "server.log"), Path::join($dataPath, "log_archive"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get())); + $logger = new MainLogger($logFile, Path::join($dataPath, "log_archive"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get())); + if($logFile === null){ + $logger->notice("Logging to file disabled. Ensure logs are collected by other means (e.g. Docker logs)."); + } \GlobalLogger::set($logger); diff --git a/src/utils/MainLogger.php b/src/utils/MainLogger.php index 20f9fb6e7..da2ba73da 100644 --- a/src/utils/MainLogger.php +++ b/src/utils/MainLogger.php @@ -39,12 +39,12 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ private bool $useFormattingCodes = false; private string $mainThreadName; private string $timezone; - private MainLoggerThread $logWriterThread; + private ?MainLoggerThread $logWriterThread = null; /** * @throws \RuntimeException */ - public function __construct(string $logFile, string $logArchiveDir, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false){ + public function __construct(?string $logFile, string $logArchiveDir, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false){ parent::__construct(); $this->logDebug = $logDebug; @@ -52,8 +52,10 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ $this->mainThreadName = $mainThreadName; $this->timezone = $timezone->getName(); - $this->logWriterThread = new MainLoggerThread($logFile, $logArchiveDir); - $this->logWriterThread->start(NativeThread::INHERIT_NONE); + if($logFile !== null){ + $this->logWriterThread = new MainLoggerThread($logFile, $logArchiveDir); + $this->logWriterThread->start(NativeThread::INHERIT_NONE); + } } /** @@ -166,10 +168,12 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ } public function shutdownLogWriterThread() : void{ - if(NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){ - $this->logWriterThread->shutdown(); - }else{ - throw new \LogicException("Only the creator thread can shutdown the logger thread"); + if($this->logWriterThread !== null){ + if(NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){ + $this->logWriterThread->shutdown(); + }else{ + throw new \LogicException("Only the creator thread can shutdown the logger thread"); + } } } @@ -193,7 +197,9 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ $this->synchronized(function() use ($message, $level, $time) : void{ Terminal::writeLine($message); - $this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL); + if($this->logWriterThread !== null){ + $this->logWriterThread->write($time->format("Y-m-d") . " " . TextFormat::clean($message) . PHP_EOL); + } /** * @var ThreadSafeLoggerAttachment $attachment @@ -205,11 +211,11 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ } public function syncFlushBuffer() : void{ - $this->logWriterThread->syncFlushBuffer(); + $this->logWriterThread?->syncFlushBuffer(); } public function __destruct(){ - if(!$this->logWriterThread->isJoined() && NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){ + if($this->logWriterThread !== null && !$this->logWriterThread->isJoined() && NativeThread::getCurrentThreadId() === $this->logWriterThread->getCreatorId()){ $this->shutdownLogWriterThread(); } } diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 51622dd22..294092ead 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -45,7 +45,7 @@ class AsyncPoolTest extends TestCase{ public function setUp() : void{ @define('pocketmine\\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 3) . '/vendor/autoload.php'); - $this->mainLogger = new MainLogger(tempnam(sys_get_temp_dir(), "pmlog"), sys_get_temp_dir(), false, "Main", new \DateTimeZone('UTC')); + $this->mainLogger = new MainLogger(null, sys_get_temp_dir(), false, "Main", new \DateTimeZone('UTC')); $this->pool = new AsyncPool(2, 1024, new ThreadSafeClassLoader(), $this->mainLogger, new SleeperHandler()); } From 981f49ff56e57610f35c146bf5c520cf2ae91049 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 15 Mar 2024 18:03:44 +0000 Subject: [PATCH 21/35] CS --- tests/phpunit/scheduler/AsyncPoolTest.php | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 294092ead..d7bacd391 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -33,7 +33,6 @@ use function define; use function dirname; use function microtime; use function sys_get_temp_dir; -use function tempnam; use function usleep; class AsyncPoolTest extends TestCase{ From 0e5395c59bdc0d130eafa6c9b385075214988e52 Mon Sep 17 00:00:00 2001 From: Dylan T Date: Mon, 18 Mar 2024 16:48:17 +0000 Subject: [PATCH 22/35] PocketMine-MP.phar self-extraction to decompressed cache (#6217) Because ext-phar sucks, tmp gets spammed by cache files for every thread when loading files from the phar on the fly. Instead, we convert the `.phar` into a decompressed `.tar` in the tmp directory and require files from inside it. Surprisingly, this works because `ext-phar` supports `tar` and `zip` natively. No stream wrapper is required, as the `PocketMine.php` bootstrap loads files relative to its location, so the cache is automatically used for everything. To be honest I would rather get rid of phars entirely, but they are still the easiest way to have PhpStorm load PocketMine-MP API information for now, and the alternatives are more complicated and inconvenient. ### Caveats Everywhere that previously used `new Phar(Phar::running(false))` in the core code needs to be updated to use `PharData` for this to work correctly. Plugins don't need to do anything. ### Why not just use `Phar::decompressFiles()`? This requires setting `phar.readonly` to `0`, which is a security issue. Technically, we could have used a subprocess to do this, but it just didn't seem right. ### WTF? `phar://` can be used on `tar` files??? Yup. I was just as surprised to find out that `require` works in such contexts. ### Relevant issues - Closes #6214 ## Changes ### API changes None. ### Behavioural changes Server startup will be slightly slower, as the phar has to decompress and convert itself into a `.tar`. However, testing showed that this generally takes less than 200 ms, so it should be barely noticeable. ## Backwards compatibility No BC issues. ## Tests Locally tested and the CI will also verify --- build/server-phar-stub.php | 168 +++++++++++++++++++++++++++++++++++++ build/server-phar.php | 18 +--- src/VersionInfo.php | 6 +- 3 files changed, 175 insertions(+), 17 deletions(-) create mode 100644 build/server-phar-stub.php diff --git a/build/server-phar-stub.php b/build/server-phar-stub.php new file mode 100644 index 000000000..b4018e3a7 --- /dev/null +++ b/build/server-phar-stub.php @@ -0,0 +1,168 @@ +convertToData(\Phar::TAR, \Phar::NONE); + unset($phar); + \Phar::unlinkArchive($tmpPharPath); + + return $tmpName . ".tar"; +} + +/** + * Locks a phar tmp cache to prevent it from being deleted by other server instances. + * This code looks similar to Filesystem::createLockFile(), but we can't use that because it's inside the compressed + * phar. + */ +function lockPharCache(string $lockFilePath) : void{ + //this static variable will keep the file(s) locked until the process ends + static $lockFiles = []; + + $lockFile = fopen($lockFilePath, "wb"); + if($lockFile === false){ + throw new \RuntimeException("Failed to open temporary file"); + } + flock($lockFile, LOCK_EX); //this tells other server instances not to delete this cache file + fwrite($lockFile, (string) getmypid()); //maybe useful for debugging + fflush($lockFile); + $lockFiles[$lockFilePath] = $lockFile; +} + +/** + * Prepares a decompressed .tar of PocketMine-MP.phar in the system temp directory for loading code from. + * + * @return string path to the temporary decompressed phar (actually a .tar) + */ +function preparePharCache(string $tmpPath, string $pharPath) : string{ + clearstatcache(); + + $tmpName = tempnam($tmpPath, "PMMP"); + if($tmpName === false){ + throw new \RuntimeException("Failed to create temporary file"); + } + + lockPharCache($tmpName . ".lock"); + return convertPharToTar($tmpName, $pharPath); +} + +$tmpDir = preparePharCacheDirectory(); +cleanupPharCache($tmpDir); +echo "Preparing PocketMine-MP.phar decompressed cache...\n"; +$start = hrtime(true); +$cacheName = preparePharCache($tmpDir, __FILE__); +echo "Cache ready at $cacheName in " . number_format((hrtime(true) - $start) / 1e9, 2) . "s\n"; + +require 'phar://' . str_replace(DIRECTORY_SEPARATOR, '/', $cacheName) . '/src/PocketMine.php'; diff --git a/build/server-phar.php b/build/server-phar.php index 8b4d410ce..f6bb29d51 100644 --- a/build/server-phar.php +++ b/build/server-phar.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\build\server_phar; +use pocketmine\utils\Filesystem; use pocketmine\utils\Git; +use Symfony\Component\Filesystem\Path; use function array_map; use function count; use function dirname; @@ -169,21 +171,7 @@ function main() : void{ 'git' => $gitHash, 'build' => $build ], - <<<'STUB' -getMetadata(); if(isset($meta["git"])){ $gitHash = $meta["git"]; @@ -82,7 +83,8 @@ final class VersionInfo{ if(self::$buildNumber === null){ self::$buildNumber = 0; if(\Phar::running(true) !== ""){ - $phar = new \Phar(\Phar::running(false)); + $pharPath = \Phar::running(false); + $phar = \Phar::isValidPharFilename($pharPath) ? new \Phar($pharPath) : new \PharData($pharPath); $meta = $phar->getMetadata(); if(is_array($meta) && isset($meta["build"]) && is_int($meta["build"])){ self::$buildNumber = $meta["build"]; From b680a1693c2a5e03937f1413e85281c49f722417 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 19 Mar 2024 13:18:30 +0300 Subject: [PATCH 23/35] Added sound when picking sweet berries (#6287) --- src/block/SweetBerryBush.php | 2 ++ src/world/sound/SweetBerriesPickSound.php | 35 +++++++++++++++++++++++ 2 files changed, 37 insertions(+) create mode 100644 src/world/sound/SweetBerriesPickSound.php diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index a0a26ef6e..eabdde118 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -36,6 +36,7 @@ use pocketmine\item\VanillaItems; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\world\sound\SweetBerriesPickSound; use function mt_rand; class SweetBerryBush extends Flowable{ @@ -81,6 +82,7 @@ class SweetBerryBush extends Flowable{ }elseif(($dropAmount = $this->getBerryDropAmount()) > 0){ $world->setBlock($this->position, $this->setAge(self::STAGE_BUSH_NO_BERRIES)); $world->dropItem($this->position, $this->asItem()->setCount($dropAmount)); + $world->addSound($this->position, new SweetBerriesPickSound()); } return true; diff --git a/src/world/sound/SweetBerriesPickSound.php b/src/world/sound/SweetBerriesPickSound.php new file mode 100644 index 000000000..fb8f3c8cf --- /dev/null +++ b/src/world/sound/SweetBerriesPickSound.php @@ -0,0 +1,35 @@ + Date: Mon, 25 Mar 2024 13:52:21 +0300 Subject: [PATCH 24/35] Added new banner patterns (#6298) --- src/block/utils/BannerPatternType.php | 2 ++ src/data/bedrock/BannerPatternTypeIdMap.php | 2 ++ 2 files changed, 4 insertions(+) diff --git a/src/block/utils/BannerPatternType.php b/src/block/utils/BannerPatternType.php index 083fada1b..cd63226a7 100644 --- a/src/block/utils/BannerPatternType.php +++ b/src/block/utils/BannerPatternType.php @@ -82,6 +82,7 @@ enum BannerPatternType{ case DIAGONAL_UP_LEFT; case DIAGONAL_UP_RIGHT; case FLOWER; + case GLOBE; case GRADIENT; case GRADIENT_UP; case HALF_HORIZONTAL; @@ -89,6 +90,7 @@ enum BannerPatternType{ case HALF_VERTICAL; case HALF_VERTICAL_RIGHT; case MOJANG; + case PIGLIN; case RHOMBUS; case SKULL; case SMALL_STRIPES; diff --git a/src/data/bedrock/BannerPatternTypeIdMap.php b/src/data/bedrock/BannerPatternTypeIdMap.php index 87f9b8f57..d1884350f 100644 --- a/src/data/bedrock/BannerPatternTypeIdMap.php +++ b/src/data/bedrock/BannerPatternTypeIdMap.php @@ -56,6 +56,7 @@ final class BannerPatternTypeIdMap{ BannerPatternType::DIAGONAL_UP_LEFT => "ld", BannerPatternType::DIAGONAL_UP_RIGHT => "rud", BannerPatternType::FLOWER => "flo", + BannerPatternType::GLOBE => "glb", BannerPatternType::GRADIENT => "gra", BannerPatternType::GRADIENT_UP => "gru", BannerPatternType::HALF_HORIZONTAL => "hh", @@ -63,6 +64,7 @@ final class BannerPatternTypeIdMap{ BannerPatternType::HALF_VERTICAL => "vh", BannerPatternType::HALF_VERTICAL_RIGHT => "vhr", BannerPatternType::MOJANG => "moj", + BannerPatternType::PIGLIN => "pig", BannerPatternType::RHOMBUS => "mr", BannerPatternType::SKULL => "sku", BannerPatternType::SMALL_STRIPES => "ss", From f799cfaba6318dcf90fce3956bb4843d12581df2 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Mon, 25 Mar 2024 17:15:54 +0300 Subject: [PATCH 25/35] Implemented sound when equipping armor (#6303) --- src/entity/Living.php | 16 +++++++++ src/item/ArmorMaterial.php | 12 ++++++- src/item/VanillaArmorMaterials.php | 21 ++++++++---- src/world/sound/ArmorEquipChainSound.php | 35 ++++++++++++++++++++ src/world/sound/ArmorEquipDiamondSound.php | 35 ++++++++++++++++++++ src/world/sound/ArmorEquipGenericSound.php | 35 ++++++++++++++++++++ src/world/sound/ArmorEquipGoldSound.php | 35 ++++++++++++++++++++ src/world/sound/ArmorEquipIronSound.php | 35 ++++++++++++++++++++ src/world/sound/ArmorEquipLeatherSound.php | 35 ++++++++++++++++++++ src/world/sound/ArmorEquipNetheriteSound.php | 35 ++++++++++++++++++++ 10 files changed, 286 insertions(+), 8 deletions(-) create mode 100644 src/world/sound/ArmorEquipChainSound.php create mode 100644 src/world/sound/ArmorEquipDiamondSound.php create mode 100644 src/world/sound/ArmorEquipGenericSound.php create mode 100644 src/world/sound/ArmorEquipGoldSound.php create mode 100644 src/world/sound/ArmorEquipIronSound.php create mode 100644 src/world/sound/ArmorEquipLeatherSound.php create mode 100644 src/world/sound/ArmorEquipNetheriteSound.php diff --git a/src/entity/Living.php b/src/entity/Living.php index e7d669fda..9a95cae07 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -149,6 +149,22 @@ abstract class Living extends Entity{ $this->getViewers(), fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobArmorChange($recipients, $this) ))); + $playArmorSound = function(Item $newItem, Item $oldItem) : void{ + if(!$newItem->isNull() && $newItem instanceof Armor && !$newItem->equalsExact($oldItem)){ + $equipSound = $newItem->getMaterial()->getEquipSound(); + if($equipSound !== null){ + $this->broadcastSound($equipSound); + } + } + }; + $this->armorInventory->getListeners()->add(new CallbackInventoryListener( + fn(Inventory $inventory, int $slot, Item $oldItem) => $playArmorSound($inventory->getItem($slot), $oldItem), + function(Inventory $inventory, array $oldContents) use ($playArmorSound) : void{ + foreach($oldContents as $slot => $oldItem){ + $playArmorSound($inventory->getItem($slot), $oldItem); + } + } + )); $health = $this->getMaxHealth(); diff --git a/src/item/ArmorMaterial.php b/src/item/ArmorMaterial.php index c7915bacc..d0ea33feb 100644 --- a/src/item/ArmorMaterial.php +++ b/src/item/ArmorMaterial.php @@ -23,10 +23,13 @@ declare(strict_types=1); namespace pocketmine\item; +use pocketmine\world\sound\Sound; + class ArmorMaterial{ public function __construct( - private readonly int $enchantability + private readonly int $enchantability, + private readonly ?Sound $equipSound = null ){ } @@ -39,4 +42,11 @@ class ArmorMaterial{ public function getEnchantability() : int{ return $this->enchantability; } + + /** + * Returns the sound that plays when equipping the armor + */ + public function getEquipSound() : ?Sound{ + return $this->equipSound; + } } diff --git a/src/item/VanillaArmorMaterials.php b/src/item/VanillaArmorMaterials.php index ab2909bce..818273d20 100644 --- a/src/item/VanillaArmorMaterials.php +++ b/src/item/VanillaArmorMaterials.php @@ -24,6 +24,13 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\utils\RegistryTrait; +use pocketmine\world\sound\ArmorEquipChainSound; +use pocketmine\world\sound\ArmorEquipDiamondSound; +use pocketmine\world\sound\ArmorEquipGenericSound; +use pocketmine\world\sound\ArmorEquipGoldSound; +use pocketmine\world\sound\ArmorEquipIronSound; +use pocketmine\world\sound\ArmorEquipLeatherSound; +use pocketmine\world\sound\ArmorEquipNetheriteSound; /** * This doc-block is generated automatically, do not modify it manually. @@ -62,12 +69,12 @@ final class VanillaArmorMaterials{ } protected static function setup() : void{ - self::register("leather", new ArmorMaterial(15)); - self::register("chainmail", new ArmorMaterial(12)); - self::register("iron", new ArmorMaterial(9)); - self::register("turtle", new ArmorMaterial(9)); - self::register("gold", new ArmorMaterial(25)); - self::register("diamond", new ArmorMaterial(10)); - self::register("netherite", new ArmorMaterial(15)); + self::register("leather", new ArmorMaterial(15, new ArmorEquipLeatherSound())); + self::register("chainmail", new ArmorMaterial(12, new ArmorEquipChainSound())); + self::register("iron", new ArmorMaterial(9, new ArmorEquipIronSound())); + self::register("turtle", new ArmorMaterial(9, new ArmorEquipGenericSound())); + self::register("gold", new ArmorMaterial(25, new ArmorEquipGoldSound())); + self::register("diamond", new ArmorMaterial(10, new ArmorEquipDiamondSound())); + self::register("netherite", new ArmorMaterial(15, new ArmorEquipNetheriteSound())); } } diff --git a/src/world/sound/ArmorEquipChainSound.php b/src/world/sound/ArmorEquipChainSound.php new file mode 100644 index 000000000..efcb4f982 --- /dev/null +++ b/src/world/sound/ArmorEquipChainSound.php @@ -0,0 +1,35 @@ + Date: Mon, 25 Mar 2024 14:58:21 +0000 Subject: [PATCH 26/35] workaround for callback-validator not understanding arrow functions --- src/entity/Living.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/entity/Living.php b/src/entity/Living.php index 9a95cae07..a6902ae69 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -158,7 +158,9 @@ abstract class Living extends Entity{ } }; $this->armorInventory->getListeners()->add(new CallbackInventoryListener( - fn(Inventory $inventory, int $slot, Item $oldItem) => $playArmorSound($inventory->getItem($slot), $oldItem), + function(Inventory $inventory, int $slot, Item $oldItem) : void{ + $playArmorSound($inventory->getItem($slot), $oldItem); + }, function(Inventory $inventory, array $oldContents) use ($playArmorSound) : void{ foreach($oldContents as $slot => $oldItem){ $playArmorSound($inventory->getItem($slot), $oldItem); From d5919dc094c60799be2747c9f4235dba26364b9b Mon Sep 17 00:00:00 2001 From: Dylan T Date: Mon, 25 Mar 2024 17:24:52 +0000 Subject: [PATCH 27/35] ... --- src/entity/Living.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/Living.php b/src/entity/Living.php index a6902ae69..e695ba425 100644 --- a/src/entity/Living.php +++ b/src/entity/Living.php @@ -158,7 +158,7 @@ abstract class Living extends Entity{ } }; $this->armorInventory->getListeners()->add(new CallbackInventoryListener( - function(Inventory $inventory, int $slot, Item $oldItem) : void{ + function(Inventory $inventory, int $slot, Item $oldItem) use ($playArmorSound) : void{ $playArmorSound($inventory->getItem($slot), $oldItem); }, function(Inventory $inventory, array $oldContents) use ($playArmorSound) : void{ From de6a91dabc9c26b183f7e08b7a0d7fd36f621361 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Mar 2024 13:56:05 +0000 Subject: [PATCH 28/35] Rework consistency check to tolerate dynamic type IDs we don't actually care about the specific values, only whether all the blocks and their states have been correctly registered. I'd prefer to track all of the state data permutations, but the APIs for that are private, so tracking the number of permutations will have to suffice (this should be good enough to detect bugs anyway, and also takes way less space). --- tests/phpunit/block/BlockTest.php | 96 ++- .../block_factory_consistency_check.json | 693 +++++++++++++++++- .../block/regenerate_consistency_check.php | 75 +- 3 files changed, 770 insertions(+), 94 deletions(-) diff --git a/tests/phpunit/block/BlockTest.php b/tests/phpunit/block/BlockTest.php index f876bf402..6ade2bffe 100644 --- a/tests/phpunit/block/BlockTest.php +++ b/tests/phpunit/block/BlockTest.php @@ -24,13 +24,16 @@ declare(strict_types=1); namespace pocketmine\block; use PHPUnit\Framework\TestCase; -use function asort; -use function file_get_contents; +use pocketmine\utils\AssumptionFailedError; +use pocketmine\utils\Filesystem; +use pocketmine\utils\Utils; +use function implode; use function is_array; +use function is_int; +use function is_string; use function json_decode; use function log; -use function print_r; -use const SORT_STRING; +use const JSON_THROW_ON_ERROR; class BlockTest extends TestCase{ @@ -91,34 +94,71 @@ class BlockTest extends TestCase{ } } - public function testConsistency() : void{ - $list = json_decode(file_get_contents(__DIR__ . '/block_factory_consistency_check.json'), true); - if(!is_array($list)){ - throw new \pocketmine\utils\AssumptionFailedError("Old table should be array{knownStates: array, stateDataBits: int}"); + /** + * @return int[] + * @phpstan-return array + */ + public static function computeConsistencyCheckTable(RuntimeBlockStateRegistry $blockStateRegistry) : array{ + $newTable = []; + + $idNameLookup = []; + //if we ever split up block registration into multiple registries (e.g. separating chemistry blocks), + //we'll need to ensure those additional registries are also included here + foreach(Utils::stringifyKeys(VanillaBlocks::getAll()) as $name => $blockType){ + $id = $blockType->getTypeId(); + if(isset($idNameLookup[$id])){ + throw new AssumptionFailedError("TypeID $name collides with " . $idNameLookup[$id]); + } + $idNameLookup[$id] = $name; } - $knownStates = []; - /** - * @var string $name - * @var int[] $stateIds - */ - foreach($list["knownStates"] as $name => $stateIds){ - foreach($stateIds as $stateId){ - $knownStates[$stateId] = $name; + + foreach($blockStateRegistry->getAllKnownStates() as $index => $block){ + if($index !== $block->getStateId()){ + throw new AssumptionFailedError("State index should always match state ID"); + } + $idName = $idNameLookup[$block->getTypeId()]; + $newTable[$idName] = ($newTable[$idName] ?? 0) + 1; + } + + return $newTable; + } + + /** + * @phpstan-param array $actual + * + * @return string[] + */ + public static function computeConsistencyCheckDiff(string $expectedFile, array $actual) : array{ + $expected = json_decode(Filesystem::fileGetContents($expectedFile), true, 2, JSON_THROW_ON_ERROR); + if(!is_array($expected)){ + throw new AssumptionFailedError("Old table should be array"); + } + + $errors = []; + foreach($expected as $typeName => $numStates){ + if(!is_string($typeName) || !is_int($numStates)){ + throw new AssumptionFailedError("Old table should be array"); + } + if(!isset($actual[$typeName])){ + $errors[] = "Removed block type $typeName ($numStates permutations)"; + }elseif($actual[$typeName] !== $numStates){ + $errors[] = "Block type $typeName permutation count changed: $numStates -> " . $actual[$typeName]; + } + } + foreach(Utils::stringifyKeys($actual) as $typeName => $numStates){ + if(!isset($expected[$typeName])){ + $errors[] = "Added block type $typeName (" . $actual[$typeName] . " permutations)"; } } - $oldStateDataSize = $list["stateDataBits"]; - self::assertSame($oldStateDataSize, Block::INTERNAL_STATE_DATA_BITS, "Changed number of state data bits - consistency check probably need regenerating"); - $states = $this->blockFactory->getAllKnownStates(); - foreach($states as $stateId => $state){ - self::assertArrayHasKey($stateId, $knownStates, "New block state $stateId (" . print_r($state, true) . ") - consistency check may need regenerating"); - self::assertSame($knownStates[$stateId], $state->getName()); - } - asort($knownStates, SORT_STRING); - foreach($knownStates as $k => $name){ - self::assertArrayHasKey($k, $states, "Missing previously-known block state $k " . ($k >> Block::INTERNAL_STATE_DATA_BITS) . ":" . ($k & Block::INTERNAL_STATE_DATA_MASK) . " ($name)"); - self::assertSame($name, $states[$k]->getName()); - } + return $errors; + } + + public function testConsistency() : void{ + $newTable = self::computeConsistencyCheckTable($this->blockFactory); + $errors = self::computeConsistencyCheckDiff(__DIR__ . '/block_factory_consistency_check.json', $newTable); + + self::assertEmpty($errors, "Block factory consistency check failed:\n" . implode("\n", $errors)); } public function testEmptyStateId() : void{ diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 9e68daf0c..27bf83184 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -1 +1,692 @@ -{"knownStates":{"???":[20992881],"Acacia Button":[20482064,20482065,20482068,20482069,20482070,20482071,20482072,20482073,20482076,20482077,20482078,20482079],"Acacia Door":[20484288,20484289,20484290,20484291,20484292,20484293,20484294,20484295,20484296,20484297,20484298,20484299,20484300,20484301,20484302,20484303,20484304,20484305,20484306,20484307,20484308,20484309,20484310,20484311,20484312,20484313,20484314,20484315,20484316,20484317,20484318,20484319],"Acacia Fence":[20487161],"Acacia Fence Gate":[20489840,20489841,20489842,20489843,20489844,20489845,20489846,20489847,20489848,20489849,20489850,20489851,20489852,20489853,20489854,20489855],"Acacia Leaves":[20490916,20490917,20490918,20490919],"Acacia Log":[20492840,20492841,20492842,20492843,20492846,20492847],"Acacia Planks":[20496170],"Acacia Pressure Plate":[20497912,20497913],"Acacia Sapling":[20499538,20499539],"Acacia Sign":[20502272,20502273,20502274,20502275,20502276,20502277,20502278,20502279,20502280,20502281,20502282,20502283,20502284,20502285,20502286,20502287],"Acacia Slab":[20502804,20502805,20502806],"Acacia Stairs":[20504680,20504681,20504682,20504683,20504684,20504685,20504686,20504687],"Acacia Trapdoor":[20508544,20508545,20508546,20508547,20508548,20508549,20508550,20508551,20508552,20508553,20508554,20508555,20508556,20508557,20508558,20508559],"Acacia Wall Sign":[20508856,20508857,20508858,20508859],"Acacia Wood":[20511120,20511121,20511122,20511123,20511124,20511125],"Actinium":[20754337],"Activator Rail":[20513778,20513779,20513780,20513781,20513782,20513783,20513786,20513787,20513788,20513789,20513790,20513791],"Air":[20481500],"All Sided Mushroom Stem":[20515945],"Allium":[20517436],"Aluminum":[20756409],"Americium":[20756900],"Amethyst":[21587565],"Amethyst Cluster":[21582624,21582625,21582626,21582627,21582628,21582629,21582630,21582631,21582632,21582633,21582634,21582635,21582636,21582637,21582638,21582639,21582640,21582641,21582642,21582643,21582644,21582645,21582646,21582647],"Ancient Debris":[21589690],"Andesite":[20520193],"Andesite Slab":[20521816,20521818,20521819],"Andesite Stairs":[20523416,20523417,20523418,20523419,20523420,20523421,20523422,20523423],"Andesite Wall":[20525824,20525825,20525826,20525827,20525828,20525829,20525830,20525831,20525832,20525833,20525834,20525835,20525836,20525837,20525838,20525839,20525846,20525888,20525889,20525890,20525891,20525892,20525893,20525894,20525895,20525896,20525897,20525898,20525899,20525900,20525901,20525902,20525903,20525904,20525905,20525906,20525907,20525908,20525909,20525910,20525911,20525912,20525913,20525914,20525915,20525916,20525917,20525918,20525919,20525920,20525921,20525922,20525923,20525924,20525925,20525926,20525927,20525928,20525929,20525930,20525931,20525932,20525933,20525934,20525935,20525936,20525937,20525938,20525939,20525940,20525941,20525942,20525943,20525944,20525945,20525946,20525947,20525948,20525949,20525950,20525951,20525952,20525953,20525954,20525955,20525956,20525957,20525958,20525959,20525960,20525961,20525962,20525963,20525964,20525965,20525966,20525967,20525974,20526016,20526017,20526018,20526019,20526020,20526021,20526022,20526023,20526024,20526025,20526026,20526027,20526028,20526029,20526030,20526031,20526032,20526033,20526034,20526035,20526036,20526037,20526038,20526039,20526040,20526041,20526042,20526043,20526044,20526045,20526046,20526047,20526048,20526049,20526050,20526051,20526052,20526053,20526054,20526055,20526056,20526057,20526058,20526059,20526060,20526061,20526062,20526063,20526064,20526065,20526066,20526067,20526068,20526069,20526070,20526071,20526072,20526073,20526074,20526075,20526076,20526077,20526078,20526079],"Antimony":[20760253],"Anvil":[20528176,20528177,20528179,20528180,20528181,20528183,20528184,20528185,20528187,20528188,20528189,20528191],"Argon":[20760826],"Arsenic":[20763178],"Astatine":[20766501],"Azalea Leaves":[21886340,21886341,21886342,21886343],"Azure Bluet":[20531031],"Bamboo":[20531777,20531778,20531779,20531781,20531782,20531783,20531785,20531786,20531787,20531789,20531790,20531791],"Bamboo Sapling":[20533516,20533517],"Banner":[20535808,20535809,20535810,20535811,20535812,20535813,20535814,20535815,20535816,20535817,20535818,20535819,20535820,20535821,20535822,20535823,20535824,20535825,20535826,20535827,20535828,20535829,20535830,20535831,20535832,20535833,20535834,20535835,20535836,20535837,20535838,20535839,20535840,20535841,20535842,20535843,20535844,20535845,20535846,20535847,20535848,20535849,20535850,20535851,20535852,20535853,20535854,20535855,20535856,20535857,20535858,20535859,20535860,20535861,20535862,20535863,20535864,20535865,20535866,20535867,20535868,20535869,20535870,20535871,20535872,20535873,20535874,20535875,20535876,20535877,20535878,20535879,20535880,20535881,20535882,20535883,20535884,20535885,20535886,20535887,20535888,20535889,20535890,20535891,20535892,20535893,20535894,20535895,20535896,20535897,20535898,20535899,20535900,20535901,20535902,20535903,20535904,20535905,20535906,20535907,20535908,20535909,20535910,20535911,20535912,20535913,20535914,20535915,20535916,20535917,20535918,20535919,20535920,20535921,20535922,20535923,20535924,20535925,20535926,20535927,20535928,20535929,20535930,20535931,20535932,20535933,20535934,20535935,20535936,20535937,20535938,20535939,20535940,20535941,20535942,20535943,20535944,20535945,20535946,20535947,20535948,20535949,20535950,20535951,20535952,20535953,20535954,20535955,20535956,20535957,20535958,20535959,20535960,20535961,20535962,20535963,20535964,20535965,20535966,20535967,20535968,20535969,20535970,20535971,20535972,20535973,20535974,20535975,20535976,20535977,20535978,20535979,20535980,20535981,20535982,20535983,20535984,20535985,20535986,20535987,20535988,20535989,20535990,20535991,20535992,20535993,20535994,20535995,20535996,20535997,20535998,20535999,20536000,20536001,20536002,20536003,20536004,20536005,20536006,20536007,20536008,20536009,20536010,20536011,20536012,20536013,20536014,20536015,20536016,20536017,20536018,20536019,20536020,20536021,20536022,20536023,20536024,20536025,20536026,20536027,20536028,20536029,20536030,20536031,20536032,20536033,20536034,20536035,20536036,20536037,20536038,20536039,20536040,20536041,20536042,20536043,20536044,20536045,20536046,20536047,20536048,20536049,20536050,20536051,20536052,20536053,20536054,20536055,20536056,20536057,20536058,20536059,20536060,20536061,20536062,20536063],"Barium":[20767788],"Barrel":[20538288,20538289,20538290,20538291,20538294,20538295,20538296,20538297,20538298,20538299,20538302,20538303],"Barrier":[20541302],"Basalt":[21591048,21591049,21591050],"Beacon":[20541801],"Bed Block":[20545280,20545281,20545282,20545283,20545284,20545285,20545286,20545287,20545288,20545289,20545290,20545291,20545292,20545293,20545294,20545295,20545296,20545297,20545298,20545299,20545300,20545301,20545302,20545303,20545304,20545305,20545306,20545307,20545308,20545309,20545310,20545311,20545312,20545313,20545314,20545315,20545316,20545317,20545318,20545319,20545320,20545321,20545322,20545323,20545324,20545325,20545326,20545327,20545328,20545329,20545330,20545331,20545332,20545333,20545334,20545335,20545336,20545337,20545338,20545339,20545340,20545341,20545342,20545343,20545344,20545345,20545346,20545347,20545348,20545349,20545350,20545351,20545352,20545353,20545354,20545355,20545356,20545357,20545358,20545359,20545360,20545361,20545362,20545363,20545364,20545365,20545366,20545367,20545368,20545369,20545370,20545371,20545372,20545373,20545374,20545375,20545376,20545377,20545378,20545379,20545380,20545381,20545382,20545383,20545384,20545385,20545386,20545387,20545388,20545389,20545390,20545391,20545392,20545393,20545394,20545395,20545396,20545397,20545398,20545399,20545400,20545401,20545402,20545403,20545404,20545405,20545406,20545407,20545408,20545409,20545410,20545411,20545412,20545413,20545414,20545415,20545416,20545417,20545418,20545419,20545420,20545421,20545422,20545423,20545424,20545425,20545426,20545427,20545428,20545429,20545430,20545431,20545432,20545433,20545434,20545435,20545436,20545437,20545438,20545439,20545440,20545441,20545442,20545443,20545444,20545445,20545446,20545447,20545448,20545449,20545450,20545451,20545452,20545453,20545454,20545455,20545456,20545457,20545458,20545459,20545460,20545461,20545462,20545463,20545464,20545465,20545466,20545467,20545468,20545469,20545470,20545471,20545472,20545473,20545474,20545475,20545476,20545477,20545478,20545479,20545480,20545481,20545482,20545483,20545484,20545485,20545486,20545487,20545488,20545489,20545490,20545491,20545492,20545493,20545494,20545495,20545496,20545497,20545498,20545499,20545500,20545501,20545502,20545503,20545504,20545505,20545506,20545507,20545508,20545509,20545510,20545511,20545512,20545513,20545514,20545515,20545516,20545517,20545518,20545519,20545520,20545521,20545522,20545523,20545524,20545525,20545526,20545527,20545528,20545529,20545530,20545531,20545532,20545533,20545534,20545535],"Bedrock":[20545540,20545541],"Beetroot Block":[20549328,20549329,20549330,20549331,20549332,20549333,20549334,20549335],"Bell":[20551488,20551489,20551490,20551491,20551492,20551493,20551494,20551495,20551496,20551497,20551498,20551499,20551500,20551501,20551502,20551503],"Berkelium":[20768774],"Beryllium":[20771395],"Big Dripleaf":[21929936,21929937,21929938,21929939,21929940,21929941,21929942,21929943,21929944,21929945,21929946,21929947,21929948,21929949,21929950,21929951],"Big Dripleaf Stem":[21931976,21931977,21931978,21931979],"Birch Button":[20552224,20552225,20552226,20552227,20552230,20552231,20552232,20552233,20552234,20552235,20552238,20552239],"Birch Door":[20554848,20554849,20554850,20554851,20554852,20554853,20554854,20554855,20554856,20554857,20554858,20554859,20554860,20554861,20554862,20554863,20554864,20554865,20554866,20554867,20554868,20554869,20554870,20554871,20554872,20554873,20554874,20554875,20554876,20554877,20554878,20554879],"Birch Fence":[20557622],"Birch Fence Gate":[20559792,20559793,20559794,20559795,20559796,20559797,20559798,20559799,20559800,20559801,20559802,20559803,20559804,20559805,20559806,20559807],"Birch Leaves":[20560344,20560345,20560346,20560347],"Birch Log":[20563648,20563649,20563652,20563653,20563654,20563655],"Birch Planks":[20564186],"Birch Pressure Plate":[20567746,20567747],"Birch Sapling":[20568972,20568973],"Birch Sign":[20571536,20571537,20571538,20571539,20571540,20571541,20571542,20571543,20571544,20571545,20571546,20571547,20571548,20571549,20571550,20571551],"Birch Slab":[20573196,20573197,20573198],"Birch Stairs":[20575080,20575081,20575082,20575083,20575084,20575085,20575086,20575087],"Birch Trapdoor":[20577008,20577009,20577010,20577011,20577012,20577013,20577014,20577015,20577016,20577017,20577018,20577019,20577020,20577021,20577022,20577023],"Birch Wall Sign":[20579020,20579021,20579022,20579023],"Birch Wood":[20580826,20580827,20580828,20580829,20580830,20580831],"Bismuth":[20773935],"Blackstone":[21596307],"Blackstone Slab":[21599012,21599014,21599015],"Blackstone Stairs":[21601800,21601801,21601802,21601803,21601804,21601805,21601806,21601807],"Blackstone Wall":[21604098,21604112,21604113,21604114,21604115,21604116,21604117,21604118,21604119,21604120,21604121,21604122,21604123,21604124,21604125,21604126,21604127,21604160,21604161,21604162,21604163,21604164,21604165,21604166,21604167,21604168,21604169,21604170,21604171,21604172,21604173,21604174,21604175,21604176,21604177,21604178,21604179,21604180,21604181,21604182,21604183,21604184,21604185,21604186,21604187,21604188,21604189,21604190,21604191,21604192,21604193,21604194,21604195,21604196,21604197,21604198,21604199,21604200,21604201,21604202,21604203,21604204,21604205,21604206,21604207,21604208,21604209,21604210,21604211,21604212,21604213,21604214,21604215,21604216,21604217,21604218,21604219,21604220,21604221,21604222,21604223,21604226,21604240,21604241,21604242,21604243,21604244,21604245,21604246,21604247,21604248,21604249,21604250,21604251,21604252,21604253,21604254,21604255,21604288,21604289,21604290,21604291,21604292,21604293,21604294,21604295,21604296,21604297,21604298,21604299,21604300,21604301,21604302,21604303,21604304,21604305,21604306,21604307,21604308,21604309,21604310,21604311,21604312,21604313,21604314,21604315,21604316,21604317,21604318,21604319,21604320,21604321,21604322,21604323,21604324,21604325,21604326,21604327,21604328,21604329,21604330,21604331,21604332,21604333,21604334,21604335,21604336,21604337,21604338,21604339,21604340,21604341,21604342,21604343,21604344,21604345,21604346,21604347,21604348,21604349,21604350,21604351],"Blast Furnace":[20585328,20585329,20585330,20585331,20585332,20585333,20585334,20585335],"Blue Ice":[20588595],"Blue Orchid":[20591829],"Blue Torch":[20593968,20593969,20593973,20593974,20593975],"Bohrium":[20775552],"Bone Block":[20596340,20596341,20596343],"Bookshelf":[20597864],"Boron":[20777985],"Brewing Stand":[20600176,20600177,20600178,20600179,20600180,20600181,20600182,20600183],"Brick Slab":[20601168,20601169,20601171],"Brick Stairs":[20604408,20604409,20604410,20604411,20604412,20604413,20604414,20604415],"Brick Wall":[20605952,20605953,20605954,20605955,20605956,20605957,20605958,20605959,20605960,20605961,20605962,20605963,20605964,20605965,20605966,20605967,20605968,20605969,20605970,20605971,20605972,20605973,20605974,20605975,20605976,20605977,20605978,20605979,20605980,20605981,20605982,20605983,20605984,20605985,20605986,20605987,20605988,20605989,20605990,20605991,20605992,20605993,20605994,20605995,20605996,20605997,20605998,20605999,20606000,20606001,20606002,20606003,20606004,20606005,20606006,20606007,20606008,20606009,20606010,20606011,20606012,20606013,20606014,20606015,20606053,20606064,20606065,20606066,20606067,20606068,20606069,20606070,20606071,20606072,20606073,20606074,20606075,20606076,20606077,20606078,20606079,20606080,20606081,20606082,20606083,20606084,20606085,20606086,20606087,20606088,20606089,20606090,20606091,20606092,20606093,20606094,20606095,20606096,20606097,20606098,20606099,20606100,20606101,20606102,20606103,20606104,20606105,20606106,20606107,20606108,20606109,20606110,20606111,20606112,20606113,20606114,20606115,20606116,20606117,20606118,20606119,20606120,20606121,20606122,20606123,20606124,20606125,20606126,20606127,20606128,20606129,20606130,20606131,20606132,20606133,20606134,20606135,20606136,20606137,20606138,20606139,20606140,20606141,20606142,20606143,20606181,20606192,20606193,20606194,20606195,20606196,20606197,20606198,20606199,20606200,20606201,20606202,20606203,20606204,20606205,20606206,20606207],"Bricks":[20608594],"Bromine":[20779949],"Brown Mushroom":[20613080],"Brown Mushroom Block":[20614032,20614033,20614034,20614035,20614036,20614037,20614038,20614039,20614044,20614045,20614047],"Budding Amethyst":[21577592],"Cactus":[20616384,20616385,20616386,20616387,20616388,20616389,20616390,20616391,20616392,20616393,20616394,20616395,20616396,20616397,20616398,20616399],"Cadmium":[20781893],"Cake":[20618584,20618585,20618586,20618587,20618588,20618589,20618590],"Cake With Candle":[21837222,21837223],"Cake With Dyed Candle":[21839232,21839233,21839234,21839235,21839236,21839237,21839238,21839239,21839240,21839241,21839242,21839243,21839244,21839245,21839246,21839247,21839248,21839249,21839250,21839251,21839252,21839253,21839254,21839255,21839256,21839257,21839258,21839259,21839260,21839261,21839262,21839263],"Calcite":[21638381],"Calcium":[20783925],"Californium":[20787131],"Candle":[21832176,21832177,21832178,21832179,21832180,21832181,21832182,21832183],"Carbon":[20789232],"Carpet":[20619280,20619281,20619282,20619283,20619284,20619285,20619286,20619287,20619288,20619289,20619290,20619291,20619292,20619293,20619294,20619295],"Carrot Block":[20622752,20622753,20622754,20622755,20622756,20622757,20622758,20622759],"Cartography Table":[21845741],"Carved Pumpkin":[20624628,20624629,20624630,20624631],"Cauldron":[21852969],"Cave Vines":[21892224,21892225,21892226,21892227,21892228,21892229,21892230,21892231,21892232,21892233,21892234,21892235,21892236,21892237,21892238,21892239,21892244,21892245,21892248,21892249,21892250,21892251,21892252,21892253,21892254,21892255,21892256,21892257,21892258,21892259,21892260,21892261,21892262,21892263,21892264,21892265,21892266,21892267,21892268,21892269,21892270,21892271,21892276,21892277,21892280,21892281,21892282,21892283,21892284,21892285,21892286,21892287,21892288,21892289,21892290,21892291,21892292,21892293,21892294,21892295,21892296,21892297,21892298,21892299,21892300,21892301,21892302,21892303,21892308,21892309,21892312,21892313,21892314,21892315,21892316,21892317,21892318,21892319,21892320,21892321,21892322,21892323,21892324,21892325,21892326,21892327,21892328,21892329,21892330,21892331,21892332,21892333,21892334,21892335,21892340,21892341,21892344,21892345,21892346,21892347,21892348,21892349,21892350,21892351],"Cerium":[20789431],"Cesium":[20791798],"Chain":[21878573,21878574,21878575],"Cherry Button":[21895570,21895571,21895572,21895573,21895574,21895575,21895578,21895579,21895580,21895581,21895582,21895583],"Cherry Door":[21898304,21898305,21898306,21898307,21898308,21898309,21898310,21898311,21898312,21898313,21898314,21898315,21898316,21898317,21898318,21898319,21898320,21898321,21898322,21898323,21898324,21898325,21898326,21898327,21898328,21898329,21898330,21898331,21898332,21898333,21898334,21898335],"Cherry Fence":[21900661],"Cherry Fence Gate":[21901904,21901905,21901906,21901907,21901908,21901909,21901910,21901911,21901912,21901913,21901914,21901915,21901916,21901917,21901918,21901919],"Cherry Leaves":[21904412,21904413,21904414,21904415],"Cherry Log":[21905784,21905785,21905786,21905787,21905790,21905791],"Cherry Planks":[21908756],"Cherry Pressure Plate":[21911536,21911537],"Cherry Sign":[21914944,21914945,21914946,21914947,21914948,21914949,21914950,21914951,21914952,21914953,21914954,21914955,21914956,21914957,21914958,21914959],"Cherry Slab":[21917632,21917633,21917635],"Cherry Stairs":[21918912,21918913,21918914,21918915,21918916,21918917,21918918,21918919],"Cherry Trapdoor":[21919968,21919969,21919970,21919971,21919972,21919973,21919974,21919975,21919976,21919977,21919978,21919979,21919980,21919981,21919982,21919983],"Cherry Wall Sign":[21923100,21923101,21923102,21923103],"Cherry Wood":[21924008,21924009,21924012,21924013,21924014,21924015],"Chest":[20628872,20628873,20628874,20628875],"Chiseled Bookshelf":[21938176,21938177,21938178,21938179,21938180,21938181,21938182,21938183,21938184,21938185,21938186,21938187,21938188,21938189,21938190,21938191,21938192,21938193,21938194,21938195,21938196,21938197,21938198,21938199,21938200,21938201,21938202,21938203,21938204,21938205,21938206,21938207,21938208,21938209,21938210,21938211,21938212,21938213,21938214,21938215,21938216,21938217,21938218,21938219,21938220,21938221,21938222,21938223,21938224,21938225,21938226,21938227,21938228,21938229,21938230,21938231,21938232,21938233,21938234,21938235,21938236,21938237,21938238,21938239,21938240,21938241,21938242,21938243,21938244,21938245,21938246,21938247,21938248,21938249,21938250,21938251,21938252,21938253,21938254,21938255,21938256,21938257,21938258,21938259,21938260,21938261,21938262,21938263,21938264,21938265,21938266,21938267,21938268,21938269,21938270,21938271,21938272,21938273,21938274,21938275,21938276,21938277,21938278,21938279,21938280,21938281,21938282,21938283,21938284,21938285,21938286,21938287,21938288,21938289,21938290,21938291,21938292,21938293,21938294,21938295,21938296,21938297,21938298,21938299,21938300,21938301,21938302,21938303,21938304,21938305,21938306,21938307,21938308,21938309,21938310,21938311,21938312,21938313,21938314,21938315,21938316,21938317,21938318,21938319,21938320,21938321,21938322,21938323,21938324,21938325,21938326,21938327,21938328,21938329,21938330,21938331,21938332,21938333,21938334,21938335,21938336,21938337,21938338,21938339,21938340,21938341,21938342,21938343,21938344,21938345,21938346,21938347,21938348,21938349,21938350,21938351,21938352,21938353,21938354,21938355,21938356,21938357,21938358,21938359,21938360,21938361,21938362,21938363,21938364,21938365,21938366,21938367,21938368,21938369,21938370,21938371,21938372,21938373,21938374,21938375,21938376,21938377,21938378,21938379,21938380,21938381,21938382,21938383,21938384,21938385,21938386,21938387,21938388,21938389,21938390,21938391,21938392,21938393,21938394,21938395,21938396,21938397,21938398,21938399,21938400,21938401,21938402,21938403,21938404,21938405,21938406,21938407,21938408,21938409,21938410,21938411,21938412,21938413,21938414,21938415,21938416,21938417,21938418,21938419,21938420,21938421,21938422,21938423,21938424,21938425,21938426,21938427,21938428,21938429,21938430,21938431],"Chiseled Deepslate":[21680290],"Chiseled Nether Bricks":[21682906],"Chiseled Polished Blackstone":[21618038],"Chiseled Quartz Block":[20630148,20630150,20630151],"Chiseled Red Sandstone":[20633244],"Chiseled Sandstone":[20635615],"Chiseled Stone Bricks":[20636034],"Chlorine":[20794258],"Chorus Flower":[21864360,21864361,21864364,21864365,21864366,21864367],"Chorus Plant":[21866123],"Chromium":[20797122],"Clay Block":[20639448],"Coal Block":[20641681],"Coal Ore":[20642280],"Cobalt":[20799311],"Cobbled Deepslate":[21662962],"Cobbled Deepslate Slab":[21665324,21665326,21665327],"Cobbled Deepslate Stairs":[21666232,21666233,21666234,21666235,21666236,21666237,21666238,21666239],"Cobbled Deepslate Wall":[21668389,21668400,21668401,21668402,21668403,21668404,21668405,21668406,21668407,21668408,21668409,21668410,21668411,21668412,21668413,21668414,21668415,21668416,21668417,21668418,21668419,21668420,21668421,21668422,21668423,21668424,21668425,21668426,21668427,21668428,21668429,21668430,21668431,21668432,21668433,21668434,21668435,21668436,21668437,21668438,21668439,21668440,21668441,21668442,21668443,21668444,21668445,21668446,21668447,21668448,21668449,21668450,21668451,21668452,21668453,21668454,21668455,21668456,21668457,21668458,21668459,21668460,21668461,21668462,21668463,21668464,21668465,21668466,21668467,21668468,21668469,21668470,21668471,21668472,21668473,21668474,21668475,21668476,21668477,21668478,21668479,21668517,21668528,21668529,21668530,21668531,21668532,21668533,21668534,21668535,21668536,21668537,21668538,21668539,21668540,21668541,21668542,21668543,21668544,21668545,21668546,21668547,21668548,21668549,21668550,21668551,21668552,21668553,21668554,21668555,21668556,21668557,21668558,21668559,21668560,21668561,21668562,21668563,21668564,21668565,21668566,21668567,21668568,21668569,21668570,21668571,21668572,21668573,21668574,21668575,21668576,21668577,21668578,21668579,21668580,21668581,21668582,21668583,21668584,21668585,21668586,21668587,21668588,21668589,21668590,21668591,21668592,21668593,21668594,21668595,21668596,21668597,21668598,21668599,21668600,21668601,21668602,21668603,21668604,21668605,21668606,21668607],"Cobblestone":[20645646],"Cobblestone Slab":[20647760,20647762,20647763],"Cobblestone Stairs":[20648720,20648721,20648722,20648723,20648724,20648725,20648726,20648727],"Cobblestone Wall":[20649984,20649985,20649986,20649987,20649988,20649989,20649990,20649991,20649992,20649993,20649994,20649995,20649996,20649997,20649998,20649999,20650000,20650001,20650002,20650003,20650004,20650005,20650006,20650007,20650008,20650009,20650010,20650011,20650012,20650013,20650014,20650015,20650016,20650017,20650018,20650019,20650020,20650021,20650022,20650023,20650024,20650025,20650026,20650027,20650028,20650029,20650030,20650031,20650032,20650033,20650034,20650035,20650036,20650037,20650038,20650039,20650040,20650041,20650042,20650043,20650044,20650045,20650046,20650047,20650080,20650081,20650082,20650083,20650084,20650085,20650086,20650087,20650088,20650089,20650090,20650091,20650092,20650093,20650094,20650095,20650099,20650112,20650113,20650114,20650115,20650116,20650117,20650118,20650119,20650120,20650121,20650122,20650123,20650124,20650125,20650126,20650127,20650128,20650129,20650130,20650131,20650132,20650133,20650134,20650135,20650136,20650137,20650138,20650139,20650140,20650141,20650142,20650143,20650144,20650145,20650146,20650147,20650148,20650149,20650150,20650151,20650152,20650153,20650154,20650155,20650156,20650157,20650158,20650159,20650160,20650161,20650162,20650163,20650164,20650165,20650166,20650167,20650168,20650169,20650170,20650171,20650172,20650173,20650174,20650175,20650208,20650209,20650210,20650211,20650212,20650213,20650214,20650215,20650216,20650217,20650218,20650219,20650220,20650221,20650222,20650223,20650227],"Cobweb":[20652464],"Cocoa Block":[20655024,20655025,20655026,20655027,20655028,20655029,20655030,20655031,20655032,20655033,20655034,20655035],"Compound Creator":[20656176,20656177,20656178,20656179],"Concrete":[20659968,20659969,20659970,20659971,20659972,20659973,20659974,20659975,20659976,20659977,20659978,20659979,20659980,20659981,20659982,20659983],"Concrete Powder":[20661296,20661297,20661298,20661299,20661300,20661301,20661302,20661303,20661304,20661305,20661306,20661307,20661308,20661309,20661310,20661311],"Copernicium":[20802812],"Copper":[20804760],"Copper Block":[21823528,21823529,21823530,21823531,21823532,21823533,21823534,21823535],"Copper Ore":[21798993],"Coral":[20663618,20663620,20663621,20663622,20663623,20663626,20663628,20663629,20663630,20663631],"Coral Block":[20664465,20664468,20664469,20664470,20664471,20664473,20664476,20664477,20664478,20664479],"Coral Fan":[20668131,20668132,20668133,20668134,20668135,20668139,20668140,20668141,20668142,20668143,20668147,20668148,20668149,20668150,20668151,20668155,20668156,20668157,20668158,20668159],"Cornflower":[20670352],"Cracked Deepslate Bricks":[21650157],"Cracked Deepslate Tiles":[21661164],"Cracked Nether Bricks":[21685186],"Cracked Polished Blackstone Bricks":[21628426],"Cracked Stone Bricks":[20670706],"Crafting Table":[20672716],"Crimson Button":[21737520,21737521,21737522,21737523,21737526,21737527,21737528,21737529,21737530,21737531,21737534,21737535],"Crimson Door":[21751360,21751361,21751362,21751363,21751364,21751365,21751366,21751367,21751368,21751369,21751370,21751371,21751372,21751373,21751374,21751375,21751376,21751377,21751378,21751379,21751380,21751381,21751382,21751383,21751384,21751385,21751386,21751387,21751388,21751389,21751390,21751391],"Crimson Fence":[21707929],"Crimson Fence Gate":[21757232,21757233,21757234,21757235,21757236,21757237,21757238,21757239,21757240,21757241,21757242,21757243,21757244,21757245,21757246,21757247],"Crimson Hyphae":[21726912,21726913,21726916,21726917,21726918,21726919],"Crimson Planks":[21701319],"Crimson Pressure Plate":[21745172,21745173],"Crimson Roots":[21934477],"Crimson Sign":[21769568,21769569,21769570,21769571,21769572,21769573,21769574,21769575,21769576,21769577,21769578,21769579,21769580,21769581,21769582,21769583],"Crimson Slab":[21714416,21714417,21714418],"Crimson Stairs":[21763896,21763897,21763898,21763899,21763900,21763901,21763902,21763903],"Crimson Stem":[21719642,21719643,21719644,21719645,21719646,21719647],"Crimson Trapdoor":[21733344,21733345,21733346,21733347,21733348,21733349,21733350,21733351,21733352,21733353,21733354,21733355,21733356,21733357,21733358,21733359],"Crimson Wall Sign":[21774384,21774385,21774386,21774387],"Crying Obsidian":[21818013],"Curium":[20805897],"Cut Copper Block":[21827208,21827209,21827210,21827211,21827212,21827213,21827214,21827215],"Cut Copper Slab Slab":[21827976,21827977,21827978,21827979,21827980,21827981,21827982,21827983,21827984,21827985,21827986,21827987,21827988,21827989,21827990,21827991,21827992,21827993,21827994,21827995,21827996,21827997,21827998,21827999],"Cut Copper Stairs":[21830080,21830081,21830082,21830083,21830084,21830085,21830086,21830087,21830088,21830089,21830090,21830091,21830092,21830093,21830094,21830095,21830096,21830097,21830098,21830099,21830100,21830101,21830102,21830103,21830104,21830105,21830106,21830107,21830108,21830109,21830110,21830111,21830112,21830113,21830114,21830115,21830116,21830117,21830118,21830119,21830120,21830121,21830122,21830123,21830124,21830125,21830126,21830127,21830128,21830129,21830130,21830131,21830132,21830133,21830134,21830135,21830136,21830137,21830138,21830139,21830140,21830141,21830142,21830143],"Cut Red Sandstone":[20674620],"Cut Red Sandstone Slab":[20676996,20676997,20676999],"Cut Sandstone":[20679139],"Cut Sandstone Slab":[20681972,20681973,20681974],"Dandelion":[20686168],"Dark Oak Button":[20688624,20688625,20688626,20688627,20688630,20688631,20688632,20688633,20688634,20688635,20688638,20688639],"Dark Oak Door":[20690144,20690145,20690146,20690147,20690148,20690149,20690150,20690151,20690152,20690153,20690154,20690155,20690156,20690157,20690158,20690159,20690160,20690161,20690162,20690163,20690164,20690165,20690166,20690167,20690168,20690169,20690170,20690171,20690172,20690173,20690174,20690175],"Dark Oak Fence":[20692929],"Dark Oak Fence Gate":[20694464,20694465,20694466,20694467,20694468,20694469,20694470,20694471,20694472,20694473,20694474,20694475,20694476,20694477,20694478,20694479],"Dark Oak Leaves":[20696240,20696241,20696242,20696243],"Dark Oak Log":[20698832,20698833,20698836,20698837,20698838,20698839],"Dark Oak Planks":[20699858],"Dark Oak Pressure Plate":[20701726,20701727],"Dark Oak Sapling":[20704518,20704519],"Dark Oak Sign":[20705760,20705761,20705762,20705763,20705764,20705765,20705766,20705767,20705768,20705769,20705770,20705771,20705772,20705773,20705774,20705775],"Dark Oak Slab":[20708816,20708817,20708818],"Dark Oak Stairs":[20711344,20711345,20711346,20711347,20711348,20711349,20711350,20711351],"Dark Oak Trapdoor":[20713024,20713025,20713026,20713027,20713028,20713029,20713030,20713031,20713032,20713033,20713034,20713035,20713036,20713037,20713038,20713039],"Dark Oak Wall Sign":[20715224,20715225,20715226,20715227],"Dark Oak Wood":[20716738,20716739,20716740,20716741,20716742,20716743],"Dark Prismarine":[20719220],"Dark Prismarine Slab":[20721200,20721202,20721203],"Dark Prismarine Stairs":[20722592,20722593,20722594,20722595,20722596,20722597,20722598,20722599],"Darmstadtium":[20809292],"Daylight Sensor":[20724416,20724417,20724418,20724419,20724420,20724421,20724422,20724423,20724424,20724425,20724426,20724427,20724428,20724429,20724430,20724431,20724432,20724433,20724434,20724435,20724436,20724437,20724438,20724439,20724440,20724441,20724442,20724443,20724444,20724445,20724446,20724447],"Dead Bush":[20727625],"Deepslate":[21640384,21640385,21640387],"Deepslate Brick Slab":[21644892,21644893,21644894],"Deepslate Brick Stairs":[21646904,21646905,21646906,21646907,21646908,21646909,21646910,21646911],"Deepslate Brick Wall":[21649152,21649153,21649154,21649155,21649156,21649157,21649158,21649159,21649160,21649161,21649162,21649163,21649164,21649165,21649166,21649167,21649168,21649169,21649170,21649171,21649172,21649173,21649174,21649175,21649176,21649177,21649178,21649179,21649180,21649181,21649182,21649183,21649184,21649185,21649186,21649187,21649188,21649189,21649190,21649191,21649192,21649193,21649194,21649195,21649196,21649197,21649198,21649199,21649200,21649201,21649202,21649203,21649204,21649205,21649206,21649207,21649208,21649209,21649210,21649211,21649212,21649213,21649214,21649215,21649217,21649232,21649233,21649234,21649235,21649236,21649237,21649238,21649239,21649240,21649241,21649242,21649243,21649244,21649245,21649246,21649247,21649280,21649281,21649282,21649283,21649284,21649285,21649286,21649287,21649288,21649289,21649290,21649291,21649292,21649293,21649294,21649295,21649296,21649297,21649298,21649299,21649300,21649301,21649302,21649303,21649304,21649305,21649306,21649307,21649308,21649309,21649310,21649311,21649312,21649313,21649314,21649315,21649316,21649317,21649318,21649319,21649320,21649321,21649322,21649323,21649324,21649325,21649326,21649327,21649328,21649329,21649330,21649331,21649332,21649333,21649334,21649335,21649336,21649337,21649338,21649339,21649340,21649341,21649342,21649343,21649345,21649360,21649361,21649362,21649363,21649364,21649365,21649366,21649367,21649368,21649369,21649370,21649371,21649372,21649373,21649374,21649375],"Deepslate Bricks":[21642473],"Deepslate Coal Ore":[21783821],"Deepslate Copper Ore":[21797493],"Deepslate Diamond Ore":[21785055],"Deepslate Emerald Ore":[21788073],"Deepslate Gold Ore":[21795656],"Deepslate Iron Ore":[21794765],"Deepslate Lapis Lazuli Ore":[21788910],"Deepslate Redstone Ore":[21791572,21791573],"Deepslate Tile Slab":[21655484,21655485,21655487],"Deepslate Tile Stairs":[21657328,21657329,21657330,21657331,21657332,21657333,21657334,21657335],"Deepslate Tile Wall":[21658912,21658913,21658914,21658915,21658916,21658917,21658918,21658919,21658920,21658921,21658922,21658923,21658924,21658925,21658926,21658927,21658930,21658944,21658945,21658946,21658947,21658948,21658949,21658950,21658951,21658952,21658953,21658954,21658955,21658956,21658957,21658958,21658959,21658960,21658961,21658962,21658963,21658964,21658965,21658966,21658967,21658968,21658969,21658970,21658971,21658972,21658973,21658974,21658975,21658976,21658977,21658978,21658979,21658980,21658981,21658982,21658983,21658984,21658985,21658986,21658987,21658988,21658989,21658990,21658991,21658992,21658993,21658994,21658995,21658996,21658997,21658998,21658999,21659000,21659001,21659002,21659003,21659004,21659005,21659006,21659007,21659040,21659041,21659042,21659043,21659044,21659045,21659046,21659047,21659048,21659049,21659050,21659051,21659052,21659053,21659054,21659055,21659058,21659072,21659073,21659074,21659075,21659076,21659077,21659078,21659079,21659080,21659081,21659082,21659083,21659084,21659085,21659086,21659087,21659088,21659089,21659090,21659091,21659092,21659093,21659094,21659095,21659096,21659097,21659098,21659099,21659100,21659101,21659102,21659103,21659104,21659105,21659106,21659107,21659108,21659109,21659110,21659111,21659112,21659113,21659114,21659115,21659116,21659117,21659118,21659119,21659120,21659121,21659122,21659123,21659124,21659125,21659126,21659127,21659128,21659129,21659130,21659131,21659132,21659133,21659134,21659135],"Deepslate Tiles":[21652461],"Detector Rail":[20728240,20728241,20728242,20728243,20728246,20728247,20728248,20728249,20728250,20728251,20728254,20728255],"Diamond Block":[20730418],"Diamond Ore":[20733642],"Diorite":[20734079],"Diorite Slab":[20737469,20737470,20737471],"Diorite Stairs":[20739504,20739505,20739506,20739507,20739508,20739509,20739510,20739511],"Diorite Wall":[20741920,20741921,20741922,20741923,20741924,20741925,20741926,20741927,20741928,20741929,20741930,20741931,20741932,20741933,20741934,20741935,20741937,20741952,20741953,20741954,20741955,20741956,20741957,20741958,20741959,20741960,20741961,20741962,20741963,20741964,20741965,20741966,20741967,20741968,20741969,20741970,20741971,20741972,20741973,20741974,20741975,20741976,20741977,20741978,20741979,20741980,20741981,20741982,20741983,20741984,20741985,20741986,20741987,20741988,20741989,20741990,20741991,20741992,20741993,20741994,20741995,20741996,20741997,20741998,20741999,20742000,20742001,20742002,20742003,20742004,20742005,20742006,20742007,20742008,20742009,20742010,20742011,20742012,20742013,20742014,20742015,20742048,20742049,20742050,20742051,20742052,20742053,20742054,20742055,20742056,20742057,20742058,20742059,20742060,20742061,20742062,20742063,20742065,20742080,20742081,20742082,20742083,20742084,20742085,20742086,20742087,20742088,20742089,20742090,20742091,20742092,20742093,20742094,20742095,20742096,20742097,20742098,20742099,20742100,20742101,20742102,20742103,20742104,20742105,20742106,20742107,20742108,20742109,20742110,20742111,20742112,20742113,20742114,20742115,20742116,20742117,20742118,20742119,20742120,20742121,20742122,20742123,20742124,20742125,20742126,20742127,20742128,20742129,20742130,20742131,20742132,20742133,20742134,20742135,20742136,20742137,20742138,20742139,20742140,20742141,20742142,20742143],"Dirt":[20742760,20742761,20742763],"Double Pitcher Crop":[21950064,21950065,21950066,21950067],"Double Tallgrass":[20744902,20744903],"Dragon Egg":[20747804],"Dried Kelp Block":[20749160],"Dubnium":[20811286],"Dyed Candle":[21834240,21834241,21834242,21834243,21834244,21834245,21834246,21834247,21834248,21834249,21834250,21834251,21834252,21834253,21834254,21834255,21834256,21834257,21834258,21834259,21834260,21834261,21834262,21834263,21834264,21834265,21834266,21834267,21834268,21834269,21834270,21834271,21834272,21834273,21834274,21834275,21834276,21834277,21834278,21834279,21834280,21834281,21834282,21834283,21834284,21834285,21834286,21834287,21834288,21834289,21834290,21834291,21834292,21834293,21834294,21834295,21834296,21834297,21834298,21834299,21834300,21834301,21834302,21834303,21834304,21834305,21834306,21834307,21834308,21834309,21834310,21834311,21834312,21834313,21834314,21834315,21834316,21834317,21834318,21834319,21834320,21834321,21834322,21834323,21834324,21834325,21834326,21834327,21834328,21834329,21834330,21834331,21834332,21834333,21834334,21834335,21834336,21834337,21834338,21834339,21834340,21834341,21834342,21834343,21834344,21834345,21834346,21834347,21834348,21834349,21834350,21834351,21834352,21834353,21834354,21834355,21834356,21834357,21834358,21834359,21834360,21834361,21834362,21834363,21834364,21834365,21834366,21834367],"Dyed Shulker Box":[20750816,20750817,20750818,20750819,20750820,20750821,20750822,20750823,20750824,20750825,20750826,20750827,20750828,20750829,20750830,20750831],"Dysprosium":[20812845],"Einsteinium":[20813977],"Element Constructor":[20799584,20799585,20799586,20799587],"Emerald Block":[20998956],"Emerald Ore":[21001840],"Enchanting Table":[21002728],"End Portal Frame":[21005904,21005905,21005906,21005907,21005908,21005909,21005910,21005911],"End Rod":[21006744,21006745,21006746,21006747,21006750,21006751],"End Stone":[21010195],"End Stone Brick Slab":[21011608,21011609,21011611],"End Stone Brick Stairs":[21014200,21014201,21014202,21014203,21014204,21014205,21014206,21014207],"End Stone Brick Wall":[21015589,21015600,21015601,21015602,21015603,21015604,21015605,21015606,21015607,21015608,21015609,21015610,21015611,21015612,21015613,21015614,21015615,21015616,21015617,21015618,21015619,21015620,21015621,21015622,21015623,21015624,21015625,21015626,21015627,21015628,21015629,21015630,21015631,21015632,21015633,21015634,21015635,21015636,21015637,21015638,21015639,21015640,21015641,21015642,21015643,21015644,21015645,21015646,21015647,21015648,21015649,21015650,21015651,21015652,21015653,21015654,21015655,21015656,21015657,21015658,21015659,21015660,21015661,21015662,21015663,21015664,21015665,21015666,21015667,21015668,21015669,21015670,21015671,21015672,21015673,21015674,21015675,21015676,21015677,21015678,21015679,21015717,21015728,21015729,21015730,21015731,21015732,21015733,21015734,21015735,21015736,21015737,21015738,21015739,21015740,21015741,21015742,21015743,21015744,21015745,21015746,21015747,21015748,21015749,21015750,21015751,21015752,21015753,21015754,21015755,21015756,21015757,21015758,21015759,21015760,21015761,21015762,21015763,21015764,21015765,21015766,21015767,21015768,21015769,21015770,21015771,21015772,21015773,21015774,21015775,21015776,21015777,21015778,21015779,21015780,21015781,21015782,21015783,21015784,21015785,21015786,21015787,21015788,21015789,21015790,21015791,21015792,21015793,21015794,21015795,21015796,21015797,21015798,21015799,21015800,21015801,21015802,21015803,21015804,21015805,21015806,21015807],"End Stone Bricks":[21017562],"Ender Chest":[21019660,21019661,21019662,21019663],"Erbium":[20817537],"Europium":[20819484],"Fake Wooden Slab":[21021009,21021010,21021011],"Farmland":[21022720,21022721,21022722,21022723,21022724,21022725,21022726,21022727,21022728,21022729,21022730,21022731,21022732,21022733,21022734,21022735,21022736,21022737,21022738,21022739,21022740,21022741,21022742,21022743,21022744,21022745,21022746,21022747,21022748,21022749,21022750,21022751,21022752,21022753,21022754,21022755,21022756,21022757,21022758,21022759,21022760,21022761,21022762,21022763,21022764,21022765,21022766,21022767,21022768,21022769,21022770,21022771,21022772,21022773,21022774,21022775,21022776,21022777,21022778,21022779,21022780,21022781,21022782,21022783,21022784,21022785,21022786,21022787,21022788,21022789,21022790,21022791,21022792,21022793,21022794,21022795,21022796,21022797,21022798,21022799,21022800,21022801,21022802,21022803,21022804,21022805,21022806,21022807,21022808,21022809,21022810,21022811,21022812,21022813,21022814,21022815,21022816,21022817,21022818,21022819,21022820,21022821,21022822,21022823,21022824,21022825,21022826,21022827,21022828,21022829,21022830,21022831,21022832,21022833,21022834,21022835,21022836,21022837,21022838,21022839,21022840,21022841,21022842,21022843,21022844,21022845,21022846,21022847,21022848,21022849,21022850,21022851,21022852,21022853,21022854,21022855,21022856,21022857,21022858,21022859,21022860,21022861,21022862,21022863,21022864,21022865,21022866,21022867,21022868,21022869,21022870,21022871,21022872,21022873,21022874,21022875,21022876,21022877,21022878,21022879,21022880,21022881,21022882,21022883,21022884,21022885,21022886,21022887,21022888,21022889,21022890,21022891,21022892,21022893,21022894,21022895,21022896,21022897,21022898,21022899,21022900,21022901,21022902,21022903,21022904,21022905,21022906,21022907,21022908,21022909,21022910,21022911,21022912,21022913,21022914,21022915,21022916,21022917,21022918,21022919,21022920,21022921,21022922,21022923,21022924,21022925,21022926,21022927,21022928,21022929,21022930,21022931,21022932,21022933,21022934,21022935,21022936,21022937,21022938,21022939,21022940,21022941,21022942,21022943,21022944,21022945,21022946,21022947,21022948,21022949,21022950,21022951,21022952,21022953,21022954,21022955,21022956,21022957,21022958,21022959,21022960,21022961,21022962,21022963,21022964,21022965,21022966,21022967,21022968,21022969,21022970,21022971,21022972,21022973,21022974,21022975,21022976,21022977,21022978,21022979,21022980,21022981,21022982,21022983,21022984,21022985,21022986,21022987,21022988,21022989,21022990,21022991,21022992,21022993,21022994,21022995,21022996,21022997,21022998,21022999,21023000,21023001,21023002,21023003,21023004,21023005,21023006,21023007,21023008,21023009,21023010,21023011,21023012,21023013,21023014,21023015,21023016,21023017,21023018,21023019,21023020,21023021,21023022,21023023,21023024,21023025,21023026,21023027,21023028,21023029,21023030,21023031,21023032,21023033,21023034,21023035,21023036,21023037,21023038,21023039,21023040,21023041,21023042,21023043,21023044,21023045,21023046,21023047,21023048,21023049,21023050,21023051,21023052,21023053,21023054,21023055,21023056,21023057,21023058,21023059,21023060,21023061,21023062,21023063,21023064,21023065,21023066,21023067,21023068,21023069,21023070,21023071,21023072,21023073,21023074,21023075,21023076,21023077,21023078,21023079,21023080,21023081,21023082,21023083,21023084,21023085,21023086,21023087,21023088,21023089,21023090,21023091,21023092,21023093,21023094,21023095,21023096,21023097,21023098,21023099,21023100,21023101,21023102,21023103,21023104,21023105,21023106,21023107,21023108,21023109,21023110,21023111,21023112,21023113,21023114,21023115,21023116,21023117,21023118,21023119,21023120,21023121,21023122,21023123,21023124,21023125,21023126,21023127,21023128,21023129,21023130,21023131,21023132,21023133,21023134,21023135,21023136,21023137,21023138,21023139,21023140,21023141,21023142,21023143,21023144,21023145,21023146,21023147,21023148,21023149,21023150,21023151,21023152,21023153,21023154,21023155,21023156,21023157,21023158,21023159,21023160,21023161,21023162,21023163,21023164,21023165,21023166,21023167,21023168,21023169,21023170,21023171,21023172,21023173,21023174,21023175,21023176,21023177,21023178,21023179,21023180,21023181,21023182,21023183,21023184,21023185,21023186,21023187,21023188,21023189,21023190,21023191,21023192,21023193,21023194,21023195,21023196,21023197,21023198,21023199,21023200,21023201,21023202,21023203,21023204,21023205,21023206,21023207,21023208,21023209,21023210,21023211,21023212,21023213,21023214,21023215,21023216,21023217,21023218,21023219,21023220,21023221,21023222,21023223,21023224,21023225,21023226,21023227,21023228,21023229,21023230,21023231,21023232,21023233,21023234,21023235,21023236,21023237,21023238,21023239,21023240,21023241,21023242,21023243,21023244,21023245,21023246,21023247,21023248,21023249,21023250,21023251,21023252,21023253,21023254,21023255,21023256,21023257,21023258,21023259,21023260,21023261,21023262,21023263,21023264,21023265,21023266,21023267,21023268,21023269,21023270,21023271,21023272,21023273,21023274,21023275,21023276,21023277,21023278,21023279,21023280,21023281,21023282,21023283,21023284,21023285,21023286,21023287,21023288,21023289,21023290,21023291,21023292,21023293,21023294,21023295,21023296,21023297,21023298,21023299,21023300,21023301,21023302,21023303,21023304,21023305,21023306,21023307,21023308,21023309,21023310,21023311,21023312,21023313,21023314,21023315,21023316,21023317,21023318,21023319,21023320,21023321,21023322,21023323,21023324,21023325,21023326,21023327,21023328,21023329,21023330,21023331,21023332,21023333,21023334,21023335,21023336,21023337,21023338,21023339,21023340,21023341,21023342,21023343,21023344,21023345,21023346,21023347,21023348,21023349,21023350,21023351,21023352,21023353,21023354,21023355,21023356,21023357,21023358,21023359,21023360,21023361,21023362,21023363,21023364,21023365,21023366,21023367,21023368,21023369,21023370,21023371,21023372,21023373,21023374,21023375,21023376,21023377,21023378,21023379,21023380,21023381,21023382,21023383,21023384,21023385,21023386,21023387,21023388,21023389,21023390,21023391,21023392,21023393,21023394,21023395,21023396,21023397,21023398,21023399,21023400,21023401,21023402,21023403,21023404,21023405,21023406,21023407,21023408,21023409,21023410,21023411,21023412,21023413,21023414,21023415,21023416,21023417,21023418,21023419,21023420,21023421,21023422,21023423,21023424,21023425,21023426,21023427,21023428,21023429,21023430,21023431,21023432,21023433,21023434,21023435,21023436,21023437,21023438,21023439,21023440,21023441,21023442,21023443,21023444,21023445,21023446,21023447,21023448,21023449,21023450,21023451,21023452,21023453,21023454,21023455,21023456,21023457,21023458,21023459,21023460,21023461,21023462,21023463,21023464,21023465,21023466,21023467,21023468,21023469,21023470,21023471,21023472,21023473,21023474,21023475,21023476,21023477,21023478,21023479,21023480,21023481,21023482,21023483,21023484,21023485,21023486,21023487,21023488,21023489,21023490,21023491,21023492,21023493,21023494,21023495,21023496,21023497,21023498,21023499,21023500,21023501,21023502,21023503,21023504,21023505,21023506,21023507,21023508,21023509,21023510,21023511,21023512,21023513,21023514,21023515,21023516,21023517,21023518,21023519,21023520,21023521,21023522,21023523,21023524,21023525,21023526,21023527,21023528,21023529,21023530,21023531,21023532,21023533,21023534,21023535,21023536,21023537,21023538,21023539,21023540,21023541,21023542,21023543,21023544,21023545,21023546,21023547,21023548,21023549,21023550,21023551,21023552,21023553,21023554,21023555,21023556,21023557,21023558,21023559,21023560,21023561,21023562,21023563,21023564,21023565,21023566,21023567,21023568,21023569,21023570,21023571,21023572,21023573,21023574,21023575,21023576,21023577,21023578,21023579,21023580,21023581,21023582,21023583,21023584,21023585,21023586,21023587,21023588,21023589,21023590,21023591,21023592,21023593,21023594,21023595,21023596,21023597,21023598,21023599,21023600,21023601,21023602,21023603,21023604,21023605,21023606,21023607,21023608,21023609,21023610,21023611,21023612,21023613,21023614,21023615,21023616,21023617,21023618,21023619,21023620,21023621,21023622,21023623,21023624,21023625,21023626,21023627,21023628,21023629,21023630,21023631,21023632,21023633,21023634,21023635,21023636,21023637,21023638,21023639,21023640,21023641,21023642,21023643,21023644,21023645,21023646,21023647,21023648,21023649,21023650,21023651,21023652,21023653,21023654,21023655,21023656,21023657,21023658,21023659,21023660,21023661,21023662,21023663,21023664,21023665,21023666,21023667,21023668,21023669,21023670,21023671,21023672,21023673,21023674,21023675,21023676,21023677,21023678,21023679,21023680,21023681,21023682,21023683,21023684,21023685,21023686,21023687,21023688,21023689,21023690,21023691,21023692,21023693,21023694,21023695,21023696,21023697,21023698,21023699,21023700,21023701,21023702,21023703,21023704,21023705,21023706,21023707,21023708,21023709,21023710,21023711,21023712,21023713,21023714,21023715,21023716,21023717,21023718,21023719,21023720,21023721,21023722,21023723,21023724,21023725,21023726,21023727,21023728,21023729,21023730,21023731,21023732,21023733,21023734,21023735,21023736,21023737,21023738,21023739,21023740,21023741,21023742,21023743,21024256,21024257,21024258,21024259,21024260,21024261,21024262,21024263,21024264,21024265,21024266,21024267,21024268,21024269,21024270,21024271,21024272,21024273,21024274,21024275,21024276,21024277,21024278,21024279,21024280,21024281,21024282,21024283,21024284,21024285,21024286,21024287,21024288,21024289,21024290,21024291,21024292,21024293,21024294,21024295,21024296,21024297,21024298,21024299,21024300,21024301,21024302,21024303,21024304,21024305,21024306,21024307,21024308,21024309,21024310,21024311,21024312,21024313,21024314,21024315,21024316,21024317,21024318,21024319,21024320,21024321,21024322,21024323,21024324,21024325,21024326,21024327,21024328,21024329,21024330,21024331,21024332,21024333,21024334,21024335,21024336,21024337,21024338,21024339,21024340,21024341,21024342,21024343,21024344,21024345,21024346,21024347,21024348,21024349,21024350,21024351,21024352,21024353,21024354,21024355,21024356,21024357,21024358,21024359,21024360,21024361,21024362,21024363,21024364,21024365,21024366,21024367,21024368,21024369,21024370,21024371,21024372,21024373,21024374,21024375,21024376,21024377,21024378,21024379,21024380,21024381,21024382,21024383,21024384,21024385,21024386,21024387,21024388,21024389,21024390,21024391,21024392,21024393,21024394,21024395,21024396,21024397,21024398,21024399,21024400,21024401,21024402,21024403,21024404,21024405,21024406,21024407,21024408,21024409,21024410,21024411,21024412,21024413,21024414,21024415,21024416,21024417,21024418,21024419,21024420,21024421,21024422,21024423,21024424,21024425,21024426,21024427,21024428,21024429,21024430,21024431,21024432,21024433,21024434,21024435,21024436,21024437,21024438,21024439,21024440,21024441,21024442,21024443,21024444,21024445,21024446,21024447,21024448,21024449,21024450,21024451,21024452,21024453,21024454,21024455,21024456,21024457,21024458,21024459,21024460,21024461,21024462,21024463,21024464,21024465,21024466,21024467,21024468,21024469,21024470,21024471,21024472,21024473,21024474,21024475,21024476,21024477,21024478,21024479,21024480,21024481,21024482,21024483,21024484,21024485,21024486,21024487,21024488,21024489,21024490,21024491,21024492,21024493,21024494,21024495,21024496,21024497,21024498,21024499,21024500,21024501,21024502,21024503,21024504,21024505,21024506,21024507,21024508,21024509,21024510,21024511,21024640,21024641,21024642,21024643,21024644,21024645,21024646,21024647,21024648,21024649,21024650,21024651,21024652,21024653,21024654,21024655,21024664,21024665,21024666,21024667,21024668,21024669,21024670,21024671],"Fermium":[20821256],"Fern":[21026077],"Fire Block":[21027184,21027185,21027186,21027187,21027188,21027189,21027190,21027191,21027192,21027193,21027194,21027195,21027196,21027197,21027198,21027199],"Flerovium":[20823370],"Fletching Table":[21030063],"Flower Pot":[21031217],"Flowering Azalea Leaves":[21888168,21888169,21888170,21888171],"Fluorine":[20824372],"Francium":[20827305],"Froglight":[21872001,21872002,21872003,21872005,21872006,21872007,21872009,21872010,21872011],"Frosted Ice":[21034052,21034053,21034054,21034055],"Furnace":[21036512,21036513,21036514,21036515,21036516,21036517,21036518,21036519],"Gadolinium":[20828273],"Gallium":[20832169],"Germanium":[20833509],"Gilded Blackstone":[21820646],"Glass":[21038329],"Glass Pane":[21040585],"Glazed Terracotta":[21585152,21585153,21585154,21585155,21585156,21585157,21585158,21585159,21585160,21585161,21585162,21585163,21585164,21585165,21585166,21585167,21585168,21585169,21585170,21585171,21585172,21585173,21585174,21585175,21585176,21585177,21585178,21585179,21585180,21585181,21585182,21585183,21585184,21585185,21585186,21585187,21585188,21585189,21585190,21585191,21585192,21585193,21585194,21585195,21585196,21585197,21585198,21585199,21585200,21585201,21585202,21585203,21585204,21585205,21585206,21585207,21585208,21585209,21585210,21585211,21585212,21585213,21585214,21585215],"Glow Item Frame":[21881552,21881553,21881556,21881557,21881558,21881559,21881560,21881561,21881564,21881565,21881566,21881567],"Glow Lichen":[21893760,21893761,21893762,21893763,21893764,21893765,21893766,21893767,21893768,21893769,21893770,21893771,21893772,21893773,21893774,21893775,21893776,21893777,21893778,21893779,21893780,21893781,21893782,21893783,21893784,21893785,21893786,21893787,21893788,21893789,21893790,21893791,21893792,21893793,21893794,21893795,21893796,21893797,21893798,21893799,21893800,21893801,21893802,21893803,21893804,21893805,21893806,21893807,21893808,21893809,21893810,21893811,21893812,21893813,21893814,21893815,21893816,21893817,21893818,21893819,21893820,21893821,21893822,21893823],"Glowing Obsidian":[21043028],"Glowstone":[21043624],"Gold":[20835704],"Gold Block":[21046696],"Gold Ore":[21049010],"Granite":[21049750],"Granite Slab":[21053256,21053257,21053258],"Granite Stairs":[21054536,21054537,21054538,21054539,21054540,21054541,21054542,21054543],"Granite Wall":[21055488,21055489,21055490,21055491,21055492,21055493,21055494,21055495,21055496,21055497,21055498,21055499,21055500,21055501,21055502,21055503,21055504,21055505,21055506,21055507,21055508,21055509,21055510,21055511,21055512,21055513,21055514,21055515,21055516,21055517,21055518,21055519,21055520,21055521,21055522,21055523,21055524,21055525,21055526,21055527,21055528,21055529,21055530,21055531,21055532,21055533,21055534,21055535,21055536,21055537,21055538,21055539,21055540,21055541,21055542,21055543,21055544,21055545,21055546,21055547,21055548,21055549,21055550,21055551,21055584,21055585,21055586,21055587,21055588,21055589,21055590,21055591,21055592,21055593,21055594,21055595,21055596,21055597,21055598,21055599,21055615,21055616,21055617,21055618,21055619,21055620,21055621,21055622,21055623,21055624,21055625,21055626,21055627,21055628,21055629,21055630,21055631,21055632,21055633,21055634,21055635,21055636,21055637,21055638,21055639,21055640,21055641,21055642,21055643,21055644,21055645,21055646,21055647,21055648,21055649,21055650,21055651,21055652,21055653,21055654,21055655,21055656,21055657,21055658,21055659,21055660,21055661,21055662,21055663,21055664,21055665,21055666,21055667,21055668,21055669,21055670,21055671,21055672,21055673,21055674,21055675,21055676,21055677,21055678,21055679,21055712,21055713,21055714,21055715,21055716,21055717,21055718,21055719,21055720,21055721,21055722,21055723,21055724,21055725,21055726,21055727,21055743],"Grass":[21058118],"Grass Path":[21061327],"Gravel":[21062456],"Green Torch":[21068985,21068986,21068987,21068988,21068989],"Hafnium":[20837560],"Hanging Roots":[21842426],"Hardened Clay":[21071190],"Hardened Glass":[21072075],"Hardened Glass Pane":[21075766],"Hassium":[20839994],"Hay Bale":[21077748,21077749,21077750],"Heat Block":[20625734],"Helium":[20840704],"Holmium":[20842739],"Honeycomb Block":[21782395],"Hopper":[21079520,21079521,21079525,21079526,21079527,21079528,21079529,21079533,21079534,21079535],"Hydrogen":[20844910],"Ice":[21080304],"Indium":[20848311],"Infested Chiseled Stone Brick":[21083943],"Infested Cobblestone":[21085393],"Infested Cracked Stone Brick":[21087627],"Infested Mossy Stone Brick":[21088835],"Infested Stone":[21091396],"Infested Stone Brick":[21092358],"Invisible Bedrock":[21100447],"Iodine":[20850310],"Iridium":[20851638],"Iron":[20854572],"Iron Bars":[21104146],"Iron Block":[21101750],"Iron Door":[21105792,21105793,21105794,21105795,21105796,21105797,21105798,21105799,21105800,21105801,21105802,21105803,21105804,21105805,21105806,21105807,21105808,21105809,21105810,21105811,21105812,21105813,21105814,21105815,21105816,21105817,21105818,21105819,21105820,21105821,21105822,21105823],"Iron Ore":[21107313],"Iron Trapdoor":[21109264,21109265,21109266,21109267,21109268,21109269,21109270,21109271,21109272,21109273,21109274,21109275,21109276,21109277,21109278,21109279],"Item Frame":[21111952,21111953,21111954,21111955,21111956,21111957,21111960,21111961,21111962,21111963,21111964,21111965],"Jack o'Lantern":[21179952,21179953,21179954,21179955],"Jukebox":[21113057],"Jungle Button":[21115488,21115489,21115490,21115491,21115492,21115493,21115496,21115497,21115498,21115499,21115500,21115501],"Jungle Door":[21117568,21117569,21117570,21117571,21117572,21117573,21117574,21117575,21117576,21117577,21117578,21117579,21117580,21117581,21117582,21117583,21117584,21117585,21117586,21117587,21117588,21117589,21117590,21117591,21117592,21117593,21117594,21117595,21117596,21117597,21117598,21117599],"Jungle Fence":[21119582],"Jungle Fence Gate":[21122144,21122145,21122146,21122147,21122148,21122149,21122150,21122151,21122152,21122153,21122154,21122155,21122156,21122157,21122158,21122159],"Jungle Leaves":[21124916,21124917,21124918,21124919],"Jungle Log":[21126656,21126657,21126658,21126659,21126662,21126663],"Jungle Planks":[21127877],"Jungle Pressure Plate":[21130628,21130629],"Jungle Sapling":[21132062,21132063],"Jungle Sign":[21134400,21134401,21134402,21134403,21134404,21134405,21134406,21134407,21134408,21134409,21134410,21134411,21134412,21134413,21134414,21134415],"Jungle Slab":[21135720,21135721,21135722],"Jungle Stairs":[21137656,21137657,21137658,21137659,21137660,21137661,21137662,21137663],"Jungle Trapdoor":[21139728,21139729,21139730,21139731,21139732,21139733,21139734,21139735,21139736,21139737,21139738,21139739,21139740,21139741,21139742,21139743],"Jungle Wall Sign":[21143348,21143349,21143350,21143351],"Jungle Wood":[21145480,21145481,21145484,21145485,21145486,21145487],"Krypton":[20856167],"Lab Table":[21146608,21146609,21146610,21146611],"Ladder":[21148984,21148985,21148986,21148987],"Lantern":[21149740,21149741],"Lanthanum":[20858521],"Lapis Lazuli Block":[21152040],"Lapis Lazuli Ore":[21154437],"Large Fern":[21156644,21156645],"Lava":[21159648,21159649,21159650,21159651,21159652,21159653,21159654,21159655,21159656,21159657,21159658,21159659,21159660,21159661,21159662,21159663,21159664,21159665,21159666,21159667,21159668,21159669,21159670,21159671,21159672,21159673,21159674,21159675,21159676,21159677,21159678,21159679],"Lava Cauldron":[21857912,21857913,21857914,21857915,21857916,21857917],"Lawrencium":[20860323],"Lead":[20861284],"Lectern":[21160360,21160361,21160362,21160363,21160364,21160365,21160366,21160367],"Legacy Stonecutter":[21162264],"Lever":[21165776,21165777,21165778,21165779,21165780,21165781,21165782,21165783,21165784,21165785,21165786,21165787,21165788,21165789,21165790,21165791],"Light Block":[21629408,21629409,21629410,21629411,21629412,21629413,21629414,21629415,21629416,21629417,21629418,21629419,21629420,21629421,21629422,21629423],"Lightning Rod":[21821570,21821571,21821572,21821573,21821574,21821575],"Lilac":[21171834,21171835],"Lily Pad":[21174431],"Lily of the Valley":[21173216],"Lithium":[20863911],"Livermorium":[20865878],"Loom":[21182044,21182045,21182046,21182047],"Lutetium":[20867537],"Magma Block":[21186488],"Magnesium":[20869155],"Manganese":[20872294],"Mangrove Button":[21735552,21735553,21735554,21735555,21735558,21735559,21735560,21735561,21735562,21735563,21735566,21735567],"Mangrove Door":[21749248,21749249,21749250,21749251,21749252,21749253,21749254,21749255,21749256,21749257,21749258,21749259,21749260,21749261,21749262,21749263,21749264,21749265,21749266,21749267,21749268,21749269,21749270,21749271,21749272,21749273,21749274,21749275,21749276,21749277,21749278,21749279],"Mangrove Fence":[21706659],"Mangrove Fence Gate":[21755424,21755425,21755426,21755427,21755428,21755429,21755430,21755431,21755432,21755433,21755434,21755435,21755436,21755437,21755438,21755439],"Mangrove Leaves":[21884612,21884613,21884614,21884615],"Mangrove Log":[21717272,21717273,21717274,21717275,21717278,21717279],"Mangrove Planks":[21700363],"Mangrove Pressure Plate":[21742138,21742139],"Mangrove Roots":[21868046],"Mangrove Sign":[21766688,21766689,21766690,21766691,21766692,21766693,21766694,21766695,21766696,21766697,21766698,21766699,21766700,21766701,21766702,21766703],"Mangrove Slab":[21711233,21711234,21711235],"Mangrove Stairs":[21760624,21760625,21760626,21760627,21760628,21760629,21760630,21760631],"Mangrove Trapdoor":[21729584,21729585,21729586,21729587,21729588,21729589,21729590,21729591,21729592,21729593,21729594,21729595,21729596,21729597,21729598,21729599],"Mangrove Wall Sign":[21774240,21774241,21774242,21774243],"Mangrove Wood":[21723914,21723915,21723916,21723917,21723918,21723919],"Material Reducer":[21187744,21187745,21187746,21187747],"Meitnerium":[20874589],"Melon Block":[21189795],"Melon Stem":[21190912,21190913,21190914,21190915,21190916,21190917,21190918,21190919,21190920,21190921,21190922,21190923,21190924,21190925,21190926,21190927,21190944,21190945,21190946,21190947,21190948,21190949,21190950,21190951,21190960,21190961,21190962,21190963,21190964,21190965,21190966,21190967,21190968,21190969,21190970,21190971,21190972,21190973,21190974,21190975],"Mendelevium":[20876625],"Mercury":[20877937],"Mob Head":[21193344,21193345,21193346,21193347,21193349,21193350,21193351,21193352,21193353,21193354,21193355,21193357,21193358,21193359,21193376,21193377,21193378,21193379,21193381,21193382,21193383,21193392,21193393,21193394,21193395,21193397,21193398,21193399,21193400,21193401,21193402,21193403,21193405,21193406,21193407],"Molybdenum":[20879537],"Monster Spawner":[21195322],"Moscovium":[20883402],"Mossy Cobblestone":[21197453],"Mossy Cobblestone Slab":[21200052,21200054,21200055],"Mossy Cobblestone Stairs":[21202568,21202569,21202570,21202571,21202572,21202573,21202574,21202575],"Mossy Cobblestone Wall":[21204224,21204225,21204226,21204227,21204228,21204229,21204230,21204231,21204232,21204233,21204234,21204235,21204236,21204237,21204238,21204239,21204240,21204241,21204242,21204243,21204244,21204245,21204246,21204247,21204248,21204249,21204250,21204251,21204252,21204253,21204254,21204255,21204256,21204257,21204258,21204259,21204260,21204261,21204262,21204263,21204264,21204265,21204266,21204267,21204268,21204269,21204270,21204271,21204272,21204273,21204274,21204275,21204276,21204277,21204278,21204279,21204280,21204281,21204282,21204283,21204284,21204285,21204286,21204287,21204288,21204289,21204290,21204291,21204292,21204293,21204294,21204295,21204296,21204297,21204298,21204299,21204300,21204301,21204302,21204303,21204306,21204352,21204353,21204354,21204355,21204356,21204357,21204358,21204359,21204360,21204361,21204362,21204363,21204364,21204365,21204366,21204367,21204368,21204369,21204370,21204371,21204372,21204373,21204374,21204375,21204376,21204377,21204378,21204379,21204380,21204381,21204382,21204383,21204384,21204385,21204386,21204387,21204388,21204389,21204390,21204391,21204392,21204393,21204394,21204395,21204396,21204397,21204398,21204399,21204400,21204401,21204402,21204403,21204404,21204405,21204406,21204407,21204408,21204409,21204410,21204411,21204412,21204413,21204414,21204415,21204416,21204417,21204418,21204419,21204420,21204421,21204422,21204423,21204424,21204425,21204426,21204427,21204428,21204429,21204430,21204431,21204434],"Mossy Stone Brick Slab":[21205992,21205993,21205995],"Mossy Stone Brick Stairs":[21207584,21207585,21207586,21207587,21207588,21207589,21207590,21207591],"Mossy Stone Brick Wall":[21210147,21210160,21210161,21210162,21210163,21210164,21210165,21210166,21210167,21210168,21210169,21210170,21210171,21210172,21210173,21210174,21210175,21210176,21210177,21210178,21210179,21210180,21210181,21210182,21210183,21210184,21210185,21210186,21210187,21210188,21210189,21210190,21210191,21210192,21210193,21210194,21210195,21210196,21210197,21210198,21210199,21210200,21210201,21210202,21210203,21210204,21210205,21210206,21210207,21210208,21210209,21210210,21210211,21210212,21210213,21210214,21210215,21210216,21210217,21210218,21210219,21210220,21210221,21210222,21210223,21210224,21210225,21210226,21210227,21210228,21210229,21210230,21210231,21210232,21210233,21210234,21210235,21210236,21210237,21210238,21210239,21210275,21210288,21210289,21210290,21210291,21210292,21210293,21210294,21210295,21210296,21210297,21210298,21210299,21210300,21210301,21210302,21210303,21210304,21210305,21210306,21210307,21210308,21210309,21210310,21210311,21210312,21210313,21210314,21210315,21210316,21210317,21210318,21210319,21210320,21210321,21210322,21210323,21210324,21210325,21210326,21210327,21210328,21210329,21210330,21210331,21210332,21210333,21210334,21210335,21210336,21210337,21210338,21210339,21210340,21210341,21210342,21210343,21210344,21210345,21210346,21210347,21210348,21210349,21210350,21210351,21210352,21210353,21210354,21210355,21210356,21210357,21210358,21210359,21210360,21210361,21210362,21210363,21210364,21210365,21210366,21210367],"Mossy Stone Bricks":[21212641],"Mud":[21804596],"Mud Brick Slab":[21807848,21807850,21807851],"Mud Brick Stairs":[21810184,21810185,21810186,21810187,21810188,21810189,21810190,21810191],"Mud Brick Wall":[21812238,21812240,21812241,21812242,21812243,21812244,21812245,21812246,21812247,21812248,21812249,21812250,21812251,21812252,21812253,21812254,21812255,21812288,21812289,21812290,21812291,21812292,21812293,21812294,21812295,21812296,21812297,21812298,21812299,21812300,21812301,21812302,21812303,21812304,21812305,21812306,21812307,21812308,21812309,21812310,21812311,21812312,21812313,21812314,21812315,21812316,21812317,21812318,21812319,21812320,21812321,21812322,21812323,21812324,21812325,21812326,21812327,21812328,21812329,21812330,21812331,21812332,21812333,21812334,21812335,21812336,21812337,21812338,21812339,21812340,21812341,21812342,21812343,21812344,21812345,21812346,21812347,21812348,21812349,21812350,21812351,21812366,21812368,21812369,21812370,21812371,21812372,21812373,21812374,21812375,21812376,21812377,21812378,21812379,21812380,21812381,21812382,21812383,21812416,21812417,21812418,21812419,21812420,21812421,21812422,21812423,21812424,21812425,21812426,21812427,21812428,21812429,21812430,21812431,21812432,21812433,21812434,21812435,21812436,21812437,21812438,21812439,21812440,21812441,21812442,21812443,21812444,21812445,21812446,21812447,21812448,21812449,21812450,21812451,21812452,21812453,21812454,21812455,21812456,21812457,21812458,21812459,21812460,21812461,21812462,21812463,21812464,21812465,21812466,21812467,21812468,21812469,21812470,21812471,21812472,21812473,21812474,21812475,21812476,21812477,21812478,21812479],"Mud Bricks":[21805580],"Muddy Mangrove Roots":[21870173,21870174,21870175],"Mushroom Stem":[21214847],"Mycelium":[21215507],"Neodymium":[20883794],"Neon":[20887178],"Neptunium":[20888366],"Nether Brick Fence":[21219143],"Nether Brick Slab":[21219584,21219585,21219587],"Nether Brick Stairs":[21222456,21222457,21222458,21222459,21222460,21222461,21222462,21222463],"Nether Brick Wall":[21223712,21223713,21223714,21223715,21223716,21223717,21223718,21223719,21223720,21223721,21223722,21223723,21223724,21223725,21223726,21223727,21223728,21223744,21223745,21223746,21223747,21223748,21223749,21223750,21223751,21223752,21223753,21223754,21223755,21223756,21223757,21223758,21223759,21223760,21223761,21223762,21223763,21223764,21223765,21223766,21223767,21223768,21223769,21223770,21223771,21223772,21223773,21223774,21223775,21223776,21223777,21223778,21223779,21223780,21223781,21223782,21223783,21223784,21223785,21223786,21223787,21223788,21223789,21223790,21223791,21223792,21223793,21223794,21223795,21223796,21223797,21223798,21223799,21223800,21223801,21223802,21223803,21223804,21223805,21223806,21223807,21223840,21223841,21223842,21223843,21223844,21223845,21223846,21223847,21223848,21223849,21223850,21223851,21223852,21223853,21223854,21223855,21223856,21223872,21223873,21223874,21223875,21223876,21223877,21223878,21223879,21223880,21223881,21223882,21223883,21223884,21223885,21223886,21223887,21223888,21223889,21223890,21223891,21223892,21223893,21223894,21223895,21223896,21223897,21223898,21223899,21223900,21223901,21223902,21223903,21223904,21223905,21223906,21223907,21223908,21223909,21223910,21223911,21223912,21223913,21223914,21223915,21223916,21223917,21223918,21223919,21223920,21223921,21223922,21223923,21223924,21223925,21223926,21223927,21223928,21223929,21223930,21223931,21223932,21223933,21223934,21223935],"Nether Bricks":[21225672],"Nether Gold Ore":[21801107],"Nether Portal":[21229304,21229305],"Nether Quartz Ore":[21230807],"Nether Reactor Core":[21233112],"Nether Wart":[21235588,21235589,21235590,21235591],"Nether Wart Block":[21237079],"Netherite Block":[21848862],"Netherrack":[21239734],"Nickel":[20891060],"Nihonium":[20892811],"Niobium":[20895630],"Nitrogen":[20897160],"Nobelium":[20898460],"Note Block":[21241302],"Oak Button":[21243152,21243153,21243156,21243157,21243158,21243159,21243160,21243161,21243164,21243165,21243166,21243167],"Oak Door":[21245696,21245697,21245698,21245699,21245700,21245701,21245702,21245703,21245704,21245705,21245706,21245707,21245708,21245709,21245710,21245711,21245712,21245713,21245714,21245715,21245716,21245717,21245718,21245719,21245720,21245721,21245722,21245723,21245724,21245725,21245726,21245727],"Oak Fence":[21247506],"Oak Fence Gate":[21248560,21248561,21248562,21248563,21248564,21248565,21248566,21248567,21248568,21248569,21248570,21248571,21248572,21248573,21248574,21248575],"Oak Leaves":[21250248,21250249,21250250,21250251],"Oak Log":[21253352,21253353,21253356,21253357,21253358,21253359],"Oak Planks":[21255504],"Oak Pressure Plate":[21257680,21257681],"Oak Sapling":[21258462,21258463],"Oak Sign":[21262208,21262209,21262210,21262211,21262212,21262213,21262214,21262215,21262216,21262217,21262218,21262219,21262220,21262221,21262222,21262223],"Oak Slab":[21264373,21264374,21264375],"Oak Stairs":[21264984,21264985,21264986,21264987,21264988,21264989,21264990,21264991],"Oak Trapdoor":[21267760,21267761,21267762,21267763,21267764,21267765,21267766,21267767,21267768,21267769,21267770,21267771,21267772,21267773,21267774,21267775],"Oak Wall Sign":[21270344,21270345,21270346,21270347],"Oak Wood":[21272410,21272411,21272412,21272413,21272414,21272415],"Obsidian":[21273247],"Oganesson":[20901372],"Orange Tulip":[21278363],"Osmium":[20902298],"Oxeye Daisy":[21280579],"Oxygen":[20904205],"Packed Ice":[21282488],"Packed Mud":[21814973],"Palladium":[20907027],"Peony":[21283348,21283349],"Phosphorus":[20908773],"Pink Petals":[21933456,21933457,21933458,21933459,21933460,21933461,21933462,21933463,21933464,21933465,21933466,21933467,21933468,21933469,21933470,21933471],"Pink Tulip":[21286997],"Pitcher Crop":[21947997,21947998,21947999],"Pitcher Plant":[21945642,21945643],"Platinum":[20911235],"Plutonium":[20912900],"Podzol":[21290197],"Polished Andesite":[21292531],"Polished Andesite Slab":[21294924,21294925,21294927],"Polished Andesite Stairs":[21295920,21295921,21295922,21295923,21295924,21295925,21295926,21295927],"Polished Basalt":[21592160,21592162,21592163],"Polished Blackstone":[21605632],"Polished Blackstone Brick Slab":[21622088,21622089,21622090],"Polished Blackstone Brick Stairs":[21622992,21622993,21622994,21622995,21622996,21622997,21622998,21622999],"Polished Blackstone Brick Wall":[21624832,21624833,21624834,21624835,21624836,21624837,21624838,21624839,21624840,21624841,21624842,21624843,21624844,21624845,21624846,21624847,21624848,21624849,21624850,21624851,21624852,21624853,21624854,21624855,21624856,21624857,21624858,21624859,21624860,21624861,21624862,21624863,21624864,21624865,21624866,21624867,21624868,21624869,21624870,21624871,21624872,21624873,21624874,21624875,21624876,21624877,21624878,21624879,21624880,21624881,21624882,21624883,21624884,21624885,21624886,21624887,21624888,21624889,21624890,21624891,21624892,21624893,21624894,21624895,21624896,21624912,21624913,21624914,21624915,21624916,21624917,21624918,21624919,21624920,21624921,21624922,21624923,21624924,21624925,21624926,21624927,21624960,21624961,21624962,21624963,21624964,21624965,21624966,21624967,21624968,21624969,21624970,21624971,21624972,21624973,21624974,21624975,21624976,21624977,21624978,21624979,21624980,21624981,21624982,21624983,21624984,21624985,21624986,21624987,21624988,21624989,21624990,21624991,21624992,21624993,21624994,21624995,21624996,21624997,21624998,21624999,21625000,21625001,21625002,21625003,21625004,21625005,21625006,21625007,21625008,21625009,21625010,21625011,21625012,21625013,21625014,21625015,21625016,21625017,21625018,21625019,21625020,21625021,21625022,21625023,21625024,21625040,21625041,21625042,21625043,21625044,21625045,21625046,21625047,21625048,21625049,21625050,21625051,21625052,21625053,21625054,21625055],"Polished Blackstone Bricks":[21619830],"Polished Blackstone Button":[21607152,21607153,21607154,21607155,21607156,21607157,21607160,21607161,21607162,21607163,21607164,21607165],"Polished Blackstone Pressure Plate":[21609892,21609893],"Polished Blackstone Slab":[21611892,21611893,21611894],"Polished Blackstone Stairs":[21614000,21614001,21614002,21614003,21614004,21614005,21614006,21614007],"Polished Blackstone Wall":[21614848,21614849,21614850,21614851,21614852,21614853,21614854,21614855,21614856,21614857,21614858,21614859,21614860,21614861,21614862,21614863,21614864,21614865,21614866,21614867,21614868,21614869,21614870,21614871,21614872,21614873,21614874,21614875,21614876,21614877,21614878,21614879,21614880,21614881,21614882,21614883,21614884,21614885,21614886,21614887,21614888,21614889,21614890,21614891,21614892,21614893,21614894,21614895,21614896,21614897,21614898,21614899,21614900,21614901,21614902,21614903,21614904,21614905,21614906,21614907,21614908,21614909,21614910,21614911,21614917,21614928,21614929,21614930,21614931,21614932,21614933,21614934,21614935,21614936,21614937,21614938,21614939,21614940,21614941,21614942,21614943,21614976,21614977,21614978,21614979,21614980,21614981,21614982,21614983,21614984,21614985,21614986,21614987,21614988,21614989,21614990,21614991,21614992,21614993,21614994,21614995,21614996,21614997,21614998,21614999,21615000,21615001,21615002,21615003,21615004,21615005,21615006,21615007,21615008,21615009,21615010,21615011,21615012,21615013,21615014,21615015,21615016,21615017,21615018,21615019,21615020,21615021,21615022,21615023,21615024,21615025,21615026,21615027,21615028,21615029,21615030,21615031,21615032,21615033,21615034,21615035,21615036,21615037,21615038,21615039,21615045,21615056,21615057,21615058,21615059,21615060,21615061,21615062,21615063,21615064,21615065,21615066,21615067,21615068,21615069,21615070,21615071],"Polished Deepslate":[21670363],"Polished Deepslate Slab":[21672268,21672270,21672271],"Polished Deepslate Stairs":[21674288,21674289,21674290,21674291,21674292,21674293,21674294,21674295],"Polished Deepslate Wall":[21676076,21676080,21676081,21676082,21676083,21676084,21676085,21676086,21676087,21676088,21676089,21676090,21676091,21676092,21676093,21676094,21676095,21676096,21676097,21676098,21676099,21676100,21676101,21676102,21676103,21676104,21676105,21676106,21676107,21676108,21676109,21676110,21676111,21676112,21676113,21676114,21676115,21676116,21676117,21676118,21676119,21676120,21676121,21676122,21676123,21676124,21676125,21676126,21676127,21676128,21676129,21676130,21676131,21676132,21676133,21676134,21676135,21676136,21676137,21676138,21676139,21676140,21676141,21676142,21676143,21676144,21676145,21676146,21676147,21676148,21676149,21676150,21676151,21676152,21676153,21676154,21676155,21676156,21676157,21676158,21676159,21676204,21676208,21676209,21676210,21676211,21676212,21676213,21676214,21676215,21676216,21676217,21676218,21676219,21676220,21676221,21676222,21676223,21676224,21676225,21676226,21676227,21676228,21676229,21676230,21676231,21676232,21676233,21676234,21676235,21676236,21676237,21676238,21676239,21676240,21676241,21676242,21676243,21676244,21676245,21676246,21676247,21676248,21676249,21676250,21676251,21676252,21676253,21676254,21676255,21676256,21676257,21676258,21676259,21676260,21676261,21676262,21676263,21676264,21676265,21676266,21676267,21676268,21676269,21676270,21676271,21676272,21676273,21676274,21676275,21676276,21676277,21676278,21676279,21676280,21676281,21676282,21676283,21676284,21676285,21676286,21676287],"Polished Diorite":[21298589],"Polished Diorite Slab":[21299568,21299570,21299571],"Polished Diorite Stairs":[21301328,21301329,21301330,21301331,21301332,21301333,21301334,21301335],"Polished Granite":[21304978],"Polished Granite Slab":[21307024,21307025,21307027],"Polished Granite Stairs":[21307536,21307537,21307538,21307539,21307540,21307541,21307542,21307543],"Polonium":[20915922],"Poppy":[21310628],"Potassium":[20916939],"Potato Block":[21312968,21312969,21312970,21312971,21312972,21312973,21312974,21312975],"Potion Cauldron":[21859216,21859217,21859218,21859219,21859220,21859221],"Powered Rail":[21313936,21313937,21313938,21313939,21313942,21313943,21313944,21313945,21313946,21313947,21313950,21313951],"Praseodymium":[20918290],"Prismarine":[21317514],"Prismarine Bricks":[21318880],"Prismarine Bricks Slab":[21319764,21319766,21319767],"Prismarine Bricks Stairs":[21322480,21322481,21322482,21322483,21322484,21322485,21322486,21322487],"Prismarine Slab":[21324568,21324569,21324571],"Prismarine Stairs":[21327832,21327833,21327834,21327835,21327836,21327837,21327838,21327839],"Prismarine Wall":[21327872,21327873,21327874,21327875,21327876,21327877,21327878,21327879,21327880,21327881,21327882,21327883,21327884,21327885,21327886,21327887,21327888,21327889,21327890,21327891,21327892,21327893,21327894,21327895,21327896,21327897,21327898,21327899,21327900,21327901,21327902,21327903,21327904,21327905,21327906,21327907,21327908,21327909,21327910,21327911,21327912,21327913,21327914,21327915,21327916,21327917,21327918,21327919,21327920,21327921,21327922,21327923,21327924,21327925,21327926,21327927,21327928,21327929,21327930,21327931,21327932,21327933,21327934,21327935,21327974,21327984,21327985,21327986,21327987,21327988,21327989,21327990,21327991,21327992,21327993,21327994,21327995,21327996,21327997,21327998,21327999,21328000,21328001,21328002,21328003,21328004,21328005,21328006,21328007,21328008,21328009,21328010,21328011,21328012,21328013,21328014,21328015,21328016,21328017,21328018,21328019,21328020,21328021,21328022,21328023,21328024,21328025,21328026,21328027,21328028,21328029,21328030,21328031,21328032,21328033,21328034,21328035,21328036,21328037,21328038,21328039,21328040,21328041,21328042,21328043,21328044,21328045,21328046,21328047,21328048,21328049,21328050,21328051,21328052,21328053,21328054,21328055,21328056,21328057,21328058,21328059,21328060,21328061,21328062,21328063,21328102,21328112,21328113,21328114,21328115,21328116,21328117,21328118,21328119,21328120,21328121,21328122,21328123,21328124,21328125,21328126,21328127],"Promethium":[20920321],"Protactinium":[20923161],"Pumpkin":[21331624],"Pumpkin Stem":[21333968,21333969,21333970,21333971,21333972,21333973,21333974,21333975,21333976,21333977,21333978,21333979,21333980,21333981,21333982,21333983,21333984,21333985,21333986,21333987,21333988,21333989,21333990,21333991,21333992,21333993,21333994,21333995,21333996,21333997,21333998,21333999,21334000,21334001,21334002,21334003,21334004,21334005,21334006,21334007],"Purple Torch":[21337610,21337611,21337612,21337613,21337614],"Purpur Block":[21338315],"Purpur Pillar":[21340545,21340546,21340547],"Purpur Slab":[21344168,21344169,21344170],"Purpur Stairs":[21344432,21344433,21344434,21344435,21344436,21344437,21344438,21344439],"Quartz Block":[21347391],"Quartz Bricks":[21678669],"Quartz Pillar":[21348820,21348821,21348823],"Quartz Slab":[21351776,21351777,21351779],"Quartz Stairs":[21352904,21352905,21352906,21352907,21352908,21352909,21352910,21352911],"Radium":[20925466],"Radon":[20927914],"Rail":[21356230,21356231,21356232,21356233,21356234,21356235,21356236,21356237,21356238,21356239],"Raw Copper Block":[21632127],"Raw Gold Block":[21634601],"Raw Iron Block":[21635367],"Red Mushroom":[21359866],"Red Mushroom Block":[21361105,21361106,21361107,21361112,21361113,21361114,21361115,21361116,21361117,21361118,21361119],"Red Nether Brick Slab":[21363940,21363942,21363943],"Red Nether Brick Stairs":[21365640,21365641,21365642,21365643,21365644,21365645,21365646,21365647],"Red Nether Brick Wall":[21367808,21367809,21367810,21367811,21367812,21367813,21367814,21367815,21367816,21367817,21367818,21367819,21367820,21367821,21367822,21367823,21367824,21367825,21367826,21367827,21367828,21367829,21367830,21367831,21367832,21367833,21367834,21367835,21367836,21367837,21367838,21367839,21367840,21367841,21367842,21367843,21367844,21367845,21367846,21367847,21367848,21367849,21367850,21367851,21367852,21367853,21367854,21367855,21367856,21367857,21367858,21367859,21367860,21367861,21367862,21367863,21367864,21367865,21367866,21367867,21367868,21367869,21367870,21367871,21367872,21367888,21367889,21367890,21367891,21367892,21367893,21367894,21367895,21367896,21367897,21367898,21367899,21367900,21367901,21367902,21367903,21367936,21367937,21367938,21367939,21367940,21367941,21367942,21367943,21367944,21367945,21367946,21367947,21367948,21367949,21367950,21367951,21367952,21367953,21367954,21367955,21367956,21367957,21367958,21367959,21367960,21367961,21367962,21367963,21367964,21367965,21367966,21367967,21367968,21367969,21367970,21367971,21367972,21367973,21367974,21367975,21367976,21367977,21367978,21367979,21367980,21367981,21367982,21367983,21367984,21367985,21367986,21367987,21367988,21367989,21367990,21367991,21367992,21367993,21367994,21367995,21367996,21367997,21367998,21367999,21368000,21368016,21368017,21368018,21368019,21368020,21368021,21368022,21368023,21368024,21368025,21368026,21368027,21368028,21368029,21368030,21368031],"Red Nether Bricks":[21368920],"Red Sand":[21371243],"Red Sandstone":[21373682],"Red Sandstone Slab":[21375308,21375309,21375310],"Red Sandstone Stairs":[21377256,21377257,21377258,21377259,21377260,21377261,21377262,21377263],"Red Sandstone Wall":[21380352,21380353,21380354,21380355,21380356,21380357,21380358,21380359,21380360,21380361,21380362,21380363,21380364,21380365,21380366,21380367,21380383,21380416,21380417,21380418,21380419,21380420,21380421,21380422,21380423,21380424,21380425,21380426,21380427,21380428,21380429,21380430,21380431,21380432,21380433,21380434,21380435,21380436,21380437,21380438,21380439,21380440,21380441,21380442,21380443,21380444,21380445,21380446,21380447,21380448,21380449,21380450,21380451,21380452,21380453,21380454,21380455,21380456,21380457,21380458,21380459,21380460,21380461,21380462,21380463,21380464,21380465,21380466,21380467,21380468,21380469,21380470,21380471,21380472,21380473,21380474,21380475,21380476,21380477,21380478,21380479,21380480,21380481,21380482,21380483,21380484,21380485,21380486,21380487,21380488,21380489,21380490,21380491,21380492,21380493,21380494,21380495,21380511,21380544,21380545,21380546,21380547,21380548,21380549,21380550,21380551,21380552,21380553,21380554,21380555,21380556,21380557,21380558,21380559,21380560,21380561,21380562,21380563,21380564,21380565,21380566,21380567,21380568,21380569,21380570,21380571,21380572,21380573,21380574,21380575,21380576,21380577,21380578,21380579,21380580,21380581,21380582,21380583,21380584,21380585,21380586,21380587,21380588,21380589,21380590,21380591,21380592,21380593,21380594,21380595,21380596,21380597,21380598,21380599,21380600,21380601,21380602,21380603,21380604,21380605,21380606,21380607],"Red Torch":[21381490,21381491,21381492,21381493,21381495],"Red Tulip":[21384139],"Redstone":[21397872,21397873,21397874,21397875,21397876,21397877,21397878,21397879,21397880,21397881,21397882,21397883,21397884,21397885,21397886,21397887],"Redstone Block":[21385654],"Redstone Comparator":[21388928,21388929,21388930,21388931,21388932,21388933,21388934,21388935,21388936,21388937,21388938,21388939,21388940,21388941,21388942,21388943],"Redstone Lamp":[21390644,21390645],"Redstone Ore":[21392016,21392017],"Redstone Repeater":[21394976,21394977,21394978,21394979,21394980,21394981,21394982,21394983,21394984,21394985,21394986,21394987,21394988,21394989,21394990,21394991,21394992,21394993,21394994,21394995,21394996,21394997,21394998,21394999,21395000,21395001,21395002,21395003,21395004,21395005,21395006,21395007],"Redstone Torch":[21396497,21396498,21396499,21396500,21396501,21396505,21396506,21396507,21396508,21396509],"Reinforced Deepslate":[21889612],"Rhenium":[20930190],"Rhodium":[20931608],"Roentgenium":[20934217],"Rose Bush":[21402408,21402409],"Rubidium":[20936670],"Ruthenium":[20937111],"Rutherfordium":[20939938],"Samarium":[20942830],"Sand":[21405599],"Sandstone":[21406854],"Sandstone Slab":[21408357,21408358,21408359],"Sandstone Stairs":[21410360,21410361,21410362,21410363,21410364,21410365,21410366,21410367],"Sandstone Wall":[21413632,21413633,21413634,21413635,21413636,21413637,21413638,21413639,21413640,21413641,21413642,21413643,21413644,21413645,21413646,21413647,21413648,21413649,21413650,21413651,21413652,21413653,21413654,21413655,21413656,21413657,21413658,21413659,21413660,21413661,21413662,21413663,21413664,21413665,21413666,21413667,21413668,21413669,21413670,21413671,21413672,21413673,21413674,21413675,21413676,21413677,21413678,21413679,21413680,21413681,21413682,21413683,21413684,21413685,21413686,21413687,21413688,21413689,21413690,21413691,21413692,21413693,21413694,21413695,21413696,21413697,21413698,21413699,21413700,21413701,21413702,21413703,21413704,21413705,21413706,21413707,21413708,21413709,21413710,21413711,21413719,21413760,21413761,21413762,21413763,21413764,21413765,21413766,21413767,21413768,21413769,21413770,21413771,21413772,21413773,21413774,21413775,21413776,21413777,21413778,21413779,21413780,21413781,21413782,21413783,21413784,21413785,21413786,21413787,21413788,21413789,21413790,21413791,21413792,21413793,21413794,21413795,21413796,21413797,21413798,21413799,21413800,21413801,21413802,21413803,21413804,21413805,21413806,21413807,21413808,21413809,21413810,21413811,21413812,21413813,21413814,21413815,21413816,21413817,21413818,21413819,21413820,21413821,21413822,21413823,21413824,21413825,21413826,21413827,21413828,21413829,21413830,21413831,21413832,21413833,21413834,21413835,21413836,21413837,21413838,21413839,21413847],"Scandium":[20944835],"Sculk":[21880279],"Sea Lantern":[21415151],"Sea Pickle":[21417344,21417345,21417346,21417347,21417348,21417349,21417350,21417351],"Seaborgium":[20946478],"Selenium":[20948963],"Shroomlight":[21696604],"Shulker Box":[21418259],"Silicon":[20949447],"Silver":[20952835],"Slime Block":[21421890],"Small Dripleaf":[21927576,21927577,21927578,21927579,21927580,21927581,21927582,21927583],"Smithing Table":[21847863],"Smoker":[21423456,21423457,21423458,21423459,21423460,21423461,21423462,21423463],"Smooth Basalt":[21595261],"Smooth Quartz Block":[21425097],"Smooth Quartz Slab":[21428056,21428057,21428058],"Smooth Quartz Stairs":[21429504,21429505,21429506,21429507,21429508,21429509,21429510,21429511],"Smooth Red Sandstone":[21431324],"Smooth Red Sandstone Slab":[21432877,21432878,21432879],"Smooth Red Sandstone Stairs":[21436184,21436185,21436186,21436187,21436188,21436189,21436190,21436191],"Smooth Sandstone":[21438242],"Smooth Sandstone Slab":[21439385,21439386,21439387],"Smooth Sandstone Stairs":[21440752,21440753,21440754,21440755,21440756,21440757,21440758,21440759],"Smooth Stone":[21444566],"Smooth Stone Slab":[21446216,21446217,21446219],"Snow Block":[21448652],"Snow Layer":[21449736,21449737,21449738,21449739,21449740,21449741,21449742,21449743],"Sodium":[20953522],"Soul Fire":[21696136],"Soul Lantern":[21692144,21692145],"Soul Sand":[21451142],"Soul Soil":[21693270],"Soul Torch":[21689448,21689449,21689452,21689454,21689455],"Sponge":[21454292,21454293],"Spore Blossom":[21852156],"Spruce Button":[21456688,21456689,21456690,21456691,21456692,21456693,21456696,21456697,21456698,21456699,21456700,21456701],"Spruce Door":[21458400,21458401,21458402,21458403,21458404,21458405,21458406,21458407,21458408,21458409,21458410,21458411,21458412,21458413,21458414,21458415,21458416,21458417,21458418,21458419,21458420,21458421,21458422,21458423,21458424,21458425,21458426,21458427,21458428,21458429,21458430,21458431],"Spruce Fence":[21459375],"Spruce Fence Gate":[21462512,21462513,21462514,21462515,21462516,21462517,21462518,21462519,21462520,21462521,21462522,21462523,21462524,21462525,21462526,21462527],"Spruce Leaves":[21463656,21463657,21463658,21463659],"Spruce Log":[21465720,21465721,21465724,21465725,21465726,21465727],"Spruce Planks":[21468562],"Spruce Pressure Plate":[21469450,21469451],"Spruce Sapling":[21472860,21472861],"Spruce Sign":[21473424,21473425,21473426,21473427,21473428,21473429,21473430,21473431,21473432,21473433,21473434,21473435,21473436,21473437,21473438,21473439],"Spruce Slab":[21475816,21475817,21475818],"Spruce Stairs":[21477456,21477457,21477458,21477459,21477460,21477461,21477462,21477463],"Spruce Trapdoor":[21481008,21481009,21481010,21481011,21481012,21481013,21481014,21481015,21481016,21481017,21481018,21481019,21481020,21481021,21481022,21481023],"Spruce Wall Sign":[21483504,21483505,21483506,21483507],"Spruce Wood":[21484912,21484913,21484914,21484915,21484918,21484919],"Stained Clay":[21486144,21486145,21486146,21486147,21486148,21486149,21486150,21486151,21486152,21486153,21486154,21486155,21486156,21486157,21486158,21486159],"Stained Glass":[21489584,21489585,21489586,21489587,21489588,21489589,21489590,21489591,21489592,21489593,21489594,21489595,21489596,21489597,21489598,21489599],"Stained Glass Pane":[21489792,21489793,21489794,21489795,21489796,21489797,21489798,21489799,21489800,21489801,21489802,21489803,21489804,21489805,21489806,21489807],"Stained Hardened Glass":[21491728,21491729,21491730,21491731,21491732,21491733,21491734,21491735,21491736,21491737,21491738,21491739,21491740,21491741,21491742,21491743],"Stained Hardened Glass Pane":[21495136,21495137,21495138,21495139,21495140,21495141,21495142,21495143,21495144,21495145,21495146,21495147,21495148,21495149,21495150,21495151],"Stone":[21496052],"Stone Brick Slab":[21498972,21498973,21498974],"Stone Brick Stairs":[21500248,21500249,21500250,21500251,21500252,21500253,21500254,21500255],"Stone Brick Wall":[21503744,21503745,21503746,21503747,21503748,21503749,21503750,21503751,21503752,21503753,21503754,21503755,21503756,21503757,21503758,21503759,21503773,21503808,21503809,21503810,21503811,21503812,21503813,21503814,21503815,21503816,21503817,21503818,21503819,21503820,21503821,21503822,21503823,21503824,21503825,21503826,21503827,21503828,21503829,21503830,21503831,21503832,21503833,21503834,21503835,21503836,21503837,21503838,21503839,21503840,21503841,21503842,21503843,21503844,21503845,21503846,21503847,21503848,21503849,21503850,21503851,21503852,21503853,21503854,21503855,21503856,21503857,21503858,21503859,21503860,21503861,21503862,21503863,21503864,21503865,21503866,21503867,21503868,21503869,21503870,21503871,21503872,21503873,21503874,21503875,21503876,21503877,21503878,21503879,21503880,21503881,21503882,21503883,21503884,21503885,21503886,21503887,21503901,21503936,21503937,21503938,21503939,21503940,21503941,21503942,21503943,21503944,21503945,21503946,21503947,21503948,21503949,21503950,21503951,21503952,21503953,21503954,21503955,21503956,21503957,21503958,21503959,21503960,21503961,21503962,21503963,21503964,21503965,21503966,21503967,21503968,21503969,21503970,21503971,21503972,21503973,21503974,21503975,21503976,21503977,21503978,21503979,21503980,21503981,21503982,21503983,21503984,21503985,21503986,21503987,21503988,21503989,21503990,21503991,21503992,21503993,21503994,21503995,21503996,21503997,21503998,21503999],"Stone Bricks":[21505414],"Stone Button":[21507712,21507713,21507714,21507715,21507718,21507719,21507720,21507721,21507722,21507723,21507726,21507727],"Stone Pressure Plate":[21508356,21508357],"Stone Slab":[21512017,21512018,21512019],"Stone Stairs":[21512192,21512193,21512194,21512195,21512196,21512197,21512198,21512199],"Stonecutter":[21514984,21514985,21514986,21514987],"Strontium":[20955164],"Sugarcane":[21542656,21542657,21542658,21542659,21542660,21542661,21542662,21542663,21542664,21542665,21542666,21542667,21542668,21542669,21542670,21542671],"Sulfur":[20958068],"Sunflower":[21544506,21544507],"Sweet Berry Bush":[21545764,21545765,21545766,21545767],"TNT":[21550004,21550005,21550006,21550007],"Tall Grass":[21547986],"Tantalum":[20959931],"Technetium":[20962279],"Tellurium":[20964192],"Tennessine":[20965501],"Terbium":[20968631],"Thallium":[20971328],"Thorium":[20973507],"Thulium":[20974346],"Tin":[20976139],"Tinted Glass":[21779734],"Titanium":[20978911],"Torch":[21551770,21551771,21551772,21551773,21551775],"Torchflower":[21940847],"Torchflower Crop":[21943796,21943797],"Trapped Chest":[21553240,21553241,21553242,21553243],"Tripwire":[21556976,21556977,21556978,21556979,21556980,21556981,21556982,21556983,21556984,21556985,21556986,21556987,21556988,21556989,21556990,21556991],"Tripwire Hook":[21559008,21559009,21559010,21559011,21559012,21559013,21559014,21559015,21559016,21559017,21559018,21559019,21559020,21559021,21559022,21559023],"Tuff":[21688162],"Tungsten":[20981723],"Twisting Vines":[21873664,21873665,21873666,21873667,21873668,21873669,21873670,21873671,21873672,21873673,21873674,21873675,21873676,21873677,21873678,21873679,21873680,21873681,21873688,21873689,21873690,21873691,21873692,21873693,21873694,21873695],"Underwater Torch":[21560248,21560249,21560252,21560254,21560255],"Uranium":[20982778],"Vanadium":[20984123],"Vines":[21562368,21562369,21562370,21562371,21562372,21562373,21562374,21562375,21562376,21562377,21562378,21562379,21562380,21562381,21562382,21562383],"Wall Banner":[21564672,21564673,21564674,21564675,21564676,21564677,21564678,21564679,21564680,21564681,21564682,21564683,21564684,21564685,21564686,21564687,21564688,21564689,21564690,21564691,21564692,21564693,21564694,21564695,21564696,21564697,21564698,21564699,21564700,21564701,21564702,21564703,21564704,21564705,21564706,21564707,21564708,21564709,21564710,21564711,21564712,21564713,21564714,21564715,21564716,21564717,21564718,21564719,21564720,21564721,21564722,21564723,21564724,21564725,21564726,21564727,21564728,21564729,21564730,21564731,21564732,21564733,21564734,21564735],"Wall Coral Fan":[21567296,21567300,21567301,21567302,21567303,21567304,21567308,21567309,21567310,21567311,21567312,21567316,21567317,21567318,21567319,21567320,21567324,21567325,21567326,21567327,21567328,21567332,21567333,21567334,21567335,21567336,21567340,21567341,21567342,21567343,21567344,21567348,21567349,21567350,21567351,21567352,21567356,21567357,21567358,21567359],"Warped Button":[21741552,21741553,21741556,21741557,21741558,21741559,21741560,21741561,21741564,21741565,21741566,21741567],"Warped Door":[21752672,21752673,21752674,21752675,21752676,21752677,21752678,21752679,21752680,21752681,21752682,21752683,21752684,21752685,21752686,21752687,21752688,21752689,21752690,21752691,21752692,21752693,21752694,21752695,21752696,21752697,21752698,21752699,21752700,21752701,21752702,21752703],"Warped Fence":[21709978],"Warped Fence Gate":[21759616,21759617,21759618,21759619,21759620,21759621,21759622,21759623,21759624,21759625,21759626,21759627,21759628,21759629,21759630,21759631],"Warped Hyphae":[21727976,21727977,21727980,21727981,21727982,21727983],"Warped Planks":[21703045],"Warped Pressure Plate":[21747528,21747529],"Warped Roots":[21938097],"Warped Sign":[21771312,21771313,21771314,21771315,21771316,21771317,21771318,21771319,21771320,21771321,21771322,21771323,21771324,21771325,21771326,21771327],"Warped Slab":[21715036,21715038,21715039],"Warped Stairs":[21765928,21765929,21765930,21765931,21765932,21765933,21765934,21765935],"Warped Stem":[21721496,21721497,21721500,21721501,21721502,21721503],"Warped Trapdoor":[21734544,21734545,21734546,21734547,21734548,21734549,21734550,21734551,21734552,21734553,21734554,21734555,21734556,21734557,21734558,21734559],"Warped Wall Sign":[21777200,21777201,21777202,21777203],"Warped Wart Block":[21815813],"Water":[21568608,21568609,21568610,21568611,21568612,21568613,21568614,21568615,21568616,21568617,21568618,21568619,21568620,21568621,21568622,21568623,21568624,21568625,21568626,21568627,21568628,21568629,21568630,21568631,21568632,21568633,21568634,21568635,21568636,21568637,21568638,21568639],"Water Cauldron":[21854480,21854481,21854482,21854483,21854486,21854487],"Weeping Vines":[21875072,21875073,21875074,21875075,21875076,21875077,21875078,21875079,21875080,21875081,21875082,21875083,21875084,21875085,21875086,21875087,21875088,21875089,21875090,21875091,21875092,21875093,21875094,21875095,21875102,21875103],"Weighted Pressure Plate Heavy":[21570544,21570545,21570546,21570547,21570548,21570549,21570550,21570551,21570552,21570553,21570554,21570555,21570556,21570557,21570558,21570559],"Weighted Pressure Plate Light":[21572160,21572161,21572162,21572163,21572164,21572165,21572166,21572167,21572168,21572169,21572170,21572171,21572172,21572173,21572174,21572175],"Wheat Block":[21575552,21575553,21575554,21575555,21575556,21575557,21575558,21575559],"White Tulip":[21578492],"Wither Rose":[21841807],"Wool":[21580784,21580785,21580786,21580787,21580788,21580789,21580790,21580791,21580792,21580793,21580794,21580795,21580796,21580797,21580798,21580799],"Xenon":[20987135],"Ytterbium":[20989425],"Yttrium":[20991002],"Zinc":[20995928],"Zirconium":[20996890],"ate!upd":[21097998],"reserved6":[21399796],"update!":[21096340]},"stateDataBits":11} \ No newline at end of file +{ + "ACACIA_BUTTON": 12, + "ACACIA_DOOR": 32, + "ACACIA_FENCE": 1, + "ACACIA_FENCE_GATE": 16, + "ACACIA_LEAVES": 4, + "ACACIA_LOG": 6, + "ACACIA_PLANKS": 1, + "ACACIA_PRESSURE_PLATE": 2, + "ACACIA_SAPLING": 2, + "ACACIA_SIGN": 16, + "ACACIA_SLAB": 3, + "ACACIA_STAIRS": 8, + "ACACIA_TRAPDOOR": 16, + "ACACIA_WALL_SIGN": 4, + "ACACIA_WOOD": 6, + "ACTIVATOR_RAIL": 12, + "AIR": 1, + "ALLIUM": 1, + "ALL_SIDED_MUSHROOM_STEM": 1, + "AMETHYST": 1, + "AMETHYST_CLUSTER": 24, + "ANCIENT_DEBRIS": 1, + "ANDESITE": 1, + "ANDESITE_SLAB": 3, + "ANDESITE_STAIRS": 8, + "ANDESITE_WALL": 162, + "ANVIL": 12, + "AZALEA_LEAVES": 4, + "AZURE_BLUET": 1, + "BAMBOO": 12, + "BAMBOO_SAPLING": 2, + "BANNER": 256, + "BARREL": 12, + "BARRIER": 1, + "BASALT": 3, + "BEACON": 1, + "BED": 256, + "BEDROCK": 2, + "BEETROOTS": 8, + "BELL": 16, + "BIG_DRIPLEAF_HEAD": 16, + "BIG_DRIPLEAF_STEM": 4, + "BIRCH_BUTTON": 12, + "BIRCH_DOOR": 32, + "BIRCH_FENCE": 1, + "BIRCH_FENCE_GATE": 16, + "BIRCH_LEAVES": 4, + "BIRCH_LOG": 6, + "BIRCH_PLANKS": 1, + "BIRCH_PRESSURE_PLATE": 2, + "BIRCH_SAPLING": 2, + "BIRCH_SIGN": 16, + "BIRCH_SLAB": 3, + "BIRCH_STAIRS": 8, + "BIRCH_TRAPDOOR": 16, + "BIRCH_WALL_SIGN": 4, + "BIRCH_WOOD": 6, + "BLACKSTONE": 1, + "BLACKSTONE_SLAB": 3, + "BLACKSTONE_STAIRS": 8, + "BLACKSTONE_WALL": 162, + "BLAST_FURNACE": 8, + "BLUE_ICE": 1, + "BLUE_ORCHID": 1, + "BLUE_TORCH": 5, + "BONE_BLOCK": 3, + "BOOKSHELF": 1, + "BREWING_STAND": 8, + "BRICKS": 1, + "BRICK_SLAB": 3, + "BRICK_STAIRS": 8, + "BRICK_WALL": 162, + "BROWN_MUSHROOM": 1, + "BROWN_MUSHROOM_BLOCK": 11, + "BUDDING_AMETHYST": 1, + "CACTUS": 16, + "CAKE": 7, + "CAKE_WITH_CANDLE": 2, + "CAKE_WITH_DYED_CANDLE": 32, + "CALCITE": 1, + "CANDLE": 8, + "CARPET": 16, + "CARROTS": 8, + "CARTOGRAPHY_TABLE": 1, + "CARVED_PUMPKIN": 4, + "CAULDRON": 1, + "CAVE_VINES": 104, + "CHAIN": 3, + "CHEMICAL_HEAT": 1, + "CHERRY_BUTTON": 12, + "CHERRY_DOOR": 32, + "CHERRY_FENCE": 1, + "CHERRY_FENCE_GATE": 16, + "CHERRY_LEAVES": 4, + "CHERRY_LOG": 6, + "CHERRY_PLANKS": 1, + "CHERRY_PRESSURE_PLATE": 2, + "CHERRY_SIGN": 16, + "CHERRY_SLAB": 3, + "CHERRY_STAIRS": 8, + "CHERRY_TRAPDOOR": 16, + "CHERRY_WALL_SIGN": 4, + "CHERRY_WOOD": 6, + "CHEST": 4, + "CHISELED_BOOKSHELF": 256, + "CHISELED_DEEPSLATE": 1, + "CHISELED_NETHER_BRICKS": 1, + "CHISELED_POLISHED_BLACKSTONE": 1, + "CHISELED_QUARTZ": 3, + "CHISELED_RED_SANDSTONE": 1, + "CHISELED_SANDSTONE": 1, + "CHISELED_STONE_BRICKS": 1, + "CHORUS_FLOWER": 6, + "CHORUS_PLANT": 1, + "CLAY": 1, + "COAL": 1, + "COAL_ORE": 1, + "COBBLED_DEEPSLATE": 1, + "COBBLED_DEEPSLATE_SLAB": 3, + "COBBLED_DEEPSLATE_STAIRS": 8, + "COBBLED_DEEPSLATE_WALL": 162, + "COBBLESTONE": 1, + "COBBLESTONE_SLAB": 3, + "COBBLESTONE_STAIRS": 8, + "COBBLESTONE_WALL": 162, + "COBWEB": 1, + "COCOA_POD": 12, + "COMPOUND_CREATOR": 4, + "CONCRETE": 16, + "CONCRETE_POWDER": 16, + "COPPER": 8, + "COPPER_ORE": 1, + "CORAL": 10, + "CORAL_BLOCK": 10, + "CORAL_FAN": 20, + "CORNFLOWER": 1, + "CRACKED_DEEPSLATE_BRICKS": 1, + "CRACKED_DEEPSLATE_TILES": 1, + "CRACKED_NETHER_BRICKS": 1, + "CRACKED_POLISHED_BLACKSTONE_BRICKS": 1, + "CRACKED_STONE_BRICKS": 1, + "CRAFTING_TABLE": 1, + "CRIMSON_BUTTON": 12, + "CRIMSON_DOOR": 32, + "CRIMSON_FENCE": 1, + "CRIMSON_FENCE_GATE": 16, + "CRIMSON_HYPHAE": 6, + "CRIMSON_PLANKS": 1, + "CRIMSON_PRESSURE_PLATE": 2, + "CRIMSON_ROOTS": 1, + "CRIMSON_SIGN": 16, + "CRIMSON_SLAB": 3, + "CRIMSON_STAIRS": 8, + "CRIMSON_STEM": 6, + "CRIMSON_TRAPDOOR": 16, + "CRIMSON_WALL_SIGN": 4, + "CRYING_OBSIDIAN": 1, + "CUT_COPPER": 8, + "CUT_COPPER_SLAB": 24, + "CUT_COPPER_STAIRS": 64, + "CUT_RED_SANDSTONE": 1, + "CUT_RED_SANDSTONE_SLAB": 3, + "CUT_SANDSTONE": 1, + "CUT_SANDSTONE_SLAB": 3, + "DANDELION": 1, + "DARK_OAK_BUTTON": 12, + "DARK_OAK_DOOR": 32, + "DARK_OAK_FENCE": 1, + "DARK_OAK_FENCE_GATE": 16, + "DARK_OAK_LEAVES": 4, + "DARK_OAK_LOG": 6, + "DARK_OAK_PLANKS": 1, + "DARK_OAK_PRESSURE_PLATE": 2, + "DARK_OAK_SAPLING": 2, + "DARK_OAK_SIGN": 16, + "DARK_OAK_SLAB": 3, + "DARK_OAK_STAIRS": 8, + "DARK_OAK_TRAPDOOR": 16, + "DARK_OAK_WALL_SIGN": 4, + "DARK_OAK_WOOD": 6, + "DARK_PRISMARINE": 1, + "DARK_PRISMARINE_SLAB": 3, + "DARK_PRISMARINE_STAIRS": 8, + "DAYLIGHT_SENSOR": 32, + "DEAD_BUSH": 1, + "DEEPSLATE": 3, + "DEEPSLATE_BRICKS": 1, + "DEEPSLATE_BRICK_SLAB": 3, + "DEEPSLATE_BRICK_STAIRS": 8, + "DEEPSLATE_BRICK_WALL": 162, + "DEEPSLATE_COAL_ORE": 1, + "DEEPSLATE_COPPER_ORE": 1, + "DEEPSLATE_DIAMOND_ORE": 1, + "DEEPSLATE_EMERALD_ORE": 1, + "DEEPSLATE_GOLD_ORE": 1, + "DEEPSLATE_IRON_ORE": 1, + "DEEPSLATE_LAPIS_LAZULI_ORE": 1, + "DEEPSLATE_REDSTONE_ORE": 2, + "DEEPSLATE_TILES": 1, + "DEEPSLATE_TILE_SLAB": 3, + "DEEPSLATE_TILE_STAIRS": 8, + "DEEPSLATE_TILE_WALL": 162, + "DETECTOR_RAIL": 12, + "DIAMOND": 1, + "DIAMOND_ORE": 1, + "DIORITE": 1, + "DIORITE_SLAB": 3, + "DIORITE_STAIRS": 8, + "DIORITE_WALL": 162, + "DIRT": 3, + "DOUBLE_PITCHER_CROP": 4, + "DOUBLE_TALLGRASS": 2, + "DRAGON_EGG": 1, + "DRIED_KELP": 1, + "DYED_CANDLE": 128, + "DYED_SHULKER_BOX": 16, + "ELEMENT_ACTINIUM": 1, + "ELEMENT_ALUMINUM": 1, + "ELEMENT_AMERICIUM": 1, + "ELEMENT_ANTIMONY": 1, + "ELEMENT_ARGON": 1, + "ELEMENT_ARSENIC": 1, + "ELEMENT_ASTATINE": 1, + "ELEMENT_BARIUM": 1, + "ELEMENT_BERKELIUM": 1, + "ELEMENT_BERYLLIUM": 1, + "ELEMENT_BISMUTH": 1, + "ELEMENT_BOHRIUM": 1, + "ELEMENT_BORON": 1, + "ELEMENT_BROMINE": 1, + "ELEMENT_CADMIUM": 1, + "ELEMENT_CALCIUM": 1, + "ELEMENT_CALIFORNIUM": 1, + "ELEMENT_CARBON": 1, + "ELEMENT_CERIUM": 1, + "ELEMENT_CESIUM": 1, + "ELEMENT_CHLORINE": 1, + "ELEMENT_CHROMIUM": 1, + "ELEMENT_COBALT": 1, + "ELEMENT_CONSTRUCTOR": 4, + "ELEMENT_COPERNICIUM": 1, + "ELEMENT_COPPER": 1, + "ELEMENT_CURIUM": 1, + "ELEMENT_DARMSTADTIUM": 1, + "ELEMENT_DUBNIUM": 1, + "ELEMENT_DYSPROSIUM": 1, + "ELEMENT_EINSTEINIUM": 1, + "ELEMENT_ERBIUM": 1, + "ELEMENT_EUROPIUM": 1, + "ELEMENT_FERMIUM": 1, + "ELEMENT_FLEROVIUM": 1, + "ELEMENT_FLUORINE": 1, + "ELEMENT_FRANCIUM": 1, + "ELEMENT_GADOLINIUM": 1, + "ELEMENT_GALLIUM": 1, + "ELEMENT_GERMANIUM": 1, + "ELEMENT_GOLD": 1, + "ELEMENT_HAFNIUM": 1, + "ELEMENT_HASSIUM": 1, + "ELEMENT_HELIUM": 1, + "ELEMENT_HOLMIUM": 1, + "ELEMENT_HYDROGEN": 1, + "ELEMENT_INDIUM": 1, + "ELEMENT_IODINE": 1, + "ELEMENT_IRIDIUM": 1, + "ELEMENT_IRON": 1, + "ELEMENT_KRYPTON": 1, + "ELEMENT_LANTHANUM": 1, + "ELEMENT_LAWRENCIUM": 1, + "ELEMENT_LEAD": 1, + "ELEMENT_LITHIUM": 1, + "ELEMENT_LIVERMORIUM": 1, + "ELEMENT_LUTETIUM": 1, + "ELEMENT_MAGNESIUM": 1, + "ELEMENT_MANGANESE": 1, + "ELEMENT_MEITNERIUM": 1, + "ELEMENT_MENDELEVIUM": 1, + "ELEMENT_MERCURY": 1, + "ELEMENT_MOLYBDENUM": 1, + "ELEMENT_MOSCOVIUM": 1, + "ELEMENT_NEODYMIUM": 1, + "ELEMENT_NEON": 1, + "ELEMENT_NEPTUNIUM": 1, + "ELEMENT_NICKEL": 1, + "ELEMENT_NIHONIUM": 1, + "ELEMENT_NIOBIUM": 1, + "ELEMENT_NITROGEN": 1, + "ELEMENT_NOBELIUM": 1, + "ELEMENT_OGANESSON": 1, + "ELEMENT_OSMIUM": 1, + "ELEMENT_OXYGEN": 1, + "ELEMENT_PALLADIUM": 1, + "ELEMENT_PHOSPHORUS": 1, + "ELEMENT_PLATINUM": 1, + "ELEMENT_PLUTONIUM": 1, + "ELEMENT_POLONIUM": 1, + "ELEMENT_POTASSIUM": 1, + "ELEMENT_PRASEODYMIUM": 1, + "ELEMENT_PROMETHIUM": 1, + "ELEMENT_PROTACTINIUM": 1, + "ELEMENT_RADIUM": 1, + "ELEMENT_RADON": 1, + "ELEMENT_RHENIUM": 1, + "ELEMENT_RHODIUM": 1, + "ELEMENT_ROENTGENIUM": 1, + "ELEMENT_RUBIDIUM": 1, + "ELEMENT_RUTHENIUM": 1, + "ELEMENT_RUTHERFORDIUM": 1, + "ELEMENT_SAMARIUM": 1, + "ELEMENT_SCANDIUM": 1, + "ELEMENT_SEABORGIUM": 1, + "ELEMENT_SELENIUM": 1, + "ELEMENT_SILICON": 1, + "ELEMENT_SILVER": 1, + "ELEMENT_SODIUM": 1, + "ELEMENT_STRONTIUM": 1, + "ELEMENT_SULFUR": 1, + "ELEMENT_TANTALUM": 1, + "ELEMENT_TECHNETIUM": 1, + "ELEMENT_TELLURIUM": 1, + "ELEMENT_TENNESSINE": 1, + "ELEMENT_TERBIUM": 1, + "ELEMENT_THALLIUM": 1, + "ELEMENT_THORIUM": 1, + "ELEMENT_THULIUM": 1, + "ELEMENT_TIN": 1, + "ELEMENT_TITANIUM": 1, + "ELEMENT_TUNGSTEN": 1, + "ELEMENT_URANIUM": 1, + "ELEMENT_VANADIUM": 1, + "ELEMENT_XENON": 1, + "ELEMENT_YTTERBIUM": 1, + "ELEMENT_YTTRIUM": 1, + "ELEMENT_ZERO": 1, + "ELEMENT_ZINC": 1, + "ELEMENT_ZIRCONIUM": 1, + "EMERALD": 1, + "EMERALD_ORE": 1, + "ENCHANTING_TABLE": 1, + "ENDER_CHEST": 4, + "END_PORTAL_FRAME": 8, + "END_ROD": 6, + "END_STONE": 1, + "END_STONE_BRICKS": 1, + "END_STONE_BRICK_SLAB": 3, + "END_STONE_BRICK_STAIRS": 8, + "END_STONE_BRICK_WALL": 162, + "FAKE_WOODEN_SLAB": 3, + "FARMLAND": 1304, + "FERN": 1, + "FIRE": 16, + "FLETCHING_TABLE": 1, + "FLOWERING_AZALEA_LEAVES": 4, + "FLOWER_POT": 1, + "FROGLIGHT": 9, + "FROSTED_ICE": 4, + "FURNACE": 8, + "GILDED_BLACKSTONE": 1, + "GLASS": 1, + "GLASS_PANE": 1, + "GLAZED_TERRACOTTA": 64, + "GLOWING_ITEM_FRAME": 12, + "GLOWING_OBSIDIAN": 1, + "GLOWSTONE": 1, + "GLOW_LICHEN": 64, + "GOLD": 1, + "GOLD_ORE": 1, + "GRANITE": 1, + "GRANITE_SLAB": 3, + "GRANITE_STAIRS": 8, + "GRANITE_WALL": 162, + "GRASS": 1, + "GRASS_PATH": 1, + "GRAVEL": 1, + "GREEN_TORCH": 5, + "HANGING_ROOTS": 1, + "HARDENED_CLAY": 1, + "HARDENED_GLASS": 1, + "HARDENED_GLASS_PANE": 1, + "HAY_BALE": 3, + "HONEYCOMB": 1, + "HOPPER": 10, + "ICE": 1, + "INFESTED_CHISELED_STONE_BRICK": 1, + "INFESTED_COBBLESTONE": 1, + "INFESTED_CRACKED_STONE_BRICK": 1, + "INFESTED_MOSSY_STONE_BRICK": 1, + "INFESTED_STONE": 1, + "INFESTED_STONE_BRICK": 1, + "INFO_UPDATE": 1, + "INFO_UPDATE2": 1, + "INVISIBLE_BEDROCK": 1, + "IRON": 1, + "IRON_BARS": 1, + "IRON_DOOR": 32, + "IRON_ORE": 1, + "IRON_TRAPDOOR": 16, + "ITEM_FRAME": 12, + "JUKEBOX": 1, + "JUNGLE_BUTTON": 12, + "JUNGLE_DOOR": 32, + "JUNGLE_FENCE": 1, + "JUNGLE_FENCE_GATE": 16, + "JUNGLE_LEAVES": 4, + "JUNGLE_LOG": 6, + "JUNGLE_PLANKS": 1, + "JUNGLE_PRESSURE_PLATE": 2, + "JUNGLE_SAPLING": 2, + "JUNGLE_SIGN": 16, + "JUNGLE_SLAB": 3, + "JUNGLE_STAIRS": 8, + "JUNGLE_TRAPDOOR": 16, + "JUNGLE_WALL_SIGN": 4, + "JUNGLE_WOOD": 6, + "LAB_TABLE": 4, + "LADDER": 4, + "LANTERN": 2, + "LAPIS_LAZULI": 1, + "LAPIS_LAZULI_ORE": 1, + "LARGE_FERN": 2, + "LAVA": 32, + "LAVA_CAULDRON": 6, + "LECTERN": 8, + "LEGACY_STONECUTTER": 1, + "LEVER": 16, + "LIGHT": 16, + "LIGHTNING_ROD": 6, + "LILAC": 2, + "LILY_OF_THE_VALLEY": 1, + "LILY_PAD": 1, + "LIT_PUMPKIN": 4, + "LOOM": 4, + "MAGMA": 1, + "MANGROVE_BUTTON": 12, + "MANGROVE_DOOR": 32, + "MANGROVE_FENCE": 1, + "MANGROVE_FENCE_GATE": 16, + "MANGROVE_LEAVES": 4, + "MANGROVE_LOG": 6, + "MANGROVE_PLANKS": 1, + "MANGROVE_PRESSURE_PLATE": 2, + "MANGROVE_ROOTS": 1, + "MANGROVE_SIGN": 16, + "MANGROVE_SLAB": 3, + "MANGROVE_STAIRS": 8, + "MANGROVE_TRAPDOOR": 16, + "MANGROVE_WALL_SIGN": 4, + "MANGROVE_WOOD": 6, + "MATERIAL_REDUCER": 4, + "MELON": 1, + "MELON_STEM": 40, + "MOB_HEAD": 35, + "MONSTER_SPAWNER": 1, + "MOSSY_COBBLESTONE": 1, + "MOSSY_COBBLESTONE_SLAB": 3, + "MOSSY_COBBLESTONE_STAIRS": 8, + "MOSSY_COBBLESTONE_WALL": 162, + "MOSSY_STONE_BRICKS": 1, + "MOSSY_STONE_BRICK_SLAB": 3, + "MOSSY_STONE_BRICK_STAIRS": 8, + "MOSSY_STONE_BRICK_WALL": 162, + "MUD": 1, + "MUDDY_MANGROVE_ROOTS": 3, + "MUD_BRICKS": 1, + "MUD_BRICK_SLAB": 3, + "MUD_BRICK_STAIRS": 8, + "MUD_BRICK_WALL": 162, + "MUSHROOM_STEM": 1, + "MYCELIUM": 1, + "NETHERITE": 1, + "NETHERRACK": 1, + "NETHER_BRICKS": 1, + "NETHER_BRICK_FENCE": 1, + "NETHER_BRICK_SLAB": 3, + "NETHER_BRICK_STAIRS": 8, + "NETHER_BRICK_WALL": 162, + "NETHER_GOLD_ORE": 1, + "NETHER_PORTAL": 2, + "NETHER_QUARTZ_ORE": 1, + "NETHER_REACTOR_CORE": 1, + "NETHER_WART": 4, + "NETHER_WART_BLOCK": 1, + "NOTE_BLOCK": 1, + "OAK_BUTTON": 12, + "OAK_DOOR": 32, + "OAK_FENCE": 1, + "OAK_FENCE_GATE": 16, + "OAK_LEAVES": 4, + "OAK_LOG": 6, + "OAK_PLANKS": 1, + "OAK_PRESSURE_PLATE": 2, + "OAK_SAPLING": 2, + "OAK_SIGN": 16, + "OAK_SLAB": 3, + "OAK_STAIRS": 8, + "OAK_TRAPDOOR": 16, + "OAK_WALL_SIGN": 4, + "OAK_WOOD": 6, + "OBSIDIAN": 1, + "ORANGE_TULIP": 1, + "OXEYE_DAISY": 1, + "PACKED_ICE": 1, + "PACKED_MUD": 1, + "PEONY": 2, + "PINK_PETALS": 16, + "PINK_TULIP": 1, + "PITCHER_CROP": 3, + "PITCHER_PLANT": 2, + "PODZOL": 1, + "POLISHED_ANDESITE": 1, + "POLISHED_ANDESITE_SLAB": 3, + "POLISHED_ANDESITE_STAIRS": 8, + "POLISHED_BASALT": 3, + "POLISHED_BLACKSTONE": 1, + "POLISHED_BLACKSTONE_BRICKS": 1, + "POLISHED_BLACKSTONE_BRICK_SLAB": 3, + "POLISHED_BLACKSTONE_BRICK_STAIRS": 8, + "POLISHED_BLACKSTONE_BRICK_WALL": 162, + "POLISHED_BLACKSTONE_BUTTON": 12, + "POLISHED_BLACKSTONE_PRESSURE_PLATE": 2, + "POLISHED_BLACKSTONE_SLAB": 3, + "POLISHED_BLACKSTONE_STAIRS": 8, + "POLISHED_BLACKSTONE_WALL": 162, + "POLISHED_DEEPSLATE": 1, + "POLISHED_DEEPSLATE_SLAB": 3, + "POLISHED_DEEPSLATE_STAIRS": 8, + "POLISHED_DEEPSLATE_WALL": 162, + "POLISHED_DIORITE": 1, + "POLISHED_DIORITE_SLAB": 3, + "POLISHED_DIORITE_STAIRS": 8, + "POLISHED_GRANITE": 1, + "POLISHED_GRANITE_SLAB": 3, + "POLISHED_GRANITE_STAIRS": 8, + "POPPY": 1, + "POTATOES": 8, + "POTION_CAULDRON": 6, + "POWERED_RAIL": 12, + "PRISMARINE": 1, + "PRISMARINE_BRICKS": 1, + "PRISMARINE_BRICKS_SLAB": 3, + "PRISMARINE_BRICKS_STAIRS": 8, + "PRISMARINE_SLAB": 3, + "PRISMARINE_STAIRS": 8, + "PRISMARINE_WALL": 162, + "PUMPKIN": 1, + "PUMPKIN_STEM": 40, + "PURPLE_TORCH": 5, + "PURPUR": 1, + "PURPUR_PILLAR": 3, + "PURPUR_SLAB": 3, + "PURPUR_STAIRS": 8, + "QUARTZ": 1, + "QUARTZ_BRICKS": 1, + "QUARTZ_PILLAR": 3, + "QUARTZ_SLAB": 3, + "QUARTZ_STAIRS": 8, + "RAIL": 10, + "RAW_COPPER": 1, + "RAW_GOLD": 1, + "RAW_IRON": 1, + "REDSTONE": 1, + "REDSTONE_COMPARATOR": 16, + "REDSTONE_LAMP": 2, + "REDSTONE_ORE": 2, + "REDSTONE_REPEATER": 32, + "REDSTONE_TORCH": 10, + "REDSTONE_WIRE": 16, + "RED_MUSHROOM": 1, + "RED_MUSHROOM_BLOCK": 11, + "RED_NETHER_BRICKS": 1, + "RED_NETHER_BRICK_SLAB": 3, + "RED_NETHER_BRICK_STAIRS": 8, + "RED_NETHER_BRICK_WALL": 162, + "RED_SAND": 1, + "RED_SANDSTONE": 1, + "RED_SANDSTONE_SLAB": 3, + "RED_SANDSTONE_STAIRS": 8, + "RED_SANDSTONE_WALL": 162, + "RED_TORCH": 5, + "RED_TULIP": 1, + "REINFORCED_DEEPSLATE": 1, + "RESERVED6": 1, + "ROSE_BUSH": 2, + "SAND": 1, + "SANDSTONE": 1, + "SANDSTONE_SLAB": 3, + "SANDSTONE_STAIRS": 8, + "SANDSTONE_WALL": 162, + "SCULK": 1, + "SEA_LANTERN": 1, + "SEA_PICKLE": 8, + "SHROOMLIGHT": 1, + "SHULKER_BOX": 1, + "SLIME": 1, + "SMALL_DRIPLEAF": 8, + "SMITHING_TABLE": 1, + "SMOKER": 8, + "SMOOTH_BASALT": 1, + "SMOOTH_QUARTZ": 1, + "SMOOTH_QUARTZ_SLAB": 3, + "SMOOTH_QUARTZ_STAIRS": 8, + "SMOOTH_RED_SANDSTONE": 1, + "SMOOTH_RED_SANDSTONE_SLAB": 3, + "SMOOTH_RED_SANDSTONE_STAIRS": 8, + "SMOOTH_SANDSTONE": 1, + "SMOOTH_SANDSTONE_SLAB": 3, + "SMOOTH_SANDSTONE_STAIRS": 8, + "SMOOTH_STONE": 1, + "SMOOTH_STONE_SLAB": 3, + "SNOW": 1, + "SNOW_LAYER": 8, + "SOUL_FIRE": 1, + "SOUL_LANTERN": 2, + "SOUL_SAND": 1, + "SOUL_SOIL": 1, + "SOUL_TORCH": 5, + "SPONGE": 2, + "SPORE_BLOSSOM": 1, + "SPRUCE_BUTTON": 12, + "SPRUCE_DOOR": 32, + "SPRUCE_FENCE": 1, + "SPRUCE_FENCE_GATE": 16, + "SPRUCE_LEAVES": 4, + "SPRUCE_LOG": 6, + "SPRUCE_PLANKS": 1, + "SPRUCE_PRESSURE_PLATE": 2, + "SPRUCE_SAPLING": 2, + "SPRUCE_SIGN": 16, + "SPRUCE_SLAB": 3, + "SPRUCE_STAIRS": 8, + "SPRUCE_TRAPDOOR": 16, + "SPRUCE_WALL_SIGN": 4, + "SPRUCE_WOOD": 6, + "STAINED_CLAY": 16, + "STAINED_GLASS": 16, + "STAINED_GLASS_PANE": 16, + "STAINED_HARDENED_GLASS": 16, + "STAINED_HARDENED_GLASS_PANE": 16, + "STONE": 1, + "STONECUTTER": 4, + "STONE_BRICKS": 1, + "STONE_BRICK_SLAB": 3, + "STONE_BRICK_STAIRS": 8, + "STONE_BRICK_WALL": 162, + "STONE_BUTTON": 12, + "STONE_PRESSURE_PLATE": 2, + "STONE_SLAB": 3, + "STONE_STAIRS": 8, + "SUGARCANE": 16, + "SUNFLOWER": 2, + "SWEET_BERRY_BUSH": 4, + "TALL_GRASS": 1, + "TINTED_GLASS": 1, + "TNT": 4, + "TORCH": 5, + "TORCHFLOWER": 1, + "TORCHFLOWER_CROP": 2, + "TRAPPED_CHEST": 4, + "TRIPWIRE": 16, + "TRIPWIRE_HOOK": 16, + "TUFF": 1, + "TWISTING_VINES": 26, + "UNDERWATER_TORCH": 5, + "VINES": 16, + "WALL_BANNER": 64, + "WALL_CORAL_FAN": 40, + "WARPED_BUTTON": 12, + "WARPED_DOOR": 32, + "WARPED_FENCE": 1, + "WARPED_FENCE_GATE": 16, + "WARPED_HYPHAE": 6, + "WARPED_PLANKS": 1, + "WARPED_PRESSURE_PLATE": 2, + "WARPED_ROOTS": 1, + "WARPED_SIGN": 16, + "WARPED_SLAB": 3, + "WARPED_STAIRS": 8, + "WARPED_STEM": 6, + "WARPED_TRAPDOOR": 16, + "WARPED_WALL_SIGN": 4, + "WARPED_WART_BLOCK": 1, + "WATER": 32, + "WATER_CAULDRON": 6, + "WEEPING_VINES": 26, + "WEIGHTED_PRESSURE_PLATE_HEAVY": 16, + "WEIGHTED_PRESSURE_PLATE_LIGHT": 16, + "WHEAT": 8, + "WHITE_TULIP": 1, + "WITHER_ROSE": 1, + "WOOL": 16 +} \ No newline at end of file diff --git a/tests/phpunit/block/regenerate_consistency_check.php b/tests/phpunit/block/regenerate_consistency_check.php index b4b3875c6..e86f70d70 100644 --- a/tests/phpunit/block/regenerate_consistency_check.php +++ b/tests/phpunit/block/regenerate_consistency_check.php @@ -21,86 +21,31 @@ declare(strict_types=1); -use pocketmine\block\Block; +use pocketmine\block\BlockTest; use pocketmine\block\RuntimeBlockStateRegistry; -use pocketmine\utils\AssumptionFailedError; -use pocketmine\utils\Utils; require dirname(__DIR__, 3) . '/vendor/autoload.php'; /* This script needs to be re-run after any intentional blockfactory change (adding or removing a block state). */ -$factory = new RuntimeBlockStateRegistry(); -$remaps = []; -$new = []; -foreach(RuntimeBlockStateRegistry::getInstance()->getAllKnownStates() as $index => $block){ - if($index !== $block->getStateId()){ - throw new AssumptionFailedError("State index should always match state ID"); - } - $new[$index] = $block->getName(); -} +$newTable = BlockTest::computeConsistencyCheckTable(RuntimeBlockStateRegistry::getInstance()); $oldTablePath = __DIR__ . '/block_factory_consistency_check.json'; if(file_exists($oldTablePath)){ - $oldTable = json_decode(file_get_contents($oldTablePath), true); - if(!is_array($oldTable)){ - throw new AssumptionFailedError("Old table should be array{knownStates: array, stateDataBits: int}"); - } - $old = []; - /** - * @var string $name - * @var int[] $stateIds - */ - foreach($oldTable["knownStates"] as $name => $stateIds){ - foreach($stateIds as $stateId){ - $old[$stateId] = $name; - } - } - $oldStateDataSize = $oldTable["stateDataBits"]; - $oldStateDataMask = ~(~0 << $oldStateDataSize); + $errors = BlockTest::computeConsistencyCheckDiff($oldTablePath, $newTable); - if($oldStateDataSize !== Block::INTERNAL_STATE_DATA_BITS){ - echo "State data bits changed from $oldStateDataSize to " . Block::INTERNAL_STATE_DATA_BITS . "\n"; - } - - foreach($old as $k => $name){ - [$oldId, $oldStateData] = [$k >> $oldStateDataSize, $k & $oldStateDataMask]; - $reconstructedK = ($oldId << Block::INTERNAL_STATE_DATA_BITS) | $oldStateData; - if(!isset($new[$reconstructedK])){ - echo "Removed state for $name ($oldId:$oldStateData)\n"; - } - } - foreach($new as $k => $name){ - [$newId, $newStateData] = [$k >> Block::INTERNAL_STATE_DATA_BITS, $k & Block::INTERNAL_STATE_DATA_MASK]; - if($newStateData > $oldStateDataMask){ - echo "Added state for $name ($newId, $newStateData)\n"; - }else{ - $reconstructedK = ($newId << $oldStateDataSize) | $newStateData; - if(!isset($old[$reconstructedK])){ - echo "Added state for $name ($newId:$newStateData)\n"; - }elseif($old[$reconstructedK] !== $name){ - echo "Name changed ($newId:$newStateData) " . $old[$reconstructedK] . " -> " . $name . "\n"; - } + if(count($errors) > 0){ + echo count($errors) . " changes detected:\n"; + foreach($errors as $error){ + echo $error . "\n"; } + }else{ + echo "No changes detected\n"; } }else{ echo "WARNING: Unable to calculate diff, no previous consistency check file found\n"; } -$newTable = []; -foreach($new as $stateId => $name){ - $newTable[$name][] = $stateId; -} ksort($newTable, SORT_STRING); -foreach(Utils::stringifyKeys($newTable) as $name => $stateIds){ - sort($stateIds, SORT_NUMERIC); - $newTable[$name] = $stateIds; -} -file_put_contents(__DIR__ . '/block_factory_consistency_check.json', json_encode( - [ - "knownStates" => $newTable, - "stateDataBits" => Block::INTERNAL_STATE_DATA_BITS - ], - JSON_THROW_ON_ERROR -)); +file_put_contents($oldTablePath, json_encode($newTable, JSON_THROW_ON_ERROR | JSON_PRETTY_PRINT)); From 98380e46bf2f905f46d6c0f61ebc048793f98447 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 2 Apr 2024 19:22:40 +0100 Subject: [PATCH 29/35] Ignore non-fatal error types in crash handlers ErrorToExceptionHandler currently prevents these from appearing by turning them into exceptions, but this won't always be the case. For example, in the future we may not want to turn ALL types of E_* errors into exceptions (e.g. E_DEPRECATED). --- src/crash/CrashDump.php | 11 ++++++++++- src/thread/CommonThreadPartsTrait.php | 3 ++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/src/crash/CrashDump.php b/src/crash/CrashDump.php index 1a558b499..49a587c34 100644 --- a/src/crash/CrashDump.php +++ b/src/crash/CrashDump.php @@ -63,6 +63,12 @@ use function strpos; use function substr; use function zend_version; use function zlib_encode; +use const E_COMPILE_ERROR; +use const E_CORE_ERROR; +use const E_ERROR; +use const E_PARSE; +use const E_RECOVERABLE_ERROR; +use const E_USER_ERROR; use const FILE_IGNORE_NEW_LINES; use const JSON_THROW_ON_ERROR; use const JSON_UNESCAPED_SLASHES; @@ -85,6 +91,9 @@ class CrashDump{ public const PLUGIN_INVOLVEMENT_DIRECT = "direct"; public const PLUGIN_INVOLVEMENT_INDIRECT = "indirect"; + public const FATAL_ERROR_MASK = + E_ERROR | E_PARSE | E_CORE_ERROR | E_COMPILE_ERROR | E_USER_ERROR | E_RECOVERABLE_ERROR; + private CrashDumpData $data; private string $encodedData; @@ -186,7 +195,7 @@ class CrashDump{ $error = $lastExceptionError; }else{ $error = error_get_last(); - if($error === null){ + if($error === null || ($error["type"] & self::FATAL_ERROR_MASK) === 0){ throw new \RuntimeException("Crash error information missing - did something use exit()?"); } $error["trace"] = Utils::printableTrace(Utils::currentTrace(3)); //Skipping CrashDump->baseCrash, CrashDump->construct, Server->crashDump diff --git a/src/thread/CommonThreadPartsTrait.php b/src/thread/CommonThreadPartsTrait.php index 9a14b2345..e1c9d7c6b 100644 --- a/src/thread/CommonThreadPartsTrait.php +++ b/src/thread/CommonThreadPartsTrait.php @@ -25,6 +25,7 @@ namespace pocketmine\thread; use pmmp\thread\Thread as NativeThread; use pmmp\thread\ThreadSafeArray; +use pocketmine\crash\CrashDump; use pocketmine\errorhandler\ErrorToExceptionHandler; use pocketmine\Server; use function error_get_last; @@ -150,7 +151,7 @@ trait CommonThreadPartsTrait{ $this->synchronized(function() : void{ if($this->isTerminated() && $this->crashInfo === null){ $last = error_get_last(); - if($last !== null){ + if($last !== null && ($last["type"] & CrashDump::FATAL_ERROR_MASK) !== 0){ //fatal error $crashInfo = ThreadCrashInfo::fromLastErrorInfo($last, $this->getThreadName()); }else{ From 022362a01ad083123af788f56319c42eea806b2c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 2 Apr 2024 19:37:07 +0100 Subject: [PATCH 30/35] Update pocketmine/errorhandler to 0.7.0 --- composer.json | 2 +- composer.lock | 20 ++++++++++---------- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/composer.json b/composer.json index 004670526..1ef7d1aa2 100644 --- a/composer.json +++ b/composer.json @@ -40,7 +40,7 @@ "pocketmine/binaryutils": "^0.2.1", "pocketmine/callback-validator": "^1.0.2", "pocketmine/color": "^0.3.0", - "pocketmine/errorhandler": "^0.6.0", + "pocketmine/errorhandler": "^0.7.0", "pocketmine/locale-data": "~2.19.0", "pocketmine/log": "^0.4.0", "pocketmine/math": "~1.0.0", diff --git a/composer.lock b/composer.lock index 694103b32..a3443229a 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": "0be8eb3203ddac7f56645de59b2a4de9", + "content-hash": "28633153e56932cbd9e1dbcd7f7e6756", "packages": [ { "name": "adhocore/json-comment", @@ -376,25 +376,25 @@ }, { "name": "pocketmine/errorhandler", - "version": "0.6.0", + "version": "0.7.0", "source": { "type": "git", "url": "https://github.com/pmmp/ErrorHandler.git", - "reference": "dae214a04348b911e8219ebf125ff1c5589cc878" + "reference": "cae94884368a74ece5294b9ff7fef18732dcd921" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/dae214a04348b911e8219ebf125ff1c5589cc878", - "reference": "dae214a04348b911e8219ebf125ff1c5589cc878", + "url": "https://api.github.com/repos/pmmp/ErrorHandler/zipball/cae94884368a74ece5294b9ff7fef18732dcd921", + "reference": "cae94884368a74ece5294b9ff7fef18732dcd921", "shasum": "" }, "require": { "php": "^8.0" }, "require-dev": { - "phpstan/phpstan": "0.12.99", - "phpstan/phpstan-strict-rules": "^0.12.2", - "phpunit/phpunit": "^9.5" + "phpstan/phpstan": "~1.10.3", + "phpstan/phpstan-strict-rules": "^1.0", + "phpunit/phpunit": "^9.5 || ^10.0 || ^11.0" }, "type": "library", "autoload": { @@ -409,9 +409,9 @@ "description": "Utilities to handle nasty PHP E_* errors in a usable way", "support": { "issues": "https://github.com/pmmp/ErrorHandler/issues", - "source": "https://github.com/pmmp/ErrorHandler/tree/0.6.0" + "source": "https://github.com/pmmp/ErrorHandler/tree/0.7.0" }, - "time": "2022-01-08T21:05:46+00:00" + "time": "2024-04-02T18:29:54+00:00" }, { "name": "pocketmine/locale-data", From 11fbc8db6fe0dbf3cc0004954e7c1e988d83892f Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 2 Apr 2024 19:40:44 +0100 Subject: [PATCH 31/35] Make use of pmmpthread 6.1.0 for Thread::getRunningCount() ThreadManager doesn't count these correctly anymore since pmmpthread since thread-safe statics aren't copied anymore. --- composer.json | 2 +- composer.lock | 4 ++-- src/utils/Process.php | 3 ++- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/composer.json b/composer.json index 1ef7d1aa2..da6536ff6 100644 --- a/composer.json +++ b/composer.json @@ -22,7 +22,7 @@ "ext-openssl": "*", "ext-pcre": "*", "ext-phar": "*", - "ext-pmmpthread": "^6.0.7", + "ext-pmmpthread": "^6.1.0", "ext-reflection": "*", "ext-simplexml": "*", "ext-sockets": "*", diff --git a/composer.lock b/composer.lock index a3443229a..5e44bd686 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": "28633153e56932cbd9e1dbcd7f7e6756", + "content-hash": "05f535c2b562b59c11b6ac535b5f1c99", "packages": [ { "name": "adhocore/json-comment", @@ -2953,7 +2953,7 @@ "ext-openssl": "*", "ext-pcre": "*", "ext-phar": "*", - "ext-pmmpthread": "^6.0.7", + "ext-pmmpthread": "^6.1.0", "ext-reflection": "*", "ext-simplexml": "*", "ext-sockets": "*", diff --git a/src/utils/Process.php b/src/utils/Process.php index c57b96d4a..2e9b46ace 100644 --- a/src/utils/Process.php +++ b/src/utils/Process.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\utils; +use pocketmine\thread\Thread; use pocketmine\thread\ThreadManager; use function count; use function exec; @@ -122,7 +123,7 @@ final class Process{ //TODO: more OS - return count(ThreadManager::getInstance()->getAll()) + 2; //MainLogger + Main Thread + return Thread::getRunningCount() + 1; //pmmpthread doesn't count the main thread } /** From f013079ff66d3c8610d5ced0f5a5be34fbf56c63 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 3 Apr 2024 15:31:37 +0100 Subject: [PATCH 32/35] Fixed MainLogger BC break --- src/PocketMine.php | 2 +- src/utils/MainLogger.php | 2 +- src/utils/MainLoggerThread.php | 23 ++++++++++++----------- tests/phpunit/scheduler/AsyncPoolTest.php | 3 +-- 4 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/PocketMine.php b/src/PocketMine.php index f0fba9dcb..84054a2de 100644 --- a/src/PocketMine.php +++ b/src/PocketMine.php @@ -327,7 +327,7 @@ JIT_WARNING } $logFile = isset($opts[BootstrapOptions::NO_LOG_FILE]) ? null : Path::join($dataPath, "server.log"); - $logger = new MainLogger($logFile, Path::join($dataPath, "log_archive"), Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get())); + $logger = new MainLogger($logFile, Terminal::hasFormattingCodes(), "Server", new \DateTimeZone(Timezone::get()), false, Path::join($dataPath, "log_archive")); if($logFile === null){ $logger->notice("Logging to file disabled. Ensure logs are collected by other means (e.g. Docker logs)."); } diff --git a/src/utils/MainLogger.php b/src/utils/MainLogger.php index da2ba73da..2eaee7883 100644 --- a/src/utils/MainLogger.php +++ b/src/utils/MainLogger.php @@ -44,7 +44,7 @@ class MainLogger extends AttachableThreadSafeLogger implements \BufferedLogger{ /** * @throws \RuntimeException */ - public function __construct(?string $logFile, string $logArchiveDir, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false){ + public function __construct(?string $logFile, bool $useFormattingCodes, string $mainThreadName, \DateTimeZone $timezone, bool $logDebug = false, ?string $logArchiveDir = null){ parent::__construct(); $this->logDebug = $logDebug; diff --git a/src/utils/MainLoggerThread.php b/src/utils/MainLoggerThread.php index e7acf9737..990644f65 100644 --- a/src/utils/MainLoggerThread.php +++ b/src/utils/MainLoggerThread.php @@ -52,12 +52,12 @@ final class MainLoggerThread extends Thread{ public function __construct( private string $logFile, - private string $archiveDir, + private ?string $archiveDir, private readonly int $maxFileSize = 32 * 1024 * 1024 //32 MB ){ $this->buffer = new ThreadSafeArray(); touch($this->logFile); - if(!@mkdir($this->archiveDir) && !is_dir($this->archiveDir)){ + if($this->archiveDir !== null && !@mkdir($this->archiveDir) && !is_dir($this->archiveDir)){ throw new \RuntimeException("Unable to create archive directory: " . ( is_file($this->archiveDir) ? "it already exists and is not a directory" : "permission denied")); } @@ -108,7 +108,7 @@ final class MainLoggerThread extends Thread{ * @param resource $logResource * @return resource */ - private function archiveLogFile($logResource, int &$size){ + private function archiveLogFile($logResource, int &$size, string $archiveDir){ fclose($logResource); clearstatcache(); @@ -125,7 +125,7 @@ final class MainLoggerThread extends Thread{ }while(file_exists($out)); //the user may have externally deleted the whole directory - make sure it exists before we do anything - @mkdir($this->archiveDir); + @mkdir($archiveDir); rename($this->logFile, $out); $logResource = $this->openLogFile($this->logFile, $size); @@ -141,12 +141,12 @@ final class MainLoggerThread extends Thread{ /** * @param resource $logResource */ - private function writeLogStream(&$logResource, int &$size) : void{ + private function writeLogStream(&$logResource, int &$size, ?string $archiveDir) : void{ while(($chunk = $this->buffer->shift()) !== null){ fwrite($logResource, $chunk); $size += strlen($chunk); - if($this->logFileReadyToArchive($size)){ - $logResource = $this->archiveLogFile($logResource, $size); + if($archiveDir !== null && $this->logFileReadyToArchive($size)){ + $logResource = $this->archiveLogFile($logResource, $size, $archiveDir); } } @@ -161,12 +161,13 @@ final class MainLoggerThread extends Thread{ public function run() : void{ $size = 0; $logResource = $this->openLogFile($this->logFile, $size); - if($this->logFileReadyToArchive($size)){ - $logResource = $this->archiveLogFile($logResource, $size); + $archiveDir = $this->archiveDir; + if($archiveDir !== null && $this->logFileReadyToArchive($size)){ + $logResource = $this->archiveLogFile($logResource, $size, $archiveDir); } while(!$this->shutdown){ - $this->writeLogStream($logResource, $size); + $this->writeLogStream($logResource, $size, $archiveDir); $this->synchronized(function() : void{ if(!$this->shutdown && !$this->syncFlush){ $this->wait(); @@ -174,7 +175,7 @@ final class MainLoggerThread extends Thread{ }); } - $this->writeLogStream($logResource, $size); + $this->writeLogStream($logResource, $size, $archiveDir); fclose($logResource); } diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index d7bacd391..479cfee44 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -44,13 +44,12 @@ class AsyncPoolTest extends TestCase{ public function setUp() : void{ @define('pocketmine\\COMPOSER_AUTOLOADER_PATH', dirname(__DIR__, 3) . '/vendor/autoload.php'); - $this->mainLogger = new MainLogger(null, sys_get_temp_dir(), false, "Main", new \DateTimeZone('UTC')); + $this->mainLogger = new MainLogger(null, false, "Main", new \DateTimeZone('UTC')); $this->pool = new AsyncPool(2, 1024, new ThreadSafeClassLoader(), $this->mainLogger, new SleeperHandler()); } public function tearDown() : void{ $this->pool->shutdown(); - $this->mainLogger->shutdownLogWriterThread(); } public function testTaskLeak() : void{ From e30e27dd57e30db0fd77a99b59c05f283207eedd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 3 Apr 2024 15:43:43 +0100 Subject: [PATCH 33/35] Fix CS --- src/utils/Process.php | 1 - tests/phpunit/scheduler/AsyncPoolTest.php | 1 - 2 files changed, 2 deletions(-) diff --git a/src/utils/Process.php b/src/utils/Process.php index 2e9b46ace..1370ab27c 100644 --- a/src/utils/Process.php +++ b/src/utils/Process.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace pocketmine\utils; use pocketmine\thread\Thread; -use pocketmine\thread\ThreadManager; use function count; use function exec; use function fclose; diff --git a/tests/phpunit/scheduler/AsyncPoolTest.php b/tests/phpunit/scheduler/AsyncPoolTest.php index 479cfee44..53ec15c12 100644 --- a/tests/phpunit/scheduler/AsyncPoolTest.php +++ b/tests/phpunit/scheduler/AsyncPoolTest.php @@ -32,7 +32,6 @@ use pocketmine\utils\MainLogger; use function define; use function dirname; use function microtime; -use function sys_get_temp_dir; use function usleep; class AsyncPoolTest extends TestCase{ From 16f29c775e3aec1edbbd8e72c773ba3a62a7e96e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 5 Apr 2024 17:13:38 +0100 Subject: [PATCH 34/35] tools/generate-blockstate-upgrade-schema: added support for generating newFlattenedName with value transforms as seen in pmmp/BedrockBlockUpgradeSchema@ebd768e5b202cae59b0a7057982e3a2f40ba1954, this enables use of newFlattenedName in more places (by allowing the flattened values to be transformed before building the new ID), as well as reducing the number of remappedStates in general by compacting stuff which was partially transformed like color silver -> light_gray. --- .../BlockStateUpgradeSchemaFlattenedName.php | 17 ++- .../upgrade/BlockStateUpgradeSchemaUtils.php | 6 +- .../block/upgrade/BlockStateUpgrader.php | 3 +- ...ckStateUpgradeSchemaModelFlattenedName.php | 27 +++- tools/generate-blockstate-upgrade-schema.php | 132 ++++++++++++++---- 5 files changed, 149 insertions(+), 36 deletions(-) diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php index d9cbc780e..1c95dd9c7 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaFlattenedName.php @@ -23,17 +23,28 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade; +use function ksort; +use const SORT_STRING; + final class BlockStateUpgradeSchemaFlattenedName{ + /** + * @param string[] $flattenedValueRemaps + * @phpstan-param array $flattenedValueRemaps + */ public function __construct( public string $prefix, public string $flattenedProperty, - public string $suffix - ){} + public string $suffix, + public array $flattenedValueRemaps + ){ + ksort($this->flattenedValueRemaps, SORT_STRING); + } public function equals(self $that) : bool{ return $this->prefix === $that->prefix && $this->flattenedProperty === $that->flattenedProperty && - $this->suffix === $that->suffix; + $this->suffix === $that->suffix && + $this->flattenedValueRemaps === $that->flattenedValueRemaps; } } diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php index 9c63d51f0..832631490 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgradeSchemaUtils.php @@ -166,7 +166,8 @@ final class BlockStateUpgradeSchemaUtils{ $remap->newName ?? new BlockStateUpgradeSchemaFlattenedName( $remap->newFlattenedName->prefix, $remap->newFlattenedName->flattenedProperty, - $remap->newFlattenedName->suffix + $remap->newFlattenedName->suffix, + $remap->newFlattenedName->flattenedValueRemaps ?? [], ), array_map(fn(BlockStateUpgradeSchemaModelTag $tag) => self::jsonModelToTag($tag), $remap->newState ?? []), $remap->copiedState ?? [] @@ -301,7 +302,8 @@ final class BlockStateUpgradeSchemaUtils{ new BlockStateUpgradeSchemaModelFlattenedName( $remap->newName->prefix, $remap->newName->flattenedProperty, - $remap->newName->suffix + $remap->newName->suffix, + $remap->newName->flattenedValueRemaps ), array_map(fn(Tag $tag) => self::tagToJsonModel($tag), $remap->newState), $remap->copiedState diff --git a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php index f4a5b6e93..4a305d8bc 100644 --- a/src/data/bedrock/block/upgrade/BlockStateUpgrader.php +++ b/src/data/bedrock/block/upgrade/BlockStateUpgrader.php @@ -142,7 +142,8 @@ final class BlockStateUpgrader{ }else{ $flattenedValue = $oldState[$remap->newName->flattenedProperty] ?? null; if($flattenedValue instanceof StringTag){ - $newName = sprintf("%s%s%s", $remap->newName->prefix, $flattenedValue->getValue(), $remap->newName->suffix); + $embedValue = $remap->newName->flattenedValueRemaps[$flattenedValue->getValue()] ?? $flattenedValue->getValue(); + $newName = sprintf("%s%s%s", $remap->newName->prefix, $embedValue, $remap->newName->suffix); unset($oldState[$remap->newName->flattenedProperty]); }else{ //flattened property is not a TAG_String, so this transformation is not applicable diff --git a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php index 4508d9a3b..001192f47 100644 --- a/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php +++ b/src/data/bedrock/block/upgrade/model/BlockStateUpgradeSchemaModelFlattenedName.php @@ -23,7 +23,9 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\upgrade\model; -final class BlockStateUpgradeSchemaModelFlattenedName{ +use function count; + +final class BlockStateUpgradeSchemaModelFlattenedName implements \JsonSerializable{ /** @required */ public string $prefix; @@ -31,10 +33,31 @@ final class BlockStateUpgradeSchemaModelFlattenedName{ public string $flattenedProperty; /** @required */ public string $suffix; + /** + * @var string[] + * @phpstan-var array + */ + public array $flattenedValueRemaps; - public function __construct(string $prefix, string $flattenedProperty, string $suffix){ + /** + * @param string[] $flattenedValueRemaps + * @phpstan-param array $flattenedValueRemaps + */ + public function __construct(string $prefix, string $flattenedProperty, string $suffix, array $flattenedValueRemaps){ $this->prefix = $prefix; $this->flattenedProperty = $flattenedProperty; $this->suffix = $suffix; + $this->flattenedValueRemaps = $flattenedValueRemaps; + } + + /** + * @return mixed[] + */ + public function jsonSerialize() : array{ + $result = (array) $this; + if(count($this->flattenedValueRemaps) === 0){ + unset($result["flattenedValueRemaps"]); + } + return $result; } } diff --git a/tools/generate-blockstate-upgrade-schema.php b/tools/generate-blockstate-upgrade-schema.php index dfb8f6066..54984d459 100644 --- a/tools/generate-blockstate-upgrade-schema.php +++ b/tools/generate-blockstate-upgrade-schema.php @@ -38,18 +38,23 @@ use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\Filesystem; use pocketmine\utils\Utils; use function array_key_first; +use function array_key_last; use function array_keys; use function array_map; use function array_shift; use function array_values; use function count; use function dirname; -use function explode; use function file_put_contents; use function fwrite; use function implode; use function json_encode; use function ksort; +use function min; +use function sort; +use function strlen; +use function strrev; +use function substr; use function usort; use const JSON_PRETTY_PRINT; use const SORT_STRING; @@ -275,6 +280,77 @@ function processStateGroup(string $oldName, array $upgradeTable, BlockStateUpgra return true; } +/** + * @param string[] $strings + */ +function findCommonPrefix(array $strings) : string{ + sort($strings, SORT_STRING); + + $first = $strings[array_key_first($strings)]; + $last = $strings[array_key_last($strings)]; + + $maxLength = min(strlen($first), strlen($last)); + for($i = 0; $i < $maxLength; ++$i){ + if($first[$i] !== $last[$i]){ + return substr($first, 0, $i); + } + } + + return substr($first, 0, $maxLength); +} + +/** + * @param string[] $strings + */ +function findCommonSuffix(array $strings) : string{ + $reversed = array_map(strrev(...), $strings); + + return strrev(findCommonPrefix($reversed)); +} + +/** + * @param string[][][] $candidateFlattenedValues + * @phpstan-param array>> $candidateFlattenedValues + * + * @return BlockStateUpgradeSchemaFlattenedName[][] + * @phpstan-return array> + */ +function buildFlattenPropertyRules(array $candidateFlattenedValues) : array{ + $flattenPropertyRules = []; + foreach(Utils::stringifyKeys($candidateFlattenedValues) as $propertyName => $filters){ + foreach(Utils::stringifyKeys($filters) as $filter => $valueToId){ + $ids = array_values($valueToId); + + //TODO: this is a bit too enthusiastic. For example, when flattening the old "stone", it will see that + //"granite", "andesite", "stone" etc all have "e" as a common suffix, which works, but looks a bit daft. + //This also causes more remaps to be generated than necessary, since some of the values are already + //contained in the new ID. + $idPrefix = findCommonPrefix($ids); + $idSuffix = findCommonSuffix($ids); + if(strlen($idSuffix) < 2){ + $idSuffix = ""; + } + + $valueMap = []; + foreach(Utils::stringifyKeys($valueToId) as $value => $newId){ + $newValue = substr($newId, strlen($idPrefix), $idSuffix !== "" ? -strlen($idSuffix) : null); + if($newValue !== $value){ + $valueMap[$value] = $newValue; + } + } + + $flattenPropertyRules[$propertyName][$filter] = new BlockStateUpgradeSchemaFlattenedName( + $idPrefix, + $propertyName, + $idSuffix, + $valueMap + ); + } + } + ksort($flattenPropertyRules, SORT_STRING); + return $flattenPropertyRules; +} + /** * Attempts to compress a list of remapped states by looking at which state properties were consistently unchanged. * This significantly reduces the output size during flattening when the flattened block has many permutations @@ -327,9 +403,9 @@ function processRemappedStates(array $upgradeTable) : array{ $unchangedStatesByNewName[$newName] = $unchangedStates; } - $flattenedProperties = []; $notFlattenedProperties = []; - $notFlattenedPropertyValues = []; + + $candidateFlattenedValues = []; foreach($upgradeTable as $pair){ foreach(Utils::stringifyKeys($pair->old->getStates()) as $propertyName => $propertyValue){ if(isset($notFlattenedProperties[$propertyName])){ @@ -344,37 +420,41 @@ function processRemappedStates(array $upgradeTable) : array{ $notFlattenedProperties[$propertyName] = true; continue; } - $parts = explode($rawValue, $pair->new->getName(), 2); - if(count($parts) !== 2){ - //the new name does not contain the property value, but it may still be able to be flattened in other cases - $notFlattenedPropertyValues[$propertyName][$rawValue] = $rawValue; - continue; - } - [$prefix, $suffix] = $parts; $filter = $pair->old->getStates(); foreach($unchangedStatesByNewName[$pair->new->getName()] as $unchangedPropertyName){ unset($filter[$unchangedPropertyName]); } unset($filter[$propertyName]); + $rawFilter = encodeOrderedProperties($filter); - $flattenRule = new BlockStateUpgradeSchemaFlattenedName( - prefix: $prefix, - flattenedProperty: $propertyName, - suffix: $suffix - ); - if(!isset($flattenedProperties[$propertyName][$rawFilter])){ - $flattenedProperties[$propertyName][$rawFilter] = $flattenRule; - }elseif(!$flattenRule->equals($flattenedProperties[$propertyName][$rawFilter])){ - $notFlattenedProperties[$propertyName] = true; + if(isset($candidateFlattenedValues[$propertyName][$rawFilter])){ + $valuesToIds = $candidateFlattenedValues[$propertyName][$rawFilter]; + $existingNewId = $valuesToIds[$rawValue] ?? null; + if($existingNewId !== null && $existingNewId !== $pair->new->getName()){ + //this old value is associated with multiple new IDs - bad candidate for flattening + $notFlattenedProperties[$propertyName] = true; + continue; + } + foreach(Utils::stringifyKeys($valuesToIds) as $otherRawValue => $otherNewId){ + if($otherRawValue === $rawValue){ + continue; + } + if($otherNewId === $pair->new->getName()){ + //this old value maps to the same new ID as another old value - bad candidate for flattening + $notFlattenedProperties[$propertyName] = true; + continue 2; + } + } } + $candidateFlattenedValues[$propertyName][$rawFilter][$rawValue] = $pair->new->getName(); } } foreach(Utils::stringifyKeys($notFlattenedProperties) as $propertyName => $_){ - unset($flattenedProperties[$propertyName]); + unset($candidateFlattenedValues[$propertyName]); } - ksort($flattenedProperties, SORT_STRING); + $flattenedProperties = buildFlattenPropertyRules($candidateFlattenedValues); $flattenProperty = array_key_first($flattenedProperties); $list = []; @@ -393,19 +473,15 @@ function processRemappedStates(array $upgradeTable) : array{ } ksort($cleanedOldState); ksort($cleanedNewState); - $flattened = false; if($flattenProperty !== null){ $flattenedValue = $cleanedOldState[$flattenProperty] ?? null; if(!$flattenedValue instanceof StringTag){ - throw new AssumptionFailedError("This should always be a TAG_String"); - } - if(!isset($notFlattenedPropertyValues[$flattenProperty][$flattenedValue->getValue()])){ - unset($cleanedOldState[$flattenProperty]); - $flattened = true; + throw new AssumptionFailedError("This should always be a TAG_String ($newName $flattenProperty)"); } + unset($cleanedOldState[$flattenProperty]); } $rawOldState = encodeOrderedProperties($cleanedOldState); - $newNameRule = $flattenProperty !== null && $flattened ? + $newNameRule = $flattenProperty !== null ? $flattenedProperties[$flattenProperty][$rawOldState] ?? throw new AssumptionFailedError("This should always be set") : $newName; From b9288c238b46b830976bd362c17f799af937fcdd Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 5 Apr 2024 17:29:16 +0100 Subject: [PATCH 35/35] Update BedrockBlockUpgradeSchema --- composer.json | 2 +- composer.lock | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/composer.json b/composer.json index da6536ff6..47648d5b9 100644 --- a/composer.json +++ b/composer.json @@ -33,7 +33,7 @@ "composer-runtime-api": "^2.0", "adhocore/json-comment": "~1.2.0", "pocketmine/netresearch-jsonmapper": "~v4.4.999", - "pocketmine/bedrock-block-upgrade-schema": "~3.6.0+bedrock-1.20.70", + "pocketmine/bedrock-block-upgrade-schema": "~4.0.0+bedrock-1.20.70", "pocketmine/bedrock-data": "~2.9.0+bedrock-1.20.70", "pocketmine/bedrock-item-upgrade-schema": "~1.8.0+bedrock-1.20.70", "pocketmine/bedrock-protocol": "~29.0.0+bedrock-1.20.70", diff --git a/composer.lock b/composer.lock index 5e44bd686..fe11fbacf 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": "05f535c2b562b59c11b6ac535b5f1c99", + "content-hash": "40f8971303dc2060ae4e28e6fc84bdfc", "packages": [ { "name": "adhocore/json-comment", @@ -122,16 +122,16 @@ }, { "name": "pocketmine/bedrock-block-upgrade-schema", - "version": "3.6.0", + "version": "4.0.0", "source": { "type": "git", "url": "https://github.com/pmmp/BedrockBlockUpgradeSchema.git", - "reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d" + "reference": "ebd768e5b202cae59b0a7057982e3a2f40ba1954" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/1496e275db5148cb96bdaa998115e5e31a5c1e4d", - "reference": "1496e275db5148cb96bdaa998115e5e31a5c1e4d", + "url": "https://api.github.com/repos/pmmp/BedrockBlockUpgradeSchema/zipball/ebd768e5b202cae59b0a7057982e3a2f40ba1954", + "reference": "ebd768e5b202cae59b0a7057982e3a2f40ba1954", "shasum": "" }, "type": "library", @@ -142,9 +142,9 @@ "description": "Schemas describing how to upgrade saved block data in older Minecraft: Bedrock Edition world saves", "support": { "issues": "https://github.com/pmmp/BedrockBlockUpgradeSchema/issues", - "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/3.6.0" + "source": "https://github.com/pmmp/BedrockBlockUpgradeSchema/tree/4.0.0" }, - "time": "2024-02-28T19:25:25+00:00" + "time": "2024-04-05T16:02:20+00:00" }, { "name": "pocketmine/bedrock-data",