Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;

@NullMarked
public class ClearGhostSpawnersSubCommand extends BaseSubCommand {
Expand Down Expand Up @@ -41,34 +42,36 @@ public int execute(CommandContext<CommandSourceStack> context) {
// Notify that the check is starting
plugin.getMessageService().sendMessage(sender, "command_ghost_spawner_check_start");

// Run the check asynchronously
Scheduler.runTaskAsync(() -> {
List<String> ghostSpawnerIds = new ArrayList<>();
List<SpawnerData> 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<SpawnerData> 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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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<String> ghostSpawnerIds = new ArrayList<>();
List<SpawnerData> 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<String, SpawnerData> spawners) {
if (spawners.isEmpty()) return true;

Expand Down