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 :)
This commit is contained in:
Greg Taylor 2006-11-26 23:11:11 +00:00
parent fa21a35fe4
commit aabce6e7e3
4 changed files with 82 additions and 45 deletions

View file

@ -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',)

View file

@ -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")

View file

@ -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.'

View file

@ -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.