GUIs β Menu Registry
MenuRegistry is to menus what CommandRegistry is to commands: register a menu once by key,
then open it by name from anywhere β a command, a click handler in another menu, a hook β without
constructing the class directly. It resolves the playerβs PlayerMenuContext, enforces the menuβs
@MenuMeta permission, and opens it.
Registering
Section titled βRegisteringβRegister menus from your menu module, alongside publishing the MenuManager:
public final class MenuModule extends AbstractModule {
@Override public void onEnable() { MenuManager manager = new MenuManager(); provide(MenuManager.class, manager);
MenuRegistry menus = new MenuRegistry(plugin, manager, service(MessageService.class)); provide(MenuRegistry.class, menus); menus.register("shop", ShopMenu::new); // MenuFactory = (plugin, context) -> Menu
service(ListenerRegistry.class).register(new MenuListener(plugin)); }}A MenuFactory is just (CorePlugin plugin, PlayerMenuContext context) -> Menu, so a constructor
reference like ShopMenu::new is usually all you need. Keys are case-insensitive.
Opening by key
Section titled βOpening by keyβMenuRegistry menus = service(MenuRegistry.class);
menus.open(player, "shop"); // returns false if the key is unknown or the player lacks permissionopen returns true when the menu opened, false when the key is unknown or the player fails
the menuβs @MenuMeta(permission = ...) (in which case a no-permission message is sent). This is
the same gating model as commands.
Seeding context data
Section titled βSeeding context dataβTo pass data into the menu (e.g. the item being edited), use the seeding overload β it runs against
the playerβs context before the menu is built, so decorate() can read it:
menus.open(player, "editor", context -> context.set("item", heldItem));// inside the menu's decorate():ItemStack item = context.get("item");Opening from a command
Section titled βOpening from a commandβCommands and menus connect cleanly through the registry:
@CommandMeta(name = "shop", permission = "core.shop", playerOnly = true)public final class ShopCommand extends CoreCommand {
private final MenuRegistry menus;
public ShopCommand(MenuRegistry menus) { this.menus = menus; }
@Override public void run(CommandContext context, Arguments args) { menus.open(context.player(), "shop"); }}Direct construction still works
Section titled βDirect construction still worksβThe registry is additive. You can always build and open a menu directly when you donβt need a key:
new ShopMenu(plugin, menus.context(player)).open();Note that direct open() does not check the menuβs permission β only MenuRegistry.open
enforces it. Gate the direct path yourself (or open through the registry) when permission matters.