diff --git a/src/main/java/dev/unnm3d/redischat/RedisChat.java b/src/main/java/dev/unnm3d/redischat/RedisChat.java index 2ec1a5f..6a18888 100644 --- a/src/main/java/dev/unnm3d/redischat/RedisChat.java +++ b/src/main/java/dev/unnm3d/redischat/RedisChat.java @@ -95,6 +95,7 @@ public final class RedisChat extends JavaPlugin { @Getter private MailGUIManager mailGUIManager; + public Config config; public FiltersConfig filterSettings; public Messages messages; @@ -391,5 +392,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/events/InventoryPlaceholderEvent.java b/src/main/java/dev/unnm3d/redischat/api/events/InventoryPlaceholderEvent.java new file mode 100644 index 0000000..6240073 --- /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 8adb78e..92f2369 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; @@ -255,19 +257,52 @@ 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); + + // 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 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); + + // … continue as before … + final Component itemName = getItemNameComponent(itemToDisplay); + toParseItemComponent = toParseItemComponent.replaceText(r -> r + .matchLiteral("%item_name%") + .replacement(itemName) + ); + toParseItemComponent = toParseItemComponent.replaceText(r -> r + .matchLiteral("%amount%") + .replacement(itemToDisplay.getAmount() > 1 + ? "x" + itemToDisplay.getAmount() + " " + : "" + ) + ); - final Component itemName = getItemNameComponent(p.getInventory().getItemInMainHand()); - 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() + " " : - "" - )); 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()) @@ -393,25 +428,88 @@ 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 + ">")) { - plugin.getDataManager().addInventory(player.getName(), 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, + combined + ); + Bukkit.getPluginManager().callEvent(ev); + plugin.getDataManager().addInventory(player.getName(), ev.getContents()); } + if (message.contains("<" + plugin.config.item_tag + ">")) { - 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 + ">")) { - 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) { @@ -468,6 +566,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..d2f88c4 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; @@ -27,103 +24,98 @@ 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) { - 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 (strings.length == 0) return true; - final String[] split = strings[0].split("-"); - if (split.length == 1) return true; + if (!(sender instanceof final Player p)) return true; + if (args.length == 0) return true; + final String[] split = args[0].split("-"); + if (split.length != 2) return true; + + final String targetName = split[0]; + final InventoryType type; + try { + type = InventoryType.valueOf(split[1].toUpperCase()); + } catch (final 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 - ); - } - } - )); - - - case INVENTORY -> plugin.getDataManager().getPlayerInventory(playerName) - .thenAccept(inventoryContents -> - RedisChat.getScheduler().runTask(() -> { - if (plugin.config.debugItemShare) { - plugin.getLogger().info("Invshare openGUI for player " + p.getName() + ": " + Arrays.toString(inventoryContents)); - } - openInvShareGui(p, - plugin.config.inv_title.replace("%player%", playerName), - 5, - inventoryContents - ); - })); - 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)); - } - openInvShareGui(p, - plugin.config.ec_title.replace("%player%", playerName), - 3, - ecContents - ); - })); + case ITEM -> plugin.getDataManager() + .getPlayerItem(targetName) + .thenAccept(item -> { + ItemStack[] guiItems = new ItemStack[27]; + guiItems[13] = item; + RedisChat.getScheduler().runTask(() -> + openRawGUI( + p, + plugin.config.item_title.replace("%player%", targetName), + guiItems + ) + ); + }); + case INVENTORY -> plugin.getDataManager() + .getPlayerInventory(targetName) + .thenAccept(combinedArray -> + RedisChat.getScheduler().runTask(() -> + openRawGUI( + p, + plugin.config.inv_title.replace("%player%", targetName), + combinedArray + ) + ) + ); + case ENDERCHEST -> plugin.getDataManager() + .getPlayerEnderchest(targetName) + .thenAccept(fetched -> { + RedisChat.getScheduler().runTask(() -> + openRawGUI( + p, + plugin.config.ec_title.replace("%player%", targetName), + fetched + ) + ); + }); } + return true; } - private void openInvShareGui(Player player, String title, int size, ItemStack[] items) { - final Gui gui = Gui.empty(9, size); - gui.addItems(Arrays.stream(items) - .map(itemStack -> { - if (itemStack == null) return new ItemBuilder(Material.AIR); - return 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); - } + private void openRawGUI(final Player viewer, + final String title, + final ItemStack[] contents) { - 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); - } + int rows = contents.length / 9 + (contents.length % 9 == 0 ? 0 : 1); + rows = Math.max(1, Math.min(6, rows)); + + 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)); - public enum InventoryType { - INVENTORY, - ENDERCHEST, - ITEM + Window.single() + .setTitle(title) + .setGui(inv) + .setCloseHandlers(List.of(() -> + new UniversalRunnable() { + @Override + public void run() { + viewer.updateInventory(); + } + }.runTaskLater(plugin, 1) + )) + .open(viewer); } + public enum InventoryType { INVENTORY, ENDERCHEST, ITEM } }