Pārlūkot izejas kodu

Reworked FluxAPIModule.java to add listeners and commands dynamically; Added some Javadocs

Jan 4 nedēļas atpakaļ
vecāks
revīzija
a53f6343cd

+ 34 - 2
src/main/java/me/lethunderhawk/fluxapi/FluxService.java

@@ -10,14 +10,33 @@ public final class FluxService {
 
     private static final Map<Class<?>, Object> services = new ConcurrentHashMap<>();
 
+    /**
+     * Allows you to register an object and later retrieve it via the get Method
+     * @param type A Class of any matching type under which to save the instance
+     * @param service The Actual Instance to store
+     * @param <T> A Class
+     */
     public static <T> T register(Class<T> type, T service) {
         services.put(type, service);
         return service;
     }
+
+    /**
+     * Allows you to register a module and later retrieve it via the get Method
+     * @param type A Class of type {@link FluxAPIModule} under which to save the instance
+     * @param service The Actual Instance to store
+     * @param <T> A Class
+     */
     public static <T> void registerModule(Class<? extends FluxAPIModule> type, T service) {
         services.put(type, service);
     }
 
+    /**
+     * Retrieve Instance-Objects saved in FluxService
+     * @param type The Class under which it was saved (E.g. a subclass of {@link FluxAPIModule})
+     * @return The Instance currently being stored inside this Service
+     * @param <T> A Class
+     */
     @SuppressWarnings("unchecked")
     public static <T> T get(Class<T> type) {
         T service = (T) services.get(type);
@@ -26,15 +45,28 @@ public final class FluxService {
         }
         return service;
     }
+    /**
+     * Remove and get Instance-Objects from FluxService
+     * @param type The Class under which it was saved (E.g. a subclass of {@link FluxAPIModule})
+     * @return The Instance currently being stored inside this Service
+     * @param <T> A Class
+     */
     @SuppressWarnings("unchecked")
     public static <T> T unregister(Class<T> type) {
         return (T) services.remove(type);
     }
-
+    /**
+     * Remove and get Instance-Objects from FluxService
+     * @param type The Class under which it was saved (E.g. a subclass of {@link FluxAPIModule})
+     * @param <T> A Class
+     */
     public static <T> void unregisterModule(Class<? extends FluxAPIModule> type) {
         services.remove(type);
     }
-
+    /**
+     * Check if an instance of the class is already saved
+     * @return If the class exists in the service
+     */
     public static boolean isRegistered(Class<NPCManager> npcManagerClass) {
         return services.containsKey(npcManagerClass);
     }

+ 0 - 1
src/main/java/me/lethunderhawk/fluxapi/main/FluxAPI.java

@@ -25,7 +25,6 @@ public class FluxAPI extends JavaPlugin {
         npcModule.onEnable();
         FluxService.registerModule(NPCAPIModule.class, npcModule);
 
-
     }
 
     @Override

+ 6 - 20
src/main/java/me/lethunderhawk/fluxapi/npc/NPCAPIModule.java

@@ -13,14 +13,12 @@ import me.lethunderhawk.fluxapi.util.config.ConfigLoader;
 import me.lethunderhawk.fluxapi.util.interfaces.FluxAPIModule;
 import org.bukkit.Bukkit;
 import org.bukkit.configuration.serialization.ConfigurationSerialization;
-import org.bukkit.event.HandlerList;
 import org.bukkit.plugin.java.JavaPlugin;
 
 public final class NPCAPIModule extends FluxAPIModule {
     private final String saveLocation = "/npc/npcs.yml";
     private NPCManager npcManager;
-    private NPCListener listener;
-    private WorldLoadListener worldLoadListener;
+
     public NPCAPIModule(JavaPlugin plugin) {
         super(plugin);
     }
@@ -41,17 +39,12 @@ public final class NPCAPIModule extends FluxAPIModule {
         }
         registerListeners();
 
-
-        NPCCommand npcCommand = new NPCCommand(this);
-        plugin.getCommand("npc").setExecutor(npcCommand);
-        plugin.getCommand("npc").setTabCompleter(npcCommand);
+        registerCommand("npc", new NPCCommand(this));
     }
 
-    private void registerListeners() {
-        this.listener = new NPCListener();
-        plugin.getServer().getPluginManager().registerEvents(listener, plugin);
-        this.worldLoadListener = new WorldLoadListener();
-        plugin.getServer().getPluginManager().registerEvents(worldLoadListener, plugin);
+    public void registerListeners() {
+        registerListener(new NPCListener());
+        registerListener(new WorldLoadListener());
     }
 
     private void printWaitingNotif() {
@@ -87,17 +80,10 @@ public final class NPCAPIModule extends FluxAPIModule {
             npcManager.saveNPCsToFile(saveLocation);
             npcManager.deleteAllNpcEntities();
         }
-        unregisterListeners();
+        unregisterAllListeners();
 
         FluxService.unregister(NPCManager.class);
 
         npcManager = null;
     }
-
-    private void unregisterListeners() {
-        HandlerList.unregisterAll(listener);
-        listener = null;
-        HandlerList.unregisterAll(worldLoadListener);
-        worldLoadListener = null;
-    }
 }

+ 11 - 7
src/main/java/me/lethunderhawk/fluxapi/npc/command/NPCCommand.java

@@ -23,24 +23,25 @@ public class NPCCommand extends CustomCommand {
     public NPCCommand(FluxAPIModule module) {
         super(module);
     }
-    public void setRootCommand() {
-        this.rootCommand = new CommandNode("npc", "Base npc command", this::sendHelp);
+    @Override
+    public CommandNode getRootCommand() {
+        return new CommandNode("npc", "Base npc command", this::sendHelp);
     }
 
     @Override
     public void createCommands() {
-        CommandNode create = registerCommand("create", "Test creation command", this::createNPC);
+        CommandNode create = registerSubCommand("create", "Test creation command", this::createNPC);
         create.setTabCompleter(this::createCompleter);
-        CommandNode delete = registerCommand("delete", "Test deletion command", this::deleteNPC);
+        CommandNode delete = registerSubCommand("delete", "Test deletion command", this::deleteNPC);
         delete.setTabCompleter(this::npcDeletionCompleter);
-        CommandNode options = registerCommand("options", "Test options command", this::showOptionsMenu);
+        CommandNode options = registerSubCommand("options", "Test options command", this::showOptionsMenu);
         options.setTabCompleter(this::npcOptionsCompleter);
-        registerCommand("reload", "Reload this module", this::reload);
+        registerSubCommand("reload", "Reload this module", this::reload);
     }
 
     private void reload(CommandSender sender, String[] strings) {
         if(!sender.hasPermission("fluxapi.npc")) return;
-        module.reload(sender, strings);
+        module.reload(sender);
     }
 
     private List<String> npcDeletionCompleter(CommandSender sender, String[] args) {
@@ -105,4 +106,7 @@ public class NPCCommand extends CustomCommand {
         if(strings.length == 0) return;
         FluxService.get(NPCManager.class).deleteNPCByUUIDString(strings[0]);
     }
+
+
+
 }

+ 3 - 3
src/main/java/me/lethunderhawk/fluxapi/npc/gui/NPCOptionsGUI.java

@@ -27,20 +27,20 @@ public class NPCOptionsGUI extends InventoryGUI {
 
     private void editName(Player player, ClickType type) {
         if(player.hasPermission("npc.edit")) {
-            SignMenuFactory.Menu menu = new SignMenuFactory(FluxService.get(FluxAPI.class)).newMenu(List.of(
+            SignMenuFactory.SignMenu signMenu = new SignMenuFactory(FluxService.get(FluxAPI.class)).newMenu(List.of(
                     npc.getName(),
                     "^^^^^^^^",
                     "Edit the name",
                     "of this NPC"
             )).reopenIfFail(true);
-            menu.response((Player pl, String[] input) -> {
+            signMenu.response((Player pl, String[] input) -> {
                 String newName = input[0];
                 if(newName.isEmpty()) return false;
                 pl.sendMessage("Renamed " + npc.getName() + " to " + newName);
                 FluxService.get(NPCManager.class).renameNPCbyId(npc.getOptions().getEntity().getUniqueId(), newName);
                 return true;
             });
-            menu.open(player);
+            signMenu.open(player);
         }
     }
 

+ 2 - 7
src/main/java/me/lethunderhawk/fluxapi/profile/ProfileAPIModule.java

@@ -6,11 +6,9 @@ import me.lethunderhawk.fluxapi.profile.representation.FluxProfile;
 import me.lethunderhawk.fluxapi.util.config.ConfigLoader;
 import me.lethunderhawk.fluxapi.util.interfaces.FluxAPIModule;
 import org.bukkit.configuration.serialization.ConfigurationSerialization;
-import org.bukkit.event.HandlerList;
 import org.bukkit.plugin.java.JavaPlugin;
 
 public class ProfileAPIModule extends FluxAPIModule {
-    private JoinListener joinListener;
     private ProfileManager profileManager;
 
     public ProfileAPIModule(JavaPlugin plugin) {
@@ -28,15 +26,12 @@ public class ProfileAPIModule extends FluxAPIModule {
         profileManager = new ProfileManager(FluxService.get(ConfigLoader.class));
         FluxService.register(ProfileManager.class, profileManager);
 
-        joinListener = new JoinListener(profileManager);
-        plugin.getServer().getPluginManager().registerEvents(joinListener, plugin);
-
+        registerListener(new JoinListener(profileManager));
         plugin.getLogger().info("Game-Profile module enabled");
     }
 
     @Override
     public void onDisable() {
-        HandlerList.unregisterAll(joinListener);
-        profileManager.saveAllProfiles();
+        unregisterAllListeners();
     }
 }

+ 6 - 0
src/main/java/me/lethunderhawk/fluxapi/util/CustomHeadCreator.java

@@ -25,6 +25,7 @@ public class CustomHeadCreator {
 
         return playerHead;
     }
+
     public static ItemStack createCustomHead(String textureValue, Component displayName, List<Component> loreText) {
         ItemStack playerHead = createCustomHead(textureValue);
         SkullMeta meta = (SkullMeta) playerHead.getItemMeta();
@@ -49,6 +50,11 @@ public class CustomHeadCreator {
         return playerHead;
     }
 
+    /**
+     * Create a custom Head from the texture Value
+     * @param textureValue The texture value (can be found at minecraftheads under developers -> value)
+     * @return The ItemStack containing the SkullMeta / Texture specified
+     */
     public static ItemStack createCustomHead(String textureValue) {
         ItemStack head = new ItemStack(Material.PLAYER_HEAD);
         SkullMeta meta = (SkullMeta) head.getItemMeta();

+ 54 - 45
src/main/java/me/lethunderhawk/fluxapi/util/command/CustomCommand.java

@@ -1,47 +1,40 @@
 package me.lethunderhawk.fluxapi.util.command;
 
+import io.papermc.paper.command.brigadier.BasicCommand;
+import io.papermc.paper.command.brigadier.CommandSourceStack;
 import me.lethunderhawk.fluxapi.util.interfaces.FluxAPIModule;
 import net.kyori.adventure.text.Component;
 import net.kyori.adventure.text.format.NamedTextColor;
-import org.bukkit.command.Command;
-import org.bukkit.command.CommandExecutor;
 import org.bukkit.command.CommandSender;
-import org.bukkit.command.TabCompleter;
+import org.jetbrains.annotations.ApiStatus;
+import org.jspecify.annotations.NonNull;
 
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.Collection;
 import java.util.List;
 import java.util.function.BiConsumer;
 import java.util.stream.Collectors;
 
-public abstract class CustomCommand implements CommandExecutor, TabCompleter {
+public abstract class CustomCommand implements BasicCommand {
 
     protected CommandNode rootCommand;
     protected final FluxAPIModule module;
     public CustomCommand(FluxAPIModule module) {
         this.module = module;
-        setRootCommand();
+        this.rootCommand = getRootCommand();
         createHelpCommand();
         createCommands();
     }
-
-    public abstract void setRootCommand();
-
-    private void createHelpCommand() {
-        rootCommand.addSubCommand(new CommandNode("help", "Displays this help menu", this::sendHelp));
-    }
-
-    public abstract void createCommands();
-
-    @Override
-    public boolean onCommand(CommandSender sender, Command command, String label, String[] args) {
+    public void execute(CommandSourceStack commandSourceStack, String[] args) {
+        CommandSender sender = commandSourceStack.getSender();
         if (args.length == 0) {
             if(rootCommand.getExecutor() != null) {
                 rootCommand.getExecutor().accept(sender, args);
             }else{
                 sendHelp(sender);
             }
-            return true;
+            return;
         }
 
         List<String> argList = new ArrayList<>(Arrays.asList(args));
@@ -70,47 +63,32 @@ public abstract class CustomCommand implements CommandExecutor, TabCompleter {
         }
 
         if (targetNode == null) {
-            module.sendText(sender, Component.text("Unknown command. Use /" +rootCommand.getName() +" help for available commands.", NamedTextColor.RED));
-            return true;
+            module.sendText(sender, Component.text("Unknown command. Use /" + rootCommand.getName() +" help for available commands.", NamedTextColor.RED));
+            return;
         }
 
         if (targetNode.getExecutor() == null) {
             module.sendText(sender,Component.text("This command requires additional arguments.", NamedTextColor.RED));
             sendSubCommands(sender, targetNode);
-            return true;
+            return;
         }
 
         // Execute the command with remaining arguments
         String[] remainingArgs = argList.toArray(new String[0]);
         targetNode.getExecutor().accept(sender, remainingArgs);
-        return true;
-    }
-    protected void sendHelp(CommandSender sender, String[] strings) {
-        sendHelp(sender);
-    }
-    protected void sendHelp(CommandSender sender) {
-        sender.sendMessage("§6=== Available Commands ===");
-        for (CommandNode cmd : rootCommand.getSubCommands()) {
-            sender.sendMessage("/" + rootCommand.getName() + " " + cmd.getName() + " §7- " + cmd.getDescription());
-        }
-    }
-
-    private void sendSubCommands(CommandSender sender, CommandNode node) {
-        sender.sendMessage("§6Available subcommands:");
-        for (CommandNode subCmd : node.getSubCommands()) {
-            sender.sendMessage("§e" + subCmd.getName() + " §7- " + subCmd.getDescription());
-        }
     }
-
-    public CommandNode registerCommand(String name, String description, BiConsumer<CommandSender, String[]> executor) {
-        return rootCommand.registerSubCommand(name, description, executor);
-    }
-
-    @Override
-    public List<String> onTabComplete(CommandSender sender, Command command, String alias, String[] args) {
+    @ApiStatus.OverrideOnly
+    public @NonNull Collection<String> suggest(final CommandSourceStack commandSourceStack, final String[] args) {
         List<String> suggestions = new ArrayList<>();
 
         if (args.length == 0) {
+            CommandNode currentNode = rootCommand;
+            if (currentNode.getTabCompleter() != null) {
+                suggestions.addAll(currentNode.getTabCompleter().apply(commandSourceStack.getSender(), args));
+            } else {
+                // Suggest subcommands
+                suggestions.addAll(currentNode.getSubCommandNames());
+            }
             return suggestions;
         }
 
@@ -131,7 +109,7 @@ public abstract class CustomCommand implements CommandExecutor, TabCompleter {
 
         // Get suggestions from current node
         if (currentNode.getTabCompleter() != null) {
-            suggestions.addAll(currentNode.getTabCompleter().apply(sender, args));
+            suggestions.addAll(currentNode.getTabCompleter().apply(commandSourceStack.getSender(), args));
         } else {
             // Suggest subcommands
             suggestions.addAll(currentNode.getSubCommandNames().stream()
@@ -141,6 +119,37 @@ public abstract class CustomCommand implements CommandExecutor, TabCompleter {
 
         return suggestions;
     }
+
+    @ApiStatus.OverrideOnly
+    public abstract CommandNode getRootCommand();
+
+    private void createHelpCommand() {
+        rootCommand.addSubCommand(new CommandNode("help", "Displays this help menu", this::sendHelp));
+    }
+    @ApiStatus.OverrideOnly
+    public abstract void createCommands();
+
+    protected void sendHelp(CommandSender sender, String[] strings) {
+        sendHelp(sender);
+    }
+    protected void sendHelp(CommandSender sender) {
+        sender.sendMessage("§6=== Available Commands ===");
+        for (CommandNode cmd : rootCommand.getSubCommands()) {
+            sender.sendMessage("/" + rootCommand.getName() + " " + cmd.getName() + " §7- " + cmd.getDescription());
+        }
+    }
+
+    private void sendSubCommands(CommandSender sender, CommandNode node) {
+        sender.sendMessage("§6Available subcommands:");
+        for (CommandNode subCmd : node.getSubCommands()) {
+            sender.sendMessage("§e" + subCmd.getName() + " §7- " + subCmd.getDescription());
+        }
+    }
+
+    public CommandNode registerSubCommand(String name, String description, BiConsumer<CommandSender, String[]> executor) {
+        return rootCommand.registerSubCommand(name, description, executor);
+    }
+
     public void reload(){
         createCommands();
     }

+ 66 - 4
src/main/java/me/lethunderhawk/fluxapi/util/gui/InventoryGUI.java

@@ -12,6 +12,7 @@ import org.bukkit.inventory.Inventory;
 import org.bukkit.inventory.InventoryHolder;
 import org.bukkit.inventory.ItemStack;
 import org.bukkit.inventory.meta.ItemMeta;
+import org.jetbrains.annotations.ApiStatus;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.*;
@@ -25,8 +26,11 @@ public abstract class InventoryGUI implements InventoryHolder {
     private final String title;
     private final int size;
     private final Inventory inventory;
-
+    /**
+     * Slots with the action to trigger upon clicking
+     */
     private final Map<Integer, BiConsumer<Player, ClickType>> slotActions = new HashMap<>();
+
     private final Stack<InventoryGUI> previousGuis = new Stack<>();
     /** Slots that the player may edit (drag‑and‑drop, shift‑click, etc.) */
     private final Set<Integer> editableSlots = new HashSet<>();
@@ -84,6 +88,7 @@ public abstract class InventoryGUI implements InventoryHolder {
                 onPlayerInventoryClick(player, event);
             }
     }
+    @ApiStatus.OverrideOnly
     public void onPlayerInventoryClick(Player player, InventoryClickEvent event){}
 
     private void runClickActionIfPresent(Player player, int rawSlot, ClickType click) {
@@ -99,18 +104,39 @@ public abstract class InventoryGUI implements InventoryHolder {
         inventory.setItem(slot, item);
     }
 
+    /**
+     * Helper method
+     * @param slot The slot Number
+     * @return The row this slot is in
+     */
     protected int getRow(int slot) {
         return slot / 9;
     }
 
+    /**
+     * Helper method
+     * @param slot The slot Number
+     * @return The column this slot is in
+     */
     protected int getColumn(int slot) {
         return slot % 9;
     }
 
+    /**
+     * Helper method
+     * @param row The row Number
+     * @param column The column Number
+     * @return The slot this corresponds to
+     */
     protected int getSlot(int row, int column) {
         return row * 9 + column;
     }
 
+    /**
+     * Helper method
+     * @param startSlot The start slot of the Rectangle (top-left)
+     * @param endSlot The end slot of the Rectangle (bottom-right)
+     */
     protected List<Integer> computeRectangleSlots(int startSlot, int endSlot) {
 
         List<Integer> slots = new ArrayList<>();
@@ -132,6 +158,13 @@ public abstract class InventoryGUI implements InventoryHolder {
         return slots;
     }
 
+    /**
+     * Copies the same Click Action onto multiple slots
+     * @param startSlot The start slot of the Rectangle (top-left)
+     * @param endSlot The end slot of the Rectangle (bottom-right)
+     * @param item The items to fill this Rectangle with
+     * @param action The action to perform upon clicking any of these slots
+     */
     public void fillRectangleWithClickAction(int startSlot, int endSlot, ItemStack item, BiConsumer<Player, ClickType> action) {
         int startRow = getRow(startSlot);
         int startCol = getColumn(startSlot);
@@ -146,7 +179,12 @@ public abstract class InventoryGUI implements InventoryHolder {
             }
         }
     }
-
+    /**
+     * Copies the same {@link ItemStack} onto multiple slots
+     * @param startSlot The start slot of the Rectangle (top-left)
+     * @param endSlot The end slot of the Rectangle (bottom-right)
+     * @param item The items to fill this Rectangle with
+     */
     public void fillRectangle(int startSlot, int endSlot, ItemStack item) {
         int startRow = getRow(startSlot);
         int startCol = getColumn(startSlot);
@@ -161,6 +199,11 @@ public abstract class InventoryGUI implements InventoryHolder {
             }
         }
     }
+    /**
+     * Sets a full Rectangle to be editable
+     * @param startSlot The start slot of the Rectangle (top-left)
+     * @param endSlot The end slot of the Rectangle (bottom-right)
+     */
     public void setRectangleEditable(int startSlot, int endSlot) {
         int startRow = getRow(startSlot);
         int startCol = getColumn(startSlot);
@@ -203,7 +246,6 @@ public abstract class InventoryGUI implements InventoryHolder {
         ItemStack background = new ItemStack(material);
         ItemMeta meta = background.getItemMeta();
         meta.displayName(Component.text(displayName));
-        meta.setHideTooltip(true);
         background.setItemMeta(meta);
         for (int i = 0; i < size; i++) {
             if (inventory.getItem(i) == null) {
@@ -211,11 +253,18 @@ public abstract class InventoryGUI implements InventoryHolder {
             }
         }
     }
+
+    /**
+     * Quick way to open this GUI with the correct Listeners, so it actually works
+     * @param player The player to open the Inventory for
+     */
     public void openWithListener(Player player){
         InventoryManager.openFor(player, this);
     }
     /**
      * Opens this GUI for a player.
+     * <br>NOTE: This doesn't make the Inventory responsive by itself, since it only displays the container to the player.
+     * <br>for that, use {@code openWithListener(player);}
      */
     public void open(Player player) {
         buildContent();
@@ -272,7 +321,6 @@ public abstract class InventoryGUI implements InventoryHolder {
         });
     }
 
-    // Also need to update the overloaded version:
     public void openPrevious(Player player, ClickType type) {
         openPrevious(player);
     }
@@ -282,9 +330,23 @@ public abstract class InventoryGUI implements InventoryHolder {
         return inventory;
     }
 
+    /**
+     * Builds the Content of the Inventory, called everytime a player is shown the GUI
+     */
+    @ApiStatus.OverrideOnly
     public abstract void buildContent();
+
+    /**
+     * Usually only called when traversing through multiple GUIs
+     */
+    @ApiStatus.OverrideOnly
     public abstract void update();
 
+    /**
+     * This interface can be implemented as well to handle cases where the player closes the inventory
+     * <br>You can e.g. stop this behavior etc.
+     */
+    @FunctionalInterface
     public interface AutoCloseHandler {
         void onClosedByPlayer(Player player);
     }

+ 28 - 1
src/main/java/me/lethunderhawk/fluxapi/util/gui/PaginatedInventoryGUI.java

@@ -7,6 +7,7 @@ import org.bukkit.Material;
 import org.bukkit.entity.Player;
 import org.bukkit.event.inventory.ClickType;
 import org.bukkit.inventory.ItemStack;
+import org.jetbrains.annotations.ApiStatus;
 
 import java.util.ArrayList;
 import java.util.Collection;
@@ -25,6 +26,11 @@ public abstract class PaginatedInventoryGUI<T> extends InventoryGUI {
         super(title, size, player);
     }
 
+    /**
+     * Setup for Pagination Logic
+     * @param elements The Collection of things to display as paginated if necessary
+     * @param slots The slots the Collection should cover (use helper methods like {@code computeRectangleSlots()})
+     */
     protected void setupPagination(Collection<T> elements, List<Integer> slots) {
 
         this.elements = new ArrayList<>(elements);
@@ -34,10 +40,17 @@ public abstract class PaginatedInventoryGUI<T> extends InventoryGUI {
         this.maxPage = (this.elements.size() - 1) / itemsPerPage;
     }
 
+    /**
+     * Sorts the elements
+     * @param comparator The {@link Comparator} used to compare elements
+     */
     protected void sortElements(Comparator<T> comparator) {
         elements.sort(comparator);
     }
 
+    /**
+     * renders the paginated Page (defined previously)
+     */
     protected void renderPage() {
 
         int itemsPerPage = contentSlots.size();
@@ -71,6 +84,9 @@ public abstract class PaginatedInventoryGUI<T> extends InventoryGUI {
         }
     }
 
+    /**
+     * Adds Navigation Buttons on the bottom left and bottom right of the Page whenever necessary
+     */
     protected void addNavigationButtons() {
 
         int size = getInventory().getSize();
@@ -92,9 +108,20 @@ public abstract class PaginatedInventoryGUI<T> extends InventoryGUI {
         }
     }
 
+    /**
+     * Used for pagination, creates the Item you want using the elements Specified
+     * @param element The individual element passed through at rendering time
+     * @return The {@link ItemStack} you want to build
+     */
     protected abstract ItemStack createItem(T element);
 
+    /**
+     * What should happen on Click of an element
+     * @param element The element being clicked
+     * @param player The player clicking
+     * @param type The {@link ClickType} of the click
+     */
+    @ApiStatus.OverrideOnly
     protected void onClick(T element, Player player, ClickType type) {
     }
-
 }

+ 33 - 14
src/main/java/me/lethunderhawk/fluxapi/util/gui/input/SignMenuFactory.java

@@ -23,7 +23,7 @@ public final class SignMenuFactory {
 
     private final Plugin plugin;
 
-    private final Map<Player, Menu> inputs;
+    private final Map<Player, SignMenu> inputs;
 
     public SignMenuFactory(Plugin plugin) {
         this.plugin = plugin;
@@ -31,8 +31,13 @@ public final class SignMenuFactory {
         this.listen();
     }
 
-    public Menu newMenu(List<String> text) {
-        return new Menu(text);
+    /**
+     * Create a new Sign-Menu that the player can write on
+     * @param text The initial text to display
+     * @return The SignMenu
+     */
+    public SignMenu newMenu(List<String> text) {
+        return new SignMenu(text);
     }
 
     private void listen() {
@@ -40,23 +45,23 @@ public final class SignMenuFactory {
             @Override
             public void onPacketReceiving(PacketEvent event) {
                 Player player = event.getPlayer();
-                Menu menu = inputs.remove(player);
-                if (menu == null) return;
+                SignMenu signMenu = inputs.remove(player);
+                if (signMenu == null) return;
 
                 event.setCancelled(true);
 
                 String[] lines = event.getPacket().getStringArrays().read(0);
 
                 Bukkit.getScheduler().runTask(plugin, () -> {
-                    boolean success = menu.response.test(player, lines);
+                    boolean success = signMenu.response.test(player, lines);
 
-                    if (!success && menu.reopenIfFail && !menu.forceClose) {
-                        Bukkit.getScheduler().runTaskLater(plugin, () -> menu.open(player), 2L);
+                    if (!success && signMenu.reopenIfFail && !signMenu.forceClose) {
+                        Bukkit.getScheduler().runTaskLater(plugin, () -> signMenu.open(player), 2L);
                     }
                     if (player.isOnline()) {
                         player.sendBlockChange(
-                                menu.location,
-                                menu.location.getBlock().getBlockData()
+                                signMenu.location,
+                                signMenu.location.getBlock().getBlockData()
                         );
                     }
                 });
@@ -64,7 +69,7 @@ public final class SignMenuFactory {
         });
     }
 
-    public final class Menu {
+    public final class SignMenu {
 
         private final List<String> text;
 
@@ -75,20 +80,34 @@ public final class SignMenuFactory {
 
         private boolean forceClose;
 
-        Menu(List<String> text) {
+        SignMenu(List<String> text) {
             this.text = text;
         }
 
-        public Menu reopenIfFail(boolean value) {
+        /**
+         * Whether the GUI should open again if the response returns false
+         * @param value Whether the GUI should open again if the response returns false
+         * @return The {@link SignMenu} you currently edit
+         */
+        public SignMenu reopenIfFail(boolean value) {
             this.reopenIfFail = value;
             return this;
         }
 
-        public Menu response(BiPredicate<Player, String[]> response) {
+        /**
+         *
+         * @param response The response that should be triggered upon closing the sign
+         * @return The {@link SignMenu} you currently edit
+         */
+        public SignMenu response(BiPredicate<Player, String[]> response) {
             this.response = response;
             return this;
         }
 
+        /**
+         * Opens the Menu for a player
+         * @param player The {@link Player} the SignMenu should be shown to
+         */
         public void open(Player player) {
             Objects.requireNonNull(player, "player");
             if (!player.isOnline()) {

+ 75 - 2
src/main/java/me/lethunderhawk/fluxapi/util/interfaces/FluxAPIModule.java

@@ -1,28 +1,101 @@
 package me.lethunderhawk.fluxapi.util.interfaces;
 
 import me.lethunderhawk.fluxapi.util.MessageSender;
+import me.lethunderhawk.fluxapi.util.command.CustomCommand;
 import net.kyori.adventure.audience.Audience;
 import net.kyori.adventure.text.Component;
 import org.bukkit.command.CommandSender;
+import org.bukkit.event.HandlerList;
+import org.bukkit.event.Listener;
 import org.bukkit.plugin.java.JavaPlugin;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public abstract class FluxAPIModule {
     protected JavaPlugin plugin;
+
+    protected List<Listener> listeners = new ArrayList<>();
+
     public FluxAPIModule(JavaPlugin plugin) {
         this.plugin = plugin;
     }
+
+    /**
+     * Used for displaying when a module was enabled/the module is sending messages to players
+     * @return The Prefix for the Module, something cool like [Module]
+     */
     public abstract String getPrefix();
+
+    /**
+     * Called when the module gets enabled.
+     * <br>
+     * Recommended to call {@code registerCommand()} and {@code registerListener()} inside this method
+     */
     public abstract void onEnable();
+
+    /**
+     * Called when the module gets disabled.
+     * <br>
+     * Recommended to call {@code unregisterAllListeners()} inside this method
+     */
     public abstract void onDisable();
 
+    /**
+     * Send a message to someone / something
+     * @param receiver The {@link Audience} that should receive the message
+     * @param infoText The text to send to the receiver
+     */
     public void sendText(Audience receiver, Component infoText){
         MessageSender.sendModuleText(receiver, infoText, getPrefix());
     }
 
-    public void reload(CommandSender sender, String[] strings) {
-        if(sender.isOp()){
+    /**
+     * Will reload the module inside the plugin
+     * @param sender The {@link CommandSender} trying to reload
+     */
+    public void reload(CommandSender sender) {
+        if(sender.hasPermission("fluxapi.reloadIndividualModules")){
             onDisable();
             onEnable();
         }
     }
+
+    /**
+     * Registers a custom command directly through the plugin
+     * @param baseCommand The basic usage, meaning "command" turns into /command
+     * @param command The underlying executor for the command
+     */
+    protected void registerCommand(String baseCommand, CustomCommand command){
+        plugin.registerCommand(baseCommand, "Custom added Command", command);
+    }
+    /**
+     * Registers a listener through the plugin. Listeners can be retrieved via the List called {@code listeners} in each module
+     * @param listener The {@link Listener} to register
+     */
+    protected void registerListener(Listener listener){
+        listeners.add(listener);
+        plugin.getServer().getPluginManager().registerEvents(listener, plugin);
+    }
+
+    /**
+     * This unregisters a {@link Listener} from the {@link HandlerList}.
+     * <br>
+     * Note: This doesn't remove the listener from the inherited list!
+     * @param listener The listener to unregister
+     */
+    protected void unregisterListener(Listener listener){
+        HandlerList.unregisterAll(listener);
+    }
+
+    /**
+     * Should be used in onDisable to avoid conflict with the same listener being registered multiple times and firing more than once
+     */
+    protected void unregisterAllListeners(){
+        for(Listener listener : listeners){
+            unregisterListener(listener);
+        }
+        listeners.clear();
+    }
+
 }

+ 10 - 0
src/main/java/me/lethunderhawk/fluxapi/util/itemdesign/ItemOptions.java

@@ -15,9 +15,19 @@ public class ItemOptions {
     private List<Component> lore;
     private int amount = 1;
     private Material material;
+
+    /**
+     * Simple way to customize Items
+     * @param material The Base material
+     */
     public ItemOptions(Material material){
         this.material = material;
     }
+
+    /**
+     * Creates the {@link ItemStack} from Scratch
+     * @return The {@link ItemStack} with the Properties specified
+     */
     public ItemStack buildItemStack(){
         ItemStack item = new ItemStack(material);
         item.setAmount(amount);

+ 30 - 1
src/main/java/me/lethunderhawk/fluxapi/util/itemdesign/LoreDesigner.java

@@ -15,6 +15,11 @@ public final class LoreDesigner {
     public static String defaultWidthReference = "The default width reference";
     private LoreDesigner() {}
 
+    /**
+     * Create a single {@link Component} with additional Regex like {@literal <red> or <blue>}
+     * @param text The Text to display
+     * @return a new {@link Component}
+     */
     public static Component createSingle(String text) {
         if (text == null || text.isBlank()) {
             return Component.empty();
@@ -42,6 +47,14 @@ public final class LoreDesigner {
 
         return builder.build();
     }
+
+    /**
+     * Create a List of {@link Component}s with additional Regex like {@literal <red> or <blue>}
+     * @param text The Text to display
+     * @param widthReference The reference used for line-wrapping
+     * @param color The base Color to use (<-- Not quite sure lol)
+     * @return A new List of {@link Component}
+     */
     public static List<Component> createLore(String text, String widthReference, NamedTextColor color) {
         List<Component> lore = createLore(text, widthReference);
         for(Component component : lore) {
@@ -49,14 +62,30 @@ public final class LoreDesigner {
         }
         return lore;
     }
+    /**
+     * Create a List of {@link Component}s with additional Regex like {@literal <red> or <blue>} and the {@code defaultWidthReference}
+     * @param text The Text to display
+     * @param color The base Color to use (<-- Not quite sure lol)
+     * @return A new List of {@link Component}
+     */
     public static List<Component> createLore(String text, NamedTextColor color) {
         return createLore(text, defaultWidthReference, color);
     }
 
+    /**
+     * Create a List of {@link Component}s with additional Regex like {@literal <red> or <blue>} and the {@code defaultWidthReference}
+     * @param text The Text to display
+     * @return A new List of {@link Component}
+     */
     public static List<Component> createLore(String text){
         return createLore(text, defaultWidthReference);
     }
-
+    /**
+     * Create a List of {@link Component}s with additional Regex like {@literal <red> or <blue>}
+     * @param text The Text to display
+     * @param widthReference The Reference for how wide it should be
+     * @return A new List of {@link Component}
+     */
     public static List<Component> createLore(String text, String widthReference) {
         int maxLength = widthReference.length();
         if (text == null || text.isBlank() || maxLength <= 0) {

+ 6 - 0
src/main/java/me/lethunderhawk/fluxapi/util/lang/RomanNumbers.java

@@ -1,6 +1,12 @@
 package me.lethunderhawk.fluxapi.util.lang;
 
 public class RomanNumbers {
+
+    /**
+     * Turns a integer into a roman number
+     * @param num The Arabic Number
+     * @return A {@link String} containing the Number as a Roman Numeral
+     */
     public static String intToRoman(int num) {
         // Arrays to hold the Roman numeral symbols and their corresponding integer values
         String[] romanSymbols = {