diff --git a/src/entity/Human.php b/src/entity/Human.php index 7ba3e8e05..73db86ac3 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -264,7 +264,12 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } } - $this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0), false); + $this->inventory->setHeldItemIndex($nbt->getInt("SelectedInventorySlot", 0)); + $this->inventory->getHeldItemIndexChangeListeners()->add(function(int $oldIndex) : void{ + foreach($this->getViewers() as $viewer){ + $viewer->getNetworkSession()->onMobEquipmentChange($this); + } + }); $this->hungerManager->setFood((float) $nbt->getInt("foodLevel", (int) $this->hungerManager->getFood())); $this->hungerManager->setExhaustion($nbt->getFloat("foodExhaustionLevel", $this->hungerManager->getExhaustion())); @@ -457,6 +462,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ protected function onDispose() : void{ $this->inventory->removeAllViewers(); + $this->inventory->getHeldItemIndexChangeListeners()->clear(); $this->enderChestInventory->removeAllViewers(); parent::onDispose(); } diff --git a/src/inventory/PlayerInventory.php b/src/inventory/PlayerInventory.php index d3de07a62..07befe054 100644 --- a/src/inventory/PlayerInventory.php +++ b/src/inventory/PlayerInventory.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\inventory; +use Ds\Set; use pocketmine\entity\Human; use pocketmine\item\Item; use pocketmine\player\Player; @@ -35,8 +36,15 @@ class PlayerInventory extends BaseInventory{ /** @var int */ protected $itemInHandIndex = 0; + /** + * @var \Closure[]|Set + * @phpstan-var Set<\Closure(int $oldIndex) : void> + */ + protected $heldItemIndexChangeListeners; + public function __construct(Human $player){ $this->holder = $player; + $this->heldItemIndexChangeListeners = new Set(); parent::__construct(36); } @@ -73,25 +81,27 @@ class PlayerInventory extends BaseInventory{ /** * Sets which hotbar slot the player is currently loading. * - * @param int $hotbarSlot 0-8 index of the hotbar slot to hold - * @param bool $send Whether to send updates back to the inventory holder. This should usually be true for plugin calls. - * It should only be false to prevent feedback loops of equipment packets between client and server. + * @param int $hotbarSlot 0-8 index of the hotbar slot to hold * * @throws \InvalidArgumentException if the hotbar slot is out of range */ - public function setHeldItemIndex(int $hotbarSlot, bool $send = true) : void{ + public function setHeldItemIndex(int $hotbarSlot) : void{ $this->throwIfNotHotbarSlot($hotbarSlot); + $oldIndex = $this->itemInHandIndex; $this->itemInHandIndex = $hotbarSlot; - if($this->holder instanceof Player and $send){ - $this->holder->getNetworkSession()->getInvManager()->syncSelectedHotbarSlot(); - } - foreach($this->holder->getViewers() as $viewer){ - $viewer->getNetworkSession()->onMobEquipmentChange($this->holder); + foreach($this->heldItemIndexChangeListeners as $callback){ + $callback($oldIndex); } } + /** + * @return \Closure[]|Set + * @phpstan-return Set<\Closure(int $oldIndex) : void> + */ + public function getHeldItemIndexChangeListeners() : Set{ return $this->heldItemIndexChangeListeners; } + /** * Returns the currently-held item. */ diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index 76f6dca2c..33570e366 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -76,6 +76,8 @@ class InventoryManager{ * @phpstan-var array> */ private $initiatedSlotChanges = []; + /** @var int */ + private $clientSelectedHotbarSlot = -1; public function __construct(Player $player, NetworkSession $session){ $this->player = $player; @@ -84,6 +86,10 @@ class InventoryManager{ $this->add(ContainerIds::INVENTORY, $this->player->getInventory()); $this->add(ContainerIds::ARMOR, $this->player->getArmorInventory()); $this->add(ContainerIds::UI, $this->player->getCursorInventory()); + + $this->player->getInventory()->getHeldItemIndexChangeListeners()->add(function() : void{ + $this->syncSelectedHotbarSlot(); + }); } private function add(int $id, Inventory $inventory) : void{ @@ -206,13 +212,21 @@ class InventoryManager{ } } + public function onClientSelectHotbarSlot(int $slot) : void{ + $this->clientSelectedHotbarSlot = $slot; + } + public function syncSelectedHotbarSlot() : void{ - $this->session->sendDataPacket(MobEquipmentPacket::create( - $this->player->getId(), - TypeConverter::getInstance()->coreItemStackToNet($this->player->getInventory()->getItemInHand()), - $this->player->getInventory()->getHeldItemIndex(), - ContainerIds::INVENTORY - )); + $selected = $this->player->getInventory()->getHeldItemIndex(); + if($selected !== $this->clientSelectedHotbarSlot){ + $this->session->sendDataPacket(MobEquipmentPacket::create( + $this->player->getId(), + TypeConverter::getInstance()->coreItemStackToNet($this->player->getInventory()->getItemInHand()), + $selected, + ContainerIds::INVENTORY + )); + $this->clientSelectedHotbarSlot = $selected; + } } public function syncCreative() : void{ diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 2699bf310..4e0c2e6e9 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -448,6 +448,7 @@ class InGamePacketHandler extends PacketHandler{ } public function handleMobEquipment(MobEquipmentPacket $packet) : bool{ + $this->session->getInvManager()->onClientSelectHotbarSlot($packet->hotbarSlot); if(!$this->player->selectHotbarSlot($packet->hotbarSlot)){ $this->session->getInvManager()->syncSelectedHotbarSlot(); } diff --git a/src/player/Player.php b/src/player/Player.php index 7ce797a36..6749b8252 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -1373,7 +1373,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return false; } - $this->inventory->setHeldItemIndex($hotbarSlot, false); + $this->inventory->setHeldItemIndex($hotbarSlot); $this->setUsingItem(false); return true;