Pretty sizable update.

* Finished the major work on the command interpreter. Still needs to do some stripping of funky characters for the sake of security, probably.
* Account creation is now syncd up with the regular object list.
* We can now determine our next free database reference number for the creation of new objects, very similar to MUX/MUSH.
* Added some of the stuff we're going to need for efficient recycling of garbage objects.
* Misc. tweaks and improvements across the board.
This commit is contained in:
Greg Taylor 2006-11-28 21:51:36 +00:00
parent 0ac644aef2
commit 2cd8c50f3d
6 changed files with 161 additions and 80 deletions

View file

@ -2,7 +2,8 @@ from django.db import models
class CommandAlias(models.Model):
"""
Command aliases.
Command aliases. If the player enters the value equal to user_input, the
command denoted by equiv_command is used instead.
"""
user_input = models.CharField(maxlength=50)
equiv_command = models.CharField(maxlength=50)

View file

@ -40,21 +40,23 @@ class Object(models.Model):
field. The different otypes denote an object's behaviors.
"""
# Do not mess with the default types (0-4).
# Do not mess with the default types (0-5).
OBJECT_TYPES = (
(0, 'NOTHING'),
(1, 'PLAYER'),
(2, 'ROOM'),
(3, 'THING'),
(4, 'EXIT'),
(5, 'GARBAGE'),
)
name = models.CharField(maxlength=255)
type = models.SmallIntegerField(choices=OBJECT_TYPES)
description = models.TextField(blank=True)
location = models.ForeignKey('self', related_name="olocation", blank=True, null=True)
contents = models.ManyToManyField("Object", related_name="object", blank=True, null=True)
attributes = models.ManyToManyField(Attribute, related_name="attributes", blank=True, null=True)
content = models.ManyToManyField("Object", related_name="contents", blank=True, null=True)
attrib = models.ManyToManyField(Attribute, related_name="attributes", blank=True, null=True)
attrib_list = {}
def __str__(self):
return "%s(%d)" % (self.name, self.id,)

View file

@ -13,15 +13,13 @@ class GenCommands:
"""
Generic command class. Pretty much every command should go here for
now.
"""
def __init__(self): pass
"""
def do_look(self, cdat):
"""
Handle looking at objects.
"""
session = cdat['session']
server = session.server
server = cdat['server']
player_loc = session.player_loc
player_loc_obj = server.object_list[player_loc]
@ -107,8 +105,8 @@ class StaffCommands:
session = cdat['session']
server = cdat['server']
nextfree = server.nextfree_objnum()
retval = "Next free object number: %d" % (nextfree,)
nextfree = server.get_nextfree_dbnum()
retval = "Next free object number: %s\n\r" % (nextfree,)
session.push(retval)
@ -116,7 +114,6 @@ class UnLoggedInCommands:
"""
Commands that are available from the connect screen.
"""
def __init__(self): pass
def do_connect(self, cdat):
"""
This is the connect command at the connection screen. Fairly simple,
@ -143,17 +140,18 @@ class UnLoggedInCommands:
Handle the creation of new accounts.
"""
session = cdat['session']
server = cdat['server']
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:
session.push("There is already a player with that name!")
session.push("There is already a player with that name!\n\r")
elif len(password) < 3:
session.push("Your password must be 3 characters or longer.")
session.push("Your password must be 3 characters or longer.\n\r")
else:
session.create_user(uname, email, password)
server.create_user(session, uname, email, password)
def do_quit(self, cdat):
"""
@ -170,8 +168,12 @@ gencommands = GenCommands()
staffcommands = StaffCommands()
unloggedincommands = UnLoggedInCommands()
class UnknownCommand(Exception):
"""
Throw this when a user enters an an invalid command.
"""
class Handler:
def __init__(self): pass
def handle(self, cdat):
"""
Use the spliced (list) uinput variable to retrieve the correct
@ -183,40 +185,49 @@ class Handler:
"""
session = cdat['session']
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'])
try:
# TODO: Protect against non-standard characters.
if cdat['uinput'] == '':
raise UnknownCommand
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 parsed_input['root_cmd'][0] != '@':
cmdtable = gencommands
if session.logged_in:
# If it's prefixed by an '@', it's a staff command.
if parsed_input['root_cmd'][0] != '@':
cmdtable = gencommands
else:
parsed_input['root_cmd'] = parsed_input['root_cmd'][1:]
cmdtable = staffcommands
else:
cmdtable = staffcommands
else:
cmdtable = unloggedincommands
# 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:
cmdtable = unloggedincommands
# 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_%s' % (parsed_input['root_cmd'],), None )
if callable(cmd):
cdat['uinput'] = parsed_input
cmd(cdat)
else:
raise UnknownCommand
except UnknownCommand:
session.push("Unknown command.\n\r")

View file

@ -0,0 +1,4 @@
from apps.objects.models import Object, Attribute
def object_find_neighbor(searcher, target_string):
pass

View file

@ -4,7 +4,8 @@ import socket, asyncore, time, sys
from sessions import PlayerSession
from django.db import models
from apps.config.models import ConfigValue, CommandAlias
from apps.objects.models import Object
from apps.objects.models import Object, Attribute
from django.contrib.auth.models import User
#
## Begin: Time Functions
@ -34,7 +35,7 @@ def Timer(timer):
#
## End: Time Functions
#
class Server(dispatcher):
"""
The main server class from which everything branches.
@ -45,10 +46,15 @@ class Server(dispatcher):
self.cmd_alias_list = {}
self.configvalue = {}
self.game_running = True
print '-'*50
# Load stuff up into memory for easy/quick access.
self.load_configvalues()
self.load_objects()
self.load_attributes()
self.load_cmd_aliases()
# Start accepting connections.
dispatcher.__init__(self)
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
self.set_reuse_addr()
@ -56,18 +62,10 @@ class Server(dispatcher):
self.listen(100)
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,))
"""
BEGIN SERVER STARTUP METHODS
"""
def load_configvalues(self):
"""
@ -88,7 +86,16 @@ class Server(dispatcher):
for object in object_list:
dbnum = object.id
self.object_list[dbnum] = object
print ' Objects Loaded: %i' % (len(self.object_list),)
print ' Objects Loaded: %d' % (len(self.object_list),)
def load_attributes(self):
"""
Load all of our attributes into memory.
"""
attribute_list = Attribute.objects.all()
for attrib in attribute_list:
attrib.object.attrib_list[attrib.name] = attrib.value
print ' Attributes Loaded: %d' % (len(attribute_list),)
def load_cmd_aliases(self):
"""
@ -109,7 +116,76 @@ class Server(dispatcher):
print 'Connection:', str(session)
self.session_list.append(session)
print 'Sessions active:', len(self.session_list)
"""
BEGIN GENERAL METHODS
"""
def create_user(self, session, uname, email, password):
"""
Handles the creation of new users.
"""
start_room = int(self.get_configvalue('player_dbnum_start'))
start_room_obj = self.object_list[start_room]
# The user's entry in the User table must match up to an object
# on the object table. The id's are the same, we need to figure out
# the next free unique ID to use and make sure the two entries are
# the same number.
uid = self.get_nextfree_dbnum()
user = User.objects.create_user(uname, email, password)
# It stinks to have to do this but it's the only trivial way now.
user.id = uid
user.save
# Create a player object of the same ID in the Objects table.
user_object = Object(id=uid, type=1, name=uname, location=start_room_obj)
user_object.save()
# Activate the player's session and set them loose.
session.login(user)
print 'Registration: %s' % (session,)
session.push("Welcome to %s, %s.\n\r" % (self.get_configvalue('site_name'), session.name,))
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 get_configvalue(self, configname):
"""
Retrieve a configuration value.
"""
return self.configvalue[configname]
def get_nextfree_dbnum(self):
"""
Figure out what our next free database reference number is.
"""
# First we'll see if there's an object of type 5 (GARBAGE) that we
# can recycle.
nextfree = Object.objects.filter(type__exact=5)
if nextfree:
# We've got at least one garbage object to recycle.
#return nextfree.id
return nextfree[0].id
else:
# No garbage to recycle, find the highest dbnum and increment it
# for our next free.
return Object.objects.order_by('-id')[0].id + 1
"""
END Server CLASS
"""
"""
BEGIN MAIN APPLICATION LOGIC
"""
if __name__ == '__main__':
server = Server()
@ -119,5 +195,5 @@ if __name__ == '__main__':
Timer(time.time())
except KeyboardInterrupt:
server.announce_all('The server has been shutdown. Please check back soon.')
server.announce_all('The server has been shutdown. Please check back soon.\n\r')
print '--> Server killed by keystroke.'

View file

@ -2,6 +2,8 @@ from asyncore import dispatcher
from asynchat import async_chat
import socket, asyncore, time, sys
from cmdhandler import *
from apps.objects.models import Object
from django.contrib.auth.models import User
chandler = Handler()
@ -27,7 +29,7 @@ class PlayerSession(async_chat):
# The time when the user connected.
self.conn_time = time.time()
# Player's room location. Move this to a player sub-class.
self.player_loc = 4
self.player_loc = 1
def collect_incoming_data(self, data):
"""
@ -85,22 +87,7 @@ class PlayerSession(async_chat):
self.conn_time = time.time()
self.push("Logging in as %s.\n\r" % (self.name,))
print "Login: %s" % (self,)
def create_user(self, uname, email, password):
"""
Handles the creation of new users.
"""
# print uname, email, password
user = User.objects.create_user(uname, email, password)
self.login(user)
print 'Registration: %s' % (self,)
self.push("Welcome to the game, %s.\n\r" % (self.name,))
def nextfree_objnum(self):
"""
Returns the next free object number.
"""
def __str__(self):
"""
String representation of the user session class. We use