InventoryManager: add a mechanism to allow plugins to inject their own container open packet creators

closes #4008
This commit is contained in:
Dylan K. Taylor 2021-04-27 14:40:43 +01:00
parent f37c414298
commit 7ce77713dd
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D

View File

@ -35,6 +35,7 @@ use pocketmine\inventory\transaction\action\SlotChangeAction;
use pocketmine\inventory\transaction\InventoryTransaction; use pocketmine\inventory\transaction\InventoryTransaction;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\network\mcpe\convert\TypeConverter; use pocketmine\network\mcpe\convert\TypeConverter;
use pocketmine\network\mcpe\protocol\ClientboundPacket;
use pocketmine\network\mcpe\protocol\ContainerClosePacket; use pocketmine\network\mcpe\protocol\ContainerClosePacket;
use pocketmine\network\mcpe\protocol\ContainerOpenPacket; use pocketmine\network\mcpe\protocol\ContainerOpenPacket;
use pocketmine\network\mcpe\protocol\ContainerSetDataPacket; use pocketmine\network\mcpe\protocol\ContainerSetDataPacket;
@ -47,10 +48,14 @@ use pocketmine\network\mcpe\protocol\types\inventory\CreativeContentEntry;
use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper; use pocketmine\network\mcpe\protocol\types\inventory\ItemStackWrapper;
use pocketmine\network\mcpe\protocol\types\inventory\WindowTypes; use pocketmine\network\mcpe\protocol\types\inventory\WindowTypes;
use pocketmine\player\Player; use pocketmine\player\Player;
use pocketmine\utils\ObjectSet;
use function array_map; use function array_map;
use function array_search; use function array_search;
use function max; use function max;
/**
* @phpstan-type ContainerOpenClosure \Closure(int $id, Inventory $inventory) : list<ClientboundPacket>|null
*/
class InventoryManager{ class InventoryManager{
//TODO: HACK! //TODO: HACK!
@ -79,10 +84,16 @@ class InventoryManager{
/** @var int */ /** @var int */
private $clientSelectedHotbarSlot = -1; private $clientSelectedHotbarSlot = -1;
/** @phpstan-var ObjectSet<ContainerOpenClosure> */
private ObjectSet $containerOpenCallbacks;
public function __construct(Player $player, NetworkSession $session){ public function __construct(Player $player, NetworkSession $session){
$this->player = $player; $this->player = $player;
$this->session = $session; $this->session = $session;
$this->containerOpenCallbacks = new ObjectSet();
$this->containerOpenCallbacks->add(\Closure::fromCallable([self::class, 'createContainerOpen']));
$this->add(ContainerIds::INVENTORY, $this->player->getInventory()); $this->add(ContainerIds::INVENTORY, $this->player->getInventory());
$this->add(ContainerIds::ARMOR, $this->player->getArmorInventory()); $this->add(ContainerIds::ARMOR, $this->player->getArmorInventory());
$this->add(ContainerIds::UI, $this->player->getCursorInventory()); $this->add(ContainerIds::UI, $this->player->getCursorInventory());
@ -125,32 +136,44 @@ class InventoryManager{
$this->onCurrentWindowRemove(); $this->onCurrentWindowRemove();
$this->add($this->lastInventoryNetworkId = max(ContainerIds::FIRST, ($this->lastInventoryNetworkId + 1) % self::RESERVED_WINDOW_ID_RANGE_START), $inventory); $this->add($this->lastInventoryNetworkId = max(ContainerIds::FIRST, ($this->lastInventoryNetworkId + 1) % self::RESERVED_WINDOW_ID_RANGE_START), $inventory);
$pk = $this->createContainerOpen($this->lastInventoryNetworkId, $inventory); foreach($this->containerOpenCallbacks as $callback){
if($pk !== null){ $pks = $callback($this->lastInventoryNetworkId, $inventory);
$this->session->sendDataPacket($pk); if($pks !== null){
$this->syncContents($inventory); foreach($pks as $pk){
}else{ $this->session->sendDataPacket($pk);
throw new \UnsupportedOperationException("Unsupported inventory type"); }
$this->syncContents($inventory);
}
return;
} }
throw new \UnsupportedOperationException("Unsupported inventory type");
} }
protected function createContainerOpen(int $id, Inventory $inv) : ?ContainerOpenPacket{ /** @phpstan-return ObjectSet<ContainerOpenClosure> */
//TODO: allow plugins to inject this public function getContainerOpenCallbacks() : ObjectSet{ return $this->containerOpenCallbacks; }
/**
* @return ClientboundPacket[]|null
* @phpstan-return list<ClientboundPacket>|null
*/
protected static function createContainerOpen(int $id, Inventory $inv) : ?array{
//TODO: we should be using some kind of tagging system to identify the types. Instanceof is flaky especially
//if the class isn't final, not to mention being inflexible.
if($inv instanceof BlockInventory){ if($inv instanceof BlockInventory){
switch(true){ switch(true){
case $inv instanceof FurnaceInventory: case $inv instanceof FurnaceInventory:
//TODO: specialized furnace types //TODO: specialized furnace types
return ContainerOpenPacket::blockInvVec3($id, WindowTypes::FURNACE, $inv->getHolder()); return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::FURNACE, $inv->getHolder())];
case $inv instanceof EnchantInventory: case $inv instanceof EnchantInventory:
return ContainerOpenPacket::blockInvVec3($id, WindowTypes::ENCHANTMENT, $inv->getHolder()); return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::ENCHANTMENT, $inv->getHolder())];
case $inv instanceof BrewingStandInventory: case $inv instanceof BrewingStandInventory:
return ContainerOpenPacket::blockInvVec3($id, WindowTypes::BREWING_STAND, $inv->getHolder()); return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::BREWING_STAND, $inv->getHolder())];
case $inv instanceof AnvilInventory: case $inv instanceof AnvilInventory:
return ContainerOpenPacket::blockInvVec3($id, WindowTypes::ANVIL, $inv->getHolder()); return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::ANVIL, $inv->getHolder())];
case $inv instanceof HopperInventory: case $inv instanceof HopperInventory:
return ContainerOpenPacket::blockInvVec3($id, WindowTypes::HOPPER, $inv->getHolder()); return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::HOPPER, $inv->getHolder())];
default: default:
return ContainerOpenPacket::blockInvVec3($id, WindowTypes::CONTAINER, $inv->getHolder()); return [ContainerOpenPacket::blockInvVec3($id, WindowTypes::CONTAINER, $inv->getHolder())];
} }
} }
return null; return null;