-
Notifications
You must be signed in to change notification settings - Fork 74
InvMenu v4.0 API Documentation
InvMenu is a pmmp/PocketMine-MP virion that provides a simple API to create portable ("fake") inventories and handle inventory transactions happening within.
If you are new to virions, refer to Using InvMenu in a plugin to learn how to setup virions to work with plugins.
Before a plugin is able to create InvMenus and handle transactions, InvMenuHandler
must be registered during plugin enable / onEnable.
final class MyPlugin extends PluginBase{
protected function onEnable() : void{
+ if(!InvMenuHandler::isRegistered()){
+ InvMenuHandler::register($this);
+ }
}
}
InvMenu comes with the following pre-registered InvMenu types out of the box: InvMenu::TYPE_CHEST
, InvMenu::TYPE_DOUBLE_CHEST
and InvMenu::TYPE_HOPPER
.
Registering an InvMenu type of inventory that hasn't already been pre-registered requires gathering some information about the MCPE protocol. Most of this information can be found in PocketMine's BedrockProtocol library.
InvMenuType objects process how inventories and GUIs are displayed to the client. These objects are mapped to a unique string identifier which can be passed to InvMenu::create()
to create an InvMenu instance of a given InvMenuType. InvMenu maps InvMenu::TYPE_CHEST
("invmenu::chest"
) to an InvMenuType object that processes how a 27-slot chest inventory is displayed to the client.
As the process of creating an InvMenuType object from scratch is rather complex, there are a few helper methods for assistance. InvMenuTypeRegistry class has a few examples on how these methods are used. To register a dispenser menu type for example, execute this during plugin enable / onEnable:
// class MyPluginMainClass extends PluginBase {
public const TYPE_DISPENSER = "my_dispenser_menu";
protected function onEnable() : void{
if(!InvMenuHandler::isRegistered()){
InvMenuHandler::register($this);
}
InvMenuHandler::getTypeRegistry()->register(self::TYPE_DISPENSER /* identifier */, InvMenuTypeBuilders::BLOCK_ACTOR_FIXED()
->setBlock(VanillaBlocks::DISPENSER()) // block type
->setSize(9) // number of slots
->setBlockActorId("Dispenser") // MCPE tile entity identifier
->setNetworkWindowType(WindowTypes::DISPENSER) // MCPE window type id
->build());
}
// }
Once registered, a dispenser menu can be created using InvMenu::create(MyPluginMainClass::TYPE_DISPENSER)
(or InvMenu::create("my_dispenser_menu")
).
An InvMenu
is an object that holds an inventory along with information about it, such as the name of the menu and transaction handlers (if any). To create an InvMenu, run InvMenu::create
specifying a registered menu identifier.
For example, to create an InvMenu of type InvMenu::TYPE_CHEST
:
$menu = InvMenu::create(InvMenu::TYPE_CHEST);
By default, an InvMenu is created with vanilla inventory title (the title of a chest inventory for example would be "Chest").
To set a custom menu name, use InvMenu::setName()
.
$menu->setName("Custom Title");
To send an InvMenu instance to a player, use InvMenu::send()
.
$menu->send($player);
An InvMenu instance can be sent to multiple players.
$menu->send($player1);
$menu->send($player2);
As an InvMenu holds just one inventory, players that are sent the same InvMenu instance will be viewing the same inventory.
A per-player custom name can be specified to InvMenus by passing the name parameter to InvMenu::send()
.
$menu->send($player, "Hello " . $player->getName());
When no per-player custom name is specified (or if null
is passed to the name parameter: $menu->setName($player, null)
), the name passed to InvMenu::setName()
is used instead. If no name was specified for the menu, the menu's vanilla inventory title is used instead.
InvMenu instances are sent asynchronously to players due to game limitations. This also means InvMenu::send()
isn't guaranteed to send the inventory to the player. To verify whether an inventory was sent to a player, specify a callback parameter to InvMenu::send()
.
$menu->send($player, $name, function(bool $success) : void{
if($success){
// successfully sent to player
}else{
// failed to send to player
}
});
An InvMenu::send
request can fail due to reasons such as:
- Another
InvMenu::send
request (cancels the previousInvMenu::send
request if there's one queued).$menu1->send($player); $menu2->send($player); // $menu1 will fail to be sent to player
- Failure when setting a ghost block for the inventory, such as InvMenu attempting to set a chest block at Y>256 or Y<0.
- Player quitting the server before the request could be fulfilled.
By default, all inventory transactions happening within an InvMenu are unhandled, meaning a player could move items in and out of the inventory just like you could within a chest under vanilla circumstances. To limit inventory actions within an InvMenu, an InvMenu handler can be assigned to a given InvMenu.
An InvMenu handler is a Closure
with the signature:
Closure(InvMenuTransaction $transaction) : InvMenuTransactionResult;
InvMenuTransaction
holds information about the inventory transaction, such as the player initiating the transaction, the inventory instance being modified, item that is being placed in the inventory, and the item that is being removed from the inventory.
An InvMenuTransactionResult
has two roles:
- Limiting the transaction (by discarding or continuing the transaction).
- Providing a mechanism to run code after the transaction has been fully processed.
To disallow players from modifying contents of an inventory, $transaction->discard()
can be returned.
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
return $transaction->discard(); // revert the inventory transaction
});
To disallow players from taking out an apple from the inventory, but allow taking out any other item:
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
if($transaction->getOut()->getId() === ItemIds::APPLE){
return $transaction->discard(); // revert the inventory transaction
}else{
return $transaction->continue(); // allow the inventory transaction
}
});
To disallow players from placing items in the inventory:
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
if($transaction->getIn()->isNull()){
return $transaction->continue(); // allow the inventory transaction
}else{
return $transaction->discard(); // revert the inventory transaction
}
});
InvMenuTransactionResult::then()
can be used to take an action once the inventory transaction has completed. This is useful in cases where you'd want to open a Form after an inventory.
$menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
return $transaction->discard()->then(function(Player $player) : void{
$player->sendForm($someForm);
});
});
A short-hand InvMenu::readonly()
can be used to discard all transactions.
$menu->setListener(InvMenu::readonly()); // disallow modifying the inventory
To listen for transactions with InvMenu::readonly()
, a Closure
of the type Closure(DeterministicInvMenuTransaction $transaction) : InvMenuTransactionResult
can be supplied to InvMenu::readonly()
. As the listener returns void
instead of InvMenuTransactionResult
, DeterministicInvMenuTransaction::then()
can be used instead to perform post-transaction actions.
$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) : void{
// do anything
}));
An InvMenu close listener is a Closure
with the signature:
Closure(Player $player, Inventory $inventory) : void;
To set an inventory close listener to an InvMenu, use InvMenu::setInventoryCloseListener()
:
$menu->setInventoryCloseListener(function(Player $player, Inventory $inventory) : void{
echo $player->getName(), " closed the inventory!";
});
Example usage of InvMenu can be found in InvMenu v4.0 Examples
A list of open-source plugins that make use of InvMenu can be found at https://github.com/Muqsit/InvMenu/wiki/List-of-plugins-using-InvMenu
- As with any object holding a scoped closure in PHP, a reference to an object may be held for longer than expected. Generally, circular references as such are automatically collected much later (when
/gc
is executed (see it'sCycles
report) or whengc_collect_cycles()
is called). You may want to set anull
transaction handler (or anInvMenu::readonly()
transaction handler) and anull
inventory close listener when you are done handling the InvMenu to ensure circular references are not held once theInvMenu
instance is out of scope.// dispose an InvMenu $menu->setListener(InvMenu::readonly() /* or null */); $menu->setInventoryCloseListener(null);