From 0a685bca27dcb9bc843423081bfca644baf7e1fb Mon Sep 17 00:00:00 2001 From: Brokkonaut Date: Fri, 6 Dec 2024 04:42:13 +0100 Subject: [PATCH] store amount in a seperate field for item updates + 1.21.4 --- pom.xml | 2 +- .../java/de/diddiz/LogBlock/BlockChange.java | 4 +- .../java/de/diddiz/LogBlock/ChestAccess.java | 6 +- .../java/de/diddiz/LogBlock/Consumer.java | 12 +- src/main/java/de/diddiz/LogBlock/Updater.java | 3 +- .../java/de/diddiz/LogBlock/WorldEditor.java | 2 +- .../LogBlock/WorldEditorEditFactory.java | 5 +- .../listeners/ChestAccessLogging.java | 14 ++- .../de/diddiz/LogBlock/util/BukkitUtils.java | 61 +++++----- .../diddiz/LogBlock/util/InventoryUtils.java | 113 ++++++++++++++++++ .../LogBlock/util/ItemStackAndAmount.java | 15 +++ .../java/de/diddiz/LogBlock/util/Utils.java | 25 +++- 12 files changed, 205 insertions(+), 57 deletions(-) create mode 100644 src/main/java/de/diddiz/LogBlock/util/InventoryUtils.java create mode 100644 src/main/java/de/diddiz/LogBlock/util/ItemStackAndAmount.java diff --git a/pom.xml b/pom.xml index c7a398a..b7d690a 100644 --- a/pom.xml +++ b/pom.xml @@ -44,7 +44,7 @@ org.spigotmc spigot-api - 1.21-R0.1-SNAPSHOT + 1.21.4-R0.1-SNAPSHOT provided diff --git a/src/main/java/de/diddiz/LogBlock/BlockChange.java b/src/main/java/de/diddiz/LogBlock/BlockChange.java index 11c9989..8cc3328 100644 --- a/src/main/java/de/diddiz/LogBlock/BlockChange.java +++ b/src/main/java/de/diddiz/LogBlock/BlockChange.java @@ -12,6 +12,7 @@ import static de.diddiz.LogBlock.util.TypeColor.DEFAULT; import de.diddiz.LogBlock.blockstate.BlockStateCodecs; import de.diddiz.LogBlock.util.BukkitUtils; +import de.diddiz.LogBlock.util.ItemStackAndAmount; import de.diddiz.LogBlock.util.Utils; import java.sql.ResultSet; import java.sql.SQLException; @@ -35,7 +36,6 @@ import org.bukkit.block.data.type.Repeater; import org.bukkit.block.data.type.Sign; import org.bukkit.block.data.type.Switch; import org.bukkit.block.data.type.WallSign; -import org.bukkit.inventory.ItemStack; public class BlockChange implements LookupCacheElement { public final long id, date; @@ -75,7 +75,7 @@ public class BlockChange implements LookupCacheElement { typeState = p.needType ? rs.getBytes("typeState") : null; ChestAccess catemp = null; if (p.needChestAccess) { - ItemStack stack = Utils.loadItemStack(rs.getBytes("item")); + ItemStackAndAmount stack = Utils.loadItemStack(rs.getBytes("item")); if (stack != null) { catemp = new ChestAccess(stack, rs.getBoolean("itemremove"), rs.getInt("itemtype")); } diff --git a/src/main/java/de/diddiz/LogBlock/ChestAccess.java b/src/main/java/de/diddiz/LogBlock/ChestAccess.java index 7157516..92480b1 100644 --- a/src/main/java/de/diddiz/LogBlock/ChestAccess.java +++ b/src/main/java/de/diddiz/LogBlock/ChestAccess.java @@ -1,13 +1,13 @@ package de.diddiz.LogBlock; -import org.bukkit.inventory.ItemStack; +import de.diddiz.LogBlock.util.ItemStackAndAmount; public class ChestAccess { - public final ItemStack itemStack; + public final ItemStackAndAmount itemStack; public final boolean remove; public final int itemType; - public ChestAccess(ItemStack itemStack, boolean remove, int itemType) { + public ChestAccess(ItemStackAndAmount itemStack, boolean remove, int itemType) { this.itemStack = itemStack; this.remove = remove; this.itemType = itemType; diff --git a/src/main/java/de/diddiz/LogBlock/Consumer.java b/src/main/java/de/diddiz/LogBlock/Consumer.java index c07db64..b9b0004 100644 --- a/src/main/java/de/diddiz/LogBlock/Consumer.java +++ b/src/main/java/de/diddiz/LogBlock/Consumer.java @@ -19,6 +19,7 @@ import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayDeque; import java.util.ArrayList; +import java.util.Collection; import java.util.Deque; import java.util.HashMap; import java.util.HashSet; @@ -52,6 +53,7 @@ import de.diddiz.LogBlock.config.Config; import de.diddiz.LogBlock.events.BlockChangePreLogEvent; import de.diddiz.LogBlock.events.EntityChangePreLogEvent; import de.diddiz.LogBlock.util.BukkitUtils; +import de.diddiz.LogBlock.util.ItemStackAndAmount; import de.diddiz.LogBlock.util.Utils; public class Consumer extends Thread { @@ -208,7 +210,7 @@ public class Consumer extends Thread { * @param remove * true if the item was removed */ - public void queueChestAccess(Actor actor, BlockState container, ItemStack itemStack, boolean remove) { + public void queueChestAccess(Actor actor, BlockState container, ItemStackAndAmount itemStack, boolean remove) { if (!(container instanceof InventoryHolder)) { throw new IllegalArgumentException("Container must be instanceof InventoryHolder"); } @@ -229,8 +231,8 @@ public class Consumer extends Thread { * @param remove * true if the item was removed */ - public void queueChestAccess(Actor actor, Location loc, BlockData type, ItemStack itemStack, boolean remove) { - queueBlock(actor, loc, type, type, null, null, new ChestAccess(itemStack, remove, MaterialConverter.getOrAddMaterialId(itemStack.getType()))); + public void queueChestAccess(Actor actor, Location loc, BlockData type, ItemStackAndAmount itemStack, boolean remove) { + queueBlock(actor, loc, type, type, null, null, new ChestAccess(itemStack, remove, MaterialConverter.getOrAddMaterialId(itemStack.stack().getType()))); } /** @@ -261,8 +263,8 @@ public class Consumer extends Thread { * The inventory of the container block */ public void queueContainerBreak(Actor actor, Location loc, BlockData type, Inventory inv) { - final ItemStack[] items = compressInventory(inv.getContents()); - for (final ItemStack item : items) { + final Collection items = compressInventory(inv.getContents()); + for (final ItemStackAndAmount item : items) { queueChestAccess(actor, loc, type, item, true); } queueBlockBreak(actor, loc, type); diff --git a/src/main/java/de/diddiz/LogBlock/Updater.java b/src/main/java/de/diddiz/LogBlock/Updater.java index b1a83f8..51e5a92 100644 --- a/src/main/java/de/diddiz/LogBlock/Updater.java +++ b/src/main/java/de/diddiz/LogBlock/Updater.java @@ -4,6 +4,7 @@ import de.diddiz.LogBlock.blockstate.BlockStateCodecSign; import de.diddiz.LogBlock.config.Config; import de.diddiz.LogBlock.config.WorldConfig; import de.diddiz.LogBlock.util.ComparableVersion; +import de.diddiz.LogBlock.util.ItemStackAndAmount; import de.diddiz.LogBlock.util.UUIDFetcher; import de.diddiz.LogBlock.util.Utils; import org.bukkit.Bukkit; @@ -534,7 +535,7 @@ class Updater { @SuppressWarnings("deprecation") ItemStack stack = weaponMaterial.getMaxDurability() > 0 ? new ItemStack(weaponMaterial, Math.abs(amount), (short) itemdata) : new ItemStack(weaponMaterial, Math.abs(amount)); insertChestData.setLong(1, id); - insertChestData.setBytes(2, Utils.saveItemStack(stack)); + insertChestData.setBytes(2, Utils.saveItemStack(ItemStackAndAmount.fromStack(stack))); insertChestData.setInt(3, amount >= 0 ? 0 : 1); insertChestData.setInt(4, MaterialConverter.getOrAddMaterialId(weaponMaterial)); insertChestData.addBatch(); diff --git a/src/main/java/de/diddiz/LogBlock/WorldEditor.java b/src/main/java/de/diddiz/LogBlock/WorldEditor.java index bac7c33..8bb41a2 100644 --- a/src/main/java/de/diddiz/LogBlock/WorldEditor.java +++ b/src/main/java/de/diddiz/LogBlock/WorldEditor.java @@ -378,7 +378,7 @@ public class WorldEditor implements Runnable { if (state instanceof InventoryHolder && state.getType() == replacedBlock.getMaterial()) { int leftover; try { - leftover = modifyContainer(state, new ItemStack(ca.itemStack), !ca.remove); + leftover = modifyContainer(state, ca.itemStack, !ca.remove); } catch (final Exception ex) { throw new WorldEditorException(ex.getMessage(), block.getLocation()); } diff --git a/src/main/java/de/diddiz/LogBlock/WorldEditorEditFactory.java b/src/main/java/de/diddiz/LogBlock/WorldEditorEditFactory.java index d167d23..cc2b6d1 100644 --- a/src/main/java/de/diddiz/LogBlock/WorldEditorEditFactory.java +++ b/src/main/java/de/diddiz/LogBlock/WorldEditorEditFactory.java @@ -3,9 +3,8 @@ package de.diddiz.LogBlock; import java.sql.ResultSet; import java.sql.SQLException; -import org.bukkit.inventory.ItemStack; - import de.diddiz.LogBlock.QueryParams.BlockChangeType; +import de.diddiz.LogBlock.util.ItemStackAndAmount; import de.diddiz.LogBlock.util.Utils; public class WorldEditorEditFactory { @@ -25,7 +24,7 @@ public class WorldEditorEditFactory { return; } ChestAccess chestaccess = null; - ItemStack stack = Utils.loadItemStack(rs.getBytes("item")); + ItemStackAndAmount stack = Utils.loadItemStack(rs.getBytes("item")); if (stack != null) { chestaccess = new ChestAccess(stack, rs.getBoolean("itemremove") == rollback, rs.getInt("itemtype")); } diff --git a/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java b/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java index ca86417..cb98600 100644 --- a/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java +++ b/src/main/java/de/diddiz/LogBlock/listeners/ChestAccessLogging.java @@ -3,6 +3,7 @@ package de.diddiz.LogBlock.listeners; import de.diddiz.LogBlock.Actor; import de.diddiz.LogBlock.LogBlock; import de.diddiz.LogBlock.Logging; +import de.diddiz.LogBlock.util.ItemStackAndAmount; import org.bukkit.Location; import org.bukkit.Material; import org.bukkit.Tag; @@ -78,9 +79,10 @@ public class ChestAccessLogging extends LoggingListener { for (Entry e : modifications.entrySet()) { ItemStack stack = e.getKey(); int amount = e.getValue(); - stack.setAmount(Math.abs(amount)); - // consumer.getLogblock().getLogger().info("Store container: " + stack + " take: " + (amount < 0)); - consumer.queueChestAccess(Actor.actorFromEntity(actor), location, location.getWorld().getBlockAt(location).getBlockData(), stack, amount < 0); + if (amount != 0) { + // consumer.getLogblock().getLogger().info("Store container: " + stack + " take: " + (amount < 0)); + consumer.queueChestAccess(Actor.actorFromEntity(actor), location, location.getWorld().getBlockAt(location).getBlockData(), new ItemStackAndAmount(stack, Math.abs(amount)), amount < 0); + } } modifications.clear(); } @@ -328,7 +330,7 @@ public class ChestAccessLogging extends LoggingListener { if (currentInPot == null || currentInPot.getType() == Material.AIR || currentInPot.isSimilar(mainHand) && currentInPot.getAmount() < currentInPot.getMaxStackSize()) { ItemStack stack = mainHand.clone(); stack.setAmount(1); - consumer.queueChestAccess(Actor.actorFromEntity(player), clicked.getLocation(), clicked.getBlockData(), stack, false); + consumer.queueChestAccess(Actor.actorFromEntity(player), clicked.getLocation(), clicked.getBlockData(), ItemStackAndAmount.fromStack(stack), false); } } } else if (type == Material.CHISELED_BOOKSHELF) { @@ -353,7 +355,7 @@ public class ChestAccessLogging extends LoggingListener { if (blockData.isSlotOccupied(slot)) { // not empty: always take if (currentInSlot != null && currentInSlot.getType() != Material.AIR) { - consumer.queueChestAccess(Actor.actorFromEntity(player), clicked.getLocation(), clicked.getBlockData(), currentInSlot, true); + consumer.queueChestAccess(Actor.actorFromEntity(player), clicked.getLocation(), clicked.getBlockData(), ItemStackAndAmount.fromStack(currentInSlot), true); } } else { // empty: put if has tag BOOKSHELF_BOOKS @@ -361,7 +363,7 @@ public class ChestAccessLogging extends LoggingListener { if (mainHand != null && mainHand.getType() != Material.AIR && Tag.ITEMS_BOOKSHELF_BOOKS.isTagged(mainHand.getType())) { ItemStack stack = mainHand.clone(); stack.setAmount(1); - consumer.queueChestAccess(Actor.actorFromEntity(player), clicked.getLocation(), clicked.getBlockData(), stack, false); + consumer.queueChestAccess(Actor.actorFromEntity(player), clicked.getLocation(), clicked.getBlockData(), ItemStackAndAmount.fromStack(stack), false); } } } diff --git a/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java b/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java index f13a07f..71bd448 100644 --- a/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java +++ b/src/main/java/de/diddiz/LogBlock/util/BukkitUtils.java @@ -5,11 +5,13 @@ import static de.diddiz.LogBlock.util.MessagingUtil.prettyMaterial; import de.diddiz.LogBlock.LogBlock; import java.io.File; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.logging.Level; import java.util.Set; import java.util.UUID; @@ -146,8 +148,11 @@ public class BukkitUtils { // https://minecraft.fandom.com/wiki/Tag#blocks_small_flowers Set smallFlowers = Tag.SMALL_FLOWERS.getValues(); - // https://minecraft.fandom.com/wiki/Tag#blocks_tall_flowers - Set tallFlowers = Tag.TALL_FLOWERS.getValues(); + Set tallFlowers = Set.of(Material.SUNFLOWER, + Material.LILAC, + Material.PEONY, + Material.ROSE_BUSH, + Material.PITCHER_PLANT); Set bannerStanding = Set.of(Material.WHITE_BANNER, Material.ORANGE_BANNER, @@ -498,24 +503,22 @@ public class BukkitUtils { return diff.toArray(new ItemStack[diff.size()]); } - public static ItemStack[] compressInventory(ItemStack[] items) { - final ArrayList compressed = new ArrayList<>(); + public static Collection compressInventory(ItemStack[] items) { + final HashMap compressed = new HashMap<>(); for (final ItemStack item : items) { - if (item != null) { - boolean found = false; - for (final ItemStack item2 : compressed) { - if (item2.isSimilar(item)) { - item2.setAmount(item2.getAmount() + item.getAmount()); - found = true; - break; - } - } - if (!found) { - compressed.add(item.clone()); - } + if (item != null && item.getType() != Material.AIR && item.getAmount() > 0) { + int amount = item.getAmount(); + ItemStack stack = item.clone(); + stack.setAmount(1); + Integer old = compressed.get(stack); + compressed.put(stack, (old == null ? 0 : old) + amount); } } - return compressed.toArray(new ItemStack[compressed.size()]); + ArrayList result = new ArrayList<>(); + for (Entry e : compressed.entrySet()) { + result.add(new ItemStackAndAmount(e.getKey(), e.getValue())); + } + return result; } public static String friendlyWorldname(String worldName) { @@ -602,15 +605,13 @@ public class BukkitUtils { return y; } - public static int modifyContainer(BlockState b, ItemStack item, boolean remove) { - if (b instanceof InventoryHolder c) { + public static int modifyContainer(BlockState b, ItemStackAndAmount item, boolean remove) { + if (item.amount() > 0 && b instanceof InventoryHolder c) { final Inventory inv = c.getInventory(); if (remove) { - final ItemStack tmp = inv.removeItem(item).get(0); - return tmp != null ? tmp.getAmount() : 0; - } else if (item.getAmount() > 0) { - final ItemStack tmp = inv.addItem(item).get(0); - return tmp != null ? tmp.getAmount() : 0; + return InventoryUtils.removeFromInventory(inv, item); + } else { + return InventoryUtils.addToInventory(inv, item); } } return 0; @@ -656,16 +657,16 @@ public class BukkitUtils { return m == Material.AIR || m == Material.CAVE_AIR || m == Material.VOID_AIR; } - public static TextComponent toString(ItemStack stack) { - if (stack == null || stack.getAmount() == 0 || isEmpty(stack.getType())) { + public static TextComponent toString(ItemStackAndAmount stack) { + if (stack == null || stack.stack() == null || stack.amount() == 0 || isEmpty(stack.stack().getType())) { return prettyMaterial("nothing"); } - TextComponent msg = MessagingUtil.createTextComponentWithColor(stack.getAmount() + "x ", TypeColor.DEFAULT.getColor()); - msg.addExtra(prettyMaterial(stack.getType())); + TextComponent msg = MessagingUtil.createTextComponentWithColor(stack.amount() + "x ", TypeColor.DEFAULT.getColor()); + msg.addExtra(prettyMaterial(stack.stack().getType())); try { - String itemTag = stack.getItemMeta().getAsString(); - msg.setHoverEvent(new HoverEvent(Action.SHOW_ITEM, new Item(stack.getType().getKey().toString(), 1, itemTag != null ? ItemTag.ofNbt(itemTag) : null))); + String itemTag = stack.stack().getItemMeta().getAsString(); + msg.setHoverEvent(new HoverEvent(Action.SHOW_ITEM, new Item(stack.stack().getType().getKey().toString(), 1, itemTag != null ? ItemTag.ofNbt(itemTag) : null))); } catch (Exception e) { LogBlock.getInstance().getLogger().log(Level.SEVERE, "Failed to convert Itemstack to JSON", e); msg.setHoverEvent(new HoverEvent(Action.SHOW_TEXT, new Text(new BaseComponent[] { MessagingUtil.createTextComponentWithColor("Error", TypeColor.ERROR.getColor()) }))); diff --git a/src/main/java/de/diddiz/LogBlock/util/InventoryUtils.java b/src/main/java/de/diddiz/LogBlock/util/InventoryUtils.java new file mode 100644 index 0000000..269be74 --- /dev/null +++ b/src/main/java/de/diddiz/LogBlock/util/InventoryUtils.java @@ -0,0 +1,113 @@ +package de.diddiz.LogBlock.util; + +import org.bukkit.Material; +import org.bukkit.inventory.Inventory; +import org.bukkit.inventory.ItemStack; + +public class InventoryUtils { + + public static int addToInventory(Inventory inventory, ItemStackAndAmount item) { + if (item == null || item.stack() == null || item.stack().getType() == Material.AIR) { + return 0; + } + int maxStackSize = Math.max(Math.min(inventory.getMaxStackSize(), item.stack().getMaxStackSize()), 1); + + ItemStack[] contents = inventory.getStorageContents(); + + int remaining = item.amount(); + int initialRemaining = remaining; + + // fill partial stacks + int firstPartial = -1; + while (remaining > 0) { + firstPartial = getFirstPartial(item.stack(), maxStackSize, contents, firstPartial + 1); + if (firstPartial < 0) { + break; + } + ItemStack content = contents[firstPartial]; + int add = Math.min(maxStackSize - content.getAmount(), remaining); + content.setAmount(content.getAmount() + add); + remaining -= add; + } + // create new stacks + int firstFree = -1; + while (remaining > 0) { + firstFree = getFirstFree(contents, firstFree + 1); + if (firstFree < 0) { + break; + } + ItemStack content = item.stack().clone(); + contents[firstFree] = content; + int add = Math.min(maxStackSize, remaining); + content.setAmount(add); + remaining -= add; + } + + if (remaining < initialRemaining) { + inventory.setStorageContents(contents); + } + return remaining; + } + + public static int removeFromInventory(Inventory inventory, ItemStackAndAmount item) { + if (item == null || item.stack() == null || item.stack().getType() == Material.AIR) { + return 0; + } + + ItemStack[] contents = inventory.getStorageContents(); + int remaining = item.amount(); + int initialRemaining = remaining; + + int firstSimilar = -1; + while (remaining > 0) { + firstSimilar = getFirstSimilar(item.stack(), contents, firstSimilar + 1); + if (firstSimilar < 0) { + break; + } + ItemStack content = contents[firstSimilar]; + int here = content.getAmount(); + if (here > remaining) { + content.setAmount(here - remaining); + remaining = 0; + } else { + contents[firstSimilar] = null; + remaining -= here; + } + } + + if (remaining < initialRemaining) { + inventory.setStorageContents(contents); + } + return remaining; + } + + private static int getFirstSimilar(ItemStack item, ItemStack[] contents, int start) { + for (int i = start; i < contents.length; i++) { + ItemStack content = contents[i]; + if (content != null && content.isSimilar(item)) { + return i; + } + } + return -1; + } + + private static int getFirstPartial(ItemStack item, int maxStackSize, ItemStack[] contents, int start) { + for (int i = start; i < contents.length; i++) { + ItemStack content = contents[i]; + if (content != null && content.isSimilar(item) && content.getAmount() < maxStackSize) { + return i; + } + } + return -1; + } + + private static int getFirstFree(ItemStack[] contents, int start) { + for (int i = start; i < contents.length; i++) { + ItemStack content = contents[i]; + if (content == null || content.getAmount() == 0 || content.getType() == Material.AIR) { + return i; + } + } + return -1; + } +} diff --git a/src/main/java/de/diddiz/LogBlock/util/ItemStackAndAmount.java b/src/main/java/de/diddiz/LogBlock/util/ItemStackAndAmount.java new file mode 100644 index 0000000..1737a4f --- /dev/null +++ b/src/main/java/de/diddiz/LogBlock/util/ItemStackAndAmount.java @@ -0,0 +1,15 @@ +package de.diddiz.LogBlock.util; + +import org.bukkit.inventory.ItemStack; + +public record ItemStackAndAmount(ItemStack stack, int amount) { + + public static ItemStackAndAmount fromStack(ItemStack stack) { + int amount = stack.getAmount(); + if (amount > 1) { + stack = stack.clone(); + stack.setAmount(1); + } + return new ItemStackAndAmount(stack, amount); + } +} diff --git a/src/main/java/de/diddiz/LogBlock/util/Utils.java b/src/main/java/de/diddiz/LogBlock/util/Utils.java index 353c4af..dd4296d 100644 --- a/src/main/java/de/diddiz/LogBlock/util/Utils.java +++ b/src/main/java/de/diddiz/LogBlock/util/Utils.java @@ -231,20 +231,35 @@ public class Utils { return untrusted.replace("\\", "\\\\").replace("'", "\\'"); } - public static ItemStack loadItemStack(byte[] data) { + public static ItemStackAndAmount loadItemStack(byte[] data) { if (data == null || data.length == 0) { return null; } YamlConfiguration conf = deserializeYamlConfiguration(data); - return conf == null ? null : conf.getItemStack("stack"); + if (conf == null) { + return null; + } + ItemStack stack = conf.getItemStack("stack"); + if (stack == null) { + return null; + } + int amount = conf.contains("amount") ? conf.getInt("amount") : stack.getAmount(); + stack.setAmount(1); + return new ItemStackAndAmount(stack, amount); } - public static byte[] saveItemStack(ItemStack stack) { - if (stack == null || BukkitUtils.isEmpty(stack.getType())) { + public static byte[] saveItemStack(ItemStackAndAmount stack) { + if (stack == null || stack.stack() == null || BukkitUtils.isEmpty(stack.stack().getType())) { return null; } YamlConfiguration conf = new YamlConfiguration(); - conf.set("stack", stack); + ItemStack itemStack = stack.stack(); + if (itemStack.getAmount() > 1) { + itemStack = itemStack.clone(); + itemStack.setAmount(1); + } + conf.set("stack", itemStack); + conf.set("amount", stack.amount()); return serializeYamlConfiguration(conf); }