my_cmd = {
'name': 'say',
'code': ref_to_say_func,
params = [
{'name': 'text', 'type': str},
{'name': 'target', 'type': actor, 'optional': True},
# etc.
]
}
@command("say", "text [player]")
def cmd_say(player, text, target=None):
if target is not None:
first_person = "You say to %s, \"%s\"" % (target.name, text)
third_person = "%s says to %s, \"%s\"" % (player.name, target.name, text)
else:
first_person = "You say, \"%s\"" % text
third_person = "%s says, \"%s\"" % (player.name, text)
player.send(first_person)
for p in player.room.players:
if p is player:
continue
p.send(third_person)
@decorator
def x(a,b,c):
# bla
def temp_func(a,b,c):
# bla
x = decorator(temp_func)
def command(name, pattern):Using the @ syntax to apply this to a function basically has this effect:
def decorator(func):
register_command(func, name, pattern)
return func
return decorator
def baz():Note that the inner decorator() function returns a reference to baz() which is then assigned to the name "baz" in the module. If the "return func" statement wasn't in the decorator() function then your decorated function wouldn't actually "exist" in its module - although if you'd stored a reference to it somewhere it obviously wouldn't be G/Ced.
pass
baz = command("foo", "bar")(baz)
## The above is the same as this:
@command("foo", "bar")
def baz():
pass
At the moment, for proof of concept testing purposes, my data layer is connected to a mock "database" which is composed mainly of arrays and hashes, and doesn't actually persist. This layer is implemented through singleton "stores", one for each type of data. (So there is an account store, a room store, a clientdata store… etc.). These stores are accessed statically through a GlobalData class where required.
For example, my "say" command, written in python, grabs the room store and the client store, and sends a message to each client in the room where the command was executed.
Commands dealing with objects get the object store, etc.
Now, obviously this isn't great. For starters, if I want to test my commands, I either need to a) prepare a set of GlobalData for each test, and set is up so I can measure the pre/post conditions of that data, or b) Test as though I were the client, which has timing issues. Both testing methods are slow and buggy. The first requires that I set up a test world in it's entirety, and reset it between tests. The second *also* requires that I do that, but adds the issue of dropped packets as a means for failure. Currently, my object creation/pickup/drop tests pass the first time they're run, but fail the second time if the server isn't restarted in between trials.
What I want to do is pass the required information from my commands into my commands. That's where I hit a snag, since python is dynamically typed. In Java, I could simply declare my command to require the objects it needs, but from Java looking at python, there's no way of knowing what types python expects to be passed in.
I was thinking of giving the Command classes a static "requires" list, which Java will read, and the command factory will attempt to supply. The python class can then just have *args as a parameter, and I can expect that args[0] will correspond to requires[0], etc. , or else throw an exception from the factory if a command is looking for data that doesn't exist. Is this an intuitive way of solving the problem, or are there better ways?
If it helps, you can pretend this is pure python, since I don't think the problem goes away. How does a class tell another class in python that it will fail if not given sufficient data? Does that just not happen? I have a feeling I'm actually trying to hammer a screw here, so if there's a more "pythony" way of dealing with the problem, I'd be happy to hear that too.