Create and manage virtual inventories in PocketMine-MP.
Download the compiled .phar file from Poggit CI and place it in your virions/ folder. Read installation and using in a plugin for a more elaborate guide on how to setup InvMenu library.
Note
You must register InvMenuHandler before you can use InvMenu.
// in class MyPlugin extends PluginBase:protectedfunctiononEnable() : void{if(!InvMenuHandler::isRegistered()){InvMenuHandler::register($this)} }Quick start, use InvMenu::create(InvMenu::TYPE_CHEST)->send($player); to display a virtual chest inventory to a player.
InvMenu::create($identifier) creates an InvMenu instance. $identifier may be an identifier of a registered InvMenuType object. InvMenu comes with 3 pre-registered inventory types of different sizes:
InvMenu::TYPE_CHEST- a 27-slot normal chest inventoryInvMenu::TYPE_DOUBLE_CHEST- a 54-slot double chest inventoryInvMenu::TYPE_HOPPER- a 5-slot hopper inventory
$menu = InvMenu::create(InvMenu::TYPE_CHEST); $inventory = $menu->getInventory();As $inventory implements PocketMine's Inventory interface, you get to access all the fancy PocketMine inventory methods.
$menu->getInventory()->setContents([ VanillaItems::DIAMOND_SWORD(), VanillaItems::DIAMOND_PICKAXE() ]); $menu->getInventory()->addItem(VanillaItems::DIAMOND_AXE()); $menu->getInventory()->setItem(3, VanillaItems::GOLD_INGOT());To send a menu to a player, use:
/** @var Player $player */$menu->send($player);Tip
One InvMenu can be sent to multiple players—even 2 players in different worlds, so everyone views and edits the same inventory as if it were one chest.
There are two ways to name an InvMenu. You can either specify a global name (see method A), or you can set a name at the time you send the menu (see method B).
$menu->setName("Custom Name"); // method A$menu->send($player, "Greetings, " . $player->getName()); // method BInvMenu::send() is not guaranteed to succeed. A failure may arise from plugins cancelling InventoryOpenEvent, a disconnected player, or the player refusing the request (e.g., because they are in pause menu). Use the $callback parameter to verify whether a menu has been opened.
$menu->send($player, callback: function(bool$success) : void{if($success){// player is viewing the menu } });InvMenu comes with a listener whereby developers can write logic to monitor movement of items in and out of inventory, and thereby take action. A listener is a callback with the following signature:
/** * @param InvMenuTransaction $transaction * * Return $transaction->continue() to continue the transaction. * Return $transaction->discard() to cancel the transaction. * @return InvMenuTransactionResult */Closure(InvMenuTransaction $transaction) : InvMenuTransactionResult;InvMenuTransaction::getPlayer()returns thePlayerthat triggered the transaction.InvMenuTransaction::getItemClicked()returns theItemthe player clicked in the menu. You may also useInvMenuTransaction::getOut().InvMenuTransaction::getItemClickedWith()returns theItemthe player had in their hand when clicking an item. You may also useInvMenuTransaction::getIn().InvMenuTransaction::getAction()returnsSlotChangeAction- you can get the slot that the player clicked in the menu.InvMenuTransaction::getTransaction()returns the completeInventoryTransactionholding all the above information.
$menu->setListener(function(InvMenuTransaction$transaction) : InvMenuTransactionResult{$player = $transaction->getPlayer(); $itemClicked = $transaction->getItemClicked(); $itemClickedWith = $transaction->getItemClickedWith(); $action = $transaction->getAction(); $txn = $transaction->getTransaction(); return$transaction->continue()});The listener below does not allow players to take out apples from the menu:
$menu->setListener(function(InvMenuTransaction$transaction) : InvMenuTransactionResult{if($transaction->getItemClicked()->getTypeId() === ItemTypeIds::APPLE){$player->sendMessage("You cannot take apples out of that inventory."); return$transaction->discard()} return$transaction->continue()});There are two methods you can use to prevent players from editing the menu. Either create a listener that discard()s the transaction, or use InvMenu::readonly().
$menu->setListener(function(InvMenuTransaction$transaction) : InvMenuTransactionResult{return$transaction->discard()}); $menu->setListener(InvMenu::readonly()); // equivalent shorthand of the above// you can also pass a callback in InvMenu::readonly()$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction$transaction) : void{// do something }));Alternatively, you may choose to write your own InventoryTransactionEvent listener that works on transactions on $menu->getInventory(). However, an InvMenu listener is enough to fulfil most tasks.
Few actions are not possible to invoke at the time a player is viewing an inventory, such as sending a form—a player cannot view a form while viewing an inventory. Close the menu and utilize InvMenuTransactionResult::then() callback to achieve this.
$menu->setListener(function(InvMenuTransaction$transaction) : InvMenuTransactionResult{$transaction->getPlayer()->removeCurrentWindow(); return$transaction->discard()->then(function(Player$player) : void{$player->sendForm(newForm())})}); // or if you are using InvMenu::readonly():$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction$transaction) : void{$transaction->getPlayer()->removeCurrentWindow(); $transaction->then(function(Player$player) : void{$player->sendForm(newForm())})}));Register an inventory close callback to run whenever a player closes the menu. An inventory close callback takes the following signature:
/** * @param Player $player the player that closed the menu * @param Inventory $inventory the inventory of the menu */Closure(Player $player, Inventory $inventory) : void;$menu->setInventoryCloseListener(function(Player$player, Inventory$inventory) : void{$player->sendMessage("You are no longer viewing the menu.")});Inventory close listener is fired during both—server-initiated requests (i.e., $player->removeCurrentWindow()) and when the player closes the inventory on their end.
Important
PocketMine does not register a dispenser block. As of PocketMine v5, the task of registering missing vanilla blocks is excessively laborious and hence beyond the scope of this guide. pmmp/RegisterBlocksDemoPM5 has a nice guide on how to achieve this. Still overwhelmed? I wrote a drag-n-drop example plugin that does all of it and registers a /dispenser command. With DevTools plugin installed, simply copy the code and paste it in a new "DispenserInvMenuPlugin.php" file in your server's plugin folder.
InvMenu does not provide a 9-slot dispenser inventory. But you can still achieve this by registering a dispenser InvMenuType. You'll need to specify inventory size, block actor identifier (tile identifier), and the window type (network property) for the creation of the graphic (block) and inventory parts.
publicconstTYPE_DISPENSER = "myplugin:dispenser"; protectedfunctiononEnable() : void{InvMenuHandler::getTypeRegistry()->register(self::TYPE_DISPENSER, InvMenuTypeBuilders::BLOCK_ACTOR_FIXED() ->setBlock(ExtraVanillaBlocks::DISPENSER()) ->setSize(9) ->setBlockActorId("Dispenser") ->setNetworkWindowType(WindowTypes::DISPENSER) ->build())}Sweet! Now you can create a dispenser menu using:
$menu = InvMenu::create(self::TYPE_DISPENSER);Applications, examples, tutorials and featured projects using InvMenu can be found on the InvMenu Wiki.