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 @@ -3,6 +3,7 @@
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.tree.LiteralCommandNode;
import github.nighter.smartspawner.SmartSpawner;
import github.nighter.smartspawner.commands.clear.ClearSubCommand;
import github.nighter.smartspawner.commands.give.GiveSubCommand;
import github.nighter.smartspawner.commands.hologram.HologramSubCommand;
import github.nighter.smartspawner.commands.list.ListSubCommand;
Expand Down Expand Up @@ -35,7 +36,8 @@ public MainCommand(SmartSpawner plugin) {
new GiveSubCommand(plugin),
new ListSubCommand(plugin),
new HologramSubCommand(plugin),
new PricesSubCommand(plugin)
new PricesSubCommand(plugin),
new ClearSubCommand(plugin)
);
}

Expand Down Expand Up @@ -68,11 +70,11 @@ private LiteralCommandNode<CommandSourceStack> buildCommandWithName(String name)

// For players, check the base permission
if (sender instanceof Player player) {
return player.hasPermission("smartspawner.admin") || player.isOp();
return player.hasPermission("smartspawner.command.use") || player.isOp();
}

// Allow other command senders (like command blocks) if they have permission
return sender.hasPermission("smartspawner.admin");
return sender.hasPermission("smartspawner.command.use");
});

// Add all subcommands to the builder
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package github.nighter.smartspawner.commands.clear;

import com.mojang.brigadier.context.CommandContext;
import github.nighter.smartspawner.SmartSpawner;
import github.nighter.smartspawner.commands.BaseSubCommand;
import github.nighter.smartspawner.spawner.properties.SpawnerData;
import github.nighter.smartspawner.Scheduler;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.command.CommandSender;
import org.jspecify.annotations.NullMarked;

import java.util.ArrayList;
import java.util.List;

@NullMarked
public class ClearGhostSpawnersSubCommand extends BaseSubCommand {

public ClearGhostSpawnersSubCommand(SmartSpawner plugin) {
super(plugin);
}

@Override
public String getName() {
return "ghost_spawners";
}

@Override
public String getPermission() {
return "smartspawner.command.clear";
}

@Override
public String getDescription() {
return "Check and remove all ghost spawners asynchronously";
}

@Override
public int execute(CommandContext<CommandSourceStack> context) {
CommandSender sender = context.getSource().getSender();

// 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());
}
}

// Remove all ghost spawners found
int removedCount = ghostSpawnerIds.size();
for (String spawnerId : ghostSpawnerIds) {
plugin.getSpawnerManager().removeGhostSpawner(spawnerId);
}

// 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");
}
});
});

return 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package github.nighter.smartspawner.commands.clear;

import com.mojang.brigadier.context.CommandContext;
import github.nighter.smartspawner.SmartSpawner;
import github.nighter.smartspawner.commands.BaseSubCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import org.bukkit.Bukkit;
import org.bukkit.command.CommandSender;
import org.jspecify.annotations.NullMarked;

