From aabce6e7e34830c761ee314ef01e1d80f0bc177b Mon Sep 17 00:00:00 2001 From: Greg Taylor Date: Sun, 26 Nov 2006 23:11:11 +0000 Subject: [PATCH] This is a rather big update, encompassing quite a few areas that will lead to what I hope will be a very easy to modify yet flexible codebase. New stuff includes: * A simple command parser that supports MUX/MUSH-style switches. * Global command alias support. Aliases can be edited via web interface or SQL. * A new SQL-based configuration system that can be edited directly or via a Django web interface. * The listen port is now configurable via aforementioned config system instead of hard-wired as it was previously. Yick :) --- evennia/trunk/apps/config/models.py | 18 +++------ evennia/trunk/cmdhandler.py | 47 ++++++++++++++++-------- evennia/trunk/server.py | 57 ++++++++++++++++++++++------- evennia/trunk/sessions.py | 5 +-- 4 files changed, 82 insertions(+), 45 deletions(-) diff --git a/evennia/trunk/apps/config/models.py b/evennia/trunk/apps/config/models.py index 464437ce5b..06d0829b38 100755 --- a/evennia/trunk/apps/config/models.py +++ b/evennia/trunk/apps/config/models.py @@ -10,18 +10,12 @@ class CommandAlias(models.Model): class Admin: list_display = ('user_input', 'equiv_command',) -class Config(models.Model): +class ConfigValue(models.Model): """ - Although we technically have the ability to create more than one Config - object via the admin interface, we only really need one. This also leaves - the possibility for multiple games hosted on the same codebase or database - in the future, although this is not a priority. In any case, this model - contains most of the game-specific configuration. + Experimental new config model. """ - site_name = models.CharField(maxlength=100) - site_description = models.TextField(blank=True) - site_website = models.URLField(blank=True) - player_start_dbnum = models.IntegerField() - + conf_key = models.CharField(maxlength=100) + conf_value = models.CharField(maxlength=255 ) + class Admin: - list_display = ('site_name', 'site_website',) + list_display = ('conf_key', 'conf_value',) diff --git a/evennia/trunk/cmdhandler.py b/evennia/trunk/cmdhandler.py index c4c7ec5235..5bd3063eaa 100755 --- a/evennia/trunk/cmdhandler.py +++ b/evennia/trunk/cmdhandler.py @@ -23,7 +23,7 @@ class GenCommands: session = cdat['session'] server = session.server player_loc = session.player_loc - player_loc_obj = Object.objects.filter(id=player_loc)[0] + player_loc_obj = server.object_list[player_loc] retval = "%s%s%s%s\n\r%s\n\r" % ( ansi["normal"], @@ -62,7 +62,7 @@ class GenCommands: """ session_list = cdat['server'].session_list session = cdat['session'] - speech = cdat['uinput'][1:] + speech = cdat['uinput']['splitted'][1:] players_present = [player for player in session_list if player.player_loc == session.player_loc and player != session] retval = "You say, '%s'\n\r" % (''.join(speech),) @@ -71,12 +71,6 @@ class GenCommands: session.push(retval) - def do_sa(self, cdat): - """ - Temporary alias until we come up with a command alias system. - """ - self.do_say(cdat) - def do_version(self, cdat): """ Version info command. @@ -129,8 +123,8 @@ class UnLoggedInCommands: uses the Django database API and User model to make it extremely simple. """ session = cdat['session'] - uname = cdat['uinput'][1] - password = cdat['uinput'][2] + uname = cdat['uinput']['splitted'][1] + password = cdat['uinput']['splitted'][2] account = User.objects.filter(username=uname) user = account[0] @@ -149,9 +143,9 @@ class UnLoggedInCommands: Handle the creation of new accounts. """ session = cdat['session'] - uname = cdat['uinput'][1] - email = cdat['uinput'][2] - password = cdat['uinput'][3] + uname = cdat['uinput']['splitted'][1] + email = cdat['uinput']['splitted'][2] + password = cdat['uinput']['splitted'][3] account = User.objects.filter(username=uname) if not account.count() == 0: @@ -188,19 +182,40 @@ class Handler: class. """ session = cdat['session'] - uinput = cdat['uinput'] + server = cdat['server'] + uinput = cdat['uinput'].split() + parsed_input = {} + + # First we split the input up by spaces. + parsed_input['splitted'] = uinput + # Now we find the root command chunk (with switches attached). + parsed_input['root_chunk'] = parsed_input['splitted'][0].split('/') + # And now for the actual root command. It's the first entry in root_chunk. + parsed_input['root_cmd'] = parsed_input['root_chunk'][0].lower() + + # Now we'll see if the user is using an alias. We do a dictionary lookup, + # if the key (the player's root command) doesn't exist on the dict, we + # don't replace the existing root_cmd. If the key exists, its value + # replaces the previously splitted root_cmd. For example, sa -> say. + alias_list = server.cmd_alias_list + parsed_input['root_cmd'] = alias_list.get(parsed_input['root_cmd'],parsed_input['root_cmd']) + if session.logged_in: # If it's prefixed by an '@', it's a staff command. - if uinput[0].find('@') == -1: + if parsed_input['root_cmd'][0] != '@': cmdtable = gencommands else: cmdtable = staffcommands else: cmdtable = unloggedincommands - cmd = getattr(cmdtable, 'do_' + uinput[0].lower(), None) + + # cmdtable now equals the command table we need to use. Do a command + # lookup for a particular function based on the user's input. + cmd = getattr(cmdtable, 'do_' + parsed_input['root_cmd'], None ) if callable(cmd): + cdat['uinput'] = parsed_input cmd(cdat) else: session.push("Unknown command.\n\r") diff --git a/evennia/trunk/server.py b/evennia/trunk/server.py index ac112dfe72..3a9039555b 100755 --- a/evennia/trunk/server.py +++ b/evennia/trunk/server.py @@ -3,7 +3,7 @@ from asynchat import async_chat import socket, asyncore, time, sys from sessions import PlayerSession from django.db import models -from apps.config.models import Config +from apps.config.models import ConfigValue, CommandAlias from apps.objects.models import Object # @@ -39,26 +39,45 @@ class Server(dispatcher): """ The main server class from which everything branches. """ - def __init__(self, port): + def __init__(self): + self.session_list = [] + self.object_list = {} + self.cmd_alias_list = {} + self.configvalue = {} + self.game_running = True + print '-'*50 + self.load_configvalues() + self.load_objects() + self.load_cmd_aliases() dispatcher.__init__(self) self.create_socket(socket.AF_INET, socket.SOCK_STREAM) self.set_reuse_addr() - self.bind(('', port)) + self.bind(('', int(self.configvalue['site_port']))) self.listen(100) - self.session_list = [] - self.object_list = {} - self.game_running = True - print '-'*50 - self.load_config() - self.load_objects() - print ' Server started on port %i.' % (port,) + print ' %s started on port %s.' % (self.configvalue['site_name'], self.configvalue['site_port'],) print '-'*50 + + def announce_all(self, message, with_ann_prefix=True): + """ + Announces something to all connected players. + """ + if with_ann_prefix: + prefix = 'Announcement:' + else: + prefix = '' + + for session in self.session_list: + session.push('%s %s' % (prefix, message,)) - def load_config(self): + def load_configvalues(self): """ Loads our site's configuration up for easy access. """ - self.config = Config.objects.all()[0] + configs = ConfigValue.objects.all() + + for conf in configs: + self.configvalue[conf.conf_key] = conf.conf_value + print ' Configuration Loaded.' def load_objects(self): @@ -71,6 +90,15 @@ class Server(dispatcher): self.object_list[dbnum] = object print ' Objects Loaded: %i' % (len(self.object_list),) + def load_cmd_aliases(self): + """ + Load up our command aliases. + """ + alias_list = CommandAlias.objects.all() + for alias in alias_list: + self.cmd_alias_list[alias.user_input] = alias.equiv_command + print ' Aliases Loaded: %i' % (len(self.cmd_alias_list),) + def handle_accept(self): """ What to do when we get a connection. @@ -83,7 +111,7 @@ class Server(dispatcher): print 'Sessions active:', len(self.session_list) if __name__ == '__main__': - server = Server(4000) + server = Server() try: while server.game_running: @@ -91,4 +119,5 @@ if __name__ == '__main__': Timer(time.time()) except KeyboardInterrupt: - print 'Interrupted' + server.announce_all('The server has been shutdown. Please check back soon.') + print '--> Server killed by keystroke.' diff --git a/evennia/trunk/sessions.py b/evennia/trunk/sessions.py index 51dc40735f..76523c1d36 100755 --- a/evennia/trunk/sessions.py +++ b/evennia/trunk/sessions.py @@ -38,12 +38,11 @@ class PlayerSession(async_chat): def found_terminator(self): """ Any line return indicates a command for the purpose of a MUD. So we take - the user input, split it up by spaces into a list, and pass it to our - command handler. + the user input and pass it to our command handler. """ line = (''.join(self.data)) line = line.strip('\r') - uinput = line.split(' ') + uinput = line self.data = [] # Increment our user's command counter.