Giter Site home page Giter Site logo

if's Introduction

IF Discord guild

This framework works for Minecraft versions 1.14-1.20

An inventory framework for managing GUIs

This framework is based on a pane principle. This means that the GUI is divided into different types of panes which all behave differently. A GUI consists of multiple panes which can interact with each other.

Next to those panes, GUIs can also be created from XML files by simple loading them in. This allows for easy GUI creation with little code.

Maven dependency

To add this project as a dependency to your pom.xml, add the following to your pom.xml:

<dependency>
    <groupId>com.github.stefvanschie.inventoryframework</groupId>
    <artifactId>IF</artifactId>
    <version>0.10.13</version>
</dependency>

The project is in the Central Repository, so specifying a repository is not needed.

Now in order to shade the project into your project, add the following to your pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-shade-plugin</artifactId>
    <version>3.5.2</version>
    <configuration>
        <dependencyReducedPomLocation>${project.build.directory}/dependency-reduced-pom.xml</dependencyReducedPomLocation>
        <relocations>
            <relocation>
                <pattern>com.github.stefvanschie.inventoryframework</pattern>
                <shadedPattern>[YOUR PACKAGE].inventoryframework</shadedPattern>
            </relocation>
        </relocations>
    </configuration>
    <executions>
        <execution>
            <phase>package</phase>
            <goals>
                <goal>shade</goal>
            </goals>
        </execution>
    </executions>
</plugin>

Replace [YOUR PACKAGE] with the top-level package of your project.

Gradle dependency

To add this project as a dependency for your Gradle project, make sure your dependencies section of your build.gradle looks like the following:

dependencies {
    implementation 'com.github.stefvanschie.inventoryframework:IF:0.10.13'
    // ...
}

The project is in Maven Central, so ensure your repositories section resembles the following:

repositories {
    mavenCentral()
    // ...
}

In order to include the project in your own project, you will need to use the shadowJar plugin. If you don't have it already, add the following to the top of your file:

plugins {
    // ...
    id "com.github.johnrengelman.shadow" version "7.1.2"
}

To relocate the project's classes to your own namespace, add the following, with [YOUR PACKAGE] being the top-level package of your project:

shadowJar {
    relocate 'com.github.stefvanschie.inventoryframework', '[YOUR PACKAGE].inventoryframework'
}

Dependency via plugin.yml

IF does not support declaring the dependency via the libraries section in the plugin.yml. Please make use of a build tool as described above to use IF as a dependency.

Building from source

If you want to build this project from source, run the following:

git clone https://github.com/stefvanschie/IF.git

This will clone this repository to your device. This project relies on NMS, for which the dependencies are not available online. Because of this, you'll need to follow additional steps to obtain all these dependencies locally.

Installing Paper manually

For versions 1.14-1.16, we have to manually install Paper. Run the following scripts for each version to install the dependencies locally. Running these commands generate additional files in the folder where you execute them. To ensure that you don't accidentallly overwrite other files, execute this in an empty folder. The files that get created can be deleted afterwards (either after installing a single version or after installing all of them), since they're no longer necessary.

1.14.4

wget https://papermc.io/api/v2/projects/paper/versions/1.14.4/builds/243/downloads/paper-1.14.4-243.jar -O paperclip/paper-1.14.4.jar
java -jar paper-1.14.4.jar
mvn install:install-file -Dfile=cache/patched_1.14.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.14.4-R0.1-SNAPSHOT" -Dpackaging="jar"

1.15.2

wget https://papermc.io/api/v2/projects/paper/versions/1.15.2/builds/391/downloads/paper-1.15.2-391.jar -O paperclip/paper-1.15.2.jar
java -jar paper-1.15.2.jar
mvn install:install-file -Dfile=cache/patched_1.15.2.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.15.2-R0.1-SNAPSHOT" -Dpackaging="jar"

1.16.1

wget https://papermc.io/api/v2/projects/paper/versions/1.16.1/builds/138/downloads/paper-1.16.1-138.jar -O paperclip/paper-1.16.1.jar
java -jar paper-1.16.1.jar
mvn install:install-file -Dfile=cache/patched_1.16.1.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.1-R0.1-SNAPSHOT" -Dpackaging="jar"

1.16.3

wget https://papermc.io/api/v2/projects/paper/versions/1.16.3/builds/253/downloads/paper-1.16.3-253.jar -O paperclip/paper-1.16.3.jar
java -jar paper-1.16.3.jar
mvn install:install-file -Dfile=cache/patched_1.16.3.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.3-R0.1-SNAPSHOT" -Dpackaging="jar"

1.16.4

