Skip to content

Implement await functions #250

@Muqsit

Description

@Muqsit

For those new to the topic, await-generator allows you to write asynchronous navigation flows sequentially in PHP. Think of yield from as a replacement for await keyword in Javascript, Python, and friends.

Menus are reusable (unlike forms), but switching between different menu navigation flows is laborious as you need to track state across different menu listeners which typically means sharing or explicitly persisting state variables across different contexts. InvMenu::send() can be readily wrapped in a Promise (as its callback is guaranteed to execute).

Menu transactions are complex - once you await a transaction, you have lost liberty over its outcome (continue/discard) as the response must be immediate. Awaiting a transaction is ideal when a menu is intended to be read-only.

// proposed solution
$menu = InvMenu::create(InvMenu::TYPE_CHEST);
while(true){
	$players = array_values($player->getServer()->getOnlinePlayers());
	$contents = array_map(fn($p) => VanillaItems::TOTEM(), $players);
	$menu->setContents($contents);

	try{
		$transaction = yield from $menu->nextTransaction($player);
	}catch(AwaitInvMenuException $e){
		break;
	}

	$select = $players[$transaction->getAction()->getSlot()];
	$player->sendToast($select->getName(), "You selected this player.");
}

Reusable menu in which each totem represents an online player.

nextTransaction can take the task of sending the menu and capturing a transaction, but a separate sendAsync($player) and waitClose($player) are still helpful for more complex functions like terminating an animation early.

// helper function
function sleep($ticks) : Generator{
	$scheduler = $plugin->getScheduler();
	yield from Await::promise(fn($resolve) => $scheduler->scheduleDelayedTask(new ClosureTask($resolve), $ticks));
}

// simple countdown animation
$menu = InvMenu::create(InvMenu::TYPE_CHEST);
yield from $menu->sendAsync($player);
for($i = 10; $i > 0; $i--){
	$menu->setItem(22, VanillaItems::ARROW()->setCount($i));
	[$k, $v] = yield from Await::race([sleep(20), $menu->waitClose($player)]);
	if($k === 1) break
}

Simple countdown animation that reduces item count for 10 seconds. Loop terminates if player closes menu before countdown completes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions