diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 94cd39bc4c..33d2d4d446 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -72,6 +72,7 @@ use pocketmine\form\FormValidationException; use pocketmine\inventory\CraftingGrid; use pocketmine\inventory\Inventory; use pocketmine\inventory\PlayerCursorInventory; +use pocketmine\inventory\PlayerUIInventory; use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\CraftingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; @@ -289,8 +290,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ protected $windowIndex = []; /** @var bool[] */ protected $permanentWindows = []; - /** @var PlayerCursorInventory */ - protected $cursorInventory; + /** @var PlayerUIInventory */ + protected $playerUIInventory; /** @var CraftingGrid */ protected $craftingGrid = null; /** @var CraftingTransaction|null */ @@ -2718,6 +2719,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if(!$this->spawned or !$this->isAlive()){ return true; } + if($packet->action === InteractPacket::ACTION_MOUSEOVER and $packet->target === 0){ //TODO HACK: silence useless spam (MCPE 1.8) //this packet is EXPECTED to only be sent when interacting with an entity, but due to some messy Mojang @@ -3552,7 +3554,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->removeAllWindows(true); $this->windows = []; $this->windowIndex = []; - $this->cursorInventory = null; + $this->playerUIInventory = null; $this->craftingGrid = null; if($this->constructed){ @@ -3817,16 +3819,19 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->addWindow($this->getArmorInventory(), ContainerIds::ARMOR, true); - $this->cursorInventory = new PlayerCursorInventory($this); - $this->addWindow($this->cursorInventory, ContainerIds::CURSOR, true); - - $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); + $this->playerUIInventory = new PlayerUIInventory($this); + $this->addWindow($this->playerUIInventory, ContainerIds::UI, true); + $this->craftingGrid = new CraftingGrid($this->playerUIInventory, CraftingGrid::SIZE_SMALL); //TODO: more windows } + public function getPlayerUIInventory() : PlayerUIInventory{ + return $this->playerUIInventory; + } + public function getCursorInventory() : PlayerCursorInventory{ - return $this->cursorInventory; + return $this->playerUIInventory->getCursorInventory(); } public function getCraftingGrid() : CraftingGrid{ @@ -3842,7 +3847,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ public function doCloseInventory() : void{ /** @var Inventory[] $inventories */ - $inventories = [$this->craftingGrid, $this->cursorInventory]; + $inventories = [$this->craftingGrid, $this->getCursorInventory()]; foreach($inventories as $inventory){ $contents = $inventory->getContents(); if(count($contents) > 0){ @@ -3856,7 +3861,7 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } if($this->craftingGrid->getGridWidth() > CraftingGrid::SIZE_SMALL){ - $this->craftingGrid = new CraftingGrid($this, CraftingGrid::SIZE_SMALL); + $this->craftingGrid = new CraftingGrid($this->playerUIInventory, CraftingGrid::SIZE_SMALL); } } diff --git a/src/pocketmine/block/CraftingTable.php b/src/pocketmine/block/CraftingTable.php index 05b52fd6ad..9e416e8725 100644 --- a/src/pocketmine/block/CraftingTable.php +++ b/src/pocketmine/block/CraftingTable.php @@ -49,7 +49,7 @@ class CraftingTable extends Solid{ public function onActivate(Item $item, Player $player = null) : bool{ if($player instanceof Player){ - $player->setCraftingGrid(new CraftingGrid($player, CraftingGrid::SIZE_BIG)); + $player->setCraftingGrid(new CraftingGrid($player->getPlayerUIInventory(), CraftingGrid::SIZE_BIG)); } return true; diff --git a/src/pocketmine/inventory/BaseUIInventory.php b/src/pocketmine/inventory/BaseUIInventory.php new file mode 100644 index 0000000000..3636e3ab75 --- /dev/null +++ b/src/pocketmine/inventory/BaseUIInventory.php @@ -0,0 +1,116 @@ +inventory = $inventory; + $this->size = $size; + $this->offset = $offset; + parent::__construct([], $size); + } + + public function getName() : string{ + return "UI"; + } + + public function getDefaultSize() : int{ + return 1; + } + + public function getSize() : int{ + return $this->size; + } + + public function getMaxStackSize() : int{ + return Inventory::MAX_STACK; + } + + public function setMaxStackSize(int $size) : void{ + } + + public function getTitle() : string{ + return ""; + } + + public function getItem(int $index) : Item{ + return $this->inventory->getItem($index + $this->offset); + } + + public function setItem(int $index, Item $item, bool $send = true) : bool{ + return $this->inventory->setItem($index + $this->offset, $item, $send); + } + + public function getContents(bool $includeEmpty = false) : array{ + $contents = []; + $air = null; + foreach($this->slots as $i => $slot){ + if($i < $this->offset || $i > $this->offset + $this->size){ + continue; + } + if($slot !== null){ + $contents[$i] = clone $slot; + }elseif($includeEmpty){ + $contents[$i] = $air ?? ($air = ItemFactory::get(Item::AIR, 0, 0)); + } + } + + return $contents; + } + + public function sendContents($target) : void{ + $this->inventory->sendContents($target); + } + + public function sendSlot(int $index, $target) : void{ + $this->inventory->sendSlot($index + $this->offset, $target); + } + + public function getViewers() : array{ + return $this->inventory->viewers; + } + + public function onOpen(Player $who) : void{} + + public function onClose(Player $who) : void{} + + public function open(Player $who) : bool{ + return false; + } + + public function close(Player $who) : void{} + + public function onSlotChange(int $index, Item $before, bool $send) : void{ + $this->inventory->onSlotChange($index + $this->offset, $before, $send); + } +} \ No newline at end of file diff --git a/src/pocketmine/inventory/CraftingGrid.php b/src/pocketmine/inventory/CraftingGrid.php index 4beb9ed71a..b54068f4e4 100644 --- a/src/pocketmine/inventory/CraftingGrid.php +++ b/src/pocketmine/inventory/CraftingGrid.php @@ -29,134 +29,23 @@ use function max; use function min; use const PHP_INT_MAX; -class CraftingGrid extends BaseInventory{ - public const SIZE_SMALL = 2; - public const SIZE_BIG = 3; +class CraftingGrid extends BaseUIInventory{ + public const SIZE_SMALL = 4; + public const SIZE_BIG = 9; - /** @var Player */ - protected $holder; /** @var int */ private $gridWidth; - /** @var int|null */ - private $startX; - /** @var int|null */ - private $xLen; - /** @var int|null */ - private $startY; - /** @var int|null */ - private $yLen; - - public function __construct(Player $holder, int $gridWidth){ - $this->holder = $holder; + public function __construct(PlayerUIInventory $inventory, int $gridWidth){ $this->gridWidth = $gridWidth; - parent::__construct(); + if($gridWidth === self::SIZE_SMALL){ + parent::__construct($inventory, $gridWidth, 28); + }elseif($gridWidth === self::SIZE_BIG){ + parent::__construct($inventory, $gridWidth, 32); + } } public function getGridWidth() : int{ return $this->gridWidth; } - - public function getDefaultSize() : int{ - return $this->getGridWidth() ** 2; - } - - public function setSize(int $size){ - throw new \BadMethodCallException("Cannot change the size of a crafting grid"); - } - - public function getName() : string{ - return "Crafting"; - } - - public function setItem(int $index, Item $item, bool $send = true) : bool{ - if(parent::setItem($index, $item, $send)){ - $this->seekRecipeBounds(); - - return true; - } - - return false; - } - - public function sendSlot(int $index, $target) : void{ - //we can't send a slot of a client-sided inventory window - } - - public function sendContents($target) : void{ - //no way to do this - } - - /** - * @return Player - */ - public function getHolder(){ - return $this->holder; - } - - private function seekRecipeBounds() : void{ - $minX = PHP_INT_MAX; - $maxX = 0; - - $minY = PHP_INT_MAX; - $maxY = 0; - - $empty = true; - - for($y = 0; $y < $this->gridWidth; ++$y){ - for($x = 0; $x < $this->gridWidth; ++$x){ - if(!$this->isSlotEmpty($y * $this->gridWidth + $x)){ - $minX = min($minX, $x); - $maxX = max($maxX, $x); - - $minY = min($minY, $y); - $maxY = max($maxY, $y); - - $empty = false; - } - } - } - - if(!$empty){ - $this->startX = $minX; - $this->xLen = $maxX - $minX + 1; - $this->startY = $minY; - $this->yLen = $maxY - $minY + 1; - }else{ - $this->startX = $this->xLen = $this->startY = $this->yLen = null; - } - } - - /** - * Returns the item at offset x,y, offset by where the starts of the recipe rectangle are. - * - * @param int $x - * @param int $y - * - * @return Item - */ - public function getIngredient(int $x, int $y) : Item{ - if($this->startX !== null and $this->startY !== null){ - return $this->getItem(($y + $this->startY) * $this->gridWidth + ($x + $this->startX)); - } - - throw new \InvalidStateException("No ingredients found in grid"); - } - - /** - * Returns the width of the recipe we're trying to craft, based on items currently in the grid. - * - * @return int - */ - public function getRecipeWidth() : int{ - return $this->xLen ?? 0; - } - - /** - * Returns the height of the recipe we're trying to craft, based on items currently in the grid. - * @return int - */ - public function getRecipeHeight() : int{ - return $this->yLen ?? 0; - } } diff --git a/src/pocketmine/inventory/PlayerCursorInventory.php b/src/pocketmine/inventory/PlayerCursorInventory.php index b967139f5f..32939f7f76 100644 --- a/src/pocketmine/inventory/PlayerCursorInventory.php +++ b/src/pocketmine/inventory/PlayerCursorInventory.php @@ -23,34 +23,9 @@ declare(strict_types=1); namespace pocketmine\inventory; -use pocketmine\Player; +class PlayerCursorInventory extends BaseUIInventory{ -class PlayerCursorInventory extends BaseInventory{ - /** @var Player */ - protected $holder; - - public function __construct(Player $holder){ - $this->holder = $holder; - parent::__construct(); - } - - public function getName() : string{ - return "Cursor"; - } - - public function getDefaultSize() : int{ - return 1; - } - - public function setSize(int $size){ - throw new \BadMethodCallException("Cursor can only carry one item at a time"); - } - - /** - * This override is here for documentation and code completion purposes only. - * @return Player - */ - public function getHolder(){ - return $this->holder; + public function __construct(PlayerUIInventory $inventory){ + parent::__construct($inventory, 1, 0); } } diff --git a/src/pocketmine/inventory/PlayerUIInventory.php b/src/pocketmine/inventory/PlayerUIInventory.php new file mode 100644 index 0000000000..a3e9e64542 --- /dev/null +++ b/src/pocketmine/inventory/PlayerUIInventory.php @@ -0,0 +1,89 @@ +holder = $holder; + $this->cursorInventory = new PlayerCursorInventory($this); + parent::__construct(); + } + + public function getCursorInventory(){ + return $this->cursorInventory; + } + + public function getName() : string{ + return "UI"; + } + + public function getDefaultSize() : int{ + return 51; + } + + public function getSize() : int{ + return 51; + } + + public function getHolder(){ + return $this->holder; + } + + public function setSize(int $size){} + + public function sendSlot(int $index, $target) : void{ + if($target instanceof Player){ + $target = [$target]; + } + $pk = new InventorySlotPacket(); + $pk->inventorySlot = $index; + $pk->item = $this->getItem($index); + foreach($target as $player){ + if($player === $this->getHolder()){ + $pk->windowId = ContainerIds::UI; + $player->dataPacket($pk); + }else{ + if(($id = $player->getWindowId($this)) === ContainerIds::NONE){ + $this->close($player); + continue; + } + $pk->windowId = $id; + $player->dataPacket($pk); + } + } + } +} \ No newline at end of file diff --git a/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php b/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php index 3b55065e51..0c9769f9f0 100644 --- a/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php +++ b/src/pocketmine/network/mcpe/protocol/types/ContainerIds.php @@ -34,6 +34,6 @@ interface ContainerIds{ public const CREATIVE = 121; public const HOTBAR = 122; public const FIXED_INVENTORY = 123; - public const CURSOR = 124; + public const UI = 124; }