From 7c521b456e181fed271b4e050731ed4ebc8faf1f Mon Sep 17 00:00:00 2001 From: "Dylan T." Date: Sun, 24 Aug 2025 14:12:18 +0100 Subject: [PATCH 01/11] Unify block serializers (#6769) This has several advantages: Easier to implement new blocks (one less file to modify) Easier to adjust serialization of existing blocks Guaranteed consistency between serializers and deserializers Potentially, exposes more metadata for programmatic analysis, instead of having everything baked inside opaque Closures There are some exceptions which still use the old approach: big dripleaf, cauldrons, mushroom stems, and pitcher crops. These all have multiple PM block types for a single ID, with relatively complex logic to select which to use. These weren't worth the effort to unify due to their small number. I may revisit this in the future, but I already spent a lot of brainpower on it. --- src/data/bedrock/MushroomBlockTypeIdMap.php | 20 +- .../bedrock/block/BlockLegacyMetadata.php | 2 + .../convert/BlockObjectToStateSerializer.php | 1619 +---------------- .../BlockSerializerDeserializerRegistrar.php | 237 +++ .../convert/BlockStateDeserializerHelper.php | 52 +- .../block/convert/BlockStateReader.php | 228 ++- .../convert/BlockStateSerializerHelper.php | 33 + .../BlockStateToObjectDeserializer.php | 1593 +--------------- .../block/convert/BlockStateWriter.php | 228 +-- .../block/convert/FlattenedIdModel.php | 107 ++ src/data/bedrock/block/convert/Model.php | 82 + .../bedrock/block/convert/StringEnumMap.php | 78 - .../bedrock/block/convert/ValueMappings.php | 83 - .../block/convert/VanillaBlockMappings.php | 1561 ++++++++++++++++ .../property/BoolFromStringProperty.php | 78 + .../block/convert/property/BoolProperty.php | 71 + .../convert/property/CommonProperties.php | 429 +++++ .../block/convert/property/DummyProperty.php | 61 + .../convert/property/EnumFromRawStateMap.php | 109 ++ .../property/FlattenedCaveVinesVariant.php | 35 + .../convert/property/IntFromRawStateMap.php | 104 ++ .../block/convert/property/IntProperty.php | 70 + .../property/OptionSetFromIntProperty.php | 93 + .../block/convert/property/Property.php | 44 + .../block/convert/property/StateMap.php | 53 + .../block/convert/property/StringProperty.php | 50 + .../convert/property/ValueFromIntProperty.php | 75 + .../property/ValueFromStringProperty.php | 81 + .../block/convert/property/ValueMappings.php | 305 ++++ .../property/WallConnectionTypeShim.php | 66 + .../format/io/GlobalBlockStateHandlers.php | 22 +- tests/phpstan/configs/actual-problems.neon | 6 + .../BlockSerializerDeserializerTest.php | 6 +- 33 files changed, 4072 insertions(+), 3609 deletions(-) create mode 100644 src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php create mode 100644 src/data/bedrock/block/convert/FlattenedIdModel.php create mode 100644 src/data/bedrock/block/convert/Model.php delete mode 100644 src/data/bedrock/block/convert/StringEnumMap.php delete mode 100644 src/data/bedrock/block/convert/ValueMappings.php create mode 100644 src/data/bedrock/block/convert/VanillaBlockMappings.php create mode 100644 src/data/bedrock/block/convert/property/BoolFromStringProperty.php create mode 100644 src/data/bedrock/block/convert/property/BoolProperty.php create mode 100644 src/data/bedrock/block/convert/property/CommonProperties.php create mode 100644 src/data/bedrock/block/convert/property/DummyProperty.php create mode 100644 src/data/bedrock/block/convert/property/EnumFromRawStateMap.php create mode 100644 src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php create mode 100644 src/data/bedrock/block/convert/property/IntFromRawStateMap.php create mode 100644 src/data/bedrock/block/convert/property/IntProperty.php create mode 100644 src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php create mode 100644 src/data/bedrock/block/convert/property/Property.php create mode 100644 src/data/bedrock/block/convert/property/StateMap.php create mode 100644 src/data/bedrock/block/convert/property/StringProperty.php create mode 100644 src/data/bedrock/block/convert/property/ValueFromIntProperty.php create mode 100644 src/data/bedrock/block/convert/property/ValueFromStringProperty.php create mode 100644 src/data/bedrock/block/convert/property/ValueMappings.php create mode 100644 src/data/bedrock/block/convert/property/WallConnectionTypeShim.php diff --git a/src/data/bedrock/MushroomBlockTypeIdMap.php b/src/data/bedrock/MushroomBlockTypeIdMap.php index a25336d89..6357d3e14 100644 --- a/src/data/bedrock/MushroomBlockTypeIdMap.php +++ b/src/data/bedrock/MushroomBlockTypeIdMap.php @@ -24,29 +24,21 @@ declare(strict_types=1); namespace pocketmine\data\bedrock; use pocketmine\block\utils\MushroomBlockType; -use pocketmine\data\bedrock\block\BlockLegacyMetadata as LegacyMeta; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\utils\SingletonTrait; +/** + * @deprecated + */ final class MushroomBlockTypeIdMap{ use SingletonTrait; /** @phpstan-use IntSaveIdMapTrait */ use IntSaveIdMapTrait; public function __construct(){ + $newMapping = ValueMappings::getInstance()->mushroomBlockType; foreach(MushroomBlockType::cases() as $case){ - $this->register(match($case){ - MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, - MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, - MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, - MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, - MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, - MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, - MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, - MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, - MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, - MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, - MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, - }, $case); + $this->register($newMapping->valueToRaw($case), $case); } } } diff --git a/src/data/bedrock/block/BlockLegacyMetadata.php b/src/data/bedrock/block/BlockLegacyMetadata.php index e094bd3bd..d324a7ccc 100644 --- a/src/data/bedrock/block/BlockLegacyMetadata.php +++ b/src/data/bedrock/block/BlockLegacyMetadata.php @@ -38,6 +38,8 @@ final class BlockLegacyMetadata{ public const CORAL_VARIANT_FIRE = 3; public const CORAL_VARIANT_HORN = 4; + public const LIQUID_FALLING_FLAG = 0x08; + public const MULTI_FACE_DIRECTION_FLAG_DOWN = 0x01; public const MULTI_FACE_DIRECTION_FLAG_UP = 0x02; public const MULTI_FACE_DIRECTION_FLAG_SOUTH = 0x04; diff --git a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php index 1a3467fe9..90f6af97a 100644 --- a/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php +++ b/src/data/bedrock/block/convert/BlockObjectToStateSerializer.php @@ -23,161 +23,17 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\convert; -use pocketmine\block\ActivatorRail; -use pocketmine\block\AmethystCluster; -use pocketmine\block\Anvil; -use pocketmine\block\Bamboo; -use pocketmine\block\BambooSapling; -use pocketmine\block\Barrel; -use pocketmine\block\Bed; -use pocketmine\block\Beetroot; -use pocketmine\block\Bell; -use pocketmine\block\BigDripleafHead; -use pocketmine\block\BigDripleafStem; use pocketmine\block\Block; -use pocketmine\block\BoneBlock; -use pocketmine\block\BrewingStand; -use pocketmine\block\BrownMushroomBlock; -use pocketmine\block\Button; -use pocketmine\block\Cactus; -use pocketmine\block\Cake; -use pocketmine\block\CakeWithCandle; -use pocketmine\block\CakeWithDyedCandle; -use pocketmine\block\Campfire; -use pocketmine\block\Candle; -use pocketmine\block\Carrot; -use pocketmine\block\CarvedPumpkin; -use pocketmine\block\CaveVines; -use pocketmine\block\Chain; -use pocketmine\block\ChemistryTable; -use pocketmine\block\Chest; -use pocketmine\block\ChiseledBookshelf; -use pocketmine\block\ChorusFlower; -use pocketmine\block\CocoaBlock; -use pocketmine\block\Copper; -use pocketmine\block\CopperBulb; -use pocketmine\block\CopperDoor; -use pocketmine\block\CopperGrate; -use pocketmine\block\CopperSlab; -use pocketmine\block\CopperStairs; -use pocketmine\block\CopperTrapdoor; -use pocketmine\block\Coral; -use pocketmine\block\CoralBlock; -use pocketmine\block\DaylightSensor; -use pocketmine\block\DetectorRail; -use pocketmine\block\Dirt; -use pocketmine\block\Door; -use pocketmine\block\DoublePitcherCrop; -use pocketmine\block\DoublePlant; -use pocketmine\block\DoubleTallGrass; -use pocketmine\block\EnderChest; -use pocketmine\block\EndPortalFrame; -use pocketmine\block\EndRod; -use pocketmine\block\Farmland; -use pocketmine\block\FenceGate; -use pocketmine\block\FillableCauldron; -use pocketmine\block\Fire; -use pocketmine\block\FloorBanner; -use pocketmine\block\FloorCoralFan; -use pocketmine\block\FloorSign; -use pocketmine\block\Froglight; -use pocketmine\block\FrostedIce; -use pocketmine\block\Furnace; -use pocketmine\block\GlazedTerracotta; -use pocketmine\block\GlowLichen; -use pocketmine\block\HayBale; -use pocketmine\block\Hopper; -use pocketmine\block\ItemFrame; -use pocketmine\block\Ladder; -use pocketmine\block\Lantern; -use pocketmine\block\Lava; -use pocketmine\block\Leaves; -use pocketmine\block\Lectern; -use pocketmine\block\Lever; -use pocketmine\block\Light; -use pocketmine\block\LightningRod; -use pocketmine\block\LitPumpkin; -use pocketmine\block\Loom; -use pocketmine\block\MelonStem; -use pocketmine\block\MobHead; -use pocketmine\block\NetherPortal; -use pocketmine\block\NetherVines; -use pocketmine\block\NetherWartPlant; -use pocketmine\block\PinkPetals; -use pocketmine\block\PitcherCrop; -use pocketmine\block\Potato; -use pocketmine\block\PoweredRail; -use pocketmine\block\PumpkinStem; -use pocketmine\block\Rail; -use pocketmine\block\RedMushroomBlock; -use pocketmine\block\RedstoneComparator; -use pocketmine\block\RedstoneLamp; -use pocketmine\block\RedstoneOre; -use pocketmine\block\RedstoneRepeater; -use pocketmine\block\RedstoneTorch; -use pocketmine\block\RedstoneWire; -use pocketmine\block\ResinClump; -use pocketmine\block\RespawnAnchor; use pocketmine\block\RuntimeBlockStateRegistry; -use pocketmine\block\Sapling; -use pocketmine\block\SeaPickle; -use pocketmine\block\SimplePillar; -use pocketmine\block\SimplePressurePlate; use pocketmine\block\Slab; -use pocketmine\block\SmallDripleaf; -use pocketmine\block\SnowLayer; -use pocketmine\block\SoulCampfire; -use pocketmine\block\Sponge; use pocketmine\block\Stair; -use pocketmine\block\StoneButton; -use pocketmine\block\Stonecutter; -use pocketmine\block\StonePressurePlate; -use pocketmine\block\Sugarcane; -use pocketmine\block\SweetBerryBush; -use pocketmine\block\TNT; -use pocketmine\block\Torch; -use pocketmine\block\TorchflowerCrop; -use pocketmine\block\Trapdoor; -use pocketmine\block\TrappedChest; -use pocketmine\block\Tripwire; -use pocketmine\block\TripwireHook; -use pocketmine\block\UnderwaterTorch; -use pocketmine\block\utils\BrewingStandSlot; -use pocketmine\block\utils\Colored; -use pocketmine\block\utils\CoralType; -use pocketmine\block\utils\DirtType; -use pocketmine\block\utils\DripleafState; -use pocketmine\block\utils\DyeColor; -use pocketmine\block\utils\FroglightType; -use pocketmine\block\utils\LeverFacing; -use pocketmine\block\utils\MobHeadType; -use pocketmine\block\VanillaBlocks as Blocks; -use pocketmine\block\Vine; -use pocketmine\block\Wall; -use pocketmine\block\WallBanner; -use pocketmine\block\WallCoralFan; -use pocketmine\block\WallSign; -use pocketmine\block\Water; -use pocketmine\block\WeightedPressurePlateHeavy; -use pocketmine\block\WeightedPressurePlateLight; -use pocketmine\block\Wheat; use pocketmine\block\Wood; -use pocketmine\block\WoodenButton; -use pocketmine\block\WoodenDoor; -use pocketmine\block\WoodenPressurePlate; -use pocketmine\block\WoodenStairs; -use pocketmine\block\WoodenTrapdoor; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateData; -use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateSerializeException; use pocketmine\data\bedrock\block\BlockStateSerializer; -use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; use pocketmine\data\bedrock\block\BlockTypeNames as Ids; use pocketmine\data\bedrock\block\convert\BlockStateSerializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer; -use pocketmine\math\Axis; -use pocketmine\math\Facing; use function get_class; final class BlockObjectToStateSerializer implements BlockStateSerializer{ @@ -196,20 +52,6 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ */ private array $cache = []; - public function __construct(){ - $this->registerCandleSerializers(); - $this->registerFlatColorBlockSerializers(); - $this->registerFlatCoralSerializers(); - $this->registerCauldronSerializers(); - $this->registerFlatWoodBlockSerializers(); - $this->registerLeavesSerializers(); - $this->registerSaplingSerializers(); - $this->registerMobHeadSerializers(); - $this->registerCopperSerializers(); - $this->registerSimpleSerializers(); - $this->registerSerializers(); - } - public function serialize(int $stateId) : BlockStateData{ //TODO: singleton usage not ideal //TODO: we may want to deduplicate cache entries to avoid wasting memory @@ -227,24 +69,36 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ */ public function map(Block $block, \Closure|Writer|BlockStateData $serializer) : void{ if(isset($this->serializers[$block->getTypeId()])){ - throw new \InvalidArgumentException("Block type ID " . $block->getTypeId() . " already has a serializer registered"); + throw new \InvalidArgumentException("Block type ID " . $block->getTypeId() . " (" . $block->getName() . ") already has a serializer registered"); } //writer accepted for convenience only $this->serializers[$block->getTypeId()] = $serializer instanceof Writer ? $serializer->getBlockStateData() : $serializer; } + /** + * @deprecated + */ public function mapSimple(Block $block, string $id) : void{ $this->map($block, BlockStateData::current($id, [])); } + /** + * @deprecated + */ public function mapSlab(Slab $block, string $singleId, string $doubleId) : void{ $this->map($block, fn(Slab $block) => Helper::encodeSlab($block, $singleId, $doubleId)); } + /** + * @deprecated + */ public function mapStairs(Stair $block, string $id) : void{ $this->map($block, fn(Stair $block) => Helper::encodeStairs($block, Writer::create($id))); } + /** + * @deprecated + */ public function mapLog(Wood $block, string $unstrippedId, string $strippedId) : void{ $this->map($block, fn(Wood $block) => Helper::encodeLog($block, $unstrippedId, $strippedId)); } @@ -279,1451 +133,4 @@ final class BlockObjectToStateSerializer implements BlockStateSerializer{ return $result instanceof Writer ? $result->getBlockStateData() : $result; } - - /** - * @phpstan-template TBlock of Block - * @phpstan-template TEnum of \UnitEnum - * - * @phpstan-param TBlock $block - * @phpstan-param StringEnumMap $mapProperty - * @phpstan-param \Closure(TBlock) : TEnum $getProperty - * @phpstan-param ?\Closure(TBlock, Writer) : Writer $extra - */ - public function mapFlattenedEnum( - Block $block, - StringEnumMap $mapProperty, - string $prefix, - string $suffix, - \Closure $getProperty, - ?\Closure $extra = null - ) : void{ - $this->map($block, function(Block $block) use ($getProperty, $mapProperty, $prefix, $suffix, $extra) : Writer{ - $property = $getProperty($block); - $infix = $mapProperty->enumToValue($property); - $id = $prefix . $infix . $suffix; - $writer = new Writer($id); - if($extra !== null){ - $extra($block, $writer); - } - return $writer; - }); - } - - /** - * @phpstan-template TBlock of Block&Colored - * @phpstan-param TBlock $block - * @phpstan-param ?\Closure(TBlock, Writer) : Writer $extra - */ - public function mapColored( - Block $block, - string $prefix, - string $suffix, - ?\Closure $extra = null - ) : void{ - $this->mapFlattenedEnum( - $block, - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - $prefix, - $suffix, - fn(Colored $block) => $block->getColor(), - $extra - ); - } - - private function registerCandleSerializers() : void{ - $this->map(Blocks::CANDLE(), fn(Candle $block) => Helper::encodeCandle($block, new Writer(Ids::CANDLE))); - $this->mapColored( - Blocks::DYED_CANDLE(), - "minecraft:", - "_candle", - Helper::encodeCandle(...) - ); - $this->map(Blocks::CAKE_WITH_CANDLE(), fn(CakeWithCandle $block) => Writer::create(Ids::CANDLE_CAKE) - ->writeBool(StateNames::LIT, $block->isLit())); - $this->mapColored( - Blocks::CAKE_WITH_DYED_CANDLE(), - "minecraft:", - "_candle_cake", - fn(CakeWithDyedCandle $block, Writer $writer) => $writer->writeBool(StateNames::LIT, $block->isLit()) - ); - } - - public function registerFlatColorBlockSerializers() : void{ - $this->mapColored(Blocks::STAINED_HARDENED_GLASS(), "minecraft:hard_", "_stained_glass"); - $this->mapColored(Blocks::STAINED_HARDENED_GLASS_PANE(), "minecraft:hard_", "_stained_glass_pane"); - - $this->mapColored(Blocks::CARPET(), "minecraft:", "_carpet"); - $this->mapColored(Blocks::CONCRETE(), "minecraft:", "_concrete"); - $this->mapColored(Blocks::CONCRETE_POWDER(), "minecraft:", "_concrete_powder"); - $this->mapColored(Blocks::DYED_SHULKER_BOX(), "minecraft:", "_shulker_box"); - $this->mapColored(Blocks::STAINED_CLAY(), "minecraft:", "_terracotta"); - $this->mapColored(Blocks::STAINED_GLASS(), "minecraft:", "_stained_glass"); - $this->mapColored(Blocks::STAINED_GLASS_PANE(), "minecraft:", "_stained_glass_pane"); - $this->mapColored(Blocks::WOOL(), "minecraft:", "_wool"); - - $this->map(Blocks::GLAZED_TERRACOTTA(), function(GlazedTerracotta $block) : Writer{ - return Writer::create(match($block->getColor()){ - DyeColor::BLACK => Ids::BLACK_GLAZED_TERRACOTTA, - DyeColor::BLUE => Ids::BLUE_GLAZED_TERRACOTTA, - DyeColor::BROWN => Ids::BROWN_GLAZED_TERRACOTTA, - DyeColor::CYAN => Ids::CYAN_GLAZED_TERRACOTTA, - DyeColor::GRAY => Ids::GRAY_GLAZED_TERRACOTTA, - DyeColor::GREEN => Ids::GREEN_GLAZED_TERRACOTTA, - DyeColor::LIGHT_BLUE => Ids::LIGHT_BLUE_GLAZED_TERRACOTTA, - DyeColor::LIGHT_GRAY => Ids::SILVER_GLAZED_TERRACOTTA, //minecraft sadness - DyeColor::LIME => Ids::LIME_GLAZED_TERRACOTTA, - DyeColor::MAGENTA => Ids::MAGENTA_GLAZED_TERRACOTTA, - DyeColor::ORANGE => Ids::ORANGE_GLAZED_TERRACOTTA, - DyeColor::PINK => Ids::PINK_GLAZED_TERRACOTTA, - DyeColor::PURPLE => Ids::PURPLE_GLAZED_TERRACOTTA, - DyeColor::RED => Ids::RED_GLAZED_TERRACOTTA, - DyeColor::WHITE => Ids::WHITE_GLAZED_TERRACOTTA, - DyeColor::YELLOW => Ids::YELLOW_GLAZED_TERRACOTTA, - }) - ->writeHorizontalFacing($block->getFacing()); - }); - } - - private function registerFlatCoralSerializers() : void{ - $this->map(Blocks::CORAL(), fn(Coral $block) => BlockStateData::current(match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL : Ids::BRAIN_CORAL, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL : Ids::BUBBLE_CORAL, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL : Ids::FIRE_CORAL, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL : Ids::HORN_CORAL, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL : Ids::TUBE_CORAL, - }, [])); - - $this->map(Blocks::CORAL_FAN(), fn(FloorCoralFan $block) => Writer::create( - match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_FAN : Ids::BRAIN_CORAL_FAN, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_FAN : Ids::BUBBLE_CORAL_FAN, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_FAN : Ids::FIRE_CORAL_FAN, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_FAN : Ids::HORN_CORAL_FAN, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_FAN : Ids::TUBE_CORAL_FAN, - }) - ->writeInt(StateNames::CORAL_FAN_DIRECTION, match($axis = $block->getAxis()){ - Axis::X => 0, - Axis::Z => 1, - default => throw new BlockStateSerializeException("Invalid axis {$axis}"), - })); - - $this->map(Blocks::CORAL_BLOCK(), fn(CoralBlock $block) => BlockStateData::current(match($block->getCoralType()){ - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_BLOCK : Ids::BRAIN_CORAL_BLOCK, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_BLOCK : Ids::BUBBLE_CORAL_BLOCK, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_BLOCK : Ids::FIRE_CORAL_BLOCK, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_BLOCK : Ids::HORN_CORAL_BLOCK, - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_BLOCK : Ids::TUBE_CORAL_BLOCK, - }, [])); - - $this->map(Blocks::WALL_CORAL_FAN(), fn(WallCoralFan $block) => Writer::create( - match($block->getCoralType()){ - CoralType::TUBE => $block->isDead() ? Ids::DEAD_TUBE_CORAL_WALL_FAN : Ids::TUBE_CORAL_WALL_FAN, - CoralType::BRAIN => $block->isDead() ? Ids::DEAD_BRAIN_CORAL_WALL_FAN : Ids::BRAIN_CORAL_WALL_FAN, - CoralType::BUBBLE => $block->isDead() ? Ids::DEAD_BUBBLE_CORAL_WALL_FAN : Ids::BUBBLE_CORAL_WALL_FAN, - CoralType::FIRE => $block->isDead() ? Ids::DEAD_FIRE_CORAL_WALL_FAN : Ids::FIRE_CORAL_WALL_FAN, - CoralType::HORN => $block->isDead() ? Ids::DEAD_HORN_CORAL_WALL_FAN : Ids::HORN_CORAL_WALL_FAN, - }) - ->writeCoralFacing($block->getFacing()) - ); - } - - private function registerCauldronSerializers() : void{ - $this->map(Blocks::CAULDRON(), Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); - $this->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel())); - //potion cauldrons store their real information in the block actor data - $this->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - $this->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - } - - private function registerFlatWoodBlockSerializers() : void{ - $this->map(Blocks::ACACIA_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::ACACIA_BUTTON))); - $this->map(Blocks::ACACIA_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::ACACIA_DOOR))); - $this->map(Blocks::ACACIA_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::ACACIA_FENCE_GATE))); - $this->map(Blocks::ACACIA_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::ACACIA_PRESSURE_PLATE))); - $this->map(Blocks::ACACIA_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::ACACIA_STANDING_SIGN))); - $this->map(Blocks::ACACIA_STAIRS(), fn(WoodenStairs $block) => Helper::encodeStairs($block, new Writer(Ids::ACACIA_STAIRS))); - $this->map(Blocks::ACACIA_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::ACACIA_TRAPDOOR))); - $this->map(Blocks::ACACIA_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::ACACIA_WALL_SIGN))); - $this->mapLog(Blocks::ACACIA_LOG(), Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG); - $this->mapLog(Blocks::ACACIA_WOOD(), Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD); - $this->mapSimple(Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE); - $this->mapSimple(Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS); - $this->mapSlab(Blocks::ACACIA_SLAB(), Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB); - - $this->map(Blocks::BIRCH_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::BIRCH_BUTTON))); - $this->map(Blocks::BIRCH_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::BIRCH_DOOR))); - $this->map(Blocks::BIRCH_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::BIRCH_FENCE_GATE))); - $this->map(Blocks::BIRCH_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::BIRCH_PRESSURE_PLATE))); - $this->map(Blocks::BIRCH_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::BIRCH_STANDING_SIGN))); - $this->map(Blocks::BIRCH_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::BIRCH_TRAPDOOR))); - $this->map(Blocks::BIRCH_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::BIRCH_WALL_SIGN))); - $this->mapLog(Blocks::BIRCH_LOG(), Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG); - $this->mapLog(Blocks::BIRCH_WOOD(), Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD); - $this->mapSimple(Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE); - $this->mapSimple(Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS); - $this->mapSlab(Blocks::BIRCH_SLAB(), Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB); - $this->mapStairs(Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS); - - $this->map(Blocks::CHERRY_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CHERRY_BUTTON))); - $this->map(Blocks::CHERRY_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CHERRY_DOOR))); - $this->map(Blocks::CHERRY_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CHERRY_FENCE_GATE))); - $this->map(Blocks::CHERRY_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG)); - $this->map(Blocks::CHERRY_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CHERRY_PRESSURE_PLATE))); - $this->map(Blocks::CHERRY_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CHERRY_STANDING_SIGN))); - $this->map(Blocks::CHERRY_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CHERRY_TRAPDOOR))); - $this->map(Blocks::CHERRY_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CHERRY_WALL_SIGN))); - $this->mapSimple(Blocks::CHERRY_FENCE(), Ids::CHERRY_FENCE); - $this->mapSimple(Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS); - $this->mapSlab(Blocks::CHERRY_SLAB(), Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB); - $this->mapStairs(Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS); - $this->mapLog(Blocks::CHERRY_WOOD(), Ids::CHERRY_WOOD, Ids::STRIPPED_CHERRY_WOOD); - - $this->map(Blocks::CRIMSON_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::CRIMSON_BUTTON))); - $this->map(Blocks::CRIMSON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::CRIMSON_DOOR))); - $this->map(Blocks::CRIMSON_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::CRIMSON_FENCE_GATE))); - $this->map(Blocks::CRIMSON_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE)); - $this->map(Blocks::CRIMSON_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::CRIMSON_PRESSURE_PLATE))); - $this->map(Blocks::CRIMSON_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::CRIMSON_STANDING_SIGN))); - $this->map(Blocks::CRIMSON_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM)); - $this->map(Blocks::CRIMSON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::CRIMSON_TRAPDOOR))); - $this->map(Blocks::CRIMSON_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::CRIMSON_WALL_SIGN))); - $this->mapSimple(Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE); - $this->mapSimple(Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS); - $this->mapSlab(Blocks::CRIMSON_SLAB(), Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB); - $this->mapStairs(Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS); - - $this->map(Blocks::DARK_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::DARK_OAK_BUTTON))); - $this->map(Blocks::DARK_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::DARK_OAK_DOOR))); - $this->map(Blocks::DARK_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::DARK_OAK_FENCE_GATE))); - $this->map(Blocks::DARK_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::DARK_OAK_PRESSURE_PLATE))); - $this->map(Blocks::DARK_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::DARKOAK_STANDING_SIGN))); - $this->map(Blocks::DARK_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::DARK_OAK_TRAPDOOR))); - $this->map(Blocks::DARK_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::DARKOAK_WALL_SIGN))); - $this->mapLog(Blocks::DARK_OAK_LOG(), Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG); - $this->mapLog(Blocks::DARK_OAK_WOOD(), Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD); - $this->mapSimple(Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE); - $this->mapSimple(Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS); - $this->mapSlab(Blocks::DARK_OAK_SLAB(), Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB); - $this->mapStairs(Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS); - - $this->map(Blocks::JUNGLE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::JUNGLE_BUTTON))); - $this->map(Blocks::JUNGLE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::JUNGLE_DOOR))); - $this->map(Blocks::JUNGLE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::JUNGLE_FENCE_GATE))); - $this->map(Blocks::JUNGLE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::JUNGLE_PRESSURE_PLATE))); - $this->map(Blocks::JUNGLE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::JUNGLE_STANDING_SIGN))); - $this->map(Blocks::JUNGLE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::JUNGLE_TRAPDOOR))); - $this->map(Blocks::JUNGLE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::JUNGLE_WALL_SIGN))); - $this->mapLog(Blocks::JUNGLE_LOG(), Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG); - $this->mapLog(Blocks::JUNGLE_WOOD(), Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD); - $this->mapSimple(Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE); - $this->mapSimple(Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS); - $this->mapSlab(Blocks::JUNGLE_SLAB(), Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB); - $this->mapStairs(Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS); - - $this->map(Blocks::MANGROVE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::MANGROVE_BUTTON))); - $this->map(Blocks::MANGROVE_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::MANGROVE_DOOR))); - $this->map(Blocks::MANGROVE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::MANGROVE_FENCE_GATE))); - $this->map(Blocks::MANGROVE_LOG(), fn(Wood $block) => Helper::encodeLog($block, Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG)); - $this->map(Blocks::MANGROVE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::MANGROVE_PRESSURE_PLATE))); - $this->map(Blocks::MANGROVE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::MANGROVE_STANDING_SIGN))); - $this->map(Blocks::MANGROVE_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::MANGROVE_TRAPDOOR))); - $this->map(Blocks::MANGROVE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::MANGROVE_WALL_SIGN))); - $this->mapSimple(Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE); - $this->mapSimple(Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS); - $this->mapSlab(Blocks::MANGROVE_SLAB(), Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB); - $this->mapStairs(Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS); - $this->mapLog(Blocks::MANGROVE_WOOD(), Ids::MANGROVE_WOOD, Ids::STRIPPED_MANGROVE_WOOD); - - $this->map(Blocks::OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::WOODEN_BUTTON))); - $this->map(Blocks::OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::WOODEN_DOOR))); - $this->map(Blocks::OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::FENCE_GATE))); - $this->map(Blocks::OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WOODEN_PRESSURE_PLATE))); - $this->map(Blocks::OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::STANDING_SIGN))); - $this->map(Blocks::OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::TRAPDOOR))); - $this->map(Blocks::OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WALL_SIGN))); - $this->mapLog(Blocks::OAK_LOG(), Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG); - $this->mapLog(Blocks::OAK_WOOD(), Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD); - $this->mapSimple(Blocks::OAK_FENCE(), Ids::OAK_FENCE); - $this->mapSimple(Blocks::OAK_PLANKS(), Ids::OAK_PLANKS); - $this->mapSlab(Blocks::OAK_SLAB(), Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB); - $this->mapStairs(Blocks::OAK_STAIRS(), Ids::OAK_STAIRS); - - $this->map(Blocks::PALE_OAK_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::PALE_OAK_BUTTON))); - $this->map(Blocks::PALE_OAK_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::PALE_OAK_DOOR))); - $this->map(Blocks::PALE_OAK_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::PALE_OAK_FENCE_GATE))); - $this->map(Blocks::PALE_OAK_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::PALE_OAK_PRESSURE_PLATE))); - $this->map(Blocks::PALE_OAK_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::PALE_OAK_STANDING_SIGN))); - $this->map(Blocks::PALE_OAK_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::PALE_OAK_TRAPDOOR))); - $this->map(Blocks::PALE_OAK_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::PALE_OAK_WALL_SIGN))); - $this->mapLog(Blocks::PALE_OAK_LOG(), Ids::PALE_OAK_LOG, Ids::STRIPPED_PALE_OAK_LOG); - $this->mapLog(Blocks::PALE_OAK_WOOD(), Ids::PALE_OAK_WOOD, Ids::STRIPPED_PALE_OAK_WOOD); - $this->mapSimple(Blocks::PALE_OAK_FENCE(), Ids::PALE_OAK_FENCE); - $this->mapSimple(Blocks::PALE_OAK_PLANKS(), Ids::PALE_OAK_PLANKS); - $this->mapSlab(Blocks::PALE_OAK_SLAB(), Ids::PALE_OAK_SLAB, Ids::PALE_OAK_DOUBLE_SLAB); - $this->mapStairs(Blocks::PALE_OAK_STAIRS(), Ids::PALE_OAK_STAIRS); - - $this->map(Blocks::SPRUCE_BUTTON(), fn(WoodenButton $block) => Helper::encodeButton($block, new Writer(Ids::SPRUCE_BUTTON))); - $this->map(Blocks::SPRUCE_DOOR(), fn(WoodenDoor $block) => Helper::encodeDoor($block, new Writer(Ids::SPRUCE_DOOR))); - $this->map(Blocks::SPRUCE_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::SPRUCE_FENCE_GATE))); - $this->map(Blocks::SPRUCE_PRESSURE_PLATE(), fn(WoodenPressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::SPRUCE_PRESSURE_PLATE))); - $this->map(Blocks::SPRUCE_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::SPRUCE_STANDING_SIGN))); - $this->map(Blocks::SPRUCE_TRAPDOOR(), fn(WoodenTrapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::SPRUCE_TRAPDOOR))); - $this->map(Blocks::SPRUCE_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::SPRUCE_WALL_SIGN))); - $this->mapLog(Blocks::SPRUCE_LOG(), Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG); - $this->mapLog(Blocks::SPRUCE_WOOD(), Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD); - $this->mapSimple(Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE); - $this->mapSimple(Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS); - $this->mapSlab(Blocks::SPRUCE_SLAB(), Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS); - //wood and slabs still use the old way of storing wood type - - $this->map(Blocks::WARPED_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::WARPED_BUTTON))); - $this->map(Blocks::WARPED_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::WARPED_DOOR))); - $this->map(Blocks::WARPED_FENCE_GATE(), fn(FenceGate $block) => Helper::encodeFenceGate($block, new Writer(Ids::WARPED_FENCE_GATE))); - $this->map(Blocks::WARPED_HYPHAE(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE)); - $this->map(Blocks::WARPED_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::WARPED_PRESSURE_PLATE))); - $this->map(Blocks::WARPED_SIGN(), fn(FloorSign $block) => Helper::encodeFloorSign($block, new Writer(Ids::WARPED_STANDING_SIGN))); - $this->map(Blocks::WARPED_STEM(), fn(Wood $block) => Helper::encodeLog($block, Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM)); - $this->map(Blocks::WARPED_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::WARPED_TRAPDOOR))); - $this->map(Blocks::WARPED_WALL_SIGN(), fn(WallSign $block) => Helper::encodeWallSign($block, new Writer(Ids::WARPED_WALL_SIGN))); - $this->mapSimple(Blocks::WARPED_FENCE(), Ids::WARPED_FENCE); - $this->mapSimple(Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS); - $this->mapSlab(Blocks::WARPED_SLAB(), Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB); - $this->mapStairs(Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS); - } - - private function registerLeavesSerializers() : void{ - //flattened IDs - $this->map(Blocks::AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES))); - $this->map(Blocks::CHERRY_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::CHERRY_LEAVES))); - $this->map(Blocks::FLOWERING_AZALEA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::AZALEA_LEAVES_FLOWERED))); - $this->map(Blocks::MANGROVE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::MANGROVE_LEAVES))); - $this->map(Blocks::PALE_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::PALE_OAK_LEAVES))); - - //legacy mess - $this->map(Blocks::ACACIA_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::ACACIA_LEAVES))); - $this->map(Blocks::BIRCH_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::BIRCH_LEAVES))); - $this->map(Blocks::DARK_OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::DARK_OAK_LEAVES))); - $this->map(Blocks::JUNGLE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::JUNGLE_LEAVES))); - $this->map(Blocks::OAK_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::OAK_LEAVES))); - $this->map(Blocks::SPRUCE_LEAVES(), fn(Leaves $block) => Helper::encodeLeaves($block, new Writer(Ids::SPRUCE_LEAVES))); - } - - private function registerSaplingSerializers() : void{ - foreach([ - Ids::ACACIA_SAPLING => Blocks::ACACIA_SAPLING(), - Ids::BIRCH_SAPLING => Blocks::BIRCH_SAPLING(), - Ids::DARK_OAK_SAPLING => Blocks::DARK_OAK_SAPLING(), - Ids::JUNGLE_SAPLING => Blocks::JUNGLE_SAPLING(), - Ids::OAK_SAPLING => Blocks::OAK_SAPLING(), - Ids::SPRUCE_SAPLING => Blocks::SPRUCE_SAPLING(), - ] as $id => $block){ - $this->map($block, fn(Sapling $block) => Helper::encodeSapling($block, new Writer($id))); - } - } - - private function registerMobHeadSerializers() : void{ - $this->map(Blocks::MOB_HEAD(), fn(MobHead $block) => Writer::create(match ($block->getMobHeadType()){ - MobHeadType::CREEPER => Ids::CREEPER_HEAD, - MobHeadType::DRAGON => Ids::DRAGON_HEAD, - MobHeadType::PIGLIN => Ids::PIGLIN_HEAD, - MobHeadType::PLAYER => Ids::PLAYER_HEAD, - MobHeadType::SKELETON => Ids::SKELETON_SKULL, - MobHeadType::WITHER_SKELETON => Ids::WITHER_SKELETON_SKULL, - MobHeadType::ZOMBIE => Ids::ZOMBIE_HEAD, - })->writeFacingWithoutDown($block->getFacing())); - } - - private function registerCopperSerializers() : void{ - $this->map(Blocks::COPPER(), function(Copper $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_COPPER, Ids::WAXED_EXPOSED_COPPER, Ids::WAXED_WEATHERED_COPPER, Ids::WAXED_OXIDIZED_COPPER) : - Helper::selectCopperId($oxidation, Ids::COPPER_BLOCK, Ids::EXPOSED_COPPER, Ids::WEATHERED_COPPER, Ids::OXIDIZED_COPPER), - [] - ); - }); - $this->map(Blocks::CHISELED_COPPER(), function(Copper $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_CHISELED_COPPER, - Ids::WAXED_EXPOSED_CHISELED_COPPER, - Ids::WAXED_WEATHERED_CHISELED_COPPER, - Ids::WAXED_OXIDIZED_CHISELED_COPPER - ) : - Helper::selectCopperId($oxidation, - Ids::CHISELED_COPPER, - Ids::EXPOSED_CHISELED_COPPER, - Ids::WEATHERED_CHISELED_COPPER, - Ids::OXIDIZED_CHISELED_COPPER - ), - [] - ); - }); - $this->map(Blocks::COPPER_GRATE(), function(CopperGrate $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_COPPER_GRATE, - Ids::WAXED_EXPOSED_COPPER_GRATE, - Ids::WAXED_WEATHERED_COPPER_GRATE, - Ids::WAXED_OXIDIZED_COPPER_GRATE - ) : - Helper::selectCopperId($oxidation, - Ids::COPPER_GRATE, - Ids::EXPOSED_COPPER_GRATE, - Ids::WEATHERED_COPPER_GRATE, - Ids::OXIDIZED_COPPER_GRATE - ), - [] - ); - }); - $this->map(Blocks::CUT_COPPER(), function(Copper $block) : BlockStateData{ - $oxidation = $block->getOxidation(); - return BlockStateData::current( - $block->isWaxed() ? - Helper::selectCopperId($oxidation, Ids::WAXED_CUT_COPPER, Ids::WAXED_EXPOSED_CUT_COPPER, Ids::WAXED_WEATHERED_CUT_COPPER, Ids::WAXED_OXIDIZED_CUT_COPPER) : - Helper::selectCopperId($oxidation, Ids::CUT_COPPER, Ids::EXPOSED_CUT_COPPER, Ids::WEATHERED_CUT_COPPER, Ids::OXIDIZED_CUT_COPPER), - [] - ); - }); - $this->map(Blocks::CUT_COPPER_SLAB(), function(CopperSlab $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeSlab( - $block, - ($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB - ) : - Helper::selectCopperId( - $oxidation, - Ids::CUT_COPPER_SLAB, - Ids::EXPOSED_CUT_COPPER_SLAB, - Ids::WEATHERED_CUT_COPPER_SLAB, - Ids::OXIDIZED_CUT_COPPER_SLAB - ) - ), - ($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB - ) : - Helper::selectCopperId( - $oxidation, - Ids::DOUBLE_CUT_COPPER_SLAB, - Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB - ) - ) - ); - }); - $this->map(Blocks::CUT_COPPER_STAIRS(), function(CopperStairs $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeStairs( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_CUT_COPPER_STAIRS, - Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, - Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, - Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS - ) : - Helper::selectCopperId( - $oxidation, - Ids::CUT_COPPER_STAIRS, - Ids::EXPOSED_CUT_COPPER_STAIRS, - Ids::WEATHERED_CUT_COPPER_STAIRS, - Ids::OXIDIZED_CUT_COPPER_STAIRS - ) - ) - ); - }); - $this->map(Blocks::COPPER_BULB(), function(CopperBulb $block) : Writer{ - $oxidation = $block->getOxidation(); - return Writer::create($block->isWaxed() ? - Helper::selectCopperId($oxidation, - Ids::WAXED_COPPER_BULB, - Ids::WAXED_EXPOSED_COPPER_BULB, - Ids::WAXED_WEATHERED_COPPER_BULB, - Ids::WAXED_OXIDIZED_COPPER_BULB) : - Helper::selectCopperId($oxidation, - Ids::COPPER_BULB, - Ids::EXPOSED_COPPER_BULB, - Ids::WEATHERED_COPPER_BULB, - Ids::OXIDIZED_COPPER_BULB - )) - ->writeBool(StateNames::LIT, $block->isLit()) - ->writeBool(StateNames::POWERED_BIT, $block->isPowered()); - }); - $this->map(Blocks::COPPER_DOOR(), function(CopperDoor $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeDoor( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_COPPER_DOOR, - Ids::WAXED_EXPOSED_COPPER_DOOR, - Ids::WAXED_WEATHERED_COPPER_DOOR, - Ids::WAXED_OXIDIZED_COPPER_DOOR - ) : - Helper::selectCopperId( - $oxidation, - Ids::COPPER_DOOR, - Ids::EXPOSED_COPPER_DOOR, - Ids::WEATHERED_COPPER_DOOR, - Ids::OXIDIZED_COPPER_DOOR - ) - ) - ); - }); - $this->map(Blocks::COPPER_TRAPDOOR(), function(CopperTrapdoor $block) : Writer{ - $oxidation = $block->getOxidation(); - return Helper::encodeTrapdoor( - $block, - new Writer($block->isWaxed() ? - Helper::selectCopperId( - $oxidation, - Ids::WAXED_COPPER_TRAPDOOR, - Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, - Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, - Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR - ) : - Helper::selectCopperId( - $oxidation, - Ids::COPPER_TRAPDOOR, - Ids::EXPOSED_COPPER_TRAPDOOR, - Ids::WEATHERED_COPPER_TRAPDOOR, - Ids::OXIDIZED_COPPER_TRAPDOOR - ) - ) - ); - }); - } - - private function registerSimpleSerializers() : void{ - $this->mapSimple(Blocks::AIR(), Ids::AIR); - $this->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK); - $this->mapSimple(Blocks::ANCIENT_DEBRIS(), Ids::ANCIENT_DEBRIS); - $this->mapSimple(Blocks::ANDESITE(), Ids::ANDESITE); - $this->mapSimple(Blocks::BARRIER(), Ids::BARRIER); - $this->mapSimple(Blocks::BEACON(), Ids::BEACON); - $this->mapSimple(Blocks::BLACKSTONE(), Ids::BLACKSTONE); - $this->mapSimple(Blocks::BLUE_ICE(), Ids::BLUE_ICE); - $this->mapSimple(Blocks::BOOKSHELF(), Ids::BOOKSHELF); - $this->mapSimple(Blocks::BRICKS(), Ids::BRICK_BLOCK); - $this->mapSimple(Blocks::BROWN_MUSHROOM(), Ids::BROWN_MUSHROOM); - $this->mapSimple(Blocks::BUDDING_AMETHYST(), Ids::BUDDING_AMETHYST); - $this->mapSimple(Blocks::CALCITE(), Ids::CALCITE); - $this->mapSimple(Blocks::CARTOGRAPHY_TABLE(), Ids::CARTOGRAPHY_TABLE); - $this->mapSimple(Blocks::CHEMICAL_HEAT(), Ids::CHEMICAL_HEAT); - $this->mapSimple(Blocks::CHISELED_DEEPSLATE(), Ids::CHISELED_DEEPSLATE); - $this->mapSimple(Blocks::CHISELED_NETHER_BRICKS(), Ids::CHISELED_NETHER_BRICKS); - $this->mapSimple(Blocks::CHISELED_POLISHED_BLACKSTONE(), Ids::CHISELED_POLISHED_BLACKSTONE); - $this->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE); - $this->mapSimple(Blocks::CHISELED_RESIN_BRICKS(), Ids::CHISELED_RESIN_BRICKS); - $this->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE); - $this->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS); - $this->mapSimple(Blocks::CHISELED_TUFF(), Ids::CHISELED_TUFF); - $this->mapSimple(Blocks::CHISELED_TUFF_BRICKS(), Ids::CHISELED_TUFF_BRICKS); - $this->mapSimple(Blocks::CHORUS_PLANT(), Ids::CHORUS_PLANT); - $this->mapSimple(Blocks::CLAY(), Ids::CLAY); - $this->mapSimple(Blocks::COAL(), Ids::COAL_BLOCK); - $this->mapSimple(Blocks::COAL_ORE(), Ids::COAL_ORE); - $this->mapSimple(Blocks::COBBLED_DEEPSLATE(), Ids::COBBLED_DEEPSLATE); - $this->mapSimple(Blocks::COBBLESTONE(), Ids::COBBLESTONE); - $this->mapSimple(Blocks::COBWEB(), Ids::WEB); - $this->mapSimple(Blocks::COPPER_ORE(), Ids::COPPER_ORE); - $this->mapSimple(Blocks::CRACKED_DEEPSLATE_BRICKS(), Ids::CRACKED_DEEPSLATE_BRICKS); - $this->mapSimple(Blocks::CRACKED_DEEPSLATE_TILES(), Ids::CRACKED_DEEPSLATE_TILES); - $this->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS); - $this->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS); - $this->mapSimple(Blocks::CRACKED_STONE_BRICKS(), Ids::CRACKED_STONE_BRICKS); - $this->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE); - $this->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS); - $this->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN); - $this->mapSimple(Blocks::DANDELION(), Ids::DANDELION); - $this->mapSimple(Blocks::CUT_RED_SANDSTONE(), Ids::CUT_RED_SANDSTONE); - $this->mapSimple(Blocks::CUT_SANDSTONE(), Ids::CUT_SANDSTONE); - $this->mapSimple(Blocks::DARK_PRISMARINE(), Ids::DARK_PRISMARINE); - $this->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH); - $this->mapSimple(Blocks::DEEPSLATE_BRICKS(), Ids::DEEPSLATE_BRICKS); - $this->mapSimple(Blocks::DEEPSLATE_COAL_ORE(), Ids::DEEPSLATE_COAL_ORE); - $this->mapSimple(Blocks::DEEPSLATE_COPPER_ORE(), Ids::DEEPSLATE_COPPER_ORE); - $this->mapSimple(Blocks::DEEPSLATE_DIAMOND_ORE(), Ids::DEEPSLATE_DIAMOND_ORE); - $this->mapSimple(Blocks::DEEPSLATE_EMERALD_ORE(), Ids::DEEPSLATE_EMERALD_ORE); - $this->mapSimple(Blocks::DEEPSLATE_GOLD_ORE(), Ids::DEEPSLATE_GOLD_ORE); - $this->mapSimple(Blocks::DEEPSLATE_IRON_ORE(), Ids::DEEPSLATE_IRON_ORE); - $this->mapSimple(Blocks::DEEPSLATE_LAPIS_LAZULI_ORE(), Ids::DEEPSLATE_LAPIS_ORE); - $this->mapSimple(Blocks::DEEPSLATE_TILES(), Ids::DEEPSLATE_TILES); - $this->mapSimple(Blocks::DIAMOND(), Ids::DIAMOND_BLOCK); - $this->mapSimple(Blocks::DIAMOND_ORE(), Ids::DIAMOND_ORE); - $this->mapSimple(Blocks::DIORITE(), Ids::DIORITE); - $this->mapSimple(Blocks::DRAGON_EGG(), Ids::DRAGON_EGG); - $this->mapSimple(Blocks::DRIED_KELP(), Ids::DRIED_KELP_BLOCK); - $this->mapSimple(Blocks::ELEMENT_ACTINIUM(), Ids::ELEMENT_89); - $this->mapSimple(Blocks::ELEMENT_ALUMINUM(), Ids::ELEMENT_13); - $this->mapSimple(Blocks::ELEMENT_AMERICIUM(), Ids::ELEMENT_95); - $this->mapSimple(Blocks::ELEMENT_ANTIMONY(), Ids::ELEMENT_51); - $this->mapSimple(Blocks::ELEMENT_ARGON(), Ids::ELEMENT_18); - $this->mapSimple(Blocks::ELEMENT_ARSENIC(), Ids::ELEMENT_33); - $this->mapSimple(Blocks::ELEMENT_ASTATINE(), Ids::ELEMENT_85); - $this->mapSimple(Blocks::ELEMENT_BARIUM(), Ids::ELEMENT_56); - $this->mapSimple(Blocks::ELEMENT_BERKELIUM(), Ids::ELEMENT_97); - $this->mapSimple(Blocks::ELEMENT_BERYLLIUM(), Ids::ELEMENT_4); - $this->mapSimple(Blocks::ELEMENT_BISMUTH(), Ids::ELEMENT_83); - $this->mapSimple(Blocks::ELEMENT_BOHRIUM(), Ids::ELEMENT_107); - $this->mapSimple(Blocks::ELEMENT_BORON(), Ids::ELEMENT_5); - $this->mapSimple(Blocks::ELEMENT_BROMINE(), Ids::ELEMENT_35); - $this->mapSimple(Blocks::ELEMENT_CADMIUM(), Ids::ELEMENT_48); - $this->mapSimple(Blocks::ELEMENT_CALCIUM(), Ids::ELEMENT_20); - $this->mapSimple(Blocks::ELEMENT_CALIFORNIUM(), Ids::ELEMENT_98); - $this->mapSimple(Blocks::ELEMENT_CARBON(), Ids::ELEMENT_6); - $this->mapSimple(Blocks::ELEMENT_CERIUM(), Ids::ELEMENT_58); - $this->mapSimple(Blocks::ELEMENT_CESIUM(), Ids::ELEMENT_55); - $this->mapSimple(Blocks::ELEMENT_CHLORINE(), Ids::ELEMENT_17); - $this->mapSimple(Blocks::ELEMENT_CHROMIUM(), Ids::ELEMENT_24); - $this->mapSimple(Blocks::ELEMENT_COBALT(), Ids::ELEMENT_27); - $this->mapSimple(Blocks::ELEMENT_COPERNICIUM(), Ids::ELEMENT_112); - $this->mapSimple(Blocks::ELEMENT_COPPER(), Ids::ELEMENT_29); - $this->mapSimple(Blocks::ELEMENT_CURIUM(), Ids::ELEMENT_96); - $this->mapSimple(Blocks::ELEMENT_DARMSTADTIUM(), Ids::ELEMENT_110); - $this->mapSimple(Blocks::ELEMENT_DUBNIUM(), Ids::ELEMENT_105); - $this->mapSimple(Blocks::ELEMENT_DYSPROSIUM(), Ids::ELEMENT_66); - $this->mapSimple(Blocks::ELEMENT_EINSTEINIUM(), Ids::ELEMENT_99); - $this->mapSimple(Blocks::ELEMENT_ERBIUM(), Ids::ELEMENT_68); - $this->mapSimple(Blocks::ELEMENT_EUROPIUM(), Ids::ELEMENT_63); - $this->mapSimple(Blocks::ELEMENT_FERMIUM(), Ids::ELEMENT_100); - $this->mapSimple(Blocks::ELEMENT_FLEROVIUM(), Ids::ELEMENT_114); - $this->mapSimple(Blocks::ELEMENT_FLUORINE(), Ids::ELEMENT_9); - $this->mapSimple(Blocks::ELEMENT_FRANCIUM(), Ids::ELEMENT_87); - $this->mapSimple(Blocks::ELEMENT_GADOLINIUM(), Ids::ELEMENT_64); - $this->mapSimple(Blocks::ELEMENT_GALLIUM(), Ids::ELEMENT_31); - $this->mapSimple(Blocks::ELEMENT_GERMANIUM(), Ids::ELEMENT_32); - $this->mapSimple(Blocks::ELEMENT_GOLD(), Ids::ELEMENT_79); - $this->mapSimple(Blocks::ELEMENT_HAFNIUM(), Ids::ELEMENT_72); - $this->mapSimple(Blocks::ELEMENT_HASSIUM(), Ids::ELEMENT_108); - $this->mapSimple(Blocks::ELEMENT_HELIUM(), Ids::ELEMENT_2); - $this->mapSimple(Blocks::ELEMENT_HOLMIUM(), Ids::ELEMENT_67); - $this->mapSimple(Blocks::ELEMENT_HYDROGEN(), Ids::ELEMENT_1); - $this->mapSimple(Blocks::ELEMENT_INDIUM(), Ids::ELEMENT_49); - $this->mapSimple(Blocks::ELEMENT_IODINE(), Ids::ELEMENT_53); - $this->mapSimple(Blocks::ELEMENT_IRIDIUM(), Ids::ELEMENT_77); - $this->mapSimple(Blocks::ELEMENT_IRON(), Ids::ELEMENT_26); - $this->mapSimple(Blocks::ELEMENT_KRYPTON(), Ids::ELEMENT_36); - $this->mapSimple(Blocks::ELEMENT_LANTHANUM(), Ids::ELEMENT_57); - $this->mapSimple(Blocks::ELEMENT_LAWRENCIUM(), Ids::ELEMENT_103); - $this->mapSimple(Blocks::ELEMENT_LEAD(), Ids::ELEMENT_82); - $this->mapSimple(Blocks::ELEMENT_LITHIUM(), Ids::ELEMENT_3); - $this->mapSimple(Blocks::ELEMENT_LIVERMORIUM(), Ids::ELEMENT_116); - $this->mapSimple(Blocks::ELEMENT_LUTETIUM(), Ids::ELEMENT_71); - $this->mapSimple(Blocks::ELEMENT_MAGNESIUM(), Ids::ELEMENT_12); - $this->mapSimple(Blocks::ELEMENT_MANGANESE(), Ids::ELEMENT_25); - $this->mapSimple(Blocks::ELEMENT_MEITNERIUM(), Ids::ELEMENT_109); - $this->mapSimple(Blocks::ELEMENT_MENDELEVIUM(), Ids::ELEMENT_101); - $this->mapSimple(Blocks::ELEMENT_MERCURY(), Ids::ELEMENT_80); - $this->mapSimple(Blocks::ELEMENT_MOLYBDENUM(), Ids::ELEMENT_42); - $this->mapSimple(Blocks::ELEMENT_MOSCOVIUM(), Ids::ELEMENT_115); - $this->mapSimple(Blocks::ELEMENT_NEODYMIUM(), Ids::ELEMENT_60); - $this->mapSimple(Blocks::ELEMENT_NEON(), Ids::ELEMENT_10); - $this->mapSimple(Blocks::ELEMENT_NEPTUNIUM(), Ids::ELEMENT_93); - $this->mapSimple(Blocks::ELEMENT_NICKEL(), Ids::ELEMENT_28); - $this->mapSimple(Blocks::ELEMENT_NIHONIUM(), Ids::ELEMENT_113); - $this->mapSimple(Blocks::ELEMENT_NIOBIUM(), Ids::ELEMENT_41); - $this->mapSimple(Blocks::ELEMENT_NITROGEN(), Ids::ELEMENT_7); - $this->mapSimple(Blocks::ELEMENT_NOBELIUM(), Ids::ELEMENT_102); - $this->mapSimple(Blocks::ELEMENT_OGANESSON(), Ids::ELEMENT_118); - $this->mapSimple(Blocks::ELEMENT_OSMIUM(), Ids::ELEMENT_76); - $this->mapSimple(Blocks::ELEMENT_OXYGEN(), Ids::ELEMENT_8); - $this->mapSimple(Blocks::ELEMENT_PALLADIUM(), Ids::ELEMENT_46); - $this->mapSimple(Blocks::ELEMENT_PHOSPHORUS(), Ids::ELEMENT_15); - $this->mapSimple(Blocks::ELEMENT_PLATINUM(), Ids::ELEMENT_78); - $this->mapSimple(Blocks::ELEMENT_PLUTONIUM(), Ids::ELEMENT_94); - $this->mapSimple(Blocks::ELEMENT_POLONIUM(), Ids::ELEMENT_84); - $this->mapSimple(Blocks::ELEMENT_POTASSIUM(), Ids::ELEMENT_19); - $this->mapSimple(Blocks::ELEMENT_PRASEODYMIUM(), Ids::ELEMENT_59); - $this->mapSimple(Blocks::ELEMENT_PROMETHIUM(), Ids::ELEMENT_61); - $this->mapSimple(Blocks::ELEMENT_PROTACTINIUM(), Ids::ELEMENT_91); - $this->mapSimple(Blocks::ELEMENT_RADIUM(), Ids::ELEMENT_88); - $this->mapSimple(Blocks::ELEMENT_RADON(), Ids::ELEMENT_86); - $this->mapSimple(Blocks::ELEMENT_RHENIUM(), Ids::ELEMENT_75); - $this->mapSimple(Blocks::ELEMENT_RHODIUM(), Ids::ELEMENT_45); - $this->mapSimple(Blocks::ELEMENT_ROENTGENIUM(), Ids::ELEMENT_111); - $this->mapSimple(Blocks::ELEMENT_RUBIDIUM(), Ids::ELEMENT_37); - $this->mapSimple(Blocks::ELEMENT_RUTHENIUM(), Ids::ELEMENT_44); - $this->mapSimple(Blocks::ELEMENT_RUTHERFORDIUM(), Ids::ELEMENT_104); - $this->mapSimple(Blocks::ELEMENT_SAMARIUM(), Ids::ELEMENT_62); - $this->mapSimple(Blocks::ELEMENT_SCANDIUM(), Ids::ELEMENT_21); - $this->mapSimple(Blocks::ELEMENT_SEABORGIUM(), Ids::ELEMENT_106); - $this->mapSimple(Blocks::ELEMENT_SELENIUM(), Ids::ELEMENT_34); - $this->mapSimple(Blocks::ELEMENT_SILICON(), Ids::ELEMENT_14); - $this->mapSimple(Blocks::ELEMENT_SILVER(), Ids::ELEMENT_47); - $this->mapSimple(Blocks::ELEMENT_SODIUM(), Ids::ELEMENT_11); - $this->mapSimple(Blocks::ELEMENT_STRONTIUM(), Ids::ELEMENT_38); - $this->mapSimple(Blocks::ELEMENT_SULFUR(), Ids::ELEMENT_16); - $this->mapSimple(Blocks::ELEMENT_TANTALUM(), Ids::ELEMENT_73); - $this->mapSimple(Blocks::ELEMENT_TECHNETIUM(), Ids::ELEMENT_43); - $this->mapSimple(Blocks::ELEMENT_TELLURIUM(), Ids::ELEMENT_52); - $this->mapSimple(Blocks::ELEMENT_TENNESSINE(), Ids::ELEMENT_117); - $this->mapSimple(Blocks::ELEMENT_TERBIUM(), Ids::ELEMENT_65); - $this->mapSimple(Blocks::ELEMENT_THALLIUM(), Ids::ELEMENT_81); - $this->mapSimple(Blocks::ELEMENT_THORIUM(), Ids::ELEMENT_90); - $this->mapSimple(Blocks::ELEMENT_THULIUM(), Ids::ELEMENT_69); - $this->mapSimple(Blocks::ELEMENT_TIN(), Ids::ELEMENT_50); - $this->mapSimple(Blocks::ELEMENT_TITANIUM(), Ids::ELEMENT_22); - $this->mapSimple(Blocks::ELEMENT_TUNGSTEN(), Ids::ELEMENT_74); - $this->mapSimple(Blocks::ELEMENT_URANIUM(), Ids::ELEMENT_92); - $this->mapSimple(Blocks::ELEMENT_VANADIUM(), Ids::ELEMENT_23); - $this->mapSimple(Blocks::ELEMENT_XENON(), Ids::ELEMENT_54); - $this->mapSimple(Blocks::ELEMENT_YTTERBIUM(), Ids::ELEMENT_70); - $this->mapSimple(Blocks::ELEMENT_YTTRIUM(), Ids::ELEMENT_39); - $this->mapSimple(Blocks::ELEMENT_ZERO(), Ids::ELEMENT_0); - $this->mapSimple(Blocks::ELEMENT_ZINC(), Ids::ELEMENT_30); - $this->mapSimple(Blocks::ELEMENT_ZIRCONIUM(), Ids::ELEMENT_40); - $this->mapSimple(Blocks::EMERALD(), Ids::EMERALD_BLOCK); - $this->mapSimple(Blocks::EMERALD_ORE(), Ids::EMERALD_ORE); - $this->mapSimple(Blocks::ENCHANTING_TABLE(), Ids::ENCHANTING_TABLE); - $this->mapSimple(Blocks::END_STONE(), Ids::END_STONE); - $this->mapSimple(Blocks::END_STONE_BRICKS(), Ids::END_BRICKS); - $this->mapSimple(Blocks::FERN(), Ids::FERN); - $this->mapSimple(Blocks::FLETCHING_TABLE(), Ids::FLETCHING_TABLE); - $this->mapSimple(Blocks::GILDED_BLACKSTONE(), Ids::GILDED_BLACKSTONE); - $this->mapSimple(Blocks::GLASS(), Ids::GLASS); - $this->mapSimple(Blocks::GLASS_PANE(), Ids::GLASS_PANE); - $this->mapSimple(Blocks::GLOWING_OBSIDIAN(), Ids::GLOWINGOBSIDIAN); - $this->mapSimple(Blocks::GLOWSTONE(), Ids::GLOWSTONE); - $this->mapSimple(Blocks::GOLD(), Ids::GOLD_BLOCK); - $this->mapSimple(Blocks::GOLD_ORE(), Ids::GOLD_ORE); - $this->mapSimple(Blocks::GRANITE(), Ids::GRANITE); - $this->mapSimple(Blocks::GRASS(), Ids::GRASS_BLOCK); - $this->mapSimple(Blocks::GRASS_PATH(), Ids::GRASS_PATH); - $this->mapSimple(Blocks::GRAVEL(), Ids::GRAVEL); - $this->mapSimple(Blocks::HANGING_ROOTS(), Ids::HANGING_ROOTS); - $this->mapSimple(Blocks::HARDENED_CLAY(), Ids::HARDENED_CLAY); - $this->mapSimple(Blocks::HARDENED_GLASS(), Ids::HARD_GLASS); - $this->mapSimple(Blocks::HARDENED_GLASS_PANE(), Ids::HARD_GLASS_PANE); - $this->mapSimple(Blocks::HONEYCOMB(), Ids::HONEYCOMB_BLOCK); - $this->mapSimple(Blocks::ICE(), Ids::ICE); - $this->mapSimple(Blocks::INFESTED_CHISELED_STONE_BRICK(), Ids::INFESTED_CHISELED_STONE_BRICKS); - $this->mapSimple(Blocks::INFESTED_COBBLESTONE(), Ids::INFESTED_COBBLESTONE); - $this->mapSimple(Blocks::INFESTED_CRACKED_STONE_BRICK(), Ids::INFESTED_CRACKED_STONE_BRICKS); - $this->mapSimple(Blocks::INFESTED_MOSSY_STONE_BRICK(), Ids::INFESTED_MOSSY_STONE_BRICKS); - $this->mapSimple(Blocks::INFESTED_STONE(), Ids::INFESTED_STONE); - $this->mapSimple(Blocks::INFESTED_STONE_BRICK(), Ids::INFESTED_STONE_BRICKS); - $this->mapSimple(Blocks::INFO_UPDATE(), Ids::INFO_UPDATE); - $this->mapSimple(Blocks::INFO_UPDATE2(), Ids::INFO_UPDATE2); - $this->mapSimple(Blocks::INVISIBLE_BEDROCK(), Ids::INVISIBLE_BEDROCK); - $this->mapSimple(Blocks::IRON(), Ids::IRON_BLOCK); - $this->mapSimple(Blocks::IRON_BARS(), Ids::IRON_BARS); - $this->mapSimple(Blocks::IRON_ORE(), Ids::IRON_ORE); - $this->mapSimple(Blocks::JUKEBOX(), Ids::JUKEBOX); - $this->mapSimple(Blocks::LAPIS_LAZULI(), Ids::LAPIS_BLOCK); - $this->mapSimple(Blocks::LAPIS_LAZULI_ORE(), Ids::LAPIS_ORE); - $this->mapSimple(Blocks::LEGACY_STONECUTTER(), Ids::STONECUTTER); - $this->mapSimple(Blocks::LILY_PAD(), Ids::WATERLILY); - $this->mapSimple(Blocks::MAGMA(), Ids::MAGMA); - $this->mapSimple(Blocks::MANGROVE_ROOTS(), Ids::MANGROVE_ROOTS); - $this->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK); - $this->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER); - $this->mapSimple(Blocks::MOSSY_COBBLESTONE(), Ids::MOSSY_COBBLESTONE); - $this->mapSimple(Blocks::MOSSY_STONE_BRICKS(), Ids::MOSSY_STONE_BRICKS); - $this->mapSimple(Blocks::MUD(), Ids::MUD); - $this->mapSimple(Blocks::MUD_BRICKS(), Ids::MUD_BRICKS); - $this->mapSimple(Blocks::MYCELIUM(), Ids::MYCELIUM); - $this->mapSimple(Blocks::NETHERITE(), Ids::NETHERITE_BLOCK); - $this->mapSimple(Blocks::NETHERRACK(), Ids::NETHERRACK); - $this->mapSimple(Blocks::NETHER_BRICKS(), Ids::NETHER_BRICK); - $this->mapSimple(Blocks::NETHER_BRICK_FENCE(), Ids::NETHER_BRICK_FENCE); - $this->mapSimple(Blocks::NETHER_GOLD_ORE(), Ids::NETHER_GOLD_ORE); - $this->mapSimple(Blocks::NETHER_QUARTZ_ORE(), Ids::QUARTZ_ORE); - $this->mapSimple(Blocks::NETHER_REACTOR_CORE(), Ids::NETHERREACTOR); - $this->mapSimple(Blocks::NETHER_WART_BLOCK(), Ids::NETHER_WART_BLOCK); - $this->mapSimple(Blocks::NOTE_BLOCK(), Ids::NOTEBLOCK); - $this->mapSimple(Blocks::OBSIDIAN(), Ids::OBSIDIAN); - $this->mapSimple(Blocks::PACKED_ICE(), Ids::PACKED_ICE); - $this->mapSimple(Blocks::PACKED_MUD(), Ids::PACKED_MUD); - $this->mapSimple(Blocks::PODZOL(), Ids::PODZOL); - $this->mapSimple(Blocks::POLISHED_ANDESITE(), Ids::POLISHED_ANDESITE); - $this->mapSimple(Blocks::POLISHED_BLACKSTONE(), Ids::POLISHED_BLACKSTONE); - $this->mapSimple(Blocks::POLISHED_BLACKSTONE_BRICKS(), Ids::POLISHED_BLACKSTONE_BRICKS); - $this->mapSimple(Blocks::POLISHED_DEEPSLATE(), Ids::POLISHED_DEEPSLATE); - $this->mapSimple(Blocks::POLISHED_DIORITE(), Ids::POLISHED_DIORITE); - $this->mapSimple(Blocks::POLISHED_GRANITE(), Ids::POLISHED_GRANITE); - $this->mapSimple(Blocks::POLISHED_TUFF(), Ids::POLISHED_TUFF); - $this->mapSimple(Blocks::PRISMARINE(), Ids::PRISMARINE); - $this->mapSimple(Blocks::PRISMARINE_BRICKS(), Ids::PRISMARINE_BRICKS); - $this->mapSimple(Blocks::QUARTZ_BRICKS(), Ids::QUARTZ_BRICKS); - $this->mapSimple(Blocks::RAW_COPPER(), Ids::RAW_COPPER_BLOCK); - $this->mapSimple(Blocks::RAW_GOLD(), Ids::RAW_GOLD_BLOCK); - $this->mapSimple(Blocks::RAW_IRON(), Ids::RAW_IRON_BLOCK); - $this->mapSimple(Blocks::REDSTONE(), Ids::REDSTONE_BLOCK); - $this->mapSimple(Blocks::RED_MUSHROOM(), Ids::RED_MUSHROOM); - $this->mapSimple(Blocks::RED_NETHER_BRICKS(), Ids::RED_NETHER_BRICK); - $this->mapSimple(Blocks::RED_SAND(), Ids::RED_SAND); - $this->mapSimple(Blocks::RED_SANDSTONE(), Ids::RED_SANDSTONE); - $this->mapSimple(Blocks::REINFORCED_DEEPSLATE(), Ids::REINFORCED_DEEPSLATE); - $this->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6); - $this->mapSimple(Blocks::RESIN(), Ids::RESIN_BLOCK); - $this->mapSimple(Blocks::RESIN_BRICKS(), Ids::RESIN_BRICKS); - $this->mapSimple(Blocks::SAND(), Ids::SAND); - $this->mapSimple(Blocks::SANDSTONE(), Ids::SANDSTONE); - $this->mapSimple(Blocks::SCULK(), Ids::SCULK); - $this->mapSimple(Blocks::SEA_LANTERN(), Ids::SEA_LANTERN); - $this->mapSimple(Blocks::SHROOMLIGHT(), Ids::SHROOMLIGHT); - $this->mapSimple(Blocks::SHULKER_BOX(), Ids::UNDYED_SHULKER_BOX); - $this->mapSimple(Blocks::SLIME(), Ids::SLIME); - $this->mapSimple(Blocks::SMITHING_TABLE(), Ids::SMITHING_TABLE); - $this->mapSimple(Blocks::SMOOTH_BASALT(), Ids::SMOOTH_BASALT); - $this->mapSimple(Blocks::SMOOTH_RED_SANDSTONE(), Ids::SMOOTH_RED_SANDSTONE); - $this->mapSimple(Blocks::SMOOTH_SANDSTONE(), Ids::SMOOTH_SANDSTONE); - $this->mapSimple(Blocks::SMOOTH_STONE(), Ids::SMOOTH_STONE); - $this->mapSimple(Blocks::SNOW(), Ids::SNOW); - $this->mapSimple(Blocks::SOUL_SAND(), Ids::SOUL_SAND); - $this->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL); - $this->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM); - $this->mapSimple(Blocks::STONE(), Ids::STONE); - $this->mapSimple(Blocks::STONE_BRICKS(), Ids::STONE_BRICKS); - $this->mapSimple(Blocks::TALL_GRASS(), Ids::SHORT_GRASS); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( - $this->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS); - $this->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER); - $this->mapSimple(Blocks::TUFF(), Ids::TUFF); - $this->mapSimple(Blocks::TUFF_BRICKS(), Ids::TUFF_BRICKS); - $this->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK); - $this->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS); - $this->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE); - - $this->mapSimple(Blocks::ALLIUM(), Ids::ALLIUM); - $this->mapSimple(Blocks::CORNFLOWER(), Ids::CORNFLOWER); - $this->mapSimple(Blocks::AZURE_BLUET(), Ids::AZURE_BLUET); - $this->mapSimple(Blocks::LILY_OF_THE_VALLEY(), Ids::LILY_OF_THE_VALLEY); - $this->mapSimple(Blocks::BLUE_ORCHID(), Ids::BLUE_ORCHID); - $this->mapSimple(Blocks::OXEYE_DAISY(), Ids::OXEYE_DAISY); - $this->mapSimple(Blocks::POPPY(), Ids::POPPY); - $this->mapSimple(Blocks::ORANGE_TULIP(), Ids::ORANGE_TULIP); - $this->mapSimple(Blocks::PINK_TULIP(), Ids::PINK_TULIP); - $this->mapSimple(Blocks::RED_TULIP(), Ids::RED_TULIP); - $this->mapSimple(Blocks::WHITE_TULIP(), Ids::WHITE_TULIP); - } - - private function registerSerializers() : void{ - $this->map(Blocks::ACTIVATOR_RAIL(), function(ActivatorRail $block) : Writer{ - return Writer::create(Ids::ACTIVATOR_RAIL) - ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); - $this->map(Blocks::AMETHYST_CLUSTER(), fn(AmethystCluster $block) => Writer::create( - match($stage = $block->getStage()){ - AmethystCluster::STAGE_SMALL_BUD => Ids::SMALL_AMETHYST_BUD, - AmethystCluster::STAGE_MEDIUM_BUD => Ids::MEDIUM_AMETHYST_BUD, - AmethystCluster::STAGE_LARGE_BUD => Ids::LARGE_AMETHYST_BUD, - AmethystCluster::STAGE_CLUSTER => Ids::AMETHYST_CLUSTER, - default => throw new BlockStateSerializeException("Invalid Amethyst Cluster stage $stage"), - }) - ->writeBlockFace($block->getFacing()) - ); - $this->mapSlab(Blocks::ANDESITE_SLAB(), Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB); - $this->map(Blocks::ANDESITE_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::ANDESITE_STAIRS))); - $this->map(Blocks::ANDESITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::ANDESITE_WALL))); - $this->map(Blocks::ANVIL(), fn(Anvil $block) : Writer => Writer::create( - match($damage = $block->getDamage()){ - 0 => Ids::ANVIL, - 1 => Ids::CHIPPED_ANVIL, - 2 => Ids::DAMAGED_ANVIL, - default => throw new BlockStateSerializeException("Invalid Anvil damage {$damage}"), - }) - ->writeCardinalHorizontalFacing($block->getFacing()) - ); - $this->map(Blocks::BAMBOO(), function(Bamboo $block) : Writer{ - return Writer::create(Ids::BAMBOO) - ->writeBool(StateNames::AGE_BIT, $block->isReady()) - ->writeString(StateNames::BAMBOO_LEAF_SIZE, match($block->getLeafSize()){ - Bamboo::NO_LEAVES => StringValues::BAMBOO_LEAF_SIZE_NO_LEAVES, - Bamboo::SMALL_LEAVES => StringValues::BAMBOO_LEAF_SIZE_SMALL_LEAVES, - Bamboo::LARGE_LEAVES => StringValues::BAMBOO_LEAF_SIZE_LARGE_LEAVES, - default => throw new BlockStateSerializeException("Invalid Bamboo leaf thickness " . $block->getLeafSize()), - }) - ->writeString(StateNames::BAMBOO_STALK_THICKNESS, $block->isThick() ? StringValues::BAMBOO_STALK_THICKNESS_THICK : StringValues::BAMBOO_STALK_THICKNESS_THIN); - }); - $this->map(Blocks::BAMBOO_SAPLING(), function(BambooSapling $block) : Writer{ - return Writer::create(Ids::BAMBOO_SAPLING) - ->writeBool(StateNames::AGE_BIT, $block->isReady()); - }); - $this->map(Blocks::BANNER(), function(FloorBanner $block) : Writer{ - return Writer::create(Ids::STANDING_BANNER) - ->writeInt(StateNames::GROUND_SIGN_DIRECTION, $block->getRotation()); - }); - $this->map(Blocks::BARREL(), function(Barrel $block) : Writer{ - return Writer::create(Ids::BARREL) - ->writeBool(StateNames::OPEN_BIT, $block->isOpen()) - ->writeFacingDirection($block->getFacing()); - }); - $this->map(Blocks::BASALT(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::BASALT) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::BED(), function(Bed $block) : Writer{ - return Writer::create(Ids::BED) - ->writeBool(StateNames::HEAD_PIECE_BIT, $block->isHeadPart()) - ->writeBool(StateNames::OCCUPIED_BIT, $block->isOccupied()) - ->writeLegacyHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::BEDROCK(), function(Block $block) : Writer{ - return Writer::create(Ids::BEDROCK) - ->writeBool(StateNames::INFINIBURN_BIT, $block->burnsForever()); - }); - $this->map(Blocks::BEETROOTS(), fn(Beetroot $block) => Helper::encodeCrops($block, new Writer(Ids::BEETROOT))); - $this->map(Blocks::BELL(), function(Bell $block) : Writer{ - return Writer::create(Ids::BELL) - ->writeBellAttachmentType($block->getAttachmentType()) - ->writeBool(StateNames::TOGGLE_BIT, false) //we don't care about this; it's just to keep MCPE happy - ->writeLegacyHorizontalFacing($block->getFacing()); - - }); - $this->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeString(StateNames::BIG_DRIPLEAF_TILT, match($block->getLeafState()){ - DripleafState::STABLE => StringValues::BIG_DRIPLEAF_TILT_NONE, - DripleafState::UNSTABLE => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE, - DripleafState::PARTIAL_TILT => StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT, - DripleafState::FULL_TILT => StringValues::BIG_DRIPLEAF_TILT_FULL_TILT, - }) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true); - }); - $this->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false); - }); - $this->mapSlab(Blocks::BLACKSTONE_SLAB(), Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS); - $this->map(Blocks::BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::BLACKSTONE_WALL))); - $this->map(Blocks::BLAST_FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::BLAST_FURNACE, Ids::LIT_BLAST_FURNACE)); - $this->map(Blocks::BLUE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_BLUE))); - $this->map(Blocks::BONE_BLOCK(), function(BoneBlock $block) : Writer{ - return Writer::create(Ids::BONE_BLOCK) - ->writeInt(StateNames::DEPRECATED, 0) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::BREWING_STAND(), function(BrewingStand $block) : Writer{ - return Writer::create(Ids::BREWING_STAND) - ->writeBool(StateNames::BREWING_STAND_SLOT_A_BIT, $block->hasSlot(BrewingStandSlot::EAST)) - ->writeBool(StateNames::BREWING_STAND_SLOT_B_BIT, $block->hasSlot(BrewingStandSlot::SOUTHWEST)) - ->writeBool(StateNames::BREWING_STAND_SLOT_C_BIT, $block->hasSlot(BrewingStandSlot::NORTHWEST)); - }); - $this->mapSlab(Blocks::BRICK_SLAB(), Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS); - $this->map(Blocks::BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::BRICK_WALL))); - $this->map(Blocks::BROWN_MUSHROOM_BLOCK(), fn(BrownMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::BROWN_MUSHROOM_BLOCK))); - $this->map(Blocks::CACTUS(), function(Cactus $block) : Writer{ - return Writer::create(Ids::CACTUS) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::CAKE(), function(Cake $block) : Writer{ - return Writer::create(Ids::CAKE) - ->writeInt(StateNames::BITE_COUNTER, $block->getBites()); - }); - $this->map(Blocks::CAMPFIRE(), function(Campfire $block) : Writer{ - return Writer::create(Ids::CAMPFIRE) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeBool(StateNames::EXTINGUISHED, !$block->isLit()); - }); - $this->map(Blocks::CARROTS(), fn(Carrot $block) => Helper::encodeCrops($block, new Writer(Ids::CARROTS))); - $this->map(Blocks::CARVED_PUMPKIN(), function(CarvedPumpkin $block) : Writer{ - return Writer::create(Ids::CARVED_PUMPKIN) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::CAVE_VINES(), function(CaveVines $block) : Writer{ - //I have no idea why this only has 3 IDs - there are 4 in Java and 4 visually distinct states in Bedrock - return Writer::create($block->hasBerries() ? - ($block->isHead() ? - Ids::CAVE_VINES_HEAD_WITH_BERRIES : - Ids::CAVE_VINES_BODY_WITH_BERRIES - ) : - Ids::CAVE_VINES - ) - ->writeInt(StateNames::GROWING_PLANT_AGE, $block->getAge()); - }); - $this->map(Blocks::CHAIN(), function(Chain $block) : Writer{ - return Writer::create(Ids::CHAIN) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::CHEST(), function(Chest $block) : Writer{ - return Writer::create(Ids::CHEST) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::CHISELED_BOOKSHELF(), function(ChiseledBookshelf $block) : Writer{ - $flags = 0; - foreach($block->getSlots() as $slot){ - $flags |= 1 << $slot->value; - } - return Writer::create(Ids::CHISELED_BOOKSHELF) - ->writeLegacyHorizontalFacing($block->getFacing()) - ->writeInt(StateNames::BOOKS_STORED, $flags); - }); - $this->map(Blocks::CHISELED_QUARTZ(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::CHISELED_QUARTZ_BLOCK))); - $this->map(Blocks::CHORUS_FLOWER(), function(ChorusFlower $block) : Writer{ - return Writer::create(Ids::CHORUS_FLOWER) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->mapSlab(Blocks::COBBLED_DEEPSLATE_SLAB(), Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB); - $this->mapStairs(Blocks::COBBLED_DEEPSLATE_STAIRS(), Ids::COBBLED_DEEPSLATE_STAIRS); - $this->map(Blocks::COBBLED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::COBBLED_DEEPSLATE_WALL))); - $this->mapSlab(Blocks::COBBLESTONE_SLAB(), Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS); - $this->map(Blocks::COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::COBBLESTONE_WALL))); - $this->map(Blocks::COCOA_POD(), function(CocoaBlock $block) : Writer{ - return Writer::create(Ids::COCOA) - ->writeInt(StateNames::AGE, $block->getAge()) - ->writeLegacyHorizontalFacing(Facing::opposite($block->getFacing())); - }); - $this->map(Blocks::COMPOUND_CREATOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::COMPOUND_CREATOR))); - $this->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB); - $this->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB); - $this->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB); - $this->mapStairs(Blocks::DARK_PRISMARINE_STAIRS(), Ids::DARK_PRISMARINE_STAIRS); - $this->map(Blocks::DAYLIGHT_SENSOR(), function(DaylightSensor $block) : Writer{ - return Writer::create($block->isInverted() ? Ids::DAYLIGHT_DETECTOR_INVERTED : Ids::DAYLIGHT_DETECTOR) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::DEEPSLATE(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::DEEPSLATE) - ->writePillarAxis($block->getAxis()); - }); - $this->mapSlab(Blocks::DEEPSLATE_BRICK_SLAB(), Ids::DEEPSLATE_BRICK_SLAB, Ids::DEEPSLATE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::DEEPSLATE_BRICK_STAIRS(), Ids::DEEPSLATE_BRICK_STAIRS); - $this->map(Blocks::DEEPSLATE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::DEEPSLATE_BRICK_WALL))); - $this->map(Blocks::DEEPSLATE_REDSTONE_ORE(), fn(RedstoneOre $block) => new Writer($block->isLit() ? Ids::LIT_DEEPSLATE_REDSTONE_ORE : Ids::DEEPSLATE_REDSTONE_ORE)); - $this->mapSlab(Blocks::DEEPSLATE_TILE_SLAB(), Ids::DEEPSLATE_TILE_SLAB, Ids::DEEPSLATE_TILE_DOUBLE_SLAB); - $this->mapStairs(Blocks::DEEPSLATE_TILE_STAIRS(), Ids::DEEPSLATE_TILE_STAIRS); - $this->map(Blocks::DEEPSLATE_TILE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::DEEPSLATE_TILE_WALL))); - $this->map(Blocks::DETECTOR_RAIL(), function(DetectorRail $block) : Writer{ - return Writer::create(Ids::DETECTOR_RAIL) - ->writeBool(StateNames::RAIL_DATA_BIT, $block->isActivated()) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->mapSlab(Blocks::DIORITE_SLAB(), Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS); - $this->map(Blocks::DIORITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::DIORITE_WALL))); - $this->map(Blocks::DIRT(), fn(Dirt $block) => BlockStateData::current(match($block->getDirtType()){ - DirtType::NORMAL => Ids::DIRT, - DirtType::COARSE => Ids::COARSE_DIRT, - DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, - }, [])); - $this->map(Blocks::DOUBLE_TALLGRASS(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::TALL_GRASS))); - $this->map(Blocks::ELEMENT_CONSTRUCTOR(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::ELEMENT_CONSTRUCTOR))); - $this->map(Blocks::ENDER_CHEST(), function(EnderChest $block) : Writer{ - return Writer::create(Ids::ENDER_CHEST) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::END_PORTAL_FRAME(), function(EndPortalFrame $block) : Writer{ - return Writer::create(Ids::END_PORTAL_FRAME) - ->writeBool(StateNames::END_PORTAL_EYE_BIT, $block->hasEye()) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::END_ROD(), function(EndRod $block) : Writer{ - return Writer::create(Ids::END_ROD) - ->writeEndRodFacingDirection($block->getFacing()); - }); - $this->mapSlab(Blocks::END_STONE_BRICK_SLAB(), Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS); - $this->map(Blocks::END_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::END_STONE_BRICK_WALL))); - $this->mapSlab(Blocks::FAKE_WOODEN_SLAB(), Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB); - $this->map(Blocks::FARMLAND(), function(Farmland $block) : Writer{ - return Writer::create(Ids::FARMLAND) - ->writeInt(StateNames::MOISTURIZED_AMOUNT, $block->getWetness()); - }); - $this->map(Blocks::FIRE(), function(Fire $block) : Writer{ - return Writer::create(Ids::FIRE) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::FLOWER_POT(), Writer::create(Ids::FLOWER_POT) - ->writeBool(StateNames::UPDATE_BIT, false) //to keep MCPE happy - ); - $this->map(Blocks::FROGLIGHT(), function(Froglight $block){ - return Writer::create(match($block->getFroglightType()){ - FroglightType::OCHRE => Ids::OCHRE_FROGLIGHT, - FroglightType::PEARLESCENT => Ids::PEARLESCENT_FROGLIGHT, - FroglightType::VERDANT => Ids::VERDANT_FROGLIGHT, - }) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::FROSTED_ICE(), function(FrostedIce $block) : Writer{ - return Writer::create(Ids::FROSTED_ICE) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::FURNACE(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::FURNACE, Ids::LIT_FURNACE)); - $this->map(Blocks::GLOW_LICHEN(), function(GlowLichen $block) : Writer{ - return Writer::create(Ids::GLOW_LICHEN) - ->writeFacingFlags($block->getFaces()); - }); - $this->map(Blocks::GLOWING_ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::GLOW_FRAME)); - $this->mapSlab(Blocks::GRANITE_SLAB(), Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS); - $this->map(Blocks::GRANITE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::GRANITE_WALL))); - $this->map(Blocks::GREEN_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_GREEN))); - $this->map(Blocks::HAY_BALE(), function(HayBale $block) : Writer{ - return Writer::create(Ids::HAY_BLOCK) - ->writeInt(StateNames::DEPRECATED, 0) - ->writePillarAxis($block->getAxis()); - }); - $this->map(Blocks::HOPPER(), function(Hopper $block) : Writer{ - return Writer::create(Ids::HOPPER) - ->writeBool(StateNames::TOGGLE_BIT, $block->isPowered()) - ->writeFacingWithoutUp($block->getFacing()); - }); - $this->map(Blocks::IRON_DOOR(), fn(Door $block) => Helper::encodeDoor($block, new Writer(Ids::IRON_DOOR))); - $this->map(Blocks::IRON_TRAPDOOR(), fn(Trapdoor $block) => Helper::encodeTrapdoor($block, new Writer(Ids::IRON_TRAPDOOR))); - $this->map(Blocks::ITEM_FRAME(), fn(ItemFrame $block) => Helper::encodeItemFrame($block, Ids::FRAME)); - $this->map(Blocks::LAB_TABLE(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::LAB_TABLE))); - $this->map(Blocks::LADDER(), function(Ladder $block) : Writer{ - return Writer::create(Ids::LADDER) - ->writeHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::LANTERN(), function(Lantern $block) : Writer{ - return Writer::create(Ids::LANTERN) - ->writeBool(StateNames::HANGING, $block->isHanging()); - }); - $this->map(Blocks::LARGE_FERN(), fn(DoubleTallGrass $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::LARGE_FERN))); - $this->map(Blocks::LAVA(), fn(Lava $block) => Helper::encodeLiquid($block, Ids::LAVA, Ids::FLOWING_LAVA)); - $this->map(Blocks::LECTERN(), function(Lectern $block) : Writer{ - return Writer::create(Ids::LECTERN) - ->writeBool(StateNames::POWERED_BIT, $block->isProducingSignal()) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::LEVER(), function(Lever $block) : Writer{ - return Writer::create(Ids::LEVER) - ->writeBool(StateNames::OPEN_BIT, $block->isActivated()) - ->writeString(StateNames::LEVER_DIRECTION, match($block->getFacing()){ - LeverFacing::DOWN_AXIS_Z => StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH, - LeverFacing::DOWN_AXIS_X => StringValues::LEVER_DIRECTION_DOWN_EAST_WEST, - LeverFacing::UP_AXIS_Z => StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH, - LeverFacing::UP_AXIS_X => StringValues::LEVER_DIRECTION_UP_EAST_WEST, - LeverFacing::NORTH => StringValues::LEVER_DIRECTION_NORTH, - LeverFacing::SOUTH => StringValues::LEVER_DIRECTION_SOUTH, - LeverFacing::WEST => StringValues::LEVER_DIRECTION_WEST, - LeverFacing::EAST => StringValues::LEVER_DIRECTION_EAST, - }); - }); - $this->map(Blocks::LIGHT(), fn(Light $block) => BlockStateData::current(match($block->getLightLevel()){ - 0 => Ids::LIGHT_BLOCK_0, - 1 => Ids::LIGHT_BLOCK_1, - 2 => Ids::LIGHT_BLOCK_2, - 3 => Ids::LIGHT_BLOCK_3, - 4 => Ids::LIGHT_BLOCK_4, - 5 => Ids::LIGHT_BLOCK_5, - 6 => Ids::LIGHT_BLOCK_6, - 7 => Ids::LIGHT_BLOCK_7, - 8 => Ids::LIGHT_BLOCK_8, - 9 => Ids::LIGHT_BLOCK_9, - 10 => Ids::LIGHT_BLOCK_10, - 11 => Ids::LIGHT_BLOCK_11, - 12 => Ids::LIGHT_BLOCK_12, - 13 => Ids::LIGHT_BLOCK_13, - 14 => Ids::LIGHT_BLOCK_14, - 15 => Ids::LIGHT_BLOCK_15, - default => throw new BlockStateSerializeException("Invalid light level " . $block->getLightLevel()), - }, [])); - $this->map(Blocks::LIGHTNING_ROD(), function(LightningRod $block) : Writer{ - return Writer::create(Ids::LIGHTNING_ROD) - ->writeFacingDirection($block->getFacing()); - }); - $this->map(Blocks::LILAC(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::LILAC))); - $this->map(Blocks::LIT_PUMPKIN(), function(LitPumpkin $block) : Writer{ - return Writer::create(Ids::LIT_PUMPKIN) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::LOOM(), function(Loom $block) : Writer{ - return Writer::create(Ids::LOOM) - ->writeLegacyHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::MATERIAL_REDUCER(), fn(ChemistryTable $block) => Helper::encodeChemistryTable($block, Writer::create(Ids::MATERIAL_REDUCER))); - $this->map(Blocks::MELON_STEM(), fn(MelonStem $block) => Helper::encodeStem($block, new Writer(Ids::MELON_STEM))); - $this->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS); - $this->map(Blocks::MOSSY_COBBLESTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_COBBLESTONE_WALL))); - $this->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS); - $this->map(Blocks::MOSSY_STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::MOSSY_STONE_BRICK_WALL))); - $this->mapSlab(Blocks::MUD_BRICK_SLAB(), Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::MUD_BRICK_STAIRS(), Ids::MUD_BRICK_STAIRS); - $this->map(Blocks::MUD_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::MUD_BRICK_WALL))); - $this->map(Blocks::MUDDY_MANGROVE_ROOTS(), fn(SimplePillar $block) => Writer::create(Ids::MUDDY_MANGROVE_ROOTS) - ->writePillarAxis($block->getAxis())); - $this->map(Blocks::MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); - $this->mapSlab(Blocks::NETHER_BRICK_SLAB(), Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); - $this->map(Blocks::NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::NETHER_BRICK_WALL))); - $this->map(Blocks::NETHER_PORTAL(), function(NetherPortal $block) : Writer{ - return Writer::create(Ids::PORTAL) - ->writeString(StateNames::PORTAL_AXIS, match($block->getAxis()){ - Axis::X => StringValues::PORTAL_AXIS_X, - Axis::Z => StringValues::PORTAL_AXIS_Z, - default => throw new BlockStateSerializeException("Invalid Nether Portal axis " . $block->getAxis()), - }); - }); - $this->map(Blocks::NETHER_WART(), function(NetherWartPlant $block) : Writer{ - return Writer::create(Ids::NETHER_WART) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::PEONY(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::PEONY))); - $this->map(Blocks::PINK_PETALS(), function(PinkPetals $block) : Writer{ - return Writer::create(Ids::PINK_PETALS) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeInt(StateNames::GROWTH, $block->getCount() - 1); - }); - $this->map(Blocks::PITCHER_PLANT(), function(DoublePlant $block) : Writer{ - return Writer::create(Ids::PITCHER_PLANT) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $this->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge()) - ->writeBool(StateNames::UPPER_BLOCK_BIT, false); - }); - $this->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $this->mapSlab(Blocks::POLISHED_ANDESITE_SLAB(), Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS); - $this->map(Blocks::POLISHED_BASALT(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::POLISHED_BASALT) - ->writePillarAxis($block->getAxis()); - }); - $this->mapSlab(Blocks::POLISHED_BLACKSTONE_BRICK_SLAB(), Ids::POLISHED_BLACKSTONE_BRICK_SLAB, Ids::POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_BLACKSTONE_BRICK_STAIRS(), Ids::POLISHED_BLACKSTONE_BRICK_STAIRS); - $this->map(Blocks::POLISHED_BLACKSTONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_BLACKSTONE_BRICK_WALL))); - $this->map(Blocks::POLISHED_BLACKSTONE_BUTTON(), fn(Button $block) => Helper::encodeButton($block, new Writer(Ids::POLISHED_BLACKSTONE_BUTTON))); - $this->map(Blocks::POLISHED_BLACKSTONE_PRESSURE_PLATE(), fn(SimplePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE))); - $this->mapSlab(Blocks::POLISHED_BLACKSTONE_SLAB(), Ids::POLISHED_BLACKSTONE_SLAB, Ids::POLISHED_BLACKSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_BLACKSTONE_STAIRS(), Ids::POLISHED_BLACKSTONE_STAIRS); - $this->map(Blocks::POLISHED_BLACKSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_BLACKSTONE_WALL))); - $this->mapSlab(Blocks::POLISHED_DEEPSLATE_SLAB(), Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_DEEPSLATE_STAIRS(), Ids::POLISHED_DEEPSLATE_STAIRS); - $this->map(Blocks::POLISHED_DEEPSLATE_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_DEEPSLATE_WALL))); - $this->mapSlab(Blocks::POLISHED_DIORITE_SLAB(), Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS); - $this->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS); - $this->mapSlab(Blocks::POLISHED_TUFF_SLAB(), Ids::POLISHED_TUFF_SLAB, Ids::POLISHED_TUFF_DOUBLE_SLAB); - $this->mapStairs(Blocks::POLISHED_TUFF_STAIRS(), Ids::POLISHED_TUFF_STAIRS); - $this->map(Blocks::POLISHED_TUFF_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::POLISHED_TUFF_WALL))); - $this->map(Blocks::POTATOES(), fn(Potato $block) => Helper::encodeCrops($block, new Writer(Ids::POTATOES))); - $this->map(Blocks::POWERED_RAIL(), function(PoweredRail $block) : Writer{ - return Writer::create(Ids::GOLDEN_RAIL) - ->writeBool(StateNames::RAIL_DATA_BIT, $block->isPowered()) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->mapSlab(Blocks::PRISMARINE_BRICKS_SLAB(), Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS); - $this->mapSlab(Blocks::PRISMARINE_SLAB(), Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB); - $this->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS); - $this->map(Blocks::PRISMARINE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::PRISMARINE_WALL))); - $this->map(Blocks::PUMPKIN(), Writer::create(Ids::PUMPKIN) - ->writeCardinalHorizontalFacing(Facing::SOUTH) //no longer used - ); - $this->map(Blocks::PUMPKIN_STEM(), fn(PumpkinStem $block) => Helper::encodeStem($block, new Writer(Ids::PUMPKIN_STEM))); - $this->map(Blocks::PURPUR(), Writer::create(Ids::PURPUR_BLOCK)->writePillarAxis(Axis::Y)); - $this->map(Blocks::PURPLE_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_PURPLE))); - $this->map(Blocks::PURPUR_PILLAR(), function(SimplePillar $block) : Writer{ - return Writer::create(Ids::PURPUR_PILLAR) - ->writePillarAxis($block->getAxis()); - }); - $this->mapSlab(Blocks::PURPUR_SLAB(), Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB); - $this->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS); - $this->map(Blocks::QUARTZ(), Helper::encodeQuartz(Axis::Y, Writer::create(Ids::QUARTZ_BLOCK))); - $this->map(Blocks::QUARTZ_PILLAR(), fn(SimplePillar $block) => Helper::encodeQuartz($block->getAxis(), Writer::create(Ids::QUARTZ_PILLAR))); - $this->mapSlab(Blocks::QUARTZ_SLAB(), Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB); - $this->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS); - $this->map(Blocks::RAIL(), function(Rail $block) : Writer{ - return Writer::create(Ids::RAIL) - ->writeInt(StateNames::RAIL_DIRECTION, $block->getShape()); - }); - $this->map(Blocks::REDSTONE_COMPARATOR(), function(RedstoneComparator $block) : Writer{ - return Writer::create($block->isPowered() ? Ids::POWERED_COMPARATOR : Ids::UNPOWERED_COMPARATOR) - ->writeBool(StateNames::OUTPUT_LIT_BIT, $block->isPowered()) - ->writeBool(StateNames::OUTPUT_SUBTRACT_BIT, $block->isSubtractMode()) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::REDSTONE_LAMP(), fn(RedstoneLamp $block) => new Writer($block->isPowered() ? Ids::LIT_REDSTONE_LAMP : Ids::REDSTONE_LAMP)); - $this->map(Blocks::REDSTONE_ORE(), fn(RedstoneOre $block) => new Writer($block->isLit() ? Ids::LIT_REDSTONE_ORE : Ids::REDSTONE_ORE)); - $this->map(Blocks::REDSTONE_REPEATER(), function(RedstoneRepeater $block) : Writer{ - return Writer::create($block->isPowered() ? Ids::POWERED_REPEATER : Ids::UNPOWERED_REPEATER) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeInt(StateNames::REPEATER_DELAY, $block->getDelay() - 1); - }); - $this->map(Blocks::REDSTONE_TORCH(), function(RedstoneTorch $block) : Writer{ - return Writer::create($block->isLit() ? Ids::REDSTONE_TORCH : Ids::UNLIT_REDSTONE_TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::REDSTONE_WIRE(), function(RedstoneWire $block) : Writer{ - return Writer::create(Ids::REDSTONE_WIRE) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::RED_MUSHROOM_BLOCK(), fn(RedMushroomBlock $block) => Helper::encodeMushroomBlock($block, new Writer(Ids::RED_MUSHROOM_BLOCK))); - $this->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS); - $this->map(Blocks::RED_NETHER_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_NETHER_BRICK_WALL))); - $this->mapSlab(Blocks::RED_SANDSTONE_SLAB(), Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS); - $this->map(Blocks::RED_SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RED_SANDSTONE_WALL))); - $this->map(Blocks::RED_TORCH(), fn(Torch $block) => Helper::encodeTorch($block, Writer::create(Ids::COLORED_TORCH_RED))); - $this->mapSlab(Blocks::RESIN_BRICK_SLAB(), Ids::RESIN_BRICK_SLAB, Ids::RESIN_BRICK_DOUBLE_SLAB); - $this->map(Blocks::RESIN_BRICK_STAIRS(), fn(Stair $block) => Helper::encodeStairs($block, new Writer(Ids::RESIN_BRICK_STAIRS))); - $this->map(Blocks::RESIN_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::RESIN_BRICK_WALL))); - $this->map(Blocks::RESIN_CLUMP(), function(ResinClump $block) : Writer{ - return Writer::create(Ids::RESIN_CLUMP) - ->writeFacingFlags($block->getFaces()); - }); - $this->map(Blocks::RESPAWN_ANCHOR(), function(RespawnAnchor $block) : Writer{ - return Writer::create(Ids::RESPAWN_ANCHOR) - ->writeInt(StateNames::RESPAWN_ANCHOR_CHARGE, $block->getCharges()); - }); - $this->map(Blocks::ROSE_BUSH(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::ROSE_BUSH))); - $this->mapSlab(Blocks::SANDSTONE_SLAB(), Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); - $this->map(Blocks::SANDSTONE_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::SANDSTONE_WALL))); - $this->map(Blocks::SEA_PICKLE(), function(SeaPickle $block) : Writer{ - return Writer::create(Ids::SEA_PICKLE) - ->writeBool(StateNames::DEAD_BIT, !$block->isUnderwater()) - ->writeInt(StateNames::CLUSTER_COUNT, $block->getCount() - 1); - }); - $this->map(Blocks::SMALL_DRIPLEAF(), function(SmallDripleaf $block) : Writer{ - return Writer::create(Ids::SMALL_DRIPLEAF_BLOCK) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $this->map(Blocks::SMOKER(), fn(Furnace $block) => Helper::encodeFurnace($block, Ids::SMOKER, Ids::LIT_SMOKER)); - $this->map(Blocks::SMOOTH_QUARTZ(), Helper::encodeQuartz(Axis::Y, Writer::create(Ids::SMOOTH_QUARTZ))); - $this->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB); - $this->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS); - $this->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SMOOTH_RED_SANDSTONE_STAIRS(), Ids::SMOOTH_RED_SANDSTONE_STAIRS); - $this->mapSlab(Blocks::SMOOTH_SANDSTONE_SLAB(), Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::SMOOTH_SANDSTONE_STAIRS(), Ids::SMOOTH_SANDSTONE_STAIRS); - $this->mapSlab(Blocks::SMOOTH_STONE_SLAB(), Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB); - $this->map(Blocks::SNOW_LAYER(), function(SnowLayer $block) : Writer{ - return Writer::create(Ids::SNOW_LAYER) - ->writeBool(StateNames::COVERED_BIT, false) - ->writeInt(StateNames::HEIGHT, $block->getLayers() - 1); - }); - $this->map(Blocks::SOUL_CAMPFIRE(), function(SoulCampfire $block) : Writer{ - return Writer::create(Ids::SOUL_CAMPFIRE) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeBool(StateNames::EXTINGUISHED, !$block->isLit()); - }); - $this->map(Blocks::SOUL_FIRE(), Writer::create(Ids::SOUL_FIRE) - ->writeInt(StateNames::AGE, 0) //useless for soul fire, we don't track it - ); - $this->map(Blocks::SOUL_LANTERN(), function(Lantern $block) : Writer{ - return Writer::create(Ids::SOUL_LANTERN) - ->writeBool(StateNames::HANGING, $block->isHanging()); - }); - $this->map(Blocks::SOUL_TORCH(), function(Torch $block) : Writer{ - return Writer::create(Ids::SOUL_TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::SPONGE(), fn(Sponge $block) => Writer::create($block->isWet() ? Ids::WET_SPONGE : Ids::SPONGE)); - $this->map(Blocks::STONECUTTER(), fn(Stonecutter $block) => Writer::create(Ids::STONECUTTER_BLOCK) - ->writeCardinalHorizontalFacing($block->getFacing())); - $this->mapSlab(Blocks::STONE_BRICK_SLAB(), Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS); - $this->map(Blocks::STONE_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, Writer::create(Ids::STONE_BRICK_WALL))); - $this->map(Blocks::STONE_BUTTON(), fn(StoneButton $block) => Helper::encodeButton($block, new Writer(Ids::STONE_BUTTON))); - $this->map(Blocks::STONE_PRESSURE_PLATE(), fn(StonePressurePlate $block) => Helper::encodeSimplePressurePlate($block, new Writer(Ids::STONE_PRESSURE_PLATE))); - $this->mapSlab(Blocks::STONE_SLAB(), Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB); - $this->mapStairs(Blocks::STONE_STAIRS(), Ids::NORMAL_STONE_STAIRS); - $this->map(Blocks::SUGARCANE(), function(Sugarcane $block) : Writer{ - return Writer::create(Ids::REEDS) - ->writeInt(StateNames::AGE, $block->getAge()); - }); - $this->map(Blocks::SUNFLOWER(), fn(DoublePlant $block) => Helper::encodeDoublePlant($block, Writer::create(Ids::SUNFLOWER))); - $this->map(Blocks::SWEET_BERRY_BUSH(), function(SweetBerryBush $block) : Writer{ - return Writer::create(Ids::SWEET_BERRY_BUSH) - ->writeInt(StateNames::GROWTH, $block->getAge()); - }); - $this->map(Blocks::TNT(), fn(TNT $block) => Writer::create($block->worksUnderwater() ? Ids::UNDERWATER_TNT : Ids::TNT) - ->writeBool(StateNames::EXPLODE_BIT, $block->isUnstable()) - ); - $this->map(Blocks::TORCH(), function(Torch $block) : Writer{ - return Writer::create(Ids::TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::TORCHFLOWER_CROP(), function(TorchflowerCrop $block){ - return Writer::create(Ids::TORCHFLOWER_CROP) - ->writeInt(StateNames::GROWTH, $block->isReady() ? 1 : 0); - }); - $this->map(Blocks::TRAPPED_CHEST(), function(TrappedChest $block) : Writer{ - return Writer::create(Ids::TRAPPED_CHEST) - ->writeCardinalHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::TRIPWIRE(), function(Tripwire $block) : Writer{ - return Writer::create(Ids::TRIP_WIRE) - ->writeBool(StateNames::ATTACHED_BIT, $block->isConnected()) - ->writeBool(StateNames::DISARMED_BIT, $block->isDisarmed()) - ->writeBool(StateNames::POWERED_BIT, $block->isTriggered()) - ->writeBool(StateNames::SUSPENDED_BIT, $block->isSuspended()); - }); - $this->map(Blocks::TRIPWIRE_HOOK(), function(TripwireHook $block) : Writer{ - return Writer::create(Ids::TRIPWIRE_HOOK) - ->writeBool(StateNames::ATTACHED_BIT, $block->isConnected()) - ->writeBool(StateNames::POWERED_BIT, $block->isPowered()) - ->writeLegacyHorizontalFacing($block->getFacing()); - }); - $this->mapSlab(Blocks::TUFF_BRICK_SLAB(), Ids::TUFF_BRICK_SLAB, Ids::TUFF_BRICK_DOUBLE_SLAB); - $this->mapStairs(Blocks::TUFF_BRICK_STAIRS(), Ids::TUFF_BRICK_STAIRS); - $this->map(Blocks::TUFF_BRICK_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::TUFF_BRICK_WALL))); - $this->mapSlab(Blocks::TUFF_SLAB(), Ids::TUFF_SLAB, Ids::TUFF_DOUBLE_SLAB); - $this->mapStairs(Blocks::TUFF_STAIRS(), Ids::TUFF_STAIRS); - $this->map(Blocks::TUFF_WALL(), fn(Wall $block) => Helper::encodeWall($block, new Writer(Ids::TUFF_WALL))); - $this->map(Blocks::TWISTING_VINES(), function(NetherVines $block) : Writer{ - return Writer::create(Ids::TWISTING_VINES) - ->writeInt(StateNames::TWISTING_VINES_AGE, $block->getAge()); - }); - $this->map(Blocks::UNDERWATER_TORCH(), function(UnderwaterTorch $block) : Writer{ - return Writer::create(Ids::UNDERWATER_TORCH) - ->writeTorchFacing($block->getFacing()); - }); - $this->map(Blocks::VINES(), function(Vine $block) : Writer{ - return Writer::create(Ids::VINE) - ->writeInt(StateNames::VINE_DIRECTION_BITS, ($block->hasFace(Facing::NORTH) ? BlockLegacyMetadata::VINE_FLAG_NORTH : 0) | ($block->hasFace(Facing::SOUTH) ? BlockLegacyMetadata::VINE_FLAG_SOUTH : 0) | ($block->hasFace(Facing::WEST) ? BlockLegacyMetadata::VINE_FLAG_WEST : 0) | ($block->hasFace(Facing::EAST) ? BlockLegacyMetadata::VINE_FLAG_EAST : 0)); - }); - $this->map(Blocks::WALL_BANNER(), function(WallBanner $block) : Writer{ - return Writer::create(Ids::WALL_BANNER) - ->writeHorizontalFacing($block->getFacing()); - }); - $this->map(Blocks::WATER(), fn(Water $block) => Helper::encodeLiquid($block, Ids::WATER, Ids::FLOWING_WATER)); - $this->map(Blocks::WEEPING_VINES(), function(NetherVines $block) : Writer{ - return Writer::create(Ids::WEEPING_VINES) - ->writeInt(StateNames::WEEPING_VINES_AGE, $block->getAge()); - }); - $this->map(Blocks::WEIGHTED_PRESSURE_PLATE_HEAVY(), function(WeightedPressurePlateHeavy $block) : Writer{ - return Writer::create(Ids::HEAVY_WEIGHTED_PRESSURE_PLATE) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), function(WeightedPressurePlateLight $block) : Writer{ - return Writer::create(Ids::LIGHT_WEIGHTED_PRESSURE_PLATE) - ->writeInt(StateNames::REDSTONE_SIGNAL, $block->getOutputSignalStrength()); - }); - $this->map(Blocks::WHEAT(), fn(Wheat $block) => Helper::encodeCrops($block, new Writer(Ids::WHEAT))); - } } diff --git a/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php new file mode 100644 index 000000000..02491bae6 --- /dev/null +++ b/src/data/bedrock/block/convert/BlockSerializerDeserializerRegistrar.php @@ -0,0 +1,237 @@ +> $components + * + * @return string[][] + * @phpstan-return list> + */ + private static function compileFlattenedIdPartMatrix(array $components) : array{ + $result = []; + foreach($components as $component){ + $column = is_string($component) ? [$component] : $component->getPossibleValues(); + + if(count($result) === 0){ + $result = array_map(fn($value) => [$value], $column); + }else{ + $stepResult = []; + foreach($result as $parts){ + foreach($column as $value){ + $stepPart = $parts; + $stepPart[] = $value; + $stepResult[] = $stepPart; + } + } + + $result = $stepResult; + } + } + + return $result; + } + + /** + * @param string[]|StringProperty[] $idComponents + * + * @phpstan-template TBlock of Block + * + * @phpstan-param TBlock $block + * @phpstan-param list> $idComponents + */ + private static function serializeFlattenedId(Block $block, array $idComponents) : string{ + $id = ""; + foreach($idComponents as $infix){ + $id .= is_string($infix) ? $infix : $infix->serializePlain($block); + } + return $id; + } + + /** + * @param string[]|StringProperty[] $idComponents + * @param string[] $idPropertyValues + * + * @phpstan-template TBlock of Block + * + * @phpstan-param TBlock $baseBlock + * @phpstan-param list> $idComponents + * @phpstan-param list $idPropertyValues + * + * @phpstan-return TBlock + */ + private static function deserializeFlattenedId(Block $baseBlock, array $idComponents, array $idPropertyValues) : Block{ + $preparedBlock = clone $baseBlock; + foreach($idComponents as $k => $component){ + if($component instanceof StringProperty){ + $fakeValue = $idPropertyValues[$k]; + $component->deserializePlain($preparedBlock, $fakeValue); + } + } + + return $preparedBlock; + } + + public function mapSimple(Block $block, string $id) : void{ + $this->deserializer->mapSimple($id, fn() => clone $block); + $this->serializer->mapSimple($block, $id); + } + + /** + * @phpstan-template TBlock of Block + * @phpstan-param FlattenedIdModel $model + */ + public function mapFlattenedId(FlattenedIdModel $model) : void{ + $block = $model->getBlock(); + + $idComponents = $model->getIdComponents(); + if(count($idComponents) === 0){ + throw new \InvalidArgumentException("No ID components provided"); + } + $properties = $model->getProperties(); + + //This is a really cursed hack that lets us essentially write flattened properties as blockstate properties, and + //then pull them out to compile an ID :D + //This works surprisingly well and is much more elegant than I would've expected + + if(count($properties) > 0){ + $this->serializer->map($block, function(Block $block) use ($idComponents, $properties) : Writer{ + $id = self::serializeFlattenedId($block, $idComponents); + + $writer = new Writer($id); + foreach($properties as $property){ + $property->serialize($block, $writer); + } + + return $writer; + }); + }else{ + $this->serializer->map($block, function(Block $block) use ($idComponents) : BlockStateData{ + //fast path for blocks with no state properties + $id = self::serializeFlattenedId($block, $idComponents); + return BlockStateData::current($id, []); + }); + } + + $idPermutations = self::compileFlattenedIdPartMatrix($idComponents); + foreach($idPermutations as $idParts){ + //deconstruct the ID into a partial state + //we can do this at registration time since there will be multiple deserializers + $preparedBlock = self::deserializeFlattenedId($block, $idComponents, $idParts); + $id = implode("", $idParts); + + if(count($properties) > 0){ + $this->deserializer->map($id, function(Reader $reader) use ($preparedBlock, $properties) : Block{ + $block = clone $preparedBlock; + + foreach($properties as $property){ + $property->deserialize($block, $reader); + } + return $block; + }); + }else{ + //fast path for blocks with no state properties + $this->deserializer->map($id, fn() => clone $preparedBlock); + } + } + } + + /** + * @phpstan-template TBlock of Block&Colored + * @phpstan-param TBlock $block + */ + public function mapColored(Block $block, string $idPrefix, string $idSuffix) : void{ + $this->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([ + $idPrefix, + CommonProperties::getInstance()->dyeColorIdInfix, + $idSuffix + ]) + ); + } + + public function mapSlab(Slab $block, string $type) : void{ + $commonProperties = CommonProperties::getInstance(); + $this->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents(["minecraft:", $type, "_", $commonProperties->slabIdInfix, "slab"]) + ->properties([$commonProperties->slabPositionProperty]) + ); + } + + public function mapStairs(Stair $block, string $id) : void{ + $this->mapModel(Model::create($block, $id)->properties(CommonProperties::getInstance()->stairProperties)); + } + + /** + * @phpstan-template TBlock of Block + * @phpstan-param Model $model + */ + public function mapModel(Model $model) : void{ + $id = $model->getId(); + $block = $model->getBlock(); + $propertyDescriptors = $model->getProperties(); + + $this->deserializer->map($id, static function(Reader $in) use ($block, $propertyDescriptors) : Block{ + $newBlock = clone $block; + foreach($propertyDescriptors as $descriptor){ + $descriptor->deserialize($newBlock, $in); + } + return $newBlock; + }); + $this->serializer->map($block, static function(Block $block) use ($id, $propertyDescriptors) : Writer{ + $writer = new Writer($id); + foreach($propertyDescriptors as $descriptor){ + $descriptor->serialize($block, $writer); + } + return $writer; + }); + } +} diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index 3cf55429e..1d48ec76f 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -56,11 +56,13 @@ use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\data\bedrock\MushroomBlockTypeIdMap; -use pocketmine\math\Axis; use pocketmine\math\Facing; -use pocketmine\utils\AssumptionFailedError; +/** + * @deprecated + */ final class BlockStateDeserializerHelper{ /** @throws BlockStateDeserializeException */ @@ -71,6 +73,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TCandle of Candle * @phpstan-param TCandle $block * @phpstan-return TCandle @@ -103,6 +106,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TBlock of CopperMaterial * * @phpstan-param TBlock $block @@ -115,6 +119,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TBlock of CopperMaterial * * @phpstan-param TBlock $block @@ -133,6 +138,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TDoor of Door * @phpstan-param TDoor $block * @phpstan-return TDoor @@ -155,7 +161,10 @@ final class BlockStateDeserializerHelper{ ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{ return $block ->setFacing($in->readCardinalHorizontalFacing()) @@ -163,17 +172,19 @@ final class BlockStateDeserializerHelper{ ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{ return $block - ->setAxis(match($in->readBoundedInt(BlockStateNames::CORAL_FAN_DIRECTION, 0, 1)){ - 0 => Axis::X, - 1 => Axis::Z, - default => throw new AssumptionFailedError("readBoundedInt() should have prevented this"), - }); + ->setAxis($in->mapIntFromInt(BlockStateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis)); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeFloorSign(FloorSign $block, BlockStateReader $in) : FloorSign{ return $block ->setRotation($in->readBoundedInt(BlockStateNames::GROUND_SIGN_DIRECTION, 0, 15)); @@ -186,7 +197,10 @@ final class BlockStateDeserializerHelper{ ->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT)); } - /** @throws BlockStateDeserializeException */ + /** + * @throws BlockStateDeserializeException + * @deprecated + */ public static function decodeLeaves(Leaves $block, BlockStateReader $in) : Leaves{ return $block ->setNoDecay($in->readBool(StateNames::PERSISTENT_BIT)) @@ -236,7 +250,10 @@ final class BlockStateDeserializerHelper{ ->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1); } - /** @throws BlockStateDeserializeException */ + /** + * @throws BlockStateDeserializeException + * @deprecated + */ public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{ return $block ->setReady($in->readBool(BlockStateNames::AGE_BIT)); @@ -273,6 +290,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TStair of Stair * @phpstan-param TStair $block * @phpstan-return TStair @@ -296,6 +314,7 @@ final class BlockStateDeserializerHelper{ } /** + * @deprecated * @phpstan-template TTrapdoor of Trapdoor * @phpstan-param TTrapdoor $block * @phpstan-return TTrapdoor @@ -320,12 +339,19 @@ final class BlockStateDeserializerHelper{ return $block; } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{ return $block ->setFacing($in->readHorizontalFacing()); } + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public static function decodeWeightedPressurePlate(WeightedPressurePlate $block, BlockStateReader $in) : WeightedPressurePlate{ return $block ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); diff --git a/src/data/bedrock/block/convert/BlockStateReader.php b/src/data/bedrock/block/convert/BlockStateReader.php index e3a02885f..4d09d2f85 100644 --- a/src/data/bedrock/block/convert/BlockStateReader.php +++ b/src/data/bedrock/block/convert/BlockStateReader.php @@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; +use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\nbt\tag\ByteTag; @@ -112,45 +115,45 @@ final class BlockStateReader{ } /** - * @param int[] $mapping - * @phpstan-param array $mapping - * @phpstan-return int + * @deprecated + * @phpstan-param IntFromRawStateMap $map * @throws BlockStateDeserializeException */ - private function parseFacingValue(int $value, array $mapping) : int{ - $result = $mapping[$value] ?? null; - if($result === null){ - throw new BlockStateDeserializeException("Unmapped facing value " . $value); - } - return $result; - } + public function mapIntFromString(string $name, IntFromRawStateMap $map) : int{ + $raw = $this->readString($name); - /** @throws BlockStateDeserializeException */ - public function readFacingDirection() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::FACING_DIRECTION), [ - 0 => Facing::DOWN, - 1 => Facing::UP, - 2 => Facing::NORTH, - 3 => Facing::SOUTH, - 4 => Facing::WEST, - 5 => Facing::EAST - ]); - } - - /** @throws BlockStateDeserializeException */ - public function readBlockFace() : int{ - return match($raw = $this->readString(BlockStateNames::MC_BLOCK_FACE)){ - StringValues::MC_BLOCK_FACE_DOWN => Facing::DOWN, - StringValues::MC_BLOCK_FACE_UP => Facing::UP, - StringValues::MC_BLOCK_FACE_NORTH => Facing::NORTH, - StringValues::MC_BLOCK_FACE_SOUTH => Facing::SOUTH, - StringValues::MC_BLOCK_FACE_WEST => Facing::WEST, - StringValues::MC_BLOCK_FACE_EAST => Facing::EAST, - default => throw $this->badValueException(BlockStateNames::MC_BLOCK_FACE, $raw) - }; + return $map->rawToValue($raw) ?? throw $this->badValueException($name, $raw); } /** + * @deprecated + * @phpstan-param IntFromRawStateMap $map + * @throws BlockStateDeserializeException + */ + public function mapIntFromInt(string $name, IntFromRawStateMap $map) : int{ + $raw = $this->readInt($name); + + return $map->rawToValue($raw) ?? throw $this->badValueException($name, (string) $raw); + } + + /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readFacingDirection() : int{ + return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing); + } + + /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readBlockFace() : int{ + return $this->mapIntFromString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace); + } + + /** + * @deprecated * @return int[] * @phpstan-return array */ @@ -173,82 +176,69 @@ final class BlockStateReader{ return $result; } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readEndRodFacingDirection() : int{ $result = $this->readFacingDirection(); return Facing::axis($result) !== Axis::Y ? Facing::opposite($result) : $result; } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::FACING_DIRECTION), [ - 0 => Facing::NORTH, //should be illegal, but 1.13 allows it - 1 => Facing::NORTH, //also should be illegal - 2 => Facing::NORTH, - 3 => Facing::SOUTH, - 4 => Facing::WEST, - 5 => Facing::EAST - ]); - } - - /** @throws BlockStateDeserializeException */ - public function readWeirdoHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::WEIRDO_DIRECTION), [ - 0 => Facing::EAST, - 1 => Facing::WEST, - 2 => Facing::SOUTH, - 3 => Facing::NORTH - ]); - } - - /** @throws BlockStateDeserializeException */ - public function readLegacyHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::DIRECTION), [ - 0 => Facing::SOUTH, - 1 => Facing::WEST, - 2 => Facing::NORTH, - 3 => Facing::EAST - ]); + return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic); } /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readWeirdoHorizontalFacing() : int{ + return $this->mapIntFromInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus); + } + + /** + * @deprecated + * @throws BlockStateDeserializeException + */ + public function readLegacyHorizontalFacing() : int{ + return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE); + } + + /** + * @deprecated * This is for trapdoors, because Mojang botched the conversion in 1.13 * @throws BlockStateDeserializeException */ public function read5MinusHorizontalFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::DIRECTION), [ - 0 => Facing::EAST, - 1 => Facing::WEST, - 2 => Facing::SOUTH, - 3 => Facing::NORTH - ]); + return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus); } /** + * @deprecated * Used by pumpkins as of 1.20.0.23 beta * @throws BlockStateDeserializeException */ public function readCardinalHorizontalFacing() : int{ - return match($raw = $this->readString(BlockStateNames::MC_CARDINAL_DIRECTION)){ - StringValues::MC_CARDINAL_DIRECTION_NORTH => Facing::NORTH, - StringValues::MC_CARDINAL_DIRECTION_SOUTH => Facing::SOUTH, - StringValues::MC_CARDINAL_DIRECTION_WEST => Facing::WEST, - StringValues::MC_CARDINAL_DIRECTION_EAST => Facing::EAST, - default => throw $this->badValueException(BlockStateNames::MC_CARDINAL_DIRECTION, $raw) - }; + return $this->mapIntFromString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readCoralFacing() : int{ - return $this->parseFacingValue($this->readInt(BlockStateNames::CORAL_DIRECTION), [ - 0 => Facing::WEST, - 1 => Facing::EAST, - 2 => Facing::NORTH, - 3 => Facing::SOUTH - ]); + return $this->mapIntFromInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readFacingWithoutDown() : int{ $result = $this->readFacingDirection(); if($result === Facing::DOWN){ //shouldn't be legal, but 1.13 allows it @@ -257,6 +247,10 @@ final class BlockStateReader{ return $result; } + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readFacingWithoutUp() : int{ $result = $this->readFacingDirection(); if($result === Facing::UP){ @@ -266,23 +260,17 @@ final class BlockStateReader{ } /** - * @phpstan-return Axis::* + * @deprecated * @throws BlockStateDeserializeException */ public function readPillarAxis() : int{ - $rawValue = $this->readString(BlockStateNames::PILLAR_AXIS); - $value = [ - StringValues::PILLAR_AXIS_X => Axis::X, - StringValues::PILLAR_AXIS_Y => Axis::Y, - StringValues::PILLAR_AXIS_Z => Axis::Z - ][$rawValue] ?? null; - if($value === null){ - throw $this->badValueException(BlockStateNames::PILLAR_AXIS, $rawValue, "Invalid axis value"); - } - return $value; + return $this->mapIntFromString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readSlabPosition() : SlabType{ return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){ StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM, @@ -292,34 +280,25 @@ final class BlockStateReader{ } /** - * @phpstan-return Facing::UP|Facing::NORTH|Facing::SOUTH|Facing::WEST|Facing::EAST + * @deprecated * @throws BlockStateDeserializeException */ public function readTorchFacing() : int{ - //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) - return match($rawValue = $this->readString(BlockStateNames::TORCH_FACING_DIRECTION)){ - StringValues::TORCH_FACING_DIRECTION_EAST => Facing::WEST, - StringValues::TORCH_FACING_DIRECTION_NORTH => Facing::SOUTH, - StringValues::TORCH_FACING_DIRECTION_SOUTH => Facing::NORTH, - StringValues::TORCH_FACING_DIRECTION_TOP => Facing::UP, - StringValues::TORCH_FACING_DIRECTION_UNKNOWN => Facing::UP, //should be illegal, but 1.13 allows it - StringValues::TORCH_FACING_DIRECTION_WEST => Facing::EAST, - default => throw $this->badValueException(BlockStateNames::TORCH_FACING_DIRECTION, $rawValue, "Invalid torch facing"), - }; + return $this->mapIntFromString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readBellAttachmentType() : BellAttachmentType{ - return match($type = $this->readString(BlockStateNames::ATTACHMENT)){ - StringValues::ATTACHMENT_HANGING => BellAttachmentType::CEILING, - StringValues::ATTACHMENT_STANDING => BellAttachmentType::FLOOR, - StringValues::ATTACHMENT_SIDE => BellAttachmentType::ONE_WALL, - StringValues::ATTACHMENT_MULTIPLE => BellAttachmentType::TWO_WALLS, - default => throw $this->badValueException(BlockStateNames::ATTACHMENT, $type), - }; + return $this->readUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType); } - /** @throws BlockStateDeserializeException */ + /** + * @deprecated + * @throws BlockStateDeserializeException + */ public function readWallConnectionType(string $name) : ?WallConnectionType{ return match($type = $this->readString($name)){ //TODO: this looks a bit confusing due to use of EAST, but the values are the same for all connections @@ -332,6 +311,23 @@ final class BlockStateReader{ }; } + /** + * @deprecated + * @phpstan-template TEnum of \UnitEnum + * @phpstan-param EnumFromRawStateMap $map + * @phpstan-return TEnum + * @throws BlockStateDeserializeException + */ + public function readUnitEnum(string $name, EnumFromRawStateMap $map) : \UnitEnum{ + $value = $this->readString($name); + + $mapped = $map->rawToValue($value); + if($mapped === null){ + throw $this->badValueException($name, $value); + } + return $mapped; + } + /** * Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled */ diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php index a25044153..da3dbb387 100644 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php @@ -55,6 +55,9 @@ use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer; use pocketmine\data\bedrock\MushroomBlockTypeIdMap; use pocketmine\math\Facing; +/** + * @deprecated + */ final class BlockStateSerializerHelper{ public static function encodeButton(Button $block, Writer $out) : Writer{ return $out @@ -77,6 +80,9 @@ final class BlockStateSerializerHelper{ return $out->writeInt(BlockStateNames::GROWTH, $block->getAge()); } + /** + * @deprecated + */ public static function encodeTorch(Torch $block, Writer $out) : Writer{ return $out ->writeTorchFacing($block->getFacing()); @@ -97,6 +103,9 @@ final class BlockStateSerializerHelper{ }; } + /** + * @deprecated + */ public static function encodeDoor(Door $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()) @@ -111,6 +120,9 @@ final class BlockStateSerializerHelper{ ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()); } + /** + * @deprecated + */ public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{ return $out ->writeCardinalHorizontalFacing($block->getFacing()) @@ -118,6 +130,9 @@ final class BlockStateSerializerHelper{ ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); } + /** + * @deprecated + */ public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{ return $out ->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation()); @@ -135,6 +150,9 @@ final class BlockStateSerializerHelper{ ->writeFacingDirection($block->getFacing()); } + /** + * @deprecated + */ public static function encodeLeaves(Leaves $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay()) @@ -159,11 +177,17 @@ final class BlockStateSerializerHelper{ ->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType())); } + /** + * @deprecated + */ public static function encodeQuartz(int $axis, Writer $out) : Writer{ return $out ->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway } + /** + * @deprecated + */ public static function encodeSapling(Sapling $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::AGE_BIT, $block->isReady()); @@ -193,6 +217,9 @@ final class BlockStateSerializerHelper{ self::encodeSingleSlab($block, $singleId); } + /** + * @deprecated + */ public static function encodeStairs(Stair $block, Writer $out) : Writer{ return $out ->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown()) @@ -208,6 +235,9 @@ final class BlockStateSerializerHelper{ ->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing); } + /** + * @deprecated + */ public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{ return $out ->write5MinusHorizontalFacing($block->getFacing()) @@ -224,6 +254,9 @@ final class BlockStateSerializerHelper{ ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST)); } + /** + * @deprecated + */ public static function encodeWallSign(WallSign $block, Writer $out) : Writer{ return $out ->writeHorizontalFacing($block->getFacing()); diff --git a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php index 1e9a4041f..ca5c12412 100644 --- a/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php +++ b/src/data/bedrock/block/convert/BlockStateToObjectDeserializer.php @@ -23,50 +23,18 @@ declare(strict_types=1); namespace pocketmine\data\bedrock\block\convert; -use pocketmine\block\AmethystCluster; -use pocketmine\block\Anvil; -use pocketmine\block\Bamboo; use pocketmine\block\Block; -use pocketmine\block\CakeWithDyedCandle; -use pocketmine\block\CaveVines; -use pocketmine\block\ChorusFlower; -use pocketmine\block\DoublePitcherCrop; -use pocketmine\block\Opaque; -use pocketmine\block\PinkPetals; -use pocketmine\block\PitcherCrop; use pocketmine\block\RuntimeBlockStateRegistry; use pocketmine\block\Slab; use pocketmine\block\Stair; -use pocketmine\block\SweetBerryBush; -use pocketmine\block\utils\BrewingStandSlot; -use pocketmine\block\utils\ChiseledBookshelfSlot; -use pocketmine\block\utils\Colored; -use pocketmine\block\utils\CopperMaterial; -use pocketmine\block\utils\CopperOxidation; -use pocketmine\block\utils\CoralType; -use pocketmine\block\utils\DirtType; -use pocketmine\block\utils\DripleafState; -use pocketmine\block\utils\DyeColor; -use pocketmine\block\utils\FroglightType; -use pocketmine\block\utils\LeverFacing; -use pocketmine\block\utils\MobHeadType; -use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Wood; -use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializer; -use pocketmine\data\bedrock\block\BlockStateNames as StateNames; -use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; -use pocketmine\data\bedrock\block\BlockTypeNames as Ids; use pocketmine\data\bedrock\block\convert\BlockStateDeserializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader; -use pocketmine\math\Axis; -use pocketmine\math\Facing; -use pocketmine\utils\Utils; use function array_key_exists; use function count; -use function min; final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ @@ -82,21 +50,6 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ */ private array $simpleCache = []; - public function __construct(){ - $this->registerCandleDeserializers(); - $this->registerFlatColorBlockDeserializers(); - $this->registerFlatCoralDeserializers(); - $this->registerCauldronDeserializers(); - $this->registerFlatWoodBlockDeserializers(); - $this->registerLeavesDeserializers(); - $this->registerSaplingDeserializers(); - $this->registerLightDeserializers(); - $this->registerMobHeadDeserializers(); - $this->registerCopperDeserializers(); - $this->registerSimpleDeserializers(); - $this->registerDeserializers(); - } - public function deserialize(BlockStateData $stateData) : int{ if(count($stateData->getStates()) === 0){ //if a block has zero properties, we can keep a map of string ID -> internal blockstate ID @@ -133,12 +86,16 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ return $this->deserializeFuncs[$id] ?? null; } - /** @phpstan-param \Closure() : Block $getBlock */ + /** + * @deprecated + * @phpstan-param \Closure() : Block $getBlock + */ public function mapSimple(string $id, \Closure $getBlock) : void{ $this->map($id, $getBlock); } /** + * @deprecated * @phpstan-param \Closure(Reader) : Slab $getBlock */ public function mapSlab(string $singleId, string $doubleId, \Closure $getBlock) : void{ @@ -147,1552 +104,22 @@ final class BlockStateToObjectDeserializer implements BlockStateDeserializer{ } /** + * @deprecated * @phpstan-param \Closure() : Stair $getBlock */ public function mapStairs(string $id, \Closure $getBlock) : void{ $this->map($id, fn(Reader $in) : Stair => Helper::decodeStairs($getBlock(), $in)); } - /** @phpstan-param \Closure() : Wood $getBlock */ + /** + * @deprecated + * @phpstan-param \Closure() : Wood $getBlock + */ public function mapLog(string $unstrippedId, string $strippedId, \Closure $getBlock) : void{ $this->map($unstrippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), false, $in)); $this->map($strippedId, fn(Reader $in) => Helper::decodeLog($getBlock(), true, $in)); } - /** - * @phpstan-template TBlock of Block - * @phpstan-template TEnum of \UnitEnum - * - * @phpstan-param StringEnumMap $mapProperty - * @phpstan-param \Closure(TEnum) : TBlock $getBlock - * @phpstan-param ?\Closure(TBlock, Reader) : TBlock $extra - */ - public function mapFlattenedEnum( - StringEnumMap $mapProperty, - string $prefix, - string $suffix, - \Closure $getBlock, - ?\Closure $extra = null - ) : void{ - foreach(Utils::stringifyKeys($mapProperty->getValueToEnum()) as $infix => $enumCase){ - $id = $prefix . $infix . $suffix; - if($extra === null){ - $this->map($id, fn() => $getBlock($enumCase)); - }else{ - $this->map($id, function(Reader $in) use ($enumCase, $getBlock, $extra) : Block{ - $block = $getBlock($enumCase); - $extra($block, $in); - return $block; - }); - } - } - } - - /** - * @phpstan-template TBlock of Block&Colored - * @phpstan-param \Closure() : TBlock $getBlock - * @phpstan-param ?\Closure(TBlock, Reader) : TBlock $extra - */ - public function mapColored(string $prefix, string $suffix, \Closure $getBlock, ?\Closure $extra = null) : void{ - $this->mapFlattenedEnum( - ValueMappings::getInstance()->getEnumMap(DyeColor::class), - $prefix, - $suffix, - fn(DyeColor $color) => $getBlock()->setColor($color), - $extra - ); - } - - private function registerCandleDeserializers() : void{ - $this->map(Ids::CANDLE, fn(Reader $in) => Helper::decodeCandle(Blocks::CANDLE(), $in)); - $this->mapColored( - "minecraft:", - "_candle", - fn() => Blocks::DYED_CANDLE(), - Helper::decodeCandle(...) - ); - - $this->map(Ids::CANDLE_CAKE, fn(Reader $in) => Blocks::CAKE_WITH_CANDLE()->setLit($in->readBool(StateNames::LIT))); - - $this->mapColored( - "minecraft:", - "_candle_cake", - fn() => Blocks::CAKE_WITH_DYED_CANDLE(), - fn(CakeWithDyedCandle $block, Reader $in) => $block->setLit($in->readBool(StateNames::LIT)) - ); - } - - private function registerFlatColorBlockDeserializers() : void{ - $this->mapColored("minecraft:hard_", "_stained_glass", fn() => Blocks::STAINED_HARDENED_GLASS()); - $this->mapColored("minecraft:hard_", "_stained_glass_pane", fn() => Blocks::STAINED_HARDENED_GLASS_PANE()); - - $this->mapColored("minecraft:", "_carpet", fn() => Blocks::CARPET()); - $this->mapColored("minecraft:", "_concrete", fn() => Blocks::CONCRETE()); - $this->mapColored("minecraft:", "_concrete_powder", fn() => Blocks::CONCRETE_POWDER()); - $this->mapColored("minecraft:", "_shulker_box", fn() => Blocks::DYED_SHULKER_BOX()); - $this->mapColored("minecraft:", "_stained_glass", fn() => Blocks::STAINED_GLASS()); - $this->mapColored("minecraft:", "_stained_glass_pane", fn() => Blocks::STAINED_GLASS_PANE()); - $this->mapColored("minecraft:", "_terracotta", fn() => Blocks::STAINED_CLAY()); - $this->mapColored("minecraft:", "_wool", fn() => Blocks::WOOL()); - - foreach([ - Ids::BLACK_GLAZED_TERRACOTTA => DyeColor::BLACK, - Ids::BLUE_GLAZED_TERRACOTTA => DyeColor::BLUE, - Ids::BROWN_GLAZED_TERRACOTTA => DyeColor::BROWN, - Ids::CYAN_GLAZED_TERRACOTTA => DyeColor::CYAN, - Ids::GRAY_GLAZED_TERRACOTTA => DyeColor::GRAY, - Ids::GREEN_GLAZED_TERRACOTTA => DyeColor::GREEN, - Ids::LIGHT_BLUE_GLAZED_TERRACOTTA => DyeColor::LIGHT_BLUE, - Ids::SILVER_GLAZED_TERRACOTTA => DyeColor::LIGHT_GRAY, //minecraft sadness - Ids::LIME_GLAZED_TERRACOTTA => DyeColor::LIME, - Ids::MAGENTA_GLAZED_TERRACOTTA => DyeColor::MAGENTA, - Ids::ORANGE_GLAZED_TERRACOTTA => DyeColor::ORANGE, - Ids::PINK_GLAZED_TERRACOTTA => DyeColor::PINK, - Ids::PURPLE_GLAZED_TERRACOTTA => DyeColor::PURPLE, - Ids::RED_GLAZED_TERRACOTTA => DyeColor::RED, - Ids::WHITE_GLAZED_TERRACOTTA => DyeColor::WHITE, - Ids::YELLOW_GLAZED_TERRACOTTA => DyeColor::YELLOW, - ] as $id => $color){ - $this->map($id, fn(Reader $in) => Blocks::GLAZED_TERRACOTTA() - ->setColor($color) - ->setFacing($in->readHorizontalFacing()) - ); - } - } - - private function registerFlatCoralDeserializers() : void{ - foreach([ - Ids::BRAIN_CORAL => CoralType::BRAIN, - Ids::BUBBLE_CORAL => CoralType::BUBBLE, - Ids::FIRE_CORAL => CoralType::FIRE, - Ids::HORN_CORAL => CoralType::HORN, - Ids::TUBE_CORAL => CoralType::TUBE, - ] as $id => $coralType){ - $this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(false)); - } - foreach([ - Ids::DEAD_BRAIN_CORAL => CoralType::BRAIN, - Ids::DEAD_BUBBLE_CORAL => CoralType::BUBBLE, - Ids::DEAD_FIRE_CORAL => CoralType::FIRE, - Ids::DEAD_HORN_CORAL => CoralType::HORN, - Ids::DEAD_TUBE_CORAL => CoralType::TUBE, - ] as $id => $coralType){ - $this->mapSimple($id, fn() => Blocks::CORAL()->setCoralType($coralType)->setDead(true)); - } - - foreach([ - [CoralType::BRAIN, Ids::BRAIN_CORAL_FAN, Ids::DEAD_BRAIN_CORAL_FAN], - [CoralType::BUBBLE, Ids::BUBBLE_CORAL_FAN, Ids::DEAD_BUBBLE_CORAL_FAN], - [CoralType::FIRE, Ids::FIRE_CORAL_FAN, Ids::DEAD_FIRE_CORAL_FAN], - [CoralType::HORN, Ids::HORN_CORAL_FAN, Ids::DEAD_HORN_CORAL_FAN], - [CoralType::TUBE, Ids::TUBE_CORAL_FAN, Ids::DEAD_TUBE_CORAL_FAN], - ] as [$coralType, $aliveId, $deadId]){ - $this->map($aliveId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(false), $in)); - $this->map($deadId, fn(Reader $in) => Helper::decodeFloorCoralFan(Blocks::CORAL_FAN()->setCoralType($coralType)->setDead(true), $in)); - } - - foreach([ - [CoralType::BRAIN, Ids::BRAIN_CORAL_BLOCK, Ids::DEAD_BRAIN_CORAL_BLOCK], - [CoralType::BUBBLE, Ids::BUBBLE_CORAL_BLOCK, Ids::DEAD_BUBBLE_CORAL_BLOCK], - [CoralType::FIRE, Ids::FIRE_CORAL_BLOCK, Ids::DEAD_FIRE_CORAL_BLOCK], - [CoralType::HORN, Ids::HORN_CORAL_BLOCK, Ids::DEAD_HORN_CORAL_BLOCK], - [CoralType::TUBE, Ids::TUBE_CORAL_BLOCK, Ids::DEAD_TUBE_CORAL_BLOCK], - ] as [$coralType, $aliveId, $deadId]){ - $this->map($aliveId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(false)); - $this->map($deadId, fn(Reader $in) => Blocks::CORAL_BLOCK()->setCoralType($coralType)->setDead(true)); - } - - foreach([ - [CoralType::BRAIN, Ids::BRAIN_CORAL_WALL_FAN, Ids::DEAD_BRAIN_CORAL_WALL_FAN], - [CoralType::BUBBLE, Ids::BUBBLE_CORAL_WALL_FAN, Ids::DEAD_BUBBLE_CORAL_WALL_FAN], - [CoralType::FIRE, Ids::FIRE_CORAL_WALL_FAN, Ids::DEAD_FIRE_CORAL_WALL_FAN], - [CoralType::HORN, Ids::HORN_CORAL_WALL_FAN, Ids::DEAD_HORN_CORAL_WALL_FAN], - [CoralType::TUBE, Ids::TUBE_CORAL_WALL_FAN, Ids::DEAD_TUBE_CORAL_WALL_FAN], - ] as [$coralType, $aliveId, $deadId]){ - $this->map($aliveId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(false)); - $this->map($deadId, fn(Reader $in) => Blocks::WALL_CORAL_FAN()->setFacing($in->readCoralFacing())->setCoralType($coralType)->setDead(true)); - } - } - - private function registerCauldronDeserializers() : void{ - $deserializer = function(Reader $in) : Block{ - $level = $in->readBoundedInt(StateNames::FILL_LEVEL, 0, 6); - if($level === 0){ - $in->ignored(StateNames::CAULDRON_LIQUID); - return Blocks::CAULDRON(); - } - - return (match($liquid = $in->readString(StateNames::CAULDRON_LIQUID)){ - StringValues::CAULDRON_LIQUID_WATER => Blocks::WATER_CAULDRON(), - StringValues::CAULDRON_LIQUID_LAVA => Blocks::LAVA_CAULDRON(), - StringValues::CAULDRON_LIQUID_POWDER_SNOW => throw new UnsupportedBlockStateException("Powder snow is not supported yet"), - default => throw $in->badValueException(StateNames::CAULDRON_LIQUID, $liquid) - })->setFillLevel($level); - }; - $this->map(Ids::CAULDRON, $deserializer); - } - - private function registerFlatWoodBlockDeserializers() : void{ - $this->map(Ids::ACACIA_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::ACACIA_BUTTON(), $in)); - $this->map(Ids::ACACIA_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::ACACIA_DOOR(), $in)); - $this->map(Ids::ACACIA_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::ACACIA_FENCE_GATE(), $in)); - $this->map(Ids::ACACIA_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::ACACIA_PRESSURE_PLATE(), $in)); - $this->map(Ids::ACACIA_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::ACACIA_SIGN(), $in)); - $this->map(Ids::ACACIA_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::ACACIA_TRAPDOOR(), $in)); - $this->map(Ids::ACACIA_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::ACACIA_WALL_SIGN(), $in)); - $this->mapLog(Ids::ACACIA_LOG, Ids::STRIPPED_ACACIA_LOG, fn() => Blocks::ACACIA_LOG()); - $this->mapLog(Ids::ACACIA_WOOD, Ids::STRIPPED_ACACIA_WOOD, fn() => Blocks::ACACIA_WOOD()); - $this->mapSimple(Ids::ACACIA_FENCE, fn() => Blocks::ACACIA_FENCE()); - $this->mapSimple(Ids::ACACIA_PLANKS, fn() => Blocks::ACACIA_PLANKS()); - $this->mapSlab(Ids::ACACIA_SLAB, Ids::ACACIA_DOUBLE_SLAB, fn() => Blocks::ACACIA_SLAB()); - $this->mapStairs(Ids::ACACIA_STAIRS, fn() => Blocks::ACACIA_STAIRS()); - - $this->map(Ids::BIRCH_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::BIRCH_BUTTON(), $in)); - $this->map(Ids::BIRCH_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::BIRCH_DOOR(), $in)); - $this->map(Ids::BIRCH_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::BIRCH_FENCE_GATE(), $in)); - $this->map(Ids::BIRCH_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::BIRCH_PRESSURE_PLATE(), $in)); - $this->map(Ids::BIRCH_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::BIRCH_SIGN(), $in)); - $this->map(Ids::BIRCH_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::BIRCH_TRAPDOOR(), $in)); - $this->map(Ids::BIRCH_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::BIRCH_WALL_SIGN(), $in)); - $this->mapLog(Ids::BIRCH_LOG, Ids::STRIPPED_BIRCH_LOG, fn() => Blocks::BIRCH_LOG()); - $this->mapLog(Ids::BIRCH_WOOD, Ids::STRIPPED_BIRCH_WOOD, fn() => Blocks::BIRCH_WOOD()); - $this->mapSimple(Ids::BIRCH_FENCE, fn() => Blocks::BIRCH_FENCE()); - $this->mapSimple(Ids::BIRCH_PLANKS, fn() => Blocks::BIRCH_PLANKS()); - $this->mapSlab(Ids::BIRCH_SLAB, Ids::BIRCH_DOUBLE_SLAB, fn() => Blocks::BIRCH_SLAB()); - $this->mapStairs(Ids::BIRCH_STAIRS, fn() => Blocks::BIRCH_STAIRS()); - - $this->map(Ids::CHERRY_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CHERRY_BUTTON(), $in)); - $this->map(Ids::CHERRY_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CHERRY_DOOR(), $in)); - $this->map(Ids::CHERRY_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CHERRY_FENCE_GATE(), $in)); - $this->map(Ids::CHERRY_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CHERRY_PRESSURE_PLATE(), $in)); - $this->map(Ids::CHERRY_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CHERRY_SIGN(), $in)); - $this->map(Ids::CHERRY_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CHERRY_TRAPDOOR(), $in)); - $this->map(Ids::CHERRY_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CHERRY_WALL_SIGN(), $in)); - $this->mapLog(Ids::CHERRY_LOG, Ids::STRIPPED_CHERRY_LOG, fn() => Blocks::CHERRY_LOG()); - $this->mapSimple(Ids::CHERRY_FENCE, fn() => Blocks::CHERRY_FENCE()); - $this->mapSimple(Ids::CHERRY_PLANKS, fn() => Blocks::CHERRY_PLANKS()); - $this->mapSlab(Ids::CHERRY_SLAB, Ids::CHERRY_DOUBLE_SLAB, fn() => Blocks::CHERRY_SLAB()); - $this->mapStairs(Ids::CHERRY_STAIRS, fn() => Blocks::CHERRY_STAIRS()); - $this->map(Ids::CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), false, $in)); - $this->map(Ids::STRIPPED_CHERRY_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::CHERRY_WOOD(), true, $in)); - - $this->map(Ids::CRIMSON_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::CRIMSON_BUTTON(), $in)); - $this->map(Ids::CRIMSON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::CRIMSON_DOOR(), $in)); - $this->map(Ids::CRIMSON_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::CRIMSON_FENCE_GATE(), $in)); - $this->map(Ids::CRIMSON_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::CRIMSON_PRESSURE_PLATE(), $in)); - $this->map(Ids::CRIMSON_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::CRIMSON_SIGN(), $in)); - $this->map(Ids::CRIMSON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::CRIMSON_TRAPDOOR(), $in)); - $this->map(Ids::CRIMSON_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::CRIMSON_WALL_SIGN(), $in)); - $this->mapLog(Ids::CRIMSON_HYPHAE, Ids::STRIPPED_CRIMSON_HYPHAE, fn() => Blocks::CRIMSON_HYPHAE()); - $this->mapLog(Ids::CRIMSON_STEM, Ids::STRIPPED_CRIMSON_STEM, fn() => Blocks::CRIMSON_STEM()); - $this->mapSimple(Ids::CRIMSON_FENCE, fn() => Blocks::CRIMSON_FENCE()); - $this->mapSimple(Ids::CRIMSON_PLANKS, fn() => Blocks::CRIMSON_PLANKS()); - $this->mapSlab(Ids::CRIMSON_SLAB, Ids::CRIMSON_DOUBLE_SLAB, fn() => Blocks::CRIMSON_SLAB()); - $this->mapStairs(Ids::CRIMSON_STAIRS, fn() => Blocks::CRIMSON_STAIRS()); - - $this->map(Ids::DARKOAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::DARK_OAK_SIGN(), $in)); - $this->map(Ids::DARKOAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::DARK_OAK_WALL_SIGN(), $in)); - $this->map(Ids::DARK_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::DARK_OAK_BUTTON(), $in)); - $this->map(Ids::DARK_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::DARK_OAK_DOOR(), $in)); - $this->map(Ids::DARK_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::DARK_OAK_FENCE_GATE(), $in)); - $this->map(Ids::DARK_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::DARK_OAK_PRESSURE_PLATE(), $in)); - $this->map(Ids::DARK_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::DARK_OAK_TRAPDOOR(), $in)); - $this->mapLog(Ids::DARK_OAK_LOG, Ids::STRIPPED_DARK_OAK_LOG, fn() => Blocks::DARK_OAK_LOG()); - $this->mapLog(Ids::DARK_OAK_WOOD, Ids::STRIPPED_DARK_OAK_WOOD, fn() => Blocks::DARK_OAK_WOOD()); - $this->mapSimple(Ids::DARK_OAK_FENCE, fn() => Blocks::DARK_OAK_FENCE()); - $this->mapSimple(Ids::DARK_OAK_PLANKS, fn() => Blocks::DARK_OAK_PLANKS()); - $this->mapSlab(Ids::DARK_OAK_SLAB, Ids::DARK_OAK_DOUBLE_SLAB, fn() => Blocks::DARK_OAK_SLAB()); - $this->mapStairs(Ids::DARK_OAK_STAIRS, fn() => Blocks::DARK_OAK_STAIRS()); - - $this->map(Ids::JUNGLE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::JUNGLE_BUTTON(), $in)); - $this->map(Ids::JUNGLE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::JUNGLE_DOOR(), $in)); - $this->map(Ids::JUNGLE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::JUNGLE_FENCE_GATE(), $in)); - $this->map(Ids::JUNGLE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::JUNGLE_PRESSURE_PLATE(), $in)); - $this->map(Ids::JUNGLE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::JUNGLE_SIGN(), $in)); - $this->map(Ids::JUNGLE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::JUNGLE_TRAPDOOR(), $in)); - $this->map(Ids::JUNGLE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::JUNGLE_WALL_SIGN(), $in)); - $this->mapLog(Ids::JUNGLE_LOG, Ids::STRIPPED_JUNGLE_LOG, fn() => Blocks::JUNGLE_LOG()); - $this->mapLog(Ids::JUNGLE_WOOD, Ids::STRIPPED_JUNGLE_WOOD, fn() => Blocks::JUNGLE_WOOD()); - $this->mapSimple(Ids::JUNGLE_FENCE, fn() => Blocks::JUNGLE_FENCE()); - $this->mapSimple(Ids::JUNGLE_PLANKS, fn() => Blocks::JUNGLE_PLANKS()); - $this->mapSlab(Ids::JUNGLE_SLAB, Ids::JUNGLE_DOUBLE_SLAB, fn() => Blocks::JUNGLE_SLAB()); - $this->mapStairs(Ids::JUNGLE_STAIRS, fn() => Blocks::JUNGLE_STAIRS()); - - $this->map(Ids::MANGROVE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::MANGROVE_BUTTON(), $in)); - $this->map(Ids::MANGROVE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::MANGROVE_DOOR(), $in)); - $this->map(Ids::MANGROVE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::MANGROVE_FENCE_GATE(), $in)); - $this->map(Ids::MANGROVE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::MANGROVE_PRESSURE_PLATE(), $in)); - $this->map(Ids::MANGROVE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::MANGROVE_SIGN(), $in)); - $this->map(Ids::MANGROVE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::MANGROVE_TRAPDOOR(), $in)); - $this->map(Ids::MANGROVE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::MANGROVE_WALL_SIGN(), $in)); - $this->mapLog(Ids::MANGROVE_LOG, Ids::STRIPPED_MANGROVE_LOG, fn() => Blocks::MANGROVE_LOG()); - $this->mapSimple(Ids::MANGROVE_FENCE, fn() => Blocks::MANGROVE_FENCE()); - $this->mapSimple(Ids::MANGROVE_PLANKS, fn() => Blocks::MANGROVE_PLANKS()); - $this->mapSlab(Ids::MANGROVE_SLAB, Ids::MANGROVE_DOUBLE_SLAB, fn() => Blocks::MANGROVE_SLAB()); - $this->mapStairs(Ids::MANGROVE_STAIRS, fn() => Blocks::MANGROVE_STAIRS()); - $this->map(Ids::MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), false, $in)); - $this->map(Ids::STRIPPED_MANGROVE_WOOD, fn(Reader $in) => Helper::decodeLog(Blocks::MANGROVE_WOOD(), true, $in)); - - //oak - due to age, many of these don't specify "oak", making for confusing reading - $this->map(Ids::WOODEN_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::OAK_BUTTON(), $in)); - $this->map(Ids::WOODEN_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::OAK_DOOR(), $in)); - $this->map(Ids::FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::OAK_FENCE_GATE(), $in)); - $this->map(Ids::WOODEN_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::OAK_PRESSURE_PLATE(), $in)); - $this->map(Ids::STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::OAK_SIGN(), $in)); - $this->map(Ids::TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::OAK_TRAPDOOR(), $in)); - $this->map(Ids::WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::OAK_WALL_SIGN(), $in)); - $this->mapLog(Ids::OAK_LOG, Ids::STRIPPED_OAK_LOG, fn() => Blocks::OAK_LOG()); - $this->mapLog(Ids::OAK_WOOD, Ids::STRIPPED_OAK_WOOD, fn() => Blocks::OAK_WOOD()); - $this->mapSimple(Ids::OAK_FENCE, fn() => Blocks::OAK_FENCE()); - $this->mapSimple(Ids::OAK_PLANKS, fn() => Blocks::OAK_PLANKS()); - $this->mapSlab(Ids::OAK_SLAB, Ids::OAK_DOUBLE_SLAB, fn() => Blocks::OAK_SLAB()); - $this->mapStairs(Ids::OAK_STAIRS, fn() => Blocks::OAK_STAIRS()); - - $this->map(Ids::PALE_OAK_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::PALE_OAK_BUTTON(), $in)); - $this->map(Ids::PALE_OAK_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::PALE_OAK_DOOR(), $in)); - $this->map(Ids::PALE_OAK_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::PALE_OAK_FENCE_GATE(), $in)); - $this->map(Ids::PALE_OAK_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::PALE_OAK_PRESSURE_PLATE(), $in)); - $this->map(Ids::PALE_OAK_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::PALE_OAK_SIGN(), $in)); - $this->map(Ids::PALE_OAK_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::PALE_OAK_TRAPDOOR(), $in)); - $this->map(Ids::PALE_OAK_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::PALE_OAK_WALL_SIGN(), $in)); - $this->mapLog(Ids::PALE_OAK_LOG, Ids::STRIPPED_PALE_OAK_LOG, fn() => Blocks::PALE_OAK_LOG()); - $this->mapLog(Ids::PALE_OAK_WOOD, Ids::STRIPPED_PALE_OAK_WOOD, fn() => Blocks::PALE_OAK_WOOD()); - $this->mapSimple(Ids::PALE_OAK_FENCE, fn() => Blocks::PALE_OAK_FENCE()); - $this->mapSimple(Ids::PALE_OAK_PLANKS, fn() => Blocks::PALE_OAK_PLANKS()); - $this->mapSlab(Ids::PALE_OAK_SLAB, Ids::PALE_OAK_DOUBLE_SLAB, fn() => Blocks::PALE_OAK_SLAB()); - $this->mapStairs(Ids::PALE_OAK_STAIRS, fn() => Blocks::PALE_OAK_STAIRS()); - - $this->map(Ids::SPRUCE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::SPRUCE_BUTTON(), $in)); - $this->map(Ids::SPRUCE_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::SPRUCE_DOOR(), $in)); - $this->map(Ids::SPRUCE_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::SPRUCE_FENCE_GATE(), $in)); - $this->map(Ids::SPRUCE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::SPRUCE_PRESSURE_PLATE(), $in)); - $this->map(Ids::SPRUCE_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::SPRUCE_SIGN(), $in)); - $this->map(Ids::SPRUCE_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::SPRUCE_TRAPDOOR(), $in)); - $this->map(Ids::SPRUCE_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::SPRUCE_WALL_SIGN(), $in)); - $this->mapLog(Ids::SPRUCE_LOG, Ids::STRIPPED_SPRUCE_LOG, fn() => Blocks::SPRUCE_LOG()); - $this->mapLog(Ids::SPRUCE_WOOD, Ids::STRIPPED_SPRUCE_WOOD, fn() => Blocks::SPRUCE_WOOD()); - $this->mapSimple(Ids::SPRUCE_FENCE, fn() => Blocks::SPRUCE_FENCE()); - $this->mapSimple(Ids::SPRUCE_PLANKS, fn() => Blocks::SPRUCE_PLANKS()); - $this->mapSlab(Ids::SPRUCE_SLAB, Ids::SPRUCE_DOUBLE_SLAB, fn() => Blocks::SPRUCE_SLAB()); - $this->mapStairs(Ids::SPRUCE_STAIRS, fn() => Blocks::SPRUCE_STAIRS()); - - $this->map(Ids::WARPED_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::WARPED_BUTTON(), $in)); - $this->map(Ids::WARPED_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::WARPED_DOOR(), $in)); - $this->map(Ids::WARPED_FENCE_GATE, fn(Reader $in) => Helper::decodeFenceGate(Blocks::WARPED_FENCE_GATE(), $in)); - $this->map(Ids::WARPED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::WARPED_PRESSURE_PLATE(), $in)); - $this->map(Ids::WARPED_STANDING_SIGN, fn(Reader $in) => Helper::decodeFloorSign(Blocks::WARPED_SIGN(), $in)); - $this->map(Ids::WARPED_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::WARPED_TRAPDOOR(), $in)); - $this->map(Ids::WARPED_WALL_SIGN, fn(Reader $in) => Helper::decodeWallSign(Blocks::WARPED_WALL_SIGN(), $in)); - $this->mapLog(Ids::WARPED_HYPHAE, Ids::STRIPPED_WARPED_HYPHAE, fn() => Blocks::WARPED_HYPHAE()); - $this->mapLog(Ids::WARPED_STEM, Ids::STRIPPED_WARPED_STEM, fn() => Blocks::WARPED_STEM()); - $this->mapSimple(Ids::WARPED_FENCE, fn() => Blocks::WARPED_FENCE()); - $this->mapSimple(Ids::WARPED_PLANKS, fn() => Blocks::WARPED_PLANKS()); - $this->mapSlab(Ids::WARPED_SLAB, Ids::WARPED_DOUBLE_SLAB, fn() => Blocks::WARPED_SLAB()); - $this->mapStairs(Ids::WARPED_STAIRS, fn() => Blocks::WARPED_STAIRS()); - } - - private function registerLeavesDeserializers() : void{ - $this->map(Ids::ACACIA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::ACACIA_LEAVES(), $in)); - $this->map(Ids::AZALEA_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::AZALEA_LEAVES(), $in)); - $this->map(Ids::AZALEA_LEAVES_FLOWERED, fn(Reader $in) => Helper::decodeLeaves(Blocks::FLOWERING_AZALEA_LEAVES(), $in)); - $this->map(Ids::BIRCH_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::BIRCH_LEAVES(), $in)); - $this->map(Ids::CHERRY_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::CHERRY_LEAVES(), $in)); - $this->map(Ids::DARK_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::DARK_OAK_LEAVES(), $in)); - $this->map(Ids::JUNGLE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::JUNGLE_LEAVES(), $in)); - $this->map(Ids::MANGROVE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::MANGROVE_LEAVES(), $in)); - $this->map(Ids::OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::OAK_LEAVES(), $in)); - $this->map(Ids::PALE_OAK_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::PALE_OAK_LEAVES(), $in)); - $this->map(Ids::SPRUCE_LEAVES, fn(Reader $in) => Helper::decodeLeaves(Blocks::SPRUCE_LEAVES(), $in)); - } - - private function registerSaplingDeserializers() : void{ - foreach([ - Ids::ACACIA_SAPLING => fn() => Blocks::ACACIA_SAPLING(), - Ids::BIRCH_SAPLING => fn() => Blocks::BIRCH_SAPLING(), - Ids::DARK_OAK_SAPLING => fn() => Blocks::DARK_OAK_SAPLING(), - Ids::JUNGLE_SAPLING => fn() => Blocks::JUNGLE_SAPLING(), - Ids::OAK_SAPLING => fn() => Blocks::OAK_SAPLING(), - Ids::SPRUCE_SAPLING => fn() => Blocks::SPRUCE_SAPLING(), - ] as $id => $getBlock){ - $this->map($id, fn(Reader $in) => Helper::decodeSapling($getBlock(), $in)); - } - } - - private function registerLightDeserializers() : void{ - foreach([ - Ids::LIGHT_BLOCK_0 => 0, - Ids::LIGHT_BLOCK_1 => 1, - Ids::LIGHT_BLOCK_2 => 2, - Ids::LIGHT_BLOCK_3 => 3, - Ids::LIGHT_BLOCK_4 => 4, - Ids::LIGHT_BLOCK_5 => 5, - Ids::LIGHT_BLOCK_6 => 6, - Ids::LIGHT_BLOCK_7 => 7, - Ids::LIGHT_BLOCK_8 => 8, - Ids::LIGHT_BLOCK_9 => 9, - Ids::LIGHT_BLOCK_10 => 10, - Ids::LIGHT_BLOCK_11 => 11, - Ids::LIGHT_BLOCK_12 => 12, - Ids::LIGHT_BLOCK_13 => 13, - Ids::LIGHT_BLOCK_14 => 14, - Ids::LIGHT_BLOCK_15 => 15, - ] as $id => $level){ - $this->mapSimple($id, fn() => Blocks::LIGHT()->setLightLevel($level)); - } - } - - private function registerMobHeadDeserializers() : void{ - foreach([ - Ids::CREEPER_HEAD => MobHeadType::CREEPER, - Ids::DRAGON_HEAD => MobHeadType::DRAGON, - Ids::PIGLIN_HEAD => MobHeadType::PIGLIN, - Ids::PLAYER_HEAD => MobHeadType::PLAYER, - Ids::SKELETON_SKULL => MobHeadType::SKELETON, - Ids::WITHER_SKELETON_SKULL => MobHeadType::WITHER_SKELETON, - Ids::ZOMBIE_HEAD => MobHeadType::ZOMBIE - ] as $id => $mobHeadType){ - $this->map($id, fn(Reader $in) => Blocks::MOB_HEAD()->setMobHeadType($mobHeadType)->setFacing($in->readFacingWithoutDown())); - } - } - - /** - * @phpstan-param \Closure(Reader) : (CopperMaterial&Block) $deserializer - */ - private function mapCopper( - string $normalId, - string $waxedNormalId, - string $exposedId, - string $waxedExposedId, - string $weatheredId, - string $waxedWeatheredId, - string $oxidizedId, - string $waxedOxidizedId, - \Closure $deserializer - ) : void{ - foreach(Utils::stringifyKeys([ - $normalId => [CopperOxidation::NONE, false], - $waxedNormalId => [CopperOxidation::NONE, true], - $exposedId => [CopperOxidation::EXPOSED, false], - $waxedExposedId => [CopperOxidation::EXPOSED, true], - $weatheredId => [CopperOxidation::WEATHERED, false], - $waxedWeatheredId => [CopperOxidation::WEATHERED, true], - $oxidizedId => [CopperOxidation::OXIDIZED, false], - $waxedOxidizedId => [CopperOxidation::OXIDIZED, true], - ]) as $id => [$oxidation, $waxed]){ - $this->map($id, fn(Reader $in) => $deserializer($in)->setOxidation($oxidation)->setWaxed($waxed)); - } - } - - private function registerCopperDeserializers() : void{ - $this->mapCopper( - Ids::CUT_COPPER_SLAB, - Ids::WAXED_CUT_COPPER_SLAB, - Ids::EXPOSED_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_CUT_COPPER_SLAB, - Ids::WEATHERED_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_CUT_COPPER_SLAB, - Ids::OXIDIZED_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_CUT_COPPER_SLAB, - fn(Reader $in) => Helper::decodeSingleSlab(Blocks::CUT_COPPER_SLAB(), $in) - ); - $this->mapCopper( - Ids::DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_DOUBLE_CUT_COPPER_SLAB, - Ids::EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_EXPOSED_DOUBLE_CUT_COPPER_SLAB, - Ids::WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_WEATHERED_DOUBLE_CUT_COPPER_SLAB, - Ids::OXIDIZED_DOUBLE_CUT_COPPER_SLAB, - Ids::WAXED_OXIDIZED_DOUBLE_CUT_COPPER_SLAB, - fn(Reader $in) => Helper::decodeDoubleSlab(Blocks::CUT_COPPER_SLAB(), $in) - ); - - $this->mapCopper( - Ids::COPPER_BULB, - Ids::WAXED_COPPER_BULB, - Ids::EXPOSED_COPPER_BULB, - Ids::WAXED_EXPOSED_COPPER_BULB, - Ids::WEATHERED_COPPER_BULB, - Ids::WAXED_WEATHERED_COPPER_BULB, - Ids::OXIDIZED_COPPER_BULB, - Ids::WAXED_OXIDIZED_COPPER_BULB, - fn(Reader $in) => Blocks::COPPER_BULB() - ->setLit($in->readBool(StateNames::LIT)) - ->setPowered($in->readBool(StateNames::POWERED_BIT)) - ); - $this->mapCopper( - Ids::COPPER_DOOR, - Ids::WAXED_COPPER_DOOR, - Ids::EXPOSED_COPPER_DOOR, - Ids::WAXED_EXPOSED_COPPER_DOOR, - Ids::WEATHERED_COPPER_DOOR, - Ids::WAXED_WEATHERED_COPPER_DOOR, - Ids::OXIDIZED_COPPER_DOOR, - Ids::WAXED_OXIDIZED_COPPER_DOOR, - fn(Reader $in) => Helper::decodeDoor(Blocks::COPPER_DOOR(), $in) - ); - $this->mapCopper( - Ids::COPPER_TRAPDOOR, - Ids::WAXED_COPPER_TRAPDOOR, - Ids::EXPOSED_COPPER_TRAPDOOR, - Ids::WAXED_EXPOSED_COPPER_TRAPDOOR, - Ids::WEATHERED_COPPER_TRAPDOOR, - Ids::WAXED_WEATHERED_COPPER_TRAPDOOR, - Ids::OXIDIZED_COPPER_TRAPDOOR, - Ids::WAXED_OXIDIZED_COPPER_TRAPDOOR, - fn(Reader $in) => Helper::decodeTrapdoor(Blocks::COPPER_TRAPDOOR(), $in) - ); - $this->mapCopper( - Ids::COPPER_BLOCK, - Ids::WAXED_COPPER, - Ids::EXPOSED_COPPER, - Ids::WAXED_EXPOSED_COPPER, - Ids::WEATHERED_COPPER, - Ids::WAXED_WEATHERED_COPPER, - Ids::OXIDIZED_COPPER, - Ids::WAXED_OXIDIZED_COPPER, - fn(Reader $in) => Blocks::COPPER() - ); - $this->mapCopper( - Ids::CHISELED_COPPER, - Ids::WAXED_CHISELED_COPPER, - Ids::EXPOSED_CHISELED_COPPER, - Ids::WAXED_EXPOSED_CHISELED_COPPER, - Ids::WEATHERED_CHISELED_COPPER, - Ids::WAXED_WEATHERED_CHISELED_COPPER, - Ids::OXIDIZED_CHISELED_COPPER, - Ids::WAXED_OXIDIZED_CHISELED_COPPER, - fn(Reader $in) => Blocks::CHISELED_COPPER() - ); - $this->mapCopper( - Ids::COPPER_GRATE, - Ids::WAXED_COPPER_GRATE, - Ids::EXPOSED_COPPER_GRATE, - Ids::WAXED_EXPOSED_COPPER_GRATE, - Ids::WEATHERED_COPPER_GRATE, - Ids::WAXED_WEATHERED_COPPER_GRATE, - Ids::OXIDIZED_COPPER_GRATE, - Ids::WAXED_OXIDIZED_COPPER_GRATE, - fn(Reader $in) => Blocks::COPPER_GRATE() - ); - $this->mapCopper( - Ids::CUT_COPPER, - Ids::WAXED_CUT_COPPER, - Ids::EXPOSED_CUT_COPPER, - Ids::WAXED_EXPOSED_CUT_COPPER, - Ids::WEATHERED_CUT_COPPER, - Ids::WAXED_WEATHERED_CUT_COPPER, - Ids::OXIDIZED_CUT_COPPER, - Ids::WAXED_OXIDIZED_CUT_COPPER, - fn(Reader $in) => Blocks::CUT_COPPER() - ); - $this->mapCopper( - Ids::CUT_COPPER_STAIRS, - Ids::WAXED_CUT_COPPER_STAIRS, - Ids::EXPOSED_CUT_COPPER_STAIRS, - Ids::WAXED_EXPOSED_CUT_COPPER_STAIRS, - Ids::WEATHERED_CUT_COPPER_STAIRS, - Ids::WAXED_WEATHERED_CUT_COPPER_STAIRS, - Ids::OXIDIZED_CUT_COPPER_STAIRS, - Ids::WAXED_OXIDIZED_CUT_COPPER_STAIRS, - fn(Reader $in) => Helper::decodeStairs(Blocks::CUT_COPPER_STAIRS(), $in) - ); - } - - private function registerSimpleDeserializers() : void{ - $this->mapSimple(Ids::AIR, fn() => Blocks::AIR()); - $this->mapSimple(Ids::AMETHYST_BLOCK, fn() => Blocks::AMETHYST()); - $this->mapSimple(Ids::ANCIENT_DEBRIS, fn() => Blocks::ANCIENT_DEBRIS()); - $this->mapSimple(Ids::ANDESITE, fn() => Blocks::ANDESITE()); - $this->mapSimple(Ids::BARRIER, fn() => Blocks::BARRIER()); - $this->mapSimple(Ids::BEACON, fn() => Blocks::BEACON()); - $this->mapSimple(Ids::BLACKSTONE, fn() => Blocks::BLACKSTONE()); - $this->mapSimple(Ids::BLUE_ICE, fn() => Blocks::BLUE_ICE()); - $this->mapSimple(Ids::BOOKSHELF, fn() => Blocks::BOOKSHELF()); - $this->mapSimple(Ids::BRICK_BLOCK, fn() => Blocks::BRICKS()); - $this->mapSimple(Ids::BROWN_MUSHROOM, fn() => Blocks::BROWN_MUSHROOM()); - $this->mapSimple(Ids::BUDDING_AMETHYST, fn() => Blocks::BUDDING_AMETHYST()); - $this->mapSimple(Ids::CALCITE, fn() => Blocks::CALCITE()); - $this->mapSimple(Ids::CARTOGRAPHY_TABLE, fn() => Blocks::CARTOGRAPHY_TABLE()); - $this->mapSimple(Ids::CHEMICAL_HEAT, fn() => Blocks::CHEMICAL_HEAT()); - $this->mapSimple(Ids::CHISELED_DEEPSLATE, fn() => Blocks::CHISELED_DEEPSLATE()); - $this->mapSimple(Ids::CHISELED_NETHER_BRICKS, fn() => Blocks::CHISELED_NETHER_BRICKS()); - $this->mapSimple(Ids::CHISELED_POLISHED_BLACKSTONE, fn() => Blocks::CHISELED_POLISHED_BLACKSTONE()); - $this->mapSimple(Ids::CHISELED_RED_SANDSTONE, fn() => Blocks::CHISELED_RED_SANDSTONE()); - $this->mapSimple(Ids::CHISELED_RESIN_BRICKS, fn() => Blocks::CHISELED_RESIN_BRICKS()); - $this->mapSimple(Ids::CHISELED_SANDSTONE, fn() => Blocks::CHISELED_SANDSTONE()); - $this->mapSimple(Ids::CHISELED_STONE_BRICKS, fn() => Blocks::CHISELED_STONE_BRICKS()); - $this->mapSimple(Ids::CHISELED_TUFF, fn() => Blocks::CHISELED_TUFF()); - $this->mapSimple(Ids::CHISELED_TUFF_BRICKS, fn() => Blocks::CHISELED_TUFF_BRICKS()); - $this->mapSimple(Ids::CHORUS_PLANT, fn() => Blocks::CHORUS_PLANT()); - $this->mapSimple(Ids::CLAY, fn() => Blocks::CLAY()); - $this->mapSimple(Ids::COAL_BLOCK, fn() => Blocks::COAL()); - $this->mapSimple(Ids::COAL_ORE, fn() => Blocks::COAL_ORE()); - $this->mapSimple(Ids::COBBLED_DEEPSLATE, fn() => Blocks::COBBLED_DEEPSLATE()); - $this->mapSimple(Ids::COBBLESTONE, fn() => Blocks::COBBLESTONE()); - $this->mapSimple(Ids::COPPER_ORE, fn() => Blocks::COPPER_ORE()); - $this->mapSimple(Ids::CRACKED_DEEPSLATE_BRICKS, fn() => Blocks::CRACKED_DEEPSLATE_BRICKS()); - $this->mapSimple(Ids::CRACKED_DEEPSLATE_TILES, fn() => Blocks::CRACKED_DEEPSLATE_TILES()); - $this->mapSimple(Ids::CRACKED_NETHER_BRICKS, fn() => Blocks::CRACKED_NETHER_BRICKS()); - $this->mapSimple(Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS()); - $this->mapSimple(Ids::CRACKED_STONE_BRICKS, fn() => Blocks::CRACKED_STONE_BRICKS()); - $this->mapSimple(Ids::CRAFTING_TABLE, fn() => Blocks::CRAFTING_TABLE()); - $this->mapSimple(Ids::CRIMSON_ROOTS, fn() => Blocks::CRIMSON_ROOTS()); - $this->mapSimple(Ids::CRYING_OBSIDIAN, fn() => Blocks::CRYING_OBSIDIAN()); - $this->mapSimple(Ids::CUT_RED_SANDSTONE, fn() => Blocks::CUT_RED_SANDSTONE()); - $this->mapSimple(Ids::CUT_SANDSTONE, fn() => Blocks::CUT_SANDSTONE()); - $this->mapSimple(Ids::DARK_PRISMARINE, fn() => Blocks::DARK_PRISMARINE()); - $this->mapSimple(Ids::DEADBUSH, fn() => Blocks::DEAD_BUSH()); - $this->mapSimple(Ids::DEEPSLATE_BRICKS, fn() => Blocks::DEEPSLATE_BRICKS()); - $this->mapSimple(Ids::DEEPSLATE_COAL_ORE, fn() => Blocks::DEEPSLATE_COAL_ORE()); - $this->mapSimple(Ids::DEEPSLATE_COPPER_ORE, fn() => Blocks::DEEPSLATE_COPPER_ORE()); - $this->mapSimple(Ids::DEEPSLATE_DIAMOND_ORE, fn() => Blocks::DEEPSLATE_DIAMOND_ORE()); - $this->mapSimple(Ids::DEEPSLATE_EMERALD_ORE, fn() => Blocks::DEEPSLATE_EMERALD_ORE()); - $this->mapSimple(Ids::DEEPSLATE_GOLD_ORE, fn() => Blocks::DEEPSLATE_GOLD_ORE()); - $this->mapSimple(Ids::DEEPSLATE_IRON_ORE, fn() => Blocks::DEEPSLATE_IRON_ORE()); - $this->mapSimple(Ids::DEEPSLATE_LAPIS_ORE, fn() => Blocks::DEEPSLATE_LAPIS_LAZULI_ORE()); - $this->mapSimple(Ids::DEEPSLATE_TILES, fn() => Blocks::DEEPSLATE_TILES()); - $this->mapSimple(Ids::DIAMOND_BLOCK, fn() => Blocks::DIAMOND()); - $this->mapSimple(Ids::DIAMOND_ORE, fn() => Blocks::DIAMOND_ORE()); - $this->mapSimple(Ids::DIORITE, fn() => Blocks::DIORITE()); - $this->mapSimple(Ids::DRAGON_EGG, fn() => Blocks::DRAGON_EGG()); - $this->mapSimple(Ids::DRIED_KELP_BLOCK, fn() => Blocks::DRIED_KELP()); - $this->mapSimple(Ids::ELEMENT_0, fn() => Blocks::ELEMENT_ZERO()); - $this->mapSimple(Ids::ELEMENT_1, fn() => Blocks::ELEMENT_HYDROGEN()); - $this->mapSimple(Ids::ELEMENT_10, fn() => Blocks::ELEMENT_NEON()); - $this->mapSimple(Ids::ELEMENT_100, fn() => Blocks::ELEMENT_FERMIUM()); - $this->mapSimple(Ids::ELEMENT_101, fn() => Blocks::ELEMENT_MENDELEVIUM()); - $this->mapSimple(Ids::ELEMENT_102, fn() => Blocks::ELEMENT_NOBELIUM()); - $this->mapSimple(Ids::ELEMENT_103, fn() => Blocks::ELEMENT_LAWRENCIUM()); - $this->mapSimple(Ids::ELEMENT_104, fn() => Blocks::ELEMENT_RUTHERFORDIUM()); - $this->mapSimple(Ids::ELEMENT_105, fn() => Blocks::ELEMENT_DUBNIUM()); - $this->mapSimple(Ids::ELEMENT_106, fn() => Blocks::ELEMENT_SEABORGIUM()); - $this->mapSimple(Ids::ELEMENT_107, fn() => Blocks::ELEMENT_BOHRIUM()); - $this->mapSimple(Ids::ELEMENT_108, fn() => Blocks::ELEMENT_HASSIUM()); - $this->mapSimple(Ids::ELEMENT_109, fn() => Blocks::ELEMENT_MEITNERIUM()); - $this->mapSimple(Ids::ELEMENT_11, fn() => Blocks::ELEMENT_SODIUM()); - $this->mapSimple(Ids::ELEMENT_110, fn() => Blocks::ELEMENT_DARMSTADTIUM()); - $this->mapSimple(Ids::ELEMENT_111, fn() => Blocks::ELEMENT_ROENTGENIUM()); - $this->mapSimple(Ids::ELEMENT_112, fn() => Blocks::ELEMENT_COPERNICIUM()); - $this->mapSimple(Ids::ELEMENT_113, fn() => Blocks::ELEMENT_NIHONIUM()); - $this->mapSimple(Ids::ELEMENT_114, fn() => Blocks::ELEMENT_FLEROVIUM()); - $this->mapSimple(Ids::ELEMENT_115, fn() => Blocks::ELEMENT_MOSCOVIUM()); - $this->mapSimple(Ids::ELEMENT_116, fn() => Blocks::ELEMENT_LIVERMORIUM()); - $this->mapSimple(Ids::ELEMENT_117, fn() => Blocks::ELEMENT_TENNESSINE()); - $this->mapSimple(Ids::ELEMENT_118, fn() => Blocks::ELEMENT_OGANESSON()); - $this->mapSimple(Ids::ELEMENT_12, fn() => Blocks::ELEMENT_MAGNESIUM()); - $this->mapSimple(Ids::ELEMENT_13, fn() => Blocks::ELEMENT_ALUMINUM()); - $this->mapSimple(Ids::ELEMENT_14, fn() => Blocks::ELEMENT_SILICON()); - $this->mapSimple(Ids::ELEMENT_15, fn() => Blocks::ELEMENT_PHOSPHORUS()); - $this->mapSimple(Ids::ELEMENT_16, fn() => Blocks::ELEMENT_SULFUR()); - $this->mapSimple(Ids::ELEMENT_17, fn() => Blocks::ELEMENT_CHLORINE()); - $this->mapSimple(Ids::ELEMENT_18, fn() => Blocks::ELEMENT_ARGON()); - $this->mapSimple(Ids::ELEMENT_19, fn() => Blocks::ELEMENT_POTASSIUM()); - $this->mapSimple(Ids::ELEMENT_2, fn() => Blocks::ELEMENT_HELIUM()); - $this->mapSimple(Ids::ELEMENT_20, fn() => Blocks::ELEMENT_CALCIUM()); - $this->mapSimple(Ids::ELEMENT_21, fn() => Blocks::ELEMENT_SCANDIUM()); - $this->mapSimple(Ids::ELEMENT_22, fn() => Blocks::ELEMENT_TITANIUM()); - $this->mapSimple(Ids::ELEMENT_23, fn() => Blocks::ELEMENT_VANADIUM()); - $this->mapSimple(Ids::ELEMENT_24, fn() => Blocks::ELEMENT_CHROMIUM()); - $this->mapSimple(Ids::ELEMENT_25, fn() => Blocks::ELEMENT_MANGANESE()); - $this->mapSimple(Ids::ELEMENT_26, fn() => Blocks::ELEMENT_IRON()); - $this->mapSimple(Ids::ELEMENT_27, fn() => Blocks::ELEMENT_COBALT()); - $this->mapSimple(Ids::ELEMENT_28, fn() => Blocks::ELEMENT_NICKEL()); - $this->mapSimple(Ids::ELEMENT_29, fn() => Blocks::ELEMENT_COPPER()); - $this->mapSimple(Ids::ELEMENT_3, fn() => Blocks::ELEMENT_LITHIUM()); - $this->mapSimple(Ids::ELEMENT_30, fn() => Blocks::ELEMENT_ZINC()); - $this->mapSimple(Ids::ELEMENT_31, fn() => Blocks::ELEMENT_GALLIUM()); - $this->mapSimple(Ids::ELEMENT_32, fn() => Blocks::ELEMENT_GERMANIUM()); - $this->mapSimple(Ids::ELEMENT_33, fn() => Blocks::ELEMENT_ARSENIC()); - $this->mapSimple(Ids::ELEMENT_34, fn() => Blocks::ELEMENT_SELENIUM()); - $this->mapSimple(Ids::ELEMENT_35, fn() => Blocks::ELEMENT_BROMINE()); - $this->mapSimple(Ids::ELEMENT_36, fn() => Blocks::ELEMENT_KRYPTON()); - $this->mapSimple(Ids::ELEMENT_37, fn() => Blocks::ELEMENT_RUBIDIUM()); - $this->mapSimple(Ids::ELEMENT_38, fn() => Blocks::ELEMENT_STRONTIUM()); - $this->mapSimple(Ids::ELEMENT_39, fn() => Blocks::ELEMENT_YTTRIUM()); - $this->mapSimple(Ids::ELEMENT_4, fn() => Blocks::ELEMENT_BERYLLIUM()); - $this->mapSimple(Ids::ELEMENT_40, fn() => Blocks::ELEMENT_ZIRCONIUM()); - $this->mapSimple(Ids::ELEMENT_41, fn() => Blocks::ELEMENT_NIOBIUM()); - $this->mapSimple(Ids::ELEMENT_42, fn() => Blocks::ELEMENT_MOLYBDENUM()); - $this->mapSimple(Ids::ELEMENT_43, fn() => Blocks::ELEMENT_TECHNETIUM()); - $this->mapSimple(Ids::ELEMENT_44, fn() => Blocks::ELEMENT_RUTHENIUM()); - $this->mapSimple(Ids::ELEMENT_45, fn() => Blocks::ELEMENT_RHODIUM()); - $this->mapSimple(Ids::ELEMENT_46, fn() => Blocks::ELEMENT_PALLADIUM()); - $this->mapSimple(Ids::ELEMENT_47, fn() => Blocks::ELEMENT_SILVER()); - $this->mapSimple(Ids::ELEMENT_48, fn() => Blocks::ELEMENT_CADMIUM()); - $this->mapSimple(Ids::ELEMENT_49, fn() => Blocks::ELEMENT_INDIUM()); - $this->mapSimple(Ids::ELEMENT_5, fn() => Blocks::ELEMENT_BORON()); - $this->mapSimple(Ids::ELEMENT_50, fn() => Blocks::ELEMENT_TIN()); - $this->mapSimple(Ids::ELEMENT_51, fn() => Blocks::ELEMENT_ANTIMONY()); - $this->mapSimple(Ids::ELEMENT_52, fn() => Blocks::ELEMENT_TELLURIUM()); - $this->mapSimple(Ids::ELEMENT_53, fn() => Blocks::ELEMENT_IODINE()); - $this->mapSimple(Ids::ELEMENT_54, fn() => Blocks::ELEMENT_XENON()); - $this->mapSimple(Ids::ELEMENT_55, fn() => Blocks::ELEMENT_CESIUM()); - $this->mapSimple(Ids::ELEMENT_56, fn() => Blocks::ELEMENT_BARIUM()); - $this->mapSimple(Ids::ELEMENT_57, fn() => Blocks::ELEMENT_LANTHANUM()); - $this->mapSimple(Ids::ELEMENT_58, fn() => Blocks::ELEMENT_CERIUM()); - $this->mapSimple(Ids::ELEMENT_59, fn() => Blocks::ELEMENT_PRASEODYMIUM()); - $this->mapSimple(Ids::ELEMENT_6, fn() => Blocks::ELEMENT_CARBON()); - $this->mapSimple(Ids::ELEMENT_60, fn() => Blocks::ELEMENT_NEODYMIUM()); - $this->mapSimple(Ids::ELEMENT_61, fn() => Blocks::ELEMENT_PROMETHIUM()); - $this->mapSimple(Ids::ELEMENT_62, fn() => Blocks::ELEMENT_SAMARIUM()); - $this->mapSimple(Ids::ELEMENT_63, fn() => Blocks::ELEMENT_EUROPIUM()); - $this->mapSimple(Ids::ELEMENT_64, fn() => Blocks::ELEMENT_GADOLINIUM()); - $this->mapSimple(Ids::ELEMENT_65, fn() => Blocks::ELEMENT_TERBIUM()); - $this->mapSimple(Ids::ELEMENT_66, fn() => Blocks::ELEMENT_DYSPROSIUM()); - $this->mapSimple(Ids::ELEMENT_67, fn() => Blocks::ELEMENT_HOLMIUM()); - $this->mapSimple(Ids::ELEMENT_68, fn() => Blocks::ELEMENT_ERBIUM()); - $this->mapSimple(Ids::ELEMENT_69, fn() => Blocks::ELEMENT_THULIUM()); - $this->mapSimple(Ids::ELEMENT_7, fn() => Blocks::ELEMENT_NITROGEN()); - $this->mapSimple(Ids::ELEMENT_70, fn() => Blocks::ELEMENT_YTTERBIUM()); - $this->mapSimple(Ids::ELEMENT_71, fn() => Blocks::ELEMENT_LUTETIUM()); - $this->mapSimple(Ids::ELEMENT_72, fn() => Blocks::ELEMENT_HAFNIUM()); - $this->mapSimple(Ids::ELEMENT_73, fn() => Blocks::ELEMENT_TANTALUM()); - $this->mapSimple(Ids::ELEMENT_74, fn() => Blocks::ELEMENT_TUNGSTEN()); - $this->mapSimple(Ids::ELEMENT_75, fn() => Blocks::ELEMENT_RHENIUM()); - $this->mapSimple(Ids::ELEMENT_76, fn() => Blocks::ELEMENT_OSMIUM()); - $this->mapSimple(Ids::ELEMENT_77, fn() => Blocks::ELEMENT_IRIDIUM()); - $this->mapSimple(Ids::ELEMENT_78, fn() => Blocks::ELEMENT_PLATINUM()); - $this->mapSimple(Ids::ELEMENT_79, fn() => Blocks::ELEMENT_GOLD()); - $this->mapSimple(Ids::ELEMENT_8, fn() => Blocks::ELEMENT_OXYGEN()); - $this->mapSimple(Ids::ELEMENT_80, fn() => Blocks::ELEMENT_MERCURY()); - $this->mapSimple(Ids::ELEMENT_81, fn() => Blocks::ELEMENT_THALLIUM()); - $this->mapSimple(Ids::ELEMENT_82, fn() => Blocks::ELEMENT_LEAD()); - $this->mapSimple(Ids::ELEMENT_83, fn() => Blocks::ELEMENT_BISMUTH()); - $this->mapSimple(Ids::ELEMENT_84, fn() => Blocks::ELEMENT_POLONIUM()); - $this->mapSimple(Ids::ELEMENT_85, fn() => Blocks::ELEMENT_ASTATINE()); - $this->mapSimple(Ids::ELEMENT_86, fn() => Blocks::ELEMENT_RADON()); - $this->mapSimple(Ids::ELEMENT_87, fn() => Blocks::ELEMENT_FRANCIUM()); - $this->mapSimple(Ids::ELEMENT_88, fn() => Blocks::ELEMENT_RADIUM()); - $this->mapSimple(Ids::ELEMENT_89, fn() => Blocks::ELEMENT_ACTINIUM()); - $this->mapSimple(Ids::ELEMENT_9, fn() => Blocks::ELEMENT_FLUORINE()); - $this->mapSimple(Ids::ELEMENT_90, fn() => Blocks::ELEMENT_THORIUM()); - $this->mapSimple(Ids::ELEMENT_91, fn() => Blocks::ELEMENT_PROTACTINIUM()); - $this->mapSimple(Ids::ELEMENT_92, fn() => Blocks::ELEMENT_URANIUM()); - $this->mapSimple(Ids::ELEMENT_93, fn() => Blocks::ELEMENT_NEPTUNIUM()); - $this->mapSimple(Ids::ELEMENT_94, fn() => Blocks::ELEMENT_PLUTONIUM()); - $this->mapSimple(Ids::ELEMENT_95, fn() => Blocks::ELEMENT_AMERICIUM()); - $this->mapSimple(Ids::ELEMENT_96, fn() => Blocks::ELEMENT_CURIUM()); - $this->mapSimple(Ids::ELEMENT_97, fn() => Blocks::ELEMENT_BERKELIUM()); - $this->mapSimple(Ids::ELEMENT_98, fn() => Blocks::ELEMENT_CALIFORNIUM()); - $this->mapSimple(Ids::ELEMENT_99, fn() => Blocks::ELEMENT_EINSTEINIUM()); - $this->mapSimple(Ids::EMERALD_BLOCK, fn() => Blocks::EMERALD()); - $this->mapSimple(Ids::EMERALD_ORE, fn() => Blocks::EMERALD_ORE()); - $this->mapSimple(Ids::ENCHANTING_TABLE, fn() => Blocks::ENCHANTING_TABLE()); - $this->mapSimple(Ids::END_BRICKS, fn() => Blocks::END_STONE_BRICKS()); - $this->mapSimple(Ids::END_STONE, fn() => Blocks::END_STONE()); - $this->mapSimple(Ids::FERN, fn() => Blocks::FERN()); - $this->mapSimple(Ids::FLETCHING_TABLE, fn() => Blocks::FLETCHING_TABLE()); - $this->mapSimple(Ids::GILDED_BLACKSTONE, fn() => Blocks::GILDED_BLACKSTONE()); - $this->mapSimple(Ids::GLASS, fn() => Blocks::GLASS()); - $this->mapSimple(Ids::GLASS_PANE, fn() => Blocks::GLASS_PANE()); - $this->mapSimple(Ids::GLOWINGOBSIDIAN, fn() => Blocks::GLOWING_OBSIDIAN()); - $this->mapSimple(Ids::GLOWSTONE, fn() => Blocks::GLOWSTONE()); - $this->mapSimple(Ids::GOLD_BLOCK, fn() => Blocks::GOLD()); - $this->mapSimple(Ids::GOLD_ORE, fn() => Blocks::GOLD_ORE()); - $this->mapSimple(Ids::GRANITE, fn() => Blocks::GRANITE()); - $this->mapSimple(Ids::GRASS_BLOCK, fn() => Blocks::GRASS()); - $this->mapSimple(Ids::GRASS_PATH, fn() => Blocks::GRASS_PATH()); - $this->mapSimple(Ids::GRAVEL, fn() => Blocks::GRAVEL()); - $this->mapSimple(Ids::HANGING_ROOTS, fn() => Blocks::HANGING_ROOTS()); - $this->mapSimple(Ids::HARD_GLASS, fn() => Blocks::HARDENED_GLASS()); - $this->mapSimple(Ids::HARD_GLASS_PANE, fn() => Blocks::HARDENED_GLASS_PANE()); - $this->mapSimple(Ids::HARDENED_CLAY, fn() => Blocks::HARDENED_CLAY()); - $this->mapSimple(Ids::HONEYCOMB_BLOCK, fn() => Blocks::HONEYCOMB()); - $this->mapSimple(Ids::ICE, fn() => Blocks::ICE()); - $this->mapSimple(Ids::INFESTED_CHISELED_STONE_BRICKS, fn() => Blocks::INFESTED_CHISELED_STONE_BRICK()); - $this->mapSimple(Ids::INFESTED_COBBLESTONE, fn() => Blocks::INFESTED_COBBLESTONE()); - $this->mapSimple(Ids::INFESTED_CRACKED_STONE_BRICKS, fn() => Blocks::INFESTED_CRACKED_STONE_BRICK()); - $this->mapSimple(Ids::INFESTED_MOSSY_STONE_BRICKS, fn() => Blocks::INFESTED_MOSSY_STONE_BRICK()); - $this->mapSimple(Ids::INFESTED_STONE, fn() => Blocks::INFESTED_STONE()); - $this->mapSimple(Ids::INFESTED_STONE_BRICKS, fn() => Blocks::INFESTED_STONE_BRICK()); - $this->mapSimple(Ids::INFO_UPDATE, fn() => Blocks::INFO_UPDATE()); - $this->mapSimple(Ids::INFO_UPDATE2, fn() => Blocks::INFO_UPDATE2()); - $this->mapSimple(Ids::INVISIBLE_BEDROCK, fn() => Blocks::INVISIBLE_BEDROCK()); - $this->mapSimple(Ids::IRON_BARS, fn() => Blocks::IRON_BARS()); - $this->mapSimple(Ids::IRON_BLOCK, fn() => Blocks::IRON()); - $this->mapSimple(Ids::IRON_ORE, fn() => Blocks::IRON_ORE()); - $this->mapSimple(Ids::JUKEBOX, fn() => Blocks::JUKEBOX()); - $this->mapSimple(Ids::LAPIS_BLOCK, fn() => Blocks::LAPIS_LAZULI()); - $this->mapSimple(Ids::LAPIS_ORE, fn() => Blocks::LAPIS_LAZULI_ORE()); - $this->mapSimple(Ids::MAGMA, fn() => Blocks::MAGMA()); - $this->mapSimple(Ids::MANGROVE_ROOTS, fn() => Blocks::MANGROVE_ROOTS()); - $this->mapSimple(Ids::MELON_BLOCK, fn() => Blocks::MELON()); - $this->mapSimple(Ids::MOB_SPAWNER, fn() => Blocks::MONSTER_SPAWNER()); - $this->mapSimple(Ids::MOSSY_COBBLESTONE, fn() => Blocks::MOSSY_COBBLESTONE()); - $this->mapSimple(Ids::MOSSY_STONE_BRICKS, fn() => Blocks::MOSSY_STONE_BRICKS()); - $this->mapSimple(Ids::MUD, fn() => Blocks::MUD()); - $this->mapSimple(Ids::MUD_BRICKS, fn() => Blocks::MUD_BRICKS()); - $this->mapSimple(Ids::MYCELIUM, fn() => Blocks::MYCELIUM()); - $this->mapSimple(Ids::NETHER_BRICK, fn() => Blocks::NETHER_BRICKS()); - $this->mapSimple(Ids::NETHER_BRICK_FENCE, fn() => Blocks::NETHER_BRICK_FENCE()); - $this->mapSimple(Ids::NETHER_GOLD_ORE, fn() => Blocks::NETHER_GOLD_ORE()); - $this->mapSimple(Ids::NETHER_WART_BLOCK, fn() => Blocks::NETHER_WART_BLOCK()); - $this->mapSimple(Ids::NETHERITE_BLOCK, fn() => Blocks::NETHERITE()); - $this->mapSimple(Ids::NETHERRACK, fn() => Blocks::NETHERRACK()); - $this->mapSimple(Ids::NETHERREACTOR, fn() => Blocks::NETHER_REACTOR_CORE()); - $this->mapSimple(Ids::NOTEBLOCK, fn() => Blocks::NOTE_BLOCK()); - $this->mapSimple(Ids::OBSIDIAN, fn() => Blocks::OBSIDIAN()); - $this->mapSimple(Ids::PACKED_ICE, fn() => Blocks::PACKED_ICE()); - $this->mapSimple(Ids::PACKED_MUD, fn() => Blocks::PACKED_MUD()); - $this->mapSimple(Ids::PODZOL, fn() => Blocks::PODZOL()); - $this->mapSimple(Ids::POLISHED_ANDESITE, fn() => Blocks::POLISHED_ANDESITE()); - $this->mapSimple(Ids::POLISHED_BLACKSTONE, fn() => Blocks::POLISHED_BLACKSTONE()); - $this->mapSimple(Ids::POLISHED_BLACKSTONE_BRICKS, fn() => Blocks::POLISHED_BLACKSTONE_BRICKS()); - $this->mapSimple(Ids::POLISHED_DEEPSLATE, fn() => Blocks::POLISHED_DEEPSLATE()); - $this->mapSimple(Ids::POLISHED_DIORITE, fn() => Blocks::POLISHED_DIORITE()); - $this->mapSimple(Ids::POLISHED_GRANITE, fn() => Blocks::POLISHED_GRANITE()); - $this->mapSimple(Ids::POLISHED_TUFF, fn() => Blocks::POLISHED_TUFF()); - $this->mapSimple(Ids::PRISMARINE, fn() => Blocks::PRISMARINE()); - $this->mapSimple(Ids::PRISMARINE_BRICKS, fn() => Blocks::PRISMARINE_BRICKS()); - $this->mapSimple(Ids::QUARTZ_BRICKS, fn() => Blocks::QUARTZ_BRICKS()); - $this->mapSimple(Ids::QUARTZ_ORE, fn() => Blocks::NETHER_QUARTZ_ORE()); - $this->mapSimple(Ids::RAW_COPPER_BLOCK, fn() => Blocks::RAW_COPPER()); - $this->mapSimple(Ids::RAW_GOLD_BLOCK, fn() => Blocks::RAW_GOLD()); - $this->mapSimple(Ids::RAW_IRON_BLOCK, fn() => Blocks::RAW_IRON()); - $this->mapSimple(Ids::RED_MUSHROOM, fn() => Blocks::RED_MUSHROOM()); - $this->mapSimple(Ids::RED_NETHER_BRICK, fn() => Blocks::RED_NETHER_BRICKS()); - $this->mapSimple(Ids::RED_SAND, fn() => Blocks::RED_SAND()); - $this->mapSimple(Ids::RED_SANDSTONE, fn() => Blocks::RED_SANDSTONE()); - $this->mapSimple(Ids::REDSTONE_BLOCK, fn() => Blocks::REDSTONE()); - $this->mapSimple(Ids::REINFORCED_DEEPSLATE, fn() => Blocks::REINFORCED_DEEPSLATE()); - $this->mapSimple(Ids::RESERVED6, fn() => Blocks::RESERVED6()); - $this->mapSimple(Ids::RESIN_BLOCK, fn() => Blocks::RESIN()); - $this->mapSimple(Ids::RESIN_BRICKS, fn() => Blocks::RESIN_BRICKS()); - $this->mapSimple(Ids::SAND, fn() => Blocks::SAND()); - $this->mapSimple(Ids::SANDSTONE, fn() => Blocks::SANDSTONE()); - $this->mapSimple(Ids::SCULK, fn() => Blocks::SCULK()); - $this->mapSimple(Ids::SEA_LANTERN, fn() => Blocks::SEA_LANTERN()); - $this->mapSimple(Ids::SHORT_GRASS, fn() => Blocks::TALL_GRASS()); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( - $this->mapSimple(Ids::SHROOMLIGHT, fn() => Blocks::SHROOMLIGHT()); - $this->mapSimple(Ids::SLIME, fn() => Blocks::SLIME()); - $this->mapSimple(Ids::SMITHING_TABLE, fn() => Blocks::SMITHING_TABLE()); - $this->mapSimple(Ids::SMOOTH_BASALT, fn() => Blocks::SMOOTH_BASALT()); - $this->mapSimple(Ids::SMOOTH_RED_SANDSTONE, fn() => Blocks::SMOOTH_RED_SANDSTONE()); - $this->mapSimple(Ids::SMOOTH_SANDSTONE, fn() => Blocks::SMOOTH_SANDSTONE()); - $this->mapSimple(Ids::SMOOTH_STONE, fn() => Blocks::SMOOTH_STONE()); - $this->mapSimple(Ids::SNOW, fn() => Blocks::SNOW()); - $this->mapSimple(Ids::SOUL_SAND, fn() => Blocks::SOUL_SAND()); - $this->mapSimple(Ids::SOUL_SOIL, fn() => Blocks::SOUL_SOIL()); - $this->mapSimple(Ids::SPORE_BLOSSOM, fn() => Blocks::SPORE_BLOSSOM()); - $this->mapSimple(Ids::SPONGE, fn() => Blocks::SPONGE()); - $this->mapSimple(Ids::STONE, fn() => Blocks::STONE()); - $this->mapSimple(Ids::STONECUTTER, fn() => Blocks::LEGACY_STONECUTTER()); - $this->mapSimple(Ids::STONE_BRICKS, fn() => Blocks::STONE_BRICKS()); - $this->mapSimple(Ids::TINTED_GLASS, fn() => Blocks::TINTED_GLASS()); - $this->mapSimple(Ids::TORCHFLOWER, fn() => Blocks::TORCHFLOWER()); - $this->mapSimple(Ids::TUFF, fn() => Blocks::TUFF()); - $this->mapSimple(Ids::TUFF_BRICKS, fn() => Blocks::TUFF_BRICKS()); - $this->mapSimple(Ids::UNDYED_SHULKER_BOX, fn() => Blocks::SHULKER_BOX()); - $this->mapSimple(Ids::WARPED_WART_BLOCK, fn() => Blocks::WARPED_WART_BLOCK()); - $this->mapSimple(Ids::WARPED_ROOTS, fn() => Blocks::WARPED_ROOTS()); - $this->mapSimple(Ids::WATERLILY, fn() => Blocks::LILY_PAD()); - $this->mapSimple(Ids::WEB, fn() => Blocks::COBWEB()); - $this->mapSimple(Ids::WET_SPONGE, fn() => Blocks::SPONGE()->setWet(true)); - $this->mapSimple(Ids::WITHER_ROSE, fn() => Blocks::WITHER_ROSE()); - $this->mapSimple(Ids::DANDELION, fn() => Blocks::DANDELION()); - - $this->mapSimple(Ids::ALLIUM, fn() => Blocks::ALLIUM()); - $this->mapSimple(Ids::CORNFLOWER, fn() => Blocks::CORNFLOWER()); - $this->mapSimple(Ids::AZURE_BLUET, fn() => Blocks::AZURE_BLUET()); - $this->mapSimple(Ids::LILY_OF_THE_VALLEY, fn() => Blocks::LILY_OF_THE_VALLEY()); - $this->mapSimple(Ids::BLUE_ORCHID, fn() => Blocks::BLUE_ORCHID()); - $this->mapSimple(Ids::OXEYE_DAISY, fn() => Blocks::OXEYE_DAISY()); - $this->mapSimple(Ids::POPPY, fn() => Blocks::POPPY()); - $this->mapSimple(Ids::ORANGE_TULIP, fn() => Blocks::ORANGE_TULIP()); - $this->mapSimple(Ids::PINK_TULIP, fn() => Blocks::PINK_TULIP()); - $this->mapSimple(Ids::RED_TULIP, fn() => Blocks::RED_TULIP()); - $this->mapSimple(Ids::WHITE_TULIP, fn() => Blocks::WHITE_TULIP()); - } - - private function registerDeserializers() : void{ - $this->map(Ids::ACTIVATOR_RAIL, function(Reader $in) : Block{ - return Blocks::ACTIVATOR_RAIL() - ->setPowered($in->readBool(StateNames::RAIL_DATA_BIT)) - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); - }); - $this->map(Ids::AMETHYST_CLUSTER, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_CLUSTER) - ->setFacing($in->readBlockFace()); - }); - $this->mapSlab(Ids::ANDESITE_SLAB, Ids::ANDESITE_DOUBLE_SLAB, fn() => Blocks::ANDESITE_SLAB()); - $this->mapStairs(Ids::ANDESITE_STAIRS, fn() => Blocks::ANDESITE_STAIRS()); - $this->map(Ids::ANDESITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::ANDESITE_WALL(), $in)); - $this->map(Ids::ANVIL, function(Reader $in) : Block{ - return Blocks::ANVIL() - ->setDamage(Anvil::UNDAMAGED) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::CHIPPED_ANVIL, function(Reader $in) : Block{ - return Blocks::ANVIL() - ->setDamage(Anvil::SLIGHTLY_DAMAGED) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::DAMAGED_ANVIL, function(Reader $in) : Block{ - return Blocks::ANVIL() - ->setDamage(Anvil::VERY_DAMAGED) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::BAMBOO, function(Reader $in) : Block{ - return Blocks::BAMBOO() - ->setLeafSize(match($value = $in->readString(StateNames::BAMBOO_LEAF_SIZE)){ - StringValues::BAMBOO_LEAF_SIZE_NO_LEAVES => Bamboo::NO_LEAVES, - StringValues::BAMBOO_LEAF_SIZE_SMALL_LEAVES => Bamboo::SMALL_LEAVES, - StringValues::BAMBOO_LEAF_SIZE_LARGE_LEAVES => Bamboo::LARGE_LEAVES, - default => throw $in->badValueException(StateNames::BAMBOO_LEAF_SIZE, $value), - }) - ->setReady($in->readBool(StateNames::AGE_BIT)) - ->setThick(match($value = $in->readString(StateNames::BAMBOO_STALK_THICKNESS)){ - StringValues::BAMBOO_STALK_THICKNESS_THIN => false, - StringValues::BAMBOO_STALK_THICKNESS_THICK => true, - default => throw $in->badValueException(StateNames::BAMBOO_STALK_THICKNESS, $value), - }); - }); - $this->map(Ids::BAMBOO_SAPLING, function(Reader $in) : Block{ - return Blocks::BAMBOO_SAPLING()->setReady($in->readBool(StateNames::AGE_BIT)); - }); - $this->map(Ids::BARREL, function(Reader $in) : Block{ - return Blocks::BARREL() - ->setFacing($in->readFacingDirection()) - ->setOpen($in->readBool(StateNames::OPEN_BIT)); - }); - $this->map(Ids::BASALT, function(Reader $in){ - return Blocks::BASALT() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::BED, function(Reader $in) : Block{ - return Blocks::BED() - ->setFacing($in->readLegacyHorizontalFacing()) - ->setHead($in->readBool(StateNames::HEAD_PIECE_BIT)) - ->setOccupied($in->readBool(StateNames::OCCUPIED_BIT)); - }); - $this->map(Ids::BEDROCK, function(Reader $in) : Block{ - return Blocks::BEDROCK() - ->setBurnsForever($in->readBool(StateNames::INFINIBURN_BIT)); - }); - $this->map(Ids::BEETROOT, fn(Reader $in) => Helper::decodeCrops(Blocks::BEETROOTS(), $in)); - $this->map(Ids::BELL, function(Reader $in) : Block{ - $in->ignored(StateNames::TOGGLE_BIT); //only useful at runtime - return Blocks::BELL() - ->setFacing($in->readLegacyHorizontalFacing()) - ->setAttachmentType($in->readBellAttachmentType()); - }); - $this->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{ - if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){ - return Blocks::BIG_DRIPLEAF_HEAD() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLeafState(match($type = $in->readString(StateNames::BIG_DRIPLEAF_TILT)){ - StringValues::BIG_DRIPLEAF_TILT_NONE => DripleafState::STABLE, - StringValues::BIG_DRIPLEAF_TILT_UNSTABLE => DripleafState::UNSTABLE, - StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT => DripleafState::PARTIAL_TILT, - StringValues::BIG_DRIPLEAF_TILT_FULL_TILT => DripleafState::FULL_TILT, - default => throw $in->badValueException(StateNames::BIG_DRIPLEAF_TILT, $type), - }); - }else{ - $in->ignored(StateNames::BIG_DRIPLEAF_TILT); - return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readCardinalHorizontalFacing()); - } - }); - $this->mapSlab(Ids::BLACKSTONE_SLAB, Ids::BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::BLACKSTONE_SLAB()); - $this->mapStairs(Ids::BLACKSTONE_STAIRS, fn() => Blocks::BLACKSTONE_STAIRS()); - $this->map(Ids::BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BLACKSTONE_WALL(), $in)); - $this->map(Ids::BLAST_FURNACE, function(Reader $in) : Block{ - return Blocks::BLAST_FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(false); - }); - $this->map(Ids::BONE_BLOCK, function(Reader $in) : Block{ - $in->ignored(StateNames::DEPRECATED); - return Blocks::BONE_BLOCK()->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::BREWING_STAND, function(Reader $in) : Block{ - return Blocks::BREWING_STAND() - ->setSlot(BrewingStandSlot::EAST, $in->readBool(StateNames::BREWING_STAND_SLOT_A_BIT)) - ->setSlot(BrewingStandSlot::SOUTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_B_BIT)) - ->setSlot(BrewingStandSlot::NORTHWEST, $in->readBool(StateNames::BREWING_STAND_SLOT_C_BIT)); - }); - $this->mapSlab(Ids::BRICK_SLAB, Ids::BRICK_DOUBLE_SLAB, fn() => Blocks::BRICK_SLAB()); - $this->mapStairs(Ids::BRICK_STAIRS, fn() => Blocks::BRICK_STAIRS()); - $this->map(Ids::BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::BRICK_WALL(), $in)); - $this->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)){ - BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(), - BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(), - default => throw new BlockStateDeserializeException("This state does not exist"), - }); - $this->map(Ids::BROWN_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::BROWN_MUSHROOM_BLOCK(), $in)); - $this->map(Ids::CACTUS, function(Reader $in) : Block{ - return Blocks::CACTUS() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); - }); - $this->map(Ids::CAKE, function(Reader $in) : Block{ - return Blocks::CAKE() - ->setBites($in->readBoundedInt(StateNames::BITE_COUNTER, 0, 6)); - }); - $this->map(Ids::CAMPFIRE, function(Reader $in) : Block{ - return Blocks::CAMPFIRE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(!$in->readBool(StateNames::EXTINGUISHED)); - }); - $this->map(Ids::CARROTS, fn(Reader $in) => Helper::decodeCrops(Blocks::CARROTS(), $in)); - $this->map(Ids::CARVED_PUMPKIN, function(Reader $in) : Block{ - return Blocks::CARVED_PUMPKIN() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::CAVE_VINES, function(Reader $in) : CaveVines{ - return Blocks::CAVE_VINES() - ->setBerries(false) - ->setHead(false) - ->setAge($in->readBoundedInt(StateNames::GROWING_PLANT_AGE, 0, 25)); - }); - $this->map(Ids::CAVE_VINES_BODY_WITH_BERRIES, function(Reader $in) : CaveVines{ - return Blocks::CAVE_VINES() - ->setBerries(true) - ->setHead(false) - ->setAge($in->readBoundedInt(StateNames::GROWING_PLANT_AGE, 0, 25)); - }); - $this->map(Ids::CAVE_VINES_HEAD_WITH_BERRIES, function(Reader $in) : CaveVines{ - return Blocks::CAVE_VINES() - ->setBerries(true) - ->setHead(true) - ->setAge($in->readBoundedInt(StateNames::GROWING_PLANT_AGE, 0, 25)); - }); - $this->map(Ids::CHAIN, function(Reader $in) : Block{ - return Blocks::CHAIN() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::CHISELED_BOOKSHELF, function(Reader $in) : Block{ - $block = Blocks::CHISELED_BOOKSHELF() - ->setFacing($in->readLegacyHorizontalFacing()); - - //we don't use API constant for bounds here as the data bounds might be different to what we support internally - $flags = $in->readBoundedInt(StateNames::BOOKS_STORED, 0, (1 << 6) - 1); - foreach(ChiseledBookshelfSlot::cases() as $slot){ - $block->setSlot($slot, ($flags & (1 << $slot->value)) !== 0); - } - - return $block; - }); - $this->map(Ids::CHISELED_QUARTZ_BLOCK, function(Reader $in) : Block{ - return Blocks::CHISELED_QUARTZ() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::CHEST, function(Reader $in) : Block{ - return Blocks::CHEST() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::CHORUS_FLOWER, function(Reader $in) : Block{ - return Blocks::CHORUS_FLOWER() - ->setAge($in->readBoundedInt(StateNames::AGE, ChorusFlower::MIN_AGE, ChorusFlower::MAX_AGE)); - }); - $this->map(Ids::COARSE_DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::COARSE)); - $this->mapSlab(Ids::COBBLED_DEEPSLATE_SLAB, Ids::COBBLED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::COBBLED_DEEPSLATE_SLAB()); - $this->mapStairs(Ids::COBBLED_DEEPSLATE_STAIRS, fn() => Blocks::COBBLED_DEEPSLATE_STAIRS()); - $this->map(Ids::COBBLED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLED_DEEPSLATE_WALL(), $in)); - $this->mapSlab(Ids::COBBLESTONE_SLAB, Ids::COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::COBBLESTONE_SLAB()); - $this->map(Ids::COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::COBBLESTONE_WALL(), $in)); - $this->map(Ids::COCOA, function(Reader $in) : Block{ - return Blocks::COCOA_POD() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 2)) - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())); - }); - $this->map(Ids::COLORED_TORCH_BLUE, fn(Reader $in) => Blocks::BLUE_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COLORED_TORCH_GREEN, fn(Reader $in) => Blocks::GREEN_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COLORED_TORCH_PURPLE, fn(Reader $in) => Blocks::PURPLE_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COLORED_TORCH_RED, fn(Reader $in) => Blocks::RED_TORCH()->setFacing($in->readTorchFacing())); - $this->map(Ids::COMPOUND_CREATOR, fn(Reader $in) => Blocks::COMPOUND_CREATOR() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->mapSlab(Ids::CUT_RED_SANDSTONE_SLAB, Ids::CUT_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_RED_SANDSTONE_SLAB()); - $this->mapSlab(Ids::CUT_SANDSTONE_SLAB, Ids::CUT_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::CUT_SANDSTONE_SLAB()); - $this->mapSlab(Ids::DARK_PRISMARINE_SLAB, Ids::DARK_PRISMARINE_DOUBLE_SLAB, fn() => Blocks::DARK_PRISMARINE_SLAB()); - $this->mapStairs(Ids::DARK_PRISMARINE_STAIRS, fn() => Blocks::DARK_PRISMARINE_STAIRS()); - $this->map(Ids::DAYLIGHT_DETECTOR, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in) - ->setInverted(false)); - $this->map(Ids::DAYLIGHT_DETECTOR_INVERTED, fn(Reader $in) => Helper::decodeDaylightSensor(Blocks::DAYLIGHT_SENSOR(), $in) - ->setInverted(true)); - $this->map(Ids::DEEPSLATE, function(Reader $in) : Block{ - return Blocks::DEEPSLATE() - ->setAxis($in->readPillarAxis()); - }); - $this->mapSlab(Ids::DEEPSLATE_BRICK_SLAB, Ids::DEEPSLATE_BRICK_DOUBLE_SLAB, fn() => Blocks::DEEPSLATE_BRICK_SLAB()); - $this->mapStairs(Ids::DEEPSLATE_BRICK_STAIRS, fn() => Blocks::DEEPSLATE_BRICK_STAIRS()); - $this->map(Ids::DEEPSLATE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DEEPSLATE_BRICK_WALL(), $in)); - $this->map(Ids::DEEPSLATE_REDSTONE_ORE, fn() => Blocks::DEEPSLATE_REDSTONE_ORE()->setLit(false)); - $this->mapSlab(Ids::DEEPSLATE_TILE_SLAB, Ids::DEEPSLATE_TILE_DOUBLE_SLAB, fn() => Blocks::DEEPSLATE_TILE_SLAB()); - $this->mapStairs(Ids::DEEPSLATE_TILE_STAIRS, fn() => Blocks::DEEPSLATE_TILE_STAIRS()); - $this->map(Ids::DEEPSLATE_TILE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DEEPSLATE_TILE_WALL(), $in)); - $this->map(Ids::DETECTOR_RAIL, function(Reader $in) : Block{ - return Blocks::DETECTOR_RAIL() - ->setActivated($in->readBool(StateNames::RAIL_DATA_BIT)) - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); - }); - $this->mapSlab(Ids::DIORITE_SLAB, Ids::DIORITE_DOUBLE_SLAB, fn() => Blocks::DIORITE_SLAB()); - $this->mapStairs(Ids::DIORITE_STAIRS, fn() => Blocks::DIORITE_STAIRS()); - $this->map(Ids::DIORITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::DIORITE_WALL(), $in)); - $this->map(Ids::DIRT, fn() => Blocks::DIRT()->setDirtType(DirtType::NORMAL)); - $this->map(Ids::DIRT_WITH_ROOTS, fn() => Blocks::DIRT()->setDirtType(DirtType::ROOTED)); - $this->map(Ids::LARGE_FERN, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LARGE_FERN(), $in)); - $this->map(Ids::TALL_GRASS, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::DOUBLE_TALLGRASS(), $in)); - $this->map(Ids::PEONY, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::PEONY(), $in)); - $this->map(Ids::ROSE_BUSH, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::ROSE_BUSH(), $in)); - $this->map(Ids::SUNFLOWER, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::SUNFLOWER(), $in)); - $this->map(Ids::LILAC, fn(Reader $in) => Helper::decodeDoublePlant(Blocks::LILAC(), $in)); - $this->map(Ids::ELEMENT_CONSTRUCTOR, fn(Reader $in) => Blocks::ELEMENT_CONSTRUCTOR() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->mapStairs(Ids::END_BRICK_STAIRS, fn() => Blocks::END_STONE_BRICK_STAIRS()); - $this->map(Ids::END_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::END_STONE_BRICK_WALL(), $in)); - $this->map(Ids::END_PORTAL_FRAME, function(Reader $in) : Block{ - return Blocks::END_PORTAL_FRAME() - ->setEye($in->readBool(StateNames::END_PORTAL_EYE_BIT)) - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::END_ROD, function(Reader $in) : Block{ - return Blocks::END_ROD() - ->setFacing($in->readEndRodFacingDirection()); - }); - $this->mapSlab(Ids::END_STONE_BRICK_SLAB, Ids::END_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::END_STONE_BRICK_SLAB()); - $this->map(Ids::ENDER_CHEST, function(Reader $in) : Block{ - return Blocks::ENDER_CHEST() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::FARMLAND, function(Reader $in) : Block{ - return Blocks::FARMLAND() - ->setWetness($in->readBoundedInt(StateNames::MOISTURIZED_AMOUNT, 0, 7)); - }); - $this->map(Ids::FIRE, function(Reader $in) : Block{ - return Blocks::FIRE() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); - }); - $this->map(Ids::FLOWER_POT, function(Reader $in) : Block{ - $in->ignored(StateNames::UPDATE_BIT); - return Blocks::FLOWER_POT(); - }); - $this->map(Ids::FLOWING_LAVA, fn(Reader $in) => Helper::decodeFlowingLiquid(Blocks::LAVA(), $in)); - $this->map(Ids::FLOWING_WATER, fn(Reader $in) => Helper::decodeFlowingLiquid(Blocks::WATER(), $in)); - $this->map(Ids::FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::ITEM_FRAME(), $in)); - $this->map(Ids::FROSTED_ICE, function(Reader $in) : Block{ - return Blocks::FROSTED_ICE() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 3)); - }); - $this->map(Ids::FURNACE, function(Reader $in) : Block{ - return Blocks::FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(false); - }); - $this->map(Ids::GLOW_LICHEN, fn(Reader $in) => Blocks::GLOW_LICHEN()->setFaces($in->readFacingFlags())); - $this->map(Ids::GLOW_FRAME, fn(Reader $in) => Helper::decodeItemFrame(Blocks::GLOWING_ITEM_FRAME(), $in)); - $this->map(Ids::GOLDEN_RAIL, function(Reader $in) : Block{ - return Blocks::POWERED_RAIL() - ->setPowered($in->readBool(StateNames::RAIL_DATA_BIT)) - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 5)); - }); - $this->mapSlab(Ids::GRANITE_SLAB, Ids::GRANITE_DOUBLE_SLAB, fn() => Blocks::GRANITE_SLAB()); - $this->mapStairs(Ids::GRANITE_STAIRS, fn() => Blocks::GRANITE_STAIRS()); - $this->map(Ids::GRANITE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::GRANITE_WALL(), $in)); - $this->map(Ids::HAY_BLOCK, function(Reader $in) : Block{ - $in->ignored(StateNames::DEPRECATED); - return Blocks::HAY_BALE()->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::HEAVY_WEIGHTED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeWeightedPressurePlate(Blocks::WEIGHTED_PRESSURE_PLATE_HEAVY(), $in)); - $this->map(Ids::HOPPER, function(Reader $in) : Block{ - return Blocks::HOPPER() - ->setFacing($in->readFacingWithoutUp()) - ->setPowered($in->readBool(StateNames::TOGGLE_BIT)); - }); - $this->map(Ids::IRON_DOOR, fn(Reader $in) => Helper::decodeDoor(Blocks::IRON_DOOR(), $in)); - $this->map(Ids::IRON_TRAPDOOR, fn(Reader $in) => Helper::decodeTrapdoor(Blocks::IRON_TRAPDOOR(), $in)); - $this->map(Ids::LAB_TABLE, fn(Reader $in) => Blocks::LAB_TABLE() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->map(Ids::LADDER, function(Reader $in) : Block{ - return Blocks::LADDER() - ->setFacing($in->readHorizontalFacing()); - }); - $this->map(Ids::LANTERN, function(Reader $in) : Block{ - return Blocks::LANTERN() - ->setHanging($in->readBool(StateNames::HANGING)); - }); - $this->map(Ids::LARGE_AMETHYST_BUD, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_LARGE_BUD) - ->setFacing($in->readBlockFace()); - }); - $this->map(Ids::LAVA, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::LAVA(), $in)); - $this->map(Ids::LECTERN, function(Reader $in) : Block{ - return Blocks::LECTERN() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setProducingSignal($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::LEVER, function(Reader $in) : Block{ - return Blocks::LEVER() - ->setActivated($in->readBool(StateNames::OPEN_BIT)) - ->setFacing(match($value = $in->readString(StateNames::LEVER_DIRECTION)){ - StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH => LeverFacing::DOWN_AXIS_Z, - StringValues::LEVER_DIRECTION_DOWN_EAST_WEST => LeverFacing::DOWN_AXIS_X, - StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH => LeverFacing::UP_AXIS_Z, - StringValues::LEVER_DIRECTION_UP_EAST_WEST => LeverFacing::UP_AXIS_X, - StringValues::LEVER_DIRECTION_NORTH => LeverFacing::NORTH, - StringValues::LEVER_DIRECTION_SOUTH => LeverFacing::SOUTH, - StringValues::LEVER_DIRECTION_WEST => LeverFacing::WEST, - StringValues::LEVER_DIRECTION_EAST => LeverFacing::EAST, - default => throw $in->badValueException(StateNames::LEVER_DIRECTION, $value), - }); - }); - $this->map(Ids::LIGHTNING_ROD, function(Reader $in) : Block{ - return Blocks::LIGHTNING_ROD() - ->setFacing($in->readFacingDirection()); - }); - $this->map(Ids::LIGHT_WEIGHTED_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeWeightedPressurePlate(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), $in)); - $this->map(Ids::LIT_BLAST_FURNACE, function(Reader $in) : Block{ - return Blocks::BLAST_FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(true); - }); - $this->map(Ids::LIT_DEEPSLATE_REDSTONE_ORE, fn() => Blocks::DEEPSLATE_REDSTONE_ORE()->setLit(true)); - $this->map(Ids::LIT_FURNACE, function(Reader $in) : Block{ - return Blocks::FURNACE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(true); - }); - $this->map(Ids::LIT_PUMPKIN, function(Reader $in) : Block{ - return Blocks::LIT_PUMPKIN() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::LIT_REDSTONE_LAMP, function() : Block{ - return Blocks::REDSTONE_LAMP() - ->setPowered(true); - }); - $this->map(Ids::LIT_REDSTONE_ORE, function() : Block{ - return Blocks::REDSTONE_ORE() - ->setLit(true); - }); - $this->map(Ids::LIT_SMOKER, function(Reader $in) : Block{ - return Blocks::SMOKER() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(true); - }); - $this->map(Ids::LOOM, function(Reader $in) : Block{ - return Blocks::LOOM() - ->setFacing($in->readLegacyHorizontalFacing()); - }); - $this->map(Ids::MATERIAL_REDUCER, fn(Reader $in) => Blocks::MATERIAL_REDUCER() - ->setFacing(Facing::opposite($in->readLegacyHorizontalFacing())) - ); - $this->map(Ids::MEDIUM_AMETHYST_BUD, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_MEDIUM_BUD) - ->setFacing($in->readBlockFace()); - }); - $this->map(Ids::MELON_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::MELON_STEM(), $in)); - $this->mapSlab(Ids::MOSSY_COBBLESTONE_SLAB, Ids::MOSSY_COBBLESTONE_DOUBLE_SLAB, fn() => Blocks::MOSSY_COBBLESTONE_SLAB()); - $this->mapStairs(Ids::MOSSY_COBBLESTONE_STAIRS, fn() => Blocks::MOSSY_COBBLESTONE_STAIRS()); - $this->map(Ids::MOSSY_COBBLESTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_COBBLESTONE_WALL(), $in)); - $this->mapSlab(Ids::MOSSY_STONE_BRICK_SLAB, Ids::MOSSY_STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::MOSSY_STONE_BRICK_SLAB()); - $this->mapStairs(Ids::MOSSY_STONE_BRICK_STAIRS, fn() => Blocks::MOSSY_STONE_BRICK_STAIRS()); - $this->map(Ids::MOSSY_STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MOSSY_STONE_BRICK_WALL(), $in)); - $this->mapSlab(Ids::MUD_BRICK_SLAB, Ids::MUD_BRICK_DOUBLE_SLAB, fn() => Blocks::MUD_BRICK_SLAB()); - $this->mapStairs(Ids::MUD_BRICK_STAIRS, fn() => Blocks::MUD_BRICK_STAIRS()); - $this->map(Ids::MUD_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::MUD_BRICK_WALL(), $in)); - $this->map(Ids::MUDDY_MANGROVE_ROOTS, function(Reader $in) : Block{ - return Blocks::MUDDY_MANGROVE_ROOTS() - ->setAxis($in->readPillarAxis()); - }); - $this->mapSlab(Ids::NETHER_BRICK_SLAB, Ids::NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::NETHER_BRICK_SLAB()); - $this->mapStairs(Ids::NETHER_BRICK_STAIRS, fn() => Blocks::NETHER_BRICK_STAIRS()); - $this->map(Ids::NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::NETHER_BRICK_WALL(), $in)); - $this->map(Ids::NETHER_WART, function(Reader $in) : Block{ - return Blocks::NETHER_WART() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 3)); - }); - $this->mapSlab(Ids::NORMAL_STONE_SLAB, Ids::NORMAL_STONE_DOUBLE_SLAB, fn() => Blocks::STONE_SLAB()); - $this->mapStairs(Ids::NORMAL_STONE_STAIRS, fn() => Blocks::STONE_STAIRS()); - $this->map(Ids::OCHRE_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::OCHRE)->setAxis($in->readPillarAxis())); - $this->map(Ids::PEARLESCENT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::PEARLESCENT)->setAxis($in->readPillarAxis())); - $this->mapSlab(Ids::PETRIFIED_OAK_SLAB, Ids::PETRIFIED_OAK_DOUBLE_SLAB, fn() => Blocks::FAKE_WOODEN_SLAB()); - $this->map(Ids::PINK_PETALS, function(Reader $in) : Block{ - //Pink petals only uses 0-3, but GROWTH state can go up to 7 - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - return Blocks::PINK_PETALS() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setCount(min($growth + 1, PinkPetals::MAX_COUNT)); - }); - $this->map(Ids::PITCHER_CROP, function(Reader $in) : Block{ - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - $top = $in->readBool(StateNames::UPPER_BLOCK_BIT); - if($growth <= PitcherCrop::MAX_AGE){ - //top pitcher crop with age 0-2 is an invalid state - //only the bottom half should exist in this case - return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth); - } - return Blocks::DOUBLE_PITCHER_CROP() - ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) - ->setTop($top); - }); - $this->map(Ids::PITCHER_PLANT, function(Reader $in) : Block{ - return Blocks::PITCHER_PLANT() - ->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT)); - }); - $this->mapSlab(Ids::POLISHED_ANDESITE_SLAB, Ids::POLISHED_ANDESITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_ANDESITE_SLAB()); - $this->mapStairs(Ids::POLISHED_ANDESITE_STAIRS, fn() => Blocks::POLISHED_ANDESITE_STAIRS()); - $this->map(Ids::POLISHED_BASALT, function(Reader $in) : Block{ - return Blocks::POLISHED_BASALT() - ->setAxis($in->readPillarAxis()); - }); - $this->map(Ids::POLISHED_BLACKSTONE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::POLISHED_BLACKSTONE_BUTTON(), $in)); - $this->mapSlab(Ids::POLISHED_BLACKSTONE_SLAB, Ids::POLISHED_BLACKSTONE_DOUBLE_SLAB, fn() => Blocks::POLISHED_BLACKSTONE_SLAB()); - $this->map(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::POLISHED_BLACKSTONE_PRESSURE_PLATE(), $in)); - $this->mapStairs(Ids::POLISHED_BLACKSTONE_STAIRS, fn() => Blocks::POLISHED_BLACKSTONE_STAIRS()); - $this->map(Ids::POLISHED_BLACKSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_BLACKSTONE_WALL(), $in)); - $this->mapSlab(Ids::POLISHED_BLACKSTONE_BRICK_SLAB, Ids::POLISHED_BLACKSTONE_BRICK_DOUBLE_SLAB, fn() => Blocks::POLISHED_BLACKSTONE_BRICK_SLAB()); - $this->mapStairs(Ids::POLISHED_BLACKSTONE_BRICK_STAIRS, fn() => Blocks::POLISHED_BLACKSTONE_BRICK_STAIRS()); - $this->map(Ids::POLISHED_BLACKSTONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_BLACKSTONE_BRICK_WALL(), $in)); - $this->mapSlab(Ids::POLISHED_DEEPSLATE_SLAB, Ids::POLISHED_DEEPSLATE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DEEPSLATE_SLAB()); - $this->mapStairs(Ids::POLISHED_DEEPSLATE_STAIRS, fn() => Blocks::POLISHED_DEEPSLATE_STAIRS()); - $this->map(Ids::POLISHED_DEEPSLATE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_DEEPSLATE_WALL(), $in)); - $this->mapSlab(Ids::POLISHED_DIORITE_SLAB, Ids::POLISHED_DIORITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_DIORITE_SLAB()); - $this->mapStairs(Ids::POLISHED_DIORITE_STAIRS, fn() => Blocks::POLISHED_DIORITE_STAIRS()); - $this->mapSlab(Ids::POLISHED_GRANITE_SLAB, Ids::POLISHED_GRANITE_DOUBLE_SLAB, fn() => Blocks::POLISHED_GRANITE_SLAB()); - $this->mapStairs(Ids::POLISHED_GRANITE_STAIRS, fn() => Blocks::POLISHED_GRANITE_STAIRS()); - $this->mapSlab(Ids::POLISHED_TUFF_SLAB, Ids::POLISHED_TUFF_DOUBLE_SLAB, fn() => Blocks::POLISHED_TUFF_SLAB()); - $this->mapStairs(Ids::POLISHED_TUFF_STAIRS, fn() => Blocks::POLISHED_TUFF_STAIRS()); - $this->map(Ids::POLISHED_TUFF_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::POLISHED_TUFF_WALL(), $in)); - $this->map(Ids::PORTAL, function(Reader $in) : Block{ - return Blocks::NETHER_PORTAL() - ->setAxis(match($value = $in->readString(StateNames::PORTAL_AXIS)){ - StringValues::PORTAL_AXIS_UNKNOWN => Axis::X, - StringValues::PORTAL_AXIS_X => Axis::X, - StringValues::PORTAL_AXIS_Z => Axis::Z, - default => throw $in->badValueException(StateNames::PORTAL_AXIS, $value), - }); - }); - $this->map(Ids::POTATOES, fn(Reader $in) => Helper::decodeCrops(Blocks::POTATOES(), $in)); - $this->map(Ids::POWERED_COMPARATOR, fn(Reader $in) => Helper::decodeComparator(Blocks::REDSTONE_COMPARATOR(), $in)); - $this->map(Ids::POWERED_REPEATER, fn(Reader $in) => Helper::decodeRepeater(Blocks::REDSTONE_REPEATER(), $in) - ->setPowered(true)); - $this->mapSlab(Ids::PRISMARINE_BRICK_SLAB, Ids::PRISMARINE_BRICK_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_BRICKS_SLAB()); - $this->mapStairs(Ids::PRISMARINE_BRICKS_STAIRS, fn() => Blocks::PRISMARINE_BRICKS_STAIRS()); - $this->map(Ids::PRISMARINE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::PRISMARINE_WALL(), $in)); - $this->mapSlab(Ids::PRISMARINE_SLAB, Ids::PRISMARINE_DOUBLE_SLAB, fn() => Blocks::PRISMARINE_SLAB()); - $this->mapStairs(Ids::PRISMARINE_STAIRS, fn() => Blocks::PRISMARINE_STAIRS()); - $this->map(Ids::PUMPKIN, function(Reader $in) : Block{ - $in->ignored(StateNames::MC_CARDINAL_DIRECTION); //obsolete - return Blocks::PUMPKIN(); - }); - $this->map(Ids::PUMPKIN_STEM, fn(Reader $in) => Helper::decodeStem(Blocks::PUMPKIN_STEM(), $in)); - $this->map(Ids::PURPUR_BLOCK, function(Reader $in) : Block{ - $in->ignored(StateNames::PILLAR_AXIS); //??? - return Blocks::PURPUR(); - }); - $this->map(Ids::PURPUR_PILLAR, fn(Reader $in) => Blocks::PURPUR_PILLAR()->setAxis($in->readPillarAxis())); - $this->mapSlab(Ids::PURPUR_SLAB, Ids::PURPUR_DOUBLE_SLAB, fn() => Blocks::PURPUR_SLAB()); - $this->mapStairs(Ids::PURPUR_STAIRS, fn() => Blocks::PURPUR_STAIRS()); - $this->map(Ids::QUARTZ_BLOCK, function(Reader $in) : Opaque{ - $in->ignored(StateNames::PILLAR_AXIS); - return Blocks::QUARTZ(); - }); - $this->map(Ids::QUARTZ_PILLAR, function(Reader $in) : Block{ - return Blocks::QUARTZ_PILLAR() - ->setAxis($in->readPillarAxis()); - }); - $this->mapSlab(Ids::QUARTZ_SLAB, Ids::QUARTZ_DOUBLE_SLAB, fn() => Blocks::QUARTZ_SLAB()); - $this->mapStairs(Ids::QUARTZ_STAIRS, fn() => Blocks::QUARTZ_STAIRS()); - $this->map(Ids::RAIL, function(Reader $in) : Block{ - return Blocks::RAIL() - ->setShape($in->readBoundedInt(StateNames::RAIL_DIRECTION, 0, 9)); - }); - $this->map(Ids::RED_MUSHROOM_BLOCK, fn(Reader $in) => Helper::decodeMushroomBlock(Blocks::RED_MUSHROOM_BLOCK(), $in)); - $this->mapSlab(Ids::RED_NETHER_BRICK_SLAB, Ids::RED_NETHER_BRICK_DOUBLE_SLAB, fn() => Blocks::RED_NETHER_BRICK_SLAB()); - $this->mapStairs(Ids::RED_NETHER_BRICK_STAIRS, fn() => Blocks::RED_NETHER_BRICK_STAIRS()); - $this->map(Ids::RED_NETHER_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_NETHER_BRICK_WALL(), $in)); - $this->mapSlab(Ids::RED_SANDSTONE_SLAB, Ids::RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::RED_SANDSTONE_SLAB()); - $this->mapStairs(Ids::RED_SANDSTONE_STAIRS, fn() => Blocks::RED_SANDSTONE_STAIRS()); - $this->map(Ids::RED_SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RED_SANDSTONE_WALL(), $in)); - $this->map(Ids::REDSTONE_LAMP, function() : Block{ - return Blocks::REDSTONE_LAMP() - ->setPowered(false); - }); - $this->map(Ids::REDSTONE_ORE, function() : Block{ - return Blocks::REDSTONE_ORE() - ->setLit(false); - }); - $this->map(Ids::REDSTONE_TORCH, function(Reader $in) : Block{ - return Blocks::REDSTONE_TORCH() - ->setFacing($in->readTorchFacing()) - ->setLit(true); - }); - $this->map(Ids::REDSTONE_WIRE, function(Reader $in) : Block{ - return Blocks::REDSTONE_WIRE() - ->setOutputSignalStrength($in->readBoundedInt(StateNames::REDSTONE_SIGNAL, 0, 15)); - }); - $this->map(Ids::REEDS, function(Reader $in) : Block{ - return Blocks::SUGARCANE() - ->setAge($in->readBoundedInt(StateNames::AGE, 0, 15)); - }); - $this->mapSlab(Ids::RESIN_BRICK_SLAB, Ids::RESIN_BRICK_DOUBLE_SLAB, fn() => Blocks::RESIN_BRICK_SLAB()); - $this->mapStairs(Ids::RESIN_BRICK_STAIRS, fn() => Blocks::RESIN_BRICK_STAIRS()); - $this->map(Ids::RESIN_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::RESIN_BRICK_WALL(), $in)); - $this->map(Ids::RESIN_CLUMP, fn(Reader $in) => Blocks::RESIN_CLUMP()->setFaces($in->readFacingFlags())); - $this->map(Ids::RESPAWN_ANCHOR, function(Reader $in) : Block{ - return Blocks::RESPAWN_ANCHOR() - ->setCharges($in->readBoundedInt(StateNames::RESPAWN_ANCHOR_CHARGE, 0, 4)); - }); - $this->mapSlab(Ids::SANDSTONE_SLAB, Ids::SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SANDSTONE_SLAB()); - $this->mapStairs(Ids::SANDSTONE_STAIRS, fn() => Blocks::SANDSTONE_STAIRS()); - $this->map(Ids::SANDSTONE_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::SANDSTONE_WALL(), $in)); - $this->map(Ids::SEA_PICKLE, function(Reader $in) : Block{ - return Blocks::SEA_PICKLE() - ->setCount($in->readBoundedInt(StateNames::CLUSTER_COUNT, 0, 3) + 1) - ->setUnderwater(!$in->readBool(StateNames::DEAD_BIT)); - }); - $this->map(Ids::SMOKER, function(Reader $in) : Block{ - return Blocks::SMOKER() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(false); - }); - $this->map(Ids::SMALL_AMETHYST_BUD, function(Reader $in) : Block{ - return Blocks::AMETHYST_CLUSTER() - ->setStage(AmethystCluster::STAGE_SMALL_BUD) - ->setFacing($in->readBlockFace()); - }); - $this->map(Ids::SMALL_DRIPLEAF_BLOCK, function(Reader $in) : Block{ - return Blocks::SMALL_DRIPLEAF() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setTop($in->readBool(StateNames::UPPER_BLOCK_BIT)); - }); - $this->map(Ids::SMOOTH_QUARTZ, function(Reader $in) : Block{ - $in->ignored(StateNames::PILLAR_AXIS); - return Blocks::SMOOTH_QUARTZ(); - }); - $this->mapSlab(Ids::SMOOTH_QUARTZ_SLAB, Ids::SMOOTH_QUARTZ_DOUBLE_SLAB, fn() => Blocks::SMOOTH_QUARTZ_SLAB()); - $this->mapStairs(Ids::SMOOTH_QUARTZ_STAIRS, fn() => Blocks::SMOOTH_QUARTZ_STAIRS()); - $this->mapSlab(Ids::SMOOTH_RED_SANDSTONE_SLAB, Ids::SMOOTH_RED_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_RED_SANDSTONE_SLAB()); - $this->mapStairs(Ids::SMOOTH_RED_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_RED_SANDSTONE_STAIRS()); - $this->mapSlab(Ids::SMOOTH_SANDSTONE_SLAB, Ids::SMOOTH_SANDSTONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_SANDSTONE_SLAB()); - $this->mapStairs(Ids::SMOOTH_SANDSTONE_STAIRS, fn() => Blocks::SMOOTH_SANDSTONE_STAIRS()); - $this->mapSlab(Ids::SMOOTH_STONE_SLAB, Ids::SMOOTH_STONE_DOUBLE_SLAB, fn() => Blocks::SMOOTH_STONE_SLAB()); - $this->map(Ids::SNOW_LAYER, function(Reader $in) : Block{ - $in->ignored(StateNames::COVERED_BIT); //seems to be useless - return Blocks::SNOW_LAYER()->setLayers($in->readBoundedInt(StateNames::HEIGHT, 0, 7) + 1); - }); - $this->map(Ids::SOUL_CAMPFIRE, function(Reader $in) : Block{ - return Blocks::SOUL_CAMPFIRE() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLit(!$in->readBool(StateNames::EXTINGUISHED)); - }); - $this->map(Ids::SOUL_FIRE, function(Reader $in) : Block{ - $in->ignored(StateNames::AGE); //this is useless for soul fire, since it doesn't have the logic associated - return Blocks::SOUL_FIRE(); - }); - $this->map(Ids::SOUL_LANTERN, function(Reader $in) : Block{ - return Blocks::SOUL_LANTERN() - ->setHanging($in->readBool(StateNames::HANGING)); - }); - $this->map(Ids::SOUL_TORCH, function(Reader $in) : Block{ - return Blocks::SOUL_TORCH() - ->setFacing($in->readTorchFacing()); - }); - $this->map(Ids::STANDING_BANNER, function(Reader $in) : Block{ - return Blocks::BANNER() - ->setRotation($in->readBoundedInt(StateNames::GROUND_SIGN_DIRECTION, 0, 15)); - }); - $this->mapSlab(Ids::STONE_BRICK_SLAB, Ids::STONE_BRICK_DOUBLE_SLAB, fn() => Blocks::STONE_BRICK_SLAB()); - $this->mapStairs(Ids::STONE_BRICK_STAIRS, fn() => Blocks::STONE_BRICK_STAIRS()); - $this->map(Ids::STONE_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::STONE_BRICK_WALL(), $in)); - $this->map(Ids::STONE_BUTTON, fn(Reader $in) => Helper::decodeButton(Blocks::STONE_BUTTON(), $in)); - $this->map(Ids::STONE_PRESSURE_PLATE, fn(Reader $in) => Helper::decodeSimplePressurePlate(Blocks::STONE_PRESSURE_PLATE(), $in)); - $this->mapStairs(Ids::STONE_STAIRS, fn() => Blocks::COBBLESTONE_STAIRS()); - $this->map(Ids::STONECUTTER_BLOCK, function(Reader $in) : Block{ - return Blocks::STONECUTTER() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::SWEET_BERRY_BUSH, function(Reader $in) : Block{ - //berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7 - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - return Blocks::SWEET_BERRY_BUSH() - ->setAge(min($growth, SweetBerryBush::STAGE_MATURE)); - }); - $this->map(Ids::TNT, function(Reader $in) : Block{ - return Blocks::TNT() - ->setUnstable($in->readBool(StateNames::EXPLODE_BIT)) - ->setWorksUnderwater(false); - }); - $this->map(Ids::TORCH, function(Reader $in) : Block{ - return Blocks::TORCH() - ->setFacing($in->readTorchFacing()); - }); - $this->map(Ids::TORCHFLOWER_CROP, function(Reader $in) : Block{ - return Blocks::TORCHFLOWER_CROP() - //this property can have values 0-7, but only 0-1 are valid - ->setReady($in->readBoundedInt(StateNames::GROWTH, 0, 7) !== 0); - }); - $this->map(Ids::TRAPPED_CHEST, function(Reader $in) : Block{ - return Blocks::TRAPPED_CHEST() - ->setFacing($in->readCardinalHorizontalFacing()); - }); - $this->map(Ids::TRIP_WIRE, function(Reader $in) : Block{ - return Blocks::TRIPWIRE() - ->setConnected($in->readBool(StateNames::ATTACHED_BIT)) - ->setDisarmed($in->readBool(StateNames::DISARMED_BIT)) - ->setSuspended($in->readBool(StateNames::SUSPENDED_BIT)) - ->setTriggered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->map(Ids::TRIPWIRE_HOOK, function(Reader $in) : Block{ - return Blocks::TRIPWIRE_HOOK() - ->setConnected($in->readBool(StateNames::ATTACHED_BIT)) - ->setFacing($in->readLegacyHorizontalFacing()) - ->setPowered($in->readBool(StateNames::POWERED_BIT)); - }); - $this->mapSlab(Ids::TUFF_BRICK_SLAB, Ids::TUFF_BRICK_DOUBLE_SLAB, fn() => Blocks::TUFF_BRICK_SLAB()); - $this->mapStairs(Ids::TUFF_BRICK_STAIRS, fn() => Blocks::TUFF_BRICK_STAIRS()); - $this->map(Ids::TUFF_BRICK_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::TUFF_BRICK_WALL(), $in)); - $this->mapSlab(Ids::TUFF_SLAB, Ids::TUFF_DOUBLE_SLAB, fn() => Blocks::TUFF_SLAB()); - $this->mapStairs(Ids::TUFF_STAIRS, fn() => Blocks::TUFF_STAIRS()); - $this->map(Ids::TUFF_WALL, fn(Reader $in) => Helper::decodeWall(Blocks::TUFF_WALL(), $in)); - $this->map(Ids::TWISTING_VINES, function(Reader $in) : Block{ - return Blocks::TWISTING_VINES() - ->setAge($in->readBoundedInt(StateNames::TWISTING_VINES_AGE, 0, 25)); - }); - $this->map(Ids::UNDERWATER_TNT, function(Reader $in) : Block{ - return Blocks::TNT() - ->setUnstable($in->readBool(StateNames::EXPLODE_BIT)) - ->setWorksUnderwater(true); - }); - $this->map(Ids::UNDERWATER_TORCH, function(Reader $in) : Block{ - return Blocks::UNDERWATER_TORCH() - ->setFacing($in->readTorchFacing()); - }); - $this->map(Ids::UNLIT_REDSTONE_TORCH, function(Reader $in) : Block{ - return Blocks::REDSTONE_TORCH() - ->setFacing($in->readTorchFacing()) - ->setLit(false); - }); - $this->map(Ids::UNPOWERED_COMPARATOR, fn(Reader $in) => Helper::decodeComparator(Blocks::REDSTONE_COMPARATOR(), $in)); - $this->map(Ids::UNPOWERED_REPEATER, fn(Reader $in) => Helper::decodeRepeater(Blocks::REDSTONE_REPEATER(), $in) - ->setPowered(false)); - $this->map(Ids::VERDANT_FROGLIGHT, fn(Reader $in) => Blocks::FROGLIGHT()->setFroglightType(FroglightType::VERDANT)->setAxis($in->readPillarAxis())); - $this->map(Ids::VINE, function(Reader $in) : Block{ - $vineDirectionFlags = $in->readBoundedInt(StateNames::VINE_DIRECTION_BITS, 0, 15); - return Blocks::VINES() - ->setFace(Facing::NORTH, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_NORTH) !== 0) - ->setFace(Facing::SOUTH, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_SOUTH) !== 0) - ->setFace(Facing::WEST, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_WEST) !== 0) - ->setFace(Facing::EAST, ($vineDirectionFlags & BlockLegacyMetadata::VINE_FLAG_EAST) !== 0); - }); - $this->map(Ids::WALL_BANNER, function(Reader $in) : Block{ - return Blocks::WALL_BANNER() - ->setFacing($in->readHorizontalFacing()); - }); - $this->map(Ids::WATER, fn(Reader $in) => Helper::decodeStillLiquid(Blocks::WATER(), $in)); - - $this->map(Ids::WEEPING_VINES, function(Reader $in) : Block{ - return Blocks::WEEPING_VINES() - ->setAge($in->readBoundedInt(StateNames::WEEPING_VINES_AGE, 0, 25)); - }); - $this->map(Ids::WHEAT, fn(Reader $in) => Helper::decodeCrops(Blocks::WHEAT(), $in)); - } - /** @throws BlockStateDeserializeException */ public function deserializeBlock(BlockStateData $blockStateData) : Block{ $id = $blockStateData->getName(); diff --git a/src/data/bedrock/block/convert/BlockStateWriter.php b/src/data/bedrock/block/convert/BlockStateWriter.php index 63af92d10..0119bd02e 100644 --- a/src/data/bedrock/block/convert/BlockStateWriter.php +++ b/src/data/bedrock/block/convert/BlockStateWriter.php @@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateSerializeException; use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; +use pocketmine\data\bedrock\block\convert\property\EnumFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\IntFromRawStateMap; +use pocketmine\data\bedrock\block\convert\property\ValueMappings; use pocketmine\math\Axis; use pocketmine\math\Facing; use pocketmine\nbt\tag\ByteTag; @@ -73,35 +76,47 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ - public function writeFacingDirection(int $value) : self{ - $this->writeInt(BlockStateNames::FACING_DIRECTION, match($value){ - Facing::DOWN => 0, - Facing::UP => 1, - Facing::NORTH => 2, - Facing::SOUTH => 3, - Facing::WEST => 4, - Facing::EAST => 5, - default => throw new BlockStateSerializeException("Invalid Facing $value") - }); - return $this; - } - - /** @return $this */ - public function writeBlockFace(int $value) : self{ - $this->writeString(BlockStateNames::MC_BLOCK_FACE, match($value){ - Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN, - Facing::UP => StringValues::MC_BLOCK_FACE_UP, - Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH, - Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH, - Facing::WEST => StringValues::MC_BLOCK_FACE_WEST, - Facing::EAST => StringValues::MC_BLOCK_FACE_EAST, - default => throw new BlockStateSerializeException("Invalid Facing $value") - }); + /** + * @deprecated + * @phpstan-param IntFromRawStateMap $map + * @return $this + */ + public function mapIntToString(string $name, IntFromRawStateMap $map, int $value) : self{ + $raw = $map->valueToRaw($value); + $this->writeString($name, $raw); return $this; } /** + * @deprecated + * @phpstan-param IntFromRawStateMap $map + * @return $this + */ + public function mapIntToInt(string $name, IntFromRawStateMap $map, int $value) : self{ + $raw = $map->valueToRaw($value); + $this->writeInt($name, $raw); + return $this; + } + + /** + * @deprecated + * @return $this + */ + public function writeFacingDirection(int $value) : self{ + return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->facing, $value); + } + + /** + * @deprecated + * @return $this + */ + public function writeBlockFace(int $value) : self{ + $this->mapIntToString(BlockStateNames::MC_BLOCK_FACE, ValueMappings::getInstance()->blockFace, $value); + return $this; + } + + /** + * @deprecated * @param int[] $faces * @phpstan-param array $faces * @return $this @@ -123,86 +138,69 @@ final class BlockStateWriter{ return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeEndRodFacingDirection(int $value) : self{ //end rods are stupid in bedrock and have everything except up/down the wrong way round return $this->writeFacingDirection(Facing::axis($value) !== Axis::Y ? Facing::opposite($value) : $value); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeHorizontalFacing(int $value) : self{ - if($value === Facing::UP || $value === Facing::DOWN){ - throw new BlockStateSerializeException("Y-axis facing is not allowed"); - } - - return $this->writeFacingDirection($value); - } - - /** @return $this */ - public function writeWeirdoHorizontalFacing(int $value) : self{ - $this->writeInt(BlockStateNames::WEIRDO_DIRECTION, match($value){ - Facing::EAST => 0, - Facing::WEST => 1, - Facing::SOUTH => 2, - Facing::NORTH => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); - return $this; - } - - /** @return $this */ - public function writeLegacyHorizontalFacing(int $value) : self{ - $this->writeInt(BlockStateNames::DIRECTION, match($value){ - Facing::SOUTH => 0, - Facing::WEST => 1, - Facing::NORTH => 2, - Facing::EAST => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); - return $this; + return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic, $value); } /** + * @deprecated + * @return $this + */ + public function writeWeirdoHorizontalFacing(int $value) : self{ + return $this->mapIntToInt(BlockStateNames::WEIRDO_DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value); + } + + /** + * @deprecated + * @return $this + */ + public function writeLegacyHorizontalFacing(int $value) : self{ + return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacingSWNE, $value); + } + + /** + * @deprecated * This is for trapdoors, because Mojang botched the conversion in 1.13 * @return $this */ public function write5MinusHorizontalFacing(int $value) : self{ - return $this->writeInt(BlockStateNames::DIRECTION, match($value){ - Facing::EAST => 0, - Facing::WEST => 1, - Facing::SOUTH => 2, - Facing::NORTH => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); + return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value); } /** + * @deprecated * Used by pumpkins as of 1.20.0.23 beta * @return $this */ public function writeCardinalHorizontalFacing(int $value) : self{ - return $this->writeString(BlockStateNames::MC_CARDINAL_DIRECTION, match($value){ - Facing::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH, - Facing::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST, - Facing::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH, - Facing::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); + return $this->mapIntToString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection, $value); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeCoralFacing(int $value) : self{ - $this->writeInt(BlockStateNames::CORAL_DIRECTION, match($value){ - Facing::WEST => 0, - Facing::EAST => 1, - Facing::NORTH => 2, - Facing::SOUTH => 3, - default => throw new BlockStateSerializeException("Invalid horizontal facing $value") - }); - return $this; + return $this->mapIntToInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, $value); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeFacingWithoutDown(int $value) : self{ if($value === Facing::DOWN){ throw new BlockStateSerializeException("Invalid facing DOWN"); @@ -211,7 +209,10 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeFacingWithoutUp(int $value) : self{ if($value === Facing::UP){ throw new BlockStateSerializeException("Invalid facing UP"); @@ -220,18 +221,19 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writePillarAxis(int $axis) : self{ - $this->writeString(BlockStateNames::PILLAR_AXIS, match($axis){ - Axis::X => StringValues::PILLAR_AXIS_X, - Axis::Y => StringValues::PILLAR_AXIS_Y, - Axis::Z => StringValues::PILLAR_AXIS_Z, - default => throw new BlockStateSerializeException("Invalid axis $axis") - }); + $this->mapIntToString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis, $axis); return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeSlabPosition(SlabType $slabType) : self{ $this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){ SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP, @@ -241,32 +243,27 @@ final class BlockStateWriter{ return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeTorchFacing(int $facing) : self{ - //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) - $this->writeString(BlockStateNames::TORCH_FACING_DIRECTION, match($facing){ - Facing::UP => StringValues::TORCH_FACING_DIRECTION_TOP, - Facing::SOUTH => StringValues::TORCH_FACING_DIRECTION_NORTH, - Facing::NORTH => StringValues::TORCH_FACING_DIRECTION_SOUTH, - Facing::EAST => StringValues::TORCH_FACING_DIRECTION_WEST, - Facing::WEST => StringValues::TORCH_FACING_DIRECTION_EAST, - default => throw new BlockStateSerializeException("Invalid Torch facing $facing") - }); + $this->mapIntToString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing, $facing); return $this; } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{ - $this->writeString(BlockStateNames::ATTACHMENT, match($attachmentType){ - BellAttachmentType::FLOOR => StringValues::ATTACHMENT_STANDING, - BellAttachmentType::CEILING => StringValues::ATTACHMENT_HANGING, - BellAttachmentType::ONE_WALL => StringValues::ATTACHMENT_SIDE, - BellAttachmentType::TWO_WALLS => StringValues::ATTACHMENT_MULTIPLE, - }); - return $this; + return $this->writeUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, $attachmentType); } - /** @return $this */ + /** + * @deprecated + * @return $this + */ public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{ $this->writeString($name, match($wallConnectionType){ null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE, @@ -276,6 +273,21 @@ final class BlockStateWriter{ return $this; } + /** + * @deprecated + * @phpstan-template TEnum of \UnitEnum + * @phpstan-param EnumFromRawStateMap $map + * @phpstan-param TEnum $case + * + * @return $this + */ + public function writeUnitEnum(string $name, EnumFromRawStateMap $map, \UnitEnum $case) : self{ + $value = $map->valueToRaw($case); + $this->writeString($name, $value); + + return $this; + } + public function getBlockStateData() : BlockStateData{ return BlockStateData::current($this->id, $this->states); } diff --git a/src/data/bedrock/block/convert/FlattenedIdModel.php b/src/data/bedrock/block/convert/FlattenedIdModel.php new file mode 100644 index 000000000..50bf5cdd9 --- /dev/null +++ b/src/data/bedrock/block/convert/FlattenedIdModel.php @@ -0,0 +1,107 @@ +> + */ + private array $idComponents = []; + + /** + * @var Property[] + * @phpstan-var list> + */ + private array $properties = []; + + /** + * @phpstan-param TBlock $block + */ + private function __construct( + private Block $block + ){} + + /** + * @phpstan-template TBlock_ of Block + * @phpstan-param TBlock_ $block + * @return self + */ + public static function create(Block $block) : self{ + /** @phpstan-var self $result */ + $result = new self($block); + return $result; + } + + /** @phpstan-return TBlock */ + public function getBlock() : Block{ return $this->block; } + + /** + * @return string[]|StringProperty[] + * @phpstan-return list> + */ + public function getIdComponents() : array{ return $this->idComponents; } + + /** + * @return Property[] + * @phpstan-return list> + */ + public function getProperties() : array{ return $this->properties; } + + /** + * @param string[]|StringProperty[] $components + * @phpstan-param non-empty-list> $components + * @return $this + * @phpstan-this-out self + */ + public function idComponents(array $components) : self{ + $this->idComponents = $components; + return $this; + } + + /** + * @param Property[] $properties + * @phpstan-param non-empty-list> $properties + * @return $this + * @phpstan-this-out self + */ + public function properties(array $properties) : self{ + $this->properties = $properties; + return $this; + } +} diff --git a/src/data/bedrock/block/convert/Model.php b/src/data/bedrock/block/convert/Model.php new file mode 100644 index 000000000..3474a8932 --- /dev/null +++ b/src/data/bedrock/block/convert/Model.php @@ -0,0 +1,82 @@ +> + */ + private array $properties = []; + + /** + * @phpstan-param TBlock $block + */ + private function __construct( + private Block $block, + private string $id + ){} + + /** @phpstan-return TBlock */ + public function getBlock() : Block{ return $this->block; } + + public function getId() : string{ return $this->id; } + + /** + * @return Property[] + * @phpstan-return list> + */ + public function getProperties() : array{ return $this->properties; } + + /** + * @phpstan-template TBlock_ of Block + * @phpstan-param TBlock_ $block + * @phpstan-return self + */ + public static function create(Block $block, string $id) : self{ + return new self($block, $id); + } + + /** + * @param Property[] $properties + * @phpstan-param list> $properties + * @phpstan-return $this + */ + public function properties(array $properties) : self{ + $this->properties = $properties; + return $this; + } +} diff --git a/src/data/bedrock/block/convert/StringEnumMap.php b/src/data/bedrock/block/convert/StringEnumMap.php deleted file mode 100644 index 6171c0e71..000000000 --- a/src/data/bedrock/block/convert/StringEnumMap.php +++ /dev/null @@ -1,78 +0,0 @@ - - */ - private array $enumToValue = []; - - /** - * @var \UnitEnum[] - * @phpstan-var array - */ - private array $valueToEnum = []; - - /** - * @phpstan-param class-string $class - * @phpstan-param \Closure(TEnum) : string $mapper - */ - public function __construct( - private string $class, - \Closure $mapper - ){ - foreach($class::cases() as $case){ - $string = $mapper($case); - $this->valueToEnum[$string] = $case; - $this->enumToValue[spl_object_id($case)] = $string; - } - } - - /** - * @phpstan-param TEnum $enum - */ - public function enumToValue(\UnitEnum $enum) : string{ - return $this->enumToValue[spl_object_id($enum)]; - } - - public function valueToEnum(string $string) : ?\UnitEnum{ - return $this->valueToEnum[$string] ?? throw new BlockStateDeserializeException("No $this->class enum mapping for \"$string\""); - } - - /** - * @return \UnitEnum[] - * @phpstan-return array - */ - public function getValueToEnum() : array{ - return $this->valueToEnum; - } -} diff --git a/src/data/bedrock/block/convert/ValueMappings.php b/src/data/bedrock/block/convert/ValueMappings.php deleted file mode 100644 index df57d6f53..000000000 --- a/src/data/bedrock/block/convert/ValueMappings.php +++ /dev/null @@ -1,83 +0,0 @@ -, StringEnumMap> - */ - private array $enumMappings = []; - - public function __construct(){ - $this->addEnum(DyeColor::class, fn(DyeColor $case) => match ($case) { - DyeColor::BLACK => "black", - DyeColor::BLUE => "blue", - DyeColor::BROWN => "brown", - DyeColor::CYAN => "cyan", - DyeColor::GRAY => "gray", - DyeColor::GREEN => "green", - DyeColor::LIGHT_BLUE => "light_blue", - DyeColor::LIGHT_GRAY => "light_gray", - DyeColor::LIME => "lime", - DyeColor::MAGENTA => "magenta", - DyeColor::ORANGE => "orange", - DyeColor::PINK => "pink", - DyeColor::PURPLE => "purple", - DyeColor::RED => "red", - DyeColor::WHITE => "white", - DyeColor::YELLOW => "yellow" - }); - } - - /** - * @phpstan-template TEnum of \UnitEnum - * @phpstan-param class-string $class - * @phpstan-param \Closure(TEnum): string $mapper - */ - private function addEnum(string $class, \Closure $mapper) : void{ - $this->enumMappings[$class] = new StringEnumMap($class, $mapper); - } - - /** - * @phpstan-template TEnum of \UnitEnum - * @phpstan-param class-string $class - * @phpstan-return StringEnumMap - */ - public function getEnumMap(string $class) : StringEnumMap{ - if(!isset($this->enumMappings[$class])){ - throw new \InvalidArgumentException("No enum mapping found for class: $class"); - } - /** - * @phpstan-var StringEnumMap $map - */ - $map = $this->enumMappings[$class]; - return $map; - } -} diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php new file mode 100644 index 000000000..16ae1e244 --- /dev/null +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -0,0 +1,1561 @@ +mapSimple(Blocks::AIR(), Ids::AIR); + $reg->mapSimple(Blocks::AMETHYST(), Ids::AMETHYST_BLOCK); + $reg->mapSimple(Blocks::ANCIENT_DEBRIS(), Ids::ANCIENT_DEBRIS); + $reg->mapSimple(Blocks::ANDESITE(), Ids::ANDESITE); + $reg->mapSimple(Blocks::BARRIER(), Ids::BARRIER); + $reg->mapSimple(Blocks::BEACON(), Ids::BEACON); + $reg->mapSimple(Blocks::BLACKSTONE(), Ids::BLACKSTONE); + $reg->mapSimple(Blocks::BLUE_ICE(), Ids::BLUE_ICE); + $reg->mapSimple(Blocks::BOOKSHELF(), Ids::BOOKSHELF); + $reg->mapSimple(Blocks::BRICKS(), Ids::BRICK_BLOCK); + $reg->mapSimple(Blocks::BROWN_MUSHROOM(), Ids::BROWN_MUSHROOM); + $reg->mapSimple(Blocks::BUDDING_AMETHYST(), Ids::BUDDING_AMETHYST); + $reg->mapSimple(Blocks::CALCITE(), Ids::CALCITE); + $reg->mapSimple(Blocks::CARTOGRAPHY_TABLE(), Ids::CARTOGRAPHY_TABLE); + $reg->mapSimple(Blocks::CHEMICAL_HEAT(), Ids::CHEMICAL_HEAT); + $reg->mapSimple(Blocks::CHISELED_DEEPSLATE(), Ids::CHISELED_DEEPSLATE); + $reg->mapSimple(Blocks::CHISELED_NETHER_BRICKS(), Ids::CHISELED_NETHER_BRICKS); + $reg->mapSimple(Blocks::CHISELED_POLISHED_BLACKSTONE(), Ids::CHISELED_POLISHED_BLACKSTONE); + $reg->mapSimple(Blocks::CHISELED_RED_SANDSTONE(), Ids::CHISELED_RED_SANDSTONE); + $reg->mapSimple(Blocks::CHISELED_RESIN_BRICKS(), Ids::CHISELED_RESIN_BRICKS); + $reg->mapSimple(Blocks::CHISELED_SANDSTONE(), Ids::CHISELED_SANDSTONE); + $reg->mapSimple(Blocks::CHISELED_STONE_BRICKS(), Ids::CHISELED_STONE_BRICKS); + $reg->mapSimple(Blocks::CHISELED_TUFF(), Ids::CHISELED_TUFF); + $reg->mapSimple(Blocks::CHISELED_TUFF_BRICKS(), Ids::CHISELED_TUFF_BRICKS); + $reg->mapSimple(Blocks::CHORUS_PLANT(), Ids::CHORUS_PLANT); + $reg->mapSimple(Blocks::CLAY(), Ids::CLAY); + $reg->mapSimple(Blocks::COAL(), Ids::COAL_BLOCK); + $reg->mapSimple(Blocks::COAL_ORE(), Ids::COAL_ORE); + $reg->mapSimple(Blocks::COBBLED_DEEPSLATE(), Ids::COBBLED_DEEPSLATE); + $reg->mapSimple(Blocks::COBBLESTONE(), Ids::COBBLESTONE); + $reg->mapSimple(Blocks::COBWEB(), Ids::WEB); + $reg->mapSimple(Blocks::COPPER_ORE(), Ids::COPPER_ORE); + $reg->mapSimple(Blocks::CRACKED_DEEPSLATE_BRICKS(), Ids::CRACKED_DEEPSLATE_BRICKS); + $reg->mapSimple(Blocks::CRACKED_DEEPSLATE_TILES(), Ids::CRACKED_DEEPSLATE_TILES); + $reg->mapSimple(Blocks::CRACKED_NETHER_BRICKS(), Ids::CRACKED_NETHER_BRICKS); + $reg->mapSimple(Blocks::CRACKED_POLISHED_BLACKSTONE_BRICKS(), Ids::CRACKED_POLISHED_BLACKSTONE_BRICKS); + $reg->mapSimple(Blocks::CRACKED_STONE_BRICKS(), Ids::CRACKED_STONE_BRICKS); + $reg->mapSimple(Blocks::CRAFTING_TABLE(), Ids::CRAFTING_TABLE); + $reg->mapSimple(Blocks::CRIMSON_ROOTS(), Ids::CRIMSON_ROOTS); + $reg->mapSimple(Blocks::CRYING_OBSIDIAN(), Ids::CRYING_OBSIDIAN); + $reg->mapSimple(Blocks::DANDELION(), Ids::DANDELION); + $reg->mapSimple(Blocks::CUT_RED_SANDSTONE(), Ids::CUT_RED_SANDSTONE); + $reg->mapSimple(Blocks::CUT_SANDSTONE(), Ids::CUT_SANDSTONE); + $reg->mapSimple(Blocks::DARK_PRISMARINE(), Ids::DARK_PRISMARINE); + $reg->mapSimple(Blocks::DEAD_BUSH(), Ids::DEADBUSH); + $reg->mapSimple(Blocks::DEEPSLATE_BRICKS(), Ids::DEEPSLATE_BRICKS); + $reg->mapSimple(Blocks::DEEPSLATE_COAL_ORE(), Ids::DEEPSLATE_COAL_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_COPPER_ORE(), Ids::DEEPSLATE_COPPER_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_DIAMOND_ORE(), Ids::DEEPSLATE_DIAMOND_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_EMERALD_ORE(), Ids::DEEPSLATE_EMERALD_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_GOLD_ORE(), Ids::DEEPSLATE_GOLD_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_IRON_ORE(), Ids::DEEPSLATE_IRON_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_LAPIS_LAZULI_ORE(), Ids::DEEPSLATE_LAPIS_ORE); + $reg->mapSimple(Blocks::DEEPSLATE_TILES(), Ids::DEEPSLATE_TILES); + $reg->mapSimple(Blocks::DIAMOND(), Ids::DIAMOND_BLOCK); + $reg->mapSimple(Blocks::DIAMOND_ORE(), Ids::DIAMOND_ORE); + $reg->mapSimple(Blocks::DIORITE(), Ids::DIORITE); + $reg->mapSimple(Blocks::DRAGON_EGG(), Ids::DRAGON_EGG); + $reg->mapSimple(Blocks::DRIED_KELP(), Ids::DRIED_KELP_BLOCK); + $reg->mapSimple(Blocks::ELEMENT_ACTINIUM(), Ids::ELEMENT_89); + $reg->mapSimple(Blocks::ELEMENT_ALUMINUM(), Ids::ELEMENT_13); + $reg->mapSimple(Blocks::ELEMENT_AMERICIUM(), Ids::ELEMENT_95); + $reg->mapSimple(Blocks::ELEMENT_ANTIMONY(), Ids::ELEMENT_51); + $reg->mapSimple(Blocks::ELEMENT_ARGON(), Ids::ELEMENT_18); + $reg->mapSimple(Blocks::ELEMENT_ARSENIC(), Ids::ELEMENT_33); + $reg->mapSimple(Blocks::ELEMENT_ASTATINE(), Ids::ELEMENT_85); + $reg->mapSimple(Blocks::ELEMENT_BARIUM(), Ids::ELEMENT_56); + $reg->mapSimple(Blocks::ELEMENT_BERKELIUM(), Ids::ELEMENT_97); + $reg->mapSimple(Blocks::ELEMENT_BERYLLIUM(), Ids::ELEMENT_4); + $reg->mapSimple(Blocks::ELEMENT_BISMUTH(), Ids::ELEMENT_83); + $reg->mapSimple(Blocks::ELEMENT_BOHRIUM(), Ids::ELEMENT_107); + $reg->mapSimple(Blocks::ELEMENT_BORON(), Ids::ELEMENT_5); + $reg->mapSimple(Blocks::ELEMENT_BROMINE(), Ids::ELEMENT_35); + $reg->mapSimple(Blocks::ELEMENT_CADMIUM(), Ids::ELEMENT_48); + $reg->mapSimple(Blocks::ELEMENT_CALCIUM(), Ids::ELEMENT_20); + $reg->mapSimple(Blocks::ELEMENT_CALIFORNIUM(), Ids::ELEMENT_98); + $reg->mapSimple(Blocks::ELEMENT_CARBON(), Ids::ELEMENT_6); + $reg->mapSimple(Blocks::ELEMENT_CERIUM(), Ids::ELEMENT_58); + $reg->mapSimple(Blocks::ELEMENT_CESIUM(), Ids::ELEMENT_55); + $reg->mapSimple(Blocks::ELEMENT_CHLORINE(), Ids::ELEMENT_17); + $reg->mapSimple(Blocks::ELEMENT_CHROMIUM(), Ids::ELEMENT_24); + $reg->mapSimple(Blocks::ELEMENT_COBALT(), Ids::ELEMENT_27); + $reg->mapSimple(Blocks::ELEMENT_COPERNICIUM(), Ids::ELEMENT_112); + $reg->mapSimple(Blocks::ELEMENT_COPPER(), Ids::ELEMENT_29); + $reg->mapSimple(Blocks::ELEMENT_CURIUM(), Ids::ELEMENT_96); + $reg->mapSimple(Blocks::ELEMENT_DARMSTADTIUM(), Ids::ELEMENT_110); + $reg->mapSimple(Blocks::ELEMENT_DUBNIUM(), Ids::ELEMENT_105); + $reg->mapSimple(Blocks::ELEMENT_DYSPROSIUM(), Ids::ELEMENT_66); + $reg->mapSimple(Blocks::ELEMENT_EINSTEINIUM(), Ids::ELEMENT_99); + $reg->mapSimple(Blocks::ELEMENT_ERBIUM(), Ids::ELEMENT_68); + $reg->mapSimple(Blocks::ELEMENT_EUROPIUM(), Ids::ELEMENT_63); + $reg->mapSimple(Blocks::ELEMENT_FERMIUM(), Ids::ELEMENT_100); + $reg->mapSimple(Blocks::ELEMENT_FLEROVIUM(), Ids::ELEMENT_114); + $reg->mapSimple(Blocks::ELEMENT_FLUORINE(), Ids::ELEMENT_9); + $reg->mapSimple(Blocks::ELEMENT_FRANCIUM(), Ids::ELEMENT_87); + $reg->mapSimple(Blocks::ELEMENT_GADOLINIUM(), Ids::ELEMENT_64); + $reg->mapSimple(Blocks::ELEMENT_GALLIUM(), Ids::ELEMENT_31); + $reg->mapSimple(Blocks::ELEMENT_GERMANIUM(), Ids::ELEMENT_32); + $reg->mapSimple(Blocks::ELEMENT_GOLD(), Ids::ELEMENT_79); + $reg->mapSimple(Blocks::ELEMENT_HAFNIUM(), Ids::ELEMENT_72); + $reg->mapSimple(Blocks::ELEMENT_HASSIUM(), Ids::ELEMENT_108); + $reg->mapSimple(Blocks::ELEMENT_HELIUM(), Ids::ELEMENT_2); + $reg->mapSimple(Blocks::ELEMENT_HOLMIUM(), Ids::ELEMENT_67); + $reg->mapSimple(Blocks::ELEMENT_HYDROGEN(), Ids::ELEMENT_1); + $reg->mapSimple(Blocks::ELEMENT_INDIUM(), Ids::ELEMENT_49); + $reg->mapSimple(Blocks::ELEMENT_IODINE(), Ids::ELEMENT_53); + $reg->mapSimple(Blocks::ELEMENT_IRIDIUM(), Ids::ELEMENT_77); + $reg->mapSimple(Blocks::ELEMENT_IRON(), Ids::ELEMENT_26); + $reg->mapSimple(Blocks::ELEMENT_KRYPTON(), Ids::ELEMENT_36); + $reg->mapSimple(Blocks::ELEMENT_LANTHANUM(), Ids::ELEMENT_57); + $reg->mapSimple(Blocks::ELEMENT_LAWRENCIUM(), Ids::ELEMENT_103); + $reg->mapSimple(Blocks::ELEMENT_LEAD(), Ids::ELEMENT_82); + $reg->mapSimple(Blocks::ELEMENT_LITHIUM(), Ids::ELEMENT_3); + $reg->mapSimple(Blocks::ELEMENT_LIVERMORIUM(), Ids::ELEMENT_116); + $reg->mapSimple(Blocks::ELEMENT_LUTETIUM(), Ids::ELEMENT_71); + $reg->mapSimple(Blocks::ELEMENT_MAGNESIUM(), Ids::ELEMENT_12); + $reg->mapSimple(Blocks::ELEMENT_MANGANESE(), Ids::ELEMENT_25); + $reg->mapSimple(Blocks::ELEMENT_MEITNERIUM(), Ids::ELEMENT_109); + $reg->mapSimple(Blocks::ELEMENT_MENDELEVIUM(), Ids::ELEMENT_101); + $reg->mapSimple(Blocks::ELEMENT_MERCURY(), Ids::ELEMENT_80); + $reg->mapSimple(Blocks::ELEMENT_MOLYBDENUM(), Ids::ELEMENT_42); + $reg->mapSimple(Blocks::ELEMENT_MOSCOVIUM(), Ids::ELEMENT_115); + $reg->mapSimple(Blocks::ELEMENT_NEODYMIUM(), Ids::ELEMENT_60); + $reg->mapSimple(Blocks::ELEMENT_NEON(), Ids::ELEMENT_10); + $reg->mapSimple(Blocks::ELEMENT_NEPTUNIUM(), Ids::ELEMENT_93); + $reg->mapSimple(Blocks::ELEMENT_NICKEL(), Ids::ELEMENT_28); + $reg->mapSimple(Blocks::ELEMENT_NIHONIUM(), Ids::ELEMENT_113); + $reg->mapSimple(Blocks::ELEMENT_NIOBIUM(), Ids::ELEMENT_41); + $reg->mapSimple(Blocks::ELEMENT_NITROGEN(), Ids::ELEMENT_7); + $reg->mapSimple(Blocks::ELEMENT_NOBELIUM(), Ids::ELEMENT_102); + $reg->mapSimple(Blocks::ELEMENT_OGANESSON(), Ids::ELEMENT_118); + $reg->mapSimple(Blocks::ELEMENT_OSMIUM(), Ids::ELEMENT_76); + $reg->mapSimple(Blocks::ELEMENT_OXYGEN(), Ids::ELEMENT_8); + $reg->mapSimple(Blocks::ELEMENT_PALLADIUM(), Ids::ELEMENT_46); + $reg->mapSimple(Blocks::ELEMENT_PHOSPHORUS(), Ids::ELEMENT_15); + $reg->mapSimple(Blocks::ELEMENT_PLATINUM(), Ids::ELEMENT_78); + $reg->mapSimple(Blocks::ELEMENT_PLUTONIUM(), Ids::ELEMENT_94); + $reg->mapSimple(Blocks::ELEMENT_POLONIUM(), Ids::ELEMENT_84); + $reg->mapSimple(Blocks::ELEMENT_POTASSIUM(), Ids::ELEMENT_19); + $reg->mapSimple(Blocks::ELEMENT_PRASEODYMIUM(), Ids::ELEMENT_59); + $reg->mapSimple(Blocks::ELEMENT_PROMETHIUM(), Ids::ELEMENT_61); + $reg->mapSimple(Blocks::ELEMENT_PROTACTINIUM(), Ids::ELEMENT_91); + $reg->mapSimple(Blocks::ELEMENT_RADIUM(), Ids::ELEMENT_88); + $reg->mapSimple(Blocks::ELEMENT_RADON(), Ids::ELEMENT_86); + $reg->mapSimple(Blocks::ELEMENT_RHENIUM(), Ids::ELEMENT_75); + $reg->mapSimple(Blocks::ELEMENT_RHODIUM(), Ids::ELEMENT_45); + $reg->mapSimple(Blocks::ELEMENT_ROENTGENIUM(), Ids::ELEMENT_111); + $reg->mapSimple(Blocks::ELEMENT_RUBIDIUM(), Ids::ELEMENT_37); + $reg->mapSimple(Blocks::ELEMENT_RUTHENIUM(), Ids::ELEMENT_44); + $reg->mapSimple(Blocks::ELEMENT_RUTHERFORDIUM(), Ids::ELEMENT_104); + $reg->mapSimple(Blocks::ELEMENT_SAMARIUM(), Ids::ELEMENT_62); + $reg->mapSimple(Blocks::ELEMENT_SCANDIUM(), Ids::ELEMENT_21); + $reg->mapSimple(Blocks::ELEMENT_SEABORGIUM(), Ids::ELEMENT_106); + $reg->mapSimple(Blocks::ELEMENT_SELENIUM(), Ids::ELEMENT_34); + $reg->mapSimple(Blocks::ELEMENT_SILICON(), Ids::ELEMENT_14); + $reg->mapSimple(Blocks::ELEMENT_SILVER(), Ids::ELEMENT_47); + $reg->mapSimple(Blocks::ELEMENT_SODIUM(), Ids::ELEMENT_11); + $reg->mapSimple(Blocks::ELEMENT_STRONTIUM(), Ids::ELEMENT_38); + $reg->mapSimple(Blocks::ELEMENT_SULFUR(), Ids::ELEMENT_16); + $reg->mapSimple(Blocks::ELEMENT_TANTALUM(), Ids::ELEMENT_73); + $reg->mapSimple(Blocks::ELEMENT_TECHNETIUM(), Ids::ELEMENT_43); + $reg->mapSimple(Blocks::ELEMENT_TELLURIUM(), Ids::ELEMENT_52); + $reg->mapSimple(Blocks::ELEMENT_TENNESSINE(), Ids::ELEMENT_117); + $reg->mapSimple(Blocks::ELEMENT_TERBIUM(), Ids::ELEMENT_65); + $reg->mapSimple(Blocks::ELEMENT_THALLIUM(), Ids::ELEMENT_81); + $reg->mapSimple(Blocks::ELEMENT_THORIUM(), Ids::ELEMENT_90); + $reg->mapSimple(Blocks::ELEMENT_THULIUM(), Ids::ELEMENT_69); + $reg->mapSimple(Blocks::ELEMENT_TIN(), Ids::ELEMENT_50); + $reg->mapSimple(Blocks::ELEMENT_TITANIUM(), Ids::ELEMENT_22); + $reg->mapSimple(Blocks::ELEMENT_TUNGSTEN(), Ids::ELEMENT_74); + $reg->mapSimple(Blocks::ELEMENT_URANIUM(), Ids::ELEMENT_92); + $reg->mapSimple(Blocks::ELEMENT_VANADIUM(), Ids::ELEMENT_23); + $reg->mapSimple(Blocks::ELEMENT_XENON(), Ids::ELEMENT_54); + $reg->mapSimple(Blocks::ELEMENT_YTTERBIUM(), Ids::ELEMENT_70); + $reg->mapSimple(Blocks::ELEMENT_YTTRIUM(), Ids::ELEMENT_39); + $reg->mapSimple(Blocks::ELEMENT_ZERO(), Ids::ELEMENT_0); + $reg->mapSimple(Blocks::ELEMENT_ZINC(), Ids::ELEMENT_30); + $reg->mapSimple(Blocks::ELEMENT_ZIRCONIUM(), Ids::ELEMENT_40); + $reg->mapSimple(Blocks::EMERALD(), Ids::EMERALD_BLOCK); + $reg->mapSimple(Blocks::EMERALD_ORE(), Ids::EMERALD_ORE); + $reg->mapSimple(Blocks::ENCHANTING_TABLE(), Ids::ENCHANTING_TABLE); + $reg->mapSimple(Blocks::END_STONE(), Ids::END_STONE); + $reg->mapSimple(Blocks::END_STONE_BRICKS(), Ids::END_BRICKS); + $reg->mapSimple(Blocks::FERN(), Ids::FERN); + $reg->mapSimple(Blocks::FLETCHING_TABLE(), Ids::FLETCHING_TABLE); + $reg->mapSimple(Blocks::GILDED_BLACKSTONE(), Ids::GILDED_BLACKSTONE); + $reg->mapSimple(Blocks::GLASS(), Ids::GLASS); + $reg->mapSimple(Blocks::GLASS_PANE(), Ids::GLASS_PANE); + $reg->mapSimple(Blocks::GLOWING_OBSIDIAN(), Ids::GLOWINGOBSIDIAN); + $reg->mapSimple(Blocks::GLOWSTONE(), Ids::GLOWSTONE); + $reg->mapSimple(Blocks::GOLD(), Ids::GOLD_BLOCK); + $reg->mapSimple(Blocks::GOLD_ORE(), Ids::GOLD_ORE); + $reg->mapSimple(Blocks::GRANITE(), Ids::GRANITE); + $reg->mapSimple(Blocks::GRASS(), Ids::GRASS_BLOCK); + $reg->mapSimple(Blocks::GRASS_PATH(), Ids::GRASS_PATH); + $reg->mapSimple(Blocks::GRAVEL(), Ids::GRAVEL); + $reg->mapSimple(Blocks::HANGING_ROOTS(), Ids::HANGING_ROOTS); + $reg->mapSimple(Blocks::HARDENED_CLAY(), Ids::HARDENED_CLAY); + $reg->mapSimple(Blocks::HARDENED_GLASS(), Ids::HARD_GLASS); + $reg->mapSimple(Blocks::HARDENED_GLASS_PANE(), Ids::HARD_GLASS_PANE); + $reg->mapSimple(Blocks::HONEYCOMB(), Ids::HONEYCOMB_BLOCK); + $reg->mapSimple(Blocks::ICE(), Ids::ICE); + $reg->mapSimple(Blocks::INFESTED_CHISELED_STONE_BRICK(), Ids::INFESTED_CHISELED_STONE_BRICKS); + $reg->mapSimple(Blocks::INFESTED_COBBLESTONE(), Ids::INFESTED_COBBLESTONE); + $reg->mapSimple(Blocks::INFESTED_CRACKED_STONE_BRICK(), Ids::INFESTED_CRACKED_STONE_BRICKS); + $reg->mapSimple(Blocks::INFESTED_MOSSY_STONE_BRICK(), Ids::INFESTED_MOSSY_STONE_BRICKS); + $reg->mapSimple(Blocks::INFESTED_STONE(), Ids::INFESTED_STONE); + $reg->mapSimple(Blocks::INFESTED_STONE_BRICK(), Ids::INFESTED_STONE_BRICKS); + $reg->mapSimple(Blocks::INFO_UPDATE(), Ids::INFO_UPDATE); + $reg->mapSimple(Blocks::INFO_UPDATE2(), Ids::INFO_UPDATE2); + $reg->mapSimple(Blocks::INVISIBLE_BEDROCK(), Ids::INVISIBLE_BEDROCK); + $reg->mapSimple(Blocks::IRON(), Ids::IRON_BLOCK); + $reg->mapSimple(Blocks::IRON_BARS(), Ids::IRON_BARS); + $reg->mapSimple(Blocks::IRON_ORE(), Ids::IRON_ORE); + $reg->mapSimple(Blocks::JUKEBOX(), Ids::JUKEBOX); + $reg->mapSimple(Blocks::LAPIS_LAZULI(), Ids::LAPIS_BLOCK); + $reg->mapSimple(Blocks::LAPIS_LAZULI_ORE(), Ids::LAPIS_ORE); + $reg->mapSimple(Blocks::LEGACY_STONECUTTER(), Ids::STONECUTTER); + $reg->mapSimple(Blocks::LILY_PAD(), Ids::WATERLILY); + $reg->mapSimple(Blocks::MAGMA(), Ids::MAGMA); + $reg->mapSimple(Blocks::MANGROVE_ROOTS(), Ids::MANGROVE_ROOTS); + $reg->mapSimple(Blocks::MELON(), Ids::MELON_BLOCK); + $reg->mapSimple(Blocks::MONSTER_SPAWNER(), Ids::MOB_SPAWNER); + $reg->mapSimple(Blocks::MOSSY_COBBLESTONE(), Ids::MOSSY_COBBLESTONE); + $reg->mapSimple(Blocks::MOSSY_STONE_BRICKS(), Ids::MOSSY_STONE_BRICKS); + $reg->mapSimple(Blocks::MUD(), Ids::MUD); + $reg->mapSimple(Blocks::MUD_BRICKS(), Ids::MUD_BRICKS); + $reg->mapSimple(Blocks::MYCELIUM(), Ids::MYCELIUM); + $reg->mapSimple(Blocks::NETHERITE(), Ids::NETHERITE_BLOCK); + $reg->mapSimple(Blocks::NETHERRACK(), Ids::NETHERRACK); + $reg->mapSimple(Blocks::NETHER_BRICKS(), Ids::NETHER_BRICK); + $reg->mapSimple(Blocks::NETHER_BRICK_FENCE(), Ids::NETHER_BRICK_FENCE); + $reg->mapSimple(Blocks::NETHER_GOLD_ORE(), Ids::NETHER_GOLD_ORE); + $reg->mapSimple(Blocks::NETHER_QUARTZ_ORE(), Ids::QUARTZ_ORE); + $reg->mapSimple(Blocks::NETHER_REACTOR_CORE(), Ids::NETHERREACTOR); + $reg->mapSimple(Blocks::NETHER_WART_BLOCK(), Ids::NETHER_WART_BLOCK); + $reg->mapSimple(Blocks::NOTE_BLOCK(), Ids::NOTEBLOCK); + $reg->mapSimple(Blocks::OBSIDIAN(), Ids::OBSIDIAN); + $reg->mapSimple(Blocks::PACKED_ICE(), Ids::PACKED_ICE); + $reg->mapSimple(Blocks::PACKED_MUD(), Ids::PACKED_MUD); + $reg->mapSimple(Blocks::PODZOL(), Ids::PODZOL); + $reg->mapSimple(Blocks::POLISHED_ANDESITE(), Ids::POLISHED_ANDESITE); + $reg->mapSimple(Blocks::POLISHED_BLACKSTONE(), Ids::POLISHED_BLACKSTONE); + $reg->mapSimple(Blocks::POLISHED_BLACKSTONE_BRICKS(), Ids::POLISHED_BLACKSTONE_BRICKS); + $reg->mapSimple(Blocks::POLISHED_DEEPSLATE(), Ids::POLISHED_DEEPSLATE); + $reg->mapSimple(Blocks::POLISHED_DIORITE(), Ids::POLISHED_DIORITE); + $reg->mapSimple(Blocks::POLISHED_GRANITE(), Ids::POLISHED_GRANITE); + $reg->mapSimple(Blocks::POLISHED_TUFF(), Ids::POLISHED_TUFF); + $reg->mapSimple(Blocks::PRISMARINE(), Ids::PRISMARINE); + $reg->mapSimple(Blocks::PRISMARINE_BRICKS(), Ids::PRISMARINE_BRICKS); + $reg->mapSimple(Blocks::QUARTZ_BRICKS(), Ids::QUARTZ_BRICKS); + $reg->mapSimple(Blocks::RAW_COPPER(), Ids::RAW_COPPER_BLOCK); + $reg->mapSimple(Blocks::RAW_GOLD(), Ids::RAW_GOLD_BLOCK); + $reg->mapSimple(Blocks::RAW_IRON(), Ids::RAW_IRON_BLOCK); + $reg->mapSimple(Blocks::REDSTONE(), Ids::REDSTONE_BLOCK); + $reg->mapSimple(Blocks::RED_MUSHROOM(), Ids::RED_MUSHROOM); + $reg->mapSimple(Blocks::RED_NETHER_BRICKS(), Ids::RED_NETHER_BRICK); + $reg->mapSimple(Blocks::RED_SAND(), Ids::RED_SAND); + $reg->mapSimple(Blocks::RED_SANDSTONE(), Ids::RED_SANDSTONE); + $reg->mapSimple(Blocks::REINFORCED_DEEPSLATE(), Ids::REINFORCED_DEEPSLATE); + $reg->mapSimple(Blocks::RESERVED6(), Ids::RESERVED6); + $reg->mapSimple(Blocks::RESIN(), Ids::RESIN_BLOCK); + $reg->mapSimple(Blocks::RESIN_BRICKS(), Ids::RESIN_BRICKS); + $reg->mapSimple(Blocks::SAND(), Ids::SAND); + $reg->mapSimple(Blocks::SANDSTONE(), Ids::SANDSTONE); + $reg->mapSimple(Blocks::SCULK(), Ids::SCULK); + $reg->mapSimple(Blocks::SEA_LANTERN(), Ids::SEA_LANTERN); + $reg->mapSimple(Blocks::SHROOMLIGHT(), Ids::SHROOMLIGHT); + $reg->mapSimple(Blocks::SHULKER_BOX(), Ids::UNDYED_SHULKER_BOX); + $reg->mapSimple(Blocks::SLIME(), Ids::SLIME); + $reg->mapSimple(Blocks::SMITHING_TABLE(), Ids::SMITHING_TABLE); + $reg->mapSimple(Blocks::SMOOTH_BASALT(), Ids::SMOOTH_BASALT); + $reg->mapSimple(Blocks::SMOOTH_RED_SANDSTONE(), Ids::SMOOTH_RED_SANDSTONE); + $reg->mapSimple(Blocks::SMOOTH_SANDSTONE(), Ids::SMOOTH_SANDSTONE); + $reg->mapSimple(Blocks::SMOOTH_STONE(), Ids::SMOOTH_STONE); + $reg->mapSimple(Blocks::SNOW(), Ids::SNOW); + $reg->mapSimple(Blocks::SOUL_SAND(), Ids::SOUL_SAND); + $reg->mapSimple(Blocks::SOUL_SOIL(), Ids::SOUL_SOIL); + $reg->mapSimple(Blocks::SPORE_BLOSSOM(), Ids::SPORE_BLOSSOM); + $reg->mapSimple(Blocks::STONE(), Ids::STONE); + $reg->mapSimple(Blocks::STONE_BRICKS(), Ids::STONE_BRICKS); + $reg->mapSimple(Blocks::TALL_GRASS(), Ids::SHORT_GRASS); //no, this is not a typo - tall_grass is now the double block, just to be confusing :( + $reg->mapSimple(Blocks::TINTED_GLASS(), Ids::TINTED_GLASS); + $reg->mapSimple(Blocks::TORCHFLOWER(), Ids::TORCHFLOWER); + $reg->mapSimple(Blocks::TUFF(), Ids::TUFF); + $reg->mapSimple(Blocks::TUFF_BRICKS(), Ids::TUFF_BRICKS); + $reg->mapSimple(Blocks::WARPED_WART_BLOCK(), Ids::WARPED_WART_BLOCK); + $reg->mapSimple(Blocks::WARPED_ROOTS(), Ids::WARPED_ROOTS); + $reg->mapSimple(Blocks::WITHER_ROSE(), Ids::WITHER_ROSE); + + $reg->mapSimple(Blocks::ALLIUM(), Ids::ALLIUM); + $reg->mapSimple(Blocks::CORNFLOWER(), Ids::CORNFLOWER); + $reg->mapSimple(Blocks::AZURE_BLUET(), Ids::AZURE_BLUET); + $reg->mapSimple(Blocks::LILY_OF_THE_VALLEY(), Ids::LILY_OF_THE_VALLEY); + $reg->mapSimple(Blocks::BLUE_ORCHID(), Ids::BLUE_ORCHID); + $reg->mapSimple(Blocks::OXEYE_DAISY(), Ids::OXEYE_DAISY); + $reg->mapSimple(Blocks::POPPY(), Ids::POPPY); + $reg->mapSimple(Blocks::ORANGE_TULIP(), Ids::ORANGE_TULIP); + $reg->mapSimple(Blocks::PINK_TULIP(), Ids::PINK_TULIP); + $reg->mapSimple(Blocks::RED_TULIP(), Ids::RED_TULIP); + $reg->mapSimple(Blocks::WHITE_TULIP(), Ids::WHITE_TULIP); + } + + private static function registerColoredMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapColored(Blocks::STAINED_HARDENED_GLASS(), "minecraft:hard_", "_stained_glass"); + $reg->mapColored(Blocks::STAINED_HARDENED_GLASS_PANE(), "minecraft:hard_", "_stained_glass_pane"); + + $reg->mapColored(Blocks::CARPET(), "minecraft:", "_carpet"); + $reg->mapColored(Blocks::CONCRETE(), "minecraft:", "_concrete"); + $reg->mapColored(Blocks::CONCRETE_POWDER(), "minecraft:", "_concrete_powder"); + $reg->mapColored(Blocks::DYED_SHULKER_BOX(), "minecraft:", "_shulker_box"); + $reg->mapColored(Blocks::STAINED_CLAY(), "minecraft:", "_terracotta"); + $reg->mapColored(Blocks::STAINED_GLASS(), "minecraft:", "_stained_glass"); + $reg->mapColored(Blocks::STAINED_GLASS_PANE(), "minecraft:", "_stained_glass_pane"); + $reg->mapColored(Blocks::WOOL(), "minecraft:", "_wool"); + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::GLAZED_TERRACOTTA()) + ->idComponents([ + "minecraft:", + new ValueFromStringProperty("color", ValueMappings::getInstance()->dyeColorWithSilver, fn(GlazedTerracotta $b) => $b->getColor(), fn(GlazedTerracotta $b, DyeColor $v) => $b->setColor($v)), + "_glazed_terracotta" + ]) + ->properties([$commonProperties->horizontalFacingClassic]) + ); + } + + private static function registerCandleMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $candleProperties = [ + $commonProperties->lit, + new IntProperty(StateNames::CANDLES, 0, 3, fn(Candle $b) => $b->getCount(), fn(Candle $b, int $v) => $b->setCount($v), offset: 1), + ]; + $cakeWithCandleProperties = [$commonProperties->lit]; + $reg->mapModel(Model::create(Blocks::CANDLE(), Ids::CANDLE)->properties($candleProperties)); + $reg->mapModel(Model::create(Blocks::CAKE_WITH_CANDLE(), Ids::CANDLE_CAKE)->properties($cakeWithCandleProperties)); + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DYED_CANDLE()) + ->idComponents([ + "minecraft:", + $commonProperties->dyeColorIdInfix, + "_candle" + ]) + ->properties($candleProperties) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CAKE_WITH_DYED_CANDLE()) + ->idComponents([ + "minecraft:", + $commonProperties->dyeColorIdInfix, + "_candle_cake" + ]) + ->properties($cakeWithCandleProperties) + ); + } + + private static function registerLeavesMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $properties = [ + new BoolProperty(StateNames::PERSISTENT_BIT, fn(Leaves $b) => $b->isNoDecay(), fn(Leaves $b, bool $v) => $b->setNoDecay($v)), + new BoolProperty(StateNames::UPDATE_BIT, fn(Leaves $b) => $b->isCheckDecay(), fn(Leaves $b, bool $v) => $b->setCheckDecay($v)), + ]; + foreach([ + Ids::ACACIA_LEAVES => Blocks::ACACIA_LEAVES(), + Ids::AZALEA_LEAVES => Blocks::AZALEA_LEAVES(), + Ids::AZALEA_LEAVES_FLOWERED => Blocks::FLOWERING_AZALEA_LEAVES(), + Ids::BIRCH_LEAVES => Blocks::BIRCH_LEAVES(), + Ids::CHERRY_LEAVES => Blocks::CHERRY_LEAVES(), + Ids::DARK_OAK_LEAVES => Blocks::DARK_OAK_LEAVES(), + Ids::JUNGLE_LEAVES => Blocks::JUNGLE_LEAVES(), + Ids::MANGROVE_LEAVES => Blocks::MANGROVE_LEAVES(), + Ids::OAK_LEAVES => Blocks::OAK_LEAVES(), + Ids::PALE_OAK_LEAVES => Blocks::PALE_OAK_LEAVES(), + Ids::SPRUCE_LEAVES => Blocks::SPRUCE_LEAVES() + ] as $id => $block){ + $reg->mapModel(Model::create($block, $id)->properties($properties)); + } + } + + private static function registerSaplingMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $properties = [ + new BoolProperty(StateNames::AGE_BIT, fn(Sapling $b) => $b->isReady(), fn(Sapling $b, bool $v) => $b->setReady($v)), + ]; + foreach([ + Ids::ACACIA_SAPLING => Blocks::ACACIA_SAPLING(), + Ids::BIRCH_SAPLING => Blocks::BIRCH_SAPLING(), + Ids::DARK_OAK_SAPLING => Blocks::DARK_OAK_SAPLING(), + Ids::JUNGLE_SAPLING => Blocks::JUNGLE_SAPLING(), + Ids::OAK_SAPLING => Blocks::OAK_SAPLING(), + Ids::SPRUCE_SAPLING => Blocks::SPRUCE_SAPLING(), + ] as $id => $block){ + $reg->mapModel(Model::create($block, $id)->properties($properties)); + } + } + + private static function registerPlantMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapModel(Model::create(Blocks::BEETROOTS(), Ids::BEETROOT)->properties([$commonProperties->cropAgeMax7])); + $reg->mapModel(Model::create(Blocks::CARROTS(), Ids::CARROTS)->properties([$commonProperties->cropAgeMax7])); + $reg->mapModel(Model::create(Blocks::POTATOES(), Ids::POTATOES)->properties([$commonProperties->cropAgeMax7])); + $reg->mapModel(Model::create(Blocks::WHEAT(), Ids::WHEAT)->properties([$commonProperties->cropAgeMax7])); + + $reg->mapModel(Model::create(Blocks::MELON_STEM(), Ids::MELON_STEM)->properties($commonProperties->stemProperties)); + $reg->mapModel(Model::create(Blocks::PUMPKIN_STEM(), Ids::PUMPKIN_STEM)->properties($commonProperties->stemProperties)); + + foreach([ + [Blocks::DOUBLE_TALLGRASS(), Ids::TALL_GRASS], + [Blocks::LARGE_FERN(), Ids::LARGE_FERN], + [Blocks::LILAC(), Ids::LILAC], + [Blocks::PEONY(), Ids::PEONY], + [Blocks::ROSE_BUSH(), Ids::ROSE_BUSH], + [Blocks::SUNFLOWER(), Ids::SUNFLOWER], + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->doublePlantHalf])); + } + + foreach([ + [Blocks::BROWN_MUSHROOM_BLOCK(), Ids::BROWN_MUSHROOM_BLOCK], + [Blocks::RED_MUSHROOM_BLOCK(), Ids::RED_MUSHROOM_BLOCK] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([ + new ValueFromIntProperty(StateNames::HUGE_MUSHROOM_BITS, ValueMappings::getInstance()->mushroomBlockType, fn(RedMushroomBlock $b) => $b->getMushroomBlockType(), fn(RedMushroomBlock $b, MushroomBlockType $v) => $b->setMushroomBlockType($v)), + ])); + } + + $reg->mapModel(Model::create(Blocks::GLOW_LICHEN(), Ids::GLOW_LICHEN)->properties([$commonProperties->multiFacingFlags])); + $reg->mapModel(Model::create(Blocks::RESIN_CLUMP(), Ids::RESIN_CLUMP)->properties([$commonProperties->multiFacingFlags])); + + $reg->mapModel(Model::create(Blocks::VINES(), Ids::VINE)->properties([ + new OptionSetFromIntProperty( + StateNames::VINE_DIRECTION_BITS, + IntFromRawStateMap::int([ + Facing::NORTH => BlockLegacyMetadata::VINE_FLAG_NORTH, + Facing::SOUTH => BlockLegacyMetadata::VINE_FLAG_SOUTH, + Facing::WEST => BlockLegacyMetadata::VINE_FLAG_WEST, + Facing::EAST => BlockLegacyMetadata::VINE_FLAG_EAST, + ]), + fn(Vine $b) => $b->getFaces(), + fn(Vine $b, array $v) => $b->setFaces($v) + ) + ])); + + $reg->mapModel(Model::create(Blocks::SWEET_BERRY_BUSH(), Ids::SWEET_BERRY_BUSH)->properties([ + //TODO: berry bush only wants 0-3, but it can be bigger in MCPE due to misuse of GROWTH state which goes up to 7 + new IntProperty(StateNames::GROWTH, 0, 7, fn(SweetBerryBush $b) => $b->getAge(), fn(SweetBerryBush $b, int $v) => $b->setAge(min($v, SweetBerryBush::STAGE_MATURE))) + ])); + $reg->mapModel(Model::create(Blocks::TORCHFLOWER_CROP(), Ids::TORCHFLOWER_CROP)->properties([ + //TODO: this property can have values 0-7, but only 0-1 are valid + new IntProperty(StateNames::GROWTH, 0, 7, fn(TorchflowerCrop $b) => $b->isReady() ? 1 : 0, fn(TorchflowerCrop $b, int $v) => $b->setReady($v !== 0)) + ])); + } + + private static function registerCoralMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL())->idComponents([...$commonProperties->coralIdPrefixes, "_coral"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL_BLOCK())->idComponents([...$commonProperties->coralIdPrefixes, "_coral_block"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CORAL_FAN()) + ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_fan"]) + ->properties([ + new ValueFromIntProperty(StateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis, fn(FloorCoralFan $b) => $b->getAxis(), fn(FloorCoralFan $b, int $v) => $b->setAxis($v)) + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::WALL_CORAL_FAN()) + ->idComponents([...$commonProperties->coralIdPrefixes, "_coral_wall_fan"]) + ->properties([ + new ValueFromIntProperty(StateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, fn(HorizontalFacing $b) => $b->getFacing(), fn(HorizontalFacing $b, int $v) => $b->setFacing($v)), + ]) + ); + } + + private static function registerCopperMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_BULB()) + ->idComponents([...$commonProperties->copperIdPrefixes, "copper_bulb"]) + ->properties([ + $commonProperties->lit, + new BoolProperty(StateNames::POWERED_BIT, fn(PoweredByRedstone $b) => $b->isPowered(), fn(PoweredByRedstone $b, bool $v) => $b->setPowered($v)), + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER()) + ->idComponents([ + ...$commonProperties->copperIdPrefixes, + "copper", + //HACK: the non-waxed, non-oxidised variant has a _block suffix, but none of the others do + new BoolFromStringProperty("bruhhhh", "", "_block", fn(Copper $b) => !$b->isWaxed() && $b->getOxidation() === CopperOxidation::NONE, fn() => null) + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CHISELED_COPPER())->idComponents([...$commonProperties->copperIdPrefixes, "chiseled_copper"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_GRATE())->idComponents([...$commonProperties->copperIdPrefixes, "copper_grate"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER())->idComponents([...$commonProperties->copperIdPrefixes, "cut_copper"])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER_STAIRS()) + ->idComponents([...$commonProperties->copperIdPrefixes, "cut_copper_stairs"]) + ->properties($commonProperties->stairProperties) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_TRAPDOOR()) + ->idComponents([...$commonProperties->copperIdPrefixes, "copper_trapdoor"]) + ->properties($commonProperties->trapdoorProperties) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::COPPER_DOOR()) + ->idComponents([...$commonProperties->copperIdPrefixes, "copper_door"]) + ->properties($commonProperties->doorProperties) + ); + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CUT_COPPER_SLAB()) + ->idComponents([ + ...$commonProperties->copperIdPrefixes, + $commonProperties->slabIdInfix, + "cut_copper_slab" + ]) + ->properties([$commonProperties->slabPositionProperty]) + ); + } + + private static function registerFlattenedEnumMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + //A + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::ANVIL()) + ->idComponents([ + new ValueFromStringProperty("id", IntFromRawStateMap::string([ + 0 => Ids::ANVIL, + 1 => Ids::CHIPPED_ANVIL, + 2 => Ids::DAMAGED_ANVIL, + ]), fn(Anvil $b) => $b->getDamage(), fn(Anvil $b, int $v) => $b->setDamage($v)) + ]) + ->properties([$commonProperties->horizontalFacingCardinal]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::AMETHYST_CLUSTER()) + ->idComponents([ + new ValueFromStringProperty("id", IntFromRawStateMap::string([ + AmethystCluster::STAGE_SMALL_BUD => Ids::SMALL_AMETHYST_BUD, + AmethystCluster::STAGE_MEDIUM_BUD => Ids::MEDIUM_AMETHYST_BUD, + AmethystCluster::STAGE_LARGE_BUD => Ids::LARGE_AMETHYST_BUD, + AmethystCluster::STAGE_CLUSTER => Ids::AMETHYST_CLUSTER + ]), fn(AmethystCluster $b) => $b->getStage(), fn(AmethystCluster $b, int $v) => $b->setStage($v)) + ]) + ->properties([$commonProperties->blockFace]) + ); + + //C + //This one is a special offender :< + //I have no idea why this only has 3 IDs - there are 4 in Java and 4 visually distinct states in Bedrock + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::CAVE_VINES()) + ->idComponents([ + "minecraft:cave_vines", + new ValueFromStringProperty( + "variant", + EnumFromRawStateMap::string(FlattenedCaveVinesVariant::class, fn(FlattenedCaveVinesVariant $case) => $case->value), + fn(CaveVines $b) => $b->hasBerries() ? + ($b->isHead() ? + FlattenedCaveVinesVariant::HEAD_WITH_BERRIES : + FlattenedCaveVinesVariant::BODY_WITH_BERRIES) : + FlattenedCaveVinesVariant::NO_BERRIES, + fn(CaveVines $b, FlattenedCaveVinesVariant $v) => match($v){ + FlattenedCaveVinesVariant::HEAD_WITH_BERRIES => $b->setBerries(true)->setHead(true), + FlattenedCaveVinesVariant::BODY_WITH_BERRIES => $b->setBerries(true)->setHead(false), + FlattenedCaveVinesVariant::NO_BERRIES => $b->setBerries(false)->setHead(false), //assume this isn't a head, since we don't have enough information + } + ) + ]) + ->properties([ + new IntProperty(StateNames::GROWING_PLANT_AGE, 0, 25, fn(CaveVines $b) => $b->getAge(), fn(CaveVines $b, int $v) => $b->setAge($v)), + ]) + ); + + //D + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DIRT()) + ->idComponents([ + new ValueFromStringProperty("id", EnumFromRawStateMap::string(DirtType::class, fn(DirtType $case) => match ($case) { + DirtType::NORMAL => Ids::DIRT, + DirtType::COARSE => Ids::COARSE_DIRT, + DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, + }), fn(Dirt $b) => $b->getDirtType(), fn(Dirt $b, DirtType $v) => $b->setDirtType($v)) + ]) + ); + + //F + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::FROGLIGHT()) + ->idComponents([ + new ValueFromStringProperty("id", ValueMappings::getInstance()->froglightType, fn(Froglight $b) => $b->getFroglightType(), fn(Froglight $b, FroglightType $v) => $b->setFroglightType($v)), + ]) + ->properties([$commonProperties->pillarAxis]) + ); + + //L + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::LIGHT()) + ->idComponents([ + "minecraft:light_block_", + //this is a bit shit but it's easier than adapting IntProperty to support flattening :D + new ValueFromStringProperty( + "light_level", + IntFromRawStateMap::string(array_map(strval(...), range(0, 15))), + fn(Light $b) => $b->getLightLevel(), + fn(Light $b, int $v) => $b->setLightLevel($v) + ) + ]) + ); + + //M + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::MOB_HEAD()) + ->idComponents([ + new ValueFromStringProperty("id", ValueMappings::getInstance()->mobHeadType, fn(MobHead $b) => $b->getMobHeadType(), fn(MobHead $b, MobHeadType $v) => $b->setMobHeadType($v)), + ]) + ->properties([ + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptDown, fn(MobHead $b) => $b->getFacing(), fn(MobHead $b, int $v) => $b->setFacing($v)) + ]) + ); + + foreach([ + [Blocks::LAVA(), "lava"], + [Blocks::WATER(), "water"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([...$commonProperties->liquidIdPrefixes, $idSuffix]) + ->properties([$commonProperties->liquidData]) + ); + } + } + + private static function registerFlattenedBoolMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + [Blocks::BLAST_FURNACE(), "blast_furnace"], + [Blocks::FURNACE(), "furnace"], + [Blocks::SMOKER(), "smoker"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([...$commonProperties->furnaceIdPrefixes, $idSuffix]) + ->properties([$commonProperties->horizontalFacingCardinal]) + ); + } + + foreach([ + [Blocks::REDSTONE_LAMP(), "redstone_lamp"], + [Blocks::REDSTONE_ORE(), "redstone_ore"], + [Blocks::DEEPSLATE_REDSTONE_ORE(), "deepslate_redstone_ore"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block)->idComponents(["minecraft:", $commonProperties->litIdInfix, $idSuffix])); + } + + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::DAYLIGHT_SENSOR()) + ->idComponents([ + "minecraft:daylight_detector", + new BoolFromStringProperty("inverted", "", "_inverted", fn(DaylightSensor $b) => $b->isInverted(), fn(DaylightSensor $b, bool $v) => $b->setInverted($v)) + ]) + ->properties([$commonProperties->analogRedstoneSignal]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_REPEATER()) + ->idComponents([ + "minecraft:", + new BoolFromStringProperty("powered", "un", "", fn(RedstoneRepeater $b) => $b->isPowered(), fn(RedstoneRepeater $b, bool $v) => $b->setPowered($v)), + "powered_repeater" + ]) + ->properties([ + $commonProperties->horizontalFacingCardinal, + new IntProperty(StateNames::REPEATER_DELAY, 0, 3, fn(RedstoneRepeater $b) => $b->getDelay(), fn(RedstoneRepeater $b, int $v) => $b->setDelay($v), offset: 1), + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_COMPARATOR()) + ->idComponents([ + "minecraft:", + //this property also appears in the state, so we ignore it in the ID + //this is baked here purely to keep minecraft happy + new BoolFromStringProperty("dummy_powered", "un", "", fn(RedstoneComparator $b) => $b->isPowered(), fn() => null), + "powered_comparator" + ]) + ->properties([ + $commonProperties->horizontalFacingCardinal, + new BoolProperty(StateNames::OUTPUT_LIT_BIT, fn(RedstoneComparator $b) => $b->isPowered(), fn(RedstoneComparator $b, bool $v) => $b->setPowered($v)), + new BoolProperty(StateNames::OUTPUT_SUBTRACT_BIT, fn(RedstoneComparator $b) => $b->isSubtractMode(), fn(RedstoneComparator $b, bool $v) => $b->setSubtractMode($v)), + ]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::REDSTONE_TORCH()) + ->idComponents([ + "minecraft:", + new BoolFromStringProperty("lit", "unlit_", "", fn(RedstoneTorch $b) => $b->isLit(), fn(RedstoneTorch $b, bool $v) => $b->setLit($v)), + "redstone_torch" + ]) + ->properties([$commonProperties->torchFacing]) + ); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::SPONGE())->idComponents([ + "minecraft:", + new BoolFromStringProperty("wet", "", "wet_", fn(Sponge $b) => $b->isWet(), fn(Sponge $b, bool $v) => $b->setWet($v)), + "sponge" + ])); + $reg->mapFlattenedId(FlattenedIdModel::create(Blocks::TNT()) + ->idComponents([ + "minecraft:", + new BoolFromStringProperty("underwater", "", "underwater_", fn(TNT $b) => $b->worksUnderwater(), fn(TNT $b, bool $v) => $b->setWorksUnderwater($v)), + "tnt" + ]) + ->properties([ + new BoolProperty(StateNames::EXPLODE_BIT, fn(TNT $b) => $b->isUnstable(), fn(TNT $b, bool $v) => $b->setUnstable($v)), + ]) + ); + } + + private static function registerStoneLikeSlabMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $reg->mapSlab(Blocks::ANDESITE_SLAB(), "andesite"); + $reg->mapSlab(Blocks::BLACKSTONE_SLAB(), "blackstone"); + $reg->mapSlab(Blocks::BRICK_SLAB(), "brick"); + $reg->mapSlab(Blocks::COBBLED_DEEPSLATE_SLAB(), "cobbled_deepslate"); + $reg->mapSlab(Blocks::COBBLESTONE_SLAB(), "cobblestone"); + $reg->mapSlab(Blocks::CUT_RED_SANDSTONE_SLAB(), "cut_red_sandstone"); + $reg->mapSlab(Blocks::CUT_SANDSTONE_SLAB(), "cut_sandstone"); + $reg->mapSlab(Blocks::DARK_PRISMARINE_SLAB(), "dark_prismarine"); + $reg->mapSlab(Blocks::DEEPSLATE_BRICK_SLAB(), "deepslate_brick"); + $reg->mapSlab(Blocks::DEEPSLATE_TILE_SLAB(), "deepslate_tile"); + $reg->mapSlab(Blocks::DIORITE_SLAB(), "diorite"); + $reg->mapSlab(Blocks::END_STONE_BRICK_SLAB(), "end_stone_brick"); + $reg->mapSlab(Blocks::FAKE_WOODEN_SLAB(), "petrified_oak"); + $reg->mapSlab(Blocks::GRANITE_SLAB(), "granite"); + $reg->mapSlab(Blocks::MOSSY_COBBLESTONE_SLAB(), "mossy_cobblestone"); + $reg->mapSlab(Blocks::MOSSY_STONE_BRICK_SLAB(), "mossy_stone_brick"); + $reg->mapSlab(Blocks::MUD_BRICK_SLAB(), "mud_brick"); + $reg->mapSlab(Blocks::NETHER_BRICK_SLAB(), "nether_brick"); + $reg->mapSlab(Blocks::POLISHED_ANDESITE_SLAB(), "polished_andesite"); + $reg->mapSlab(Blocks::POLISHED_BLACKSTONE_BRICK_SLAB(), "polished_blackstone_brick"); + $reg->mapSlab(Blocks::POLISHED_BLACKSTONE_SLAB(), "polished_blackstone"); + $reg->mapSlab(Blocks::POLISHED_DEEPSLATE_SLAB(), "polished_deepslate"); + $reg->mapSlab(Blocks::POLISHED_DIORITE_SLAB(), "polished_diorite"); + $reg->mapSlab(Blocks::POLISHED_GRANITE_SLAB(), "polished_granite"); + $reg->mapSlab(Blocks::POLISHED_TUFF_SLAB(), "polished_tuff"); + $reg->mapSlab(Blocks::PRISMARINE_BRICKS_SLAB(), "prismarine_brick"); + $reg->mapSlab(Blocks::PRISMARINE_SLAB(), "prismarine"); + $reg->mapSlab(Blocks::PURPUR_SLAB(), "purpur"); + $reg->mapSlab(Blocks::QUARTZ_SLAB(), "quartz"); + $reg->mapSlab(Blocks::RED_NETHER_BRICK_SLAB(), "red_nether_brick"); + $reg->mapSlab(Blocks::RED_SANDSTONE_SLAB(), "red_sandstone"); + $reg->mapSlab(Blocks::RESIN_BRICK_SLAB(), "resin_brick"); + $reg->mapSlab(Blocks::SANDSTONE_SLAB(), "sandstone"); + $reg->mapSlab(Blocks::SMOOTH_QUARTZ_SLAB(), "smooth_quartz"); + $reg->mapSlab(Blocks::SMOOTH_RED_SANDSTONE_SLAB(), "smooth_red_sandstone"); + $reg->mapSlab(Blocks::SMOOTH_SANDSTONE_SLAB(), "smooth_sandstone"); + $reg->mapSlab(Blocks::SMOOTH_STONE_SLAB(), "smooth_stone"); + $reg->mapSlab(Blocks::STONE_BRICK_SLAB(), "stone_brick"); + $reg->mapSlab(Blocks::STONE_SLAB(), "normal_stone"); + $reg->mapSlab(Blocks::TUFF_BRICK_SLAB(), "tuff_brick"); + $reg->mapSlab(Blocks::TUFF_SLAB(), "tuff"); + } + + private static function registerStoneLikeStairMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + $reg->mapStairs(Blocks::ANDESITE_STAIRS(), Ids::ANDESITE_STAIRS); + $reg->mapStairs(Blocks::BLACKSTONE_STAIRS(), Ids::BLACKSTONE_STAIRS); + $reg->mapStairs(Blocks::BRICK_STAIRS(), Ids::BRICK_STAIRS); + $reg->mapStairs(Blocks::COBBLED_DEEPSLATE_STAIRS(), Ids::COBBLED_DEEPSLATE_STAIRS); + $reg->mapStairs(Blocks::COBBLESTONE_STAIRS(), Ids::STONE_STAIRS); + $reg->mapStairs(Blocks::DARK_PRISMARINE_STAIRS(), Ids::DARK_PRISMARINE_STAIRS); + $reg->mapStairs(Blocks::DEEPSLATE_BRICK_STAIRS(), Ids::DEEPSLATE_BRICK_STAIRS); + $reg->mapStairs(Blocks::DEEPSLATE_TILE_STAIRS(), Ids::DEEPSLATE_TILE_STAIRS); + $reg->mapStairs(Blocks::DIORITE_STAIRS(), Ids::DIORITE_STAIRS); + $reg->mapStairs(Blocks::END_STONE_BRICK_STAIRS(), Ids::END_BRICK_STAIRS); + $reg->mapStairs(Blocks::GRANITE_STAIRS(), Ids::GRANITE_STAIRS); + $reg->mapStairs(Blocks::MOSSY_COBBLESTONE_STAIRS(), Ids::MOSSY_COBBLESTONE_STAIRS); + $reg->mapStairs(Blocks::MOSSY_STONE_BRICK_STAIRS(), Ids::MOSSY_STONE_BRICK_STAIRS); + $reg->mapStairs(Blocks::MUD_BRICK_STAIRS(), Ids::MUD_BRICK_STAIRS); + $reg->mapStairs(Blocks::NETHER_BRICK_STAIRS(), Ids::NETHER_BRICK_STAIRS); + $reg->mapStairs(Blocks::POLISHED_ANDESITE_STAIRS(), Ids::POLISHED_ANDESITE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_BLACKSTONE_BRICK_STAIRS(), Ids::POLISHED_BLACKSTONE_BRICK_STAIRS); + $reg->mapStairs(Blocks::POLISHED_BLACKSTONE_STAIRS(), Ids::POLISHED_BLACKSTONE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_DEEPSLATE_STAIRS(), Ids::POLISHED_DEEPSLATE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_DIORITE_STAIRS(), Ids::POLISHED_DIORITE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_GRANITE_STAIRS(), Ids::POLISHED_GRANITE_STAIRS); + $reg->mapStairs(Blocks::POLISHED_TUFF_STAIRS(), Ids::POLISHED_TUFF_STAIRS); + $reg->mapStairs(Blocks::PRISMARINE_BRICKS_STAIRS(), Ids::PRISMARINE_BRICKS_STAIRS); + $reg->mapStairs(Blocks::PRISMARINE_STAIRS(), Ids::PRISMARINE_STAIRS); + $reg->mapStairs(Blocks::PURPUR_STAIRS(), Ids::PURPUR_STAIRS); + $reg->mapStairs(Blocks::QUARTZ_STAIRS(), Ids::QUARTZ_STAIRS); + $reg->mapStairs(Blocks::RED_NETHER_BRICK_STAIRS(), Ids::RED_NETHER_BRICK_STAIRS); + $reg->mapStairs(Blocks::RED_SANDSTONE_STAIRS(), Ids::RED_SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::RESIN_BRICK_STAIRS(), Ids::RESIN_BRICK_STAIRS); + $reg->mapStairs(Blocks::SANDSTONE_STAIRS(), Ids::SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::SMOOTH_QUARTZ_STAIRS(), Ids::SMOOTH_QUARTZ_STAIRS); + $reg->mapStairs(Blocks::SMOOTH_RED_SANDSTONE_STAIRS(), Ids::SMOOTH_RED_SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::SMOOTH_SANDSTONE_STAIRS(), Ids::SMOOTH_SANDSTONE_STAIRS); + $reg->mapStairs(Blocks::STONE_BRICK_STAIRS(), Ids::STONE_BRICK_STAIRS); + $reg->mapStairs(Blocks::STONE_STAIRS(), Ids::NORMAL_STONE_STAIRS); + $reg->mapStairs(Blocks::TUFF_BRICK_STAIRS(), Ids::TUFF_BRICK_STAIRS); + $reg->mapStairs(Blocks::TUFF_STAIRS(), Ids::TUFF_STAIRS); + } + + private static function registerStoneLikeWallMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + Ids::ANDESITE_WALL => Blocks::ANDESITE_WALL(), + Ids::BLACKSTONE_WALL => Blocks::BLACKSTONE_WALL(), + Ids::BRICK_WALL => Blocks::BRICK_WALL(), + Ids::COBBLED_DEEPSLATE_WALL => Blocks::COBBLED_DEEPSLATE_WALL(), + Ids::COBBLESTONE_WALL => Blocks::COBBLESTONE_WALL(), + Ids::DEEPSLATE_BRICK_WALL => Blocks::DEEPSLATE_BRICK_WALL(), + Ids::DEEPSLATE_TILE_WALL => Blocks::DEEPSLATE_TILE_WALL(), + Ids::DIORITE_WALL => Blocks::DIORITE_WALL(), + Ids::END_STONE_BRICK_WALL => Blocks::END_STONE_BRICK_WALL(), + Ids::GRANITE_WALL => Blocks::GRANITE_WALL(), + Ids::MOSSY_COBBLESTONE_WALL => Blocks::MOSSY_COBBLESTONE_WALL(), + Ids::MOSSY_STONE_BRICK_WALL => Blocks::MOSSY_STONE_BRICK_WALL(), + Ids::MUD_BRICK_WALL => Blocks::MUD_BRICK_WALL(), + Ids::NETHER_BRICK_WALL => Blocks::NETHER_BRICK_WALL(), + Ids::POLISHED_BLACKSTONE_BRICK_WALL => Blocks::POLISHED_BLACKSTONE_BRICK_WALL(), + Ids::POLISHED_BLACKSTONE_WALL => Blocks::POLISHED_BLACKSTONE_WALL(), + Ids::POLISHED_DEEPSLATE_WALL => Blocks::POLISHED_DEEPSLATE_WALL(), + Ids::POLISHED_TUFF_WALL => Blocks::POLISHED_TUFF_WALL(), + Ids::PRISMARINE_WALL => Blocks::PRISMARINE_WALL(), + Ids::RED_NETHER_BRICK_WALL => Blocks::RED_NETHER_BRICK_WALL(), + Ids::RED_SANDSTONE_WALL => Blocks::RED_SANDSTONE_WALL(), + Ids::RESIN_BRICK_WALL => Blocks::RESIN_BRICK_WALL(), + Ids::SANDSTONE_WALL => Blocks::SANDSTONE_WALL(), + Ids::STONE_BRICK_WALL => Blocks::STONE_BRICK_WALL(), + Ids::TUFF_BRICK_WALL => Blocks::TUFF_BRICK_WALL(), + Ids::TUFF_WALL => Blocks::TUFF_WALL() + ] as $id => $block){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->wallProperties)); + } + } + + private static function registerWoodMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + //buttons + foreach([ + [Blocks::ACACIA_BUTTON(), Ids::ACACIA_BUTTON], + [Blocks::BIRCH_BUTTON(), Ids::BIRCH_BUTTON], + [Blocks::CHERRY_BUTTON(), Ids::CHERRY_BUTTON], + [Blocks::CRIMSON_BUTTON(), Ids::CRIMSON_BUTTON], + [Blocks::DARK_OAK_BUTTON(), Ids::DARK_OAK_BUTTON], + [Blocks::JUNGLE_BUTTON(), Ids::JUNGLE_BUTTON], + [Blocks::MANGROVE_BUTTON(), Ids::MANGROVE_BUTTON], + [Blocks::OAK_BUTTON(), Ids::WOODEN_BUTTON], + [Blocks::PALE_OAK_BUTTON(), Ids::PALE_OAK_BUTTON], + [Blocks::SPRUCE_BUTTON(), Ids::SPRUCE_BUTTON], + [Blocks::WARPED_BUTTON(), Ids::WARPED_BUTTON] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->buttonProperties)); + } + + //doors + foreach([ + [Blocks::ACACIA_DOOR(), Ids::ACACIA_DOOR], + [Blocks::BIRCH_DOOR(), Ids::BIRCH_DOOR], + [Blocks::CHERRY_DOOR(), Ids::CHERRY_DOOR], + [Blocks::CRIMSON_DOOR(), Ids::CRIMSON_DOOR], + [Blocks::DARK_OAK_DOOR(), Ids::DARK_OAK_DOOR], + [Blocks::JUNGLE_DOOR(), Ids::JUNGLE_DOOR], + [Blocks::MANGROVE_DOOR(), Ids::MANGROVE_DOOR], + [Blocks::OAK_DOOR(), Ids::WOODEN_DOOR], + [Blocks::PALE_OAK_DOOR(), Ids::PALE_OAK_DOOR], + [Blocks::SPRUCE_DOOR(), Ids::SPRUCE_DOOR], + [Blocks::WARPED_DOOR(), Ids::WARPED_DOOR] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->doorProperties)); + } + + //fences + foreach([ + [Blocks::ACACIA_FENCE(), Ids::ACACIA_FENCE], + [Blocks::BIRCH_FENCE(), Ids::BIRCH_FENCE], + [Blocks::CHERRY_FENCE(), Ids::CHERRY_FENCE], + [Blocks::DARK_OAK_FENCE(), Ids::DARK_OAK_FENCE], + [Blocks::JUNGLE_FENCE(), Ids::JUNGLE_FENCE], + [Blocks::MANGROVE_FENCE(), Ids::MANGROVE_FENCE], + [Blocks::OAK_FENCE(), Ids::OAK_FENCE], + [Blocks::PALE_OAK_FENCE(), Ids::PALE_OAK_FENCE], + [Blocks::SPRUCE_FENCE(), Ids::SPRUCE_FENCE], + [Blocks::CRIMSON_FENCE(), Ids::CRIMSON_FENCE], + [Blocks::WARPED_FENCE(), Ids::WARPED_FENCE] + ] as [$block, $id]){ + $reg->mapSimple($block, $id); + } + + foreach([ + [Blocks::ACACIA_FENCE_GATE(), Ids::ACACIA_FENCE_GATE], + [Blocks::BIRCH_FENCE_GATE(), Ids::BIRCH_FENCE_GATE], + [Blocks::CHERRY_FENCE_GATE(), Ids::CHERRY_FENCE_GATE], + [Blocks::DARK_OAK_FENCE_GATE(), Ids::DARK_OAK_FENCE_GATE], + [Blocks::JUNGLE_FENCE_GATE(), Ids::JUNGLE_FENCE_GATE], + [Blocks::MANGROVE_FENCE_GATE(), Ids::MANGROVE_FENCE_GATE], + [Blocks::OAK_FENCE_GATE(), Ids::FENCE_GATE], + [Blocks::PALE_OAK_FENCE_GATE(), Ids::PALE_OAK_FENCE_GATE], + [Blocks::SPRUCE_FENCE_GATE(), Ids::SPRUCE_FENCE_GATE], + [Blocks::CRIMSON_FENCE_GATE(), Ids::CRIMSON_FENCE_GATE], + [Blocks::WARPED_FENCE_GATE(), Ids::WARPED_FENCE_GATE] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->fenceGateProperties)); + } + + foreach([ + [Blocks::ACACIA_SIGN(), Ids::ACACIA_STANDING_SIGN], + [Blocks::BIRCH_SIGN(), Ids::BIRCH_STANDING_SIGN], + [Blocks::CHERRY_SIGN(), Ids::CHERRY_STANDING_SIGN], + [Blocks::DARK_OAK_SIGN(), Ids::DARKOAK_STANDING_SIGN], + [Blocks::JUNGLE_SIGN(), Ids::JUNGLE_STANDING_SIGN], + [Blocks::MANGROVE_SIGN(), Ids::MANGROVE_STANDING_SIGN], + [Blocks::OAK_SIGN(), Ids::STANDING_SIGN], + [Blocks::PALE_OAK_SIGN(), Ids::PALE_OAK_STANDING_SIGN], + [Blocks::SPRUCE_SIGN(), Ids::SPRUCE_STANDING_SIGN], + [Blocks::CRIMSON_SIGN(), Ids::CRIMSON_STANDING_SIGN], + [Blocks::WARPED_SIGN(), Ids::WARPED_STANDING_SIGN] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->floorSignLikeRotation])); + } + + //logs + foreach([ + [Blocks::ACACIA_LOG(), "acacia_log"], + [Blocks::BIRCH_LOG(), "birch_log"], + [Blocks::CHERRY_LOG(), "cherry_log"], + [Blocks::DARK_OAK_LOG(), "dark_oak_log"], + [Blocks::JUNGLE_LOG(), "jungle_log"], + [Blocks::MANGROVE_LOG(), "mangrove_log"], + [Blocks::OAK_LOG(), "oak_log"], + [Blocks::PALE_OAK_LOG(), "pale_oak_log"], + [Blocks::SPRUCE_LOG(), "spruce_log"], + [Blocks::CRIMSON_STEM(), "crimson_stem"], + [Blocks::WARPED_STEM(), "warped_stem"], + + //all-sided logs + [Blocks::ACACIA_WOOD(), "acacia_wood"], + [Blocks::BIRCH_WOOD(), "birch_wood"], + [Blocks::CHERRY_WOOD(), "cherry_wood"], + [Blocks::DARK_OAK_WOOD(), "dark_oak_wood"], + [Blocks::JUNGLE_WOOD(), "jungle_wood"], + [Blocks::MANGROVE_WOOD(), "mangrove_wood"], + [Blocks::OAK_WOOD(), "oak_wood"], + [Blocks::PALE_OAK_WOOD(), "pale_oak_wood"], + [Blocks::SPRUCE_WOOD(), "spruce_wood"], + [Blocks::CRIMSON_HYPHAE(), "crimson_hyphae"], + [Blocks::WARPED_HYPHAE(), "warped_hyphae"] + ] as [$block, $idSuffix]){ + $reg->mapFlattenedId(FlattenedIdModel::create($block) + ->idComponents([...$commonProperties->woodIdPrefixes, $idSuffix]) + ->properties([$commonProperties->pillarAxis]) + ); + } + + //planks + foreach([ + [Blocks::ACACIA_PLANKS(), Ids::ACACIA_PLANKS], + [Blocks::BIRCH_PLANKS(), Ids::BIRCH_PLANKS], + [Blocks::CHERRY_PLANKS(), Ids::CHERRY_PLANKS], + [Blocks::DARK_OAK_PLANKS(), Ids::DARK_OAK_PLANKS], + [Blocks::JUNGLE_PLANKS(), Ids::JUNGLE_PLANKS], + [Blocks::MANGROVE_PLANKS(), Ids::MANGROVE_PLANKS], + [Blocks::OAK_PLANKS(), Ids::OAK_PLANKS], + [Blocks::PALE_OAK_PLANKS(), Ids::PALE_OAK_PLANKS], + [Blocks::SPRUCE_PLANKS(), Ids::SPRUCE_PLANKS], + [Blocks::CRIMSON_PLANKS(), Ids::CRIMSON_PLANKS], + [Blocks::WARPED_PLANKS(), Ids::WARPED_PLANKS] + ] as [$block, $id]){ + $reg->mapSimple($block, $id); + } + + //pressure plates + foreach([ + [Blocks::ACACIA_PRESSURE_PLATE(), Ids::ACACIA_PRESSURE_PLATE], + [Blocks::BIRCH_PRESSURE_PLATE(), Ids::BIRCH_PRESSURE_PLATE], + [Blocks::CHERRY_PRESSURE_PLATE(), Ids::CHERRY_PRESSURE_PLATE], + [Blocks::DARK_OAK_PRESSURE_PLATE(), Ids::DARK_OAK_PRESSURE_PLATE], + [Blocks::JUNGLE_PRESSURE_PLATE(), Ids::JUNGLE_PRESSURE_PLATE], + [Blocks::MANGROVE_PRESSURE_PLATE(), Ids::MANGROVE_PRESSURE_PLATE], + [Blocks::OAK_PRESSURE_PLATE(), Ids::WOODEN_PRESSURE_PLATE], + [Blocks::PALE_OAK_PRESSURE_PLATE(), Ids::PALE_OAK_PRESSURE_PLATE], + [Blocks::SPRUCE_PRESSURE_PLATE(), Ids::SPRUCE_PRESSURE_PLATE], + [Blocks::CRIMSON_PRESSURE_PLATE(), Ids::CRIMSON_PRESSURE_PLATE], + [Blocks::WARPED_PRESSURE_PLATE(), Ids::WARPED_PRESSURE_PLATE] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->simplePressurePlateProperties)); + } + + //slabs + foreach([ + [Blocks::ACACIA_SLAB(), "acacia"], + [Blocks::BIRCH_SLAB(), "birch"], + [Blocks::CHERRY_SLAB(), "cherry"], + [Blocks::DARK_OAK_SLAB(), "dark_oak"], + [Blocks::JUNGLE_SLAB(), "jungle"], + [Blocks::MANGROVE_SLAB(), "mangrove"], + [Blocks::OAK_SLAB(), "oak"], + [Blocks::PALE_OAK_SLAB(), "pale_oak"], + [Blocks::SPRUCE_SLAB(), "spruce"], + [Blocks::CRIMSON_SLAB(), "crimson"], + [Blocks::WARPED_SLAB(), "warped"] + ] as [$block, $type]){ + $reg->mapSlab($block, $type); + } + + //stairs + foreach([ + [Blocks::ACACIA_STAIRS(), Ids::ACACIA_STAIRS], + [Blocks::BIRCH_STAIRS(), Ids::BIRCH_STAIRS], + [Blocks::CHERRY_STAIRS(), Ids::CHERRY_STAIRS], + [Blocks::DARK_OAK_STAIRS(), Ids::DARK_OAK_STAIRS], + [Blocks::JUNGLE_STAIRS(), Ids::JUNGLE_STAIRS], + [Blocks::MANGROVE_STAIRS(), Ids::MANGROVE_STAIRS], + [Blocks::OAK_STAIRS(), Ids::OAK_STAIRS], + [Blocks::PALE_OAK_STAIRS(), Ids::PALE_OAK_STAIRS], + [Blocks::SPRUCE_STAIRS(), Ids::SPRUCE_STAIRS], + [Blocks::CRIMSON_STAIRS(), Ids::CRIMSON_STAIRS], + [Blocks::WARPED_STAIRS(), Ids::WARPED_STAIRS] + ] as [$block, $id]){ + $reg->mapStairs($block, $id); + } + + //trapdoors + foreach([ + [Blocks::ACACIA_TRAPDOOR(), Ids::ACACIA_TRAPDOOR], + [Blocks::BIRCH_TRAPDOOR(), Ids::BIRCH_TRAPDOOR], + [Blocks::CHERRY_TRAPDOOR(), Ids::CHERRY_TRAPDOOR], + [Blocks::DARK_OAK_TRAPDOOR(), Ids::DARK_OAK_TRAPDOOR], + [Blocks::JUNGLE_TRAPDOOR(), Ids::JUNGLE_TRAPDOOR], + [Blocks::MANGROVE_TRAPDOOR(), Ids::MANGROVE_TRAPDOOR], + [Blocks::OAK_TRAPDOOR(), Ids::TRAPDOOR], + [Blocks::PALE_OAK_TRAPDOOR(), Ids::PALE_OAK_TRAPDOOR], + [Blocks::SPRUCE_TRAPDOOR(), Ids::SPRUCE_TRAPDOOR], + [Blocks::CRIMSON_TRAPDOOR(), Ids::CRIMSON_TRAPDOOR], + [Blocks::WARPED_TRAPDOOR(), Ids::WARPED_TRAPDOOR] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties($commonProperties->trapdoorProperties)); + } + + //wall signs + foreach([ + [Blocks::ACACIA_WALL_SIGN(), Ids::ACACIA_WALL_SIGN], + [Blocks::BIRCH_WALL_SIGN(), Ids::BIRCH_WALL_SIGN], + [Blocks::CHERRY_WALL_SIGN(), Ids::CHERRY_WALL_SIGN], + [Blocks::DARK_OAK_WALL_SIGN(), Ids::DARKOAK_WALL_SIGN], + [Blocks::JUNGLE_WALL_SIGN(), Ids::JUNGLE_WALL_SIGN], + [Blocks::MANGROVE_WALL_SIGN(), Ids::MANGROVE_WALL_SIGN], + [Blocks::OAK_WALL_SIGN(), Ids::WALL_SIGN], + [Blocks::PALE_OAK_WALL_SIGN(), Ids::PALE_OAK_WALL_SIGN], + [Blocks::SPRUCE_WALL_SIGN(), Ids::SPRUCE_WALL_SIGN], + [Blocks::CRIMSON_WALL_SIGN(), Ids::CRIMSON_WALL_SIGN], + [Blocks::WARPED_WALL_SIGN(), Ids::WARPED_WALL_SIGN] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->horizontalFacingClassic])); + } + } + + private static function registerTorchMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + [Blocks::BLUE_TORCH(), Ids::COLORED_TORCH_BLUE], + [Blocks::GREEN_TORCH(), Ids::COLORED_TORCH_GREEN], + [Blocks::PURPLE_TORCH(), Ids::COLORED_TORCH_PURPLE], + [Blocks::RED_TORCH(), Ids::COLORED_TORCH_RED], + [Blocks::SOUL_TORCH(), Ids::SOUL_TORCH], + [Blocks::TORCH(), Ids::TORCH], + [Blocks::UNDERWATER_TORCH(), Ids::UNDERWATER_TORCH] + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->torchFacing])); + } + } + + private static function registerChemistryMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + foreach([ + [Blocks::COMPOUND_CREATOR(), Ids::COMPOUND_CREATOR], + [Blocks::ELEMENT_CONSTRUCTOR(), Ids::ELEMENT_CONSTRUCTOR], + [Blocks::LAB_TABLE(), Ids::LAB_TABLE], + [Blocks::MATERIAL_REDUCER(), Ids::MATERIAL_REDUCER], + ] as [$block, $id]){ + $reg->mapModel(Model::create($block, $id)->properties([$commonProperties->horizontalFacingSWNEInverted])); + } + } + + private static function register1to1CustomMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ + //TODO: some of these have repeated accessor refs, we might be able to deduplicate them + //A + $reg->mapModel(Model::create(Blocks::ACTIVATOR_RAIL(), Ids::ACTIVATOR_RAIL)->properties([ + new BoolProperty(StateNames::RAIL_DATA_BIT, fn(ActivatorRail $b) => $b->isPowered(), fn(ActivatorRail $b, bool $v) => $b->setPowered($v)), + new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(ActivatorRail $b) => $b->getShape(), fn(ActivatorRail $b, int $v) => $b->setShape($v)) + ])); + + //B + $reg->mapModel(Model::create(Blocks::BAMBOO(), Ids::BAMBOO)->properties([ + new ValueFromStringProperty(StateNames::BAMBOO_LEAF_SIZE, ValueMappings::getInstance()->bambooLeafSize, fn(Bamboo $b) => $b->getLeafSize(), fn(Bamboo $b, int $v) => $b->setLeafSize($v)), + new BoolProperty(StateNames::AGE_BIT, fn(Bamboo $b) => $b->isReady(), fn(Bamboo $b, bool $v) => $b->setReady($v)), + new BoolFromStringProperty(StateNames::BAMBOO_STALK_THICKNESS, StringValues::BAMBOO_STALK_THICKNESS_THIN, StringValues::BAMBOO_STALK_THICKNESS_THICK, fn(Bamboo $b) => $b->isThick(), fn(Bamboo $b, bool $v) => $b->setThick($v)) + ])); + $reg->mapModel(Model::create(Blocks::BAMBOO_SAPLING(), Ids::BAMBOO_SAPLING)->properties([ + new BoolProperty(StateNames::AGE_BIT, fn(BambooSapling $b) => $b->isReady(), fn(BambooSapling $b, bool $v) => $b->setReady($v)) + ])); + $reg->mapModel(Model::create(Blocks::BANNER(), Ids::STANDING_BANNER)->properties([$commonProperties->floorSignLikeRotation])); + $reg->mapModel(Model::create(Blocks::BARREL(), Ids::BARREL)->properties([ + $commonProperties->anyFacingClassic, + new BoolProperty(StateNames::OPEN_BIT, fn(Barrel $b) => $b->isOpen(), fn(Barrel $b, bool $v) => $b->setOpen($v)) + ])); + $reg->mapModel(Model::create(Blocks::BASALT(), Ids::BASALT)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::BED(), Ids::BED)->properties([ + new BoolProperty(StateNames::HEAD_PIECE_BIT, fn(Bed $b) => $b->isHeadPart(), fn(Bed $b, bool $v) => $b->setHead($v)), + new BoolProperty(StateNames::OCCUPIED_BIT, fn(Bed $b) => $b->isOccupied(), fn(Bed $b, bool $v) => $b->setOccupied($v)), + $commonProperties->horizontalFacingSWNE + ])); + $reg->mapModel(Model::create(Blocks::BEDROCK(), Ids::BEDROCK)->properties([ + new BoolProperty(StateNames::INFINIBURN_BIT, fn(Bedrock $b) => $b->burnsForever(), fn(Bedrock $b, bool $v) => $b->setBurnsForever($v)) + ])); + $reg->mapModel(Model::create(Blocks::BELL(), Ids::BELL)->properties([ + BoolProperty::unused(StateNames::TOGGLE_BIT, false), + new ValueFromStringProperty(StateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, fn(Bell $b) => $b->getAttachmentType(), fn(Bell $b, BellAttachmentType $v) => $b->setAttachmentType($v)), + $commonProperties->horizontalFacingSWNE + ])); + $reg->mapModel(Model::create(Blocks::BONE_BLOCK(), Ids::BONE_BLOCK)->properties([ + IntProperty::unused(StateNames::DEPRECATED, 0), + $commonProperties->pillarAxis + ])); + + $reg->mapModel(Model::create(Blocks::BREWING_STAND(), Ids::BREWING_STAND)->properties(array_map(fn(BrewingStandSlot $slot) => new BoolProperty(match ($slot) { + BrewingStandSlot::EAST => StateNames::BREWING_STAND_SLOT_A_BIT, + BrewingStandSlot::SOUTHWEST => StateNames::BREWING_STAND_SLOT_B_BIT, + BrewingStandSlot::NORTHWEST => StateNames::BREWING_STAND_SLOT_C_BIT + }, fn(BrewingStand $b) => $b->hasSlot($slot), fn(BrewingStand $b, bool $v) => $b->setSlot($slot, $v)), BrewingStandSlot::cases()))); + + //C + $reg->mapModel(Model::create(Blocks::CACTUS(), Ids::CACTUS)->properties([ + new IntProperty(StateNames::AGE, 0, 15, fn(Cactus $b) => $b->getAge(), fn(Cactus $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::CAKE(), Ids::CAKE)->properties([ + new IntProperty(StateNames::BITE_COUNTER, 0, 6, fn(Cake $b) => $b->getBites(), fn(Cake $b, int $v) => $b->setBites($v)) + ])); + $reg->mapModel(Model::create(Blocks::CAMPFIRE(), Ids::CAMPFIRE)->properties($commonProperties->campfireProperties)); + $reg->mapModel(Model::create(Blocks::CARVED_PUMPKIN(), Ids::CARVED_PUMPKIN)->properties([ + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::CHAIN(), Ids::CHAIN)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::CHISELED_BOOKSHELF(), Ids::CHISELED_BOOKSHELF)->properties([ + $commonProperties->horizontalFacingSWNE, + new OptionSetFromIntProperty( + StateNames::BOOKS_STORED, + EnumFromRawStateMap::int(ChiseledBookshelfSlot::class, fn(ChiseledBookshelfSlot $case) => match($case){ + //these are (currently) the same as the internal values, but it's best not to rely on those in case Mojang mess with the flags + ChiseledBookshelfSlot::TOP_LEFT => 1 << 0, + ChiseledBookshelfSlot::TOP_MIDDLE => 1 << 1, + ChiseledBookshelfSlot::TOP_RIGHT => 1 << 2, + ChiseledBookshelfSlot::BOTTOM_LEFT => 1 << 3, + ChiseledBookshelfSlot::BOTTOM_MIDDLE => 1 << 4, + ChiseledBookshelfSlot::BOTTOM_RIGHT => 1 << 5 + }), + fn(ChiseledBookshelf $b) => $b->getSlots(), + fn(ChiseledBookshelf $b, array $v) => $b->setSlots($v) + ) + ])); + $reg->mapModel(Model::create(Blocks::CHISELED_QUARTZ(), Ids::CHISELED_QUARTZ_BLOCK)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::CHEST(), Ids::CHEST)->properties([$commonProperties->horizontalFacingCardinal])); + $reg->mapModel(Model::create(Blocks::CHORUS_FLOWER(), Ids::CHORUS_FLOWER)->properties([ + new IntProperty(StateNames::AGE, ChorusFlower::MIN_AGE, ChorusFlower::MAX_AGE, fn(ChorusFlower $b) => $b->getAge(), fn(ChorusFlower $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::COCOA_POD(), Ids::COCOA)->properties([ + new IntProperty(StateNames::AGE, 0, 2, fn(CocoaBlock $b) => $b->getAge(), fn(CocoaBlock $b, int $v) => $b->setAge($v)), + $commonProperties->horizontalFacingSWNEInverted + ])); + + //D + $reg->mapModel(Model::create(Blocks::DEEPSLATE(), Ids::DEEPSLATE)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::DETECTOR_RAIL(), Ids::DETECTOR_RAIL)->properties([ + new BoolProperty(StateNames::RAIL_DATA_BIT, fn(DetectorRail $b) => $b->isActivated(), fn(DetectorRail $b, bool $v) => $b->setActivated($v)), + new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail + ])); + + //E + $reg->mapModel(Model::create(Blocks::ENDER_CHEST(), Ids::ENDER_CHEST)->properties([$commonProperties->horizontalFacingCardinal])); + $reg->mapModel(Model::create(Blocks::END_PORTAL_FRAME(), Ids::END_PORTAL_FRAME)->properties([ + new BoolProperty(StateNames::END_PORTAL_EYE_BIT, fn(EndPortalFrame $b) => $b->hasEye(), fn(EndPortalFrame $b, bool $v) => $b->setEye($v)), + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::END_ROD(), Ids::END_ROD)->properties([ + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingEndRod, fn(EndRod $b) => $b->getFacing(), fn(EndRod $b, int $v) => $b->setFacing($v)), + ])); + + //F + $reg->mapModel(Model::create(Blocks::FARMLAND(), Ids::FARMLAND)->properties([ + new IntProperty(StateNames::MOISTURIZED_AMOUNT, 0, 7, fn(Farmland $b) => $b->getWetness(), fn(Farmland $b, int $v) => $b->setWetness($v)) + ])); + $reg->mapModel(Model::create(Blocks::FIRE(), Ids::FIRE)->properties([ + new IntProperty(StateNames::AGE, 0, 15, fn(Fire $b) => $b->getAge(), fn(Fire $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::FLOWER_POT(), Ids::FLOWER_POT)->properties([ + BoolProperty::unused(StateNames::UPDATE_BIT, false) + ])); + $reg->mapModel(Model::create(Blocks::FROSTED_ICE(), Ids::FROSTED_ICE)->properties([ + new IntProperty(StateNames::AGE, 0, 3, fn(FrostedIce $b) => $b->getAge(), fn(FrostedIce $b, int $v) => $b->setAge($v)) + ])); + + //G + $reg->mapModel(Model::create(Blocks::GLOWING_ITEM_FRAME(), Ids::GLOW_FRAME)->properties($commonProperties->itemFrameProperties)); + + //H + $reg->mapModel(Model::create(Blocks::HAY_BALE(), Ids::HAY_BLOCK)->properties([ + IntProperty::unused(StateNames::DEPRECATED, 0), + $commonProperties->pillarAxis + ])); + $reg->mapModel(Model::create(Blocks::HOPPER(), Ids::HOPPER)->properties([ + //kinda weird this doesn't use powered_bit? + new BoolProperty(StateNames::TOGGLE_BIT, fn(PoweredByRedstone $b) => $b->isPowered(), fn(PoweredByRedstone $b, bool $v) => $b->setPowered($v)), + new ValueFromIntProperty(StateNames::FACING_DIRECTION, ValueMappings::getInstance()->facingExceptUp, fn(Hopper $b) => $b->getFacing(), fn(Hopper $b, int $v) => $b->setFacing($v)), + ])); + + //I + $reg->mapModel(Model::create(Blocks::IRON_DOOR(), Ids::IRON_DOOR)->properties($commonProperties->doorProperties)); + $reg->mapModel(Model::create(Blocks::IRON_TRAPDOOR(), Ids::IRON_TRAPDOOR)->properties($commonProperties->trapdoorProperties)); + $reg->mapModel(Model::create(Blocks::ITEM_FRAME(), Ids::FRAME)->properties($commonProperties->itemFrameProperties)); + + //L + $reg->mapModel(Model::create(Blocks::LADDER(), Ids::LADDER)->properties([$commonProperties->horizontalFacingClassic])); + $reg->mapModel(Model::create(Blocks::LANTERN(), Ids::LANTERN)->properties([ + new BoolProperty(StateNames::HANGING, fn(Lantern $b) => $b->isHanging(), fn(Lantern $b, bool $v) => $b->setHanging($v)) + ])); + $reg->mapModel(Model::create(Blocks::LECTERN(), Ids::LECTERN)->properties([ + new BoolProperty(StateNames::POWERED_BIT, fn(Lectern $b) => $b->isProducingSignal(), fn(Lectern $b, bool $v) => $b->setProducingSignal($v)), + $commonProperties->horizontalFacingCardinal, + ])); + $reg->mapModel(Model::create(Blocks::LEVER(), Ids::LEVER)->properties([ + new ValueFromStringProperty(StateNames::LEVER_DIRECTION, ValueMappings::getInstance()->leverFacing, fn(Lever $b) => $b->getFacing(), fn(Lever $b, LeverFacing $v) => $b->setFacing($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(Lever $b) => $b->isActivated(), fn(Lever $b, bool $v) => $b->setActivated($v)), + ])); + $reg->mapModel(Model::create(Blocks::LIGHTNING_ROD(), Ids::LIGHTNING_ROD)->properties([$commonProperties->anyFacingClassic])); + $reg->mapModel(Model::create(Blocks::LIT_PUMPKIN(), Ids::LIT_PUMPKIN)->properties([$commonProperties->horizontalFacingCardinal])); + $reg->mapModel(Model::create(Blocks::LOOM(), Ids::LOOM)->properties([$commonProperties->horizontalFacingSWNE])); + + //M + $reg->mapModel(Model::create(Blocks::MUDDY_MANGROVE_ROOTS(), Ids::MUDDY_MANGROVE_ROOTS)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::NETHER_WART(), Ids::NETHER_WART)->properties([ + new IntProperty(StateNames::AGE, 0, 3, fn(NetherWartPlant $b) => $b->getAge(), fn(NetherWartPlant $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::NETHER_PORTAL(), Ids::PORTAL)->properties([ + new ValueFromStringProperty(StateNames::PORTAL_AXIS, ValueMappings::getInstance()->portalAxis, fn(NetherPortal $b) => $b->getAxis(), fn(NetherPortal $b, int $v) => $b->setAxis($v)) + ])); + + //P + $reg->mapModel(Model::create(Blocks::PINK_PETALS(), Ids::PINK_PETALS)->properties([ + //Pink petals only uses 0-3, but GROWTH state can go up to 7 + new IntProperty(StateNames::GROWTH, 0, 7, fn(PinkPetals $b) => $b->getCount(), fn(PinkPetals $b, int $v) => $b->setCount(min($v, PinkPetals::MAX_COUNT)), offset: 1), + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::POWERED_RAIL(), Ids::GOLDEN_RAIL)->properties([ + new BoolProperty(StateNames::RAIL_DATA_BIT, fn(PoweredRail $b) => $b->isPowered(), fn(PoweredRail $b, bool $v) => $b->setPowered($v)), //TODO: shared with ActivatorRail + new IntProperty(StateNames::RAIL_DIRECTION, 0, 5, fn(StraightOnlyRail $b) => $b->getShape(), fn(StraightOnlyRail $b, int $v) => $b->setShape($v)) //TODO: shared with ActivatorRail + ])); + $reg->mapModel(Model::create(Blocks::PITCHER_PLANT(), Ids::PITCHER_PLANT)->properties([ + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePlant $b) => $b->isTop(), fn(DoublePlant $b, bool $v) => $b->setTop($v)), //TODO: don't we have helpers for this? + ])); + $reg->mapModel(Model::create(Blocks::POLISHED_BASALT(), Ids::POLISHED_BASALT)->properties([$commonProperties->pillarAxis])); + $reg->mapModel(Model::create(Blocks::POLISHED_BLACKSTONE_BUTTON(), Ids::POLISHED_BLACKSTONE_BUTTON)->properties($commonProperties->buttonProperties)); + $reg->mapModel(Model::create(Blocks::POLISHED_BLACKSTONE_PRESSURE_PLATE(), Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE)->properties($commonProperties->simplePressurePlateProperties)); + $reg->mapModel(Model::create(Blocks::PUMPKIN(), Ids::PUMPKIN)->properties([ + //not used, has no visible effect + $commonProperties->dummyCardinalDirection + ])); + $reg->mapModel(Model::create(Blocks::PURPUR(), Ids::PURPUR_BLOCK)->properties([ + $commonProperties->dummyPillarAxis + ])); + $reg->mapModel(Model::create(Blocks::PURPUR_PILLAR(), Ids::PURPUR_PILLAR)->properties([$commonProperties->pillarAxis])); + + //Q + $reg->mapModel(Model::create(Blocks::QUARTZ(), Ids::QUARTZ_BLOCK)->properties([ + $commonProperties->dummyPillarAxis + ])); + $reg->mapModel(Model::create(Blocks::QUARTZ_PILLAR(), Ids::QUARTZ_PILLAR)->properties([$commonProperties->pillarAxis])); + + //R + $reg->mapModel(Model::create(Blocks::RAIL(), Ids::RAIL)->properties([ + new IntProperty(StateNames::RAIL_DIRECTION, 0, 9, fn(Rail $b) => $b->getShape(), fn(Rail $b, int $v) => $b->setShape($v)) + ])); + $reg->mapModel(Model::create(Blocks::REDSTONE_WIRE(), Ids::REDSTONE_WIRE)->properties([$commonProperties->analogRedstoneSignal])); + $reg->mapModel(Model::create(Blocks::RESPAWN_ANCHOR(), Ids::RESPAWN_ANCHOR)->properties([ + new IntProperty(StateNames::RESPAWN_ANCHOR_CHARGE, 0, 4, fn(RespawnAnchor $b) => $b->getCharges(), fn(RespawnAnchor $b, int $v) => $b->setCharges($v)) + ])); + + //S + $reg->mapModel(Model::create(Blocks::SEA_PICKLE(), Ids::SEA_PICKLE)->properties([ + new IntProperty(StateNames::CLUSTER_COUNT, 0, 3, fn(SeaPickle $b) => $b->getCount(), fn(SeaPickle $b, int $v) => $b->setCount($v), offset: 1), + new BoolProperty(StateNames::DEAD_BIT, fn(SeaPickle $b) => $b->isUnderwater(), fn(SeaPickle $b, bool $v) => $b->setUnderwater($v), inverted: true) + ])); + $reg->mapModel(Model::create(Blocks::SMALL_DRIPLEAF(), Ids::SMALL_DRIPLEAF_BLOCK)->properties([ + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(SmallDripleaf $b) => $b->isTop(), fn(SmallDripleaf $b, bool $v) => $b->setTop($v)), + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::SMOOTH_QUARTZ(), Ids::SMOOTH_QUARTZ)->properties([ + $commonProperties->dummyPillarAxis + ])); + $reg->mapModel(Model::create(Blocks::SNOW_LAYER(), Ids::SNOW_LAYER)->properties([ + new DummyProperty(StateNames::COVERED_BIT, false), + new IntProperty(StateNames::HEIGHT, 0, 7, fn(SnowLayer $b) => $b->getLayers(), fn(SnowLayer $b, int $v) => $b->setLayers($v), offset: 1) + ])); + $reg->mapModel(Model::create(Blocks::SOUL_CAMPFIRE(), Ids::SOUL_CAMPFIRE)->properties($commonProperties->campfireProperties)); + $reg->mapModel(Model::create(Blocks::SOUL_FIRE(), Ids::SOUL_FIRE)->properties([ + new DummyProperty(StateNames::AGE, 0) //this is useless for soul fire, since it doesn't have the logic associated + ])); + $reg->mapModel(Model::create(Blocks::SOUL_LANTERN(), Ids::SOUL_LANTERN)->properties([ + new BoolProperty(StateNames::HANGING, fn(Lantern $b) => $b->isHanging(), fn(Lantern $b, bool $v) => $b->setHanging($v)) //TODO: repeated + ])); + $reg->mapModel(Model::create(Blocks::STONE_BUTTON(), Ids::STONE_BUTTON)->properties($commonProperties->buttonProperties)); + $reg->mapModel(Model::create(Blocks::STONE_PRESSURE_PLATE(), Ids::STONE_PRESSURE_PLATE)->properties($commonProperties->simplePressurePlateProperties)); + $reg->mapModel(Model::create(Blocks::STONECUTTER(), Ids::STONECUTTER_BLOCK)->properties([ + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::SUGARCANE(), Ids::REEDS)->properties([ + new IntProperty(StateNames::AGE, 0, 15, fn(Sugarcane $b) => $b->getAge(), fn(Sugarcane $b, int $v) => $b->setAge($v)) + ])); + + //T + $reg->mapModel(Model::create(Blocks::TRAPPED_CHEST(), Ids::TRAPPED_CHEST)->properties([ + $commonProperties->horizontalFacingCardinal + ])); + $reg->mapModel(Model::create(Blocks::TRIPWIRE(), Ids::TRIP_WIRE)->properties([ + new BoolProperty(StateNames::ATTACHED_BIT, fn(Tripwire $b) => $b->isConnected(), fn(Tripwire $b, bool $v) => $b->setConnected($v)), + new BoolProperty(StateNames::DISARMED_BIT, fn(Tripwire $b) => $b->isDisarmed(), fn(Tripwire $b, bool $v) => $b->setDisarmed($v)), + new BoolProperty(StateNames::SUSPENDED_BIT, fn(Tripwire $b) => $b->isSuspended(), fn(Tripwire $b, bool $v) => $b->setSuspended($v)), + new BoolProperty(StateNames::POWERED_BIT, fn(Tripwire $b) => $b->isTriggered(), fn(Tripwire $b, bool $v) => $b->setTriggered($v)), + ])); + $reg->mapModel(Model::create(Blocks::TRIPWIRE_HOOK(), Ids::TRIPWIRE_HOOK)->properties([ + new BoolProperty(StateNames::ATTACHED_BIT, fn(TripwireHook $b) => $b->isConnected(), fn(TripwireHook $b, bool $v) => $b->setConnected($v)), + new BoolProperty(StateNames::POWERED_BIT, fn(TripwireHook $b) => $b->isPowered(), fn(TripwireHook $b, bool $v) => $b->setPowered($v)), + $commonProperties->horizontalFacingSWNE + ])); + + $reg->mapModel(Model::create(Blocks::TWISTING_VINES(), Ids::TWISTING_VINES)->properties([ + new IntProperty(StateNames::TWISTING_VINES_AGE, 0, 25, fn(NetherVines $b) => $b->getAge(), fn(NetherVines $b, int $v) => $b->setAge($v)) + ])); + + //W + $reg->mapModel(Model::create(Blocks::WALL_BANNER(), Ids::WALL_BANNER)->properties([$commonProperties->horizontalFacingClassic])); + $reg->mapModel(Model::create(Blocks::WEEPING_VINES(), Ids::WEEPING_VINES)->properties([ + new IntProperty(StateNames::WEEPING_VINES_AGE, 0, 25, fn(NetherVines $b) => $b->getAge(), fn(NetherVines $b, int $v) => $b->setAge($v)) + ])); + $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_HEAVY(), Ids::HEAVY_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal])); + $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), Ids::LIGHT_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal])); + } + + /** + * All mappings that still use the split form of serializer/deserializer registration + * This is typically only used by blocks with one ID but multiple PM types (split by property) + * These currently can't be registered in a unified way, and due to their small number it may not be worth the + * effort to implement a unified way to deal with them + */ + private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + //big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state + $reg->serializer->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ + return Writer::create(Ids::BIG_DRIPLEAF) + ->writeCardinalHorizontalFacing($block->getFacing()) + ->writeUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState, $block->getLeafState()) + ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true); + }); + $reg->serializer->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{ + return Writer::create(Ids::BIG_DRIPLEAF) + ->writeCardinalHorizontalFacing($block->getFacing()) + ->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE) + ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false); + }); + $reg->deserializer->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{ + if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){ + return Blocks::BIG_DRIPLEAF_HEAD() + ->setFacing($in->readCardinalHorizontalFacing()) + ->setLeafState($in->readUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState)); + }else{ + $in->ignored(StateNames::BIG_DRIPLEAF_TILT); + return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readCardinalHorizontalFacing()); + } + }); + + //cauldrons - split into liquid variants, as each have different behaviour + $reg->serializer->map(Blocks::CAULDRON(), Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); + $reg->serializer->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel())); + //potion cauldrons store their real information in the block actor data + $reg->serializer->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); + $reg->serializer->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); + $reg->deserializer->map(Ids::CAULDRON, function(Reader $in) : Block{ + $level = $in->readBoundedInt(StateNames::FILL_LEVEL, 0, 6); + if($level === 0){ + $in->ignored(StateNames::CAULDRON_LIQUID); + return Blocks::CAULDRON(); + } + + return (match ($liquid = $in->readString(StateNames::CAULDRON_LIQUID)) { + StringValues::CAULDRON_LIQUID_WATER => Blocks::WATER_CAULDRON(), + StringValues::CAULDRON_LIQUID_LAVA => Blocks::LAVA_CAULDRON(), + StringValues::CAULDRON_LIQUID_POWDER_SNOW => throw new UnsupportedBlockStateException("Powder snow is not supported yet"), + default => throw $in->badValueException(StateNames::CAULDRON_LIQUID, $liquid) + })->setFillLevel($level); + }); + + //mushroom stems, split for consistency with all-sided logs vs normal logs + $reg->serializer->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) + ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); + $reg->serializer->map(Blocks::MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) + ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); + $reg->deserializer->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match ($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)) { + BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(), + BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(), + default => throw new BlockStateDeserializeException("This state does not exist"), + }); + + //pitcher crop, split into single and double variants as double has different properties and behaviour + //this will probably be the most annoying to unify + $reg->serializer->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{ + return Writer::create(Ids::PITCHER_CROP) + ->writeInt(StateNames::GROWTH, $block->getAge()) + ->writeBool(StateNames::UPPER_BLOCK_BIT, false); + }); + $reg->serializer->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{ + return Writer::create(Ids::PITCHER_CROP) + ->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE) + ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); + }); + $reg->deserializer->map(Ids::PITCHER_CROP, function(Reader $in) : Block{ + $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); + $top = $in->readBool(StateNames::UPPER_BLOCK_BIT); + if($growth <= PitcherCrop::MAX_AGE){ + //top pitcher crop with age 0-2 is an invalid state + //only the bottom half should exist in this case + return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth); + } + return Blocks::DOUBLE_PITCHER_CROP() + ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) + ->setTop($top); + }); + } +} diff --git a/src/data/bedrock/block/convert/property/BoolFromStringProperty.php b/src/data/bedrock/block/convert/property/BoolFromStringProperty.php new file mode 100644 index 000000000..89c64188d --- /dev/null +++ b/src/data/bedrock/block/convert/property/BoolFromStringProperty.php @@ -0,0 +1,78 @@ + + */ +final class BoolFromStringProperty implements StringProperty{ + + /** + * @param \Closure(TBlock) : bool $getter + * @param \Closure(TBlock, bool) : mixed $setter + */ + public function __construct( + private string $name, + private string $falseValue, + private string $trueValue, + private \Closure $getter, + private \Closure $setter + ){} + + public function getName() : string{ + return $this->name; + } + + public function getPossibleValues() : array{ + return [$this->falseValue, $this->trueValue]; + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $this->deserializePlain($block, $in->readString($this->name)); + } + + public function deserializePlain(object $block, string $raw) : void{ + $value = match($raw){ + $this->falseValue => false, + $this->trueValue => true, + default => throw new BlockStateSerializeException("Invalid value for {$this->name}: $raw"), + }; + + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $out->writeString($this->name, $this->serializePlain($block)); + } + + public function serializePlain(object $block) : string{ + $value = ($this->getter)($block); + return $value ? $this->trueValue : $this->falseValue; + } +} diff --git a/src/data/bedrock/block/convert/property/BoolProperty.php b/src/data/bedrock/block/convert/property/BoolProperty.php new file mode 100644 index 000000000..299ec4076 --- /dev/null +++ b/src/data/bedrock/block/convert/property/BoolProperty.php @@ -0,0 +1,71 @@ + + */ +final class BoolProperty implements Property{ + /** + * @phpstan-param \Closure(TBlock) : bool $getter + * @phpstan-param \Closure(TBlock, bool) : mixed $setter + */ + public function __construct( + private string $name, + private \Closure $getter, + private \Closure $setter, + private bool $inverted = false //we don't *need* this, but it avoids accidentally forgetting a ! in the getter/setter closures (and makes it analysable) + ){} + + /** + * @phpstan-return self + */ + public static function unused(string $name, bool $serializedValue) : self{ + return new self($name, fn() => $serializedValue, fn() => null); + } + + public function getName() : string{ return $this->name; } + + /** + * @phpstan-param TBlock $block + */ + public function deserialize(object $block, BlockStateReader $in) : void{ + $raw = $in->readBool($this->name); + $value = $raw !== $this->inverted; + ($this->setter)($block, $value); + } + + /** + * @phpstan-param TBlock $block + */ + public function serialize(object $block, BlockStateWriter $out) : void{ + $value = ($this->getter)($block); + $raw = $value !== $this->inverted; + $out->writeBool($this->name, $raw); + } +} diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php new file mode 100644 index 000000000..71b87139c --- /dev/null +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -0,0 +1,429 @@ + */ + public readonly ValueFromStringProperty $blockFace; + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $pillarAxis; + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $torchFacing; + + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $horizontalFacingCardinal; + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $horizontalFacingSWNE; + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $horizontalFacingSWNEInverted; + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $horizontalFacingClassic; + + /** @phpstan-var ValueFromIntProperty */ + public readonly ValueFromIntProperty $anyFacingClassic; + + /** @phpstan-var OptionSetFromIntProperty */ + public readonly OptionSetFromIntProperty $multiFacingFlags; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $floorSignLikeRotation; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $analogRedstoneSignal; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $cropAgeMax7; + /** @phpstan-var BoolProperty */ + public readonly BoolProperty $doublePlantHalf; + + /** @phpstan-var IntProperty */ + public readonly IntProperty $liquidData; + + /** @phpstan-var BoolProperty */ + public readonly BoolProperty $lit; + + public readonly DummyProperty $dummyCardinalDirection; + public readonly DummyProperty $dummyPillarAxis; + + /** @phpstan-var ValueFromStringProperty */ + public readonly ValueFromStringProperty $dyeColorIdInfix; + + /** @phpstan-var BoolFromStringProperty */ + public readonly BoolFromStringProperty $litIdInfix; + + /** @phpstan-var BoolFromStringProperty */ + public readonly BoolFromStringProperty $slabIdInfix; + /** @phpstan-var BoolFromStringProperty */ + public readonly BoolFromStringProperty $slabPositionProperty; + + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $coralIdPrefixes; + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $copperIdPrefixes; + + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $furnaceIdPrefixes; + + /** + * @var StringProperty[]|string[] + * @phpstan-var non-empty-list> + */ + public readonly array $liquidIdPrefixes; + + /** + * @var StringProperty[] + * @phpstan-var non-empty-list> + */ + public readonly array $woodIdPrefixes; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $buttonProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $campfireProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $doorProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $fenceGateProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $itemFrameProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $simplePressurePlateProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $stairProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $stemProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $trapdoorProperties; + + /** + * @var Property[] + * @phpstan-var non-empty-list> + */ + public readonly array $wallProperties; + + private function __construct(){ + $vm = ValueMappings::getInstance(); + + $hfGet = fn(HorizontalFacing $v) => $v->getFacing(); + $hfSet = fn(HorizontalFacing $v, int $facing) => $v->setFacing($facing); + $this->horizontalFacingCardinal = new ValueFromStringProperty(StateNames::MC_CARDINAL_DIRECTION, $vm->cardinalDirection, $hfGet, $hfSet); + + $this->blockFace = new ValueFromStringProperty( + StateNames::MC_BLOCK_FACE, + $vm->blockFace, + fn(AnyFacing $b) => $b->getFacing(), + fn(AnyFacing $b, int $v) => $b->setFacing($v) + ); + + $this->pillarAxis = new ValueFromStringProperty( + StateNames::PILLAR_AXIS, + $vm->pillarAxis, + fn(PillarRotation $b) => $b->getAxis(), + fn(PillarRotation $b, int $v) => $b->setAxis($v) + ); + + $this->torchFacing = new ValueFromStringProperty( + StateNames::TORCH_FACING_DIRECTION, + $vm->torchFacing, + fn(Torch $b) => $b->getFacing(), + fn(Torch $b, int $v) => $b->setFacing($v) + ); + + $this->horizontalFacingSWNE = new ValueFromIntProperty(StateNames::DIRECTION, $vm->horizontalFacingSWNE, $hfGet, $hfSet); + $this->horizontalFacingSWNEInverted = new ValueFromIntProperty(StateNames::DIRECTION, $vm->horizontalFacingSWNEInverted, $hfGet, $hfSet); + $this->horizontalFacingClassic = new ValueFromIntProperty(StateNames::FACING_DIRECTION, $vm->horizontalFacingClassic, $hfGet, $hfSet); + + $this->anyFacingClassic = new ValueFromIntProperty( + StateNames::FACING_DIRECTION, + $vm->facing, + fn(AnyFacing $b) => $b->getFacing(), + fn(AnyFacing $b, int $v) => $b->setFacing($v) + ); + + $this->multiFacingFlags = new OptionSetFromIntProperty( + StateNames::MULTI_FACE_DIRECTION_BITS, + IntFromRawStateMap::int([ + Facing::DOWN => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_DOWN, + Facing::UP => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_UP, + Facing::NORTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_NORTH, + Facing::SOUTH => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_SOUTH, + Facing::WEST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_WEST, + Facing::EAST => BlockLegacyMetadata::MULTI_FACE_DIRECTION_FLAG_EAST + ]), + fn(MultiFacing $b) => $b->getFaces(), + fn(MultiFacing $b, array $v) => $b->setFaces($v) + ); + + $this->floorSignLikeRotation = new IntProperty(StateNames::GROUND_SIGN_DIRECTION, 0, 15, fn(SignLikeRotation $b) => $b->getRotation(), fn(SignLikeRotation $b, int $v) => $b->setRotation($v)); + + $this->analogRedstoneSignal = new IntProperty(StateNames::REDSTONE_SIGNAL, 0, 15, fn(AnalogRedstoneSignalEmitter $b) => $b->getOutputSignalStrength(), fn(AnalogRedstoneSignalEmitter $b, int $v) => $b->setOutputSignalStrength($v)); + + $this->cropAgeMax7 = new IntProperty(StateNames::GROWTH, 0, 7, fn(Ageable $b) => $b->getAge(), fn(Ageable $b, int $v) => $b->setAge($v)); + $this->doublePlantHalf = new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePlant $b) => $b->isTop(), fn(DoublePlant $b, bool $v) => $b->setTop($v)); + + $fallingFlag = BlockLegacyMetadata::LIQUID_FALLING_FLAG; + $this->liquidData = new IntProperty( + StateNames::LIQUID_DEPTH, + 0, + 15, + fn(Liquid $b) => $b->getDecay() | ($b->isFalling() ? $fallingFlag : 0), + fn(Liquid $b, int $v) => $b->setDecay($v & ~$fallingFlag)->setFalling(($v & $fallingFlag) !== 0) + ); + + $this->lit = new BoolProperty(StateNames::LIT, fn(Lightable $b) => $b->isLit(), fn(Lightable $b, bool $v) => $b->setLit($v)); + + $this->dummyCardinalDirection = new DummyProperty(StateNames::MC_CARDINAL_DIRECTION, BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH); + $this->dummyPillarAxis = new DummyProperty(StateNames::PILLAR_AXIS, BlockStateStringValues::PILLAR_AXIS_Y); + + $this->dyeColorIdInfix = new ValueFromStringProperty("color", $vm->dyeColor, fn(Colored $b) => $b->getColor(), fn(Colored $b, DyeColor $v) => $b->setColor($v)); + $this->litIdInfix = new BoolFromStringProperty("lit", "", "lit_", fn(Lightable $b) => $b->isLit(), fn(Lightable $b, bool $v) => $b->setLit($v)); + + $this->slabIdInfix = new BoolFromStringProperty( + "double", + "", + "double_", + fn(Slab $b) => $b->getSlabType() === SlabType::DOUBLE, + + //we don't know this is actually a bottom slab yet but we don't have enough information to set the + //correct type in this handler + //BOTTOM serves as a signal value for the state deserializer to decide whether to ignore the + //upper_block_bit property + fn(Slab $b, bool $v) => $b->setSlabType($v ? SlabType::DOUBLE : SlabType::BOTTOM) + ); + $this->slabPositionProperty = new BoolFromStringProperty( + StateNames::MC_VERTICAL_HALF, + BlockStateStringValues::MC_VERTICAL_HALF_BOTTOM, + BlockStateStringValues::MC_VERTICAL_HALF_TOP, + fn(Slab $b) => $b->getSlabType() === SlabType::TOP, + + //Ignore the value for double slabs (should be set by ID component before this is reached) + fn(Slab $b, bool $v) => $b->getSlabType() !== SlabType::DOUBLE ? $b->setSlabType($v ? SlabType::TOP : SlabType::BOTTOM) : null + ); + + $this->coralIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("dead", "", "dead_", fn(CoralMaterial $b) => $b->isDead(), fn(CoralMaterial $b, bool $v) => $b->setDead($v)), + new ValueFromStringProperty("type", EnumFromRawStateMap::string(CoralType::class, fn(CoralType $case) => match ($case) { + CoralType::BRAIN => "brain", + CoralType::BUBBLE => "bubble", + CoralType::FIRE => "fire", + CoralType::HORN => "horn", + CoralType::TUBE => "tube" + }), fn(CoralMaterial $b) => $b->getCoralType(), fn(CoralMaterial $b, CoralType $v) => $b->setCoralType($v)), + ]; + $this->copperIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("waxed", "", "waxed_", fn(CopperMaterial $b) => $b->isWaxed(), fn(CopperMaterial $b, bool $v) => $b->setWaxed($v)), + new ValueFromStringProperty("oxidation", EnumFromRawStateMap::string(CopperOxidation::class, fn(CopperOxidation $case) => match ($case) { + CopperOxidation::NONE => "", + CopperOxidation::EXPOSED => "exposed_", + CopperOxidation::WEATHERED => "weathered_", + CopperOxidation::OXIDIZED => "oxidized_", + }), fn(CopperMaterial $b) => $b->getOxidation(), fn(CopperMaterial $b, CopperOxidation $v) => $b->setOxidation($v)) + ]; + + $this->furnaceIdPrefixes = ["minecraft:", $this->litIdInfix]; + + $this->liquidIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("still", "flowing_", "", fn(Liquid $b) => $b->isStill(), fn(Liquid $b, bool $v) => $b->setStill($v)) + ]; + + $this->woodIdPrefixes = [ + "minecraft:", + new BoolFromStringProperty("stripped", "", "stripped_", fn(Wood $b) => $b->isStripped(), fn(Wood $b, bool $v) => $b->setStripped($v)), + ]; + + $this->buttonProperties = [ + $this->anyFacingClassic, + new BoolProperty(StateNames::BUTTON_PRESSED_BIT, fn(Button $b) => $b->isPressed(), fn(Button $b, bool $v) => $b->setPressed($v)), + ]; + + $this->campfireProperties = [ + $this->horizontalFacingCardinal, + new BoolProperty(StateNames::EXTINGUISHED, fn(Lightable $b) => $b->isLit(), fn(Lightable $b, bool $v) => $b->setLit($v), inverted: true), + ]; + + //TODO: check if these need any special treatment to get the appropriate data to both halves of the door + $this->doorProperties = [ + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(Door $b) => $b->isTop(), fn(Door $b, bool $v) => $b->setTop($v)), + new BoolProperty(StateNames::DOOR_HINGE_BIT, fn(Door $b) => $b->isHingeRight(), fn(Door $b, bool $v) => $b->setHingeRight($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(Door $b) => $b->isOpen(), fn(Door $b, bool $v) => $b->setOpen($v)), + new ValueFromStringProperty( + StateNames::MC_CARDINAL_DIRECTION, + IntFromRawStateMap::string([ + //a door facing "east" is actually facing north - thanks mojang + Facing::NORTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_EAST, + Facing::EAST => BlockStateStringValues::MC_CARDINAL_DIRECTION_SOUTH, + Facing::SOUTH => BlockStateStringValues::MC_CARDINAL_DIRECTION_WEST, + Facing::WEST => BlockStateStringValues::MC_CARDINAL_DIRECTION_NORTH + ]), + fn(HorizontalFacing $b) => $b->getFacing(), + fn(HorizontalFacing $b, int $v) => $b->setFacing($v) + ) + ]; + + $this->fenceGateProperties = [ + new BoolProperty(StateNames::IN_WALL_BIT, fn(FenceGate $b) => $b->isInWall(), fn(FenceGate $b, bool $v) => $b->setInWall($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(FenceGate $b) => $b->isOpen(), fn(FenceGate $b, bool $v) => $b->setOpen($v)), + $this->horizontalFacingCardinal, + ]; + + $this->itemFrameProperties = [ + new DummyProperty(StateNames::ITEM_FRAME_PHOTO_BIT, false), //TODO: not sure what the point of this is + new BoolProperty(StateNames::ITEM_FRAME_MAP_BIT, fn(ItemFrame $b) => $b->hasMap(), fn(ItemFrame $b, bool $v) => $b->setHasMap($v)), + $this->anyFacingClassic + ]; + + $this->simplePressurePlateProperties = [ + //TODO: not sure what the deal is here ... seems like a mojang bug / artifact of bad implementation? + //best to keep this separate from weighted plates anyway... + new IntProperty( + StateNames::REDSTONE_SIGNAL, + 0, + 15, + fn(SimplePressurePlate $b) => $b->isPressed() ? 15 : 0, + fn(SimplePressurePlate $b, int $v) => $b->setPressed($v !== 0) + ) + ]; + + $this->stairProperties = [ + new BoolProperty(StateNames::UPSIDE_DOWN_BIT, fn(Stair $b) => $b->isUpsideDown(), fn(Stair $b, bool $v) => $b->setUpsideDown($v)), + new ValueFromIntProperty(StateNames::WEIRDO_DIRECTION, $vm->horizontalFacing5Minus, $hfGet, $hfSet), + ]; + + $this->stemProperties = [ + new ValueFromIntProperty(StateNames::FACING_DIRECTION, $vm->facingStem, fn(Stem $b) => $b->getFacing(), fn(Stem $b, int $v) => $b->setFacing($v)), + $this->cropAgeMax7 + ]; + + $this->trapdoorProperties = [ + //this uses the same values as stairs, but the state is named differently + new ValueFromIntProperty(StateNames::DIRECTION, $vm->horizontalFacing5Minus, $hfGet, $hfSet), + + new BoolProperty(StateNames::UPSIDE_DOWN_BIT, fn(Trapdoor $b) => $b->isTop(), fn(Trapdoor $b, bool $v) => $b->setTop($v)), + new BoolProperty(StateNames::OPEN_BIT, fn(Trapdoor $b) => $b->isOpen(), fn(Trapdoor $b, bool $v) => $b->setOpen($v)), + ]; + + $wallProperties = [ + new BoolProperty(StateNames::WALL_POST_BIT, fn(Wall $b) => $b->isPost(), fn(Wall $b, bool $v) => $b->setPost($v)), + ]; + foreach([ + Facing::NORTH => StateNames::WALL_CONNECTION_TYPE_NORTH, + Facing::SOUTH => StateNames::WALL_CONNECTION_TYPE_SOUTH, + Facing::WEST => StateNames::WALL_CONNECTION_TYPE_WEST, + Facing::EAST => StateNames::WALL_CONNECTION_TYPE_EAST + ] as $facing => $stateName){ + $wallProperties[] = new ValueFromStringProperty( + $stateName, + EnumFromRawStateMap::string(WallConnectionTypeShim::class, fn(WallConnectionTypeShim $case) => $case->getValue()), + fn(Wall $b) => WallConnectionTypeShim::serialize($b->getConnection($facing)), + fn(Wall $b, WallConnectionTypeShim $v) => $b->setConnection($facing, $v->deserialize()) + ); + } + $this->wallProperties = $wallProperties; + } +} diff --git a/src/data/bedrock/block/convert/property/DummyProperty.php b/src/data/bedrock/block/convert/property/DummyProperty.php new file mode 100644 index 000000000..a9d32b417 --- /dev/null +++ b/src/data/bedrock/block/convert/property/DummyProperty.php @@ -0,0 +1,61 @@ + + */ +final class DummyProperty implements Property{ + public function __construct( + private string $name, + private bool|int|string $value + ){} + + public function getName() : string{ + return $this->name; + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $in->ignored($this->name); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + if(is_bool($this->value)){ + $out->writeBool($this->name, $this->value); + }elseif(is_int($this->value)){ + $out->writeInt($this->name, $this->value); + }elseif(is_string($this->value)){ + $out->writeString($this->name, $this->value); + }else{ + throw new AssumptionFailedError(); + } + } +} diff --git a/src/data/bedrock/block/convert/property/EnumFromRawStateMap.php b/src/data/bedrock/block/convert/property/EnumFromRawStateMap.php new file mode 100644 index 000000000..c6f4d0516 --- /dev/null +++ b/src/data/bedrock/block/convert/property/EnumFromRawStateMap.php @@ -0,0 +1,109 @@ + + */ +class EnumFromRawStateMap implements StateMap{ + /** + * @var int[] + * @phpstan-var array + */ + private array $enumToValue = []; + + /** + * @var \UnitEnum[] + * @phpstan-var array + */ + private array $valueToEnum = []; + + /** + * @phpstan-param class-string $class + * @phpstan-param \Closure(TEnum) : TRaw $mapper + * @phpstan-param ?\Closure(TEnum) : list $aliasMapper + */ + public function __construct( + string $class, + \Closure $mapper, + ?\Closure $aliasMapper = null + ){ + foreach($class::cases() as $case){ + $int = $mapper($case); + $this->valueToEnum[$int] = $case; + $this->enumToValue[spl_object_id($case)] = $int; + + if($aliasMapper !== null){ + $aliases = $aliasMapper($case); + foreach($aliases as $alias){ + $this->valueToEnum[$alias] = $case; + } + } + } + } + + /** + * Workaround PHPStan too-specific literal type inference - if it ever gets fixed we can get rid of these functions + * + * @phpstan-template TEnum_ of \UnitEnum + * @phpstan-param class-string $class + * @param \Closure(TEnum_) : string $mapper + * @param ?\Closure(TEnum_) : list $aliasMapper + * + * @phpstan-return EnumFromRawStateMap + */ + public static function string(string $class, \Closure $mapper, ?\Closure $aliasMapper = null) : self{ return new self($class, $mapper, $aliasMapper); } + + /** + * Workaround PHPStan too-specific literal type inference - if it ever gets fixed we can get rid of these functions + * + * @phpstan-template TEnum_ of \UnitEnum + * @phpstan-param class-string $class + * @param \Closure(TEnum_) : int $mapper + * @param ?\Closure(TEnum_) : list $aliasMapper + * + * @phpstan-return EnumFromRawStateMap + */ + public static function int(string $class, \Closure $mapper, ?\Closure $aliasMapper = null) : self{ return new self($class, $mapper, $aliasMapper); } + + public function getRawToValueMap() : array{ + return $this->valueToEnum; + } + + public function valueToRaw(mixed $value) : int|string{ + return $this->enumToValue[spl_object_id($value)]; + } + + public function rawToValue(int|string $raw) : ?\UnitEnum{ + return $this->valueToEnum[$raw] ?? null; + } + + public function printableValue(mixed $value) : string{ + return $value::class . "::" . $value->name; + } +} diff --git a/src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php b/src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php new file mode 100644 index 000000000..979fc4751 --- /dev/null +++ b/src/data/bedrock/block/convert/property/FlattenedCaveVinesVariant.php @@ -0,0 +1,35 @@ + + */ +class IntFromRawStateMap implements StateMap{ + + /** + * @var int[] + * @phpstan-var array + */ + private array $deserializeMap; + + /** + * Constructs a bidirectional mapping, given a mapping of internal values -> serialized values, and an optional set + * of aliases per internal value (used for deserializing invalid serialized values). + * + * @param (int|string)[] $serializeMap + * @param (int|int[])|(string|string[]) $deserializeAliases + * + * @phpstan-param array $serializeMap + * @phpstan-param array> $deserializeAliases + */ + public function __construct( + private array $serializeMap, + array $deserializeAliases = [] + ){ + $this->deserializeMap = array_flip($this->serializeMap); + foreach($deserializeAliases as $pmValue => $mcValues){ + if(!is_array($mcValues)){ + $this->deserializeMap[$mcValues] = $pmValue; + }else{ + foreach($mcValues as $mcValue){ + $this->deserializeMap[$mcValue] = $pmValue; + } + } + } + } + + /** + * @param int[] $serializeMap + * @param (int|int[]) $deserializeAliases + * + * @phpstan-param array $serializeMap + * @phpstan-param array> $deserializeAliases + * + * @phpstan-return self + */ + public static function int(array $serializeMap, array $deserializeAliases = []) : self{ return new self($serializeMap, $deserializeAliases); } + + /** + * @param string[] $serializeMap + * @param (string|string[]) $deserializeAliases + * + * @phpstan-param array $serializeMap + * @phpstan-param array> $deserializeAliases + * + * @phpstan-return self + */ + public static function string(array $serializeMap, array $deserializeAliases = []) : self{ return new self($serializeMap, $deserializeAliases); } + + public function getRawToValueMap() : array{ + return $this->deserializeMap; + } + + public function valueToRaw(mixed $value) : int|string{ + return $this->serializeMap[$value]; + } + + public function rawToValue(int|string $raw) : mixed{ + return $this->deserializeMap[$raw] ?? null; + } + + public function printableValue(mixed $value) : string{ + return "$value"; + } +} diff --git a/src/data/bedrock/block/convert/property/IntProperty.php b/src/data/bedrock/block/convert/property/IntProperty.php new file mode 100644 index 000000000..bab865522 --- /dev/null +++ b/src/data/bedrock/block/convert/property/IntProperty.php @@ -0,0 +1,70 @@ + + */ +final class IntProperty implements Property{ + /** + * @phpstan-param \Closure(TBlock) : int $getter + * @phpstan-param \Closure(TBlock, int) : mixed $setter + */ + public function __construct( + private string $name, + private int $min, + private int $max, + private \Closure $getter, + private \Closure $setter, + private int $offset = 0 + ){ + if($min > $max){ + throw new \InvalidArgumentException("Min value cannot be greater than max value"); + } + } + + public function getName() : string{ return $this->name; } + + /** + * @phpstan-return self + */ + public static function unused(string $name, int $serializedValue) : self{ + return new self($name, Limits::INT32_MIN, Limits::INT32_MAX, fn() => $serializedValue, fn() => null); + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $value = $in->readBoundedInt($this->name, $this->min, $this->max); + ($this->setter)($block, $value + $this->offset); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $value = ($this->getter)($block); + $out->writeInt($this->name, $value - $this->offset); + } +} diff --git a/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php b/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php new file mode 100644 index 000000000..a91c681b8 --- /dev/null +++ b/src/data/bedrock/block/convert/property/OptionSetFromIntProperty.php @@ -0,0 +1,93 @@ + + */ +class OptionSetFromIntProperty implements Property{ + + private int $maxValue = 0; + + /** + * @phpstan-param StateMap $map + * @phpstan-param \Closure(TBlock) : array $getter + * @phpstan-param \Closure(TBlock, array) : mixed $setter + */ + public function __construct( + private string $name, + private StateMap $map, + private \Closure $getter, + private \Closure $setter + ){ + $flagsToCases = $this->map->getRawToValueMap(); + foreach($flagsToCases as $possibleFlag => $option){ + if(($this->maxValue & $possibleFlag) !== 0){ + foreach($flagsToCases as $otherFlag => $otherOption){ + if(($possibleFlag & $otherFlag) === $otherFlag && $otherOption !== $option){ + $printableOption = $this->map->printableValue($option); + $printableOtherOption = $this->map->printableValue($otherOption); + throw new \InvalidArgumentException("Flag for option $printableOption overlaps with flag for option $printableOtherOption in property $this->name"); + } + } + + throw new AssumptionFailedError("Unreachable"); + } + + $this->maxValue |= $possibleFlag; + } + } + + public function getName() : string{ return $this->name; } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $flags = $in->readBoundedInt($this->name, 0, $this->maxValue); + + $value = []; + foreach($this->map->getRawToValueMap() as $possibleFlag => $option){ + if(($flags & $possibleFlag) === $possibleFlag){ + $value[] = $option; + } + } + + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $flags = 0; + + $value = ($this->getter)($block); + foreach($value as $option){ + $flags |= $this->map->valueToRaw($option); + } + + $out->writeInt($this->name, $flags); + } +} diff --git a/src/data/bedrock/block/convert/property/Property.php b/src/data/bedrock/block/convert/property/Property.php new file mode 100644 index 000000000..30868dcd1 --- /dev/null +++ b/src/data/bedrock/block/convert/property/Property.php @@ -0,0 +1,44 @@ + + */ + public function getRawToValueMap() : array; + + /** + * @phpstan-param TValue $value + * @phpstan-return TRaw + */ + public function valueToRaw(mixed $value) : int|string; + + /** + * @phpstan-param TRaw $raw + * @phpstan-return TValue|null + */ + public function rawToValue(int|string $raw) : mixed; + + /** + * @phpstan-param TValue $value + */ + public function printableValue(mixed $value) : string; +} diff --git a/src/data/bedrock/block/convert/property/StringProperty.php b/src/data/bedrock/block/convert/property/StringProperty.php new file mode 100644 index 000000000..14ef1beff --- /dev/null +++ b/src/data/bedrock/block/convert/property/StringProperty.php @@ -0,0 +1,50 @@ + + */ +interface StringProperty extends Property{ + /** + * @return string[] + * @phpstan-return list + */ + public function getPossibleValues() : array; + + /** + * TODO: These are only used for flattened IDs for now, we should expand their use to all properties + * in the future and remove the dependencies on BlockStateReader and BlockStateWriter + * @phpstan-param TBlock $block + */ + public function deserializePlain(object $block, string $raw) : void; + + /** + * TODO: These are only used for flattened IDs for now, we should expand their use to all properties + * in the future and remove the dependencies on BlockStateReader and BlockStateWriter + * @phpstan-param TBlock $block + */ + public function serializePlain(object $block) : string; +} diff --git a/src/data/bedrock/block/convert/property/ValueFromIntProperty.php b/src/data/bedrock/block/convert/property/ValueFromIntProperty.php new file mode 100644 index 000000000..46b721255 --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueFromIntProperty.php @@ -0,0 +1,75 @@ + + */ +final class ValueFromIntProperty implements Property{ + + /** + * @phpstan-param StateMap $map + * @phpstan-param \Closure(TBlock) : TValue $getter + * @phpstan-param \Closure(TBlock, TValue) : mixed $setter + */ + public function __construct( + private string $name, + private StateMap $map, + private \Closure $getter, + private \Closure $setter + ){} + + public function getName() : string{ return $this->name; } + + /** + * @return int[] + * @phpstan-return list + */ + public function getPossibleValues() : array{ + return array_keys($this->map->getRawToValueMap()); + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $raw = $in->readInt($this->name); + $value = $this->map->rawToValue($raw); + + if($value === null){ + throw $in->badValueException($this->name, (string) $raw); + } + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $value = ($this->getter)($block); + $raw = $this->map->valueToRaw($value); + + $out->writeInt($this->name, $raw); + } +} diff --git a/src/data/bedrock/block/convert/property/ValueFromStringProperty.php b/src/data/bedrock/block/convert/property/ValueFromStringProperty.php new file mode 100644 index 000000000..b33634ae9 --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueFromStringProperty.php @@ -0,0 +1,81 @@ + + */ +final class ValueFromStringProperty implements StringProperty{ + + /** + * @phpstan-param StateMap $map + * @phpstan-param \Closure(TBlock) : TValue $getter + * @phpstan-param \Closure(TBlock, TValue) : mixed $setter + */ + public function __construct( + private string $name, + private StateMap $map, + private \Closure $getter, + private \Closure $setter + ){} + + public function getName() : string{ return $this->name; } + + /** + * @return string[] + * @phpstan-return list + */ + public function getPossibleValues() : array{ + //PHP sucks + return array_map(strval(...), array_keys($this->map->getRawToValueMap())); + } + + public function deserialize(object $block, BlockStateReader $in) : void{ + $this->deserializePlain($block, $in->readString($this->name)); + } + + public function deserializePlain(object $block, string $raw) : void{ + //TODO: duplicated code from BlockStateReader :( + $value = $this->map->rawToValue($raw) ?? throw new BlockStateDeserializeException("Property \"$this->name\" has invalid value \"$raw\""); + ($this->setter)($block, $value); + } + + public function serialize(object $block, BlockStateWriter $out) : void{ + $out->writeString($this->name, $this->serializePlain($block)); + } + + public function serializePlain(object $block) : string{ + $value = ($this->getter)($block); + return $this->map->valueToRaw($value); + } +} diff --git a/src/data/bedrock/block/convert/property/ValueMappings.php b/src/data/bedrock/block/convert/property/ValueMappings.php new file mode 100644 index 000000000..22e9803a5 --- /dev/null +++ b/src/data/bedrock/block/convert/property/ValueMappings.php @@ -0,0 +1,305 @@ + */ + public readonly EnumFromRawStateMap $dyeColor; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $dyeColorWithSilver; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $mobHeadType; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $froglightType; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $dirtType; + + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $dripleafState; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $bellAttachmentType; + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $leverFacing; + + /** @phpstan-var EnumFromRawStateMap */ + public readonly EnumFromRawStateMap $mushroomBlockType; + + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $cardinalDirection; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $blockFace; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $pillarAxis; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $torchFacing; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $portalAxis; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $bambooLeafSize; + + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacing5Minus; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingSWNE; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingSWNEInverted; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingCoral; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $horizontalFacingClassic; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facing; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingEndRod; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $coralAxis; + + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingExceptDown; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingExceptUp; + /** @phpstan-var IntFromRawStateMap */ + public readonly IntFromRawStateMap $facingStem; + + public function __construct(){ + //flattened ID components - we can't generate constants for these + $this->dyeColor = EnumFromRawStateMap::string(DyeColor::class, fn(DyeColor $case) => match ($case) { + DyeColor::BLACK => "black", + DyeColor::BLUE => "blue", + DyeColor::BROWN => "brown", + DyeColor::CYAN => "cyan", + DyeColor::GRAY => "gray", + DyeColor::GREEN => "green", + DyeColor::LIGHT_BLUE => "light_blue", + DyeColor::LIGHT_GRAY => "light_gray", + DyeColor::LIME => "lime", + DyeColor::MAGENTA => "magenta", + DyeColor::ORANGE => "orange", + DyeColor::PINK => "pink", + DyeColor::PURPLE => "purple", + DyeColor::RED => "red", + DyeColor::WHITE => "white", + DyeColor::YELLOW => "yellow" + }); + $this->dyeColorWithSilver = EnumFromRawStateMap::string(DyeColor::class, fn(DyeColor $case) => match ($case) { + DyeColor::LIGHT_GRAY => "silver", + default => $this->dyeColor->valueToRaw($case) + }); + + $this->mobHeadType = EnumFromRawStateMap::string(MobHeadType::class, fn(MobHeadType $case) => match ($case) { + MobHeadType::CREEPER => Ids::CREEPER_HEAD, + MobHeadType::DRAGON => Ids::DRAGON_HEAD, + MobHeadType::PIGLIN => Ids::PIGLIN_HEAD, + MobHeadType::PLAYER => Ids::PLAYER_HEAD, + MobHeadType::SKELETON => Ids::SKELETON_SKULL, + MobHeadType::WITHER_SKELETON => Ids::WITHER_SKELETON_SKULL, + MobHeadType::ZOMBIE => Ids::ZOMBIE_HEAD + }); + $this->froglightType = EnumFromRawStateMap::string(FroglightType::class, fn(FroglightType $case) => match ($case) { + FroglightType::OCHRE => Ids::OCHRE_FROGLIGHT, + FroglightType::PEARLESCENT => Ids::PEARLESCENT_FROGLIGHT, + FroglightType::VERDANT => Ids::VERDANT_FROGLIGHT, + }); + $this->dirtType = EnumFromRawStateMap::string(DirtType::class, fn(DirtType $case) => match ($case) { + DirtType::NORMAL => Ids::DIRT, + DirtType::COARSE => Ids::COARSE_DIRT, + DirtType::ROOTED => Ids::DIRT_WITH_ROOTS, + }); + + //state value mappings + $this->dripleafState = EnumFromRawStateMap::string(DripleafState::class, fn(DripleafState $case) => match ($case) { + DripleafState::STABLE => StringValues::BIG_DRIPLEAF_TILT_NONE, + DripleafState::UNSTABLE => StringValues::BIG_DRIPLEAF_TILT_UNSTABLE, + DripleafState::PARTIAL_TILT => StringValues::BIG_DRIPLEAF_TILT_PARTIAL_TILT, + DripleafState::FULL_TILT => StringValues::BIG_DRIPLEAF_TILT_FULL_TILT + }); + $this->bellAttachmentType = EnumFromRawStateMap::string(BellAttachmentType::class, fn(BellAttachmentType $case) => match ($case) { + BellAttachmentType::FLOOR => StringValues::ATTACHMENT_STANDING, + BellAttachmentType::CEILING => StringValues::ATTACHMENT_HANGING, + BellAttachmentType::ONE_WALL => StringValues::ATTACHMENT_SIDE, + BellAttachmentType::TWO_WALLS => StringValues::ATTACHMENT_MULTIPLE, + }); + $this->leverFacing = EnumFromRawStateMap::string(LeverFacing::class, fn(LeverFacing $case) => match ($case) { + LeverFacing::DOWN_AXIS_Z => StringValues::LEVER_DIRECTION_DOWN_NORTH_SOUTH, + LeverFacing::DOWN_AXIS_X => StringValues::LEVER_DIRECTION_DOWN_EAST_WEST, + LeverFacing::UP_AXIS_Z => StringValues::LEVER_DIRECTION_UP_NORTH_SOUTH, + LeverFacing::UP_AXIS_X => StringValues::LEVER_DIRECTION_UP_EAST_WEST, + LeverFacing::NORTH => StringValues::LEVER_DIRECTION_NORTH, + LeverFacing::SOUTH => StringValues::LEVER_DIRECTION_SOUTH, + LeverFacing::WEST => StringValues::LEVER_DIRECTION_WEST, + LeverFacing::EAST => StringValues::LEVER_DIRECTION_EAST + }); + + $this->mushroomBlockType = EnumFromRawStateMap::int( + MushroomBlockType::class, + fn(MushroomBlockType $case) => match ($case) { + MushroomBlockType::PORES => LegacyMeta::MUSHROOM_BLOCK_ALL_PORES, + MushroomBlockType::CAP_NORTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHWEST_CORNER, + MushroomBlockType::CAP_NORTH => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTH_SIDE, + MushroomBlockType::CAP_NORTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_NORTHEAST_CORNER, + MushroomBlockType::CAP_WEST => LegacyMeta::MUSHROOM_BLOCK_CAP_WEST_SIDE, + MushroomBlockType::CAP_MIDDLE => LegacyMeta::MUSHROOM_BLOCK_CAP_TOP_ONLY, + MushroomBlockType::CAP_EAST => LegacyMeta::MUSHROOM_BLOCK_CAP_EAST_SIDE, + MushroomBlockType::CAP_SOUTHWEST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHWEST_CORNER, + MushroomBlockType::CAP_SOUTH => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTH_SIDE, + MushroomBlockType::CAP_SOUTHEAST => LegacyMeta::MUSHROOM_BLOCK_CAP_SOUTHEAST_CORNER, + MushroomBlockType::ALL_CAP => LegacyMeta::MUSHROOM_BLOCK_ALL_CAP, + }, + fn(MushroomBlockType $case) => match ($case) { + MushroomBlockType::ALL_CAP => [11, 12, 13], + default => [] + } + ); + + $this->cardinalDirection = IntFromRawStateMap::string([ + Facing::NORTH => StringValues::MC_CARDINAL_DIRECTION_NORTH, + Facing::SOUTH => StringValues::MC_CARDINAL_DIRECTION_SOUTH, + Facing::WEST => StringValues::MC_CARDINAL_DIRECTION_WEST, + Facing::EAST => StringValues::MC_CARDINAL_DIRECTION_EAST, + ]); + $this->blockFace = IntFromRawStateMap::string([ + Facing::DOWN => StringValues::MC_BLOCK_FACE_DOWN, + Facing::UP => StringValues::MC_BLOCK_FACE_UP, + Facing::NORTH => StringValues::MC_BLOCK_FACE_NORTH, + Facing::SOUTH => StringValues::MC_BLOCK_FACE_SOUTH, + Facing::WEST => StringValues::MC_BLOCK_FACE_WEST, + Facing::EAST => StringValues::MC_BLOCK_FACE_EAST, + ]); + $this->pillarAxis = IntFromRawStateMap::string([ + Axis::X => StringValues::PILLAR_AXIS_X, + Axis::Y => StringValues::PILLAR_AXIS_Y, + Axis::Z => StringValues::PILLAR_AXIS_Z + ]); + $this->torchFacing = IntFromRawStateMap::string([ + //TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) + Facing::WEST => StringValues::TORCH_FACING_DIRECTION_EAST, + Facing::SOUTH => StringValues::TORCH_FACING_DIRECTION_NORTH, + Facing::NORTH => StringValues::TORCH_FACING_DIRECTION_SOUTH, + Facing::UP => StringValues::TORCH_FACING_DIRECTION_TOP, + Facing::EAST => StringValues::TORCH_FACING_DIRECTION_WEST, + ], deserializeAliases: [ + Facing::UP => StringValues::TORCH_FACING_DIRECTION_UNKNOWN //should be illegal, but still supported + ]); + $this->portalAxis = IntFromRawStateMap::string([ + Axis::X => StringValues::PORTAL_AXIS_X, + Axis::Z => StringValues::PORTAL_AXIS_Z, + ], deserializeAliases: [ + Axis::X => StringValues::PORTAL_AXIS_UNKNOWN, + ]); + $this->bambooLeafSize = IntFromRawStateMap::string([ + Bamboo::NO_LEAVES => StringValues::BAMBOO_LEAF_SIZE_NO_LEAVES, + Bamboo::SMALL_LEAVES => StringValues::BAMBOO_LEAF_SIZE_SMALL_LEAVES, + Bamboo::LARGE_LEAVES => StringValues::BAMBOO_LEAF_SIZE_LARGE_LEAVES, + ]); + + $this->horizontalFacing5Minus = IntFromRawStateMap::int([ + Facing::EAST => 0, + Facing::WEST => 1, + Facing::SOUTH => 2, + Facing::NORTH => 3 + ]); + $this->horizontalFacingSWNE = IntFromRawStateMap::int([ + Facing::SOUTH => 0, + Facing::WEST => 1, + Facing::NORTH => 2, + Facing::EAST => 3 + ]); + $this->horizontalFacingSWNEInverted = IntFromRawStateMap::int([ + Facing::NORTH => 0, + Facing::EAST => 1, + Facing::SOUTH => 2, + Facing::WEST => 3, + ]); + $this->horizontalFacingCoral = IntFromRawStateMap::int([ + Facing::WEST => 0, + Facing::EAST => 1, + Facing::NORTH => 2, + Facing::SOUTH => 3 + ]); + $horizontalFacingClassicTable = [ + Facing::NORTH => 2, + Facing::SOUTH => 3, + Facing::WEST => 4, + Facing::EAST => 5 + ]; + $this->horizontalFacingClassic = IntFromRawStateMap::int($horizontalFacingClassicTable, deserializeAliases: [ + Facing::NORTH => [0, 1] //should be illegal but still technically possible + ]); + + $this->facing = IntFromRawStateMap::int([ + Facing::DOWN => 0, + Facing::UP => 1 + ] + $horizontalFacingClassicTable); + + //end rods have all the horizontal facing values opposite to classic facing + $this->facingEndRod = IntFromRawStateMap::int([ + Facing::DOWN => 0, + Facing::UP => 1, + Facing::SOUTH => 2, + Facing::NORTH => 3, + Facing::EAST => 4, + Facing::WEST => 5, + ]); + + $this->coralAxis = IntFromRawStateMap::int([ + Axis::X => 0, + Axis::Z => 1, + ]); + + //TODO: shitty copy pasta job, we can do this better but this is good enough for now + $this->facingExceptDown = IntFromRawStateMap::int( + [Facing::UP => 1] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP => 0]); + $this->facingExceptUp = IntFromRawStateMap::int( + [Facing::DOWN => 0] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::DOWN => 1] + ); + + //In PM, we use Facing::UP to indicate that the stem is not attached to a pumpkin/melon, since this makes the + //most intuitive sense (the stem is pointing at the sky). However, Bedrock uses the DOWN state for this, which + //is absurd, and I refuse to make our API similarly absurd. + $this->facingStem = IntFromRawStateMap::int( + [Facing::UP => 0] + $horizontalFacingClassicTable, + deserializeAliases: [Facing::UP => 1] + ); + } +} diff --git a/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php new file mode 100644 index 000000000..bdd878b52 --- /dev/null +++ b/src/data/bedrock/block/convert/property/WallConnectionTypeShim.php @@ -0,0 +1,66 @@ + BlockStateStringValues::WALL_CONNECTION_TYPE_EAST_NONE, + self::SHORT => BlockStateStringValues::WALL_CONNECTION_TYPE_EAST_SHORT, + self::TALL => BlockStateStringValues::WALL_CONNECTION_TYPE_EAST_TALL, + }; + } + + public function deserialize() : ?WallConnectionType{ + return match($this){ + self::NONE => null, + self::SHORT => WallConnectionType::SHORT, + self::TALL => WallConnectionType::TALL, + }; + } + + public static function serialize(?WallConnectionType $value) : self{ + return match($value){ + null => self::NONE, + WallConnectionType::SHORT => self::SHORT, + WallConnectionType::TALL => self::TALL, + }; + } +} diff --git a/src/world/format/io/GlobalBlockStateHandlers.php b/src/world/format/io/GlobalBlockStateHandlers.php index c1d3934cf..731b15f74 100644 --- a/src/world/format/io/GlobalBlockStateHandlers.php +++ b/src/world/format/io/GlobalBlockStateHandlers.php @@ -26,7 +26,9 @@ namespace pocketmine\world\format\io; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockTypeNames; use pocketmine\data\bedrock\block\convert\BlockObjectToStateSerializer; +use pocketmine\data\bedrock\block\convert\BlockSerializerDeserializerRegistrar; use pocketmine\data\bedrock\block\convert\BlockStateToObjectDeserializer; +use pocketmine\data\bedrock\block\convert\VanillaBlockMappings; use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockIdMetaUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader; @@ -44,20 +46,28 @@ use const pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH; * benefits for now. */ final class GlobalBlockStateHandlers{ - private static ?BlockObjectToStateSerializer $blockStateSerializer = null; - - private static ?BlockStateToObjectDeserializer $blockStateDeserializer = null; - private static ?BlockDataUpgrader $blockDataUpgrader = null; private static ?BlockStateData $unknownBlockStateData = null; + private static ?BlockSerializerDeserializerRegistrar $registrar = null; + + public static function getRegistrar() : BlockSerializerDeserializerRegistrar{ + if(self::$registrar === null){ + $deserializer = new BlockStateToObjectDeserializer(); + $serializer = new BlockObjectToStateSerializer(); + self::$registrar = new BlockSerializerDeserializerRegistrar($deserializer, $serializer); + VanillaBlockMappings::init(self::$registrar); + } + return self::$registrar; + } + public static function getDeserializer() : BlockStateToObjectDeserializer{ - return self::$blockStateDeserializer ??= new BlockStateToObjectDeserializer(); + return self::getRegistrar()->deserializer; } public static function getSerializer() : BlockObjectToStateSerializer{ - return self::$blockStateSerializer ??= new BlockObjectToStateSerializer(); + return self::getRegistrar()->serializer; } public static function getUpgrader() : BlockDataUpgrader{ diff --git a/tests/phpstan/configs/actual-problems.neon b/tests/phpstan/configs/actual-problems.neon index 2d609ae2c..06abe7fee 100644 --- a/tests/phpstan/configs/actual-problems.neon +++ b/tests/phpstan/configs/actual-problems.neon @@ -600,6 +600,12 @@ parameters: count: 1 path: ../../../src/crash/CrashDumpRenderer.php + - + message: '#^Parameter \#1 \$faces of method pocketmine\\block\\Vine\:\:setFaces\(\) expects list\<2\|3\|4\|5\>, array\ given\.$#' + identifier: argument.type + count: 1 + path: ../../../src/data/bedrock/block/convert/VanillaBlockMappings.php + - message: '#^Parameter \#1 \$blockToItemId of class pocketmine\\data\\bedrock\\item\\BlockItemIdMap constructor expects array\, array\ given\.$#' identifier: argument.type diff --git a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php index a47a9b155..674ae8152 100644 --- a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php +++ b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php @@ -42,6 +42,8 @@ final class BlockSerializerDeserializerTest extends TestCase{ public function setUp() : void{ $this->deserializer = new BlockStateToObjectDeserializer(); $this->serializer = new BlockObjectToStateSerializer(); + $registrar = new BlockSerializerDeserializerRegistrar($this->deserializer, $this->serializer); + VanillaBlockMappings::init($registrar); } public function testAllKnownBlockStatesSerializableAndDeserializable() : void{ @@ -49,12 +51,12 @@ final class BlockSerializerDeserializerTest extends TestCase{ try{ $blockStateData = $this->serializer->serializeBlock($block); }catch(BlockStateSerializeException $e){ - self::fail($e->getMessage()); + self::fail("Failed to serialize " . $block->getName() . ": " . $e->getMessage()); } try{ $newBlock = $this->deserializer->deserializeBlock($blockStateData); }catch(BlockStateDeserializeException $e){ - self::fail($e->getMessage()); + self::fail("Failed to deserialize " . $blockStateData->getName() . ": " . $e->getMessage() . " with data " . $blockStateData->toNbt()); } if($block->getTypeId() === BlockTypeIds::POTION_CAULDRON){ From 8f9478e82fec72a8b9f75a90882a278af0ab3a08 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 15:31:10 +0100 Subject: [PATCH 02/11] Illager banners finally working closes #2951 --- src/block/BaseBanner.php | 6 ++ src/block/BaseOminousBanner.php | 85 +++++++++++++++++++ src/block/BlockTypeIds.php | 4 +- src/block/FloorBanner.php | 4 + src/block/OminousFloorBanner.php | 53 ++++++++++++ src/block/OminousWallBanner.php | 49 +++++++++++ src/block/VanillaBlocks.php | 4 + src/block/WallBanner.php | 4 + src/block/tile/Banner.php | 14 +++ .../block/convert/VanillaBlockMappings.php | 19 ++++- .../ItemSerializerDeserializerRegistrar.php | 25 ++++-- src/item/ItemTypeIds.php | 3 +- src/item/VanillaItems.php | 2 + 13 files changed, 260 insertions(+), 12 deletions(-) create mode 100644 src/block/BaseOminousBanner.php create mode 100644 src/block/OminousFloorBanner.php create mode 100644 src/block/OminousWallBanner.php diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index 376f1f9dc..71a892c20 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -50,6 +50,10 @@ abstract class BaseBanner extends Transparent implements Colored{ parent::readStateFromWorld(); $tile = $this->position->getWorld()->getTile($this->position); if($tile instanceof TileBanner){ + if($tile->getType() === TileBanner::TYPE_OMINOUS){ + //illager banner is implemented as a separate block, as it doesn't support base color or custom patterns + return $this->getOminousVersion(); + } $this->color = $tile->getBaseColor(); $this->setPatterns($tile->getPatterns()); } @@ -57,6 +61,8 @@ abstract class BaseBanner extends Transparent implements Colored{ return $this; } + abstract protected function getOminousVersion() : Block; + public function writeStateToWorld() : void{ parent::writeStateToWorld(); $tile = $this->position->getWorld()->getTile($this->position); diff --git a/src/block/BaseOminousBanner.php b/src/block/BaseOminousBanner.php new file mode 100644 index 000000000..ef2ef9c9a --- /dev/null +++ b/src/block/BaseOminousBanner.php @@ -0,0 +1,85 @@ +position->getWorld()->getTile($this->position); + assert($tile instanceof TileBanner); + $tile->setBaseColor(DyeColor::WHITE); + $tile->setPatterns([]); + $tile->setType(TileBanner::TYPE_OMINOUS); + } + + public function isSolid() : bool{ + return false; + } + + public function getMaxStackSize() : int{ + return 16; + } + + public function getFuelTime() : int{ + return 300; + } + + protected function recalculateCollisionBoxes() : array{ + return []; + } + + public function getSupportType(int $facing) : SupportType{ + return SupportType::NONE; + } + + private function canBeSupportedBy(Block $block) : bool{ + return $block->isSolid(); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(!$this->canBeSupportedBy($blockReplace->getSide($this->getSupportingFace()))){ + return false; + } + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + abstract protected function getSupportingFace() : int; + + public function onNearbyBlockChange() : void{ + if(!$this->canBeSupportedBy($this->getSide($this->getSupportingFace()))){ + $this->position->getWorld()->useBreakOn($this->position); + } + } +} diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index 4af1894bd..52b141bcf 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -787,8 +787,10 @@ final class BlockTypeIds{ public const RESIN_CLUMP = 10757; public const CHISELED_RESIN_BRICKS = 10758; public const RESPAWN_ANCHOR = 10759; + public const OMINOUS_BANNER = 10760; + public const OMINOUS_WALL_BANNER = 10761; - public const FIRST_UNUSED_BLOCK_ID = 10760; + public const FIRST_UNUSED_BLOCK_ID = 10762; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/FloorBanner.php b/src/block/FloorBanner.php index ba089b6c0..ff57b5973 100644 --- a/src/block/FloorBanner.php +++ b/src/block/FloorBanner.php @@ -34,6 +34,10 @@ use pocketmine\world\BlockTransaction; final class FloorBanner extends BaseBanner implements SignLikeRotation{ use SignLikeRotationTrait; + protected function getOminousVersion() : Block{ + return VanillaBlocks::OMINOUS_BANNER()->setRotation($this->rotation); + } + protected function getSupportingFace() : int{ return Facing::DOWN; } diff --git a/src/block/OminousFloorBanner.php b/src/block/OminousFloorBanner.php new file mode 100644 index 000000000..240aeccfc --- /dev/null +++ b/src/block/OminousFloorBanner.php @@ -0,0 +1,53 @@ +rotation = self::getRotationFromYaw($player->getLocation()->getYaw()); + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/OminousWallBanner.php b/src/block/OminousWallBanner.php new file mode 100644 index 000000000..1eb5ba753 --- /dev/null +++ b/src/block/OminousWallBanner.php @@ -0,0 +1,49 @@ +facing); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(Facing::axis($face) === Axis::Y){ + return false; + } + $this->facing = $face; + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 54ec27fc2..3255c6f6f 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -587,6 +587,8 @@ use function strtolower; * @method static WallSign OAK_WALL_SIGN() * @method static Wood OAK_WOOD() * @method static Opaque OBSIDIAN() + * @method static OminousFloorBanner OMINOUS_BANNER() + * @method static OminousWallBanner OMINOUS_WALL_BANNER() * @method static Flower ORANGE_TULIP() * @method static Flower OXEYE_DAISY() * @method static PackedIce PACKED_ICE() @@ -873,6 +875,8 @@ final class VanillaBlocks{ $bannerBreakInfo = new Info(BreakInfo::axe(1.0)); self::register("banner", fn(BID $id) => new FloorBanner($id, "Banner", $bannerBreakInfo), TileBanner::class); self::register("wall_banner", fn(BID $id) => new WallBanner($id, "Wall Banner", $bannerBreakInfo), TileBanner::class); + self::register("ominous_banner", fn(BID $id) => new OminousFloorBanner($id, "Ominous Banner", $bannerBreakInfo), TileBanner::class); + self::register("ominous_wall_banner", fn(BID $id) => new OminousWallBanner($id, "Ominous Wall Banner", $bannerBreakInfo), TileBanner::class); self::register("barrel", fn(BID $id) => new Barrel($id, "Barrel", new Info(BreakInfo::axe(2.5))), TileBarrel::class); self::register("barrier", fn(BID $id) => new Transparent($id, "Barrier", new Info(BreakInfo::indestructible()))); self::register("beacon", fn(BID $id) => new Beacon($id, "Beacon", new Info(new BreakInfo(3.0))), TileBeacon::class); diff --git a/src/block/WallBanner.php b/src/block/WallBanner.php index ddb157cda..b631e0c81 100644 --- a/src/block/WallBanner.php +++ b/src/block/WallBanner.php @@ -35,6 +35,10 @@ use pocketmine\world\BlockTransaction; final class WallBanner extends BaseBanner implements HorizontalFacing{ use HorizontalFacingTrait; + protected function getOminousVersion() : Block{ + return VanillaBlocks::OMINOUS_WALL_BANNER()->setFacing($this->facing); + } + protected function getSupportingFace() : int{ return Facing::opposite($this->facing); } diff --git a/src/block/tile/Banner.php b/src/block/tile/Banner.php index 08a560707..97ffe630d 100644 --- a/src/block/tile/Banner.php +++ b/src/block/tile/Banner.php @@ -41,6 +41,10 @@ class Banner extends Spawnable{ public const TAG_PATTERNS = "Patterns"; public const TAG_PATTERN_COLOR = "Color"; public const TAG_PATTERN_NAME = "Pattern"; + public const TAG_TYPE = "Type"; + + public const TYPE_NORMAL = 0; + public const TYPE_OMINOUS = 1; private DyeColor $baseColor = DyeColor::BLACK; @@ -50,6 +54,8 @@ class Banner extends Spawnable{ */ private array $patterns = []; + private int $type = self::TYPE_NORMAL; + public function readSaveData(CompoundTag $nbt) : void{ $colorIdMap = DyeColorIdMap::getInstance(); if( @@ -75,6 +81,8 @@ class Banner extends Spawnable{ $this->patterns[] = new BannerPatternLayer($patternType, $patternColor); } } + + $this->type = $nbt->getInt(self::TAG_TYPE); } protected function writeSaveData(CompoundTag $nbt) : void{ @@ -89,6 +97,7 @@ class Banner extends Spawnable{ ); } $nbt->setTag(self::TAG_PATTERNS, $patterns); + $nbt->setInt(self::TAG_TYPE, $this->type); } protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ @@ -103,6 +112,7 @@ class Banner extends Spawnable{ ); } $nbt->setTag(self::TAG_PATTERNS, $patterns); + $nbt->setInt(self::TAG_TYPE, $this->type); } /** @@ -136,6 +146,10 @@ class Banner extends Spawnable{ $this->patterns = $patterns; } + public function getType() : int{ return $this->type; } + + public function setType(int $type) : void{ $this->type = $type; } + public function getDefaultName() : string{ return "Banner"; } diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index 16ae1e244..3dfa81644 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -55,6 +55,7 @@ use pocketmine\block\Farmland; use pocketmine\block\FillableCauldron; use pocketmine\block\Fire; use pocketmine\block\FloorCoralFan; +use pocketmine\block\OminousFloorBanner; use pocketmine\block\Froglight; use pocketmine\block\FrostedIce; use pocketmine\block\GlazedTerracotta; @@ -103,6 +104,7 @@ use pocketmine\block\utils\MushroomBlockType; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Vine; +use pocketmine\block\OminousWallBanner; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; @@ -154,7 +156,7 @@ final class VanillaBlockMappings{ self::registerChemistryMappings($reg, $commonProperties); self::register1to1CustomMappings($reg, $commonProperties); - self::registerSplitMappings($reg); + self::registerSplitMappings($reg, $commonProperties); } private static function registerSimpleIdOnlyMappings(BlockSerializerDeserializerRegistrar $reg) : void{ @@ -1476,7 +1478,7 @@ final class VanillaBlockMappings{ * These currently can't be registered in a unified way, and due to their small number it may not be worth the * effort to implement a unified way to deal with them */ - private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg) : void{ + private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ //big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state $reg->serializer->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ return Writer::create(Ids::BIG_DRIPLEAF) @@ -1557,5 +1559,18 @@ final class VanillaBlockMappings{ ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) ->setTop($top); }); + + //these only exist within PM (mapped from tile properties) as they don't support the same properties as a + //normal banner + $reg->serializer->map(Blocks::OMINOUS_BANNER(), function(OminousFloorBanner $block) use ($commonProperties) : Writer{ + $writer = Writer::create(Ids::STANDING_BANNER); + $commonProperties->floorSignLikeRotation->serialize($block, $writer); + return $writer; + }); + $reg->serializer->map(Blocks::OMINOUS_WALL_BANNER(), function(OminousWallBanner $block) use ($commonProperties) : Writer{ + $writer = Writer::create(Ids::WALL_BANNER); + $commonProperties->horizontalFacingClassic->serialize($block, $writer); + return $writer; + }); } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 771154d46..f176351b7 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -26,6 +26,7 @@ namespace pocketmine\data\bedrock\item; use pocketmine\block\Bed; use pocketmine\block\Block; use pocketmine\block\CopperDoor; +use pocketmine\block\tile\Banner as TileBanner; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\DyeColor; use pocketmine\block\VanillaBlocks as Blocks; @@ -46,6 +47,7 @@ use pocketmine\item\Potion; use pocketmine\item\SplashPotion; use pocketmine\item\SuspiciousStew; use pocketmine\item\VanillaItems as Items; +use pocketmine\nbt\tag\CompoundTag; final class ItemSerializerDeserializerRegistrar{ @@ -487,14 +489,6 @@ final class ItemSerializerDeserializerRegistrar{ * in a unified manner. */ private function register1to1ItemWithMetaMappings() : void{ - $this->map1to1ItemWithMeta( - Ids::BANNER, - Items::BANNER(), - function(Banner $item, int $meta) : void{ - $item->setColor(DyeColorIdMap::getInstance()->fromInvertedId($meta) ?? throw new ItemTypeDeserializeException("Unknown banner meta $meta")); - }, - fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor()) - ); $this->map1to1ItemWithMeta( Ids::GOAT_HORN, Items::GOAT_HORN(), @@ -550,6 +544,21 @@ final class ItemSerializerDeserializerRegistrar{ $this->deserializer?->map($id, fn() => Items::DYE()->setColor($color)); } $this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor()))); + + $this->deserializer?->map(Ids::BANNER, function(Data $data) : Item{ + $type = $data->getTag()?->getInt(TileBanner::TAG_TYPE) ?? TileBanner::TYPE_NORMAL; + if($type === TileBanner::TYPE_OMINOUS){ + return Items::OMINOUS_BANNER(); + } + $color = DyeColorIdMap::getInstance()->fromInvertedId($data->getMeta()) ?? throw new ItemTypeDeserializeException("Unknown banner meta " . $data->getMeta()); + return Items::BANNER()->setColor($color); + }); + $this->serializer?->map(Items::OMINOUS_BANNER(), fn() => new Data(Ids::BANNER, tag: CompoundTag::create() + ->setInt(TileBanner::TAG_TYPE, TileBanner::TYPE_OMINOUS)) + ); + $this->serializer?->map(Items::BANNER(), function(Banner $item) : Data{ + return new Data(Ids::BANNER, DyeColorIdMap::getInstance()->toInvertedId($item->getColor())); + }); } /** diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index c63046c6b..37be3ab9e 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -334,8 +334,9 @@ final class ItemTypeIds{ public const RECORD_CREATOR = 20295; public const RECORD_CREATOR_MUSIC_BOX = 20296; public const RECORD_PRECIPICE = 20297; + public const OMINOUS_BANNER = 20298; - public const FIRST_UNUSED_ITEM_ID = 20298; + public const FIRST_UNUSED_ITEM_ID = 20299; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index f76cf369f..7103d8878 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -242,6 +242,7 @@ use function strtolower; * @method static Item NETHER_STAR() * @method static Boat OAK_BOAT() * @method static ItemBlockWallOrFloor OAK_SIGN() + * @method static ItemBlockWallOrFloor OMINOUS_BANNER() * @method static PaintingItem PAINTING() * @method static ItemBlockWallOrFloor PALE_OAK_SIGN() * @method static Item PAPER() @@ -540,6 +541,7 @@ final class VanillaItems{ public function isFireProof() : bool{ return true; } }); self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); + self::register("ominous_banner", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OMINOUS_BANNER(), Blocks::OMINOUS_WALL_BANNER())); self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting")); self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN())); self::register("paper", fn(IID $id) => new Item($id, "Paper")); From ef53676a596dd581efa1792e7252bdc43214ffb3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 15:38:07 +0100 Subject: [PATCH 03/11] Fix unit tests --- src/block/BaseOminousBanner.php | 5 +++++ .../bedrock/item/ItemSerializerDeserializerRegistrar.php | 2 +- tests/phpunit/block/block_factory_consistency_check.json | 4 ++++ .../block/convert/BlockSerializerDeserializerTest.php | 9 +++++++-- 4 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/block/BaseOminousBanner.php b/src/block/BaseOminousBanner.php index ef2ef9c9a..192e6fac2 100644 --- a/src/block/BaseOminousBanner.php +++ b/src/block/BaseOminousBanner.php @@ -27,6 +27,7 @@ use pocketmine\block\tile\Banner as TileBanner; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\world\BlockTransaction; @@ -82,4 +83,8 @@ abstract class BaseOminousBanner extends Transparent{ $this->position->getWorld()->useBreakOn($this->position); } } + + public function asItem() : Item{ + return VanillaItems::OMINOUS_BANNER(); + } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index f176351b7..fa6f88483 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -546,7 +546,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->serializer?->map(Items::DYE(), fn(Dye $item) => new Data(DyeColorIdMap::getInstance()->toItemId($item->getColor()))); $this->deserializer?->map(Ids::BANNER, function(Data $data) : Item{ - $type = $data->getTag()?->getInt(TileBanner::TAG_TYPE) ?? TileBanner::TYPE_NORMAL; + $type = $data->getTag()?->getInt(TileBanner::TAG_TYPE, TileBanner::TYPE_NORMAL) ?? TileBanner::TYPE_NORMAL; if($type === TileBanner::TYPE_OMINOUS){ return Items::OMINOUS_BANNER(); } diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index c7629656d..86a44113f 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -508,6 +508,8 @@ "OAK_WALL_SIGN": 4, "OAK_WOOD": 6, "OBSIDIAN": 1, + "OMINOUS_BANNER": 16, + "OMINOUS_WALL_BANNER": 4, "ORANGE_TULIP": 1, "OXEYE_DAISY": 1, "PACKED_ICE": 1, @@ -776,6 +778,8 @@ "NOTE_BLOCK": "pocketmine\\block\\tile\\Note", "OAK_SIGN": "pocketmine\\block\\tile\\Sign", "OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "OMINOUS_BANNER": "pocketmine\\block\\tile\\Banner", + "OMINOUS_WALL_BANNER": "pocketmine\\block\\tile\\Banner", "PALE_OAK_SIGN": "pocketmine\\block\\tile\\Sign", "PALE_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "POTION_CAULDRON": "pocketmine\\block\\tile\\Cauldron", diff --git a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php index 674ae8152..c0f850027 100644 --- a/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php +++ b/tests/phpunit/data/bedrock/block/convert/BlockSerializerDeserializerTest.php @@ -59,8 +59,13 @@ final class BlockSerializerDeserializerTest extends TestCase{ self::fail("Failed to deserialize " . $blockStateData->getName() . ": " . $e->getMessage() . " with data " . $blockStateData->toNbt()); } - if($block->getTypeId() === BlockTypeIds::POTION_CAULDRON){ - //this pretends to be a water cauldron in the blockstate, and stores its actual data in the blockentity + if(match ($block->getTypeId()) { + BlockTypeIds::POTION_CAULDRON, + BlockTypeIds::OMINOUS_BANNER, + BlockTypeIds::OMINOUS_WALL_BANNER => true, + default => false + }){ + //these pretend to be something else in the blockstate, and the variant switching is done via block entity data continue; } From 5bf0cbec875f8cbc7f6f2ef37433837041e7f3c4 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 15:39:23 +0100 Subject: [PATCH 04/11] ... --- src/data/bedrock/block/convert/VanillaBlockMappings.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index 3dfa81644..4438b01bf 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -55,7 +55,6 @@ use pocketmine\block\Farmland; use pocketmine\block\FillableCauldron; use pocketmine\block\Fire; use pocketmine\block\FloorCoralFan; -use pocketmine\block\OminousFloorBanner; use pocketmine\block\Froglight; use pocketmine\block\FrostedIce; use pocketmine\block\GlazedTerracotta; @@ -69,6 +68,8 @@ use pocketmine\block\MobHead; use pocketmine\block\NetherPortal; use pocketmine\block\NetherVines; use pocketmine\block\NetherWartPlant; +use pocketmine\block\OminousFloorBanner; +use pocketmine\block\OminousWallBanner; use pocketmine\block\PinkPetals; use pocketmine\block\PitcherCrop; use pocketmine\block\PoweredRail; @@ -104,7 +105,6 @@ use pocketmine\block\utils\MushroomBlockType; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\VanillaBlocks as Blocks; use pocketmine\block\Vine; -use pocketmine\block\OminousWallBanner; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; From 4cdf064344a6aba4d0136865508ffadb3c9bce03 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 16:37:42 +0100 Subject: [PATCH 05/11] VanillaBlockMappings: Use some model mappings this way there are some minor symmetry benefits, and the only asymmetric parts are the code that selects which model to use. it also has the added benefit of removing some duplicated code paths (e.g. now it's possible to get rid of readUnitEnum() and such). --- .../block/convert/VanillaBlockMappings.php | 185 ++++++++++-------- 1 file changed, 106 insertions(+), 79 deletions(-) diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index 4438b01bf..f70de9f4c 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -96,6 +96,7 @@ use pocketmine\block\utils\BrewingStandSlot; use pocketmine\block\utils\ChiseledBookshelfSlot; use pocketmine\block\utils\CopperOxidation; use pocketmine\block\utils\DirtType; +use pocketmine\block\utils\DripleafState; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\FroglightType; use pocketmine\block\utils\HorizontalFacing; @@ -1472,6 +1473,35 @@ final class VanillaBlockMappings{ $reg->mapModel(Model::create(Blocks::WEIGHTED_PRESSURE_PLATE_LIGHT(), Ids::LIGHT_WEIGHTED_PRESSURE_PLATE)->properties([$commonProperties->analogRedstoneSignal])); } + /** + * @phpstan-template TBlock of Block + * @phpstan-param Model $model + */ + private static function mapAsymmetricSerializer(BlockSerializerDeserializerRegistrar $reg, Model $model) : void{ + $id = $model->getId(); + $properties = $model->getProperties(); + $reg->serializer->map($model->getBlock(), function(Block $block) use ($id, $properties) : Writer{ + $writer = new Writer($id); + foreach($properties as $property){ + $property->serialize($block, $writer); + } + return $writer; + }); + } + + /** + * @phpstan-template TBlock of Block + * @phpstan-param Model $model + * @phpstan-return TBlock + */ + private static function deserializeAsymmetric(Model $model, Reader $in) : Block{ + $block = clone $model->getBlock(); + foreach($model->getProperties() as $property){ + $property->deserialize($block, $in); + } + return $block; + } + /** * All mappings that still use the split form of serializer/deserializer registration * This is typically only used by blocks with one ID but multiple PM types (split by property) @@ -1480,97 +1510,94 @@ final class VanillaBlockMappings{ */ private static function registerSplitMappings(BlockSerializerDeserializerRegistrar $reg, CommonProperties $commonProperties) : void{ //big dripleaf - split into head / stem variants, as stems don't have tilt or leaf state - $reg->serializer->map(Blocks::BIG_DRIPLEAF_HEAD(), function(BigDripleafHead $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState, $block->getLeafState()) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, true); - }); - $reg->serializer->map(Blocks::BIG_DRIPLEAF_STEM(), function(BigDripleafStem $block) : Writer{ - return Writer::create(Ids::BIG_DRIPLEAF) - ->writeCardinalHorizontalFacing($block->getFacing()) - ->writeString(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE) - ->writeBool(StateNames::BIG_DRIPLEAF_HEAD, false); - }); - $reg->deserializer->map(Ids::BIG_DRIPLEAF, function(Reader $in) : Block{ - if($in->readBool(StateNames::BIG_DRIPLEAF_HEAD)){ - return Blocks::BIG_DRIPLEAF_HEAD() - ->setFacing($in->readCardinalHorizontalFacing()) - ->setLeafState($in->readUnitEnum(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState)); - }else{ - $in->ignored(StateNames::BIG_DRIPLEAF_TILT); - return Blocks::BIG_DRIPLEAF_STEM()->setFacing($in->readCardinalHorizontalFacing()); - } - }); + $bigDripleafHeadModel = Model::create(Blocks::BIG_DRIPLEAF_HEAD(), Ids::BIG_DRIPLEAF)->properties([ + $commonProperties->horizontalFacingCardinal, + new ValueFromStringProperty(StateNames::BIG_DRIPLEAF_TILT, ValueMappings::getInstance()->dripleafState, fn(BigDripleafHead $b) => $b->getLeafState(), fn(BigDripleafHead $b, DripleafState $v) => $b->setLeafState($v)), + new DummyProperty(StateNames::BIG_DRIPLEAF_HEAD, true) + ]); + $bigDripleafStemModel = Model::create(Blocks::BIG_DRIPLEAF_STEM(), Ids::BIG_DRIPLEAF)->properties([ + $commonProperties->horizontalFacingCardinal, + new DummyProperty(StateNames::BIG_DRIPLEAF_TILT, StringValues::BIG_DRIPLEAF_TILT_NONE), + new DummyProperty(StateNames::BIG_DRIPLEAF_HEAD, false) + ]); + self::mapAsymmetricSerializer($reg, $bigDripleafHeadModel); + self::mapAsymmetricSerializer($reg, $bigDripleafStemModel); + $reg->deserializer->map(Ids::BIG_DRIPLEAF, fn(Reader $in) => $in->readBool(StateNames::BIG_DRIPLEAF_HEAD) ? + self::deserializeAsymmetric($bigDripleafHeadModel, $in) : + self::deserializeAsymmetric($bigDripleafStemModel, $in) + ); - //cauldrons - split into liquid variants, as each have different behaviour - $reg->serializer->map(Blocks::CAULDRON(), Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, 0)); - $reg->serializer->map(Blocks::LAVA_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_LAVA, $b->getFillLevel())); - //potion cauldrons store their real information in the block actor data - $reg->serializer->map(Blocks::POTION_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - $reg->serializer->map(Blocks::WATER_CAULDRON(), fn(FillableCauldron $b) => Helper::encodeCauldron(StringValues::CAULDRON_LIQUID_WATER, $b->getFillLevel())); - $reg->deserializer->map(Ids::CAULDRON, function(Reader $in) : Block{ - $level = $in->readBoundedInt(StateNames::FILL_LEVEL, 0, 6); - if($level === 0){ - $in->ignored(StateNames::CAULDRON_LIQUID); - return Blocks::CAULDRON(); - } + $fillLevelProperty = new IntProperty(StateNames::FILL_LEVEL, 1, 6, fn(FillableCauldron $b) => $b->getFillLevel(), fn(FillableCauldron $b, int $v) => $b->setFillLevel($v)); - return (match ($liquid = $in->readString(StateNames::CAULDRON_LIQUID)) { - StringValues::CAULDRON_LIQUID_WATER => Blocks::WATER_CAULDRON(), - StringValues::CAULDRON_LIQUID_LAVA => Blocks::LAVA_CAULDRON(), + //this pretends to be a water cauldron on disk and stores its real information in the block actor data, therefore only a serializer is needed + self::mapAsymmetricSerializer($reg, Model::create(Blocks::POTION_CAULDRON(), Ids::CAULDRON)->properties([$fillLevelProperty, new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_WATER)])); + + $lavaCauldronModel = Model::create(Blocks::LAVA_CAULDRON(), Ids::CAULDRON)->properties([ + $fillLevelProperty, + new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_LAVA) + ]); + $waterCauldronModel = Model::create(Blocks::WATER_CAULDRON(), Ids::CAULDRON)->properties([ + $fillLevelProperty, + new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_WATER) + ]); + $emptyCauldronModel = Model::create(Blocks::CAULDRON(), Ids::CAULDRON)->properties([ + new DummyProperty(StateNames::FILL_LEVEL, 0), + new DummyProperty(StateNames::CAULDRON_LIQUID, StringValues::CAULDRON_LIQUID_WATER) + ]); + self::mapAsymmetricSerializer($reg, $lavaCauldronModel); + self::mapAsymmetricSerializer($reg, $waterCauldronModel); + self::mapAsymmetricSerializer($reg, $emptyCauldronModel); + $reg->deserializer->map(Ids::CAULDRON, fn(Reader $in) => $in->readInt(StateNames::FILL_LEVEL) === 0 ? + self::deserializeAsymmetric($emptyCauldronModel, $in) : + match ($liquid = $in->readString(StateNames::CAULDRON_LIQUID)) { + StringValues::CAULDRON_LIQUID_WATER => self::deserializeAsymmetric($waterCauldronModel, $in), + StringValues::CAULDRON_LIQUID_LAVA => self::deserializeAsymmetric($lavaCauldronModel, $in), StringValues::CAULDRON_LIQUID_POWDER_SNOW => throw new UnsupportedBlockStateException("Powder snow is not supported yet"), default => throw $in->badValueException(StateNames::CAULDRON_LIQUID, $liquid) - })->setFillLevel($level); - }); + } + ); //mushroom stems, split for consistency with all-sided logs vs normal logs - $reg->serializer->map(Blocks::ALL_SIDED_MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)); - $reg->serializer->map(Blocks::MUSHROOM_STEM(), Writer::create(Ids::MUSHROOM_STEM) - ->writeInt(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)); - $reg->deserializer->map(Ids::MUSHROOM_STEM, fn(Reader $in) => match ($in->readBoundedInt(StateNames::HUGE_MUSHROOM_BITS, 0, 15)) { - BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => Blocks::ALL_SIDED_MUSHROOM_STEM(), - BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => Blocks::MUSHROOM_STEM(), + $allSidedMushroomStemModel = Model::create(Blocks::ALL_SIDED_MUSHROOM_STEM(), Ids::MUSHROOM_STEM)->properties([new DummyProperty(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM)]); + $mushroomStemModel = Model::create(Blocks::MUSHROOM_STEM(), Ids::MUSHROOM_STEM)->properties([new DummyProperty(StateNames::HUGE_MUSHROOM_BITS, BlockLegacyMetadata::MUSHROOM_BLOCK_STEM)]); + self::mapAsymmetricSerializer($reg, $allSidedMushroomStemModel); + self::mapAsymmetricSerializer($reg, $mushroomStemModel); + $reg->deserializer->map(Ids::MUSHROOM_STEM, fn(Reader $in) : Block => match ($in->readInt(StateNames::HUGE_MUSHROOM_BITS)) { + BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM => self::deserializeAsymmetric($allSidedMushroomStemModel, $in), + BlockLegacyMetadata::MUSHROOM_BLOCK_STEM => self::deserializeAsymmetric($mushroomStemModel, $in), default => throw new BlockStateDeserializeException("This state does not exist"), }); //pitcher crop, split into single and double variants as double has different properties and behaviour //this will probably be the most annoying to unify - $reg->serializer->map(Blocks::PITCHER_CROP(), function(PitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge()) - ->writeBool(StateNames::UPPER_BLOCK_BIT, false); - }); - $reg->serializer->map(Blocks::DOUBLE_PITCHER_CROP(), function(DoublePitcherCrop $block) : Writer{ - return Writer::create(Ids::PITCHER_CROP) - ->writeInt(StateNames::GROWTH, $block->getAge() + 1 + PitcherCrop::MAX_AGE) - ->writeBool(StateNames::UPPER_BLOCK_BIT, $block->isTop()); - }); - $reg->deserializer->map(Ids::PITCHER_CROP, function(Reader $in) : Block{ - $growth = $in->readBoundedInt(StateNames::GROWTH, 0, 7); - $top = $in->readBool(StateNames::UPPER_BLOCK_BIT); - if($growth <= PitcherCrop::MAX_AGE){ - //top pitcher crop with age 0-2 is an invalid state - //only the bottom half should exist in this case - return $top ? Blocks::AIR() : Blocks::PITCHER_CROP()->setAge($growth); - } - return Blocks::DOUBLE_PITCHER_CROP() - ->setAge(min($growth - PitcherCrop::MAX_AGE - 1, DoublePitcherCrop::MAX_AGE)) - ->setTop($top); - }); + $pitcherCropModel = Model::create(Blocks::PITCHER_CROP(), Ids::PITCHER_CROP)->properties([ + new IntProperty(StateNames::GROWTH, 0, PitcherCrop::MAX_AGE, fn(PitcherCrop $b) => $b->getAge(), fn(PitcherCrop $b, int $v) => $b->setAge($v)), + new DummyProperty(StateNames::UPPER_BLOCK_BIT, false) + ]); + $doublePitcherCropAgeOffset = PitcherCrop::MAX_AGE + 1; + $doublePitcherCropModel = Model::create(Blocks::DOUBLE_PITCHER_CROP(), Ids::PITCHER_CROP)->properties([ + new IntProperty( + StateNames::GROWTH, + $doublePitcherCropAgeOffset, //TODO: it would be a bit less awkward if the bounds applied _after_ applying the offset, instead of before + 7, + fn(DoublePitcherCrop $b) => $b->getAge(), + fn(DoublePitcherCrop $b, int $v) => $b->setAge(min($v, DoublePitcherCrop::MAX_AGE)), //state may give up to 7, but only up to 4 is valid + offset: -$doublePitcherCropAgeOffset + ), + new BoolProperty(StateNames::UPPER_BLOCK_BIT, fn(DoublePitcherCrop $b) => $b->isTop(), fn(DoublePitcherCrop $b, bool $v) => $b->setTop($v)) + ]); + self::mapAsymmetricSerializer($reg, $pitcherCropModel); + self::mapAsymmetricSerializer($reg, $doublePitcherCropModel); + $reg->deserializer->map(Ids::PITCHER_CROP, fn(Reader $in) => $in->readInt(StateNames::GROWTH) <= PitcherCrop::MAX_AGE ? + ($in->readBool(StateNames::UPPER_BLOCK_BIT) ? + Blocks::AIR() : + self::deserializeAsymmetric($pitcherCropModel, $in) + ) : self::deserializeAsymmetric($doublePitcherCropModel, $in) + ); //these only exist within PM (mapped from tile properties) as they don't support the same properties as a - //normal banner - $reg->serializer->map(Blocks::OMINOUS_BANNER(), function(OminousFloorBanner $block) use ($commonProperties) : Writer{ - $writer = Writer::create(Ids::STANDING_BANNER); - $commonProperties->floorSignLikeRotation->serialize($block, $writer); - return $writer; - }); - $reg->serializer->map(Blocks::OMINOUS_WALL_BANNER(), function(OminousWallBanner $block) use ($commonProperties) : Writer{ - $writer = Writer::create(Ids::WALL_BANNER); - $commonProperties->horizontalFacingClassic->serialize($block, $writer); - return $writer; - }); + //normal banner, therefore no deserializer is needed + self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_BANNER(), Ids::STANDING_BANNER)->properties([$commonProperties->floorSignLikeRotation])); + self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_WALL_BANNER(), Ids::WALL_BANNER)->properties([$commonProperties->horizontalFacingClassic])); } } From 93e33dad8e2b607efba3ef8a84caeb1b75420451 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 16:42:05 +0100 Subject: [PATCH 06/11] tidy CS --- src/data/bedrock/block/convert/VanillaBlockMappings.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index f70de9f4c..e276ed6e7 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -33,7 +33,6 @@ use pocketmine\block\Bed; use pocketmine\block\Bedrock; use pocketmine\block\Bell; use pocketmine\block\BigDripleafHead; -use pocketmine\block\BigDripleafStem; use pocketmine\block\Block; use pocketmine\block\BrewingStand; use pocketmine\block\Cactus; @@ -68,8 +67,6 @@ use pocketmine\block\MobHead; use pocketmine\block\NetherPortal; use pocketmine\block\NetherVines; use pocketmine\block\NetherWartPlant; -use pocketmine\block\OminousFloorBanner; -use pocketmine\block\OminousWallBanner; use pocketmine\block\PinkPetals; use pocketmine\block\PitcherCrop; use pocketmine\block\PoweredRail; @@ -112,7 +109,6 @@ use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; use pocketmine\data\bedrock\block\BlockTypeNames as Ids; use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader; -use pocketmine\data\bedrock\block\convert\BlockStateSerializerHelper as Helper; use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer; use pocketmine\data\bedrock\block\convert\property\BoolFromStringProperty; use pocketmine\data\bedrock\block\convert\property\BoolProperty; @@ -1590,6 +1586,7 @@ final class VanillaBlockMappings{ self::mapAsymmetricSerializer($reg, $doublePitcherCropModel); $reg->deserializer->map(Ids::PITCHER_CROP, fn(Reader $in) => $in->readInt(StateNames::GROWTH) <= PitcherCrop::MAX_AGE ? ($in->readBool(StateNames::UPPER_BLOCK_BIT) ? + //top pitcher crop with age 0-2 is an invalid state, only the bottom half should exist in this case Blocks::AIR() : self::deserializeAsymmetric($pitcherCropModel, $in) ) : self::deserializeAsymmetric($doublePitcherCropModel, $in) From 17ecf11a1bae60600fbf56637e50aea714255e5b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 16:49:49 +0100 Subject: [PATCH 07/11] Remove stupid thing PhpStorm keeps doing --- src/data/bedrock/block/convert/property/CommonProperties.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/data/bedrock/block/convert/property/CommonProperties.php b/src/data/bedrock/block/convert/property/CommonProperties.php index 71b87139c..1a3224270 100644 --- a/src/data/bedrock/block/convert/property/CommonProperties.php +++ b/src/data/bedrock/block/convert/property/CommonProperties.php @@ -55,7 +55,6 @@ use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockLegacyMetadata; use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateStringValues; -use pocketmine\data\bedrock\block\convert\non; use pocketmine\math\Facing; use pocketmine\utils\SingletonTrait; From be90c6c399b4fa40ae86bcdd9fd8c3623a0ef78c Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 17:01:59 +0100 Subject: [PATCH 08/11] World: trigger readStateFromWorld on tile blocks immediately on load this ensures that the state IDs reflect the actual PM block type, which would probably be important for a bunch of different async things. --- src/world/World.php | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/world/World.php b/src/world/World.php index 8602c5803..bd790c299 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2978,7 +2978,7 @@ class World implements ChunkManager{ unset($this->blockCache[$chunkHash]); unset($this->blockCollisionBoxCache[$chunkHash]); - $this->initChunk($x, $z, $chunkData); + $this->initChunk($x, $z, $chunkData, $chunk); if(ChunkLoadEvent::hasHandlers()){ (new ChunkLoadEvent($this, $x, $z, $this->chunks[$chunkHash], false))->call(); @@ -2998,7 +2998,7 @@ class World implements ChunkManager{ return $this->chunks[$chunkHash]; } - private function initChunk(int $chunkX, int $chunkZ, ChunkData $chunkData) : void{ + private function initChunk(int $chunkX, int $chunkZ, ChunkData $chunkData, Chunk $chunk) : void{ $logger = new \PrefixedLogger($this->logger, "Loading chunk $chunkX $chunkZ"); if(count($chunkData->getEntityNBT()) !== 0){ @@ -3063,6 +3063,16 @@ class World implements ChunkManager{ }else{ $this->addTile($tile); } + $expectedStateId = $chunk->getBlockStateId($tilePosition->getFloorX() & Chunk::COORD_MASK, $tilePosition->getFloorY(), $tilePosition->getFloorZ() & Chunk::COORD_MASK); + $actualStateId = $this->getBlock($tilePosition)->getStateId(); + if($expectedStateId !== $actualStateId){ + //state ID was updated by readStateFromWorld - typically because the block pulled some data from the tile + //make sure this is synced to the chunk + //TODO: in the future we should pull tile reading logic out of readStateFromWorld() and do it only + //when the tile is loaded - this would be cleaner and faster + $chunk->setBlockStateId($tilePosition->getFloorX() & Chunk::COORD_MASK, $tilePosition->getFloorY(), $tilePosition->getFloorZ() & Chunk::COORD_MASK, $actualStateId); + $this->logger->debug("Tile " . $tile::class . " at x=$tilePosition->x,y=$tilePosition->y,z=$tilePosition->z updated block state ID from $expectedStateId to $actualStateId"); + } } foreach(Utils::promoteKeys($deletedTiles) as $saveId => $count){ From 00d617146355815549b0f86fd9a355166fbd5fe7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 20:07:59 +0100 Subject: [PATCH 09/11] Implement hanging signs --- src/block/BaseSign.php | 19 +++++- src/block/BlockTypeIds.php | 35 +++++++++- src/block/CeilingCenterHangingSign.php | 52 +++++++++++++++ src/block/CeilingEdgesHangingSign.php | 51 ++++++++++++++ src/block/VanillaBlocks.php | 51 ++++++++++++++ src/block/WallHangingSign.php | 63 ++++++++++++++++++ src/block/tile/HangingSign.php | 33 ++++++++++ src/block/tile/TileFactory.php | 2 + .../block/convert/VanillaBlockMappings.php | 46 +++++++++++++ .../ItemSerializerDeserializerRegistrar.php | 11 ++++ src/item/HangingSign.php | 66 +++++++++++++++++++ src/item/Item.php | 4 ++ src/item/ItemTypeIds.php | 13 +++- src/item/StringToItemParser.php | 11 ++++ src/item/VanillaItems.php | 22 +++++++ src/world/World.php | 2 +- .../block_factory_consistency_check.json | 66 +++++++++++++++++++ 17 files changed, 543 insertions(+), 4 deletions(-) create mode 100644 src/block/CeilingCenterHangingSign.php create mode 100644 src/block/CeilingEdgesHangingSign.php create mode 100644 src/block/WallHangingSign.php create mode 100644 src/block/tile/HangingSign.php create mode 100644 src/item/HangingSign.php diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 0efaa603c..b01157343 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -103,10 +103,27 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ return SupportType::NONE; } + /** + * @deprecated + */ abstract protected function getSupportingFace() : int; + /** + * @return int[] + */ + protected function getSupportingFaceOptions() : array{ + return [$this->getSupportingFace()]; + } + public function onNearbyBlockChange() : void{ - if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){ + $foundSupport = false; + foreach($this->getSupportingFaceOptions() as $face){ + if($this->getSide($face)->getTypeId() !== BlockTypeIds::AIR){ + $foundSupport = true; + break; + } + } + if(!$foundSupport){ $this->position->getWorld()->useBreakOn($this->position); } } diff --git a/src/block/BlockTypeIds.php b/src/block/BlockTypeIds.php index 52b141bcf..e4d49746f 100644 --- a/src/block/BlockTypeIds.php +++ b/src/block/BlockTypeIds.php @@ -789,8 +789,41 @@ final class BlockTypeIds{ public const RESPAWN_ANCHOR = 10759; public const OMINOUS_BANNER = 10760; public const OMINOUS_WALL_BANNER = 10761; + public const ACACIA_CEILING_CENTER_HANGING_SIGN = 10762; + public const ACACIA_CEILING_EDGES_HANGING_SIGN = 10763; + public const ACACIA_WALL_HANGING_SIGN = 10764; + public const BIRCH_CEILING_CENTER_HANGING_SIGN = 10765; + public const BIRCH_CEILING_EDGES_HANGING_SIGN = 10766; + public const BIRCH_WALL_HANGING_SIGN = 10767; + public const CHERRY_CEILING_CENTER_HANGING_SIGN = 10768; + public const CHERRY_CEILING_EDGES_HANGING_SIGN = 10769; + public const CHERRY_WALL_HANGING_SIGN = 10770; + public const CRIMSON_CEILING_CENTER_HANGING_SIGN = 10771; + public const CRIMSON_CEILING_EDGES_HANGING_SIGN = 10772; + public const CRIMSON_WALL_HANGING_SIGN = 10773; + public const DARK_OAK_CEILING_CENTER_HANGING_SIGN = 10774; + public const DARK_OAK_CEILING_EDGES_HANGING_SIGN = 10775; + public const DARK_OAK_WALL_HANGING_SIGN = 10776; + public const JUNGLE_CEILING_CENTER_HANGING_SIGN = 10777; + public const JUNGLE_CEILING_EDGES_HANGING_SIGN = 10778; + public const JUNGLE_WALL_HANGING_SIGN = 10779; + public const MANGROVE_CEILING_CENTER_HANGING_SIGN = 10780; + public const MANGROVE_CEILING_EDGES_HANGING_SIGN = 10781; + public const MANGROVE_WALL_HANGING_SIGN = 10782; + public const OAK_CEILING_CENTER_HANGING_SIGN = 10783; + public const OAK_CEILING_EDGES_HANGING_SIGN = 10784; + public const OAK_WALL_HANGING_SIGN = 10785; + public const PALE_OAK_CEILING_CENTER_HANGING_SIGN = 10786; + public const PALE_OAK_CEILING_EDGES_HANGING_SIGN = 10787; + public const PALE_OAK_WALL_HANGING_SIGN = 10788; + public const SPRUCE_CEILING_CENTER_HANGING_SIGN = 10789; + public const SPRUCE_CEILING_EDGES_HANGING_SIGN = 10790; + public const SPRUCE_WALL_HANGING_SIGN = 10791; + public const WARPED_CEILING_CENTER_HANGING_SIGN = 10792; + public const WARPED_CEILING_EDGES_HANGING_SIGN = 10793; + public const WARPED_WALL_HANGING_SIGN = 10794; - public const FIRST_UNUSED_BLOCK_ID = 10762; + public const FIRST_UNUSED_BLOCK_ID = 10795; private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID; diff --git a/src/block/CeilingCenterHangingSign.php b/src/block/CeilingCenterHangingSign.php new file mode 100644 index 000000000..7078f38d8 --- /dev/null +++ b/src/block/CeilingCenterHangingSign.php @@ -0,0 +1,52 @@ +rotation = self::getRotationFromYaw($player->getLocation()->getYaw()); + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/CeilingEdgesHangingSign.php b/src/block/CeilingEdgesHangingSign.php new file mode 100644 index 000000000..5dafe6932 --- /dev/null +++ b/src/block/CeilingEdgesHangingSign.php @@ -0,0 +1,51 @@ +facing = Facing::opposite($player->getHorizontalFacing()); + } + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 3255c6f6f..bf9f7e5f5 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -45,6 +45,7 @@ use pocketmine\block\tile\EnchantTable as TileEnchantingTable; use pocketmine\block\tile\EnderChest as TileEnderChest; use pocketmine\block\tile\FlowerPot as TileFlowerPot; use pocketmine\block\tile\GlowingItemFrame as TileGlowingItemFrame; +use pocketmine\block\tile\HangingSign as TileHangingSign; use pocketmine\block\tile\Hopper as TileHopper; use pocketmine\block\tile\ItemFrame as TileItemFrame; use pocketmine\block\tile\Jukebox as TileJukebox; @@ -80,6 +81,8 @@ use function strtolower; * @generate-registry-docblock * * @method static WoodenButton ACACIA_BUTTON() + * @method static CeilingCenterHangingSign ACACIA_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign ACACIA_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor ACACIA_DOOR() * @method static WoodenFence ACACIA_FENCE() * @method static FenceGate ACACIA_FENCE_GATE() @@ -92,6 +95,7 @@ use function strtolower; * @method static WoodenSlab ACACIA_SLAB() * @method static WoodenStairs ACACIA_STAIRS() * @method static WoodenTrapdoor ACACIA_TRAPDOOR() + * @method static WallHangingSign ACACIA_WALL_HANGING_SIGN() * @method static WallSign ACACIA_WALL_SIGN() * @method static Wood ACACIA_WOOD() * @method static ActivatorRail ACTIVATOR_RAIL() @@ -122,6 +126,8 @@ use function strtolower; * @method static BigDripleafHead BIG_DRIPLEAF_HEAD() * @method static BigDripleafStem BIG_DRIPLEAF_STEM() * @method static WoodenButton BIRCH_BUTTON() + * @method static CeilingCenterHangingSign BIRCH_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign BIRCH_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor BIRCH_DOOR() * @method static WoodenFence BIRCH_FENCE() * @method static FenceGate BIRCH_FENCE_GATE() @@ -134,6 +140,7 @@ use function strtolower; * @method static WoodenSlab BIRCH_SLAB() * @method static WoodenStairs BIRCH_STAIRS() * @method static WoodenTrapdoor BIRCH_TRAPDOOR() + * @method static WallHangingSign BIRCH_WALL_HANGING_SIGN() * @method static WallSign BIRCH_WALL_SIGN() * @method static Wood BIRCH_WOOD() * @method static Opaque BLACKSTONE() @@ -170,6 +177,8 @@ use function strtolower; * @method static Chain CHAIN() * @method static ChemicalHeat CHEMICAL_HEAT() * @method static WoodenButton CHERRY_BUTTON() + * @method static CeilingCenterHangingSign CHERRY_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign CHERRY_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor CHERRY_DOOR() * @method static WoodenFence CHERRY_FENCE() * @method static FenceGate CHERRY_FENCE_GATE() @@ -181,6 +190,7 @@ use function strtolower; * @method static WoodenSlab CHERRY_SLAB() * @method static WoodenStairs CHERRY_STAIRS() * @method static WoodenTrapdoor CHERRY_TRAPDOOR() + * @method static WallHangingSign CHERRY_WALL_HANGING_SIGN() * @method static WallSign CHERRY_WALL_SIGN() * @method static Wood CHERRY_WOOD() * @method static Chest CHEST() @@ -231,6 +241,8 @@ use function strtolower; * @method static Opaque CRACKED_STONE_BRICKS() * @method static CraftingTable CRAFTING_TABLE() * @method static WoodenButton CRIMSON_BUTTON() + * @method static CeilingCenterHangingSign CRIMSON_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign CRIMSON_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor CRIMSON_DOOR() * @method static WoodenFence CRIMSON_FENCE() * @method static FenceGate CRIMSON_FENCE_GATE() @@ -243,6 +255,7 @@ use function strtolower; * @method static WoodenStairs CRIMSON_STAIRS() * @method static Wood CRIMSON_STEM() * @method static WoodenTrapdoor CRIMSON_TRAPDOOR() + * @method static WallHangingSign CRIMSON_WALL_HANGING_SIGN() * @method static WallSign CRIMSON_WALL_SIGN() * @method static Opaque CRYING_OBSIDIAN() * @method static Copper CUT_COPPER() @@ -254,6 +267,8 @@ use function strtolower; * @method static Slab CUT_SANDSTONE_SLAB() * @method static Flower DANDELION() * @method static WoodenButton DARK_OAK_BUTTON() + * @method static CeilingCenterHangingSign DARK_OAK_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign DARK_OAK_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor DARK_OAK_DOOR() * @method static WoodenFence DARK_OAK_FENCE() * @method static FenceGate DARK_OAK_FENCE_GATE() @@ -266,6 +281,7 @@ use function strtolower; * @method static WoodenSlab DARK_OAK_SLAB() * @method static WoodenStairs DARK_OAK_STAIRS() * @method static WoodenTrapdoor DARK_OAK_TRAPDOOR() + * @method static WallHangingSign DARK_OAK_WALL_HANGING_SIGN() * @method static WallSign DARK_OAK_WALL_SIGN() * @method static Wood DARK_OAK_WOOD() * @method static Opaque DARK_PRISMARINE() @@ -488,6 +504,8 @@ use function strtolower; * @method static ItemFrame ITEM_FRAME() * @method static Jukebox JUKEBOX() * @method static WoodenButton JUNGLE_BUTTON() + * @method static CeilingCenterHangingSign JUNGLE_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign JUNGLE_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor JUNGLE_DOOR() * @method static WoodenFence JUNGLE_FENCE() * @method static FenceGate JUNGLE_FENCE_GATE() @@ -500,6 +518,7 @@ use function strtolower; * @method static WoodenSlab JUNGLE_SLAB() * @method static WoodenStairs JUNGLE_STAIRS() * @method static WoodenTrapdoor JUNGLE_TRAPDOOR() + * @method static WallHangingSign JUNGLE_WALL_HANGING_SIGN() * @method static WallSign JUNGLE_WALL_SIGN() * @method static Wood JUNGLE_WOOD() * @method static ChemistryTable LAB_TABLE() @@ -522,6 +541,8 @@ use function strtolower; * @method static Loom LOOM() * @method static Magma MAGMA() * @method static WoodenButton MANGROVE_BUTTON() + * @method static CeilingCenterHangingSign MANGROVE_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign MANGROVE_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor MANGROVE_DOOR() * @method static WoodenFence MANGROVE_FENCE() * @method static FenceGate MANGROVE_FENCE_GATE() @@ -534,6 +555,7 @@ use function strtolower; * @method static WoodenSlab MANGROVE_SLAB() * @method static WoodenStairs MANGROVE_STAIRS() * @method static WoodenTrapdoor MANGROVE_TRAPDOOR() + * @method static WallHangingSign MANGROVE_WALL_HANGING_SIGN() * @method static WallSign MANGROVE_WALL_SIGN() * @method static Wood MANGROVE_WOOD() * @method static ChemistryTable MATERIAL_REDUCER() @@ -572,6 +594,8 @@ use function strtolower; * @method static Opaque NETHER_WART_BLOCK() * @method static Note NOTE_BLOCK() * @method static WoodenButton OAK_BUTTON() + * @method static CeilingCenterHangingSign OAK_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign OAK_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor OAK_DOOR() * @method static WoodenFence OAK_FENCE() * @method static FenceGate OAK_FENCE_GATE() @@ -584,6 +608,7 @@ use function strtolower; * @method static WoodenSlab OAK_SLAB() * @method static WoodenStairs OAK_STAIRS() * @method static WoodenTrapdoor OAK_TRAPDOOR() + * @method static WallHangingSign OAK_WALL_HANGING_SIGN() * @method static WallSign OAK_WALL_SIGN() * @method static Wood OAK_WOOD() * @method static Opaque OBSIDIAN() @@ -594,6 +619,8 @@ use function strtolower; * @method static PackedIce PACKED_ICE() * @method static Opaque PACKED_MUD() * @method static WoodenButton PALE_OAK_BUTTON() + * @method static CeilingCenterHangingSign PALE_OAK_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign PALE_OAK_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor PALE_OAK_DOOR() * @method static WoodenFence PALE_OAK_FENCE() * @method static FenceGate PALE_OAK_FENCE_GATE() @@ -605,6 +632,7 @@ use function strtolower; * @method static WoodenSlab PALE_OAK_SLAB() * @method static WoodenStairs PALE_OAK_STAIRS() * @method static WoodenTrapdoor PALE_OAK_TRAPDOOR() + * @method static WallHangingSign PALE_OAK_WALL_HANGING_SIGN() * @method static WallSign PALE_OAK_WALL_SIGN() * @method static Wood PALE_OAK_WOOD() * @method static DoublePlant PEONY() @@ -735,6 +763,8 @@ use function strtolower; * @method static Sponge SPONGE() * @method static SporeBlossom SPORE_BLOSSOM() * @method static WoodenButton SPRUCE_BUTTON() + * @method static CeilingCenterHangingSign SPRUCE_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign SPRUCE_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor SPRUCE_DOOR() * @method static WoodenFence SPRUCE_FENCE() * @method static FenceGate SPRUCE_FENCE_GATE() @@ -747,6 +777,7 @@ use function strtolower; * @method static WoodenSlab SPRUCE_SLAB() * @method static WoodenStairs SPRUCE_STAIRS() * @method static WoodenTrapdoor SPRUCE_TRAPDOOR() + * @method static WallHangingSign SPRUCE_WALL_HANGING_SIGN() * @method static WallSign SPRUCE_WALL_SIGN() * @method static Wood SPRUCE_WOOD() * @method static StainedHardenedClay STAINED_CLAY() @@ -790,6 +821,8 @@ use function strtolower; * @method static WallBanner WALL_BANNER() * @method static WallCoralFan WALL_CORAL_FAN() * @method static WoodenButton WARPED_BUTTON() + * @method static CeilingCenterHangingSign WARPED_CEILING_CENTER_HANGING_SIGN() + * @method static CeilingEdgesHangingSign WARPED_CEILING_EDGES_HANGING_SIGN() * @method static WoodenDoor WARPED_DOOR() * @method static WoodenFence WARPED_FENCE() * @method static FenceGate WARPED_FENCE_GATE() @@ -802,6 +835,7 @@ use function strtolower; * @method static WoodenStairs WARPED_STAIRS() * @method static Wood WARPED_STEM() * @method static WoodenTrapdoor WARPED_TRAPDOOR() + * @method static WallHangingSign WARPED_WALL_HANGING_SIGN() * @method static WallSign WARPED_WALL_SIGN() * @method static Opaque WARPED_WART_BLOCK() * @method static Water WATER() @@ -1396,6 +1430,23 @@ final class VanillaBlocks{ }; self::register($idName("sign"), fn(BID $id) => new FloorSign($id, $name . " Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); self::register($idName("wall_sign"), fn(BID $id) => new WallSign($id, $name . " Wall Sign", $signBreakInfo, $woodType, $signAsItem), TileSign::class); + + $hangingSignAsItem = match($woodType){ + WoodType::OAK => VanillaItems::OAK_HANGING_SIGN(...), + WoodType::SPRUCE => VanillaItems::SPRUCE_HANGING_SIGN(...), + WoodType::BIRCH => VanillaItems::BIRCH_HANGING_SIGN(...), + WoodType::JUNGLE => VanillaItems::JUNGLE_HANGING_SIGN(...), + WoodType::ACACIA => VanillaItems::ACACIA_HANGING_SIGN(...), + WoodType::DARK_OAK => VanillaItems::DARK_OAK_HANGING_SIGN(...), + WoodType::MANGROVE => VanillaItems::MANGROVE_HANGING_SIGN(...), + WoodType::CRIMSON => VanillaItems::CRIMSON_HANGING_SIGN(...), + WoodType::WARPED => VanillaItems::WARPED_HANGING_SIGN(...), + WoodType::CHERRY => VanillaItems::CHERRY_HANGING_SIGN(...), + WoodType::PALE_OAK => VanillaItems::PALE_OAK_HANGING_SIGN(...), + }; + self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . "Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . "Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); } } diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php new file mode 100644 index 000000000..c167036b1 --- /dev/null +++ b/src/block/WallHangingSign.php @@ -0,0 +1,63 @@ +facing, clockwise: true); + } + + protected function getSupportingFaceOptions() : array{ + //wall hanging signs can be supported from either end of the post + return [ + Facing::rotateY($this->facing, clockwise: true), + Facing::rotateY($this->facing, clockwise: false) + ]; + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if(Facing::axis($face) === Axis::Y){ + return false; + } + + $this->facing = Facing::rotateY($face, clockwise: true); + //the front should always face the player if possible + if($player !== null && $this->facing === $player->getHorizontalFacing()){ + $this->facing = Facing::opposite($this->facing); + } + + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } +} diff --git a/src/block/tile/HangingSign.php b/src/block/tile/HangingSign.php new file mode 100644 index 000000000..9bf088f47 --- /dev/null +++ b/src/block/tile/HangingSign.php @@ -0,0 +1,33 @@ +register(SporeBlossom::class, ["SporeBlossom", "minecraft:spore_blossom"]); $this->register(MobHead::class, ["Skull", "minecraft:skull"]); $this->register(GlowingItemFrame::class, ["GlowItemFrame"]); + $this->register(HangingSign::class, ["HangingSign", "minecraft:hanging_sign"]); //TODO: ChalkboardBlock //TODO: ChemistryTable diff --git a/src/data/bedrock/block/convert/VanillaBlockMappings.php b/src/data/bedrock/block/convert/VanillaBlockMappings.php index e276ed6e7..fc1c6acb6 100644 --- a/src/data/bedrock/block/convert/VanillaBlockMappings.php +++ b/src/data/bedrock/block/convert/VanillaBlockMappings.php @@ -1596,5 +1596,51 @@ final class VanillaBlockMappings{ //normal banner, therefore no deserializer is needed self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_BANNER(), Ids::STANDING_BANNER)->properties([$commonProperties->floorSignLikeRotation])); self::mapAsymmetricSerializer($reg, Model::create(Blocks::OMINOUS_WALL_BANNER(), Ids::WALL_BANNER)->properties([$commonProperties->horizontalFacingClassic])); + + foreach([ + Ids::ACACIA_HANGING_SIGN => [Blocks::ACACIA_CEILING_CENTER_HANGING_SIGN(), Blocks::ACACIA_CEILING_EDGES_HANGING_SIGN(), Blocks::ACACIA_WALL_HANGING_SIGN()], + Ids::BIRCH_HANGING_SIGN => [Blocks::BIRCH_CEILING_CENTER_HANGING_SIGN(), Blocks::BIRCH_CEILING_EDGES_HANGING_SIGN(), Blocks::BIRCH_WALL_HANGING_SIGN()], + Ids::CHERRY_HANGING_SIGN => [Blocks::CHERRY_CEILING_CENTER_HANGING_SIGN(), Blocks::CHERRY_CEILING_EDGES_HANGING_SIGN(), Blocks::CHERRY_WALL_HANGING_SIGN()], + Ids::CRIMSON_HANGING_SIGN => [Blocks::CRIMSON_CEILING_CENTER_HANGING_SIGN(), Blocks::CRIMSON_CEILING_EDGES_HANGING_SIGN(), Blocks::CRIMSON_WALL_HANGING_SIGN()], + Ids::DARK_OAK_HANGING_SIGN => [Blocks::DARK_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::DARK_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::DARK_OAK_WALL_HANGING_SIGN()], + Ids::JUNGLE_HANGING_SIGN => [Blocks::JUNGLE_CEILING_CENTER_HANGING_SIGN(), Blocks::JUNGLE_CEILING_EDGES_HANGING_SIGN(), Blocks::JUNGLE_WALL_HANGING_SIGN()], + Ids::MANGROVE_HANGING_SIGN => [Blocks::MANGROVE_CEILING_CENTER_HANGING_SIGN(), Blocks::MANGROVE_CEILING_EDGES_HANGING_SIGN(), Blocks::MANGROVE_WALL_HANGING_SIGN()], + Ids::OAK_HANGING_SIGN => [Blocks::OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::OAK_WALL_HANGING_SIGN()], + Ids::PALE_OAK_HANGING_SIGN => [Blocks::PALE_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::PALE_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::PALE_OAK_WALL_HANGING_SIGN()], + Ids::SPRUCE_HANGING_SIGN => [Blocks::SPRUCE_CEILING_CENTER_HANGING_SIGN(), Blocks::SPRUCE_CEILING_EDGES_HANGING_SIGN(), Blocks::SPRUCE_WALL_HANGING_SIGN()], + Ids::WARPED_HANGING_SIGN => [Blocks::WARPED_CEILING_CENTER_HANGING_SIGN(), Blocks::WARPED_CEILING_EDGES_HANGING_SIGN(), Blocks::WARPED_WALL_HANGING_SIGN()], + ] as $id => [$center, $edges, $wall]){ + //attached_bit - true for ceiling center signs, false for ceiling edges signs and wall signs + //hanging - true for all ceiling signs, false for wall signs + //facing_direction - used for ceiling edges signs and wall signs + //ground_sign_direction - used by ceiling center signs only + $centerModel = Model::create($center, $id)->properties([ + $commonProperties->floorSignLikeRotation, + new DummyProperty(StateNames::ATTACHED_BIT, true), + new DummyProperty(StateNames::HANGING, true), + new DummyProperty(StateNames::FACING_DIRECTION, 2) + ]); + $edgesModel = Model::create($edges, $id)->properties([ + new DummyProperty(StateNames::GROUND_SIGN_DIRECTION, 0), + new DummyProperty(StateNames::ATTACHED_BIT, false), + new DummyProperty(StateNames::HANGING, true), + $commonProperties->horizontalFacingClassic, + ]); + $wallModel = Model::create($wall, $id)->properties([ + new DummyProperty(StateNames::GROUND_SIGN_DIRECTION, 0), + new DummyProperty(StateNames::ATTACHED_BIT, false), + new DummyProperty(StateNames::HANGING, false), + $commonProperties->horizontalFacingClassic + ]); + self::mapAsymmetricSerializer($reg, $centerModel); + self::mapAsymmetricSerializer($reg, $edgesModel); + self::mapAsymmetricSerializer($reg, $wallModel); + $reg->deserializer->map($id, fn(Reader $in) => $in->readBool(StateNames::HANGING) ? + ($in->readBool(StateNames::ATTACHED_BIT) ? + self::deserializeAsymmetric($centerModel, $in) : + self::deserializeAsymmetric($edgesModel, $in) + ) : + self::deserializeAsymmetric($wallModel, $in)); + } } } diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index fa6f88483..f5c03dbeb 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -167,6 +167,7 @@ final class ItemSerializerDeserializerRegistrar{ */ private function register1to1ItemMappings() : void{ $this->map1to1Item(Ids::ACACIA_BOAT, Items::ACACIA_BOAT()); + $this->map1to1Item(Ids::ACACIA_HANGING_SIGN, Items::ACACIA_HANGING_SIGN()); $this->map1to1Item(Ids::ACACIA_SIGN, Items::ACACIA_SIGN()); $this->map1to1Item(Ids::AMETHYST_SHARD, Items::AMETHYST_SHARD()); $this->map1to1Item(Ids::APPLE, Items::APPLE()); @@ -176,6 +177,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::BEETROOT_SEEDS, Items::BEETROOT_SEEDS()); $this->map1to1Item(Ids::BEETROOT_SOUP, Items::BEETROOT_SOUP()); $this->map1to1Item(Ids::BIRCH_BOAT, Items::BIRCH_BOAT()); + $this->map1to1Item(Ids::BIRCH_HANGING_SIGN, Items::BIRCH_HANGING_SIGN()); $this->map1to1Item(Ids::BIRCH_SIGN, Items::BIRCH_SIGN()); $this->map1to1Item(Ids::BLAZE_POWDER, Items::BLAZE_POWDER()); $this->map1to1Item(Ids::BLAZE_ROD, Items::BLAZE_ROD()); @@ -194,6 +196,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::CHAINMAIL_HELMET, Items::CHAINMAIL_HELMET()); $this->map1to1Item(Ids::CHAINMAIL_LEGGINGS, Items::CHAINMAIL_LEGGINGS()); $this->map1to1Item(Ids::CHARCOAL, Items::CHARCOAL()); + $this->map1to1Item(Ids::CHERRY_HANGING_SIGN, Items::CHERRY_HANGING_SIGN()); $this->map1to1Item(Ids::CHERRY_SIGN, Items::CHERRY_SIGN()); $this->map1to1Item(Ids::CHICKEN, Items::RAW_CHICKEN()); $this->map1to1Item(Ids::CHORUS_FRUIT, Items::CHORUS_FRUIT()); @@ -213,8 +216,10 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::COOKED_SALMON, Items::COOKED_SALMON()); $this->map1to1Item(Ids::COOKIE, Items::COOKIE()); $this->map1to1Item(Ids::COPPER_INGOT, Items::COPPER_INGOT()); + $this->map1to1Item(Ids::CRIMSON_HANGING_SIGN, Items::CRIMSON_HANGING_SIGN()); $this->map1to1Item(Ids::CRIMSON_SIGN, Items::CRIMSON_SIGN()); $this->map1to1Item(Ids::DARK_OAK_BOAT, Items::DARK_OAK_BOAT()); + $this->map1to1Item(Ids::DARK_OAK_HANGING_SIGN, Items::DARK_OAK_HANGING_SIGN()); $this->map1to1Item(Ids::DARK_OAK_SIGN, Items::DARK_OAK_SIGN()); $this->map1to1Item(Ids::DIAMOND, Items::DIAMOND()); $this->map1to1Item(Ids::DIAMOND_AXE, Items::DIAMOND_AXE()); @@ -283,6 +288,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::IRON_SHOVEL, Items::IRON_SHOVEL()); $this->map1to1Item(Ids::IRON_SWORD, Items::IRON_SWORD()); $this->map1to1Item(Ids::JUNGLE_BOAT, Items::JUNGLE_BOAT()); + $this->map1to1Item(Ids::JUNGLE_HANGING_SIGN, Items::JUNGLE_HANGING_SIGN()); $this->map1to1Item(Ids::JUNGLE_SIGN, Items::JUNGLE_SIGN()); $this->map1to1Item(Ids::LAPIS_LAZULI, Items::LAPIS_LAZULI()); $this->map1to1Item(Ids::LAVA_BUCKET, Items::LAVA_BUCKET()); @@ -293,6 +299,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::LEATHER_LEGGINGS, Items::LEATHER_PANTS()); $this->map1to1Item(Ids::MAGMA_CREAM, Items::MAGMA_CREAM()); $this->map1to1Item(Ids::MANGROVE_BOAT, Items::MANGROVE_BOAT()); + $this->map1to1Item(Ids::MANGROVE_HANGING_SIGN, Items::MANGROVE_HANGING_SIGN()); $this->map1to1Item(Ids::MANGROVE_SIGN, Items::MANGROVE_SIGN()); $this->map1to1Item(Ids::MELON_SEEDS, Items::MELON_SEEDS()); $this->map1to1Item(Ids::MELON_SLICE, Items::MELON()); @@ -336,8 +343,10 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::NETHERITE_SWORD, Items::NETHERITE_SWORD()); $this->map1to1Item(Ids::NETHERITE_UPGRADE_SMITHING_TEMPLATE, Items::NETHERITE_UPGRADE_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::OAK_BOAT, Items::OAK_BOAT()); + $this->map1to1Item(Ids::OAK_HANGING_SIGN, Items::OAK_HANGING_SIGN()); $this->map1to1Item(Ids::OAK_SIGN, Items::OAK_SIGN()); $this->map1to1Item(Ids::PAINTING, Items::PAINTING()); + $this->map1to1Item(Ids::PALE_OAK_HANGING_SIGN, Items::PALE_OAK_HANGING_SIGN()); $this->map1to1Item(Ids::PALE_OAK_SIGN, Items::PALE_OAK_SIGN()); $this->map1to1Item(Ids::PAPER, Items::PAPER()); $this->map1to1Item(Ids::PHANTOM_MEMBRANE, Items::PHANTOM_MEMBRANE()); @@ -378,6 +387,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::SPIDER_EYE, Items::SPIDER_EYE()); $this->map1to1Item(Ids::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE, Items::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::SPRUCE_BOAT, Items::SPRUCE_BOAT()); + $this->map1to1Item(Ids::SPRUCE_HANGING_SIGN, Items::SPRUCE_HANGING_SIGN()); $this->map1to1Item(Ids::SPRUCE_SIGN, Items::SPRUCE_SIGN()); $this->map1to1Item(Ids::SPYGLASS, Items::SPYGLASS()); $this->map1to1Item(Ids::SQUID_SPAWN_EGG, Items::SQUID_SPAWN_EGG()); @@ -398,6 +408,7 @@ final class ItemSerializerDeserializerRegistrar{ $this->map1to1Item(Ids::VEX_ARMOR_TRIM_SMITHING_TEMPLATE, Items::VEX_ARMOR_TRIM_SMITHING_TEMPLATE()); $this->map1to1Item(Ids::VILLAGER_SPAWN_EGG, Items::VILLAGER_SPAWN_EGG()); $this->map1to1Item(Ids::WARD_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WARD_ARMOR_TRIM_SMITHING_TEMPLATE()); + $this->map1to1Item(Ids::WARPED_HANGING_SIGN, Items::WARPED_HANGING_SIGN()); $this->map1to1Item(Ids::WARPED_SIGN, Items::WARPED_SIGN()); $this->map1to1Item(Ids::WATER_BUCKET, Items::WATER_BUCKET()); $this->map1to1Item(Ids::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE, Items::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE()); diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php new file mode 100644 index 000000000..e3e95d68a --- /dev/null +++ b/src/item/HangingSign.php @@ -0,0 +1,66 @@ +getSide(Facing::UP)->getSupportType(Facing::DOWN) === SupportType::CENTER ? + $this->centerPointCeilingVariant : + $this->edgePointCeilingVariant + : $this->wallVariant; + return clone $result; + } + + public function getBlock(?int $clickedFace = null) : Block{ + //we don't have enough information here to decide which ceiling type to use + return $clickedFace === Facing::DOWN ? clone $this->centerPointCeilingVariant : clone $this->wallVariant; + } + + public function getMaxStackSize() : int{ + return 16; + } + + public function getFuelTime() : int{ + return 200; + } +} diff --git a/src/item/Item.php b/src/item/Item.php index 205f15e13..6786238b0 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -485,6 +485,10 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } + public function getPlacementBlock(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ + return $this->getBlock($face); + } + /** * Returns the block corresponding to this Item. */ diff --git a/src/item/ItemTypeIds.php b/src/item/ItemTypeIds.php index 37be3ab9e..af32cbcc2 100644 --- a/src/item/ItemTypeIds.php +++ b/src/item/ItemTypeIds.php @@ -335,8 +335,19 @@ final class ItemTypeIds{ public const RECORD_CREATOR_MUSIC_BOX = 20296; public const RECORD_PRECIPICE = 20297; public const OMINOUS_BANNER = 20298; + public const ACACIA_HANGING_SIGN = 20299; + public const BIRCH_HANGING_SIGN = 20300; + public const CHERRY_HANGING_SIGN = 20301; + public const CRIMSON_HANGING_SIGN = 20302; + public const DARK_OAK_HANGING_SIGN = 20303; + public const JUNGLE_HANGING_SIGN = 20304; + public const MANGROVE_HANGING_SIGN = 20305; + public const OAK_HANGING_SIGN = 20306; + public const PALE_OAK_HANGING_SIGN = 20307; + public const SPRUCE_HANGING_SIGN = 20308; + public const WARPED_HANGING_SIGN = 20309; - public const FIRST_UNUSED_ITEM_ID = 20299; + public const FIRST_UNUSED_ITEM_ID = 20310; private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID; diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 7a90babed..2f316f66b 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1232,6 +1232,7 @@ final class StringToItemParser extends StringToTParser{ private static function registerItems(self $result) : void{ $result->register("acacia_boat", fn() => Items::ACACIA_BOAT()); + $result->register("acacia_hanging_sign", fn() => Items::ACACIA_HANGING_SIGN()); $result->register("amethyst_shard", fn() => Items::AMETHYST_SHARD()); $result->register("antidote", fn() => Items::MEDICINE()->setType(MedicineType::ANTIDOTE)); $result->register("apple", fn() => Items::APPLE()); @@ -1246,6 +1247,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("beetroot_seeds", fn() => Items::BEETROOT_SEEDS()); $result->register("beetroot_soup", fn() => Items::BEETROOT_SOUP()); $result->register("birch_boat", fn() => Items::BIRCH_BOAT()); + $result->register("birch_hanging_sign", fn() => Items::BIRCH_HANGING_SIGN()); $result->register("blaze_powder", fn() => Items::BLAZE_POWDER()); $result->register("blaze_rod", fn() => Items::BLAZE_ROD()); $result->register("bleach", fn() => Items::BLEACH()); @@ -1307,6 +1309,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("chemical_sulphate", fn() => Items::CHEMICAL_SULPHATE()); $result->register("chemical_tungsten_chloride", fn() => Items::CHEMICAL_TUNGSTEN_CHLORIDE()); $result->register("chemical_water", fn() => Items::CHEMICAL_WATER()); + $result->register("cherry_hanging_sign", fn() => Items::CHERRY_HANGING_SIGN()); $result->register("chicken", fn() => Items::RAW_CHICKEN()); $result->register("chorus_fruit", fn() => Items::CHORUS_FRUIT()); $result->register("chorus_fruit_popped", fn() => Items::POPPED_CHORUS_FRUIT()); @@ -1331,7 +1334,9 @@ final class StringToItemParser extends StringToTParser{ $result->register("cooked_salmon", fn() => Items::COOKED_SALMON()); $result->register("cookie", fn() => Items::COOKIE()); $result->register("copper_ingot", fn() => Items::COPPER_INGOT()); + $result->register("crimson_hanging_sign", fn() => Items::CRIMSON_HANGING_SIGN()); $result->register("dark_oak_boat", fn() => Items::DARK_OAK_BOAT()); + $result->register("dark_oak_hanging_sign", fn() => Items::DARK_OAK_HANGING_SIGN()); $result->register("diamond", fn() => Items::DIAMOND()); $result->register("diamond_axe", fn() => Items::DIAMOND_AXE()); $result->register("diamond_boots", fn() => Items::DIAMOND_BOOTS()); @@ -1416,6 +1421,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("iron_shovel", fn() => Items::IRON_SHOVEL()); $result->register("iron_sword", fn() => Items::IRON_SWORD()); $result->register("jungle_boat", fn() => Items::JUNGLE_BOAT()); + $result->register("jungle_hanging_sign", fn() => Items::JUNGLE_HANGING_SIGN()); $result->register("lapis_lazuli", fn() => Items::LAPIS_LAZULI()); $result->register("lava_bucket", fn() => Items::LAVA_BUCKET()); $result->register("leather", fn() => Items::LEATHER()); @@ -1427,6 +1433,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("leather_pants", fn() => Items::LEATHER_PANTS()); $result->register("leather_tunic", fn() => Items::LEATHER_TUNIC()); $result->register("magma_cream", fn() => Items::MAGMA_CREAM()); + $result->register("mangrove_hanging_sign", fn() => Items::MANGROVE_HANGING_SIGN()); $result->register("melon", fn() => Items::MELON()); $result->register("melon_seeds", fn() => Items::MELON_SEEDS()); $result->register("melon_slice", fn() => Items::MELON()); @@ -1458,7 +1465,9 @@ final class StringToItemParser extends StringToTParser{ $result->register("netherstar", fn() => Items::NETHER_STAR()); $result->register("netherite_upgrade_smithing_template", fn() => Items::NETHERITE_UPGRADE_SMITHING_TEMPLATE()); $result->register("oak_boat", fn() => Items::OAK_BOAT()); + $result->register("oak_hanging_sign", fn() => Items::OAK_HANGING_SIGN()); $result->register("painting", fn() => Items::PAINTING()); + $result->register("pale_oak_hanging_sign", fn() => Items::PALE_OAK_HANGING_SIGN()); $result->register("paper", fn() => Items::PAPER()); $result->register("phantom_membrane", fn() => Items::PHANTOM_MEMBRANE()); $result->register("pitcher_pod", fn() => Items::PITCHER_POD()); @@ -1532,6 +1541,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("spire_armor_trim_smithing_template", fn() => Items::SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE()); $result->register("splash_potion", fn() => Items::SPLASH_POTION()); $result->register("spruce_boat", fn() => Items::SPRUCE_BOAT()); + $result->register("spruce_hanging_sign", fn() => Items::SPRUCE_HANGING_SIGN()); $result->register("spyglass", fn() => Items::SPYGLASS()); $result->register("squid_spawn_egg", fn() => Items::SQUID_SPAWN_EGG()); $result->register("steak", fn() => Items::STEAK()); @@ -1555,6 +1565,7 @@ final class StringToItemParser extends StringToTParser{ $result->register("turtle_shell_piece", fn() => Items::SCUTE()); $result->register("villager_spawn_egg", fn() => Items::VILLAGER_SPAWN_EGG()); $result->register("ward_armor_trim_smithing_template", fn() => Items::WARD_ARMOR_TRIM_SMITHING_TEMPLATE()); + $result->register("warped_hanging_sign", fn() => Items::WARPED_HANGING_SIGN()); $result->register("water_bucket", fn() => Items::WATER_BUCKET()); $result->register("wayfinder_armor_trim_smithing_template", fn() => Items::WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE()); $result->register("wheat", fn() => Items::WHEAT()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 7103d8878..e4eeffc1d 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -48,6 +48,7 @@ use function strtolower; * @generate-registry-docblock * * @method static Boat ACACIA_BOAT() + * @method static HangingSign ACACIA_HANGING_SIGN() * @method static ItemBlockWallOrFloor ACACIA_SIGN() * @method static ItemBlock AIR() * @method static Item AMETHYST_SHARD() @@ -60,6 +61,7 @@ use function strtolower; * @method static BeetrootSeeds BEETROOT_SEEDS() * @method static BeetrootSoup BEETROOT_SOUP() * @method static Boat BIRCH_BOAT() + * @method static HangingSign BIRCH_HANGING_SIGN() * @method static ItemBlockWallOrFloor BIRCH_SIGN() * @method static Item BLAZE_POWDER() * @method static BlazeRod BLAZE_ROD() @@ -116,6 +118,7 @@ use function strtolower; * @method static Item CHEMICAL_SULPHATE() * @method static Item CHEMICAL_TUNGSTEN_CHLORIDE() * @method static Item CHEMICAL_WATER() + * @method static HangingSign CHERRY_HANGING_SIGN() * @method static ItemBlockWallOrFloor CHERRY_SIGN() * @method static ChorusFruit CHORUS_FRUIT() * @method static Item CLAY() @@ -134,8 +137,10 @@ use function strtolower; * @method static Cookie COOKIE() * @method static Item COPPER_INGOT() * @method static CoralFan CORAL_FAN() + * @method static HangingSign CRIMSON_HANGING_SIGN() * @method static ItemBlockWallOrFloor CRIMSON_SIGN() * @method static Boat DARK_OAK_BOAT() + * @method static HangingSign DARK_OAK_HANGING_SIGN() * @method static ItemBlockWallOrFloor DARK_OAK_SIGN() * @method static Item DIAMOND() * @method static Axe DIAMOND_AXE() @@ -206,6 +211,7 @@ use function strtolower; * @method static Shovel IRON_SHOVEL() * @method static Sword IRON_SWORD() * @method static Boat JUNGLE_BOAT() + * @method static HangingSign JUNGLE_HANGING_SIGN() * @method static ItemBlockWallOrFloor JUNGLE_SIGN() * @method static Item LAPIS_LAZULI() * @method static LiquidBucket LAVA_BUCKET() @@ -216,6 +222,7 @@ use function strtolower; * @method static Armor LEATHER_TUNIC() * @method static Item MAGMA_CREAM() * @method static Boat MANGROVE_BOAT() + * @method static HangingSign MANGROVE_HANGING_SIGN() * @method static ItemBlockWallOrFloor MANGROVE_SIGN() * @method static Medicine MEDICINE() * @method static Melon MELON() @@ -241,9 +248,11 @@ use function strtolower; * @method static Item NETHER_QUARTZ() * @method static Item NETHER_STAR() * @method static Boat OAK_BOAT() + * @method static HangingSign OAK_HANGING_SIGN() * @method static ItemBlockWallOrFloor OAK_SIGN() * @method static ItemBlockWallOrFloor OMINOUS_BANNER() * @method static PaintingItem PAINTING() + * @method static HangingSign PALE_OAK_HANGING_SIGN() * @method static ItemBlockWallOrFloor PALE_OAK_SIGN() * @method static Item PAPER() * @method static Item PHANTOM_MEMBRANE() @@ -308,6 +317,7 @@ use function strtolower; * @method static Item SPIRE_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static SplashPotion SPLASH_POTION() * @method static Boat SPRUCE_BOAT() + * @method static HangingSign SPRUCE_HANGING_SIGN() * @method static ItemBlockWallOrFloor SPRUCE_SIGN() * @method static Spyglass SPYGLASS() * @method static SpawnEgg SQUID_SPAWN_EGG() @@ -329,6 +339,7 @@ use function strtolower; * @method static Item VEX_ARMOR_TRIM_SMITHING_TEMPLATE() * @method static SpawnEgg VILLAGER_SPAWN_EGG() * @method static Item WARD_ARMOR_TRIM_SMITHING_TEMPLATE() + * @method static HangingSign WARPED_HANGING_SIGN() * @method static ItemBlockWallOrFloor WARPED_SIGN() * @method static LiquidBucket WATER_BUCKET() * @method static Item WAYFINDER_ARMOR_TRIM_SMITHING_TEMPLATE() @@ -398,6 +409,7 @@ final class VanillaItems{ self::_registryRegister("air", Blocks::AIR()->asItem()->setCount(0)); self::register("acacia_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN())); + self::register("acacia_hanging_sign", fn(IID $id) => new HangingSign($id, "Acacia Hanging Sign", Blocks::ACACIA_CEILING_CENTER_HANGING_SIGN(), Blocks::ACACIA_CEILING_EDGES_HANGING_SIGN(), Blocks::ACACIA_WALL_HANGING_SIGN())); self::register("amethyst_shard", fn(IID $id) => new Item($id, "Amethyst Shard")); self::register("apple", fn(IID $id) => new Apple($id, "Apple")); self::register("arrow", fn(IID $id) => new Arrow($id, "Arrow")); @@ -408,6 +420,7 @@ final class VanillaItems{ self::register("beetroot_seeds", fn(IID $id) => new BeetrootSeeds($id, "Beetroot Seeds")); self::register("beetroot_soup", fn(IID $id) => new BeetrootSoup($id, "Beetroot Soup")); self::register("birch_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::BIRCH_SIGN(), Blocks::BIRCH_WALL_SIGN())); + self::register("birch_hanging_sign", fn(IID $id) => new HangingSign($id, "Birch Hanging Sign", Blocks::BIRCH_CEILING_CENTER_HANGING_SIGN(), Blocks::BIRCH_CEILING_EDGES_HANGING_SIGN(), Blocks::BIRCH_WALL_HANGING_SIGN())); self::register("blaze_powder", fn(IID $id) => new Item($id, "Blaze Powder")); self::register("blaze_rod", fn(IID $id) => new BlazeRod($id, "Blaze Rod")); self::register("bleach", fn(IID $id) => new Item($id, "Bleach")); @@ -422,6 +435,7 @@ final class VanillaItems{ self::register("carrot", fn(IID $id) => new Carrot($id, "Carrot")); self::register("charcoal", fn(IID $id) => new Coal($id, "Charcoal")); self::register("cherry_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CHERRY_SIGN(), Blocks::CHERRY_WALL_SIGN())); + self::register("cherry_hanging_sign", fn(IID $id) => new HangingSign($id, "Cherry Hanging Sign", Blocks::CHERRY_CEILING_CENTER_HANGING_SIGN(), Blocks::CHERRY_CEILING_EDGES_HANGING_SIGN(), Blocks::CHERRY_WALL_HANGING_SIGN())); self::register("chemical_aluminium_oxide", fn(IID $id) => new Item($id, "Aluminium Oxide")); self::register("chemical_ammonia", fn(IID $id) => new Item($id, "Ammonia")); self::register("chemical_barium_sulphate", fn(IID $id) => new Item($id, "Barium Sulphate")); @@ -477,7 +491,9 @@ final class VanillaItems{ self::register("copper_ingot", fn(IID $id) => new Item($id, "Copper Ingot")); self::register("coral_fan", fn(IID $id) => new CoralFan($id)); self::register("crimson_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::CRIMSON_SIGN(), Blocks::CRIMSON_WALL_SIGN())); + self::register("crimson_hanging_sign", fn(IID $id) => new HangingSign($id, "Crimson Hanging Sign", Blocks::CRIMSON_CEILING_CENTER_HANGING_SIGN(), Blocks::CRIMSON_CEILING_EDGES_HANGING_SIGN(), Blocks::CRIMSON_WALL_HANGING_SIGN())); self::register("dark_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::DARK_OAK_SIGN(), Blocks::DARK_OAK_WALL_SIGN())); + self::register("dark_oak_hanging_sign", fn(IID $id) => new HangingSign($id, "Dark Oak Hanging Sign", Blocks::DARK_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::DARK_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::DARK_OAK_WALL_HANGING_SIGN())); self::register("diamond", fn(IID $id) => new Item($id, "Diamond")); self::register("disc_fragment_5", fn(IID $id) => new Item($id, "Disc Fragment (5)")); self::register("dragon_breath", fn(IID $id) => new Item($id, "Dragon's Breath")); @@ -518,11 +534,13 @@ final class VanillaItems{ self::register("iron_ingot", fn(IID $id) => new Item($id, "Iron Ingot")); self::register("iron_nugget", fn(IID $id) => new Item($id, "Iron Nugget")); self::register("jungle_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::JUNGLE_SIGN(), Blocks::JUNGLE_WALL_SIGN())); + self::register("jungle_hanging_sign", fn(IID $id) => new HangingSign($id, "Jungle Hanging Sign", Blocks::JUNGLE_CEILING_CENTER_HANGING_SIGN(), Blocks::JUNGLE_CEILING_EDGES_HANGING_SIGN(), Blocks::JUNGLE_WALL_HANGING_SIGN())); self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli")); self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA())); self::register("leather", fn(IID $id) => new Item($id, "Leather")); self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream")); self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); + self::register("mangrove_hanging_sign", fn(IID $id) => new HangingSign($id, "Mangrove Hanging Sign", Blocks::MANGROVE_CEILING_CENTER_HANGING_SIGN(), Blocks::MANGROVE_CEILING_EDGES_HANGING_SIGN(), Blocks::MANGROVE_WALL_HANGING_SIGN())); self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine")); self::register("melon", fn(IID $id) => new Melon($id, "Melon")); self::register("melon_seeds", fn(IID $id) => new MelonSeeds($id, "Melon Seeds")); @@ -541,9 +559,11 @@ final class VanillaItems{ public function isFireProof() : bool{ return true; } }); self::register("oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OAK_SIGN(), Blocks::OAK_WALL_SIGN())); + self::register("oak_hanging_sign", fn(IID $id) => new HangingSign($id, "Oak Hanging Sign", Blocks::OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::OAK_WALL_HANGING_SIGN())); self::register("ominous_banner", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::OMINOUS_BANNER(), Blocks::OMINOUS_WALL_BANNER())); self::register("painting", fn(IID $id) => new PaintingItem($id, "Painting")); self::register("pale_oak_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::PALE_OAK_SIGN(), Blocks::PALE_OAK_WALL_SIGN())); + self::register("pale_oak_hanging_sign", fn(IID $id) => new HangingSign($id, "Pale Oak Hanging Sign", Blocks::PALE_OAK_CEILING_CENTER_HANGING_SIGN(), Blocks::PALE_OAK_CEILING_EDGES_HANGING_SIGN(), Blocks::PALE_OAK_WALL_HANGING_SIGN())); self::register("paper", fn(IID $id) => new Item($id, "Paper")); self::register("phantom_membrane", fn(IID $id) => new Item($id, "Phantom Membrane")); self::register("pitcher_pod", fn(IID $id) => new PitcherPod($id, "Pitcher Pod")); @@ -600,6 +620,7 @@ final class VanillaItems{ self::register("spider_eye", fn(IID $id) => new SpiderEye($id, "Spider Eye")); self::register("splash_potion", fn(IID $id) => new SplashPotion($id, "Splash Potion")); self::register("spruce_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::SPRUCE_SIGN(), Blocks::SPRUCE_WALL_SIGN())); + self::register("spruce_hanging_sign", fn(IID $id) => new HangingSign($id, "Spruce Hanging Sign", Blocks::SPRUCE_CEILING_CENTER_HANGING_SIGN(), Blocks::SPRUCE_CEILING_EDGES_HANGING_SIGN(), Blocks::SPRUCE_WALL_HANGING_SIGN())); self::register("spyglass", fn(IID $id) => new Spyglass($id, "Spyglass")); self::register("steak", fn(IID $id) => new Steak($id, "Steak")); self::register("stick", fn(IID $id) => new Stick($id, "Stick")); @@ -610,6 +631,7 @@ final class VanillaItems{ self::register("torchflower_seeds", fn(IID $id) => new TorchflowerSeeds($id, "Torchflower Seeds")); self::register("totem", fn(IID $id) => new Totem($id, "Totem of Undying")); self::register("warped_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::WARPED_SIGN(), Blocks::WARPED_WALL_SIGN())); + self::register("warped_hanging_sign", fn(IID $id) => new HangingSign($id, "Warped Hanging Sign", Blocks::WARPED_CEILING_CENTER_HANGING_SIGN(), Blocks::WARPED_CEILING_EDGES_HANGING_SIGN(), Blocks::WARPED_WALL_HANGING_SIGN())); self::register("water_bucket", fn(IID $id) => new LiquidBucket($id, "Water Bucket", Blocks::WATER())); self::register("wheat", fn(IID $id) => new Item($id, "Wheat")); self::register("wheat_seeds", fn(IID $id) => new WheatSeeds($id, "Wheat Seeds")); diff --git a/src/world/World.php b/src/world/World.php index bd790c299..4f2e222ca 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2298,7 +2298,7 @@ class World implements ChunkManager{ if($item->isNull() || !$item->canBePlaced()){ return false; } - $hand = $item->getBlock($face); + $hand = $item->getPlacementBlock($blockReplace, $blockClicked, $face, $clickVector); $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ diff --git a/tests/phpunit/block/block_factory_consistency_check.json b/tests/phpunit/block/block_factory_consistency_check.json index 86a44113f..b96607c0b 100644 --- a/tests/phpunit/block/block_factory_consistency_check.json +++ b/tests/phpunit/block/block_factory_consistency_check.json @@ -1,6 +1,8 @@ { "stateCounts": { "ACACIA_BUTTON": 12, + "ACACIA_CEILING_CENTER_HANGING_SIGN": 16, + "ACACIA_CEILING_EDGES_HANGING_SIGN": 4, "ACACIA_DOOR": 32, "ACACIA_FENCE": 1, "ACACIA_FENCE_GATE": 16, @@ -13,6 +15,7 @@ "ACACIA_SLAB": 3, "ACACIA_STAIRS": 8, "ACACIA_TRAPDOOR": 16, + "ACACIA_WALL_HANGING_SIGN": 4, "ACACIA_WALL_SIGN": 4, "ACACIA_WOOD": 6, "ACTIVATOR_RAIL": 12, @@ -43,6 +46,8 @@ "BIG_DRIPLEAF_HEAD": 16, "BIG_DRIPLEAF_STEM": 4, "BIRCH_BUTTON": 12, + "BIRCH_CEILING_CENTER_HANGING_SIGN": 16, + "BIRCH_CEILING_EDGES_HANGING_SIGN": 4, "BIRCH_DOOR": 32, "BIRCH_FENCE": 1, "BIRCH_FENCE_GATE": 16, @@ -55,6 +60,7 @@ "BIRCH_SLAB": 3, "BIRCH_STAIRS": 8, "BIRCH_TRAPDOOR": 16, + "BIRCH_WALL_HANGING_SIGN": 4, "BIRCH_WALL_SIGN": 4, "BIRCH_WOOD": 6, "BLACKSTONE": 1, @@ -91,6 +97,8 @@ "CHAIN": 3, "CHEMICAL_HEAT": 1, "CHERRY_BUTTON": 12, + "CHERRY_CEILING_CENTER_HANGING_SIGN": 16, + "CHERRY_CEILING_EDGES_HANGING_SIGN": 4, "CHERRY_DOOR": 32, "CHERRY_FENCE": 1, "CHERRY_FENCE_GATE": 16, @@ -102,6 +110,7 @@ "CHERRY_SLAB": 3, "CHERRY_STAIRS": 8, "CHERRY_TRAPDOOR": 16, + "CHERRY_WALL_HANGING_SIGN": 4, "CHERRY_WALL_SIGN": 4, "CHERRY_WOOD": 6, "CHEST": 4, @@ -152,6 +161,8 @@ "CRACKED_STONE_BRICKS": 1, "CRAFTING_TABLE": 1, "CRIMSON_BUTTON": 12, + "CRIMSON_CEILING_CENTER_HANGING_SIGN": 16, + "CRIMSON_CEILING_EDGES_HANGING_SIGN": 4, "CRIMSON_DOOR": 32, "CRIMSON_FENCE": 1, "CRIMSON_FENCE_GATE": 16, @@ -164,6 +175,7 @@ "CRIMSON_STAIRS": 8, "CRIMSON_STEM": 6, "CRIMSON_TRAPDOOR": 16, + "CRIMSON_WALL_HANGING_SIGN": 4, "CRIMSON_WALL_SIGN": 4, "CRYING_OBSIDIAN": 1, "CUT_COPPER": 8, @@ -175,6 +187,8 @@ "CUT_SANDSTONE_SLAB": 3, "DANDELION": 1, "DARK_OAK_BUTTON": 12, + "DARK_OAK_CEILING_CENTER_HANGING_SIGN": 16, + "DARK_OAK_CEILING_EDGES_HANGING_SIGN": 4, "DARK_OAK_DOOR": 32, "DARK_OAK_FENCE": 1, "DARK_OAK_FENCE_GATE": 16, @@ -187,6 +201,7 @@ "DARK_OAK_SLAB": 3, "DARK_OAK_STAIRS": 8, "DARK_OAK_TRAPDOOR": 16, + "DARK_OAK_WALL_HANGING_SIGN": 4, "DARK_OAK_WALL_SIGN": 4, "DARK_OAK_WOOD": 6, "DARK_PRISMARINE": 1, @@ -409,6 +424,8 @@ "ITEM_FRAME": 12, "JUKEBOX": 1, "JUNGLE_BUTTON": 12, + "JUNGLE_CEILING_CENTER_HANGING_SIGN": 16, + "JUNGLE_CEILING_EDGES_HANGING_SIGN": 4, "JUNGLE_DOOR": 32, "JUNGLE_FENCE": 1, "JUNGLE_FENCE_GATE": 16, @@ -421,6 +438,7 @@ "JUNGLE_SLAB": 3, "JUNGLE_STAIRS": 8, "JUNGLE_TRAPDOOR": 16, + "JUNGLE_WALL_HANGING_SIGN": 4, "JUNGLE_WALL_SIGN": 4, "JUNGLE_WOOD": 6, "LAB_TABLE": 4, @@ -443,6 +461,8 @@ "LOOM": 4, "MAGMA": 1, "MANGROVE_BUTTON": 12, + "MANGROVE_CEILING_CENTER_HANGING_SIGN": 16, + "MANGROVE_CEILING_EDGES_HANGING_SIGN": 4, "MANGROVE_DOOR": 32, "MANGROVE_FENCE": 1, "MANGROVE_FENCE_GATE": 16, @@ -455,6 +475,7 @@ "MANGROVE_SLAB": 3, "MANGROVE_STAIRS": 8, "MANGROVE_TRAPDOOR": 16, + "MANGROVE_WALL_HANGING_SIGN": 4, "MANGROVE_WALL_SIGN": 4, "MANGROVE_WOOD": 6, "MATERIAL_REDUCER": 4, @@ -493,6 +514,8 @@ "NETHER_WART_BLOCK": 1, "NOTE_BLOCK": 1, "OAK_BUTTON": 12, + "OAK_CEILING_CENTER_HANGING_SIGN": 16, + "OAK_CEILING_EDGES_HANGING_SIGN": 4, "OAK_DOOR": 32, "OAK_FENCE": 1, "OAK_FENCE_GATE": 16, @@ -505,6 +528,7 @@ "OAK_SLAB": 3, "OAK_STAIRS": 8, "OAK_TRAPDOOR": 16, + "OAK_WALL_HANGING_SIGN": 4, "OAK_WALL_SIGN": 4, "OAK_WOOD": 6, "OBSIDIAN": 1, @@ -515,6 +539,8 @@ "PACKED_ICE": 1, "PACKED_MUD": 1, "PALE_OAK_BUTTON": 12, + "PALE_OAK_CEILING_CENTER_HANGING_SIGN": 16, + "PALE_OAK_CEILING_EDGES_HANGING_SIGN": 4, "PALE_OAK_DOOR": 32, "PALE_OAK_FENCE": 1, "PALE_OAK_FENCE_GATE": 16, @@ -526,6 +552,7 @@ "PALE_OAK_SLAB": 3, "PALE_OAK_STAIRS": 8, "PALE_OAK_TRAPDOOR": 16, + "PALE_OAK_WALL_HANGING_SIGN": 4, "PALE_OAK_WALL_SIGN": 4, "PALE_OAK_WOOD": 6, "PEONY": 2, @@ -656,6 +683,8 @@ "SPONGE": 2, "SPORE_BLOSSOM": 1, "SPRUCE_BUTTON": 12, + "SPRUCE_CEILING_CENTER_HANGING_SIGN": 16, + "SPRUCE_CEILING_EDGES_HANGING_SIGN": 4, "SPRUCE_DOOR": 32, "SPRUCE_FENCE": 1, "SPRUCE_FENCE_GATE": 16, @@ -668,6 +697,7 @@ "SPRUCE_SLAB": 3, "SPRUCE_STAIRS": 8, "SPRUCE_TRAPDOOR": 16, + "SPRUCE_WALL_HANGING_SIGN": 4, "SPRUCE_WALL_SIGN": 4, "SPRUCE_WOOD": 6, "STAINED_CLAY": 16, @@ -711,6 +741,8 @@ "WALL_BANNER": 64, "WALL_CORAL_FAN": 40, "WARPED_BUTTON": 12, + "WARPED_CEILING_CENTER_HANGING_SIGN": 16, + "WARPED_CEILING_EDGES_HANGING_SIGN": 4, "WARPED_DOOR": 32, "WARPED_FENCE": 1, "WARPED_FENCE_GATE": 16, @@ -723,6 +755,7 @@ "WARPED_STAIRS": 8, "WARPED_STEM": 6, "WARPED_TRAPDOOR": 16, + "WARPED_WALL_HANGING_SIGN": 4, "WARPED_WALL_SIGN": 4, "WARPED_WART_BLOCK": 1, "WATER": 32, @@ -736,26 +769,41 @@ "WOOL": 16 }, "tiles": { + "ACACIA_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "ACACIA_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "ACACIA_SIGN": "pocketmine\\block\\tile\\Sign", + "ACACIA_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "ACACIA_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "BANNER": "pocketmine\\block\\tile\\Banner", "BARREL": "pocketmine\\block\\tile\\Barrel", "BEACON": "pocketmine\\block\\tile\\Beacon", "BED": "pocketmine\\block\\tile\\Bed", "BELL": "pocketmine\\block\\tile\\Bell", + "BIRCH_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "BIRCH_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "BIRCH_SIGN": "pocketmine\\block\\tile\\Sign", + "BIRCH_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "BIRCH_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "BLAST_FURNACE": "pocketmine\\block\\tile\\BlastFurnace", "BREWING_STAND": "pocketmine\\block\\tile\\BrewingStand", "CAMPFIRE": "pocketmine\\block\\tile\\Campfire", "CAULDRON": "pocketmine\\block\\tile\\Cauldron", + "CHERRY_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "CHERRY_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CHERRY_SIGN": "pocketmine\\block\\tile\\Sign", + "CHERRY_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CHERRY_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "CHEST": "pocketmine\\block\\tile\\Chest", "CHISELED_BOOKSHELF": "pocketmine\\block\\tile\\ChiseledBookshelf", + "CRIMSON_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "CRIMSON_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CRIMSON_SIGN": "pocketmine\\block\\tile\\Sign", + "CRIMSON_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "CRIMSON_WALL_SIGN": "pocketmine\\block\\tile\\Sign", + "DARK_OAK_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "DARK_OAK_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "DARK_OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "DARK_OAK_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "DARK_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "DAYLIGHT_SENSOR": "pocketmine\\block\\tile\\DaylightSensor", "DYED_SHULKER_BOX": "pocketmine\\block\\tile\\ShulkerBox", @@ -767,31 +815,49 @@ "HOPPER": "pocketmine\\block\\tile\\Hopper", "ITEM_FRAME": "pocketmine\\block\\tile\\ItemFrame", "JUKEBOX": "pocketmine\\block\\tile\\Jukebox", + "JUNGLE_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "JUNGLE_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "JUNGLE_SIGN": "pocketmine\\block\\tile\\Sign", + "JUNGLE_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "JUNGLE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "LAVA_CAULDRON": "pocketmine\\block\\tile\\Cauldron", "LECTERN": "pocketmine\\block\\tile\\Lectern", + "MANGROVE_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "MANGROVE_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "MANGROVE_SIGN": "pocketmine\\block\\tile\\Sign", + "MANGROVE_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "MANGROVE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "MOB_HEAD": "pocketmine\\block\\tile\\MobHead", "MONSTER_SPAWNER": "pocketmine\\block\\tile\\MonsterSpawner", "NOTE_BLOCK": "pocketmine\\block\\tile\\Note", + "OAK_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "OAK_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "OAK_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "OMINOUS_BANNER": "pocketmine\\block\\tile\\Banner", "OMINOUS_WALL_BANNER": "pocketmine\\block\\tile\\Banner", + "PALE_OAK_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "PALE_OAK_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "PALE_OAK_SIGN": "pocketmine\\block\\tile\\Sign", + "PALE_OAK_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "PALE_OAK_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "POTION_CAULDRON": "pocketmine\\block\\tile\\Cauldron", "REDSTONE_COMPARATOR": "pocketmine\\block\\tile\\Comparator", "SHULKER_BOX": "pocketmine\\block\\tile\\ShulkerBox", "SMOKER": "pocketmine\\block\\tile\\Smoker", "SOUL_CAMPFIRE": "pocketmine\\block\\tile\\Campfire", + "SPRUCE_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "SPRUCE_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "SPRUCE_SIGN": "pocketmine\\block\\tile\\Sign", + "SPRUCE_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "SPRUCE_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "TRAPPED_CHEST": "pocketmine\\block\\tile\\Chest", "WALL_BANNER": "pocketmine\\block\\tile\\Banner", + "WARPED_CEILING_CENTER_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", + "WARPED_CEILING_EDGES_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "WARPED_SIGN": "pocketmine\\block\\tile\\Sign", + "WARPED_WALL_HANGING_SIGN": "pocketmine\\block\\tile\\HangingSign", "WARPED_WALL_SIGN": "pocketmine\\block\\tile\\Sign", "WATER_CAULDRON": "pocketmine\\block\\tile\\Cauldron" } From 0e498720bddc34407dc2b48526ad9c6783624c13 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 20:10:34 +0100 Subject: [PATCH 10/11] Regenerate phpstan-bugs baseline --- tests/phpstan/configs/phpstan-bugs.neon | 78 +++++++++++++++++++++---- 1 file changed, 66 insertions(+), 12 deletions(-) diff --git a/tests/phpstan/configs/phpstan-bugs.neon b/tests/phpstan/configs/phpstan-bugs.neon index cb92bf968..e67f5768e 100644 --- a/tests/phpstan/configs/phpstan-bugs.neon +++ b/tests/phpstan/configs/phpstan-bugs.neon @@ -18,78 +18,138 @@ parameters: count: 1 path: ../../../src/Server.php - - - message: '#^Method pocketmine\\block\\Block\:\:readStateFromWorld\(\) is marked as impure but does not have any side effects\.$#' - identifier: impureMethod.pure - count: 1 - path: ../../../src/block/Block.php - - message: '#^Method pocketmine\\block\\DoubleTallGrass\:\:traitGetDropsForIncompatibleTool\(\) return type has no value type specified in iterable type array\.$#' identifier: missingType.iterableValue count: 1 path: ../../../src/block/DoubleTallGrass.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:ACACIA_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:ACACIA_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:BIRCH_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:BIRCH_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CHERRY_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CHERRY_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CRIMSON_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:CRIMSON_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:DARK_OAK_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:DARK_OAK_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:JUNGLE_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:JUNGLE_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:MANGROVE_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:MANGROVE_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:OAK_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:OAK_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:PALE_OAK_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:PALE_OAK_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:SPRUCE_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:SPRUCE_SIGN\(\)\.$#' identifier: callable.nonNativeMethod count: 1 path: ../../../src/block/VanillaBlocks.php + - + message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:WARPED_HANGING_SIGN\(\)\.$#' + identifier: callable.nonNativeMethod + count: 1 + path: ../../../src/block/VanillaBlocks.php + - message: '#^Creating callable from a non\-native static method pocketmine\\item\\VanillaItems\:\:WARPED_SIGN\(\)\.$#' identifier: callable.nonNativeMethod @@ -252,9 +312,3 @@ parameters: count: 2 path: ../../phpunit/promise/PromiseTest.php - - - message: '#^Strict comparison using \=\=\= between 0 and 0 will always evaluate to true\.$#' - identifier: identical.alwaysTrue - count: 1 - path: ../rules/UnsafeForeachArrayWithStringKeysRule.php - From 31f6f5d2522f088b78c57188de8ecb967df2cdb6 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Aug 2025 20:13:15 +0100 Subject: [PATCH 11/11] CS again --- src/block/tile/HangingSign.php | 2 -- src/block/tile/TileFactory.php | 1 - src/item/HangingSign.php | 1 - 3 files changed, 4 deletions(-) diff --git a/src/block/tile/HangingSign.php b/src/block/tile/HangingSign.php index 9bf088f47..a5be9ba5c 100644 --- a/src/block/tile/HangingSign.php +++ b/src/block/tile/HangingSign.php @@ -23,8 +23,6 @@ declare(strict_types=1); namespace pocketmine\block\tile; -use pocketmine\nbt\tag\CompoundTag; - /** * @deprecated */ diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php index 6cab78b2c..108483894 100644 --- a/src/block/tile/TileFactory.php +++ b/src/block/tile/TileFactory.php @@ -23,7 +23,6 @@ declare(strict_types=1); namespace pocketmine\block\tile; -use pocketmine\block\HangingRoots; use pocketmine\data\SavedDataLoadingException; use pocketmine\math\Vector3; use pocketmine\nbt\NbtException; diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php index e3e95d68a..7143637ba 100644 --- a/src/item/HangingSign.php +++ b/src/item/HangingSign.php @@ -27,7 +27,6 @@ use pocketmine\block\Block; use pocketmine\block\utils\SupportType; use pocketmine\math\Facing; use pocketmine\math\Vector3; -use pocketmine\player\Player; final class HangingSign extends Item{