From 1da4c459794ba0af8e04bd6e56e6ac8173de72b2 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 25 Jun 2022 15:59:38 +0100 Subject: [PATCH] Add runtime support for wall connections this doesn't match the 1.16+ behaviour yet, but it at least recognizes walls that are already in the post-1.16 way and doesn't break them if not interacted with. --- src/block/Block.php | 2 +- src/block/Wall.php | 81 ++++++++++++++++--- src/block/utils/BlockDataReader.php | 21 +++++ src/block/utils/BlockDataWriter.php | 19 +++++ src/block/utils/WallConnectionType.php | 2 - .../convert/BlockStateDeserializerHelper.php | 11 ++- .../block/convert/BlockStateReader.php | 14 ++++ .../convert/BlockStateSerializerHelper.php | 12 ++- .../block/convert/BlockStateWriter.php | 12 +++ 9 files changed, 149 insertions(+), 25 deletions(-) diff --git a/src/block/Block.php b/src/block/Block.php index 5bf58b47c..4bdc776f2 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -49,7 +49,7 @@ use function count; use const PHP_INT_MAX; class Block{ - public const INTERNAL_STATE_DATA_BITS = 6; + public const INTERNAL_STATE_DATA_BITS = 9; public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS); protected BlockIdentifier $idInfo; diff --git a/src/block/Wall.php b/src/block/Wall.php index e9a6cb9e0..ed3325a34 100644 --- a/src/block/Wall.php +++ b/src/block/Wall.php @@ -23,30 +23,93 @@ declare(strict_types=1); namespace pocketmine\block; +use pocketmine\block\utils\BlockDataReader; +use pocketmine\block\utils\BlockDataWriter; use pocketmine\block\utils\SupportType; +use pocketmine\block\utils\WallConnectionType; use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; +/** + * @phpstan-type WallConnectionSet array + */ class Wall extends Transparent{ - /** @var int[] facing => facing */ + /** + * @var WallConnectionType[] + * @phpstan-var WallConnectionSet + */ protected array $connections = []; - protected bool $up = false; + protected bool $post = false; - public function readStateFromWorld() : void{ - parent::readStateFromWorld(); + protected function decodeState(BlockDataReader $r) : void{ + $this->connections = $r->readWallConnections(); + $this->post = $r->readBool(); + } - $this->recalculateConnections(); + protected function encodeState(BlockDataWriter $w) : void{ + $w->writeWallConnections($this->connections); + $w->writeBool($this->post); + } + + public function onNearbyBlockChange() : void{ + if($this->recalculateConnections()){ + $this->position->getWorld()->setBlock($this->position, $this); + } + } + + /** + * @return WallConnectionType[] + * @phpstan-return WallConnectionSet + */ + public function getConnections() : array{ return $this->connections; } + + public function getConnection(int $face) : ?WallConnectionType{ + return $this->connections[$face] ?? null; + } + + /** + * @param WallConnectionType[] $connections + * @phpstan-param WallConnectionSet $connections + * @return $this + */ + public function setConnections(array $connections) : self{ + $this->connections = $connections; + return $this; + } + + /** @return $this */ + public function setConnection(int $face, ?WallConnectionType $type) : self{ + if($face !== Facing::NORTH && $face !== Facing::SOUTH && $face !== Facing::WEST && $face !== Facing::EAST){ + throw new \InvalidArgumentException("Facing can only be north, east, south or west"); + } + if($type !== null){ + $this->connections[$face] = $type; + }else{ + unset($this->connections[$face]); + } + return $this; + } + + public function isPost() : bool{ return $this->post; } + + /** @return $this */ + public function setPost(bool $post) : self{ + $this->post = $post; + return $this; } protected function recalculateConnections() : bool{ $changed = 0; + + //TODO: implement tall/short connections - right now we only support short as per pre-1.16 + foreach(Facing::HORIZONTAL as $facing){ $block = $this->getSide($facing); if($block instanceof static || $block instanceof FenceGate || ($block->isSolid() && !$block->isTransparent())){ if(!isset($this->connections[$facing])){ - $this->connections[$facing] = $facing; + $this->connections[$facing] = WallConnectionType::SHORT(); $changed++; } }elseif(isset($this->connections[$facing])){ @@ -56,8 +119,8 @@ class Wall extends Transparent{ } $up = $this->getSide(Facing::UP)->getTypeId() !== BlockTypeIds::AIR; - if($up !== $this->up){ - $this->up = $up; + if($up !== $this->post){ + $this->post = $up; $changed++; } @@ -74,7 +137,7 @@ class Wall extends Transparent{ $inset = 0.25; if( - !$this->up && //if there is a block on top, it stays as a post + !$this->post && //if there is a block on top, it stays as a post ( ($north && $south && !$west && !$east) || (!$north && !$south && $west && $east) diff --git a/src/block/utils/BlockDataReader.php b/src/block/utils/BlockDataReader.php index e1e7bb689..22d55dbc8 100644 --- a/src/block/utils/BlockDataReader.php +++ b/src/block/utils/BlockDataReader.php @@ -97,4 +97,25 @@ final class BlockDataReader{ default => throw new AssumptionFailedError("Unreachable") }; } + + /** + * @return WallConnectionType[] + * @phpstan-return array + */ + public function readWallConnections() : array{ + $connections = []; + //TODO: we can pack this into 7 bits instead of 8 + foreach(Facing::HORIZONTAL as $facing){ + $type = $this->readBoundedInt(2, 0, 2); + if($type !== 0){ + $connections[$facing] = match($type){ + 1 => WallConnectionType::SHORT(), + 2 => WallConnectionType::TALL(), + default => throw new AssumptionFailedError("Unreachable") + }; + } + } + + return $connections; + } } diff --git a/src/block/utils/BlockDataWriter.php b/src/block/utils/BlockDataWriter.php index 9b37a8153..774a58484 100644 --- a/src/block/utils/BlockDataWriter.php +++ b/src/block/utils/BlockDataWriter.php @@ -25,6 +25,7 @@ namespace pocketmine\block\utils; use pocketmine\math\Axis; use pocketmine\math\Facing; +use pocketmine\utils\AssumptionFailedError; final class BlockDataWriter{ @@ -95,6 +96,24 @@ final class BlockDataWriter{ }); } + /** + * @param WallConnectionType[] $connections + * @phpstan-param array $connections + */ + public function writeWallConnections(array $connections) : self{ + //TODO: we can pack this into 7 bits instead of 8 + foreach(Facing::HORIZONTAL as $facing){ + $this->writeInt(2, match($connections[$facing] ?? null){ + null => 0, + WallConnectionType::SHORT() => 1, + WallConnectionType::TALL() => 2, + default => throw new AssumptionFailedError("Unreachable") + }); + } + + return $this; + } + public function getValue() : int{ return $this->value; } public function getOffset() : int{ return $this->offset; } diff --git a/src/block/utils/WallConnectionType.php b/src/block/utils/WallConnectionType.php index 60f2d01af..11765ab33 100644 --- a/src/block/utils/WallConnectionType.php +++ b/src/block/utils/WallConnectionType.php @@ -31,7 +31,6 @@ use pocketmine\utils\EnumTrait; * @see build/generate-registry-annotations.php * @generate-registry-docblock * - * @method static WallConnectionType NONE() * @method static WallConnectionType SHORT() * @method static WallConnectionType TALL() */ @@ -40,7 +39,6 @@ final class WallConnectionType{ protected static function setup() : void{ self::registerAll( - new self("none"), new self("short"), new self("tall") ); diff --git a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php index b5a76394d..a1c2719fc 100644 --- a/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateDeserializerHelper.php @@ -196,12 +196,11 @@ final class BlockStateDeserializerHelper{ /** @throws BlockStateDeserializeException */ public static function decodeWall(Wall $block, BlockStateReader $in) : Wall{ - //TODO: our walls don't support the full range of needed states yet - $in->todo(BlockStateNames::WALL_POST_BIT); //TODO - $in->todo(BlockStateNames::WALL_CONNECTION_TYPE_EAST); - $in->todo(BlockStateNames::WALL_CONNECTION_TYPE_NORTH); - $in->todo(BlockStateNames::WALL_CONNECTION_TYPE_SOUTH); - $in->todo(BlockStateNames::WALL_CONNECTION_TYPE_WEST); + $block->setPost($in->readBool(BlockStateNames::WALL_POST_BIT)); + $block->setConnection(Facing::NORTH, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_NORTH)); + $block->setConnection(Facing::SOUTH, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_SOUTH)); + $block->setConnection(Facing::WEST, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST)); + $block->setConnection(Facing::EAST, $in->readWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_EAST)); return $block; } diff --git a/src/data/bedrock/block/convert/BlockStateReader.php b/src/data/bedrock/block/convert/BlockStateReader.php index 3380721f5..0d1a0000f 100644 --- a/src/data/bedrock/block/convert/BlockStateReader.php +++ b/src/data/bedrock/block/convert/BlockStateReader.php @@ -27,6 +27,7 @@ use pocketmine\block\utils\BellAttachmentType; use pocketmine\block\utils\CoralType; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SlabType; +use pocketmine\block\utils\WallConnectionType; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateDeserializeException; use pocketmine\data\bedrock\block\BlockStateNames; @@ -296,6 +297,19 @@ final class BlockStateReader{ }; } + /** @throws BlockStateDeserializeException */ + public function readWallConnectionType(string $name) : ?WallConnectionType{ + return match($type = $this->readString($name)){ + //TODO: this looks a bit confusing due to use of EAST, but the values are the same for all connections + //we need to find a better way to auto-generate the constant names when they are reused + //for now, using these constants is better than nothing since it still gives static analysability + StringValues::WALL_CONNECTION_TYPE_EAST_NONE => null, + StringValues::WALL_CONNECTION_TYPE_EAST_SHORT => WallConnectionType::SHORT(), + StringValues::WALL_CONNECTION_TYPE_EAST_TALL => WallConnectionType::TALL(), + default => throw $this->badValueException($name, $type), + }; + } + /** * Explicitly mark a property as unused, so it doesn't get flagged as an error when debug mode is enabled */ diff --git a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php index 1abe7059a..b65abbaae 100644 --- a/src/data/bedrock/block/convert/BlockStateSerializerHelper.php +++ b/src/data/bedrock/block/convert/BlockStateSerializerHelper.php @@ -48,7 +48,6 @@ use pocketmine\block\Wall; use pocketmine\block\WallSign; use pocketmine\block\Wood; use pocketmine\data\bedrock\block\BlockStateNames; -use pocketmine\data\bedrock\block\BlockStateStringValues; use pocketmine\data\bedrock\block\BlockTypeNames as Ids; use pocketmine\data\bedrock\MushroomBlockTypeIdMap; use pocketmine\math\Axis; @@ -245,13 +244,12 @@ final class BlockStateSerializerHelper{ } public static function encodeWall(Wall $block, BlockStateWriter $out) : BlockStateWriter{ - //TODO: our walls don't support the full range of needed states yet return $out - ->writeBool(BlockStateNames::WALL_POST_BIT, false) - ->writeString(BlockStateNames::WALL_CONNECTION_TYPE_EAST, BlockStateStringValues::WALL_CONNECTION_TYPE_EAST_NONE) - ->writeString(BlockStateNames::WALL_CONNECTION_TYPE_NORTH, BlockStateStringValues::WALL_CONNECTION_TYPE_NORTH_NONE) - ->writeString(BlockStateNames::WALL_CONNECTION_TYPE_SOUTH, BlockStateStringValues::WALL_CONNECTION_TYPE_SOUTH_NONE) - ->writeString(BlockStateNames::WALL_CONNECTION_TYPE_WEST, BlockStateStringValues::WALL_CONNECTION_TYPE_WEST_NONE); + ->writeBool(BlockStateNames::WALL_POST_BIT, $block->isPost()) + ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_EAST, $block->getConnection(Facing::EAST)) + ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_NORTH, $block->getConnection(Facing::NORTH)) + ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_SOUTH, $block->getConnection(Facing::SOUTH)) + ->writeWallConnectionType(BlockStateNames::WALL_CONNECTION_TYPE_WEST, $block->getConnection(Facing::WEST)); } public static function encodeLegacyWall(Wall $block, string $type) : BlockStateWriter{ diff --git a/src/data/bedrock/block/convert/BlockStateWriter.php b/src/data/bedrock/block/convert/BlockStateWriter.php index 294c0c015..4186972dd 100644 --- a/src/data/bedrock/block/convert/BlockStateWriter.php +++ b/src/data/bedrock/block/convert/BlockStateWriter.php @@ -28,6 +28,7 @@ use pocketmine\block\utils\CoralType; use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SlabType; use pocketmine\block\utils\TreeType; +use pocketmine\block\utils\WallConnectionType; use pocketmine\data\bedrock\block\BlockStateData; use pocketmine\data\bedrock\block\BlockStateNames; use pocketmine\data\bedrock\block\BlockStateSerializeException; @@ -263,6 +264,17 @@ final class BlockStateWriter{ return $this; } + /** @return $this */ + public function writeWallConnectionType(string $name, ?WallConnectionType $wallConnectionType) : self{ + $this->writeString($name, match($wallConnectionType){ + null => StringValues::WALL_CONNECTION_TYPE_EAST_NONE, + WallConnectionType::SHORT() => StringValues::WALL_CONNECTION_TYPE_EAST_SHORT, + WallConnectionType::TALL() => StringValues::WALL_CONNECTION_TYPE_EAST_TALL, + default => throw new BlockStateSerializeException("Invalid Wall connection type " . $wallConnectionType->name()) + }); + return $this; + } + public function getBlockStateData() : BlockStateData{ return new BlockStateData($this->id, $this->states, BlockStateData::CURRENT_VERSION); }