Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Nickcolour gui #115

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion src/main/java/dev/majek/hexnicks/HexNicks.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
import dev.majek.hexnicks.api.HexNicksApi;
import dev.majek.hexnicks.command.*;
import dev.majek.hexnicks.config.ConfigValues;
import dev.majek.hexnicks.gui.GuiManager;
import dev.majek.hexnicks.storage.*;
import dev.majek.hexnicks.event.PaperTabCompleteEvent;
import dev.majek.hexnicks.event.PlayerChat;
Expand Down Expand Up @@ -67,6 +68,7 @@ public final class HexNicks extends JavaPlugin {
private static ConfigValues config;
private static HookManager hooks;
private static StorageMethod storage;
private static GuiManager guis;
private final File jsonFile;
private final Map<UUID, Component> nickMap;
private final Metrics metrics;
Expand All @@ -83,6 +85,7 @@ public HexNicks() {
logging = new LoggingManager(this, new File(this.getDataFolder(), "logs"));
config = new ConfigValues();
hooks = new HookManager();
guis = new GuiManager();
this.jsonFile = new File(this.getDataFolder(), "nicknames.json");
this.nickMap = new HashMap<>();
// Track plugin metrics through bStats
Expand Down Expand Up @@ -167,7 +170,7 @@ public void onEnable() {
() -> String.valueOf(config.CHAT_FORMATTER)));

// Register events
this.registerEvents(new PlayerJoin(), new PaperTabCompleteEvent(), new PlayerChat());
this.registerEvents(new PlayerJoin(), new PaperTabCompleteEvent(), new PlayerChat(), guis);

// Check for updates - prompt to update if there is one
if (this.updateChecker.isBehindSpigot()) {
Expand Down Expand Up @@ -285,6 +288,15 @@ public static LoggingManager logging() {
return logging;
}

/**
* Get the plugin's gui manager.
*
* @return gui manager
*/
public static GuiManager guis() {
return guis;
}

/**
* Reload the plugin's configuration file.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
import dev.majek.hexnicks.HexNicks;
import dev.majek.hexnicks.api.NickColorEvent;
import dev.majek.hexnicks.config.Messages;
import dev.majek.hexnicks.gui.NickColorGui;
import dev.majek.hexnicks.message.MiniMessageWrapper;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -53,7 +54,12 @@ public boolean onCommand(@NotNull CommandSender sender, @NotNull Command command
}

if (args.length == 0) {
return false;
if (HexNicks.config().NICKCOLOR_GUI) {
new NickColorGui().openGui(player);
return true;
} else {
return false;
}
}

String nickInput = String.join(" ", args);
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/dev/majek/hexnicks/config/ConfigValues.java
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ public class ConfigValues {
public Boolean PREVENT_DUPLICATE_NICKS;
public Boolean PREVENT_DUPLICATE_NICKS_STRICT;
public List<String> BLOCKED_NICKNAMES;
public Boolean NICKCOLOR_GUI;
public Boolean DEBUG;

public ConfigValues() {
Expand All @@ -84,6 +85,7 @@ public void reload() {
PREVENT_DUPLICATE_NICKS = HexNicks.core().getConfig().getBoolean("prevent-duplicate-nicks", true);
PREVENT_DUPLICATE_NICKS_STRICT = HexNicks.core().getConfig().getBoolean("prevent-duplicate-nicks-strict", false);
BLOCKED_NICKNAMES = HexNicks.core().getConfig().getStringList("blocked-nicknames");
NICKCOLOR_GUI = HexNicks.core().getConfig().getBoolean("nickcolor-gui", true);
DEBUG = HexNicks.core().getConfig().getBoolean("debug", false);
}

Expand Down
22 changes: 22 additions & 0 deletions src/main/java/dev/majek/hexnicks/config/Messages.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import dev.majek.hexnicks.util.MiscUtils;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.TextReplacementConfig;
import net.kyori.adventure.text.format.TextColor;
import net.kyori.adventure.text.minimessage.MiniMessage;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
Expand Down Expand Up @@ -120,6 +121,27 @@ public interface Messages {
.replaceText(TextReplacementConfig.builder().matchLiteral("%player%").replacement(player.getName()).build())
.replaceText(TextReplacementConfig.builder().matchLiteral("%nick%").replacement(nickname).build());

Args0 GUI_BACK = () -> MiscUtils.configString("messages.guiBack", "<yellow><!italic>Back");

Args0 GUI_NICK_COLOR_TITLE = () -> MiscUtils.configString("messages.guiNickColorTitle", "Nick Color");

Args1<TextColor> GUI_NICK_COLOR_HEX_TITLE = (col) -> MiniMessage.miniMessage().deserialize(HexNicks.core().getConfig().getString(
"messages.guiNickColorHexTitle",
"Nick Color - <color:%color%>%color%"
).replace("%color%", col.toString()));

Args0 GUI_NICK_COLOR_RANDOM_HEX = () -> MiscUtils.configString("messages.guiNickColorRandomHex", "<!italic><rainbow>Random Hexadecimal Color");

Args1<Integer> GUI_NICK_COLOR_HEX_RED = (c) -> MiscUtils.configString("messages.guiNickColorHexRed", "<!italic><red>Red %step%")
.replaceText(TextReplacementConfig.builder().matchLiteral("%step%").replacement("%+d".formatted(c)).build());
Args1<Integer> GUI_NICK_COLOR_HEX_GREEN = (c) -> MiscUtils.configString("messages.guiNickColorHexGreen", "<!italic><green>Green %step%")
.replaceText(TextReplacementConfig.builder().matchLiteral("%step%").replacement("%+d".formatted(c)).build());
Args1<Integer> GUI_NICK_COLOR_HEX_BLUE = (c) -> MiscUtils.configString("messages.guiNickColorHexBlue", "<!italic><blue>Blue %step%")
.replaceText(TextReplacementConfig.builder().matchLiteral("%step%").replacement("%+d".formatted(c)).build());

Args0 GUI_NICK_COLOR_HEX_BUTTON = () -> MiscUtils.configString("messages.guiNickColorHexButton", "<!italic><yellow>Custom Hexadecimal Color");
Args0 GUI_NICK_COLOR_HEX_SAVE = () -> MiscUtils.configString("messages.guiNickColorHexSave", "<!italic><yellow>Click to save nickname");

/**
* A message that has no arguments that need to be replaced.
*/
Expand Down
144 changes: 144 additions & 0 deletions src/main/java/dev/majek/hexnicks/gui/Gui.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
package dev.majek.hexnicks.gui;

import dev.majek.hexnicks.HexNicks;
import net.kyori.adventure.text.Component;
import org.bukkit.Bukkit;
import org.bukkit.Material;
import org.bukkit.entity.Player;
import org.bukkit.event.inventory.InventoryAction;
import org.bukkit.event.inventory.InventoryClickEvent;
import org.bukkit.inventory.Inventory;
import org.bukkit.inventory.ItemStack;
import org.bukkit.inventory.meta.ItemMeta;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Range;

import java.util.Arrays;
import java.util.HashMap;
import java.util.function.Consumer;

public abstract class Gui {

private final Inventory inventory;
private Player user;

// Map of slot : slot action -- slot action can either be Consumer<InventoryAction> or Runnable
private final HashMap<Integer, Object> actionMap;

// The item to use as a background colour
private static final ItemStack BLANK_ITEM;

static {
BLANK_ITEM = new ItemStack(Material.GRAY_STAINED_GLASS_PANE);
ItemMeta meta = BLANK_ITEM.getItemMeta();
meta.displayName(Component.empty());
BLANK_ITEM.setItemMeta(meta);
}

/**
* Create a gui with a given size and a name
*
* @param size The amount of slots for the GUI -- must be a multiple of 9
* @param name The name for the gui, shown in the top-left corner
*/
public Gui(@Range(from = 1, to = 54) int size, @NotNull Component name) {
this.inventory = Bukkit.createInventory(null, size, name);
this.user = null;
this.actionMap = new HashMap<>();
}

/**
* Open the gui for a player, this will register the gui in the GuiManager to handle all events, and intialise the inventory
*
* @param player the player to open this gui for
*/
public void openGui(Player player) {
this.user = player;
fillInventory();
player.openInventory(inventory);
HexNicks.guis().registerOpenGui(this.user.getUniqueId(), this);
}

/**
* Set the contents of the inventory to only {@link Gui#BLANK_ITEM}.
*
* <i>Note: This overwrites all slots in the inventory and should be used before setting other items.</i>
*/
protected void blankInventory() {
ItemStack[] contents = new ItemStack[this.inventory.getSize()];
// Clone here so that the user may modify the inventory slots without changing it for _all_ guis
Arrays.fill(contents, BLANK_ITEM.clone());
this.inventory.setContents(contents);
}

/**
* Add a button into the gui that may run an action
*
* @param slot the slot in which to place the item stack
* @param stack the stack to be placed -- This is _not_ cloned
* @param action the action to be run
*/
protected void addActionButton(int slot, ItemStack stack, Runnable action) {
this.inventory.setItem(slot, stack);
this.actionMap.put(slot, action);
}

/**
* Add a button into the gui that may run an action
*
* @param slot the slot in which to place the item stack
* @param stack the stack to be placed -- This is _not_ cloned
* @param action the action to be run, passed the inventory action that occurs on the click.
*/
protected void addActionButton(int slot, ItemStack stack, Consumer<InventoryAction> action) {
this.inventory.setItem(slot, stack);
this.actionMap.put(slot, action);
}

/**
* Fill the inventory with items. This is run when the inventory is first opened for a player and may be run after to refresh the gui.
* <p>
* It is recommended to call {@link Gui#blankInventory()} at the beginning if you want a background.
*/
protected abstract void fillInventory();

/**
* Handle the inventory click event for this gui
*
* @param event the event that occurred
*/
public void onInventoryClick(InventoryClickEvent event) {
if (this.inventory().equals(event.getClickedInventory())) {
this.onInventoryClick(event.getSlot(), event.getAction());
event.setCancelled(true);
}
}

/**
* Handle inventory click on a specific slot for this gui
* <p>
* Default implementation is to call the action for that slot
*
* @param slot the slot which has been clicked
* @param invAction the InventoryAction that was called on the slot
*/
protected void onInventoryClick(int slot, InventoryAction invAction) {
Object action = this.actionMap.get(slot);
if (action != null) {
if (action instanceof Runnable runnable) {
runnable.run();
} else if (action instanceof Consumer) {
// This cast is okay because we know that the only way to insert a Consumer into the map is to use `addActionButton(.., Consumer<InventoryAction>)`.
((Consumer<InventoryAction>) action).accept(invAction);
}
}
}

protected Inventory inventory() {
return this.inventory;
}

protected Player user() {
return this.user;
}
}
96 changes: 96 additions & 0 deletions src/main/java/dev/majek/hexnicks/gui/GuiManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package dev.majek.hexnicks.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.inventory.InventoryCloseEvent;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.HashMap;
import java.util.UUID;

/**
* A manager for Guis, this handles events such as {@link InventoryClickEvent} and determines which {@link Gui} to send the event to
*/
public class GuiManager implements Listener {

private final HashMap<UUID, Gui> openGuis;

/**
* Create the new GUI Manager
*/
public GuiManager() {
this.openGuis = new HashMap<>();
}

/**
* Add a GUI for this manager to handle
*
* @param uuid the Player who opened this gui
* @param gui the gui which was opened
*/
public void registerOpenGui(@NotNull UUID uuid, @NotNull Gui gui) {
this.openGuis.put(uuid, gui);
}

/**
* Get a gui that is open for a player
*
* @param uuid the player
* @return the gui that is open -- {@code null} if no gui is open
*/
public @Nullable Gui getOpenGuiFor(@NotNull UUID uuid) {
return this.openGuis.get(uuid);
}

/**
* Close a specific gui for a player
*
* @param uuid the player
* @param gui the gui to close
* @return true if the gui was open, false otherwise
*/
public boolean closeGui(@NotNull UUID uuid, @NotNull Gui gui) {
if (this.openGuis.remove(uuid, gui)) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
player.closeInventory(InventoryCloseEvent.Reason.PLUGIN);
}
return true;
}
return false;
}

/**
* Close the open gui for a player, if the player had this gui, then it is
*
* @param uuid the player
* @return true if a gui was open, false otherwise
*/
public boolean closeGui(@NotNull UUID uuid) {
if (this.openGuis.remove(uuid) != null) {
Player player = Bukkit.getPlayer(uuid);
if (player != null) {
player.closeInventory(InventoryCloseEvent.Reason.PLUGIN);
}
return true;
}
return false;
}

@EventHandler
public void onInventoryClick(InventoryClickEvent event) {
Gui gui = this.getOpenGuiFor(event.getWhoClicked().getUniqueId());
if (gui != null) {
gui.onInventoryClick(event);
}
}

@EventHandler
public void onInventoryClose(InventoryCloseEvent event) {
this.openGuis.remove(event.getPlayer().getUniqueId());
}
}
Loading