*/ private array $networkIdCache = []; /** Used when a blockstate can't be correctly serialized (e.g. because it's unknown) */ private BlockStateData $fallbackStateData; private int $fallbackStateId; private static function make() : self{ $canonicalBlockStatesFile = Path::join(\pocketmine\BEDROCK_DATA_PATH, "canonical_block_states.nbt"); $canonicalBlockStatesRaw = Utils::assumeNotFalse(file_get_contents($canonicalBlockStatesFile), "Missing required resource file"); $metaMappingFile = Path::join(\pocketmine\BEDROCK_DATA_PATH, 'block_state_meta_map.json'); $metaMappingRaw = Utils::assumeNotFalse(file_get_contents($metaMappingFile), "Missing required resource file"); return new self( BlockStateDictionary::loadFromString($canonicalBlockStatesRaw, $metaMappingRaw), GlobalBlockStateHandlers::getSerializer() ); } public function __construct( private BlockStateDictionary $blockStateDictionary, private BlockStateSerializer $blockStateSerializer ){ $this->fallbackStateId = $this->blockStateDictionary->lookupStateIdFromData( new BlockStateData(BlockTypeNames::INFO_UPDATE, [], BlockStateData::CURRENT_VERSION) ) ?? throw new AssumptionFailedError(BlockTypeNames::INFO_UPDATE . " should always exist"); //lookup the state data from the dictionary to avoid keeping two copies of the same data around $this->fallbackStateData = $this->blockStateDictionary->getDataFromStateId($this->fallbackStateId) ?? throw new AssumptionFailedError("We just looked up this state data, so it must exist"); } public function toRuntimeId(int $internalStateId) : int{ if(isset($this->networkIdCache[$internalStateId])){ return $this->networkIdCache[$internalStateId]; } try{ $blockStateData = $this->blockStateSerializer->serialize($internalStateId); $networkId = $this->blockStateDictionary->lookupStateIdFromData($blockStateData); if($networkId === null){ throw new AssumptionFailedError("Unmapped blockstate returned by blockstate serializer: " . $blockStateData->toNbt()); } }catch(BlockStateSerializeException){ //TODO: this will swallow any error caused by invalid block properties; this is not ideal, but it should be //covered by unit tests, so this is probably a safe assumption. $networkId = $this->fallbackStateId; } return $this->networkIdCache[$internalStateId] = $networkId; } /** * Looks up the network state data associated with the given internal state ID. */ public function toStateData(int $internalStateId) : BlockStateData{ //we don't directly use the blockstate serializer here - we can't assume that the network blockstate NBT is the //same as the disk blockstate NBT, in case we decide to have different world version than network version (or in //case someone wants to implement multi version). $networkRuntimeId = $this->toRuntimeId($internalStateId); return $this->blockStateDictionary->getDataFromStateId($networkRuntimeId) ?? throw new AssumptionFailedError("We just looked up this state ID, so it must exist"); } public function getBlockStateDictionary() : BlockStateDictionary{ return $this->blockStateDictionary; } public function getFallbackStateData() : BlockStateData{ return $this->fallbackStateData; } }