muqsit/invmenu

A PocketMine-MP virion to create and manage virtual inventories!

4.7.0 2025-06-28 16:24 UTC

This package is auto-updated.

Last update: 2025-06-28 19:27:29 UTC


README

Create and manage virtual inventories in PocketMine-MP.

Installation and setup

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:
protected function onEnable() : void{
	if(!InvMenuHandler::isRegistered()){
		InvMenuHandler::register($this);
	}
}

Create a virtual inventory

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 inventory
  • InvMenu::TYPE_DOUBLE_CHEST - a 54-slot double chest inventory
  • InvMenu::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.

Set a custom name

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 B

Verify whether a menu is sent successfully

InvMenu::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
	}
});

Monitor movement of items

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 the Player that triggered the transaction.
  • InvMenuTransaction::getItemClicked() returns the Item the player clicked in the menu. You may also use InvMenuTransaction::getOut().
  • InvMenuTransaction::getItemClickedWith() returns the Item the player had in their hand when clicking an item. You may also use InvMenuTransaction::getIn().
  • InvMenuTransaction::getAction() returns SlotChangeAction - you can get the slot that the player clicked in the menu.
  • InvMenuTransaction::getTransaction() returns the complete InventoryTransaction holding 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.

Execute a task post-transaction

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

// 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(new Form());
	});
}));

Monitor menu close events

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.

Advanced usage: Register a custom InvMenuType

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.

public const TYPE_DISPENSER = "myplugin:dispenser";

protected function onEnable() : 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);

InvMenu Wiki

Applications, examples, tutorials and featured projects using InvMenu can be found on the InvMenu Wiki.