@NullMarked
public class ClearHologramsSubCommand extends BaseSubCommand {

public ClearHologramsSubCommand(SmartSpawner plugin) {
super(plugin);
}

@Override
public String getName() {
return "holograms";
}

@Override
public String getPermission() {
return "smartspawner.command.clear";
}

@Override
public String getDescription() {
return "Clear all text display holograms";
}

@Override
public int execute(CommandContext<CommandSourceStack> context) {
CommandSender sender = context.getSource().getSender();

try {
// Execute the Minecraft command to kill all text_display entities
Bukkit.dispatchCommand(Bukkit.getConsoleSender(), "minecraft:kill @e[type=text_display]");

// Send success message to player
plugin.getMessageService().sendMessage(sender, "command_hologram_cleared");

return 1;
} catch (Exception e) {
plugin.getLogger().severe("Error clearing holograms: " + e.getMessage());
plugin.getMessageService().sendMessage(sender, "command_hologram_clear_error");
return 0;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package github.nighter.smartspawner.commands.clear;

import com.mojang.brigadier.builder.LiteralArgumentBuilder;
import com.mojang.brigadier.context.CommandContext;
import github.nighter.smartspawner.SmartSpawner;
import github.nighter.smartspawner.commands.BaseSubCommand;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import org.bukkit.command.CommandSender;
import org.jspecify.annotations.NullMarked;

@NullMarked
public class ClearSubCommand extends BaseSubCommand {
private final ClearHologramsSubCommand clearHologramsSubCommand;
private final ClearGhostSpawnersSubCommand clearGhostSpawnersSubCommand;

public ClearSubCommand(SmartSpawner plugin) {
super(plugin);
this.clearHologramsSubCommand = new ClearHologramsSubCommand(plugin);
this.clearGhostSpawnersSubCommand = new ClearGhostSpawnersSubCommand(plugin);
}

@Override
public String getName() {
return "clear";
}

@Override
public String getPermission() {
return "smartspawner.command.clear";
}

@Override
public String getDescription() {
return "Clear holograms or ghost spawners";
}

@Override
public LiteralArgumentBuilder<CommandSourceStack> build() {
LiteralArgumentBuilder<CommandSourceStack> builder = Commands.literal(getName());
builder.requires(source -> hasPermission(source.getSender()));

// Add subcommands
builder.then(clearHologramsSubCommand.build());
builder.then(clearGhostSpawnersSubCommand.build());

return builder;
}

@Override
public int execute(CommandContext<CommandSourceStack> context) {
CommandSender sender = context.getSource().getSender();

// When no subcommand is provided, show usage
plugin.getMessageService().sendMessage(sender, "clear_command_usage");
return 0;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public String getName() {

@Override
public String getPermission() {
return "smartspawner.give";
return "smartspawner.command.give";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public String getName() {

@Override
public String getPermission() {
return "smartspawner.hologram";
return "smartspawner.command.hologram";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public String getName() {

@Override
public String getPermission() {
return "smartspawner.hologram";
return "smartspawner.command.hologram";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public String getName() {

@Override
public String getPermission() {
return "smartspawner.list";
return "smartspawner.command.list";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public String getName() {

@Override
public String getPermission() {
return "smartspawner.prices";
return "smartspawner.command.prices";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public String getName() {

@Override
public String getPermission() {
return "smartspawner.reload";
return "smartspawner.command.reload";
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,12 @@ public SpawnerRangeChecker(SmartSpawner plugin) {
this.spawnerLootGenerator = plugin.getSpawnerLootGenerator();
this.spawnerTasks = new ConcurrentHashMap<>();
this.executor = Executors.newSingleThreadExecutor();
this.checkGhostSpawnersOnApproach = plugin.getConfig().getBoolean("ghost_spawners.remove_on_approach", false);
this.checkGhostSpawnersOnApproach = false; // Disabled by default, use command to clear
initializeRangeCheckTask();
}

public void reload() {
this.checkGhostSpawnersOnApproach = plugin.getConfig().getBoolean("ghost_spawners.remove_on_approach", false);
this.checkGhostSpawnersOnApproach = false; // Always disabled
}

private void initializeRangeCheckTask() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,8 @@ private void setupSpawnerDataFile() {
}

private void startSaveTask() {
long intervalTicks = plugin.getTimeFromConfig("data_saving.interval", "5m");
// Hardcoded 5-minute interval (5 * 60 * 20 = 6000 ticks)
long intervalTicks = 6000L;

if (saveTask != null) {
saveTask.cancel();
Expand Down
23 changes: 0 additions & 23 deletions core/src/main/resources/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -210,29 +210,6 @@ particle:
spawner_activate: true # Show effects when spawner activates
spawner_generate_loot: true # Show effects when items are generated

#---------------------------------------------------
# Data Management
#---------------------------------------------------
# Controls how and when spawner data is saved
data_saving:
# Periodic auto-save interval
interval: 5m # Time between saves (see time format guide above)

# Save spawner data on server shutdown
save_on_shutdown: true

# Ghost spawners are error spawners that exist in the world but no longer have a physical spawner block
# This can happen due to server crashes, chunk corruption, or other issues
# These settings control how ghost spawners are handled and removed
ghost_spawners:
# Remove ghost spawners when server starts up
# Only checks spawners in loaded chunks
remove_on_startup: true

# Remove ghost spawners when players approach them
# This checks when players enter spawner range
remove_on_approach: false

#---------------------------------------------------
# Spawner Action Logging
#---------------------------------------------------
Expand Down
16 changes: 16 additions & 0 deletions core/src/main/resources/language/en_US/messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,22 @@ command_hologram_clear_error:
message: "&#ff5252ꜰᴀɪʟᴇᴅ ᴛᴏ ᴄʟᴇᴀʀ ʜᴏʟᴏɢʀᴀᴍꜱ"
sound: block.note_block.pling

# Clear command messages
clear_command_usage:
message: "&#f8f8ffᴜꜱᴀɢᴇ: &#3498db/ss clear <holograms|ghost_spawners>"

command_ghost_spawner_check_start:
message: "&#f8f8ffᴄʜᴇᴄᴋɪɴɢ ꜰᴏʀ ɢʜᴏꜱᴛ ꜱᴘᴀᴡɴᴇʀꜱ..."
sound: block.note_block.hat

command_ghost_spawner_cleared:
message: "&#37eb9aꜱᴜᴄᴄᴇꜱꜱꜰᴜʟʟʏ &#f8f8ffʀᴇᴍᴏᴠᴇᴅ &#37eb9a%count% &#f8f8ffɢʜᴏꜱᴛ ꜱᴘᴀᴡɴᴇʀ(ꜱ)"
sound: entity.experience_orb.pickup

command_ghost_spawner_none_found:
message: "&#37eb9aɴᴏ ɢʜᴏꜱᴛ ꜱᴘᴀᴡɴᴇʀꜱ ꜰᴏᴜɴᴅ"
sound: entity.experience_orb.pickup

# Prices command messagess
prices_not_available:
message: "&#ff5252ꜱᴇʟʟ ɪɴᴛᴇɢʀᴀᴛɪᴏɴ ɪꜱ ɴᴏᴛ ᴀᴠᴀɪʟᴀʙʟᴇ. ᴘʟᴇᴀꜱᴇ ᴄᴏɴꜰɪɢᴜʀᴇ ᴇᴄᴏɴᴏᴍʏ ꜱᴇᴛᴛɪɴɢꜱ."
Expand Down
Loading