wget https://papermc.io/api/v2/projects/paper/versions/1.16.4/builds/416/downloads/paper-1.16.4-416.jar -O paperclip/paper-1.16.4.jar
java -jar paper-1.16.4.jar
mvn install:install-file -Dfile=cache/patched_1.16.4.jar -DgroupId="io.papermc" -DartifactId="paper" -Dversion="1.16.4-R0.1-SNAPSHOT" -Dpackaging="jar"

Installing Paper via the maven plugin

For versions 1.17-1.18, we use Paper via the paper-nms-maven-plugin. To install these versions locally, we must run a few maven commands. These commands should be ran in the root directory of the project.

mvn paper-nms:init -pl nms/1_17_0
mvn paper-nms:init -pl nms/1_17_1
mvn paper-nms:init -pl nms/1_18_0
mvn paper-nms:init -pl nms/1_18_1
mvn paper-nms:init -pl nms/1_18_2
mvn paper-nms:init -pl nms/1_19_0
mvn paper-nms:init -pl nms/1_19_1
mvn paper-nms:init -pl nms/1_19_2
mvn paper-nms:init -pl nms/1_19_3
mvn paper-nms:init -pl nms/1_19_4
mvn paper-nms:init -pl nms/1_20_0-1
mvn paper-nms:init -pl nms/1_20_2
mvn paper-nms:init -pl nms/1_20_3-4

Your environment is now set up correctly. To create a build, run the following inside the root folder of the project.

mvn clean package

Your build is now available in the /IF/target folder.

Adventure support

IF supports Adventure, but does not shade it in itself. The use of Adventure Components instead of legacy Strings is completely optional. If you do not wish to use Adventure you can safely ignore all TextHolder related methods.

What is Adventure?

Adventure is a library that adds proper modern text support to Minecraft. Modern text is represented using bungee-chat and BaseComponent instances in Spigot. Adventure is an alternative to bungee-chat and offers more features.

Using Adventure on 1.16.5+ Paper

You don't need to import/shade anything for Adventure support in this case!

Note: Paper only supports Adventure on build 473 and above. If you aren't running months old builds, then you are fine.

Using Adventure on Spigot and older Paper

On Spigot Adventure isn't included in the server, therefore you have to shade and relocate it yourself. The following dependencies need to be imported and shaded:

  • adventure-api
  • adventure-platform-bukkit

Please consult the Adventure documentation for more information.

How to use Adventure Components

Example of migration from legacy String to Adventure Component:

  • legacy: namedGui.setTitle("My Title!");
  • Adventure: namedGui.setTitle(ComponentHolder.of(Component.text("My Title!")));

We apologize for the boilerplate (the ComponentHolder.of(...) call), but that was the only way to not make IF hard-depend on Adventure.

Full Adventure support is only achieved when your server natively supports Adventure (it is running Paper) and your plugin depends on Paper (instead of Spigot). In other words, you won't benefit from Adventure as much if you use Spigot instead of Paper. This is because when Adventure is relocated we have to convert everything back to legacy Strings before passing them to the Bukkit API.

if's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

if's Issues

PaginatedPane's offset in Gui is doubled

Encountered the issue some time ago but forgot to mention it.

PaginatedPane offset doesn't work properly, when moved either on x or y axis it's moved by double value.

Example:

        Gui gui = new Gui(plugin, 4, "test");
        PaginatedPane pane = new PaginatedPane(1, 1, 2, 2);
        gui.addPane(pane);
        pane.populateWithItemStacks(new ArrayList<ItemStack>() {{
          for(int i = 0; i < 10; i++) {
            add(new ItemStack(Material.DIRT));
          }
        }});

Expected: https://i.imgur.com/daLMk48.png
Got: https://i.imgur.com/KfzWBQ0.png x and y axis are in 2 slots offset not one

When setting x/y to 2 it goes 4 also.

Tested on StaticPane and OutlinePane, works good, broken only on this one.

Discussion: HumanEntityCache safeguard

