[Open Source] SkyBlock Minecraft Addon [gui.sk]

2018-12-01_00.37.18.png

Hello guys!

Yesterday, i published a SkyBlock Skript to GitHub, read more about it here. Today, i continued with commits and also created a new add-on to this Skript, gui.sk, which is also published under the MIT license.

gui.sk

This skript adds minecraft inventory menu capabilities directly in skript and makes the setup for new server operators easier. Opening custom inventorys to players as a menu, fill the menus with custom items and execution on a click can be achieved fast and reliable with gui.sk.
Additional, gui.sk is very lightweight, it has only 98 lines of code.

Skript has some default capabilities to open inventories and add items to it. But there are some problems:

  1. Adding items into a new inventory with lore (the information text below the name of the item) is not working in Skript without add-ons
  2. To get this working in Skript, the server operator would have to set up events (click trigger) for the menu, code for the menu and formatting of items and also have to search for add-ons which actually work for the right version.

To make it as easy as possible for server operators, i'm making SKYBLOCK.SK as dependency free as possible by using the apis of the minecraft server software Spigot and Bukkit directly in Skript by using skript-mirror, which is a powerful reflection tool for Skript.

Now, let's look what it actually does:
Because some needed triggers aren't existing in Skript, i use skript-mirror as a add-on, which allows to use java classes and events within Skript:

import:
    java.util.ArrayList
    org.bukkit.Bukkit
    org.bukkit.event.inventory.InventoryClickEvent
    org.bukkit.event.inventory.InventoryCloseEvent
    org.bukkit.inventory.Inventory
    org.bukkit.inventory.ItemStack
    org.bukkit.inventory.meta.ItemMeta
    org.bukkit.event.inventory.InventoryType

Then i started with the first function of this skript. It opens a inventory by any available inventory type, custom size and name. Once the function is loaded, it can be called across the whole server in every skript, this allows to make clean looking skripts and wíth less code.

function opengui(player:player,size:integer,name:text,type:text=CHEST) :: boolean:
    delete {SK::GUI::items::%{_player}%::*}
    # > If {_type} is CHEST (default)
    if {_type} is "CHEST":
        set {_invtype} to InventoryType.CHEST!
    # > If it is not a chest, check the input and set the InventoryType:
    # > This is done like this to avoid errors:
    else:
        if {_type} is "ANVIL":
            set {_invtype} to InventoryType.ANVIL!
        else if {_type} is "BEACON":
            set {_invtype} to InventoryType.BEACON!
        else if {_type} is "BREWING":
            set {_invtype} to InventoryType.BREWING!
        else if {_type} is "CHEST":
            set {_invtype} to InventoryType.CHEST!
        else if {_type} is "CRAFTING":
            set {_invtype} to InventoryType.CRAFTING!
        else if {_type} is "CREATIVE":
            set {_invtype} to InventoryType.CREATIVE!
        else if {_type} is "DISPENSER":
            set {_invtype} to InventoryType.DISPENSER!
        else if {_type} is "ENCHANTING":
            set {_invtype} to InventoryType.ENCHANTING!
        else if {_type} is "ENDER_CHEST":
            set {_invtype} to InventoryType.ENDER_CHEST!
        else if {_type} is "HOPPER":
            set {_invtype} to InventoryType.HOPPER!
        else if {_type} is "MERCHANT":
            set {_invtype} to InventoryType.MERCHANT!
        else if {_type} is "PLAYER":
            set {_invtype} to InventoryType.PLAYER!
        else if {_type} is "SHULKER_BOX":
            set {_invtype} to InventoryType.SHULKER_BOX!
        else if {_type} is "WORKBENCH":
            set {_invtype} to InventoryType.WORKBENCH!
        # > If {_invtype} has been found and set go forward:
    if {_invtype} is set:
        # > Set the {_inventory} variable to a new inventory, owned by the player and set to the {_invtype}, named after {_name}
        if {_type} is "CHEST":
            set {_inventory} to Bukkit.createInventory({_player}, {_size}, {_name})
        else:
            set {_inventory} to Bukkit.createInventory({_player}, {_invtype}, {_name})
        # > Open the inventory to the player
        {_player}.openInventory({_inventory})
        # > Set the created inventory into the variable to check later, if the user clicks in the correct gui
        set {SK::GUI::inv::%{_player}%} to {_inventory}
        # > Everything went fine, return true.
        return true
    # > If not, print an error to the player who wanted to open the gui menu and stop the skript
    else:
        message "&5&lInvalid InventoryType: %{_type}%" to {_player}
        stop

I know, it looks a bit messy because i have to checking trough all the types and then setting the {_invtype} variable to the InventoryType, but i had troubles adding them directly. This works, it is bigger, but it works always fine.

Once the function is called, the {SK::GUI::items::%{_player}%::}* array is being deleted. You can determine arrays from variables by looking at their end, if it ends with ::*}, it's a array.
This is needed, because this array contains all items of the GUI menu, since we're opening a fresh menu without any content, this has to be empty too.

Then, it is determined what type of inventory should be opened, there are many and i'm actually pretty delighted that all of them seem to work. =) The if and else if statements are filtering out the right inventory type, i have to make this shorter in the future...

