A library for convenient and easy creation of Minecraft plugins.
GUI
publicclassGuiTestextendsAdvancedGui {
publicGuiTest() {
super("Gui title");
setItem("example", ItemWrapper.builder(Material.STONE)
.slots(1, 5, 7)
.displayName(Component.text("This is the name dude"))
.onClick(event -> {
event.setCancelled(true);
player.sendMessage("Clicked on slot: " + event.getSlot());
})
.build());
}
}To open a GUI for a player, call open():
newGuiTest().open(player);ParsedGui — Config-driven GUIs
ParsedGui lets you define an entire inventory GUI in a YAML config file — items, slots, click actions, open/close
hooks, and placeholders — with no boilerplate code.
id: my_guititle: "<gold>My Shop"size: 54# must be a multiple of 9on_open: # optional — action list to run when GUI opens
- "[sound] UI_BUTTON_CLICK;1;1"on_close: # optional — action list to run when GUI closes
- "[message] <gray>Closed the shop."Items:
my_item:
material: DIAMONDslot: 13# single slotdisplay_name: "<aqua>Buy Diamond"lore:
- ""
- " <gray>Click to purchase"
- ""on_click:
any: # fires on every click type
- "[sound] UI_BUTTON_CLICK;1;1"
- "[player] buy diamond"left: # fires only on left click
- "[message] <green>Left clicked!"shift_left:
- "[message] <yellow>Shift+Left!"Supported click types:any, left, shift_left, right, shift_right, middle, drop, control_drop,
double
Slot formats:
slot: 13# single slotslots:
- '0-8'# range
- '45-53'# another range
- '27'# single inside a list// From a FileConfiguration:FileConfigurationconfig = YamlConfiguration.loadConfiguration(file);
newParsedGui(player, config, myPlugin).open(player);
// From a pre-parsed Gui record (more efficient for many players):Guigui = ...; // parsed once at startupnewParsedGui(player, gui, myPlugin).open(player);Use setReplace() to inject values into display names, lore, and action lines at runtime.
Call it beforeopen() — items are built on open.
ParsedGuigui = newParsedGui(player, config, myPlugin);
gui.setReplace("%price%", "500")
.setReplace("%item_name%", "Diamond Sword");
gui.open(player);In YAML:
display_name: "<white>Price: <green>$%price%"lore:
- " <gray>Item: <white>%item_name%"PlaceholderAPI placeholders (%papi_placeholder%) are applied automatically — no extra setup needed.
Register Java-side click logic for items by their YAML section key.
Runs in addition to whatever on_click is defined in YAML.
ParsedGuigui = newParsedGui(player, config, myPlugin);
gui.addClickHandler("my_item", event -> {
Playerclicker = (Player) event.getWhoClicked();
clicker.sendMessage("You clicked my_item!");
gui.refresh();
});
gui.open(player);When a player clicks an item, ParsedGui puts itself into the ActionContext automatically.
Inside a custom action you can retrieve it:
ActionRegistry.register("myplugin", "my_action", (ctx, text) -> {
ParsedGuigui = ctx.get(ParsedGui.class);
if (gui == null) return;
// do something, then refreshgui.refresh();
});In config:
on_click:
any:
- "[myplugin:my_action]"Multiple items can target the same slot. The one with the lowest priority value whose view_requirements all pass
wins.
This is useful for conditional items — e.g. show a locked version until the player has enough money.
Items:
buy_locked:
material: RED_STAINED_GLASS_PANEslot: 13priority: 1display_name: "<red>Not enough money"view_requirements:
- "%vault_eco_balance% < 100"# shown when balance < 100buy_unlocked:
material: EMERALDslot: 13priority: 2# fallback — shown when locked item's requirement failsdisplay_name: "<green>Buy"view_requirements supports ==, !=, >=, <=, >, < with both numbers and strings.
PlaceholderAPI placeholders are resolved before comparison.
Call refresh() to clear and rebuild all items — re-evaluates view_requirements and re-applies all placeholders.
gui.refresh();Typically called inside a click handler after state changes:
gui.addClickHandler("toggle", event -> {
toggleSomething(player);
gui.refresh();
});You can subclass ParsedGui to add custom inventory slots, override rendering logic, etc.
⚠️ super(viewer, config, plugin)callsbuildItems()internally during construction — before your subclass fields are initialized. OverridebuildItems()with a null-check guard:
publicclassMyGuiextendsParsedGui {
privatefinalMyPluginplugin;
publicMyGui(Playerviewer, FileConfigurationconfig, MyPluginplugin) {
super(viewer, config, plugin);
this.plugin = plugin;
// your init here
}
@OverridepublicvoidbuildItems(List<Item> items) {
if (plugin == null) { // guard: called from super() before our fields existsuper.buildItems(items);
return;
}
// your custom logic, then:super.buildItems(items);
}
@Overridepublicvoidrefresh() {
// update your replacements before items are rebuiltsetReplace("%score%", String.valueOf(getScore()));
super.refresh();
}
}Actions
Actions are config-driven commands executed on a player. Each action is a string in the format [key] text.
| Key | Description |
|---|---|
[message] | Send a message to the player |
[broadcast_message] | Broadcast a message to all players |
[console] | Run a command from console |
[player] | Run a command as the player |
[effect] | Apply a potion effect |
[action_bar] | Send an action bar message |
[broadcast_action_bar] | Broadcast an action bar to all players |
[title] | Send a title to the player |
[broadcast_title] | Broadcast a title to all players |
[sound] | Play a sound for the player |
[broadcast_sound] | Play a sound for all players |
[open] | Open a GUI |
Simple run:
ActionExecute.run(ActionContext.of(player), "[message] Hello!");With extra objects in context:
ActionExecute.run(
ActionContext.of(player).with(entity),
"[myplugin:give_diamond] 64"
);Extra objects added via
.with()can be retrieved inside the handler usingctx.get(YourClass.class).
Custom actions are registered in onEnable and unregistered in onDisable.
Actions from different plugins can share the same key without conflict — the full key is namespace:command:
# config.ymlactions:
- "[plugina:spawn] text"# resolves plugina's spawn
- "[pluginb:spawn] text"# resolves pluginb's spawn — no conflict
- "[spawn] text"# resolves whichever was registered first@OverridepublicvoidonEnable() {
ActionRegistry.register("myplugin", "give_diamond", (ctx, text) -> {
Playerplayer = ctx.getPlayer();
if (player == null || text == null) return;
intamount = Integer.parseInt(text);
player.getInventory().addItem(newItemStack(Material.DIAMOND, amount));
player.sendMessage("You got " + amount + " diamonds!");
});
}
@OverridepublicvoidonDisable() {
ActionRegistry.unregisterAll("myplugin");
}Register in onEnable:
@OverridepublicvoidonEnable() {
ActionRegistry.register("myplugin", "give_diamond", newGiveDiamondAction());
}
@OverridepublicvoidonDisable() {
ActionRegistry.unregisterAll("myplugin");
}Implement Action:
publicclassGiveDiamondActionimplementsAction {
@Overridepublicvoidexecute(@NotNullActionContextctx, @NullableStringtext) {
Playerplayer = ctx.getPlayer();
if (player == null || text == null) return;
// Parse text argumentintamount = Integer.parseInt(text);
player.getInventory().addItem(newItemStack(Material.DIAMOND, amount));
player.sendMessage("You got " + amount + " diamonds!");
// Retrieve a custom object from context — null if not providedEntityentity = ctx.get(Entity.class);
if (entity == null) return;
entity.teleport(player.getLocation());
ActionExecute.run(
ActionContext.of(player),
"[message] <red>Entity has been teleported to you"
);
}
}ActionContext is a type-safe container for objects passed into an action. Objects are stored and retrieved by class —
no string keys needed.
// Put objects inActionContextctx = ActionContext.of(player)
.with(entity) // store by entity.getClass()
.with(myGui); // store by myGui.getClass()// Get objects out (inside a handler)Entityentity = ctx.get(Entity.class); // null if not providedEntityentity = ctx.require(Entity.class); // throws if not providedIf you want to store an object under an interface rather than its concrete class:
ctx.with(MyInterface.class, myObject);
// retrieve as:ctx.get(MyInterface.class);<repository>
<id>JetbyMC</id>
<url>https://api.jetby.org/</url>
</repository><dependency>
<groupId>org.jetby.libb</groupId>
<artifactId>api</artifactId>
<version>1.2</version>
<scope>provided</scope>
</dependency>repositories {
maven {
url "https://api.jetby.org/"
name "JetbyMC"
}
}dependencies {
compileOnly "org.jetby.libb:api:1.2"
}