CommandMap.java 5.49 KB
Newer Older
Jaida Wu's avatar
Jaida Wu committed
1
package emu.grasscutter.command;
KingRainbow44's avatar
KingRainbow44 committed
2
3

import emu.grasscutter.Grasscutter;
4
import emu.grasscutter.game.Account;
Melledy's avatar
Melledy committed
5
6
import emu.grasscutter.game.player.Player;

KingRainbow44's avatar
KingRainbow44 committed
7
8
9
10
import org.reflections.Reflections;

import java.util.*;

Jaida Wu's avatar
Jaida Wu committed
11
@SuppressWarnings({"UnusedReturnValue", "unused"})
KingRainbow44's avatar
KingRainbow44 committed
12
public final class CommandMap {
Jaida Wu's avatar
Jaida Wu committed
13
14
15
16
17
18
19
20
21
22
    private final Map<String, CommandHandler> commands = new HashMap<>();
    private final Map<String, Command> annotations = new HashMap<>();
    public CommandMap() {
        this(false);
    }

    public CommandMap(boolean scan) {
        if (scan) this.scan();
    }

KingRainbow44's avatar
KingRainbow44 committed
23
24
25
26
27
28
    public static CommandMap getInstance() {
        return Grasscutter.getGameServer().getCommandMap();
    }

    /**
     * Register a command handler.
Jaida Wu's avatar
Jaida Wu committed
29
30
     *
     * @param label   The command label.
KingRainbow44's avatar
KingRainbow44 committed
31
32
33
34
     * @param command The command handler.
     * @return Instance chaining.
     */
    public CommandMap registerCommand(String label, CommandHandler command) {
35
        Grasscutter.getLogger().debug("Registered command: " + label);
Jaida Wu's avatar
Jaida Wu committed
36

KingRainbow44's avatar
KingRainbow44 committed
37
        // Get command data.
38
        Command annotation = command.getClass().getAnnotation(Command.class);
39
        this.annotations.put(label, annotation);
KingRainbow44's avatar
KingRainbow44 committed
40
        this.commands.put(label, command);
Jaida Wu's avatar
Jaida Wu committed
41

KingRainbow44's avatar
KingRainbow44 committed
42
        // Register aliases.
Jaida Wu's avatar
Jaida Wu committed
43
        if (annotation.aliases().length > 0) {
KingRainbow44's avatar
KingRainbow44 committed
44
            for (String alias : annotation.aliases()) {
45
                this.commands.put(alias, command);
46
                this.annotations.put(alias, annotation);
KingRainbow44's avatar
KingRainbow44 committed
47
            }
Jaida Wu's avatar
Jaida Wu committed
48
49
        }
        return this;
KingRainbow44's avatar
KingRainbow44 committed
50
51
52
53
    }

