Merge branch 'development' into more-events

# Conflicts:
#	src/main/java/emu/grasscutter/command/commands/TeleportAllCommand.java
#	src/main/java/emu/grasscutter/game/entity/EntityAvatar.java
#	src/main/java/emu/grasscutter/game/entity/GameEntity.java
#	src/main/java/emu/grasscutter/game/managers/mapmark/MapMarksManager.java
This commit is contained in:
KingRainbow44
2022-07-22 17:52:58 -04:00
257 changed files with 17788 additions and 16667 deletions

View File

@@ -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 {

View File

@@ -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,41 @@ 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 -> "@<UID> "; // TODO: make translation keys for offline and online players
case ONLINE -> (player == null) ? "@<UID> " : "[@<UID>] "; // TODO: make translation keys for offline and online players
case PLAYER -> (player == null) ? "@<UID> " : "[@<UID>] ";
};
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());
return translate(player, key);
}
/**
* Called when a player/console invokes a command.
* @param sender The player/console that invoked the command.

View File

@@ -8,9 +8,9 @@ import java.util.*;
@SuppressWarnings({"UnusedReturnValue", "unused"})
public final class CommandMap {
private final Map<String, CommandHandler> commands = new HashMap<>();
private final Map<String, CommandHandler> aliases = new HashMap<>();
private final Map<String, Command> annotations = new HashMap<>();
private final Map<String, CommandHandler> commands = new TreeMap<>();
private final Map<String, CommandHandler> aliases = new TreeMap<>();
private final Map<String, Command> annotations = new TreeMap<>();
private final Map<String, Integer> targetPlayerIds = new HashMap<>();
private static final String consoleId = "console";
@@ -35,6 +35,7 @@ public final class CommandMap {
*/
public CommandMap registerCommand(String label, CommandHandler command) {
Grasscutter.getLogger().debug("Registered command: " + label);
label = label.toLowerCase();
// Get command data.
Command annotation = command.getClass().getAnnotation(Command.class);
@@ -167,7 +168,7 @@ public final class CommandMap {
CommandHandler.sendTranslatedMessage(player, "commands.execution.clear_target");
return true;
}
// Sets default targetPlayer to the UID provided.
try {
int uid = Integer.parseInt(targetUid);
@@ -203,7 +204,7 @@ public final class CommandMap {
// Parse message.
String[] split = rawMessage.split(" ");
List<String> args = new LinkedList<>(Arrays.asList(split));
String label = args.remove(0);
String label = args.remove(0).toLowerCase();
String playerId = (player == null) ? consoleId : player.getAccount().getId();
// Check for special cases - currently only target command.
@@ -237,7 +238,7 @@ public final class CommandMap {
Command annotation = this.annotations.get(label);
// Resolve targetPlayer
try{
try {
targetPlayer = getTargetPlayer(playerId, player, targetPlayer, args);
} catch (IllegalArgumentException e) {
return;

View File

@@ -1,8 +1,10 @@
package emu.grasscutter.command.commands;
import at.favre.lib.crypto.bcrypt.BCrypt;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.config.Configuration;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player;
@@ -11,18 +13,24 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "account", usage = "account <create|delete> <username> [uid]", description = "commands.account.description", targetRequirement = Command.TargetRequirement.NONE)
@Command(
label = "account",
usage = {
"create <username> [<UID>]", // Only with EXPERIMENTAL_RealPassword == false
"delete <username>",
"create <username> <password> [<UID>]", // Only with EXPERIMENTAL_RealPassword == true
"resetpass <username> <password>"}, // Only with EXPERIMENTAL_RealPassword == true
targetRequirement = Command.TargetRequirement.NONE)
public final class AccountCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> 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"));
sendUsageMessage(sender);
return;
}
@@ -31,28 +39,55 @@ public final class AccountCommand implements CommandHandler {
switch (action) {
default:
CommandHandler.sendMessage(null, translate(sender, "commands.account.command_usage"));
sendUsageMessage(sender);
return;
case "create":
int uid = 0;
if (args.size() > 2) {
try {
uid = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(null, translate(sender, "commands.account.invalid"));
String password = "";
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 <username> <password> [uid]");
return;
}
password = args.get(2);
if (args.size() == 4) {
try {
uid = Integer.parseInt(args.get(3));
} catch (NumberFormatException ignored) {
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 <username> <password> [uid]");
}
return;
}
}
} else {
if (args.size() > 2) {
try {
uid = Integer.parseInt(args.get(2));
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.account.invalid"));
return;
}
}
}
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) {
account.setPassword(BCrypt.withDefaults().hashToString(12, password.toCharArray()));
}
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":
@@ -60,20 +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(sender, "resetpass requires EXPERIMENTAL_RealPassword to be true.");
return;
}
if(args.size() != 3) {
CommandHandler.sendMessage(sender, "Invalid Args");
CommandHandler.sendMessage(sender, "Usage: account resetpass <username> <password>");
return;
}
Account toUpdate = DatabaseHelper.getAccountByName(username);
if (toUpdate == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.account.no_account"));
return;
}
// 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(sender, "Password Updated.");
return;
}
}
private void kickAccount(Account account) {
Player player = Grasscutter.getGameServer().getPlayerByAccountId(account.getId());
if (player != null) {
player.getSession().close();
}
}
}

View File

