package emu.nebula.command; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.stream.Collectors; import org.reflections.Reflections; import emu.nebula.Nebula; import emu.nebula.game.player.Player; import it.unimi.dsi.fastutil.objects.Object2ObjectMap; import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; import lombok.Getter; @Getter public class CommandManager { private Object2ObjectMap labels; private Object2ObjectMap commands; public CommandManager() { this.labels = new Object2ObjectOpenHashMap<>(); this.commands = new Object2ObjectOpenHashMap<>(); // Scan for commands var commandClasses = new Reflections(CommandManager.class.getPackageName()).getTypesAnnotatedWith(Command.class); for (var cls : commandClasses) { if (!CommandHandler.class.isAssignableFrom(cls)) { continue; } try { CommandHandler handler = (CommandHandler) cls.getDeclaredConstructor().newInstance(); this.registerCommand(handler); } catch (Exception e) { e.printStackTrace(); } } } /** * Adds a command that players and server console users can use. Command handlers must have the proper command annotation attached to them. */ public CommandManager registerCommand(CommandHandler handler) { Command command = handler.getClass().getAnnotation(Command.class); if (command == null) return this; this.getLabels().put(command.label(), handler); this.getCommands().put(command.label(), handler); for (String alias : command.aliases()) { this.getCommands().put(alias, handler); } return this; } /** * Removes a command from use. * @param label The command name * @return */ public CommandManager unregisterCommand(String label) { CommandHandler handler = this.getLabels().get(label); if (handler == null) return this; Command command = handler.getClass().getAnnotation(Command.class); if (command == null) { return this; } this.getLabels().remove(command.label()); this.getCommands().remove(command.label()); for (String alias : command.aliases()) { this.getCommands().remove(alias); } return this; } /** * Checks if the sender has permission to use this command. Will always return true if the sender is the server console. * @param sender The sender of the command. * @param command * @return true if the sender has permission to use this command */ public boolean checkPermission(Player sender, Command command) { if (sender == null || command.permission().isEmpty()) { return true; } return sender.getAccount().hasPermission(command.permission()); } /** * Checks if the sender has permission to use this command on other players. Will always return true if the sender is the server console. * @param sender The sender of the command. * @param command * @return true if the sender has permission to use this command */ private boolean checkTargetPermission(Player sender, Command command) { if (sender == null || command.permission().isEmpty()) { return true; } return sender.getAccount().hasPermission("target." + command.permission()); } public CommandResult invoke(Player sender, String message) { // Parse message into arguments List args = Arrays.stream(message.split(" ")).collect(Collectors.toCollection(ArrayList::new)); // Get command label String label = args.remove(0).toLowerCase(); // Filter out command prefixes if (label.startsWith("/") || label.startsWith("!")) { label = label.substring(1); } // Get command handler CommandHandler handler = this.commands.get(label); // Create result object var result = CommandResult.builder(); // Execute command if (handler != null) { // Get command annotation data Command command = handler.getData(); // Check if sender has permission to run the command. if (sender != null && !this.checkPermission(sender, command)) { // We have a double null check here just in case result.message("Error - You do not have permission to use this command"); return result.build(); } // Build command arguments CommandArgs cmdArgs = new CommandArgs(sender, args); // Check targeted permission if (sender != cmdArgs.getTarget() && !this.checkTargetPermission(sender, command)) { result.message("Error - You do not have permission to use this command on another player"); return result.build(); } // Make sure our command has a target if (command.requireTarget() && cmdArgs.getTarget() == null) { result.message("Error - Targeted player not found or offline"); return result.build(); } // Log if (sender != null && Nebula.getConfig().getLogOptions().commands) { Nebula.getLogger().info("[UID: " + sender.getUid() + "] " + sender.getName() + " used command: " + message); } // Run command String commandMessage = handler.execute(cmdArgs); // Parse out last newline if (commandMessage.endsWith("\n")) { commandMessage = commandMessage.substring(0, commandMessage.length() - 1); } // Set result data result.command(handler); result.message(commandMessage); } else { result.message("Invalid Command!"); } return result.build(); } }