-
Notifications
You must be signed in to change notification settings - Fork 2
Developer Docs
First, you have to get the GuiEngine builds into your project. When using maven, you can use the following:
<repositories>
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.github.ToberoCat</groupId>
<artifactId>GuiEngine</artifactId>
<version>Tag</version>
</dependency>
</dependencies>
For gradle:
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation 'com.github.ToberoCat:GuiEngine:Tag'
}
GuiEngine requires you to use a own GuiEngineAPI when you want to display guis. Creating such a instance is as easy as writing a single line of code.
import org.bukkit.plugin.java.JavaPlugin;
import java.io.IOException;
import java.net.URISyntaxException;
public class MyGuiPlugin extends JavaPlugin {
@Override
public void onEnable() {
GuiEngineApi guiEngineApi;
try {
guiEngineApi = new GuiEngineApi(this);
} catch (IOException | URISyntaxException e) {
getLogger().severe(e.getMessage());
return;
}
}
}
The code above creates a new GuiEngineApi instance bound to this plugin. This binding to the plugin isn't necessary, as there are two other constructors. But this way, you don't have to worry about copying your guis from the resource folder into your data folder. Make sure you have a guis folder in your resources where you can put the guis your plugin needs. To see how to create your own guis, this beginner guide might help you.
If you decide to not use the constructor with the JavaPlugin, you have to create the api as follows:
import org.bukkit.plugin.java.JavaPlugin;
import java.io.File;
public class MyGuiPlugin extends JavaPlugin {
@Override
public void onEnable() {
GuiEngineApi guiEngineApi = new GuiEngineApi("my-plugin", new File(getDataFolder(), "guis"),
pathname -> {
return pathname.canRead();
});
//GuiEngineApi guiEngineApi = new GuiEngineApi("my-plugin", new File(getDataFolder(), "guis"));
}
}
The not commeted example uses the GuiEngineApi constructor that takes the id of the api, the folder where the guis are located and a FileFilter. The ID is used to identify the api from other apis registered. The FileFilter determains what files in the folder are gui files that should get loaded by the GuiEngine.
If you use the commented approach, you can leave out the FileFilter. Then the default one will be used, where all files ending with .gui will get loaded.
Now that you have your GuiEngineApi instance, you can use it to open a gui. This is again very easy and straight forward. You can use the open Method provided by the GuiEngine.
GuIContext context = guiEngineApi.open(player, "my-gui");
That's all you need. Now the player will see the gui you desgined pop up on their screen. Notice the my-gui. This is actually a file in the guis folder, called my-gui.gui. When opening a gui, the extension is always stripped from it. The open method also returns you the GuiContext. This is a class containing the entire gui the user can see. You can use it to interact with the gui from the code side, but to this a little bit later.
The open method has a overloaded method too. You can pass it placeholders, which can be used in the gui. A placeholder is always indicated with: %%. The method where you don't pass any placeholders has a default placeholder, the viewer one. Let's try to replicate this with the overloaded method.
Map<String, String> placeholders = new HashMap<>();
placeholders.put("viewer", player.getUniqueId().toString());
GuiContext context = guiEngineApi.open(player, "my-gui");
In this primitive example, you can see that passing placeholders is almost as easy as opening the gui. Just create a map, populate it and pass it as method argument.
There is another way to open a gui for a player that doesn't involve any files, only pure code. This method can be set equal to using a regualr gui framework. I personally don't recommend it, as you should always try to use this tool to make everything configurable, but something there isn't a way around.
You will still need a gui context, aswell as a interpreter, but you can skip the file.
GuiInterpreter interpreter = guiEngineApi
.getInterpreterManager()
.getInterpreter("default"); // Or just use new DefaultInterpreter();
if (interpreter == null)
return;
GuiContext context = new GuiContext(interpreter, "§eMy gui", 9, 5);
context.add(guiEngineApi, new SimpleItemComponentBuilder<>()
.setName("§aMy Item")
.setMaterial(Material.SLIME_BALL)
.createComponent());
Map<String, String> placeholders = new HashMap<>();
interpreter.getRenderEngine().showGui(context, player, placeholders);
As you can see, this isn't as simple as it was before. So, let's go over this step by step. The first thing we need is a interpreter. Even though we don't have to interprete a gui from a file, we still need it to to create a gui context. For getting a gui interpreter, there are two ways. The first one is probably the easies. Just create a new instance of the interpreter you need, like I do in the commented part. This might work well for some interpreters, but especially ones that aren't included in the default guiengine plugin, you mgith prefer the second approach. You can get a instance of the interpreter manager by using getInterpreterManager() on the guiEngineApi. This then allows you to search for a interpreter, using the getInterpreter method. This is then the same id as you would use in a gui file. Because I want to use the default one, I put in "default". The interpreter returned is marked as nullable. This means there is possible the chance that this might be null. Because I know that the default interpreter is always there, I could probably skip this null check.
Now after we got the interpreter, we can create the GuiContext. This context now has the settings for the gui, like the title, width, height. Once we have the context, we can use it to add components to it. By using the add method, we can add a component. To add a component we must also pass the guiEngine api reference again. This is needed so the component can get bound to this api. Then we can start creating out component. GuiEngine aims on creating Components that are easy to make with the gui files while also being easy to create without one. That's why we a builder for each gui component. We just have to create a builder of the desired component, use the methods provided and once where're finished call the createComponent() method.
In this case I'm using the SimpleItemComponentBuilder. It's basically the builder that hides behind the type "item" in the gui files. I can then set the properties like in the gui file.
Once I've designed my gui context, I can show it to a player. This can only be done using a gui render engine. This is the thing responsible for handling events and rendering your content onto a inventory. You pass the player, the context and the placeholders. Even though the placeholders won't get parsed from any components when calling the showGui method, it's still required, so components that render their own guis inside can parse the gui with the same placeholders.
Note: You could theoretically use a different render engine then the gui interpreter provides, which might work well in some cases, but as soon as the context gets requested to be redrawn, it chooses the interpreters one, which might cause issues.
Now, to avoid having to create your gui context by hand, it's a good idea to create your own compontens, you and others can use.
Each component has to implement in some way the GuiComponent interface. It provides methods used for rendering, event handling and serialization. Each registered component can only get register with an appropriate builder.
A component builder is a seperate interface, which should help you in creating inhertiable builders. These builders must have a deserilaize method and a createComponent.
You can think of serialization as a process, where you convert the componet into one you would be able to load from a gui file. Deserialization on the other hand takes the gui file component and converts it in something java is able to work wit - So these two things are something very important for GuiEngine. This is why most work has gone into making them easily usable.
Creating a custom component from scratch is a lot of code. That's why it's important to know the tricks when it comes to choosing a good foundation. A bad one makes writing the component a pain in the ass, while a good one can make it a walk in the park.
That's why it's important to understand how componets are inherited:
This is the hierarchy of the GuiComponent. As you can see, the interface GuiComponent is the root of all components. When you implement it, it gives you the finest control over your component - But this also has a bad side. The more control you get over the component's behaviour, the more code has to be written by yourself.
That's why I would recommend before you start creating your own custom component, you search for one that comes very close to your needs. This search isn't only giving the least amount of code you'll have to write, maybe the component you found provides enough for your needs, so you don't have to write your own component at all.
I'd recommend starting from the bottom of the hierarchy (This might also include components from other plugins) and slowly going up, until it's abstract enough for you to work with it. The tradeoff lies in finding the right balance between abstraction and the amount of code needed for implementation.
After you've finally found a good base and decided that it's nessecary to create your own component, you first have to create a new class. In my example, I'll be creating a new component ontop of the SimpleItemComponent. The purpose of my component is to change it's x and y position all x seconds.
So let's start with the class RandomPositionComponent
. I then extend from the SimpleItemComponent class and create the constructor calling the super constructor. I also added new constructor argument, repsonsible for setting the ticks between the new position choosen.
import io.github.toberocat.guiengine.components.provided.item.SimpleItemComponent;
import io.github.toberocat.guiengine.function.GuiFunction;
import io.github.toberocat.guiengine.render.RenderPriority;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.NotNull;
import java.util.List;
/**
* Created: 21.07.2023
*
* @author Tobias Madlberger (Tobias)
*/
public class RandomPositionComponent extends SimpleItemComponent {
private final long ticksBetweenNewPosition;
/**
* Constructor for SimpleItemComponent.
*
* @param offsetX The X offset of the GUI component.
* @param offsetY The Y offset of the GUI component.
* @param priority The rendering priority of the GUI component.
* @param id The ID of the GUI component.
* @param clickFunctions The list of click functions for the GUI component.
* @param dragFunctions The list of drag functions for the GUI component.
* @param closeFunctions The list of close functions for the GUI component.
* @param stack The ItemStack to be displayed in the GUI component.
* @param hidden true if the GUI component is hidden, false otherwise.
* @param ticksBetweenNewPosition Ticks that have to pass that the component randomizes its position again
*/
public RandomPositionComponent(int offsetX,
int offsetY,
@NotNull RenderPriority priority,
@NotNull String id,
@NotNull List<GuiFunction> clickFunctions,
@NotNull List<GuiFunction> dragFunctions,
@NotNull List<GuiFunction> closeFunctions,
@NotNull ItemStack stack,
boolean hidden,
long ticksBetweenNewPosition) {
super(offsetX, offsetY, priority, id, clickFunctions, dragFunctions, closeFunctions, stack, hidden);
this.ticksBetweenNewPosition = ticksBetweenNewPosition;
}
}
This is my class right now. As you can see, the constructor is massive, containg a hole lot of parameters. That's why using a builder is a great option - It also allows for easier optional parameters.
Now, talking about the builder, let's create one. As you know, all components registered need to have a builder. So does the SimpleItemComponent.
Thank you for choosing GuiEngine for your GUI development needs. Explore the Developer Docs and User Beginner Guide to unleash the full potential of this powerful framework and create stunning GUIs that leave a lasting impression on your players!