Re-organization.
This commit is contained in:
parent
5db3ae2933
commit
5421ab7f6e
38 changed files with 0 additions and 0 deletions
|
|
@ -1,74 +0,0 @@
|
|||
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.
|
||||
|
|
@ -1,59 +0,0 @@
|
|||
About Evennia
|
||||
-------------
|
||||
Evennia is a proof-of-concept MU* server that aims to provide a functional
|
||||
base for developers. While there are quite a few codebases that do the same
|
||||
(and very well in many cases), we are taking a unique spin on the problem.
|
||||
Some of our flagship features include (or will one day include):
|
||||
|
||||
* Extensive web integration.
|
||||
* The ability to build/administer through a web browser.
|
||||
* Shared accounts between the website and the game.
|
||||
* Optional web-based character creation.
|
||||
* Extremely easy-to-manipulate SQL database back-end via Django
|
||||
(djangoproject.com)
|
||||
* Simple and easily extensible design.
|
||||
* Very granular permissions. Individual and group based.
|
||||
|
||||
The essential points here are the web integration and the SQL backing via
|
||||
Django. The Django framework has database abstraction abilities that give us
|
||||
many features free, such as:
|
||||
|
||||
* The codebase will run transparently on MySQL, SQLite, or Postgres
|
||||
* At the time of this document's writing, our SQL-backed application here
|
||||
contains 0 lines of SQL. Django's database abstraction layer is absolutely
|
||||
simple yet very powerful.
|
||||
* For any model we outline for the server's use, we have the ability to
|
||||
more or less automatically generate a web-based admin interface for it with
|
||||
two lines of code. This lets you Create, Update, or Delete entries.
|
||||
* On the web-based side of things, features such as automatic form validation,
|
||||
abstraction of sessions and cookies, and access to whatever game data you
|
||||
desire are all attractive.
|
||||
|
||||
Installation
|
||||
------------
|
||||
At this point in time, the codebase is changing so rapidly that writing
|
||||
installation instructions is pretty much pointless. When we get to that stage
|
||||
in development, we'll make sure to update this. But for the really determined
|
||||
(or stubborn), here's a rough outline:
|
||||
|
||||
* Install Django.
|
||||
* Copy the Evennia source somewhere.
|
||||
* Set up your apache2.conf to point mod-python to the settings.py file if you
|
||||
want the web features.
|
||||
* Copy settings.py.dist to settings.py.
|
||||
* Edit settings.py with your database info.
|
||||
* Run 'python manage.py syncdb'
|
||||
* Run prepenv.sh. This will start the MU* server on port 4000 by default. You
|
||||
may change this via the web interface or by editing the config table in SQL.
|
||||
|
||||
Support and Development
|
||||
-----------------------
|
||||
Since we're so early in development, we really can't hope to offer much support.
|
||||
However, if you'd like to report bugs, make suggestions, or help with the
|
||||
code work, visit either or both of the following links:
|
||||
|
||||
* Evennia Code Page
|
||||
http://code.google.com/p/evennia/
|
||||
|
||||
* Evennia Google Group
|
||||
http://groups-beta.google.com/group/evennia?hl=en
|
||||
|
|
@ -1,31 +0,0 @@
|
|||
TODO List
|
||||
---------
|
||||
The TODO list has all of the things currently needing attention the most in it.
|
||||
If you are feeling ambitious, tackle as much as you can and send patches
|
||||
to the project site or via email to gtaylor@clemson.edu.
|
||||
|
||||
High Priority Tasks
|
||||
-------------------
|
||||
* Pretty much all of the commands need some kind of required permission(s).
|
||||
Right now, pretty much everyone has access to everything. Django has
|
||||
a built-in permissions system, it should be trivial to use it for
|
||||
the purpose.
|
||||
* Figure out how to handle our scripting support.
|
||||
* Handle multi-word account names gracefully. Need to modify login prompt
|
||||
to use quotes like MUX.
|
||||
* Look into logging in with your email address instead of username.
|
||||
* Implement @alias. Can probably just use an attribute instead of a new
|
||||
object DB field. Shouldn't be too bad. Also needs proper search
|
||||
functions in functions_db.py.
|
||||
* Implement player to player paging/telling.
|
||||
* Implement a global comsys of some sort. Use MUX2's commands and stuff.
|
||||
|
||||
Medium Priority Tasks
|
||||
---------------------
|
||||
* We're going to need a delayed event queue in addition to the scheduler.
|
||||
For example: I want player X to perform this action in Y seconds.
|
||||
|
||||
Low Priority Tasks
|
||||
------------------
|
||||
* Write lots of in-game help files.
|
||||
* Start webbifying the room/thing/exit building process.
|
||||
|
|
@ -1,94 +0,0 @@
|
|||
import re
|
||||
"""
|
||||
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"
|
||||
|
||||
# Formatting Characters
|
||||
ansi["return"] = "\r\n"
|
||||
ansi["tab"] = "\t"
|
||||
ansi["space"] = " "
|
||||
|
||||
def parse_ansi(string, strip_ansi=False, strip_formatting=False):
|
||||
"""
|
||||
Parses a string, subbing color codes as needed.
|
||||
"""
|
||||
if strip_formatting:
|
||||
char_return = ""
|
||||
char_tab = ""
|
||||
char_space = ""
|
||||
else:
|
||||
char_return = ansi["return"]
|
||||
char_tab = ansi["tab"]
|
||||
char_space = ansi["space"]
|
||||
|
||||
ansi_subs = [
|
||||
(r'%r', char_return),
|
||||
(r'%t', char_tab),
|
||||
(r'%b', char_space),
|
||||
(r'%cf', ansi["blink"]),
|
||||
(r'%ci', ansi["inverse"]),
|
||||
(r'%ch', ansi["hilite"]),
|
||||
(r'%cn', ansi["normal"]),
|
||||
(r'%cx', ansi["black"]),
|
||||
(r'%cX', ansi["back_black"]),
|
||||
(r'%cr', ansi["red"]),
|
||||
(r'%cR', ansi["back_red"]),
|
||||
(r'%cg', ansi["green"]),
|
||||
(r'%cG', ansi["back_green"]),
|
||||
(r'%cy', ansi["yellow"]),
|
||||
(r'%cY', ansi["back_yellow"]),
|
||||
(r'%cb', ansi["blue"]),
|
||||
(r'%cB', ansi["back_blue"]),
|
||||
(r'%cm', ansi["magenta"]),
|
||||
(r'%cM', ansi["back_magenta"]),
|
||||
(r'%cc', ansi["cyan"]),
|
||||
(r'%cC', ansi["back_cyan"]),
|
||||
(r'%cw', ansi["white"]),
|
||||
(r'%cW', ansi["back_white"]),
|
||||
]
|
||||
|
||||
for sub in ansi_subs:
|
||||
p = re.compile(sub[0], re.DOTALL)
|
||||
if strip_ansi:
|
||||
string = p.sub("", string)
|
||||
else:
|
||||
string = p.sub(sub[1], string)
|
||||
|
||||
if strip_ansi:
|
||||
return '%s' % (string)
|
||||
else:
|
||||
return '%s%s' % (string, ansi["normal"])
|
||||
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
from django.db import models
|
||||
|
||||
class CommandAlias(models.Model):
|
||||
"""
|
||||
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)
|
||||
|
||||
class Admin:
|
||||
list_display = ('user_input', 'equiv_command',)
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Command aliases"
|
||||
ordering = ['user_input']
|
||||
|
||||
class ConfigValue(models.Model):
|
||||
"""
|
||||
Experimental new config model.
|
||||
"""
|
||||
conf_key = models.CharField(maxlength=100)
|
||||
conf_value = models.CharField(maxlength=255 )
|
||||
|
||||
class Admin:
|
||||
list_display = ('conf_key', 'conf_value',)
|
||||
|
|
@ -1 +0,0 @@
|
|||
# Create your views here.
|
||||
|
|
@ -1,24 +0,0 @@
|
|||
from django.db import models
|
||||
|
||||
class GenericPerm(models.Model):
|
||||
"""
|
||||
This is merely a container class for some generic permissions that don't
|
||||
fit under a particular module.
|
||||
"""
|
||||
class Meta:
|
||||
permissions = (
|
||||
("announce", "May use @wall to make announcements"),
|
||||
("boot", "May use @boot to kick players"),
|
||||
("builder", "May build"),
|
||||
("chown_all", "Can @chown anything to anyone."),
|
||||
("control_all", "May control everything"),
|
||||
("examine_all", "Can examine any object"),
|
||||
("extended_who", "May see extended WHO list"),
|
||||
("free_money", "Has infinite money"),
|
||||
("long_fingers", "May get/look/examine etc. from a distance"),
|
||||
("steal", "May give negative money"),
|
||||
("set_hide", "May set themself invisible"),
|
||||
("shutdown", "May @shutdown the site"),
|
||||
("tel_anywhere", "May @teleport anywhere"),
|
||||
("tel_anyone", "May @teleport anything"),
|
||||
)
|
||||
|
|
@ -1 +0,0 @@
|
|||
# Create your views here.
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
from django.db import models
|
||||
import ansi
|
||||
|
||||
class HelpEntry(models.Model):
|
||||
"""
|
||||
A generic help entry.
|
||||
"""
|
||||
topicname = models.CharField(maxlength=255)
|
||||
entrytext = models.TextField(blank=True, null=True)
|
||||
staff_only = models.BooleanField(default=0)
|
||||
|
||||
class Admin:
|
||||
list_display = ('id', 'topicname', 'staff_only')
|
||||
list_filter = ('staff_only',)
|
||||
search_fields = ['entrytext']
|
||||
|
||||
class Meta:
|
||||
verbose_name_plural = "Help entries"
|
||||
ordering = ['topicname']
|
||||
|
||||
def __str__(self):
|
||||
return self.topicname
|
||||
|
||||
def get_topicname(self):
|
||||
"""
|
||||
Returns the topic's name.
|
||||
"""
|
||||
try:
|
||||
return self.topicname
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_entrytext_ingame(self):
|
||||
"""
|
||||
Gets the entry text for in-game viewing.
|
||||
"""
|
||||
try:
|
||||
return ansi.parse_ansi(self.entrytext)
|
||||
except:
|
||||
return None
|
||||
|
|
@ -1 +0,0 @@
|
|||
# Create your views here.
|
||||
|
|
@ -1,608 +0,0 @@
|
|||
from django.db import models
|
||||
from django.contrib.auth.models import User
|
||||
import defines_global as global_defines
|
||||
import ansi
|
||||
|
||||
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)
|
||||
is_hidden = models.BooleanField(default=0)
|
||||
object = models.ForeignKey("Object")
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%s)" % (self.name, self.id)
|
||||
|
||||
class Admin:
|
||||
list_display = ('object', 'name', 'value',)
|
||||
search_fields = ['name']
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Returns an attribute's name.
|
||||
"""
|
||||
return self.name
|
||||
|
||||
class Object(models.Model):
|
||||
"""
|
||||
The Object class is very generic representation of a THING, PLAYER, EXIT,
|
||||
ROOM, or other entities within the database. Pretty much anything in the
|
||||
game is an object. Objects may be one of several different types, and
|
||||
may be parented to allow for differing behaviors.
|
||||
|
||||
We eventually want to find some way to implement object parents via loadable
|
||||
modules or sub-classing.
|
||||
"""
|
||||
name = models.CharField(maxlength=255)
|
||||
ansi_name = models.CharField(maxlength=255)
|
||||
owner = models.ForeignKey('self', related_name="obj_owner", blank=True, null=True)
|
||||
zone = models.ForeignKey('self', related_name="obj_zone", blank=True, null=True)
|
||||
home = models.ForeignKey('self', related_name="obj_home", blank=True, null=True)
|
||||
type = models.SmallIntegerField(choices=global_defines.OBJECT_TYPES)
|
||||
description = models.TextField(blank=True, null=True)
|
||||
location = models.ForeignKey('self', related_name="obj_location", blank=True, null=True)
|
||||
flags = models.TextField(blank=True, null=True)
|
||||
nosave_flags = models.TextField(blank=True, null=True)
|
||||
date_created = models.DateField(editable=False, auto_now_add=True)
|
||||
|
||||
def __cmp__(self, other):
|
||||
"""
|
||||
Used to figure out if one object is the same as another.
|
||||
"""
|
||||
return self.id == other.id
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % (self.get_name(),)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-date_created', 'id']
|
||||
|
||||
class Admin:
|
||||
list_display = ('id', 'name', 'type', 'date_created')
|
||||
list_filter = ('type',)
|
||||
search_fields = ['name']
|
||||
save_on_top = True
|
||||
|
||||
"""
|
||||
BEGIN COMMON METHODS
|
||||
"""
|
||||
def emit_to(self, message):
|
||||
"""
|
||||
Emits something to any sessions attached to the object.
|
||||
|
||||
message: (str) The message to send
|
||||
"""
|
||||
# We won't allow emitting to objects... yet.
|
||||
if not self.is_player():
|
||||
return False
|
||||
|
||||
session = session_mgr.session_from_object(self)
|
||||
if session:
|
||||
session.msg(ansi.parse_ansi(message))
|
||||
else:
|
||||
return False
|
||||
|
||||
def emit_to_contents(self, message, exclude=None):
|
||||
"""
|
||||
Emits something to all objects inside an object.
|
||||
"""
|
||||
contents = self.get_contents()
|
||||
|
||||
if exclude:
|
||||
contents.remove(exclude)
|
||||
|
||||
for obj in contents:
|
||||
obj.emit_to(message)
|
||||
|
||||
def is_staff(self):
|
||||
"""
|
||||
Returns TRUE if the object is a staff player.
|
||||
"""
|
||||
if not self.is_player():
|
||||
return False
|
||||
|
||||
profile = User.objects.filter(id=self.id)
|
||||
|
||||
if len(profile) == 0:
|
||||
return False
|
||||
else:
|
||||
return profile[0].is_staff
|
||||
|
||||
def is_superuser(self):
|
||||
"""
|
||||
Returns TRUE if the object is a super user player.
|
||||
"""
|
||||
if not self.is_player():
|
||||
return False
|
||||
|
||||
profile = User.objects.filter(id=self.id)
|
||||
|
||||
if len(profile) == 0:
|
||||
return False
|
||||
else:
|
||||
return profile[0].is_superuser
|
||||
|
||||
def owns_other(self, other_obj):
|
||||
"""
|
||||
See if the envoked object owns another object.
|
||||
other_obj: (Object) Reference for object to check ownership of.
|
||||
"""
|
||||
return self.id == other_obj.get_owner().id
|
||||
|
||||
def controls_other(self, other_obj):
|
||||
"""
|
||||
See if the envoked object controls another object.
|
||||
other_obj: (Object) Reference for object to check dominance of.
|
||||
"""
|
||||
if self == other_obj:
|
||||
return True
|
||||
|
||||
if self.is_superuser():
|
||||
# Don't allow superusers to dominate other superusers.
|
||||
if not other_obj.is_superuser():
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
if self.owns_other(other_obj):
|
||||
# If said object owns the target, then give it the green.
|
||||
return True
|
||||
else:
|
||||
# Still pending more stuff here, for now assume we have
|
||||
# dominance. Bad assumption.
|
||||
return True
|
||||
|
||||
def set_home(self, new_home):
|
||||
"""
|
||||
Sets an object's home.
|
||||
"""
|
||||
self.home = new_home
|
||||
self.save()
|
||||
|
||||
def set_name(self, new_name):
|
||||
"""
|
||||
Rename an object.
|
||||
"""
|
||||
self.name = ansi.parse_ansi(new_name, strip_ansi=True)
|
||||
self.ansi_name = ansi.parse_ansi(new_name, strip_formatting=True)
|
||||
self.save()
|
||||
|
||||
# If it's a player, we need to update their user object as well.
|
||||
if self.is_player():
|
||||
pobject = User.objects.get(id=self.id)
|
||||
pobject.name = new_name
|
||||
pobject.save()
|
||||
|
||||
def get_user_account(self):
|
||||
"""
|
||||
Returns the player object's account object.
|
||||
"""
|
||||
if not self.is_player():
|
||||
return False
|
||||
return User.objects.get(id=self.id)
|
||||
|
||||
def get_name(self, fullname=False):
|
||||
"""
|
||||
Returns an object's name.
|
||||
"""
|
||||
if fullname:
|
||||
return "%s(#%s%s)" % (ansi.parse_ansi(self.name, strip_ansi=True),self.id, self.flag_string())
|
||||
else:
|
||||
return "%s(#%s%s)" % (ansi.parse_ansi(self.name.split(';')[0], strip_ansi=True), self.id, self.flag_string())
|
||||
|
||||
def get_ansiname(self, fullname=False):
|
||||
"""
|
||||
Returns an object's ANSI'd name.
|
||||
"""
|
||||
if self.ansi_name:
|
||||
if fullname:
|
||||
return "%s(#%s%s)" % (ansi.parse_ansi(self.ansi_name), self.id, self.flag_string())
|
||||
else:
|
||||
return "%s(#%s%s)" % (ansi.parse_ansi(self.ansi_name.split(';')[0]), self.id, self.flag_string())
|
||||
else:
|
||||
return self.get_name()
|
||||
|
||||
def set_description(self, new_desc):
|
||||
"""
|
||||
Rename an object.
|
||||
"""
|
||||
self.description = new_desc
|
||||
self.save()
|
||||
|
||||
def get_description(self, no_parsing=False, wrap_width=False):
|
||||
"""
|
||||
Returns an object's ANSI'd description.
|
||||
"""
|
||||
try:
|
||||
if no_parsing:
|
||||
retval = self.description
|
||||
else:
|
||||
retval = ansi.parse_ansi(self.description)
|
||||
|
||||
if wrap_width:
|
||||
# TODO: Broken for some reason? Returning None.
|
||||
return functions_general.word_wrap(retval, width=wrap_width)
|
||||
else:
|
||||
return retval
|
||||
except:
|
||||
return ""
|
||||
|
||||
def get_flags(self):
|
||||
"""
|
||||
Returns an object's flag list.
|
||||
"""
|
||||
flags = self.flags
|
||||
nosave_flags = self.nosave_flags
|
||||
if not flags:
|
||||
flags = ""
|
||||
if not nosave_flags:
|
||||
nosave_flags = ""
|
||||
|
||||
return '%s %s' % (flags, nosave_flags)
|
||||
|
||||
def clear_attribute(self, attribute):
|
||||
"""
|
||||
Removes an attribute entirely.
|
||||
|
||||
attribute: (str) The attribute's name.
|
||||
"""
|
||||
if self.has_attribute(attribute):
|
||||
attrib_obj = self.get_attribute_obj(attribute)
|
||||
attrib_obj.delete()
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_all_attributes(self):
|
||||
"""
|
||||
Returns a QuerySet of an object's attributes.
|
||||
"""
|
||||
return self.attribute_set.all()
|
||||
|
||||
def clear_all_attributes(self):
|
||||
"""
|
||||
Clears all of an object's attributes.
|
||||
"""
|
||||
attribs = self.get_all_attributes()
|
||||
for attrib in attribs:
|
||||
self.delete()
|
||||
|
||||
def destroy(self):
|
||||
"""
|
||||
Destroys an object, sets it to GOING. Can still be recovered
|
||||
if the user decides to.
|
||||
"""
|
||||
|
||||
# See if we need to kick the player off.
|
||||
session = session_mgr.session_from_object(self)
|
||||
if session:
|
||||
session.msg("You have been destroyed, goodbye.")
|
||||
session.handle_close()
|
||||
|
||||
# If the object is a player, set the player account object to inactive.
|
||||
# It can still be recovered at this point.
|
||||
if self.is_player():
|
||||
try:
|
||||
uobj = User.objects.get(id=self.id)
|
||||
uobj.is_active = False
|
||||
uobj.save()
|
||||
except:
|
||||
functions_general.print_errmsg('Destroying object %s but no matching player.' % (self,))
|
||||
|
||||
# Set the object type to GOING
|
||||
self.type = 5
|
||||
self.save()
|
||||
|
||||
def delete(self):
|
||||
"""
|
||||
Deletes an object permanently. Marks it for re-use by a new object.
|
||||
"""
|
||||
# Delete the associated player object permanently.
|
||||
uobj = User.objects.filter(id=self.id)
|
||||
if len(uobj) > 0:
|
||||
uobj[0].delete()
|
||||
|
||||
# Set the object to type GARBAGE.
|
||||
self.type = 6
|
||||
self.save()
|
||||
self.clear_all_attributes()
|
||||
|
||||
def set_attribute(self, attribute, new_value):
|
||||
"""
|
||||
Sets an attribute on an object. Creates the attribute if need
|
||||
be.
|
||||
|
||||
attribute: (str) The attribute's name.
|
||||
new_value: (str) The value to set the attribute to.
|
||||
"""
|
||||
if self.has_attribute(attribute):
|
||||
# Attribute already exists, update it.
|
||||
attrib_obj = Attribute.objects.filter(object=self).filter(name=attribute)
|
||||
attrib_obj.value = new_value
|
||||
attrib_obj.save()
|
||||
else:
|
||||
# Attribute object doesn't exist, create it.
|
||||
new_attrib = Attribute()
|
||||
new_attrib.name = attribute
|
||||
new_attrib.value = new_value
|
||||
new_attrib.object = self
|
||||
new_attrib.save()
|
||||
|
||||
def has_attribute(self, attribute):
|
||||
"""
|
||||
See if we have an attribute set on the object.
|
||||
|
||||
attribute: (str) The attribute's name.
|
||||
"""
|
||||
attr = Attribute.objects.filter(object=self).filter(name=attribute)
|
||||
if attr.count() == 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def has_flag(self, flag):
|
||||
"""
|
||||
Does our object have a certain flag?
|
||||
|
||||
flag: (str) Flag name
|
||||
"""
|
||||
# For whatever reason, we have to do this so things work
|
||||
# in SQLite.
|
||||
flags = str(self.flags).split()
|
||||
nosave_flags = str(self.nosave_flags).split()
|
||||
return flag in flags or flag in nosave_flags
|
||||
|
||||
def set_flag(self, flag, value):
|
||||
"""
|
||||
Add a flag to our object's flag list.
|
||||
|
||||
flag: (str) Flag name
|
||||
value: (bool) Set (True) or un-set (False)
|
||||
"""
|
||||
flag = flag.upper()
|
||||
has_flag = self.has_flag(flag)
|
||||
|
||||
if value == False and has_flag:
|
||||
# Clear the flag.
|
||||
if functions_db.is_unsavable_flag(flag):
|
||||
# Not a savable flag (CONNECTED, etc)
|
||||
flags = self.nosave_flags.split()
|
||||
flags.remove(flag)
|
||||
self.nosave_flags = ' '.join(flags)
|
||||
else:
|
||||
# Is a savable flag.
|
||||
flags = self.flags.split()
|
||||
flags.remove(flag)
|
||||
self.flags = ' '.join(flags)
|
||||
self.save()
|
||||
|
||||
elif value == False and not has_flag:
|
||||
# Object doesn't have the flag to begin with.
|
||||
pass
|
||||
elif value == True and has_flag:
|
||||
# We've already go it.
|
||||
pass
|
||||
else:
|
||||
# Setting a flag.
|
||||
if functions_db.is_unsavable_flag(flag):
|
||||
# Not a savable flag (CONNECTED, etc)
|
||||
flags = str(self.nosave_flags).split()
|
||||
flags.append(flag)
|
||||
self.nosave_flags = ' '.join(flags)
|
||||
else:
|
||||
# Is a savable flag.
|
||||
flags = str(self.flags).split()
|
||||
flags.append(flag)
|
||||
self.flags = ' '.join(flags)
|
||||
self.save()
|
||||
|
||||
def is_connected_plr(self):
|
||||
"""
|
||||
Is this object a connected player?
|
||||
"""
|
||||
if self.is_player():
|
||||
if session_mgr.session_from_object(self):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_owner(self):
|
||||
"""
|
||||
Returns an object's owner.
|
||||
"""
|
||||
# Players always own themselves.
|
||||
if self.is_player():
|
||||
return self
|
||||
else:
|
||||
return self.owner
|
||||
|
||||
def get_home(self):
|
||||
"""
|
||||
Returns an object's home.
|
||||
"""
|
||||
try:
|
||||
return self.home
|
||||
except:
|
||||
return None
|
||||
|
||||
def get_location(self):
|
||||
"""
|
||||
Returns an object's location.
|
||||
"""
|
||||
try:
|
||||
return self.location
|
||||
except:
|
||||
return False
|
||||
|
||||
def get_attribute_value(self, attrib, default=False):
|
||||
"""
|
||||
Returns the value of an attribute on an object.
|
||||
|
||||
attrib: (str) The attribute's name.
|
||||
"""
|
||||
if self.has_attribute(attrib):
|
||||
attrib = Attribute.objects.filter(object=self).filter(name=attrib)
|
||||
attrib_value = attrib[0].value
|
||||
return attrib_value.value
|
||||
else:
|
||||
if default:
|
||||
return default
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_attribute_obj(self, attrib):
|
||||
"""
|
||||
Returns the attribute object matching the specified name.
|
||||
|
||||
attrib: (str) The attribute's name.
|
||||
"""
|
||||
if self.has_attribute(attrib):
|
||||
attrib_obj = Attribute.objects.filter(object=self).filter(name=attrib)
|
||||
return attrib_obj
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_contents(self, filter_type=None):
|
||||
"""
|
||||
Returns the contents of an object.
|
||||
|
||||
filter_type: (int) An object type number to filter by.
|
||||
"""
|
||||
if filter_type:
|
||||
return list(Object.objects.filter(location__id=self.id).filter(type=filter_type))
|
||||
else:
|
||||
return list(Object.objects.filter(location__id=self.id).exclude(type__gt=4))
|
||||
|
||||
def get_zone(self):
|
||||
"""
|
||||
Returns the object that is marked as this object's zone.
|
||||
"""
|
||||
try:
|
||||
return self.zone
|
||||
except:
|
||||
return None
|
||||
|
||||
def move_to(self, target, quiet=False):
|
||||
"""
|
||||
Moves the object to a new location.
|
||||
|
||||
target: (Object) Reference to the object to move to.
|
||||
quiet: (bool) If true, don't emit left/arrived messages.
|
||||
"""
|
||||
if not quiet:
|
||||
if self.get_location():
|
||||
self.get_location().emit_to_contents("%s has left." % (self.get_ansiname(),), exclude=self)
|
||||
|
||||
self.location = target
|
||||
self.save()
|
||||
|
||||
if not quiet:
|
||||
self.get_location().emit_to_contents("%s has arrived." % (self.get_ansiname(),), exclude=self)
|
||||
|
||||
def dbref_match(self, oname):
|
||||
"""
|
||||
Check if the input (oname) can be used to identify this particular object
|
||||
by means of a dbref match.
|
||||
|
||||
oname: (str) Name to match against.
|
||||
"""
|
||||
if not functions_db.is_dbref(oname):
|
||||
return False
|
||||
|
||||
try:
|
||||
is_match = int(oname[1:]) == self.id
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
return is_match
|
||||
|
||||
def name_match(self, oname):
|
||||
"""
|
||||
See if the input (oname) can be used to identify this particular object.
|
||||
Check the # sign for dbref (exact) reference, and anything else is a
|
||||
name comparison.
|
||||
|
||||
NOTE: A 'name' can be a dbref or the actual name of the object. See
|
||||
dbref_match for an exclusively name-based match.
|
||||
"""
|
||||
if oname[0] == '#':
|
||||
return self.dbref_match(oname)
|
||||
else:
|
||||
return oname.lower() in self.name.lower()
|
||||
|
||||
def filter_contents_from_str(self, oname):
|
||||
"""
|
||||
Search an object's contents for name and dbref matches. Don't put any
|
||||
logic in here, we'll do that from the end of the command or function.
|
||||
|
||||
oname: (str) The string to filter from.
|
||||
"""
|
||||
contents = self.get_contents()
|
||||
return [prospect for prospect in contents if prospect.name_match(oname)]
|
||||
|
||||
# Type comparison methods.
|
||||
def is_player(self):
|
||||
return self.type == 1
|
||||
def is_room(self):
|
||||
return self.type == 2
|
||||
def is_thing(self):
|
||||
return self.type == 3
|
||||
def is_exit(self):
|
||||
return self.type == 4
|
||||
def is_going(self):
|
||||
return self.type == 5
|
||||
def is_garbage(self):
|
||||
return self.type == 6
|
||||
|
||||
def get_type(self, return_number=False):
|
||||
"""
|
||||
Returns the numerical or string representation of an object's type.
|
||||
|
||||
return_number: (bool) True returns numeric type, False returns string.
|
||||
"""
|
||||
if return_number:
|
||||
return self.type
|
||||
else:
|
||||
return global_defines.OBJECT_TYPES[self.type][1]
|
||||
|
||||
def is_type(self, otype):
|
||||
"""
|
||||
See if an object is a certain type.
|
||||
|
||||
otype: (str) A string representation of the object's type (ROOM, THING)
|
||||
"""
|
||||
otype = otype[0]
|
||||
|
||||
if otype == 'p':
|
||||
return self.is_player()
|
||||
elif otype == 'r':
|
||||
return self.is_room()
|
||||
elif otype == 't':
|
||||
return self.is_thing()
|
||||
elif otype == 'e':
|
||||
return self.is_exit()
|
||||
elif otype == 'g':
|
||||
return self.is_garbage()
|
||||
|
||||
def flag_string(self):
|
||||
"""
|
||||
Returns the flag string for an object. This abbreviates all of the flags
|
||||
set on the object into a list of single-character flag characters.
|
||||
"""
|
||||
# We have to cast this because the admin interface is really picky
|
||||
# about tuple index types. Bleh.
|
||||
otype = int(self.type)
|
||||
return global_defines.OBJECT_TYPES[otype][1][0]
|
||||
|
||||
import functions_db
|
||||
import functions_general
|
||||
import session_mgr
|
||||
|
|
@ -1 +0,0 @@
|
|||
# Create your views here.
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
import commands_privileged
|
||||
import commands_general
|
||||
import commands_unloggedin
|
||||
import functions_db
|
||||
|
||||
"""
|
||||
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 UnknownCommand(Exception):
|
||||
"""
|
||||
Throw this when a user enters an an invalid command.
|
||||
"""
|
||||
|
||||
def match_exits(pobject, searchstr):
|
||||
"""
|
||||
See if we can find an input match to exits.
|
||||
"""
|
||||
exits = pobject.get_location().get_contents(filter_type=4)
|
||||
return functions_db.list_search_object_namestr(exits, searchstr)
|
||||
|
||||
def handle(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 'cmd_' and looking it up in the GenCommands
|
||||
class.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = cdat['server']
|
||||
|
||||
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] != '@':
|
||||
cmd = getattr(commands_general, 'cmd_%s' % (parsed_input['root_cmd'],), None )
|
||||
else:
|
||||
parsed_input['root_cmd'] = parsed_input['root_cmd'][1:]
|
||||
cmd = getattr(commands_privileged, 'cmd_%s' % (parsed_input['root_cmd'],), None )
|
||||
else:
|
||||
cmd = getattr(commands_unloggedin, 'cmd_%s' % (parsed_input['root_cmd'],), None )
|
||||
|
||||
if callable(cmd):
|
||||
cdat['uinput'] = parsed_input
|
||||
cmd(cdat)
|
||||
return
|
||||
|
||||
if session.logged_in:
|
||||
# If we're not logged in, don't check exits.
|
||||
pobject = session.get_pobject()
|
||||
exit_matches = match_exits(pobject, ' '.join(parsed_input['splitted']))
|
||||
if exit_matches:
|
||||
exit = exit_matches[0]
|
||||
if exit.get_home():
|
||||
cdat['uinput'] = parsed_input
|
||||
pobject.move_to(exit.get_home())
|
||||
commands_general.cmd_look(cdat)
|
||||
else:
|
||||
session.msg("That exit leads to nowhere.")
|
||||
return
|
||||
|
||||
# If we reach this point, we haven't matched anything.
|
||||
raise UnknownCommand
|
||||
|
||||
except UnknownCommand:
|
||||
session.msg("Unknown command.")
|
||||
|
||||
|
|
@ -1,361 +0,0 @@
|
|||
import settings
|
||||
import time
|
||||
import functions_general
|
||||
import functions_db
|
||||
import functions_help
|
||||
import defines_global as global_defines
|
||||
import session_mgr
|
||||
import ansi
|
||||
import os
|
||||
"""
|
||||
Generic command module. Pretty much every command should go here for
|
||||
now.
|
||||
"""
|
||||
def cmd_idle(cdat):
|
||||
"""
|
||||
Returns nothing, this lets the player set an idle timer without spamming
|
||||
his screen.
|
||||
"""
|
||||
pass
|
||||
|
||||
def cmd_inventory(cdat):
|
||||
"""
|
||||
Shows a player's inventory.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
session.msg("You are carrying:")
|
||||
|
||||
for item in pobject.get_contents():
|
||||
session.msg(" %s" % (item.get_ansiname(),))
|
||||
|
||||
money = pobject.get_attribute_value("MONEY", default=0)
|
||||
if money > 0:
|
||||
money_name = functions_db.get_server_config("MONEY_NAME_PLURAL")
|
||||
else:
|
||||
money_name = functions_db.get_server_config("MONEY_NAME_SINGULAR")
|
||||
|
||||
session.msg("You have %d %s." % (money,money_name))
|
||||
|
||||
def cmd_look(cdat):
|
||||
"""
|
||||
Handle looking at objects.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
|
||||
if len(args) == 0:
|
||||
target_obj = pobject.get_location()
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, ' '.join(args))
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
else:
|
||||
target_obj = results[0]
|
||||
|
||||
retval = "%s\r\n%s" % (
|
||||
target_obj.get_ansiname(),
|
||||
target_obj.get_description(),
|
||||
)
|
||||
session.msg(retval)
|
||||
|
||||
con_players = []
|
||||
con_things = []
|
||||
con_exits = []
|
||||
|
||||
for obj in target_obj.get_contents():
|
||||
if obj.is_player():
|
||||
if obj != pobject and obj.is_connected_plr():
|
||||
con_players.append(obj)
|
||||
elif obj.is_exit():
|
||||
con_exits.append(obj)
|
||||
else:
|
||||
con_things.append(obj)
|
||||
|
||||
if con_players:
|
||||
session.msg("%sPlayers:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],))
|
||||
for player in con_players:
|
||||
session.msg('%s' %(player.get_ansiname(),))
|
||||
if con_things:
|
||||
session.msg("%sContents:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],))
|
||||
for thing in con_things:
|
||||
session.msg('%s' %(thing.get_ansiname(),))
|
||||
if con_exits:
|
||||
session.msg("%sExits:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],))
|
||||
for exit in con_exits:
|
||||
session.msg('%s' %(exit.get_ansiname(),))
|
||||
|
||||
def cmd_get(cdat):
|
||||
"""
|
||||
Get an object and put it in a player's inventory.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
plr_is_staff = pobject.is_staff()
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Get what?")
|
||||
return
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, ' '.join(args), search_contents=False)
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
else:
|
||||
# We've got a victim to get now.
|
||||
target_obj = results[0]
|
||||
|
||||
if pobject == target_obj:
|
||||
session.msg("You can't get yourself.")
|
||||
return
|
||||
|
||||
if not plr_is_staff and (target_obj.is_player() or target_obj.is_exit()):
|
||||
session.msg("You can't get that.")
|
||||
return
|
||||
|
||||
if target_obj.is_room() or target_obj.is_garbage() or target_obj.is_going():
|
||||
session.msg("You can't get that.")
|
||||
return
|
||||
|
||||
target_obj.move_to(pobject, quiet=True)
|
||||
session.msg("You pick up %s." % (target_obj.get_ansiname(),))
|
||||
pobject.get_location().emit_to_contents("%s picks up %s." % (pobject.get_ansiname(), target_obj.get_ansiname()), exclude=pobject)
|
||||
|
||||
def cmd_drop(cdat):
|
||||
"""
|
||||
Drop an object from a player's inventory into their current location.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
plr_is_staff = pobject.is_staff()
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Drop what?")
|
||||
return
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, ' '.join(args), search_location=False)
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("You don't appear to be carrying that.")
|
||||
return
|
||||
else:
|
||||
# We've got a victim to get now.
|
||||
target_obj = results[0]
|
||||
|
||||
if not pobject == target_obj.get_location():
|
||||
session.msg("You don't appear to be carrying that.")
|
||||
return
|
||||
|
||||
target_obj.move_to(pobject.get_location(), quiet=True)
|
||||
session.msg("You drop %s." % (target_obj.get_ansiname(),))
|
||||
pobject.get_location().emit_to_contents("%s drops %s." % (pobject.get_ansiname(), target_obj.get_ansiname()), exclude=pobject)
|
||||
|
||||
def cmd_examine(cdat):
|
||||
"""
|
||||
Detailed object examine command
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
|
||||
if len(args) == 0:
|
||||
target_obj = pobject.get_location()
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, ' '.join(args))
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
else:
|
||||
target_obj = results[0]
|
||||
session.msg("%s\r\n%s" % (
|
||||
target_obj.get_ansiname(fullname=True),
|
||||
target_obj.get_description(no_parsing=True),
|
||||
))
|
||||
session.msg("Type: %s Flags: %s" % (target_obj.get_type(), target_obj.get_flags()))
|
||||
session.msg("Owner: %s " % (target_obj.get_owner(),))
|
||||
session.msg("Zone: %s" % (target_obj.get_zone(),))
|
||||
|
||||
for attribute in target_obj.get_all_attributes():
|
||||
session.msg("%s%s%s: %s" % (ansi.ansi["hilite"], attribute.get_name(), ansi.ansi["normal"], attribute.value))
|
||||
|
||||
con_players = []
|
||||
con_things = []
|
||||
con_exits = []
|
||||
|
||||
for obj in target_obj.get_contents():
|
||||
if obj.is_player():
|
||||
con_players.append(obj)
|
||||
elif obj.is_exit():
|
||||
con_exits.append(obj)
|
||||
elif obj.is_thing():
|
||||
con_things.append(obj)
|
||||
|
||||
if con_players or con_things:
|
||||
session.msg("%sContents:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],))
|
||||
for player in con_players:
|
||||
session.msg('%s' % (player.get_ansiname(fullname=True),))
|
||||
for thing in con_things:
|
||||
session.msg('%s' % (thing.get_ansiname(fullname=True),))
|
||||
|
||||
if con_exits:
|
||||
session.msg("%sExits:%s" % (ansi.ansi["hilite"], ansi.ansi["normal"],))
|
||||
for exit in con_exits:
|
||||
session.msg('%s' %(exit.get_ansiname(fullname=True),))
|
||||
|
||||
if not target_obj.is_room():
|
||||
if target_obj.is_exit():
|
||||
session.msg("Destination: %s" % (target_obj.get_home(),))
|
||||
else:
|
||||
session.msg("Home: %s" % (target_obj.get_home(),))
|
||||
|
||||
session.msg("Location: %s" % (target_obj.get_location(),))
|
||||
|
||||
def cmd_quit(cdat):
|
||||
"""
|
||||
Gracefully disconnect the user as per his own request.
|
||||
"""
|
||||
session = cdat['session']
|
||||
session.msg("Quitting!")
|
||||
session.handle_close()
|
||||
|
||||
def cmd_who(cdat):
|
||||
"""
|
||||
Generic WHO command.
|
||||
"""
|
||||
session_list = session_mgr.get_session_list()
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
|
||||
retval = "Player Name On For Idle Room Cmds Host\n\r"
|
||||
for player in session_list:
|
||||
if not player.logged_in:
|
||||
continue
|
||||
delta_cmd = time.time() - player.cmd_last
|
||||
delta_conn = time.time() - player.conn_time
|
||||
plr_pobject = player.get_pobject()
|
||||
|
||||
retval += '%-16s%9s %4s%-3s#%-6d%5d%3s%-25s\r\n' % \
|
||||
(plr_pobject.get_name(), \
|
||||
# On-time
|
||||
functions_general.time_format(delta_conn,0), \
|
||||
# Idle time
|
||||
functions_general.time_format(delta_cmd,1), \
|
||||
# Flags
|
||||
'', \
|
||||
# Location
|
||||
plr_pobject.get_location().id, \
|
||||
player.cmd_total, \
|
||||
# More flags?
|
||||
'', \
|
||||
player.address[0])
|
||||
retval += '%d Players logged in.' % (len(session_list),)
|
||||
|
||||
session.msg(retval)
|
||||
|
||||
def cmd_say(cdat):
|
||||
"""
|
||||
Room-based speech command.
|
||||
"""
|
||||
session_list = session_mgr.get_session_list()
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
speech = ' '.join(cdat['uinput']['splitted'][1:])
|
||||
|
||||
players_present = [player for player in session_list if player.get_pobject().get_location() == session.get_pobject().get_location() and player != session]
|
||||
|
||||
retval = "You say, '%s'" % (speech,)
|
||||
for player in players_present:
|
||||
player.msg("%s says, '%s'" % (pobject.get_name(), speech,))
|
||||
|
||||
session.msg(retval)
|
||||
|
||||
def cmd_help(cdat):
|
||||
"""
|
||||
Help system commands.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
topicstr = ' '.join(cdat['uinput']['splitted'][1:])
|
||||
|
||||
if len(topicstr) == 0:
|
||||
topicstr = "Help Index"
|
||||
elif len(topicstr) < 2 and not topicstr.isdigit():
|
||||
session.msg("Your search query is too short. It must be at least three letters long.")
|
||||
return
|
||||
|
||||
topics = functions_help.find_topicmatch(pobject, topicstr)
|
||||
|
||||
if len(topics) == 0:
|
||||
session.msg("No matching topics found, please refine your search.")
|
||||
suggestions = functions_help.find_topicsuggestions(pobject, topicstr)
|
||||
if len(suggestions) > 0:
|
||||
session.msg("Matching similarly named topics:")
|
||||
for result in suggestions:
|
||||
session.msg(" %s" % (result,))
|
||||
session.msg("You may type 'help <#>' to see any of these topics.")
|
||||
elif len(topics) > 1:
|
||||
session.msg("More than one match found:")
|
||||
for result in topics:
|
||||
session.msg("%3d. %s" % (result.id, result.get_topicname()))
|
||||
session.msg("You may type 'help <#>' to see any of these topics.")
|
||||
else:
|
||||
topic = topics[0]
|
||||
session.msg("\r\n%s%s%s" % (ansi.ansi["hilite"], topic.get_topicname(), ansi.ansi["normal"]))
|
||||
session.msg(topic.get_entrytext_ingame())
|
||||
|
||||
def cmd_version(cdat):
|
||||
"""
|
||||
Version info command.
|
||||
"""
|
||||
session = cdat['session']
|
||||
retval = "-"*50 +"\n\r"
|
||||
retval += "Evennia %s\n\r" % (global_defines.EVENNIA_VERSION,)
|
||||
retval += "-"*50
|
||||
session.msg(retval)
|
||||
|
||||
def cmd_time(cdat):
|
||||
"""
|
||||
Server local time.
|
||||
"""
|
||||
session = cdat['session']
|
||||
session.msg('Current server time : %s' % (time.strftime('%a %b %d %H:%M %Y (%Z)', time.localtime(),)))
|
||||
|
||||
def cmd_uptime(cdat):
|
||||
"""
|
||||
Server uptime and stats.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = cdat['server']
|
||||
start_delta = time.time() - server.start_time
|
||||
loadavg = os.getloadavg()
|
||||
session.msg('Current server time : %s' % (time.strftime('%a %b %d %H:%M %Y (%Z)', time.localtime(),)))
|
||||
session.msg('Server start time : %s' % (time.strftime('%a %b %d %H:%M %Y', time.localtime(server.start_time),)))
|
||||
session.msg('Server uptime : %s' % functions_general.time_format(start_delta, style=2))
|
||||
session.msg('Server load (1 min) : %.2f' % loadavg[0])
|
||||
|
|
@ -1,640 +0,0 @@
|
|||
import os
|
||||
import resource
|
||||
|
||||
import functions_db
|
||||
import functions_general
|
||||
import commands_general
|
||||
import commands_unloggedin
|
||||
import cmdhandler
|
||||
import session_mgr
|
||||
import ansi
|
||||
import defines_global
|
||||
from django.contrib.auth.models import User
|
||||
from apps.objects.models import Object
|
||||
"""
|
||||
Any command here is prefixed by an '@' sign, usually denoting a
|
||||
builder, staff or otherwise manipulative command that doesn't fall within
|
||||
the scope of normal gameplay.
|
||||
"""
|
||||
|
||||
def cmd_destroy(cdat):
|
||||
"""
|
||||
Destroy an object.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
switches = cdat['uinput']['root_chunk'][1:]
|
||||
switch_override = False
|
||||
|
||||
if "override" in switches:
|
||||
switch_override = True
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Destroy what?")
|
||||
return
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, ' '.join(args))
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
elif results[0].is_player():
|
||||
if pobject.id == results[0].id:
|
||||
session.msg("You can't destroy yourself.")
|
||||
return
|
||||
if not switch_override:
|
||||
session.msg("You must use @destroy/override on players.")
|
||||
return
|
||||
if results[0].is_superuser():
|
||||
session.msg("You can't destroy a superuser.")
|
||||
return
|
||||
target_obj = results[0]
|
||||
elif results[0].is_going():
|
||||
session.msg("That object is already destroyed.")
|
||||
return
|
||||
elif results[0].is_garbage():
|
||||
session.msg("That object is already destroyed.")
|
||||
return
|
||||
else:
|
||||
target_obj = results[0]
|
||||
|
||||
session.msg("You destroy %s." % (target_obj,))
|
||||
target_obj.destroy()
|
||||
|
||||
def cmd_list(cdat):
|
||||
"""
|
||||
Shows some game related information.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
argstr = ''.join(args)
|
||||
|
||||
msg_invalid = "Unknown option. Use one of: commands, flags, process"
|
||||
|
||||
if len(argstr) == 0:
|
||||
session.msg(msg_invalid)
|
||||
elif argstr == "commands":
|
||||
session.msg('Commands: '+' '.join(functions_general.command_list()))
|
||||
elif argstr == "process":
|
||||
loadvg = os.getloadavg()
|
||||
psize = resource.getpagesize()
|
||||
rusage = resource.getrusage(resource.RUSAGE_SELF)
|
||||
session.msg("Process ID: %10d %10d bytes per page" % (os.getpid(), psize))
|
||||
session.msg("Time used: %10d user %10d sys" % (rusage[0],rusage[1]))
|
||||
session.msg("Integral mem:%10d shared %10d private%10d stack" % (rusage[3], rusage[4], rusage[5]))
|
||||
session.msg("Max res mem: %10d pages %10d bytes" % (rusage[2],rusage[2] * psize))
|
||||
session.msg("Page faults: %10d hard %10d soft %10d swapouts" % (rusage[7], rusage[6], rusage[8]))
|
||||
session.msg("Disk I/O: %10d reads %10d writes" % (rusage[9], rusage[10]))
|
||||
session.msg("Network I/O: %10d in %10d out" % (rusage[12], rusage[11]))
|
||||
session.msg("Context swi: %10d vol %10d forced %10d sigs" % (rusage[14], rusage[15], rusage[13]))
|
||||
elif argstr == "flags":
|
||||
session.msg("Flags: "+" ".join(defines_global.SERVER_FLAGS))
|
||||
else:
|
||||
session.msg(msg_invalid)
|
||||
|
||||
def cmd_description(cdat):
|
||||
"""
|
||||
Set an object's description.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
eq_args = ' '.join(args).split('=')
|
||||
searchstring = ''.join(eq_args[0])
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("What do you want to describe?")
|
||||
elif len(eq_args) < 2:
|
||||
session.msg("How would you like to describe that object?")
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, searchstring)
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
else:
|
||||
new_desc = '='.join(eq_args[1:])
|
||||
target_obj = results[0]
|
||||
session.msg("%s - DESCRIPTION set." % (target_obj,))
|
||||
target_obj.set_description(new_desc)
|
||||
|
||||
def cmd_newpassword(cdat):
|
||||
"""
|
||||
Set a player's password.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
eq_args = ' '.join(args).split('=')
|
||||
searchstring = ''.join(eq_args[0])
|
||||
newpass = ''.join(eq_args[1:])
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("What player's password do you want to change")
|
||||
return
|
||||
if len(newpass) == 0:
|
||||
session.msg("You must supply a new password.")
|
||||
return
|
||||
|
||||
results = functions_db.local_and_global_search(pobject, searchstring)
|
||||
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
elif not pobject.controls_other(results[0]):
|
||||
session.msg("You do not control %s." % (results[0],))
|
||||
else:
|
||||
uaccount = results[0].get_user_account()
|
||||
if len(newpass) == 0:
|
||||
uaccount.set_password()
|
||||
else:
|
||||
uaccount.set_password(newpass)
|
||||
uaccount.save()
|
||||
session.msg("%s - PASSWORD set." % (results[0],))
|
||||
results[0].emit_to("%s has changed your password." % (pobject,))
|
||||
|
||||
def cmd_password(cdat):
|
||||
"""
|
||||
Changes your own password.
|
||||
|
||||
@newpass <Oldpass>=<Newpass>
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
eq_args = ' '.join(args).split('=')
|
||||
oldpass = ''.join(eq_args[0])
|
||||
newpass = ''.join(eq_args[1:])
|
||||
|
||||
if len(oldpass) == 0:
|
||||
session.msg("You must provide your old password.")
|
||||
elif len(newpass) == 0:
|
||||
session.msg("You must provide your new password.")
|
||||
else:
|
||||
uaccount = User.objects.get(id=pobject.id)
|
||||
|
||||
if not uaccount.check_password(oldpass):
|
||||
session.msg("The specified old password isn't correct.")
|
||||
elif len(newpass) < 3:
|
||||
session.msg("Passwords must be at least three characters long.")
|
||||
return
|
||||
else:
|
||||
uaccount.set_password(newpass)
|
||||
uaccount.save()
|
||||
session.msg("Password changed.")
|
||||
|
||||
def cmd_name(cdat):
|
||||
"""
|
||||
Handle naming an object.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
eq_args = ' '.join(args).split('=')
|
||||
searchstring = ''.join(eq_args[0])
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("What do you want to name?")
|
||||
elif len(eq_args) < 2:
|
||||
session.msg("What would you like to name that object?")
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, searchstring)
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
elif len(eq_args[1]) == 0:
|
||||
session.msg("What would you like to name that object?")
|
||||
else:
|
||||
newname = '='.join(eq_args[1:])
|
||||
target_obj = results[0]
|
||||
session.msg("You have renamed %s to %s." % (target_obj, ansi.parse_ansi(newname, strip_formatting=True)))
|
||||
target_obj.set_name(newname)
|
||||
|
||||
def cmd_dig(cdat):
|
||||
"""
|
||||
Creates a new object of type 'ROOM'.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
uinput= cdat['uinput']['splitted']
|
||||
roomname = ' '.join(uinput[1:])
|
||||
|
||||
if roomname == '':
|
||||
session.msg("You must supply a name!")
|
||||
else:
|
||||
# Create and set the object up.
|
||||
odat = {"name": roomname, "type": 2, "location": None, "owner": pobject}
|
||||
new_object = functions_db.create_object(odat)
|
||||
|
||||
session.msg("You create a new room: %s" % (new_object,))
|
||||
|
||||
def cmd_emit(cdat):
|
||||
"""
|
||||
Emits something to your location.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
uinput= cdat['uinput']['splitted']
|
||||
message = ' '.join(uinput[1:])
|
||||
|
||||
if message == '':
|
||||
session.msg("Emit what?")
|
||||
else:
|
||||
pobject.get_location().emit_to_contents(message)
|
||||
|
||||
def cmd_create(cdat):
|
||||
"""
|
||||
Creates a new object of type 'THING'.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = session.server
|
||||
pobject = session.get_pobject()
|
||||
uinput= cdat['uinput']['splitted']
|
||||
thingname = ' '.join(uinput[1:])
|
||||
|
||||
if thingname == '':
|
||||
session.msg("You must supply a name!")
|
||||
else:
|
||||
# Create and set the object up.
|
||||
odat = {"name": thingname, "type": 3, "location": pobject, "owner": pobject}
|
||||
new_object = functions_db.create_object(odat)
|
||||
|
||||
session.msg("You create a new thing: %s" % (new_object,))
|
||||
|
||||
def cmd_nextfree(cdat):
|
||||
"""
|
||||
Returns the next free object number.
|
||||
"""
|
||||
session = cdat['session']
|
||||
|
||||
nextfree = functions_db.get_nextfree_dbnum()
|
||||
if str(nextfree).isdigit():
|
||||
retval = "Next free object number: #%s" % (nextfree,)
|
||||
else:
|
||||
retval = "Next free object number: #%s (GARBAGE)" % (nextfree.id,)
|
||||
|
||||
session.msg(retval)
|
||||
|
||||
def cmd_open(cdat):
|
||||
"""
|
||||
Handle the opening of exits.
|
||||
|
||||
Forms:
|
||||
@open <Name>
|
||||
@open <Name>=<Dbref>
|
||||
@open <Name>=<Dbref>,<Name>
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
server = cdat['server']
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Open an exit to where?")
|
||||
return
|
||||
|
||||
eq_args = args[0].split('=')
|
||||
exit_name = eq_args[0]
|
||||
|
||||
if len(exit_name) == 0:
|
||||
session.msg("You must supply an exit name.")
|
||||
return
|
||||
|
||||
# If we have more than one entry in our '=' delimited argument list,
|
||||
# then we're doing a @open <Name>=<Dbref>[,<Name>]. If not, we're doing
|
||||
# an un-linked exit, @open <Name>.
|
||||
if len(eq_args) > 1:
|
||||
# Opening an exit to another location via @open <Name>=<Dbref>[,<Name>].
|
||||
comma_split = eq_args[1].split(',')
|
||||
destination = functions_db.local_and_global_search(pobject, comma_split[0])
|
||||
|
||||
if len(destination) == 0:
|
||||
session.msg("I can't find the location to link to.")
|
||||
return
|
||||
elif len(destination) > 1:
|
||||
session.msg("Multiple results returned for exit destination!")
|
||||
else:
|
||||
destination = destination[0]
|
||||
if destination.is_exit():
|
||||
session.msg("You can't open an exit to an exit!")
|
||||
return
|
||||
|
||||
odat = {"name": exit_name, "type": 4, "location": pobject.get_location(), "owner": pobject, "home":destination}
|
||||
new_object = functions_db.create_object(odat)
|
||||
|
||||
session.msg("You open the exit - %s" % (new_object,))
|
||||
|
||||
if len(comma_split) > 1:
|
||||
second_exit_name = ','.join(comma_split[1:])
|
||||
odat = {"name": second_exit_name, "type": 4, "location": destination, "owner": pobject, "home": pobject.get_location()}
|
||||
new_object = functions_db.create_object(odat)
|
||||
else:
|
||||
# Create an un-linked exit.
|
||||
odat = {"name": exit_name, "type": 4, "location": pobject.get_location(), "owner": pobject, "home":None}
|
||||
new_object = functions_db.create_object(odat)
|
||||
|
||||
session.msg("You open an unlinked exit - %s" % (new_object,))
|
||||
|
||||
def cmd_link(cdat):
|
||||
"""
|
||||
Sets an object's home or an exit's destination.
|
||||
|
||||
Forms:
|
||||
@link <Object>=<Target>
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
server = cdat['server']
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Link what?")
|
||||
return
|
||||
|
||||
eq_args = args[0].split('=')
|
||||
target_name = eq_args[0]
|
||||
dest_name = '='.join(eq_args[1:])
|
||||
|
||||
if len(target_name) == 0:
|
||||
session.msg("What do you want to link?")
|
||||
return
|
||||
|
||||
if len(eq_args) > 1:
|
||||
target = functions_db.local_and_global_search(pobject, target_name)
|
||||
|
||||
if len(target) == 0:
|
||||
session.msg("I can't find the object you want to link.")
|
||||
return
|
||||
elif len(target) > 1:
|
||||
session.msg("Multiple results returned for link target.")
|
||||
return
|
||||
|
||||
# We know we can get the first entry now.
|
||||
target = target[0]
|
||||
|
||||
# If we do something like "@link blah=", we unlink the object.
|
||||
if len(dest_name) == 0:
|
||||
target.set_home(None)
|
||||
session.msg("You have unlinked %s." % (target,))
|
||||
return
|
||||
|
||||
destination = functions_db.local_and_global_search(pobject, dest_name)
|
||||
|
||||
if len(destination) == 0:
|
||||
session.msg("I can't find the location to link to.")
|
||||
return
|
||||
elif len(destination) > 1:
|
||||
session.msg("Multiple results returned for destination.")
|
||||
return
|
||||
|
||||
destination = destination[0]
|
||||
target.set_home(destination)
|
||||
session.msg("You link %s to %s." % (target,destination))
|
||||
|
||||
else:
|
||||
# We haven't provided a target.
|
||||
session.msg("You must provide a destination to link to.")
|
||||
return
|
||||
|
||||
def cmd_unlink(cdat):
|
||||
"""
|
||||
Unlinks an object.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Unlink what?")
|
||||
return
|
||||
else:
|
||||
results = functions_db.local_and_global_search(pobject, ' '.join(args))
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
return
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
else:
|
||||
results[0].set_home(None)
|
||||
session.msg("You have unlinked %s." % (results[0],))
|
||||
|
||||
def cmd_teleport(cdat):
|
||||
"""
|
||||
Teleports an object somewhere.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
server = cdat['server']
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Teleport where/what?")
|
||||
return
|
||||
|
||||
eq_args = args[0].split('=')
|
||||
search_str = ''.join(args)
|
||||
|
||||
# If we have more than one entry in our '=' delimited argument list,
|
||||
# then we're doing a @tel <victim>=<location>. If not, we're doing
|
||||
# a direct teleport, @tel <destination>.
|
||||
if len(eq_args) > 1:
|
||||
# Equal sign teleport.
|
||||
victim = functions_db.local_and_global_search(pobject, eq_args[0])
|
||||
destination = functions_db.local_and_global_search(pobject, eq_args[1])
|
||||
|
||||
if len(victim) == 0:
|
||||
session.msg("I can't find the victim to teleport.")
|
||||
return
|
||||
elif len(destination) == 0:
|
||||
session.msg("I can't find the destination for the victim.")
|
||||
return
|
||||
elif len(victim) > 1:
|
||||
session.msg("Multiple results returned for victim!")
|
||||
return
|
||||
elif len(destination) > 1:
|
||||
session.msg("Multiple results returned for destination!")
|
||||
else:
|
||||
if victim == destination:
|
||||
session.msg("You can't teleport an object inside of itself!")
|
||||
return
|
||||
session.msg("Teleported.")
|
||||
victim[0].move_to(destination[0])
|
||||
|
||||
# This is somewhat kludgy right now, we'll have to find a better way
|
||||
# to do it sometime else. If we can find a session in the server's
|
||||
# session list matching the object we're teleporting, force it to
|
||||
# look. This is going to typically be a player.
|
||||
victim_session = session_mgr.session_from_object(victim[0])
|
||||
if victim_session:
|
||||
# We need to form up a new cdat dictionary to pass with the command.
|
||||
# Kinda yucky I guess.
|
||||
cdat2 = {"server": server, "uinput": 'look', "session": victim_session}
|
||||
cmdhandler.handle(cdat2)
|
||||
|
||||
else:
|
||||
# Direct teleport (no equal sign)
|
||||
results = functions_db.local_and_global_search(pobject, search_str)
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(),))
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
else:
|
||||
if results[0] == pobject:
|
||||
session.msg("You can't teleport inside yourself!")
|
||||
return
|
||||
session.msg("Teleported.")
|
||||
pobject.move_to(results[0])
|
||||
commands_general.cmd_look(cdat)
|
||||
|
||||
def cmd_set(cdat):
|
||||
"""
|
||||
Sets flags or attributes on objects.
|
||||
"""
|
||||
session = cdat['session']
|
||||
pobject = session.get_pobject()
|
||||
server = cdat['server']
|
||||
args = cdat['uinput']['splitted'][1:]
|
||||
|
||||
if len(args) == 0:
|
||||
session.msg("Set what?")
|
||||
return
|
||||
|
||||
# There's probably a better way to do this. Break the arguments (minus
|
||||
# the root command) up so we have two items in the list, 0 being the victim,
|
||||
# 1 being the list of flags or the attribute/value pair.
|
||||
eq_args = ' '.join(args).split('=')
|
||||
|
||||
if len(eq_args) < 2:
|
||||
session.msg("Set what?")
|
||||
return
|
||||
|
||||
victim = functions_db.local_and_global_search(pobject, eq_args[0])
|
||||
|
||||
if len(victim) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return
|
||||
elif len(victim) > 1:
|
||||
session.msg("I don't know which one you mean!")
|
||||
return
|
||||
|
||||
victim = victim[0]
|
||||
attrib_args = eq_args[1].split(':')
|
||||
|
||||
if len(attrib_args) > 1:
|
||||
# We're dealing with an attribute/value pair.
|
||||
attrib_name = attrib_args[0].upper()
|
||||
splicenum = eq_args[1].find(':') + 1
|
||||
attrib_value = eq_args[1][splicenum:]
|
||||
|
||||
# In global_defines.py, see NOSET_ATTRIBS for protected attribute names.
|
||||
if not functions_db.is_modifiable_attrib(attrib_name):
|
||||
session.msg("You can't modify that attribute.")
|
||||
return
|
||||
|
||||
if attrib_value:
|
||||
# An attribute value was specified, create or set the attribute.
|
||||
verb = 'set'
|
||||
victim.set_attribute(attrib_name, attrib_value)
|
||||
else:
|
||||
# No value was given, this means we delete the attribute.
|
||||
verb = 'cleared'
|
||||
victim.clear_attribute(attrib_name)
|
||||
session.msg("%s - %s %s." % (victim.get_name(), attrib_name, verb))
|
||||
else:
|
||||
# Flag manipulation form.
|
||||
flag_list = eq_args[1].split()
|
||||
|
||||
for flag in flag_list:
|
||||
flag = flag.upper()
|
||||
if flag[0] == '!':
|
||||
# We're un-setting the flag.
|
||||
flag = flag[1:]
|
||||
if not functions_db.is_modifiable_flag(flag):
|
||||
session.msg("You can't set/unset the flag - %s." % (flag,))
|
||||
else:
|
||||
session.msg('%s - %s cleared.' % (victim.get_name(), flag.upper(),))
|
||||
victim.set_flag(flag, False)
|
||||
else:
|
||||
# We're setting the flag.
|
||||
if not functions_db.is_modifiable_flag(flag):
|
||||
session.msg("You can't set/unset the flag - %s." % (flag,))
|
||||
else:
|
||||
session.msg('%s - %s set.' % (victim.get_name(), flag.upper(),))
|
||||
victim.set_flag(flag, True)
|
||||
|
||||
def cmd_find(cdat):
|
||||
"""
|
||||
Searches for an object of a particular name.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = cdat['server']
|
||||
searchstring = ' '.join(cdat['uinput']['splitted'][1:])
|
||||
|
||||
if searchstring == '':
|
||||
session.msg("No search pattern given.")
|
||||
return
|
||||
|
||||
results = functions_db.global_object_name_search(searchstring)
|
||||
|
||||
if len(results) > 0:
|
||||
session.msg("Name matches for: %s" % (searchstring,))
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_ansiname(fullname=True),))
|
||||
session.msg("%d matches returned." % (len(results),))
|
||||
else:
|
||||
session.msg("No name matches found for: %s" % (searchstring,))
|
||||
|
||||
def cmd_wall(cdat):
|
||||
"""
|
||||
Announces a message to all connected players.
|
||||
"""
|
||||
session = cdat['session']
|
||||
wallstring = ' '.join(cdat['uinput']['splitted'][1:])
|
||||
|
||||
if wallstring == '':
|
||||
session.msg("Announce what?")
|
||||
return
|
||||
|
||||
message = "%s shouts \"%s\"" % (session.get_pobject().get_name(), wallstring)
|
||||
functions_general.announce_all(message)
|
||||
|
||||
def cmd_shutdown(cdat):
|
||||
"""
|
||||
Shut the server down gracefully.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = cdat['server']
|
||||
|
||||
session.msg('Shutting down...')
|
||||
print 'Server shutdown by %s(#%d)' % (session.get_pobject().get_name(), session.get_pobject().id,)
|
||||
server.shutdown()
|
||||
|
|
@ -1,63 +0,0 @@
|
|||
from django.contrib.auth.models import User
|
||||
import functions_db
|
||||
|
||||
"""
|
||||
Commands that are available from the connect screen.
|
||||
"""
|
||||
|
||||
def cmd_connect(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']['splitted'][1]
|
||||
password = cdat['uinput']['splitted'][2]
|
||||
|
||||
account = User.objects.filter(username__iexact=uname)
|
||||
|
||||
autherror = "Invalid username or password!"
|
||||
# No username match
|
||||
if account.count() == 0:
|
||||
session.msg(autherror)
|
||||
return
|
||||
|
||||
# We have at least one result, so we can check hte password.
|
||||
user = account[0]
|
||||
|
||||
if not user.check_password(password):
|
||||
session.msg(autherror)
|
||||
else:
|
||||
user = account[0]
|
||||
uname = user.username
|
||||
session.login(user)
|
||||
|
||||
def cmd_create(cdat):
|
||||
"""
|
||||
Handle the creation of new accounts.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = session.server
|
||||
uname = cdat['uinput']['splitted'][1]
|
||||
email = cdat['uinput']['splitted'][2]
|
||||
password = cdat['uinput']['splitted'][3]
|
||||
|
||||
# Search for a user object with the specified username.
|
||||
account = User.objects.filter(username=uname)
|
||||
|
||||
if not account.count() == 0:
|
||||
session.msg("There is already a player with that name!")
|
||||
elif len(password) < 3:
|
||||
session.msg("Your password must be 3 characters or longer.")
|
||||
else:
|
||||
functions_db.create_user(cdat, uname, email, password)
|
||||
|
||||
def cmd_quit(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.msg("Disconnecting...")
|
||||
session.handle_close()
|
||||
|
|
@ -1,26 +0,0 @@
|
|||
# Do not mess with the default types (0-5).
|
||||
OBJECT_TYPES = (
|
||||
(0, 'NOTHING'),
|
||||
(1, 'PLAYER'),
|
||||
(2, 'ROOM'),
|
||||
(3, 'THING'),
|
||||
(4, 'EXIT'),
|
||||
(5, 'GOING'),
|
||||
(6, 'GARBAGE'),
|
||||
)
|
||||
|
||||
# This is a list of flags that the server actually uses. Anything not in this
|
||||
# list is a custom flag.
|
||||
SERVER_FLAGS = ["CONNECTED", "DARK", "FLOATING", "GAGGED", "HAVEN", "OPAQUE", "SAFE", "SLAVE", "SUSPECT", "TRANSPARENT"]
|
||||
|
||||
# These flags are not saved.
|
||||
NOSAVE_FLAGS = ["CONNECTED"]
|
||||
|
||||
# These flags can't be modified by players.
|
||||
NOSET_FLAGS = ["CONNECTED"]
|
||||
|
||||
# These attribute names can't be modified by players.
|
||||
NOSET_ATTRIBS = ["MONEY", "ALIAS"]
|
||||
|
||||
# Server version number.
|
||||
EVENNIA_VERSION = 'Pre-Alpha'
|
||||
Binary file not shown.
|
|
@ -1,40 +0,0 @@
|
|||
# Add this vhost file to your Apache sites-enabled directory, typically found
|
||||
# at /etc/apache2/sites-enabled. You'll need to go through and change the
|
||||
# /home/evennia to point to the correct home directory, and the evennia
|
||||
# subdir must be under that home directory.
|
||||
|
||||
# A NOTE ON IMAGES/CSS: These files must be handled separately from the actual
|
||||
# dynamic content which is interpreted by Python. You may host these static
|
||||
# images/css from the same server or a different one. The static media
|
||||
# is served from /home/evennia/evennia/media. The easiest way to serve
|
||||
# this stuff is by either hosting them remotely or symlinking
|
||||
# the media directory into an existing Apache site's directory.
|
||||
|
||||
# A NOTE ON ADMIN IMAGES/CSS: You'll need to create a symlink called
|
||||
# 'amedia' under the media directory if you want to use the admin interface.
|
||||
# This can be found in the Django package in your Python path. For example,
|
||||
# the default is: /usr/lib/python2.4/site-packages/django/contrib/admin/media/
|
||||
# although your location may vary.
|
||||
<VirtualHost *>
|
||||
# Set ServerName and ServerAdmin appropriately
|
||||
ServerName evennia.somewhere.com
|
||||
ServerAdmin someone@somewhere.com
|
||||
DocumentRoot /home/evennia/evennia
|
||||
|
||||
<Directory "/home/evennia/evennia">
|
||||
SetHandler python-program
|
||||
PythonHandler django.core.handlers.modpython
|
||||
PythonPath "['/home/evennia/evennia'] + sys.path"
|
||||
SetEnv DJANGO_SETTINGS_MODULE settings
|
||||
PythonDebug On
|
||||
</Directory>
|
||||
|
||||
Alias /media/ "/home/evennia/evennia/media/"
|
||||
<Directory "/home/evennia/evennia/media/">
|
||||
SetHandler None
|
||||
</Directory>
|
||||
|
||||
<LocationMatch "\.(jpg|gif|png|css)$">
|
||||
SetHandler None
|
||||
</LocationMatch>
|
||||
</VirtualHost>
|
||||
|
|
@ -1,15 +0,0 @@
|
|||
"""
|
||||
Holds the events scheduled in scheduler.py.
|
||||
"""
|
||||
|
||||
schedule = {
|
||||
'event_example': 60,
|
||||
}
|
||||
|
||||
lastrun = {}
|
||||
|
||||
def event_example():
|
||||
"""
|
||||
This is where the example event would be placed.
|
||||
"""
|
||||
pass
|
||||
|
|
@ -1,222 +0,0 @@
|
|||
import sets
|
||||
from django.db import connection
|
||||
from django.contrib.auth.models import User
|
||||
from apps.objects.models import Object
|
||||
from apps.config.models import ConfigValue
|
||||
import defines_global as global_defines
|
||||
import gameconf
|
||||
|
||||
"""
|
||||
Common database functions.
|
||||
"""
|
||||
def get_server_config(configname):
|
||||
"""
|
||||
Returns a server config value.
|
||||
"""
|
||||
return ConfigValue.objects.get(conf_key__iexact=configname).conf_value
|
||||
|
||||
def is_unsavable_flag(flagname):
|
||||
"""
|
||||
Returns TRUE if the flag is an unsavable flag.
|
||||
"""
|
||||
return flagname in global_defines.NOSAVE_FLAGS
|
||||
|
||||
def is_modifiable_flag(flagname):
|
||||
"""
|
||||
Check to see if a particular flag is modifiable.
|
||||
"""
|
||||
if flagname not in global_defines.NOSET_FLAGS:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_modifiable_attrib(attribname):
|
||||
"""
|
||||
Check to see if a particular attribute is modifiable.
|
||||
"""
|
||||
if attribname not in global_defines.NOSET_ATTRIBS:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_nextfree_dbnum():
|
||||
"""
|
||||
Figure out what our next free database reference number is.
|
||||
|
||||
If we need to recycle a GARBAGE object, return the object to recycle
|
||||
Otherwise, return the first free dbref.
|
||||
"""
|
||||
# First we'll see if there's an object of type 6 (GARBAGE) that we
|
||||
# can recycle.
|
||||
nextfree = Object.objects.filter(type__exact=6)
|
||||
if nextfree:
|
||||
# We've got at least one garbage object to recycle.
|
||||
return nextfree[0]
|
||||
else:
|
||||
# No garbage to recycle, find the highest dbnum and increment it
|
||||
# for our next free.
|
||||
return int(Object.objects.order_by('-id')[0].id + 1)
|
||||
|
||||
def global_object_name_search(ostring):
|
||||
"""
|
||||
Searches through all objects for a name match.
|
||||
"""
|
||||
return Object.objects.filter(name__icontains=ostring).exclude(type=6)
|
||||
|
||||
def list_search_object_namestr(searchlist, ostring, dbref_only=False):
|
||||
"""
|
||||
Iterates through a list of objects and returns a list of
|
||||
name matches.
|
||||
"""
|
||||
if dbref_only:
|
||||
return [prospect for prospect in searchlist if prospect.dbref_match(ostring)]
|
||||
else:
|
||||
return [prospect for prospect in searchlist if prospect.name_match(ostring)]
|
||||
|
||||
def local_and_global_search(searcher, ostring, search_contents=True, search_location=True, dbref_only=False):
|
||||
"""
|
||||
Searches an object's location then globally for a dbref or name match.
|
||||
search_contents: (bool) While true, check the contents of the searcher.
|
||||
search_location: (bool) While true, check the searcher's surroundings.
|
||||
"""
|
||||
search_query = ''.join(ostring)
|
||||
|
||||
# This is a global dbref search. Not applicable if we're only searching
|
||||
# searcher's contents/locations, dbref comparisons for location/contents
|
||||
# searches are handled by list_search_object_namestr() below.
|
||||
if is_dbref(ostring) and search_contents and search_location:
|
||||
search_num = search_query[1:]
|
||||
dbref_match = list(Object.objects.filter(id=search_num).exclude(type=6))
|
||||
if len(dbref_match) > 0:
|
||||
return dbref_match
|
||||
|
||||
local_matches = []
|
||||
# Handle our location/contents searches. list_search_object_namestr() does
|
||||
# name and dbref comparisons against search_query.
|
||||
if search_contents:
|
||||
local_matches += list_search_object_namestr(searcher.get_contents(), search_query)
|
||||
if search_location:
|
||||
local_matches += list_search_object_namestr(searcher.get_location().get_contents(), search_query)
|
||||
|
||||
# If the object the invoker is in matches, add it as well.
|
||||
if searcher.get_location().dbref_match(ostring) or ostring == 'here':
|
||||
local_matches.append(searcher.get_location())
|
||||
elif ostring == 'me' and searcher:
|
||||
local_matches.append(searcher)
|
||||
|
||||
return local_matches
|
||||
|
||||
def is_dbref(dbstring):
|
||||
"""
|
||||
Is the input a well-formed dbref number?
|
||||
"""
|
||||
try:
|
||||
number = int(dbstring[1:])
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
if dbstring[0] != '#':
|
||||
return False
|
||||
elif number < 1:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_object_from_dbref(dbref):
|
||||
"""
|
||||
Returns an object when given a dbref.
|
||||
"""
|
||||
return Object.objects.get(id=dbref)
|
||||
|
||||
def create_object(odat):
|
||||
"""
|
||||
Create a new object. odat is a dictionary that contains the following keys.
|
||||
REQUIRED KEYS:
|
||||
* type: Integer representing the object's type.
|
||||
* name: The name of the new object.
|
||||
* location: Reference to another object for the new object to reside in.
|
||||
* owner: The creator of the object.
|
||||
OPTIONAL KEYS:
|
||||
* home: Reference to another object to home to. If not specified, use
|
||||
location key for home.
|
||||
"""
|
||||
next_dbref = get_nextfree_dbnum()
|
||||
if not str(next_dbref).isdigit():
|
||||
# Recycle a garbage object.
|
||||
new_object = next_dbref
|
||||
else:
|
||||
new_object = Object()
|
||||
|
||||
new_object.type = odat["type"]
|
||||
new_object.set_name(odat["name"])
|
||||
|
||||
# If this is a player, we don't want him owned by anyone.
|
||||
# The get_owner() function will return that the player owns
|
||||
# himself.
|
||||
if odat["type"] == 1:
|
||||
new_object.owner = None
|
||||
new_object.zone = None
|
||||
else:
|
||||
new_object.owner = odat["owner"]
|
||||
|
||||
if new_object.get_owner().get_zone():
|
||||
new_object.zone = new_object.get_owner().get_zone()
|
||||
|
||||
# If we have a 'home' key, use that for our home value. Otherwise use
|
||||
# the location key.
|
||||
if odat.has_key("home"):
|
||||
new_object.home = odat["home"]
|
||||
else:
|
||||
if new_object.is_exit():
|
||||
new_object.home = None
|
||||
else:
|
||||
new_object.home = odat["location"]
|
||||
|
||||
new_object.save()
|
||||
new_object.move_to(odat['location'])
|
||||
|
||||
return new_object
|
||||
|
||||
def create_user(cdat, uname, email, password):
|
||||
"""
|
||||
Handles the creation of new users.
|
||||
"""
|
||||
session = cdat['session']
|
||||
server = cdat['server']
|
||||
start_room = int(gameconf.get_configvalue('player_dbnum_start'))
|
||||
start_room_obj = get_object_from_dbref(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 = get_nextfree_dbnum()
|
||||
print 'UID', uid
|
||||
|
||||
# If this is an object, we know to recycle it since it's garbage. We'll
|
||||
# pluck the user ID from it.
|
||||
if not str(uid).isdigit():
|
||||
uid = uid.id
|
||||
print 'UID2', uid
|
||||
|
||||
user = User.objects.create_user(uname, email, password)
|
||||
# It stinks to have to do this but it's the only trivial way now.
|
||||
user.save()
|
||||
|
||||
# We can't use the user model to change the id because of the way keys
|
||||
# are handled, so we actually need to fall back to raw SQL. Boo hiss.
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("UPDATE auth_user SET id=%d WHERE id=%d" % (uid, user.id))
|
||||
|
||||
# Grab the user object again since we've changed it and the old reference
|
||||
# is no longer valid.
|
||||
user = User.objects.get(id=uid)
|
||||
|
||||
# Create a player object of the same ID in the Objects table.
|
||||
odat = {"id": uid, "name": uname, "type": 1, "location": start_room_obj, "owner": None}
|
||||
user_object = create_object(odat)
|
||||
|
||||
# Activate the player's session and set them loose.
|
||||
session.login(user)
|
||||
print 'Registration: %s' % (session,)
|
||||
session.push("Welcome to %s, %s.\n\r" % (gameconf.get_configvalue('site_name'), session.get_pobject().get_name(),))
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
import session_mgr
|
||||
import commands_privileged
|
||||
import commands_general
|
||||
import commands_unloggedin
|
||||
"""
|
||||
General commonly used functions.
|
||||
"""
|
||||
def print_errmsg(errormsg):
|
||||
"""
|
||||
Prints/logs an error message. Pipe any errors to be logged through here.
|
||||
For now we're just printing to standard out.
|
||||
"""
|
||||
print 'ERROR: %s' % (errormsg,)
|
||||
|
||||
def command_list():
|
||||
"""
|
||||
Return a list of all commands.
|
||||
"""
|
||||
commands = dir(commands_unloggedin) + dir(commands_general)
|
||||
stf_commands = dir(commands_privileged)
|
||||
filtered = [prospect for prospect in commands if "cmd_" in prospect]
|
||||
stf_filtered = [prospect for prospect in stf_commands if "cmd_" in prospect]
|
||||
processed = []
|
||||
for cmd in filtered:
|
||||
processed.append(cmd[4:])
|
||||
for cmd in stf_filtered:
|
||||
processed.append('@%s' %(cmd[4:],))
|
||||
return processed
|
||||
|
||||
def time_format(seconds, style=0):
|
||||
"""
|
||||
Function to return a 'prettified' version of a value in seconds.
|
||||
|
||||
Style 0: 1d 08:30
|
||||
Style 1: 1d
|
||||
Style 2: 1 day, 8 hours, 30 minutes, 10 seconds
|
||||
"""
|
||||
if seconds < 0:
|
||||
seconds = 0
|
||||
else:
|
||||
# We'll just use integer math, no need for decimal precision.
|
||||
seconds = int(seconds)
|
||||
|
||||
days = seconds / 86400
|
||||
seconds -= days * 86400
|
||||
hours = seconds / 3600
|
||||
seconds -= hours * 3600
|
||||
minutes = seconds / 60
|
||||
seconds -= minutes * 60
|
||||
|
||||
if style is 0:
|
||||
"""
|
||||
Standard colon-style output.
|
||||
"""
|
||||
if days > 0:
|
||||
retval = '%id %02i:%02i' % (days, hours, minutes,)
|
||||
else:
|
||||
retval = '%02i:%02i' % (hours, minutes,)
|
||||
|
||||
return retval
|
||||
elif style is 1:
|
||||
"""
|
||||
Simple, abbreviated form that only shows the highest time amount.
|
||||
"""
|
||||
if days > 0:
|
||||
return '%id' % (days,)
|
||||
elif hours > 0:
|
||||
return '%ih' % (hours,)
|
||||
elif minutes > 0:
|
||||
return '%im' % (minutes,)
|
||||
else:
|
||||
return '%is' % (seconds,)
|
||||
|
||||
elif style is 2:
|
||||
"""
|
||||
Full-detailed, long-winded format.
|
||||
"""
|
||||
days_str = hours_str = minutes_str = ''
|
||||
if days > 0:
|
||||
days_str = '%i days, ' % (days,)
|
||||
if days or hours > 0:
|
||||
hours_str = '%i hours, ' % (hours,)
|
||||
if hours or minutes > 0:
|
||||
minutes_str = '%i minutes, ' % (minutes,)
|
||||
seconds_str = '%i seconds' % (seconds,)
|
||||
|
||||
retval = '%s%s%s%s' % (days_str, hours_str, minutes_str, seconds_str,)
|
||||
return retval
|
||||
|
||||
def announce_all(message, with_ann_prefix=True, with_nl=True):
|
||||
"""
|
||||
Announces something to all connected players.
|
||||
"""
|
||||
if with_ann_prefix:
|
||||
prefix = 'Announcement:'
|
||||
else:
|
||||
prefix = ''
|
||||
|
||||
if with_nl:
|
||||
newline = '\r\n'
|
||||
else:
|
||||
newline = ''
|
||||
|
||||
for session in session_mgr.get_session_list():
|
||||
session.msg_no_nl('%s %s%s' % (prefix, message,newline,))
|
||||
|
||||
def word_wrap(text, width=78):
|
||||
"""
|
||||
A word-wrap function that preserves existing line breaks
|
||||
and most spaces in the text. Expects that existing line
|
||||
breaks are posix newlines (\n).
|
||||
|
||||
Function originally by Mike Brown
|
||||
"""
|
||||
return reduce(lambda line, word, width=width: '%s%s%s' %
|
||||
(line,
|
||||
' \n'[(len(line)-line.rfind('\n')-1
|
||||
+ len(word.split('\n',1)[0]
|
||||
) >= width)],
|
||||
word),
|
||||
text.split(' ')
|
||||
)
|
||||
|
|
@ -1,29 +0,0 @@
|
|||
from apps.helpsys.models import HelpEntry
|
||||
"""
|
||||
Help system functions.
|
||||
"""
|
||||
def find_topicmatch(pobject, topicstr):
|
||||
"""
|
||||
Searches for matching topics based on player's input.
|
||||
"""
|
||||
is_staff = pobject.is_staff()
|
||||
if topicstr.isdigit():
|
||||
if is_staff:
|
||||
return HelpEntry.objects.filter(id=topicstr)
|
||||
else:
|
||||
return HelpEntry.objects.filter(id=topicstr).exclude(staff_only=1)
|
||||
else:
|
||||
if is_staff:
|
||||
return HelpEntry.objects.filter(topicname__istartswith=topicstr)
|
||||
else:
|
||||
return HelpEntry.objects.filter(topicname__istartswith=topicstr).exclude(staff_only=1)
|
||||
|
||||
def find_topicsuggestions(pobject, topicstr):
|
||||
"""
|
||||
Do a fuzzier "contains" match.
|
||||
"""
|
||||
is_staff = pobject.is_staff()
|
||||
if is_staff:
|
||||
return HelpEntry.objects.filter(topicname__icontains=topicstr)
|
||||
else:
|
||||
return HelpEntry.objects.filter(topicname__icontains=topicstr).exclude(staff_only=1)
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
from apps.config.models import ConfigValue
|
||||
"""
|
||||
Handle the setting/retrieving of server config directives.
|
||||
"""
|
||||
|
||||
def get_configvalue(configname):
|
||||
"""
|
||||
Retrieve a configuration value.
|
||||
"""
|
||||
return ConfigValue.objects.get(conf_key=configname).conf_value
|
||||
|
|
@ -1,11 +0,0 @@
|
|||
#!/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)
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
#!/bin/bash
|
||||
export DJANGO_SETTINGS_MODULE="settings"
|
||||
#python2.5 -m cProfile -o profiler.log -s time server.py
|
||||
python2.5 server.py
|
||||
|
|
@ -1,34 +0,0 @@
|
|||
import time
|
||||
import events
|
||||
"""
|
||||
A really simple scheduler. We can probably get a lot fancier with this
|
||||
in the future, but it'll do for now.
|
||||
|
||||
ADDING AN EVENT:
|
||||
* Add an entry to the 'schedule' dictionary.
|
||||
* Add the proper event_ function here.
|
||||
* Profit.
|
||||
"""
|
||||
|
||||
# The timer method to be triggered by the main server loop.
|
||||
def heartbeat():
|
||||
"""
|
||||
Handle one tic/heartbeat.
|
||||
"""
|
||||
tictime = time.time()
|
||||
for event in events.schedule:
|
||||
try:
|
||||
events.lastrun[event]
|
||||
except:
|
||||
events.lastrun[event] = time.time()
|
||||
|
||||
diff = tictime - events.lastrun[event]
|
||||
|
||||
if diff >= events.schedule[event]:
|
||||
event_func = getattr(events, event)
|
||||
|
||||
if callable(event_func):
|
||||
event_func()
|
||||
|
||||
# We'll get a new reading for time for accuracy.
|
||||
events.lastrun[event] = time.time()
|
||||
|
|
@ -1,108 +0,0 @@
|
|||
from asyncore import dispatcher
|
||||
from asynchat import async_chat
|
||||
import socket, asyncore, time
|
||||
from django.db import models
|
||||
from django.db import connection
|
||||
|
||||
from apps.config.models import CommandAlias
|
||||
import scheduler
|
||||
import functions_general
|
||||
import session_mgr
|
||||
import gameconf
|
||||
import settings
|
||||
|
||||
class Server(dispatcher):
|
||||
"""
|
||||
The main server class from which everything branches.
|
||||
"""
|
||||
def __init__(self):
|
||||
self.cmd_alias_list = {}
|
||||
self.game_running = True
|
||||
|
||||
# Database-specific startup optimizations.
|
||||
if settings.DATABASE_ENGINE == "sqlite3":
|
||||
self.sqlite3_prep()
|
||||
|
||||
# Wipe our temporary flags on all of the objects.
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("UPDATE objects_object SET nosave_flags=''")
|
||||
|
||||
print '-'*50
|
||||
# Load command aliases into memory for easy/quick access.
|
||||
self.load_cmd_aliases()
|
||||
self.port = gameconf.get_configvalue('site_port')
|
||||
|
||||
# Start accepting connections.
|
||||
dispatcher.__init__(self)
|
||||
self.create_socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.set_reuse_addr()
|
||||
self.bind(('', int(self.port)))
|
||||
self.listen(100)
|
||||
self.start_time = time.time()
|
||||
|
||||
print ' %s started on port %s.' % (gameconf.get_configvalue('site_name'), self.port,)
|
||||
print '-'*50
|
||||
|
||||
"""
|
||||
BEGIN SERVER STARTUP METHODS
|
||||
"""
|
||||
|
||||
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 ' Command Aliases Loaded: %i' % (len(self.cmd_alias_list),)
|
||||
|
||||
def handle_accept(self):
|
||||
"""
|
||||
What to do when we get a connection.
|
||||
"""
|
||||
conn, addr = self.accept()
|
||||
session = session_mgr.new_session(self, conn, addr)
|
||||
session.game_connect_screen(session)
|
||||
print 'Connection:', str(session)
|
||||
print 'Sessions active:', len(session_mgr.get_session_list())
|
||||
|
||||
def sqlite3_prep(self):
|
||||
"""
|
||||
Optimize some SQLite stuff at startup since we can't save it to the
|
||||
database.
|
||||
"""
|
||||
cursor = connection.cursor()
|
||||
cursor.execute("PRAGMA cache_size=10000")
|
||||
cursor.execute("PRAGMA synchronous=OFF")
|
||||
cursor.execute("PRAGMA count_changes=OFF")
|
||||
cursor.execute("PRAGMA temp_store=2")
|
||||
|
||||
"""
|
||||
BEGIN GENERAL METHODS
|
||||
"""
|
||||
def shutdown(self, message='The server has been shutdown. Please check back soon.'):
|
||||
functions_general.announce_all(message)
|
||||
self.game_running = False
|
||||
|
||||
"""
|
||||
END Server CLASS
|
||||
"""
|
||||
|
||||
"""
|
||||
BEGIN MAIN APPLICATION LOGIC
|
||||
"""
|
||||
if __name__ == '__main__':
|
||||
server = Server()
|
||||
|
||||
try:
|
||||
while server.game_running:
|
||||
asyncore.loop(timeout=5, count=1)
|
||||
scheduler.heartbeat()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
server.shutdown()
|
||||
print '--> Server killed by keystroke.'
|
||||
|
||||
except:
|
||||
server.shutdown(message="The server has encountered an error and has been shut down. Please check back soon.")
|
||||
print '-!> Server crashed.'
|
||||
|
|
@ -1,135 +0,0 @@
|
|||
from asyncore import dispatcher
|
||||
from asynchat import async_chat
|
||||
import socket, asyncore, time, sys
|
||||
import cmdhandler
|
||||
from apps.objects.models import Object
|
||||
from django.contrib.auth.models import User
|
||||
import commands_general
|
||||
import functions_db
|
||||
import session_mgr
|
||||
|
||||
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.uid = None
|
||||
self.sock = sock
|
||||
self.logged_in = False
|
||||
# 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()
|
||||
|
||||
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 and pass it to our command handler.
|
||||
"""
|
||||
line = (''.join(self.data))
|
||||
line = line.strip('\r')
|
||||
uinput = line
|
||||
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}
|
||||
cmdhandler.handle(cdat)
|
||||
|
||||
def handle_close(self):
|
||||
"""
|
||||
Break the connection and do some accounting.
|
||||
"""
|
||||
pobject = self.get_pobject()
|
||||
pobject.set_flag("CONNECTED", False)
|
||||
pobject.get_location().emit_to_contents("%s has disconnected." % (pobject.get_name(),), exclude=pobject)
|
||||
async_chat.handle_close(self)
|
||||
self.logged_in = False
|
||||
session_mgr.remove_session(self)
|
||||
print 'Sessions active:', len(session_mgr.get_session_list())
|
||||
|
||||
def get_pobject(self):
|
||||
"""
|
||||
Returns the object associated with a session.
|
||||
"""
|
||||
try:
|
||||
result = Object.objects.get(id=self.uid)
|
||||
return result
|
||||
except:
|
||||
return False
|
||||
|
||||
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
|
||||
session.msg(buffer)
|
||||
|
||||
def login(self, user):
|
||||
"""
|
||||
After the user has authenticated, handle logging him in.
|
||||
"""
|
||||
self.uid = user.id
|
||||
self.name = user.username
|
||||
self.logged_in = True
|
||||
self.conn_time = time.time()
|
||||
pobject = self.get_pobject()
|
||||
pobject.set_flag("CONNECTED", True)
|
||||
|
||||
self.msg("You are now logged in as %s." % (self.name,))
|
||||
pobject.get_location().emit_to_contents("%s has connected." % (pobject.get_name(),), exclude=pobject)
|
||||
cdat = {"session": self, "uinput":'look', "server": self.server}
|
||||
cmdhandler.handle(cdat)
|
||||
print "Login: %s" % (self,)
|
||||
|
||||
def msg(self, message):
|
||||
"""
|
||||
Sends a message with the newline/return included. Use this instead of
|
||||
directly calling push().
|
||||
"""
|
||||
self.push("%s\n\r" % (message,))
|
||||
|
||||
def msg_no_nl(self, message):
|
||||
"""
|
||||
Sends a message without the newline/return included. Use this instead of
|
||||
directly calling push().
|
||||
"""
|
||||
self.push("%s" % (message,))
|
||||
|
||||
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()
|
||||
|
|
@ -1,53 +0,0 @@
|
|||
from session import PlayerSession
|
||||
|
||||
"""
|
||||
Session manager, handles connected players.
|
||||
"""
|
||||
# Our list of connected sessions.
|
||||
session_list = []
|
||||
|
||||
def new_session(server, conn, addr):
|
||||
"""
|
||||
Create and return a new session.
|
||||
"""
|
||||
session = PlayerSession(server, conn, addr)
|
||||
session_list.insert(0, session)
|
||||
return session
|
||||
|
||||
def get_session_list():
|
||||
"""
|
||||
Lists the connected session objects.
|
||||
"""
|
||||
return session_list
|
||||
|
||||
def remove_session(session):
|
||||
"""
|
||||
Removes a session from the session list.
|
||||
"""
|
||||
session_list.remove(session)
|
||||
|
||||
def session_from_object(targobject):
|
||||
"""
|
||||
Return the session object given a object (if there is one open).
|
||||
|
||||
session_list: (list) The server's session_list attribute.
|
||||
targobject: (Object) The object to match.
|
||||
"""
|
||||
results = [prospect for prospect in session_list if prospect.get_pobject() == targobject]
|
||||
if results:
|
||||
return results[0]
|
||||
else:
|
||||
return False
|
||||
|
||||
def session_from_dbref(dbstring):
|
||||
"""
|
||||
Return the session object given a dbref (if there is one open).
|
||||
|
||||
dbstring: (int) The dbref number to match against.
|
||||
"""
|
||||
if is_dbref(dbstring):
|
||||
results = [prospect for prospect in session_list if prospect.get_pobject().dbref_match(dbstring)]
|
||||
if results:
|
||||
return results[0]
|
||||
else:
|
||||
return False
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
# Django settings for evennia project.
|
||||
|
||||
DEBUG = True
|
||||
TEMPLATE_DEBUG = DEBUG
|
||||
|
||||
ADMINS = (
|
||||
# ('Your Name', 'your_email@domain.com'),
|
||||
)
|
||||
|
||||
MANAGERS = ADMINS
|
||||
|
||||
DATABASE_ENGINE = 'sqlite3' # 'postgresql', 'mysql', 'sqlite3' or 'ado_mssql'.
|
||||
DATABASE_NAME = '/home/evennia/evennia/evennia.sql' # Or path to database file if using sqlite3.
|
||||
DATABASE_USER = '' # Not used with sqlite3.
|
||||
DATABASE_PASSWORD = '' # 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/evennia/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 = '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',
|
||||
'apps.config',
|
||||
'apps.objects',
|
||||
'apps.helpsys',
|
||||
'apps.genperms',
|
||||
)
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
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