From 5e8a0ebc54b0f70778ae63e026407f1a870b3cdb Mon Sep 17 00:00:00 2001 From: Gabriel Tofvesson Date: Tue, 4 May 2021 04:16:02 +0200 Subject: [PATCH] Implement basic inventory search command --- res/plugin.yml | 6 +- src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java | 5 + .../command/SearchCommandExecutor.java | 169 ++++++++++++++++++ 3 files changed, 179 insertions(+), 1 deletion(-) create mode 100644 src/dev/w1zzrd/invtweaks/command/SearchCommandExecutor.java diff --git a/res/plugin.yml b/res/plugin.yml index 1e91668..3483c2f 100644 --- a/res/plugin.yml +++ b/res/plugin.yml @@ -11,4 +11,8 @@ commands: magnet: description: Toggle item magnet mode for player usage: / - permission: invtweaks.magnet \ No newline at end of file + permission: invtweaks.magnet + search: + description: Search for a given item in all nearby inventories + usage: / {item type} + permission: invtweaks.search \ No newline at end of file diff --git a/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java b/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java index 714e677..add2d1e 100644 --- a/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java +++ b/src/dev/w1zzrd/invtweaks/InvTweaksPlugin.java @@ -1,6 +1,7 @@ package dev.w1zzrd.invtweaks; import dev.w1zzrd.invtweaks.command.MagnetCommandExecutor; +import dev.w1zzrd.invtweaks.command.SearchCommandExecutor; import dev.w1zzrd.invtweaks.command.SortCommandExecutor; import dev.w1zzrd.invtweaks.listener.MagnetismListener; import dev.w1zzrd.invtweaks.serialization.MagnetConfig; @@ -33,6 +34,7 @@ public final class InvTweaksPlugin extends JavaPlugin { // Command executor references in case I need them or something idk private SortCommandExecutor sortCommandExecutor; private MagnetCommandExecutor magnetCommandExecutor; + private SearchCommandExecutor searchCommandExecutor; private DataStore data; @Override @@ -77,10 +79,12 @@ public final class InvTweaksPlugin extends JavaPlugin { private void initCommands() { sortCommandExecutor = new SortCommandExecutor(); magnetCommandExecutor = new MagnetCommandExecutor(this, getPersistentData()); + searchCommandExecutor = new SearchCommandExecutor(); // TODO: Bind command by annotation Objects.requireNonNull(getCommand("sort")).setExecutor(sortCommandExecutor); Objects.requireNonNull(getCommand("magnet")).setExecutor(magnetCommandExecutor); + Objects.requireNonNull(getCommand("search")).setExecutor(searchCommandExecutor); } /** @@ -102,6 +106,7 @@ public final class InvTweaksPlugin extends JavaPlugin { sortCommandExecutor = null; magnetCommandExecutor = null; + searchCommandExecutor = null; } /** diff --git a/src/dev/w1zzrd/invtweaks/command/SearchCommandExecutor.java b/src/dev/w1zzrd/invtweaks/command/SearchCommandExecutor.java new file mode 100644 index 0000000..f931d53 --- /dev/null +++ b/src/dev/w1zzrd/invtweaks/command/SearchCommandExecutor.java @@ -0,0 +1,169 @@ +package dev.w1zzrd.invtweaks.command; + +import org.bukkit.Bukkit; +import org.bukkit.Location; +import org.bukkit.Material; +import org.bukkit.World; +import org.bukkit.block.*; +import org.bukkit.command.Command; +import org.bukkit.command.CommandExecutor; +import org.bukkit.command.CommandSender; +import org.bukkit.entity.HumanEntity; +import org.bukkit.entity.Player; +import org.bukkit.event.inventory.InventoryType; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.InventoryHolder; +import org.bukkit.inventory.InventoryView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.logging.Logger; + +import static dev.w1zzrd.invtweaks.command.CommandUtils.assertTrue; + +public class SearchCommandExecutor implements CommandExecutor { + + private static final Logger logger = Bukkit.getLogger(); + + // TODO: Move to config (magic values) + private static final String + ERR_NOT_PLAYER = "Command must be run by an in-game player", + ERR_NO_ARG = "Command expects an item or block name as an argument", + ERR_UNKNOWN = "Unknown item/block name \"%s\"", + ERR_NO_INVENTORIES = "No inventories could be found"; + + private static final int RADIUS_X = 8, RADIUS_Y = 8, RADIUS_Z = 8; + + + @Override + public boolean onCommand(CommandSender sender, Command command, String label, String[] args) { + final Material target; + if (assertTrue(sender instanceof Player, ERR_NOT_PLAYER, sender) || + assertTrue(args.length == 1, ERR_NO_ARG, sender) || + assertTrue((target = Material.getMaterial(args[0])) != null, String.format(ERR_UNKNOWN, args[0]), sender) + ) return false; + + assert target != null; + assert sender instanceof Player; + final Player player = (Player) sender; + + final List matches = searchBlocks( + player.getLocation(), + player.getWorld(), + RADIUS_X, RADIUS_Y, RADIUS_Z, + Material.CHEST, Material.SHULKER_BOX + ); + + // Ensure we found inventory-holding blocks + if (assertTrue(matches.size() != 0, ERR_NO_INVENTORIES, sender)) + return false; + + final InventoryHolder result; + + FIND_RESULT: + { + for (final BlockState check : matches) { + final InventoryHolder holder; + if (check instanceof Chest) + holder = Objects.requireNonNull(((Chest) check).getBlockInventory().getHolder()).getInventory().getHolder(); + else if (check instanceof ShulkerBox) + holder = ((ShulkerBox) check).getInventory().getHolder(); + else { + logger.info("Found non-matching block"); + continue; + } + + assert holder != null; + + if (holder instanceof DoubleChest) { + final InventoryHolder left = Objects.requireNonNull(((DoubleChest) holder).getLeftSide()); + final InventoryHolder right = Objects.requireNonNull(((DoubleChest) holder).getRightSide()); + + if (left.getInventory().contains(target) || right.getInventory().contains(target)) { + result = holder; + break FIND_RESULT; + } + } else if (holder.getInventory().contains(target)) { + result = holder; + break FIND_RESULT; + } + } + assertTrue(false, "Could not find inventory with target item/block", sender); + return false; + } + + if (result instanceof DoubleChest) { + final DoubleChest dChest = (DoubleChest) result; + + //final InventoryView view = player.openInventory(dChest.getInventory()); + System.out.println("Opened inventory"); + player.openInventory(new SearchInventoryView(dChest, player, ((Chest)dChest.getLeftSide()).getCustomName() == null ? "Large Chest" : Objects.requireNonNull(((Container) result).getCustomName()))); + } else { + player.openInventory(result.getInventory()); + } + + return true; + } + + private List searchBlocks(final Location centre, final World world, final int rx, final int ry, final int rz, final Material... targets) { + if (targets.length == 0) + return Collections.emptyList(); + + final int x = centre.getBlockX(), y = centre.getBlockY(), z = centre.getBlockZ(); + final ArrayList matches = new ArrayList<>(); + for (int dx = -rx; dx < rx; ++dx) + for (int dy = -ry; dy < ry; ++dy) + CHECK_Z: + for (int dz = -rz; dz < rz; ++dz) { + final Block check = world.getBlockAt(x + dx, y + dy, z + dz); + final Material checkMaterial = check.getType(); + for (Material target : targets) + if (target == checkMaterial) { + matches.add(check.getState()); + continue CHECK_Z; + } + } + + return matches; + } + + public static class SearchInventoryView extends InventoryView { + + private final DoubleChest dChest; + private final Player player; + private final String title; + + SearchInventoryView(final DoubleChest dChest, final Player player, final String title) { + this.dChest = dChest; + this.player = player; + this.title = title; + } + + @Override + public Inventory getTopInventory() { + return dChest.getInventory(); + } + + @Override + public Inventory getBottomInventory() { + return player.getInventory(); + } + + @Override + public HumanEntity getPlayer() { + return player; + } + + @Override + public InventoryType getType() { + return InventoryType.CHEST; + } + + @Override + public String getTitle() { + return title; + } + } +}