From 3c6b0993ccbb6528b5274d96c14b64db4f069ddf Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 30 Aug 2025 23:38:07 +0100 Subject: [PATCH] Finally deal with APIs on container and menu blocks Two main interfaces are introduced: - Container (chest, furnace, etc) - MenuAccessor (includes Containers but also anvils, crafting tables etc. which are not containers) Containers have inventories, locks, and everything from MenuAccessor MenuAccessors have obstruction checks, and openToUnchecked() for plugins to use I opted not to include precondition checks in openTo() because it's possible to replicate those using the provided APIs if desired. --- src/block/Anvil.php | 9 ++-- src/block/Barrel.php | 15 ++++--- src/block/BrewingStand.php | 5 ++- src/block/CartographyTable.php | 9 ++-- src/block/Chest.php | 29 ++++++------ src/block/CraftingTable.php | 9 ++-- src/block/EnchantingTable.php | 9 ++-- src/block/EnderChest.php | 40 ++++++++--------- src/block/Furnace.php | 5 ++- src/block/Hopper.php | 5 ++- src/block/Loom.php | 9 ++-- src/block/ShulkerBox.php | 19 ++++---- src/block/SmithingTable.php | 9 ++-- src/block/Stonecutter.php | 9 ++-- .../inventory/window/BlockInventoryWindow.php | 10 ++--- ...ontainer.php => AnimatedContainerLike.php} | 10 +++-- ...ait.php => AnimatedContainerLikeTrait.php} | 32 +++++++------- src/block/utils/Container.php | 44 +++++++++++++++++++ src/block/utils/ContainerTrait.php | 40 ++++++++--------- ...ntainerOpenResult.php => MenuAccessor.php} | 28 ++++++------ ...oryMenuTrait.php => MenuAccessorTrait.php} | 18 +++----- 21 files changed, 211 insertions(+), 152 deletions(-) rename src/block/utils/{AnimatedContainer.php => AnimatedContainerLike.php} (77%) rename src/block/utils/{AnimatedContainerTrait.php => AnimatedContainerLikeTrait.php} (60%) create mode 100644 src/block/utils/Container.php rename src/block/utils/{ContainerOpenResult.php => MenuAccessor.php} (55%) rename src/block/utils/{InventoryMenuTrait.php => MenuAccessorTrait.php} (70%) diff --git a/src/block/Anvil.php b/src/block/Anvil.php index 7de5e471a..87b11dfd7 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -29,7 +29,8 @@ use pocketmine\block\utils\FallableTrait; use pocketmine\block\utils\HorizontalFacing; use pocketmine\block\utils\HorizontalFacingOption; use pocketmine\block\utils\HorizontalFacingTrait; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessor; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\object\FallingBlock; @@ -45,10 +46,10 @@ use pocketmine\world\sound\AnvilFallSound; use pocketmine\world\sound\Sound; use function round; -class Anvil extends Transparent implements Fallable, HorizontalFacing{ +class Anvil extends Transparent implements Fallable, HorizontalFacing, MenuAccessor{ use FallableTrait; use HorizontalFacingTrait; - use InventoryMenuTrait; + use MenuAccessorTrait; public const UNDAMAGED = 0; public const SLIGHTLY_DAMAGED = 1; @@ -83,7 +84,7 @@ class Anvil extends Transparent implements Fallable, HorizontalFacing{ return SupportType::NONE; } - protected function newWindow(Player $player, Position $position) : AnvilInventoryWindow{ + protected function newMenu(Player $player, Position $position) : AnvilInventoryWindow{ return new AnvilInventoryWindow($player, $position); } diff --git a/src/block/Barrel.php b/src/block/Barrel.php index 363b73acd..37d29579a 100644 --- a/src/block/Barrel.php +++ b/src/block/Barrel.php @@ -23,10 +23,11 @@ declare(strict_types=1); namespace pocketmine\block; -use pocketmine\block\utils\AnimatedContainer; -use pocketmine\block\utils\AnimatedContainerTrait; +use pocketmine\block\utils\AnimatedContainerLike; +use pocketmine\block\utils\AnimatedContainerLikeTrait; use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; +use pocketmine\block\utils\Container; use pocketmine\block\utils\ContainerTrait; use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\item\Item; @@ -40,8 +41,8 @@ use pocketmine\world\sound\BarrelOpenSound; use pocketmine\world\sound\Sound; use function abs; -class Barrel extends Opaque implements AnimatedContainer, AnyFacing{ - use AnimatedContainerTrait; +class Barrel extends Opaque implements AnimatedContainerLike, AnyFacing, Container{ + use AnimatedContainerLikeTrait; use AnyFacingTrait; use ContainerTrait; @@ -86,15 +87,15 @@ class Barrel extends Opaque implements AnimatedContainer, AnyFacing{ return 300; } - protected function getContainerOpenSound() : Sound{ + protected function getOpenSound() : Sound{ return new BarrelOpenSound(); } - protected function getContainerCloseSound() : Sound{ + protected function getCloseSound() : Sound{ return new BarrelCloseSound(); } - protected function doContainerAnimation(Position $position, bool $isOpen) : void{ + protected function playAnimationVisual(Position $position, bool $isOpen) : void{ $world = $position->getWorld(); $block = $world->getBlock($position); if($block instanceof Barrel){ diff --git a/src/block/BrewingStand.php b/src/block/BrewingStand.php index 210384ea8..6ccffd28e 100644 --- a/src/block/BrewingStand.php +++ b/src/block/BrewingStand.php @@ -26,6 +26,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\BrewingStandInventoryWindow; use pocketmine\block\tile\BrewingStand as TileBrewingStand; use pocketmine\block\utils\BrewingStandSlot; +use pocketmine\block\utils\Container; use pocketmine\block\utils\ContainerTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -38,7 +39,7 @@ use pocketmine\world\Position; use function array_key_exists; use function spl_object_id; -class BrewingStand extends Transparent{ +class BrewingStand extends Transparent implements Container{ use ContainerTrait; /** @@ -98,7 +99,7 @@ class BrewingStand extends Transparent{ return $this; } - protected function newWindow(Player $player, Inventory $inventory, Position $position) : BrewingStandInventoryWindow{ + protected function newMenu(Player $player, Inventory $inventory, Position $position) : BrewingStandInventoryWindow{ return new BrewingStandInventoryWindow($player, $inventory, $position); } diff --git a/src/block/CartographyTable.php b/src/block/CartographyTable.php index a1eeb6cb2..798b5a7af 100644 --- a/src/block/CartographyTable.php +++ b/src/block/CartographyTable.php @@ -24,14 +24,15 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\inventory\window\CartographyTableInventoryWindow; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessor; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\player\Player; use pocketmine\world\Position; -final class CartographyTable extends Opaque{ - use InventoryMenuTrait; +final class CartographyTable extends Opaque implements MenuAccessor{ + use MenuAccessorTrait; - protected function newWindow(Player $player, Position $position) : CartographyTableInventoryWindow{ + protected function newMenu(Player $player, Position $position) : CartographyTableInventoryWindow{ return new CartographyTableInventoryWindow($player, $position); } diff --git a/src/block/Chest.php b/src/block/Chest.php index 77cb02492..18868a606 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -26,8 +26,9 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\BlockInventoryWindow; use pocketmine\block\inventory\window\DoubleChestInventoryWindow; use pocketmine\block\tile\Chest as TileChest; -use pocketmine\block\utils\AnimatedContainer; -use pocketmine\block\utils\AnimatedContainerTrait; +use pocketmine\block\utils\AnimatedContainerLike; +use pocketmine\block\utils\AnimatedContainerLikeTrait; +use pocketmine\block\utils\Container; use pocketmine\block\utils\ContainerTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; @@ -45,8 +46,8 @@ use pocketmine\world\sound\ChestCloseSound; use pocketmine\world\sound\ChestOpenSound; use pocketmine\world\sound\Sound; -class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{ - use AnimatedContainerTrait; +class Chest extends Transparent implements AnimatedContainerLike, Container, HorizontalFacing{ + use AnimatedContainerLikeTrait; use ContainerTrait; use FacesOppositePlacingPlayerTrait; @@ -102,7 +103,7 @@ class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{ } } - protected function isOpeningObstructed() : bool{ + public function isOpeningObstructed() : bool{ if(!$this->getSide(Facing::UP)->isTransparent()){ return true; } @@ -110,7 +111,7 @@ class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{ return $pair !== null && !$pair->getBlock()->getSide(Facing::UP)->isTransparent(); } - protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ + protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ [$pairOnLeft, $pair] = $this->locatePair($position) ?? [false, null]; if($pair === null){ return new BlockInventoryWindow($player, $inventory, $position); @@ -127,29 +128,29 @@ class Chest extends Transparent implements AnimatedContainer, HorizontalFacing{ return 300; } - protected function getContainerOpenSound() : Sound{ + protected function getOpenSound() : Sound{ return new ChestOpenSound(); } - protected function getContainerCloseSound() : Sound{ + protected function getCloseSound() : Sound{ return new ChestCloseSound(); } - protected function doContainerAnimation(Position $position, bool $isOpen) : void{ + protected function playAnimationVisual(Position $position, bool $isOpen) : void{ //event ID is always 1 for a chest //TODO: we probably shouldn't be sending a packet directly here, but it doesn't fit anywhere into existing systems $position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0)); } - protected function doContainerEffects(bool $isOpen) : void{ - $this->doContainerAnimation($this->position, $isOpen); - $this->playContainerSound($this->position, $isOpen); + protected function doAnimationEffects(bool $isOpen) : void{ + $this->playAnimationVisual($this->position, $isOpen); + $this->playAnimationSound($this->position, $isOpen); $pairInfo = $this->locatePair($this->position); if($pairInfo !== null){ [, $pair] = $pairInfo; - $this->doContainerAnimation($pair->getPosition(), $isOpen); - $this->playContainerSound($pair->getPosition(), $isOpen); + $this->playAnimationVisual($pair->getPosition(), $isOpen); + $this->playAnimationSound($pair->getPosition(), $isOpen); } } } diff --git a/src/block/CraftingTable.php b/src/block/CraftingTable.php index d8ef03def..9ea2380e1 100644 --- a/src/block/CraftingTable.php +++ b/src/block/CraftingTable.php @@ -24,14 +24,15 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\inventory\window\CraftingTableInventoryWindow; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessor; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\player\Player; use pocketmine\world\Position; -class CraftingTable extends Opaque{ - use InventoryMenuTrait; +class CraftingTable extends Opaque implements MenuAccessor{ + use MenuAccessorTrait; - protected function newWindow(Player $player, Position $position) : CraftingTableInventoryWindow{ + protected function newMenu(Player $player, Position $position) : CraftingTableInventoryWindow{ return new CraftingTableInventoryWindow($player, $position); } diff --git a/src/block/EnchantingTable.php b/src/block/EnchantingTable.php index 7756e10fb..ec906b534 100644 --- a/src/block/EnchantingTable.php +++ b/src/block/EnchantingTable.php @@ -24,15 +24,16 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\inventory\window\EnchantingTableInventoryWindow; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessor; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\block\utils\SupportType; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\player\Player; use pocketmine\world\Position; -class EnchantingTable extends Transparent{ - use InventoryMenuTrait; +class EnchantingTable extends Transparent implements MenuAccessor{ + use MenuAccessorTrait; protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trimmedCopy(Facing::UP, 0.25)]; @@ -42,7 +43,7 @@ class EnchantingTable extends Transparent{ return SupportType::NONE; } - protected function newWindow(Player $player, Position $position) : EnchantingTableInventoryWindow{ + protected function newMenu(Player $player, Position $position) : EnchantingTableInventoryWindow{ return new EnchantingTableInventoryWindow($player, $position); } } diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php index ce07f0e73..8047bd770 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -25,11 +25,11 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\BlockInventoryWindow; use pocketmine\block\tile\EnderChest as TileEnderChest; -use pocketmine\block\utils\AnimatedContainer; -use pocketmine\block\utils\AnimatedContainerTrait; +use pocketmine\block\utils\AnimatedContainerLike; +use pocketmine\block\utils\AnimatedContainerLikeTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; @@ -42,12 +42,12 @@ use pocketmine\world\sound\EnderChestCloseSound; use pocketmine\world\sound\EnderChestOpenSound; use pocketmine\world\sound\Sound; -class EnderChest extends Transparent implements AnimatedContainer, HorizontalFacing{ - use AnimatedContainerTrait { - onContainerOpen as private traitOnContainerOpen; - onContainerClose as private traitOnContainerClose; +class EnderChest extends Transparent implements AnimatedContainerLike, HorizontalFacing{ + use AnimatedContainerLikeTrait { + onViewerAdded as private traitOnViewerAdded; + onViewerRemoved as private traitOnViewerRemoved; } - use InventoryMenuTrait; + use MenuAccessorTrait; use FacesOppositePlacingPlayerTrait; public function getLightLevel() : int{ @@ -67,7 +67,7 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac return !$this->getSide(Facing::UP)->isTransparent(); } - protected function newWindow(Player $player, Position $position) : BlockInventoryWindow{ + protected function newMenu(Player $player, Position $position) : BlockInventoryWindow{ return new BlockInventoryWindow($player, $player->getEnderInventory(), $position); } @@ -81,7 +81,7 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac return true; } - protected function getContainerViewerCount() : int{ + protected function getViewerCount() : int{ $enderChest = $this->position->getWorld()->getTile($this->position); if(!$enderChest instanceof TileEnderChest){ return 0; @@ -89,34 +89,34 @@ class EnderChest extends Transparent implements AnimatedContainer, HorizontalFac return $enderChest->getViewerCount(); } - private function updateContainerViewerCount(int $amount) : void{ + private function updateViewerCount(int $amount) : void{ $enderChest = $this->position->getWorld()->getTile($this->position); if($enderChest instanceof TileEnderChest){ $enderChest->setViewerCount($enderChest->getViewerCount() + $amount); } } - protected function getContainerOpenSound() : Sound{ + protected function getOpenSound() : Sound{ return new EnderChestOpenSound(); } - protected function getContainerCloseSound() : Sound{ + protected function getCloseSound() : Sound{ return new EnderChestCloseSound(); } - protected function doContainerAnimation(Position $position, bool $isOpen) : void{ + protected function playAnimationVisual(Position $position, bool $isOpen) : void{ //event ID is always 1 for a chest //TODO: we probably shouldn't be sending a packet directly here, but it doesn't fit anywhere into existing systems $position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0)); } - public function onContainerOpen() : void{ - $this->updateContainerViewerCount(1); - $this->traitOnContainerOpen(); + public function onViewerAdded() : void{ + $this->updateViewerCount(1); + $this->traitOnViewerAdded(); } - public function onContainerClose() : void{ - $this->traitOnContainerClose(); - $this->updateContainerViewerCount(-1); + public function onViewerRemoved() : void{ + $this->traitOnViewerRemoved(); + $this->updateViewerCount(-1); } } diff --git a/src/block/Furnace.php b/src/block/Furnace.php index 4ced668e2..f45d301a5 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\FurnaceInventoryWindow; use pocketmine\block\tile\Furnace as TileFurnace; +use pocketmine\block\utils\Container; use pocketmine\block\utils\ContainerTrait; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; @@ -38,7 +39,7 @@ use pocketmine\player\Player; use pocketmine\world\Position; use function mt_rand; -class Furnace extends Opaque implements Lightable, HorizontalFacing{ +class Furnace extends Opaque implements Container, Lightable, HorizontalFacing{ use ContainerTrait; use FacesOppositePlacingPlayerTrait; use LightableTrait; @@ -63,7 +64,7 @@ class Furnace extends Opaque implements Lightable, HorizontalFacing{ return $this->lit ? 13 : 0; } - protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ + protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ return new FurnaceInventoryWindow($player, $inventory, $position, $this->furnaceType); } diff --git a/src/block/Hopper.php b/src/block/Hopper.php index 0117edb5f..f0394ad05 100644 --- a/src/block/Hopper.php +++ b/src/block/Hopper.php @@ -24,6 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\inventory\window\HopperInventoryWindow; +use pocketmine\block\utils\Container; use pocketmine\block\utils\ContainerTrait; use pocketmine\block\utils\PoweredByRedstone; use pocketmine\block\utils\PoweredByRedstoneTrait; @@ -39,7 +40,7 @@ use pocketmine\player\Player; use pocketmine\world\BlockTransaction; use pocketmine\world\Position; -class Hopper extends Transparent implements PoweredByRedstone{ +class Hopper extends Transparent implements Container, PoweredByRedstone{ use ContainerTrait; use PoweredByRedstoneTrait; @@ -86,7 +87,7 @@ class Hopper extends Transparent implements PoweredByRedstone{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ + protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ return new HopperInventoryWindow($player, $inventory, $position); } diff --git a/src/block/Loom.php b/src/block/Loom.php index 564117692..a56f4a712 100644 --- a/src/block/Loom.php +++ b/src/block/Loom.php @@ -26,15 +26,16 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\LoomInventoryWindow; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessor; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\player\Player; use pocketmine\world\Position; -final class Loom extends Opaque implements HorizontalFacing{ +final class Loom extends Opaque implements HorizontalFacing, MenuAccessor{ use FacesOppositePlacingPlayerTrait; - use InventoryMenuTrait; + use MenuAccessorTrait; - protected function newWindow(Player $player, Position $position) : LoomInventoryWindow{ + protected function newMenu(Player $player, Position $position) : LoomInventoryWindow{ return new LoomInventoryWindow($player, $position); } } diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php index 5b6d051c3..fa78c8695 100644 --- a/src/block/ShulkerBox.php +++ b/src/block/ShulkerBox.php @@ -25,10 +25,11 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\BlockInventoryWindow; use pocketmine\block\tile\ShulkerBox as TileShulkerBox; -use pocketmine\block\utils\AnimatedContainer; -use pocketmine\block\utils\AnimatedContainerTrait; +use pocketmine\block\utils\AnimatedContainerLike; +use pocketmine\block\utils\AnimatedContainerLikeTrait; use pocketmine\block\utils\AnyFacing; use pocketmine\block\utils\AnyFacingTrait; +use pocketmine\block\utils\Container; use pocketmine\block\utils\ContainerTrait; use pocketmine\block\utils\SupportType; use pocketmine\data\runtime\RuntimeDataDescriber; @@ -46,8 +47,8 @@ use pocketmine\world\sound\ShulkerBoxCloseSound; use pocketmine\world\sound\ShulkerBoxOpenSound; use pocketmine\world\sound\Sound; -class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{ - use AnimatedContainerTrait; +class ShulkerBox extends Opaque implements AnimatedContainerLike, AnyFacing, Container{ + use AnimatedContainerLikeTrait; use AnyFacingTrait; use ContainerTrait; @@ -109,11 +110,11 @@ class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{ return $result; } - protected function isOpeningObstructed() : bool{ + public function isOpeningObstructed() : bool{ return $this->getSide($this->facing)->isSolid(); } - protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ + protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ return new BlockInventoryWindow($player, $inventory, $position); } @@ -121,15 +122,15 @@ class ShulkerBox extends Opaque implements AnimatedContainer, AnyFacing{ return SupportType::NONE; } - protected function getContainerOpenSound() : Sound{ + protected function getOpenSound() : Sound{ return new ShulkerBoxOpenSound(); } - protected function getContainerCloseSound() : Sound{ + protected function getCloseSound() : Sound{ return new ShulkerBoxCloseSound(); } - protected function doContainerAnimation(Position $position, bool $isOpen) : void{ + protected function playAnimationVisual(Position $position, bool $isOpen) : void{ //event ID is always 1 for a chest //TODO: we probably shouldn't be sending a packet directly here, but it doesn't fit anywhere into existing systems $position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0)); diff --git a/src/block/SmithingTable.php b/src/block/SmithingTable.php index d6c6ddd5d..19cec3d41 100644 --- a/src/block/SmithingTable.php +++ b/src/block/SmithingTable.php @@ -24,15 +24,16 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\inventory\window\SmithingTableInventoryWindow; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessor; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\player\InventoryWindow; use pocketmine\player\Player; use pocketmine\world\Position; -final class SmithingTable extends Opaque{ - use InventoryMenuTrait; +final class SmithingTable extends Opaque implements MenuAccessor{ + use MenuAccessorTrait; - protected function newWindow(Player $player, Position $position) : InventoryWindow{ + protected function newMenu(Player $player, Position $position) : InventoryWindow{ return new SmithingTableInventoryWindow($player, $position); } diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php index 31dc10220..7d0e80958 100644 --- a/src/block/Stonecutter.php +++ b/src/block/Stonecutter.php @@ -26,18 +26,19 @@ namespace pocketmine\block; use pocketmine\block\inventory\window\StonecutterInventoryWindow; use pocketmine\block\utils\FacesOppositePlacingPlayerTrait; use pocketmine\block\utils\HorizontalFacing; -use pocketmine\block\utils\InventoryMenuTrait; +use pocketmine\block\utils\MenuAccessor; +use pocketmine\block\utils\MenuAccessorTrait; use pocketmine\block\utils\SupportType; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\player\Player; use pocketmine\world\Position; -class Stonecutter extends Transparent implements HorizontalFacing{ +class Stonecutter extends Transparent implements HorizontalFacing, MenuAccessor{ use FacesOppositePlacingPlayerTrait; - use InventoryMenuTrait; + use MenuAccessorTrait; - protected function newWindow(Player $player, Position $position) : StonecutterInventoryWindow{ + protected function newMenu(Player $player, Position $position) : StonecutterInventoryWindow{ return new StonecutterInventoryWindow($player, $position); } diff --git a/src/block/inventory/window/BlockInventoryWindow.php b/src/block/inventory/window/BlockInventoryWindow.php index 149c2e814..1364dbf10 100644 --- a/src/block/inventory/window/BlockInventoryWindow.php +++ b/src/block/inventory/window/BlockInventoryWindow.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\block\inventory\window; -use pocketmine\block\utils\AnimatedContainer; +use pocketmine\block\utils\AnimatedContainerLike; use pocketmine\inventory\Inventory; use pocketmine\player\InventoryWindow; use pocketmine\player\Player; @@ -44,15 +44,15 @@ class BlockInventoryWindow extends InventoryWindow{ public function onOpen() : void{ parent::onOpen(); $block = $this->holder->getWorld()->getBlock($this->holder); - if($block instanceof AnimatedContainer){ - $block->onContainerOpen(); + if($block instanceof AnimatedContainerLike){ + $block->onViewerAdded(); } } public function onClose() : void{ $block = $this->holder->getWorld()->getBlock($this->holder); - if($block instanceof AnimatedContainer){ - $block->onContainerClose(); + if($block instanceof AnimatedContainerLike){ + $block->onViewerRemoved(); } parent::onClose(); } diff --git a/src/block/utils/AnimatedContainer.php b/src/block/utils/AnimatedContainerLike.php similarity index 77% rename from src/block/utils/AnimatedContainer.php rename to src/block/utils/AnimatedContainerLike.php index 9af901f18..8529e68a7 100644 --- a/src/block/utils/AnimatedContainer.php +++ b/src/block/utils/AnimatedContainerLike.php @@ -23,16 +23,20 @@ declare(strict_types=1); namespace pocketmine\block\utils; -interface AnimatedContainer{ +/** + * Blocks which have audiovisual behaviour (like chests) and remain in their "open" state for as long as at least 1 + * viewer is viewing the menu they provide access to + */ +interface AnimatedContainerLike extends MenuAccessor{ /** * Do actions when the container block is opened by a player. * If you have a custom viewer counter (like ender chests), you should increment it here. */ - public function onContainerOpen() : void; + public function onViewerAdded() : void; /** * Do actions when the container block is closed by a player. * As above, you should decrement your custom viewer counter here, if you have one. */ - public function onContainerClose() : void; + public function onViewerRemoved() : void; } diff --git a/src/block/utils/AnimatedContainerTrait.php b/src/block/utils/AnimatedContainerLikeTrait.php similarity index 60% rename from src/block/utils/AnimatedContainerTrait.php rename to src/block/utils/AnimatedContainerLikeTrait.php index ab1337b1b..96aaad7ec 100644 --- a/src/block/utils/AnimatedContainerTrait.php +++ b/src/block/utils/AnimatedContainerLikeTrait.php @@ -28,9 +28,9 @@ use pocketmine\world\Position; use pocketmine\world\sound\Sound; use function count; -trait AnimatedContainerTrait{ +trait AnimatedContainerLikeTrait{ - protected function getContainerViewerCount() : int{ + protected function getViewerCount() : int{ $position = $this->getPosition(); $tile = $position->getWorld()->getTile($position); if($tile instanceof InventoryHolder){ @@ -39,33 +39,33 @@ trait AnimatedContainerTrait{ return 0; } - abstract protected function getContainerOpenSound() : Sound; + abstract protected function getOpenSound() : Sound; - abstract protected function getContainerCloseSound() : Sound; + abstract protected function getCloseSound() : Sound; - abstract protected function doContainerAnimation(Position $position, bool $isOpen) : void; + abstract protected function playAnimationVisual(Position $position, bool $isOpen) : void; - protected function playContainerSound(Position $position, bool $isOpen) : void{ - $position->getWorld()->addSound($position->add(0.5, 0.5, 0.5), $isOpen ? $this->getContainerOpenSound() : $this->getContainerCloseSound()); + protected function playAnimationSound(Position $position, bool $isOpen) : void{ + $position->getWorld()->addSound($position->add(0.5, 0.5, 0.5), $isOpen ? $this->getOpenSound() : $this->getCloseSound()); } abstract protected function getPosition() : Position; - protected function doContainerEffects(bool $isOpen) : void{ + protected function doAnimationEffects(bool $isOpen) : void{ $position = $this->getPosition(); - $this->doContainerAnimation($position, $isOpen); - $this->playContainerSound($position, $isOpen); + $this->playAnimationVisual($position, $isOpen); + $this->playAnimationSound($position, $isOpen); } - public function onContainerOpen() : void{ - if($this->getContainerViewerCount() === 1){ - $this->doContainerEffects(true); + public function onViewerAdded() : void{ + if($this->getViewerCount() === 1){ + $this->doAnimationEffects(true); } } - public function onContainerClose() : void{ - if($this->getContainerViewerCount() === 1){ - $this->doContainerEffects(false); + public function onViewerRemoved() : void{ + if($this->getViewerCount() === 1){ + $this->doAnimationEffects(false); } } } diff --git a/src/block/utils/Container.php b/src/block/utils/Container.php new file mode 100644 index 000000000..7d68728dc --- /dev/null +++ b/src/block/utils/Container.php @@ -0,0 +1,44 @@ +openTo($player, ignoreObstruction: false, ignoreLock: false); + if($player instanceof Player && !$this->isOpeningObstructed() && $this->canOpenWith($item->getCustomName())){ + $this->openToUnchecked($player); } return true; } - protected function isOpeningObstructed() : bool{ - return false; - } - - protected function newWindow(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ + protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{ return new BlockInventoryWindow($player, $inventory, $position); } - public function openTo(Player $player, bool $ignoreObstruction, bool $ignoreLock) : ContainerOpenResult{ + public function isOpeningObstructed() : bool{ + return false; + } + + public function canOpenWith(string $key) : bool{ + //TODO: maybe we can bring the key to the block in readStateFromWorld()? $tile = $this->position->getWorld()->getTile($this->position); - if(!$tile instanceof ContainerTile){ - return ContainerOpenResult::CONTAINER_NOT_FOUND; - } - if(!$ignoreLock && !$tile->canOpenWith($player->getHotbar()->getHeldItem()->getCustomName())){ - return ContainerOpenResult::INCORRECT_KEY; - } - if(!$ignoreObstruction && $this->isOpeningObstructed()){ - return ContainerOpenResult::OBSTRUCTED; - } - $window = $this->newWindow($player, $tile->getInventory(), $this->position); - $player->setCurrentWindow($window); - return ContainerOpenResult::SUCCESS; + return $tile instanceof ContainerTile && $tile->canOpenWith($key); + } + + public function openToUnchecked(Player $player) : bool{ + $tile = $this->position->getWorld()->getTile($this->position); + return $tile instanceof ContainerTile && $player->setCurrentWindow($this->newMenu($player, $tile->getInventory(), $this->position)); + } + + public function getInventory() : ?Inventory{ + $tile = $this->position->getWorld()->getTile($this->position); + return $tile instanceof ContainerTile ? $tile->getInventory() : null; } } diff --git a/src/block/utils/ContainerOpenResult.php b/src/block/utils/MenuAccessor.php similarity index 55% rename from src/block/utils/ContainerOpenResult.php rename to src/block/utils/MenuAccessor.php index b06c0d875..a1e59da11 100644 --- a/src/block/utils/ContainerOpenResult.php +++ b/src/block/utils/MenuAccessor.php @@ -23,21 +23,23 @@ declare(strict_types=1); namespace pocketmine\block\utils; -enum ContainerOpenResult{ +use pocketmine\player\Player; + +/** + * Blocks which open a menu when interacted with + * This could be a container menu, or a menu that otherwise deals with items, such as a crafting menu + */ +interface MenuAccessor{ /** - * Opening the container succeeded and the player is now viewing the contents + * Returns whether the block's ability to open the menu is currently obstructed (e.g. by nearby blocks). */ - case SUCCESS; + public function isOpeningObstructed() : bool; + /** - * No container tile (and therefore no content) was found on the block's position + * Opens the menu to the player. + * Note: No preconditions are checked. Do not check for obstruction or locks here. + * + * Returns true if successful, false otherwise (e.g. event cancelled, container missing) */ - case CONTAINER_NOT_FOUND; - /** - * The container's opening is obstructed (e.g a block on top of a chest's lid) - */ - case OBSTRUCTED; - /** - * The container is locked and the used item doesn't have the correct custom name to unlock it - */ - case INCORRECT_KEY; + public function openToUnchecked(Player $player) : bool; } diff --git a/src/block/utils/InventoryMenuTrait.php b/src/block/utils/MenuAccessorTrait.php similarity index 70% rename from src/block/utils/InventoryMenuTrait.php rename to src/block/utils/MenuAccessorTrait.php index 3ba02e51a..11eb8414b 100644 --- a/src/block/utils/InventoryMenuTrait.php +++ b/src/block/utils/MenuAccessorTrait.php @@ -31,30 +31,26 @@ use pocketmine\player\InventoryWindow; use pocketmine\player\Player; use pocketmine\world\Position; -trait InventoryMenuTrait{ +trait MenuAccessorTrait{ /** * @see Block::onInteract() */ public function onInteract(Item $item, Facing $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ - if($player instanceof Player){ - $this->openTo($player, ignoreObstruction: false); + if($player instanceof Player && !$this->isOpeningObstructed()){ + $this->openToUnchecked($player); } return true; } - abstract protected function newWindow(Player $player, Position $position) : InventoryWindow; + abstract protected function newMenu(Player $player, Position $position) : InventoryWindow; - protected function isOpeningObstructed() : bool{ + public function isOpeningObstructed() : bool{ return false; } - public function openTo(Player $player, bool $ignoreObstruction) : bool{ - if(!$ignoreObstruction && $this->isOpeningObstructed()){ - return false; - } - $player->setCurrentWindow($this->newWindow($player, $this->position)); - return true; + public function openToUnchecked(Player $player) : bool{ + return $player->setCurrentWindow($this->newMenu($player, $this->position)); } }