From 78826145a74ed43e352b509125269ef533d14f8b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 02:28:58 +0000 Subject: [PATCH 1/3] Initial plan From fd8524d0f8433bd0fd82c4eb77925053f6521a51 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 02:35:20 +0000 Subject: [PATCH 2/3] Fix CraftLegacy warning and hopper stopping issue Co-authored-by: ptthanh02 <73684260+ptthanh02@users.noreply.github.com> --- .../smartspawner/extras/HopperHandler.java | 27 ++++++++++++++++++- .../click/SpawnerClickManager.java | 2 +- .../destroy/SpawnerBreakListener.java | 2 +- .../place/SpawnerPlaceListener.java | 6 ++--- .../spawner/properties/SpawnerData.java | 6 +++++ 5 files changed, 37 insertions(+), 6 deletions(-) diff --git a/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java b/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java index 9c99780d..6392a65c 100644 --- a/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java +++ b/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java @@ -180,6 +180,31 @@ public void stopHopperTask(Location hopperLoc) { } } + /** + * Restart hopper task for a spawner location. + * This is called when spawner stack size changes to ensure hopper continues working. + * @param spawnerLoc The location of the spawner + */ + public void restartHopperForSpawner(Location spawnerLoc) { + if (!plugin.getConfig().getBoolean("hopper.enabled", false)) return; + + // Find hopper below the spawner + Block spawnerBlock = spawnerLoc.getBlock(); + if (spawnerBlock.getType() != Material.SPAWNER) return; + + Block hopperBlock = spawnerBlock.getRelative(BlockFace.DOWN); + if (hopperBlock.getType() != Material.HOPPER) return; + + Location hopperLoc = hopperBlock.getLocation(); + + // Stop existing hopper task if any + stopHopperTask(hopperLoc); + + // Start new hopper task + startHopperTask(hopperLoc, spawnerLoc); + } + + private void transferItems(Location hopperLoc, Location spawnerLoc) { SpawnerData spawner = spawnerManager.getSpawnerByLocation(spawnerLoc); if (spawner == null) return; @@ -190,7 +215,7 @@ private void transferItems(Location hopperLoc, Location spawnerLoc) { try { VirtualInventory virtualInv = spawner.getVirtualInventory(); - Hopper hopper = (Hopper) hopperLoc.getBlock().getState(false); + Hopper hopper = (Hopper) hopperLoc.getBlock().getState(); int itemsPerTransfer = plugin.getConfig().getInt("hopper.stack_per_transfer", 5); int transferred = 0; diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java index 7ab98682..9d95e86f 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java @@ -163,7 +163,7 @@ private void handleSpawnerInteraction(Player player, Block block, ItemStack held // Handle spawn egg usage if (isSpawnEgg(itemType)) { - spawnEggHandler.handleSpawnEggUse(player, (CreatureSpawner) block.getState(false), spawner, heldItem); + spawnEggHandler.handleSpawnEggUse(player, (CreatureSpawner) block.getState(), spawner, heldItem); return; } diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java index 7a209e64..c2433ac2 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java @@ -91,7 +91,7 @@ public void onSpawnerBreak(BlockBreakEvent event) { handleSmartSpawnerBreak(block, spawner, player); plugin.getRangeChecker().stopSpawnerTask(spawner); } else { - CreatureSpawner creatureSpawner = (CreatureSpawner) block.getState(false); + CreatureSpawner creatureSpawner = (CreatureSpawner) block.getState(); if(callAPIEvent(player, block.getLocation(), 1)) { event.setCancelled(true); return; diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java index 25aa4940..6a9a01aa 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java @@ -232,7 +232,7 @@ private void handleSpawnerSetup(Block block, Player player, EntityType entityTyp return; } - CreatureSpawner spawner = (CreatureSpawner) block.getState(false); + CreatureSpawner spawner = (CreatureSpawner) block.getState(); if (isVanillaSpawner) { spawner.setSpawnedType(entityType); @@ -245,7 +245,7 @@ private void handleSpawnerSetup(Block block, Player player, EntityType entityTyp return; } - CreatureSpawner delayedSpawner = (CreatureSpawner) block.getState(false); + CreatureSpawner delayedSpawner = (CreatureSpawner) block.getState(); EntityType finalEntityType = getEntityType(entityType, delayedSpawner); delayedSpawner.setSpawnedType(finalEntityType); @@ -272,7 +272,7 @@ private EntityType getEntityType(EntityType storedEntityType, CreatureSpawner pl private void createSmartSpawner(Block block, Player player, EntityType entityType, int stackSize) { String spawnerId = UUID.randomUUID().toString().substring(0, 8); - BlockState state = block.getState(false); + BlockState state = block.getState(); if (state instanceof CreatureSpawner spawner) { spawner.setSpawnedType(entityType); spawner.update(true, false); 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 fdf192fe..b206b5c1 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 @@ -243,6 +243,12 @@ private void updateStackSize(int newStackSize) { if (plugin.getSpawnerMenuFormUI() != null) { plugin.getSpawnerMenuFormUI().invalidateSpawnerCache(this.spawnerId); } + + // Restart hopper task if hopper integration is enabled + // This ensures hopper continues to work after stack size changes + if (plugin.getHopperHandler() != null) { + plugin.getHopperHandler().restartHopperForSpawner(this.spawnerLocation); + } } private void recreateVirtualInventory() { From fcd536ddc58710a9665f1aac5f0cc9df56312ff4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 1 Nov 2025 03:16:42 +0000 Subject: [PATCH 3/3] Fix CraftLegacy warning by removing DynamicMaterialDetector and add entity type validation Co-authored-by: ptthanh02 <73684260+ptthanh02@users.noreply.github.com> --- .../smartspawner/extras/HopperHandler.java | 3 +- .../spawner/gui/main/SpawnerMenuAction.java | 15 ++++-- .../click/SpawnerClickManager.java | 2 +- .../destroy/SpawnerBreakListener.java | 2 +- .../place/SpawnerPlaceListener.java | 6 +-- .../spawner/loot/EntityLootRegistry.java | 10 ++++ .../utils/DynamicMaterialDetector.java | 51 ------------------- 7 files changed, 28 insertions(+), 61 deletions(-) delete mode 100644 core/src/main/java/github/nighter/smartspawner/utils/DynamicMaterialDetector.java diff --git a/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java b/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java index 6392a65c..0607f0ed 100644 --- a/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java +++ b/core/src/main/java/github/nighter/smartspawner/extras/HopperHandler.java @@ -204,7 +204,6 @@ public void restartHopperForSpawner(Location spawnerLoc) { startHopperTask(hopperLoc, spawnerLoc); } - private void transferItems(Location hopperLoc, Location spawnerLoc) { SpawnerData spawner = spawnerManager.getSpawnerByLocation(spawnerLoc); if (spawner == null) return; @@ -215,7 +214,7 @@ private void transferItems(Location hopperLoc, Location spawnerLoc) { try { VirtualInventory virtualInv = spawner.getVirtualInventory(); - Hopper hopper = (Hopper) hopperLoc.getBlock().getState(); + Hopper hopper = (Hopper) hopperLoc.getBlock().getState(false); int itemsPerTransfer = plugin.getConfig().getInt("hopper.stack_per_transfer", 5); int transferred = 0; diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java index 5d258eb9..f98800d1 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/gui/main/SpawnerMenuAction.java @@ -10,7 +10,6 @@ import github.nighter.smartspawner.spawner.gui.synchronization.SpawnerGuiViewManager; import github.nighter.smartspawner.spawner.properties.SpawnerData; import github.nighter.smartspawner.spawner.sell.SpawnerSellManager; -import github.nighter.smartspawner.utils.DynamicMaterialDetector; import org.bukkit.Bukkit; import org.bukkit.Material; import org.bukkit.Particle; @@ -32,8 +31,18 @@ import java.util.concurrent.ConcurrentHashMap; public class SpawnerMenuAction implements Listener { - private static final Set SPAWNER_INFO_MATERIALS = - DynamicMaterialDetector.getSpawnerInfoMaterials(); + // Spawner info materials - manually defined to avoid Material.values() iteration + // which can trigger CraftLegacy initialization + private static final Set SPAWNER_INFO_MATERIALS = Set.of( + Material.PLAYER_HEAD, + Material.SPAWNER, + Material.ZOMBIE_HEAD, + Material.SKELETON_SKULL, + Material.WITHER_SKELETON_SKULL, + Material.CREEPER_HEAD, + Material.PIGLIN_HEAD, + Material.DRAGON_HEAD + ); private final SmartSpawner plugin; private final SpawnerMenuUI spawnerMenuUI; private final SpawnerStackerUI spawnerStackerUI; diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java index 9d95e86f..7ab98682 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/click/SpawnerClickManager.java @@ -163,7 +163,7 @@ private void handleSpawnerInteraction(Player player, Block block, ItemStack held // Handle spawn egg usage if (isSpawnEgg(itemType)) { - spawnEggHandler.handleSpawnEggUse(player, (CreatureSpawner) block.getState(), spawner, heldItem); + spawnEggHandler.handleSpawnEggUse(player, (CreatureSpawner) block.getState(false), spawner, heldItem); return; } diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java index c2433ac2..7a209e64 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/destroy/SpawnerBreakListener.java @@ -91,7 +91,7 @@ public void onSpawnerBreak(BlockBreakEvent event) { handleSmartSpawnerBreak(block, spawner, player); plugin.getRangeChecker().stopSpawnerTask(spawner); } else { - CreatureSpawner creatureSpawner = (CreatureSpawner) block.getState(); + CreatureSpawner creatureSpawner = (CreatureSpawner) block.getState(false); if(callAPIEvent(player, block.getLocation(), 1)) { event.setCancelled(true); return; diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java index 6a9a01aa..25aa4940 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/interactions/place/SpawnerPlaceListener.java @@ -232,7 +232,7 @@ private void handleSpawnerSetup(Block block, Player player, EntityType entityTyp return; } - CreatureSpawner spawner = (CreatureSpawner) block.getState(); + CreatureSpawner spawner = (CreatureSpawner) block.getState(false); if (isVanillaSpawner) { spawner.setSpawnedType(entityType); @@ -245,7 +245,7 @@ private void handleSpawnerSetup(Block block, Player player, EntityType entityTyp return; } - CreatureSpawner delayedSpawner = (CreatureSpawner) block.getState(); + CreatureSpawner delayedSpawner = (CreatureSpawner) block.getState(false); EntityType finalEntityType = getEntityType(entityType, delayedSpawner); delayedSpawner.setSpawnedType(finalEntityType); @@ -272,7 +272,7 @@ private EntityType getEntityType(EntityType storedEntityType, CreatureSpawner pl private void createSmartSpawner(Block block, Player player, EntityType entityType, int stackSize) { String spawnerId = UUID.randomUUID().toString().substring(0, 8); - BlockState state = block.getState(); + BlockState state = block.getState(false); if (state instanceof CreatureSpawner spawner) { spawner.setSpawnedType(entityType); spawner.update(true, false); diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/loot/EntityLootRegistry.java b/core/src/main/java/github/nighter/smartspawner/spawner/loot/EntityLootRegistry.java index 45d7f8b0..a4bd4f1e 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/loot/EntityLootRegistry.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/loot/EntityLootRegistry.java @@ -54,6 +54,16 @@ public void loadConfigurations() { continue; } + // Validate entity type exists in current version + EntityType entityType; + try { + entityType = EntityType.valueOf(entityName.toUpperCase()); + } catch (IllegalArgumentException e) { + plugin.getLogger().warning("Entity type '" + entityName + "' is not available in server version " + + plugin.getServer().getBukkitVersion() + " - skipping"); + continue; + } + ConfigurationSection entitySection = lootConfig.getConfigurationSection(entityName); if (entitySection == null) continue; diff --git a/core/src/main/java/github/nighter/smartspawner/utils/DynamicMaterialDetector.java b/core/src/main/java/github/nighter/smartspawner/utils/DynamicMaterialDetector.java deleted file mode 100644 index b0b80d79..00000000 --- a/core/src/main/java/github/nighter/smartspawner/utils/DynamicMaterialDetector.java +++ /dev/null @@ -1,51 +0,0 @@ -package github.nighter.smartspawner.utils; - -import org.bukkit.Material; - -import java.util.Arrays; -import java.util.Set; -import java.util.stream.Collectors; - -/** - * Dynamically detects valid materials for spawner GUI based on current Minecraft version. - * Supports automatic detection of: - * - Player heads - * - Mob skulls/heads - * - Spawner blocks - */ -public class DynamicMaterialDetector { - private static final Set SPAWNER_INFO_MATERIALS; - - static { - // Auto-detect all head/skull materials from current version - SPAWNER_INFO_MATERIALS = Arrays.stream(Material.values()) - .filter(material -> { - String name = material.name(); - return name.equals("PLAYER_HEAD") || - name.equals("SPAWNER") || - name.endsWith("_HEAD") || - name.endsWith("_SKULL"); - }) - .filter(Material::isItem) - .collect(Collectors.toUnmodifiableSet()); - } - - /** - * Get all valid spawner info materials for the current Minecraft version - * - * @return Unmodifiable set of spawner info materials - */ - public static Set getSpawnerInfoMaterials() { - return SPAWNER_INFO_MATERIALS; - } - - /** - * Check if a material is a valid spawner info material - * - * @param material Material to check - * @return true if the material is a valid spawner info material - */ - public static boolean isValidSpawnerInfoMaterial(Material material) { - return SPAWNER_INFO_MATERIALS.contains(material); - } -}