    /**
     * Removes a registered command handler.
Jaida Wu's avatar
Jaida Wu committed
54
     *
KingRainbow44's avatar
KingRainbow44 committed
55
56
57
58
     * @param label The command label.
     * @return Instance chaining.
     */
    public CommandMap unregisterCommand(String label) {
59
60
        Grasscutter.getLogger().debug("Unregistered command: " + label);
        CommandHandler handler = this.commands.get(label);
Jaida Wu's avatar
Jaida Wu committed
61
62
        if (handler == null) return this;

63
        Command annotation = handler.getClass().getAnnotation(Command.class);
64
        this.annotations.remove(label);
KingRainbow44's avatar
KingRainbow44 committed
65
        this.commands.remove(label);
Jaida Wu's avatar
Jaida Wu committed
66

KingRainbow44's avatar
KingRainbow44 committed
67
        // Unregister aliases.
Jaida Wu's avatar
Jaida Wu committed
68
        if (annotation.aliases().length > 0) {
KingRainbow44's avatar
KingRainbow44 committed
69
            for (String alias : annotation.aliases()) {
70
                this.commands.remove(alias);
71
                this.annotations.remove(alias);
KingRainbow44's avatar
KingRainbow44 committed
72
73
            }
        }
Jaida Wu's avatar
Jaida Wu committed
74

75
        return this;
KingRainbow44's avatar
KingRainbow44 committed
76
77
    }

KingRainbow44's avatar
KingRainbow44 committed
78
79
    /**
     * Returns a list of all registered commands.
Jaida Wu's avatar
Jaida Wu committed
80
     *
KingRainbow44's avatar
KingRainbow44 committed
81
82
     * @return All command handlers as a list.
     */
83
    public List<CommandHandler> getHandlersAsList() {
KingRainbow44's avatar
KingRainbow44 committed
84
85
86
        return new LinkedList<>(this.commands.values());
    }

Jaida Wu's avatar
Jaida Wu committed
87
88
89
    public HashMap<String, CommandHandler> getHandlers() {
        return new LinkedHashMap<>(this.commands);
    }
90

KingRainbow44's avatar
KingRainbow44 committed
91
92
    /**
     * Returns a handler by label/alias.
Jaida Wu's avatar
Jaida Wu committed
93
     *
KingRainbow44's avatar
KingRainbow44 committed
94
95
96
97
98
99
100
     * @param label The command label.
     * @return The command handler.
     */
    public CommandHandler getHandler(String label) {
        return this.commands.get(label);
    }

KingRainbow44's avatar
KingRainbow44 committed
101
102
    /**
     * Invoke a command handler with the given arguments.
Jaida Wu's avatar
Jaida Wu committed
103
104
     *
     * @param player     The player invoking the command or null for the server console.
KingRainbow44's avatar
KingRainbow44 committed
105
106
     * @param rawMessage The messaged used to invoke the command.
     */
107
    public void invoke(Player player, String rawMessage) {
108
        rawMessage = rawMessage.trim();
BaiSugar's avatar
BaiSugar committed
109
110
111
        if (rawMessage.length() == 0) {
            CommandHandler.sendMessage(player, "No command specified.");
            return;
112
        }
Jaida Wu's avatar
Jaida Wu committed
113

KingRainbow44's avatar
KingRainbow44 committed
114
        // Remove prefix if present.
Jaida Wu's avatar
Jaida Wu committed
115
        if (!Character.isLetter(rawMessage.charAt(0)))
KingRainbow44's avatar
KingRainbow44 committed
116
            rawMessage = rawMessage.substring(1);
Jaida Wu's avatar
Jaida Wu committed
117

KingRainbow44's avatar
KingRainbow44 committed
118
119
        // Parse message.
        String[] split = rawMessage.split(" ");
120
        List<String> args = new LinkedList<>(Arrays.asList(split));
KingRainbow44's avatar
KingRainbow44 committed
121
122
123
        String label = args.remove(0);
        // Get command handler.
        CommandHandler handler = this.commands.get(label);
Jaida Wu's avatar
Jaida Wu committed
124
125
126
        if (handler == null) {
            CommandHandler.sendMessage(player, "Unknown command: " + label);
            return;
KingRainbow44's avatar
KingRainbow44 committed
127
        }
Jaida Wu's avatar
Jaida Wu committed
128

129
        // Check for permission.
Jaida Wu's avatar
Jaida Wu committed
130
        if (player != null) {
131
132
            String permissionNode = this.annotations.get(label).permission();
            Account account = player.getAccount();
BaiSugar's avatar
BaiSugar committed
133
            if (!permissionNode.isEmpty() && !account.hasPermission(permissionNode)) {
Jaida Wu's avatar
Jaida Wu committed
134
135
                CommandHandler.sendMessage(player, "You do not have permission to run this command.");
                return;
136
137
            }
        }
Jaida Wu's avatar
Jaida Wu committed
138

KingRainbow44's avatar
KingRainbow44 committed
139
        // Invoke execute method for handler.
BaiSugar's avatar
BaiSugar committed
140
141
142
143
144
145
146
147
148
        boolean threading  = this.annotations.get(label).threading();
        Runnable runnable = () -> handler.execute(player, args);
        if(threading) {
            Thread command = new Thread(runnable);
            command.start();
        }
        else {
            runnable.run();
        }
KingRainbow44's avatar
KingRainbow44 committed
149
150
151
152
153
154
155
    }

    /**
     * Scans for all classes annotated with {@link Command} and registers them.
     */
    private void scan() {
        Reflections reflector = Grasscutter.reflector;
156
        Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
KingRainbow44's avatar
KingRainbow44 committed
157
158
        classes.forEach(annotated -> {
            try {
159
160
                Command cmdData = annotated.getAnnotation(Command.class);
                Object object = annotated.newInstance();
KingRainbow44's avatar
KingRainbow44 committed
161
162
                if (object instanceof CommandHandler)
                    this.registerCommand(cmdData.label(), (CommandHandler) object);
163
164
165
166
                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);
            }
KingRainbow44's avatar
KingRainbow44 committed
167
168
169
        });
    }
}