diff --git a/core/src/main/java/github/nighter/smartspawner/commands/clear/ClearGhostSpawnersSubCommand.java b/core/src/main/java/github/nighter/smartspawner/commands/clear/ClearGhostSpawnersSubCommand.java index e74af3cc..2bfa0fa3 100644 --- a/core/src/main/java/github/nighter/smartspawner/commands/clear/ClearGhostSpawnersSubCommand.java +++ b/core/src/main/java/github/nighter/smartspawner/commands/clear/ClearGhostSpawnersSubCommand.java @@ -11,6 +11,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.atomic.AtomicInteger; @NullMarked public class ClearGhostSpawnersSubCommand extends BaseSubCommand { @@ -41,34 +42,36 @@ public int execute(CommandContext context) { // Notify that the check is starting plugin.getMessageService().sendMessage(sender, "command_ghost_spawner_check_start"); - // Run the check asynchronously - Scheduler.runTaskAsync(() -> { - List ghostSpawnerIds = new ArrayList<>(); - List allSpawners = plugin.getSpawnerManager().getAllSpawners(); - - // Check each spawner - for (SpawnerData spawner : allSpawners) { - if (plugin.getSpawnerManager().isGhostSpawner(spawner)) { - ghostSpawnerIds.add(spawner.getSpawnerId()); - } + // Get all spawners first + List allSpawners = plugin.getSpawnerManager().getAllSpawners(); + + // Track how many spawners are being checked using thread-safe counter + final AtomicInteger removedCount = new AtomicInteger(0); + final int totalSpawners = allSpawners.size(); + + // Check each spawner on its location thread for Folia compatibility + for (SpawnerData spawner : allSpawners) { + org.bukkit.Location loc = spawner.getSpawnerLocation(); + if (loc != null && loc.getWorld() != null) { + Scheduler.runLocationTask(loc, () -> { + if (plugin.getSpawnerManager().isGhostSpawner(spawner)) { + plugin.getSpawnerManager().removeGhostSpawner(spawner.getSpawnerId()); + removedCount.incrementAndGet(); + } + }); } - - // Remove all ghost spawners found - int removedCount = ghostSpawnerIds.size(); - for (String spawnerId : ghostSpawnerIds) { - plugin.getSpawnerManager().removeGhostSpawner(spawnerId); + } + + // Schedule a delayed message to report results (give time for checks to complete) + Scheduler.runTaskLater(() -> { + int count = removedCount.get(); + if (count > 0) { + plugin.getMessageService().sendMessage(sender, "command_ghost_spawner_cleared", + java.util.Map.of("count", String.valueOf(count))); + } else { + plugin.getMessageService().sendMessage(sender, "command_ghost_spawner_none_found"); } - - // Send result message back on main thread - Scheduler.runTask(() -> { - if (removedCount > 0) { - plugin.getMessageService().sendMessage(sender, "command_ghost_spawner_cleared", - java.util.Map.of("count", String.valueOf(removedCount))); - } else { - plugin.getMessageService().sendMessage(sender, "command_ghost_spawner_none_found"); - } - }); - }); + }, 100L); // Wait 5 seconds (100 ticks) for checks to complete return 1; } diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerManager.java b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerManager.java index ea88089b..03d452df 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerManager.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/properties/SpawnerManager.java @@ -172,11 +172,14 @@ public boolean isGhostSpawner(SpawnerData spawner) { Location loc = spawner.getSpawnerLocation(); if (loc == null || loc.getWorld() == null) return true; - // Only check loaded chunks - if (!loc.getChunk().isLoaded()) { + // Only check loaded chunks - use thread-safe method for Folia compatibility + int chunkX = loc.getBlockX() >> 4; + int chunkZ = loc.getBlockZ() >> 4; + if (!loc.getWorld().isChunkLoaded(chunkX, chunkZ)) { return false; // Can't confirm, assume valid } + // Note: This method should only be called from region threads for Folia compatibility return loc.getBlock().getType() != Material.SPAWNER; } diff --git a/core/src/main/java/github/nighter/smartspawner/spawner/utils/SpawnerFileHandler.java b/core/src/main/java/github/nighter/smartspawner/spawner/utils/SpawnerFileHandler.java index 5af564a1..8be3a7ea 100644 --- a/core/src/main/java/github/nighter/smartspawner/spawner/utils/SpawnerFileHandler.java +++ b/core/src/main/java/github/nighter/smartspawner/spawner/utils/SpawnerFileHandler.java @@ -134,8 +134,7 @@ public void flushChanges() { } } - // After saving, schedule async ghost spawner check - scheduleGhostSpawnerCheck(); + // Automatic ghost spawner check removed - use /ss clear ghost_spawners command instead } catch (Exception e) { plugin.getLogger().severe("Error during flush: " + e.getMessage()); e.printStackTrace(); @@ -152,36 +151,6 @@ public void flushChanges() { }); } - /** - * Schedules an async check for ghost spawners without blocking the main thread. - * This runs after data is saved to ensure spawner data integrity. - */ - private void scheduleGhostSpawnerCheck() { - plugin.debug("Scheduling ghost spawner check after save"); - - // Run async check to avoid blocking - Scheduler.runTaskAsync(() -> { - List ghostSpawnerIds = new ArrayList<>(); - List allSpawners = plugin.getSpawnerManager().getAllSpawners(); - - // Check each spawner asynchronously - for (SpawnerData spawner : allSpawners) { - if (plugin.getSpawnerManager().isGhostSpawner(spawner)) { - ghostSpawnerIds.add(spawner.getSpawnerId()); - } - } - - // If ghost spawners found, remove them - if (!ghostSpawnerIds.isEmpty()) { - plugin.debug("Found " + ghostSpawnerIds.size() + " ghost spawners during scheduled check"); - - for (String spawnerId : ghostSpawnerIds) { - plugin.getSpawnerManager().removeGhostSpawner(spawnerId); - } - } - }); - } - private boolean saveSpawnerBatch(Map spawners) { if (spawners.isEmpty()) return true;