From 5966f20c2bf52d7cec12707481cf2086335a8b07 Mon Sep 17 00:00:00 2001 From: RVSkeLe Date: Thu, 28 May 2026 20:00:01 +0200 Subject: [PATCH 1/2] Use cached values in ItemSignature --- .../spawner/gui/main/SpawnerMenuUI.java | 9 ++- .../gui/sell/SpawnerSellConfirmUI.java | 4 +- .../spawner/gui/storage/SpawnerStorageUI.java | 4 +- .../spawner/lootgen/SpawnerLootGenerator.java | 2 +- .../spawner/properties/ItemSignature.java | 8 +-- .../spawner/properties/SpawnerData.java | 57 +++++++++---------- .../spawner/properties/VirtualInventory.java | 17 +++--- .../spawner/sell/SpawnerSellManager.java | 6 +- .../spawner/utils/ItemStackSerializer.java | 20 ++----- 9 files changed, 56 insertions(+), 71 deletions(-) diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuUI.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuUI.java index 2f040532..a70a2b66 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuUI.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuUI.java @@ -287,7 +287,7 @@ private String buildLootItemsText(EntityType entityType, Map materialAmountMap = new HashMap<>(); for (Map.Entry entry : storedItems.entrySet()) { - Material material = entry.getKey().getTemplateRef().getType(); + Material material = entry.getKey().getMaterial(); materialAmountMap.merge(material, entry.getValue(), Long::sum); } @@ -333,8 +333,7 @@ private String buildLootItemsText(EntityType entityType, Map e.getKey().getMaterialName())); for (Map.Entry entry : sortedItems) { - ItemStack templateItem = entry.getKey().getTemplateRef(); - Material material = templateItem.getType(); + Material material = entry.getKey().getMaterial(); long amount = entry.getValue(); String materialName = languageManager.getVanillaItemName(material); @@ -606,7 +605,7 @@ private int calculatePercentage(long current, long maximum) { private List buildLootItemComponents(EntityType entityType, Map storedItems) { Map materialAmountMap = new HashMap<>(); for (Map.Entry entry : storedItems.entrySet()) { - Material material = entry.getKey().getTemplateRef().getType(); + Material material = entry.getKey().getMaterial(); materialAmountMap.merge(material, entry.getValue(), Long::sum); } @@ -633,7 +632,7 @@ private List buildLootItemComponents(EntityType entityType, Map(storedItems.entrySet()); sortedItems.sort(Comparator.comparing(e -> e.getKey().getMaterialName())); for (Map.Entry entry : sortedItems) { - Material material = entry.getKey().getTemplateRef().getType(); + Material material = entry.getKey().getMaterial(); long amount = entry.getValue(); String formattedAmount = languageManager.formatNumber(amount); components.add(languageManager.buildTranslatableGuiLootLine( diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/sell/SpawnerSellConfirmUI.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/sell/SpawnerSellConfirmUI.java index 2f88e9ef..5f633b8d 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/sell/SpawnerSellConfirmUI.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/sell/SpawnerSellConfirmUI.java @@ -203,7 +203,7 @@ private ItemStack createSpawnerInfoButton(Player player, SpawnerData spawner, Ma private List buildSellInfoLootComponents(SpawnerData spawner, Map storedItems) { Map materialAmountMap = new HashMap<>(); for (Map.Entry entry : storedItems.entrySet()) { - Material material = entry.getKey().getTemplateRef().getType(); + Material material = entry.getKey().getMaterial(); materialAmountMap.merge(material, entry.getValue(), Long::sum); } @@ -230,7 +230,7 @@ private List buildSellInfoLootComponents(SpawnerData spawner, Map> sortedItems = new ArrayList<>(storedItems.entrySet()); sortedItems.sort(Comparator.comparing(e -> e.getKey().getMaterialName())); for (Map.Entry entry : sortedItems) { - Material material = entry.getKey().getTemplateRef().getType(); + Material material = entry.getKey().getMaterial(); long amount = entry.getValue(); String formattedAmount = languageManager.formatNumber(amount); components.add(languageManager.buildTranslatableGuiLootLine( diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageUI.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageUI.java index 34a686d5..321db3f3 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageUI.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/storage/SpawnerStorageUI.java @@ -591,7 +591,7 @@ private List buildStorageInfoLootComponents(SpawnerData spawner, Map storedItems) { Map materialAmountMap = new HashMap<>(); for (Map.Entry entry : storedItems.entrySet()) { - Material mat = entry.getKey().getTemplateRef().getType(); + Material mat = entry.getKey().getMaterial(); materialAmountMap.merge(mat, entry.getValue(), Long::sum); } @@ -622,7 +622,7 @@ private List buildStorageInfoLootComponents(SpawnerData spawner, List> sortedItems = new ArrayList<>(storedItems.entrySet()); sortedItems.sort(Comparator.comparing(e -> e.getKey().getMaterialName())); for (Map.Entry entry : sortedItems) { - Material mat = entry.getKey().getTemplateRef().getType(); + Material mat = entry.getKey().getMaterial(); long amount = entry.getValue(); String formattedAmount = languageManager.formatNumber(amount); components.add(languageManager.buildTranslatableGuiLootLine( diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/lootgen/SpawnerLootGenerator.java b/core/src/main/java/github/nighter/smartspawner/spawner/lootgen/SpawnerLootGenerator.java index b73e0c07..d19eaf05 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/lootgen/SpawnerLootGenerator.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/lootgen/SpawnerLootGenerator.java @@ -341,7 +341,7 @@ private int calculateSlots(Map items) { return items.entrySet().stream() .mapToInt(entry -> { long amount = entry.getValue(); - int maxStackSize = entry.getKey().getTemplateRef().getMaxStackSize(); + int maxStackSize = entry.getKey().getMaxStackSize(); // Use integer division with ceiling function return (int) ((amount + maxStackSize - 1) / maxStackSize); }) diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java b/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java index 986d5adb..d6161391 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java @@ -1,6 +1,7 @@ package github.nighter.smartspawner.spawner.properties; import lombok.Getter; +import lombok.experimental.Accessors; import org.bukkit.Material; import org.bukkit.inventory.ItemStack; import org.bukkit.inventory.meta.Damageable; @@ -13,11 +14,10 @@ public class ItemSignature { @Getter private final Material material; @Getter private final int maxStackSize; @Getter private final int damage; - @Getter private final boolean hasItemMeta; + @Getter @Accessors(fluent = true) private final boolean hasItemMeta; public ItemSignature(ItemStack item) { - this.template = item.clone(); - this.template.setAmount(1); + this.template = item.asQuantity(1); // Clone with new amount this.material = template.getType(); this.maxStackSize = template.getMaxStackSize(); @@ -92,4 +92,4 @@ private int extractDamage(ItemMeta meta) { return 0; } -} \ No newline at end of file +} diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java index 12aa51a3..dd3abc6f 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java @@ -565,12 +565,9 @@ public void incrementSellValue(Map itemsAdded, double addedValue = 0.0; for (Map.Entry entry : itemsAdded.entrySet()) { - // Use getTemplateRef() to avoid cloning - we only need to read properties - ItemStack template = entry.getKey().getTemplateRef(); - long amount = entry.getValue(); - double itemPrice = findItemPrice(template, priceCache); + double itemPrice = findItemPrice(entry.getKey(), priceCache); if (itemPrice > 0.0) { - addedValue += itemPrice * amount; + addedValue += itemPrice * entry.getValue(); } } @@ -599,12 +596,9 @@ public void decrementSellValue(List itemsRemoved, Map double removedValue = 0.0; for (Map.Entry entry : consolidated.entrySet()) { - // Use getTemplateRef() to avoid cloning - we only need to read properties - ItemStack template = entry.getKey().getTemplateRef(); - long amount = entry.getValue(); - double itemPrice = findItemPrice(template, priceCache); + double itemPrice = findItemPrice(entry.getKey(), priceCache); if (itemPrice > 0.0) { - removedValue += itemPrice * amount; + removedValue += itemPrice * entry.getValue(); } } @@ -630,12 +624,9 @@ public void recalculateSellValue() { double totalValue = 0.0; for (Map.Entry entry : items.entrySet()) { - // Use getTemplateRef() to avoid cloning - we only need to read properties - ItemStack template = entry.getKey().getTemplateRef(); - long amount = entry.getValue(); - double itemPrice = findItemPrice(template, priceCache); + double itemPrice = findItemPrice(entry.getKey(), priceCache); if (itemPrice > 0.0) { - totalValue += itemPrice * amount; + totalValue += itemPrice * entry.getValue(); } } @@ -678,45 +669,51 @@ public Map createPriceCache() { /** * Finds item price using the cache */ - private double findItemPrice(ItemStack item, Map priceCache) { - if (item == null || priceCache == null) { + private double findItemPrice(ItemSignature itemSignature, Map priceCache) { + if (priceCache == null) { return 0.0; } - String itemKey = createItemKey(item); + String itemKey = createItemKey(itemSignature); Double price = priceCache.get(itemKey); return price != null ? price : 0.0; } /** - * Creates a unique key for an item (same logic as SpawnerSellManager) + * Convenience overload */ - private String createItemKey(ItemStack item) { - if (item == null) { - return "null"; - } + private String createItemKey(ItemStack itemStack) { + if (itemStack == null) return "null"; + + return createItemKey(new ItemSignature(itemStack)); + } + /** + * Creates a unique key for an item (same logic as SpawnerSellManager) + * TODO: this feels very wrong ngl + */ + private String createItemKey(ItemSignature itemSignature) { StringBuilder key = new StringBuilder(); - key.append(item.getType().name()); + key.append(itemSignature.getMaterial().name()); // Add enchantments if present - if (item.hasItemMeta() && item.getItemMeta().hasEnchants()) { + ItemMeta meta = itemSignature.getTemplateRef().getItemMeta(); // Read-only + if (itemSignature.hasItemMeta() && meta.hasEnchants()) { key.append("_enchants:"); - item.getItemMeta().getEnchants().entrySet().stream() + meta.getEnchants().entrySet().stream() .sorted(java.util.Map.Entry.comparingByKey(java.util.Comparator.comparing(enchantment -> enchantment.getKey().toString()))) .forEach(entry -> key.append(entry.getKey().getKey()).append(":").append(entry.getValue()).append(",")); } // Add custom model data if present - if (item.hasItemMeta()) { - ItemMeta meta = item.getItemMeta(); + if (itemSignature.hasItemMeta()) { if (VersionInitializer.hasCustomModelData(meta)) { key.append("_cmd:").append(VersionInitializer.getCustomModelDataString(meta)); } } // Add display name if present - if (item.hasItemMeta() && item.getItemMeta().hasDisplayName()) { - key.append("_name:").append(item.getItemMeta().displayName()); + if (itemSignature.hasItemMeta() && meta.hasDisplayName()) { + key.append("_name:").append(meta.displayName()); } return key.toString(); diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/properties/VirtualInventory.java b/core/src/main/java/github/nighter/smartspawner/spawner/properties/VirtualInventory.java index 547ca464..27c29719 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/properties/VirtualInventory.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/properties/VirtualInventory.java @@ -130,8 +130,8 @@ public Map getDisplayInventory() { if (preferredSortMaterial != null) { sortedEntriesCache.sort((e1, e2) -> { // Use getTemplateRef() to avoid cloning - we only need to read the type - boolean e1Preferred = e1.getKey().getTemplateRef().getType() == preferredSortMaterial; - boolean e2Preferred = e2.getKey().getTemplateRef().getType() == preferredSortMaterial; + boolean e1Preferred = e1.getKey().getMaterial() == preferredSortMaterial; + boolean e2Preferred = e2.getKey().getMaterial() == preferredSortMaterial; if (e1Preferred && !e2Preferred) return -1; if (!e1Preferred && e2Preferred) return 1; @@ -153,15 +153,14 @@ public Map getDisplayInventory() { ItemSignature sig = entry.getKey(); long totalAmount = entry.getValue(); - ItemStack templateItem = sig.getTemplateRef(); - int maxStackSize = templateItem.getMaxStackSize(); + int maxStackSize = sig.getMaxStackSize(); // Create as many stacks as needed for this item type while (totalAmount > 0 && currentSlot < maxSlots) { int stackSize = (int) Math.min(totalAmount, maxStackSize); // Create the display item only once per slot - ItemStack displayItem = templateItem.clone(); + ItemStack displayItem = sig.getTemplate(); displayItem.setAmount(stackSize); // Store in cache @@ -203,7 +202,7 @@ public int getUsedSlots() { int estimatedSlots = 0; for (Map.Entry entry : consolidatedItems.entrySet()) { long amount = entry.getValue(); - int maxStackSize = entry.getKey().getTemplateRef().getMaxStackSize(); + int maxStackSize = entry.getKey().getMaxStackSize(); estimatedSlots += (int) Math.ceil((double) amount / maxStackSize); if (estimatedSlots >= maxSlots) { return maxSlots; // Cap at max slots @@ -250,8 +249,8 @@ public void sortItems(org.bukkit.Material preferredMaterial) { this.sortedEntriesCache = consolidatedItems.entrySet().stream() .sorted((e1, e2) -> { // Use getTemplateRef() to avoid cloning - we only need to read the type - boolean e1Preferred = e1.getKey().getTemplateRef().getType() == preferredMaterial; - boolean e2Preferred = e2.getKey().getTemplateRef().getType() == preferredMaterial; + boolean e1Preferred = e1.getKey().getMaterial() == preferredMaterial; + boolean e2Preferred = e2.getKey().getMaterial() == preferredMaterial; if (e1Preferred && !e2Preferred) return -1; if (!e1Preferred && e2Preferred) return 1; @@ -296,4 +295,4 @@ public void resize(int newMaxSlots) { // but they won't be accessible in the display } } -} \ No newline at end of file +} diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/sell/SpawnerSellManager.java b/core/src/main/java/github/nighter/smartspawner/spawner/sell/SpawnerSellManager.java index 878b431c..66aadab2 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/sell/SpawnerSellManager.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/sell/SpawnerSellManager.java @@ -188,9 +188,9 @@ private SellResult calculateSellValue(Map consolidatedItems ArrayList itemsToRemove = new ArrayList<>(); for (Map.Entry entry : consolidatedItems.entrySet()) { - ItemStack templateRef = entry.getKey().getTemplateRef(); + ItemSignature signature = entry.getKey(); long amount = entry.getValue(); - int maxStackSize = templateRef.getMaxStackSize(); + int maxStackSize = signature.getMaxStackSize(); totalItemsSold += amount; @@ -199,7 +199,7 @@ private SellResult calculateSellValue(Map consolidatedItems long remaining = amount; while (remaining > 0) { - ItemStack stack = templateRef.clone(); + ItemStack stack = signature.getTemplate(); stack.setAmount((int) Math.min(remaining, maxStackSize)); itemsToRemove.add(stack); remaining -= stack.getAmount(); diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java b/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java index 37ee0242..d9d9a0e2 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java @@ -43,12 +43,12 @@ public static List serializeInventory(Map items) { for (Map.Entry entry : items.entrySet()) { // Use getTemplateRef() to avoid cloning - we only need to read properties - ItemStack template = entry.getKey().getTemplateRef(); - Material material = template.getType(); + ItemSignature signature = entry.getKey(); + Material material = signature.getMaterial(); ItemGroup group = groupedItems.computeIfAbsent(material, ItemGroup::new); if (material == Material.TIPPED_ARROW) { - PotionMeta meta = (PotionMeta) template.getItemMeta(); + PotionMeta meta = (PotionMeta) signature.getTemplateRef().getItemMeta(); // Read-only if (meta != null && meta.getBasePotionType() != null) { group.addPotionArrow(meta.getBasePotionType(), entry.getValue().intValue()); } else { @@ -57,7 +57,7 @@ public static List serializeInventory(Map items) { } } else if (isDestructibleItem(material)) { // Use modern damage system instead of durability - int damage = getDamageValue(template); + int damage = signature.getDamage(); group.addItem(damage, entry.getValue().intValue()); } else { // For non-destructible items, always use damage 0 @@ -157,16 +157,6 @@ public static Map deserializeInventory(List data) { return result; } - /** - * Get damage value from ItemStack using modern API - */ - private static int getDamageValue(ItemStack item) { - if (item.getItemMeta() instanceof Damageable damageable) { - return damageable.getDamage(); - } - return 0; - } - /** * Set damage value to ItemStack using modern API */ @@ -209,4 +199,4 @@ public static boolean isDestructibleItem(Material material) { || name.equals("WARPED_FUNGUS_ON_A_STICK") || name.equals("MACE"); } -} \ No newline at end of file +} From c3d6eca68bd404be72a0f59d1358f2e1b9e591b2 Mon Sep 17 00:00:00 2001 From: RVSkeLe Date: Thu, 28 May 2026 20:07:48 +0200 Subject: [PATCH 2/2] Make getTemplateRef sound scary --- .../nighter/smartspawner/spawner/properties/ItemSignature.java | 2 +- .../nighter/smartspawner/spawner/properties/SpawnerData.java | 2 +- .../nighter/smartspawner/spawner/utils/ItemStackSerializer.java | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java b/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java index d6161391..9c20e3a3 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/properties/ItemSignature.java @@ -77,7 +77,7 @@ public ItemStack getTemplate() { } // Non-cloning method for internal use - public ItemStack getTemplateRef() { + public ItemStack getUnsafeTemplateRef() { return template; } diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java index dd3abc6f..3ee6521e 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerData.java @@ -696,7 +696,7 @@ private String createItemKey(ItemSignature itemSignature) { key.append(itemSignature.getMaterial().name()); // Add enchantments if present - ItemMeta meta = itemSignature.getTemplateRef().getItemMeta(); // Read-only + ItemMeta meta = itemSignature.getUnsafeTemplateRef().getItemMeta(); // Read-only if (itemSignature.hasItemMeta() && meta.hasEnchants()) { key.append("_enchants:"); meta.getEnchants().entrySet().stream() diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java b/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java index d9d9a0e2..470cfeec 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/utils/ItemStackSerializer.java @@ -48,7 +48,7 @@ public static List serializeInventory(Map items) { ItemGroup group = groupedItems.computeIfAbsent(material, ItemGroup::new); if (material == Material.TIPPED_ARROW) { - PotionMeta meta = (PotionMeta) signature.getTemplateRef().getItemMeta(); // Read-only + PotionMeta meta = (PotionMeta) signature.getUnsafeTemplateRef().getItemMeta(); // Read-only if (meta != null && meta.getBasePotionType() != null) { group.addPotionArrow(meta.getBasePotionType(), entry.getValue().intValue()); } else {