ActionApi icon indicating copy to clipboard operation
ActionApi copied to clipboard

Generalized user interface framework for PocketMine plugins.

ActionApi

Generalized user interface framework for PocketMine plugins.

How does this work?

ActionApi introduces the following concepts:

Action

Action is an interface implemented by plugins to represent something that a user can do.

Each action type implements a new subclass of Action. A new instance of Action is created for every execution. An Action subclass holds the arguments required for the action.

Arg

Arg is an argument that the user provides to customize the action. They can be inferred from the Hook (action trigger), or separately requested from the user.

Implementations provided by ActionApi:

Type Command line support Form UI support Inference from Hook
StringArg Yes (single argument or space implosion) Yes (CustomForm Input) None
StringEnumArg Yes (single argument) Yes (CustomForm Dropdown, or CustomForm StepSlider, or MenuForm) None
StringEnumSetArg Yes (last argument only, or if delimiter is provided) Yes (CustomForm Toggles) None
IntArg Yes Yes (CustomForm Slider, or CustomForm Input) None
FloatArg Yes Yes (CustomForm Slider, or CustomForm Input) None
BoolArg Yes Yes (CustomForm Toggle, or ModalForm) None
GenericListArg Yes if element type supports (rearranged as last argument, variadic) Yes if element type supports (interactive loop) If element type is inferrable, singleton list
PlayerArg (online players only) Yes (by name prefix or full name) Yes (CustomForm Dropdown, or MenuForm) If hook is clicking on a player
PlayerSetArg (online players only) Yes (comma-separated) Yes (CustomForm Toggles) None
EntityArg No Yes (click on entity in world) If hook is clicking on an entity
BlockTypeArg Yes (by name, with variant colon-separated) Yes (interactive selector with search button) If hook is clicking on a block
ItemArg Yes (by name, with damage and count colon-separated) Yes (interactive selector with search button) If hook is clicking on air, a block or entity (default value is currently held item)
ItemTypeArg Yes (by name, with damage colon-separated) Yes (interactive selector with search button) If hook is clicking on air, a block or entity (default value is currently held item)
PositionArg Yes (by typing x y z world-name) Yes (CustomForm Dropdown to select current position, CustomForm Input, current crosshair target or next-click position) If hook is clicking on a block or entity (otherwise)
WorldArg Yes (world name) Yes (CustomForm Dropdown, or MenuForm) None (default value is player current world)
WorldSetArg Yes (last argument only) Yes (CustomForm Toggles) None
YawArg Yes (in degrees, not recommended) Yes (CustomForm Slider) Player current orientation
PitchArg Yes (in degrees, not recommended) Yes (CustomForm Slider) Player current orientation
DateTimeArg Yes (ISO 8601, or + + duration) Yes (CustomForm Sliders for YMDhmsZ) None
DurationArg Yes (single argument, e.g. 1w2d3h4m5s) Yes (interactive loop with CustomForm Input + Dropdown for unit) None

Inference from Hook may be suppressed as the default value instead of silently selected by an extra flag from the constructor call in the Action implementation.

Hook

Hook is the method that the user used to trigger the initiation of this action. It stores data related to the trigger.

Implementations provided by ActionApi:

  • command
  • block click
  • air click
  • entity click
  • item selection
  • hotbar switch
  • action menu (a navigation index for actions defined in config.yml)

Example Action implementation

final class AddWarpAction implements Action {
	private DataProvider $db;

	private ?string $name = null;
	private ?Position $pos = null;
	private ?bool $open = null;

	public function __construct(DataProvider $db) {
		$this->db = $db;
	}

	public function isMissingArg() : bool {
		return $this->name === null || $this->pos === null || $this->open === null;
	}

	public function getArguments() : iterable {
		yield StringArg::new("Warp name", $this->name);
		yield PositionArg::new("Warp position", $this->pos);
		yield BoolArg::new("Open to public?", $this->open)->default(true);
	}

	public function run(CommandSender $sender) : void {
		$this->db->addWarp($this->name, $this->pos->getX(), $this->pos->getY(), $this->pos->getZ(),
				$this->pos->getWorld()->getFolderName(), $this->open);
		$sender->sendMessage("Warp \"$this->name\" created at $this->pos");
	}
}

Implementing Arg and Hook

If the existing arguments are not sufficient, other plugins can implement additional Arg types. Plugins that want to trigger actions from other methods can also implement their own Hook types.

Each Arg supports command line and form UI interface. However, if an Arg additionally infers arguments from a Hook, the logic is implemented under a separate Inferrer interface. Plugins may register an Inferrer instance that supports a specific type of Arg and a specific type of Hook. An example implementation for inferring EntityArg from an EntityClickHook:

final class EntityClickItemInferrer implements Inferrer {
	public function infer(Hook $hook) {
		assert($hook instanceof EntityClickHook); // this is true if you registered the inferrer for the correct class
		return $hook->getClickedEntity();
	}
}