@@ -13,31 +13,30 @@ import java.util.Random;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "announce",
usage = "announce|a <\"tpl\" templateId|\"refresh\"|\"revoke\" templateId|content>",
usage = {"<content>", "refresh", "(tpl|revoke) <templateId>"},
permission = "server.announce",
aliases = {"a"},
description = "commands.announce.description",
targetRequirement = Command.TargetRequirement.NONE)
public final class AnnounceCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
var manager = Grasscutter.getGameServer().getAnnouncementManager();
var manager = Grasscutter.getGameServer().getAnnouncementSystem();
if (args.size() < 1) {
CommandHandler.sendTranslatedMessage(sender, "commands.announce.command_usage");
sendUsageMessage(sender);
return;
}
switch (args.get(0)){
switch (args.get(0)) {
case "tpl":
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.announce.command_usage");
sendUsageMessage(sender);
return;
}
var templateId = Integer.parseInt(args.get(1));
var tpl = manager.getAnnounceConfigItemMap().get(templateId);
if(tpl == null){
if (tpl == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.announce.not_found", templateId));
return;
}
@@ -53,7 +52,7 @@ public final class AnnounceCommand implements CommandHandler {
case "revoke":
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.announce.command_usage");
sendUsageMessage(sender);
return;
}

View File

@@ -10,8 +10,7 @@ import emu.grasscutter.server.game.GameSession;
@Command(
label = "ban",
usage = "ban <@player> [time] [reason]",
description = "commands.ban.description",
usage = {"[<time> [<reason>]]"},
permission = "server.ban",
targetRequirement = Command.TargetRequirement.PLAYER
)

View File

@@ -12,10 +12,11 @@ import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Stream;
@Command(label = "clear", usage = "clear <all|wp|art|mat> [lv<max level>] [r<max refinement>] [<max rarity>*]",
description = "commands.clear.description",
aliases = {"clear"}, permission = "player.clearinv", permissionTargeted = "player.clearinv.others")
@Command(
label = "clear",
usage = {"(all|wp|art|mat) [lv<max level>] [r<max refinement>] [<max rarity>*]"},
permission = "player.clearinv",
permissionTargeted = "player.clearinv.others")
public final class ClearCommand implements CommandHandler {
private static Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java doesn't have raw string literals :(
private static Pattern refineRegex = Pattern.compile("r(\\d+)");
@@ -82,7 +83,7 @@ public final class ClearCommand implements CommandHandler {
}
if (args.size() < 1) {
CommandHandler.sendTranslatedMessage(sender, "commands.clear.command_usage");
sendUsageMessage(sender);
return;
}

View File

@@ -7,9 +7,7 @@ import emu.grasscutter.game.player.Player;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "coop", usage = "coop [host uid]", permission = "server.coop", permissionTargeted = "server.coop.others", description = "commands.coop.description")
@Command(label = "coop", usage = {"[<host UID>]"}, permission = "server.coop", permissionTargeted = "server.coop.others")
public final class CoopCommand implements CommandHandler {
@Override
@@ -17,34 +15,35 @@ public final class CoopCommand implements CommandHandler {
Player host = sender;
switch (args.size()) {
case 0: // Summon target to self
CommandHandler.sendMessage(sender, translate(sender, "commands.coop.usage"));
if (sender == null) // Console doesn't have a self to summon to
if (sender == null) { // Console doesn't have a self to summon to
sendUsageMessage(sender);
return;
}
break;
case 1: // Summon target to argument
try {
int hostId = Integer.parseInt(args.get(0));
host = Grasscutter.getGameServer().getPlayerByUid(hostId);
if (host == null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.execution.player_offline_error"));
CommandHandler.sendTranslatedMessage(sender, "commands.execution.player_offline_error");
return;
}
break;
} catch (NumberFormatException ignored) {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.uid"));
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.uid");
return;
}
default:
CommandHandler.sendMessage(sender, translate(sender, "commands.coop.usage"));
sendUsageMessage(sender);
return;
}
// There's no target==host check but this just places them in multiplayer in their own world which seems fine.
if (targetPlayer.isInMultiplayer()) {
targetPlayer.getServer().getMultiplayerManager().leaveCoop(targetPlayer);
targetPlayer.getServer().getMultiplayerSystem().leaveCoop(targetPlayer);
}
host.getServer().getMultiplayerManager().applyEnterMp(targetPlayer, host.getUid());
targetPlayer.getServer().getMultiplayerManager().applyEnterMpReply(host, targetPlayer.getUid(), true);
CommandHandler.sendMessage(sender, translate(sender, "commands.coop.success", targetPlayer.getNickname(), host.getNickname()));
host.getServer().getMultiplayerSystem().applyEnterMp(targetPlayer, host.getUid());
targetPlayer.getServer().getMultiplayerSystem().applyEnterMpReply(host, targetPlayer.getUid(), true);
CommandHandler.sendTranslatedMessage(sender, "commands.coop.success", targetPlayer.getNickname(), host.getNickname());
}
}

View File

@@ -8,24 +8,24 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "enterdungeon", usage = "enterdungeon <dungeonId>", aliases = {"dungeon"}, permission = "player.enterdungeon", permissionTargeted = "player.enterdungeon.others", description = "commands.enter_dungeon.description")
@Command(label = "enter_dungeon", aliases = {"enterdungeon", "dungeon"}, usage = {"<dungeonId>"}, permission = "player.enterdungeon", permissionTargeted = "player.enterdungeon.others")
public final class EnterDungeonCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.usage"));
sendUsageMessage(sender);
return;
}
try {
int dungeonId = Integer.parseInt(args.get(0));
if (dungeonId == targetPlayer.getSceneId()) {
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
return;
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.in_dungeon_error"));
return;
}
boolean result = targetPlayer.getServer().getDungeonManager().enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId);
boolean result = targetPlayer.getServer().getDungeonSystem().enterDungeon(targetPlayer.getSession().getPlayer(), 0, dungeonId);
if (!result) {
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.not_found_error"));
@@ -33,7 +33,7 @@ public final class EnterDungeonCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.changed", dungeonId));
}
} catch (Exception e) {
CommandHandler.sendMessage(sender, translate(sender, "commands.enter_dungeon.usage"));
sendUsageMessage(sender);
}
}
}

View File

