-
Notifications
You must be signed in to change notification settings - Fork 79
Examples InvMenu v4
Do NOT skip reading the precautions!
- Simple Hello World GUI
- Server-selector GUI
- Close an inventory when player clicks an item!
- Recursive GUI inventories!
- Trash Can
-
Register the
InvMenuHandler
before you createInvMenu
instances.class MyPluginMainFile extends PluginBase{ public function onEnable() : void{ if(!InvMenuHandler::isRegistered()){ InvMenuHandler::register($this); } } }
-
If you are sending a modal/form UI when a player clicks something in an inventory, remove the inventory and use the
then()
method. Or else, the player won't see the form (client-sided behaviour).$menu = InvMenu::create(InvMenu::TYPE_CHEST); $menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) : void{ $player = $transaction->getPlayer(); $action = $transaction->getAction(); $player->removeWindow($action->getInventory()); $transaction->then(function(Player $player) : void{ $player->sendForm(new class() implements Form{}); }); })); $menu->getInventory()->addItem(ItemFactory::get(ItemIds::APPLE)); $menu->send($player); // for non-readonly menus $menu = InvMenu::create(InvMenu::TYPE_CHEST); $menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{ $player = $transaction->getPlayer(); $action = $transaction->getAction(); $player->removeWindow($action->getInventory()); return $transaction->discard()->then(function(Player $player) : void{ $player->sendForm(new class() implements Form{}); }); }); $menu->getInventory()->addItem(ItemFactory::get(ItemIds::APPLE)); $menu->send($player);
-
InvMenu::send()
is asynchronous. Due to the behaviour of bedrock clients, this method doesn't send the inventory soon as it's called. Fake inventories consist of two main portions — the graphic (i.e., the block and the block entity) and the inventory window itself. The graphic is sent first, after which the inventory window is sent several ticks later. This method is also subjected to possible failure in situations such as:- A player logging out in-between receiving the graphic and the container portions
- Another call to
InvMenu::send()
before the previous one completed - A plugin cancelling
InventoryOpenEvent
So, to be careful, catch the failure by passing a third parameter to the method, which is of the type
Closure(bool $success) : void
./** * @var InvMenu $menu * @var Player $player * @var string|null $name */ $menu->send($player, $name, function(bool $success) : void{ if($success){ // menu sent successfully }else{ // menu failed to send } });
$menu = InvMenu::create(InvMenu::TYPE_CHEST); // use TYPE_HOPPER for hopper, TYPE_DOUBLE_CHEST for double chest
$menu->setName("Click the diamond!")
$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) : void{
$player = $transaction->getPlayer();
$itemTakenOut = $transaction->getClickedWith(); // or $transaction->getOut();
if($itemTakenOut->getId() === ItemIds::DIAMOND){
$player->removeWindow($action->getInventory());
$player->sendMessage("Hello, world!");
}
}));
$menu->getInventory()->addItem(ItemFactory::get(ItemIds::DIAMOND));
/** @var Player $player */
$menu->send($player);
class ServerSelectorGUI{
/** @var InvMenu */
private $menu;
public function __construct(string $name){
$this->menu = InvMenu::create(InvMenu::TYPE_CHEST)
->setName($name)
->setListener(InvMenu::readonly(Closure::fromCallable([$this, "onServerSelectorTransaction"])))//you can call class functions this way
->setInventoryCloseListener(function(Player $player) : void{
$player->sendMessage(TextFormat::GREEN . "You are being transferred...");
});
}
public function addServerToList(Item $item, string $address, int $port) : void{
$nbt = $item->getNamedTag();
$nbt->setString("Server", $address . ":" . $port);
$item->setNamedTag($nbt);
$this->menu->addItem($item);
}
public function onServerSelectorTransaction(DeterministicInvMenuTransaction $transaction) : void{
$player = $transaction->getPlayer();
$itemClickedOn = $transaction->getItemClicked();
$player->transfer(...explode(":", $itemClickedOn->getNamedTag()->getString("Server", "play.onthefallbackserv.er:19132")));
}
public function sendTo(Player $player) : void{
$this->menu->send($player);
}
}
$gui = new ServerSelectorGUI("Server Selector");
$gui->addServerToList(Item::get(Item::DIAMOND_PICKAXE), "play.onmyserverplea.se", 19132);
$gui->addServerToList(Item::get(Item::IRON), "play.onmyserverplea.se", 19133);
/** @var Player $player */
$gui->sendTo($player);
class Example{
/** @var InvMenu */
private $menu;
public function __construct(string $name){
$this->menu = InvMenu::create(InvMenu::TYPE_CHEST)
->setName($name)
->setListener(InvMenu::readonly(Closure::fromCallable([$this, "onTransaction"])));
$this->menu->getInventory()->addItem(Item::get(Item::CHEST)->setCustomName(TextFormat::BOLD . TextFormat::RED . 'CLICK ME!' . TextFormat::RESET);
}
public function onTransaction(DeterministicInvMenuTransaction $transaction) : void{
if(!$transaction->getOut()->isNull()){ // getOut() is same as getItemClickedWith()
$transaction->getPlayer()->removeWindow($transaction->getAction()->getInventory());
}
}
public function sendTo(Player $player) : void{
$this->menu->send($player);
}
}
$example = new Example("Take the chest out of me!");
/** @var Player */
$example->sendTo($player);
class Example{
/** @var InvMenu */
private $menu1;
/** @var InvMenu */
private $menu2;
public function __construct(string $menu1name, string $menu2name){
$this->menu1 = InvMenu::create(InvMenu::TYPE_CHEST)
->setName($menu1name)
->setListener(InvMenu::readonly(Closure::fromCallable([$this, "onUseFirstMenu"])));
$this->menu1->getInventory()->addItem(Item::get(Item::DIAMOND)->setCustomName(TextFormat::RED . "Click ME!"));
$this->menu2 = InvMenu::create(InvMenu::TYPE_CHEST)
->setName($menu2name)
->setListener(InvMenu::readonly(Closure::fromCallable([$this, "onUseSecondMenu"])));
$this->menu2->getInventory()->addItem(Item::get(Item::GOLD_INGOT)->setCustomName(TextFormat::RED . "Click ME!"));
}
public function onUseFirstMenu(DeterministicInvMenuTransaction $transaction) : void{
$this->menu2->send($transaction->getPlayer());
}
public function onUseSecondMenu(DeterministicInvMenuTransaction $transaction) : void{
$this->menu1->send($transaction->getPlayer());
}
public function sendTo(Player $player) : void{
$this->menu1->send($player);
}
}
$example = new Example("Menu #1", "Menu #2");
/** @var Player $player */
$example->sendTo($player);
class Trashcan{
private static function getTrashMenu() : InvMenu{
return InvMenu::create(InvMenu::TYPE_CHEST)
->setName("Trash Can")
->setInventoryCloseListener(Closure::fromCallable(Trashcan::class . "::dispose"));
}
public static function dispose(Player $player, Inventory $inventory) : void{
$items_count = 0;
foreach($inventory->getContents() as $item){
$items_count += $item->getCount();
}
if($items_count > 0){
$inventory->clearAll();
$player->sendMessage("You disposed " . $items_count . " items!");
}
}
public static function send(Player $player) : void{
self::getTrashMenu()->send($player);
}
}
/** @var Player $player */
Trashcan::send($player);