add configuration commands

- commands can dynamically enable/disable modules
- commands can reload modules and plugin-wide config
- commands can show info about modules
- also added description methods for modules to support the commands
This commit is contained in:
Piotr W. committed 2026-04-23 01:23:46 +01:00
1 parent c9160b59fa
commit 21840488af
6 files changed
+207 -4

No files matched your search

@@ -7,13 +7,13 @@
package dev.piotr_weychan.szlaban;
import com.comphenix.protocol.ProtocolLibrary;
import com.comphenix.protocol.ProtocolManager;
import dev.piotr_weychan.szlaban.behaviour.Capability;
import dev.piotr_weychan.szlaban.command.ConfigurationCommand;
import dev.piotr_weychan.szlaban.firewall.FirewallModule;
import dev.piotr_weychan.szlaban.module.Module;
import dev.piotr_weychan.szlaban.module.ModuleManager;
import io.papermc.paper.plugin.lifecycle.event.types.LifecycleEvents;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.plugin.java.JavaPlugin;
@@ -45,6 +45,17 @@ public final class Szlaban extends JavaPlugin {
);
}
@SuppressWarnings("UnstableApiUsage")
private void registerCommands() {
// register all commands here
ConfigurationCommand cfgCommand = new ConfigurationCommand(this, moduleManager);
this.getLifecycleManager().registerEventHandler(
LifecycleEvents.COMMANDS,
commands -> commands.registrar().register(cfgCommand.szlabanConfig("szlaban"))
);
}
/**
* (Re)load modules based on whether they are enabled in the configuration file.
*/
@@ -121,7 +132,7 @@ public final class Szlaban extends JavaPlugin {
registerModules();
loadModules();
registerCommands();
}
@@ -0,0 +1,161 @@
package dev.piotr_weychan.szlaban.command;
import com.mojang.brigadier.Command;
import com.mojang.brigadier.arguments.StringArgumentType;
import com.mojang.brigadier.context.CommandContext;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import com.mojang.brigadier.exceptions.DynamicCommandExceptionType;
import dev.piotr_weychan.szlaban.module.Module;
import dev.piotr_weychan.szlaban.module.ModuleManager;
import com.mojang.brigadier.tree.LiteralCommandNode;
import io.papermc.paper.command.brigadier.CommandSourceStack;
import io.papermc.paper.command.brigadier.Commands;
import io.papermc.paper.command.brigadier.MessageComponentSerializer;
import net.kyori.adventure.text.Component;
import net.kyori.adventure.text.event.HoverEvent;
import net.kyori.adventure.text.format.NamedTextColor;
import org.bukkit.command.CommandSender;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.List;
@SuppressWarnings("UnstableApiUsage")
public class ConfigurationCommand {
private final ModuleManager moduleManager;
private final JavaPlugin plugin;
public ConfigurationCommand(JavaPlugin plugin, ModuleManager moduleManager) {
this.plugin = plugin;
this.moduleManager = moduleManager;
}
private static final DynamicCommandExceptionType ERROR_INVALID_MODULE = new DynamicCommandExceptionType(module ->
MessageComponentSerializer.message().serialize(
Component.text(module + " is not a valid module.")
)
);
private Module findModule(CommandContext<CommandSourceStack> context) throws CommandSyntaxException {
String moduleName = StringArgumentType.getString(context, "module");
@Nullable Module mod = moduleManager.getModule(moduleName);
if (mod == null) throw ERROR_INVALID_MODULE.create(moduleName);
return mod;
}
public final LiteralCommandNode<CommandSourceStack> szlabanConfig(@NotNull final String name) {
return Commands.literal(name)
.requires(ctx -> ctx.getSender().hasPermission("szlaban.config"))
.then(Commands.literal("module")
.then(Commands.argument("module", StringArgumentType.string())
.suggests((ctx, builder) -> {
for (String module : moduleManager.getModuleIds()) {
builder.suggest(module);
}
return builder.buildFuture();
})
.executes(ctx -> {
Module mod = findModule(ctx);
ctx.getSource().getSender().sendRichMessage(
"<bold><gold>" + mod.getName() + "</bold>\n" +
"<yellow>" + mod.getDescription() + "\n" +
"Status: " + (mod.isEnabled() ? "<green>enabled" : "<red>disabled")
);
return Command.SINGLE_SUCCESS;
})
.then(Commands.literal("enable")
.executes(ctx -> {
Module mod = findModule(ctx);
CommandSender sender = ctx.getSource().getSender();
if (mod.isEnabled()) {
sender.sendRichMessage("<yellow>" + mod.getName() + "<gold> is already enabled!");
} else {
mod.enable();
sender.sendRichMessage("<yellow>" + mod.getName() + "<gold> has been <green>enabled</green>!");
}
return Command.SINGLE_SUCCESS;
})
)
.then(Commands.literal("disable")
.executes(ctx -> {
Module mod = findModule(ctx);
CommandSender sender = ctx.getSource().getSender();
if (!mod.isEnabled()) {
sender.sendRichMessage("<yellow>" + mod.getName() + "<gold> is already disabled!");
} else {
mod.disable();
sender.sendRichMessage("<yellow>" + mod.getName() + "<gold> has been <red>disabled</red>!");
}
return Command.SINGLE_SUCCESS;
})
)
.then(Commands.literal("reload")
.executes(ctx -> {
// only use as input validation
findModule(ctx);
String moduleName = StringArgumentType.getString(ctx, "module");
CommandSender sender = ctx.getSource().getSender();
moduleManager.reloadModule(moduleName);
sender.sendRichMessage("<gold>Module <yellow>" + moduleName + "</yellow> has been reloaded!");
return Command.SINGLE_SUCCESS;
})
)
)
)
.then(Commands.literal("modules")
.executes(ctx -> {
List<Component> modules = moduleManager.getModuleIds().stream().map(id -> {
Module module = moduleManager.getModule(id);
assert module != null;
return Component.text(module.getName())
.hoverEvent(HoverEvent.showText(Component.text(module.getDescription())))
.color(module.isEnabled() ? NamedTextColor.GREEN : NamedTextColor.RED)
.asComponent();
}).toList();
Component statusMessage = Component.text("The following modules are available:")
.color(NamedTextColor.GOLD)
.appendNewline()
.appendSpace();
for (int i = 0; i < modules.size(); i++) {
statusMessage = statusMessage.append(modules.get(i));
if (i != modules.size() - 1) {
statusMessage = statusMessage.append(
Component.text(", ")
.color(NamedTextColor.GRAY)
);
}
}
ctx.getSource().getSender().sendMessage(statusMessage);
return Command.SINGLE_SUCCESS;
})
)
.then(Commands.literal("reload")
.executes(ctx -> {
plugin.reloadConfig();
moduleManager.getModuleIds().forEach(moduleManager::reloadModule);
ctx.getSource().getSender().sendRichMessage("<gold>Configuration has been reloaded!");
return Command.SINGLE_SUCCESS;
})
)
.build();
}
}
@@ -41,6 +41,18 @@ public final class FirewallModule extends AbstractModule {
return new FilterListFile(file);
}
@Override
@NotNull
public String getName() {
return "Firewall";
}
@Override
@NotNull
public String getDescription() {
return "An advanced configurable firewall system.";
}
/**
* Read the configuration files and build the filter trie
*/
@@ -35,6 +35,24 @@ public abstract class AbstractModule implements Module {
this.behaviourContext = new BehaviourContext(plugin, capabilities);
}
/**
* Get the human-readable name for the module
* @return the module name
*/
@Override
public String getName() {
return this.getClass().getSimpleName();
}
/**
* Get the module's description
* @return the description
*/
@Override
public String getDescription() {
return this.getName();
}
/**
* Called when the module is registered, may be used as an alternative to the constructor.
*/
@@ -8,6 +8,8 @@
package dev.piotr_weychan.szlaban.module;
public interface Module {
String getName();
String getDescription();
void enable();
void disable();
boolean isEnabled();
-1
View File
@@ -2,4 +2,3 @@
# 0.0.0.0/0 # Blocks ALL IPv4 Addresses
# ::/0 # Blocks ALL IPv6 Addresses
# Add custom entries below:
192.168.1.151/32