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.
This commit is contained in:
Dylan T. 2025-08-24 14:12:18 +01:00 committed by GitHub
parent 47140cb8d7
commit 7c521b456e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
33 changed files with 4072 additions and 3609 deletions

View File

@ -24,29 +24,21 @@ declare(strict_types=1);
namespace pocketmine\data\bedrock; namespace pocketmine\data\bedrock;
use pocketmine\block\utils\MushroomBlockType; 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; use pocketmine\utils\SingletonTrait;
/**
* @deprecated
*/
final class MushroomBlockTypeIdMap{ final class MushroomBlockTypeIdMap{
use SingletonTrait; use SingletonTrait;
/** @phpstan-use IntSaveIdMapTrait<MushroomBlockType> */ /** @phpstan-use IntSaveIdMapTrait<MushroomBlockType> */
use IntSaveIdMapTrait; use IntSaveIdMapTrait;
public function __construct(){ public function __construct(){
$newMapping = ValueMappings::getInstance()->mushroomBlockType;
foreach(MushroomBlockType::cases() as $case){ foreach(MushroomBlockType::cases() as $case){
$this->register(match($case){ $this->register($newMapping->valueToRaw($case), $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);
} }
} }
} }

View File

@ -38,6 +38,8 @@ final class BlockLegacyMetadata{
public const CORAL_VARIANT_FIRE = 3; public const CORAL_VARIANT_FIRE = 3;
public const CORAL_VARIANT_HORN = 4; 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_DOWN = 0x01;
public const MULTI_FACE_DIRECTION_FLAG_UP = 0x02; public const MULTI_FACE_DIRECTION_FLAG_UP = 0x02;
public const MULTI_FACE_DIRECTION_FLAG_SOUTH = 0x04; public const MULTI_FACE_DIRECTION_FLAG_SOUTH = 0x04;

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,237 @@
<?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\Slab;
use pocketmine\block\Stair;
use pocketmine\block\utils\Colored;
use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\convert\BlockStateReader as Reader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer;
use pocketmine\data\bedrock\block\convert\property\CommonProperties;
use pocketmine\data\bedrock\block\convert\property\StringProperty;
use function array_map;
use function count;
use function implode;
use function is_string;
/**
* Registers serializers and deserializers for block data in a unified style, to avoid code duplication.
* Not all blocks can be registered this way, but we can avoid a lot of repetition for the ones that can.
*/
final class BlockSerializerDeserializerRegistrar{
public function __construct(
public readonly BlockStateToObjectDeserializer $deserializer,
public readonly BlockObjectToStateSerializer $serializer
){}
/**
* @param string[]|StringProperty[] $components
*
* @phpstan-param list<string|StringProperty<*>> $components
*
* @return string[][]
* @phpstan-return list<list<string>>
*/
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<string|StringProperty<contravariant TBlock>> $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<string|StringProperty<contravariant TBlock>> $idComponents
* @phpstan-param list<string> $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<TBlock, true> $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<TBlock> $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;
});
}
}

View File

