setFacing($in->readFacingDirection()) ->setPressed($in->readBool(BlockStateNames::BUTTON_PRESSED_BIT)); } /** @throws BlockStateDeserializeException */ public static function decodeCandle(Candle $block, BlockStateReader $in) : Candle{ return $block ->setCount($in->readBoundedInt(StateNames::CANDLES, 0, 3) + 1) ->setLit($in->readBool(StateNames::LIT)); } /** * @phpstan-template TCrops of Crops * @phpstan-param TCrops $block * @phpstan-return TCrops * * @throws BlockStateDeserializeException */ public static function decodeCrops(Crops $block, BlockStateReader $in) : Crops{ return $block->setAge($in->readBoundedInt(BlockStateNames::GROWTH, 0, 7)); } /** @throws BlockStateDeserializeException */ public static function decodeComparator(RedstoneComparator $block, BlockStateReader $in) : RedstoneComparator{ return $block ->setFacing($in->readCardinalHorizontalFacing()) ->setPowered($in->readBool(BlockStateNames::OUTPUT_LIT_BIT)) ->setSubtractMode($in->readBool(BlockStateNames::OUTPUT_SUBTRACT_BIT)); } /** * @phpstan-template TBlock of Copper|CopperSlab|CopperStairs * * @phpstan-param TBlock $block * @phpstan-return TBlock */ public static function decodeCopper(Copper|CopperSlab|CopperStairs $block, CopperOxidation $oxidation) : Copper|CopperSlab|CopperStairs{ $block->setOxidation($oxidation); $block->setWaxed(false); return $block; } /** * @phpstan-template TBlock of Copper|CopperSlab|CopperStairs * * @phpstan-param TBlock $block * @phpstan-return TBlock */ public static function decodeWaxedCopper(Copper|CopperSlab|CopperStairs $block, CopperOxidation $oxidation) : Copper|CopperSlab|CopperStairs{ $block->setOxidation($oxidation); $block->setWaxed(true); return $block; } /** @throws BlockStateDeserializeException */ public static function decodeDaylightSensor(DaylightSensor $block, BlockStateReader $in) : DaylightSensor{ return $block ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); } /** @throws BlockStateDeserializeException */ public static function decodeDoor(Door $block, BlockStateReader $in) : Door{ //TODO: check if these need any special treatment to get the appropriate data to both halves of the door return $block ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)) ->setFacing(Facing::rotateY($in->readLegacyHorizontalFacing(), false)) ->setHingeRight($in->readBool(BlockStateNames::DOOR_HINGE_BIT)) ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } /** @throws BlockStateDeserializeException */ public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{ return $block ->setFacing($in->readLegacyHorizontalFacing()) ->setInWall($in->readBool(BlockStateNames::IN_WALL_BIT)) ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } /** @throws BlockStateDeserializeException */ public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{ return $block ->setCoralType($in->readCoralType()) ->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"), }); } /** @throws BlockStateDeserializeException */ public static function decodeFloorSign(FloorSign $block, BlockStateReader $in) : FloorSign{ return $block ->setRotation($in->readBoundedInt(BlockStateNames::GROUND_SIGN_DIRECTION, 0, 15)); } public static function decodeItemFrame(ItemFrame $block, BlockStateReader $in) : ItemFrame{ $in->todo(StateNames::ITEM_FRAME_PHOTO_BIT); //TODO: not sure what the point of this is return $block ->setFacing($in->readFacingDirection()) ->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT)); } /** @throws BlockStateDeserializeException */ public static function decodeLeaves(Leaves $block, BlockStateReader $in) : Leaves{ return $block ->setNoDecay($in->readBool(StateNames::PERSISTENT_BIT)) ->setCheckDecay($in->readBool(StateNames::UPDATE_BIT)); } /** @throws BlockStateDeserializeException */ public static function decodeLiquid(Liquid $block, BlockStateReader $in, bool $still) : Liquid{ $fluidHeightState = $in->readBoundedInt(BlockStateNames::LIQUID_DEPTH, 0, 15); return $block ->setDecay($fluidHeightState & 0x7) ->setFalling(($fluidHeightState & 0x8) !== 0) ->setStill($still); } public static function decodeFlowingLiquid(Liquid $block, BlockStateReader $in) : Liquid{ return self::decodeLiquid($block, $in, false); } public static function decodeStillLiquid(Liquid $block, BlockStateReader $in) : Liquid{ return self::decodeLiquid($block, $in, true); } /** @throws BlockStateDeserializeException */ public static function decodeLog(Wood $block, bool $stripped, BlockStateReader $in) : Wood{ return $block ->setAxis($in->readPillarAxis()) ->setStripped($stripped); } /** @throws BlockStateDeserializeException */ public static function decodeMushroomBlock(RedMushroomBlock $block, BlockStateReader $in) : Block{ switch($type = $in->readBoundedInt(BlockStateNames::HUGE_MUSHROOM_BITS, 0, 15)){ case BlockLegacyMetadata::MUSHROOM_BLOCK_ALL_STEM: return VanillaBlocks::ALL_SIDED_MUSHROOM_STEM(); case BlockLegacyMetadata::MUSHROOM_BLOCK_STEM: return VanillaBlocks::MUSHROOM_STEM(); default: //invalid types get left as default $type = MushroomBlockTypeIdMap::getInstance()->fromId($type); return $type !== null ? $block->setMushroomBlockType($type) : $block; } } /** @throws BlockStateDeserializeException */ public static function decodeRepeater(RedstoneRepeater $block, BlockStateReader $in) : RedstoneRepeater{ return $block ->setFacing($in->readCardinalHorizontalFacing()) ->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1); } /** @throws BlockStateDeserializeException */ public static function decodeSimplePressurePlate(SimplePressurePlate $block, BlockStateReader $in) : SimplePressurePlate{ //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... return $block->setPressed($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15) !== 0); } /** @throws BlockStateDeserializeException */ public static function decodeStairs(Stair $block, BlockStateReader $in) : Stair{ return $block ->setUpsideDown($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT)) ->setFacing($in->readWeirdoHorizontalFacing()); } /** @throws BlockStateDeserializeException */ public static function decodeStem(Stem $block, BlockStateReader $in) : Stem{ //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. $facing = $in->readFacingWithoutUp(); return self::decodeCrops($block, $in) ->setFacing($facing === Facing::DOWN ? Facing::UP : $facing); } /** @throws BlockStateDeserializeException */ public static function decodeTrapdoor(Trapdoor $block, BlockStateReader $in) : Trapdoor{ return $block ->setFacing($in->read5MinusHorizontalFacing()) ->setTop($in->readBool(BlockStateNames::UPSIDE_DOWN_BIT)) ->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); } /** @throws BlockStateDeserializeException */ public static function decodeWall(Wall $block, BlockStateReader $in) : Wall{ $block->setPost($in->readBool(BlockStateNames::WALL_POST_BIT)); $block->setConnection(Facing::NORTH, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_NORTH)); $block->setConnection(Facing::SOUTH, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_SOUTH)); $block->setConnection(Facing::WEST, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST)); $block->setConnection(Facing::EAST, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_EAST)); return $block; } /** @throws BlockStateDeserializeException */ public static function decodeWallCoralFan(WallCoralFan $block, BlockStateReader $in) : WallCoralFan{ return $block ->setDead($in->readBool(BlockStateNames::DEAD_BIT)) ->setFacing($in->readCoralFacing()); } /** @throws BlockStateDeserializeException */ public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{ return $block ->setFacing($in->readHorizontalFacing()); } public static function decodeWeightedPressurePlate(WeightedPressurePlate $block, BlockStateReader $in) : WeightedPressurePlate{ return $block ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); } /** @throws BlockStateDeserializeException */ public static function mapLegacyWallType(BlockStateReader $in) : Wall{ return self::decodeWall(match($type = $in->readString(BlockStateNames::WALL_BLOCK_TYPE)){ StringValues::WALL_BLOCK_TYPE_ANDESITE => VanillaBlocks::ANDESITE_WALL(), StringValues::WALL_BLOCK_TYPE_BRICK => VanillaBlocks::BRICK_WALL(), StringValues::WALL_BLOCK_TYPE_COBBLESTONE => VanillaBlocks::COBBLESTONE_WALL(), StringValues::WALL_BLOCK_TYPE_DIORITE => VanillaBlocks::DIORITE_WALL(), StringValues::WALL_BLOCK_TYPE_END_BRICK => VanillaBlocks::END_STONE_BRICK_WALL(), StringValues::WALL_BLOCK_TYPE_GRANITE => VanillaBlocks::GRANITE_WALL(), StringValues::WALL_BLOCK_TYPE_MOSSY_COBBLESTONE => VanillaBlocks::MOSSY_COBBLESTONE_WALL(), StringValues::WALL_BLOCK_TYPE_MOSSY_STONE_BRICK => VanillaBlocks::MOSSY_STONE_BRICK_WALL(), StringValues::WALL_BLOCK_TYPE_NETHER_BRICK => VanillaBlocks::NETHER_BRICK_WALL(), StringValues::WALL_BLOCK_TYPE_PRISMARINE => VanillaBlocks::PRISMARINE_WALL(), StringValues::WALL_BLOCK_TYPE_RED_NETHER_BRICK => VanillaBlocks::RED_NETHER_BRICK_WALL(), StringValues::WALL_BLOCK_TYPE_RED_SANDSTONE => VanillaBlocks::RED_SANDSTONE_WALL(), StringValues::WALL_BLOCK_TYPE_SANDSTONE => VanillaBlocks::SANDSTONE_WALL(), StringValues::WALL_BLOCK_TYPE_STONE_BRICK => VanillaBlocks::STONE_BRICK_WALL(), default => throw $in->badValueException(BlockStateNames::WALL_BLOCK_TYPE, $type), }, $in); } /** @throws BlockStateDeserializeException */ public static function mapStoneSlab1Type(BlockStateReader $in) : Slab{ //* stone_slab_type (StringTag) = brick, cobblestone, nether_brick, quartz, sandstone, smooth_stone, stone_brick, wood return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE)){ StringValues::STONE_SLAB_TYPE_BRICK => VanillaBlocks::BRICK_SLAB(), StringValues::STONE_SLAB_TYPE_COBBLESTONE => VanillaBlocks::COBBLESTONE_SLAB(), StringValues::STONE_SLAB_TYPE_NETHER_BRICK => VanillaBlocks::NETHER_BRICK_SLAB(), StringValues::STONE_SLAB_TYPE_QUARTZ => VanillaBlocks::QUARTZ_SLAB(), StringValues::STONE_SLAB_TYPE_SANDSTONE => VanillaBlocks::SANDSTONE_SLAB(), StringValues::STONE_SLAB_TYPE_SMOOTH_STONE => VanillaBlocks::SMOOTH_STONE_SLAB(), StringValues::STONE_SLAB_TYPE_STONE_BRICK => VanillaBlocks::STONE_BRICK_SLAB(), StringValues::STONE_SLAB_TYPE_WOOD => VanillaBlocks::FAKE_WOODEN_SLAB(), default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE, $type), }; } /** @throws BlockStateDeserializeException */ public static function mapStoneSlab2Type(BlockStateReader $in) : Slab{ // * stone_slab_type_2 (StringTag) = mossy_cobblestone, prismarine_brick, prismarine_dark, prismarine_rough, purpur, red_nether_brick, red_sandstone, smooth_sandstone return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_2)){ StringValues::STONE_SLAB_TYPE_2_MOSSY_COBBLESTONE => VanillaBlocks::MOSSY_COBBLESTONE_SLAB(), StringValues::STONE_SLAB_TYPE_2_PRISMARINE_BRICK => VanillaBlocks::PRISMARINE_BRICKS_SLAB(), StringValues::STONE_SLAB_TYPE_2_PRISMARINE_DARK => VanillaBlocks::DARK_PRISMARINE_SLAB(), StringValues::STONE_SLAB_TYPE_2_PRISMARINE_ROUGH => VanillaBlocks::PRISMARINE_SLAB(), StringValues::STONE_SLAB_TYPE_2_PURPUR => VanillaBlocks::PURPUR_SLAB(), StringValues::STONE_SLAB_TYPE_2_RED_NETHER_BRICK => VanillaBlocks::RED_NETHER_BRICK_SLAB(), StringValues::STONE_SLAB_TYPE_2_RED_SANDSTONE => VanillaBlocks::RED_SANDSTONE_SLAB(), StringValues::STONE_SLAB_TYPE_2_SMOOTH_SANDSTONE => VanillaBlocks::SMOOTH_SANDSTONE_SLAB(), default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_2, $type), }; } /** @throws BlockStateDeserializeException */ public static function mapStoneSlab3Type(BlockStateReader $in) : Slab{ // * stone_slab_type_3 (StringTag) = andesite, diorite, end_stone_brick, granite, polished_andesite, polished_diorite, polished_granite, smooth_red_sandstone return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_3)){ StringValues::STONE_SLAB_TYPE_3_ANDESITE => VanillaBlocks::ANDESITE_SLAB(), StringValues::STONE_SLAB_TYPE_3_DIORITE => VanillaBlocks::DIORITE_SLAB(), StringValues::STONE_SLAB_TYPE_3_END_STONE_BRICK => VanillaBlocks::END_STONE_BRICK_SLAB(), StringValues::STONE_SLAB_TYPE_3_GRANITE => VanillaBlocks::GRANITE_SLAB(), StringValues::STONE_SLAB_TYPE_3_POLISHED_ANDESITE => VanillaBlocks::POLISHED_ANDESITE_SLAB(), StringValues::STONE_SLAB_TYPE_3_POLISHED_DIORITE => VanillaBlocks::POLISHED_DIORITE_SLAB(), StringValues::STONE_SLAB_TYPE_3_POLISHED_GRANITE => VanillaBlocks::POLISHED_GRANITE_SLAB(), StringValues::STONE_SLAB_TYPE_3_SMOOTH_RED_SANDSTONE => VanillaBlocks::SMOOTH_RED_SANDSTONE_SLAB(), default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_3, $type), }; } /** @throws BlockStateDeserializeException */ public static function mapStoneSlab4Type(BlockStateReader $in) : Slab{ // * stone_slab_type_4 (StringTag) = cut_red_sandstone, cut_sandstone, mossy_stone_brick, smooth_quartz, stone return match($type = $in->readString(BlockStateNames::STONE_SLAB_TYPE_4)){ StringValues::STONE_SLAB_TYPE_4_CUT_RED_SANDSTONE => VanillaBlocks::CUT_RED_SANDSTONE_SLAB(), StringValues::STONE_SLAB_TYPE_4_CUT_SANDSTONE => VanillaBlocks::CUT_SANDSTONE_SLAB(), StringValues::STONE_SLAB_TYPE_4_MOSSY_STONE_BRICK => VanillaBlocks::MOSSY_STONE_BRICK_SLAB(), StringValues::STONE_SLAB_TYPE_4_SMOOTH_QUARTZ => VanillaBlocks::SMOOTH_QUARTZ_SLAB(), StringValues::STONE_SLAB_TYPE_4_STONE => VanillaBlocks::STONE_SLAB(), default => throw $in->badValueException(BlockStateNames::STONE_SLAB_TYPE_4, $type), }; } }