From 5c0a109f1849a148731c8adf0e53d5b2a98d16f3 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 01:48:29 +0100 Subject: [PATCH 1/7] Sign: Strip trailing newlines from text blobs fixes sign editor always putting the cursor on the last line when right-clicking to edit --- src/block/tile/Sign.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index 0bb21a6d3..aef83e3cc 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -37,6 +37,7 @@ use function array_slice; use function explode; use function implode; use function mb_scrub; +use function rtrim; use function sprintf; /** @@ -117,7 +118,7 @@ class Sign extends Spawnable{ protected function writeSaveData(CompoundTag $nbt) : void{ $nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create() - ->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines())) + ->setString(self::TAG_TEXT_BLOB, rtrim(implode("\n", $this->text->getLines()), "\n")) ->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())) ->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0) ->setByte(self::TAG_PERSIST_FORMATTING, 1) @@ -162,7 +163,7 @@ class Sign extends Spawnable{ protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ $nbt->setTag(self::TAG_FRONT_TEXT, CompoundTag::create() - ->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines())) + ->setString(self::TAG_TEXT_BLOB, rtrim(implode("\n", $this->text->getLines()), "\n")) ->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())) ->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0) ->setByte(self::TAG_PERSIST_FORMATTING, 1) //TODO: not sure what this is used for From 4a2c7dc684f110cab3c37a75f1f79af4246c9ee7 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 02:15:24 +0100 Subject: [PATCH 2/7] Apparently hanging signs are self supporting --- src/block/BaseSign.php | 19 +------------------ src/block/WallHangingSign.php | 14 ++++++++------ 2 files changed, 9 insertions(+), 24 deletions(-) diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index b01157343..5a7df4663 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -103,27 +103,10 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ return SupportType::NONE; } - /** - * @deprecated - */ abstract protected function getSupportingFace() : int; - /** - * @return int[] - */ - protected function getSupportingFaceOptions() : array{ - return [$this->getSupportingFace()]; - } - public function onNearbyBlockChange() : void{ - $foundSupport = false; - foreach($this->getSupportingFaceOptions() as $face){ - if($this->getSide($face)->getTypeId() !== BlockTypeIds::AIR){ - $foundSupport = true; - break; - } - } - if(!$foundSupport){ + if($this->getSide($this->getSupportingFace())->getTypeId() !== BlockTypeIds::AIR){ $this->position->getWorld()->useBreakOn($this->position); } } diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php index c167036b1..2332f8e4f 100644 --- a/src/block/WallHangingSign.php +++ b/src/block/WallHangingSign.php @@ -27,6 +27,7 @@ use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; use pocketmine\item\Item; use pocketmine\math\Axis; +use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; @@ -39,12 +40,13 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ return Facing::rotateY($this->facing, clockwise: true); } - protected function getSupportingFaceOptions() : array{ - //wall hanging signs can be supported from either end of the post - return [ - Facing::rotateY($this->facing, clockwise: true), - Facing::rotateY($this->facing, clockwise: false) - ]; + public function onNearbyBlockChange() : void{ + //NOOP - disable default self-destruct behaviour + } + + protected function recalculateCollisionBoxes() : array{ + //only the cross bar is collidable + return [AxisAlignedBB::one()->trim(Facing::DOWN, 7 / 8)->squash(Facing::axis($this->facing), 3 / 4)]; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ From c54892311612b2786e964c9bc7f416fbdf4c2d64 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 02:16:38 +0100 Subject: [PATCH 3/7] ... --- src/block/BaseSign.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index 5a7df4663..0efaa603c 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -106,7 +106,7 @@ abstract class BaseSign extends Transparent implements WoodMaterial{ abstract protected function getSupportingFace() : int; public function onNearbyBlockChange() : void{ - if($this->getSide($this->getSupportingFace())->getTypeId() !== BlockTypeIds::AIR){ + if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){ $this->position->getWorld()->useBreakOn($this->position); } } From ec56d65bcc3b2696f759867d30c5e2f6d3d99082 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 25 Aug 2025 02:17:45 +0100 Subject: [PATCH 4/7] Fix BC break in BaseBanner --- src/block/BaseBanner.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/block/BaseBanner.php b/src/block/BaseBanner.php index 71a892c20..e8bf187ee 100644 --- a/src/block/BaseBanner.php +++ b/src/block/BaseBanner.php @@ -61,7 +61,12 @@ abstract class BaseBanner extends Transparent implements Colored{ return $this; } - abstract protected function getOminousVersion() : Block; + /** + * TODO: make this abstract in PM6 (BC break) + */ + protected function getOminousVersion() : Block{ + return VanillaBlocks::AIR(); + } public function writeStateToWorld() : void{ parent::writeStateToWorld(); From ac2c07c3fe7e90585cce4fc2b215aa0e20b687d0 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Mon, 25 Aug 2025 19:00:41 +0300 Subject: [PATCH 5/7] Added a space after hanging sign wood type (#6776) --- src/block/VanillaBlocks.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index bf9f7e5f5..e3b7a5f06 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -1444,8 +1444,8 @@ final class VanillaBlocks{ WoodType::CHERRY => VanillaItems::CHERRY_HANGING_SIGN(...), WoodType::PALE_OAK => VanillaItems::PALE_OAK_HANGING_SIGN(...), }; - self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . "Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); - self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . "Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . " Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . " Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); } } From db54c481aa5248a01ae59435cf3a7d954f3e28f8 Mon Sep 17 00:00:00 2001 From: ipad54 <63200545+ipad54@users.noreply.github.com> Date: Tue, 26 Aug 2025 01:27:17 +0300 Subject: [PATCH 6/7] Fixed hanging signs placement criteria (#6775) --- src/block/BlockTypeTags.php | 1 + src/block/CeilingCenterHangingSign.php | 9 ++++++++ src/block/CeilingEdgesHangingSign.php | 17 +++++++++++++++ src/block/VanillaBlocks.php | 7 ++++--- src/block/WallHangingSign.php | 22 ++++++++++++++++--- src/item/HangingSign.php | 29 ++++++++++++++++++++------ src/item/Item.php | 2 +- src/world/World.php | 2 +- 8 files changed, 75 insertions(+), 14 deletions(-) diff --git a/src/block/BlockTypeTags.php b/src/block/BlockTypeTags.php index 19a4825d9..531f3bcb3 100644 --- a/src/block/BlockTypeTags.php +++ b/src/block/BlockTypeTags.php @@ -31,4 +31,5 @@ final class BlockTypeTags{ public const SAND = self::PREFIX . "sand"; public const POTTABLE_PLANTS = self::PREFIX . "pottable"; public const FIRE = self::PREFIX . "fire"; + public const HANGING_SIGN = self::PREFIX . "hanging_sign"; } diff --git a/src/block/CeilingCenterHangingSign.php b/src/block/CeilingCenterHangingSign.php index 7078f38d8..1125de553 100644 --- a/src/block/CeilingCenterHangingSign.php +++ b/src/block/CeilingCenterHangingSign.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\SignLikeRotation; use pocketmine\block\utils\SignLikeRotationTrait; +use pocketmine\block\utils\StaticSupportTrait; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -33,6 +34,7 @@ use pocketmine\world\BlockTransaction; final class CeilingCenterHangingSign extends BaseSign implements SignLikeRotation{ use SignLikeRotationTrait; + use StaticSupportTrait; protected function getSupportingFace() : int{ return Facing::UP; @@ -49,4 +51,11 @@ final class CeilingCenterHangingSign extends BaseSign implements SignLikeRotatio } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } + + private function canBeSupportedAt(Block $block) : bool{ + $supportBlock = $block->getSide(Facing::UP); + return + $supportBlock->getSupportType(Facing::DOWN)->hasCenterSupport() || + $supportBlock->hasTypeTag(BlockTypeTags::HANGING_SIGN); + } } diff --git a/src/block/CeilingEdgesHangingSign.php b/src/block/CeilingEdgesHangingSign.php index 5dafe6932..3f7b6489b 100644 --- a/src/block/CeilingEdgesHangingSign.php +++ b/src/block/CeilingEdgesHangingSign.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\Facing; use pocketmine\math\Vector3; @@ -45,7 +46,23 @@ final class CeilingEdgesHangingSign extends BaseSign implements HorizontalFacing if($player !== null){ $this->facing = Facing::opposite($player->getHorizontalFacing()); } + if(!$this->canBeSupportedAt($blockReplace)){ + return false; + } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } + + public function onNearbyBlockChange() : void{ + if(!$this->canBeSupportedAt($this)){ + $this->position->getWorld()->useBreakOn($this->position); + } + } + + private function canBeSupportedAt(Block $block) : bool{ + $supportBlock = $block->getSide(Facing::UP); + return + $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || + (($supportBlock instanceof WallHangingSign || $supportBlock instanceof CeilingEdgesHangingSign) && Facing::axis($supportBlock->getFacing()) === Facing::axis($this->facing)); + } } diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index e3b7a5f06..36074c606 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -1391,6 +1391,7 @@ final class VanillaBlocks{ private static function registerWoodenBlocks() : void{ $planksBreakInfo = new Info(BreakInfo::axe(2.0, null, 15.0)); $signBreakInfo = new Info(BreakInfo::axe(1.0)); + $hangingSignBreakInfo = new Info(BreakInfo::axe(1.0), [Tags::HANGING_SIGN]); $logBreakInfo = new Info(BreakInfo::axe(2.0)); $woodenDoorBreakInfo = new Info(BreakInfo::axe(3.0, null, 15.0)); $woodenButtonBreakInfo = new Info(BreakInfo::axe(0.5)); @@ -1444,9 +1445,9 @@ final class VanillaBlocks{ WoodType::CHERRY => VanillaItems::CHERRY_HANGING_SIGN(...), WoodType::PALE_OAK => VanillaItems::PALE_OAK_HANGING_SIGN(...), }; - self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . " Center Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); - self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . " Edges Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); - self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $signBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_center_hanging_sign"), fn(BID $id) => new CeilingCenterHangingSign($id, $name . " Center Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("ceiling_edges_hanging_sign"), fn(BID $id) => new CeilingEdgesHangingSign($id, $name . " Edges Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); + self::register($idName("wall_hanging_sign"), fn(BID $id) => new WallHangingSign($id, $name . " Wall Hanging Sign", $hangingSignBreakInfo, $woodType, $hangingSignAsItem), TileHangingSign::class); } } diff --git a/src/block/WallHangingSign.php b/src/block/WallHangingSign.php index 2332f8e4f..df959c720 100644 --- a/src/block/WallHangingSign.php +++ b/src/block/WallHangingSign.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingTrait; +use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\Axis; use pocketmine\math\AxisAlignedBB; @@ -50,16 +51,31 @@ final class WallHangingSign extends BaseSign implements HorizontalFacing{ } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if(Facing::axis($face) === Axis::Y){ + if($player === null){ + return false; + } + $attachFace = Facing::axis($face) === Axis::Y ? Facing::rotateY($player->getHorizontalFacing(), clockwise: true) : $face; + + if($this->canBeSupportedAt($blockReplace->getSide($attachFace), $attachFace)){ + $direction = $attachFace; + }elseif($this->canBeSupportedAt($blockReplace->getSide($opposite = Facing::opposite($attachFace)), $opposite)){ + $direction = $opposite; + }else{ return false; } - $this->facing = Facing::rotateY($face, clockwise: true); + $this->facing = Facing::rotateY(Facing::opposite($direction), clockwise: true); //the front should always face the player if possible - if($player !== null && $this->facing === $player->getHorizontalFacing()){ + if($this->facing === $player->getHorizontalFacing()){ $this->facing = Facing::opposite($this->facing); } return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } + + private function canBeSupportedAt(Block $block, int $face) : bool{ + return + ($block instanceof WallHangingSign && Facing::axis(Facing::rotateY($block->getFacing(), clockwise: true)) === Facing::axis($face)) || + $block->getSupportType(Facing::opposite($face)) === SupportType::FULL; + } } diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php index 7143637ba..5e3bd068a 100644 --- a/src/item/HangingSign.php +++ b/src/item/HangingSign.php @@ -24,9 +24,13 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\block\Block; +use pocketmine\block\CeilingCenterHangingSign; +use pocketmine\block\CeilingEdgesHangingSign; use pocketmine\block\utils\SupportType; +use pocketmine\block\WallHangingSign; use pocketmine\math\Facing; use pocketmine\math\Vector3; +use pocketmine\player\Player; final class HangingSign extends Item{ @@ -40,13 +44,26 @@ final class HangingSign extends Item{ parent::__construct($identifier, $name); } - public function getPlacementBlock(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ + public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ //we don't verify valid placement conditions here, only decide which block to return - $result = $face === Facing::DOWN ? - $blockReplace->getSide(Facing::UP)->getSupportType(Facing::DOWN) === SupportType::CENTER ? - $this->centerPointCeilingVariant : - $this->edgePointCeilingVariant - : $this->wallVariant; + if($face === Facing::DOWN){ + if($player !== null && $player->isSneaking()){ + return clone $this->centerPointCeilingVariant; + } + + //we select the center variant when support is edge/wall sign with perpendicular player facing, + //support is a center sign itself, or support provides center support. + //otherwise use the edge variant. + $support = $blockReplace->getSide(Facing::UP); + $result = + (($support instanceof CeilingEdgesHangingSign || $support instanceof WallHangingSign) && ($player === null || Facing::axis($player->getHorizontalFacing()) !== Facing::axis($support->getFacing()))) || + $support instanceof CeilingCenterHangingSign || + $support->getSupportType(Facing::DOWN) === SupportType::CENTER ? + $this->centerPointCeilingVariant : + $this->edgePointCeilingVariant; + }else{ + $result = $this->wallVariant; + } return clone $result; } diff --git a/src/item/Item.php b/src/item/Item.php index 6786238b0..c286a2bff 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -485,7 +485,7 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } - public function getPlacementBlock(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ + public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ return $this->getBlock($face); } diff --git a/src/world/World.php b/src/world/World.php index 4f2e222ca..bd79ae083 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2298,7 +2298,7 @@ class World implements ChunkManager{ if($item->isNull() || !$item->canBePlaced()){ return false; } - $hand = $item->getPlacementBlock($blockReplace, $blockClicked, $face, $clickVector); + $hand = $item->getPlacementBlock($player, $blockReplace, $blockClicked, $face, $clickVector); $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ From de234d1f382ca4b4c8bf489e016ae59013693910 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Tue, 26 Aug 2025 00:10:50 +0100 Subject: [PATCH 7/7] Improved placement logic --- src/item/HangingSign.php | 33 +++++++++------------------------ src/item/Item.php | 15 +++++++++++++-- src/world/World.php | 35 +++++++++++++++-------------------- 3 files changed, 37 insertions(+), 46 deletions(-) diff --git a/src/item/HangingSign.php b/src/item/HangingSign.php index 5e3bd068a..a6752087a 100644 --- a/src/item/HangingSign.php +++ b/src/item/HangingSign.php @@ -24,13 +24,10 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\block\Block; -use pocketmine\block\CeilingCenterHangingSign; -use pocketmine\block\CeilingEdgesHangingSign; -use pocketmine\block\utils\SupportType; -use pocketmine\block\WallHangingSign; use pocketmine\math\Facing; use pocketmine\math\Vector3; use pocketmine\player\Player; +use pocketmine\world\BlockTransaction; final class HangingSign extends Item{ @@ -44,27 +41,15 @@ final class HangingSign extends Item{ parent::__construct($identifier, $name); } - public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ - //we don't verify valid placement conditions here, only decide which block to return - if($face === Facing::DOWN){ - if($player !== null && $player->isSneaking()){ - return clone $this->centerPointCeilingVariant; - } - - //we select the center variant when support is edge/wall sign with perpendicular player facing, - //support is a center sign itself, or support provides center support. - //otherwise use the edge variant. - $support = $blockReplace->getSide(Facing::UP); - $result = - (($support instanceof CeilingEdgesHangingSign || $support instanceof WallHangingSign) && ($player === null || Facing::axis($player->getHorizontalFacing()) !== Facing::axis($support->getFacing()))) || - $support instanceof CeilingCenterHangingSign || - $support->getSupportType(Facing::DOWN) === SupportType::CENTER ? - $this->centerPointCeilingVariant : - $this->edgePointCeilingVariant; - }else{ - $result = $this->wallVariant; + public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ + if($face !== Facing::DOWN){ + return $this->tryPlacementTransaction(clone $this->wallVariant, $blockReplace, $blockClicked, $face, $clickVector, $player); } - return clone $result; + //ceiling edges sign has stricter placement conditions than ceiling center sign, so try that first + $ceilingEdgeTx = $player === null || !$player->isSneaking() ? + $this->tryPlacementTransaction(clone $this->edgePointCeilingVariant, $blockReplace, $blockClicked, $face, $clickVector, $player) : + null; + return $ceilingEdgeTx ?? $this->tryPlacementTransaction(clone $this->centerPointCeilingVariant, $blockReplace, $blockClicked, $face, $clickVector, $player); } public function getBlock(?int $clickedFace = null) : Block{ diff --git a/src/item/Item.php b/src/item/Item.php index c286a2bff..af7cab433 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -48,6 +48,7 @@ use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\TreeRoot; use pocketmine\player\Player; use pocketmine\utils\Utils; +use pocketmine\world\BlockTransaction; use pocketmine\world\format\io\GlobalItemDataHandlers; use function base64_decode; use function base64_encode; @@ -485,8 +486,18 @@ class Item implements \JsonSerializable{ return $this->getBlock()->canBePlaced(); } - public function getPlacementBlock(?Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : Block{ - return $this->getBlock($face); + protected final function tryPlacementTransaction(Block $blockPlace, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player) : ?BlockTransaction{ + $position = $blockReplace->getPosition(); + $blockPlace->position($position->getWorld(), $position->getFloorX(), $position->getFloorY(), $position->getFloorZ()); + if(!$blockPlace->canBePlacedAt($blockReplace, $clickVector, $face, $blockReplace->getPosition()->equals($blockClicked->getPosition()))){ + return null; + } + $transaction = new BlockTransaction($position->getWorld()); + return $blockPlace->place($transaction, $this, $blockReplace, $blockClicked, $face, $clickVector, $player) ? $transaction : null; + } + + public function getPlacementTransaction(Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : ?BlockTransaction{ + return $this->tryPlacementTransaction($this->getBlock($face), $blockReplace, $blockClicked, $face, $clickVector, $player); } /** diff --git a/src/world/World.php b/src/world/World.php index bd79ae083..236fd6e56 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -2298,22 +2298,15 @@ class World implements ChunkManager{ if($item->isNull() || !$item->canBePlaced()){ return false; } - $hand = $item->getPlacementBlock($player, $blockReplace, $blockClicked, $face, $clickVector); - $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); - if($hand->canBePlacedAt($blockClicked, $clickVector, $face, true)){ - $blockReplace = $blockClicked; - //TODO: while this mimics the vanilla behaviour with replaceable blocks, we should really pass some other - //value like NULL and let place() deal with it. This will look like a bug to anyone who doesn't know about - //the vanilla behaviour. - $face = Facing::UP; - $hand->position($this, $blockReplace->getPosition()->x, $blockReplace->getPosition()->y, $blockReplace->getPosition()->z); - }elseif(!$hand->canBePlacedAt($blockReplace, $clickVector, $face, false)){ - return false; - } - - $tx = new BlockTransaction($this); - if(!$hand->place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player)){ + //TODO: while passing Facing::UP mimics the vanilla behaviour with replaceable blocks, we should really pass + //some other value like NULL and let place() deal with it. This will look like a bug to anyone who doesn't know + //about the vanilla behaviour. + $tx = + $item->getPlacementTransaction($blockClicked, $blockClicked, Facing::UP, $clickVector, $player) ?? + $item->getPlacementTransaction($blockReplace, $blockClicked, $face, $clickVector, $player); + if($tx === null){ + //no placement options available return false; } @@ -2357,6 +2350,7 @@ class World implements ChunkManager{ if(!$tx->apply()){ return false; } + $first = true; foreach($tx->getBlocks() as [$x, $y, $z, $_]){ $tile = $this->getTileAt($x, $y, $z); if($tile !== null){ @@ -2364,11 +2358,12 @@ class World implements ChunkManager{ $tile->copyDataFromItem($item); } - $this->getBlockAt($x, $y, $z)->onPostPlace(); - } - - if($playSound){ - $this->addSound($hand->getPosition(), new BlockPlaceSound($hand)); + $placed = $this->getBlockAt($x, $y, $z); + $placed->onPostPlace(); + if($first && $playSound){ + $this->addSound($placed->getPosition(), new BlockPlaceSound($placed)); + } + $first = false; } $item->pop();