@ -56,11 +56,13 @@ use pocketmine\data\bedrock\block\BlockLegacyMetadata;
use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateNames as StateNames; use pocketmine\data\bedrock\block\BlockStateNames as StateNames;
use pocketmine\data\bedrock\block\convert\property\ValueMappings;
use pocketmine\data\bedrock\MushroomBlockTypeIdMap; use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Axis;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use pocketmine\utils\AssumptionFailedError;
/**
* @deprecated
*/
final class BlockStateDeserializerHelper{ final class BlockStateDeserializerHelper{
/** @throws BlockStateDeserializeException */ /** @throws BlockStateDeserializeException */
@ -71,6 +73,7 @@ final class BlockStateDeserializerHelper{
} }
/** /**
* @deprecated
* @phpstan-template TCandle of Candle * @phpstan-template TCandle of Candle
* @phpstan-param TCandle $block * @phpstan-param TCandle $block
* @phpstan-return TCandle * @phpstan-return TCandle
@ -103,6 +106,7 @@ final class BlockStateDeserializerHelper{
} }
/** /**
* @deprecated
* @phpstan-template TBlock of CopperMaterial * @phpstan-template TBlock of CopperMaterial
* *
* @phpstan-param TBlock $block * @phpstan-param TBlock $block
@ -115,6 +119,7 @@ final class BlockStateDeserializerHelper{
} }
/** /**
* @deprecated
* @phpstan-template TBlock of CopperMaterial * @phpstan-template TBlock of CopperMaterial
* *
* @phpstan-param TBlock $block * @phpstan-param TBlock $block
@ -133,6 +138,7 @@ final class BlockStateDeserializerHelper{
} }
/** /**
* @deprecated
* @phpstan-template TDoor of Door * @phpstan-template TDoor of Door
* @phpstan-param TDoor $block * @phpstan-param TDoor $block
* @phpstan-return TDoor * @phpstan-return TDoor
@ -155,7 +161,10 @@ final class BlockStateDeserializerHelper{
->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT)); ->setTop($in->readBool(BlockStateNames::UPPER_BLOCK_BIT));
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{ public static function decodeFenceGate(FenceGate $block, BlockStateReader $in) : FenceGate{
return $block return $block
->setFacing($in->readCardinalHorizontalFacing()) ->setFacing($in->readCardinalHorizontalFacing())
@ -163,17 +172,19 @@ final class BlockStateDeserializerHelper{
->setOpen($in->readBool(BlockStateNames::OPEN_BIT)); ->setOpen($in->readBool(BlockStateNames::OPEN_BIT));
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{ public static function decodeFloorCoralFan(FloorCoralFan $block, BlockStateReader $in) : FloorCoralFan{
return $block return $block
->setAxis(match($in->readBoundedInt(BlockStateNames::CORAL_FAN_DIRECTION, 0, 1)){ ->setAxis($in->mapIntFromInt(BlockStateNames::CORAL_FAN_DIRECTION, ValueMappings::getInstance()->coralAxis));
0 => Axis::X,
1 => Axis::Z,
default => throw new AssumptionFailedError("readBoundedInt() should have prevented this"),
});
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public static function decodeFloorSign(FloorSign $block, BlockStateReader $in) : FloorSign{ public static function decodeFloorSign(FloorSign $block, BlockStateReader $in) : FloorSign{
return $block return $block
->setRotation($in->readBoundedInt(BlockStateNames::GROUND_SIGN_DIRECTION, 0, 15)); ->setRotation($in->readBoundedInt(BlockStateNames::GROUND_SIGN_DIRECTION, 0, 15));
@ -186,7 +197,10 @@ final class BlockStateDeserializerHelper{
->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT)); ->setHasMap($in->readBool(StateNames::ITEM_FRAME_MAP_BIT));
} }
/** @throws BlockStateDeserializeException */ /**
* @throws BlockStateDeserializeException
* @deprecated
*/
public static function decodeLeaves(Leaves $block, BlockStateReader $in) : Leaves{ public static function decodeLeaves(Leaves $block, BlockStateReader $in) : Leaves{
return $block return $block
->setNoDecay($in->readBool(StateNames::PERSISTENT_BIT)) ->setNoDecay($in->readBool(StateNames::PERSISTENT_BIT))
@ -236,7 +250,10 @@ final class BlockStateDeserializerHelper{
->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1); ->setDelay($in->readBoundedInt(BlockStateNames::REPEATER_DELAY, 0, 3) + 1);
} }
/** @throws BlockStateDeserializeException */ /**
* @throws BlockStateDeserializeException
* @deprecated
*/
public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{ public static function decodeSapling(Sapling $block, BlockStateReader $in) : Sapling{
return $block return $block
->setReady($in->readBool(BlockStateNames::AGE_BIT)); ->setReady($in->readBool(BlockStateNames::AGE_BIT));
@ -273,6 +290,7 @@ final class BlockStateDeserializerHelper{
} }
/** /**
* @deprecated
* @phpstan-template TStair of Stair * @phpstan-template TStair of Stair
* @phpstan-param TStair $block * @phpstan-param TStair $block
* @phpstan-return TStair * @phpstan-return TStair
@ -296,6 +314,7 @@ final class BlockStateDeserializerHelper{
} }
/** /**
* @deprecated
* @phpstan-template TTrapdoor of Trapdoor * @phpstan-template TTrapdoor of Trapdoor
* @phpstan-param TTrapdoor $block * @phpstan-param TTrapdoor $block
* @phpstan-return TTrapdoor * @phpstan-return TTrapdoor
@ -320,12 +339,19 @@ final class BlockStateDeserializerHelper{
return $block; return $block;
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{ public static function decodeWallSign(WallSign $block, BlockStateReader $in) : WallSign{
return $block return $block
->setFacing($in->readHorizontalFacing()); ->setFacing($in->readHorizontalFacing());
} }
/**
* @deprecated
* @throws BlockStateDeserializeException
*/
public static function decodeWeightedPressurePlate(WeightedPressurePlate $block, BlockStateReader $in) : WeightedPressurePlate{ public static function decodeWeightedPressurePlate(WeightedPressurePlate $block, BlockStateReader $in) : WeightedPressurePlate{
return $block return $block
->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15)); ->setOutputSignalStrength($in->readBoundedInt(BlockStateNames::REDSTONE_SIGNAL, 0, 15));

View File

@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; 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\Axis;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\ByteTag;
@ -112,45 +115,45 @@ final class BlockStateReader{
} }
/** /**
* @param int[] $mapping * @deprecated
* @phpstan-param array<int, int> $mapping * @phpstan-param IntFromRawStateMap<string> $map
* @phpstan-return int
* @throws BlockStateDeserializeException * @throws BlockStateDeserializeException
*/ */
private function parseFacingValue(int $value, array $mapping) : int{ public function mapIntFromString(string $name, IntFromRawStateMap $map) : int{
$result = $mapping[$value] ?? null; $raw = $this->readString($name);
if($result === null){
throw new BlockStateDeserializeException("Unmapped facing value " . $value);
}
return $result;
}
/** @throws BlockStateDeserializeException */ return $map->rawToValue($raw) ?? throw $this->badValueException($name, $raw);
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)
};
} }
/** /**
* @deprecated
* @phpstan-param IntFromRawStateMap<int> $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[] * @return int[]
* @phpstan-return array<int, int> * @phpstan-return array<int, int>
*/ */
@ -173,82 +176,69 @@ final class BlockStateReader{
return $result; return $result;
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readEndRodFacingDirection() : int{ public function readEndRodFacingDirection() : int{
$result = $this->readFacingDirection(); $result = $this->readFacingDirection();
return Facing::axis($result) !== Axis::Y ? Facing::opposite($result) : $result; return Facing::axis($result) !== Axis::Y ? Facing::opposite($result) : $result;
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readHorizontalFacing() : int{ public function readHorizontalFacing() : int{
return $this->parseFacingValue($this->readInt(BlockStateNames::FACING_DIRECTION), [ return $this->mapIntFromInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic);
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
]);
} }
/** /**
* @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 * This is for trapdoors, because Mojang botched the conversion in 1.13
* @throws BlockStateDeserializeException * @throws BlockStateDeserializeException
*/ */
public function read5MinusHorizontalFacing() : int{ public function read5MinusHorizontalFacing() : int{
return $this->parseFacingValue($this->readInt(BlockStateNames::DIRECTION), [ return $this->mapIntFromInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus);
0 => Facing::EAST,
1 => Facing::WEST,
2 => Facing::SOUTH,
3 => Facing::NORTH
]);
} }
/** /**
* @deprecated
* Used by pumpkins as of 1.20.0.23 beta * Used by pumpkins as of 1.20.0.23 beta
* @throws BlockStateDeserializeException * @throws BlockStateDeserializeException
*/ */
public function readCardinalHorizontalFacing() : int{ public function readCardinalHorizontalFacing() : int{
return match($raw = $this->readString(BlockStateNames::MC_CARDINAL_DIRECTION)){ return $this->mapIntFromString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection);
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)
};
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readCoralFacing() : int{ public function readCoralFacing() : int{
return $this->parseFacingValue($this->readInt(BlockStateNames::CORAL_DIRECTION), [ return $this->mapIntFromInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral);
0 => Facing::WEST,
1 => Facing::EAST,
2 => Facing::NORTH,
3 => Facing::SOUTH
]);
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readFacingWithoutDown() : int{ public function readFacingWithoutDown() : int{
$result = $this->readFacingDirection(); $result = $this->readFacingDirection();
if($result === Facing::DOWN){ //shouldn't be legal, but 1.13 allows it if($result === Facing::DOWN){ //shouldn't be legal, but 1.13 allows it
@ -257,6 +247,10 @@ final class BlockStateReader{
return $result; return $result;
} }
/**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readFacingWithoutUp() : int{ public function readFacingWithoutUp() : int{
$result = $this->readFacingDirection(); $result = $this->readFacingDirection();
if($result === Facing::UP){ if($result === Facing::UP){
@ -266,23 +260,17 @@ final class BlockStateReader{
} }
/** /**
* @phpstan-return Axis::* * @deprecated
* @throws BlockStateDeserializeException * @throws BlockStateDeserializeException
*/ */
public function readPillarAxis() : int{ public function readPillarAxis() : int{
$rawValue = $this->readString(BlockStateNames::PILLAR_AXIS); return $this->mapIntFromString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis);
$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;
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readSlabPosition() : SlabType{ public function readSlabPosition() : SlabType{
return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){ return match($rawValue = $this->readString(BlockStateNames::MC_VERTICAL_HALF)){
StringValues::MC_VERTICAL_HALF_BOTTOM => SlabType::BOTTOM, 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 * @throws BlockStateDeserializeException
*/ */
public function readTorchFacing() : int{ public function readTorchFacing() : int{
//TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) return $this->mapIntFromString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing);
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"),
};
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readBellAttachmentType() : BellAttachmentType{ public function readBellAttachmentType() : BellAttachmentType{
return match($type = $this->readString(BlockStateNames::ATTACHMENT)){ return $this->readUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType);
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),
};
} }
/** @throws BlockStateDeserializeException */ /**
* @deprecated
* @throws BlockStateDeserializeException
*/
public function readWallConnectionType(string $name) : ?WallConnectionType{ public function readWallConnectionType(string $name) : ?WallConnectionType{
return match($type = $this->readString($name)){ 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 //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<TEnum, string> $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 * Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled
*/ */

View File

@ -55,6 +55,9 @@ use pocketmine\data\bedrock\block\convert\BlockStateWriter as Writer;
use pocketmine\data\bedrock\MushroomBlockTypeIdMap; use pocketmine\data\bedrock\MushroomBlockTypeIdMap;
use pocketmine\math\Facing; use pocketmine\math\Facing;
/**
* @deprecated
*/
final class BlockStateSerializerHelper{ final class BlockStateSerializerHelper{
public static function encodeButton(Button $block, Writer $out) : Writer{ public static function encodeButton(Button $block, Writer $out) : Writer{
return $out return $out
@ -77,6 +80,9 @@ final class BlockStateSerializerHelper{
return $out->writeInt(BlockStateNames::GROWTH, $block->getAge()); return $out->writeInt(BlockStateNames::GROWTH, $block->getAge());
} }
/**
* @deprecated
*/
public static function encodeTorch(Torch $block, Writer $out) : Writer{ public static function encodeTorch(Torch $block, Writer $out) : Writer{
return $out return $out
->writeTorchFacing($block->getFacing()); ->writeTorchFacing($block->getFacing());
@ -97,6 +103,9 @@ final class BlockStateSerializerHelper{
}; };
} }
/**
* @deprecated
*/
public static function encodeDoor(Door $block, Writer $out) : Writer{ public static function encodeDoor(Door $block, Writer $out) : Writer{
return $out return $out
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()) ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop())
@ -111,6 +120,9 @@ final class BlockStateSerializerHelper{
->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop()); ->writeBool(BlockStateNames::UPPER_BLOCK_BIT, $block->isTop());
} }
/**
* @deprecated
*/
public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{ public static function encodeFenceGate(FenceGate $block, Writer $out) : Writer{
return $out return $out
->writeCardinalHorizontalFacing($block->getFacing()) ->writeCardinalHorizontalFacing($block->getFacing())
@ -118,6 +130,9 @@ final class BlockStateSerializerHelper{
->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen()); ->writeBool(BlockStateNames::OPEN_BIT, $block->isOpen());
} }
/**
* @deprecated
*/
public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{ public static function encodeFloorSign(FloorSign $block, Writer $out) : Writer{
return $out return $out
->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation()); ->writeInt(BlockStateNames::GROUND_SIGN_DIRECTION, $block->getRotation());
@ -135,6 +150,9 @@ final class BlockStateSerializerHelper{
->writeFacingDirection($block->getFacing()); ->writeFacingDirection($block->getFacing());
} }
/**
* @deprecated
*/
public static function encodeLeaves(Leaves $block, Writer $out) : Writer{ public static function encodeLeaves(Leaves $block, Writer $out) : Writer{
return $out return $out
->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay()) ->writeBool(BlockStateNames::PERSISTENT_BIT, $block->isNoDecay())
@ -159,11 +177,17 @@ final class BlockStateSerializerHelper{
->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType())); ->writeInt(BlockStateNames::HUGE_MUSHROOM_BITS, MushroomBlockTypeIdMap::getInstance()->toId($block->getMushroomBlockType()));
} }
/**
* @deprecated
*/
public static function encodeQuartz(int $axis, Writer $out) : Writer{ public static function encodeQuartz(int $axis, Writer $out) : Writer{
return $out return $out
->writePillarAxis($axis); //this isn't needed for all types, but we have to write it anyway ->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{ public static function encodeSapling(Sapling $block, Writer $out) : Writer{
return $out return $out
->writeBool(BlockStateNames::AGE_BIT, $block->isReady()); ->writeBool(BlockStateNames::AGE_BIT, $block->isReady());
@ -193,6 +217,9 @@ final class BlockStateSerializerHelper{
self::encodeSingleSlab($block, $singleId); self::encodeSingleSlab($block, $singleId);
} }
/**
* @deprecated
*/
public static function encodeStairs(Stair $block, Writer $out) : Writer{ public static function encodeStairs(Stair $block, Writer $out) : Writer{
return $out return $out
->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown()) ->writeBool(BlockStateNames::UPSIDE_DOWN_BIT, $block->isUpsideDown())
@ -208,6 +235,9 @@ final class BlockStateSerializerHelper{
->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing); ->writeFacingWithoutUp($facing === Facing::UP ? Facing::DOWN : $facing);
} }
/**
* @deprecated
*/
public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{ public static function encodeTrapdoor(Trapdoor $block, Writer $out) : Writer{
return $out return $out
->write5MinusHorizontalFacing($block->getFacing()) ->write5MinusHorizontalFacing($block->getFacing())
@ -224,6 +254,9 @@ final class BlockStateSerializerHelper{
->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST)); ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST));
} }
/**
* @deprecated
*/
public static function encodeWallSign(WallSign $block, Writer $out) : Writer{ public static function encodeWallSign(WallSign $block, Writer $out) : Writer{
return $out return $out
->writeHorizontalFacing($block->getFacing()); ->writeHorizontalFacing($block->getFacing());

View File

@ -31,6 +31,9 @@ use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateNames;
use pocketmine\data\bedrock\block\BlockStateSerializeException; use pocketmine\data\bedrock\block\BlockStateSerializeException;
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues; 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\Axis;
use pocketmine\math\Facing; use pocketmine\math\Facing;
use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\ByteTag;
@ -73,35 +76,47 @@ final class BlockStateWriter{
return $this; return $this;
} }
/** @return $this */ /**
public function writeFacingDirection(int $value) : self{ * @deprecated
$this->writeInt(BlockStateNames::FACING_DIRECTION, match($value){ * @phpstan-param IntFromRawStateMap<string> $map
Facing::DOWN => 0, * @return $this
Facing::UP => 1, */
Facing::NORTH => 2, public function mapIntToString(string $name, IntFromRawStateMap $map, int $value) : self{
Facing::SOUTH => 3, $raw = $map->valueToRaw($value);
Facing::WEST => 4, $this->writeString($name, $raw);
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")
});
return $this; return $this;
} }
/** /**
* @deprecated
* @phpstan-param IntFromRawStateMap<int> $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 * @param int[] $faces
* @phpstan-param array<int, int> $faces * @phpstan-param array<int, int> $faces
* @return $this * @return $this
@ -123,86 +138,69 @@ final class BlockStateWriter{
return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result); return $this->writeInt(BlockStateNames::MULTI_FACE_DIRECTION_BITS, $result);
} }
/** @return $this */ /**
* @deprecated
* @return $this
*/
public function writeEndRodFacingDirection(int $value) : self{ public function writeEndRodFacingDirection(int $value) : self{
//end rods are stupid in bedrock and have everything except up/down the wrong way round //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->writeFacingDirection(Facing::axis($value) !== Axis::Y ? Facing::opposite($value) : $value);
} }
/** @return $this */ /**
* @deprecated
* @return $this
*/
public function writeHorizontalFacing(int $value) : self{ public function writeHorizontalFacing(int $value) : self{
if($value === Facing::UP || $value === Facing::DOWN){ return $this->mapIntToInt(BlockStateNames::FACING_DIRECTION, ValueMappings::getInstance()->horizontalFacingClassic, $value);
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;
} }
/** /**
* @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 * This is for trapdoors, because Mojang botched the conversion in 1.13
* @return $this * @return $this
*/ */
public function write5MinusHorizontalFacing(int $value) : self{ public function write5MinusHorizontalFacing(int $value) : self{
return $this->writeInt(BlockStateNames::DIRECTION, match($value){ return $this->mapIntToInt(BlockStateNames::DIRECTION, ValueMappings::getInstance()->horizontalFacing5Minus, $value);
Facing::EAST => 0,
Facing::WEST => 1,
Facing::SOUTH => 2,
Facing::NORTH => 3,
default => throw new BlockStateSerializeException("Invalid horizontal facing $value")
});
} }
/** /**
* @deprecated
* Used by pumpkins as of 1.20.0.23 beta * Used by pumpkins as of 1.20.0.23 beta
* @return $this * @return $this
*/ */
public function writeCardinalHorizontalFacing(int $value) : self{ public function writeCardinalHorizontalFacing(int $value) : self{
return $this->writeString(BlockStateNames::MC_CARDINAL_DIRECTION, match($value){ return $this->mapIntToString(BlockStateNames::MC_CARDINAL_DIRECTION, ValueMappings::getInstance()->cardinalDirection, $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 */ /**
* @deprecated
* @return $this
*/
public function writeCoralFacing(int $value) : self{ public function writeCoralFacing(int $value) : self{
$this->writeInt(BlockStateNames::CORAL_DIRECTION, match($value){ return $this->mapIntToInt(BlockStateNames::CORAL_DIRECTION, ValueMappings::getInstance()->horizontalFacingCoral, $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 */ /**
* @deprecated
* @return $this
*/
public function writeFacingWithoutDown(int $value) : self{ public function writeFacingWithoutDown(int $value) : self{
if($value === Facing::DOWN){ if($value === Facing::DOWN){
throw new BlockStateSerializeException("Invalid facing DOWN"); throw new BlockStateSerializeException("Invalid facing DOWN");
@ -211,7 +209,10 @@ final class BlockStateWriter{
return $this; return $this;
} }
/** @return $this */ /**
* @deprecated
* @return $this
*/
public function writeFacingWithoutUp(int $value) : self{ public function writeFacingWithoutUp(int $value) : self{
if($value === Facing::UP){ if($value === Facing::UP){
throw new BlockStateSerializeException("Invalid facing UP"); throw new BlockStateSerializeException("Invalid facing UP");
@ -220,18 +221,19 @@ final class BlockStateWriter{
return $this; return $this;
} }
/** @return $this */ /**
* @deprecated
* @return $this
*/
public function writePillarAxis(int $axis) : self{ public function writePillarAxis(int $axis) : self{
$this->writeString(BlockStateNames::PILLAR_AXIS, match($axis){ $this->mapIntToString(BlockStateNames::PILLAR_AXIS, ValueMappings::getInstance()->pillarAxis, $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")
});
return $this; return $this;
} }
/** @return $this */ /**
* @deprecated
* @return $this
*/
public function writeSlabPosition(SlabType $slabType) : self{ public function writeSlabPosition(SlabType $slabType) : self{
$this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){ $this->writeString(BlockStateNames::MC_VERTICAL_HALF, match($slabType){
SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP, SlabType::TOP => StringValues::MC_VERTICAL_HALF_TOP,
@ -241,32 +243,27 @@ final class BlockStateWriter{
return $this; return $this;
} }
/** @return $this */ /**
* @deprecated
* @return $this
*/
public function writeTorchFacing(int $facing) : self{ public function writeTorchFacing(int $facing) : self{
//TODO: horizontal directions are flipped (MCPE bug: https://bugs.mojang.com/browse/MCPE-152036) $this->mapIntToString(BlockStateNames::TORCH_FACING_DIRECTION, ValueMappings::getInstance()->torchFacing, $facing);
$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")
});
return $this; return $this;
} }
/** @return $this */ /**
* @deprecated
* @return $this
*/
public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{ public function writeBellAttachmentType(BellAttachmentType $attachmentType) : self{
$this->writeString(BlockStateNames::ATTACHMENT, match($attachmentType){ return $this->writeUnitEnum(BlockStateNames::ATTACHMENT, ValueMappings::getInstance()->bellAttachmentType, $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 */ /**
* @deprecated
* @return $this
*/
public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{ public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{
$this->writeString($name, match($wallConnectionType){ $this->writeString($name, match($wallConnectionType){
null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE, null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE,
@ -276,6 +273,21 @@ final class BlockStateWriter{
return $this; return $this;
} }
/**
* @deprecated
* @phpstan-template TEnum of \UnitEnum
* @phpstan-param EnumFromRawStateMap<TEnum, string> $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{ public function getBlockStateData() : BlockStateData{
return BlockStateData::current($this->id, $this->states); return BlockStateData::current($this->id, $this->states);
} }

View File

@ -0,0 +1,107 @@
<?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\data\bedrock\block\convert\property\Property;
use pocketmine\data\bedrock\block\convert\property\StringProperty;
/**
* This class works around a limitation in PHPStan.
* Ideally, we'd just have a function that accepted ($block, $id, $properties) all together and just have the template
* type inferred from $block alone.
* However, there's no way to tell PHPStan to ignore $properties for inference, so we're stuck with this hack.
*
* @phpstan-template TBlock of Block
* @phpstan-template THasIdComponents of bool
*/
final class FlattenedIdModel{
/**
* @var string[]|StringProperty[]
* @phpstan-var list<string|StringProperty<contravariant TBlock>>
*/
private array $idComponents = [];
/**
* @var Property[]
* @phpstan-var list<Property<contravariant TBlock>>
*/
private array $properties = [];
/**
* @phpstan-param TBlock $block
*/
private function __construct(
private Block $block
){}
/**
* @phpstan-template TBlock_ of Block
* @phpstan-param TBlock_ $block
* @return self<TBlock_, false>
*/
public static function create(Block $block) : self{
/** @phpstan-var self<TBlock_, false> $result */
$result = new self($block);
return $result;
}
/** @phpstan-return TBlock */
public function getBlock() : Block{ return $this->block; }
/**
* @return string[]|StringProperty[]
* @phpstan-return list<string|StringProperty<contravariant TBlock>>
*/
public function getIdComponents() : array{ return $this->idComponents; }
/**
* @return Property[]
* @phpstan-return list<Property<contravariant TBlock>>
*/
public function getProperties() : array{ return $this->properties; }
/**
* @param string[]|StringProperty[] $components
* @phpstan-param non-empty-list<string|StringProperty<contravariant TBlock>> $components
* @return $this
* @phpstan-this-out self<TBlock, true>
*/
public function idComponents(array $components) : self{
$this->idComponents = $components;
return $this;
}
/**
* @param Property[] $properties
* @phpstan-param non-empty-list<Property<contravariant TBlock>> $properties
* @return $this
* @phpstan-this-out self<TBlock, THasIdComponents>
*/
public function properties(array $properties) : self{
$this->properties = $properties;
return $this;
}
}

View File

@ -0,0 +1,82 @@
<?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\data\bedrock\block\convert\property\Property;
/**
* This class works around a limitation in PHPStan.
* Ideally, we'd just have a function that accepted ($block, $id, $properties) all together and just have the template
* type inferred from $block alone.
* However, there's no way to tell PHPStan to ignore $properties for inference, so we're stuck with this hack.
*
* @phpstan-template TBlock of Block
*/
final class Model{
/**
* @var Property[]
* @phpstan-var list<Property<contravariant TBlock>>
*/
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<Property<contravariant TBlock>>
*/
public function getProperties() : array{ return $this->properties; }
/**
* @phpstan-template TBlock_ of Block
* @phpstan-param TBlock_ $block
* @phpstan-return self<TBlock_>
*/
public static function create(Block $block, string $id) : self{
return new self($block, $id);
}
/**
* @param Property[] $properties
* @phpstan-param list<Property<contravariant TBlock>> $properties
* @phpstan-return $this
*/
public function properties(array $properties) : self{
$this->properties = $properties;
return $this;
}
}

View File

@ -1,78 +0,0 @@
<?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\data\bedrock\block\BlockStateDeserializeException;
use function spl_object_id;
/**
* @phpstan-template TEnum of \UnitEnum
*/
class StringEnumMap{
/**
* @var string[]
* @phpstan-var array<int, string>
*/
private array $enumToValue = [];
/**
* @var \UnitEnum[]
* @phpstan-var array<string, TEnum>
*/
private array $valueToEnum = [];
/**
* @phpstan-param class-string<TEnum> $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<string, TEnum>
*/
public function getValueToEnum() : array{
return $this->valueToEnum;
}
}

View File

@ -1,83 +0,0 @@
<?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\utils\DyeColor;
use pocketmine\utils\SingletonTrait;
final class ValueMappings{
use SingletonTrait; //???
/**
* @var StringEnumMap[]
* @phpstan-var array<class-string<covariant \UnitEnum>, StringEnumMap<covariant \UnitEnum>>
*/
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<TEnum> $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<TEnum> $class
* @phpstan-return StringEnumMap<TEnum>
*/
public function getEnumMap(string $class) : StringEnumMap{
if(!isset($this->enumMappings[$class])){
throw new \InvalidArgumentException("No enum mapping found for class: $class");
}
/**
* @phpstan-var StringEnumMap<TEnum> $map
*/
$map = $this->enumMappings[$class];
return $map;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,78 @@
<?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\property;
use pocketmine\data\bedrock\block\BlockStateSerializeException;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
/**
* @phpstan-template TBlock of object
* @phpstan-implements StringProperty<TBlock>
*/
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;
}
}

View File

@ -0,0 +1,71 @@
<?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\property;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
/**
* @phpstan-template TBlock of object
* @phpstan-implements Property<TBlock>
*/
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<object>
*/
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);
}
}

View File

@ -0,0 +1,429 @@
<?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\property;
use pocketmine\block\Button;
use pocketmine\block\Door;
use pocketmine\block\DoublePlant;
use pocketmine\block\FenceGate;
use pocketmine\block\ItemFrame;
use pocketmine\block\Liquid;
use pocketmine\block\SimplePressurePlate;
use pocketmine\block\Slab;
use pocketmine\block\Stair;
use pocketmine\block\Stem;
use pocketmine\block\Torch;
use pocketmine\block\Trapdoor;
use pocketmine\block\utils\Ageable;
use pocketmine\block\utils\AnalogRedstoneSignalEmitter;
use pocketmine\block\utils\AnyFacing;
use pocketmine\block\utils\Colored;
use pocketmine\block\utils\CopperMaterial;
use pocketmine\block\utils\CopperOxidation;
use pocketmine\block\utils\CoralMaterial;
use pocketmine\block\utils\CoralType;
use pocketmine\block\utils\DyeColor;
use pocketmine\block\utils\HorizontalFacing;
use pocketmine\block\utils\Lightable;
use pocketmine\block\utils\MultiFacing;
use pocketmine\block\utils\PillarRotation;
use pocketmine\block\utils\SignLikeRotation;
use pocketmine\block\utils\SlabType;
use pocketmine\block\Wall;
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;
final class CommonProperties{
use SingletonTrait;
/** @phpstan-var ValueFromStringProperty<AnyFacing, int> */
public readonly ValueFromStringProperty $blockFace;
/** @phpstan-var ValueFromStringProperty<PillarRotation, int> */
public readonly ValueFromStringProperty $pillarAxis;
/** @phpstan-var ValueFromStringProperty<Torch, int> */
public readonly ValueFromStringProperty $torchFacing;
/** @phpstan-var ValueFromStringProperty<HorizontalFacing, int> */
public readonly ValueFromStringProperty $horizontalFacingCardinal;
/** @phpstan-var ValueFromIntProperty<HorizontalFacing, int> */
public readonly ValueFromIntProperty $horizontalFacingSWNE;
/** @phpstan-var ValueFromIntProperty<HorizontalFacing, int> */
public readonly ValueFromIntProperty $horizontalFacingSWNEInverted;
/** @phpstan-var ValueFromIntProperty<HorizontalFacing, int> */
public readonly ValueFromIntProperty $horizontalFacingClassic;
/** @phpstan-var ValueFromIntProperty<AnyFacing, int> */
public readonly ValueFromIntProperty $anyFacingClassic;
/** @phpstan-var OptionSetFromIntProperty<MultiFacing, int> */
public readonly OptionSetFromIntProperty $multiFacingFlags;
/** @phpstan-var IntProperty<SignLikeRotation> */
public readonly IntProperty $floorSignLikeRotation;
/** @phpstan-var IntProperty<AnalogRedstoneSignalEmitter> */
public readonly IntProperty $analogRedstoneSignal;
/** @phpstan-var IntProperty<Ageable> */
public readonly IntProperty $cropAgeMax7;
/** @phpstan-var BoolProperty<DoublePlant> */
public readonly BoolProperty $doublePlantHalf;
/** @phpstan-var IntProperty<Liquid> */
public readonly IntProperty $liquidData;
/** @phpstan-var BoolProperty<Lightable> */
public readonly BoolProperty $lit;
public readonly DummyProperty $dummyCardinalDirection;
public readonly DummyProperty $dummyPillarAxis;
/** @phpstan-var ValueFromStringProperty<Colored, DyeColor> */
public readonly ValueFromStringProperty $dyeColorIdInfix;
/** @phpstan-var BoolFromStringProperty<Lightable> */
public readonly BoolFromStringProperty $litIdInfix;
/** @phpstan-var BoolFromStringProperty<Slab> */
public readonly BoolFromStringProperty $slabIdInfix;
/** @phpstan-var BoolFromStringProperty<Slab> */
public readonly BoolFromStringProperty $slabPositionProperty;
/**
* @var StringProperty[]
* @phpstan-var non-empty-list<string|StringProperty<CoralMaterial>>
*/
public readonly array $coralIdPrefixes;
/**
* @var StringProperty[]
* @phpstan-var non-empty-list<string|StringProperty<CopperMaterial>>
*/
public readonly array $copperIdPrefixes;
/**
* @var StringProperty[]
* @phpstan-var non-empty-list<string|StringProperty<contravariant Lightable>>
*/
public readonly array $furnaceIdPrefixes;
/**
* @var StringProperty[]|string[]
* @phpstan-var non-empty-list<string|StringProperty<contravariant Liquid>>
*/
public readonly array $liquidIdPrefixes;
/**
* @var StringProperty[]
* @phpstan-var non-empty-list<string|StringProperty<contravariant Wood>>
*/
public readonly array $woodIdPrefixes;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant Button>>
*/
public readonly array $buttonProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant Lightable & HorizontalFacing>>
*/
public readonly array $campfireProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant Door>>
*/
public readonly array $doorProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant FenceGate>>
*/
public readonly array $fenceGateProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant ItemFrame>>
*/
public readonly array $itemFrameProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant SimplePressurePlate>>
*/
public readonly array $simplePressurePlateProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant Stair>>
*/
public readonly array $stairProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant Stem>>
*/
public readonly array $stemProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant Trapdoor>>
*/
public readonly array $trapdoorProperties;
/**
* @var Property[]
* @phpstan-var non-empty-list<Property<contravariant Wall>>
*/
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;
}
}

View File

@ -0,0 +1,61 @@
<?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\property;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
use pocketmine\utils\AssumptionFailedError;
use function is_bool;
use function is_int;
use function is_string;
/**
* @phpstan-implements Property<object>
*/
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();
}
}
}

View File

@ -0,0 +1,109 @@
<?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\property;
use function spl_object_id;
/**
* @phpstan-template TEnum of \UnitEnum
* @phpstan-template TRaw of int|string
* @phpstan-implements StateMap<TEnum, TRaw>
*/
class EnumFromRawStateMap implements StateMap{
/**
* @var int[]
* @phpstan-var array<int, TRaw>
*/
private array $enumToValue = [];
/**
* @var \UnitEnum[]
* @phpstan-var array<TRaw, TEnum>
*/
private array $valueToEnum = [];
/**
* @phpstan-param class-string<TEnum> $class
* @phpstan-param \Closure(TEnum) : TRaw $mapper
* @phpstan-param ?\Closure(TEnum) : list<TRaw> $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<TEnum_> $class
* @param \Closure(TEnum_) : string $mapper
* @param ?\Closure(TEnum_) : list<string> $aliasMapper
*
* @phpstan-return EnumFromRawStateMap<TEnum_, string>
*/
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<TEnum_> $class
* @param \Closure(TEnum_) : int $mapper
* @param ?\Closure(TEnum_) : list<int> $aliasMapper
*
* @phpstan-return EnumFromRawStateMap<TEnum_, int>
*/
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;
}
}

View File

@ -0,0 +1,35 @@
<?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\property;
/**
* In PM we treat head/body as a bool and berries/no berries as a second bool.
* However, Bedrock doesn't have IDs to represent a separate head/body without berries, so this enum lets us use an
* EnumFromStringProperty to deal with this using special getter/setter logic.
*/
enum FlattenedCaveVinesVariant : string{
case NO_BERRIES = "";
case HEAD_WITH_BERRIES = "_head_with_berries";
case BODY_WITH_BERRIES = "_body_with_berries";
}

View File

@ -0,0 +1,104 @@
<?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\property;
use function array_flip;
use function is_array;
/**
* @phpstan-template TRaw of int|string
* @phpstan-implements StateMap<int, TRaw>
*/
class IntFromRawStateMap implements StateMap{
/**
* @var int[]
* @phpstan-var array<TRaw, int>
*/
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<int, TRaw> $serializeMap
* @phpstan-param array<int, TRaw|list<TRaw>> $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<int, int> $serializeMap
* @phpstan-param array<int, int|list<int>> $deserializeAliases
*
* @phpstan-return self<int>
*/
public static function int(array $serializeMap, array $deserializeAliases = []) : self{ return new self($serializeMap, $deserializeAliases); }
/**
* @param string[] $serializeMap
* @param (string|string[]) $deserializeAliases
*
* @phpstan-param array<int, string> $serializeMap
* @phpstan-param array<int, string|list<string>> $deserializeAliases
*
* @phpstan-return self<string>
*/
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";
}
}

View File

@ -0,0 +1,70 @@
<?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\property;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
use pocketmine\utils\Limits;
/**
* @phpstan-template TBlock of object
* @phpstan-implements Property<TBlock>
*/
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<object>
*/
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);
}
}

View File

@ -0,0 +1,93 @@
<?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\property;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
use pocketmine\utils\AssumptionFailedError;
/**
* @phpstan-template TBlock of object
* @phpstan-template TOption of int|\UnitEnum
* @phpstan-implements Property<TBlock>
*/
class OptionSetFromIntProperty implements Property{
private int $maxValue = 0;
/**
* @phpstan-param StateMap<TOption, int> $map
* @phpstan-param \Closure(TBlock) : array<TOption> $getter
* @phpstan-param \Closure(TBlock, array<TOption>) : 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);
}
}

View File

@ -0,0 +1,44 @@
<?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\property;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
/**
* @phpstan-template TBlock of object
*/
interface Property{
public function getName() : string;
/**
* @phpstan-param TBlock $block
*/
public function deserialize(object $block, BlockStateReader $in) : void;
/**
* @phpstan-param TBlock $block
*/
public function serialize(object $block, BlockStateWriter $out) : void;
}

View File

@ -0,0 +1,53 @@
<?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\property;
/**
* @phpstan-template TValue
* @phpstan-template TRaw of int|string
*/
interface StateMap{
/**
* @phpstan-return array<TRaw, TValue>
*/
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;
}

View File

@ -0,0 +1,50 @@
<?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\property;
/**
* @phpstan-template TBlock of object
* @phpstan-extends Property<TBlock>
*/
interface StringProperty extends Property{
/**
* @return string[]
* @phpstan-return list<string>
*/
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;
}

View File

@ -0,0 +1,75 @@
<?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\property;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
use function array_keys;
/**
* @phpstan-template TBlock of object
* @phpstan-template TValue of int|\UnitEnum
* @phpstan-implements Property<TBlock>
*/
final class ValueFromIntProperty implements Property{
/**
* @phpstan-param StateMap<TValue, int> $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<int>
*/
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);
}
}

View File

@ -0,0 +1,81 @@
<?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\property;
use pocketmine\data\bedrock\block\BlockStateDeserializeException;
use pocketmine\data\bedrock\block\convert\BlockStateReader;
use pocketmine\data\bedrock\block\convert\BlockStateWriter;
use function array_keys;
use function array_map;
use function strval;
/**
* @phpstan-template TBlock of object
* @phpstan-template TValue of int|\UnitEnum
* @phpstan-implements StringProperty<TBlock>
*/
final class ValueFromStringProperty implements StringProperty{
/**
* @phpstan-param StateMap<TValue, string> $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<string>
*/
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);
}
}

View File

@ -0,0 +1,305 @@
<?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\property;
use pocketmine\block\Bamboo;
use pocketmine\block\utils\BellAttachmentType;
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\utils\MushroomBlockType;
use pocketmine\data\bedrock\block\BlockLegacyMetadata as LegacyMeta;
use pocketmine\data\bedrock\block\BlockStateStringValues as StringValues;
use pocketmine\data\bedrock\block\BlockTypeNames as Ids;
use pocketmine\math\Axis;
use pocketmine\math\Facing;
use pocketmine\utils\SingletonTrait;
final class ValueMappings{
use SingletonTrait; //???
/** @phpstan-var EnumFromRawStateMap<DyeColor, string> */
public readonly EnumFromRawStateMap $dyeColor;
/** @phpstan-var EnumFromRawStateMap<DyeColor, string> */
public readonly EnumFromRawStateMap $dyeColorWithSilver;
/** @phpstan-var EnumFromRawStateMap<MobHeadType, string> */
public readonly EnumFromRawStateMap $mobHeadType;
/** @phpstan-var EnumFromRawStateMap<FroglightType, string> */
public readonly EnumFromRawStateMap $froglightType;
/** @phpstan-var EnumFromRawStateMap<DirtType, string> */
public readonly EnumFromRawStateMap $dirtType;
/** @phpstan-var EnumFromRawStateMap<DripleafState, string> */
public readonly EnumFromRawStateMap $dripleafState;
/** @phpstan-var EnumFromRawStateMap<BellAttachmentType, string> */
public readonly EnumFromRawStateMap $bellAttachmentType;
/** @phpstan-var EnumFromRawStateMap<LeverFacing, string> */
public readonly EnumFromRawStateMap $leverFacing;
/** @phpstan-var EnumFromRawStateMap<MushroomBlockType, int> */
public readonly EnumFromRawStateMap $mushroomBlockType;
/** @phpstan-var IntFromRawStateMap<string> */
public readonly IntFromRawStateMap $cardinalDirection;
/** @phpstan-var IntFromRawStateMap<string> */
public readonly IntFromRawStateMap $blockFace;
/** @phpstan-var IntFromRawStateMap<string> */
public readonly IntFromRawStateMap $pillarAxis;
/** @phpstan-var IntFromRawStateMap<string> */
public readonly IntFromRawStateMap $torchFacing;
/** @phpstan-var IntFromRawStateMap<string> */
public readonly IntFromRawStateMap $portalAxis;
/** @phpstan-var IntFromRawStateMap<string> */
public readonly IntFromRawStateMap $bambooLeafSize;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $horizontalFacing5Minus;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $horizontalFacingSWNE;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $horizontalFacingSWNEInverted;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $horizontalFacingCoral;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $horizontalFacingClassic;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $facing;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $facingEndRod;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $coralAxis;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $facingExceptDown;
/** @phpstan-var IntFromRawStateMap<int> */
public readonly IntFromRawStateMap $facingExceptUp;
/** @phpstan-var IntFromRawStateMap<int> */
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]
);
}
}

View File

@ -0,0 +1,66 @@
<?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\property;
use pocketmine\block\utils\WallConnectionType;
use pocketmine\data\bedrock\block\BlockStateStringValues;
/**
* Internally we use null for no connections, but accepting this in the mapping code would require a fair amount of
* extra complexity for this one case. This shim allows us to use the regular systems for handling walls.
* TODO: get rid of this in PM6 and make the internal enum have a NONE case
*/
enum WallConnectionTypeShim{
case NONE;
case SHORT;
case TALL;
/**
* TODO: Would've just put this as enum values, but enum backing values can't reference constants in other files in
* PHP 8.1 :(
*/
public function getValue() : string{
return match($this){
self::NONE => 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,
};
}
}

View File

@ -26,7 +26,9 @@ namespace pocketmine\world\format\io;
use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateData;
use pocketmine\data\bedrock\block\BlockTypeNames; use pocketmine\data\bedrock\block\BlockTypeNames;
use pocketmine\data\bedrock\block\convert\BlockObjectToStateSerializer; 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\BlockStateToObjectDeserializer;
use pocketmine\data\bedrock\block\convert\VanillaBlockMappings;
use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockDataUpgrader;
use pocketmine\data\bedrock\block\upgrade\BlockIdMetaUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockIdMetaUpgrader;
use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader; use pocketmine\data\bedrock\block\upgrade\BlockStateUpgrader;
@ -44,20 +46,28 @@ use const pocketmine\BEDROCK_BLOCK_UPGRADE_SCHEMA_PATH;
* benefits for now. * benefits for now.
*/ */
final class GlobalBlockStateHandlers{ final class GlobalBlockStateHandlers{
private static ?BlockObjectToStateSerializer $blockStateSerializer = null;
private static ?BlockStateToObjectDeserializer $blockStateDeserializer = null;
private static ?BlockDataUpgrader $blockDataUpgrader = null; private static ?BlockDataUpgrader $blockDataUpgrader = null;
private static ?BlockStateData $unknownBlockStateData = 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{ public static function getDeserializer() : BlockStateToObjectDeserializer{
return self::$blockStateDeserializer ??= new BlockStateToObjectDeserializer(); return self::getRegistrar()->deserializer;
} }
public static function getSerializer() : BlockObjectToStateSerializer{ public static function getSerializer() : BlockObjectToStateSerializer{
return self::$blockStateSerializer ??= new BlockObjectToStateSerializer(); return self::getRegistrar()->serializer;
} }
public static function getUpgrader() : BlockDataUpgrader{ public static function getUpgrader() : BlockDataUpgrader{

View File

@ -600,6 +600,12 @@ parameters:
count: 1 count: 1
path: ../../../src/crash/CrashDumpRenderer.php path: ../../../src/crash/CrashDumpRenderer.php
-
message: '#^Parameter \#1 \$faces of method pocketmine\\block\\Vine\:\:setFaces\(\) expects list\<2\|3\|4\|5\>, array\<int\> 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\<string, string\>, array\<mixed, mixed\> given\.$#' message: '#^Parameter \#1 \$blockToItemId of class pocketmine\\data\\bedrock\\item\\BlockItemIdMap constructor expects array\<string, string\>, array\<mixed, mixed\> given\.$#'
identifier: argument.type identifier: argument.type

View File

@ -42,6 +42,8 @@ final class BlockSerializerDeserializerTest extends TestCase{
public function setUp() : void{ public function setUp() : void{
$this->deserializer = new BlockStateToObjectDeserializer(); $this->deserializer = new BlockStateToObjectDeserializer();
$this->serializer = new BlockObjectToStateSerializer(); $this->serializer = new BlockObjectToStateSerializer();
$registrar = new BlockSerializerDeserializerRegistrar($this->deserializer, $this->serializer);
VanillaBlockMappings::init($registrar);
} }
public function testAllKnownBlockStatesSerializableAndDeserializable() : void{ public function testAllKnownBlockStatesSerializableAndDeserializable() : void{
@ -49,12 +51,12 @@ final class BlockSerializerDeserializerTest extends TestCase{
try{ try{
$blockStateData = $this->serializer->serializeBlock($block); $blockStateData = $this->serializer->serializeBlock($block);
}catch(BlockStateSerializeException $e){ }catch(BlockStateSerializeException $e){
self::fail($e->getMessage()); self::fail("Failed to serialize " . $block->getName() . ": " . $e->getMessage());
} }
try{ try{
$newBlock = $this->deserializer->deserializeBlock($blockStateData); $newBlock = $this->deserializer->deserializeBlock($blockStateData);
}catch(BlockStateDeserializeException $e){ }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){ if($block->getTypeId() === BlockTypeIds::POTION_CAULDRON){