From 1b4c5af4da4d3e58c3ceb641fdcf88d527889ed4 Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Fri, 18 Apr 2025 00:39:12 +0200 Subject: [PATCH 1/8] Added a custom way to fill Inventorys Added Support for a custom Inventory filler, mostly for Enderchest, but also working for Inventory and Item --- .../java/dev/unnm3d/redischat/RedisChat.java | 14 +- .../redischat/api/CustomInventoryAPI.java | 385 ++++++++++++++++++ .../redischat/chat/ComponentProvider.java | 48 ++- .../redischat/commands/InvShareCommand.java | 11 +- 4 files changed, 446 insertions(+), 12 deletions(-) create mode 100644 src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java diff --git a/src/main/java/dev/unnm3d/redischat/RedisChat.java b/src/main/java/dev/unnm3d/redischat/RedisChat.java index 2ec1a5f..abb0b16 100644 --- a/src/main/java/dev/unnm3d/redischat/RedisChat.java +++ b/src/main/java/dev/unnm3d/redischat/RedisChat.java @@ -9,6 +9,7 @@ import dev.jorel.commandapi.CommandAPI; import dev.jorel.commandapi.CommandAPIBukkitConfig; import dev.jorel.commandapi.CommandAPICommand; +import dev.unnm3d.redischat.api.CustomInventoryAPI; import dev.unnm3d.redischat.api.DataManager; import dev.unnm3d.redischat.channels.ChannelCommand; import dev.unnm3d.redischat.channels.ChannelManager; @@ -45,9 +46,11 @@ import dev.unnm3d.redischat.utils.AdventureWebuiEditorAPI; import dev.unnm3d.redischat.utils.Metrics; import lombok.Getter; +import org.bukkit.Bukkit; import org.bukkit.command.CommandExecutor; import org.bukkit.command.PluginCommand; import org.bukkit.command.TabCompleter; +import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -94,6 +97,9 @@ public final class RedisChat extends JavaPlugin { private ExecutorService executorService; @Getter private MailGUIManager mailGUIManager; + // New field for the custom inventory API + @Getter + private CustomInventoryAPI customInventoryAPI; public Config config; public FiltersConfig filterSettings; @@ -125,6 +131,11 @@ public void onEnable() { this.executorService = Executors.newFixedThreadPool(config.chatThreads); + // Initialize the custom inventory API + this.customInventoryAPI = new CustomInventoryAPI(); + Bukkit.getServicesManager().register(CustomInventoryAPI.class, this.customInventoryAPI, this, ServicePriority.Normal); + + //Redis section this.dataManager = switch (config.getDataType()) { case REDIS -> RedisDataManager.startup(this); @@ -391,5 +402,4 @@ private void loadCommandAPICommand(CommandAPICommand commandAPICommand) { registeredCommands.add(commandAPICommand); getLogger().info("Command " + commandAPICommand.getName() + " registered on CommandAPI!"); } - -} +} \ No newline at end of file diff --git a/src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java b/src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java new file mode 100644 index 0000000..fd0ed20 --- /dev/null +++ b/src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java @@ -0,0 +1,385 @@ +package dev.unnm3d.redischat.api; + +import org.bukkit.Bukkit; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +/** + * API to override the inventory/item/enderchest shown when a player uses the chat tags + */ +public class CustomInventoryAPI { + private final Map customInventories = new HashMap<>(); + private final Map customItems = new HashMap<>(); + private final Map customEnderChests = new HashMap<>(); + private final Map customInventorySizes = new HashMap<>(); + + /** + * Set a custom inventory to be displayed when a player uses the inventory tag + * + * @param player The player to set the custom inventory for + * @param contents The contents to display, can be null to remove the override + */ + public void setCustomInventory(@NotNull Player player, @Nullable ItemStack[] contents) { + if (contents == null) { + customInventories.remove(player.getUniqueId()); + customInventorySizes.remove(player.getUniqueId()); + } else { + customInventories.put(player.getUniqueId(), contents.clone()); + // Default size based on contents length, rounded up to nearest multiple of 9 + int size = Math.min(54, (int) Math.ceil(contents.length / 9.0) * 9); + customInventorySizes.putIfAbsent(player.getUniqueId(), size); + } + } + + /** + * Set a custom inventory with a specific size + * + * @param player The player to set the custom inventory for + * @param contents The contents to display, can be null to remove the override + * @param size The size of the inventory (must be a multiple of 9, between 9 and 54) + */ + public void setCustomInventory(@NotNull Player player, @Nullable ItemStack[] contents, int size) { + if (contents == null) { + customInventories.remove(player.getUniqueId()); + customInventorySizes.remove(player.getUniqueId()); + return; + } + + // Validate size + if (size % 9 != 0 || size < 9 || size > 54) { + throw new IllegalArgumentException("Inventory size must be a multiple of 9 between 9 and 54"); + } + + // Create a new array with the specified size + ItemStack[] resizedContents = new ItemStack[size]; + // Copy contents, limited by either the original array length or the new size + System.arraycopy(contents, 0, resizedContents, 0, Math.min(contents.length, size)); + + customInventories.put(player.getUniqueId(), resizedContents); + customInventorySizes.put(player.getUniqueId(), size); + } + + /** + * Set an item at a specific slot in the custom inventory + * + * @param player The player to set the item for + * @param slot The slot to set the item at (0-based index) + * @param item The item to set, can be null to clear the slot + */ + public void setCustomInventoryItem(@NotNull Player player, int slot, @Nullable ItemStack item) { + UUID playerId = player.getUniqueId(); + + // If no custom inventory exists yet, create one with default size + if (!customInventories.containsKey(playerId)) { + int size = customInventorySizes.getOrDefault(playerId, 27); + customInventories.put(playerId, new ItemStack[size]); + } + + ItemStack[] contents = customInventories.get(playerId); + + // Check if the slot is within bounds + if (slot < 0 || slot >= contents.length) { + throw new IllegalArgumentException("Slot " + slot + " is outside the inventory bounds (0-" + (contents.length - 1) + ")"); + } + + // Set the item at the specified slot + contents[slot] = item != null ? item.clone() : null; + } + + /** + * Get the custom inventory size for a player + * + * @param player The player to get the inventory size for + * @return The custom inventory size, or 27 if not set + */ + public int getCustomInventorySize(@NotNull Player player) { + return customInventorySizes.getOrDefault(player.getUniqueId(), 27); + } + + /** + * Set the custom inventory size for a player + * + * @param player The player to set the inventory size for + * @param size The size to set (must be a multiple of 9 between 9 and 54) + */ + public void setCustomInventorySize(@NotNull Player player, int size) { + if (size % 9 != 0 || size < 9 || size > 54) { + throw new IllegalArgumentException("Inventory size must be a multiple of 9 between 9 and 54"); + } + + UUID playerId = player.getUniqueId(); + customInventorySizes.put(playerId, size); + + // If there's an existing inventory, resize it + if (customInventories.containsKey(playerId)) { + ItemStack[] oldContents = customInventories.get(playerId); + ItemStack[] newContents = new ItemStack[size]; + + // Copy the contents from the old array to the new one + System.arraycopy(oldContents, 0, newContents, 0, Math.min(oldContents.length, size)); + + customInventories.put(playerId, newContents); + } + } + + /** + * Set a custom item to be displayed when a player uses the item tag + * + * @param player The player to set the custom item for + * @param item The item to display, can be null to remove the override + */ + public void setCustomItem(@NotNull Player player, @Nullable ItemStack item) { + if (item == null) { + customItems.remove(player.getUniqueId()); + } else { + customItems.put(player.getUniqueId(), item.clone()); + } + } + + /** + * Set a custom enderchest to be displayed when a player uses the enderchest tag + * + * @param player The player to set the custom enderchest for + * @param contents The contents to display, can be null to remove the override + */ + public void setCustomEnderChest(@NotNull Player player, @Nullable ItemStack[] contents) { + if (contents == null) { + customEnderChests.remove(player.getUniqueId()); + } else { + customEnderChests.put(player.getUniqueId(), contents.clone()); + } + } + + /** + * Check if a player has a custom inventory override + * + * @param player The player to check + * @return True if the player has a custom inventory, false otherwise + */ + public boolean hasCustomInventory(@NotNull Player player) { + return customInventories.containsKey(player.getUniqueId()); + } + + /** + * Check if a player has a custom item override + * + * @param player The player to check + * @return True if the player has a custom item, false otherwise + */ + public boolean hasCustomItem(@NotNull Player player) { + return customItems.containsKey(player.getUniqueId()); + } + + /** + * Check if a player has a custom enderchest override + * + * @param player The player to check + * @return True if the player has a custom enderchest, false otherwise + */ + public boolean hasCustomEnderChest(@NotNull Player player) { + return customEnderChests.containsKey(player.getUniqueId()); + } + + /** + * Get the custom inventory for a player + * + * @param player The player to get the custom inventory for + * @return The custom inventory, or null if none is set + */ + public @Nullable ItemStack[] getCustomInventory(@NotNull Player player) { + ItemStack[] contents = customInventories.get(player.getUniqueId()); + return contents != null ? contents.clone() : null; + } + + /** + * Get the custom item for a player + * + * @param player The player to get the custom item for + * @return The custom item, or null if none is set + */ + public @Nullable ItemStack getCustomItem(@NotNull Player player) { + ItemStack item = customItems.get(player.getUniqueId()); + return item != null ? item.clone() : null; + } + + /** + * Get the custom enderchest for a player + * + * @param player The player to get the custom enderchest for + * @return The custom enderchest, or null if none is set + */ + public @Nullable ItemStack[] getCustomEnderChest(@NotNull Player player) { + ItemStack[] contents = customEnderChests.get(player.getUniqueId()); + return contents != null ? contents.clone() : null; + } + + /** + * Clear all custom inventories/items/enderchests for a player + * + * @param player The player to clear custom content for + */ + public void clearCustomContent(@NotNull Player player) { + UUID playerId = player.getUniqueId(); + customInventories.remove(playerId); + customItems.remove(playerId); + customEnderChests.remove(playerId); + customInventorySizes.remove(playerId); + } + + /** + * Clear all custom inventories/items/enderchests for all players + */ + public void clearAllCustomContent() { + customInventories.clear(); + customItems.clear(); + customEnderChests.clear(); + customInventorySizes.clear(); + } + + /** + * Create a new GUI builder for a player + * + * @param player The player to create the GUI for + * @return A new GUI builder + */ + public GUIBuilder createGUI(@NotNull Player player) { + return new GUIBuilder(player, this); + } + + /** + * Builder class for creating custom GUIs + */ + public static class GUIBuilder { + private final Player player; + private final CustomInventoryAPI api; + private final ItemStack[] contents; + private final int size; + + private GUIBuilder(Player player, CustomInventoryAPI api) { + this(player, api, 27); // Default size + } + + private GUIBuilder(Player player, CustomInventoryAPI api, int size) { + this.player = player; + this.api = api; + this.size = size; + this.contents = new ItemStack[size]; + } + + /** + * Create a new GUI builder with a specific size + * + * @param size The size of the inventory (must be a multiple of 9, between 9 and 54) + * @return This builder for chaining + */ + public GUIBuilder withSize(int size) { + return new GUIBuilder(player, api, size); + } + + /** + * Set an item at a specific slot + * + * @param slot The slot to set (0-based index) + * @param item The item to set + * @return This builder for chaining + */ + public GUIBuilder setItem(int slot, ItemStack item) { + if (slot < 0 || slot >= contents.length) { + throw new IllegalArgumentException("Slot " + slot + " is outside the inventory bounds (0-" + (contents.length - 1) + ")"); + } + contents[slot] = item != null ? item.clone() : null; + return this; + } + + /** + * Fill all empty slots with the specified item + * + * @param item The item to fill with + * @return This builder for chaining + */ + public GUIBuilder fillEmpty(ItemStack item) { + for (int i = 0; i < contents.length; i++) { + if (contents[i] == null) { + contents[i] = item.clone(); + } + } + return this; + } + + /** + * Fill the borders of the inventory with the specified item + * + * @param item The item to fill with + * @return This builder for chaining + */ + public GUIBuilder fillBorders(ItemStack item) { + int rows = size / 9; + + // Top and bottom rows + for (int i = 0; i < 9; i++) { + contents[i] = item.clone(); // Top row + contents[(rows - 1) * 9 + i] = item.clone(); // Bottom row + } + + // Left and right columns (excluding corners that are already filled) + for (int row = 1; row < rows - 1; row++) { + contents[row * 9] = item.clone(); // Left column + contents[row * 9 + 8] = item.clone(); // Right column + } + + return this; + } + + /** + * Fill a row with the specified item + * + * @param row The row to fill (0-based index) + * @param item The item to fill with + * @return This builder for chaining + */ + public GUIBuilder fillRow(int row, ItemStack item) { + if (row < 0 || row >= size / 9) { + throw new IllegalArgumentException("Row " + row + " is outside the inventory bounds (0-" + (size / 9 - 1) + ")"); + } + + for (int i = 0; i < 9; i++) { + contents[row * 9 + i] = item.clone(); + } + + return this; + } + + /** + * Fill a column with the specified item + * + * @param column The column to fill (0-based index) + * @param item The item to fill with + * @return This builder for chaining + */ + public GUIBuilder fillColumn(int column, ItemStack item) { + if (column < 0 || column >= 9) { + throw new IllegalArgumentException("Column " + column + " is outside the inventory bounds (0-8)"); + } + + for (int row = 0; row < size / 9; row++) { + contents[row * 9 + column] = item.clone(); + } + + return this; + } + + /** + * Build the GUI and apply it to the player + */ + public void build() { + api.setCustomInventory(player, contents, size); + } + } +} \ No newline at end of file diff --git a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java index 8adb78e..f4482f2 100644 --- a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java +++ b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java @@ -20,6 +20,7 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; +import org.intellij.lang.annotations.Subst; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -34,6 +35,7 @@ @AllArgsConstructor public class ComponentProvider { + @Subst("") private final RedisChat plugin; private final MiniMessage miniMessage; @Getter @@ -257,11 +259,16 @@ public Component parseChatMessageContent(@NotNull CommandSender cmdSender, @NotN .replace("%command%", "/invshare " + player.getName() + "-item"); Component toParseItemComponent = parse(player, toParseItem, true, false, false, this.standardTagResolver); - final Component itemName = getItemNameComponent(p.getInventory().getItemInMainHand()); + // Get the item to display (either custom or actual) + ItemStack itemToDisplay = plugin.getCustomInventoryAPI().hasCustomItem(p) ? + plugin.getCustomInventoryAPI().getCustomItem(p) : + p.getInventory().getItemInMainHand(); + + final Component itemName = getItemNameComponent(itemToDisplay); toParseItemComponent = toParseItemComponent.replaceText(rTextBuilder -> rTextBuilder.matchLiteral("%item_name%").replacement(itemName)); toParseItemComponent = toParseItemComponent.replaceText(rTextBuilder -> rTextBuilder.matchLiteral("%amount%").replacement( - p.getInventory().getItemInMainHand().getAmount() > 1 ? - "x" + p.getInventory().getItemInMainHand().getAmount() + " " : + itemToDisplay.getAmount() > 1 ? + "x" + itemToDisplay.getAmount() + " " : "" )); @@ -400,15 +407,40 @@ public String invShareFormatting(CommandSender sender, String message) { .replace("[enderchest]", "<" + plugin.config.ec_tag + ">") .replace("[ec]", "<" + plugin.config.ec_tag + ">"); } + if (message.contains("<" + plugin.config.inv_tag + ">")) { - plugin.getDataManager().addInventory(player.getName(), player.getInventory().getContents()); + if (plugin.getCustomInventoryAPI().hasCustomInventory(player)) { + // Use custom inventory if available + plugin.getDataManager().addInventory(player.getName(), + plugin.getCustomInventoryAPI().getCustomInventory(player)); + } else { + // Fall back to actual inventory + plugin.getDataManager().addInventory(player.getName(), player.getInventory().getContents()); + } } + if (message.contains("<" + plugin.config.item_tag + ">")) { - plugin.getDataManager().addItem(player.getName(), player.getInventory().getItemInMainHand()); + if (plugin.getCustomInventoryAPI().hasCustomItem(player)) { + // Use custom item if available + plugin.getDataManager().addItem(player.getName(), + plugin.getCustomInventoryAPI().getCustomItem(player)); + } else { + // Fall back to actual item + plugin.getDataManager().addItem(player.getName(), player.getInventory().getItemInMainHand()); + } } + if (message.contains("<" + plugin.config.ec_tag + ">")) { - plugin.getDataManager().addEnderchest(player.getName(), player.getEnderChest().getContents()); + if (plugin.getCustomInventoryAPI().hasCustomEnderChest(player)) { + // Use custom enderchest if available + plugin.getDataManager().addEnderchest(player.getName(), + plugin.getCustomInventoryAPI().getCustomEnderChest(player)); + } else { + // Fall back to actual enderchest + plugin.getDataManager().addEnderchest(player.getName(), player.getEnderChest().getContents()); + } } + return message; } @@ -468,6 +500,4 @@ public void sendMessage(CommandSender sender, String message) { public void sendMessage(CommandSender sender, Component component) { bukkitAudiences.sender(sender).sendMessage(component); } - -} - +} \ No newline at end of file diff --git a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java index 3ddc540..4e94294 100644 --- a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java +++ b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java @@ -81,9 +81,18 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command if (plugin.config.debugItemShare) { plugin.getLogger().info("ECshare openGUI for player " + p.getName() + ": " + Arrays.toString(ecContents)); } + int totalSlots; + if (plugin.getCustomInventoryAPI().hasCustomEnderChest(p)) { + ItemStack[] custom = plugin.getCustomInventoryAPI().getCustomEnderChest(p); + totalSlots = custom.length; + } else { + totalSlots = p.getEnderChest().getSize(); + } + int rows = totalSlots / 9; + openInvShareGui(p, plugin.config.ec_title.replace("%player%", playerName), - 3, + rows, ecContents ); })); From 183b3936d9dd7e98f5952f135fa8c9288b89e7e8 Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Fri, 18 Apr 2025 12:01:24 +0200 Subject: [PATCH 2/8] Reordered the Inventory, for better User-expirience Reorderd the Inventory, to have a way better look and nicer view of the GUI with the armors being now at the top and the hotbar at the bottom fixing an ugly appearence and better code structure --- .../redischat/commands/InvShareCommand.java | 144 +++++++++++------- 1 file changed, 91 insertions(+), 53 deletions(-) diff --git a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java index 4e94294..4317d7e 100644 --- a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java +++ b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java @@ -27,60 +27,89 @@ public class InvShareCommand implements CommandExecutor { private final RedisChat plugin; @Override - public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] strings) { + public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { if (!(commandSender instanceof Player p)) return true; + if (args.length == 0) return true; - if (strings.length == 0) return true; - final String[] split = strings[0].split("-"); - if (split.length == 1) return true; + String[] split = args[0].split("-"); + if (split.length != 2) return true; + String playerName = split[0]; + InventoryType type; + try { + type = InventoryType.valueOf(split[1].toUpperCase()); + } catch (IllegalArgumentException e) { + return true; + } - final String playerName = split[0]; - InventoryType type = InventoryType.valueOf(split[1].toUpperCase()); switch (type) { case ITEM -> plugin.getDataManager().getPlayerItem(playerName) .thenAccept(item -> RedisChat.getScheduler().runTask(() -> { - if (plugin.config.debugItemShare) { - plugin.getLogger().info("Itemshare openGUI for player " + p.getName() + ": " + item.getType()); - } - if (item.getType().toString().endsWith("SHULKER_BOX")) { - if (item.getItemMeta() instanceof BlockStateMeta bsm) - if (bsm.getBlockState() instanceof Container shulkerBox) { - openInvShareGui(p, - plugin.config.shulker_title.replace("%player%", playerName), - 3, - shulkerBox.getSnapshotInventory().getContents() - ); - } - } else { - openInvShareGuiItem(p, - plugin.config.item_title.replace("%player%", playerName), - item - ); - } - } - )); + if (plugin.config.debugItemShare) { + plugin.getLogger().info("Itemshare openGUI for player " + p.getName() + ": " + item.getType()); + } + if (item.getType().toString().endsWith("SHULKER_BOX") && item.getItemMeta() instanceof BlockStateMeta bsm + && bsm.getBlockState() instanceof Container shulkerBox) { + openInvShareGui(p, + plugin.config.shulker_title.replace("%player%", playerName), + 3, + shulkerBox.getSnapshotInventory().getContents() + ); + } else { + openInvShareGuiItem(p, + plugin.config.item_title.replace("%player%", playerName), + item + ); + } + }) + ); case INVENTORY -> plugin.getDataManager().getPlayerInventory(playerName) - .thenAccept(inventoryContents -> + .thenAccept(rawInv -> RedisChat.getScheduler().runTask(() -> { + // GUI dimensions: 9 columns × 5 rows = 45 slots + ItemStack[] guiItems = new ItemStack[45]; + + // Hotbar & Armor Slots reorder + for (int i = 0; i < 9; i++) { + int rawIndex = 36 + i; + ItemStack item = (rawInv.length > rawIndex && rawInv[rawIndex] != null) + ? rawInv[rawIndex] + : new ItemStack(Material.AIR); + + guiItems[i] = item; // Top row + guiItems[36 + i] = item; // Bottom row (hotbar) + } + + // Main inventory + for (int slot = 9; slot < 36; slot++) { + guiItems[slot] = (rawInv.length > slot && rawInv[slot] != null) + ? rawInv[slot] + : new ItemStack(Material.AIR); + } + if (plugin.config.debugItemShare) { - plugin.getLogger().info("Invshare openGUI for player " + p.getName() + ": " + Arrays.toString(inventoryContents)); + plugin.getLogger().info("Invshare openGUI for player " + p.getName() + ": " + Arrays.toString(guiItems)); } - openInvShareGui(p, + + openInvShareGui( + p, plugin.config.inv_title.replace("%player%", playerName), - 5, - inventoryContents + 5, // number of rows + guiItems ); - })); + }) + ); + case ENDERCHEST -> plugin.getDataManager().getPlayerEnderchest(playerName) .thenAccept(ecContents -> RedisChat.getScheduler().runTask(() -> { if (plugin.config.debugItemShare) { plugin.getLogger().info("ECshare openGUI for player " + p.getName() + ": " + Arrays.toString(ecContents)); } + int totalSlots; if (plugin.getCustomInventoryAPI().hasCustomEnderChest(p)) { ItemStack[] custom = plugin.getCustomInventoryAPI().getCustomEnderChest(p); @@ -90,43 +119,53 @@ public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command } int rows = totalSlots / 9; - openInvShareGui(p, + openInvShareGui( + p, plugin.config.ec_title.replace("%player%", playerName), rows, ecContents ); - })); + }) + ); } + return true; } - private void openInvShareGui(Player player, String title, int size, ItemStack[] items) { - final Gui gui = Gui.empty(9, size); + private void openInvShareGui(Player player, String title, int rows, ItemStack[] items) { + Gui gui = Gui.empty(9, rows); gui.addItems(Arrays.stream(items) - .map(itemStack -> { - if (itemStack == null) return new ItemBuilder(Material.AIR); - return new ItemBuilder(itemStack); - }) + .map(itemStack -> itemStack == null ? new ItemBuilder(Material.AIR) : new ItemBuilder(itemStack)) .map(SimpleItem::new) .toArray(Item[]::new) ); - Window.single().setTitle(title).setGui(gui).setCloseHandlers(List.of(() -> new UniversalRunnable() { - @Override - public void run() { - player.updateInventory(); - } - }.runTaskLater(plugin, 1))).open(player); + + Window.single() + .setTitle(title) + .setGui(gui) + .setCloseHandlers(List.of(() -> new UniversalRunnable() { + @Override + public void run() { + player.updateInventory(); + } + }.runTaskLater(plugin, 1))) + .open(player); } private void openInvShareGuiItem(Player player, String title, ItemStack item) { Gui gui = Gui.empty(9, 3); gui.setItem(13, new SimpleItem(item)); - Window.single().setTitle(title).setGui(gui).setCloseHandlers(List.of(() -> new UniversalRunnable() { - @Override - public void run() { - player.updateInventory(); - } - }.runTaskLater(plugin, 1))).open(player); + + Window.single() + .setTitle(title) + .setGui(gui) + .setCloseHandlers(List.of(() -> new UniversalRunnable() { + @Override + public void run() { + player.updateInventory(); + } + }.runTaskLater(plugin, 1))) + .open(player); } public enum InventoryType { @@ -134,5 +173,4 @@ public enum InventoryType { ENDERCHEST, ITEM } - } From 6bc72b7b154ca00a4087e767f2d6f2e750c65422 Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Sat, 19 Apr 2025 00:54:59 +0200 Subject: [PATCH 3/8] Added an Event instead of an complete API This event replaces the CustomInventoryAPI with a simple Event --- .../java/dev/unnm3d/redischat/RedisChat.java | 11 +- .../redischat/api/CustomInventoryAPI.java | 385 ------------------ .../api/events/InventoryPlaceholderEvent.java | 45 ++ .../redischat/chat/ComponentProvider.java | 109 +++-- .../redischat/commands/InvShareCommand.java | 212 ++++------ 5 files changed, 204 insertions(+), 558 deletions(-) delete mode 100644 src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java create mode 100644 src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java diff --git a/src/main/java/dev/unnm3d/redischat/RedisChat.java b/src/main/java/dev/unnm3d/redischat/RedisChat.java index abb0b16..5c399ee 100644 --- a/src/main/java/dev/unnm3d/redischat/RedisChat.java +++ b/src/main/java/dev/unnm3d/redischat/RedisChat.java @@ -9,7 +9,6 @@ import dev.jorel.commandapi.CommandAPI; import dev.jorel.commandapi.CommandAPIBukkitConfig; import dev.jorel.commandapi.CommandAPICommand; -import dev.unnm3d.redischat.api.CustomInventoryAPI; import dev.unnm3d.redischat.api.DataManager; import dev.unnm3d.redischat.channels.ChannelCommand; import dev.unnm3d.redischat.channels.ChannelManager; @@ -46,11 +45,9 @@ import dev.unnm3d.redischat.utils.AdventureWebuiEditorAPI; import dev.unnm3d.redischat.utils.Metrics; import lombok.Getter; -import org.bukkit.Bukkit; import org.bukkit.command.CommandExecutor; import org.bukkit.command.PluginCommand; import org.bukkit.command.TabCompleter; -import org.bukkit.plugin.ServicePriority; import org.bukkit.plugin.java.JavaPlugin; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -97,9 +94,7 @@ public final class RedisChat extends JavaPlugin { private ExecutorService executorService; @Getter private MailGUIManager mailGUIManager; - // New field for the custom inventory API - @Getter - private CustomInventoryAPI customInventoryAPI; + public Config config; public FiltersConfig filterSettings; @@ -131,9 +126,7 @@ public void onEnable() { this.executorService = Executors.newFixedThreadPool(config.chatThreads); - // Initialize the custom inventory API - this.customInventoryAPI = new CustomInventoryAPI(); - Bukkit.getServicesManager().register(CustomInventoryAPI.class, this.customInventoryAPI, this, ServicePriority.Normal); + //Redis section diff --git a/src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java b/src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java deleted file mode 100644 index fd0ed20..0000000 --- a/src/main/java/dev/unnm3d/redischat/api/CustomInventoryAPI.java +++ /dev/null @@ -1,385 +0,0 @@ -package dev.unnm3d.redischat.api; - -import org.bukkit.Bukkit; -import org.bukkit.entity.Player; -import org.bukkit.inventory.Inventory; -import org.bukkit.inventory.ItemStack; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; - -import java.util.HashMap; -import java.util.Map; -import java.util.UUID; - -/** - * API to override the inventory/item/enderchest shown when a player uses the chat tags - */ -public class CustomInventoryAPI { - private final Map customInventories = new HashMap<>(); - private final Map customItems = new HashMap<>(); - private final Map customEnderChests = new HashMap<>(); - private final Map customInventorySizes = new HashMap<>(); - - /** - * Set a custom inventory to be displayed when a player uses the inventory tag - * - * @param player The player to set the custom inventory for - * @param contents The contents to display, can be null to remove the override - */ - public void setCustomInventory(@NotNull Player player, @Nullable ItemStack[] contents) { - if (contents == null) { - customInventories.remove(player.getUniqueId()); - customInventorySizes.remove(player.getUniqueId()); - } else { - customInventories.put(player.getUniqueId(), contents.clone()); - // Default size based on contents length, rounded up to nearest multiple of 9 - int size = Math.min(54, (int) Math.ceil(contents.length / 9.0) * 9); - customInventorySizes.putIfAbsent(player.getUniqueId(), size); - } - } - - /** - * Set a custom inventory with a specific size - * - * @param player The player to set the custom inventory for - * @param contents The contents to display, can be null to remove the override - * @param size The size of the inventory (must be a multiple of 9, between 9 and 54) - */ - public void setCustomInventory(@NotNull Player player, @Nullable ItemStack[] contents, int size) { - if (contents == null) { - customInventories.remove(player.getUniqueId()); - customInventorySizes.remove(player.getUniqueId()); - return; - } - - // Validate size - if (size % 9 != 0 || size < 9 || size > 54) { - throw new IllegalArgumentException("Inventory size must be a multiple of 9 between 9 and 54"); - } - - // Create a new array with the specified size - ItemStack[] resizedContents = new ItemStack[size]; - // Copy contents, limited by either the original array length or the new size - System.arraycopy(contents, 0, resizedContents, 0, Math.min(contents.length, size)); - - customInventories.put(player.getUniqueId(), resizedContents); - customInventorySizes.put(player.getUniqueId(), size); - } - - /** - * Set an item at a specific slot in the custom inventory - * - * @param player The player to set the item for - * @param slot The slot to set the item at (0-based index) - * @param item The item to set, can be null to clear the slot - */ - public void setCustomInventoryItem(@NotNull Player player, int slot, @Nullable ItemStack item) { - UUID playerId = player.getUniqueId(); - - // If no custom inventory exists yet, create one with default size - if (!customInventories.containsKey(playerId)) { - int size = customInventorySizes.getOrDefault(playerId, 27); - customInventories.put(playerId, new ItemStack[size]); - } - - ItemStack[] contents = customInventories.get(playerId); - - // Check if the slot is within bounds - if (slot < 0 || slot >= contents.length) { - throw new IllegalArgumentException("Slot " + slot + " is outside the inventory bounds (0-" + (contents.length - 1) + ")"); - } - - // Set the item at the specified slot - contents[slot] = item != null ? item.clone() : null; - } - - /** - * Get the custom inventory size for a player - * - * @param player The player to get the inventory size for - * @return The custom inventory size, or 27 if not set - */ - public int getCustomInventorySize(@NotNull Player player) { - return customInventorySizes.getOrDefault(player.getUniqueId(), 27); - } - - /** - * Set the custom inventory size for a player - * - * @param player The player to set the inventory size for - * @param size The size to set (must be a multiple of 9 between 9 and 54) - */ - public void setCustomInventorySize(@NotNull Player player, int size) { - if (size % 9 != 0 || size < 9 || size > 54) { - throw new IllegalArgumentException("Inventory size must be a multiple of 9 between 9 and 54"); - } - - UUID playerId = player.getUniqueId(); - customInventorySizes.put(playerId, size); - - // If there's an existing inventory, resize it - if (customInventories.containsKey(playerId)) { - ItemStack[] oldContents = customInventories.get(playerId); - ItemStack[] newContents = new ItemStack[size]; - - // Copy the contents from the old array to the new one - System.arraycopy(oldContents, 0, newContents, 0, Math.min(oldContents.length, size)); - - customInventories.put(playerId, newContents); - } - } - - /** - * Set a custom item to be displayed when a player uses the item tag - * - * @param player The player to set the custom item for - * @param item The item to display, can be null to remove the override - */ - public void setCustomItem(@NotNull Player player, @Nullable ItemStack item) { - if (item == null) { - customItems.remove(player.getUniqueId()); - } else { - customItems.put(player.getUniqueId(), item.clone()); - } - } - - /** - * Set a custom enderchest to be displayed when a player uses the enderchest tag - * - * @param player The player to set the custom enderchest for - * @param contents The contents to display, can be null to remove the override - */ - public void setCustomEnderChest(@NotNull Player player, @Nullable ItemStack[] contents) { - if (contents == null) { - customEnderChests.remove(player.getUniqueId()); - } else { - customEnderChests.put(player.getUniqueId(), contents.clone()); - } - } - - /** - * Check if a player has a custom inventory override - * - * @param player The player to check - * @return True if the player has a custom inventory, false otherwise - */ - public boolean hasCustomInventory(@NotNull Player player) { - return customInventories.containsKey(player.getUniqueId()); - } - - /** - * Check if a player has a custom item override - * - * @param player The player to check - * @return True if the player has a custom item, false otherwise - */ - public boolean hasCustomItem(@NotNull Player player) { - return customItems.containsKey(player.getUniqueId()); - } - - /** - * Check if a player has a custom enderchest override - * - * @param player The player to check - * @return True if the player has a custom enderchest, false otherwise - */ - public boolean hasCustomEnderChest(@NotNull Player player) { - return customEnderChests.containsKey(player.getUniqueId()); - } - - /** - * Get the custom inventory for a player - * - * @param player The player to get the custom inventory for - * @return The custom inventory, or null if none is set - */ - public @Nullable ItemStack[] getCustomInventory(@NotNull Player player) { - ItemStack[] contents = customInventories.get(player.getUniqueId()); - return contents != null ? contents.clone() : null; - } - - /** - * Get the custom item for a player - * - * @param player The player to get the custom item for - * @return The custom item, or null if none is set - */ - public @Nullable ItemStack getCustomItem(@NotNull Player player) { - ItemStack item = customItems.get(player.getUniqueId()); - return item != null ? item.clone() : null; - } - - /** - * Get the custom enderchest for a player - * - * @param player The player to get the custom enderchest for - * @return The custom enderchest, or null if none is set - */ - public @Nullable ItemStack[] getCustomEnderChest(@NotNull Player player) { - ItemStack[] contents = customEnderChests.get(player.getUniqueId()); - return contents != null ? contents.clone() : null; - } - - /** - * Clear all custom inventories/items/enderchests for a player - * - * @param player The player to clear custom content for - */ - public void clearCustomContent(@NotNull Player player) { - UUID playerId = player.getUniqueId(); - customInventories.remove(playerId); - customItems.remove(playerId); - customEnderChests.remove(playerId); - customInventorySizes.remove(playerId); - } - - /** - * Clear all custom inventories/items/enderchests for all players - */ - public void clearAllCustomContent() { - customInventories.clear(); - customItems.clear(); - customEnderChests.clear(); - customInventorySizes.clear(); - } - - /** - * Create a new GUI builder for a player - * - * @param player The player to create the GUI for - * @return A new GUI builder - */ - public GUIBuilder createGUI(@NotNull Player player) { - return new GUIBuilder(player, this); - } - - /** - * Builder class for creating custom GUIs - */ - public static class GUIBuilder { - private final Player player; - private final CustomInventoryAPI api; - private final ItemStack[] contents; - private final int size; - - private GUIBuilder(Player player, CustomInventoryAPI api) { - this(player, api, 27); // Default size - } - - private GUIBuilder(Player player, CustomInventoryAPI api, int size) { - this.player = player; - this.api = api; - this.size = size; - this.contents = new ItemStack[size]; - } - - /** - * Create a new GUI builder with a specific size - * - * @param size The size of the inventory (must be a multiple of 9, between 9 and 54) - * @return This builder for chaining - */ - public GUIBuilder withSize(int size) { - return new GUIBuilder(player, api, size); - } - - /** - * Set an item at a specific slot - * - * @param slot The slot to set (0-based index) - * @param item The item to set - * @return This builder for chaining - */ - public GUIBuilder setItem(int slot, ItemStack item) { - if (slot < 0 || slot >= contents.length) { - throw new IllegalArgumentException("Slot " + slot + " is outside the inventory bounds (0-" + (contents.length - 1) + ")"); - } - contents[slot] = item != null ? item.clone() : null; - return this; - } - - /** - * Fill all empty slots with the specified item - * - * @param item The item to fill with - * @return This builder for chaining - */ - public GUIBuilder fillEmpty(ItemStack item) { - for (int i = 0; i < contents.length; i++) { - if (contents[i] == null) { - contents[i] = item.clone(); - } - } - return this; - } - - /** - * Fill the borders of the inventory with the specified item - * - * @param item The item to fill with - * @return This builder for chaining - */ - public GUIBuilder fillBorders(ItemStack item) { - int rows = size / 9; - - // Top and bottom rows - for (int i = 0; i < 9; i++) { - contents[i] = item.clone(); // Top row - contents[(rows - 1) * 9 + i] = item.clone(); // Bottom row - } - - // Left and right columns (excluding corners that are already filled) - for (int row = 1; row < rows - 1; row++) { - contents[row * 9] = item.clone(); // Left column - contents[row * 9 + 8] = item.clone(); // Right column - } - - return this; - } - - /** - * Fill a row with the specified item - * - * @param row The row to fill (0-based index) - * @param item The item to fill with - * @return This builder for chaining - */ - public GUIBuilder fillRow(int row, ItemStack item) { - if (row < 0 || row >= size / 9) { - throw new IllegalArgumentException("Row " + row + " is outside the inventory bounds (0-" + (size / 9 - 1) + ")"); - } - - for (int i = 0; i < 9; i++) { - contents[row * 9 + i] = item.clone(); - } - - return this; - } - - /** - * Fill a column with the specified item - * - * @param column The column to fill (0-based index) - * @param item The item to fill with - * @return This builder for chaining - */ - public GUIBuilder fillColumn(int column, ItemStack item) { - if (column < 0 || column >= 9) { - throw new IllegalArgumentException("Column " + column + " is outside the inventory bounds (0-8)"); - } - - for (int row = 0; row < size / 9; row++) { - contents[row * 9 + column] = item.clone(); - } - - return this; - } - - /** - * Build the GUI and apply it to the player - */ - public void build() { - api.setCustomInventory(player, contents, size); - } - } -} \ No newline at end of file diff --git a/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java b/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java new file mode 100644 index 0000000..f70c15a --- /dev/null +++ b/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java @@ -0,0 +1,45 @@ +package dev.unnm3d.redischat.api.events; + +import lombok.Getter; +import org.bukkit.entity.Player; +import org.bukkit.event.Event; +import org.bukkit.event.HandlerList; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.NotNull; + +public class InventoryPlaceholderEvent extends Event { + public enum Type { INVENTORY, ITEM, ENDERCHEST } + + private static final HandlerList HANDLERS = new HandlerList(); + + @Getter + private final Player player; + @Getter + private final Type type; + private ItemStack[] contents; + + public InventoryPlaceholderEvent(Player player, Type type, ItemStack[] defaultContents) { + super(!player.getServer().isPrimaryThread()); + this.player = player; + this.type = type; + this.contents = defaultContents; + } + + public ItemStack[] getContents() { + return contents.clone(); + } + + /** Replace the inventory that will be shown when someone uses the chat tag */ + public void setContents(ItemStack[] newContents) { + this.contents = newContents.clone(); + } + + @Override + public @NotNull HandlerList getHandlers() { + return HANDLERS; + } + + public static HandlerList getHandlerList() { + return HANDLERS; + } +} diff --git a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java index f4482f2..0ed002b 100644 --- a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java +++ b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java @@ -3,6 +3,7 @@ import dev.unnm3d.redischat.Permissions; import dev.unnm3d.redischat.RedisChat; import dev.unnm3d.redischat.api.TagResolverIntegration; +import dev.unnm3d.redischat.api.events.InventoryPlaceholderEvent; import dev.unnm3d.redischat.utils.ItemNameProvider; import lombok.AllArgsConstructor; import lombok.Getter; @@ -16,6 +17,7 @@ import net.kyori.adventure.text.serializer.legacy.LegacyComponentSerializer; import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import org.bukkit.Bukkit; +import org.bukkit.Material; import org.bukkit.OfflinePlayer; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; @@ -257,24 +259,45 @@ public Component parseChatMessageContent(@NotNull CommandSender cmdSender, @NotN String toParseItem = plugin.config.itemFormat .replace("%player%", player.getName()) .replace("%command%", "/invshare " + player.getName() + "-item"); - Component toParseItemComponent = parse(player, toParseItem, true, false, false, this.standardTagResolver); + Component toParseItemComponent = parse(player, toParseItem, + true, false, false, this.standardTagResolver); - // Get the item to display (either custom or actual) - ItemStack itemToDisplay = plugin.getCustomInventoryAPI().hasCustomItem(p) ? - plugin.getCustomInventoryAPI().getCustomItem(p) : - p.getInventory().getItemInMainHand(); + // 1) Default item from main hand + ItemStack defaultItem = p.getInventory().getItemInMainHand(); + // 2) Fire event and allow override + InventoryPlaceholderEvent itemEvent = new InventoryPlaceholderEvent( + p, + InventoryPlaceholderEvent.Type.ITEM, + new ItemStack[]{ defaultItem } + ); + Bukkit.getPluginManager().callEvent(itemEvent); + + // 3) Unwrap result + ItemStack[] itemArr = itemEvent.getContents(); + ItemStack itemToDisplay = (itemArr.length > 0 && itemArr[0] != null) + ? itemArr[0] + : new ItemStack(Material.AIR); + + // … and continue as usual: final Component itemName = getItemNameComponent(itemToDisplay); - toParseItemComponent = toParseItemComponent.replaceText(rTextBuilder -> rTextBuilder.matchLiteral("%item_name%").replacement(itemName)); - toParseItemComponent = toParseItemComponent.replaceText(rTextBuilder -> rTextBuilder.matchLiteral("%amount%").replacement( - itemToDisplay.getAmount() > 1 ? - "x" + itemToDisplay.getAmount() + " " : - "" - )); + toParseItemComponent = toParseItemComponent.replaceText(r -> r + .matchLiteral("%item_name%") + .replacement(itemName) + ); + toParseItemComponent = toParseItemComponent.replaceText(r -> r + .matchLiteral("%amount%") + .replacement(itemToDisplay.getAmount() > 1 + ? "x" + itemToDisplay.getAmount() + " " + : "" + ) + ); builder.resolver(Placeholder.component(plugin.config.item_tag, toParseItemComponent)); } + + if (player.hasPermission(Permissions.USE_ENDERCHEST.getPermission())) { String toParseEnderChest = plugin.config.enderChestFormat .replace("%player%", player.getName()) @@ -400,50 +423,58 @@ public String invShareFormatting(CommandSender sender, String message) { if (!(sender instanceof Player player)) return message; if (plugin.config.interactiveChatNostalgia) { - message = message.replace("[inv]", "<" + plugin.config.inv_tag + ">") + message = message + .replace("[inv]", "<" + plugin.config.inv_tag + ">") .replace("[inventory]", "<" + plugin.config.inv_tag + ">") - .replace("[i]", "<" + plugin.config.item_tag + ">") .replace("[item]", "<" + plugin.config.item_tag + ">") - .replace("[enderchest]", "<" + plugin.config.ec_tag + ">") - .replace("[ec]", "<" + plugin.config.ec_tag + ">"); + .replace("[i]", "<" + plugin.config.item_tag + ">") + .replace("[ec]", "<" + plugin.config.ec_tag + ">") + .replace("[enderchest]", "<" + plugin.config.ec_tag + ">"); } + // Inventory-Tag → Event + save if (message.contains("<" + plugin.config.inv_tag + ">")) { - if (plugin.getCustomInventoryAPI().hasCustomInventory(player)) { - // Use custom inventory if available - plugin.getDataManager().addInventory(player.getName(), - plugin.getCustomInventoryAPI().getCustomInventory(player)); - } else { - // Fall back to actual inventory - plugin.getDataManager().addInventory(player.getName(), player.getInventory().getContents()); - } + ItemStack[] inv = player.getInventory().getContents(); + InventoryPlaceholderEvent ev = new InventoryPlaceholderEvent( + player, + InventoryPlaceholderEvent.Type.INVENTORY, + inv + ); + Bukkit.getPluginManager().callEvent(ev); + plugin.getDataManager().addInventory(player.getName(), ev.getContents()); } if (message.contains("<" + plugin.config.item_tag + ">")) { - if (plugin.getCustomInventoryAPI().hasCustomItem(player)) { - // Use custom item if available - plugin.getDataManager().addItem(player.getName(), - plugin.getCustomInventoryAPI().getCustomItem(player)); - } else { - // Fall back to actual item - plugin.getDataManager().addItem(player.getName(), player.getInventory().getItemInMainHand()); - } + ItemStack inHand = player.getInventory().getItemInMainHand(); + InventoryPlaceholderEvent ev = new InventoryPlaceholderEvent( + player, + InventoryPlaceholderEvent.Type.ITEM, + new ItemStack[]{ inHand } + ); + Bukkit.getPluginManager().callEvent(ev); + // save only one item + ItemStack toSave = ev.getContents().length > 0 + ? ev.getContents()[0] + : new ItemStack(Material.AIR); + plugin.getDataManager().addItem(player.getName(), toSave); } + // Enderchest-Tag → Event + save if (message.contains("<" + plugin.config.ec_tag + ">")) { - if (plugin.getCustomInventoryAPI().hasCustomEnderChest(player)) { - // Use custom enderchest if available - plugin.getDataManager().addEnderchest(player.getName(), - plugin.getCustomInventoryAPI().getCustomEnderChest(player)); - } else { - // Fall back to actual enderchest - plugin.getDataManager().addEnderchest(player.getName(), player.getEnderChest().getContents()); - } + ItemStack[] ec = player.getEnderChest().getContents(); + InventoryPlaceholderEvent ev = new InventoryPlaceholderEvent( + player, + InventoryPlaceholderEvent.Type.ENDERCHEST, + ec + ); + Bukkit.getPluginManager().callEvent(ev); + plugin.getDataManager().addEnderchest(player.getName(), ev.getContents()); } return message; } + @SuppressWarnings("ResultOfMethodCallIgnored") public void logComponent(Component component) { if (!plugin.config.chatLogging) { diff --git a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java index 4317d7e..7d6bb0d 100644 --- a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java +++ b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java @@ -4,16 +4,13 @@ import dev.unnm3d.redischat.RedisChat; import lombok.AllArgsConstructor; import org.bukkit.Material; -import org.bukkit.block.Container; import org.bukkit.command.Command; import org.bukkit.command.CommandExecutor; import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.bukkit.inventory.meta.BlockStateMeta; import org.jetbrains.annotations.NotNull; import xyz.xenondevs.invui.gui.Gui; -import xyz.xenondevs.invui.item.Item; import xyz.xenondevs.invui.item.builder.ItemBuilder; import xyz.xenondevs.invui.item.impl.SimpleItem; import xyz.xenondevs.invui.window.Window; @@ -24,153 +21,118 @@ @AllArgsConstructor public class InvShareCommand implements CommandExecutor { - private final RedisChat plugin; + private final RedisChat plugin; // schon final @Override - public boolean onCommand(@NotNull CommandSender commandSender, @NotNull Command command, @NotNull String s, @NotNull String[] args) { - if (!(commandSender instanceof Player p)) return true; + public boolean onCommand(final @NotNull CommandSender sender, + final @NotNull Command command, + final @NotNull String label, + final @NotNull String[] args) { + + if (!(sender instanceof final Player p)) return true; if (args.length == 0) return true; - String[] split = args[0].split("-"); + final String[] split = args[0].split("-"); if (split.length != 2) return true; - String playerName = split[0]; - InventoryType type; + final String targetName = split[0]; + final InventoryType type; try { type = InventoryType.valueOf(split[1].toUpperCase()); - } catch (IllegalArgumentException e) { + } catch (final IllegalArgumentException e) { return true; } switch (type) { - case ITEM -> plugin.getDataManager().getPlayerItem(playerName) - .thenAccept(item -> - RedisChat.getScheduler().runTask(() -> { - if (plugin.config.debugItemShare) { - plugin.getLogger().info("Itemshare openGUI for player " + p.getName() + ": " + item.getType()); - } - if (item.getType().toString().endsWith("SHULKER_BOX") && item.getItemMeta() instanceof BlockStateMeta bsm - && bsm.getBlockState() instanceof Container shulkerBox) { - - openInvShareGui(p, - plugin.config.shulker_title.replace("%player%", playerName), - 3, - shulkerBox.getSnapshotInventory().getContents() - ); - } else { - openInvShareGuiItem(p, - plugin.config.item_title.replace("%player%", playerName), - item - ); - } - }) - ); - - case INVENTORY -> plugin.getDataManager().getPlayerInventory(playerName) - .thenAccept(rawInv -> - RedisChat.getScheduler().runTask(() -> { - // GUI dimensions: 9 columns × 5 rows = 45 slots - ItemStack[] guiItems = new ItemStack[45]; - - // Hotbar & Armor Slots reorder - for (int i = 0; i < 9; i++) { - int rawIndex = 36 + i; - ItemStack item = (rawInv.length > rawIndex && rawInv[rawIndex] != null) - ? rawInv[rawIndex] - : new ItemStack(Material.AIR); - - guiItems[i] = item; // Top row - guiItems[36 + i] = item; // Bottom row (hotbar) - } - - // Main inventory - for (int slot = 9; slot < 36; slot++) { - guiItems[slot] = (rawInv.length > slot && rawInv[slot] != null) - ? rawInv[slot] - : new ItemStack(Material.AIR); - } - - if (plugin.config.debugItemShare) { - plugin.getLogger().info("Invshare openGUI for player " + p.getName() + ": " + Arrays.toString(guiItems)); - } - - openInvShareGui( + case ITEM -> plugin.getDataManager() + .getPlayerItem(targetName) + .thenAccept(item -> { + // item ist effektiv final + RedisChat.getScheduler().runTask(() -> + openRawGUI( + p, + plugin.config.item_title.replace("%player%", targetName), + new ItemStack[]{ item } + ) + ); + }); + + case INVENTORY -> plugin.getDataManager() + .getPlayerInventory(targetName) + .thenAccept(rawInv -> { + final ItemStack[] guiItems = new ItemStack[45]; + for (int i = 0; i < 9; i++) { + final int idx = 36 + i; + final ItemStack it = idx < rawInv.length && rawInv[idx] != null + ? rawInv[idx] + : new ItemStack(Material.AIR); + guiItems[i] = guiItems[36 + i] = it; + } + for (int i = 9; i < 36; i++) { + guiItems[i] = i < rawInv.length && rawInv[i] != null + ? rawInv[i] + : new ItemStack(Material.AIR); + } + + RedisChat.getScheduler().runTask(() -> + openRawGUI( p, - plugin.config.inv_title.replace("%player%", playerName), - 5, // number of rows + plugin.config.inv_title.replace("%player%", targetName), guiItems - ); - }) - ); - - case ENDERCHEST -> plugin.getDataManager().getPlayerEnderchest(playerName) - .thenAccept(ecContents -> - RedisChat.getScheduler().runTask(() -> { - if (plugin.config.debugItemShare) { - plugin.getLogger().info("ECshare openGUI for player " + p.getName() + ": " + Arrays.toString(ecContents)); - } - - int totalSlots; - if (plugin.getCustomInventoryAPI().hasCustomEnderChest(p)) { - ItemStack[] custom = plugin.getCustomInventoryAPI().getCustomEnderChest(p); - totalSlots = custom.length; - } else { - totalSlots = p.getEnderChest().getSize(); - } - int rows = totalSlots / 9; - - openInvShareGui( + ) + ); + }); + + case ENDERCHEST -> plugin.getDataManager() + .getPlayerEnderchest(targetName) + .thenAccept(fetched -> { + // fetched ist effektiv final + RedisChat.getScheduler().runTask(() -> + openRawGUI( p, - plugin.config.ec_title.replace("%player%", playerName), - rows, - ecContents - ); - }) - ); + plugin.config.ec_title.replace("%player%", targetName), + fetched + ) + ); + }); } return true; } - private void openInvShareGui(Player player, String title, int rows, ItemStack[] items) { - Gui gui = Gui.empty(9, rows); - gui.addItems(Arrays.stream(items) - .map(itemStack -> itemStack == null ? new ItemBuilder(Material.AIR) : new ItemBuilder(itemStack)) - .map(SimpleItem::new) - .toArray(Item[]::new) - ); + private void openRawGUI(final Player viewer, + final String title, + final ItemStack[] contents) { - Window.single() - .setTitle(title) - .setGui(gui) - .setCloseHandlers(List.of(() -> new UniversalRunnable() { - @Override - public void run() { - player.updateInventory(); - } - }.runTaskLater(plugin, 1))) - .open(player); - } + // Inhalte auf die GUI‑Größe trimmen/padden + int rows = contents.length / 9 + (contents.length % 9 == 0 ? 0 : 1); + rows = Math.max(1, Math.min(6, rows)); - private void openInvShareGuiItem(Player player, String title, ItemStack item) { - Gui gui = Gui.empty(9, 3); - gui.setItem(13, new SimpleItem(item)); + final ItemStack[] gui = Arrays.copyOf(contents, rows * 9); + for (int i = 0; i < gui.length; i++) { + if (gui[i] == null) gui[i] = new ItemStack(Material.AIR); + } + + final Gui inv = Gui.empty(9, rows); + final List items = Arrays.stream(gui) + .map(ItemBuilder::new) + .map(SimpleItem::new) + .toList(); + inv.addItems(items.toArray(SimpleItem[]::new)); Window.single() .setTitle(title) - .setGui(gui) - .setCloseHandlers(List.of(() -> new UniversalRunnable() { - @Override - public void run() { - player.updateInventory(); - } - }.runTaskLater(plugin, 1))) - .open(player); + .setGui(inv) + .setCloseHandlers(List.of(() -> + new UniversalRunnable() { + @Override + public void run() { + viewer.updateInventory(); + } + }.runTaskLater(plugin, 1) + )) + .open(viewer); } - public enum InventoryType { - INVENTORY, - ENDERCHEST, - ITEM - } + public enum InventoryType { INVENTORY, ENDERCHEST, ITEM } } From 2198784732af4b944ded253d147cf26cf77bdacb Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Sat, 19 Apr 2025 01:04:21 +0200 Subject: [PATCH 4/8] Update InvShareCommand.java updated the ordering of the Armor and Items in the Inventory Share GUI --- .../redischat/commands/InvShareCommand.java | 22 +++++++++++++------ 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java index 7d6bb0d..3f49aa5 100644 --- a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java +++ b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java @@ -62,18 +62,24 @@ public boolean onCommand(final @NotNull CommandSender sender, .thenAccept(rawInv -> { final ItemStack[] guiItems = new ItemStack[45]; for (int i = 0; i < 9; i++) { - final int idx = 36 + i; - final ItemStack it = idx < rawInv.length && rawInv[idx] != null - ? rawInv[idx] + int armorIdx = 36 + i; + ItemStack armorOrAir = (armorIdx < rawInv.length && rawInv[armorIdx] != null) + ? rawInv[armorIdx] : new ItemStack(Material.AIR); - guiItems[i] = guiItems[36 + i] = it; + guiItems[i] = armorOrAir; } - for (int i = 9; i < 36; i++) { - guiItems[i] = i < rawInv.length && rawInv[i] != null + for (int slot = 9; slot < 36; slot++) { + ItemStack content = (slot < rawInv.length && rawInv[slot] != null) + ? rawInv[slot] + : new ItemStack(Material.AIR); + guiItems[slot] = content; + } + for (int i = 0; i < 9; i++) { + ItemStack hotbarOrAir = (i < rawInv.length && rawInv[i] != null) ? rawInv[i] : new ItemStack(Material.AIR); + guiItems[36 + i] = hotbarOrAir; } - RedisChat.getScheduler().runTask(() -> openRawGUI( p, @@ -83,6 +89,8 @@ public boolean onCommand(final @NotNull CommandSender sender, ); }); + + case ENDERCHEST -> plugin.getDataManager() .getPlayerEnderchest(targetName) .thenAccept(fetched -> { From fcc08c42b593c22433d0e2cda0e9565b8cfdcb87 Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Sat, 19 Apr 2025 15:46:21 +0200 Subject: [PATCH 5/8] Update InvShareCommand.java Fixed an Issue with the Item Display --- .../dev/unnm3d/redischat/commands/InvShareCommand.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java index 3f49aa5..165a86f 100644 --- a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java +++ b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java @@ -47,16 +47,19 @@ public boolean onCommand(final @NotNull CommandSender sender, case ITEM -> plugin.getDataManager() .getPlayerItem(targetName) .thenAccept(item -> { - // item ist effektiv final + ItemStack[] guiItems = new ItemStack[27]; + Arrays.fill(guiItems, new ItemStack(Material.AIR)); + guiItems[13] = item; RedisChat.getScheduler().runTask(() -> openRawGUI( p, plugin.config.item_title.replace("%player%", targetName), - new ItemStack[]{ item } + guiItems ) ); }); + case INVENTORY -> plugin.getDataManager() .getPlayerInventory(targetName) .thenAccept(rawInv -> { From f3219678ebd6349b1bd3a9ba5afb30d620cae7e1 Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Thu, 24 Apr 2025 18:40:58 +0200 Subject: [PATCH 6/8] fixxed everything! idk what i should write here --- .../java/dev/unnm3d/redischat/RedisChat.java | 3 --- .../api/events/InventoryPlaceholderEvent.java | 4 ++-- .../redischat/chat/ComponentProvider.java | 18 ++++++++++++------ .../redischat/commands/InvShareCommand.java | 1 - 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/src/main/java/dev/unnm3d/redischat/RedisChat.java b/src/main/java/dev/unnm3d/redischat/RedisChat.java index 5c399ee..6a18888 100644 --- a/src/main/java/dev/unnm3d/redischat/RedisChat.java +++ b/src/main/java/dev/unnm3d/redischat/RedisChat.java @@ -126,9 +126,6 @@ public void onEnable() { this.executorService = Executors.newFixedThreadPool(config.chatThreads); - - - //Redis section this.dataManager = switch (config.getDataType()) { case REDIS -> RedisDataManager.startup(this); diff --git a/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java b/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java index f70c15a..6240073 100644 --- a/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java +++ b/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java @@ -20,8 +20,8 @@ public enum Type { INVENTORY, ITEM, ENDERCHEST } public InventoryPlaceholderEvent(Player player, Type type, ItemStack[] defaultContents) { super(!player.getServer().isPrimaryThread()); - this.player = player; - this.type = type; + this.player = player; + this.type = type; this.contents = defaultContents; } diff --git a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java index 0ed002b..a0305f4 100644 --- a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java +++ b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java @@ -37,7 +37,6 @@ @AllArgsConstructor public class ComponentProvider { - @Subst("") private final RedisChat plugin; private final MiniMessage miniMessage; @Getter @@ -273,13 +272,19 @@ public Component parseChatMessageContent(@NotNull CommandSender cmdSender, @NotN ); Bukkit.getPluginManager().callEvent(itemEvent); - // 3) Unwrap result - ItemStack[] itemArr = itemEvent.getContents(); - ItemStack itemToDisplay = (itemArr.length > 0 && itemArr[0] != null) - ? itemArr[0] + + // 3) Unwrap result and clone contents + ItemStack[] originalArr = itemEvent.getContents(); + ItemStack[] clonedArr = Arrays.stream(originalArr) + .map(item -> item == null ? null : item.clone()) + .toArray(ItemStack[]::new); + + // safely grab a display item + ItemStack itemToDisplay = (clonedArr.length > 0 && clonedArr[0] != null) + ? clonedArr[0] : new ItemStack(Material.AIR); - // … and continue as usual: + // … continue as before … final Component itemName = getItemNameComponent(itemToDisplay); toParseItemComponent = toParseItemComponent.replaceText(r -> r .matchLiteral("%item_name%") @@ -293,6 +298,7 @@ public Component parseChatMessageContent(@NotNull CommandSender cmdSender, @NotN ) ); + builder.resolver(Placeholder.component(plugin.config.item_tag, toParseItemComponent)); } diff --git a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java index 165a86f..809746a 100644 --- a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java +++ b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java @@ -48,7 +48,6 @@ public boolean onCommand(final @NotNull CommandSender sender, .getPlayerItem(targetName) .thenAccept(item -> { ItemStack[] guiItems = new ItemStack[27]; - Arrays.fill(guiItems, new ItemStack(Material.AIR)); guiItems[13] = item; RedisChat.getScheduler().runTask(() -> openRawGUI( From 200c9c39794244962511863c1d2a733b475ab637 Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Thu, 24 Apr 2025 20:47:17 +0200 Subject: [PATCH 7/8] fixed/changed a bit Happy? --- .../redischat/chat/ComponentProvider.java | 38 +++++++++++++-- .../redischat/commands/InvShareCommand.java | 47 ++++--------------- 2 files changed, 45 insertions(+), 40 deletions(-) diff --git a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java index a0305f4..b42972d 100644 --- a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java +++ b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java @@ -22,7 +22,6 @@ import org.bukkit.command.CommandSender; import org.bukkit.entity.Player; import org.bukkit.inventory.ItemStack; -import org.intellij.lang.annotations.Subst; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; @@ -440,16 +439,49 @@ public String invShareFormatting(CommandSender sender, String message) { // Inventory-Tag → Event + save if (message.contains("<" + plugin.config.inv_tag + ">")) { - ItemStack[] inv = player.getInventory().getContents(); + ItemStack[] contents = player.getInventory().getContents(); + ItemStack[] armor = player.getInventory().getArmorContents(); + ItemStack offhand = player.getInventory().getItemInOffHand(); + + ItemStack[] combined = new ItemStack[45]; + Arrays.fill(combined, new ItemStack(Material.AIR)); + + for (int i = 0; i < armor.length && i < 4; i++) { + if (armor[i] != null) { + combined[i] = armor[i].clone(); + } + } + combined[4] = offhand.clone(); + + for (int i = 9; i < contents.length; i++) { + ItemStack it = contents[i]; + if (it != null) { + combined[i] = it.clone(); + } + } + + for (int i = 0; i < 9; i++) { + ItemStack hot = contents[i]; + if (hot != null) { + combined[36 + i] = hot.clone(); + } else { + //Secure to not show duplicated Armour + combined[36 + i] = new ItemStack(Material.AIR); + } + } + InventoryPlaceholderEvent ev = new InventoryPlaceholderEvent( player, InventoryPlaceholderEvent.Type.INVENTORY, - inv + combined ); Bukkit.getPluginManager().callEvent(ev); plugin.getDataManager().addInventory(player.getName(), ev.getContents()); } + + + if (message.contains("<" + plugin.config.item_tag + ">")) { ItemStack inHand = player.getInventory().getItemInMainHand(); InventoryPlaceholderEvent ev = new InventoryPlaceholderEvent( diff --git a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java index 809746a..d2f88c4 100644 --- a/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java +++ b/src/main/java/dev/unnm3d/redischat/commands/InvShareCommand.java @@ -21,7 +21,7 @@ @AllArgsConstructor public class InvShareCommand implements CommandExecutor { - private final RedisChat plugin; // schon final + private final RedisChat plugin; @Override public boolean onCommand(final @NotNull CommandSender sender, @@ -57,46 +57,20 @@ public boolean onCommand(final @NotNull CommandSender sender, ) ); }); - - case INVENTORY -> plugin.getDataManager() .getPlayerInventory(targetName) - .thenAccept(rawInv -> { - final ItemStack[] guiItems = new ItemStack[45]; - for (int i = 0; i < 9; i++) { - int armorIdx = 36 + i; - ItemStack armorOrAir = (armorIdx < rawInv.length && rawInv[armorIdx] != null) - ? rawInv[armorIdx] - : new ItemStack(Material.AIR); - guiItems[i] = armorOrAir; - } - for (int slot = 9; slot < 36; slot++) { - ItemStack content = (slot < rawInv.length && rawInv[slot] != null) - ? rawInv[slot] - : new ItemStack(Material.AIR); - guiItems[slot] = content; - } - for (int i = 0; i < 9; i++) { - ItemStack hotbarOrAir = (i < rawInv.length && rawInv[i] != null) - ? rawInv[i] - : new ItemStack(Material.AIR); - guiItems[36 + i] = hotbarOrAir; - } - RedisChat.getScheduler().runTask(() -> - openRawGUI( - p, - plugin.config.inv_title.replace("%player%", targetName), - guiItems - ) - ); - }); - - - + .thenAccept(combinedArray -> + RedisChat.getScheduler().runTask(() -> + openRawGUI( + p, + plugin.config.inv_title.replace("%player%", targetName), + combinedArray + ) + ) + ); case ENDERCHEST -> plugin.getDataManager() .getPlayerEnderchest(targetName) .thenAccept(fetched -> { - // fetched ist effektiv final RedisChat.getScheduler().runTask(() -> openRawGUI( p, @@ -114,7 +88,6 @@ private void openRawGUI(final Player viewer, final String title, final ItemStack[] contents) { - // Inhalte auf die GUI‑Größe trimmen/padden int rows = contents.length / 9 + (contents.length % 9 == 0 ? 0 : 1); rows = Math.max(1, Math.min(6, rows)); From af68e1420ece2cb425c4f08a5741a8a39d6216c9 Mon Sep 17 00:00:00 2001 From: SaltyAlpaca Date: Thu, 24 Apr 2025 20:47:56 +0200 Subject: [PATCH 8/8] Update ComponentProvider.java --- src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java index b42972d..92f2369 100644 --- a/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java +++ b/src/main/java/dev/unnm3d/redischat/chat/ComponentProvider.java @@ -479,9 +479,6 @@ public String invShareFormatting(CommandSender sender, String message) { plugin.getDataManager().addInventory(player.getName(), ev.getContents()); } - - - if (message.contains("<" + plugin.config.item_tag + ">")) { ItemStack inHand = player.getInventory().getItemInMainHand(); InventoryPlaceholderEvent ev = new InventoryPlaceholderEvent(