summaryrefslogtreecommitdiff
path: root/wrapper_command.py
blob: 90eaf019f60501a7404dcbf1eaedd22032f20b53 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
from wrapper_player import *
from helpers import *

root_commands = Command_dict() # {"command": command_object}

def check_arguments(command, arguments):
    prev_required = True
    type_message_seen = False
    prev_arg = arguments[0] if len(arguments) > 0 else None
    for arg_info in arguments[1:]:

        if not prev_arg.required and arg_info.required:
            raise Argument_exception("Command: %s; There may not be required arguments after non-required arguments" % command)

        if prev_arg.type == Argument.MESSAGE:
            raise Argument_exception("Command: %s; An argument of type MESSAGE may not be followed by other arguments" % command)

        prev_arg = arg_info

#--------------------------------------------------------------------------------------

class Command_dict(dict):
    #{"cmd1" : cmd_object}
    def get_command_object(self, alias):
        for cmd_name, cmd_obj in self.iteritems():
            if alias == cmd_name or alias in cmd_obj.aliases:
                return cmd_obj
        raise KeyError("Subcommand '%s' was not found" % alias)

#--------------------------------------------------------------------------------------

class Command(object):

    def __init__(self, 
                command, 
                aliases = (), 
                arguments = (
                    Argument("target", Argument.string, "the player to teleport to"), 
                    Argument("second target", Argument.string, "the player to teleport", False),
                ), 
                parent = None):

        self.command = command.lower()
        self.arguments = arguments

        check_arguments(self.command, self.arguments)

        prev_required = True
        for arg_info in self.arguments:
            if not prev_required and arg_info.required:
                raise Argument_exception("Command: %s; There may not be required arguments after non-required arguments" % self.command)

        self.aliases = tuple(alias.lower() for alias in aliases)
        self.parent = parent
        self.sub_commands = Command_dict()

        if self.parent == None:
            root_commands[self.command] = self
        else:
            try:
                parent_route = self.parent.split(" ")
                parent_sub_commands = root_commands
                parent_obj = None
                for cmd_name in parent_route:
                    parent_obj = parent_sub_commands.get_command_object(cmd_name)
                    parent_sub_commands = parent_obj.sub_commands
                parent_obj.sub_commands[self.command] = self

            except command_exception, e:
                error("Error occurred while setting up command hierarchy. " + e.message + "\n" + trace())

    def __call__(self, handler):
        self.handler = handler

        if parent == None:
            @hook.command(self.command, self.aliases)
            def run(sender, command, label, args):
                try:
                    message = self.execute(sender, command, label, args)
                except Command_exception as e:
                    message = e.message
                except Exception:
                    error(trace())
                    return True
                if message:
                    sender.sendMessage(message)
                return True

        return handler

    def execute(self, sender, command, label, args):
        try:
            return self.sub_commands.get_command_object(args[0].lower()).execute(sender, command, label, args[1:])
        except (KeyError, IndexError):
            self.execute_checks(sender, command, label, args)

    def execute_checks(self, sender, command, label, args):
        #TODO

        scape = Command_scape(args, self.arguments)
        if is_player(sender):
            sender = py_players[sender]

        return self.handler(sender, self, scape)

    def syntax(self):
        return " ".join(tuple(arg_info.syntax() for arg_info in self.arguments))

#--------------------------------------------------------------------------------------

class Command_scape(list):

    def __init__(self, args, arg_layout):
        super(list, self).__init__()
        self.raw = args
        self.arg_layout = arg_layout

        has_message = False
        for i in range(len(arg_layout)):
            arg_info = arg_layout[i]

            given = (len(args) >= i + 1)
            if arg_info.required and not given:
                raise Argument_exception("You must specify the " + arg_info.name)

            if not given:
                self.append(None)
                continue

            given_arg = args[i]
            arg_type = arg_info.type

            if arg_type == Argument.STRING:
                self.append(given_arg)

            elif arg_type == Argument.INTEGER:
                try:
                    value = int(given_arg)
                except ValueError:
                    raise Argument_exception("The %s has to be a round number" % arg_info.name)
                self.append(value)

            elif arg_type == Argument.FLOAT:
                try:
                    value = float(given_arg)
                except ValueError:
                    raise Argument_exception("The %s has to be a number" % arg_info.name)
                self.append(value)

            elif arg_type == Argument.PLAYER:
                target = server.getPlayer(given_arg)
                if target == None:
                    raise Argument_exception("The %s has to be an online player" % arg_info.name)
                self.append(py_players[target])

            elif arg_type == Argument.OFFLINE_PLAYER:
                try:
                    # Code to get the PY PLAYER by name. Possibly, uid(server.getOfflinePlayer(given_arg)) can be used?
                    pass
                except KeyError:
                    raise Argument_exception("The %s has to be an existing player" % arg_info.name)
                self.append(None)

            elif arg_type == Argument.MESSAGE:
                self.append(" ".join(args[i:]))
                has_message = True
            else:
                error("Argument type not found: %d" % arg_type)
                raise Argument_exception("A weird thing has happened, please contact an administrator")

        if not has_message:
            self.remainder = args[len(arg_layout):]
        else:
            self.remainder = None

    def has_flag(self, flag, check_all = False):
        return (("-" + flag) in self.raw) if check_all else (("-" + flag) in self.remainder)

    def get_raw(self):
        return self.raw

    def get_arg_layout(self):
        return self.arg_layout

#--------------------------------------------------------------------------------------

class Command_exception(Exception):

    def __init__(self, message):
        self.message = message

class Argument_exception(Exception):

    def __init__(self, message):
        self.message = message

#--------------------------------------------------------------------------------------

class Argument():

    STRING = 0
    INTEGER = 1
    FLOAT = 2
    PLAYER = 3
    OFFLINE_PLAYER = 4
    MESSAGE = 5

    def __init__(self, name, type, definition, required = True):
        self.name = name
        self.type = type
        self.definition = definition
        self.required = required

    def syntax(self):
        syntax = self.name
        if self.type == Argument.MESSAGE:
            syntax += "..."
        return (("<%s>" if self.required else "[%s]") % syntax)

#--------------------------------------------------------------------------------------

class Validate():

    @staticmethod
    def is_true(expression, fail_message):
        if not expression:
            raise Command_exception(fail_message)

    @staticmethod
    def not_none(obj, fail_message):
        if obj == None:
            raise Command_exception(fail_message)