diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index f9046017c..5414deabc 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -152,9 +152,6 @@ jobs: - name: Regenerate KnownTranslation APIs run: php build/generate-known-translation-apis.php - - name: Regenerate RuntimeEnum(De)serializer - run: php build/generate-runtime-enum-serializers.php - - name: Regenerate BedrockData available files constants run: php build/generate-bedrockdata-path-consts.php diff --git a/build/generate-runtime-enum-serializers.php b/build/generate-runtime-enum-serializers.php deleted file mode 100644 index d822fa539..000000000 --- a/build/generate-runtime-enum-serializers.php +++ /dev/null @@ -1,265 +0,0 @@ - $memberNames - * - * @return string[] - * @phpstan-return list - */ -function buildWriterFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{ - $bits = getBitsRequired($memberNames); - $lines = []; - - $lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{"; - $lines[] = "\t\$this->writeInt($bits, match(\$value){"; - - foreach($memberNames as $key => $memberName){ - $lines[] = "\t\t$memberName => $key,"; - } - $lines[] = "\t\tdefault => throw new \pocketmine\utils\AssumptionFailedError(\"All $virtualTypeName cases should be covered\")"; - $lines[] = "\t});"; - $lines[] = "}"; - - return $lines; -} - -/** - * @param string[] $memberNames - * @phpstan-param list $memberNames - * - * @return string[] - * @phpstan-return list - */ -function buildReaderFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{ - $bits = getBitsRequired($memberNames); - $lines = []; - - $lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{"; - $lines[] = "\t\$value = match(\$this->readInt($bits)){"; - - foreach($memberNames as $key => $memberName){ - $lines[] = "\t\t$key => $memberName,"; - } - $lines[] = "\t\tdefault => throw new InvalidSerializedRuntimeDataException(\"Invalid serialized value for $virtualTypeName\")"; - $lines[] = "\t};"; - $lines[] = "}"; - - return $lines; -} - -function buildInterfaceFunc(string $nativeTypeName, string $functionName) : string{ - return "public function $functionName(\\$nativeTypeName &\$value) : void;"; -} - -/** - * @param string[] $memberNames - * @phpstan-param list $memberNames - * - * @return string[] - * @phpstan-return list - */ -function buildSizeCalculationFunc(string $nativeTypeName, string $functionName, array $memberNames) : array{ - $lines = []; - $lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{"; - $lines[] = "\t\$this->addBits(" . getBitsRequired($memberNames) . ");"; - $lines[] = "}"; - - return $lines; -} - -/** - * @param mixed[] $members - */ -function getBitsRequired(array $members) : int{ - return (int) ceil(log(count($members), 2)); -} - -/** - * @param \UnitEnum[] $members - * @phpstan-param list<\UnitEnum> $members - * - * @return string[] - * @phpstan-return list - */ -function stringifyEnumMembers(array $members, string $enumClass) : array{ - usort($members, fn(\UnitEnum $a, \UnitEnum $b) => $a->name <=> $b->name); - return array_map(fn(\UnitEnum $case) => "\\$enumClass::$case->name", $members); -} - -$enumsUsed = [ - BellAttachmentType::cases(), - CopperOxidation::cases(), - CoralType::cases(), - DirtType::cases(), - DripleafState::cases(), - DyeColor::cases(), - FroglightType::cases(), - LeverFacing::cases(), - MedicineType::cases(), - MushroomBlockType::cases(), - MobHeadType::cases(), - SlabType::cases(), - SuspiciousStewType::cases(), - PotionType::cases() -]; - -$readerFuncs = [ - "" => [ - "abstract protected function readInt(int \$bits) : int;" - ] -]; -$writerFuncs = [ - "" => [ - "abstract protected function writeInt(int \$bits, int \$value) : void;" - ] -]; -$interfaceFuncs = []; -$sizeCalculationFuncs = [ - "" => [ - "abstract protected function addBits(int \$bits) : void;" - ] -]; - -foreach($enumsUsed as $enumMembers){ - if(count($enumMembers) === 0){ - throw new \InvalidArgumentException("Enum members cannot be empty"); - } - $reflect = new \ReflectionClass($enumMembers[array_key_first($enumMembers)]); - $virtualTypeName = $reflect->getShortName(); - $nativeTypeName = $reflect->getName(); - $functionName = lcfirst($virtualTypeName); - - $stringifiedMembers = stringifyEnumMembers($enumMembers, $nativeTypeName); - $writerFuncs[$functionName] = buildWriterFunc( - $virtualTypeName, - $nativeTypeName, - $stringifiedMembers, - $functionName - ); - $readerFuncs[$functionName] = buildReaderFunc( - $virtualTypeName, - $nativeTypeName, - $stringifiedMembers, - $functionName - ); - $interfaceFuncs[$functionName] = [buildInterfaceFunc( - $nativeTypeName, - $functionName - )]; - $sizeCalculationFuncs[$functionName] = buildSizeCalculationFunc( - $nativeTypeName, - $functionName, - $stringifiedMembers - ); -} - -/** - * @param string[][] $functions - * @phpstan-param array> $functions - */ -function printFunctions(array $functions, string $className, string $classType) : void{ - ksort($functions, SORT_STRING); - - ob_start(); - - echo <<<'HEADER' - "\t" . implode("\n\t", $functionLines), $functions)); - echo "\n\n}\n"; - - file_put_contents(dirname(__DIR__) . '/src/data/runtime/' . $className . '.php', ob_get_clean()); -} - -printFunctions($writerFuncs, "RuntimeEnumSerializerTrait", "trait"); -printFunctions($readerFuncs, "RuntimeEnumDeserializerTrait", "trait"); -printFunctions($interfaceFuncs, "RuntimeEnumDescriber", "interface"); -printFunctions($sizeCalculationFuncs, "RuntimeEnumSizeCalculatorTrait", "trait"); - -echo "Done. Don't forget to run CS fixup after generating code.\n"; diff --git a/src/block/Bell.php b/src/block/Bell.php index e91082a26..ec033cef8 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -44,7 +44,7 @@ final class Bell extends Transparent{ private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->bellAttachmentType($this->attachmentType); + $w->enum($this->attachmentType); $w->horizontalFacing($this->facing); } diff --git a/src/block/BigDripleafHead.php b/src/block/BigDripleafHead.php index cb64423d3..a9b87bf7f 100644 --- a/src/block/BigDripleafHead.php +++ b/src/block/BigDripleafHead.php @@ -39,7 +39,7 @@ class BigDripleafHead extends BaseBigDripleaf{ protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ parent::describeBlockOnlyState($w); - $w->dripleafState($this->leafState); + $w->enum($this->leafState); } protected function isHead() : bool{ diff --git a/src/block/Dirt.php b/src/block/Dirt.php index 6f3d62e7f..b3a3c6090 100644 --- a/src/block/Dirt.php +++ b/src/block/Dirt.php @@ -41,7 +41,7 @@ class Dirt extends Opaque{ protected DirtType $dirtType = DirtType::NORMAL; public function describeBlockItemState(RuntimeDataDescriber $w) : void{ - $w->dirtType($this->dirtType); + $w->enum($this->dirtType); } public function getDirtType() : DirtType{ return $this->dirtType; } diff --git a/src/block/Froglight.php b/src/block/Froglight.php index 2c54b830a..562b33f17 100644 --- a/src/block/Froglight.php +++ b/src/block/Froglight.php @@ -31,7 +31,7 @@ final class Froglight extends SimplePillar{ private FroglightType $froglightType = FroglightType::OCHRE; public function describeBlockItemState(RuntimeDataDescriber $w) : void{ - $w->froglightType($this->froglightType); + $w->enum($this->froglightType); } public function getFroglightType() : FroglightType{ return $this->froglightType; } diff --git a/src/block/Lever.php b/src/block/Lever.php index 01aa89245..d2b98efc3 100644 --- a/src/block/Lever.php +++ b/src/block/Lever.php @@ -40,7 +40,7 @@ class Lever extends Flowable{ protected bool $activated = false; protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->leverFacing($this->facing); + $w->enum($this->facing); $w->bool($this->activated); } diff --git a/src/block/MobHead.php b/src/block/MobHead.php index 125bafe8b..f4e945841 100644 --- a/src/block/MobHead.php +++ b/src/block/MobHead.php @@ -45,7 +45,7 @@ class MobHead extends Flowable{ protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling public function describeBlockItemState(RuntimeDataDescriber $w) : void{ - $w->mobHeadType($this->mobHeadType); + $w->enum($this->mobHeadType); } protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ diff --git a/src/block/RedMushroomBlock.php b/src/block/RedMushroomBlock.php index cf368a30a..0a7cb30f2 100644 --- a/src/block/RedMushroomBlock.php +++ b/src/block/RedMushroomBlock.php @@ -34,7 +34,7 @@ class RedMushroomBlock extends Opaque{ public function describeBlockItemState(RuntimeDataDescriber $w) : void{ //these blocks always drop as all-cap, but may exist in other forms in the inventory (particularly creative), //so this information needs to be kept in the type info - $w->mushroomBlockType($this->mushroomBlockType); + $w->enum($this->mushroomBlockType); } public function getMushroomBlockType() : MushroomBlockType{ return $this->mushroomBlockType; } diff --git a/src/block/Slab.php b/src/block/Slab.php index bdf2bb5eb..6000bec39 100644 --- a/src/block/Slab.php +++ b/src/block/Slab.php @@ -41,7 +41,7 @@ class Slab extends Transparent{ } protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{ - $w->slabType($this->slabType); + $w->enum($this->slabType); } public function isTransparent() : bool{ diff --git a/src/block/utils/ColoredTrait.php b/src/block/utils/ColoredTrait.php index 11e98167e..2ecd58e20 100644 --- a/src/block/utils/ColoredTrait.php +++ b/src/block/utils/ColoredTrait.php @@ -32,7 +32,7 @@ trait ColoredTrait{ /** @see Block::describeBlockItemState() */ public function describeBlockItemState(RuntimeDataDescriber $w) : void{ - $w->dyeColor($this->color); + $w->enum($this->color); } public function getColor() : DyeColor{ return $this->color; } diff --git a/src/block/utils/CopperTrait.php b/src/block/utils/CopperTrait.php index f0d708821..5ad8aa82d 100644 --- a/src/block/utils/CopperTrait.php +++ b/src/block/utils/CopperTrait.php @@ -38,7 +38,7 @@ trait CopperTrait{ private bool $waxed = false; public function describeBlockItemState(RuntimeDataDescriber $w) : void{ - $w->copperOxidation($this->oxidation); + $w->enum($this->oxidation); $w->bool($this->waxed); } diff --git a/src/block/utils/CoralTypeTrait.php b/src/block/utils/CoralTypeTrait.php index fcd7dbf24..a335bf9ec 100644 --- a/src/block/utils/CoralTypeTrait.php +++ b/src/block/utils/CoralTypeTrait.php @@ -32,7 +32,7 @@ trait CoralTypeTrait{ /** @see Block::describeBlockItemState() */ public function describeBlockItemState(RuntimeDataDescriber $w) : void{ - $w->coralType($this->coralType); + $w->enum($this->coralType); $w->bool($this->dead); } diff --git a/src/data/runtime/RuntimeEnumSizeCalculatorTrait.php b/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php similarity index 74% rename from src/data/runtime/RuntimeEnumSizeCalculatorTrait.php rename to src/data/runtime/LegacyRuntimeEnumDescriberTrait.php index 2ab62f03e..dd35fabfb 100644 --- a/src/data/runtime/RuntimeEnumSizeCalculatorTrait.php +++ b/src/data/runtime/LegacyRuntimeEnumDescriberTrait.php @@ -24,67 +24,71 @@ declare(strict_types=1); namespace pocketmine\data\runtime; /** - * This class is auto-generated. Do not edit it manually. - * @see build/generate-runtime-enum-serializers.php + * Provides backwards-compatible shims for the old codegen'd enum describer methods. + * This is kept for plugin backwards compatibility, but these functions should not be used in new code. + * @deprecated */ -trait RuntimeEnumSizeCalculatorTrait{ +trait LegacyRuntimeEnumDescriberTrait{ - abstract protected function addBits(int $bits) : void; + /** + * @phpstan-template T of \UnitEnum + * @phpstan-param T $case + */ + abstract protected function enum(\UnitEnum &$case) : void; public function bellAttachmentType(\pocketmine\block\utils\BellAttachmentType &$value) : void{ - $this->addBits(2); + $this->enum($value); } public function copperOxidation(\pocketmine\block\utils\CopperOxidation &$value) : void{ - $this->addBits(2); + $this->enum($value); } public function coralType(\pocketmine\block\utils\CoralType &$value) : void{ - $this->addBits(3); + $this->enum($value); } public function dirtType(\pocketmine\block\utils\DirtType &$value) : void{ - $this->addBits(2); + $this->enum($value); } public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{ - $this->addBits(2); + $this->enum($value); } public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{ - $this->addBits(4); + $this->enum($value); } public function froglightType(\pocketmine\block\utils\FroglightType &$value) : void{ - $this->addBits(2); + $this->enum($value); } public function leverFacing(\pocketmine\block\utils\LeverFacing &$value) : void{ - $this->addBits(3); + $this->enum($value); } public function medicineType(\pocketmine\item\MedicineType &$value) : void{ - $this->addBits(2); + $this->enum($value); } public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{ - $this->addBits(3); + $this->enum($value); } public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{ - $this->addBits(4); + $this->enum($value); } public function potionType(\pocketmine\item\PotionType &$value) : void{ - $this->addBits(6); + $this->enum($value); } public function slabType(\pocketmine\block\utils\SlabType &$value) : void{ - $this->addBits(2); + $this->enum($value); } public function suspiciousStewType(\pocketmine\item\SuspiciousStewType &$value) : void{ - $this->addBits(4); + $this->enum($value); } - } diff --git a/src/data/runtime/RuntimeDataDescriber.php b/src/data/runtime/RuntimeDataDescriber.php index 36822a6f3..2d8c52ec2 100644 --- a/src/data/runtime/RuntimeDataDescriber.php +++ b/src/data/runtime/RuntimeDataDescriber.php @@ -77,4 +77,10 @@ interface RuntimeDataDescriber extends RuntimeEnumDescriber{ public function railShape(int &$railShape) : void; public function straightOnlyRailShape(int &$railShape) : void; + + /** + * @phpstan-template T of \UnitEnum + * @phpstan-param T $case + */ + public function enum(\UnitEnum &$case) : void; } diff --git a/src/data/runtime/RuntimeDataReader.php b/src/data/runtime/RuntimeDataReader.php index 1677cea36..9b6445073 100644 --- a/src/data/runtime/RuntimeDataReader.php +++ b/src/data/runtime/RuntimeDataReader.php @@ -29,11 +29,12 @@ use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\utils\AssumptionFailedError; +use function get_class; use function intdiv; use function spl_object_id; final class RuntimeDataReader implements RuntimeDataDescriber{ - use RuntimeEnumDeserializerTrait; + use LegacyRuntimeEnumDescriberTrait; private int $offset = 0; @@ -210,5 +211,16 @@ final class RuntimeDataReader implements RuntimeDataDescriber{ $railShape = $result; } + public function enum(\UnitEnum &$case) : void{ + $metadata = RuntimeEnumMetadata::from($case); + $raw = $this->readInt($metadata->bits); + $result = $metadata->intToEnum($raw); + if($result === null){ + throw new InvalidSerializedRuntimeDataException("Invalid serialized value $raw for " . get_class($case)); + } + + $case = $result; + } + public function getOffset() : int{ return $this->offset; } } diff --git a/src/data/runtime/RuntimeDataSizeCalculator.php b/src/data/runtime/RuntimeDataSizeCalculator.php index 352e8e1a1..82e360213 100644 --- a/src/data/runtime/RuntimeDataSizeCalculator.php +++ b/src/data/runtime/RuntimeDataSizeCalculator.php @@ -28,7 +28,7 @@ use pocketmine\math\Facing; use function count; final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ - use RuntimeEnumSizeCalculatorTrait; + use LegacyRuntimeEnumDescriberTrait; private int $bits = 0; @@ -95,4 +95,9 @@ final class RuntimeDataSizeCalculator implements RuntimeDataDescriber{ public function straightOnlyRailShape(int &$railShape) : void{ $this->addBits(3); } + + public function enum(\UnitEnum &$case) : void{ + $metadata = RuntimeEnumMetadata::from($case); + $this->addBits($metadata->bits); + } } diff --git a/src/data/runtime/RuntimeDataWriter.php b/src/data/runtime/RuntimeDataWriter.php index 3a5855a25..9e78875ea 100644 --- a/src/data/runtime/RuntimeDataWriter.php +++ b/src/data/runtime/RuntimeDataWriter.php @@ -31,7 +31,7 @@ use function array_flip; use function spl_object_id; final class RuntimeDataWriter implements RuntimeDataDescriber{ - use RuntimeEnumSerializerTrait; + use LegacyRuntimeEnumDescriberTrait; private int $value = 0; private int $offset = 0; @@ -174,6 +174,11 @@ final class RuntimeDataWriter implements RuntimeDataDescriber{ $this->int(3, $railShape); } + public function enum(\UnitEnum &$case) : void{ + $metadata = RuntimeEnumMetadata::from($case); + $this->writeInt($metadata->bits, $metadata->enumToInt($case)); + } + public function getValue() : int{ return $this->value; } public function getOffset() : int{ return $this->offset; } diff --git a/src/data/runtime/RuntimeEnumDescriber.php b/src/data/runtime/RuntimeEnumDescriber.php index 7103017f7..79550d041 100644 --- a/src/data/runtime/RuntimeEnumDescriber.php +++ b/src/data/runtime/RuntimeEnumDescriber.php @@ -24,8 +24,9 @@ declare(strict_types=1); namespace pocketmine\data\runtime; /** - * This class is auto-generated. Do not edit it manually. - * @see build/generate-runtime-enum-serializers.php + * Provides backwards-compatible shims for the old codegen'd enum describer methods. + * This is kept for plugin backwards compatibility, but these functions should not be used in new code. + * @deprecated */ interface RuntimeEnumDescriber{ diff --git a/src/data/runtime/RuntimeEnumDeserializerTrait.php b/src/data/runtime/RuntimeEnumDeserializerTrait.php deleted file mode 100644 index aa0c1bd7b..000000000 --- a/src/data/runtime/RuntimeEnumDeserializerTrait.php +++ /dev/null @@ -1,243 +0,0 @@ -readInt(2)){ - 0 => \pocketmine\block\utils\BellAttachmentType::CEILING, - 1 => \pocketmine\block\utils\BellAttachmentType::FLOOR, - 2 => \pocketmine\block\utils\BellAttachmentType::ONE_WALL, - 3 => \pocketmine\block\utils\BellAttachmentType::TWO_WALLS, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for BellAttachmentType") - }; - } - - public function copperOxidation(\pocketmine\block\utils\CopperOxidation &$value) : void{ - $value = match($this->readInt(2)){ - 0 => \pocketmine\block\utils\CopperOxidation::EXPOSED, - 1 => \pocketmine\block\utils\CopperOxidation::NONE, - 2 => \pocketmine\block\utils\CopperOxidation::OXIDIZED, - 3 => \pocketmine\block\utils\CopperOxidation::WEATHERED, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for CopperOxidation") - }; - } - - public function coralType(\pocketmine\block\utils\CoralType &$value) : void{ - $value = match($this->readInt(3)){ - 0 => \pocketmine\block\utils\CoralType::BRAIN, - 1 => \pocketmine\block\utils\CoralType::BUBBLE, - 2 => \pocketmine\block\utils\CoralType::FIRE, - 3 => \pocketmine\block\utils\CoralType::HORN, - 4 => \pocketmine\block\utils\CoralType::TUBE, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for CoralType") - }; - } - - public function dirtType(\pocketmine\block\utils\DirtType &$value) : void{ - $value = match($this->readInt(2)){ - 0 => \pocketmine\block\utils\DirtType::COARSE, - 1 => \pocketmine\block\utils\DirtType::NORMAL, - 2 => \pocketmine\block\utils\DirtType::ROOTED, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for DirtType") - }; - } - - public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{ - $value = match($this->readInt(2)){ - 0 => \pocketmine\block\utils\DripleafState::FULL_TILT, - 1 => \pocketmine\block\utils\DripleafState::PARTIAL_TILT, - 2 => \pocketmine\block\utils\DripleafState::STABLE, - 3 => \pocketmine\block\utils\DripleafState::UNSTABLE, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for DripleafState") - }; - } - - public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{ - $value = match($this->readInt(4)){ - 0 => \pocketmine\block\utils\DyeColor::BLACK, - 1 => \pocketmine\block\utils\DyeColor::BLUE, - 2 => \pocketmine\block\utils\DyeColor::BROWN, - 3 => \pocketmine\block\utils\DyeColor::CYAN, - 4 => \pocketmine\block\utils\DyeColor::GRAY, - 5 => \pocketmine\block\utils\DyeColor::GREEN, - 6 => \pocketmine\block\utils\DyeColor::LIGHT_BLUE, - 7 => \pocketmine\block\utils\DyeColor::LIGHT_GRAY, - 8 => \pocketmine\block\utils\DyeColor::LIME, - 9 => \pocketmine\block\utils\DyeColor::MAGENTA, - 10 => \pocketmine\block\utils\DyeColor::ORANGE, - 11 => \pocketmine\block\utils\DyeColor::PINK, - 12 => \pocketmine\block\utils\DyeColor::PURPLE, - 13 => \pocketmine\block\utils\DyeColor::RED, - 14 => \pocketmine\block\utils\DyeColor::WHITE, - 15 => \pocketmine\block\utils\DyeColor::YELLOW, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for DyeColor") - }; - } - - public function froglightType(\pocketmine\block\utils\FroglightType &$value) : void{ - $value = match($this->readInt(2)){ - 0 => \pocketmine\block\utils\FroglightType::OCHRE, - 1 => \pocketmine\block\utils\FroglightType::PEARLESCENT, - 2 => \pocketmine\block\utils\FroglightType::VERDANT, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for FroglightType") - }; - } - - public function leverFacing(\pocketmine\block\utils\LeverFacing &$value) : void{ - $value = match($this->readInt(3)){ - 0 => \pocketmine\block\utils\LeverFacing::DOWN_AXIS_X, - 1 => \pocketmine\block\utils\LeverFacing::DOWN_AXIS_Z, - 2 => \pocketmine\block\utils\LeverFacing::EAST, - 3 => \pocketmine\block\utils\LeverFacing::NORTH, - 4 => \pocketmine\block\utils\LeverFacing::SOUTH, - 5 => \pocketmine\block\utils\LeverFacing::UP_AXIS_X, - 6 => \pocketmine\block\utils\LeverFacing::UP_AXIS_Z, - 7 => \pocketmine\block\utils\LeverFacing::WEST, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for LeverFacing") - }; - } - - public function medicineType(\pocketmine\item\MedicineType &$value) : void{ - $value = match($this->readInt(2)){ - 0 => \pocketmine\item\MedicineType::ANTIDOTE, - 1 => \pocketmine\item\MedicineType::ELIXIR, - 2 => \pocketmine\item\MedicineType::EYE_DROPS, - 3 => \pocketmine\item\MedicineType::TONIC, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MedicineType") - }; - } - - public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{ - $value = match($this->readInt(3)){ - 0 => \pocketmine\block\utils\MobHeadType::CREEPER, - 1 => \pocketmine\block\utils\MobHeadType::DRAGON, - 2 => \pocketmine\block\utils\MobHeadType::PIGLIN, - 3 => \pocketmine\block\utils\MobHeadType::PLAYER, - 4 => \pocketmine\block\utils\MobHeadType::SKELETON, - 5 => \pocketmine\block\utils\MobHeadType::WITHER_SKELETON, - 6 => \pocketmine\block\utils\MobHeadType::ZOMBIE, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MobHeadType") - }; - } - - public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{ - $value = match($this->readInt(4)){ - 0 => \pocketmine\block\utils\MushroomBlockType::ALL_CAP, - 1 => \pocketmine\block\utils\MushroomBlockType::CAP_EAST, - 2 => \pocketmine\block\utils\MushroomBlockType::CAP_MIDDLE, - 3 => \pocketmine\block\utils\MushroomBlockType::CAP_NORTH, - 4 => \pocketmine\block\utils\MushroomBlockType::CAP_NORTHEAST, - 5 => \pocketmine\block\utils\MushroomBlockType::CAP_NORTHWEST, - 6 => \pocketmine\block\utils\MushroomBlockType::CAP_SOUTH, - 7 => \pocketmine\block\utils\MushroomBlockType::CAP_SOUTHEAST, - 8 => \pocketmine\block\utils\MushroomBlockType::CAP_SOUTHWEST, - 9 => \pocketmine\block\utils\MushroomBlockType::CAP_WEST, - 10 => \pocketmine\block\utils\MushroomBlockType::PORES, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for MushroomBlockType") - }; - } - - public function potionType(\pocketmine\item\PotionType &$value) : void{ - $value = match($this->readInt(6)){ - 0 => \pocketmine\item\PotionType::AWKWARD, - 1 => \pocketmine\item\PotionType::FIRE_RESISTANCE, - 2 => \pocketmine\item\PotionType::HARMING, - 3 => \pocketmine\item\PotionType::HEALING, - 4 => \pocketmine\item\PotionType::INVISIBILITY, - 5 => \pocketmine\item\PotionType::LEAPING, - 6 => \pocketmine\item\PotionType::LONG_FIRE_RESISTANCE, - 7 => \pocketmine\item\PotionType::LONG_INVISIBILITY, - 8 => \pocketmine\item\PotionType::LONG_LEAPING, - 9 => \pocketmine\item\PotionType::LONG_MUNDANE, - 10 => \pocketmine\item\PotionType::LONG_NIGHT_VISION, - 11 => \pocketmine\item\PotionType::LONG_POISON, - 12 => \pocketmine\item\PotionType::LONG_REGENERATION, - 13 => \pocketmine\item\PotionType::LONG_SLOWNESS, - 14 => \pocketmine\item\PotionType::LONG_SLOW_FALLING, - 15 => \pocketmine\item\PotionType::LONG_STRENGTH, - 16 => \pocketmine\item\PotionType::LONG_SWIFTNESS, - 17 => \pocketmine\item\PotionType::LONG_TURTLE_MASTER, - 18 => \pocketmine\item\PotionType::LONG_WATER_BREATHING, - 19 => \pocketmine\item\PotionType::LONG_WEAKNESS, - 20 => \pocketmine\item\PotionType::MUNDANE, - 21 => \pocketmine\item\PotionType::NIGHT_VISION, - 22 => \pocketmine\item\PotionType::POISON, - 23 => \pocketmine\item\PotionType::REGENERATION, - 24 => \pocketmine\item\PotionType::SLOWNESS, - 25 => \pocketmine\item\PotionType::SLOW_FALLING, - 26 => \pocketmine\item\PotionType::STRENGTH, - 27 => \pocketmine\item\PotionType::STRONG_HARMING, - 28 => \pocketmine\item\PotionType::STRONG_HEALING, - 29 => \pocketmine\item\PotionType::STRONG_LEAPING, - 30 => \pocketmine\item\PotionType::STRONG_POISON, - 31 => \pocketmine\item\PotionType::STRONG_REGENERATION, - 32 => \pocketmine\item\PotionType::STRONG_SLOWNESS, - 33 => \pocketmine\item\PotionType::STRONG_STRENGTH, - 34 => \pocketmine\item\PotionType::STRONG_SWIFTNESS, - 35 => \pocketmine\item\PotionType::STRONG_TURTLE_MASTER, - 36 => \pocketmine\item\PotionType::SWIFTNESS, - 37 => \pocketmine\item\PotionType::THICK, - 38 => \pocketmine\item\PotionType::TURTLE_MASTER, - 39 => \pocketmine\item\PotionType::WATER, - 40 => \pocketmine\item\PotionType::WATER_BREATHING, - 41 => \pocketmine\item\PotionType::WEAKNESS, - 42 => \pocketmine\item\PotionType::WITHER, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for PotionType") - }; - } - - public function slabType(\pocketmine\block\utils\SlabType &$value) : void{ - $value = match($this->readInt(2)){ - 0 => \pocketmine\block\utils\SlabType::BOTTOM, - 1 => \pocketmine\block\utils\SlabType::DOUBLE, - 2 => \pocketmine\block\utils\SlabType::TOP, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for SlabType") - }; - } - - public function suspiciousStewType(\pocketmine\item\SuspiciousStewType &$value) : void{ - $value = match($this->readInt(4)){ - 0 => \pocketmine\item\SuspiciousStewType::ALLIUM, - 1 => \pocketmine\item\SuspiciousStewType::AZURE_BLUET, - 2 => \pocketmine\item\SuspiciousStewType::BLUE_ORCHID, - 3 => \pocketmine\item\SuspiciousStewType::CORNFLOWER, - 4 => \pocketmine\item\SuspiciousStewType::DANDELION, - 5 => \pocketmine\item\SuspiciousStewType::LILY_OF_THE_VALLEY, - 6 => \pocketmine\item\SuspiciousStewType::OXEYE_DAISY, - 7 => \pocketmine\item\SuspiciousStewType::POPPY, - 8 => \pocketmine\item\SuspiciousStewType::TULIP, - 9 => \pocketmine\item\SuspiciousStewType::WITHER_ROSE, - default => throw new InvalidSerializedRuntimeDataException("Invalid serialized value for SuspiciousStewType") - }; - } - -} diff --git a/src/data/runtime/RuntimeEnumMetadata.php b/src/data/runtime/RuntimeEnumMetadata.php new file mode 100644 index 000000000..261b7a1bc --- /dev/null +++ b/src/data/runtime/RuntimeEnumMetadata.php @@ -0,0 +1,114 @@ + + */ + private readonly array $intToEnum; + /** + * @var int[] + * @phpstan-var array + */ + private readonly array $enumToInt; + + /** + * @param \UnitEnum[] $members + * @phpstan-param list $members + */ + public function __construct( + array $members + ){ + usort($members, fn(\UnitEnum $a, \UnitEnum $b) => $a->name <=> $b->name); //sort by name to ensure consistent ordering (and thus consistent bit assignments) + + $this->bits = (int) ceil(log(count($members), 2)); + $this->intToEnum = array_values($members); + + $reversed = []; + foreach($this->intToEnum as $int => $enum){ + $reversed[spl_object_id($enum)] = $int; + } + + $this->enumToInt = $reversed; + } + + /** + * @phpstan-return T|null + */ + public function intToEnum(int $value) : ?object{ + return $this->intToEnum[$value] ?? null; + } + + /** + * @phpstan-param T $enum + */ + public function enumToInt(object $enum) : int{ + return $this->enumToInt[spl_object_id($enum)]; + } + + /** + * @var self[] + * @phpstan-var array + */ + private static array $cache = []; + + /** + * @phpstan-template TEnum of \UnitEnum + * @phpstan-param TEnum $case + * + * @phpstan-return self + */ + public static function from(\UnitEnum $case) : self{ + $class = $case::class; + /** @phpstan-var self|null $metadata */ + $metadata = self::$cache[$class] ?? null; + if($metadata === null){ + /** + * PHPStan can't infer this correctly :( https://github.com/phpstan/phpstan/issues/7162 + * @phpstan-var list $cases + */ + $cases = $case::cases(); + self::$cache[$class] = $metadata = new self($cases); + } + + return $metadata; + } +} diff --git a/src/data/runtime/RuntimeEnumSerializerTrait.php b/src/data/runtime/RuntimeEnumSerializerTrait.php deleted file mode 100644 index 12a205a9e..000000000 --- a/src/data/runtime/RuntimeEnumSerializerTrait.php +++ /dev/null @@ -1,243 +0,0 @@ -writeInt(2, match($value){ - \pocketmine\block\utils\BellAttachmentType::CEILING => 0, - \pocketmine\block\utils\BellAttachmentType::FLOOR => 1, - \pocketmine\block\utils\BellAttachmentType::ONE_WALL => 2, - \pocketmine\block\utils\BellAttachmentType::TWO_WALLS => 3, - default => throw new \pocketmine\utils\AssumptionFailedError("All BellAttachmentType cases should be covered") - }); - } - - public function copperOxidation(\pocketmine\block\utils\CopperOxidation &$value) : void{ - $this->writeInt(2, match($value){ - \pocketmine\block\utils\CopperOxidation::EXPOSED => 0, - \pocketmine\block\utils\CopperOxidation::NONE => 1, - \pocketmine\block\utils\CopperOxidation::OXIDIZED => 2, - \pocketmine\block\utils\CopperOxidation::WEATHERED => 3, - default => throw new \pocketmine\utils\AssumptionFailedError("All CopperOxidation cases should be covered") - }); - } - - public function coralType(\pocketmine\block\utils\CoralType &$value) : void{ - $this->writeInt(3, match($value){ - \pocketmine\block\utils\CoralType::BRAIN => 0, - \pocketmine\block\utils\CoralType::BUBBLE => 1, - \pocketmine\block\utils\CoralType::FIRE => 2, - \pocketmine\block\utils\CoralType::HORN => 3, - \pocketmine\block\utils\CoralType::TUBE => 4, - default => throw new \pocketmine\utils\AssumptionFailedError("All CoralType cases should be covered") - }); - } - - public function dirtType(\pocketmine\block\utils\DirtType &$value) : void{ - $this->writeInt(2, match($value){ - \pocketmine\block\utils\DirtType::COARSE => 0, - \pocketmine\block\utils\DirtType::NORMAL => 1, - \pocketmine\block\utils\DirtType::ROOTED => 2, - default => throw new \pocketmine\utils\AssumptionFailedError("All DirtType cases should be covered") - }); - } - - public function dripleafState(\pocketmine\block\utils\DripleafState &$value) : void{ - $this->writeInt(2, match($value){ - \pocketmine\block\utils\DripleafState::FULL_TILT => 0, - \pocketmine\block\utils\DripleafState::PARTIAL_TILT => 1, - \pocketmine\block\utils\DripleafState::STABLE => 2, - \pocketmine\block\utils\DripleafState::UNSTABLE => 3, - default => throw new \pocketmine\utils\AssumptionFailedError("All DripleafState cases should be covered") - }); - } - - public function dyeColor(\pocketmine\block\utils\DyeColor &$value) : void{ - $this->writeInt(4, match($value){ - \pocketmine\block\utils\DyeColor::BLACK => 0, - \pocketmine\block\utils\DyeColor::BLUE => 1, - \pocketmine\block\utils\DyeColor::BROWN => 2, - \pocketmine\block\utils\DyeColor::CYAN => 3, - \pocketmine\block\utils\DyeColor::GRAY => 4, - \pocketmine\block\utils\DyeColor::GREEN => 5, - \pocketmine\block\utils\DyeColor::LIGHT_BLUE => 6, - \pocketmine\block\utils\DyeColor::LIGHT_GRAY => 7, - \pocketmine\block\utils\DyeColor::LIME => 8, - \pocketmine\block\utils\DyeColor::MAGENTA => 9, - \pocketmine\block\utils\DyeColor::ORANGE => 10, - \pocketmine\block\utils\DyeColor::PINK => 11, - \pocketmine\block\utils\DyeColor::PURPLE => 12, - \pocketmine\block\utils\DyeColor::RED => 13, - \pocketmine\block\utils\DyeColor::WHITE => 14, - \pocketmine\block\utils\DyeColor::YELLOW => 15, - default => throw new \pocketmine\utils\AssumptionFailedError("All DyeColor cases should be covered") - }); - } - - public function froglightType(\pocketmine\block\utils\FroglightType &$value) : void{ - $this->writeInt(2, match($value){ - \pocketmine\block\utils\FroglightType::OCHRE => 0, - \pocketmine\block\utils\FroglightType::PEARLESCENT => 1, - \pocketmine\block\utils\FroglightType::VERDANT => 2, - default => throw new \pocketmine\utils\AssumptionFailedError("All FroglightType cases should be covered") - }); - } - - public function leverFacing(\pocketmine\block\utils\LeverFacing &$value) : void{ - $this->writeInt(3, match($value){ - \pocketmine\block\utils\LeverFacing::DOWN_AXIS_X => 0, - \pocketmine\block\utils\LeverFacing::DOWN_AXIS_Z => 1, - \pocketmine\block\utils\LeverFacing::EAST => 2, - \pocketmine\block\utils\LeverFacing::NORTH => 3, - \pocketmine\block\utils\LeverFacing::SOUTH => 4, - \pocketmine\block\utils\LeverFacing::UP_AXIS_X => 5, - \pocketmine\block\utils\LeverFacing::UP_AXIS_Z => 6, - \pocketmine\block\utils\LeverFacing::WEST => 7, - default => throw new \pocketmine\utils\AssumptionFailedError("All LeverFacing cases should be covered") - }); - } - - public function medicineType(\pocketmine\item\MedicineType &$value) : void{ - $this->writeInt(2, match($value){ - \pocketmine\item\MedicineType::ANTIDOTE => 0, - \pocketmine\item\MedicineType::ELIXIR => 1, - \pocketmine\item\MedicineType::EYE_DROPS => 2, - \pocketmine\item\MedicineType::TONIC => 3, - default => throw new \pocketmine\utils\AssumptionFailedError("All MedicineType cases should be covered") - }); - } - - public function mobHeadType(\pocketmine\block\utils\MobHeadType &$value) : void{ - $this->writeInt(3, match($value){ - \pocketmine\block\utils\MobHeadType::CREEPER => 0, - \pocketmine\block\utils\MobHeadType::DRAGON => 1, - \pocketmine\block\utils\MobHeadType::PIGLIN => 2, - \pocketmine\block\utils\MobHeadType::PLAYER => 3, - \pocketmine\block\utils\MobHeadType::SKELETON => 4, - \pocketmine\block\utils\MobHeadType::WITHER_SKELETON => 5, - \pocketmine\block\utils\MobHeadType::ZOMBIE => 6, - default => throw new \pocketmine\utils\AssumptionFailedError("All MobHeadType cases should be covered") - }); - } - - public function mushroomBlockType(\pocketmine\block\utils\MushroomBlockType &$value) : void{ - $this->writeInt(4, match($value){ - \pocketmine\block\utils\MushroomBlockType::ALL_CAP => 0, - \pocketmine\block\utils\MushroomBlockType::CAP_EAST => 1, - \pocketmine\block\utils\MushroomBlockType::CAP_MIDDLE => 2, - \pocketmine\block\utils\MushroomBlockType::CAP_NORTH => 3, - \pocketmine\block\utils\MushroomBlockType::CAP_NORTHEAST => 4, - \pocketmine\block\utils\MushroomBlockType::CAP_NORTHWEST => 5, - \pocketmine\block\utils\MushroomBlockType::CAP_SOUTH => 6, - \pocketmine\block\utils\MushroomBlockType::CAP_SOUTHEAST => 7, - \pocketmine\block\utils\MushroomBlockType::CAP_SOUTHWEST => 8, - \pocketmine\block\utils\MushroomBlockType::CAP_WEST => 9, - \pocketmine\block\utils\MushroomBlockType::PORES => 10, - default => throw new \pocketmine\utils\AssumptionFailedError("All MushroomBlockType cases should be covered") - }); - } - - public function potionType(\pocketmine\item\PotionType &$value) : void{ - $this->writeInt(6, match($value){ - \pocketmine\item\PotionType::AWKWARD => 0, - \pocketmine\item\PotionType::FIRE_RESISTANCE => 1, - \pocketmine\item\PotionType::HARMING => 2, - \pocketmine\item\PotionType::HEALING => 3, - \pocketmine\item\PotionType::INVISIBILITY => 4, - \pocketmine\item\PotionType::LEAPING => 5, - \pocketmine\item\PotionType::LONG_FIRE_RESISTANCE => 6, - \pocketmine\item\PotionType::LONG_INVISIBILITY => 7, - \pocketmine\item\PotionType::LONG_LEAPING => 8, - \pocketmine\item\PotionType::LONG_MUNDANE => 9, - \pocketmine\item\PotionType::LONG_NIGHT_VISION => 10, - \pocketmine\item\PotionType::LONG_POISON => 11, - \pocketmine\item\PotionType::LONG_REGENERATION => 12, - \pocketmine\item\PotionType::LONG_SLOWNESS => 13, - \pocketmine\item\PotionType::LONG_SLOW_FALLING => 14, - \pocketmine\item\PotionType::LONG_STRENGTH => 15, - \pocketmine\item\PotionType::LONG_SWIFTNESS => 16, - \pocketmine\item\PotionType::LONG_TURTLE_MASTER => 17, - \pocketmine\item\PotionType::LONG_WATER_BREATHING => 18, - \pocketmine\item\PotionType::LONG_WEAKNESS => 19, - \pocketmine\item\PotionType::MUNDANE => 20, - \pocketmine\item\PotionType::NIGHT_VISION => 21, - \pocketmine\item\PotionType::POISON => 22, - \pocketmine\item\PotionType::REGENERATION => 23, - \pocketmine\item\PotionType::SLOWNESS => 24, - \pocketmine\item\PotionType::SLOW_FALLING => 25, - \pocketmine\item\PotionType::STRENGTH => 26, - \pocketmine\item\PotionType::STRONG_HARMING => 27, - \pocketmine\item\PotionType::STRONG_HEALING => 28, - \pocketmine\item\PotionType::STRONG_LEAPING => 29, - \pocketmine\item\PotionType::STRONG_POISON => 30, - \pocketmine\item\PotionType::STRONG_REGENERATION => 31, - \pocketmine\item\PotionType::STRONG_SLOWNESS => 32, - \pocketmine\item\PotionType::STRONG_STRENGTH => 33, - \pocketmine\item\PotionType::STRONG_SWIFTNESS => 34, - \pocketmine\item\PotionType::STRONG_TURTLE_MASTER => 35, - \pocketmine\item\PotionType::SWIFTNESS => 36, - \pocketmine\item\PotionType::THICK => 37, - \pocketmine\item\PotionType::TURTLE_MASTER => 38, - \pocketmine\item\PotionType::WATER => 39, - \pocketmine\item\PotionType::WATER_BREATHING => 40, - \pocketmine\item\PotionType::WEAKNESS => 41, - \pocketmine\item\PotionType::WITHER => 42, - default => throw new \pocketmine\utils\AssumptionFailedError("All PotionType cases should be covered") - }); - } - - public function slabType(\pocketmine\block\utils\SlabType &$value) : void{ - $this->writeInt(2, match($value){ - \pocketmine\block\utils\SlabType::BOTTOM => 0, - \pocketmine\block\utils\SlabType::DOUBLE => 1, - \pocketmine\block\utils\SlabType::TOP => 2, - default => throw new \pocketmine\utils\AssumptionFailedError("All SlabType cases should be covered") - }); - } - - public function suspiciousStewType(\pocketmine\item\SuspiciousStewType &$value) : void{ - $this->writeInt(4, match($value){ - \pocketmine\item\SuspiciousStewType::ALLIUM => 0, - \pocketmine\item\SuspiciousStewType::AZURE_BLUET => 1, - \pocketmine\item\SuspiciousStewType::BLUE_ORCHID => 2, - \pocketmine\item\SuspiciousStewType::CORNFLOWER => 3, - \pocketmine\item\SuspiciousStewType::DANDELION => 4, - \pocketmine\item\SuspiciousStewType::LILY_OF_THE_VALLEY => 5, - \pocketmine\item\SuspiciousStewType::OXEYE_DAISY => 6, - \pocketmine\item\SuspiciousStewType::POPPY => 7, - \pocketmine\item\SuspiciousStewType::TULIP => 8, - \pocketmine\item\SuspiciousStewType::WITHER_ROSE => 9, - default => throw new \pocketmine\utils\AssumptionFailedError("All SuspiciousStewType cases should be covered") - }); - } - -} diff --git a/src/item/Banner.php b/src/item/Banner.php index db54e9766..2fc53f5ae 100644 --- a/src/item/Banner.php +++ b/src/item/Banner.php @@ -58,7 +58,7 @@ class Banner extends ItemBlockWallOrFloor{ } protected function describeState(RuntimeDataDescriber $w) : void{ - $w->dyeColor($this->color); + $w->enum($this->color); } /** diff --git a/src/item/Dye.php b/src/item/Dye.php index 6fd3f6dcb..9fdfb9671 100644 --- a/src/item/Dye.php +++ b/src/item/Dye.php @@ -30,7 +30,7 @@ class Dye extends Item{ private DyeColor $color = DyeColor::BLACK; protected function describeState(RuntimeDataDescriber $w) : void{ - $w->dyeColor($this->color); + $w->enum($this->color); } public function getColor() : DyeColor{ diff --git a/src/item/Medicine.php b/src/item/Medicine.php index 1df748414..bd2f72464 100644 --- a/src/item/Medicine.php +++ b/src/item/Medicine.php @@ -32,7 +32,7 @@ class Medicine extends Item implements ConsumableItem{ private MedicineType $medicineType = MedicineType::EYE_DROPS; protected function describeState(RuntimeDataDescriber $w) : void{ - $w->medicineType($this->medicineType); + $w->enum($this->medicineType); } public function getType() : MedicineType{ return $this->medicineType; } diff --git a/src/item/Potion.php b/src/item/Potion.php index 597430242..41b0f634a 100644 --- a/src/item/Potion.php +++ b/src/item/Potion.php @@ -32,7 +32,7 @@ class Potion extends Item implements ConsumableItem{ private PotionType $potionType = PotionType::WATER; protected function describeState(RuntimeDataDescriber $w) : void{ - $w->potionType($this->potionType); + $w->enum($this->potionType); } public function getType() : PotionType{ return $this->potionType; } diff --git a/src/item/SplashPotion.php b/src/item/SplashPotion.php index 8c18fd54c..e1c9167ac 100644 --- a/src/item/SplashPotion.php +++ b/src/item/SplashPotion.php @@ -34,7 +34,7 @@ class SplashPotion extends ProjectileItem{ private PotionType $potionType = PotionType::WATER; protected function describeState(RuntimeDataDescriber $w) : void{ - $w->potionType($this->potionType); + $w->enum($this->potionType); } public function getType() : PotionType{ return $this->potionType; } diff --git a/src/item/SuspiciousStew.php b/src/item/SuspiciousStew.php index 8d7c2ff3a..7d1d30cc0 100644 --- a/src/item/SuspiciousStew.php +++ b/src/item/SuspiciousStew.php @@ -30,7 +30,7 @@ class SuspiciousStew extends Food{ private SuspiciousStewType $suspiciousStewType = SuspiciousStewType::POPPY; protected function describeState(RuntimeDataDescriber $w) : void{ - $w->suspiciousStewType($this->suspiciousStewType); + $w->enum($this->suspiciousStewType); } public function getType() : SuspiciousStewType{ return $this->suspiciousStewType; }