diff --git a/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java b/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java index 29cee23..315110b 100644 --- a/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java +++ b/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java @@ -1,7 +1,9 @@ package dev.w1zzrd.invtweaks; -import dev.w1zzrd.invtweaks.listeners.StackReplaceListener; +import dev.w1zzrd.invtweaks.command.SortCommandExecutor; +import dev.w1zzrd.invtweaks.listener.StackReplaceListener; import dev.w1zzrd.logging.LoggerFactory; +import org.bukkit.event.HandlerList; import org.bukkit.plugin.java.JavaPlugin; import java.util.logging.Logger; @@ -15,11 +17,17 @@ public class InvTweaksPlugin extends JavaPlugin { public void onEnable() { logger.info("Inventory Tweaks enabled"); + getCommand("sort").setExecutor(new SortCommandExecutor()); + getServer().getPluginManager().registerEvents(new StackReplaceListener(), this); } @Override public void onDisable() { logger.info("Inventory Tweaks disabled"); + + getCommand("sort").setExecutor(null); + + HandlerList.unregisterAll(this); } } diff --git a/src/dev/w1zzrd/invtweaks/command/SortCommandExecutor.java b/src/dev/w1zzrd/invtweaks/command/SortCommandExecutor.java new file mode 100644 index 0000000..5f119ed --- /dev/null +++ b/src/dev/w1zzrd/invtweaks/command/SortCommandExecutor.java @@ -0,0 +1,113 @@ +package dev.w1zzrd.invtweaks.command; + +import dev.w1zzrd.logging.LoggerFactory; +import org.bukkit.Material; +import org.bukkit.block.BlockState; +import org.bukkit.block.Chest; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.Player; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +import java.util.*; +import java.util.logging.Logger; + +import static org.bukkit.Material.*; + +public class SortCommandExecutor implements CommandExecutor { + + private static final Logger logger = LoggerFactory.getLogger(SortCommandExecutor.class); + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + if (!(sender instanceof Player)) { + sender.sendMessage("Command must be run by a player"); + return false; + } + + final Player player = (Player) sender; + + final BlockState target = player.getTargetBlock(null, 6).getState(); + + if (!(target instanceof Chest)) return false; + + final Chest chest = (Chest) target; + + final Inventory chestInventory = chest.getBlockInventory(); + mergeStacks(chestInventory); + sortInventory(chestInventory); + + return true; + } + + private static void sortInventory(final Inventory inventory) { + inventory.setContents(Arrays.stream(inventory.getContents()) + .sorted(new ItemStackComparator()) + .toArray(ItemStack[]::new) + ); + } + + private static void mergeStacks(final Inventory inventory) { + final HashMap count = new HashMap<>(); + for (final ItemStack stack : inventory) { + final Optional tracked = count.keySet().stream().filter(stack::isSimilar).findFirst(); + + if (tracked.isPresent()) + count.put(tracked.get(), count.get(tracked.get()) + stack.getAmount()); + else + count.put(stack, stack.getAmount()); + } + + for (int i = inventory.getSize() - 1; i >= 0; --i) { + final ItemStack current = Objects.requireNonNull(inventory.getItem(i)); + final Optional tracked = count.keySet().stream().filter(current::isSimilar).findFirst(); + + // Should always be true but I don't know Spigot, so I'm gonna play it safe + if (tracked.isPresent()) { + final ItemStack key = tracked.get(); + final int amount = count.get(tracked.get()); + + if (amount == 0) { + inventory.setItem(i, new ItemStack(Material.AIR)); + } else { + final int currentAmount = current.getAmount(); + + if (current.getAmount() < current.getMaxStackSize()) { + final int newAmount = Math.min(currentAmount + amount, current.getMaxStackSize()); + current.setAmount(newAmount); + + // Update remaining count of given material + count.put(key, amount - (newAmount - currentAmount)); + } + } + } else { + logger.warning("Found untracked ItemStack while merging stacks"); + } + } + } + + private static class ItemStackComparator implements Comparator { + @Override + public int compare(ItemStack o1, ItemStack o2) { + final Material m1 = o1.getType(); + final Material m2 = o2.getType(); + + // Technically not a stable sort, but w/e + if (m1 == AIR || m1 == CAVE_AIR || m1 == VOID_AIR) + return m2 == AIR || m2 == CAVE_AIR || m2 == VOID_AIR ? 0 : -1; + + // Blocks appear earlier than items + if (m1.isBlock() != m2.isBlock()) + return m1.isBlock() ? 1 : -1; + + // Stacks of similar type are organized according to stack size + if (o1.isSimilar(o2)) + return Integer.compare(o1.getAmount(), o2.getAmount()); + + // Differing stacks are sorted according to enum ordinal (arbitrary but I'm not manually designing an order) + return Integer.compare(m1.ordinal(), m2.ordinal()); + } + } +}