mirror of
https://github.com/evennia/evennia.git
synced 2026-03-16 21:06:30 +01:00
Initial import.
This commit is contained in:
parent
24ead8690c
commit
2421c23521
30 changed files with 773 additions and 0 deletions
74
evennia/ABOUT
Executable file
74
evennia/ABOUT
Executable file
|
|
@ -0,0 +1,74 @@
|
|||
Evennia Proof-of-Concept
|
||||
------------------------
|
||||
Evennia is a proof-of-concept MUD server written entirely in Python, backed
|
||||
by SQL. The project rises from a general dissatisfaction with the limitations
|
||||
of softcode in MUX and MUSH, and the generally inflexible Diku-derivatives and
|
||||
relatives.
|
||||
|
||||
Evennia represents a combination of several technologies, and most importantly
|
||||
of all, my first venture into codebase design. You may find things within
|
||||
the source that look strange to you, perhaps not ideally designed. I'm open
|
||||
to suggestions, but this really is largely an experiment and a learning
|
||||
experience.
|
||||
|
||||
Design Objectives
|
||||
-----------------
|
||||
1) To create a MU* server that serves as a great foundation for capable admins
|
||||
to craft into their respective games. It is not my intention to provide a
|
||||
full-fledged, ready-to-run base, I'm releasing the means to make such games.
|
||||
|
||||
2) Development of games on Evennia must be easy for anyone with some degree
|
||||
of Python experience. Building needs to be easy, and per-room, per-object,
|
||||
and environmental customizations need to be simple to do.
|
||||
|
||||
3) The server must utilize SQL as a storage back-end to allow for web->game
|
||||
integration. See the details on Django later on in the document for more
|
||||
details.
|
||||
|
||||
4) Any and all game-specific configuration must reside in SQL, not
|
||||
external configuration files. The only exception is the settings.py file
|
||||
containing the SQL information.
|
||||
|
||||
How it all Works
|
||||
----------------
|
||||
Python (Including the SQL driver of your choice)
|
||||
|-asynchat (included with Python2)
|
||||
|-SQL (MySQL, SQLite, Postgresql)
|
||||
|-Django (http://djangoproject.com)
|
||||
|
||||
Evennia is built on top of asynchat, an asynchronous TCP conversation/chat
|
||||
library. This makes the actual socket/connection handling an absolute
|
||||
no-brainer.
|
||||
|
||||
Serving as our storage medium, SQL is one of the more important and unique
|
||||
features of the codebase. It allows for very simple code in many cases, and
|
||||
can lead to a game being a lot more scalable due to the inherent speed of
|
||||
most modern SQL servers. Another extremely important benefit is that by
|
||||
storing everything in SQL, we make the entire game accessible from other
|
||||
means, such as a website. Which leads us to the next component.
|
||||
|
||||
Django is perhaps one of the most interesting introductions to the codebase,
|
||||
since I'm not aware of any other server using it to run MU*'s. Django is
|
||||
technically a Python web framework, but it also includes a great data modeling
|
||||
and database abstraction module. This means that things like Players or
|
||||
Objects can be represented by a very short class, then related to one another.
|
||||
This allows us to add, remove, delete, and manipulate things in our database
|
||||
very easily. Another huge benefit is the admin interface that Django more
|
||||
or less automatically generates for us. Instead of a bunch of clunky admin
|
||||
commands, you can fire up your web browser and administer pretty much
|
||||
everything from there, although equivalent in-game commands may be offered.
|
||||
The possibilities for developing your game's website are nearly endless with
|
||||
this tandem of MU* server, SQL, and Django.
|
||||
|
||||
Support
|
||||
-------
|
||||
At this time, I am offering no formal support for Evennia. It is not ready for
|
||||
use and is subject to change in a major way from week to week. I can't hope
|
||||
to support such a young product. However, if you have questions or ideas,
|
||||
please direct them to squishywaffle@gmail.com.
|
||||
|
||||
Reporting Bugs
|
||||
--------------
|
||||
Feel free to contact me by email at squishywaffle@gmail.com with as much
|
||||
details on the bug that you can find. Copy/pasting server logs is generally
|
||||
a good idea.
|
||||
16
evennia/README
Executable file
16
evennia/README
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
Starting the Server
|
||||
-------------------
|
||||
Prior to starting up Evennia, you'll need the following environmental variable
|
||||
set.
|
||||
|
||||
export DJANGO_SETTINGS_MODULE="settings"
|
||||
|
||||
You may wish to put this in your .bashrc file, or you can simple copy/paste
|
||||
it before each startup. I'll fix this later so you don't have to, but it'll
|
||||
do for now.
|
||||
|
||||
Once you've got the evar set, simply enter the following:
|
||||
|
||||
python server.py
|
||||
|
||||
The default port is 4000.
|
||||
0
evennia/__init__.py
Executable file
0
evennia/__init__.py
Executable file
BIN
evennia/__init__.pyc
Executable file
BIN
evennia/__init__.pyc
Executable file
Binary file not shown.
36
evennia/ansi.py
Executable file
36
evennia/ansi.py
Executable file
|
|
@ -0,0 +1,36 @@
|
|||
"""
|
||||
ANSI related stuff.
|
||||
"""
|
||||
ansi = {}
|
||||
ansi["beep"] = "\07"
|
||||
ansi["escape"] = "\033"
|
||||
ansi["normal"] = "\033[0m"
|
||||
|
||||
ansi["underline"] = "\033[4m"
|
||||
ansi["hilite"] = "\033[1m"
|
||||
ansi["blink"] = "\033[5m"
|
||||
ansi["inverse"] = "\033[7m"
|
||||
ansi["inv_hilite"] = "\033[1;7m"
|
||||
ansi["inv_blink"] = "\033[7;5m"
|
||||
ansi["blink_hilite"] = "\033[1;5m"
|
||||
ansi["inv_blink_hilite"] = "\033[1;5;7m"
|
||||
|
||||
# Foreground colors
|
||||
ansi["black"] = "\033[30m"
|
||||
ansi["red"] = "\033[31m"
|
||||
ansi["green"] = "\033[32m"
|
||||
ansi["yellow"] = "\033[33m"
|
||||
ansi["blue"] = "\033[34m"
|
||||
ansi["magenta"] = "\033[35m"
|
||||
ansi["cyan"] = "\033[36m"
|
||||
ansi["white"] = "\033[37m"
|
||||
|
||||
# Background colors
|
||||
ansi["back_black"] = "\033[40m"
|
||||
ansi["back_red"] = "\033[41m"
|
||||
ansi["back_green"] = "\033[42m"
|
||||
ansi["back_yellow"] = "\033[43m"
|
||||
ansi["back_blue"] = "\033[44m"
|
||||
ansi["back_magenta"] = "\033[45m"
|
||||
ansi["back_cyan"] = "\033[46m"
|
||||
ansi["back_white"] = "\033[47m"
|
||||
BIN
evennia/ansi.pyc
Executable file
BIN
evennia/ansi.pyc
Executable file
Binary file not shown.
0
evennia/apps/__init__.py
Executable file
0
evennia/apps/__init__.py
Executable file
BIN
evennia/apps/__init__.pyc
Executable file
BIN
evennia/apps/__init__.pyc
Executable file
Binary file not shown.
0
evennia/apps/config/__init__.py
Executable file
0
evennia/apps/config/__init__.py
Executable file
BIN
evennia/apps/config/__init__.pyc
Executable file
BIN
evennia/apps/config/__init__.pyc
Executable file
Binary file not shown.
27
evennia/apps/config/models.py
Executable file
27
evennia/apps/config/models.py
Executable file
|
|
@ -0,0 +1,27 @@
|
|||
from django.db import models
|
||||
|
||||
class CommandAlias(models.Model):
|
||||
"""
|
||||
Command aliases.
|
||||
"""
|
||||
user_input = models.CharField(maxlength=50)
|
||||
equiv_command = models.CharField(maxlength=50)
|
||||
|
||||
class Admin:
|
||||
list_display = ('user_input', 'equiv_command',)
|
||||
|
||||
class Config(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.
|
||||
"""
|
||||
site_name = models.CharField(maxlength=100)
|
||||
site_description = models.TextField(blank=True)
|
||||
site_website = models.URLField(blank=True)
|
||||
player_start_dbnum = models.IntegerField()
|
||||
|
||||
class Admin:
|
||||
list_display = ('site_name', 'site_website',)
|
||||
BIN
evennia/apps/config/models.pyc
Executable file
BIN
evennia/apps/config/models.pyc
Executable file
Binary file not shown.
1
evennia/apps/config/views.py
Executable file
1
evennia/apps/config/views.py
Executable file
|
|
@ -0,0 +1 @@
|
|||
# Create your views here.
|
||||
0
evennia/apps/objects/__init__.py
Executable file
0
evennia/apps/objects/__init__.py
Executable file
BIN
evennia/apps/objects/__init__.pyc
Executable file
BIN
evennia/apps/objects/__init__.pyc
Executable file
Binary file not shown.
92
evennia/apps/objects/models.py
Executable file
92
evennia/apps/objects/models.py
Executable file
|
|
@ -0,0 +1,92 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
class ObjectClass(models.Model):
|
||||
"""
|
||||
Each object class can have different behaviors to apply to it.
|
||||
"""
|
||||
name = models.CharField(maxlength=255)
|
||||
description = models.TextField()
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%d)" % (self.name, self.id,)
|
||||
|
||||
class Admin:
|
||||
list_display = ('name', 'description',)
|
||||
|
||||
class Attribute(models.Model):
|
||||
"""
|
||||
Attributes are things that are specific to different types of objects. For
|
||||
example, a drink container needs to store its fill level, whereas an exit
|
||||
needs to store its open/closed/locked/unlocked state. These are done via
|
||||
attributes, rather than making different classes for each object type and
|
||||
storing them directly. The added benefit is that we can add/remove attributes
|
||||
on the fly as we like.
|
||||
"""
|
||||
name = models.CharField(maxlength=255)
|
||||
value = models.CharField(maxlength=255)
|
||||
object = models.ForeignKey("Object")
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%d)" % (self.name, self.id,)
|
||||
|
||||
class Admin:
|
||||
list_display = ('name', 'value',)
|
||||
|
||||
class Object(models.Model):
|
||||
"""
|
||||
The Object class is very generic. We put all of our common attributes
|
||||
here and anything very particular into the attribute field. Notice the otype
|
||||
field. The different otypes denote an object's behaviors.
|
||||
"""
|
||||
|
||||
# Do not mess with the default types (0-4).
|
||||
OBJECT_TYPES = (
|
||||
(0, 'NOTHING'),
|
||||
(1, 'PLAYER'),
|
||||
(2, 'ROOM'),
|
||||
(3, 'THING'),
|
||||
(4, 'EXIT'),
|
||||
)
|
||||
|
||||
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)
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%d)" % (self.name, self.id,)
|
||||
|
||||
def is_type(self, typename):
|
||||
"""
|
||||
Do a string comparison of user's input and the object's type class object's
|
||||
name.
|
||||
"""
|
||||
return self.type.name == typename
|
||||
|
||||
def set_type(self, typename):
|
||||
"""
|
||||
Sets a object's type.
|
||||
"""
|
||||
pass
|
||||
|
||||
class Admin:
|
||||
list_display = ('name',)
|
||||
"""
|
||||
class Player(models.Model):
|
||||
#
|
||||
# Model representation of our players.
|
||||
#
|
||||
# Link back to our Django User class for password, username, email, etc.
|
||||
account = models.ForeignKey(User)
|
||||
location = models.ForeignKey(Object, related_name="plocation")
|
||||
is_connected = models.BooleanField()
|
||||
last_connected = models.DateTimeField()
|
||||
contents = models.ManyToManyField(Object)
|
||||
attributes = models.ManyToManyField(Attribute)
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%d)" % (self.name, self.id,)
|
||||
"""
|
||||
BIN
evennia/apps/objects/models.pyc
Executable file
BIN
evennia/apps/objects/models.pyc
Executable file
Binary file not shown.
1
evennia/apps/objects/views.py
Executable file
1
evennia/apps/objects/views.py
Executable file
|
|
@ -0,0 +1 @@
|
|||
# Create your views here.
|
||||
207
evennia/cmdhandler.py
Executable file
207
evennia/cmdhandler.py
Executable file
|
|
@ -0,0 +1,207 @@
|
|||
from django.contrib.auth.models import User
|
||||
from apps.objects.models import Object
|
||||
import settings
|
||||
import string
|
||||
from ansi import *
|
||||
"""
|
||||
This is the command processing module. It is instanced once in the main
|
||||
server module and the handle() function is hit every time a player sends
|
||||
something.
|
||||
"""
|
||||
|
||||
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
|
||||
player_loc = session.player_loc
|
||||
player_loc_obj = Object.objects.filter(id=player_loc)[0]
|
||||
|
||||
retval = "%s%s%s%s\n\r%s\n\r" % (
|
||||
ansi["normal"],
|
||||
ansi["hilite"],
|
||||
player_loc_obj.name,
|
||||
ansi["normal"],
|
||||
player_loc_obj.description,
|
||||
)
|
||||
session.push(retval)
|
||||
|
||||
def do_quit(self, cdat):
|
||||
"""
|
||||
Gracefully disconnect the user as per his own request.
|
||||
"""
|
||||
session = cdat['session']
|
||||
session.push("Quitting!\n\r")
|
||||
session.handle_close()
|
||||
|
||||
def do_who(self, cdat):
|
||||
"""
|
||||
Generic WHO command.
|
||||
"""
|
||||
session_list = cdat['server'].session_list
|
||||
session = cdat['session']
|
||||
|
||||
retval = "Player Name\n\r"
|
||||
for player in session_list:
|
||||
retval += '%s\n\r' % (player,)
|
||||
retval += '%d Players logged in.\n\r' % (len(session_list),)
|
||||
|
||||
session.push(retval)
|
||||
|
||||
def do_say(self, cdat):
|
||||
"""
|
||||
Room-based speech command.
|
||||
"""
|
||||
session_list = cdat['server'].session_list
|
||||
session = cdat['session']
|
||||
speech = cdat['uinput'][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),)
|
||||
for player in players_present:
|
||||
player.push("%s says, '%s'\n\r" % (session.name, speech,))
|
||||
|
||||
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.
|
||||
"""
|
||||
session = cdat['session']
|
||||
retval = "-"*50 +"\n\r"
|
||||
retval += "Evennia %s\n\r" % (settings.EVENNIA_VERSION,)
|
||||
retval += "-"*50 +"\n\r"
|
||||
session.push(retval)
|
||||
|
||||
class StaffCommands:
|
||||
"""
|
||||
Restricted staff commands.
|
||||
"""
|
||||
def do_dig(self, cdat):
|
||||
"""
|
||||
Digs a new room out.
|
||||
"""
|
||||
session = cdat['session']
|
||||
uinput= cdat['uinput']
|
||||
roomname = ''.join(uinput[1:])
|
||||
|
||||
if roomname == '':
|
||||
session.push("You must supply a room name!")
|
||||
else:
|
||||
newroom = Object()
|
||||
newroom.name = roomname
|
||||
newroom.type = "Room"
|
||||
|
||||
def do_nextfree(self, cdat):
|
||||
"""
|
||||
Returns the next free object number.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = cdat['server']
|
||||
|
||||
nextfree = server.nextfree_objnum()
|
||||
retval = "Next free object number: %d" % (nextfree,)
|
||||
|
||||
session.push(retval)
|
||||
|
||||
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,
|
||||
uses the Django database API and User model to make it extremely simple.
|
||||
"""
|
||||
session = cdat['session']
|
||||
uname = cdat['uinput'][1]
|
||||
password = cdat['uinput'][2]
|
||||
|
||||
account = User.objects.filter(username=uname)
|
||||
user = account[0]
|
||||
|
||||
autherror = "Invalid username or password!\n\r"
|
||||
if account.count() == 0:
|
||||
session.push(autherror)
|
||||
if not user.check_password(password):
|
||||
session.push(autherror)
|
||||
else:
|
||||
uname = user.username
|
||||
session.login(user)
|
||||
|
||||
def do_create(self, cdat):
|
||||
"""
|
||||
Handle the creation of new accounts.
|
||||
"""
|
||||
session = cdat['session']
|
||||
uname = cdat['uinput'][1]
|
||||
email = cdat['uinput'][2]
|
||||
password = cdat['uinput'][3]
|
||||
account = User.objects.filter(username=uname)
|
||||
|
||||
if not account.count() == 0:
|
||||
session.push("There is already a player with that name!")
|
||||
elif len(password) < 3:
|
||||
session.push("Your password must be 3 characters or longer.")
|
||||
else:
|
||||
session.create_user(uname, email, password)
|
||||
|
||||
def do_quit(self, cdat):
|
||||
"""
|
||||
We're going to maintain a different version of the quit command
|
||||
here for unconnected users for the sake of simplicity. The logged in
|
||||
version will be a bit more complicated.
|
||||
"""
|
||||
session = cdat['session']
|
||||
session.push("Disconnecting...\n\r")
|
||||
session.handle_close()
|
||||
|
||||
# We'll use this for our getattr() in the Handler class.
|
||||
gencommands = GenCommands()
|
||||
staffcommands = StaffCommands()
|
||||
unloggedincommands = UnLoggedInCommands()
|
||||
|
||||
class Handler:
|
||||
def __init__(self): pass
|
||||
def handle(self, cdat):
|
||||
"""
|
||||
Use the spliced (list) uinput variable to retrieve the correct
|
||||
command, or return an invalid command error.
|
||||
|
||||
We're basically grabbing the player's command by tacking
|
||||
their input on to 'do_' and looking it up in the GenCommands
|
||||
class.
|
||||
"""
|
||||
session = cdat['session']
|
||||
uinput = cdat['uinput']
|
||||
|
||||
if session.logged_in:
|
||||
# If it's prefixed by an '@', it's a staff command.
|
||||
if uinput[0].find('@') == -1:
|
||||
cmdtable = gencommands
|
||||
else:
|
||||
cmdtable = staffcommands
|
||||
else:
|
||||
cmdtable = unloggedincommands
|
||||
cmd = getattr(cmdtable, 'do_' + uinput[0].lower(), None)
|
||||
|
||||
if callable(cmd):
|
||||
cmd(cdat)
|
||||
else:
|
||||
session.push("Unknown command.\n\r")
|
||||
|
||||
BIN
evennia/cmdhandler.pyc
Executable file
BIN
evennia/cmdhandler.pyc
Executable file
Binary file not shown.
0
evennia/commonfuncs.py
Executable file
0
evennia/commonfuncs.py
Executable file
11
evennia/manage.py
Executable file
11
evennia/manage.py
Executable file
|
|
@ -0,0 +1,11 @@
|
|||
#!/usr/bin/env python
|
||||
from django.core.management import execute_manager
|
||||
try:
|
||||
import settings # Assumed to be in the same directory.
|
||||
except ImportError:
|
||||
import sys
|
||||
sys.stderr.write("Error: Can't find the file 'settings.py' in the directory containing %r. It appears you've customized things.\nYou'll have to run django-admin.py, passing it your settings module.\n(If the file settings.py does indeed exist, it's causing an ImportError somehow.)\n" % __file__)
|
||||
sys.exit(1)
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_manager(settings)
|
||||
3
evennia/prepenv.sh
Executable file
3
evennia/prepenv.sh
Executable file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
export DJANGO_SETTINGS_MODULE="settings"
|
||||
python server.py
|
||||
94
evennia/server.py
Executable file
94
evennia/server.py
Executable file
|
|
@ -0,0 +1,94 @@
|
|||
from asyncore import dispatcher
|
||||
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.objects.models import Object
|
||||
|
||||
#
|
||||
## Begin: Time Functions
|
||||
#
|
||||
|
||||
schedule = {'heal':100.0}
|
||||
lastrun = {}
|
||||
|
||||
def heal():
|
||||
pass
|
||||
|
||||
# The timer loop
|
||||
def Timer(timer):
|
||||
|
||||
sched = schedule.iteritems()
|
||||
for i in sched:
|
||||
try: lastrun[i[0]]
|
||||
except: lastrun[i[0]] = time.time()
|
||||
|
||||
diff = timer - lastrun[i[0]]
|
||||
|
||||
# Every 100 seconds, run heal(), defined above.
|
||||
if diff >= schedule['heal']:
|
||||
heal()
|
||||
lastrun['heal'] = time.time()
|
||||
|
||||
#
|
||||
## End: Time Functions
|
||||
#
|
||||
|
||||
class Server(dispatcher):
|
||||
"""
|
||||
The main server class from which everything branches.
|
||||
"""
|
||||
def __init__(self, port):
|
||||
dispatcher.__init__(self)
|
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.set_reuse_addr()
|
||||
self.bind(('', 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 '-'*50
|
||||
|
||||
def load_config(self):
|
||||
"""
|
||||
Loads our site's configuration up for easy access.
|
||||
"""
|
||||
self.config = Config.objects.all()[0]
|
||||
print ' Configuration Loaded.'
|
||||
|
||||
def load_objects(self):
|
||||
"""
|
||||
Load all of our objects into memory.
|
||||
"""
|
||||
object_list = Object.objects.all()
|
||||
for object in object_list:
|
||||
dbnum = object.id
|
||||
self.object_list[dbnum] = object
|
||||
print ' Objects Loaded: %i' % (len(self.object_list),)
|
||||
|
||||
def handle_accept(self):
|
||||
"""
|
||||
What to do when we get a connection.
|
||||
"""
|
||||
conn, addr = self.accept()
|
||||
session = PlayerSession(self, conn, addr)
|
||||
session.game_connect_screen(session)
|
||||
print 'Connection:', str(session)
|
||||
self.session_list.append(session)
|
||||
print 'Sessions active:', len(self.session_list)
|
||||
|
||||
if __name__ == '__main__':
|
||||
server = Server(4000)
|
||||
|
||||
try:
|
||||
while server.game_running:
|
||||
asyncore.loop(timeout=5, count=1) # Timer() called every 5 seconds.
|
||||
Timer(time.time())
|
||||
|
||||
except KeyboardInterrupt:
|
||||
print 'Interrupted'
|
||||
BIN
evennia/server.pyc
Executable file
BIN
evennia/server.pyc
Executable file
Binary file not shown.
117
evennia/sessions.py
Executable file
117
evennia/sessions.py
Executable file
|
|
@ -0,0 +1,117 @@
|
|||
from asyncore import dispatcher
|
||||
from asynchat import async_chat
|
||||
import socket, asyncore, time, sys
|
||||
from cmdhandler import *
|
||||
|
||||
chandler = Handler()
|
||||
|
||||
class PlayerSession(async_chat):
|
||||
"""
|
||||
This class represents a player's sesssion. From here we branch down into
|
||||
other various classes, please try to keep this one tidy!
|
||||
"""
|
||||
def __init__(self, server, sock, addr):
|
||||
async_chat.__init__(self, sock)
|
||||
self.server = server
|
||||
self.address = addr
|
||||
self.set_terminator("\n")
|
||||
self.name = None
|
||||
self.data = []
|
||||
self.sock = sock
|
||||
self.logged_in = False
|
||||
self.user = None
|
||||
# The time the user last issued a command.
|
||||
self.cmd_last = time.time()
|
||||
# Total number of commands issued.
|
||||
self.cmd_total = 0
|
||||
# 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
|
||||
|
||||
def collect_incoming_data(self, data):
|
||||
"""
|
||||
Stuff any incoming data into our buffer, self.data
|
||||
"""
|
||||
self.data.append(data)
|
||||
|
||||
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.
|
||||
"""
|
||||
line = (''.join(self.data))
|
||||
line = line.strip('\r')
|
||||
uinput = line.split(' ')
|
||||
self.data = []
|
||||
|
||||
# Increment our user's command counter.
|
||||
self.cmd_total += 1
|
||||
# Store the timestamp of the user's last command.
|
||||
self.cmd_last = time.time()
|
||||
# Stuff anything we need to pass in this dictionary.
|
||||
cdat = {"server": self.server, "uinput": uinput, "session": self}
|
||||
chandler.handle(cdat)
|
||||
|
||||
def handle_close(self):
|
||||
"""
|
||||
Break the connection and do some accounting.
|
||||
"""
|
||||
async_chat.handle_close(self)
|
||||
self.logged_in = False
|
||||
self.server.session_list.remove(self)
|
||||
print 'Sessions active:', len(self.server.session_list)
|
||||
|
||||
def game_connect_screen(self, session):
|
||||
"""
|
||||
Show our banner screen.
|
||||
"""
|
||||
buffer = '-'*50
|
||||
buffer += ' \n\rWelcome to Evennia!\n\r'
|
||||
buffer += '-'*50 + '\n\r'
|
||||
buffer += """Please type one of the following to begin:\n\r
|
||||
connect <username> <password>\n\r
|
||||
create <username> <email> <password>\n\r"""
|
||||
buffer += '-'*50 + '\n\r'
|
||||
session.push(buffer)
|
||||
|
||||
def login(self, user):
|
||||
"""
|
||||
After the user has authenticated, handle logging him in.
|
||||
"""
|
||||
self.user = user
|
||||
self.name = user.username
|
||||
self.logged_in = True
|
||||
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
|
||||
this a lot in the server logs and stuff.
|
||||
"""
|
||||
if self.logged_in:
|
||||
symbol = '#'
|
||||
else:
|
||||
symbol = '?'
|
||||
return "<%s> %s@%s" % (symbol, self.name, self.address,)
|
||||
|
||||
# def handle_error(self):
|
||||
# self.handle_close()
|
||||
BIN
evennia/sessions.pyc
Executable file
BIN
evennia/sessions.pyc
Executable file
Binary file not shown.
85
evennia/settings.py
Executable file
85
evennia/settings.py
Executable file
|
|
@ -0,0 +1,85 @@
|
|||
# Django settings for evennia project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@domain.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASE_ENGINE = 'mysql' # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
|
||||
DATABASE_NAME = 'evennia' # Or path to database file if using sqlite3.
|
||||
DATABASE_USER = 'evennia' # Not used with sqlite3.
|
||||
DATABASE_PASSWORD = 'CvAPpy:FFRTmTMHf' # Not used with sqlite3.
|
||||
DATABASE_HOST = '' # Set to empty string for localhost. Not used with sqlite3.
|
||||
DATABASE_PORT = '' # Set to empty string for default. Not used with sqlite3.
|
||||
|
||||
# Local time zone for this installation. All choices can be found here:
|
||||
# http://www.postgresql.org/docs/current/static/datetime-keywords.html#DATETIME-TIMEZONE-SET-TABLE
|
||||
TIME_ZONE = 'America/New_York'
|
||||
|
||||
# Language code for this installation. All choices can be found here:
|
||||
# http://www.w3.org/TR/REC-html40/struct/dirlang.html#langcodes
|
||||
# http://blogs.law.harvard.edu/tech/stories/storyReader$15
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# If you set this to False, Django will make some optimizations so as not
|
||||
# to load the internationalization machinery.
|
||||
USE_I18N = False
|
||||
|
||||
# Absolute path to the directory that holds media.
|
||||
# Example: "/home/media/media.lawrence.com/"
|
||||
MEDIA_ROOT = '/home/gtaylor/dev/evennia/media'
|
||||
|
||||
# URL that handles the media served from MEDIA_ROOT.
|
||||
# Example: "http://media.lawrence.com"
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
# URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a
|
||||
# trailing slash.
|
||||
# Examples: "http://foo.com/media/", "/media/".
|
||||
ADMIN_MEDIA_PREFIX = '/media/amedia/'
|
||||
|
||||
# Make this unique, and don't share it with anybody.
|
||||
SECRET_KEY = 'fsd&lkj^LKJ8398200(@)(38919#23892(*$*#(981'
|
||||
|
||||
# List of callables that know how to import templates from various sources.
|
||||
TEMPLATE_LOADERS = (
|
||||
'django.template.loaders.filesystem.load_template_source',
|
||||
'django.template.loaders.app_directories.load_template_source',
|
||||
# 'django.template.loaders.eggs.load_template_source',
|
||||
)
|
||||
|
||||
MIDDLEWARE_CLASSES = (
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.middleware.doc.XViewMiddleware',
|
||||
)
|
||||
|
||||
ROOT_URLCONF = 'evennia.urls'
|
||||
|
||||
TEMPLATE_DIRS = (
|
||||
# Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
|
||||
# Always use forward slashes, even on Windows.
|
||||
# Don't forget to use absolute paths, not relative paths.
|
||||
)
|
||||
|
||||
INSTALLED_APPS = (
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.admin',
|
||||
'evennia.apps.config',
|
||||
'evennia.apps.objects',
|
||||
# 'django.contrib.sites',
|
||||
)
|
||||
|
||||
#
|
||||
## Evennia Config
|
||||
#
|
||||
EVENNIA_VERSION = 'Pre-Alpha 0.0'
|
||||
BIN
evennia/settings.pyc
Executable file
BIN
evennia/settings.pyc
Executable file
Binary file not shown.
9
evennia/urls.py
Executable file
9
evennia/urls.py
Executable file
|
|
@ -0,0 +1,9 @@
|
|||
from django.conf.urls.defaults import *
|
||||
|
||||
urlpatterns = patterns('',
|
||||
# Example:
|
||||
# (r'^evennia/', include('evennia.apps.foo.urls.foo')),
|
||||
|
||||
# Uncomment this for admin:
|
||||
(r'^admin/', include('django.contrib.admin.urls')),
|
||||
)
|
||||
Loading…
Add table
Add a link
Reference in a new issue