From 2dda881db80d6170fe8ab4b997d5df8da7f393ef Mon Sep 17 00:00:00 2001 From: v0ey <113121361+v0ey@users.noreply.github.com> Date: Mon, 24 Nov 2025 20:21:42 +0300 Subject: [PATCH 1/2] refactor nbt container handling and improve item reversion --- .../zeroBzeroT/antiillegals/AntiIllegals.java | 23 +- .../org/zeroBzeroT/antiillegals/Events.java | 53 +++++ .../antiillegals/helpers/BookHelper.java | 203 ++++++++++++++++-- .../antiillegals/helpers/MaterialHelper.java | 29 +-- .../antiillegals/helpers/RevertHelper.java | 141 +++++++++++- src/main/resources/config.yml | 25 ++- 6 files changed, 423 insertions(+), 51 deletions(-) diff --git a/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java b/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java index c663e29..88ca9c1 100644 --- a/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java +++ b/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java @@ -53,19 +53,32 @@ public void onEnable() { log("unbreakables", "" + getConfig().getBoolean("unbreakables")); log("durability", "" + getConfig().getBoolean("durability")); log("illegalBlocks", "" + getConfig().getBoolean("illegalBlocks")); - log("nbtContainers", "" + getConfig().getBoolean("nbtContainers")); + log("nbtContainers.enabled", "" + getConfig().getBoolean("nbtContainers.enabled", + getConfig().getBoolean("nbtContainers"))); + log("nbtContainers.armorStandItems", "" + getConfig().getBoolean("nbtContainers.armorStandItems", true)); + log("nbtContainers.itemFrameItems", "" + getConfig().getBoolean("nbtContainers.itemFrameItems", true)); + log("nbtContainers.bundleItems", "" + getConfig().getBoolean("nbtContainers.bundleItems", true)); + log("nbtContainers.stripOtherContainers", "" + + getConfig().getBoolean("nbtContainers.stripOtherContainers", true)); log("blockStates.enabled", "" + getConfig().getBoolean("blockStates.enabled")); log("blockStates.keys", String.join(", ", getConfig().getStringList("blockStates.keys"))); log("overstackedItems", "" + getConfig().getBoolean("overstackedItems")); log("allowCollectibles", "" + getConfig().getBoolean("allowCollectibles")); log("conflictingEnchantments", "" + getConfig().getBoolean("conflictingEnchantments")); log("maxEnchantments", "" + getConfig().getBoolean("maxEnchantments")); - log("shulkerBoxes", "" + getConfig().getBoolean("shulkerBoxes")); - log("maxBooksInShulker", "" + getConfig().getInt("maxBooksInShulker")); - log("maxBooksShulkersInInventory", "" + getConfig().getInt("maxBooksShulkersInInventory")); + log("interactItems", "" + getConfig().getBoolean("interactItems", true)); + log("books.lecternCheck", "" + getConfig().getBoolean("books.lecternCheck", true)); + log("books.shulkerBoxes.enabled", "" + getConfig().getBoolean("books.shulkerBoxes.enabled", true)); + log("books.shulkerBoxes.maxBooksInside", "" + getConfig().getInt("books.shulkerBoxes.maxBooksInside", 10)); + log("books.shulkerBoxes.maxContainersPerInventory", + "" + getConfig().getInt("books.shulkerBoxes.maxContainersPerInventory", 3)); + log("books.bundles.enabled", "" + getConfig().getBoolean("books.bundles.enabled", true)); + log("books.bundles.maxBooksInside", "" + getConfig().getInt("books.bundles.maxBooksInside", 10)); + log("books.bundles.maxContainersPerInventory", + "" + getConfig().getInt("books.bundles.maxContainersPerInventory", 1)); log("attributeModifiers", "" + getConfig().getBoolean("attributeModifiers")); log("customPotionEffects", "" + getConfig().getBoolean("customPotionEffects")); - log("crossbowProjectiles", "" + getConfig().getBoolean("crossbowProjectiles")); + log("crossbowProjectiles", "" + getConfig().getBoolean("crossbowProjectiles")); log("suspiciousStewEffects", "" + getConfig().getBoolean("suspiciousStewEffects")); log("hideFlags", "" + getConfig().getBoolean("hideFlags")); diff --git a/src/main/java/org/zeroBzeroT/antiillegals/Events.java b/src/main/java/org/zeroBzeroT/antiillegals/Events.java index ce0f373..eccfec7 100644 --- a/src/main/java/org/zeroBzeroT/antiillegals/Events.java +++ b/src/main/java/org/zeroBzeroT/antiillegals/Events.java @@ -64,6 +64,44 @@ public void onBlockBreak(@NotNull final BlockBreakEvent event) { .ifPresent(inventory -> RevertHelper.checkInventory(inventory, location, true)); } + @EventHandler(priority = EventPriority.LOWEST, ignoreCancelled = true) + public void onPlayerInteractUseItem(@NotNull final PlayerInteractEvent event) { + if (!AntiIllegals.config().getBoolean("interactItems")) + return; + + if (event.getAction() == Action.PHYSICAL) + return; + + final Player player = event.getPlayer(); + final PlayerInventory inventory = player.getInventory(); + final Location location = player.getLocation(); + + final ItemStack mainHandStack = inventory.getItemInMainHand(); + final ItemStack offhandHandStack = inventory.getItemInOffHand(); + + boolean removedIllegal = false; + + final ItemState mainState = RevertHelper.checkItemStack(mainHandStack, location, true); + if (mainState == ItemState.ILLEGAL && mainHandStack != null) + mainHandStack.setAmount(0); + + final ItemState offState = RevertHelper.checkItemStack(offhandHandStack, location, true); + if (offState == ItemState.ILLEGAL && offhandHandStack != null) + offhandHandStack.setAmount(0); + + removedIllegal = mainState == ItemState.ILLEGAL || offState == ItemState.ILLEGAL; + + inventory.setItemInMainHand(mainHandStack); + inventory.setItemInOffHand(offhandHandStack); + + if (!removedIllegal) + return; + + event.setCancelled(true); + AntiIllegals.log(event.getEventName(), "Removed illegal items from " + player.getName() + + " during interaction."); + } + @EventHandler(ignoreCancelled = true) public void onPlayerInteractBlock(@NotNull final PlayerInteractEvent event) { if (event.getAction() != Action.RIGHT_CLICK_BLOCK) @@ -86,6 +124,21 @@ public void onPlayerInteractBlock(@NotNull final PlayerInteractEvent event) { }); } + @EventHandler(ignoreCancelled = true) + public void onPlayerTakeLecternBook(@NotNull final PlayerTakeLecternBookEvent event) { + if (!BookHelper.shouldCheckLecternBooks()) + return; + + final ItemStack book = event.getBook(); + final Location location = event.getLectern().getLocation(); + + if (!RevertHelper.revert(book, location, true, ItemState::wasReverted)) + return; + + AntiIllegals.log(event.getEventName(), "Reverted lectern book taken by " + + event.getPlayer().getName() + "."); + } + @EventHandler(ignoreCancelled = true) public void onInventoryOpen(@NotNull final InventoryOpenEvent event) { final Inventory inventory = event.getInventory(); diff --git a/src/main/java/org/zeroBzeroT/antiillegals/helpers/BookHelper.java b/src/main/java/org/zeroBzeroT/antiillegals/helpers/BookHelper.java index 1f6648c..3f0b6ff 100644 --- a/src/main/java/org/zeroBzeroT/antiillegals/helpers/BookHelper.java +++ b/src/main/java/org/zeroBzeroT/antiillegals/helpers/BookHelper.java @@ -1,5 +1,8 @@ package org.zeroBzeroT.antiillegals.helpers; +import de.tr7zw.changeme.nbtapi.NBT; +import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBT; +import de.tr7zw.changeme.nbtapi.iface.ReadWriteNBTCompoundList; import org.bukkit.Bukkit; import org.bukkit.Location; import org.bukkit.Material; @@ -10,9 +13,9 @@ import org.jetbrains.annotations.Nullable; import org.zeroBzeroT.antiillegals.AntiIllegals; -import java.util.Arrays; -import java.util.Collection; -import java.util.Objects; +import java.util.*; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; import java.util.stream.Stream; public class BookHelper { @@ -21,32 +24,84 @@ private BookHelper() { } - public static int maxBookShulkers() { - return AntiIllegals.config().getInt("maxBooksShulkersInInventory"); + private static boolean hasConfigValue(@NotNull final String path) { + return AntiIllegals.config().isSet(path); } - public static int maxBookItemsInShulker() { - return AntiIllegals.config().getInt("maxBooksInShulker"); + private static boolean getBoolean(@NotNull final String newPath, @Nullable final String legacyPath, + final boolean defaultValue) { + if (hasConfigValue(newPath)) + return AntiIllegals.config().getBoolean(newPath); + if (legacyPath != null && hasConfigValue(legacyPath)) + return AntiIllegals.config().getBoolean(legacyPath); + return defaultValue; + } + + private static int getInt(@NotNull final String newPath, @Nullable final String legacyPath, + final int defaultValue) { + if (hasConfigValue(newPath)) + return AntiIllegals.config().getInt(newPath); + if (legacyPath != null && hasConfigValue(legacyPath)) + return AntiIllegals.config().getInt(legacyPath); + return defaultValue; + } + + public static boolean shouldCheckLecternBooks() { + return getBoolean("books.lecternCheck", null, true); } public static boolean shouldCleanBookShulkers() { - return AntiIllegals.config().getBoolean("shulkerBoxes"); + return getBoolean("books.shulkerBoxes.enabled", "shulkerBoxes", true); + } + + public static int maxBookItemsInShulker() { + return getInt("books.shulkerBoxes.maxBooksInside", "maxBooksInShulker", 10); + } + + public static int maxBookShulkers() { + return getInt("books.shulkerBoxes.maxContainersPerInventory", "maxBooksShulkersInInventory", 3); + } + + public static boolean shouldCleanBundleBooks() { + return getBoolean("books.bundles.enabled", null, true); } - public static void dropBookShulkerItem(@NotNull final Location location, @NotNull final ItemStack itemStack) { + public static int maxBooksInBundle() { + return getInt("books.bundles.maxBooksInside", null, 10); + } + + public static int maxBookBundlesInInventory() { + return getInt("books.bundles.maxContainersPerInventory", null, 1); + } + + public static void dropBookContainerItem(@NotNull final Location location, @NotNull final ItemStack itemStack) { location.getWorld().dropItem(location, itemStack).setPickupDelay(100); } public static int cleanBookItems(@NotNull final Inventory inventory, @Nullable final Location location, - @NotNull final Collection shulkerWithBooksItemStack) { + @NotNull final Collection shulkerWithBooksItemStack) { + if (!shouldCleanBookShulkers()) + return 0; + return cleanOversizedItems(inventory, location, shulkerWithBooksItemStack, maxBookItemsInShulker()); } public static int cleanBookShulkers(@NotNull final Inventory inventory, @Nullable final Location location, - @NotNull final Collection shulkerWithBooksItemStack) { + @NotNull final Collection shulkerWithBooksItemStack) { + if (!shouldCleanBookShulkers()) + return 0; + return cleanOversizedItems(inventory, location, shulkerWithBooksItemStack, maxBookShulkers()); } + public static int cleanBookBundles(@NotNull final Inventory inventory, @Nullable final Location location, + @NotNull final Collection bundleItemStacks) { + if (!shouldCleanBundleBooks()) + return 0; + + return cleanOversizedItems(inventory, location, bundleItemStacks, maxBookBundlesInInventory()); + } + public static boolean isBookItem(@NotNull final ItemStack itemStack) { return isBookItem(itemStack.getType()); } @@ -85,12 +140,14 @@ public static boolean containsBooks(@NotNull final Inventory inventory) { * @return whether it contains books */ public static boolean containsBooks(@Nullable final ItemStack itemStack) { - if (itemStack == null) return false; + if (itemStack == null) + return false; return InventoryHolderHelper.mapInventory(itemStack, BookHelper::containsBooks).orElse(false); } public static int cleanBookShulkers(@NotNull final Inventory inventory, @Nullable final Location location) { - if (!shouldCleanBookShulkers()) return 0; + if (!shouldCleanBookShulkers()) + return 0; final Collection shulkersWithBooks = Arrays.stream(inventory.getContents()) .filter(BookHelper::containsBooks) @@ -99,32 +156,134 @@ public static int cleanBookShulkers(@NotNull final Inventory inventory, @Nullabl return BookHelper.cleanBookShulkers(inventory, location, shulkersWithBooks); } + public static boolean bundleContainsBooks(@NotNull final ItemStack itemStack) { + if (!shouldCleanBundleBooks() || itemStack.getType() != Material.BUNDLE) + return false; + + final AtomicBoolean containsBooks = new AtomicBoolean(false); + + NBT.modify(itemStack, nbtItem -> { + final ReadWriteNBTCompoundList items = nbtItem.getCompoundList("Items"); + if (items == null) + return; + + for (final ReadWriteNBT entry : items) { + if (entry == null) + continue; + + final ItemStack stored = NBT.itemStackFromNBT(entry); + if (stored == null) + continue; + + if (!isBookItem(stored)) + continue; + + containsBooks.set(true); + break; + } + }); + + return containsBooks.get(); + } + + public static int trimBundleBooks(@NotNull final Collection bundleItemStacks, + @Nullable final Location location) { + if (!shouldCleanBundleBooks() || location == null || bundleItemStacks.isEmpty()) + return 0; + + final int maxBooks = maxBooksInBundle(); + if (maxBooks < 0) + return 0; + + int removed = 0; + for (final ItemStack bundle : bundleItemStacks) { + removed += trimBooksFromBundle(bundle, location, maxBooks); + } + return removed; + } + + private static int trimBooksFromBundle(@NotNull final ItemStack bundle, @NotNull final Location location, + final int maxBooks) { + final AtomicInteger removed = new AtomicInteger(0); + + NBT.modify(bundle, nbtItem -> { + final ReadWriteNBTCompoundList items = nbtItem.getCompoundList("Items"); + if (items == null || items.isEmpty()) + return; + + int seenBooks = 0; + final List removalIndexes = new ArrayList<>(); + final List droppedBooks = new ArrayList<>(); + + for (int i = 0; i < items.size(); i++) { + final ReadWriteNBT entry = items.get(i); + if (entry == null) + continue; + + final ItemStack stored = NBT.itemStackFromNBT(entry); + if (stored == null || !isBookItem(stored)) + continue; + + if (seenBooks < maxBooks) { + seenBooks++; + continue; + } + + removalIndexes.add(i); + droppedBooks.add(stored); + } + + if (removalIndexes.isEmpty()) + return; + + removalIndexes.sort(Integer::compareTo); + Collections.reverse(removalIndexes); + removalIndexes.forEach(items::remove); + + removed.addAndGet(droppedBooks.size()); + if (location.getWorld() != null) + droppedBooks.forEach(stack -> dropBookContainerItem(location, stack)); + }); + + return removed.get(); + } + public static int cleanOversizedItems(@NotNull final Inventory inventory, @Nullable final Location location, - @NotNull final Collection shulkerWithBooksItemStack, - final int maxItems) { - if (location == null || maxItems < 0 || shulkerWithBooksItemStack.size() <= maxItems) return 0; + @NotNull final Collection shulkerWithBooksItemStack, + final int maxItems) { + if (location == null || maxItems < 0 || shulkerWithBooksItemStack.size() <= maxItems) + return 0; int counter = 0; for (final ItemStack shulkerItemStack : shulkerWithBooksItemStack) { inventory.remove(shulkerItemStack); - Bukkit.getScheduler().runTask(AntiIllegals.INSTANCE, () -> dropBookShulkerItem(location, shulkerItemStack)); + Bukkit.getScheduler().runTask(AntiIllegals.INSTANCE, + () -> dropBookContainerItem(location, shulkerItemStack)); counter++; } return counter; } public static void checkEnderChest(@NotNull final InventoryOpenEvent inventoryOpenEvent, - @Nullable final Location location) { + @Nullable final Location location) { final Inventory inventory = inventoryOpenEvent.getInventory(); final ItemStack[] inventoryContents = inventory.getContents(); for (final ItemStack itemStack : inventoryContents) { - if (itemStack == null) continue; + if (itemStack == null) + continue; - InventoryHolderHelper.iterateInventory(itemStack, inv -> - BookHelper.cleanBookItems(inv, location, BookHelper.filterBooks(inv).toList()) - ); + InventoryHolderHelper.iterateInventory(itemStack, + inv -> BookHelper.cleanBookItems(inv, location, BookHelper.filterBooks(inv).toList())); } + + final List bundlesWithBooks = Arrays.stream(inventoryContents) + .filter(Objects::nonNull) + .filter(BookHelper::bundleContainsBooks) + .toList(); + + BookHelper.cleanBookBundles(inventory, location, bundlesWithBooks); + BookHelper.trimBundleBooks(bundlesWithBooks, location); } } diff --git a/src/main/java/org/zeroBzeroT/antiillegals/helpers/MaterialHelper.java b/src/main/java/org/zeroBzeroT/antiillegals/helpers/MaterialHelper.java index b1bf84f..65b9e89 100644 --- a/src/main/java/org/zeroBzeroT/antiillegals/helpers/MaterialHelper.java +++ b/src/main/java/org/zeroBzeroT/antiillegals/helpers/MaterialHelper.java @@ -38,8 +38,7 @@ public class MaterialHelper { Material.NETHERITE_CHESTPLATE, Material.NETHERITE_LEGGINGS, Material.NETHERITE_BOOTS, - Material.TURTLE_HELMET - ); + Material.TURTLE_HELMET); @NotNull public static final Set WEAPON_MATERIALS = Set.of( Material.WOODEN_AXE, @@ -56,8 +55,7 @@ public class MaterialHelper { Material.NETHERITE_AXE, Material.NETHERITE_SWORD, Material.CROSSBOW, - Material.TRIDENT - ); + Material.TRIDENT); @NotNull public static final Set TOOLS_MATERIALS = Set.of( Material.WOODEN_SHOVEL, @@ -83,8 +81,7 @@ public class MaterialHelper { Material.NETHERITE_SHOVEL, Material.NETHERITE_HOE, Material.BRUSH, - Material.WARPED_FUNGUS_ON_A_STICK - ); + Material.WARPED_FUNGUS_ON_A_STICK); @NotNull private static final Set NON_SHULKER_CONTAINERS = Set.of( Material.ARMOR_STAND, @@ -112,13 +109,13 @@ public class MaterialHelper { Material.CHEST_MINECART, Material.HOPPER_MINECART, Material.SMOKER, - Material.TRAPPED_CHEST - ); + Material.TRAPPED_CHEST, + Material.SUSPICIOUS_SAND, + Material.SUSPICIOUS_GRAVEL); @NotNull private static final Set BEE_CONTAINER_MATERIALS = Set.of( Material.BEEHIVE, - Material.BEE_NEST - ); + Material.BEE_NEST); /** * Filled dynamically from the config. */ @@ -146,7 +143,8 @@ public static Set requireIllegalMaterials() { * finds a given material by its name. no wildcard support * * @param name the name of the material to find (case-insensitive) - * @return an optional material. present if the material was found by name, empty if not + * @return an optional material. present if the material was found by name, + * empty if not */ @NotNull public static Optional findMaterial(@NotNull final String name) { @@ -169,7 +167,8 @@ public static Set matchMaterialsByPattern(@NotNull final String patter } /** - * slightly optimized version of matchMaterialsByPattern. if no wildcard match is used, no extra work is done. + * slightly optimized version of matchMaterialsByPattern. if no wildcard match + * is used, no extra work is done. * * @param pattern the pattern to match materials for (case-insensitive) * @return the set of materials that matched the pattern @@ -186,8 +185,10 @@ private static Set findMatchingMaterials(@NotNull final String pattern } private static boolean matchPattern(@NotNull final String pattern, @NotNull final String str) { - if (pattern.isEmpty() && str.isEmpty()) return true; - if (pattern.length() > 1 && pattern.charAt(0) == '*' && str.isEmpty()) return false; + if (pattern.isEmpty() && str.isEmpty()) + return true; + if (pattern.length() > 1 && pattern.charAt(0) == '*' && str.isEmpty()) + return false; if ((pattern.length() > 1 && pattern.charAt(0) == '?') || (!pattern.isEmpty() && !str.isEmpty() && pattern.charAt(0) == str.charAt(0))) diff --git a/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java b/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java index a8424bb..044237d 100644 --- a/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java +++ b/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java @@ -237,10 +237,14 @@ public static ReversionResult checkInventory(@NotNull final Inventory inventory, final List removeItemStacks = new ArrayList<>(); final List bookItemStacks = new ArrayList<>(); final List shulkerWithBooksItemStack = new ArrayList<>(); + final List bundlesWithBooks = new ArrayList<>(); boolean wasFixed = false; for (final ItemStack itemStack : inventory.getContents()) { + if (itemStack != null && BookHelper.bundleContainsBooks(itemStack)) + bundlesWithBooks.add(itemStack); + switch (checkItemStack(itemStack, location, checkRecursive)) { case EMPTY, CLEAN -> { } @@ -254,15 +258,20 @@ public static ReversionResult checkInventory(@NotNull final Inventory inventory, final int fixesIllegals = removeItemStacks.size(); final int fixesBooks = isInsideShulker ? BookHelper.cleanBookItems(inventory, location, bookItemStacks) : 0; + final int trimmedBundleBooks = BookHelper.trimBundleBooks(bundlesWithBooks, location); + final int fixesBookBundles = BookHelper.cleanBookBundles(inventory, location, bundlesWithBooks); final int fixesBookShulkers = BookHelper.cleanBookShulkers(inventory, location, shulkerWithBooksItemStack); - final boolean wasReverted = wasFixed || fixesIllegals > 0; - final boolean wasChecked = wasReverted && fixesBooks + fixesBookShulkers > 0; + final boolean wasReverted = wasFixed || fixesIllegals > 0 || fixesBookBundles > 0 || trimmedBundleBooks > 0; + final boolean hasBookActions = fixesBooks + fixesBookShulkers + fixesBookBundles + trimmedBundleBooks > 0; + final boolean wasChecked = wasReverted && hasBookActions; if (wasChecked) log("checkInventory", "Illegal Blocks: " + fixesIllegals + " - Dropped Books: " + fixesBooks + " - Dropped Shulkers: " + fixesBookShulkers + + " - Dropped Bundles: " + fixesBookBundles + + " - Trimmed Bundle Books: " + trimmedBundleBooks + " - Illegal NBT: " + wasFixed + "."); return new ReversionResult(bookItemStacks.size(), wasReverted); @@ -490,11 +499,30 @@ private static boolean deleteIllegalItem(@NotNull final ItemStack itemStack) { * @param itemStack the item to revert * @return whether the nbt tag was removed */ + private static boolean configSectionEnabled(@NotNull final String path, final boolean defaultValue) { + if (!AntiIllegals.config().isSet(path)) + return defaultValue; + return AntiIllegals.config().getBoolean(path); + } + private static boolean revertNBTContainers(@NotNull final ItemStack itemStack) { - if (!AntiIllegals.config().getBoolean("nbtContainers")) + if (!configSectionEnabled("nbtContainers.enabled", AntiIllegals.config().getBoolean("nbtContainers"))) return false; - if (!MaterialHelper.isNonShulkerContainer(itemStack)) + final Material material = itemStack.getType(); + + if (material == Material.ARMOR_STAND && configSectionEnabled("nbtContainers.armorStandItems", true)) + return sanitizeArmorStandEntity(itemStack); + + if ((material == Material.ITEM_FRAME || material == Material.GLOW_ITEM_FRAME) + && configSectionEnabled("nbtContainers.itemFrameItems", true)) + return sanitizeItemFrameEntity(itemStack); + + if (material == Material.BUNDLE && configSectionEnabled("nbtContainers.bundleItems", true)) + return sanitizeBundleItems(itemStack); + + if (!MaterialHelper.isNonShulkerContainer(itemStack) + || !configSectionEnabled("nbtContainers.stripOtherContainers", true)) return false; final AtomicBoolean modified = new AtomicBoolean(false); @@ -517,6 +545,111 @@ private static boolean revertNBTContainers(@NotNull final ItemStack itemStack) { return modified.get(); } + private static boolean sanitizeArmorStandEntity(@NotNull final ItemStack itemStack) { + final AtomicBoolean modified = new AtomicBoolean(false); + + NBT.modify(itemStack, nbtItem -> { + final ReadWriteNBT entityTag = nbtItem.getCompound("EntityTag"); + if (entityTag == null) + return; + + boolean changed = sanitizeItemList(entityTag.getCompoundList("ArmorItems")); + changed |= sanitizeItemList(entityTag.getCompoundList("HandItems")); + + if (changed) + modified.set(true); + }); + + return modified.get(); + } + + private static boolean sanitizeItemFrameEntity(@NotNull final ItemStack itemStack) { + final AtomicBoolean modified = new AtomicBoolean(false); + + NBT.modify(itemStack, nbtItem -> { + final ReadWriteNBT entityTag = nbtItem.getCompound("EntityTag"); + if (entityTag == null) + return; + + final ReadWriteNBT itemCompound = entityTag.getCompound("Item"); + if (itemCompound == null) + return; + + if (sanitizeItemCompound(itemCompound)) + modified.set(true); + }); + + return modified.get(); + } + + private static boolean sanitizeBundleItems(@NotNull final ItemStack itemStack) { + final AtomicBoolean modified = new AtomicBoolean(false); + + NBT.modify(itemStack, nbtItem -> { + final ReadWriteNBTCompoundList items = nbtItem.getCompoundList("Items"); + if (items == null) + return; + + if (sanitizeItemList(items)) + modified.set(true); + }); + + return modified.get(); + } + + private static boolean sanitizeItemList(@Nullable final ReadWriteNBTCompoundList list) { + if (list == null) + return false; + + boolean modified = false; + for (final ReadWriteNBT compound : list) { + if (compound == null) + continue; + + if (sanitizeItemCompound(compound)) + modified = true; + } + return modified; + } + + private static boolean sanitizeItemCompound(@NotNull final ReadWriteNBT compound) { + final ItemStack stack; + try { + stack = NBT.itemStackFromNBT(compound); + } catch (Exception ex) { + log("sanitizeItemCompound", "Failed to deserialize nested item: " + ex.getMessage()); + return false; + } + + if (stack == null || stack.getType() == Material.AIR) + return false; + + final ItemState state = checkItemStack(stack, null, true); + if (!state.wasReverted()) + return false; + + final ItemStack sanitizedStack = stack.getAmount() <= 0 ? new ItemStack(Material.AIR) : stack; + + overwriteCompoundWithItem(compound, sanitizedStack); + return true; + } + + private static void overwriteCompoundWithItem(@NotNull final ReadWriteNBT target, + @NotNull final ItemStack source) { + final Byte slot = target.hasTag("Slot") ? target.getByte("Slot") : null; + final ReadWriteNBT replacement = NBT.itemStackToNBT(source); + + final Set keys = new HashSet<>(target.getKeys()); + for (final String key : keys) { + target.removeKey(key); + } + + target.mergeCompound(replacement); + + if (slot != null) + target.setByte("Slot", slot); + } + private static boolean revertBlockStateTags(@NotNull final ItemStack itemStack) { if (!AntiIllegals.config().getBoolean("blockStates.enabled")) return false; diff --git a/src/main/resources/config.yml b/src/main/resources/config.yml index 46d48e2..99d8901 100644 --- a/src/main/resources/config.yml +++ b/src/main/resources/config.yml @@ -4,7 +4,13 @@ unbreakables: false flightTime: true durability: true illegalBlocks: true -nbtContainers: true +nbtContainers: + enabled: true + armorStandItems: true + itemFrameItems: true + bundleItems: true + stripOtherContainers: true +interactItems: true blockStates: enabled: true keys: @@ -23,13 +29,20 @@ overstackedItems: true allowCollectibles: true conflictingEnchantments: true maxEnchantments: true -shulkerBoxes: true -maxBooksInShulker: 10 -maxBooksShulkersInInventory: 3 +books: + lecternCheck: true + shulkerBoxes: + enabled: true + maxBooksInside: 10 + maxContainersPerInventory: 3 + bundles: + enabled: true + maxBooksInside: 10 + maxContainersPerInventory: 1 attributeModifiers: true customPotionEffects: true crossbowProjectiles: true -hideFlags: true +hideFlags: false suspiciousStewEffects: true illegalMaterials: @@ -50,9 +63,9 @@ illegalMaterials: - "vault" - "trial_spawner" - "reinforced_deepslate" - - "bundle" # technically illegal, but not reverting these by default +# - "bundle" # - "chorus_plant" # - "dirt_path" # - "farmland" From 29c516f0d140b752c0c29c5a1ca51bebf7226676 Mon Sep 17 00:00:00 2001 From: v0ey <113121361+v0ey@users.noreply.github.com> Date: Sun, 4 Jan 2026 15:06:20 +0300 Subject: [PATCH 2/2] fix legacy nbtContainers default --- .../java/org/zeroBzeroT/antiillegals/AntiIllegals.java | 9 ++++++++- .../zeroBzeroT/antiillegals/helpers/RevertHelper.java | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java b/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java index 88ca9c1..8c4ae56 100644 --- a/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java +++ b/src/main/java/org/zeroBzeroT/antiillegals/AntiIllegals.java @@ -31,6 +31,13 @@ public static FileConfiguration config() { return INSTANCE.getConfig(); } + public static boolean resolveNbtContainersDefault() { + final FileConfiguration cfg = config(); + if (cfg.isBoolean("nbtContainers")) + return cfg.getBoolean("nbtContainers"); + return true; + } + /** * log formatting and output */ @@ -54,7 +61,7 @@ public void onEnable() { log("durability", "" + getConfig().getBoolean("durability")); log("illegalBlocks", "" + getConfig().getBoolean("illegalBlocks")); log("nbtContainers.enabled", "" + getConfig().getBoolean("nbtContainers.enabled", - getConfig().getBoolean("nbtContainers"))); + resolveNbtContainersDefault())); log("nbtContainers.armorStandItems", "" + getConfig().getBoolean("nbtContainers.armorStandItems", true)); log("nbtContainers.itemFrameItems", "" + getConfig().getBoolean("nbtContainers.itemFrameItems", true)); log("nbtContainers.bundleItems", "" + getConfig().getBoolean("nbtContainers.bundleItems", true)); diff --git a/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java b/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java index 044237d..3e27d9d 100644 --- a/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java +++ b/src/main/java/org/zeroBzeroT/antiillegals/helpers/RevertHelper.java @@ -506,7 +506,7 @@ private static boolean configSectionEnabled(@NotNull final String path, final bo } private static boolean revertNBTContainers(@NotNull final ItemStack itemStack) { - if (!configSectionEnabled("nbtContainers.enabled", AntiIllegals.config().getBoolean("nbtContainers"))) + if (!configSectionEnabled("nbtContainers.enabled", AntiIllegals.resolveNbtContainersDefault())) return false; final Material material = itemStack.getType();