@@ -23,8 +23,15 @@ import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
@Command(label = "give", usage = "give <itemId|avatarId|\"all\"|\"weapons\"|\"mats\"|\"avatars\"> [lv<level>] [r<refinement>] [x<amount>] | give <artifactId> [lv<level>] [x<amount>] [mainPropId] [<appendPropId>[,<times>]]...", aliases = {
"g", "item", "giveitem"}, permission = "player.give", permissionTargeted = "player.give.others", description = "commands.give.description")
@Command(
label = "give",
aliases = {"g", "item", "giveitem"},
usage = {
"(<itemId>|<avatarId>|all|weapons|mats|avatars) [lv<level>] [r<refinement>] [x<amount>] [c<constellation>]",
"<artifactId> [lv<level>] [x<amount>] [<mainPropId>] [<appendPropId>[,<times>]]..."},
permission = "player.give",
permissionTargeted = "player.give.others",
threading = true)
public final class GiveCommand implements CommandHandler {
private static Pattern lvlRegex = Pattern.compile("l(?:vl?)?(\\d+)"); // Java doesn't have raw string literals :(
private static Pattern refineRegex = Pattern.compile("r(\\d+)");
@@ -60,7 +67,7 @@ public final class GiveCommand implements CommandHandler {
public GiveAllType giveAllType = GiveAllType.NONE;
};
private static GiveItemParameters parseArgs(Player sender, List<String> args) throws IllegalArgumentException {
private GiveItemParameters parseArgs(Player sender, List<String> args) throws IllegalArgumentException {
GiveItemParameters param = new GiveItemParameters();
// Extract any tagged arguments (e.g. "lv90", "x100", "r5")
@@ -92,7 +99,7 @@ public final class GiveCommand implements CommandHandler {
// At this point, first remaining argument MUST be itemId/avatarId
if (args.size() < 1) {
CommandHandler.sendTranslatedMessage(sender, "commands.give.usage"); // Reachable if someone does `/give lv90` or similar
sendUsageMessage(sender); // Reachable if someone does `/give lv90` or similar
throw new IllegalArgumentException();
}
String id = args.remove(0);
@@ -162,7 +169,7 @@ public final class GiveCommand implements CommandHandler {
throw e;
}
} else {
CommandHandler.sendTranslatedMessage(sender, "commands.give.usage");
sendUsageMessage(sender);
throw new IllegalArgumentException();
}
}
@@ -173,7 +180,7 @@ public final class GiveCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1) { // *No args*
CommandHandler.sendTranslatedMessage(sender, "commands.give.usage");
sendUsageMessage(sender);
return;
}
try {
@@ -257,7 +264,7 @@ public final class GiveCommand implements CommandHandler {
if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_MALE) {
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(504));
}
else if(avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
else if (avatar.getAvatarId() == GameConstants.MAIN_CHARACTER_FEMALE) {
avatar.setSkillDepotData(GameData.getAvatarSkillDepotDataMap().get(704));
}
@@ -352,11 +359,11 @@ public final class GiveCommand implements CommandHandler {
return affixes;
}
private static int getAppendPropId(String substatText, ItemData itemData) throws IllegalArgumentException {
// If the given substat text is an integer, we just use that as the append prop ID.
try {
return Integer.parseInt(substatText);
} catch (NumberFormatException ignored) {
private static int getAppendPropId(String substatText, ItemData itemData) throws IllegalArgumentException {
// If the given substat text is an integer, we just use that as the append prop ID.
try {
return Integer.parseInt(substatText);
} catch (NumberFormatException ignored) {
// If the argument was not an integer, we try to determine
// the append prop ID from the given text + artifact information.
// A substat string has the format `substat_tier`, with the
@@ -378,8 +385,8 @@ public final class GiveCommand implements CommandHandler {
substatTier -= 1; // 1-indexed to 0-indexed
substatTier = Math.min(Math.max(0, substatTier), substats.size() - 1);
return substats.get(substatTier);
}
}
}
}
private static void parseRelicArgs(GiveItemParameters param, List<String> args) throws IllegalArgumentException {
// Get the main stat from the arguments.
@@ -476,11 +483,11 @@ public final class GiveCommand implements CommandHandler {
10000-10008, 11411, 11506-11508, 12505, 12506, 12508, 12509,
13503, 13506, 14411, 14503, 14505, 14508, 15504-15506
""");
private static final SparseSet illegalRelicIds = new SparseSet("""
20001, 23300-23340, 23383-23385, 78310-78554, 99310-99554
""");
private static final SparseSet illegalItemIds = new SparseSet("""
100086, 100087, 100100-101000, 101106-101110, 101306, 101500-104000,
105001, 105004, 106000-107000, 107011, 108000, 109000-110000,

View File

@@ -11,7 +11,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "heal", usage = "heal|h", aliases = {"h"}, permission = "player.heal", permissionTargeted = "player.heal.others", description = "commands.heal.description")
@Command(label = "heal", aliases = {"h"}, permission = "player.heal", permissionTargeted = "player.heal.others")
public final class HealCommand implements CommandHandler {
@Override

View File

@@ -3,80 +3,81 @@ package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.command.CommandMap;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player;
import java.util.*;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "help", usage = "help [command]", description = "commands.help.description", targetRequirement = Command.TargetRequirement.NONE)
@Command(label = "help", usage = {"[<command>]"}, targetRequirement = Command.TargetRequirement.NONE)
public final class HelpCommand implements CommandHandler {
private final boolean SHOW_COMMANDS_WITHOUT_PERMISSIONS = false; // TODO: Make this into a server config key
private void createCommand(StringBuilder builder, Player player, Command annotation) {
builder.append("\n").append(annotation.label()).append(" - ").append(translate(player, annotation.description()));
builder.append("\n\t").append(translate(player, "commands.help.usage"));
if (annotation.aliases().length >= 1) {
private String createCommand(Player player, CommandHandler command, List<String> args) {
StringBuilder builder = new StringBuilder(command.getLabel())
.append(" - ")
.append(command.getDescriptionString(player))
.append("\n\t")
.append(command.getUsageString(player, args.toArray(new String[0])));
Command annotation = command.getClass().getAnnotation(Command.class);
if (annotation.aliases().length > 0) {
builder.append("\n\t").append(translate(player, "commands.help.aliases"));
for (String alias : annotation.aliases()) {
builder.append(alias).append(" ");
}
}
builder.append("\n\t").append(translate(player, "commands.help.tip_need_permission"));
if(annotation.permission().isEmpty() || annotation.permission().isBlank()) {
builder.append(translate(player, "commands.help.tip_need_no_permission"));
} else {
if (!annotation.permission().isEmpty()) {
builder.append(annotation.permission());
} else {
builder.append(translate(player, "commands.help.tip_need_no_permission"));
}
if(!annotation.permissionTargeted().isEmpty() && !annotation.permissionTargeted().isBlank()) {
if (!annotation.permissionTargeted().isEmpty()) {
String permissionTargeted = annotation.permissionTargeted();
builder.append(" ").append(translate(player, "commands.help.tip_permission_targeted", permissionTargeted));
}
return builder.toString();
}
@Override
public void execute(Player player, Player targetPlayer, List<String> args) {
if (args.size() < 1) {
HashMap<String, CommandHandler> handlers = CommandMap.getInstance().getHandlers();
List<Command> annotations = new ArrayList<>();
Account account = (player == null) ? null : player.getAccount();
Map<String, CommandHandler> handlers = CommandMap.getInstance().getHandlers();
List<String> commands = new ArrayList<>();
List<String> commands_no_permission = new ArrayList<>();
if (args.isEmpty()) {
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);
CommandHandler command = handlers.get(key);
Command annotation = command.getClass().getAnnotation(Command.class);
if (player == null || account.hasPermission(annotation.permission())) {
commands.add(createCommand(player, command, args));
} else if (SHOW_COMMANDS_WITHOUT_PERMISSIONS) {
commands_no_permission.add(createCommand(player, command, args));
}
}
SendAllHelpMessage(player, annotations);
CommandHandler.sendTranslatedMessage(player, "commands.help.available_commands");
} else {
String command = args.get(0);
CommandHandler handler = CommandMap.getInstance().getHandler(command);
StringBuilder builder = new StringBuilder("");
if (handler == null) {
builder.append(translate(player, "commands.generic.command_exist_error"));
String command_str = args.remove(0).toLowerCase();
CommandHandler command = handlers.get(command_str);
if (command == null) {
CommandHandler.sendTranslatedMessage(player, "commands.generic.command_exist_error");
return;
} else {
Command annotation = handler.getClass().getAnnotation(Command.class);
this.createCommand(builder, player, annotation);
if (player != null && !Objects.equals(annotation.permission(), "") && !player.getAccount().hasPermission(annotation.permission())) {
builder.append("\n\t").append(translate(player, "commands.help.warn_player_has_no_permission"));
Command annotation = command.getClass().getAnnotation(Command.class);
if (player == null || account.hasPermission(annotation.permission())) {
commands.add(createCommand(player, command, args));
} else {
commands_no_permission.add(createCommand(player, command, args));
}
}
CommandHandler.sendMessage(player, builder.toString());
}
}
void SendAllHelpMessage(Player player, List<Command> annotations) {
StringBuilder builder = new StringBuilder(translate(player, "commands.help.available_commands"));
annotations.forEach(annotation -> {
this.createCommand(builder, player, annotation);
builder.append("\n");
});
CommandHandler.sendMessage(player, builder.toString());
for (String s : commands)
CommandHandler.sendMessage(player, s);
for (String s : commands_no_permission)
CommandHandler.sendMessage(player, s + "\n\t" + translate(player, "commands.help.warn_player_has_no_permission"));
}
}

View File

@@ -6,19 +6,18 @@ import emu.grasscutter.game.player.Player;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "kick", usage = "kick", aliases = {"restart"}, permissionTargeted = "server.kick", description = "commands.kick.description")
@Command(label = "kick", aliases = {"restart"}, permissionTargeted = "server.kick")
public final class KickCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (sender != null) {
CommandHandler.sendMessage(sender, translate(sender, "commands.kick.player_kick_player",
CommandHandler.sendTranslatedMessage(sender, "commands.kick.player_kick_player",
Integer.toString(sender.getUid()), sender.getAccount().getUsername(),
Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername()));
Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername());
} else {
CommandHandler.sendMessage(null, translate(sender, "commands.kick.server_kick_player", Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername()));
CommandHandler.sendTranslatedMessage(sender, "commands.kick.server_kick_player",
Integer.toString(targetPlayer.getUid()), targetPlayer.getAccount().getUsername());
}
targetPlayer.getSession().close();

View File

@@ -1,6 +1,5 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.entity.EntityMonster;
@@ -12,7 +11,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "killall", usage = "killall [sceneId]", permission = "server.killall", permissionTargeted = "server.killall.others", description = "commands.killall.description")
@Command(label = "killall", usage = {"[<sceneId>]"}, permission = "server.killall", permissionTargeted = "server.killall.others")
public final class KillAllCommand implements CommandHandler {
@Override
@@ -26,7 +25,7 @@ public final class KillAllCommand implements CommandHandler {
scene = targetPlayer.getWorld().getSceneById(Integer.parseInt(args.get(0)));
break;
default:
CommandHandler.sendMessage(sender, translate(sender, "commands.killall.usage"));
sendUsageMessage(sender);
return;
}
} catch (NumberFormatException ignored) {

View File

@@ -13,16 +13,11 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "killcharacter", usage = "killcharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", permissionTargeted = "player.killcharacter.others", description = "commands.killCharacter.description")
@Command(label = "killCharacter", aliases = {"suicide", "kill"}, permission = "player.killcharacter", permissionTargeted = "player.killcharacter.others")
public final class KillCharacterCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
CommandHandler.sendMessage(sender, translate(sender, "commands.killCharacter.usage"));
return;
}
EntityAvatar entity = targetPlayer.getTeamManager().getCurrentAvatarEntity();
entity.setFightProperty(FightProperty.FIGHT_PROP_CUR_HP, 0f);
// Packets

View File

@@ -3,8 +3,6 @@ package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.database.DatabaseHelper;
import emu.grasscutter.game.Account;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.utils.Utils;
@@ -13,7 +11,7 @@ import java.util.Locale;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "language", usage = "language [language code]", description = "commands.language.description", aliases = {"lang"}, targetRequirement = Command.TargetRequirement.NONE)
@Command(label = "language", usage = {"[<language code>]"}, aliases = {"lang"}, targetRequirement = Command.TargetRequirement.NONE)
public final class LanguageCommand implements CommandHandler {
@Override

View File

@@ -10,7 +10,7 @@ import java.util.Map;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "list", usage = "list [uid]", aliases = {"players"}, description = "commands.list.description", targetRequirement = Command.TargetRequirement.NONE)
@Command(label = "list", aliases = {"players"}, usage = {"[<UID>]"}, targetRequirement = Command.TargetRequirement.NONE)
public final class ListCommand implements CommandHandler {
@Override

View File

@@ -11,13 +11,13 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "permission", usage = "permission <add|remove> <permission>", permission = "permission", description = "commands.permission.description", targetRequirement = TargetRequirement.PLAYER)
@Command(label = "permission", usage = "(add|remove) <permission>", permission = "permission", targetRequirement = TargetRequirement.PLAYER)
public final class PermissionCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.usage"));
sendUsageMessage(sender);
return;
}
@@ -37,7 +37,7 @@ public final class PermissionCommand implements CommandHandler {
switch (action) {
default:
CommandHandler.sendMessage(sender, translate(sender, "commands.permission.usage"));
sendUsageMessage(sender);
break;
case "add":
if (account.addPermission(permission)) {

View File

@@ -7,16 +7,14 @@ import emu.grasscutter.utils.Position;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "position", usage = "position", aliases = {"pos"}, description = "commands.position.description")
@Command(label = "position", aliases = {"pos"})
public final class PositionCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
Position pos = targetPlayer.getPos();
CommandHandler.sendMessage(sender, translate(sender, "commands.position.success",
Position pos = targetPlayer.getPosition();
CommandHandler.sendTranslatedMessage(sender, "commands.position.success",
Float.toString(pos.getX()), Float.toString(pos.getY()), Float.toString(pos.getZ()),
Integer.toString(targetPlayer.getSceneId())));
Integer.toString(targetPlayer.getSceneId()));
}
}

View File

@@ -1,6 +1,5 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
@@ -10,13 +9,13 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "quest", usage = "quest <add|finish> [questId]", permission = "player.quest", permissionTargeted = "player.quest.others", description = "commands.quest.description")
@Command(label = "quest", usage = {"(add|finish) [<questId>]"}, permission = "player.quest", permissionTargeted = "player.quest.others")
public final class QuestCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage"));
sendUsageMessage(sender);
return;
}
@@ -54,7 +53,7 @@ public final class QuestCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.finished", questId));
}
default -> {
CommandHandler.sendMessage(sender, translate(sender, "commands.quest.usage"));
sendUsageMessage(sender);
}
}
}

