From 3d7027bf47d0dfd523a993fa9aa2cbd55a257cbc Mon Sep 17 00:00:00 2001 From: Auxilor Date: Fri, 16 Apr 2021 14:51:30 +0100 Subject: [PATCH] Added initial GUI system --- config/checkstyle/suppression.xml | 2 + .../willfp/eco/core/gui/menu/FillerMask.java | 52 ++++++++++ .../com/willfp/eco/core/gui/menu/Menu.java | 95 +++++++++++++++++++ .../com/willfp/eco/core/gui/slot/Slot.java | 63 ++++++++++++ .../com/willfp/eco/internal/gui/EcoMenu.java | 60 ++++++++++++ .../com/willfp/eco/internal/gui/EcoSlot.java | 61 ++++++++++++ .../willfp/eco/internal/gui/FillerSlot.java | 15 +++ .../willfp/eco/internal/gui/MenuHandler.java | 29 ++++++ .../willfp/eco/spigot/EcoSpigotPlugin.java | 15 +-- .../willfp/eco/spigot/gui/GUIListener.java | 63 ++++++++++++ 10 files changed, 449 insertions(+), 6 deletions(-) create mode 100644 eco-api/src/main/java/com/willfp/eco/core/gui/menu/FillerMask.java create mode 100644 eco-api/src/main/java/com/willfp/eco/core/gui/menu/Menu.java create mode 100644 eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java create mode 100644 eco-api/src/main/java/com/willfp/eco/internal/gui/EcoMenu.java create mode 100644 eco-api/src/main/java/com/willfp/eco/internal/gui/EcoSlot.java create mode 100644 eco-api/src/main/java/com/willfp/eco/internal/gui/FillerSlot.java create mode 100644 eco-api/src/main/java/com/willfp/eco/internal/gui/MenuHandler.java create mode 100644 eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/gui/GUIListener.java diff --git a/config/checkstyle/suppression.xml b/config/checkstyle/suppression.xml index 343b3f35..cdf4a9ce 100644 --- a/config/checkstyle/suppression.xml +++ b/config/checkstyle/suppression.xml @@ -8,6 +8,8 @@ + + diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/menu/FillerMask.java b/eco-api/src/main/java/com/willfp/eco/core/gui/menu/FillerMask.java new file mode 100644 index 00000000..8c858fb6 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/menu/FillerMask.java @@ -0,0 +1,52 @@ +package com.willfp.eco.core.gui.menu; + +import com.willfp.eco.internal.gui.FillerSlot; +import lombok.Getter; +import org.bukkit.Material; +import org.bukkit.inventory.ItemStack; +import org.bukkit.inventory.meta.ItemMeta; +import org.jetbrains.annotations.NotNull; + +public class FillerMask { + @Getter + private final FillerSlot[][] mask; + + public FillerMask(@NotNull final Material material, + @NotNull final Menu menu, + @NotNull final String... pattern) { + if (pattern.length != menu.getRows()) { + throw new IllegalArgumentException("Invalid amount of rows specified in pattern!"); + } + + if (material == Material.AIR) { + throw new IllegalArgumentException("Material cannot be air!"); + } + + mask = new FillerSlot[menu.getRows()][9]; + + ItemStack itemStack = new ItemStack(material); + ItemMeta meta = itemStack.getItemMeta(); + assert meta != null; + meta.setDisplayName(""); + itemStack.setItemMeta(meta); + + int row = 0; + + for (String patternRow : pattern) { + int column = 0; + if (pattern.length != 9) { + throw new IllegalArgumentException("Invalid amount of columns in pattern!"); + } + for (char c : patternRow.toCharArray()) { + if (c == '0') { + mask[row][column] = null; + } else if (c == '1') { + mask[row][column] = new FillerSlot(itemStack); + } else { + throw new IllegalArgumentException("Invalid character in pattern! (Must only be 0 and 1)"); + } + } + row++; + } + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/menu/Menu.java b/eco-api/src/main/java/com/willfp/eco/core/gui/menu/Menu.java new file mode 100644 index 00000000..b29daaf8 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/menu/Menu.java @@ -0,0 +1,95 @@ +package com.willfp.eco.core.gui.menu; + +import com.willfp.eco.core.gui.slot.Slot; +import com.willfp.eco.internal.gui.EcoMenu; +import com.willfp.eco.internal.gui.FillerSlot; +import org.bukkit.Material; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +public interface Menu { + int getRows(); + + Slot getSlot(int row, + int column); + + String getTitle(); + + Inventory open(@NotNull Player player); + + static Builder builder(final int rows) { + return new Builder(rows); + } + + class Builder { + private final int rows; + private String title = "Menu"; + private Slot[][] maskSlots; + private final Slot[][] slots; + private Consumer onClose = (event) -> { + }; + + Builder(final int rows) { + this.rows = rows; + this.slots = new Slot[rows][9]; + this.maskSlots = new Slot[rows][9]; + } + + public Builder setTitle(@NotNull final String title) { + this.title = title; + return this; + } + + public Builder setSlot(final int row, + final int column, + @NotNull final Slot slot) { + if (row < 0 || row > this.rows - 1) { + throw new IllegalArgumentException("Invalid row number!"); + } + + if (column < 0 || column > 8) { + throw new IllegalArgumentException("Invalid column number!"); + } + + slots[row][column] = slot; + return this; + } + + public Builder setMask(@NotNull final FillerMask mask) { + this.maskSlots = mask.getMask(); + return this; + } + + public Builder onClose(@NotNull final Consumer action) { + this.onClose = action; + return this; + } + + public Menu build() { + Slot[][] finalSlots = maskSlots; + for (int i = 0; i < slots.length; i++) { + for (int j = 0; j < slots[i].length; j++) { + Slot slot = slots[i][j]; + if (slot != null) { + finalSlots[i][j] = slot; + } + } + } + + for (int i = 0; i < finalSlots.length; i++) { + for (int j = 0; j < finalSlots[i].length; j++) { + if (finalSlots[i][j] == null) { + finalSlots[i][j] = new FillerSlot(new ItemStack(Material.AIR)); + } + } + } + + return new EcoMenu(rows, finalSlots, title, onClose); + } + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java new file mode 100644 index 00000000..e04c2ce6 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/core/gui/slot/Slot.java @@ -0,0 +1,63 @@ +package com.willfp.eco.core.gui.slot; + +import com.willfp.eco.internal.gui.EcoSlot; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +import java.util.function.BiConsumer; + +public interface Slot { + ItemStack getItemStack(); + + static Builder builder(@NotNull final ItemStack itemStack) { + return new Builder(itemStack); + } + + class Builder { + private final ItemStack itemStack; + + private BiConsumer onLeftClick = null; + + private BiConsumer onRightClick = null; + + private BiConsumer onShiftLeftClick = null; + + private BiConsumer onShiftRightClick = null; + + private BiConsumer onMiddleClick = null; + + Builder(@NotNull final ItemStack itemStack) { + this.itemStack = itemStack; + } + + public Builder onLeftClick(@NotNull final BiConsumer action) { + this.onLeftClick = action; + return this; + } + + public Builder onRightClick(@NotNull final BiConsumer action) { + this.onRightClick = action; + return this; + } + + public Builder onShiftLeftClick(@NotNull final BiConsumer action) { + this.onShiftLeftClick = action; + return this; + } + + public Builder onShiftRightClick(@NotNull final BiConsumer action) { + this.onShiftRightClick = action; + return this; + } + + public Builder onMiddleClick(@NotNull final BiConsumer action) { + this.onMiddleClick = action; + return this; + } + + public Slot build() { + return new EcoSlot(itemStack, onLeftClick, onRightClick, onShiftLeftClick, onShiftRightClick, onMiddleClick); + } + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/internal/gui/EcoMenu.java b/eco-api/src/main/java/com/willfp/eco/internal/gui/EcoMenu.java new file mode 100644 index 00000000..a9bfcc4d --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/internal/gui/EcoMenu.java @@ -0,0 +1,60 @@ +package com.willfp.eco.internal.gui; + +import com.willfp.eco.core.gui.menu.Menu; +import com.willfp.eco.core.gui.slot.Slot; +import lombok.Getter; +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryCloseEvent; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; + +import java.util.function.Consumer; + +public class EcoMenu implements Menu { + @Getter + private final int rows; + + private final Slot[][] slots; + + @Getter + private final String title; + + private final Consumer onClose; + + public EcoMenu(final int rows, + @NotNull final Slot[][] slots, + @NotNull final String title, + @NotNull final Consumer onClose) { + this.rows = rows; + this.slots = slots; + this.title = title; + this.onClose = onClose; + } + + @Override + public Slot getSlot(final int row, + final int column) { + if (row < 0 || row > this.rows - 1) { + throw new IllegalArgumentException("Invalid row number!"); + } + + if (column < 0 || column > 8) { + throw new IllegalArgumentException("Invalid column number!"); + } + + return slots[row][column]; + } + + @Override + public Inventory open(@NotNull final Player player) { + Inventory inventory = Bukkit.createInventory(null, rows * 9, title); + player.openInventory(inventory); + MenuHandler.registerMenu(inventory, this); + return inventory; + } + + public void handleClose(@NotNull final InventoryCloseEvent event) { + onClose.accept(event); + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/internal/gui/EcoSlot.java b/eco-api/src/main/java/com/willfp/eco/internal/gui/EcoSlot.java new file mode 100644 index 00000000..cc6ee9c1 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/internal/gui/EcoSlot.java @@ -0,0 +1,61 @@ +package com.willfp.eco.internal.gui; + +import com.willfp.eco.core.gui.slot.Slot; +import lombok.Getter; +import org.bukkit.event.inventory.InventoryClickEvent; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.function.BiConsumer; + +public class EcoSlot implements Slot { + @Getter + private final ItemStack itemStack; + + private final BiConsumer onLeftClick; + + private final BiConsumer onRightClick; + + private final BiConsumer onShiftLeftClick; + + private final BiConsumer onShiftRightClick; + + private final BiConsumer onMiddleClick; + + public EcoSlot(@NotNull final ItemStack itemStack, + @Nullable final BiConsumer onLeftClick, + @Nullable final BiConsumer onRightClick, + @Nullable final BiConsumer onShiftLeftClick, + @Nullable final BiConsumer onShiftRightClick, + @Nullable final BiConsumer onMiddleClick) { + this.itemStack = itemStack; + this.onLeftClick = onLeftClick == null ? ((event, slot) -> { }) : onLeftClick; + this.onRightClick = onRightClick == null ? ((event, slot) -> { }) : onRightClick; + this.onShiftLeftClick = onShiftLeftClick == null ? ((event, slot) -> { }) : onShiftLeftClick; + this.onShiftRightClick = onShiftRightClick == null ? ((event, slot) -> { }) : onShiftRightClick; + this.onMiddleClick = onMiddleClick == null ? ((event, slot) -> { }) : onMiddleClick; + } + + public void handleInventoryClick(@NotNull final InventoryClickEvent event) { + switch (event.getClick()) { + case LEFT: + this.onLeftClick.accept(event, this); + break; + case RIGHT: + this.onRightClick.accept(event, this); + break; + case SHIFT_LEFT: + this.onShiftLeftClick.accept(event, this); + break; + case SHIFT_RIGHT: + this.onShiftRightClick.accept(event, this); + break; + case MIDDLE: + this.onMiddleClick.accept(event, this); + break; + default: + break; + } + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/internal/gui/FillerSlot.java b/eco-api/src/main/java/com/willfp/eco/internal/gui/FillerSlot.java new file mode 100644 index 00000000..cdd5224a --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/internal/gui/FillerSlot.java @@ -0,0 +1,15 @@ +package com.willfp.eco.internal.gui; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class FillerSlot extends EcoSlot { + /** + * Create new filler slot. + * + * @param itemStack The ItemStack. + */ + public FillerSlot(@NotNull final ItemStack itemStack) { + super(itemStack, null, null, null, null, null); + } +} diff --git a/eco-api/src/main/java/com/willfp/eco/internal/gui/MenuHandler.java b/eco-api/src/main/java/com/willfp/eco/internal/gui/MenuHandler.java new file mode 100644 index 00000000..50de1cc7 --- /dev/null +++ b/eco-api/src/main/java/com/willfp/eco/internal/gui/MenuHandler.java @@ -0,0 +1,29 @@ +package com.willfp.eco.internal.gui; + +import com.willfp.eco.core.gui.menu.Menu; +import lombok.experimental.UtilityClass; +import org.bukkit.inventory.Inventory; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +@UtilityClass +public class MenuHandler { + private static final Map MENUS = new HashMap<>(); + + public void registerMenu(@NotNull final Inventory inventory, + @NotNull final Menu menu) { + MENUS.put(inventory, menu); + } + + public void unregisterMenu(@NotNull final Inventory inventory) { + MENUS.remove(inventory); + } + + @Nullable + public Menu getMenu(@NotNull final Inventory inventory) { + return MENUS.get(inventory); + } +} diff --git a/eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/EcoSpigotPlugin.java b/eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/EcoSpigotPlugin.java index 6f8b6a4c..f0914d04 100644 --- a/eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/EcoSpigotPlugin.java +++ b/eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/EcoSpigotPlugin.java @@ -25,6 +25,7 @@ import com.willfp.eco.spigot.eventlisteners.ArmorListener; import com.willfp.eco.spigot.eventlisteners.DispenserArmorListener; import com.willfp.eco.spigot.eventlisteners.EntityDeathByEntityListeners; import com.willfp.eco.spigot.eventlisteners.NaturalExpGainListeners; +import com.willfp.eco.spigot.gui.GUIListener; import com.willfp.eco.spigot.integrations.anticheat.*; import com.willfp.eco.spigot.integrations.antigrief.AntigriefCombatLogX; import com.willfp.eco.spigot.integrations.antigrief.AntigriefFactionsUUID; @@ -83,11 +84,6 @@ public class EcoSpigotPlugin extends EcoPlugin { @Override public void enable() { new CollatedRunnable(this); - this.getEventManager().registerListener(new NaturalExpGainListeners()); - this.getEventManager().registerListener(new ArmorListener()); - this.getEventManager().registerListener(new DispenserArmorListener()); - this.getEventManager().registerListener(new EntityDeathByEntityListeners(this)); - this.getEventManager().registerListener(new ShapedRecipeListener()); } @Override @@ -157,7 +153,14 @@ public class EcoSpigotPlugin extends EcoPlugin { @Override public List getListeners() { - return new ArrayList<>(); + return Arrays.asList( + new NaturalExpGainListeners(), + new ArmorListener(), + new DispenserArmorListener(), + new EntityDeathByEntityListeners(this), + new ShapedRecipeListener(), + new GUIListener(this) + ); } @Override diff --git a/eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/gui/GUIListener.java b/eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/gui/GUIListener.java new file mode 100644 index 00000000..6ae40529 --- /dev/null +++ b/eco-core/core-plugin/src/main/java/com/willfp/eco/spigot/gui/GUIListener.java @@ -0,0 +1,63 @@ +package com.willfp.eco.spigot.gui; + +import com.willfp.eco.core.EcoPlugin; +import com.willfp.eco.core.PluginDependent; +import com.willfp.eco.core.gui.menu.Menu; +import com.willfp.eco.internal.gui.EcoMenu; +import com.willfp.eco.internal.gui.EcoSlot; +import com.willfp.eco.internal.gui.MenuHandler; +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.inventory.InventoryCloseEvent; +import org.jetbrains.annotations.NotNull; + +public class GUIListener extends PluginDependent implements Listener { + /** + * Pass an {@link EcoPlugin} in order to interface with it. + * + * @param plugin The plugin to manage. + */ + public GUIListener(@NotNull final EcoPlugin plugin) { + super(plugin); + } + + @EventHandler + public void handleSlotClick(@NotNull final InventoryClickEvent event) { + if (!(event.getWhoClicked() instanceof Player)) { + return; + } + + if (event.getClickedInventory() == null) { + return; + } + Menu menu = MenuHandler.getMenu(event.getClickedInventory()); + if (menu == null) { + return; + } + + int row = Math.floorDiv(event.getSlot(), 9); + int column = event.getSlot() - (row * 9); + + EcoSlot slot = (EcoSlot) menu.getSlot(row, column); + event.setCancelled(true); + slot.handleInventoryClick(event); + } + + @EventHandler + public void handleClose(@NotNull final InventoryCloseEvent event) { + if (!(event.getPlayer() instanceof Player)) { + return; + } + + EcoMenu menu = (EcoMenu) MenuHandler.getMenu(event.getInventory()); + if (menu == null) { + return; + } + + menu.handleClose(event); + + this.getPlugin().getScheduler().run(() -> MenuHandler.unregisterMenu(event.getInventory())); + } +}