mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
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.
This commit is contained in:
parent
b9542b4908
commit
1da4c45979
@ -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;
|
||||
|
@ -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<Facing::NORTH|Facing::EAST|Facing::SOUTH|Facing::WEST, WallConnectionType>
|
||||
*/
|
||||
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)
|
||||
|
@ -97,4 +97,25 @@ final class BlockDataReader{
|
||||
default => throw new AssumptionFailedError("Unreachable")
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* @return WallConnectionType[]
|
||||
* @phpstan-return array<Facing::NORTH|Facing::EAST|Facing::SOUTH|Facing::WEST, WallConnectionType>
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -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<Facing::NORTH|Facing::EAST|Facing::SOUTH|Facing::WEST, WallConnectionType> $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; }
|
||||
|
@ -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")
|
||||
);
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
*/
|
||||
|
@ -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{
|
||||
|
@ -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);
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user