View File

@@ -9,19 +9,19 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "reload", usage = "reload", permission = "server.reload", description = "commands.reload.description", targetRequirement = Command.TargetRequirement.NONE)
@Command(label = "reload", permission = "server.reload", targetRequirement = Command.TargetRequirement.NONE)
public final class ReloadCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_start"));
Grasscutter.loadConfig();
Grasscutter.loadLanguage();
Grasscutter.getGameServer().getGachaManager().load();
Grasscutter.getGameServer().getDropManager().load();
Grasscutter.getGameServer().getShopManager().load();
Grasscutter.getGameServer().getGachaSystem().load();
Grasscutter.getGameServer().getDropSystem().load();
Grasscutter.getGameServer().getShopSystem().load();
CommandHandler.sendMessage(sender, translate(sender, "commands.reload.reload_done"));
}
}

View File

@@ -10,8 +10,12 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "resetconst", usage = "resetconst [all]",
aliases = {"resetconstellation"}, permission = "player.resetconstellation", permissionTargeted = "player.resetconstellation.others", description = "commands.resetConst.description")
@Command(
label = "resetConst",
aliases = {"resetconstellation"},
usage = "[all]",
permission = "player.resetconstellation",
permissionTargeted = "player.resetconstellation.others")
public final class ResetConstCommand implements CommandHandler {
@Override

View File

@@ -9,16 +9,11 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "resetshop", usage = "resetshop", permission = "server.resetshop", permissionTargeted = "server.resetshop.others", description = "commands.resetShopLimit.description")
@Command(label = "resetShopLimit", aliases = {"resetshop"}, permission = "server.resetshop", permissionTargeted = "server.resetshop.others")
public final class ResetShopLimitCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.usage"));
return;
}
targetPlayer.getShopLimit().forEach(x -> x.setNextRefreshTime(0));
targetPlayer.save();
CommandHandler.sendMessage(sender, translate(sender, "commands.resetShopLimit.success"));

View File