Plugins can get disabled for many reasons (the topic is explored a bit in #45), but one of them is when the server stops (gracefully). In this case, plugins are disabled before players quit/close their inventories. So if the plugins don't explicitly close their GUIs in onDisable or some PluginDisableEvent listener, then problems arise. Even if I'm wrong about /stop, /reload is surely affected. (Although whether we want to support that command is another question.)

I suggest the addition of a safeguard that catches plugins neglecting the proper closure of inventories and therefore the restoration of player inventories. The best way to this would be to have a PluginDisableEvent listener and keep track of all plugin-to-gui connections in some sort of Map. To be able to properly implement this, #45 should be finished first.

I would be more than happy to implement and PR this, I am just asking for input regarding it. This is a place for discussion.

Not Shading Correctly

I first attempted to shade IF into my plugin. It did not work and never ended up in the plugin. So then i copied exact word-for-word, char-for-char your example, replacing my maven-shade-plugin configuration with yours. Still does not work. Please help!

Also, in-game stacktrace for not finding the lib
Caused by: java.lang.NoClassDefFoundError: com/elytraforce/libs/inventoryframework/pane/Pane at com.elytraforce.voidcore.commands.TeleportCommands$VisitCommand.onCommand(TeleportCommands.java:59) ~[?:?] at org.bukkit.command.PluginCommand.execute(PluginCommand.java:45) ~[server.jar:git-Paper-385] ... 17 more Caused by: java.lang.ClassNotFoundException: com.elytraforce.libs.inventoryframework.pane.Pane at java.net.URLClassLoader.findClass(URLClassLoader.java:471) ~[?:?] at org.bukkit.plugin.java.PluginClassLoader.findClass(PluginClassLoader.java:171) ~[server.jar:git-Paper-385] at org.bukkit.plugin.java.PluginClassLoader.findClass(PluginClassLoader.java:100) ~[server.jar:git-Paper-385] at java.lang.ClassLoader.loadClass(ClassLoader.java:588) ~[?:?] at java.lang.ClassLoader.loadClass(ClassLoader.java:521) ~[?:?] at com.elytraforce.voidcore.commands.TeleportCommands$VisitCommand.onCommand(TeleportCommands.java:59) ~[?:?] at org.bukkit.command.PluginCommand.execute(PluginCommand.java:45) ~[server.jar:git-Paper-385] ... 17 more

As well as the compiler log

[INFO] --- maven-shade-plugin:3.1.0:shade (default) @ voidcore --- [INFO] Including com.intellectualsites.fawe:FAWE-Bukkit:jar:1.16-434 in the shaded jar. [INFO] Replacing original artifact with shaded artifact. [INFO] Replacing C:\Users\Matt\VoidCore\target\VoidCore-1.0.0-SNAPSHOT.jar with C:\Users\Matt\VoidCore\target\voidcore-1.0.0-SNAPSHOT-shaded.jar [INFO] Dependency-reduced POM written at: C:\Users\Matt\VoidCore\target\dependency-reduced-pom.xml [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 49.328 s [INFO] Finished at: 2020-11-08T20:34:22-07:00 [INFO] ------------------------------------------------------------------------

Empty element in OutlinePane via XML causes error

When adding an empty element to an outline pane in an XML file, this causes an exception to occur when this XML file is being loaded. This is because gui items don't accept air items, since they can't store persistent data.

A possible workaround would be to not set the persistent data for air items. This is unnecessary to begin with, since air items cannot be moved and therefore do not need to be tracked.

Example code that causes such an error:

<gui title="test" rows="1" type="chest">
  <outlinepane x="0" y= "0" length="1" height="1">
    <empty />
  </outlinepane>
</gui>

Stacktrace: https://pastie.io/qgvtbz.txt

Items not cleared after updating

After updating a gui, the previous items in the underlying inventory component(s) are not removed. This can cause issues when spots previously occupied by an item, are now no longer occupied. In such a case, the previous item will render again erroneously. A good example is making a pane invisible,. Currently unless the spots the pane occupied are overwritten, the pane will appear to stay as is, even after an update.

XML "Placeholders"

Using properties, it's possible to transfer data from the XML to the plugin.

Is the reverse also possible?

For example, if I wanted to use the return string of a method named "getPlayersBalance" to populate the lore of an item in my XML representation of the GuiItem

Players receiving items while their inventory is also used as a GUI

What happens while players receive items while their inventory (the player inventory) is also used as a GUI?

As far as I know, there are two main ways in which players can receive items:

  • PlayerPickupItemEvent: this event could simply be cancelled while such an IF GUI is opened
  • Inventory#addItem or equivalent methods: we could attempt to detect foreign items in the GUI when it is updated/closed and just drop them on the ground

Still an issue: Inventory#removeItem, which might be used for eg. item-based economies.

The best solution would be to publish the GUI to the player using packets, in which case the Bukkit API method calls from other plugins, etc would still act upon the real player GUI. But such a system is complicated and probably beyond the scope of this project, especially due to maintenance costs.

I personally believe the best course of action, if it's not already done, is to cancel the PlayerPickupItemEvent if such a IF GUI is open, and leave this issue at that. The other solutions are too complicated and the problems they solve are probably orders of magnitude rarer.

InventoryDragEvent is not handled/cancelled

The listener currently does not seem to handle or cancel the InventoryDragEvent. This could lead to cases where a player could insert (and loose) items into the GUI or even extracting them as dragging of an item (even for a single pixel!) does not call a click event anymore but a drag event. The best solution imo. would be to just cancel it and handle it as if it was clicked if only one item was dragged. (This would allow accidental drags that are meant to be clicks but resulted in slight mouse movement to still be counted as clicks)

Displaying percentage bar throws exception

When trying to open a gui with a percentage bar in it, an exception will be thrown. This is caused by not properly resizing the mask when an outline pane is resized, causing lookups to invalid indices. When resizing an outline pane via OutlinePane#setLength and OutlinePane#setHeight, the mask should be properly resized as well, to keep the mask size and outline pane size consistent.

Code to reproduce:

ChestGui gui = new ChestGui(6, "Gui");

PercentageBar percentageBar = new PercentageBar(1, 3, 7, 1);
percentageBar.setPercentage(0.5f);

gui.addPane(percentageBar);

gui.show(humanEntity);

The stacktrace can be found here: https://pastie.io/fskbcm.groovy

(Issue reported by DMtp.)

PaginatedPane inside of MasonryPane does not show updates

When switching between pages in a PaginatedPane thats inside of a MasonryPane the page index is updated but the items in the GUI stay the same.

It seems that MasonryPane ignores index of the paginated pane when its already shown.
recreating the Gui and setting the paginated pane index before showing it works fine

Discussion: registration of events listeners and the plugin instance required for that

An instance of Plugin is required to register an event listener. At the moment only one listener exists in IF: GuiListener, so I will exclusively use that in my examples. The Gui class has a static hasRegisteredListeners boolean variable that is used to register an event listener if one hasn't been registered yet. The plugin instance is the one passed into the Gui constructor. What happens if that plugin gets disabled? Even if we ignore the broken reload command and the even better PlugMan plugin, plugins are still disabled if they throw an exception in their onEnable(). If the plugin associated with IF's event listener is disabled, IF's event listener gets unregistered without IF knowing about it. I see three options:

  • don't rely on a real plugin, try to "mock" one for Bukkit (it's a hackish solution, but luckily only the server itself can have access to this "virtual" plugin instance)
  • listen to PluginDisableEvent, migrate to another plugin and register a new listener when it's called (this solution seems a bit complicated, could go wrong a thousand ways)
  • don't worry about this, since if a plugin gets disabled, something's wrong and IF can't make itself that much fool- and bugproof

I would be more than happy to implement and PR whatever we decide on (if it's not the "don't worry about this" option, of course). This is a place for arguments and counter-arguments.

Discussion: should all callbacks be wrapped so that exceptions are caught

I personally strongly believe that all callbacks should be wrapped in try-catch blocks so that even if these 3rd party code sections throw exceptions, the rest of the code continues as normal. This way we don't have to worry about IF code being interrupted, left in an intermediate, broken state. The plugin's name and if possible, some sort of gui identifier (eg. the associated class's simple name) should be included the error messages that the catch blocks produce.

Sometimes the best way to achieve this wrapping/boxing is to not expose a getter to the callback, but a method that calls the callback instead. This breaks all logic that relies on the getters, the real question is whether anything at all relies on it. Eg. GuiItem#getAction() returns a non-null value (it internally creates an empty lambda), in that case relying on the return value is very hard and probably no one does it. It might be possible in connection with nullable getters though.

List of what would be affected: (hopefully this is a complete list)

  • GuiItem#getAction()
  • Pane#onClick (protected variable)
  • Gui#getOnClose()
  • Gui#getOnOutsideClick()
  • Gui#getOnGlobalClick()
  • Gui#getOnBottomClick()
  • Gui#getOnTopClick()

I would be more than happy to implement and PR this, the question is whether I should. This is a place for arguments and counter-arguments.

Add populateWithGuiItem method to PaginatedPane

Would it be possible to implement a method that lets us populate a paginated pane with gui items? this would allow us to populate easily with events. Otherwise we need to add panes manually and populate them manually as well since theres no way to attach events.

A dumb issue incoming...

Yeah, not the best title...

Well, I've been having some issues looping through a Set and adding items into a static pane, my math is probably all wrong but it works very well when I enter the number itself and not by my math stuff, although I've debugged and my math doesn't seem to be wrong since it prints out the correct numbers.

Screenshot of the current issue: https://gyazo.com/ee658c4b25df45e4646d5c10b8fd50dc
My code for the explanation above is: https://sourceb.in/6e9cb16ecd
( The github code selector didn't want to work )

Any assistance would be lovely at this point, thanks in advance.
~ Sincerely, RarLab.

A Quick Question

Hello!
I'm currently working on a project where I'd like to have an item that, when clicked, displays a pane underneath. I just wanted to see if it was possible to add a pane to a GUI that someone currently has open (to specify, it would only appear for that one player, as a new GUI is created each time a player opens it)
Thanks

Dragging into inventory bypasses setCancelled

https://youtu.be/Oq2GrtH9gK4
This video shows that dragging items into an inventory where gui.setOnTopClick(e -> e.setCancelled(true)); is used gets bypassed.

I suggest adding:

gui.setOnTopDrag(event -> {});
gui.setOnBottomDrag(event -> {});
gui.setOnGlobalDrag(event -> {});

so you can cancel Inventory drags separately from Inventory clicks.

Usage example:

gui.setOnGlobalDrag(event -> e.setCancelled(true));

cancels all inventory drags.

Pane `click` uses incorrect length attribute in coordinate calculation

The click calculation of panes such as StaticPane uses the incorrect length variable in the last stage of calculation.

public boolean click(@NotNull Gui gui, @NotNull InventoryComponent inventoryComponent,
@NotNull InventoryClickEvent event, int slot, int paneOffsetX, int paneOffsetY, int maxLength,
int maxHeight) {
int length = Math.min(this.length, maxLength);
int height = Math.min(this.height, maxHeight);
int adjustedSlot = slot - (getX() + paneOffsetX) - inventoryComponent.getLength() * (getY() + paneOffsetY);
int x = adjustedSlot % length;
int y = adjustedSlot / length;
//this isn't our item
if (x < 0 || x >= length || y < 0 || y >= height) {
return false;
}

Lines 125 and 126 should be using inventoryComponent.getLength() instead of length.

Below is an example of the values generated by this formula on an example 9x6 inventory (the '5x3 test pane' is highlighted in blue):
image
Top left are the slot values for each inventory slot, top middle are the adjustedSlot values, top right are the x and y values for the respective slot, and bottom right are the x and y values for the respective slot using inventoryComponent.getLength() instead of length.

Testing the original version in-game yields unexpected results (the behavior is correctly predicted by the top right table), and testing a version using a custompane with the different click calculation produces the expected behaviour.
I haven't done in-game testing of the other pane types, but as they share this code I imagine they also have this logical error.

Workaround SPIGOT-4274

Hi there,

Since you added non-chest inventory types to your framework, I wanted to make you aware of SPIGOT-4274. I encountered this regression in Spigot myself when I worked on my own inventory gui framework. Paper has patched it, but it seems that md_5 considers it a wontfix for Spigot.

An example plugin that triggers the issue:

package com.janboerman.iftest;

import org.bukkit.plugin.java.JavaPlugin;
import org.bukkit.event.Listener;
import org.bukkit.event.EventHandler;
import org.bukkit.event.player.PlayerJoinEvent;
import org.bukkit.inventory.ItemStack;
import org.bukkit.Material;

import com.github.stefvanschie.inventoryframework.gui.*;
import com.github.stefvanschie.inventoryframework.gui.type.*;
import com.github.stefvanschie.inventoryframework.pane.*;
import com.github.stefvanschie.inventoryframework.pane.OutlinePane;


public final class IFTest extends JavaPlugin implements Listener {

    @Override
    public void onEnable() {
        // Plugin startup logic

        getServer().getPluginManager().registerEvents(this, this);
    }

    @EventHandler
    public void onJoin(PlayerJoinEvent joinEvent) {
        getServer().getScheduler().runTaskLater(this, () -> {
            //create a hopper gui and the title My GUI
            HopperGui gui = new HopperGui("My GUI");
            //create a new pane occupying the entire gui
            OutlinePane pane = new OutlinePane(0, 0, 5, 1);
            ItemStack item = new ItemStack(Material.ICE);
            //create an item which will send a message when clicked
            GuiItem guiItem = new GuiItem(item, event -> event.getWhoClicked().sendMessage("You clicked on ice!"));
            //add the item to the pane
            pane.addItem(guiItem);
            //add the pane to the gui
            gui.getSlotsComponent().addPane(pane);

            //open the gui!
            gui.show(joinEvent.getPlayer());

        }, 20L);
    }
}

I worked around this bug in Spigot by storing the custom inventoryholders in a WeakHashMap<Inventory, WeakReference<GuiInventoryHolder>> in the GuiListener if the Inventory itself does not store the InventoryHolder internally (it's a WeakReference because entries won't get removed from the map automatically if a value has a strong reference to a key). See GuiListener.

Edit:
One other possbile way to fix it is to set the InventoryHolder in the nms class for the inventory. I didn't want to resort to NMS for my plugin, but for you this might be a viable alternative.

Groetjes,
Jan

Open new gui when the other is open

Hi, I have a problem to open new gui while another gui is already open.

This problem happens because of this feature: https://github.com/stefvanschie/IF/blob/master/IF/src/main/java/com/github/stefvanschie/inventoryframework/gui/GuiListener.java#L310-L314

To avoid the problem problem, I used below code in my plugin, but in my opinion its not good solution:

        player.closeInventory();

        Bukkit.getScheduler().runTask(JavaPlugin.getProvidingPlugin(this.getClass()), () ->
        {
            Gui menu = this.getGui();
            menu.show(player);
        });

According to me the problem should be resolved in inventory framework because I lost long time to find solution and it hinders implementation.

Discord

Hi, i was just wondering if u have a discord where u can ask questions and stuff like that cuz i don't see a link to one. (not that i have any questions rn but there will certainly be some in the future.

Resetting the title of a gui doesn't update other parts of the gui

When setting the name of a gui via NamedGui#setTitle, any other changes that were done in between the last Gui#update call and this NamedGui#setTitle invocation will not be updated in the newly refreshed gui. This is unlike the javadocs and wiki state, which both specify that calling this method will automatically update the gui as well.

The framework doesn't cancel the click event

I might be stupid for saying this but as I added the framework as a dependency and made a gui object, set it up and showed it to the player, I can grab the things from inside etc (the usual nonworking-gui stuff). I wandered around your code and I cannot find any parts cancelling the event. Isn't the gui supposed to have clickable items which are unobtainable from the player?

Click outside make error

This happen when i try to click outside ui
can you help me with ##this
Error : Could not pass event InventoryClickEvent java.lang.NullPointerException: null

GuiItem click events seemingly stopped firing after updating

After updating to 8.0.0, all my click events stopped working, I was unable to make them fire at all. Copied the first navigation example from the wiki and seems like the issue was still present, so it shouldn't be because of my code. So the gui opens up just fine, but no click actions fire and no errors show up.

So to reproduce, just create the navigator example from wiki and add for example logs in click events, I didn't manage to get them firing. I was using ChestGui and 1.16.2.

Opening/updating an empty inventory results in a NPE

lets say you put a few items in a GUI and let users remove those items. Once the last item is removed, after calling the Gui#update() method, you'll get an NPE. This also happens when just trying to display an empty gui.

Stacktrace:

org.bukkit.event.EventException
        at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:306) ~[spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at org.bukkit.plugin.RegisteredListener.callEvent(RegisteredListener.java:62) ~[spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at org.bukkit.plugin.SimplePluginManager.fireEvent(SimplePluginManager.java:502) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at org.bukkit.plugin.SimplePluginManager.callEvent(SimplePluginManager.java:487) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.PlayerConnection.a(PlayerConnection.java:1849) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.PacketPlayInWindowClick.a(SourceFile:33) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.PacketPlayInWindowClick.a(SourceFile:10) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.PlayerConnectionUtils$1.run(SourceFile:13) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source) [?:1.8.0_202]
        at java.util.concurrent.FutureTask.run(Unknown Source) [?:1.8.0_202]
        at net.minecraft.server.v1_11_R1.SystemUtils.a(SourceFile:46) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.MinecraftServer.D(MinecraftServer.java:747) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.DedicatedServer.D(DedicatedServer.java:399) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.MinecraftServer.C(MinecraftServer.java:678) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at net.minecraft.server.v1_11_R1.MinecraftServer.run(MinecraftServer.java:576) [spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        at java.lang.Thread.run(Unknown Source) [?:1.8.0_202]
Caused by: java.lang.NullPointerException
        at ca.yomnetwork.mythicshop.inventoryframework.pane.PaginatedPane.display(PaginatedPane.java:170) ~[?:?]
        at ca.yomnetwork.mythicshop.inventoryframework.Gui.lambda$show$0(Gui.java:160) ~[?:?]
        at java.util.stream.ForEachOps$ForEachOp$OfRef.accept(Unknown Source) ~[?:1.8.0_202]
        at java.util.stream.ReferencePipeline$2$1.accept(Unknown Source) ~[?:1.8.0_202]
        at java.util.ArrayList$ArrayListSpliterator.forEachRemaining(Unknown Source) ~[?:1.8.0_202]
        at java.util.stream.AbstractPipeline.copyInto(Unknown Source) ~[?:1.8.0_202]
        at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source) ~[?:1.8.0_202]
        at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source) ~[?:1.8.0_202]
        at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source) ~[?:1.8.0_202]
        at java.util.stream.AbstractPipeline.evaluate(Unknown Source) ~[?:1.8.0_202]
        at java.util.stream.ReferencePipeline.forEach(Unknown Source) ~[?:1.8.0_202]
        at ca.yomnetwork.mythicshop.inventoryframework.Gui.show(Gui.java:160) ~[?:?]
        at java.lang.Iterable.forEach(Unknown Source) ~[?:1.8.0_202]
        at ca.yomnetwork.mythicshop.inventoryframework.Gui.update(Gui.java:238) ~[?:?]
        at ca.yomnetwork.mythicshop.command.SurvivorShop.lambda$onCommand$1(SurvivorShop.java:100) ~[?:?]
        at ca.yomnetwork.mythicshop.inventoryframework.GuiListener.onInventoryClick(GuiListener.java:55) ~[?:?]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[?:1.8.0_202]
        at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_202]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source) ~[?:1.8.0_202]
        at java.lang.reflect.Method.invoke(Unknown Source) ~[?:1.8.0_202]
        at org.bukkit.plugin.java.JavaPluginLoader$1.execute(JavaPluginLoader.java:302) ~[spigot-1.11.2.jar:git-Spigot-3fb9445-6e3cec8]
        ... 15 more```

ToggleButton and dumb Material...

ToggleButton referring to Material for GREEN_STAINED_GLASS_PANE or RED_STAINED_GLASS_PANE, but fields not available on some versions or has changed to another, we need to move it to constructor or something else to prevent NoSuchFieldException on some versions.

Discussion: use of Bukkit's API wrapper instead of MiniNBT

Since Bukkit has its own "NBT API", we should consider using it instead of a third-party library. This Bukkit one is called PersistentDataContainer or org.bukkit.persistence. Using it requires a Plugin instance for creating a NamespacedKey, but that should be no issue: Gui instances already keep a reference to the plugin that created them. This feature does not have any of the issues described in #45.

I would be more than happy to implement and PR this, I am just asking for input regarding it. This is a place for discussion.

Suggestion: making actions not final

If i understood how it all works correctly, right now when making a copy of gui, the actions called on click still reference original class, where the gui was constructed from xml.
I think an option to update the action performed by button after copying the gui would help a lot.

Broken item equality check on 1.12

Item equality comparisons on 1.12 for some items are broken.
https://github.com/stefvanschie/IF/blob/master/src/main/java/com/github/stefvanschie/inventoryframework/pane/OutlinePane.java#L213 this line will always return false when comparing broken item in the code below, tested StaticPane and OutlinePane, didn't work for both.

Code:

        Gui brokenGui = new Gui(plugin, 2, "test");
        OutlinePane pane = new OutlinePane(9, 2);
        brokenGui.addPane(pane);
        brokenGui.setOnGlobalClick(e -> Bukkit.broadcastMessage("Global click"));

        ItemStack brokenBanner = new ItemStack(Material.BANNER);
        BannerMeta bannerMeta = (BannerMeta) brokenBanner.getItemMeta();
        bannerMeta.setBaseColor(DyeColor.ORANGE);
        bannerMeta.addPattern(new Pattern(DyeColor.LIGHT_BLUE, PatternType.CREEPER));
        brokenBanner.setItemMeta(bannerMeta);
        pane.addItem(new GuiItem(brokenBanner, e -> Bukkit.broadcastMessage("Banner click")));
        
        ItemStack workingItem = new ItemStack(Material.DIAMOND);
        ItemMeta meta = workingItem.getItemMeta();
        meta.setDisplayName("test test");
        meta.setLore(Arrays.asList("testtt"));
        workingItem.setItemMeta(meta);
        pane.addItem(new GuiItem(workingItem, e -> Bukkit.broadcastMessage("Working diamond click")));
        brokenGui.show(player);

Expected behaviour:

  1. Broadcast "Global click" on GUI click
  2. Broadcast "Banner click" on banner click
  3. Broadcast "Working diamond click" on diamond click

Actual behaviour:

  1. Broadcast "Global click" on GUI click
  2. Broadcast "Working diamond click" on diamond click

Affected versions:
1.12 only, tested on 1.13 with BLACK_BANNER as an item and worked

I applied a temporary fix for this issue by removing item equality comparison and it was working, not sure if it's safe though.

Working with playerheads

Hello,

Player heads are causing an issue with IF, whenever I set the playerhead's owner, when showing the GUI, it changes the item as it has to show the player.
Then the system in IF for checking if the item stack is equal no longer works.
Since the head is changed. What can we do about tracking this somehow and making it recognize, as the head will no longer use its assigned code for cancelling event etc.

Thanks,
ProSavage

Example on the wiki

I folowed the emxaple on the wiki so I have this

`public class BankGui {
public static Gui bankGUI;

public BankGui() {
    bankGUI = new Gui(6, "Test Bank GUI");
    OutlinePane pane = new OutlinePane(0, 0, 9, 6);
    ItemStack item = new ItemStack(Material.ICE);
    GuiItem guiItem = new GuiItem(item, e -> e.getWhoClicked().sendMessage(ChatColor.AQUA + "YOU CLICKED ON ICE"));
    pane.addItem(guiItem);
    bankGUI.addPane(pane);
}

public void openBankGui(Player p) {
    bankGUI.show(p);
}

}`

And when I create a new BankGui and then call openBankGui I guess an error saying null...

It also did this with other librairies .. Am I missing something ? Might be me just being dumb but if anyone can help would be appreciated :)

IllegalAccessError: no such method

Hi,

I'm trying to use this framework but I keep getting this error:

java.lang.BootstrapMethodError: java.lang.IllegalAccessError: no such method: net.matixmedia.mlgrush.inventoryframework.pane.MasonryPane.load(Object,Element)MasonryPane/invokeStatic

I've already tried version 0.8.0 and 0.9.0 but the same error reoccurs.

I've already checked the jar and decompiled it, but the library is where it should be. If you need the pom.xml dependency:

<dependency>
    <groupId>com.github.stefvanschie.inventoryframework</groupId>
    <artifactId>IF</artifactId>
    <version>0.9.0</version>
</dependency>

Where I create the GUI:

mapSelectionGUI = new ChestGui(5, ChatColor.DARK_AQUA + "Select a map"); // Error occurses here.
OutlinePane mapsPane = new OutlinePane(0, 0, 9, 5);
mapManager.getMaps().forEach(map -> {
    ItemStack item = new ItemBuilder(Material.PAPER, 0).setName(ChatColor.AQUA + map.getName())
            .setLore(ChatColor.GRAY + "Right/Left click to select").toItemStack();

    GuiItem gItem = new GuiItem(item, event -> {
        if (votes.containsKey(map.getName())) {
            votes.put(map.getName(), votes.get(map.getName()) + 1);
        } else {
            votes.put(map.getName(), 1);
        }
        if (event.getCurrentItem() != null) {
            event.getCurrentItem().setAmount(votes.get(map.getName()));
        }
        event.getWhoClicked().sendMessage(Main.getPrefix() + "You voted for " + ChatColor.GREEN +
                map.getName() + ChatColor.GRAY + "!");
    });
    mapsPane.addItem(gItem);
});
mapSelectionGUI.addPane(mapsPane);

I hope you can help me out!

setOnClose not working in version 0.9.0

@Override public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { ChestGui cg = new ChestGui(3, "test"); cg.setOnClose(e -> System.out.println("TEST"); }); cg.show((HumanEntity) sender); return true; }
I was using version 0.9.0 and "TEST" was not being printed to the terminal when the gui was closed. I then switched back to version 0.8.0 and it then worked. (Tested on spigot 1.16.4)
UPDATE - Just tested with version 0.8.1 and it appears to have been broken here.

Losing ItemMeta and lore with MiniNBT

By using MiniNBT it seems that you cannot set ItemMeta and lore anymore, I tried to change the code like this (at line 58~):

NBTWrappers.NBTTagCompound compound = ItemNBTUtil.getTag(item);
compound.setString("IF-uuid", uuid.toString());

this.item = ItemNBTUtil.setNBTTag(compound, item);

Now you will not lose ItemMeta data but only lore.

Suggestion: Ability to clone GUIs

Let's say i have some basic GUI which will have some information different for all of the players - such as a playerhead and some texts inside of it.
Right now, i need to construct a fresh new GUI from existing XML template, and then manually update it for my needs for each player issuing a command.
This means that each time someone runs a command, there happens a file read, which isnt that good is it?
What i suggest, is the ability to create a copy of an existing gui instance, allowing to store one instance of a gui, and make a copy of it for each player who i need to show it to, and then destroy it when it isnt needed anymore.

XML item onClick methods do not accept all possible InventoryClickEvent variations

When referring to a to be invoked method for an item click in an XML file, the optional first parameter for the underlying InventoryClickEvent only accepts InventoryClickEvents and not any super type (e.g., Cancellable). This should be changed so that super types of InventoryClickEvent are also accepted as possible parameter values.

[Feature Inquiry] Refreshing Manipulated Inventory

Hey there,

After some testing, I have come to realize that the update() method only works on GUI's that have been manipulated programatically. Currently I have a plugin that uses IF to load items into a virtual inventory, where players can edit these items, and later save them back to the config. Thing is, If I open the inventory using IF, remove items, and run GUI.update(), once the GUI is shown to another user, the changes are reset back to it's initial loaded state. Is there any possibility update() could also refresh backwards?

Let me know if this can be done, thanks in advanced!

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.