Skip to content

Advanced User Guide

Tobero edited this page Sep 17, 2023 · 12 revisions

Work in Progress

Advanced User Guide

In this user guide we'll be building a server hub gui with an admin page. The hub menu should teleport players to different minigames and let them view stats, while the admin page is supposed to make managing players easier.

Setup

The first thing I'll be doing is creating a folder where I'm storing my hub related guis. In this folder, I'll create my main gui, called main.gui

image

Writing the main gui

In my case, I want to have a gui with a header and a page for each minigame category.

Creating the layout

I've created a new file in the hub folder, called navigation-bar.gui. I've written the following content in it:

<gui title="Navigation Bar" width="9" height="1" implicit-update="false">
</gui>

This should look kinda familiar to you, except the implicit-update. The implicit update value determines if the gui should automatically refresh all component's attributes (Like name, material, type, etc) and check if there have been any external changes (They might have been caused due to compute functions, functions that compute values when rendering). Because this is a very costly process, I've disabled it for this gui, as I know for sure that I'm not going to have a content that updates while being viewed, I'll come back to it later.

Now that cleared, let's continue and add some items. I want my player's head to be in the center, showing some information about myself.

<gui title="Navigation Bar" width="9" height="1" implicit-update="false">
    <component type="item" x="0" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="1" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="2" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="3" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="4" name="§e{%player_name%}'s Profile">
        <head-owner>%viewer%</head-owner>
        <lore/>
        <lore>§7Language: §e{%player_locale_display_name%}</lore>
        <lore>§7Ping: §a{%player_ping%}</lore>
        <lore>§7IP: §e{%player_ip%}</lore>
    </component>
    <component type="item" x="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="6" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="7" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" material="BLACK_STAINED_GLASS_PANE"/>
</gui>

image

Okay, so that's content of the navigation-bar.gui. Let's go over it.

As you can see, there are 9 item component's I've added, 8 of them should already be understandable (I suppose you've already read the beginner guide), therefore I'll only take a look at the item positioned at the center.

Compute functions

As you can see, this item is utilizing papi with the Player extension (Run /papi ecloud download Player to use it). This allows us to get some information about the player, as this information isn't provided by gui engine (After all, it's only responsible for rendering guis). You can use any placeholders provided by placeholderapi by using {%your_placeholder%}.

So, you have probably spotted the small outliner: %viewer%. This is a placeholder provided by gui engine itself. It's a reference to the viewer's uuid. It's usually only needed for setting the head owner. But when're already here - What's the difference between %viewer% and {%player_ping%} for example. Basically, the one in {} isn't actually a placeholder. It's a computable function. Computable functions are a powerful way to make your guis more interactive. You can use them to check for permissions, receive values from components or just use them to get papi placeholders (Read more about them here). And because we're already taking about compute functions, let's also talk about the implicit-update property defined above.

Implicit update

So, the implicit update is responsible for updating all compute functions when the gui gets rerendered. This process doesn't take much time itself for a single component, but the issue is, just the navigation bar already uses 4 compute functions. All these for functions will have to be reparsed and rerendered everytime the gui schedules a new render. This is useless in this case, because the values from papi won't change anyways (except the ping), therefore I've set it to false. But be aware. When you notice your guis slow down the server, implicit update is enabled by default.

Making the main menu

Let's start by defining the main.gui:

<gui title="Hub" width="9" height="6" implicit-update="false">
</gui>

As you can see, I'm going to disable the implicit-update again.

Now the next thing I would like to do is somehow add the previously designed navigation bar.

Embedding a gui

For this exact usecase, there is a component type, called the embedded gui. As the name suggests, it integrates a gui into another one - Exactly what we need.

<gui title="Hub" width="9" height="6" implicit-update="false">
    <component type="embedded" target-gui="hub/navigation-bar" width="9" height="1"/>
</gui>

image

Okay, so how does it work? As you can see, the embedded component requires you to set a target-gui. This is basically the name and location you gave it. Please note, this only works for guis within your api scope. In my case, the gui I wanted is called navigation-bar and it's located in the hub folder - Therefore the target-gui must be hub/navigation-bar (It may help to take a look at the guiengine open command arguments, as they're the same as this one).

Now, the component also needs to know the width an height the virtualized gui should have. This can be any size - Smaller or bigger than the target gui. In my case, I just set it to be the same, I want to show everything, not just a small portion of it.

Making the minigame category selector

Okay, so let's move on and work on the minigame selector.

<gui title="Hub" width="9" height="6" implicit-update="false">
    <component type="embedded" target-gui="hub/navigation-bar" width="9" height="1"/>

    <component type="paged" x="1" y="1" width="7" height="4"></component>

    <!-- Frame around paged -->
    <component type="item" x="0" y="1" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="2" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="3" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="4" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="5" material="BLACK_STAINED_GLASS_PANE"/>

    <component type="item" x="8" y="1" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="2" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="3" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="4" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="5" material="BLACK_STAINED_GLASS_PANE"/>

    <component type="item" x="1" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="2" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="3" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="4" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="5" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="6" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="7" y="5" material="BLACK_STAINED_GLASS_PANE"/>
</gui>

image

Okay, this might seem overwhelming at first, but most of the components are just making the frame around the paged component. But what is the paged component?

