Skip to content
Rossi Lorenzo edited this page Jun 12, 2017 · 2 revisions
  1. What is GuiApi
  2. Hotbars
  3. Guis
  4. Error reporting
  5. More

What is GuiApi


A lot of spigot developers develop their own API for having a simpler interaction with the User or, if they don't have one, they try to "run-away" from it writing complex and not-really-user-friendly commands that are already hard to learn for admins, they couldn't be used with something like a minigame. This API provides the tools to develop powerful Hotbars and GUIs with simple yet concise code.

Hotbars


I want to explain the hotbars first because they are simpler but they still provide a general idea of the code style.

Hotbar Base

A Hotbar is a List of ItemStacks that is mapped to a list of Links (I'll explain this later). Practically every ItemStack (that is the object you see in the MC Hotbar) has a Link that represents the action to do once the player clicks. Aaand that's it. Here are some examples to see how this translates into code.

Hotbar hotbar = SimpleHotbar.of(HotbarLink.newLink(Link.consoleCommand("kick <player>"), GuiUtils.wool(DyeColor.BLUE, ChatColor.BLUE + "Kick")));

This creates an Hotbar with a single item that is displayed as a Blue wool with "Kick" as the name.

  • SimpleHotbar is the default implementation of the Hotbar, you're higly suggested to use this.
  • HotbarLink is a class that has both a Link and an ItemStack for the displayed item
  • Link is a functional interface that is (simply explained) void run(Player player), so it takes a Player as the input and gets nothing as output.
  • the Link class has already some implementation like Link.command and Link.consoleCommand. Both generate an Action that runs the command specified in the argument (replacing "" with the player name), the difference between command and consoleCommand is that consoleCommand executes the command with the ConsoleSender as the sender while command uses the player as the sender.
  • the GuiUtils class contains a bunch of methods that could help you creating your ItemStack faster

Notes: SimpleHotbar.of() takes an array of HotbarLinks, HotbarLink.newLink (that should be static-imported when possible) takes the Link before the ItemStack (later you'll se why)

Using Lambdas as Links

newLink(p -> p.sendMessage("pong"), wool(DyeColor.ORANGE, ChatColor.GOLD +"Ping"))

Note: newLink and wool are static-imported

Opening Hotbars

Ok, now you now how to create an Hotbar, but it's quite useless if you don't ever add it to a Player, right?

To do this we use the HotbarManager! particularly the methods add and set.

HotbarManager.add(player, hotbar);

Note: the HotbarManager can have multiple hotbars used togheter (if multiple Hotbars are added from the same or even from multiple plugins). This is why there's a distinction between add and set. In particular add "appends" the passed Hotbar to the player while set overrides any previously set Hotbar with the one passed as argument. So never use HotbarManager.set unless you know what you're doing

Removing Hotbars

This isn't really that much supported, but for now you can do HotbarManager.remove(player) to remove every Hotbar that the player has.

Reprinting/Reloading Hotbars

This operation should NOT be used unless there are some visual bugs, in that case please don't try to fix it by yourself but report it (with the appropriate description) at the Issue Tracker

Anyway the Hotbar reprinting can be achieved by writing HotbarManager.reprint(player)

Getting a player's links

There are a lot of ways to interact with a Player's links (intended as visual HotbarLinks, the ItemStack viewed in the Hotbar). The main way to do it is by HotbarManager.getLinks(player) that returns a Stream of ItemStacks. There are also some helper methods like isItemSimilarToLink, isItemLink, anyItemLink, isInventorySlotLink and hasLinkInHand (they are being used from the plugin internal listener)

Doing more Hotbar operations on the same player (Optimized)

If you want to do more Hotbar operations on the same player you don't want to use the HotbarManager more times because that would lookup the player more times. The suggested way is to use HotbarManager.get(player) to get the HotbarData of the player and then do the same methods you would use with the HotbarManager in the HotbarData (removing the player argument)

Guis


A GUI is something like a Window in an OS. The only thing different is that a GUI shouldn't change, instead she should open other GUIs. In the GuiManager the GUIs are organized with a stack-like system that helps the developer to make easy Game-like player-system interaction.

Gui base

a GUI has 4 overridable methods: onClick, onOpen, onClose and print. note that the first 3 methods are all listeners while the last one isn't. it's used to open the GUI to the player. This class is used to give maximum operability to every GUI system (this API shoul even implement Anvil input and Book output GUIs).

BaseGui

In most cases you want to use something simpler and more optimized. even if it isn't as flexible. The BaseGui class is an Abstract implementation of the Gui interface, it opens an Inventory to a player. the Gui stores the printed Inventory so it doesn't have to reprint it every time.

The overridable methods are: onOpen, onClose, render and needsUpdate. onOpen and onClose are the same of the Gui interface, render is a little different, it has no player as input and it must provide a (non-null) Inventory as output (that is the printed Gui), needsUpdate isn't really used but it could be, in some cases, it returns true only if the buffer (that contains the last print) should be reprinted. By default it always returns true only if the buffer is null.

There are other methods you could call like the clear() method that clears the buffer (consequentially) reprints the Gui on the next player print.

Example:

class RainbowGui extends BaseGui {
    @Override
    public void onClick(InventoryClickEvent event) {
        //This should process the click
        if(event.getCurrentItem().getType() != Material.AIR)
            event.getWhoClicked().sendMessage("Clicked: " + DyeColor.getByWoolData(event.getCurrentItem().getData().getData()));
    }

    @Override
    protected Inventory render() {
        DyeColor[] colors = DyeColor.values();
        Inventory inv = Bukkit.createInventory(null, GuiSize.min(colors.length));
        for(int i = 0; i < colors.length; i++)
            inv.setItem(i, wool(colors[i], colors[i].name().toLowerCase()));
        return inv;
    }
}

Note: the inventory returned can even be a non-chest inventory:

class DispenserGui extends BaseGui {
    @Override
    public void onClick(InventoryClickEvent event) {}

    @Override
    protected Inventory render() {
        Inventory inv = Bukkit.createInventory(null, InventoryType.DISPENSER);
        for(int i = 0; i < 9; i++)
            inv.setItem(i, new ItemStack(Material.DIAMOND, 64));
        return inv;
    }
}

Gui operations

Once you have your beautiful Gui instance you want to open it to someone. The Gui management is a little bit more complex that the Hotbar system because of the stack-like system. There are 4 operations that change the stack:

  • Open appends the Gui on top of the stack (clearing the stack before, if needed)
  • Close clears the stack
  • Back removes the last Gui from the stack, opening the Gui before or closing the inventory (if the stack is empty)
  • Change back + open, changes the last Gui with the one passed as argument (the last gui is removed ony if the stack is not empty)

It's suggested not to chain the operations, those methods are optimized in reducing the player's inventory changes, removing, when possible, the flickering.

Note: by default the open method does not clear the stack.

FolderGUI

An already implemented Gui is the FolderGui. This provides a Gui that contains a list of Items and Links (like an hotbar) that players can click. It's better to explain with an example:

gui = new FolderGui("Tools")//Gui title
    .addLink(GuiAction.close().and(Link.consoleCommand("kick <player>")), wool(DyeColor.YELLOW, "Kick"))
    .addLink(close().and(Link.consoleCommand("say <player> is STUPID!")), wool(DyeColor.GREEN, "Poke"))
    .addLink(change(new RainbowGui()), wool(DyeColor.LIGHT_BLUE, "Rainbow :)")), //Example of the stack-like Gui system

So, method explanation:

  • addLink: adds to the FolderGui a link with the respective ItemStack
  • GuiAction.operation(): Tells the system to do the operation, for example GuiAction.change(newgui) creates a Link that once executed changes the gui opened by the player into the one passed as argument
  • link.add(otherlink): link merging, it could aslo be done with Link.add(link, otherlink), it respects the given order. In the example close().and(consoleCommand(...))) it closes the Gui and, after, executes a command

The FolderGui has other things that could be customized: the title and the backButton. the backButton is the button used by the player to go back without selecting anything. it can be removed setting it to null or it can be customized setting it to any other ItemStack

ConfirmGui

The ConfrimGui is an already implemented Gui that asks the user for confirmation. It has a lot of customizable parameters that we could change like:

  • onConfirm link executed on player confirmation (default: GuiAction.close())
  • onDecline link executed on player declination (defaukt: GuiAction.close())
  • onClose link whenever the player closes the inventory (default: Link.EMPTY or nothing)
  • acceptItem item used as the accept button (default: Green wool)
  • declineItem item used as the decline button (default: Red wool)
  • title the title of the Gui (default: "Confirm")
  • descriptionItem item printed on top of the gui that the user can hover on to see the confirmation details (defailt: null)

Example:

new ConfirmGui()
    .title("Sure to ban?")
    .descriptionItem(wool(DyeColor.RED, "Ban")) //Optional, but suggesed
    .onConfirm(Link.consoleCommand("ban <player>"))

Anvils

Anvils are also supported (using spigot-anvil-gui), to create them you only need to use the AnvilInputGui class. There are sme parameters that you can change in the gui, those are:

  • message: the message initially displayed as the item's name
  • listener: the listener of that input, it takes two inputs (player and message) and has one output that is the error message (null or "" if no error is found) The listener can be astracted using filters from InputFilter, the filters are InputFilters.plain(BiConsumer<Player, String>) if any answer is ok and InputFilters.filter(BiConsumer<Player, >)` for any other filter. let's see a simple example:
new AnvilInputGui()
    .message("Put your age")
    .listener(InputFilters.filterInt((player, age) -> {
        GuiManager.close(player);
        player.sendMessage(age >= 18 ? "You can watch this" : "Go away!");
    }))

NOTE: Remember to put the GuiManager.close or the anvil won't close

More examples can be found in the example file

Books

This API includes tools to create interactive books (using spigot-book-api) while not providing any higher-level Helper for the utils, books are supported.

NOTE: Books are NOT Guis

Thats because of some protocol limitations (server doesn't know when a book is closed). So there isn't any BookGui but there's the original BookUtil class. When a book is opened to a player all the player's history (Gui stack) is cleared.

Here's a brief BookUtil's example:

new BookUtil.PageBuilder()
    .add(
        BookUtil.TextBuilder.of("Here's the drug")
            .color(ChatColor.DARK_GREEN)
            .style(ChatColor.BOLD)
            .onHover(BookUtil.HoverAction.showText("Take the drugs!"))
            .onClick(BookUtil.ClickAction.openUrl("https://www.google.it/search?q=drugs"))
            .build()
    )
    .newLine()
    .add("Now run! the ")
    .add(
        BookUtil.TextBuilder.of("POLICE")
            .color(ChatColor.BLUE)
            .onHover(BookUtil.HoverAction.showText("RUUUN"))
            .onClick(BookUtil.ClickAction.runCommand("The police caught me :("))
            .build()
    )
    .add(" is following us")
    .build()

If you want a more in-depth explanation you can visit the BookApi's wiki, if you want more examples you can use both the BookApi's examples or the GuiApi's examples

Error reporting


If you see a bug you should do the following operations: 1 check if you have the latest version (check on the spigot page) 2 gather all the possible information about the bug, possible cause, enviromnent.... 3 create an issue in the issue tracker with all the possible information in it

More


If you want to find an example with all the things in this tutorial you can use the official test that we are using to test all the features

The code is open-source and it isn't all that big so, explore it!