work on moving inventory network functionality to network layer

This commit is contained in:
Dylan K. Taylor 2019-06-25 13:28:01 +01:00
parent 9f09dc3dd7
commit d15284e638
21 changed files with 277 additions and 265 deletions

View File

@ -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);
}
}

View File

@ -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

View File

@ -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);
}
}
}

View File

@ -0,0 +1,43 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\math\Vector3;
abstract class BlockInventory extends BaseInventory{
/** @var Vector3 */
protected $holder;
public function __construct(Vector3 $holder, int $size, array $items = []){
$this->holder = $holder;
parent::__construct($size, $items);
}
/**
* @return Vector3
*/
public function getHolder(){
return $this->holder;
}
}

View File

@ -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;
}
}

View File

@ -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

View File

@ -1,72 +0,0 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\inventory;
use pocketmine\entity\Entity;
use pocketmine\math\Vector3;
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\player\Player;
abstract class ContainerInventory extends BaseInventory{
/** @var Vector3 */
protected $holder;
public function __construct(Vector3 $holder, int $size, array $items = []){
$this->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;
}
}

View File

@ -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

View File

@ -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);
}
/**

View File

@ -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

View File

@ -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;
}
}

View File

@ -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());
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -0,0 +1,157 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\network\mcpe;
use pocketmine\inventory\AnvilInventory;
use pocketmine\inventory\BlockInventory;
use pocketmine\inventory\BrewingStandInventory;
use pocketmine\inventory\CreativeInventory;
use pocketmine\inventory\EnchantInventory;
use pocketmine\inventory\FurnaceInventory;
use pocketmine\inventory\HopperInventory;
use pocketmine\inventory\Inventory;
use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
use pocketmine\network\mcpe\protocol\InventoryContentPacket;
use pocketmine\network\mcpe\protocol\InventorySlotPacket;
use pocketmine\network\mcpe\protocol\types\ContainerIds;
use pocketmine\network\mcpe\protocol\types\WindowTypes;
use pocketmine\player\Player;
use function array_search;
use function max;
class InventoryManager{
/** @var Player */
private $player;
/** @var NetworkSession */
private $session;
/** @var Inventory[] */
private $windowMap = [];
/** @var int */
private $lastInventoryNetworkId = ContainerIds::FIRST;
public function __construct(Player $player, NetworkSession $session){
$this->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));
}
}

View File

@ -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{

View File

@ -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{

View File

@ -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());

View File

@ -26,6 +26,7 @@ namespace pocketmine\network\mcpe\protocol;
#include <rules/DataPacket.h>
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;

View File

@ -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);
}

View File

@ -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 = [];
}
/**