From c62daa4c9bb7de7299316269e105f42003a032eb Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 04:11:53 +0000 Subject: [PATCH 1/4] Initial plan From 749d0abd27a8febad9a3d8960a3e42de1bf43670 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 04:15:48 +0000 Subject: [PATCH 2/4] Fix Folia async chunk retrieval error in ghost spawner check Co-authored-by: ptthanh02 <73684260+ptthanh02@users.noreply.github.com> --- .../spawner/properties/SpawnerManager.java | 7 +++- .../spawner/utils/SpawnerFileHandler.java | 37 ++++++++----------- 2 files changed, 21 insertions(+), 23 deletions(-) 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..ab0b2f98 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 @@ -153,33 +153,28 @@ public void flushChanges() { } /** - * Schedules an async check for ghost spawners without blocking the main thread. + * Schedules a check for ghost spawners on region threads. * This runs after data is saved to ensure spawner data integrity. + * Each spawner is checked on its own region thread for Folia compatibility. */ 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); - } + List allSpawners = plugin.getSpawnerManager().getAllSpawners(); + + // Check each spawner on its location thread for Folia compatibility + for (SpawnerData spawner : allSpawners) { + Location loc = spawner.getSpawnerLocation(); + if (loc != null && loc.getWorld() != null) { + // Schedule check on the region thread for this location + Scheduler.runLocationTask(loc, () -> { + if (plugin.getSpawnerManager().isGhostSpawner(spawner)) { + plugin.debug("Found ghost spawner " + spawner.getSpawnerId() + " during scheduled check"); + plugin.getSpawnerManager().removeGhostSpawner(spawner.getSpawnerId()); + } + }); } - }); + } } private boolean saveSpawnerBatch(Map spawners) { From e8478da7031ec5643175ffbd9a753ada7014db68 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 04:17:52 +0000 Subject: [PATCH 3/4] Fix ClearGhostSpawnersSubCommand to use region threads for Folia Co-authored-by: ptthanh02 <73684260+ptthanh02@users.noreply.github.com> --- .../clear/ClearGhostSpawnersSubCommand.java | 55 ++++++++++--------- 1 file changed, 29 insertions(+), 26 deletions(-) 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; } From 5287d92adec7699a257cf4b71abb2f35eccc3550 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 20 Oct 2025 04:48:11 +0000 Subject: [PATCH 4/4] Remove automatic ghost spawner check from save operation Co-authored-by: ptthanh02 <73684260+ptthanh02@users.noreply.github.com> --- .../spawner/utils/SpawnerFileHandler.java | 28 +------------------ 1 file changed, 1 insertion(+), 27 deletions(-) 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 ab0b2f98..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,31 +151,6 @@ public void flushChanges() { }); } - /** - * Schedules a check for ghost spawners on region threads. - * This runs after data is saved to ensure spawner data integrity. - * Each spawner is checked on its own region thread for Folia compatibility. - */ - private void scheduleGhostSpawnerCheck() { - plugin.debug("Scheduling ghost spawner check after save"); - - List allSpawners = plugin.getSpawnerManager().getAllSpawners(); - - // Check each spawner on its location thread for Folia compatibility - for (SpawnerData spawner : allSpawners) { - Location loc = spawner.getSpawnerLocation(); - if (loc != null && loc.getWorld() != null) { - // Schedule check on the region thread for this location - Scheduler.runLocationTask(loc, () -> { - if (plugin.getSpawnerManager().isGhostSpawner(spawner)) { - plugin.debug("Found ghost spawner " + spawner.getSpawnerId() + " during scheduled check"); - plugin.getSpawnerManager().removeGhostSpawner(spawner.getSpawnerId()); - } - }); - } - } - } - private boolean saveSpawnerBatch(Map spawners) { if (spawners.isEmpty()) return true;