diff --git a/src/block/Block.php b/src/block/Block.php index 9aa0459aa..a6defb949 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -111,7 +111,7 @@ class Block{ * @internal */ public function getRuntimeId() : int{ - return RuntimeBlockMapping::toStaticRuntimeId($this->getId(), $this->getMeta()); + return RuntimeBlockMapping::getInstance()->toStaticRuntimeId($this->getId(), $this->getMeta()); } public function getMeta() : int{ diff --git a/src/network/mcpe/protocol/StartGamePacket.php b/src/network/mcpe/protocol/StartGamePacket.php index 4d9dc419f..f6ef854f9 100644 --- a/src/network/mcpe/protocol/StartGamePacket.php +++ b/src/network/mcpe/protocol/StartGamePacket.php @@ -289,7 +289,7 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{ if($this->blockTable === null){ if(self::$blockTableCache === null){ //this is a really nasty hack, but it'll do for now - self::$blockTableCache = (new NetworkNbtSerializer())->write(new TreeRoot(new ListTag(RuntimeBlockMapping::getBedrockKnownStates()))); + self::$blockTableCache = (new NetworkNbtSerializer())->write(new TreeRoot(new ListTag(RuntimeBlockMapping::getInstance()->getBedrockKnownStates()))); } $out->put(self::$blockTableCache); }else{ diff --git a/src/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/network/mcpe/protocol/types/RuntimeBlockMapping.php index 45b422dd8..da1c0ce79 100644 --- a/src/network/mcpe/protocol/types/RuntimeBlockMapping.php +++ b/src/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -39,19 +39,25 @@ use function shuffle; * @internal */ final class RuntimeBlockMapping{ + /** @var self|null */ + private static $instance = null; - /** @var int[] */ - private static $legacyToRuntimeMap = []; - /** @var int[] */ - private static $runtimeToLegacyMap = []; - /** @var CompoundTag[]|null */ - private static $bedrockKnownStates = null; + public static function getInstance() : self{ + if(self::$instance === null){ + self::$instance = new self; + } - private function __construct(){ - //NOOP + return self::$instance; } - public static function init() : void{ + /** @var int[] */ + private $legacyToRuntimeMap = []; + /** @var int[] */ + private $runtimeToLegacyMap = []; + /** @var CompoundTag[]|null */ + private $bedrockKnownStates = null; + + private function __construct(){ $tag = (new NetworkNbtSerializer())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/required_block_states.nbt"))->getTag(); if(!($tag instanceof ListTag) or $tag->getTagType() !== NBT::TAG_Compound){ //this is a little redundant currently, but good for auto complete and makes phpstan happy throw new \RuntimeException("Invalid blockstates table, expected TAG_List root"); @@ -59,12 +65,12 @@ final class RuntimeBlockMapping{ /** @var CompoundTag[] $list */ $list = $tag->getValue(); - self::$bedrockKnownStates = self::randomizeTable($list); + $this->bedrockKnownStates = self::randomizeTable($list); - self::setupLegacyMappings(); + $this->setupLegacyMappings(); } - private static function setupLegacyMappings() : void{ + private function setupLegacyMappings() : void{ $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/block_id_map.json"), true); $legacyStateMap = (new NetworkNbtSerializer())->read(file_get_contents(\pocketmine\RESOURCE_PATH . "vanilla/r12_to_current_block_map.nbt"))->getTag(); if(!($legacyStateMap instanceof ListTag) or $legacyStateMap->getTagType() !== NBT::TAG_Compound){ @@ -75,7 +81,7 @@ final class RuntimeBlockMapping{ * @var int[][] $idToStatesMap string id -> int[] list of candidate state indices */ $idToStatesMap = []; - foreach(self::$bedrockKnownStates as $k => $state){ + foreach($this->bedrockKnownStates as $k => $state){ $idToStatesMap[$state->getCompoundTag("block")->getString("name")][] = $k; } /** @var CompoundTag $pair */ @@ -93,9 +99,9 @@ final class RuntimeBlockMapping{ throw new \RuntimeException("Mapped new state does not appear in network table"); } foreach($idToStatesMap[$mappedName] as $k){ - $networkState = self::$bedrockKnownStates[$k]; + $networkState = $this->bedrockKnownStates[$k]; if($mappedState->equals($networkState->getCompoundTag("block"))){ - self::registerMapping($k, $id, $data); + $this->registerMapping($k, $id, $data); continue 2; } } @@ -103,12 +109,6 @@ final class RuntimeBlockMapping{ } } - private static function lazyInit() : void{ - if(self::$bedrockKnownStates === null){ - self::init(); - } - } - /** * Randomizes the order of the runtimeID table to prevent plugins relying on them. * Plugins shouldn't use this stuff anyway, but plugin devs have an irritating habit of ignoring what they @@ -126,35 +126,32 @@ final class RuntimeBlockMapping{ return $table; } - public static function toStaticRuntimeId(int $id, int $meta = 0) : int{ - self::lazyInit(); + public function toStaticRuntimeId(int $id, int $meta = 0) : int{ /* * try id+meta first * if not found, try id+0 (strip meta) * if still not found, return update! block */ - return self::$legacyToRuntimeMap[($id << 4) | $meta] ?? self::$legacyToRuntimeMap[$id << 4] ?? self::$legacyToRuntimeMap[BlockLegacyIds::INFO_UPDATE << 4]; + return $this->legacyToRuntimeMap[($id << 4) | $meta] ?? $this->legacyToRuntimeMap[$id << 4] ?? $this->legacyToRuntimeMap[BlockLegacyIds::INFO_UPDATE << 4]; } /** * @return int[] [id, meta] */ - public static function fromStaticRuntimeId(int $runtimeId) : array{ - self::lazyInit(); - $v = self::$runtimeToLegacyMap[$runtimeId]; + public function fromStaticRuntimeId(int $runtimeId) : array{ + $v = $this->runtimeToLegacyMap[$runtimeId]; return [$v >> 4, $v & 0xf]; } - private static function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{ - self::$legacyToRuntimeMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId; - self::$runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta; + private function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{ + $this->legacyToRuntimeMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId; + $this->runtimeToLegacyMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta; } /** * @return CompoundTag[] */ - public static function getBedrockKnownStates() : array{ - self::lazyInit(); - return self::$bedrockKnownStates; + public function getBedrockKnownStates() : array{ + return $this->bedrockKnownStates; } } diff --git a/src/network/mcpe/serializer/ChunkSerializer.php b/src/network/mcpe/serializer/ChunkSerializer.php index 2b1643ac0..06dc2e1f2 100644 --- a/src/network/mcpe/serializer/ChunkSerializer.php +++ b/src/network/mcpe/serializer/ChunkSerializer.php @@ -53,6 +53,7 @@ final class ChunkSerializer{ public static function serialize(Chunk $chunk, ?string $tiles = null) : string{ $stream = new NetworkBinaryStream(); $subChunkCount = self::getSubChunkCount($chunk); + $blockMapper = RuntimeBlockMapping::getInstance(); for($y = 0; $y < $subChunkCount; ++$y){ $layers = $chunk->getSubChunk($y)->getBlockLayers(); $stream->putByte(8); //version @@ -69,7 +70,7 @@ final class ChunkSerializer{ //zigzag and just shift directly. $stream->putUnsignedVarInt(count($palette) << 1); //yes, this is intentionally zigzag foreach($palette as $p){ - $stream->putUnsignedVarInt(RuntimeBlockMapping::toStaticRuntimeId($p >> 4, $p & 0xf) << 1); + $stream->putUnsignedVarInt($blockMapper->toStaticRuntimeId($p >> 4, $p & 0xf) << 1); } } }