diff --git a/src/block/RuntimeBlockStateRegistry.php b/src/block/RuntimeBlockStateRegistry.php index 5aef7006f..15106d227 100644 --- a/src/block/RuntimeBlockStateRegistry.php +++ b/src/block/RuntimeBlockStateRegistry.php @@ -29,6 +29,7 @@ use pocketmine\data\runtime\InvalidSerializedRuntimeDataException; use pocketmine\utils\AssumptionFailedError; use pocketmine\utils\SingletonTrait; use pocketmine\world\light\LightUpdate; +use function get_class; use function min; /** @@ -81,6 +82,34 @@ class RuntimeBlockStateRegistry{ } } + /** + * Generates all the possible valid blockstates for a given block type. + * + * @phpstan-return \Generator + */ + private static function generateAllStatesForType(Block $block) : \Generator{ + //TODO: this bruteforce approach to discovering all valid states is very inefficient for larger state data sizes + //at some point we'll need to find a better way to do this + $bits = $block->getRequiredTypeDataBits() + $block->getRequiredStateDataBits(); + if($bits > Block::INTERNAL_STATE_DATA_BITS){ + throw new \InvalidArgumentException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits"); + } + for($stateData = 0; $stateData < (1 << $bits); ++$stateData){ + $v = clone $block; + try{ + $v->decodeStateData($stateData); + if($v->computeStateData() !== $stateData){ + //TODO: this should probably be a hard error + throw new InvalidSerializedRuntimeDataException(get_class($block) . "::decodeStateData() accepts invalid state data (returned " . $v->computeStateData() . " for input $stateData)"); + } + }catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it + continue; + } + + yield $v; + } + } + /** * Maps a block type to its corresponding type ID. This is necessary for the block to be recognized when loading * from disk, and also when being read at runtime. @@ -102,24 +131,7 @@ class RuntimeBlockStateRegistry{ $this->typeIndex[$typeId] = clone $block; - //TODO: this bruteforce approach to discovering all valid states is very inefficient for larger state data sizes - //at some point we'll need to find a better way to do this - $bits = $block->getRequiredTypeDataBits() + $block->getRequiredStateDataBits(); - if($bits > Block::INTERNAL_STATE_DATA_BITS){ - throw new \InvalidArgumentException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits"); - } - for($stateData = 0; $stateData < (1 << $bits); ++$stateData){ - $v = clone $block; - try{ - $v->decodeStateData($stateData); - if($v->computeStateData() !== $stateData){ - //if the fullID comes back different, this is a broken state that we can't rely on; map it to default - throw new InvalidSerializedRuntimeDataException("Corrupted state"); - } - }catch(InvalidSerializedRuntimeDataException $e){ //invalid property combination, leave it - continue; - } - + foreach(self::generateAllStatesForType($block) as $v){ $this->fillStaticArrays($v->getStateId(), $v); } }