2022-04-20 12:21:38 +00:00
|
|
|
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.*;
|
|
|
|
|
2022-04-20 12:21:38 +00:00
|
|
|
@SuppressWarnings({"UnusedReturnValue", "unused"})
|
2022-04-18 22:24:08 +00:00
|
|
|
public final class CommandMap {
|
2022-04-20 12:21:38 +00:00
|
|
|
private final Map<String, CommandHandler> commands = new HashMap<>();
|
|
|
|
private final Map<String, Command> annotations = new HashMap<>();
|
2022-05-04 06:32:09 +00:00
|
|
|
private final Map<String, Player> targetPlayers = new HashMap<>();
|
|
|
|
private static final String consoleId = "console";
|
2022-04-20 12:21:38 +00:00
|
|
|
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.
|
2022-04-20 12:21:38 +00:00
|
|
|
*
|
|
|
|
* @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) {
|
2022-04-19 00:11:23 +00:00
|
|
|
Grasscutter.getLogger().debug("Registered command: " + label);
|
2022-04-20 12:21:38 +00:00
|
|
|
|
2022-04-19 02:09:51 +00:00
|
|
|
// Get command data.
|
2022-04-19 00:11:23 +00:00
|
|
|
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-20 12:21:38 +00:00
|
|
|
|
2022-04-19 02:09:51 +00:00
|
|
|
// Register aliases.
|
2022-04-20 12:21:38 +00:00
|
|
|
if (annotation.aliases().length > 0) {
|
2022-04-19 02:09:51 +00:00
|
|
|
for (String alias : annotation.aliases()) {
|
2022-04-19 00:11:23 +00:00
|
|
|
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
|
|
|
}
|
2022-04-20 12:21:38 +00:00
|
|
|
}
|
|
|
|
return this;
|
2022-04-18 22:24:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a registered command handler.
|
2022-04-20 12:21:38 +00:00
|
|
|
*
|
2022-04-18 22:24:08 +00:00
|
|
|
* @param label The command label.
|
|
|
|
* @return Instance chaining.
|
|
|
|
*/
|
|
|
|
public CommandMap unregisterCommand(String label) {
|
2022-04-19 00:11:23 +00:00
|
|
|
Grasscutter.getLogger().debug("Unregistered command: " + label);
|
|
|
|
CommandHandler handler = this.commands.get(label);
|
2022-04-20 12:21:38 +00:00
|
|
|
if (handler == null) return this;
|
|
|
|
|
2022-04-19 00:11:23 +00:00
|
|
|
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-20 12:21:38 +00:00
|
|
|
|
2022-04-19 02:09:51 +00:00
|
|
|
// Unregister aliases.
|
2022-04-20 12:21:38 +00:00
|
|
|
if (annotation.aliases().length > 0) {
|
2022-04-19 02:09:51 +00:00
|
|
|
for (String alias : annotation.aliases()) {
|
2022-04-19 00:11:23 +00:00
|
|
|
this.commands.remove(alias);
|
2022-04-19 03:46:04 +00:00
|
|
|
this.annotations.remove(alias);
|
2022-04-19 02:09:51 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-20 12:21:38 +00:00
|
|
|
|
2022-04-19 00:11:23 +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-20 12:21:38 +00:00
|
|
|
*
|
2022-04-19 03:06:03 +00:00
|
|
|
* @return All command handlers as a list.
|
|
|
|
*/
|
2022-04-19 10:17:19 +00:00
|
|
|
public List<CommandHandler> getHandlersAsList() {
|
2022-04-19 03:06:03 +00:00
|
|
|
return new LinkedList<>(this.commands.values());
|
|
|
|
}
|
|
|
|
|
2022-04-20 12:21:38 +00:00
|
|
|
public HashMap<String, CommandHandler> getHandlers() {
|
|
|
|
return new LinkedHashMap<>(this.commands);
|
|
|
|
}
|
2022-04-19 10:17:19 +00:00
|
|
|
|
2022-04-19 03:06:03 +00:00
|
|
|
/**
|
|
|
|
* Returns a handler by label/alias.
|
2022-04-20 12:21:38 +00:00
|
|
|
*
|
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.
|
2022-04-20 12:21:38 +00:00
|
|
|
*
|
|
|
|
* @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) {
|
2022-04-19 00:11:23 +00:00
|
|
|
rawMessage = rawMessage.trim();
|
2022-04-24 13:52:50 +00:00
|
|
|
if (rawMessage.length() == 0) {
|
2022-05-03 06:23:25 +00:00
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().No_command_specified);
|
2022-04-24 13:52:50 +00:00
|
|
|
return;
|
2022-04-19 00:11:23 +00:00
|
|
|
}
|
2022-04-20 12:21:38 +00:00
|
|
|
|
2022-04-18 22:24:08 +00:00
|
|
|
// Parse message.
|
|
|
|
String[] split = rawMessage.split(" ");
|
2022-04-19 00:11:23 +00:00
|
|
|
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
|
|
|
// Check for special case
|
|
|
|
String playerId = (player == null) ? consoleId : player.getAccount().getId();
|
|
|
|
if (label == "target") { // Sets or clears default targetPlayer
|
|
|
|
if (args.size() < 1) {
|
|
|
|
targetPlayers.remove(playerId);
|
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().Target_cleared);
|
|
|
|
} else {
|
|
|
|
try {
|
|
|
|
String sUid = args.get(0);
|
|
|
|
int uid = Integer.parseInt(sUid);
|
|
|
|
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
|
|
|
|
if (targetPlayer == null) {
|
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().Player_not_found_or_offline);
|
|
|
|
} else {
|
|
|
|
targetPlayers.put(playerId, targetPlayer);
|
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().Target_set.replace("{uid}", sUid));
|
|
|
|
}
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().Invalid_UID);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
2022-04-18 22:24:08 +00:00
|
|
|
// Get command handler.
|
|
|
|
CommandHandler handler = this.commands.get(label);
|
2022-04-20 12:21:38 +00:00
|
|
|
if (handler == null) {
|
2022-05-03 06:23:25 +00:00
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().Unknown_command + label);
|
2022-04-20 12:21:38 +00:00
|
|
|
return;
|
2022-04-18 22:24:08 +00:00
|
|
|
}
|
2022-04-20 12:21:38 +00:00
|
|
|
|
2022-05-04 06:32:09 +00:00
|
|
|
// If any @UID argument is present, override targetPlayer with it
|
|
|
|
for (int i = 0; i < args.size(); i++) {
|
|
|
|
String arg = args.get(i);
|
|
|
|
if (!arg.startsWith("@")) {
|
|
|
|
continue;
|
|
|
|
} else {
|
|
|
|
arg = args.remove(i).substring(1);
|
|
|
|
try {
|
|
|
|
int uid = Integer.parseInt(arg);
|
|
|
|
targetPlayer = Grasscutter.getGameServer().getPlayerByUid(uid);
|
|
|
|
if (targetPlayer == null) {
|
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().Player_not_found_or_offline);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
} catch (NumberFormatException e) {
|
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().Invalid_UID);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If there's still no targetPlayer at this point, use previously-set target
|
|
|
|
if (targetPlayer == null) {
|
|
|
|
targetPlayer = targetPlayers.getOrDefault(playerId, null);
|
|
|
|
}
|
|
|
|
|
2022-04-19 03:46:04 +00:00
|
|
|
// Check for permission.
|
2022-04-20 12:21:38 +00:00
|
|
|
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, Grasscutter.getLanguage().You_not_permission_run_command);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
2022-04-24 13:52:50 +00:00
|
|
|
if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
|
2022-05-03 06:23:25 +00:00
|
|
|
CommandHandler.sendMessage(player, Grasscutter.getLanguage().You_not_permission_run_command);
|
2022-04-20 12:21:38 +00:00
|
|
|
return;
|
2022-04-19 03:46:04 +00:00
|
|
|
}
|
|
|
|
}
|
2022-04-20 12:21:38 +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) {
|
|
|
|
Thread command = new Thread(runnable);
|
|
|
|
command.start();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
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;
|
2022-04-19 00:11:23 +00:00
|
|
|
Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
|
2022-04-18 22:24:08 +00:00
|
|
|
classes.forEach(annotated -> {
|
|
|
|
try {
|
2022-04-19 00:11:23 +00:00
|
|
|
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);
|
2022-04-19 00:11:23 +00:00
|
|
|
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
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|