A library for convenient and easy creation of Minecraft plugins.
GUI
public class GuiTest extends AdvancedGui {
public GuiTest() {
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():
new GuiTest().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_gui
title: "<gold>My Shop"
size: 54 # must be a multiple of 9
on_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: DIAMOND
slot: 13 # single slot
display_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 slot
slots:
- '0-8' # range
- '45-53' # another range
- '27' # single inside a list// From a FileConfiguration:
FileConfiguration config = YamlConfiguration.loadConfiguration(file);
new ParsedGui(player, config, myPlugin).open(player);
// From a pre-parsed Gui record (more efficient for many players):
Gui gui = ...; // parsed once at startup
new ParsedGui(player, gui, myPlugin).open(player);Use setReplace() to inject values into display names, lore, and action lines at runtime.
Call it before open() — items are built on open.
ParsedGui gui = new ParsedGui(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.
ParsedGui gui = new ParsedGui(player, config, myPlugin);
gui.addClickHandler("my_item", event -> {
Player clicker = (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) -> {
ParsedGui gui = ctx.get(ParsedGui.class);
if (gui == null) return;
// do something, then refresh
gui.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_PANE
slot: 13
priority: 1
display_name: "<red>Not enough money"
view_requirements:
- "%vault_eco_balance% < 100" # shown when balance < 100
buy_unlocked:
material: EMERALD
slot: 13
priority: 2 # fallback — shown when locked item's requirement fails
display_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:
public class MyGui extends ParsedGui {
private final MyPlugin plugin;
public MyGui(Player viewer, FileConfiguration config, MyPlugin plugin) {
super(viewer, config, plugin);
this.plugin = plugin;
// your init here
}
@Override
public void buildItems(List<Item> items) {
if (plugin == null) { // guard: called from super() before our fields exist
super.buildItems(items);
return;
}
// your custom logic, then:
super.buildItems(items);
}
@Override
public void refresh() {
// update your replacements before items are rebuilt
setReplace("%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.yml
actions:
- "[plugina:spawn] text" # resolves plugina's spawn
- "[pluginb:spawn] text" # resolves pluginb's spawn — no conflict
- "[spawn] text" # resolves whichever was registered first@Override
public void onEnable() {
ActionRegistry.register("myplugin", "give_diamond", (ctx, text) -> {
Player player = ctx.getPlayer();
if (player == null || text == null) return;
int amount = Integer.parseInt(text);
player.getInventory().addItem(new ItemStack(Material.DIAMOND, amount));
player.sendMessage("You got " + amount + " diamonds!");
});
}
@Override
public void onDisable() {
ActionRegistry.unregisterAll("myplugin");
}Register in onEnable:
@Override
public void onEnable() {
ActionRegistry.register("myplugin", "give_diamond", new GiveDiamondAction());
}
@Override
public void onDisable() {
ActionRegistry.unregisterAll("myplugin");
}Implement Action:
public class GiveDiamondAction implements Action {
@Override
public void execute(@NotNull ActionContext ctx, @Nullable String text) {
Player player = ctx.getPlayer();
if (player == null || text == null) return;
// Parse text argument
int amount = Integer.parseInt(text);
player.getInventory().addItem(new ItemStack(Material.DIAMOND, amount));
player.sendMessage("You got " + amount + " diamonds!");
// Retrieve a custom object from context — null if not provided
Entity entity = 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 in
ActionContext ctx = ActionContext.of(player)
.with(entity) // store by entity.getClass()
.with(myGui); // store by myGui.getClass()
// Get objects out (inside a handler)
Entity entity = ctx.get(Entity.class); // null if not provided
Entity entity = 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>me.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 "me.jetby.libb:api:1.2"
}