@@ -13,7 +13,11 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@SuppressWarnings("ConstantConditions")
@Command(label = "sendmail", usage = "sendmail <userId|all|help> [templateId]", permission = "server.sendmail", description = "commands.sendMail.description", targetRequirement = Command.TargetRequirement.NONE)
@Command(
label = "sendMail",
usage = {"(<userId>|all) [<templateId>]", "help"},
permission = "server.sendmail",
targetRequirement = Command.TargetRequirement.NONE)
public final class SendMailCommand implements CommandHandler {
// TODO: You should be able to do /sendmail and then just send subsequent messages until you finish
@@ -27,7 +31,7 @@ public final class SendMailCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
int senderId;
if(sender != null) {
if (sender != null) {
senderId = sender.getUid();
} else {
senderId = -1;
@@ -39,7 +43,7 @@ public final class SendMailCommand implements CommandHandler {
MailBuilder mailBuilder;
switch (args.get(0).toLowerCase()) {
case "help" -> {
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMail.usage"));
sendUsageMessage(sender);
return;
}
case "all" -> mailBuilder = new MailBuilder(true, new Mail());
@@ -146,7 +150,7 @@ public final class SendMailCommand implements CommandHandler {
}
break;
default: // *No args*
CommandHandler.sendMessage(sender, translate(sender, "commands.give.usage"));
CommandHandler.sendTranslatedMessage(sender, "commands.sendMail.give_usage");
return;
}
mailBuilder.mail.itemList.add(new Mail.MailItem(item, amount, lvl));
@@ -162,7 +166,7 @@ public final class SendMailCommand implements CommandHandler {
}
private String getConstructionArgs(int stage, Player sender) {
return switch(stage) {
return switch (stage) {
case 0 -> translate(sender, "commands.sendMail.title");
case 1 -> translate(sender, "commands.sendMail.message");
case 2 -> translate(sender, "commands.sendMail.sender");

View File

@@ -8,16 +8,19 @@ import emu.grasscutter.game.player.Player;
import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "sendmessage", usage = "sendmessage <message>",
aliases = {"say", "sendservmsg", "sendservermessage", "b", "broadcast"}, permission = "server.sendmessage", permissionTargeted = "server.sendmessage.others", description = "commands.sendMessage.description", targetRequirement = TargetRequirement.NONE)
@Command(
label = "sendMessage",
aliases = {"say", "sendservmsg", "sendservermessage", "b", "broadcast"},
usage = {"<message>"},
permission = "server.sendmessage",
permissionTargeted = "server.sendmessage.others",
targetRequirement = TargetRequirement.NONE)
public final class SendMessageCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() == 0) {
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMessage.usage"));
sendUsageMessage(sender);
return;
}
@@ -30,6 +33,6 @@ public final class SendMessageCommand implements CommandHandler {
} else {
CommandHandler.sendMessage(targetPlayer, message);
}
CommandHandler.sendMessage(sender, translate(sender, "commands.sendMessage.success"));
CommandHandler.sendTranslatedMessage(sender, "commands.sendMessage.success");
}
}

View File

@@ -11,14 +11,18 @@ import emu.grasscutter.server.packet.send.PacketAvatarFetterDataNotify;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "setfetterlevel", usage = "setfetterlevel <level>",
aliases = {"setfetterlvl", "setfriendship"}, permission = "player.setfetterlevel", permissionTargeted = "player.setfetterlevel.others", description = "commands.setFetterLevel.description")
@Command(
label = "setFetterLevel",
usage = {"<level>"},
aliases = {"setfetterlvl", "setfriendship"},
permission = "player.setfetterlevel",
permissionTargeted = "player.setfetterlevel.others")
public final class SetFetterLevelCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 1) {
CommandHandler.sendMessage(sender, translate(sender, "commands.setFetterLevel.usage"));
sendUsageMessage(sender);
return;
}

View File

@@ -1,205 +1,205 @@
package emu.grasscutter.command.commands;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
@Command(label = "setprop", usage = "setprop|prop <prop> <value>", aliases = {"prop"}, permission = "player.setprop", permissionTargeted = "player.setprop.others", description = "commands.setProp.description")
public final class SetPropCommand implements CommandHandler {
static enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
NO_STAMINA,
UNLIMITED_ENERGY
}
static class Prop {
String name;
PlayerProperty prop;
PseudoProp pseudoProp;
public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE);
}
public Prop(String name) {
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
}
public Prop(String name, PseudoProp pseudoProp) {
this(name, PlayerProperty.PROP_NONE, pseudoProp);
}
public Prop(String name, PlayerProperty prop) {
this(name, prop, PseudoProp.NONE);
}
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
this.name = name;
this.prop = prop;
this.pseudoProp = pseudoProp;
}
}
Map<String, Prop> props;
public SetPropCommand() {
this.props = new HashMap<>();
// Full PlayerProperty enum that won't be advertised but can be used by devs
for (PlayerProperty prop : PlayerProperty.values()) {
String name = prop.toString().substring(5); // PROP_EXP -> EXP
String key = name.toLowerCase(); // EXP -> exp
this.props.put(key, new Prop(name, prop));
}
// Add special props
Prop worldlevel = new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
this.props.put("worldlevel", worldlevel);
this.props.put("wl", worldlevel);
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
this.props.put("abyss", abyss);
this.props.put("abyssfloor", abyss);
this.props.put("ut", abyss);
this.props.put("tower", abyss);
this.props.put("towerlevel", abyss);
this.props.put("unlocktower", abyss);
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
this.props.put("bplevel", bplevel);
this.props.put("bp", bplevel);
this.props.put("battlepass", bplevel);
Prop godmode = new Prop("godmode", PseudoProp.GOD_MODE);
this.props.put("godmode", godmode);
this.props.put("god", godmode);
Prop nostamina = new Prop("nostamina", PseudoProp.NO_STAMINA);
this.props.put("nostamina", nostamina);
this.props.put("nostam", nostamina);
this.props.put("ns", nostamina);
Prop unlimitedenergy = new Prop("unlimitedenergy", PseudoProp.UNLIMITED_ENERGY);
this.props.put("unlimitedenergy", unlimitedenergy);
this.props.put("ue", unlimitedenergy);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
CommandHandler.sendTranslatedMessage(sender, "commands.setProp.usage");
return;
}
try {
value = switch(valueStr.toLowerCase()) {
case "on", "true" -> 1;
case "off", "false" -> 0;
case "toggle" -> -1;
default -> Integer.parseInt(valueStr);
};
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return;
}
boolean success = false;
Prop prop = props.get(propStr);
success = switch (prop.pseudoProp) {
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
case GOD_MODE, NO_STAMINA, UNLIMITED_ENERGY -> this.setBool(sender, targetPlayer, prop.pseudoProp, value);
default -> targetPlayer.setProperty(prop.prop, value);
};
if (success) {
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_to", prop.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
}
} else {
if (prop.prop != PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
String min = Integer.toString(targetPlayer.getPropertyMin(prop.prop));
String max = Integer.toString(targetPlayer.getPropertyMax(prop.prop));
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", prop.name, min, max);
}
}
}
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
List<Integer> floorIds = targetPlayer.getServer().getTowerScheduleManager().getAllFloors();
if (topFloor < 0 || topFloor > floorIds.size()) {
String min = Integer.toString(0);
String max = Integer.toString(floorIds.size());
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", "Tower Level", min, max);
return false;
}
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
// Add records for each unlocked floor
for (int floor : floorIds.subList(0, topFloor)) {
if (!recordMap.containsKey(floor)) {
recordMap.put(floor, new TowerLevelRecord(floor));
}
}
// Remove records for each floor past our target
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
if (recordMap.containsKey(floor)) {
recordMap.remove(floor);
}
}
// Six stars required on Floor 8 to unlock Floor 9+
if (topFloor > 8) {
recordMap.get(floorIds.get(7)).setLevelStars(0, 6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at all
}
return true;
}
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
boolean enabled = switch (pseudoProp) {
case GOD_MODE -> targetPlayer.inGodmode();
case NO_STAMINA -> targetPlayer.getUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
default -> false;
};
enabled = switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setGodmode(enabled);
break;
case NO_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
}
package emu.grasscutter.command.commands;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.PlayerProperty;
import emu.grasscutter.game.tower.TowerLevelRecord;
@Command(label = "setProp", aliases = {"prop"}, usage = {"<prop> <value>"}, permission = "player.setprop", permissionTargeted = "player.setprop.others")
public final class SetPropCommand implements CommandHandler {
static enum PseudoProp {
NONE,
WORLD_LEVEL,
TOWER_LEVEL,
BP_LEVEL,
GOD_MODE,
NO_STAMINA,
UNLIMITED_ENERGY
}
static class Prop {
String name;
PlayerProperty prop;
PseudoProp pseudoProp;
public Prop(PlayerProperty prop) {
this(prop.toString(), prop, PseudoProp.NONE);
}
public Prop(String name) {
this(name, PlayerProperty.PROP_NONE, PseudoProp.NONE);
}
public Prop(String name, PseudoProp pseudoProp) {
this(name, PlayerProperty.PROP_NONE, pseudoProp);
}
public Prop(String name, PlayerProperty prop) {
this(name, prop, PseudoProp.NONE);
}
public Prop(String name, PlayerProperty prop, PseudoProp pseudoProp) {
this.name = name;
this.prop = prop;
this.pseudoProp = pseudoProp;
}
}
Map<String, Prop> props;
public SetPropCommand() {
this.props = new HashMap<>();
// Full PlayerProperty enum that won't be advertised but can be used by devs
for (PlayerProperty prop : PlayerProperty.values()) {
String name = prop.toString().substring(5); // PROP_EXP -> EXP
String key = name.toLowerCase(); // EXP -> exp
this.props.put(key, new Prop(name, prop));
}
// Add special props
Prop worldlevel = new Prop("World Level", PlayerProperty.PROP_PLAYER_WORLD_LEVEL, PseudoProp.WORLD_LEVEL);
this.props.put("worldlevel", worldlevel);
this.props.put("wl", worldlevel);
Prop abyss = new Prop("Tower Level", PseudoProp.TOWER_LEVEL);
this.props.put("abyss", abyss);
this.props.put("abyssfloor", abyss);
this.props.put("ut", abyss);
this.props.put("tower", abyss);
this.props.put("towerlevel", abyss);
this.props.put("unlocktower", abyss);
Prop bplevel = new Prop("BP Level", PseudoProp.BP_LEVEL);
this.props.put("bplevel", bplevel);
this.props.put("bp", bplevel);
this.props.put("battlepass", bplevel);
Prop godmode = new Prop("godmode", PseudoProp.GOD_MODE);
this.props.put("godmode", godmode);
this.props.put("god", godmode);
Prop nostamina = new Prop("nostamina", PseudoProp.NO_STAMINA);
this.props.put("nostamina", nostamina);
this.props.put("nostam", nostamina);
this.props.put("ns", nostamina);
Prop unlimitedenergy = new Prop("unlimitedenergy", PseudoProp.UNLIMITED_ENERGY);
this.props.put("unlimitedenergy", unlimitedenergy);
this.props.put("ue", unlimitedenergy);
}
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() != 2) {
sendUsageMessage(sender);
return;
}
String propStr = args.get(0).toLowerCase();
String valueStr = args.get(1).toLowerCase();
int value;
if (!props.containsKey(propStr)) {
sendUsageMessage(sender);
return;
}
try {
value = switch (valueStr.toLowerCase()) {
case "on", "true" -> 1;
case "off", "false" -> 0;
case "toggle" -> -1;
default -> Integer.parseInt(valueStr);
};
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.execution.argument_error");
return;
}
boolean success = false;
Prop prop = props.get(propStr);
success = switch (prop.pseudoProp) {
case WORLD_LEVEL -> targetPlayer.setWorldLevel(value);
case BP_LEVEL -> targetPlayer.getBattlePassManager().setLevel(value);
case TOWER_LEVEL -> this.setTowerLevel(sender, targetPlayer, value);
case GOD_MODE, NO_STAMINA, UNLIMITED_ENERGY -> this.setBool(sender, targetPlayer, prop.pseudoProp, value);
default -> targetPlayer.setProperty(prop.prop, value);
};
if (success) {
if (targetPlayer == sender) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_to", prop.name, valueStr);
} else {
String uidStr = targetPlayer.getAccount().getId();
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", prop.name, uidStr, valueStr);
}
} else {
if (prop.prop != PlayerProperty.PROP_NONE) { // PseudoProps need to do their own error messages
String min = Integer.toString(targetPlayer.getPropertyMin(prop.prop));
String max = Integer.toString(targetPlayer.getPropertyMax(prop.prop));
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", prop.name, min, max);
}
}
}
private boolean setTowerLevel(Player sender, Player targetPlayer, int topFloor) {
List<Integer> floorIds = targetPlayer.getServer().getTowerSystem().getAllFloors();
if (topFloor < 0 || topFloor > floorIds.size()) {
String min = Integer.toString(0);
String max = Integer.toString(floorIds.size());
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.value_between", "Tower Level", min, max);
return false;
}
Map<Integer, TowerLevelRecord> recordMap = targetPlayer.getTowerManager().getRecordMap();
// Add records for each unlocked floor
for (int floor : floorIds.subList(0, topFloor)) {
if (!recordMap.containsKey(floor)) {
recordMap.put(floor, new TowerLevelRecord(floor));
}
}
// Remove records for each floor past our target
for (int floor : floorIds.subList(topFloor, floorIds.size())) {
if (recordMap.containsKey(floor)) {
recordMap.remove(floor);
}
}
// Six stars required on Floor 8 to unlock Floor 9+
if (topFloor > 8) {
recordMap.get(floorIds.get(7)).setLevelStars(0, 6); // levelIds seem to start at 1 for Floor 1 Chamber 1, so this doesn't get shown at all
}
return true;
}
private boolean setBool(Player sender, Player targetPlayer, PseudoProp pseudoProp, int value) {
boolean enabled = switch (pseudoProp) {
case GOD_MODE -> targetPlayer.inGodmode();
case NO_STAMINA -> targetPlayer.getUnlimitedStamina();
case UNLIMITED_ENERGY -> !targetPlayer.getEnergyManager().getEnergyUsage();
default -> false;
};
enabled = switch (value) {
case -1 -> !enabled;
case 0 -> false;
default -> true;
};
switch (pseudoProp) {
case GOD_MODE:
targetPlayer.setGodmode(enabled);
break;
case NO_STAMINA:
targetPlayer.setUnlimitedStamina(enabled);
break;
case UNLIMITED_ENERGY:
targetPlayer.getEnergyManager().setEnergyUsage(!enabled);
break;
default:
return false;
}
return true;
}
}

