Grasscutter/src/main/java/emu/grasscutter/command/CommandMap.java

247 lines
9.4 KiB
Java
Raw Normal View History

package emu.grasscutter.command;
2022-04-18 22:24:08 +00:00
import emu.grasscutter.Grasscutter;
2022-04-19 03:46:04 +00:00
import emu.grasscutter.game.Account;
2022-04-27 04:24:25 +00:00
import emu.grasscutter.game.player.Player;
2022-04-18 22:24:08 +00:00
import org.reflections.Reflections;
import java.util.*;
import static emu.grasscutter.utils.Language.translate;
@SuppressWarnings({"UnusedReturnValue", "unused"})
2022-04-18 22:24:08 +00:00
public final class CommandMap {
private final Map<String, CommandHandler> commands = new HashMap<>();
private final Map<String, Command> annotations = new HashMap<>();
2022-05-05 05:03:08 +00:00
private final Map<String, Integer> targetPlayerIds = new HashMap<>();
2022-05-04 06:32:09 +00:00
private static final String consoleId = "console";
public CommandMap() {
this(false);
}
public CommandMap(boolean scan) {
if (scan) this.scan();
}
2022-04-18 22:24:08 +00:00
public static CommandMap getInstance() {
return Grasscutter.getGameServer().getCommandMap();
}
/**
* Register a command handler.
*
* @param label The command label.
2022-04-18 22:24:08 +00:00
* @param command The command handler.
* @return Instance chaining.
*/
public CommandMap registerCommand(String label, CommandHandler command) {
Grasscutter.getLogger().debug("Registered command: " + label);
2022-04-19 02:09:51 +00:00
// Get command data.
Command annotation = command.getClass().getAnnotation(Command.class);
2022-04-19 03:46:04 +00:00
this.annotations.put(label, annotation);
2022-04-19 02:09:51 +00:00
this.commands.put(label, command);
2022-04-19 02:09:51 +00:00
// Register aliases.
if (annotation.aliases().length > 0) {
2022-04-19 02:09:51 +00:00
for (String alias : annotation.aliases()) {
this.commands.put(alias, command);
2022-04-19 03:46:04 +00:00
this.annotations.put(alias, annotation);
2022-04-19 02:09:51 +00:00
}
}
return this;
2022-04-18 22:24:08 +00:00
}
/**
* Removes a registered command handler.
*
2022-04-18 22:24:08 +00:00
* @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;
Command annotation = handler.getClass().getAnnotation(Command.class);
2022-04-19 03:46:04 +00:00
this.annotations.remove(label);
2022-04-19 02:09:51 +00:00
this.commands.remove(label);
2022-04-19 02:09:51 +00:00
// Unregister aliases.
if (annotation.aliases().length > 0) {
2022-04-19 02:09:51 +00:00
for (String alias : annotation.aliases()) {
this.commands.remove(alias);
2022-04-19 03:46:04 +00:00
this.annotations.remove(alias);
2022-04-19 02:09:51 +00:00
}
}
return this;
2022-04-18 22:24:08 +00:00
}
2022-04-19 03:06:03 +00:00
/**
* Returns a list of all registered commands.
*
2022-04-19 03:06:03 +00:00
* @return All command handlers as a list.
*/
public List<CommandHandler> getHandlersAsList() {
2022-04-19 03:06:03 +00:00
return new LinkedList<>(this.commands.values());
}
public HashMap<String, CommandHandler> getHandlers() {
return new LinkedHashMap<>(this.commands);
}
2022-04-19 03:06:03 +00:00
/**
* Returns a handler by label/alias.
*
2022-04-19 03:06:03 +00:00
* @param label The command label.
* @return The command handler.
*/
public CommandHandler getHandler(String label) {
return this.commands.get(label);
}
2022-04-18 22:24:08 +00:00
/**
* Invoke a command handler with the given arguments.
*
* @param player The player invoking the command or null for the server console.
2022-04-18 22:24:08 +00:00
* @param rawMessage The messaged used to invoke the command.
*/
2022-05-04 06:32:09 +00:00
public void invoke(Player player, Player targetPlayer, String rawMessage) {
rawMessage = rawMessage.trim();
2022-04-24 13:52:50 +00:00
if (rawMessage.length() == 0) {
CommandHandler.sendMessage(player, translate("commands.generic.not_specified"));
2022-04-24 13:52:50 +00:00
return;
}
2022-04-18 22:24:08 +00:00
// Parse message.
String[] split = rawMessage.split(" ");
List<String> args = new LinkedList<>(Arrays.asList(split));
2022-04-18 22:24:08 +00:00
String label = args.remove(0);
2022-05-04 06:32:09 +00:00
String playerId = (player == null) ? consoleId : player.getAccount().getId();
// Check for special cases - currently only target command.
2022-05-04 09:16:42 +00:00
String targetUidStr = null;
if (label.startsWith("@")) { // @[UID]
2022-05-04 09:16:42 +00:00
targetUidStr = label.substring(1);
} else if (label.equalsIgnoreCase("target")) { // target [[@]UID]
2022-05-05 05:03:08 +00:00
if (args.size() > 0) {
targetUidStr = args.get(0);
if (targetUidStr.startsWith("@")) {
targetUidStr = targetUidStr.substring(1);
}
} else {
targetUidStr = "";
2022-05-04 09:16:42 +00:00
}
}
2022-05-05 05:03:08 +00:00
if (targetUidStr != null) {
if (targetUidStr.equals("")) { // Clears the default targetPlayer.
2022-05-05 05:03:08 +00:00
targetPlayerIds.remove(playerId);
CommandHandler.sendMessage(player, translate("commands.execution.clear_target"));
} else { // Sets default targetPlayer to the UID provided.
2022-05-05 05:03:08 +00:00
try {
int uid = Integer.parseInt(targetUidStr);
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
if (targetPlayer == null) {
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
2022-05-05 05:03:08 +00:00
} else {
targetPlayerIds.put(playerId, uid);
CommandHandler.sendMessage(player, translate("commands.execution.set_target", targetUidStr));
2022-05-05 05:03:08 +00:00
}
} catch (NumberFormatException e) {
CommandHandler.sendMessage(player, translate("commands.execution.uid_error"));
2022-05-04 06:32:09 +00:00
}
}
return;
2022-05-05 05:03:08 +00:00
}
2022-05-04 09:16:42 +00:00
2022-04-18 22:24:08 +00:00
// Get command handler.
CommandHandler handler = this.commands.get(label);
if (handler == null) {
CommandHandler.sendMessage(player, translate("commands.generic.unknown_command", label));
return;
2022-04-18 22:24:08 +00:00
}
// If any @UID argument is present, override targetPlayer with it.
2022-05-04 06:32:09 +00:00
for (int i = 0; i < args.size(); i++) {
String arg = args.get(i);
if (arg.startsWith("@")) {
2022-05-04 06:32:09 +00:00
arg = args.remove(i).substring(1);
try {
int uid = Integer.parseInt(arg);
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
if (targetPlayer == null) {
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
2022-05-04 06:32:09 +00:00
return;
}
break;
} catch (NumberFormatException e) {
CommandHandler.sendMessage(player, translate("commands.execution.uid_error"));
2022-05-04 06:32:09 +00:00
return;
}
}
}
2022-05-04 06:32:09 +00:00
// If there's still no targetPlayer at this point, use previously-set target
if (targetPlayer == null) {
2022-05-05 05:03:08 +00:00
if (targetPlayerIds.containsKey(playerId)) {
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(targetPlayerIds.get(playerId)); // We check every time in case the target goes offline after being targeted
if (targetPlayer == null) {
CommandHandler.sendMessage(player, translate("commands.generic.execution.player_exist_offline_error"));
2022-05-05 05:03:08 +00:00
return;
}
} else {
// If there's still no targetPlayer at this point, use executor.
targetPlayer = player;
2022-05-05 05:03:08 +00:00
}
2022-05-04 06:32:09 +00:00
}
2022-04-19 03:46:04 +00:00
// Check for permission.
if (player != null) {
2022-04-19 03:46:04 +00:00
String permissionNode = this.annotations.get(label).permission();
2022-05-04 06:32:09 +00:00
String permissionNodeTargeted = this.annotations.get(label).permissionTargeted();
2022-04-19 03:46:04 +00:00
Account account = player.getAccount();
2022-05-04 06:32:09 +00:00
if (player != targetPlayer) { // Additional permission required for targeting another player
if (!permissionNodeTargeted.isEmpty() && !account.hasPermission(permissionNodeTargeted)) {
CommandHandler.sendMessage(player, translate("commands.generic.permission_error"));
2022-05-04 06:32:09 +00:00
return;
}
}
2022-04-24 13:52:50 +00:00
if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
CommandHandler.sendMessage(player, translate("commands.generic.permission_error"));
return;
2022-04-19 03:46:04 +00:00
}
}
2022-04-18 22:24:08 +00:00
// Invoke execute method for handler.
2022-04-24 13:52:50 +00:00
boolean threading = this.annotations.get(label).threading();
2022-05-04 06:32:09 +00:00
final Player targetPlayerF = targetPlayer; // Is there a better way to do this?
Runnable runnable = () -> handler.execute(player, targetPlayerF, args);
2022-04-24 13:52:50 +00:00
if(threading) {
new Thread(runnable).start();
} else {
2022-04-24 13:52:50 +00:00
runnable.run();
}
2022-04-18 22:24:08 +00:00
}
/**
* Scans for all classes annotated with {@link Command} and registers them.
*/
private void scan() {
Reflections reflector = Grasscutter.reflector;
Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
2022-04-18 22:24:08 +00:00
classes.forEach(annotated -> {
try {
Command cmdData = annotated.getAnnotation(Command.class);
Object object = annotated.newInstance();
2022-04-18 22:24:08 +00:00
if (object instanceof CommandHandler)
this.registerCommand(cmdData.label(), (CommandHandler) object);
else Grasscutter.getLogger().error("Class " + annotated.getName() + " is not a CommandHandler!");
} catch (Exception exception) {
Grasscutter.getLogger().error("Failed to register command handler for " + annotated.getSimpleName(), exception);
}
2022-04-18 22:24:08 +00:00
});
}
}