From cd103cefcc3aa94c41f4633a1a6daf68b8d1596b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 19 May 2019 16:36:38 +0100 Subject: [PATCH] Redesign inventory listening API given that inventory listeners are no longer allowed to fiddle with the outcome of changing contents, it's now possible to allow having multiple listeners receiving events. It's likely that this will be used for network inventory sync in the near future. --- src/pocketmine/entity/Human.php | 6 +- src/pocketmine/inventory/BaseInventory.php | 41 ++++++------ .../CallbackInventoryChangeListener.php | 65 +++++++++++++++++++ src/pocketmine/inventory/Inventory.php | 13 ++-- .../inventory/InventoryChangeListener.php | 38 +++++++++++ src/pocketmine/tile/ContainerTrait.php | 6 +- src/pocketmine/tile/Furnace.php | 9 ++- 7 files changed, 145 insertions(+), 33 deletions(-) create mode 100644 src/pocketmine/inventory/CallbackInventoryChangeListener.php create mode 100644 src/pocketmine/inventory/InventoryChangeListener.php diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 240d51b04..74d099df0 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -618,8 +618,8 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ $inventoryTag = $nbt->getListTag("Inventory"); if($inventoryTag !== null){ - $armorListener = $this->armorInventory->getSlotChangeListener(); - $this->armorInventory->setSlotChangeListener(null); + $armorListeners = $this->armorInventory->getChangeListeners(); + $this->armorInventory->removeChangeListeners(...$armorListeners); /** @var CompoundTag $item */ foreach($inventoryTag as $i => $item){ @@ -633,7 +633,7 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } } - $this->armorInventory->setSlotChangeListener($armorListener); + $this->armorInventory->addChangeListeners(...$armorListeners); } $enderChestInventoryTag = $nbt->getListTag("EnderChestInventory"); diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index bb1b52ad0..9be938cbf 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -27,7 +27,6 @@ use pocketmine\event\inventory\InventoryOpenEvent; use pocketmine\item\Item; use pocketmine\item\ItemFactory; use pocketmine\Player; -use pocketmine\utils\Utils; use function array_slice; use function count; use function max; @@ -42,8 +41,8 @@ abstract class BaseInventory implements Inventory{ protected $slots = []; /** @var Player[] */ protected $viewers = []; - /** @var \Closure */ - protected $slotChangeListener; + /** @var InventoryChangeListener[] */ + protected $listeners = []; /** * @param int $size @@ -121,6 +120,10 @@ abstract class BaseInventory implements Inventory{ } } + foreach($this->listeners as $listener){ + $listener->onContentChange($this); + } + if($send){ $this->sendContents($this->getViewers()); } @@ -138,10 +141,6 @@ abstract class BaseInventory implements Inventory{ $this->slots[$index] = $item->isNull() ? null : $item; $this->onSlotChange($index, $oldItem, $send); - if($this->slotChangeListener !== null){ - ($this->slotChangeListener)($this, $index); - } - return true; } @@ -331,13 +330,7 @@ abstract class BaseInventory implements Inventory{ } public function clearAll(bool $send = true) : void{ - for($i = 0, $size = $this->getSize(); $i < $size; ++$i){ - $this->clear($i, false); - } - - if($send){ - $this->sendContents($this->getViewers()); - } + $this->setContents([], $send); } public function swap(int $slot1, int $slot2) : void{ @@ -394,6 +387,9 @@ abstract class BaseInventory implements Inventory{ } protected function onSlotChange(int $index, Item $before, bool $send) : void{ + foreach($this->listeners as $listener){ + $listener->onSlotChange($this, $index); + } if($send){ $this->sendSlot($index, $this->getViewers()); } @@ -430,14 +426,19 @@ abstract class BaseInventory implements Inventory{ return $slot >= 0 and $slot < $this->slots->getSize(); } - public function getSlotChangeListener() : ?\Closure{ - return $this->slotChangeListener; + public function addChangeListeners(InventoryChangeListener ...$listeners) : void{ + foreach($listeners as $listener){ + $this->listeners[spl_object_id($listener)] = $listener; + } } - public function setSlotChangeListener(?\Closure $eventProcessor) : void{ - if($eventProcessor !== null){ - Utils::validateCallableSignature(function(Inventory $inventory, int $slot) : void{}, $eventProcessor); + public function removeChangeListeners(InventoryChangeListener ...$listeners) : void{ + foreach($listeners as $listener){ + unset($this->listeners[spl_object_id($listener)]); } - $this->slotChangeListener = $eventProcessor; + } + + public function getChangeListeners() : array{ + return $this->listeners; } } diff --git a/src/pocketmine/inventory/CallbackInventoryChangeListener.php b/src/pocketmine/inventory/CallbackInventoryChangeListener.php new file mode 100644 index 000000000..b27499740 --- /dev/null +++ b/src/pocketmine/inventory/CallbackInventoryChangeListener.php @@ -0,0 +1,65 @@ +onSlotChangeCallback = $onSlotChange; + $this->onContentChangeCallback = $onContentChange; + } + + public static function onAnyChange(\Closure $onChange) : self{ + return new self( + static function(Inventory $inventory, int $unused) use($onChange) : void{ + $onChange($inventory); + }, + static function(Inventory $inventory) use($onChange) : void{ + $onChange($inventory); + } + ); + } + + public function onSlotChange(Inventory $inventory, int $slot) : void{ + ($this->onSlotChangeCallback)($inventory, $slot); + } + + public function onContentChange(Inventory $inventory) : void{ + ($this->onContentChangeCallback)($inventory); + } +} diff --git a/src/pocketmine/inventory/Inventory.php b/src/pocketmine/inventory/Inventory.php index c0fba1d2b..a27a8525c 100644 --- a/src/pocketmine/inventory/Inventory.php +++ b/src/pocketmine/inventory/Inventory.php @@ -231,12 +231,17 @@ interface Inventory{ public function slotExists(int $slot) : bool; /** - * @return null|\Closure + * @param InventoryChangeListener ...$listeners */ - public function getSlotChangeListener() : ?\Closure; + public function addChangeListeners(InventoryChangeListener ...$listeners) : void; /** - * @param \Closure|null $eventProcessor + * @param InventoryChangeListener ...$listeners */ - public function setSlotChangeListener(?\Closure $eventProcessor) : void; + public function removeChangeListeners(InventoryChangeListener ...$listeners) : void; + + /** + * @return InventoryChangeListener[] + */ + public function getChangeListeners() : array; } diff --git a/src/pocketmine/inventory/InventoryChangeListener.php b/src/pocketmine/inventory/InventoryChangeListener.php new file mode 100644 index 000000000..d13edf7f0 --- /dev/null +++ b/src/pocketmine/inventory/InventoryChangeListener.php @@ -0,0 +1,38 @@ +getListTag(Container::TAG_ITEMS); $inventory = $this->getRealInventory(); - $slotChangeListener = $inventory->getSlotChangeListener(); - $inventory->setSlotChangeListener(null); //prevent any events being fired by initialization + $listeners = $inventory->getChangeListeners(); + $inventory->removeChangeListeners(...$listeners); //prevent any events being fired by initialization $inventory->clearAll(); /** @var CompoundTag $itemNBT */ foreach($inventoryTag as $itemNBT){ $inventory->setItem($itemNBT->getByte("Slot"), Item::nbtDeserialize($itemNBT)); } - $inventory->setSlotChangeListener($slotChangeListener); + $inventory->addChangeListeners(...$listeners); } if($tag->hasTag(Container::TAG_LOCK, StringTag::class)){ diff --git a/src/pocketmine/tile/Furnace.php b/src/pocketmine/tile/Furnace.php index 26ce225ab..ad9337139 100644 --- a/src/pocketmine/tile/Furnace.php +++ b/src/pocketmine/tile/Furnace.php @@ -26,6 +26,7 @@ namespace pocketmine\tile; use pocketmine\block\Furnace as BlockFurnace; use pocketmine\event\inventory\FurnaceBurnEvent; use pocketmine\event\inventory\FurnaceSmeltEvent; +use pocketmine\inventory\CallbackInventoryChangeListener; use pocketmine\inventory\FurnaceInventory; use pocketmine\inventory\FurnaceRecipe; use pocketmine\inventory\Inventory; @@ -60,9 +61,11 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{ public function __construct(World $world, Vector3 $pos){ $this->inventory = new FurnaceInventory($this); - $this->inventory->setSlotChangeListener(function(Inventory $inventory, int $slot) : void{ - $this->scheduleUpdate(); - }); + $this->inventory->addChangeListeners(CallbackInventoryChangeListener::onAnyChange( + function(Inventory $unused) : void{ + $this->scheduleUpdate(); + }) + ); parent::__construct($world, $pos); }