If that is done, the inventory is being created and then displayed to the player. If something went wrong, the player gets a message that the inventory type is not available. Since that is the only thing that could go wrong here. Hopefully :3.

Now, we have a empty inventory which has to be filled with stuff. To do this, i have created another function:

function setguiitem(player:player,slot:integer,item:text,amount:integer,name:text,lore:text,exec:text="",close:boolean=false):
    set {_ItemStack} to {_item} parsed as item
    set {_ItemStack} to {_ItemStack} named {_name}
    set {_ItemMeta} to {_ItemStack}.getItemMeta()
    set {_itemlore} to new ArrayList()
    {_itemlore}.add({_lore})
    {_ItemMeta}.setLore({_itemlore})
    {_ItemStack}.setItemMeta({_ItemMeta})
    set slot {_slot} of {SK::GUI::inv::%{_player}%} to {_ItemStack}
    if {_exec} != "":
        set {SK::GUI::items::%{_player}%::%{_slot}%} to {_exec}
    if {_close} is true:
        set {SK::GUI::itemsclose::%{_player}%::%{_slot}%} to true

This function has many arguments, these needed to create a new item and also needed for a click event later.
Here, the item input is set as text and then being parsed as item, which reduces errors by users, since the right item name automatically chosen. Then the ItemMeta is set into a new variable, which is used to set the lore of the item. This would not work in Skript direcly without skript-mirror or another skript add-on.
Then, the item is placed into the predefined inventory from the function above. A global variable is set to allow the server to detect, which item slot in the new menu has a command to execute code on click.

Now, we're ready for the events (trigger)!
The following event is triggered if the user clicks somewhere in the inventory, then i can get player who clicked on it to check, if the click happened within a GUI menu or not:

on InventoryClickEvent:
    set {_player} to event.getWhoClicked()
    if {SK::GUI::inv::%{_player}%} is set:
        if size of {SK::GUI::items::%{_player}%::*} is not 0:
            cancel event
            set {_slot} to event.getRawSlot()
            evaluate "%{SK::GUI::items::%{_player}%::%{_slot}%}%"
            if {SK::GUI::itemsclose::%{_player}%::%{_slot}%} is true:
                wait 1 tick
                close {_player}'s inventory

Only, if {SK::GUI::inv::%{_player}%} is set and {SK::GUI::items::%{_player}%::}* is not 0, the predefined command is being executed, thanks to evaluate, plain text can be parsed as skript code, this is very helpful at this point.

If the {SK::GUI::itemsclose::%{_player}%::%{_slot}%} variable is set to true, it is going to wait for one tick and then closing the inventory.
Closing the inventory instantly would create a display bug, that is why it is waiting for 1 tick to prevent the display bug.

Now, since we closed the inventory, we can go to the InventoryCloseEvent event, which is called, when the user is closing his inventory.

on InventoryCloseEvent:
    set {_player} to event.getPlayer()
    delete {SK::GUI::inv::%{_player}%}
    delete {SK::GUI::items::%{_player}%::*}
    delete {SK::GUI::itemsclose::%{_player}%::*}

To make sure, no data is left by a corruption or server fault, the variables are deleted on load:

on load:
    delete {SK::GUI::inv::*}
    delete {SK::GUI::items::*}
    delete {SK::GUI::itemsclose::*} 

Thats all what it needs to do.

gui.sk on GitHub

You can find gui.sk here on GitHub, it is part of the SKYBLOCK.SK Skript, which allows server operators to provide a highly customized SkyBlock experience for the users with Skript without to know how Java works.

How to contribute

To contribute, you can either create a pull request on GitHub or contact us on the Skyroad Discord. Really looking forward to see more people using skript for minecraft. =)

Usage example

Now, i want to show how it works, this is a new command, which can be executed in the game by a player by typing in "/test", it opens a gui menu for the player whith the name "&6FREE STUFF" with the inventory type of a hopper.

Then it executes the second function, which adds a diamond ore to this inventory, which is named "&r&6&lFREE DIAMONDS!!!1", with a lore (text below the name) of "&r&fFREEEEEE! Click here!", the following text is the command, which is going to be executed, if the player clicks on it.

If true is set at the end, the inventory menu is going to be closed on click. Example code:

command /test:
    trigger:
        opengui(player,9,"&6FREE STUFF","HOPPER")
        setguiitem(player,0,"diamond ore",1,"&r&6&lFREE DIAMONDS!!!1","&r&fFREEEEEE! Click here!","execute console command ""give %player% diamond_ore 1""",true)

Well, thats all i have to share for now. I had much stuff to do the last couple of days for SKYBLOCK.SK, since the game started today on our server, join.skyroad.me. Feel free to test it out and leave some feedback. This is currently the only server where you can earn STEEM by playing minecraft and SkyBlock at the same time.

I made this post more code-like than the last post, hope that this is how you guys like it =). I also added more detailed comments to the skript to make it easier to understand whats happening there.

EDIT: messed up the tags, should be fixed now.^^

I wish all of you guys a great weekend,

@immanuel94

H2
H3
H4
Upload from PC
Video gallery
3 columns
2 columns
1 column
15 Comments