store amount in a seperate field for item updates + 1.21.4

This commit is contained in:
Brokkonaut
2024-12-06 04:42:13 +01:00
parent 8c024532b2
commit 0a685bca27
12 changed files with 205 additions and 57 deletions

View File

@@ -44,7 +44,7 @@
<dependency>
<groupId>org.spigotmc</groupId>
<artifactId>spigot-api</artifactId>
<version>1.21-R0.1-SNAPSHOT</version>
<version>1.21.4-R0.1-SNAPSHOT</version>
<scope>provided</scope>
</dependency>
<dependency>

View File

@@ -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"));
}

View File

@@ -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;

View File

@@ -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<ItemStackAndAmount> items = compressInventory(inv.getContents());
for (final ItemStackAndAmount item : items) {
queueChestAccess(actor, loc, type, item, true);
}
queueBlockBreak(actor, loc, type);

View File

@@ -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();

View File

@@ -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());
}

View File

@@ -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"));
}

View File

@@ -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<ItemStack, Integer> 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);
}
}
}

View File

@@ -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<Material> smallFlowers = Tag.SMALL_FLOWERS.getValues();
// https://minecraft.fandom.com/wiki/Tag#blocks_tall_flowers
Set<Material> tallFlowers = Tag.TALL_FLOWERS.getValues();
Set<Material> tallFlowers = Set.of(Material.SUNFLOWER,
Material.LILAC,
Material.PEONY,
Material.ROSE_BUSH,
Material.PITCHER_PLANT);
Set<Material> 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<ItemStack> compressed = new ArrayList<>();
public static Collection<ItemStackAndAmount> compressInventory(ItemStack[] items) {
final HashMap<ItemStack, Integer> 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<ItemStackAndAmount> result = new ArrayList<>();
for (Entry<ItemStack, Integer> 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()) })));

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}