diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 726a71e66..787c49621 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -1195,7 +1195,6 @@ class Server{ EntityFactory::init(); TileFactory::init(); BlockFactory::init(); - BlockFactory::registerStaticRuntimeIdMappings(); Enchantment::init(); ItemFactory::init(); Item::initCreativeItems(); diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index 93784eeda..0eb7f1efc 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -39,6 +39,7 @@ use pocketmine\math\RayTraceResult; use pocketmine\math\Vector3; use pocketmine\metadata\Metadatable; use pocketmine\metadata\MetadataValue; +use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use pocketmine\Player; use pocketmine\plugin\Plugin; use pocketmine\tile\TileFactory; @@ -124,7 +125,7 @@ class Block extends Position implements BlockLegacyIds, Metadatable{ * @return int */ public function getRuntimeId() : int{ - return BlockFactory::toStaticRuntimeId($this->getId(), $this->getMeta()); + return RuntimeBlockMapping::toStaticRuntimeId($this->getId(), $this->getMeta()); } /** diff --git a/src/pocketmine/block/BlockFactory.php b/src/pocketmine/block/BlockFactory.php index 0074439f0..fbef665a0 100644 --- a/src/pocketmine/block/BlockFactory.php +++ b/src/pocketmine/block/BlockFactory.php @@ -32,13 +32,11 @@ use pocketmine\item\Item; use pocketmine\item\ItemFactory; use pocketmine\item\ItemIds; use pocketmine\level\Position; +use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use pocketmine\tile\Comparator; use function array_fill; use function array_filter; -use function file_get_contents; use function get_class; -use function json_decode; -use function max; use function min; /** @@ -55,15 +53,6 @@ class BlockFactory{ /** @var \SplFixedArray|float[] */ public static $blastResistance = null; - /** @var int[] */ - public static $staticRuntimeIdMap = []; - - /** @var int[] */ - public static $legacyIdMap = []; - - /** @var int */ - private static $lastRuntimeId = 0; - /** * Initializes the block factory. By default this is called only once on server start, however you may wish to use * this if you need to reset the block factory back to its original defaults for whatever reason. @@ -680,21 +669,9 @@ class BlockFactory{ return $b !== null and !($b instanceof UnknownBlock); } - public static function registerStaticRuntimeIdMappings() : void{ - /** @var mixed[] $runtimeIdMap */ - $runtimeIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true); - $legacyIdMap = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "legacy_id_map.json"), true); - foreach($runtimeIdMap as $k => $obj){ - //this has to use the json offset to make sure the mapping is consistent with what we send over network, even though we aren't using all the entries - if(!isset($legacyIdMap[$obj["name"]])){ - continue; - } - self::registerMapping($k, $legacyIdMap[$obj["name"]], $obj["data"]); - } - } - /** * @internal + * @deprecated * * @param int $id * @param int $meta @@ -702,15 +679,11 @@ class BlockFactory{ * @return int */ public static 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::$staticRuntimeIdMap[($id << 4) | $meta] ?? self::$staticRuntimeIdMap[$id << 4] ?? self::$staticRuntimeIdMap[BlockLegacyIds::INFO_UPDATE << 4]; + return RuntimeBlockMapping::toStaticRuntimeId($id, $meta); } /** + * @deprecated * @internal * * @param int $runtimeId @@ -718,14 +691,7 @@ class BlockFactory{ * @return int[] [id, meta] */ public static function fromStaticRuntimeId(int $runtimeId) : array{ - $v = self::$legacyIdMap[$runtimeId]; - return [$v >> 4, $v & 0xf]; - } - - private static function registerMapping(int $staticRuntimeId, int $legacyId, int $legacyMeta) : void{ - self::$staticRuntimeIdMap[($legacyId << 4) | $legacyMeta] = $staticRuntimeId; - self::$legacyIdMap[$staticRuntimeId] = ($legacyId << 4) | $legacyMeta; - self::$lastRuntimeId = max(self::$lastRuntimeId, $staticRuntimeId); + return RuntimeBlockMapping::fromStaticRuntimeId($runtimeId); } /** diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 5f6a88668..10743f696 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -79,6 +79,7 @@ use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\LevelEventPacket; use pocketmine\network\mcpe\protocol\SetDifficultyPacket; use pocketmine\network\mcpe\protocol\SetTimePacket; +use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use pocketmine\network\mcpe\protocol\UpdateBlockPacket; use pocketmine\Player; use pocketmine\plugin\Plugin; @@ -939,7 +940,7 @@ class Level implements ChunkManager, Metadatable{ $pk->blockRuntimeId = $b->getRuntimeId(); }else{ $fullBlock = $this->getFullBlock($b->x, $b->y, $b->z); - $pk->blockRuntimeId = BlockFactory::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf); + $pk->blockRuntimeId = RuntimeBlockMapping::toStaticRuntimeId($fullBlock >> 4, $fullBlock & 0xf); } $packets[] = $pk; diff --git a/src/pocketmine/level/format/Chunk.php b/src/pocketmine/level/format/Chunk.php index 1e338e34e..a523df3c7 100644 --- a/src/pocketmine/level/format/Chunk.php +++ b/src/pocketmine/level/format/Chunk.php @@ -33,6 +33,7 @@ use pocketmine\entity\EntityFactory; use pocketmine\level\Level; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\NetworkBinaryStream; +use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use pocketmine\Player; use pocketmine\tile\Spawnable; use pocketmine\tile\Tile; @@ -749,10 +750,6 @@ class Chunk{ $subChunkCount = $this->getSubChunkSendCount(); $stream->putByte($subChunkCount); - if(empty(BlockFactory::$staticRuntimeIdMap)){ - BlockFactory::registerStaticRuntimeIdMappings(); - } - for($y = 0; $y < $subChunkCount; ++$y){ $layers = $this->subChunks[$y]->getBlockLayers(); $stream->putByte(8); //version @@ -765,7 +762,7 @@ class Chunk{ $palette = $blocks->getPalette(); $stream->putVarInt(count($palette)); //yes, this is intentionally zigzag foreach($palette as $p){ - $stream->putVarInt(BlockFactory::toStaticRuntimeId($p >> 4, $p & 0xf)); + $stream->putVarInt(RuntimeBlockMapping::toStaticRuntimeId($p >> 4, $p & 0xf)); } } } diff --git a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php index 211cc24fb..09d736b8b 100644 --- a/src/pocketmine/network/mcpe/protocol/StartGamePacket.php +++ b/src/pocketmine/network/mcpe/protocol/StartGamePacket.php @@ -30,9 +30,8 @@ use pocketmine\math\Vector3; use pocketmine\network\mcpe\handler\SessionHandler; use pocketmine\network\mcpe\NetworkBinaryStream; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; +use pocketmine\network\mcpe\protocol\types\RuntimeBlockMapping; use function count; -use function file_get_contents; -use function json_decode; class StartGamePacket extends DataPacket implements ClientboundPacket{ public const NETWORK_ID = ProtocolInfo::START_GAME_PACKET; @@ -251,7 +250,7 @@ class StartGamePacket extends DataPacket implements ClientboundPacket{ if(self::$runtimeIdTable === null){ //this is a really nasty hack, but it'll do for now $stream = new NetworkBinaryStream(); - $data = json_decode(file_get_contents(\pocketmine\RESOURCE_PATH . "runtimeid_table.json"), true); + $data = RuntimeBlockMapping::getBedrockKnownStates(); $stream->putUnsignedVarInt(count($data)); foreach($data as $v){ $stream->putString($v["name"]); diff --git a/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php new file mode 100644 index 000000000..5a2f2d656 --- /dev/null +++ b/src/pocketmine/network/mcpe/protocol/types/RuntimeBlockMapping.php @@ -0,0 +1,118 @@ + $obj){ + //this has to use the json offset to make sure the mapping is consistent with what we send over network, even though we aren't using all the entries + if(!isset($legacyIdMap[$obj["name"]])){ + continue; + } + self::registerMapping($k, $legacyIdMap[$obj["name"]], $obj["data"]); + } + } + + /** + * 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 + * aren't supposed to do, so we have to deliberately break it to make them stop. + * + * @param array $table + * + * @return array + */ + private static function randomizeTable(array $table) : array{ + $postSeed = mt_rand(); //save a seed to set afterwards, to avoid poor quality randoms + mt_srand(getmypid() ?: 0); //Use a seed which is the same on all threads. This isn't a secure seed, but we don't care. + shuffle($table); + mt_srand($postSeed); //restore a good quality seed that isn't dependent on PID + return $table; + } + + /** + * @param int $id + * @param int $meta + * + * @return int + */ + public static 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]; + } + + /** + * @param int $runtimeId + * + * @return int[] [id, meta] + */ + public static function fromStaticRuntimeId(int $runtimeId) : array{ + $v = self::$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; + } + + /** + * @return array + */ + public static function getBedrockKnownStates() : array{ + return self::$bedrockKnownStates; + } +} +RuntimeBlockMapping::init();