PocketMine-MP/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php
2024-08-17 11:17:48 -05:00

328 lines
13 KiB
PHP

<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\data\bedrock\block\convert;
use pocketmine\block\Block;
use pocketmine\block\Button;
use pocketmine\block\Candle;
use pocketmine\block\Crops;
use pocketmine\block\DaylightSensor;
use pocketmine\block\Door;
use pocketmine\block\DoublePlant;
use pocketmine\block\FenceGate;
use pocketmine\block\FloorCoralFan;
use pocketmine\block\FloorSign;
use pocketmine\block\ItemFrame;
use pocketmine\block\Leaves;
use pocketmine\block\Liquid;
use pocketmine\block\RedMushroomBlock;
use pocketmine\block\RedstoneComparator;
use pocketmine\block\RedstoneRepeater;
use pocketmine\block\Sapling;
use pocketmine\block\SimplePressurePlate;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\Stem;
use pocketmine\block\Trapdoor;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\ICopper;
use pocketmine\block\utils\SlabType;
use pocketmine\block\VanillaBlocks;
use pocketmine\block\Wall;
use pocketmine\block\WallCoralFan;
use pocketmine\block\WallSign;
use pocketmine\block\WeightedPressurePlate;
use pocketmine\block\Wood;
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\BlockStateStringValues as StringValues;
use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\utils\AssumptionFailedError;
final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */
public static function decodeButton(Button $block, BlockStateReader $in) : Button{
return $block
->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 ICopper
*
* @phpstan-param TBlock $block
* @phpstan-return TBlock
*/
public static function decodeCopper(ICopper $block, CopperOxidation $oxidation) : ICopper{
$block->setOxidation($oxidation);
$block->setWaxed(false);
return $block;
}
/**
* @phpstan-template TBlock of ICopper
*
* @phpstan-param TBlock $block
* @phpstan-return TBlock
*/
public static function decodeWaxedCopper(ICopper $block, CopperOxidation $oxidation) : ICopper{
$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 decodeDoublePlant(DoublePlant $block, BlockStateReader $in) : DoublePlant{
return $block
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_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
->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 decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{
return $block
->setReady($in->readBool(BlockStateNames::AGE_BIT));
}
/** @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 decodeSingleSlab(Slab $block, BlockStateReader $in) : Slab{
return $block->setSlabType($in->readSlabPosition());
}
/** @throws BlockStateDeserializeException */
public static function decodeDoubleSlab(Slab $block, BlockStateReader $in) : Slab{
$in->ignored(StateNames::MC_VERTICAL_HALF);
return $block->setSlabType(SlabType::DOUBLE);
}
/** @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);
}
}