From 2d1d2287f7f69ab969c4ea5b302ec1daf1c633b0 Mon Sep 17 00:00:00 2001 From: rozhur Date: Fri, 11 Aug 2023 19:38:17 +0500 Subject: [PATCH] Bukkit utilities for creating GUI --- .../varioutil/bukkit/LegacyMaterial.java | 51 ++++++++ .../varioutil/bukkit/gui/ClickHandler.java | 8 ++ .../org/zhdev/varioutil/bukkit/gui/Gui.java | 95 ++++++++++++++ .../zhdev/varioutil/bukkit/gui/GuiConfig.java | 63 +++++++++ .../zhdev/varioutil/bukkit/gui/GuiHolder.java | 23 ++++ .../zhdev/varioutil/bukkit/gui/GuiIcon.java | 120 ++++++++++++++++++ .../varioutil/bukkit/gui/GuiIconConfig.java | 54 ++++++++ .../varioutil/bukkit/gui/GuiListener.java | 59 +++++++++ .../zhdev/varioutil/bukkit/gui/GuiType.java | 41 ++++++ 9 files changed, 514 insertions(+) create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/LegacyMaterial.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/ClickHandler.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/Gui.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiConfig.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiHolder.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIcon.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIconConfig.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiListener.java create mode 100644 bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiType.java diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/LegacyMaterial.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/LegacyMaterial.java new file mode 100644 index 0000000..c6f1fef --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/LegacyMaterial.java @@ -0,0 +1,51 @@ +package org.zhdev.varioutil.bukkit; + +import org.bukkit.Material; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +public enum LegacyMaterial { + SKULL("PLAYER_HEAD"); + + private static final Map BY_NAME = new HashMap<>(); + + private final Material material; + private final String[] aliases; + + LegacyMaterial(String... aliases) { + Material material = Material.getMaterial(name()); + if (material == null) { + for (String name : aliases) { + material = Material.getMaterial(name); + if (material != null) break; + } + if (material == null) { + throw new IllegalStateException("Unknown material names: " + Arrays.toString(aliases)); + } + } + this.material = material; + this.aliases = aliases; + } + + public Material getMaterial() { + return material; + } + + public static Material of(String name) { + Material material = BY_NAME.get(name); + if (material == null) { + material = Material.getMaterial(name); + } + return material; + } + + static { + for (LegacyMaterial material : values()) { + for (String alias : material.aliases) { + BY_NAME.put(alias, material.material); + } + } + } +} diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/ClickHandler.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/ClickHandler.java new file mode 100644 index 0000000..7c9aef7 --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/ClickHandler.java @@ -0,0 +1,8 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.bukkit.event.inventory.InventoryClickEvent; + +import java.util.function.Function; + +@FunctionalInterface +public interface ClickHandler extends Function { } diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/Gui.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/Gui.java new file mode 100644 index 0000000..7648455 --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/Gui.java @@ -0,0 +1,95 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.HashMap; +import java.util.Map; + +public class Gui { + final GuiHolder holder = new GuiHolder(this); + + private final Map iconMap = new HashMap<>(); + + private String title; + private GuiType type = GuiType.SIZE_27; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public GuiType getType() { + return type; + } + + public void setType(GuiType type) { + this.type = type; + } + + public GuiIcon getIcon(int slot) { + return iconMap.get(slot); + } + + public GuiIcon removeIcon(int slot) { + GuiIcon existsIcon = iconMap.remove(slot); + holder.inventory.setItem(slot, null); + return existsIcon; + } + + public void updateIconIfChanged(int slot, GuiIcon icon) { + ItemStack exists = holder.inventory.getItem(slot); + if (exists != null && exists.getType() != Material.AIR && exists.equals(icon.stack)) { + return; + } + iconMap.put(slot, icon); + holder.inventory.setItem(slot, icon.stack); + } + + public void updateIcon(int slot, GuiIcon icon) { + iconMap.put(slot, icon); + holder.inventory.setItem(slot, icon.stack); + } + + public void createInventory() { + holder.inventory = type.createInventory(holder, title); + } + + public void updateChangedIcons() { + for (int i = 0; i < holder.inventory.getSize(); i++) { + GuiIcon icon = iconMap.get(i); + ItemStack exists = holder.inventory.getItem(i); + if (icon == null) { + if (exists != null) holder.inventory.setItem(i, null); + continue; + } + if (exists != null && exists.getType() != Material.AIR && exists.equals(icon.stack)) { + continue; + } + holder.inventory.setItem(i, icon.stack); + } + } + + public void update() { + for (int i = 0; i < holder.inventory.getSize(); i++) { + GuiIcon icon = iconMap.get(i); + if (icon == null) { + holder.inventory.setItem(i, null); + } else { + holder.inventory.setItem(i, icon.stack); + } + } + } + + public void open(Player viewer) { + Inventory inventory = viewer.getOpenInventory().getTopInventory(); + if (inventory.getHolder() != holder) { + viewer.openInventory(holder.inventory); + } + } +} diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiConfig.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiConfig.java new file mode 100644 index 0000000..f3dbe05 --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiConfig.java @@ -0,0 +1,63 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.zhdev.varioutil.config.ConfigSection; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class GuiConfig { + private final GuiType type; + private final Map iconMap = new HashMap<>(); + private final Map charToSlotMap = new HashMap<>(); + + public GuiConfig(ConfigSection config) { + ConfigSection itemsSection = config.getOrCreateSection("items"); + List list = config.getList("shape", Collections.emptyList()); + int x = 0, maxSlot = 0; + for (; x < list.size(); x++) { + String l = String.valueOf(list.get(x)); + int y; + for (y = 0; y < l.length(); y++) { + char c = l.charAt(y); + if (c == ' ') continue; + ConfigSection iconSection = itemsSection.getOrCreateSection(String.valueOf(c)); + GuiIconConfig iconConfig = new GuiIconConfig(iconSection); + int slot = x * 9 + y; + iconMap.put(slot, iconConfig); + charToSlotMap.put(c, slot); + } + if (y > maxSlot) maxSlot = y; + } + + GuiType type; + try { + if (x == 1 && maxSlot < 6) { + type = GuiType.SIZE_5; + } else if (x == 3 && maxSlot < 4) { + type = GuiType.SIZE_3X3; + } else if (x > 0) { + type = GuiType.valueOf("SIZE_" + (x * 9)); + } else { + type = GuiType.SIZE_9; + } + } catch (IllegalArgumentException e) { + type = GuiType.SIZE_9; + } + + this.type = type; + } + + public GuiType getType() { + return type; + } + + public GuiIconConfig getIconConfig(int slot) { + return iconMap.get(slot); + } + + public int getSlot(char c) { + return charToSlotMap.get(c); + } +} diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiHolder.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiHolder.java new file mode 100644 index 0000000..ab31d90 --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiHolder.java @@ -0,0 +1,23 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.bukkit.Bukkit; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.jetbrains.annotations.NotNull; + +public class GuiHolder implements InventoryHolder { + private static final Inventory FALLBACK_INVENTORY = Bukkit.createInventory(null, 9, ""); + + final Gui gui; + Inventory inventory = FALLBACK_INVENTORY; + + GuiHolder(Gui gui) { + this.gui = gui; + } + + @NotNull + @Override + public Inventory getInventory() { + return inventory; + } +} diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIcon.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIcon.java new file mode 100644 index 0000000..698d3fe --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIcon.java @@ -0,0 +1,120 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.bukkit.Material; +import org.bukkit.inventory.ItemFlag; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.bukkit.inventory.meta.SkullMeta; +import org.zhdev.varioutil.util.BukkitUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +public class GuiIcon { + ItemStack stack; + ClickHandler clickHandler; + + public ItemStack stack() { + return stack; + } + + public GuiIcon stack(ItemStack stack) { + this.stack = stack; + return this; + } + + public StackBuilder stackBuilder(Material type) { + return new StackBuilder(type); + } + + public StackBuilder stackBuilder(Material type, Byte data) { + return new StackBuilder(type, data); + } + + public ClickHandler clickHandler() { + return clickHandler; + } + + public GuiIcon clickHandler(ClickHandler clickHandler) { + this.clickHandler = clickHandler; + return this; + } + + public class StackBuilder { + private final Material type; + private final Byte data; + private int amount = 1; + private String displayName; + private List lore; + private ItemFlag[] flags; + private String texture; + + private StackBuilder(Material type) { + this.type = type; + this.data = null; + } + + private StackBuilder(Material type, Byte data) { + this.type = type; + this.data = data; + } + + public StackBuilder amount(int amount) { + this.amount = amount; + return this; + } + + public StackBuilder displayName(String displayName) { + this.displayName = displayName; + return this; + } + + public StackBuilder lore(List lore) { + this.lore = lore; + return this; + } + + public StackBuilder lore(String lore) { + return lore(Collections.singletonList(lore)); + } + + public StackBuilder lore(String... lore) { + return lore(Arrays.asList(lore)); + } + + public StackBuilder flags(ItemFlag... flags) { + this.flags = flags; + return this; + } + + public StackBuilder flagsAll() { + this.flags = ItemFlag.values(); + return this; + } + + public StackBuilder texture(String texture) { + this.texture = texture; + return this; + } + + public GuiIcon build() { + ItemStack stack; + if (data == null) stack = new ItemStack(type, amount); + else stack = new ItemStack(type, amount, (short) 0, data); + ItemMeta meta = stack.getItemMeta(); + if (meta != null) { + meta.setDisplayName(displayName); + meta.setLore(lore); + meta.addItemFlags(flags); + if (texture != null && meta instanceof SkullMeta) { + BukkitUtils.setSkullTexture(((SkullMeta) meta), texture); + } + stack.setItemMeta(meta); + } + GuiIcon.this.stack = stack; + return GuiIcon.this; + } + } +} + diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIconConfig.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIconConfig.java new file mode 100644 index 0000000..6a4cd08 --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiIconConfig.java @@ -0,0 +1,54 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.bukkit.Material; +import org.zhdev.varioutil.bukkit.LegacyMaterial; +import org.zhdev.varioutil.config.ConfigSection; +import org.zhdev.varioutil.util.StringUtils; + +public class GuiIconConfig { + protected final Material type; + protected final Byte data; + protected final int amount; + protected final String texture; + + public GuiIconConfig(ConfigSection config) { + String[] rawType = config.getString("type", "AIR").toUpperCase().split(":", 2); + Material type = LegacyMaterial.of(rawType[0]); + Byte data = rawType.length > 1 ? StringUtils.parseByte(rawType[1]) : null; + if (type == null) type = Material.STONE; + int amount = config.getInteger("amount", 1); + String texture = config.getString("texture"); + this.type = type; + this.data = data; + this.amount = amount; + this.texture = texture; + } + + public Material getType() { + return type; + } + + public Byte getData() { + return data; + } + + public int getAmount() { + return amount; + } + + public String getTexture() { + return texture; + } + + public GuiIcon.StackBuilder stackBuilder() { + return new GuiIcon() + .stackBuilder(type, data) + .amount(amount) + .texture(texture) + .flagsAll(); + } + + public GuiIcon create() { + return stackBuilder().build(); + } +} diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiListener.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiListener.java new file mode 100644 index 0000000..72465b4 --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiListener.java @@ -0,0 +1,59 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.event.server.PluginDisableEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.plugin.Plugin; + +public class GuiListener implements Listener { + private final Plugin plugin; + + public GuiListener(Plugin plugin) { + this.plugin = plugin; + } + + @EventHandler + public void onClick(InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player)) { + return; + } + + if (!(event.getInventory().getHolder() instanceof GuiHolder)) { + return; + } + + switch (event.getAction()) { + case CLONE_STACK: case COLLECT_TO_CURSOR: case MOVE_TO_OTHER_INVENTORY: event.setCancelled(true); + break; + } + + int slot = event.getRawSlot(); + if (slot > event.getInventory().getSize() - 1) { + return; + } + + GuiHolder holder = (GuiHolder) event.getInventory().getHolder(); + GuiIcon icon = holder.gui.getIcon(slot); + if (icon == null || icon.clickHandler == null) { + event.setCancelled(true); + return; + } + + Boolean result = icon.clickHandler.apply(event); + event.setCancelled(result == null || result); + } + + @EventHandler + public void onPluginDisable(PluginDisableEvent event) { + if (event.getPlugin() == plugin) { + for (Player player : Bukkit.getOnlinePlayers()) { + Inventory inventory = player.getOpenInventory().getTopInventory(); + if (inventory.getHolder() instanceof GuiHolder) player.closeInventory(); + } + } + } +} diff --git a/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiType.java b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiType.java new file mode 100644 index 0000000..250be28 --- /dev/null +++ b/bukkit/src/main/java/org/zhdev/varioutil/bukkit/gui/GuiType.java @@ -0,0 +1,41 @@ +package org.zhdev.varioutil.bukkit.gui; + +import org.bukkit.Bukkit; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; + +public enum GuiType { + SIZE_3X3(9) { + @Override + public Inventory createInventory(InventoryHolder holder, String title) { + return Bukkit.createInventory(holder, InventoryType.WORKBENCH, title); + } + }, + SIZE_5(5) { + @Override + public Inventory createInventory(InventoryHolder holder, String title) { + return Bukkit.createInventory(holder, InventoryType.HOPPER, title); + } + }, + SIZE_9(9), + SIZE_18(18), + SIZE_27(27), + SIZE_36(36), + SIZE_45(45), + SIZE_54(54); + + private final int size; + + GuiType(int size) { + this.size = size; + } + + public int getSize() { + return size; + } + + public Inventory createInventory(InventoryHolder holder, String title) { + return Bukkit.createInventory(holder, size, title); + } +}