diff --git a/src/main/java/emu/grasscutter/command/Command.java b/src/main/java/emu/grasscutter/command/Command.java index 4af98e788..095e64cdb 100644 --- a/src/main/java/emu/grasscutter/command/Command.java +++ b/src/main/java/emu/grasscutter/command/Command.java @@ -7,14 +7,12 @@ import java.lang.annotation.RetentionPolicy; public @interface Command { String label() default ""; - String usage() default "commands.generic.no_usage_specified"; - - String description() default "commands.generic.no_description_specified"; - String[] aliases() default {}; + String[] usage() default {""}; + String permission() default ""; - + String permissionTargeted() default ""; public enum TargetRequirement { diff --git a/src/main/java/emu/grasscutter/command/CommandHandler.java b/src/main/java/emu/grasscutter/command/CommandHandler.java index 4803b154f..c9a690040 100644 --- a/src/main/java/emu/grasscutter/command/CommandHandler.java +++ b/src/main/java/emu/grasscutter/command/CommandHandler.java @@ -2,12 +2,11 @@ package emu.grasscutter.command; import emu.grasscutter.Grasscutter; import emu.grasscutter.game.player.Player; -import emu.grasscutter.server.event.game.CommandResponseEvent; import emu.grasscutter.server.event.game.ReceiveCommandFeedbackEvent; -import emu.grasscutter.server.event.types.ServerEvent; import static emu.grasscutter.utils.Language.translate; import java.util.List; +import java.util.StringJoiner; public interface CommandHandler { @@ -37,6 +36,42 @@ public interface CommandHandler { sendMessage(player, translate(player, messageKey, args)); } + default String getUsageString(Player player, String... args) { + Command annotation = this.getClass().getAnnotation(Command.class); + String usage_prefix = translate(player, "commands.execution.usage_prefix"); + String command = annotation.label(); + for (String alias : annotation.aliases()) { + if (alias.length() < command.length()) + command = alias; + } + String target = switch (annotation.targetRequirement()) { + case NONE -> ""; + case OFFLINE -> "@ "; // TODO: make translation keys for offline and online players + case ONLINE -> (player == null) ? "@ " : "[@] "; // TODO: make translation keys for offline and online players + case PLAYER -> (player == null) ? "@ " : "[@] "; + }; + String[] usages = annotation.usage(); + StringJoiner joiner = new StringJoiner("\n\t"); + for (String usage : usages) + joiner.add(usage_prefix + command + " " + target + usage); + return joiner.toString(); + } + + default void sendUsageMessage(Player player, String... args) { + sendMessage(player, getUsageString(player, args)); + } + + default String getLabel() { + return this.getClass().getAnnotation(Command.class).label(); + } + + default String getDescriptionString(Player player) { + Command annotation = this.getClass().getAnnotation(Command.class); + String key = "commands.%s.description".formatted(annotation.label()); + // TODO: fallback to "commands.generic.no_description_specified" + return translate(player, key); + } + /** * Called when a player/console invokes a command. * @param sender The player/console that invoked the command. diff --git a/src/main/java/emu/grasscutter/command/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java index da37f0b37..5bfeab7d7 100644 --- a/src/main/java/emu/grasscutter/command/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -8,9 +8,9 @@ import java.util.*; @SuppressWarnings({"UnusedReturnValue", "unused"}) public final class CommandMap { - private final Map commands = new HashMap<>(); - private final Map aliases = new HashMap<>(); - private final Map annotations = new HashMap<>(); + private final Map commands = new LinkedHashMap<>(); + private final Map aliases = new LinkedHashMap<>(); + private final Map annotations = new LinkedHashMap<>(); private final Map targetPlayerIds = new HashMap<>(); private static final String consoleId = "console"; diff --git a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java index 843b78913..15855c4c9 100644 --- a/src/main/java/emu/grasscutter/command/commands/AccountCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/AccountCommand.java @@ -13,18 +13,24 @@ import java.util.List; import static emu.grasscutter.utils.Language.translate; -@Command(label = "account", usage = "account [uid|password] [uid] ", description = "commands.account.description", targetRequirement = Command.TargetRequirement.NONE) +@Command( + label = "account", + usage = { + "create []", // Only with EXPERIMENTAL_RealPassword == false + "delete ", + "create []", // Only with EXPERIMENTAL_RealPassword == true + "resetpass "}, // Only with EXPERIMENTAL_RealPassword == true + targetRequirement = Command.TargetRequirement.NONE) public final class AccountCommand implements CommandHandler { - @Override public void execute(Player sender, Player targetPlayer, List args) { if (sender != null) { - CommandHandler.sendMessage(sender, translate(sender, "commands.generic.console_execute_error")); + CommandHandler.sendTranslatedMessage(sender, "commands.generic.console_execute_error"); return; } if (args.size() < 2) { - CommandHandler.sendMessage(null, translate(sender, "commands.account.command_usage")); + CommandHandler.sendTranslatedMessage(sender, "commands.account.command_usage"); return; } @@ -33,17 +39,16 @@ public final class AccountCommand implements CommandHandler { switch (action) { default: - CommandHandler.sendMessage(null, translate(sender, "commands.account.command_usage")); + CommandHandler.sendTranslatedMessage(sender, "commands.account.command_usage"); return; case "create": int uid = 0; String password = ""; - if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) { - if (args.size() < 3) { - CommandHandler.sendMessage(null, "EXPERIMENTAL_RealPassword requires a password argument"); - CommandHandler.sendMessage(null, "Usage: account create [uid]"); - + if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) { + if(args.size() < 3) { + CommandHandler.sendMessage(sender, "EXPERIMENTAL_RealPassword requires a password argument"); + CommandHandler.sendMessage(sender, "Usage: account create [uid]"); return; } password = args.get(2); @@ -52,10 +57,10 @@ public final class AccountCommand implements CommandHandler { try { uid = Integer.parseInt(args.get(3)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, translate(sender, "commands.account.invalid")); - if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) { - CommandHandler.sendMessage(null, "EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid"); - CommandHandler.sendMessage(null, "Usage: account create [uid]"); + CommandHandler.sendMessage(sender, translate(sender, "commands.account.invalid")); + if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) { + CommandHandler.sendMessage(sender, "EXPERIMENTAL_RealPassword requires argument 2 to be a password, not a uid"); + CommandHandler.sendMessage(sender, "Usage: account create [uid]"); } return; } @@ -65,7 +70,7 @@ public final class AccountCommand implements CommandHandler { try { uid = Integer.parseInt(args.get(2)); } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, translate(sender, "commands.account.invalid")); + CommandHandler.sendMessage(sender, translate(sender, "commands.account.invalid")); return; } } @@ -73,7 +78,7 @@ public final class AccountCommand implements CommandHandler { emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithUid(username, uid); if (account == null) { - CommandHandler.sendMessage(null, translate(sender, "commands.account.exists")); + CommandHandler.sendMessage(sender, translate(sender, "commands.account.exists")); return; } else { if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword == true) { @@ -82,7 +87,7 @@ public final class AccountCommand implements CommandHandler { account.addPermission("*"); account.save(); // Save account to database. - CommandHandler.sendMessage(null, translate(sender, "commands.account.create", Integer.toString(account.getReservedPlayerUid()))); + CommandHandler.sendMessage(sender, translate(sender, "commands.account.create", Integer.toString(account.getReservedPlayerUid()))); } return; case "delete": @@ -90,51 +95,50 @@ public final class AccountCommand implements CommandHandler { Account toDelete = DatabaseHelper.getAccountByName(username); if (toDelete == null) { - CommandHandler.sendMessage(null, translate(sender, "commands.account.no_account")); + CommandHandler.sendMessage(sender, translate(sender, "commands.account.no_account")); return; } - // Get the player for the account. - // If that player is currently online, we kick them before proceeding with the deletion. - Player player = Grasscutter.getGameServer().getPlayerByAccountId(toDelete.getId()); - if (player != null) { - player.getSession().close(); - } + // Make sure player isn't online as we delete their account. + kickAccount(toDelete); // Finally, we do the actual deletion. DatabaseHelper.deleteAccount(toDelete); - CommandHandler.sendMessage(null, translate(sender, "commands.account.delete")); + CommandHandler.sendMessage(sender, translate(sender, "commands.account.delete")); return; case "resetpass": - if (Configuration.ACCOUNT.EXPERIMENTAL_RealPassword != true) { - CommandHandler.sendMessage(null, "resetpass requires EXPERIMENTAL_RealPassword to be true."); + if(Configuration.ACCOUNT.EXPERIMENTAL_RealPassword != true) { + CommandHandler.sendMessage(sender, "resetpass requires EXPERIMENTAL_RealPassword to be true."); return; } - if (args.size() != 3) { - CommandHandler.sendMessage(null, "Invalid Args"); - CommandHandler.sendMessage(null, "Usage: account resetpass "); + if(args.size() != 3) { + CommandHandler.sendMessage(sender, "Invalid Args"); + CommandHandler.sendMessage(sender, "Usage: account resetpass "); return; } Account toUpdate = DatabaseHelper.getAccountByName(username); if (toUpdate == null) { - CommandHandler.sendMessage(null, translate(sender, "commands.account.no_account")); + CommandHandler.sendMessage(sender, translate(sender, "commands.account.no_account")); return; } - // Get the player for the account. - // If that player is currently online, we kick them before proceeding with the deletion. - Player uPlayer = Grasscutter.getGameServer().getPlayerByAccountId(toUpdate.getId()); - if (uPlayer != null) { - uPlayer.getSession().close(); - } + // Make sure player can't stay logged in with old password. + kickAccount(toUpdate); toUpdate.setPassword(BCrypt.withDefaults().hashToString(12, args.get(2).toCharArray())); toUpdate.save(); - CommandHandler.sendMessage(null, "Password Updated."); + CommandHandler.sendMessage(sender, "Password Updated."); return; } } + + private void kickAccount(Account account) { + Player player = Grasscutter.getGameServer().getPlayerByAccountId(account.getId()); + if (player != null) { + player.getSession().close(); + } + } } diff --git a/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java b/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java index 136db705d..90881793b 100644 --- a/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/AnnounceCommand.java @@ -13,10 +13,9 @@ import java.util.Random; import static emu.grasscutter.utils.Language.translate; @Command(label = "announce", - usage = "announce|a <\"tpl\" templateId|\"refresh\"|\"revoke\" templateId|content>", + usage = {"", "refresh", "(tpl|revoke) "}, permission = "server.announce", aliases = {"a"}, - description = "commands.announce.description", targetRequirement = Command.TargetRequirement.NONE) public final class AnnounceCommand implements CommandHandler { diff --git a/src/main/java/emu/grasscutter/command/commands/BanCommand.java b/src/main/java/emu/grasscutter/command/commands/BanCommand.java index 6b4dd9766..6f0b0abec 100644 --- a/src/main/java/emu/grasscutter/command/commands/BanCommand.java +++ b/src/main/java/emu/grasscutter/command/commands/BanCommand.java @@ -10,8 +10,7 @@ import emu.grasscutter.server.game.GameSession; @Command( label = "ban", - usage = "ban <@player> [time] [reason]", - description = "commands.ban.description", + usage = {"[