Skip to content

Examples InvMenu v4

Muqsit Rayyan edited this page Dec 29, 2021 · 12 revisions

Index

Do NOT skip reading the precautions!

Precautions

  • Register the InvMenuHandler before you create InvMenu 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();
    	$player->removeCurrentWindow();
    	$transaction->then(function(Player $player) : void{
    		$player->sendForm(new class() implements Form{});
    	});
    }));
    $menu->getInventory()->addItem(VanillaItems::APPLE());
    $menu->send($player);
    
    // for non-readonly menus
    $menu = InvMenu::create(InvMenu::TYPE_CHEST);
    $menu->setListener(function(InvMenuTransaction $transaction) : InvMenuTransactionResult{
    	$player = $transaction->getPlayer();
    	$player->removeCurrentWindow();
    	return $transaction->discard()->then(function(Player $player) : void{
    		$player->sendForm(new class() implements Form{});
    	});
    });
    $menu->getInventory()->addItem(VanillaItems::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
    	}
    });

Examples

Simple Hello World GUI

$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->getItemClicked(); // or $transaction->getOut();

	if($itemTakenOut->getId() === VanillaItems::DIAMOND()->getId()){
		$player->removeCurrentWindow();
		// on PocketMine-MP API 3, use:
		//     $player->removeWindow($transaction->getAction()->getInventory());
		$player->sendMessage("Hello, world!");
	}
}));
$menu->getInventory()->addItem(VanillaItems::DIAMOND());

/** @var Player $player */
$menu->send($player);

Server-selector GUI

$menu = InvMenu::create(InvMenu::TYPE_CHEST);
$menu->setName("Server Selector");
$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) : void{
	$player = $transaction->getPlayer();
	$itemClicked = $transaction->getItemClicked();

	$server_tag = $itemClicked->getNamedTag()->getCompoundTag("server");
	if($server_tag === null){
		return;
	}

	$ip = $server_tag->getString("address");
	$port = $server_tag->getInt("port");
	$player->transfer($ip, $port);
}));

$item = VanillaItems::APPLE();
$item->setNamedTag(CompoundTag::create()
	->setTag("server", CompoundTag::create()
		->setString("ip", "play.serverip.com")
		->setInt("port", 19132)
	));

$inventory = $menu->getInventory();
$inventory->addItem($item);

/** @var Player $player */
$menu->send($player);

Close an inventory when player clicks an item!

$menu = InvMenu::create(InvMenu::TYPE_CHEST);
$menu->setName("Don't steal!");
$menu->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) : void{
	if(!$transaction->getOut()->isNull()){
		$transaction->getPlayer()->removeCurrentWindow();
		// for PocketMine-MP API 3, use:
		//     $transaction->getPlayer()->removeWindow($transaction->getAction()->getInventory());
	}
}));

$menu->getInventory()->addItem(VanillaItems::APPLE());

/** @var Player $player */
$menu->send($player);

Recursive GUI inventories!

$menu1 = InvMenu::create(InvMenu::TYPE_CHEST);
$menu1->setName("Menu 1");
$menu2 = InvMenu::create(InvMenu::TYPE_CHEST);
$menu2->setName("Menu 2");

$menu1->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) use($menu2) : void{
	$menu2->send($transaction->getPlayer());
}));
$menu2->setListener(InvMenu::readonly(function(DeterministicInvMenuTransaction $transaction) use($menu1) : void{
	$menu1->send($transaction->getPlayer());
}));

$menu1->getInventory()->addItem(VanillaItems::APPLE());
$menu2->getInventory()->addItem(VanillaItems::STEAK());

/** @var Player $player */
$menu1->send($player);

Trash Can

$menu = InvMenu::create(InvMenu::TYPE_CHEST);
$menu->setName("Trash Can - Place Trash Here!");

$menu->setInventoryCloseListener(function(Player $player, Inventory $inventory) : void{
	$items = 0;
	foreach($inventory->getContents() as $item){
		$items += $item->getCount();
	}
	$inventory->clearAll();
	$player->sendMessage("Disposed {$items} item(s)!");
});

/** @var Player $player */
$menu->send($player);