View File

@@ -11,7 +11,7 @@ import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.server.packet.send.PacketEntityFightPropUpdateNotify;
@Command(label = "setstats", usage = "setstats|stats <stat> <value>", aliases = {"stats"}, permission = "player.setstats", permissionTargeted = "player.setstats.others", description = "commands.setStats.description")
@Command(label = "setStats", aliases = {"stats", "stat"}, usage = {"<stat> <value>"}, permission = "player.setstats", permissionTargeted = "player.setstats.others")
public final class SetStatsCommand implements CommandHandler {
static class Stat {
String name;
@@ -71,7 +71,7 @@ public final class SetStatsCommand implements CommandHandler {
statStr = args.get(0).toLowerCase();
valueStr = args.get(1);
} else {
CommandHandler.sendTranslatedMessage(sender, "commands.setStats.usage");
sendUsageMessage(sender);
return;
}
@@ -105,7 +105,7 @@ public final class SetStatsCommand implements CommandHandler {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.set_for_to", stat.name, uidStr, valueStr);
}
} else {
CommandHandler.sendTranslatedMessage(sender, "commands.setStats.usage");
sendUsageMessage(sender);
}
return;
}

View File

@@ -1,29 +1,28 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.GameData;
import emu.grasscutter.data.excels.AvatarData;
import emu.grasscutter.data.excels.GadgetData;
import emu.grasscutter.data.excels.ItemData;
import emu.grasscutter.data.excels.MonsterData;
import emu.grasscutter.game.avatar.Avatar;
import emu.grasscutter.game.entity.*;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.EntityType;
import emu.grasscutter.game.props.FightProperty;
import emu.grasscutter.utils.Position;
import emu.grasscutter.game.world.Scene;
import javax.swing.text.html.parser.Entity;
import java.util.List;
import java.util.Random;
import static emu.grasscutter.Configuration.*;
import static emu.grasscutter.config.Configuration.*;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "spawn", usage = "spawn <entityId> [amount] [level(monster only)] [<x> <y> <z>(monster only, optional)]", aliases = {"drop"}, permission = "server.spawn", permissionTargeted = "server.spawn.others", description = "commands.spawn.description")
@Command(
label = "spawn",
usage = {"spawn <entityId> [amount] [level(monster only)] [<x> <y> <z>(monster only)]"},
aliases = {"drop"},
permission = "server.spawn",
permissionTargeted = "server.spawn.others")
public final class SpawnCommand implements CommandHandler {
@Override
@@ -31,7 +30,7 @@ public final class SpawnCommand implements CommandHandler {
int id = 0; // This is just to shut up the linter, it's not a real default
int amount = 1;
int level = 1;
float x = 0, y = 0, z = 0;
float x = 0, y = 0, z = 0;
switch (args.size()) {
case 6:
try {
@@ -61,10 +60,10 @@ public final class SpawnCommand implements CommandHandler {
}
break;
default:
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.usage"));
sendUsageMessage(sender);
return;
}
MonsterData monsterData = GameData.getMonsterDataMap().get(id);
GadgetData gadgetData = GameData.getGadgetDataMap().get(id);
ItemData itemData = GameData.getItemDataMap().get(id);
@@ -72,21 +71,21 @@ public final class SpawnCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.generic.invalid.entityId"));
return;
}
Scene scene = targetPlayer.getScene();
if (scene.getEntities().size() + amount > GAME_OPTIONS.sceneEntityLimit) {
amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0);
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount));
if (amount <= 0) {
return;
}
amount = Math.max(Math.min(GAME_OPTIONS.sceneEntityLimit - scene.getEntities().size(), amount), 0);
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.limit_reached", amount));
if (amount <= 0) {
return;
}
}
double maxRadius = Math.sqrt(amount * 0.2 / Math.PI);
for (int i = 0; i < amount; i++) {
Position pos = GetRandomPositionInCircle(targetPlayer.getPos(), maxRadius).addY(3);
if(x != 0 && y != 0 && z != 0) {
Position pos = GetRandomPositionInCircle(targetPlayer.getPosition(), maxRadius).addY(3);
if (x != 0 && y != 0 && z != 0) {
pos = GetRandomPositionInCircle(new Position(x, y, z), maxRadius).addY(3);
}
GameEntity entity = null;
@@ -120,7 +119,7 @@ public final class SpawnCommand implements CommandHandler {
CommandHandler.sendMessage(sender, translate(sender, "commands.spawn.success", Integer.toString(amount), Integer.toString(id)));
}
private Position GetRandomPositionInCircle(Position origin, double radius){
private Position GetRandomPositionInCircle(Position origin, double radius) {
Position target = origin.clone();
double angle = Math.random() * 360;
double r = Math.sqrt(Math.random() * radius * radius);

View File

@@ -9,7 +9,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "stop", usage = "stop", permission = "server.stop", description = "commands.stop.description", targetRequirement = Command.TargetRequirement.NONE)
@Command(label = "stop", permission = "server.stop", targetRequirement = Command.TargetRequirement.NONE)
public final class StopCommand implements CommandHandler {
@Override

View File

@@ -1,6 +1,5 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.data.excels.AvatarSkillDepotData;
@@ -14,7 +13,11 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "talent", usage = "talent <talentId> <value>", permission = "player.settalent", permissionTargeted = "player.settalent.others", description = "commands.talent.description")
@Command(
label = "talent",
usage = {"set <talentId> <level>", "(n|e|q) <level>", "getid"},
permission = "player.settalent",
permissionTargeted = "player.settalent.others")
public final class TalentCommand implements CommandHandler {
private void setTalentLevel(Player sender, Player player, Avatar avatar, int talentId, int talentLevel) {
int oldLevel = avatar.getSkillLevelMap().get(talentId);
@@ -46,9 +49,7 @@ public final class TalentCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 1){
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_1"));
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_2"));
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_3"));
sendUsageMessage(sender);
return;
}
@@ -57,15 +58,13 @@ public final class TalentCommand implements CommandHandler {
String cmdSwitch = args.get(0);
switch (cmdSwitch) {
default -> {
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_1"));
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_2"));
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_3"));
sendUsageMessage(sender);
return;
}
case "set" -> {
if (args.size() < 3) {
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_1"));
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_3"));
sendUsageMessage(sender);
sendUsageMessage(sender);
return;
}
try {
@@ -79,7 +78,7 @@ public final class TalentCommand implements CommandHandler {
}
case "n", "e", "q" -> {
if (args.size() < 2) {
CommandHandler.sendMessage(sender, translate(sender, "commands.talent.usage_2"));
sendUsageMessage(sender);
return;
}
AvatarSkillDepotData SkillDepot = avatar.getData().getSkillDepot();

View File

@@ -1,254 +1,259 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
import java.util.List;
import java.util.ArrayList;
import java.util.HashSet;
import static emu.grasscutter.Configuration.*;
@Command(label = "team", usage = "team <add|remove|set> [avatarId,...] [index|first|last|index-index,...]",
permission = "player.team", permissionTargeted = "player.team.others", description = "commands.team.description")
public final class TeamCommand implements CommandHandler {
private static final int BASE_AVATARID = 10000000;
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
return;
}
switch (args.get(0)) {
case "add":
if (!addCommand(sender, targetPlayer, args)) return;
break;
case "remove":
if (!removeCommand(sender, targetPlayer, args)) return;
break;
case "set":
if (!setCommand(sender, targetPlayer, args)) return;
break;
default:
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.usage");
return;
}
targetPlayer.getTeamManager().updateTeamEntities(
new PacketChangeMpTeamAvatarRsp(targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
}
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_usage");
return false;
}
int index = -1;
if (args.size() > 2) {
try {
index = Integer.parseInt(args.get(2)) - 1;
if (index < 0) index = 0;
} catch (Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
return false;
}
}
var avatarIds = args.get(1).split(",");
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
return false;
}
for (var avatarId: avatarIds) {
int id = Integer.parseInt(avatarId);
var success = addAvatar(sender, targetPlayer, id, index);
if (index > 0) ++index;
}
return true;
}
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_usage");
return false;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
var avatarCount = currentTeamAvatars.size();
var metaIndexList = args.get(1).split(",");
var indexes = new HashSet<Integer>();
var ignoreList = new ArrayList<Integer>();
for (var metaIndex: metaIndexList) {
// step 1: parse metaIndex to indexes
var subIndexes = transformToIndexes(metaIndex, avatarCount);
if (subIndexes == null) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", metaIndex);
continue;
}
// step 2: get all of the avatar id through indexes
for (var avatarIndex: subIndexes) {
try {
indexes.add(currentTeamAvatars.get(avatarIndex - 1));
} catch (Exception e) {
ignoreList.add(avatarIndex);
continue;
}
}
}
// step 3: check if user remove all of the avatar
if (indexes.size() >= avatarCount) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
return false;
}
// step 4: hint user for ignore index
if (!ignoreList.isEmpty()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
}
// step 5: remove
currentTeamAvatars.removeAll(indexes);
return true;
}
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 3) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
CommandHandler.sendTranslatedMessage(sender, "commands.team.set_usage");
return false;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
int index;
try {
index = Integer.parseInt(args.get(1)) - 1;
if (index < 0) index = 0;
} catch(Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", args.get(1));
return false;
}
if (index + 1 > currentTeamAvatars.size()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
return false;
}
int avatarId;
try {
avatarId = Integer.parseInt(args.get(2));
} catch(Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_parse_avatar_id", args.get(2));
return false;
}
if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID;
}
if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
return false;
}
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false;
}
currentTeamAvatars.set(index, avatarId);
return true;
}
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
return false;
}
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false;
}
if (index < 0) {
currentTeamAvatars.add(avatarId);
} else {
currentTeamAvatars.add(index, avatarId);
}
return true;
}
private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
// step 1: check if metaIndexes is a special constants
if (metaIndexes.equals("first")) {
return List.of(1);
} else if (metaIndexes.equals("last")) {
return List.of(listLength);
}
// step 2: check if metaIndexes is a range
if (metaIndexes.contains("-")) {
var range = metaIndexes.split("-");
if (range.length < 2) {
return null;
}
int min, max;
try {
min = switch (range[0]) {
case "first" -> 1;
case "last" -> listLength;
default -> Integer.parseInt(range[0]);
};
max = switch (range[1]) {
case "first" -> 1;
case "last" -> listLength;
default -> Integer.parseInt(range[1]);
};
} catch (Exception e) {
return null;
}
if (min > max) {
min ^= max;
max ^= min;
min ^= max;
}
var indexes = new ArrayList<Integer>();
for (int i = min; i <= max; ++i) {
indexes.add(i);
}
return indexes;
}
// step 3: index is a value, simply return
try {
int index = Integer.parseInt(metaIndexes);
return List.of(index);
} catch (Exception e) {
return null;
}
}
}
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.server.packet.send.PacketChangeMpTeamAvatarRsp;
import java.util.List;
import static emu.grasscutter.config.Configuration.*;
import java.util.ArrayList;
import java.util.HashSet;
@Command(
label = "team",
usage = {"add <avatarId,...>", "(remove|set) [index|first|last|index-index,...]"},
permission = "player.team",
permissionTargeted = "player.team.others")
public final class TeamCommand implements CommandHandler {
private static final int BASE_AVATARID = 10000000;
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
if (args.isEmpty()) {
sendUsageMessage(sender);
return;
}
switch (args.get(0)) {
case "add":
if (!addCommand(sender, targetPlayer, args)) return;
break;
case "remove":
if (!removeCommand(sender, targetPlayer, args)) return;
break;
case "set":
if (!setCommand(sender, targetPlayer, args)) return;
break;
default:
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender);
return;
}
targetPlayer.getTeamManager().updateTeamEntities(
new PacketChangeMpTeamAvatarRsp(targetPlayer, targetPlayer.getTeamManager().getCurrentTeamInfo()));
}
private boolean addCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender);
return false;
}
int index = -1;
if (args.size() > 2) {
try {
index = Integer.parseInt(args.get(2)) - 1;
if (index < 0) index = 0;
} catch (Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_index");
return false;
}
}
var avatarIds = args.get(1).split(",");
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.size() + avatarIds.length > GAME_OPTIONS.avatarLimits.singlePlayerTeam) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.add_too_much", GAME_OPTIONS.avatarLimits.singlePlayerTeam);
return false;
}
for (var avatarId: avatarIds) {
int id = Integer.parseInt(avatarId);
if (!addAvatar(sender, targetPlayer, id, index))
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_add_avatar", avatarId);
if (index > 0) ++index;
}
return true;
}
private boolean removeCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 2) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender);
return false;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
var avatarCount = currentTeamAvatars.size();
var metaIndexList = args.get(1).split(",");
var indexes = new HashSet<Integer>();
var ignoreList = new ArrayList<Integer>();
for (var metaIndex: metaIndexList) {
// step 1: parse metaIndex to indexes
var subIndexes = transformToIndexes(metaIndex, avatarCount);
if (subIndexes == null) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", metaIndex);
continue;
}
// step 2: get all of the avatar id through indexes
for (var avatarIndex: subIndexes) {
try {
indexes.add(currentTeamAvatars.get(avatarIndex - 1));
} catch (Exception e) {
ignoreList.add(avatarIndex);
continue;
}
}
}
// step 3: check if user remove all of the avatar
if (indexes.size() >= avatarCount) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.remove_too_much");
return false;
}
// step 4: hint user for ignore index
if (!ignoreList.isEmpty()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.ignore_index", ignoreList);
}
// step 5: remove
currentTeamAvatars.removeAll(indexes);
return true;
}
private boolean setCommand(Player sender, Player targetPlayer, List<String> args) {
if (args.size() < 3) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.invalid_usage");
sendUsageMessage(sender);
return false;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
int index;
try {
index = Integer.parseInt(args.get(1)) - 1;
if (index < 0) index = 0;
} catch (Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_to_parse_index", args.get(1));
return false;
}
if (index + 1 > currentTeamAvatars.size()) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.index_out_of_range");
return false;
}
int avatarId;
try {
avatarId = Integer.parseInt(args.get(2));
} catch (Exception e) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.failed_parse_avatar_id", args.get(2));
return false;
}
if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID;
}
if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
return false;
}
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false;
}
currentTeamAvatars.set(index, avatarId);
return true;
}
private boolean addAvatar(Player sender, Player targetPlayer, int avatarId, int index) {
if (avatarId < BASE_AVATARID) {
avatarId += BASE_AVATARID;
}
var currentTeamAvatars = targetPlayer.getTeamManager().getCurrentTeamInfo().getAvatars();
if (currentTeamAvatars.contains(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_already_in_team", avatarId);
return false;
}
if (!targetPlayer.getAvatars().hasAvatar(avatarId)) {
CommandHandler.sendTranslatedMessage(sender, "commands.team.avatar_not_found", avatarId);
return false;
}
if (index < 0) {
currentTeamAvatars.add(avatarId);
} else {
currentTeamAvatars.add(index, avatarId);
}
return true;
}
private List<Integer> transformToIndexes(String metaIndexes, int listLength) {
// step 1: check if metaIndexes is a special constants
if (metaIndexes.equals("first")) {
return List.of(1);
} else if (metaIndexes.equals("last")) {
return List.of(listLength);
}
// step 2: check if metaIndexes is a range
if (metaIndexes.contains("-")) {
var range = metaIndexes.split("-");
if (range.length < 2) {
return null;
}
int min, max;
try {
min = switch (range[0]) {
case "first" -> 1;
case "last" -> listLength;
default -> Integer.parseInt(range[0]);
};
max = switch (range[1]) {
case "first" -> 1;
case "last" -> listLength;
default -> Integer.parseInt(range[1]);
};
} catch (Exception e) {
return null;
}
if (min > max) {
min ^= max;
max ^= min;
min ^= max;
}
var indexes = new ArrayList<Integer>();
for (int i = min; i <= max; ++i) {
indexes.add(i);
}
return indexes;
}
// step 3: index is a value, simply return
try {
int index = Integer.parseInt(metaIndexes);
return List.of(index);
} catch (Exception e) {
return null;
}
}
}

