From d4ca566fd0bb683a3932e21eb889ab1f6df4d6fb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 21 Apr 2023 20:38:28 +0100 Subject: [PATCH] Move block permutation generation into Block this allows sealing off a whole bunch of internal APIs. --- src/block/Block.php | 48 ++++++++++++++++--------- src/block/RuntimeBlockStateRegistry.php | 31 +--------------- tests/phpunit/block/BlockTest.php | 2 +- 3 files changed, 34 insertions(+), 47 deletions(-) diff --git a/src/block/Block.php b/src/block/Block.php index d5cd821fd..bb2224723 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -29,6 +29,7 @@ namespace pocketmine\block; use pocketmine\block\tile\Spawnable; use pocketmine\block\tile\Tile; use pocketmine\block\utils\SupportType; +use pocketmine\data\runtime\InvalidSerializedRuntimeDataException; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\data\runtime\RuntimeDataReader; use pocketmine\data\runtime\RuntimeDataSizeCalculator; @@ -185,14 +186,6 @@ class Block{ return new ItemBlock($normalized); } - final public function getRequiredTypeDataBits() : int{ - return $this->requiredTypeDataBits; - } - - final public function getRequiredStateDataBits() : int{ - return $this->requiredStateDataBits; - } - private function decodeTypeData(int $data) : void{ $reader = new RuntimeDataReader($this->requiredTypeDataBits, $data); @@ -213,10 +206,7 @@ class Block{ } } - /** - * @internal - */ - final public function decodeTypeAndStateData(int $data) : void{ + private function decodeTypeAndStateData(int $data) : void{ $reader = new RuntimeDataReader($this->requiredTypeDataBits + $this->requiredStateDataBits, $data); $this->decodeTypeData($reader->readInt($this->requiredTypeDataBits)); $this->decodeStateData($reader->readInt($this->requiredStateDataBits)); @@ -246,10 +236,7 @@ class Block{ return $writer->getValue(); } - /** - * @internal - */ - final public function computeTypeAndStateData() : int{ + private function computeTypeAndStateData() : int{ $writer = new RuntimeDataWriter($this->requiredTypeDataBits + $this->requiredStateDataBits); $writer->writeInt($this->requiredTypeDataBits, $this->computeTypeData()); $writer->writeInt($this->requiredStateDataBits, $this->computeStateData()); @@ -281,6 +268,35 @@ class Block{ //NOOP } + /** + * Generates copies of this Block in all possible state permutations. + * Every possible combination of known properties (e.g. facing, open/closed, powered/unpowered, on/off) will be + * generated. + * + * @phpstan-return \Generator + */ + public function generateStatePermutations() : \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 = $this->requiredTypeDataBits + $this->requiredStateDataBits; + if($bits > Block::INTERNAL_STATE_DATA_BITS){ + throw new \LogicException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits"); + } + for($stateData = 0; $stateData < (1 << $bits); ++$stateData){ + $v = clone $this; + try{ + $v->decodeTypeAndStateData($stateData); + if($v->computeTypeAndStateData() !== $stateData){ + throw new \LogicException(static::class . "::decodeStateData() accepts invalid state data (returned " . $v->computeTypeAndStateData() . " for input $stateData)"); + } + }catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it + continue; + } + + yield $v; + } + } + /** * Called when this block is created, set, or has a neighbouring block update, to re-detect dynamic properties which * are not saved on the world. diff --git a/src/block/RuntimeBlockStateRegistry.php b/src/block/RuntimeBlockStateRegistry.php index 080690c28..bc08c6a5d 100644 --- a/src/block/RuntimeBlockStateRegistry.php +++ b/src/block/RuntimeBlockStateRegistry.php @@ -25,11 +25,9 @@ namespace pocketmine\block; use pocketmine\block\BlockBreakInfo as BreakInfo; use pocketmine\block\BlockIdentifier as BID; -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; /** @@ -82,33 +80,6 @@ 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->decodeTypeAndStateData($stateData); - if($v->computeTypeAndStateData() !== $stateData){ - throw new \LogicException(get_class($block) . "::decodeStateData() accepts invalid state data (returned " . $v->computeTypeAndStateData() . " 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. @@ -130,7 +101,7 @@ class RuntimeBlockStateRegistry{ $this->typeIndex[$typeId] = clone $block; - foreach(self::generateAllStatesForType($block) as $v){ + foreach($block->generateStatePermutations() as $v){ $this->fillStaticArrays($v->getStateId(), $v); } } diff --git a/tests/phpunit/block/BlockTest.php b/tests/phpunit/block/BlockTest.php index cf0b674d6..e10ef7fc7 100644 --- a/tests/phpunit/block/BlockTest.php +++ b/tests/phpunit/block/BlockTest.php @@ -119,7 +119,7 @@ class BlockTest extends TestCase{ $states = $this->blockFactory->getAllKnownStates(); foreach($states as $stateId => $state){ - self::assertArrayHasKey($stateId, $knownStates, "New block state $stateId (" . $state->getTypeId() . ":" . $state->computeTypeAndStateData() . ", " . print_r($state, true) . ") - consistency check may need regenerating"); + 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);