diff --git a/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java b/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java deleted file mode 100644 index 2e8be354d..000000000 --- a/src/deprecated/java/emu/grasscutter/commands/PlayerCommands.java +++ /dev/null @@ -1,307 +0,0 @@ -package emu.grasscutter.commands; - -import java.lang.reflect.Modifier; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; - -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.MonsterData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.avatar.GenshinAvatar; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.EntityItem; -import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.game.entity.GenshinEntity; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.game.inventory.ItemType; -import emu.grasscutter.game.props.ActionReason; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; -import emu.grasscutter.utils.Position; - -public class PlayerCommands { - private static HashMap list = new HashMap<>(); - - static { - try { - // Look for classes - for (Class cls : PlayerCommands.class.getDeclaredClasses()) { - // Get non abstract classes - if (!Modifier.isAbstract(cls.getModifiers())) { - Command commandAnnotation = cls.getAnnotation(Command.class); - PlayerCommand command = (PlayerCommand) cls.newInstance(); - - if (commandAnnotation != null) { - command.setLevel(commandAnnotation.gmLevel()); - for (String alias : commandAnnotation.aliases()) { - if (alias.length() == 0) { - continue; - } - - String commandName = "!" + alias; - list.put(commandName, command); - commandName = "/" + alias; - list.put(commandName, command); - } - } - - String commandName = "!" + cls.getSimpleName().toLowerCase(); - list.put(commandName, command); - commandName = "/" + cls.getSimpleName().toLowerCase(); - list.put(commandName, command); - } - - } - } catch (Exception e) { - - } - } - - public static void handle(GenshinPlayer player, String msg) { - String[] split = msg.split(" "); - - // End if invalid - if (split.length == 0) { - return; - } - - // - String first = split[0].toLowerCase(); - PlayerCommand c = PlayerCommands.list.get(first); - - if (c != null) { - // Level check - if (player.getGmLevel() < c.getLevel()) { - return; - } - // Execute - int len = Math.min(first.length() + 1, msg.length()); - c.execute(player, msg.substring(len)); - } - } - - public static abstract class PlayerCommand { - // GM level required to use this command - private int level; - protected int getLevel() { return this.level; } - protected void setLevel(int minLevel) { this.level = minLevel; } - - // Main - public abstract void execute(GenshinPlayer player, String raw); - } - - // ================ Commands ================ - - @Command(aliases = {"g", "item", "additem"}, helpText = "/give [item id] [count] - Gives {count} amount of {item id}") - public static class Give extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int itemId = 0, count = 1; - - try { - itemId = Integer.parseInt(split[0]); - } catch (Exception e) { - itemId = 0; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); - } catch (Exception e) { - count = 1; - } - - // Give - ItemData itemData = GenshinData.getItemDataMap().get(itemId); - GenshinItem item; - - if (itemData == null) { - player.dropMessage("Error: Item data not found"); - return; - } - - if (itemData.isEquip()) { - List items = new LinkedList<>(); - for (int i = 0; i < count; i++) { - item = new GenshinItem(itemData); - items.add(item); - } - player.getInventory().addItems(items); - player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); - } else { - item = new GenshinItem(itemData, count); - player.getInventory().addItem(item); - player.sendPacket(new PacketItemAddHintNotify(item, ActionReason.SubfieldDrop)); - } - } - } - - @Command(aliases = {"d"}, helpText = "/drop [item id] [count] - Drops {count} amount of {item id}") - public static class Drop extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int itemId = 0, count = 1; - - try { - itemId = Integer.parseInt(split[0]); - } catch (Exception e) { - itemId = 0; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[1]), Integer.MAX_VALUE), 1); - } catch (Exception e) { - count = 1; - } - - // Give - ItemData itemData = GenshinData.getItemDataMap().get(itemId); - - if (itemData == null) { - player.dropMessage("Error: Item data not found"); - return; - } - - if (itemData.isEquip()) { - float range = (5f + (.1f * count)); - for (int i = 0; i < count; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityItem entity = new EntityItem(player.getWorld(), player, itemData, pos, 1); - player.getWorld().addEntity(entity); - } - } else { - EntityItem entity = new EntityItem(player.getWorld(), player, itemData, player.getPos().clone().addY(3f), count); - player.getWorld().addEntity(entity); - } - } - } - - @Command(helpText = "/spawn [monster id] [count] - Creates {count} amount of {item id}") - public static class Spawn extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int monsterId = 0, count = 1, level = 1; - - try { - monsterId = Integer.parseInt(split[0]); - } catch (Exception e) { - monsterId = 0; - } - - try { - level = Math.max(Math.min(Integer.parseInt(split[1]), 200), 1); - } catch (Exception e) { - level = 1; - } - - try { - count = Math.max(Math.min(Integer.parseInt(split[2]), 1000), 1); - } catch (Exception e) { - count = 1; - } - - // Give - MonsterData monsterData = GenshinData.getMonsterDataMap().get(monsterId); - - if (monsterData == null) { - player.dropMessage("Error: Monster data not found"); - return; - } - - float range = (5f + (.1f * count)); - for (int i = 0; i < count; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityMonster entity = new EntityMonster(player.getWorld(), monsterData, pos, level); - player.getWorld().addEntity(entity); - } - } - } - - @Command(helpText = "/killall") - public static class KillAll extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - List toRemove = new LinkedList<>(); - for (GenshinEntity entity : player.getWorld().getEntities().values()) { - if (entity instanceof EntityMonster) { - toRemove.add(entity); - } - } - toRemove.forEach(e -> player.getWorld().killEntity(e, 0)); - } - } - - @Command(helpText = "/resetconst - Resets all constellations for the currently active character") - public static class ResetConst extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - - if (entity == null) { - return; - } - - GenshinAvatar avatar = entity.getAvatar(); - - avatar.getTalentIdList().clear(); - avatar.setCoreProudSkillLevel(0); - avatar.recalcStats(); - avatar.save(); - - player.dropMessage("Constellations for " + entity.getAvatar().getAvatarData().getName() + " have been reset. Please relogin to see changes."); - } - } - - @Command(helpText = "/godmode - Prevents you from taking damage") - public static class Godmode extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - player.setGodmode(!player.hasGodmode()); - player.dropMessage("Godmode is now " + (player.hasGodmode() ? "ON" : "OFF")); - } - } - - @Command(helpText = "/sethp [hp]") - public static class Sethp extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - String[] split = raw.split(" "); - int hp = 0; - - try { - hp = Math.max(Integer.parseInt(split[0]), 1); - } catch (Exception e) { - hp = 1; - } - - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - - if (entity == null) { - return; - } - - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, hp); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - } - } - - @Command(aliases = {"clearart"}, helpText = "/clearartifacts") - public static class ClearArtifacts extends PlayerCommand { - @Override - public void execute(GenshinPlayer player, String raw) { - List toRemove = new LinkedList<>(); - for (GenshinItem item : player.getInventory().getItems().values()) { - if (item.getItemType() == ItemType.ITEM_RELIQUARY && item.getLevel() == 1 && item.getExp() == 0 && !item.isLocked() && !item.isEquipped()) { - toRemove.add(item); - } - } - - player.getInventory().removeItems(toRemove); - } - } -} diff --git a/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java b/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java deleted file mode 100644 index 2039d5227..000000000 --- a/src/deprecated/java/emu/grasscutter/commands/ServerCommands.java +++ /dev/null @@ -1,171 +0,0 @@ -package emu.grasscutter.commands; - -import java.lang.reflect.Modifier; -import java.util.Arrays; -import java.util.HashMap; -import java.util.LinkedList; -import java.util.List; -import java.util.stream.Collectors; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.utils.Crypto; -import emu.grasscutter.utils.Utils; - -public class ServerCommands { - private static HashMap list = new HashMap<>(); - - static { - try { - // Look for classes - for (Class cls : ServerCommands.class.getDeclaredClasses()) { - // Get non abstract classes - if (!Modifier.isAbstract(cls.getModifiers())) { - String commandName = cls.getSimpleName().toLowerCase(); - list.put(commandName, (ServerCommand) cls.newInstance()); - } - - } - } catch (Exception e) { - - } - } - - public static void handle(String msg) { - String[] split = msg.split(" "); - - // End if invalid - if (split.length == 0) { - return; - } - - // - String first = split[0].toLowerCase(); - ServerCommand c = ServerCommands.list.get(first); - - if (c != null) { - // Execute - int len = Math.min(first.length() + 1, msg.length()); - c.execute(msg.substring(len)); - } - } - - public static abstract class ServerCommand { - public abstract void execute(String raw); - } - - // ================ Commands ================ - - public static class Reload extends ServerCommand { - @Override - public void execute(String raw) { - Grasscutter.getLogger().info("Reloading config."); - Grasscutter.loadConfig(); - Grasscutter.getDispatchServer().loadQueries(); - Grasscutter.getLogger().info("Reload complete."); - } - } - - public static class sendMsg extends ServerCommand { - @Override - public void execute(String raw) { - List split = Arrays.asList(raw.split(" ")); - - if (split.size() < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - String playerID = split.get(0); - String message = split.stream().skip(1).collect(Collectors.joining(" ")); - - - emu.grasscutter.game.Account account = DatabaseHelper.getAccountByPlayerId(Integer.parseInt(playerID)); - if (account != null) { - GenshinPlayer player = Grasscutter.getGameServer().getPlayerById(Integer.parseInt(playerID)); - if(player != null) { - player.dropMessage(message); - Grasscutter.getLogger().info(String.format("Successfully sent message to %s: %s", playerID, message)); - } else { - Grasscutter.getLogger().error("Player not online"); - } - } else { - Grasscutter.getLogger().error(String.format("Player %s does not exist", playerID)); - } - } - } - - public static class Account extends ServerCommand { - @Override - public void execute(String raw) { - String[] split = raw.split(" "); - - if (split.length < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - String command = split[0].toLowerCase(); - String username = split[1]; - - switch (command) { - case "create": - if (split.length < 2) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - int reservedId = 0; - try { - reservedId = Integer.parseInt(split[2]); - } catch (Exception e) { - reservedId = 0; - } - - emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, reservedId); - if (account != null) { - Grasscutter.getLogger().info("Account created" + (reservedId > 0 ? " with an id of " + reservedId : "")); - } else { - Grasscutter.getLogger().error("Account already exists"); - } - break; - case "delete": - boolean success = DatabaseHelper.deleteAccount(username); - - if (success) { - Grasscutter.getLogger().info("Account deleted"); - } - break; - /* - case "setpw": - case "setpass": - case "setpassword": - if (split.length < 3) { - Grasscutter.getLogger().error("Invalid amount of args"); - return; - } - - account = DatabaseHelper.getAccountByName(username); - - if (account == null) { - Grasscutter.getLogger().error("No account found!"); - return; - } - - token = split[2]; - token = PasswordHelper.hashPassword(token); - - account.setPassword(token); - DatabaseHelper.saveAccount(account); - - Grasscutter.getLogger().info("Password set"); - break; - */ - } - } - } -} diff --git a/src/main/java/emu/grasscutter/Grasscutter.java b/src/main/java/emu/grasscutter/Grasscutter.java index c9ccb551c..0e34c120d 100644 --- a/src/main/java/emu/grasscutter/Grasscutter.java +++ b/src/main/java/emu/grasscutter/Grasscutter.java @@ -7,7 +7,7 @@ import java.io.FileWriter; import java.io.InputStreamReader; import java.net.InetSocketAddress; -import emu.grasscutter.commands.CommandMap; +import emu.grasscutter.command.CommandMap; import emu.grasscutter.utils.Utils; import org.reflections.Reflections; import org.slf4j.LoggerFactory; diff --git a/src/main/java/emu/grasscutter/command/Command.java b/src/main/java/emu/grasscutter/command/Command.java new file mode 100644 index 000000000..d8a57e1a8 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/Command.java @@ -0,0 +1,17 @@ +package emu.grasscutter.command; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +public @interface Command { + String label() default ""; + + String usage() default "No usage specified"; + + String description() default "No description specified"; + + String[] aliases() default {}; + + String permission() default ""; +} diff --git a/src/main/java/emu/grasscutter/command/CommandHandler.java b/src/main/java/emu/grasscutter/command/CommandHandler.java new file mode 100644 index 000000000..187dc0b6a --- /dev/null +++ b/src/main/java/emu/grasscutter/command/CommandHandler.java @@ -0,0 +1,25 @@ +package emu.grasscutter.command; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +public interface CommandHandler { + /** + * Send a message to the target. + * + * @param player The player to send the message to, or null for the server console. + * @param message The message to send. + */ + static void sendMessage(GenshinPlayer player, String message) { + if (player == null) { + Grasscutter.getLogger().info(message); + } else { + player.dropMessage(message); + } + } + + default void onCommand(GenshinPlayer sender, List args) { + } +} diff --git a/src/main/java/emu/grasscutter/commands/CommandMap.java b/src/main/java/emu/grasscutter/command/CommandMap.java similarity index 75% rename from src/main/java/emu/grasscutter/commands/CommandMap.java rename to src/main/java/emu/grasscutter/command/CommandMap.java index a802551ce..d61e46833 100644 --- a/src/main/java/emu/grasscutter/commands/CommandMap.java +++ b/src/main/java/emu/grasscutter/command/CommandMap.java @@ -1,4 +1,4 @@ -package emu.grasscutter.commands; +package emu.grasscutter.command; import emu.grasscutter.Grasscutter; import emu.grasscutter.game.Account; @@ -7,75 +7,89 @@ import org.reflections.Reflections; import java.util.*; -@SuppressWarnings("UnusedReturnValue") +@SuppressWarnings({"UnusedReturnValue", "unused"}) public final class CommandMap { + private final Map commands = new HashMap<>(); + private final Map annotations = new HashMap<>(); + public CommandMap() { + this(false); + } + + public CommandMap(boolean scan) { + if (scan) this.scan(); + } + public static CommandMap getInstance() { return Grasscutter.getGameServer().getCommandMap(); } - - private final Map commands = new HashMap<>(); - private final Map annotations = new HashMap<>(); /** * Register a command handler. - * @param label The command label. + * + * @param label The command label. * @param command The command handler. * @return Instance chaining. */ public CommandMap registerCommand(String label, CommandHandler command) { Grasscutter.getLogger().debug("Registered command: " + label); - + // Get command data. Command annotation = command.getClass().getAnnotation(Command.class); this.annotations.put(label, annotation); this.commands.put(label, command); - + // Register aliases. - if(annotation.aliases().length > 0) { + if (annotation.aliases().length > 0) { for (String alias : annotation.aliases()) { this.commands.put(alias, command); this.annotations.put(alias, annotation); } - } return this; + } + return this; } /** * Removes a registered command handler. + * * @param label The command label. * @return Instance chaining. */ public CommandMap unregisterCommand(String label) { Grasscutter.getLogger().debug("Unregistered command: " + label); CommandHandler handler = this.commands.get(label); - if(handler == null) return this; - + if (handler == null) return this; + Command annotation = handler.getClass().getAnnotation(Command.class); this.annotations.remove(label); this.commands.remove(label); - + // Unregister aliases. - if(annotation.aliases().length > 0) { + if (annotation.aliases().length > 0) { for (String alias : annotation.aliases()) { this.commands.remove(alias); this.annotations.remove(alias); } } - + return this; } /** * Returns a list of all registered commands. + * * @return All command handlers as a list. */ public List getHandlersAsList() { return new LinkedList<>(this.commands.values()); } - public HashMap getHandlers() { return new LinkedHashMap<>(this.commands); } + public HashMap getHandlers() { + return new LinkedHashMap<>(this.commands); + } /** * Returns a handler by label/alias. + * * @param label The command label. * @return The command handler. */ @@ -85,58 +99,44 @@ public final class CommandMap { /** * Invoke a command handler with the given arguments. - * @param player The player invoking the command or null for the server console. + * + * @param player The player invoking the command or null for the server console. * @param rawMessage The messaged used to invoke the command. */ public void invoke(GenshinPlayer player, String rawMessage) { rawMessage = rawMessage.trim(); - if(rawMessage.length() == 0) { + if (rawMessage.length() == 0) { CommandHandler.sendMessage(player, "No command specified."); } - + // Remove prefix if present. - if(!Character.isLetter(rawMessage.charAt(0))) + if (!Character.isLetter(rawMessage.charAt(0))) rawMessage = rawMessage.substring(1); - + // Parse message. String[] split = rawMessage.split(" "); List args = new LinkedList<>(Arrays.asList(split)); String label = args.remove(0); - + // Get command handler. CommandHandler handler = this.commands.get(label); - if(handler == null) { - CommandHandler.sendMessage(player, "Unknown command: " + label); return; + if (handler == null) { + CommandHandler.sendMessage(player, "Unknown command: " + label); + return; } - + // Check for permission. - if(player != null) { + if (player != null) { String permissionNode = this.annotations.get(label).permission(); Account account = player.getAccount(); - if(permissionNode != "" && !account.hasPermission(permissionNode)) { - CommandHandler.sendMessage(player, "You do not have permission to run this command."); return; + if (!Objects.equals(permissionNode, "") && !account.hasPermission(permissionNode)) { + CommandHandler.sendMessage(player, "You do not have permission to run this command."); + return; } } - - // Execution power check. - Command.Execution executionPower = this.annotations.get(label).execution(); - if(player == null && executionPower == Command.Execution.PLAYER) { - CommandHandler.sendMessage(null, "Run this command in-game."); return; - } else if (player != null && executionPower == Command.Execution.CONSOLE) { - CommandHandler.sendMessage(player, "This command can only be run from the console."); return; - } - + // Invoke execute method for handler. - if(player == null) handler.execute(args); - else handler.execute(player, args); - } - - public CommandMap() { - this(false); - } - - public CommandMap(boolean scan) { - if(scan) this.scan(); + handler.onCommand(player, args); } /** diff --git a/src/main/java/emu/grasscutter/command/commands/Account.java b/src/main/java/emu/grasscutter/command/commands/Account.java new file mode 100644 index 000000000..4d4a80db4 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Account.java @@ -0,0 +1,61 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.database.DatabaseHelper; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "account", usage = "account [uid]", + description = "Modify user accounts") +public class Account implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender != null) { + CommandHandler.sendMessage(sender, "This command can only be run from the console."); + return; + } + + if (args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: account [uid]"); + return; + } + + String action = args.get(0); + String username = args.get(1); + + switch (action) { + default: + CommandHandler.sendMessage(null, "Usage: account [uid]"); + return; + case "create": + int uid = 0; + if (args.size() > 2) { + try { + uid = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid UID."); + return; + } + } + + emu.grasscutter.game.Account account = DatabaseHelper.createAccountWithId(username, uid); + if (account == null) { + CommandHandler.sendMessage(null, "Account already exists."); + return; + } else { + CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); + account.addPermission("*"); // Grant the player superuser permissions. + } + return; + case "delete": + if (DatabaseHelper.deleteAccount(username)) { + CommandHandler.sendMessage(null, "Account deleted."); + } else { + CommandHandler.sendMessage(null, "Account not found."); + } + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Broadcast.java b/src/main/java/emu/grasscutter/command/commands/Broadcast.java new file mode 100644 index 000000000..f8d08eb9c --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Broadcast.java @@ -0,0 +1,29 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "broadcast", usage = "broadcast ", + description = "Sends a message to all the players", aliases = {"b"}, permission = "server.broadcast") +public class Broadcast implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: broadcast "); + return; + } + + String message = String.join(" ", args.subList(0, args.size())); + + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + CommandHandler.sendMessage(p, message); + } + + CommandHandler.sendMessage(sender, "Message sent."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ChangeScene.java b/src/main/java/emu/grasscutter/command/commands/ChangeScene.java new file mode 100644 index 000000000..fe1521fba --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ChangeScene.java @@ -0,0 +1,36 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "changescene", usage = "changescene ", + description = "Changes your scene", aliases = {"scene"}, permission = "player.changescene") +public class ChangeScene implements CommandHandler { + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: changescene "); + return; + } + + try { + int sceneId = Integer.parseInt(args.get(0)); + boolean result = sender.getWorld().transferPlayerToScene(sender, sceneId, sender.getPos()); + + CommandHandler.sendMessage(sender, "Changed to scene " + sceneId); + if (!result) { + CommandHandler.sendMessage(sender, "Scene does not exist or you are already in it"); + } + } catch (Exception e) { + CommandHandler.sendMessage(sender, "Usage: changescene "); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java b/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java new file mode 100644 index 000000000..2069b0466 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ClearArtifacts.java @@ -0,0 +1,30 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.Inventory; +import emu.grasscutter.game.inventory.ItemType; + +import java.util.List; + +@Command(label = "clearartifacts", usage = "clearartifacts", + description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory", + aliases = {"clearart"}, permission = "player.clearartifacts") +public class ClearArtifacts implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: clear player's artifacts from console or other players + } + + Inventory playerInventory = sender.getInventory(); + playerInventory.getItems().values().stream() + .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) + .filter(item -> item.getLevel() == 1 && item.getExp() == 0) + .filter(item -> !item.isLocked() && !item.isEquipped()) + .forEach(item -> playerInventory.removeItem(item, item.getCount())); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Drop.java b/src/main/java/emu/grasscutter/command/commands/Drop.java new file mode 100644 index 000000000..3ef3bc840 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Drop.java @@ -0,0 +1,56 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityItem; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Command(label = "drop", usage = "drop [amount]", + description = "Drops an item near you", aliases = {"d", "dropitem"}, permission = "server.drop") +public class Drop implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: drop [amount]"); + return; + } + + try { + int item = Integer.parseInt(args.get(0)); + int amount = 1; + if (args.size() > 1) amount = Integer.parseInt(args.get(1)); + + ItemData itemData = GenshinData.getItemDataMap().get(item); + if (itemData == null) { + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + + if (itemData.isEquip()) { + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, pos, 1); + sender.getScene().addEntity(entity); + } + } else { + EntityItem entity = new EntityItem(sender.getScene(), sender, itemData, sender.getPos().clone().addY(3f), amount); + sender.getScene().addEntity(entity); + } + CommandHandler.sendMessage(sender, String.format("Dropped %s of %s.", amount, item)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + } + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/Give.java b/src/main/java/emu/grasscutter/command/commands/Give.java new file mode 100644 index 000000000..43f1961cc --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Give.java @@ -0,0 +1,112 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.ItemData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.inventory.GenshinItem; +import emu.grasscutter.game.props.ActionReason; +import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; + +import java.util.LinkedList; +import java.util.List; + +@Command(label = "give", usage = "give [player] [amount]", + description = "Gives an item to you or the specified player", aliases = {"g", "item", "giveitem"}, permission = "player.give") +public class Give implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + int target, item, amount = 1; + + if (sender == null && args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: give [amount]"); + return; + } + + switch (args.size()) { + default: // *No args* + CommandHandler.sendMessage(sender, "Usage: give [player] [amount]"); + return; + case 1: // + try { + item = Integer.parseInt(args.get(0)); + target = sender.getUid(); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + break; + case 2: // [amount] | [player] + try { + target = Integer.parseInt(args.get(0)); + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + item = Integer.parseInt(args.get(0)); + amount = Integer.parseInt(args.get(1)); + } else { + item = Integer.parseInt(args.get(1)); + } + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + return; + } + break; + case 3: // [player] [amount] + try { + target = Integer.parseInt(args.get(0)); + + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + return; + } + + item = Integer.parseInt(args.get(1)); + amount = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + // TODO: Parse from item name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + return; + } + break; + } + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + ItemData itemData = GenshinData.getItemDataMap().get(item); + if (itemData == null) { + CommandHandler.sendMessage(sender, "Invalid item id."); + return; + } + + this.item(targetPlayer, itemData, amount); + + CommandHandler.sendMessage(sender, String.format("Given %s of %s to %s.", amount, item, target)); + } + + private void item(GenshinPlayer player, ItemData itemData, int amount) { + GenshinItem genshinItem = new GenshinItem(itemData); + if (itemData.isEquip()) { + List items = new LinkedList<>(); + for (int i = 0; i < amount; i++) { + items.add(genshinItem); + } + player.getInventory().addItems(items); + player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); + } else { + genshinItem.setCount(amount); + player.getInventory().addItem(genshinItem); + player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/GiveChar.java b/src/main/java/emu/grasscutter/command/commands/GiveChar.java new file mode 100644 index 000000000..7eaa56a47 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/GiveChar.java @@ -0,0 +1,93 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.AvatarData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.avatar.GenshinAvatar; + +import java.util.List; + +@Command(label = "givechar", usage = "givechar [level]", + description = "Gives the player a specified character", aliases = {"givec"}, permission = "player.givechar") +public class GiveChar implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + int target, avatarId, level = 1, ascension; + + if (sender == null && args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: givechar [amount]"); + return; + } + + switch (args.size()) { + default: + CommandHandler.sendMessage(sender, "Usage: givechar [level]"); + return; + case 2: + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null && sender != null) { + target = sender.getUid(); + level = Integer.parseInt(args.get(1)); + avatarId = Integer.parseInt(args.get(0)); + } else { + avatarId = Integer.parseInt(args.get(1)); + } + } catch (NumberFormatException ignored) { + // TODO: Parse from avatar name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); + return; + } + break; + case 3: + try { + target = Integer.parseInt(args.get(0)); + if (Grasscutter.getGameServer().getPlayerByUid(target) == null) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + return; + } + + avatarId = Integer.parseInt(args.get(1)); + level = Integer.parseInt(args.get(2)); + } catch (NumberFormatException ignored) { + // TODO: Parse from avatar name using GM Handbook. + CommandHandler.sendMessage(sender, "Invalid avatar or player ID."); + return; + } + break; + } + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId); + if (avatarData == null) { + CommandHandler.sendMessage(sender, "Invalid avatar id."); + return; + } + + // Calculate ascension level. + if (level <= 40) { + ascension = (int) Math.ceil(level / 20f); + } else { + ascension = (int) Math.ceil(level / 10f) - 3; + } + + GenshinAvatar avatar = new GenshinAvatar(avatarId); + avatar.setLevel(level); + avatar.setPromoteLevel(ascension); + + // This will handle stats and talents + avatar.recalcStats(); + + targetPlayer.addAvatar(avatar); + CommandHandler.sendMessage(sender, String.format("Given %s to %s.", avatarId, target)); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/GodMode.java b/src/main/java/emu/grasscutter/command/commands/GodMode.java new file mode 100644 index 000000000..491bf0ba8 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/GodMode.java @@ -0,0 +1,22 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "godmode", usage = "godmode [playerId]", + description = "Prevents you from taking damage", permission = "player.godmode") +public class GodMode implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: toggle player's godmode statue from console or other players + } + sender.setGodmode(!sender.inGodmode()); + sender.dropMessage("Godmode is now " + (sender.inGodmode() ? "enabled" : "disabled") + "."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Help.java b/src/main/java/emu/grasscutter/command/commands/Help.java new file mode 100644 index 000000000..3350160a7 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Help.java @@ -0,0 +1,91 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.command.CommandMap; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.*; + +@Command(label = "help", usage = "help [command]", + description = "Sends the help message or shows information about a specified command") +public class Help implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer player, List args) { + if (args.size() < 1) { + HashMap handlers = CommandMap.getInstance().getHandlers(); + List annotations = new ArrayList<>(); + for (String key : handlers.keySet()) { + Command annotation = handlers.get(key).getClass().getAnnotation(Command.class); + + if (!Arrays.asList(annotation.aliases()).contains(key)) { + if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) + continue; + annotations.add(annotation); + } + } + + SendAllHelpMessage(player, annotations); + } else { + String command = args.get(0); + CommandHandler handler = CommandMap.getInstance().getHandler(command); + StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n"); + if (handler == null) { + builder.append("No command found."); + } else { + Command annotation = handler.getClass().getAnnotation(Command.class); + + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) { + builder.append("\n Warning: You do not have permission to run this command."); + } + } + + CommandHandler.sendMessage(player, builder.toString()); + } + } + + void SendAllHelpMessage(GenshinPlayer player, List annotations) { + if (player == null) { + StringBuilder builder = new StringBuilder("\nAvailable commands:\n"); + annotations.forEach(annotation -> { + builder.append(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + builder.append("\n"); + }); + + CommandHandler.sendMessage(null, builder.toString()); + } else { + CommandHandler.sendMessage(player, "Available commands:"); + annotations.forEach(annotation -> { + StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); + builder.append(" ").append(annotation.description()).append("\n"); + builder.append(" Usage: ").append(annotation.usage()); + if (annotation.aliases().length >= 1) { + builder.append("\n").append(" Aliases: "); + for (String alias : annotation.aliases()) { + builder.append(alias).append(" "); + } + } + + CommandHandler.sendMessage(player, builder.toString()); + }); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Kick.java b/src/main/java/emu/grasscutter/command/commands/Kick.java new file mode 100644 index 000000000..d1ef5077e --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Kick.java @@ -0,0 +1,31 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "kick", usage = "kick ", + description = "Kicks the specified player from the server (WIP)", permission = "server.kick") +public class Kick implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + int target = Integer.parseInt(args.get(0)); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + if (sender != null) { + CommandHandler.sendMessage(sender, String.format("Player [%s:%s] has kicked player [%s:%s]", sender.getAccount().getPlayerId(), sender.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); + } + CommandHandler.sendMessage(sender, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); + + targetPlayer.getSession().close(); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/KillAll.java b/src/main/java/emu/grasscutter/command/commands/KillAll.java new file mode 100644 index 000000000..540c01ce3 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/KillAll.java @@ -0,0 +1,64 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.GenshinScene; +import emu.grasscutter.game.entity.EntityMonster; + +import java.util.List; + +@Command(label = "killall", usage = "killall [playerUid] [sceneId]", + description = "Kill all entities", permission = "server.killall") +public class KillAll implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + GenshinScene scene; + GenshinPlayer genshinPlayer; + + try { + switch (args.size()) { + case 0: // *No args* + if (sender == null) { + CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); + return; + } + scene = sender.getScene(); + break; + case 1: // [playerUid] + genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); + if (genshinPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); + return; + } + scene = genshinPlayer.getScene(); + break; + case 2: // [playerUid] [sceneId] + genshinPlayer = Grasscutter.getGameServer().getPlayerByUid(Integer.parseInt(args.get(0))); + if (genshinPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found or offline."); + return; + } + GenshinScene genshinScene = sender.getWorld().getSceneById(Integer.parseInt(args.get(1))); + if (genshinScene == null) { + CommandHandler.sendMessage(sender, "Scene not found in player world"); + return; + } + scene = genshinScene; + break; + default: + CommandHandler.sendMessage(sender, "Usage: killall [playerUid] [sceneId]"); + return; + } + + scene.getEntities().values().stream() + .filter(entity -> entity instanceof EntityMonster) + .forEach(entity -> scene.killEntity(entity, 0)); + CommandHandler.sendMessage(sender, "Killing all monsters in scene " + scene.getId()); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid arguments."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Permission.java b/src/main/java/emu/grasscutter/command/commands/Permission.java new file mode 100644 index 000000000..02c047f48 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Permission.java @@ -0,0 +1,50 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.Account; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "permission", usage = "permission ", + description = "Grants or removes a permission for a user", permission = "*") +public class Permission implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (args.size() < 3) { + CommandHandler.sendMessage(sender, "Usage: permission "); + return; + } + + String action = args.get(0); + String username = args.get(1); + String permission = args.get(2); + + Account account = Grasscutter.getGameServer().getAccountByName(username); + if (account == null) { + CommandHandler.sendMessage(sender, "Account not found."); + return; + } + + switch (action) { + default: + CommandHandler.sendMessage(sender, "Usage: permission "); + break; + case "add": + if (account.addPermission(permission)) { + CommandHandler.sendMessage(sender, "Permission added."); + } else CommandHandler.sendMessage(sender, "They already have this permission!"); + break; + case "remove": + if (account.removePermission(permission)) { + CommandHandler.sendMessage(sender, "Permission removed."); + } else CommandHandler.sendMessage(sender, "They don't have this permission!"); + break; + } + + account.save(); + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/Reload.java b/src/main/java/emu/grasscutter/command/commands/Reload.java new file mode 100644 index 000000000..587ffcd9c --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Reload.java @@ -0,0 +1,21 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "reload", usage = "reload", + description = "Reload server config", permission = "server.reload") +public class Reload implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + CommandHandler.sendMessage(sender, "Reloading config."); + Grasscutter.loadConfig(); + Grasscutter.getDispatchServer().loadQueries(); + CommandHandler.sendMessage(sender, "Reload complete."); + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/ResetConst.java b/src/main/java/emu/grasscutter/command/commands/ResetConst.java new file mode 100644 index 000000000..cabc00b64 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/ResetConst.java @@ -0,0 +1,45 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.avatar.GenshinAvatar; +import emu.grasscutter.game.entity.EntityAvatar; + +import java.util.List; + +@Command(label = "resetconst", usage = "resetconst [all]", + description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.", + aliases = {"resetconstellation"}, permission = "player.resetconstellation") +public class ResetConst implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { + sender.getAvatars().forEach(this::resetConstellation); + sender.dropMessage("Reset all avatars' constellations."); + } else { + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + if (entity == null) { + return; + } + + GenshinAvatar avatar = entity.getAvatar(); + this.resetConstellation(avatar); + + sender.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); + } + } + + private void resetConstellation(GenshinAvatar avatar) { + avatar.getTalentIdList().clear(); + avatar.setCoreProudSkillLevel(0); + avatar.recalcStats(); + avatar.save(); + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/SendMessage.java b/src/main/java/emu/grasscutter/command/commands/SendMessage.java new file mode 100644 index 000000000..3d2fd7b70 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SendMessage.java @@ -0,0 +1,37 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "say", usage = "say ", description = "Sends a message to a player as the server", + aliases = {"sendservmsg", "sendservermessage", "sendmessage"}, permission = "server.sendmessage") +public class SendMessage implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (args.size() < 2) { + CommandHandler.sendMessage(null, "Usage: sendmessage "); + return; + } + + try { + int target = Integer.parseInt(args.get(0)); + String message = String.join(" ", args.subList(1, args.size())); + + GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); + if (targetPlayer == null) { + CommandHandler.sendMessage(sender, "Player not found."); + return; + } + + CommandHandler.sendMessage(targetPlayer, message); + CommandHandler.sendMessage(sender, "Message sent."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid player ID."); + } + } +} \ No newline at end of file diff --git a/src/main/java/emu/grasscutter/command/commands/SetHealth.java b/src/main/java/emu/grasscutter/command/commands/SetHealth.java new file mode 100644 index 000000000..2daf792ef --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SetHealth.java @@ -0,0 +1,42 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityAvatar; +import emu.grasscutter.game.props.FightProperty; +import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; + +import java.util.List; + +@Command(label = "sethealth", usage = "sethealth ", + description = "Sets your health to the specified value", aliases = {"sethp"}, permission = "player.sethealth") +public class SetHealth implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: set player's health from console or other players + } + + if (args.size() < 1) { + CommandHandler.sendMessage(null, "Usage: sethealth "); + return; + } + + try { + int health = Integer.parseInt(args.get(0)); + EntityAvatar entity = sender.getTeamManager().getCurrentAvatarEntity(); + if (entity == null) { + return; + } + + entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); + entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); + sender.dropMessage("Health set to " + health + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid health value."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java b/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java new file mode 100644 index 000000000..69eeae1d9 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/SetWorldLevel.java @@ -0,0 +1,39 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.props.PlayerProperty; + +import java.util.List; + +@Command(label = "setworldlevel", usage = "setworldlevel ", + description = "Sets your world level (Relog to see proper effects)", + aliases = {"setworldlvl"}, permission = "player.setworldlevel") +public class SetWorldLevel implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; // TODO: set player's world level from console or other players + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: setworldlevel "); + return; + } + + try { + int level = Integer.parseInt(args.get(0)); + + // Set in both world and player props + sender.getWorld().setWorldLevel(level); + sender.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level); + + sender.dropMessage("World level set to " + level + "."); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(null, "Invalid world level."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Spawn.java b/src/main/java/emu/grasscutter/command/commands/Spawn.java new file mode 100644 index 000000000..def06c362 --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Spawn.java @@ -0,0 +1,51 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.data.GenshinData; +import emu.grasscutter.data.def.MonsterData; +import emu.grasscutter.game.GenshinPlayer; +import emu.grasscutter.game.entity.EntityMonster; +import emu.grasscutter.utils.Position; + +import java.util.List; + +@Command(label = "spawn", usage = "spawn [level] [amount]", + description = "Spawns an entity near you", permission = "server.spawn") +public class Spawn implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + if (sender == null) { + CommandHandler.sendMessage(null, "Run this command in-game."); + return; + } + + if (args.size() < 1) { + CommandHandler.sendMessage(sender, "Usage: spawn [amount]"); + return; + } + + try { + int entity = Integer.parseInt(args.get(0)); + int level = args.size() > 1 ? Integer.parseInt(args.get(1)) : 1; + int amount = args.size() > 2 ? Integer.parseInt(args.get(2)) : 1; + + MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); + if (entityData == null) { + CommandHandler.sendMessage(sender, "Invalid entity id."); + return; + } + + float range = (5f + (.1f * amount)); + for (int i = 0; i < amount; i++) { + Position pos = sender.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); + EntityMonster monster = new EntityMonster(sender.getScene(), entityData, pos, level); + sender.getScene().addEntity(monster); + } + CommandHandler.sendMessage(sender, String.format("Spawned %s of %s.", amount, entity)); + } catch (NumberFormatException ignored) { + CommandHandler.sendMessage(sender, "Invalid item or player ID."); + } + } +} diff --git a/src/main/java/emu/grasscutter/command/commands/Stop.java b/src/main/java/emu/grasscutter/command/commands/Stop.java new file mode 100644 index 000000000..21676cb7d --- /dev/null +++ b/src/main/java/emu/grasscutter/command/commands/Stop.java @@ -0,0 +1,23 @@ +package emu.grasscutter.command.commands; + +import emu.grasscutter.Grasscutter; +import emu.grasscutter.command.Command; +import emu.grasscutter.command.CommandHandler; +import emu.grasscutter.game.GenshinPlayer; + +import java.util.List; + +@Command(label = "stop", usage = "stop", + description = "Stops the server", permission = "server.stop") +public class Stop implements CommandHandler { + + @Override + public void onCommand(GenshinPlayer sender, List args) { + CommandHandler.sendMessage(null, "Server shutting down..."); + for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { + CommandHandler.sendMessage(p, "Server shutting down..."); + } + + System.exit(1); + } +} diff --git a/src/main/java/emu/grasscutter/commands/Command.java b/src/main/java/emu/grasscutter/commands/Command.java deleted file mode 100644 index d26d7fe29..000000000 --- a/src/main/java/emu/grasscutter/commands/Command.java +++ /dev/null @@ -1,25 +0,0 @@ -package emu.grasscutter.commands; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -@Retention(RetentionPolicy.RUNTIME) -public @interface Command { - String label() default ""; - - String usage() default "No usage specified"; - - String description() default "No description specified"; - - String[] aliases() default {}; - - Execution execution() default Execution.ALL; - - String permission() default ""; - - enum Execution { - ALL, - CONSOLE, - PLAYER - } -} diff --git a/src/main/java/emu/grasscutter/commands/CommandHandler.java b/src/main/java/emu/grasscutter/commands/CommandHandler.java deleted file mode 100644 index 97bd8c81f..000000000 --- a/src/main/java/emu/grasscutter/commands/CommandHandler.java +++ /dev/null @@ -1,28 +0,0 @@ -package emu.grasscutter.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.List; - -public interface CommandHandler { - /* Invoked on player execution. */ - default void execute(GenshinPlayer player, List args) { } - /* Invoked on server execution. */ - default void execute(List args) { } - - /* - * Utilities. - */ - - /** - * Send a message to the target. - * @param player The player to send the message to, or null for the server console. - * @param message The message to send. - */ - static void sendMessage(GenshinPlayer player, String message) { - if(player == null) { - Grasscutter.getLogger().info(message); - } else player.dropMessage(message); - } -} diff --git a/src/main/java/emu/grasscutter/commands/PlayerCommands.java b/src/main/java/emu/grasscutter/commands/PlayerCommands.java deleted file mode 100644 index 779e91ef9..000000000 --- a/src/main/java/emu/grasscutter/commands/PlayerCommands.java +++ /dev/null @@ -1,539 +0,0 @@ -package emu.grasscutter.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.data.GenshinData; -import emu.grasscutter.data.def.ItemData; -import emu.grasscutter.data.def.AvatarData; -import emu.grasscutter.data.def.AvatarSkillDepotData; -import emu.grasscutter.data.def.MonsterData; -import emu.grasscutter.game.GenshinPlayer; -import emu.grasscutter.game.GenshinScene; -import emu.grasscutter.game.World; -import emu.grasscutter.game.avatar.GenshinAvatar; -import emu.grasscutter.game.entity.EntityAvatar; -import emu.grasscutter.game.entity.EntityItem; -import emu.grasscutter.game.entity.EntityMonster; -import emu.grasscutter.game.inventory.GenshinItem; -import emu.grasscutter.game.inventory.Inventory; -import emu.grasscutter.game.inventory.ItemType; -import emu.grasscutter.game.props.ActionReason; -import emu.grasscutter.game.props.FightProperty; -import emu.grasscutter.game.props.PlayerProperty; -import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify; -import emu.grasscutter.server.packet.send.PacketItemAddHintNotify; -import emu.grasscutter.utils.Position; - -import java.util.LinkedList; -import java.util.List; - -/** - * A container for player-related commands. - */ -public final class PlayerCommands { - @Command(label = "give", aliases = {"g", "item", "giveitem"}, - usage = "give [player] [amount]", description = "Gives an item to you or the specified player", permission = "player.give") - public static class GiveCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - int target, item, amount = 1; - - switch(args.size()) { - default: - CommandHandler.sendMessage(player, "Usage: give [amount]"); - return; - case 1: - try { - item = Integer.parseInt(args.get(0)); - target = player.getAccount().getPlayerId(); - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid item id."); - return; - } - break; - case 2: - try { - target = Integer.parseInt(args.get(0)); - - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = player.getUid(); amount = Integer.parseInt(args.get(1)); - item = Integer.parseInt(args.get(0)); - } else { - item = Integer.parseInt(args.get(1)); - } - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid item or player ID."); - return; - } - break; - case 3: - try { - target = Integer.parseInt(args.get(0)); - - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(player, "Invalid player ID."); return; - } - - item = Integer.parseInt(args.get(1)); - amount = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - // TODO: Parse from item name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid item or player ID."); - return; - } - break; - } - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; - } - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(player, "Invalid item id."); return; - } - - this.item(targetPlayer, itemData, amount); - } - - /** - * give [player] [itemId|itemName] [amount] - */ - @Override public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: give [amount]"); - return; - } - - try { - int target = Integer.parseInt(args.get(0)); - int item = Integer.parseInt(args.get(1)); - int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(null, "Invalid item id."); return; - } - - this.item(targetPlayer, itemData, amount); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid item or player ID."); - } - } - - private void item(GenshinPlayer player, ItemData itemData, int amount) { - GenshinItem genshinItem = new GenshinItem(itemData); - if(itemData.isEquip()) { - List items = new LinkedList<>(); - for(int i = 0; i < amount; i++) { - items.add(genshinItem); - } player.getInventory().addItems(items); - player.sendPacket(new PacketItemAddHintNotify(items, ActionReason.SubfieldDrop)); - } else { - genshinItem.setCount(amount); - player.getInventory().addItem(genshinItem); - player.sendPacket(new PacketItemAddHintNotify(genshinItem, ActionReason.SubfieldDrop)); - } - } - } - - @Command(label = "drop", aliases = {"d", "dropitem"}, - usage = "drop [amount]", - execution = Command.Execution.PLAYER, description = "Drops an item near you", permission = "server.drop") - public static class DropCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: drop [amount]"); - return; - } - - try { - int item = Integer.parseInt(args.get(0)); - int amount = 1; if(args.size() > 1) amount = Integer.parseInt(args.get(1)); - - ItemData itemData = GenshinData.getItemDataMap().get(item); - if(itemData == null) { - CommandHandler.sendMessage(player, "Invalid item id."); return; - } - - if (itemData.isEquip()) { - float range = (5f + (.1f * amount)); - for (int i = 0; i < amount; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityItem entity = new EntityItem(player.getScene(), player, itemData, pos, 1); - player.getScene().addEntity(entity); - } - } else { - EntityItem entity = new EntityItem(player.getScene(), player, itemData, player.getPos().clone().addY(3f), amount); - player.getScene().addEntity(entity); - } - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(player, "Invalid item or player ID."); - } - } - } - - @Command(label = "givechar", aliases = { "givec" }, usage = "givechar [level]", - description = "Gives the player a specified character", permission = "player.givechar") - public static class GiveCharCommand implements CommandHandler { - @Override public void execute(GenshinPlayer player, List args) { - int target, avatarId, level = 1, ascension = 1; - - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: givechar [level]"); - return; - } - - switch(args.size()) { - default: - CommandHandler.sendMessage(player, "Usage: givechar [level]"); - return; - case 2: - try { - target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - target = player.getUid(); - level = Integer.parseInt(args.get(1)); - avatarId = Integer.parseInt(args.get(0)); - } else { - avatarId = Integer.parseInt(args.get(1)); - } - } catch (NumberFormatException ignored) { - // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid avatar or player ID."); - return; - } - break; - case 3: - try { - target = Integer.parseInt(args.get(0)); - if(Grasscutter.getGameServer().getPlayerByUid(target) == null) { - CommandHandler.sendMessage(player, "Invalid player ID."); return; - } - - avatarId = Integer.parseInt(args.get(1)); - level = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - // TODO: Parse from avatar name using GM Handbook. - CommandHandler.sendMessage(player, "Invalid avatar or player ID."); - return; - } - break; - } - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; - } - - AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarId); - if(avatarData == null) { - CommandHandler.sendMessage(player, "Invalid avatar id."); return; - } - - // Calculate ascension level. - if (level <= 40) { - ascension = (int) Math.ceil(level / 20f); - } else { - ascension = (int) Math.ceil(level / 10f) - 3; - } - - GenshinAvatar avatar = new GenshinAvatar(avatarId); - avatar.setLevel(level); - avatar.setPromoteLevel(ascension); - - // This will handle stats and talents - avatar.recalcStats(); - - targetPlayer.addAvatar(avatar); - } - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: givechar [amount]"); - return; - } - - try { - int target = Integer.parseInt(args.get(0)); - int avatarID = Integer.parseInt(args.get(1)); - int level = 1; if(args.size() > 2) level = Integer.parseInt(args.get(2)); - int ascension; - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - AvatarData avatarData = GenshinData.getAvatarDataMap().get(avatarID); - if(avatarData == null) { - CommandHandler.sendMessage(null, "Invalid avatar id."); return; - } - - // Calculate ascension level. - if (level <= 40) { - ascension = (int) Math.ceil(level / 20f); - } else { - ascension = (int) Math.ceil(level / 10f) - 3; - } - - GenshinAvatar avatar = new GenshinAvatar(avatarID); - avatar.setLevel(level); - avatar.setPromoteLevel(ascension); - - // This will handle stats and talents - avatar.recalcStats(); - - targetPlayer.addAvatar(avatar); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid item or player ID."); - } - } - } - - @Command(label = "spawn", execution = Command.Execution.PLAYER, - usage = "spawn [level] [amount]", description = "Spawns an entity near you", permission = "server.spawn") - public static class SpawnCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: spawn [amount]"); - return; - } - - try { - int entity = Integer.parseInt(args.get(0)); - int level = 1; if(args.size() > 1) level = Integer.parseInt(args.get(1)); - int amount = 1; if(args.size() > 2) amount = Integer.parseInt(args.get(2)); - - MonsterData entityData = GenshinData.getMonsterDataMap().get(entity); - if(entityData == null) { - CommandHandler.sendMessage(null, "Invalid entity id."); return; - } - - float range = (5f + (.1f * amount)); - for (int i = 0; i < amount; i++) { - Position pos = player.getPos().clone().addX((float) (Math.random() * range) - (range / 2)).addY(3f).addZ((float) (Math.random() * range) - (range / 2)); - EntityMonster monster = new EntityMonster(player.getScene(), entityData, pos, level); - player.getScene().addEntity(monster); - } - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid item or player ID."); - } - } - } - - @Command(label = "killall", - usage = "killall [playerUid] [sceneId]", description = "Kill all entities", permission = "server.killall") - public static class KillAllCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - GenshinScene scene = player.getScene(); - scene.getEntities().values().stream() - .filter(entity -> entity instanceof EntityMonster) - .forEach(entity -> scene.killEntity(entity, 0)); - CommandHandler.sendMessage(null, "Killing all monsters in scene " + scene.getId()); - } - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: killall [playerUid] [sceneId]"); return; - } - - try { - int playerUid = Integer.parseInt(args.get(0)); - int sceneId = Integer.parseInt(args.get(1)); - - GenshinPlayer player = Grasscutter.getGameServer().getPlayerByUid(playerUid); - if (player == null) { - CommandHandler.sendMessage(null, "Player not found or offline."); - return; - } - - GenshinScene scene = player.getWorld().getSceneById(sceneId); - if (scene == null) { - CommandHandler.sendMessage(null, "Scene not found in player world"); - return; - } - - scene.getEntities().values().stream() - .filter(entity -> entity instanceof EntityMonster) - .forEach(entity -> scene.killEntity(entity, 0)); - CommandHandler.sendMessage(null, "Killing all monsters in scene " + scene.getId()); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid arguments."); - } - } - } - - @Command(label = "resetconst", aliases = {"resetconstellation"}, - usage = "resetconst [all]", execution = Command.Execution.PLAYER, permission = "player.resetconstellation", - description = "Resets the constellation level on your current active character, will need to relog after using the command to see any changes.") - public static class ResetConstellationCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() > 0 && args.get(0).equalsIgnoreCase("all")) { - player.getAvatars().forEach(this::resetConstellation); - player.dropMessage("Reset all avatars' constellations."); - } else { - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - if(entity == null) - return; - - GenshinAvatar avatar = entity.getAvatar(); - this.resetConstellation(avatar); - - player.dropMessage("Constellations for " + avatar.getAvatarData().getName() + " have been reset. Please relog to see changes."); - } - } - - private void resetConstellation(GenshinAvatar avatar) { - avatar.getTalentIdList().clear(); - avatar.setCoreProudSkillLevel(0); - avatar.recalcStats(); - avatar.save(); - } - } - - @Command(label = "godmode", - usage = "godmode", execution = Command.Execution.PLAYER, description = "Prevents you from taking damage", permission = "player.godmode") - public static class GodModeCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - player.setGodmode(!player.inGodmode()); - player.dropMessage("Godmode is now " + (player.inGodmode() ? "enabled" : "disabled") + "."); - } - } - - @Command(label = "sethealth", aliases = {"sethp"}, - usage = "sethealth ", execution = Command.Execution.PLAYER, description = "Sets your health to the specified value", - permission = "player.sethealth") - public static class SetHealthCommand implements CommandHandler { - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: sethealth "); return; - } - - try { - int health = Integer.parseInt(args.get(0)); - EntityAvatar entity = player.getTeamManager().getCurrentAvatarEntity(); - if(entity == null) - return; - - entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, health); - entity.getWorld().broadcastPacket(new PacketEntityFightPropUpdateNotify(entity, FightProperty.FIGHT_PROP_CUR_HP)); - player.dropMessage("Health set to " + health + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid health value."); - } - } - } - - @Command(label = "setworldlevel", aliases = {"setworldlvl"}, usage = "setworldlevel ", - description = "Sets your world level (Relog to see proper effects)", permission = "player.setworldlevel", - execution = Command.Execution.PLAYER) - public static class SetWorldLevelCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: setworldlevel "); return; - } - - try { - int level = Integer.parseInt(args.get(0)); - - // Set in both world and player props - player.getWorld().setWorldLevel(level); - player.setProperty(PlayerProperty.PROP_PLAYER_WORLD_LEVEL, level); - - player.dropMessage("World level set to " + level + "."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid world level."); - } - } - } - - @Command(label = "clearartifacts", aliases = {"clearart"}, - usage = "clearartifacts", execution = Command.Execution.PLAYER, permission = "player.clearartifacts", - description = "Deletes all unequipped and unlocked level 0 artifacts, including yellow rarity ones from your inventory") - public static class ClearArtifactsCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - Inventory playerInventory = player.getInventory(); - playerInventory.getItems().values().stream() - .filter(item -> item.getItemType() == ItemType.ITEM_RELIQUARY) - .filter(item -> item.getLevel() == 1 && item.getExp() == 0) - .filter(item -> !item.isLocked() && !item.isEquipped()) - .forEach(item -> playerInventory.removeItem(item, item.getCount())); - } - } - - @Command(label = "changescene", aliases = {"scene"}, - usage = "changescene ", description = "Changes your scene", permission = "player.changescene", execution = Command.Execution.PLAYER) - public static class ChangeSceneCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: changescene "); return; - } - - try { - int sceneId = Integer.parseInt(args.get(0)); - boolean result = player.getWorld().transferPlayerToScene(player, sceneId, player.getPos()); - - if (!result) { - CommandHandler.sendMessage(null, "Scene does not exist or you are already in it"); - } - } catch (Exception e) { - CommandHandler.sendMessage(player, "Usage: changescene "); return; - } - } - } - - @Command(label = "sendservermessage", aliases = {"sendservmsg"}, - usage = "sendservermessage ", description = "Sends a message to a player as the server", - execution = Command.Execution.PLAYER, permission = "server.sendmessage") - public static class SendServerMessageCommand implements CommandHandler { - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: sendmessage "); return; - } - - try { - int target = Integer.parseInt(args.get(0)); - String message = String.join(" ", args.subList(1, args.size())); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - targetPlayer.dropMessage(message); - CommandHandler.sendMessage(null, "Message sent."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid player ID."); - } - } - } -} diff --git a/src/main/java/emu/grasscutter/commands/ServerCommands.java b/src/main/java/emu/grasscutter/commands/ServerCommands.java deleted file mode 100644 index edeac6ca3..000000000 --- a/src/main/java/emu/grasscutter/commands/ServerCommands.java +++ /dev/null @@ -1,333 +0,0 @@ -package emu.grasscutter.commands; - -import emu.grasscutter.Grasscutter; -import emu.grasscutter.database.DatabaseHelper; -import emu.grasscutter.game.Account; -import emu.grasscutter.game.GenshinPlayer; - -import java.util.*; - -/** - * A container for server-related commands. - */ -public final class ServerCommands { - @Command(label = "reload", usage = "reload", description = "Reload server config", permission = "server.reload") - public static class ReloadCommand implements CommandHandler { - - @Override - public void execute(List args) { - Grasscutter.getLogger().info("Reloading config."); - Grasscutter.loadConfig(); - Grasscutter.getDispatchServer().loadQueries(); - Grasscutter.getLogger().info("Reload complete."); - } - - @Override - public void execute(GenshinPlayer player, List args) { - CommandHandler.sendMessage(player, "Reloading config."); - this.execute(args); - CommandHandler.sendMessage(player, "Reload complete."); - } - } - - @Command(label = "kick", usage = "kick ", description = "Kicks the specified player from the server (WIP)", permission = "server.kick") - public static class KickCommand implements CommandHandler { - @Override - public void execute(List args) { - this.execute(null, args); - } - - @Override - public void execute(GenshinPlayer player, List args) { - int target = Integer.parseInt(args.get(0)); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); - return; - } - - if(player != null) { - CommandHandler.sendMessage(null, String.format("Player [%s:%s] has kicked player [%s:%s]", player.getAccount().getPlayerId(), player.getAccount().getUsername(), target, targetPlayer.getAccount().getUsername())); - } - CommandHandler.sendMessage(player, String.format("Kicking player [%s:%s]", target, targetPlayer.getAccount().getUsername())); - - targetPlayer.getSession().close(); - } - } - - @Command(label = "stop", usage = "stop", description = "Stops the server", permission = "server.stop") - public static class StopCommand implements CommandHandler { - @Override - public void execute(List args) { - Grasscutter.getLogger().info("Server shutting down..."); - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - p.dropMessage("Server shutting down..."); - } - - System.exit(1); - } - } - - @Command(label = "broadcast", aliases = {"b"}, - usage = "broadcast ", description = "Sends a message to all the players", permission = "server.broadcast") - public static class BroadcastCommand implements CommandHandler { - @Override - public void execute(List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(null, "Usage: broadcast "); return; - } - - String message = String.join(" ", args.subList(0, args.size())); - - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - p.dropMessage(message); - } - - CommandHandler.sendMessage(null, "Message sent."); - } - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - CommandHandler.sendMessage(player, "Usage: broadcast "); return; - } - - String message = String.join(" ", args.subList(0, args.size())); - - for (GenshinPlayer p : Grasscutter.getGameServer().getPlayers().values()) { - p.dropMessage(message); - } - - CommandHandler.sendMessage(player, "Message sent."); - } - } - - @Command(label = "sendmessage", aliases = {"sendmsg", "msg"}, - usage = "sendmessage ", description = "Sends a message to a player") - public static class SendMessageCommand implements CommandHandler { - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: sendmessage "); return; - } - - try { - int target = Integer.parseInt(args.get(0)); - String message = String.join(" ", args.subList(1, args.size())); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(null, "Player not found."); return; - } - - targetPlayer.dropMessage(message); - CommandHandler.sendMessage(null, "Message sent."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid player ID."); - } - } - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(player, "Usage: sendmessage "); return; - } - - try { - int target = Integer.parseInt(args.get(0)); - String message = String.join(" ", args.subList(1, args.size())); - - GenshinPlayer targetPlayer = Grasscutter.getGameServer().getPlayerByUid(target); - - if(targetPlayer == null) { - CommandHandler.sendMessage(player, "Player not found."); return; - } - - targetPlayer.sendMessage(player, message); - CommandHandler.sendMessage(player, "Message sent."); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(player, "Invalid player ID."); - } - } - } - - @Command(label = "account", - usage = "account [uid]", - description = "Modify user accounts", execution = Command.Execution.CONSOLE) - public static class AccountCommand implements CommandHandler { - - @Override - public void execute(List args) { - if(args.size() < 2) { - CommandHandler.sendMessage(null, "Usage: account [uid]"); return; - } - - String action = args.get(0); - String username = args.get(1); - - switch(action) { - default: - CommandHandler.sendMessage(null, "Usage: account [uid]"); - return; - case "create": - int uid = 0; - if(args.size() > 2) { - try { - uid = Integer.parseInt(args.get(2)); - } catch (NumberFormatException ignored) { - CommandHandler.sendMessage(null, "Invalid UID."); return; - } - } - - Account account = DatabaseHelper.createAccountWithId(username, uid); - if(account == null) { - CommandHandler.sendMessage(null, "Account already exists."); return; - } else { - CommandHandler.sendMessage(null, "Account created with UID " + account.getPlayerId() + "."); - account.addPermission("*"); // Grant the player superuser permissions. - } - return; - case "delete": - if(DatabaseHelper.deleteAccount(username)) { - CommandHandler.sendMessage(null, "Account deleted."); return; - } else CommandHandler.sendMessage(null, "Account not found."); - return; - } - } - } - - @Command(label = "permission", - usage = "permission ", - description = "Grants or removes a permission for a user", permission = "*") - public static class PermissionCommand implements CommandHandler { - - @Override - public void execute(List args) { - if(args.size() < 3) { - CommandHandler.sendMessage(null, "Usage: permission "); return; - } - - String action = args.get(0); - String username = args.get(1); - String permission = args.get(2); - - Account account = Grasscutter.getGameServer().getAccountByName(username); - if(account == null) { - CommandHandler.sendMessage(null, "Account not found."); return; - } - - switch(action) { - default: - CommandHandler.sendMessage(null, "Usage: permission "); - break; - case "add": - if(account.addPermission(permission)) { - CommandHandler.sendMessage(null, "Permission added."); - } else CommandHandler.sendMessage(null, "They already have this permission!"); - break; - case "remove": - if(account.removePermission(permission)) { - CommandHandler.sendMessage(null, "Permission removed."); - } else CommandHandler.sendMessage(null, "They don't have this permission!"); - break; - } - - account.save(); - } - } - - @Command(label = "help", - usage = "help [command]", description = "Sends the help message or shows information about a specified command") - public static class HelpCommand implements CommandHandler { - - @Override - public void execute(List args) { - this.execute(null, args); - } - - @Override - public void execute(GenshinPlayer player, List args) { - if(args.size() < 1) { - HashMap handlers = CommandMap.getInstance().getHandlers(); - List annotations = new ArrayList<>(); - for(String key : handlers.keySet()) { - Command annotation = handlers.get(key).getClass().getAnnotation(Command.class); - - if(!Arrays.asList(annotation.aliases()).contains(key)) { - if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) continue; - annotations.add(annotation); - } - } - - - SendAllHelpMessage(player, annotations); - } else { - String command = args.get(0); - CommandHandler handler = CommandMap.getInstance().getHandler(command); - StringBuilder builder = new StringBuilder(player == null ? "\nHelp - " : "Help - ").append(command).append(": \n"); - if(handler == null) { - builder.append("No command found."); - } else { - Command annotation = handler.getClass().getAnnotation(Command.class); - - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if(annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - if(player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) { - builder.append("\n Warning: You do not have permission to run this command."); - } - } - - CommandHandler.sendMessage(player, builder.toString()); - } - } - - void SendAllHelpMessage(GenshinPlayer player, List annotations) { - if(player == null) { - StringBuilder builder = new StringBuilder("\nAvailable commands:\n"); - annotations.forEach(annotation -> { - if (annotation.execution() != Command.Execution.PLAYER) { - builder.append(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if (annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - - builder.append("\n"); - } - }); - - CommandHandler.sendMessage(null, builder.toString()); - } else { - CommandHandler.sendMessage(player, "Available commands:"); - annotations.forEach(annotation -> { - if (annotation.execution() != Command.Execution.CONSOLE) { - StringBuilder builder = new StringBuilder(annotation.label()).append("\n"); - builder.append(" ").append(annotation.description()).append("\n"); - builder.append(" Usage: ").append(annotation.usage()); - if (annotation.aliases().length >= 1) { - builder.append("\n").append(" Aliases: "); - for (String alias : annotation.aliases()) { - builder.append(alias).append(" "); - } - } - - CommandHandler.sendMessage(player, builder.toString()); - } - }); - } - } - } -} diff --git a/src/main/java/emu/grasscutter/game/managers/ChatManager.java b/src/main/java/emu/grasscutter/game/managers/ChatManager.java index 40b1a55b5..d9867e875 100644 --- a/src/main/java/emu/grasscutter/game/managers/ChatManager.java +++ b/src/main/java/emu/grasscutter/game/managers/ChatManager.java @@ -1,6 +1,6 @@ package emu.grasscutter.game.managers; -import emu.grasscutter.commands.CommandMap; +import emu.grasscutter.command.CommandMap; import emu.grasscutter.game.GenshinPlayer; import emu.grasscutter.net.packet.GenshinPacket; import emu.grasscutter.server.game.GameServer; diff --git a/src/main/java/emu/grasscutter/server/game/GameServer.java b/src/main/java/emu/grasscutter/server/game/GameServer.java index b42ced55c..fce6823fc 100644 --- a/src/main/java/emu/grasscutter/server/game/GameServer.java +++ b/src/main/java/emu/grasscutter/server/game/GameServer.java @@ -6,7 +6,7 @@ import java.util.concurrent.ConcurrentHashMap; import emu.grasscutter.GenshinConstants; import emu.grasscutter.Grasscutter; -import emu.grasscutter.commands.CommandMap; +import emu.grasscutter.command.CommandMap; import emu.grasscutter.database.DatabaseHelper; import emu.grasscutter.game.Account; import emu.grasscutter.game.GenshinPlayer;