View File

@@ -1,6 +1,5 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
@@ -11,7 +10,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "tpall", usage = "tpall", permission = "player.tpall", permissionTargeted = "player.tpall.others", description = "commands.teleportAll.description")
@Command(label = "teleportAll", aliases = {"tpall"}, permission = "player.tpall", permissionTargeted = "player.tpall.others")
public final class TeleportAllCommand implements CommandHandler {
@Override
@@ -25,9 +24,9 @@ public final class TeleportAllCommand implements CommandHandler {
if (player.equals(targetPlayer))
continue;
Position pos = targetPlayer.getPos();
Position pos = targetPlayer.getPosition();
PlayerTeleportEvent event = new PlayerTeleportEvent(targetPlayer, PlayerTeleportEvent.TeleportType.COMMAND,
targetPlayer.getPos(), pos);
targetPlayer.getPosition(), pos);
event.call();
if(!event.isCanceled())

View File

@@ -1,6 +1,5 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.Grasscutter;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
@@ -11,7 +10,7 @@ import java.util.List;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "teleport", usage = "teleport <x> <y> <z> [sceneId]", aliases = {"tp"}, permission = "player.teleport", permissionTargeted = "player.teleport.others", description = "commands.teleport.description")
@Command(label = "teleport", aliases = {"tp"}, usage = {"<x> <y> <z> [sceneId]"}, permission = "player.teleport", permissionTargeted = "player.teleport.others")
public final class TeleportCommand implements CommandHandler {
private float parseRelative(String input, Float current) { // TODO: Maybe this will be useful elsewhere later
@@ -27,7 +26,7 @@ public final class TeleportCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
Position pos = targetPlayer.getPos();
Position pos = targetPlayer.getPosition();
float x = pos.getX();
float y = pos.getY();
float z = pos.getZ();
@@ -50,7 +49,7 @@ public final class TeleportCommand implements CommandHandler {
}
break;
default:
CommandHandler.sendMessage(sender, translate(sender, "commands.teleport.usage"));
sendUsageMessage(sender);
return;
}

View File

@@ -9,8 +9,6 @@ import emu.grasscutter.game.player.Player;
@Command(
label = "unban",
usage = "unban <@player>",
description = "commands.unban.description",
permission = "server.ban",
targetRequirement = Command.TargetRequirement.PLAYER
)

View File

@@ -0,0 +1,35 @@
package emu.grasscutter.command.commands;
import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.OpenState;
import emu.grasscutter.server.packet.send.PacketOpenStateChangeNotify;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import static emu.grasscutter.utils.Language.translate;
@Command(label = "unlockall", usage = {""}, permission = "player.unlockall", permissionTargeted = "player.unlockall.others")
public final class UnlockAllCommand implements CommandHandler {
@Override
public void execute(Player sender, Player targetPlayer, List<String> args) {
Map<Integer, Integer> changed = new HashMap<>();
for (OpenState state : OpenState.values()) {
if (state == OpenState.OPEN_STATE_NONE || state == OpenState.OPEN_STATE_LIMIT_REGION_GLOBAL) continue;
if (targetPlayer.getOpenStateManager().getOpenStateMap().getOrDefault(state.getValue(), 0) == 0) {
targetPlayer.getOpenStateManager().getOpenStateMap().put(state.getValue(), 1);
changed.put(state.getValue(), 1);
}
}
targetPlayer.sendPacket(new PacketOpenStateChangeNotify(changed));
CommandHandler.sendMessage(sender, translate(sender, "commands.unlockall.success", targetPlayer.getNickname()));
}
}

View File

@@ -4,11 +4,10 @@ import emu.grasscutter.command.Command;
import emu.grasscutter.command.CommandHandler;
import emu.grasscutter.game.player.Player;
import emu.grasscutter.game.props.ClimateType;
import emu.grasscutter.game.world.Scene;
import java.util.List;
@Command(label = "weather", usage = "weather [weatherId] [climateType]", aliases = {"w"}, permission = "player.weather", permissionTargeted = "player.weather.others", description = "commands.weather.description")
@Command(label = "weather", aliases = {"w"}, usage = {"weather [<weatherId>] [<climateType>]"}, permission = "player.weather", permissionTargeted = "player.weather.others")
public final class WeatherCommand implements CommandHandler {
@Override
@@ -31,7 +30,7 @@ public final class WeatherCommand implements CommandHandler {
weatherId = Integer.parseInt(arg);
} catch (NumberFormatException ignored) {
CommandHandler.sendTranslatedMessage(sender, "commands.generic.invalid.id");
CommandHandler.sendTranslatedMessage(sender, "commands.weather.usage");
sendUsageMessage(sender);
return;
}
}