The paged component is currently not doing much. It's just sitting here with no content to show. But effectively, it's like the embedded component. It needs a width and a height. Then, you can create pages, each page representing it's own gui. The pages then can be changes, but let's start with creating them first.

<gui title="Hub" width="9" height="6" implicit-update="false">
    <component type="embedded" target-gui="hub/navigation-bar" width="9" height="1"/>

    <component type="paged" x="1" y="1" width="7" height="4">
        <page position="0">
            <component type="item" x="1" y="1" name="§eBedwars 1vs1" material="RED_BED"/>
            <component type="item" x="3" y="1" name="§eBedwars 2vs2" material="WHITE_BED"/>
            <component type="item" x="5" y="1" name="§eBedwars 4vs4" material="BLUE_BED"/>

            <component type="item" x="3" y="3" name="§eRandom" material="ENDER_PEARL"/>
        </page>
        <page position="1">
            <component type="item" x="1" y="1" name="§eSkywars 1vs1" material="RED_WOOL"/>
            <component type="item" x="3" y="1" name="§eSkywars 2vs2" material="WHITE_WOOL"/>
            <component type="item" x="5" y="1" name="§eSkywars 4vs4" material="BLUE_WOOL"/>

            <component type="item" x="3" y="3" name="§eRandom" material="ENDER_PEARL"/>
        </page>
        <page position="2">
            <component type="item" x="1" y="1" name="§eBuilder Battle 1vs1" material="RED_CONCRETE"/>
            <component type="item" x="3" y="1" name="§eBuilder Battle 2vs2" material="WHITE_CONCRETE"/>
            <component type="item" x="5" y="1" name="§eBuilder Battle 4vs4" material="BLUE_CONCRETE"/>

            <component type="item" x="3" y="3" name="§eRandom" material="ENDER_PEARL"/>
        </page>
    </component>

    <!-- Frame around paged -->
    <component type="item" x="0" y="1" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="2" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="3" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="4" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="5" material="BLACK_STAINED_GLASS_PANE"/>

    <component type="item" x="8" y="1" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="2" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="3" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="4" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="5" material="BLACK_STAINED_GLASS_PANE"/>

    <component type="item" x="1" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="2" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="3" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="4" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="5" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="6" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="7" y="5" material="BLACK_STAINED_GLASS_PANE"/>
</gui>

image

Okay, as you can see, I've now created three pages. When you now open the gui, you'll only see a single page, no way to change it. For this, there are two actions: :next and :previous. For them to exist, you must give the component a id.

<gui title="Hub" width="9" height="6" implicit-update="false">
    <component type="embedded" target-gui="hub/navigation-bar" width="9" height="1"/>
    <component type="item" x="0" y="0" name="§cBack" material="ARROW" priority="LOW">
        <on-click type="action">[container:previous]</on-click>
    </component>
    <component type="item" x="8" y="0" name="§eNext" material="ARROW" priority="LOW">
        <on-click type="action">[container:next]</on-click>
    </component>
    <component type="paged" id="container" x="1" y="1" width="7" height="4">
        <page position="0">
            <component type="item" x="1" y="1" name="§eBedwars 1vs1" material="RED_BED"/>
            <component type="item" x="3" y="1" name="§eBedwars 2vs2" material="WHITE_BED"/>
            <component type="item" x="5" y="1" name="§eBedwars 4vs4" material="BLUE_BED"/>

            <component type="item" x="3" y="3" name="§eRandom" material="ENDER_PEARL"/>
        </page>
        <page position="1">
            <component type="item" x="1" y="1" name="§eSkywars 1vs1" material="RED_WOOL"/>
            <component type="item" x="3" y="1" name="§eSkywars 2vs2" material="WHITE_WOOL"/>
            <component type="item" x="5" y="1" name="§eSkywars 4vs4" material="BLUE_WOOL"/>

            <component type="item" x="3" y="3" name="§eRandom" material="ENDER_PEARL"/>
        </page>
        <page position="2">
            <component type="item" x="1" y="1" name="§eBuilder Battle 1vs1" material="RED_CONCRETE"/>
            <component type="item" x="3" y="1" name="§eBuilder Battle 2vs2" material="WHITE_CONCRETE"/>
            <component type="item" x="5" y="1" name="§eBuilder Battle 4vs4" material="BLUE_CONCRETE"/>

            <component type="item" x="3" y="3" name="§eRandom" material="ENDER_PEARL"/>
        </page>
    </component>

    <!-- Frame around paged -->
    <component type="item" x="0" y="1" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="2" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="3" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="4" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="0" y="5" material="BLACK_STAINED_GLASS_PANE"/>

    <component type="item" x="8" y="1" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="2" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="3" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="4" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="8" y="5" material="BLACK_STAINED_GLASS_PANE"/>

    <component type="item" x="1" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="2" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="3" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="4" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="5" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="6" y="5" material="BLACK_STAINED_GLASS_PANE"/>
    <component type="item" x="7" y="5" material="BLACK_STAINED_GLASS_PANE"/>
</gui>

image

As you can see, I've added a id to the paged component and two items. Both trigger an action, previous or next. Additionally, I've set the priority to low, as it will make sure to render above the embedded component and events will get triggered accordingly.

Clone this wiki locally