diff --git a/src/pocketmine/block/tile/Furnace.php b/src/pocketmine/block/tile/Furnace.php index dcc21281e..a5c64d4e1 100644 --- a/src/pocketmine/block/tile/Furnace.php +++ b/src/pocketmine/block/tile/Furnace.php @@ -203,17 +203,17 @@ class Furnace extends Spawnable implements Container, Nameable{ if($prevCookTime !== $this->cookTime){ foreach($this->inventory->getViewers() as $v){ - $v->getNetworkSession()->syncInventoryData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_SMELT_PROGRESS, $this->cookTime); + $v->getNetworkSession()->getInvManager()->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_SMELT_PROGRESS, $this->cookTime); } } if($prevRemainingFuelTime !== $this->remainingFuelTime){ foreach($this->inventory->getViewers() as $v){ - $v->getNetworkSession()->syncInventoryData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_REMAINING_FUEL_TIME, $this->remainingFuelTime); + $v->getNetworkSession()->getInvManager()->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_REMAINING_FUEL_TIME, $this->remainingFuelTime); } } if($prevMaxFuelTime !== $this->maxFuelTime){ foreach($this->inventory->getViewers() as $v){ - $v->getNetworkSession()->syncInventoryData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_MAX_FUEL_TIME, $this->maxFuelTime); + $v->getNetworkSession()->getInvManager()->syncData($this->inventory, ContainerSetDataPacket::PROPERTY_FURNACE_MAX_FUEL_TIME, $this->maxFuelTime); } } diff --git a/src/pocketmine/inventory/AnvilInventory.php b/src/pocketmine/inventory/AnvilInventory.php index 95b932d9c..c069816d0 100644 --- a/src/pocketmine/inventory/AnvilInventory.php +++ b/src/pocketmine/inventory/AnvilInventory.php @@ -23,11 +23,10 @@ declare(strict_types=1); namespace pocketmine\inventory; -use pocketmine\network\mcpe\protocol\types\WindowTypes; use pocketmine\player\Player; use pocketmine\world\Position; -class AnvilInventory extends ContainerInventory{ +class AnvilInventory extends BlockInventory{ /** @var Position */ protected $holder; @@ -36,10 +35,6 @@ class AnvilInventory extends ContainerInventory{ parent::__construct($pos->asPosition(), 2); } - public function getNetworkType() : int{ - return WindowTypes::ANVIL; - } - /** * This override is here for documentation and code completion purposes only. * @return Position diff --git a/src/pocketmine/inventory/BaseInventory.php b/src/pocketmine/inventory/BaseInventory.php index dc2948662..d8d918ae4 100644 --- a/src/pocketmine/inventory/BaseInventory.php +++ b/src/pocketmine/inventory/BaseInventory.php @@ -116,7 +116,7 @@ abstract class BaseInventory implements Inventory{ if($send){ foreach($this->getViewers() as $viewer){ - $viewer->getNetworkSession()->syncInventoryContents($this); + $viewer->getNetworkSession()->getInvManager()->syncContents($this); } } } @@ -367,7 +367,7 @@ abstract class BaseInventory implements Inventory{ } if($send){ foreach($this->viewers as $viewer){ - $viewer->getNetworkSession()->syncInventorySlot($this, $index); + $viewer->getNetworkSession()->getInvManager()->syncSlot($this, $index); } } } diff --git a/src/pocketmine/inventory/BlockInventory.php b/src/pocketmine/inventory/BlockInventory.php new file mode 100644 index 000000000..7c464ecda --- /dev/null +++ b/src/pocketmine/inventory/BlockInventory.php @@ -0,0 +1,43 @@ +holder = $holder; + parent::__construct($size, $items); + } + + /** + * @return Vector3 + */ + public function getHolder(){ + return $this->holder; + } +} diff --git a/src/pocketmine/inventory/BrewingStandInventory.php b/src/pocketmine/inventory/BrewingStandInventory.php index a699ed184..e6a3e6b5b 100644 --- a/src/pocketmine/inventory/BrewingStandInventory.php +++ b/src/pocketmine/inventory/BrewingStandInventory.php @@ -24,15 +24,10 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\math\Vector3; -use pocketmine\network\mcpe\protocol\types\WindowTypes; -class BrewingStandInventory extends ContainerInventory{ +class BrewingStandInventory extends BlockInventory{ public function __construct(Vector3 $holder, int $size = 5, array $items = []){ parent::__construct($holder, $size, $items); } - - public function getNetworkType() : int{ - return WindowTypes::BREWING_STAND; - } } diff --git a/src/pocketmine/inventory/ChestInventory.php b/src/pocketmine/inventory/ChestInventory.php index f19ec2517..db7a788cf 100644 --- a/src/pocketmine/inventory/ChestInventory.php +++ b/src/pocketmine/inventory/ChestInventory.php @@ -25,14 +25,13 @@ namespace pocketmine\inventory; use pocketmine\block\tile\Chest; use pocketmine\network\mcpe\protocol\BlockEventPacket; -use pocketmine\network\mcpe\protocol\types\WindowTypes; use pocketmine\player\Player; use pocketmine\world\sound\ChestCloseSound; use pocketmine\world\sound\ChestOpenSound; use pocketmine\world\sound\Sound; use function count; -class ChestInventory extends ContainerInventory{ +class ChestInventory extends BlockInventory{ /** @var Chest */ protected $holder; @@ -44,10 +43,6 @@ class ChestInventory extends ContainerInventory{ parent::__construct($tile, 27); } - public function getNetworkType() : int{ - return WindowTypes::CONTAINER; - } - /** * This override is here for documentation and code completion purposes only. * @return Chest diff --git a/src/pocketmine/inventory/ContainerInventory.php b/src/pocketmine/inventory/ContainerInventory.php deleted file mode 100644 index 48a4a2be5..000000000 --- a/src/pocketmine/inventory/ContainerInventory.php +++ /dev/null @@ -1,72 +0,0 @@ -holder = $holder; - parent::__construct($size, $items); - } - - public function onOpen(Player $who) : void{ - parent::onOpen($who); - - $windowId = $who->getWindowId($this); - $holder = $this->getHolder(); - if($holder instanceof Entity){ - $who->sendDataPacket(ContainerOpenPacket::entityInv($windowId, $this->getNetworkType(), $holder->getId())); - }elseif($holder instanceof Vector3){ - $who->sendDataPacket(ContainerOpenPacket::blockInv($windowId, $this->getNetworkType(), $holder->getFloorX(), $holder->getFloorY(), $holder->getFloorZ())); - } - - $who->getNetworkSession()->syncInventoryContents($this); - } - - public function onClose(Player $who) : void{ - $who->sendDataPacket(ContainerClosePacket::create($who->getWindowId($this))); - parent::onClose($who); - } - - /** - * Returns the Minecraft PE inventory type used to show the inventory window to clients. - * @return int - */ - abstract public function getNetworkType() : int; - - /** - * @return Vector3 - */ - public function getHolder(){ - return $this->holder; - } -} diff --git a/src/pocketmine/inventory/EnchantInventory.php b/src/pocketmine/inventory/EnchantInventory.php index d4510767b..74d40d687 100644 --- a/src/pocketmine/inventory/EnchantInventory.php +++ b/src/pocketmine/inventory/EnchantInventory.php @@ -23,11 +23,10 @@ declare(strict_types=1); namespace pocketmine\inventory; -use pocketmine\network\mcpe\protocol\types\WindowTypes; use pocketmine\player\Player; use pocketmine\world\Position; -class EnchantInventory extends ContainerInventory{ +class EnchantInventory extends BlockInventory{ /** @var Position */ protected $holder; @@ -36,10 +35,6 @@ class EnchantInventory extends ContainerInventory{ parent::__construct($pos->asPosition(), 2); } - public function getNetworkType() : int{ - return WindowTypes::ENCHANTMENT; - } - /** * This override is here for documentation and code completion purposes only. * @return Position diff --git a/src/pocketmine/inventory/EnderChestInventory.php b/src/pocketmine/inventory/EnderChestInventory.php index 0dcd970c0..1986ac7d0 100644 --- a/src/pocketmine/inventory/EnderChestInventory.php +++ b/src/pocketmine/inventory/EnderChestInventory.php @@ -24,7 +24,6 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\block\tile\EnderChest; -use pocketmine\network\mcpe\protocol\types\WindowTypes; use pocketmine\world\Position; use pocketmine\world\sound\EnderChestCloseSound; use pocketmine\world\sound\EnderChestOpenSound; @@ -36,11 +35,7 @@ class EnderChestInventory extends ChestInventory{ protected $holder; public function __construct(){ - ContainerInventory::__construct(new Position(), 27); - } - - public function getNetworkType() : int{ - return WindowTypes::CONTAINER; + BlockInventory::__construct(new Position(), 27); } /** diff --git a/src/pocketmine/inventory/FurnaceInventory.php b/src/pocketmine/inventory/FurnaceInventory.php index 81820747b..6a95c49aa 100644 --- a/src/pocketmine/inventory/FurnaceInventory.php +++ b/src/pocketmine/inventory/FurnaceInventory.php @@ -25,9 +25,8 @@ namespace pocketmine\inventory; use pocketmine\block\tile\Furnace; use pocketmine\item\Item; -use pocketmine\network\mcpe\protocol\types\WindowTypes; -class FurnaceInventory extends ContainerInventory{ +class FurnaceInventory extends BlockInventory{ /** @var Furnace */ protected $holder; @@ -35,10 +34,6 @@ class FurnaceInventory extends ContainerInventory{ parent::__construct($tile, 3); } - public function getNetworkType() : int{ - return WindowTypes::FURNACE; - } - /** * This override is here for documentation and code completion purposes only. * @return Furnace diff --git a/src/pocketmine/inventory/HopperInventory.php b/src/pocketmine/inventory/HopperInventory.php index 728453c9c..de9337645 100644 --- a/src/pocketmine/inventory/HopperInventory.php +++ b/src/pocketmine/inventory/HopperInventory.php @@ -24,15 +24,10 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\math\Vector3; -use pocketmine\network\mcpe\protocol\types\WindowTypes; -class HopperInventory extends ContainerInventory{ +class HopperInventory extends BlockInventory{ public function __construct(Vector3 $holder, int $size = 5, array $items = []){ parent::__construct($holder, $size, $items); } - - public function getNetworkType() : int{ - return WindowTypes::HOPPER; - } } diff --git a/src/pocketmine/inventory/PlayerInventory.php b/src/pocketmine/inventory/PlayerInventory.php index 9c3f1f2c5..fbc2748d1 100644 --- a/src/pocketmine/inventory/PlayerInventory.php +++ b/src/pocketmine/inventory/PlayerInventory.php @@ -137,12 +137,12 @@ class PlayerInventory extends BaseInventory{ if(!is_array($target)){ $target->sendDataPacket($pk); if($target === $this->getHolder()){ - $target->getNetworkSession()->syncInventorySlot($this, $this->getHeldItemIndex()); + $target->getNetworkSession()->getInvManager()->syncSlot($this, $this->getHeldItemIndex()); } }else{ $this->getHolder()->getWorld()->getServer()->broadcastPacket($target, $pk); if(in_array($this->getHolder(), $target, true)){ - $target->getNetworkSession()->syncInventorySlot($this, $this->getHeldItemIndex()); + $target->getNetworkSession()->getInvManager()->syncSlot($this, $this->getHeldItemIndex()); } } } diff --git a/src/pocketmine/inventory/transaction/InventoryTransaction.php b/src/pocketmine/inventory/transaction/InventoryTransaction.php index ea73c22fc..9333ce4b9 100644 --- a/src/pocketmine/inventory/transaction/InventoryTransaction.php +++ b/src/pocketmine/inventory/transaction/InventoryTransaction.php @@ -282,7 +282,7 @@ class InventoryTransaction{ protected function sendInventories() : void{ foreach($this->inventories as $inventory){ - $this->source->getNetworkSession()->syncInventoryContents($inventory); + $this->source->getNetworkSession()->getInvManager()->syncContents($inventory); } } diff --git a/src/pocketmine/inventory/transaction/action/SlotChangeAction.php b/src/pocketmine/inventory/transaction/action/SlotChangeAction.php index 1ec24af63..e1145022f 100644 --- a/src/pocketmine/inventory/transaction/action/SlotChangeAction.php +++ b/src/pocketmine/inventory/transaction/action/SlotChangeAction.php @@ -100,7 +100,7 @@ class SlotChangeAction extends InventoryAction{ $this->inventory->setItem($this->inventorySlot, $this->targetItem, false); foreach($this->inventory->getViewers() as $viewer){ if($viewer !== $source){ - $viewer->getNetworkSession()->syncInventorySlot($this->inventory, $this->inventorySlot); + $viewer->getNetworkSession()->getInvManager()->syncSlot($this->inventory, $this->inventorySlot); } } } diff --git a/src/pocketmine/network/mcpe/InventoryManager.php b/src/pocketmine/network/mcpe/InventoryManager.php new file mode 100644 index 000000000..097e55ee6 --- /dev/null +++ b/src/pocketmine/network/mcpe/InventoryManager.php @@ -0,0 +1,157 @@ +player = $player; + $this->session = $session; + + $this->windowMap[ContainerIds::INVENTORY] = $this->player->getInventory(); + $this->windowMap[ContainerIds::ARMOR] = $this->player->getArmorInventory(); + $this->windowMap[ContainerIds::CURSOR] = $this->player->getCursorInventory(); + } + + public function getWindowId(Inventory $inventory) : ?int{ + return ($id = array_search($inventory, $this->windowMap, true)) !== false ? $id : null; + } + + public function getCurrentWindowId() : int{ + return $this->lastInventoryNetworkId; + } + + public function getWindow(int $windowId) : ?Inventory{ + return $this->windowMap[$windowId] ?? null; + } + + public function onCurrentWindowChange(Inventory $inventory) : void{ + $this->onCurrentWindowRemove(); + $this->windowMap[$this->lastInventoryNetworkId = max(ContainerIds::FIRST, ($this->lastInventoryNetworkId + 1) % ContainerIds::LAST)] = $inventory; + + $pk = $this->createContainerOpen($this->lastInventoryNetworkId, $inventory); + if($pk !== null){ + $this->session->sendDataPacket($pk); + $this->syncContents($inventory); + }else{ + throw new \UnsupportedOperationException("Unsupported inventory type"); + } + } + + protected function createContainerOpen(int $id, Inventory $inv) : ?ContainerOpenPacket{ + //TODO: allow plugins to inject this + if($inv instanceof BlockInventory){ + switch(true){ + case $inv instanceof FurnaceInventory: + //TODO: specialized furnace types + return ContainerOpenPacket::blockInvVec3($id, WindowTypes::FURNACE, $inv->getHolder()); + case $inv instanceof EnchantInventory: + return ContainerOpenPacket::blockInvVec3($id, WindowTypes::ENCHANTMENT, $inv->getHolder()); + case $inv instanceof BrewingStandInventory: + return ContainerOpenPacket::blockInvVec3($id, WindowTypes::BREWING_STAND, $inv->getHolder()); + case $inv instanceof AnvilInventory: + return ContainerOpenPacket::blockInvVec3($id, WindowTypes::ANVIL, $inv->getHolder()); + case $inv instanceof HopperInventory: + return ContainerOpenPacket::blockInvVec3($id, WindowTypes::HOPPER, $inv->getHolder()); + default: + return ContainerOpenPacket::blockInvVec3($id, WindowTypes::CONTAINER, $inv->getHolder()); + } + } + return null; + } + + public function onCurrentWindowRemove() : void{ + if(isset($this->windowMap[$this->lastInventoryNetworkId])){ + unset($this->windowMap[$this->lastInventoryNetworkId]); + $this->session->sendDataPacket(ContainerClosePacket::create($this->lastInventoryNetworkId)); + } + } + + public function syncSlot(Inventory $inventory, int $slot) : void{ + $windowId = $this->getWindowId($inventory); + if($windowId !== null){ + $this->session->sendDataPacket(InventorySlotPacket::create($windowId, $slot, $inventory->getItem($slot))); + } + } + + public function syncContents(Inventory $inventory) : void{ + $windowId = $this->getWindowId($inventory); + if($windowId !== null){ + $this->session->sendDataPacket(InventoryContentPacket::create($windowId, $inventory->getContents(true))); + } + } + + public function syncAll() : void{ + foreach($this->player->getAllWindows() as $inventory){ + $this->syncContents($inventory); + } + } + + public function syncData(Inventory $inventory, int $propertyId, int $value) : void{ + $windowId = $this->getWindowId($inventory); + if($windowId !== null){ + $this->session->sendDataPacket(ContainerSetDataPacket::create($windowId, $propertyId, $value)); + } + } + + public function syncCreative() : void{ + $items = []; + if(!$this->player->isSpectator()){ //fill it for all gamemodes except spectator + foreach(CreativeInventory::getAll() as $i => $item){ + $items[$i] = clone $item; + } + } + + $this->session->sendDataPacket(InventoryContentPacket::create(ContainerIds::CREATIVE, $items)); + } +} diff --git a/src/pocketmine/network/mcpe/NetworkSession.php b/src/pocketmine/network/mcpe/NetworkSession.php index 97f952ef9..5e54db62a 100644 --- a/src/pocketmine/network/mcpe/NetworkSession.php +++ b/src/pocketmine/network/mcpe/NetworkSession.php @@ -29,8 +29,6 @@ use pocketmine\event\player\PlayerCreationEvent; use pocketmine\event\server\DataPacketReceiveEvent; use pocketmine\event\server\DataPacketSendEvent; use pocketmine\form\Form; -use pocketmine\inventory\CreativeInventory; -use pocketmine\inventory\Inventory; use pocketmine\math\Vector3; use pocketmine\network\BadPacketException; use pocketmine\network\mcpe\handler\DeathPacketHandler; @@ -45,11 +43,8 @@ use pocketmine\network\mcpe\protocol\AdventureSettingsPacket; use pocketmine\network\mcpe\protocol\AvailableCommandsPacket; use pocketmine\network\mcpe\protocol\ChunkRadiusUpdatedPacket; use pocketmine\network\mcpe\protocol\ClientboundPacket; -use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; use pocketmine\network\mcpe\protocol\DisconnectPacket; use pocketmine\network\mcpe\protocol\GarbageServerboundPacket; -use pocketmine\network\mcpe\protocol\InventoryContentPacket; -use pocketmine\network\mcpe\protocol\InventorySlotPacket; use pocketmine\network\mcpe\protocol\MobArmorEquipmentPacket; use pocketmine\network\mcpe\protocol\MobEffectPacket; use pocketmine\network\mcpe\protocol\ModalFormRequestPacket; @@ -67,7 +62,6 @@ use pocketmine\network\mcpe\protocol\TransferPacket; use pocketmine\network\mcpe\protocol\types\CommandData; use pocketmine\network\mcpe\protocol\types\CommandEnum; use pocketmine\network\mcpe\protocol\types\CommandParameter; -use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\PlayerListEntry; use pocketmine\network\mcpe\protocol\types\PlayerPermissions; use pocketmine\network\mcpe\protocol\UpdateAttributesPacket; @@ -139,6 +133,9 @@ class NetworkSession{ /** @var \SplQueue|CompressBatchPromise[] */ private $compressedQueue; + /** @var InventoryManager|null */ + private $invManager = null; + public function __construct(Server $server, NetworkSessionManager $manager, NetworkInterface $interface, string $ip, int $port){ $this->server = $server; $this->manager = $manager; @@ -176,6 +173,8 @@ class NetworkSession{ * @see Player::__construct() */ $this->player = new $class($this->server, $this, $this->info, $this->authenticated); + + $this->invManager = new InventoryManager($this->player, $this); } public function getPlayer() : ?Player{ @@ -443,6 +442,8 @@ class NetworkSession{ $this->connected = false; $this->manager->remove($this); $this->logger->info("Session closed due to $reason"); + + $this->invManager = null; //break cycles - TODO: this really ought to be deferred until it's safe } } @@ -594,7 +595,7 @@ class NetworkSession{ $this->player->sendData($this->player->getViewers()); $this->syncAdventureSettings($this->player); - $this->syncAllInventoryContents(); + $this->invManager->syncAll(); $this->setHandler(new InGamePacketHandler($this->player, $this)); } @@ -629,7 +630,7 @@ class NetworkSession{ $this->sendDataPacket(SetPlayerGameTypePacket::create(self::getClientFriendlyGamemode($mode))); $this->syncAdventureSettings($this->player); if(!$isRollback){ - $this->syncCreativeInventoryContents(); + $this->invManager->syncCreative(); } } @@ -765,42 +766,11 @@ class NetworkSession{ } - public function syncInventorySlot(Inventory $inventory, int $slot) : void{ - $windowId = $this->player->getWindowId($inventory); - if($windowId !== null){ - $this->sendDataPacket(InventorySlotPacket::create($windowId, $slot, $inventory->getItem($slot))); - } - } - - public function syncInventoryContents(Inventory $inventory) : void{ - $windowId = $this->player->getWindowId($inventory); - if($windowId !== null){ - $this->sendDataPacket(InventoryContentPacket::create($windowId, $inventory->getContents(true))); - } - } - - public function syncAllInventoryContents() : void{ - foreach($this->player->getAllWindows() as $inventory){ - $this->syncInventoryContents($inventory); - } - } - - public function syncInventoryData(Inventory $inventory, int $propertyId, int $value) : void{ - $windowId = $this->player->getWindowId($inventory); - if($windowId !== null){ - $this->sendDataPacket(ContainerSetDataPacket::create($windowId, $propertyId, $value)); - } - } - - public function syncCreativeInventoryContents() : void{ - $items = []; - if(!$this->player->isSpectator()){ //fill it for all gamemodes except spectator - foreach(CreativeInventory::getAll() as $i => $item){ - $items[$i] = clone $item; - } - } - - $this->sendDataPacket(InventoryContentPacket::create(ContainerIds::CREATIVE, $items)); + /** + * @return InventoryManager + */ + public function getInvManager() : InventoryManager{ + return $this->invManager; } public function onMobArmorChange(Living $mob) : void{ diff --git a/src/pocketmine/network/mcpe/handler/InGamePacketHandler.php b/src/pocketmine/network/mcpe/handler/InGamePacketHandler.php index 2014968d4..7df975db1 100644 --- a/src/pocketmine/network/mcpe/handler/InGamePacketHandler.php +++ b/src/pocketmine/network/mcpe/handler/InGamePacketHandler.php @@ -162,7 +162,7 @@ class InGamePacketHandler extends PacketHandler{ public function handleInventoryTransaction(InventoryTransactionPacket $packet) : bool{ if($this->player->isSpectator()){ - $this->session->syncAllInventoryContents(); + $this->session->getInvManager()->syncAll(); return true; } @@ -172,7 +172,7 @@ class InGamePacketHandler extends PacketHandler{ $result = $this->handleNormalTransaction($packet->trData); }elseif($packet->trData instanceof MismatchTransactionData){ $this->session->getLogger()->debug("Mismatch transaction received"); - $this->session->syncAllInventoryContents(); + $this->session->getInvManager()->syncAll(); $result = true; }elseif($packet->trData instanceof UseItemTransactionData){ $result = $this->handleUseItemTransaction($packet->trData); @@ -183,7 +183,7 @@ class InGamePacketHandler extends PacketHandler{ } if(!$result){ - $this->session->syncAllInventoryContents(); + $this->session->getInvManager()->syncAll(); } return $result; } @@ -344,7 +344,7 @@ class InGamePacketHandler extends PacketHandler{ switch($data->getActionType()){ case ReleaseItemTransactionData::ACTION_RELEASE: if(!$this->player->releaseHeldItem()){ - $this->session->syncInventoryContents($this->player->getInventory()); + $this->session->getInvManager()->syncContents($this->player->getInventory()); } return true; case ReleaseItemTransactionData::ACTION_CONSUME: @@ -462,7 +462,21 @@ class InGamePacketHandler extends PacketHandler{ } public function handleContainerClose(ContainerClosePacket $packet) : bool{ - return $this->player->doCloseWindow($packet->windowId); + $this->player->doCloseInventory(); + + if($packet->windowId === 255){ + //Closed a fake window + return true; + } + + $window = $this->player->getCurrentWindow(); + if($window !== null and $packet->windowId === $this->session->getInvManager()->getCurrentWindowId()){ + $this->player->removeCurrentWindow(); + return true; + } + + $this->session->getLogger()->debug("Attempted to close inventory with network ID $packet->windowId, but current is " . $this->session->getInvManager()->getCurrentWindowId()); + return false; } public function handlePlayerHotbar(PlayerHotbarPacket $packet) : bool{ diff --git a/src/pocketmine/network/mcpe/handler/PreSpawnPacketHandler.php b/src/pocketmine/network/mcpe/handler/PreSpawnPacketHandler.php index 1cab9d63c..5f7e3a8ae 100644 --- a/src/pocketmine/network/mcpe/handler/PreSpawnPacketHandler.php +++ b/src/pocketmine/network/mcpe/handler/PreSpawnPacketHandler.php @@ -91,8 +91,8 @@ class PreSpawnPacketHandler extends PacketHandler{ $this->player->sendPotionEffects($this->player); $this->player->sendData($this->player); - $this->session->syncAllInventoryContents(); - $this->session->syncCreativeInventoryContents(); + $this->session->getInvManager()->syncAll(); + $this->session->getInvManager()->syncCreative(); $this->player->getInventory()->sendHeldItem($this->player); $this->session->queueCompressed($this->server->getCraftingManager()->getCraftingDataPacket()); diff --git a/src/pocketmine/network/mcpe/protocol/ContainerOpenPacket.php b/src/pocketmine/network/mcpe/protocol/ContainerOpenPacket.php index 3addd12bb..6b36f9782 100644 --- a/src/pocketmine/network/mcpe/protocol/ContainerOpenPacket.php +++ b/src/pocketmine/network/mcpe/protocol/ContainerOpenPacket.php @@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol; #include +use pocketmine\math\Vector3; use pocketmine\network\mcpe\handler\PacketHandler; class ContainerOpenPacket extends DataPacket implements ClientboundPacket{ @@ -52,6 +53,10 @@ class ContainerOpenPacket extends DataPacket implements ClientboundPacket{ return $result; } + public static function blockInvVec3(int $windowId, int $windowType, Vector3 $vector3) : self{ + return self::blockInv($windowId, $windowType, $vector3->getFloorX(), $vector3->getFloorY(), $vector3->getFloorZ()); + } + public static function entityInv(int $windowId, int $windowType, int $entityUniqueId) : self{ $result = new self; $result->windowId = $windowId; diff --git a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php index 7d689f73e..2a5851a14 100644 --- a/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php +++ b/src/pocketmine/network/mcpe/protocol/types/NetworkInventoryAction.php @@ -171,7 +171,7 @@ class NetworkInventoryAction{ public function createInventoryAction(Player $player) : ?InventoryAction{ switch($this->sourceType){ case self::SOURCE_CONTAINER: - $window = $player->getWindow($this->windowId); + $window = $player->getNetworkSession()->getInvManager()->getWindow($this->windowId); if($window !== null){ return new SlotChangeAction($window, $this->inventorySlot, $this->oldItem, $this->newItem); } diff --git a/src/pocketmine/player/Player.php b/src/pocketmine/player/Player.php index 96b5e6ec7..0ba07f1e0 100644 --- a/src/pocketmine/player/Player.php +++ b/src/pocketmine/player/Player.php @@ -92,7 +92,6 @@ use pocketmine\network\mcpe\protocol\ClientboundPacket; use pocketmine\network\mcpe\protocol\LevelEventPacket; use pocketmine\network\mcpe\protocol\MovePlayerPacket; use pocketmine\network\mcpe\protocol\SetTitlePacket; -use pocketmine\network\mcpe\protocol\types\ContainerIds; use pocketmine\network\mcpe\protocol\types\EntityMetadataFlags; use pocketmine\network\mcpe\protocol\types\EntityMetadataProperties; use pocketmine\network\mcpe\protocol\types\PlayerMetadataFlags; @@ -111,7 +110,6 @@ use pocketmine\world\particle\PunchBlockParticle; use pocketmine\world\Position; use pocketmine\world\World; use function abs; -use function array_search; use function assert; use function ceil; use function count; @@ -119,7 +117,6 @@ use function explode; use function floor; use function get_class; use function is_int; -use function max; use function microtime; use function min; use function preg_match; @@ -182,10 +179,6 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, /** @var PlayerInfo */ protected $playerInfo; - /** @var int */ - protected $lastInventoryNetworkId = 2; - /** @var Inventory[] network ID => inventory */ - protected $networkIdToInventoryMap = []; /** @var Inventory|null */ protected $currentWindow = null; /** @var Inventory[] */ @@ -2316,10 +2309,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, $this->loadQueue = []; $this->removeCurrentWindow(); - foreach($this->permanentWindows as $objectId => $inventory){ - $this->closeInventoryInternal($inventory, true); - } - assert(empty($this->networkIdToInventoryMap)); + $this->removePermanentInventories(); $this->perm->clearPermissions(); @@ -2570,15 +2560,11 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, } protected function addDefaultWindows(){ - $this->openInventoryInternal($this->getInventory(), ContainerIds::INVENTORY, true); - - $this->openInventoryInternal($this->getArmorInventory(), ContainerIds::ARMOR, true); - $this->cursorInventory = new PlayerCursorInventory($this); - $this->openInventoryInternal($this->cursorInventory, ContainerIds::CURSOR, true); - $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); + $this->addPermanentInventories($this->inventory, $this->armorInventory, $this->cursorInventory); + //TODO: more windows } @@ -2621,29 +2607,6 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, } } - /** - * @internal Called by the network session when a player closes a window. - * - * @param int $windowId - * - * @return bool - */ - public function doCloseWindow(int $windowId) : bool{ - $this->doCloseInventory(); - - if($windowId === $this->lastInventoryNetworkId and $this->currentWindow !== null){ - $this->removeCurrentWindow(); - return true; - } - if($windowId === 255){ - //Closed a fake window - return true; - } - - $this->logger->debug("Attempted to close inventory with network ID $windowId, but current is $this->lastInventoryNetworkId"); - return false; - } - /** * Returns the inventory the player is currently viewing. This might be a chest, furnace, or any other container. * @@ -2673,9 +2636,9 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, //TODO: client side race condition here makes the opening work incorrectly $this->removeCurrentWindow(); - $networkId = $this->lastInventoryNetworkId = max(ContainerIds::FIRST, ($this->lastInventoryNetworkId + 1) % ContainerIds::LAST); - - $this->openInventoryInternal($inventory, $networkId, false); + $this->logger->debug("Opening inventory " . get_class($inventory) . "#" . spl_object_id($inventory)); + $this->networkSession->getInvManager()->onCurrentWindowChange($inventory); + $inventory->onOpen($this); $this->currentWindow = $inventory; return true; } @@ -2683,61 +2646,28 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener, public function removeCurrentWindow() : void{ if($this->currentWindow !== null){ (new InventoryCloseEvent($this->currentWindow, $this))->call(); - $this->closeInventoryInternal($this->currentWindow, false); + + $this->logger->debug("Closing inventory " . get_class($this->currentWindow) . "#" . spl_object_id($this->currentWindow)); + $this->currentWindow->onClose($this); + if($this->isConnected()){ + $this->networkSession->getInvManager()->onCurrentWindowRemove(); + } + $this->currentWindow = null; } } - /** - * Returns the window ID which the inventory has for this player, or -1 if the window is not open to the player. - * - * @param Inventory $inventory - * - * @return int|null - */ - public function getWindowId(Inventory $inventory) : ?int{ - return ($id = array_search($inventory, $this->networkIdToInventoryMap, true)) !== false ? $id : null; - } - - /** - * Returns the inventory window open to the player with the specified window ID, or null if no window is open with - * that ID. - * - * @param int $windowId - * - * @return Inventory|null - */ - public function getWindow(int $windowId) : ?Inventory{ - return $this->networkIdToInventoryMap[$windowId] ?? null; - } - - protected function openInventoryInternal(Inventory $inventory, int $networkId, bool $permanent) : void{ - $this->logger->debug("Opening inventory " . get_class($inventory) . "#" . spl_object_id($inventory) . " with network ID $networkId"); - $this->networkIdToInventoryMap[$networkId] = $inventory; - $inventory->onOpen($this); - if($permanent){ + protected function addPermanentInventories(Inventory ...$inventories) : void{ + foreach($inventories as $inventory){ + $inventory->onOpen($this); $this->permanentWindows[spl_object_id($inventory)] = $inventory; } } - protected function closeInventoryInternal(Inventory $inventory, bool $force) : bool{ - $this->logger->debug("Closing inventory " . get_class($inventory) . "#" . spl_object_id($inventory)); - $objectId = spl_object_id($inventory); - if($inventory === $this->currentWindow){ - $this->currentWindow = null; - }elseif(isset($this->permanentWindows[$objectId])){ - if(!$force){ - throw new \InvalidArgumentException("Cannot remove fixed window " . get_class($inventory) . " from " . $this->getName()); - } - unset($this->permanentWindows[$objectId]); - }else{ - return false; + protected function removePermanentInventories() : void{ + foreach($this->permanentWindows as $inventory){ + $inventory->onClose($this); } - - $inventory->onClose($this); - $networkId = $this->getWindowId($inventory); - assert($networkId !== null); - unset($this->networkIdToInventoryMap[$networkId]); - return true; + $this->permanentWindows = []; } /**