Guis from XML - ParzivalExe/guiapi GitHub Wiki
Now we start with the really big addition from version 2.0 of GuiAPI: GUIs written in XML. In this tutorial I will cover everything from some of the basics of XML over how to create your own .mgui-files with xml-code for a gui to the creating of a Gui-Instance based on this xml-code. So, lets start as there is a lot of ground to cover:
Generally, XML is build up out of two elements: Elements
and Attributes
. First let's start with Elements
All XML-Code is created out of Elements. The Syntax for an Element is: <ElementName></ElementName>
. The <...>
and </...>
Part is called a Tag. As you can see here already, there is always a Start-Tag and an End-Tag. You can distinguish between them because an End-Tag has a /
before the ElementName. Of course, the ElementNames of Start-Tag and End-Take must be the same. Between both Tags you have the Content of the Element. Content could either be a String representing a value or more Elements (although in Gui-XML it is only other Elements and never a value-string).
If you don't need Content for an Element we could also write an Empty-Element with a self-closing Tag. A self-closing Tag can be written like this: <ElementName/>
.
Attributes can further define an Element and therefore give it additional values to work with. The syntax of an Attribute looks like this: attrName="attrValue"
. As you can see, here too the value can only be given as Text/String. You can add an Attribute to an Element by writing it into the Start-Tag like this:
<ElementName attr0="attr0Value" attr1="attr1Value"...></ElementName
or of course
<ElementName attr0="attr0Value" attr1="attr1Value"/>
Every XML-Document must start with the Prolog which holds important information like the version and character-encoding the program using this XML-Document must know about. This sounds rather complicated but in general, it is actually pretty easy as most of the time (like with GuiAPI) you will simply use the following Prolog as most programs can understand this type of XML-Document:
<?xml version="1.0" encoding="UTF-8"?>
As you might have already read at some points in this Wiki, the general FileType for the XML-Files used to create Guis is called mgui
(short for MinecraftGui) and therefore your Files should also end with .mgui just like TextFiles should always end with .txt.
So, first you have to create a new File called TestGui.mgui
(or some other name). Now we can edit this file and create your very own Gui.
As already talked about in XML-Basics, we first need the Prolog in the first line of this file. For this simply write <?xml version="1.0" encoding="UTF-8"?>
.
Now we can create the Root-Element, which must in the case of Guis always be a Gui. So, simply write
<?xml version="1.0" encoding="UTF-8"?>
<Gui title="XML Gui">
</Gui>
As you can see, we used the attribute title
and set the value to XML Gui
which should give your Gui the title XML Gui.
After this we could already create this Gui in game, but I will first teach you how to populate this Gui with Components so that the first Gui we create already has some functionality to it.
Inside the Gui-Element we must now create a Library
-Element which we then can populate with Include
-Elements. Every Include-Element has a synonym
-attribute and a path
-attribute. With Include
-Elements we can link to a class by using it's packages + classname much like an import in Java. We can then give this Class a synonym
which we then can use throughout your Gui-XML Code. So, let's include MessageComponent
into your XML-Gui:
<?xml version="1.0" encoding="UTF-8"?>
<Gui title="XML Gui">
<Library>
<Include synonym="MessageComponent" path="io.github.parzivalExe.guiApi.components.MessageComponent"/>
</Library>
</Gui>
Well, now that we have Included MessageComponent
we can simply use the synonym
as the ElementName
if we want to create this Component, which we will do straight away. For this you simply need to add the MessageComponent-Element
as Content
for Gui as well:
<?xml version="1.0" encoding="UTF-8"?>
<Gui title="XML Gui">
<Library>
<Include synonym="MessageComponent" path="io.github.parzivalExe.guiApi.components.MessageComponent"/>
</Library>
<MessageComponent title="HELLO WORLD!" look="166" message="Hello World!"/>
</Gui>
As you might already realised, different Components
can have different attributes
. That's why there is a XML-Attribute Part in every single deep Component-Overview. Some Components even need other Elements as Content which (when necessary) is also explained in these deep Component-Overviews under the title XML-Content.
Some of the attributes are however the same for almost every Component as they are defined in the Component-Class itself:
-
title="{String}"
: sets the Title of the Component, just like the first argument when creating aComponentMeta
does. This is pretty simple to implement as you just need to write your Title as the value -
look="{AMOUNT}xID:{DATA}{[DURABILITY]}"
: As you might already realise from the Overview, this is a bit more Complicated. Here you set the look (meaning the Item that represents this Component). All the Values surrounded by{...}
are optional and can therefore not be written if you don't need to change them.AMOUNT
gives the amount of Items stacked,ID
the Item-Id or the Material-Name (forMaterial.STONE
that would be either1
orSTONE
), although for 1.13 or higher, you need to write the Material-Name as IDs are no longer supported by Bukkit.DATA
is there to signal variants (for example different Colours of Wool) although again since 1.13 it is no longer supported by Bukkit and is now part of the Material-Name.DURABILITY
is the amount of durability already lost on the Component. As an example, 3x Orange-Wool would belook="3x35:1"
orlook="3xWOOL:1"
or since 1.13look="3xORANGE_WOOL"
while 1x White-Wool could simply belook="35"
orlook="WOOL"
and since 1.13look="WHITE_WOOL"
-
description="[{line1}, {line2}...]"
: As you can see, description is an ArrayList and needs therefore always be surrounded by[...]
, even if only one line exists. Lines (different values in the ArrayList) are marked by a,
. If you want to write a comma in your string, you need to use a\
before the,
(\,
=,
) -
place="{PLACE}"
: This is simply an Integer representing the place this component is placed at inside the Gui, starting from the top-left.
Now, let's look at how we can create a Gui-Instance out of these files to then open for the player.
To create a Gui-Instance form a *.mgui-File we need to use the static function inside Gui called createGui(...)
.
To create a Gui you need to know two things about your GuiFile:
- The
Path
- The
PathOrigin
Let's start with the Path
. The Path
is simply all the Folders and at the end the FileName (without .mgui, your FileType) as a String. So, if we for example would have your File on your Desktop, we would write: "/Users/[USER]/Desktop/TestGui"
.
The PathOrigin
now tells the GuiAPI from where the Path described here begins. There are three options:
PC_ORIGIN
SERVER_ORIGIN
PROJECT_ORIGIN
What we wrote there was a Path based on PC_ORIGIN
as this path starts from the point where the file-system of the PC this plugin is used on starts.
SERVER_ORIGIN
on the other hand starts at the same point where the server the plugin is running on is located. So, if we take the path "plugins/GuiAPI/Guis/TestGui"
we would open a File called TestGui.mgui
inside the folders GuiAPI/Guis
in your plugin
-folder.
With PROJECT_ORIGIN
the path starts where your project/plugin lies at the moment, meaning, that the path written is the path inside your .jar-file. If you don't know how your .jar-file looks inside, use a zip-tool like 7zip and open the .jar-file of your plugin with this tool, but as an example: If you put your TestGui.mgui
-File at the same position as the plugin.yml
, you would simply have to use "TestGui"
as your path if you use PROJECT_ORIGIN
.
Additionally to these two arguments you need for createGui(...) you can also change the file-suffix by defining the fileType (default is here "mgui"). Important: You should not write the dot in front of the FileType you specify here.
As an example lets create the Gui from First Gui together with the Component from First Component completely through XML.
To start, we create a file called FirstXmlGui.mgui
at the same location inside your plugin/project as your plugin.yml
.
Into this file we then write:
<?xml version="1.0" encoding="UTF-8"?>
<Gui title="first Gui">
<Library>
<Include synonym="MessageComponent" path="io.github.parzivalExe.guiApi.components.MessageComponent"/>
</Library>
<MessageComponent title="HELLO WORLD!" look="166" message="Hello World!"/>
</Gui>
Now we need to create another command (I will not cover this here) and then simply write the following logic into this Command:
Gui xmlGui = Gui.createGui("FirstXmlGui", PathOrigin.PROJECT_ORIGIN);
xmlGui.openGui((Player) sender);
And that's it. Simple and easy, am I right 😅👍
There is one more topic we can talk over when it comes to Guis based on XML because for the Layout itself it is even easier to test them than programmed Guis. As long as the file is outside your project, you can simply change the content of the file and then safe it. The next time the file will be loaded it will already be updated because for now these files are loaded at runtime. This means that you don't have to build the project or even reload the server.
If you don't have a location outside your project to test your XML-Guis, no problem. GuiAPI has already build-in a location for you. Simply create the Folder GuiAPI
inside your plugins-folder and inside this folder the folder Guis
. Now you can put the .mgui-Files you want to test into this folder and by executing the Command /guixml [FileName] open it. Important: The File-Name must be without the FileType (.mgui)
Example XML-Gui: src/main/resources/TestGui.mgui
This is build with ANTLR 4
Code for XML-Conversion: io.github.parzivalExe.guiApi.antlr.*
XML-Lexer: src/main/antlr4/XMLLexer.g4
XML-Parser: src/main/antlr4/XMLParser.g4
Standard-Converter: io.github.parzivalExe.guiApi.antlr.converter.*