javaminecraftspigot

How to handle spigot inventory click without using the title?


I want to have an inventory for each players when using spigot. So I'm using this code:

Inventory inv = Bukkit.createInventory(null, 9, "Title name");

player.openInventory(inv);

And when click on it, I'm using:

@EventHandler
public void onPlayerClick(InventoryClickEvent event) {

   if (event.getCurrentItem() == null && event.getAction() != null) return;
 
   Player player = (Player) event.getWhoClicked();

   if (event.getView().getTitle().equalsIgnoreCase("Title name")) {
       // here I can manage the inventory
   }
}

But when I put variable in the title like that:

Inventory inv = Bukkit.createInventory(null, 9, "Title " + myVar);

It can break the click event, mostly when the event don't know the variable.

How can I fix it?


Solution

  • It's not recommended to use title for checking inventory. Instead, you can use the InventoryHolder which is attached on the inventory (each holder is unique).

    To do this, you should start by creating your inventory holder object. Example:

    public class MyHolder implements InventoryHolder {
        
        @Override
        public Inventory getInventory() {
            return null; // required by bukkit
        }
    }
    

    Then, to create your inventory, you can simply put it on the main argument of createInventory:

    Inventory inv = Bukkit.createInventory(new MyHolder(), 9, "Title " + myVar);
    // now we have the inventory
    

    For the event part, you have to check if it's an instance of:

    @EventHandler
    public void onInventoryClick(InventoryClickEvent e) {
        if (item == null || e.getClickedInventory() == null || !(e.getWhoClicked() instanceof Player))
            return; // skip if click outside of inventory
        Player p = (Player) e.getWhoClicked();
        InventoryHolder holder = e.getClickedInventory().getHolder();
        if(holder instanceof MyHolder) { // our holder!
            MyHolder nh = (MyHolder) holder;
        }
    }
    

    Useful trick: Add content in holder

    It enable to save all data that you want: list, object, map etc. No need to do json behavior, or having global list. Those variables are locale to the holder, and so for the inventory.

    For example, you want to list all players you showed on an inventory, you can do:

    public class MySecondHolder implements InventoryHolder {
        
        // the map that will contains our data
        public HashMap<Integer, Player> playerBySlot = new HashMap<>();
    
        @Override
        public Inventory getInventory() {
            return null;
        }
    }
    

    When creating the inventory:

    MySecondHolder holder = new MySecondHolder();
    Inventory inv = Bukkit.createInventory(holder, 9, "Players");
    int slot = 0;
    for(Player p : Bukkit.getOnlinePlayers()) {
       inv.setItem(slot, ItemUtils.createHeadFor(p)); // 'createHeadFor' is only for example, it doesn't exist
       holder.playerBySlot.put(slot, p); // add player into holder
       slot++; // next slot
    }
    player.openInventory(inv);
    

    Finally, on click you will be able to do that:

    InventoryHolder inventoryHolder = e.getClickedInventory().getHolder();
    if(inventoryHolder instanceof MySecondHolder) { // our holder!
        MySecondHolder holder = (MySecondHolder) inventoryHolder;
        Player cible = holder.playerBySlot.get(event.getSlot());
        if(cible == null) {
            // no player on this slot
        } else {
            // you know which player were at this slot!
        }
    }