CommandMap.java 5.75 KB
Newer Older
KingRainbow44's avatar
KingRainbow44 committed
1
2
3
package emu.grasscutter.commands;

import emu.grasscutter.Grasscutter;
4
import emu.grasscutter.game.Account;
KingRainbow44's avatar
KingRainbow44 committed
5
6
7
8
9
10
11
12
13
14
15
16
import emu.grasscutter.game.GenshinPlayer;
import org.reflections.Reflections;

import java.util.*;

@SuppressWarnings("UnusedReturnValue")
public final class CommandMap {
    public static CommandMap getInstance() {
        return Grasscutter.getGameServer().getCommandMap();
    }
    
    private final Map<String, CommandHandler> commands = new HashMap<>();
17
    private final Map<String, Command> annotations = new HashMap<>();
KingRainbow44's avatar
KingRainbow44 committed
18
19
20
21
22
23
24
25

    /**
     * Register a command handler.
     * @param label The command label.
     * @param command The command handler.
     * @return Instance chaining.
     */
    public CommandMap registerCommand(String label, CommandHandler command) {
26
27
        Grasscutter.getLogger().debug("Registered command: " + label);
        
KingRainbow44's avatar
KingRainbow44 committed
28
        // Get command data.
29
        Command annotation = command.getClass().getAnnotation(Command.class);
30
        this.annotations.put(label, annotation);
KingRainbow44's avatar
KingRainbow44 committed
31
32
33
        this.commands.put(label, command);
        
        // Register aliases.
34
        if(annotation.aliases().length > 0) {
KingRainbow44's avatar
KingRainbow44 committed
35
            for (String alias : annotation.aliases()) {
36
                this.commands.put(alias, command);
37
                this.annotations.put(alias, annotation);
KingRainbow44's avatar
KingRainbow44 committed
38
39
            }
        } return this;
KingRainbow44's avatar
KingRainbow44 committed
40
41
42
43
44
45
46
47
    }

    /**
     * Removes a registered command handler.
     * @param label The command label.
     * @return Instance chaining.
     */
    public CommandMap unregisterCommand(String label) {
48
49
50
51
52
        Grasscutter.getLogger().debug("Unregistered command: " + label);
        CommandHandler handler = this.commands.get(label);
        if(handler == null) return this;
        
        Command annotation = handler.getClass().getAnnotation(Command.class);
53
        this.annotations.remove(label);
KingRainbow44's avatar
KingRainbow44 committed
54
55
56
        this.commands.remove(label);
        
        // Unregister aliases.
57
        if(annotation.aliases().length > 0) {
KingRainbow44's avatar
KingRainbow44 committed
58
            for (String alias : annotation.aliases()) {
59
                this.commands.remove(alias);
60
                this.annotations.remove(alias);
KingRainbow44's avatar
KingRainbow44 committed
61
62
            }
        }
63
64
        
        return this;
KingRainbow44's avatar
KingRainbow44 committed
65
66
    }

KingRainbow44's avatar
KingRainbow44 committed
67
68
69
70
    /**
     * Returns a list of all registered commands.
     * @return All command handlers as a list.
     */
71
    public List<CommandHandler> getHandlersAsList() {
KingRainbow44's avatar
KingRainbow44 committed
72
73
74
        return new LinkedList<>(this.commands.values());
    }

75
76
    public HashMap<String, CommandHandler> getHandlers() { return new LinkedHashMap<>(this.commands); }

KingRainbow44's avatar
KingRainbow44 committed
77
78
79
80
81
82
83
84
85
    /**
     * Returns a handler by label/alias.
     * @param label The command label.
     * @return The command handler.
     */
    public CommandHandler getHandler(String label) {
        return this.commands.get(label);
    }

KingRainbow44's avatar
KingRainbow44 committed
86
87
88
89
90
91
    /**
     * Invoke a command handler with the given arguments.
     * @param player The player invoking the command or null for the server console.
     * @param rawMessage The messaged used to invoke the command.
     */
    public void invoke(GenshinPlayer player, String rawMessage) {
92
93
        rawMessage = rawMessage.trim();
        if(rawMessage.length() == 0) {
94
            CommandHandler.sendMessage(player, "No command specified."); return;
95
96
        }
        
KingRainbow44's avatar
KingRainbow44 committed
97
98
99
100
101
102
        // Remove prefix if present.
        if(!Character.isLetter(rawMessage.charAt(0)))
            rawMessage = rawMessage.substring(1);
        
        // Parse message.
        String[] split = rawMessage.split(" ");
103
        List<String> args = new LinkedList<>(Arrays.asList(split));
KingRainbow44's avatar
KingRainbow44 committed
104
105
106
107
108
109
110
111
        String label = args.remove(0);
        
        // Get command handler.
        CommandHandler handler = this.commands.get(label);
        if(handler == null) {
            CommandHandler.sendMessage(player, "Unknown command: " + label); return;
        }
        
112
113
114
115
        // Check for permission.
        if(player != null) {
            String permissionNode = this.annotations.get(label).permission();
            Account account = player.getAccount();
116
            if(!Objects.equals(permissionNode, "") && !account.hasPermission(permissionNode)) {
117
118
119
120
                CommandHandler.sendMessage(player, "You do not have permission to run this command."); return;
            }
        }
        
KingRainbow44's avatar
KingRainbow44 committed
121
        // Execution power check.
122
        Command.Execution executionPower = this.annotations.get(label).execution();
KingRainbow44's avatar
KingRainbow44 committed
123
124
125
126
127
128
        if(player == null && executionPower == Command.Execution.PLAYER) {
            CommandHandler.sendMessage(null, "Run this command in-game."); return;
        } else if (player != null && executionPower == Command.Execution.CONSOLE) {
            CommandHandler.sendMessage(player, "This command can only be run from the console."); return;
        }
        
KingRainbow44's avatar
KingRainbow44 committed
129
        // Invoke execute method for handler.
KingRainbow44's avatar
KingRainbow44 committed
130
        if(player == null) handler.execute(args); 
KingRainbow44's avatar
KingRainbow44 committed
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
        else handler.execute(player, args);
    }
    
    public CommandMap() {
        this(false);
    }
    
    public CommandMap(boolean scan) {
        if(scan) this.scan();
    }

    /**
     * Scans for all classes annotated with {@link Command} and registers them.
     */
    private void scan() {
        Reflections reflector = Grasscutter.reflector;
147
        Set<Class<?>> classes = reflector.getTypesAnnotatedWith(Command.class);
KingRainbow44's avatar
KingRainbow44 committed
148
149
        classes.forEach(annotated -> {
            try {
150
151
                Command cmdData = annotated.getAnnotation(Command.class);
                Object object = annotated.newInstance();
KingRainbow44's avatar
KingRainbow44 committed
152
153
                if (object instanceof CommandHandler)
                    this.registerCommand(cmdData.label(), (CommandHandler) object);
154
155
156
157
                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
158
159
160
        });
    }
}