Starting here, trunk is broken pending re-organizations. Check out the previous revision if you'd like to tinker.
This commit is contained in:
parent
4b25a08597
commit
837f1152c6
28 changed files with 0 additions and 0 deletions
0
src/objects/__init__.py
Executable file
0
src/objects/__init__.py
Executable file
11
src/objects/exceptions.py
Normal file
11
src/objects/exceptions.py
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
"""
|
||||
Exceptions for the object application.
|
||||
"""
|
||||
from src.exceptions_generic import GenericException
|
||||
|
||||
class ObjectNotExist(GenericException):
|
||||
"""
|
||||
Raised when an object is queried for but does not exist.
|
||||
"""
|
||||
def __str__(self):
|
||||
return repr("No such object: %s" % self.value)
|
||||
0
src/objects/managers/__init__.py
Normal file
0
src/objects/managers/__init__.py
Normal file
18
src/objects/managers/attribute.py
Normal file
18
src/objects/managers/attribute.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
"""
|
||||
Custom manager for Attribute objects.
|
||||
"""
|
||||
from django.db import models
|
||||
|
||||
from src import defines_global
|
||||
|
||||
class AttributeManager(models.Manager):
|
||||
def is_modifiable_attrib(self, attribname):
|
||||
"""
|
||||
Check to see if a particular attribute is modifiable.
|
||||
|
||||
attribname: (string) An attribute name to check.
|
||||
"""
|
||||
if attribname.upper() not in defines_global.NOSET_ATTRIBS:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
8
src/objects/managers/commchannel.py
Normal file
8
src/objects/managers/commchannel.py
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
"""
|
||||
Custom manager for CommChannel objects.
|
||||
"""
|
||||
from django.db import models
|
||||
|
||||
class CommChannelManager(models.Manager):
|
||||
pass
|
||||
|
||||
369
src/objects/managers/object.py
Normal file
369
src/objects/managers/object.py
Normal file
|
|
@ -0,0 +1,369 @@
|
|||
"""
|
||||
Custom manager for Object objects.
|
||||
"""
|
||||
import sets
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from django.db import models
|
||||
from django.db import connection
|
||||
from django.contrib.auth.models import User
|
||||
from django.db.models import Q
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
|
||||
from apps.config.models import ConfigValue
|
||||
from apps.objects.exceptions import ObjectNotExist
|
||||
from apps.objects.util import object as util_object
|
||||
from src import defines_global
|
||||
|
||||
class ObjectManager(models.Manager):
|
||||
def num_total_players(self):
|
||||
"""
|
||||
Returns the total number of registered players.
|
||||
"""
|
||||
return User.objects.count()
|
||||
|
||||
def get_connected_players(self):
|
||||
"""
|
||||
Returns the a QuerySet containing the currently connected players.
|
||||
"""
|
||||
return self.filter(nosave_flags__contains="CONNECTED")
|
||||
|
||||
def get_recently_created_users(self, days=7):
|
||||
"""
|
||||
Returns a QuerySet containing the player User accounts that have been
|
||||
connected within the last <days> days.
|
||||
"""
|
||||
end_date = datetime.now()
|
||||
tdelta = timedelta(days)
|
||||
start_date = end_date - tdelta
|
||||
return User.objects.filter(date_joined__range=(start_date, end_date))
|
||||
|
||||
def get_recently_connected_users(self, days=7):
|
||||
"""
|
||||
Returns a QuerySet containing the player User accounts that have been
|
||||
connected within the last <days> days.
|
||||
"""
|
||||
end_date = datetime.now()
|
||||
tdelta = timedelta(days)
|
||||
start_date = end_date - tdelta
|
||||
return User.objects.filter(last_login__range=(start_date, end_date)).order_by('-last_login')
|
||||
|
||||
def get_nextfree_dbnum(self):
|
||||
"""
|
||||
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 = self.filter(type__exact=defines_global.OTYPE_GARBAGE)
|
||||
if nextfree:
|
||||
# We've got at least one garbage object to recycle.
|
||||
return nextfree.id
|
||||
else:
|
||||
# No garbage to recycle, find the highest dbnum and increment it
|
||||
# for our next free.
|
||||
return int(self.order_by('-id')[0].id + 1)
|
||||
|
||||
def global_object_name_search(self, ostring, exact_match=False):
|
||||
"""
|
||||
Searches through all objects for a name match.
|
||||
"""
|
||||
if exact_match:
|
||||
o_query = self.filter(name__iexact=ostring)
|
||||
else:
|
||||
o_query = self.filter(name__icontains=ostring)
|
||||
|
||||
return o_query.exclude(type__in=[defines_global.OTYPE_GARBAGE,
|
||||
defines_global.OTYPE_GOING])
|
||||
|
||||
def list_search_object_namestr(self, searchlist, ostring, dbref_only=False,
|
||||
limit_types=False, match_type="fuzzy"):
|
||||
"""
|
||||
Iterates through a list of objects and returns a list of
|
||||
name matches.
|
||||
searchlist: (List of Objects) The objects to perform name comparisons on.
|
||||
ostring: (string) The string to match against.
|
||||
dbref_only: (bool) Only compare dbrefs.
|
||||
limit_types: (list of int) A list of Object type numbers to filter by.
|
||||
"""
|
||||
if dbref_only:
|
||||
if limit_types:
|
||||
return [prospect for prospect in searchlist if prospect.dbref_match(ostring) and prospect.type in limit_types]
|
||||
else:
|
||||
return [prospect for prospect in searchlist if prospect.dbref_match(ostring)]
|
||||
else:
|
||||
if limit_types:
|
||||
return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type) and prospect.type in limit_types]
|
||||
else:
|
||||
return [prospect for prospect in searchlist if prospect.name_match(ostring, match_type=match_type)]
|
||||
|
||||
|
||||
def standard_plr_objsearch(self, session, ostring, search_contents=True,
|
||||
search_location=True, dbref_only=False,
|
||||
limit_types=False):
|
||||
"""
|
||||
Perform a standard object search via a player session, handling multiple
|
||||
results and lack thereof gracefully.
|
||||
|
||||
session: (SessionProtocol) Reference to the player's session.
|
||||
ostring: (str) The string to match object names against.
|
||||
"""
|
||||
pobject = session.get_pobject()
|
||||
results = self.local_and_global_search(pobject, ostring,
|
||||
search_contents=search_contents,
|
||||
search_location=search_location,
|
||||
dbref_only=dbref_only,
|
||||
limit_types=limit_types)
|
||||
|
||||
if len(results) > 1:
|
||||
session.msg("More than one match found (please narrow target):")
|
||||
for result in results:
|
||||
session.msg(" %s" % (result.get_name(),))
|
||||
return False
|
||||
elif len(results) == 0:
|
||||
session.msg("I don't see that here.")
|
||||
return False
|
||||
else:
|
||||
return results[0]
|
||||
|
||||
def object_totals(self):
|
||||
"""
|
||||
Returns a dictionary with database object totals.
|
||||
"""
|
||||
dbtotals = {
|
||||
"objects": self.count(),
|
||||
"things": self.filter(type=defines_global.OTYPE_THING).count(),
|
||||
"exits": self.filter(type=defines_global.OTYPE_EXIT).count(),
|
||||
"rooms": self.filter(type=defines_global.OTYPE_ROOM).count(),
|
||||
"garbage": self.filter(type=defines_global.OTYPE_GARBAGE).count(),
|
||||
"players": self.filter(type=defines_global.OTYPE_PLAYER).count(),
|
||||
}
|
||||
return dbtotals
|
||||
|
||||
def player_alias_search(self, searcher, ostring):
|
||||
"""
|
||||
Search players by alias. Returns a list of objects whose "ALIAS"
|
||||
attribute exactly (not case-sensitive) matches ostring.
|
||||
|
||||
searcher: (Object) The object doing the searching.
|
||||
ostring: (string) The alias string to search for.
|
||||
"""
|
||||
Attribute = ContentType.objects.get(app_label="objects",
|
||||
model="attribute").model_class()
|
||||
results = Attribute.objects.select_related().filter(attr_name__exact="ALIAS").filter(attr_value__iexact=ostring)
|
||||
return [prospect.get_object() for prospect in results if prospect.get_object().is_player()]
|
||||
|
||||
def player_name_search(self, search_string):
|
||||
"""
|
||||
Combines an alias and global search for a player's name. If there are
|
||||
no alias matches, do a global search limiting by type PLAYER.
|
||||
|
||||
search_string: (string) The name string to search for.
|
||||
"""
|
||||
# Handle the case where someone might have started the search_string
|
||||
# with a *
|
||||
if search_string.startswith('*') is True:
|
||||
search_string = search_string[1:]
|
||||
# Use Q objects to build complex OR query to look at either
|
||||
# the player name or ALIAS attribute
|
||||
player_filter = Q(name__iexact=search_string)
|
||||
alias_filter = Q(attribute__attr_name__exact="ALIAS") & \
|
||||
Q(attribute__attr_value__iexact=search_string)
|
||||
player_matches = self.filter(
|
||||
player_filter | alias_filter).filter(
|
||||
type=defines_global.OTYPE_PLAYER).distinct()
|
||||
try:
|
||||
return player_matches[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def is_dbref(self, dbstring):
|
||||
"""
|
||||
Is the input a well-formed dbref number?
|
||||
"""
|
||||
util_object.is_dbref(dbstring)
|
||||
|
||||
def dbref_search(self, dbref_string, limit_types=False):
|
||||
"""
|
||||
Searches for a given dbref.
|
||||
|
||||
dbref_number: (string) The dbref to search for. With # sign.
|
||||
limit_types: (list of int) A list of Object type numbers to filter by.
|
||||
"""
|
||||
if not util_object.is_dbref(dbref_string):
|
||||
return None
|
||||
dbref_string = dbref_string[1:]
|
||||
dbref_matches = self.filter(id=dbref_string).exclude(
|
||||
type=defines_global.OTYPE_GARBAGE)
|
||||
# Check for limiters
|
||||
if limit_types is not False:
|
||||
for limiter in limit_types:
|
||||
dbref_matches.filter(type=limiter)
|
||||
try:
|
||||
return dbref_matches[0]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def local_and_global_search(self, searcher, ostring, search_contents=True,
|
||||
search_location=True, dbref_only=False,
|
||||
limit_types=False):
|
||||
"""
|
||||
Searches an object's location then globally for a dbref or name match.
|
||||
|
||||
searcher: (Object) The object performing the search.
|
||||
ostring: (string) The string to compare names against.
|
||||
search_contents: (bool) While true, check the contents of the searcher.
|
||||
search_location: (bool) While true, check the searcher's surroundings.
|
||||
dbref_only: (bool) Only compare dbrefs.
|
||||
limit_types: (list of int) A list of Object type numbers to filter by.
|
||||
"""
|
||||
search_query = 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 util_object.is_dbref(ostring):
|
||||
dbref_match = self.dbref_search(search_query, limit_types)
|
||||
if dbref_match is not None:
|
||||
return [dbref_match]
|
||||
|
||||
# If the search string is one of the following, return immediately with
|
||||
# the appropriate result.
|
||||
if searcher.get_location().dbref_match(ostring) or ostring == 'here':
|
||||
return [searcher.get_location()]
|
||||
elif ostring == 'me' and searcher:
|
||||
return [searcher]
|
||||
|
||||
if search_query[0] == "*":
|
||||
# Player search- gotta search by name or alias
|
||||
search_target = search_query[1:]
|
||||
player_match = self.player_name_search(search_target)
|
||||
if player_match is not None:
|
||||
return [player_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 += self.list_search_object_namestr(searcher.get_contents(),
|
||||
search_query, limit_types)
|
||||
if search_location:
|
||||
local_matches += self.list_search_object_namestr(searcher.get_location().get_contents(),
|
||||
search_query, limit_types=limit_types)
|
||||
return local_matches
|
||||
|
||||
def get_user_from_email(self, uemail):
|
||||
"""
|
||||
Returns a player's User object when given an email address.
|
||||
"""
|
||||
return User.objects.filter(email__iexact=uemail)
|
||||
|
||||
def get_object_from_dbref(self, dbref):
|
||||
"""
|
||||
Returns an object when given a dbref.
|
||||
"""
|
||||
try:
|
||||
return self.get(id=dbref)
|
||||
except self.model.DoesNotExist:
|
||||
raise ObjectNotExist(dbref)
|
||||
|
||||
def create_object(self, 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 = self.get_nextfree_dbnum()
|
||||
Object = ContentType.objects.get(app_label="objects",
|
||||
model="object").model_class()
|
||||
new_object = Object()
|
||||
|
||||
new_object.id = next_dbref
|
||||
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()
|
||||
|
||||
# Rooms have a NULL location.
|
||||
if not new_object.is_room():
|
||||
new_object.move_to(odat['location'])
|
||||
|
||||
return new_object
|
||||
|
||||
def create_user(self, command, uname, email, password):
|
||||
"""
|
||||
Handles the creation of new users.
|
||||
"""
|
||||
session = command.session
|
||||
server = command.server
|
||||
start_room = int(ConfigValue.objects.get_configvalue('player_dbnum_start'))
|
||||
start_room_obj = self.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 = self.get_nextfree_dbnum()
|
||||
|
||||
# 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
|
||||
|
||||
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 = self.create_object(odat)
|
||||
|
||||
# Activate the player's session and set them loose.
|
||||
session.login(user)
|
||||
print 'Registration: %s' % (session,)
|
||||
session.msg("Welcome to %s, %s.\n\r" % (
|
||||
ConfigValue.objects.get_configvalue('site_name'),
|
||||
session.get_pobject().get_name(show_dbref=False)))
|
||||
930
src/objects/models.py
Executable file
930
src/objects/models.py
Executable file
|
|
@ -0,0 +1,930 @@
|
|||
import re
|
||||
from django.db import models
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.contrib import admin
|
||||
from apps.config.models import ConfigValue
|
||||
from apps.objects.util import object as util_object
|
||||
from apps.objects.managers.commchannel import CommChannelManager
|
||||
from apps.objects.managers.object import ObjectManager
|
||||
from apps.objects.managers.attribute import AttributeManager
|
||||
from src import scripthandler
|
||||
from src import defines_global
|
||||
from src import ansi
|
||||
from src import session_mgr
|
||||
# Import as the absolute path to avoid local variable clashes.
|
||||
import src.flags
|
||||
from src.util import functions_general
|
||||
|
||||
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.
|
||||
"""
|
||||
attr_name = models.CharField(max_length=255)
|
||||
attr_value = models.CharField(max_length=255)
|
||||
attr_hidden = models.BooleanField(default=0)
|
||||
attr_object = models.ForeignKey("Object")
|
||||
|
||||
objects = AttributeManager()
|
||||
|
||||
def __str__(self):
|
||||
return "%s(%s)" % (self.attr_name, self.id)
|
||||
|
||||
class Admin:
|
||||
list_display = ('attr_object', 'attr_name', 'attr_value',)
|
||||
search_fields = ['attr_name']
|
||||
|
||||
"""
|
||||
BEGIN COMMON METHODS
|
||||
"""
|
||||
def get_name(self):
|
||||
"""
|
||||
Returns an attribute's name.
|
||||
"""
|
||||
return self.attr_name
|
||||
|
||||
def get_value(self):
|
||||
"""
|
||||
Returns an attribute's value.
|
||||
"""
|
||||
return self.attr_value
|
||||
|
||||
def get_object(self):
|
||||
"""
|
||||
Returns the object that the attribute resides on.
|
||||
"""
|
||||
return self.attr_object
|
||||
|
||||
def is_hidden(self):
|
||||
"""
|
||||
Returns True if the attribute is hidden.
|
||||
"""
|
||||
if self.attr_hidden or self.get_name().upper() in defines_global.HIDDEN_ATTRIBS:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def is_noset(self):
|
||||
"""
|
||||
Returns True if the attribute is unsettable.
|
||||
"""
|
||||
if self.get_name().upper() in defines_global.NOSET_ATTRIBS:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def get_attrline(self):
|
||||
"""
|
||||
Best described as a __str__ method for in-game. Renders the attribute's
|
||||
name and value as per MUX.
|
||||
"""
|
||||
return "%s%s%s: %s" % (ansi.ansi["hilite"],
|
||||
self.get_name(),
|
||||
ansi.ansi["normal"],
|
||||
self.get_value())
|
||||
admin.site.register(Attribute)
|
||||
|
||||
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(max_length=255)
|
||||
ansi_name = models.CharField(max_length=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=defines_global.OBJECT_TYPES)
|
||||
# TODO: Move description to an attribute.
|
||||
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)
|
||||
# 'scriptlink' is a 'get' property for retrieving a reference to the correct
|
||||
# script object. Defined down by get_scriptlink()
|
||||
scriptlink_cached = None
|
||||
|
||||
objects = ObjectManager()
|
||||
|
||||
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(no_ansi=True),)
|
||||
|
||||
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 get_session(self):
|
||||
"""
|
||||
Returns the session object for a player, or None if none exists.
|
||||
"""
|
||||
if self.is_player():
|
||||
return session_mgr.session_from_object(self)
|
||||
else:
|
||||
return None
|
||||
|
||||
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 = self.get_session()
|
||||
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 user_has_perm(self, perm):
|
||||
"""
|
||||
Checks to see whether a user has the specified permission or is a super
|
||||
user.
|
||||
|
||||
perm: (string) A string representing the desired permission.
|
||||
"""
|
||||
if not self.is_player():
|
||||
return False
|
||||
|
||||
if self.is_superuser():
|
||||
return True
|
||||
|
||||
if self.get_user_account().has_perm(perm):
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def user_has_perm_list(self, perm_list):
|
||||
"""
|
||||
Checks to see whether a user has the specified permission or is a super
|
||||
user. This form accepts an iterable of strings representing permissions,
|
||||
if the user has any of them return true.
|
||||
|
||||
perm: (iterable) An iterable of strings of permissions.
|
||||
"""
|
||||
if not self.is_player():
|
||||
return False
|
||||
|
||||
if self.is_superuser():
|
||||
return True
|
||||
|
||||
for perm in perm_list:
|
||||
# Stop searching perms on the first match.
|
||||
if self.get_user_account().has_perm(perm):
|
||||
return True
|
||||
|
||||
# Fall through to failure
|
||||
return False
|
||||
|
||||
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
|
||||
|
||||
# They've failed to meet any of the above conditions.
|
||||
return False
|
||||
|
||||
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 = self.get_user_account()
|
||||
pobject.username = new_name
|
||||
pobject.save()
|
||||
|
||||
def get_user_account(self):
|
||||
"""
|
||||
Returns the player object's account object (User object).
|
||||
"""
|
||||
if not self.is_player():
|
||||
return False
|
||||
return User.objects.get(id=self.id)
|
||||
|
||||
def get_name(self, fullname=False, show_dbref=True, no_ansi=False):
|
||||
"""
|
||||
Returns an object's name.
|
||||
"""
|
||||
if not no_ansi and self.ansi_name:
|
||||
name_string = self.ansi_name
|
||||
else:
|
||||
name_string = self.name
|
||||
|
||||
if show_dbref:
|
||||
dbref_string = "(#%s%s)" % (self.id, self.flag_string())
|
||||
else:
|
||||
dbref_string = ""
|
||||
|
||||
if fullname:
|
||||
return "%s%s" % (ansi.parse_ansi(name_string, strip_ansi=no_ansi), dbref_string)
|
||||
else:
|
||||
return "%s%s" % (ansi.parse_ansi(name_string.split(';')[0], strip_ansi=no_ansi), dbref_string)
|
||||
|
||||
def set_description(self, new_desc):
|
||||
"""
|
||||
Set an objects description
|
||||
"""
|
||||
if new_desc == '':
|
||||
self.description = None
|
||||
else:
|
||||
self.description = new_desc
|
||||
self.save()
|
||||
|
||||
def get_description(self, no_parsing=False, wrap_text=False):
|
||||
"""
|
||||
Returns an object's ANSI'd description or None if description is not
|
||||
set.
|
||||
"""
|
||||
try:
|
||||
# If no description is set, return None
|
||||
if self.description is None:
|
||||
return None
|
||||
# Evaluate ANSI and stuff?
|
||||
if no_parsing:
|
||||
retval = self.description
|
||||
else:
|
||||
retval = ansi.parse_ansi(self.description)
|
||||
|
||||
# Default to a 78 character wrap.
|
||||
if wrap_text:
|
||||
return functions_general.word_wrap(retval)
|
||||
else:
|
||||
return retval
|
||||
except:
|
||||
# No description attribute present, return empty string.
|
||||
return None
|
||||
|
||||
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 [attr for attr in self.attribute_set.all() if not attr.is_hidden()]
|
||||
|
||||
def clear_all_attributes(self):
|
||||
"""
|
||||
Clears all of an object's attributes.
|
||||
"""
|
||||
attribs = self.get_all_attributes()
|
||||
for attrib in attribs:
|
||||
attrib.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 = self.get_session()
|
||||
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.log_errmsg('Destroying object %s but no matching player.'
|
||||
% (self,))
|
||||
|
||||
# Set the object type to GOING
|
||||
self.type = 5
|
||||
# Destroy any exits to and from this room, do this first
|
||||
self.clear_exits()
|
||||
# Clear out any objects located within the object
|
||||
self.clear_objects()
|
||||
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()
|
||||
|
||||
# Clear all attributes
|
||||
self.clear_all_attributes()
|
||||
|
||||
def clear_exits(self):
|
||||
"""
|
||||
Destroys all of the exits and any exits pointing to this
|
||||
object as a destination.
|
||||
"""
|
||||
exits = self.get_contents(filter_type=4)
|
||||
exits += self.obj_home.all().filter(type__exact=4)
|
||||
|
||||
for exit in exits:
|
||||
exit.destroy()
|
||||
|
||||
def clear_objects(self):
|
||||
"""
|
||||
Moves all objects (players/things) currently in a GOING -> GARBAGE location
|
||||
to their home or default home (if it can be found).
|
||||
"""
|
||||
# Gather up everything, other than exits and going/garbage, that is under
|
||||
# the belief this is its location.
|
||||
objs = self.obj_location.filter(type__in=[1,2,3])
|
||||
default_home_id = ConfigValue.objects.get_configvalue('default_home')
|
||||
try:
|
||||
default_home = Object.objects.get(id=default_home_id)
|
||||
except:
|
||||
functions_general.log_errmsg("Could not find default home '(#%d)'." % (default_home_id))
|
||||
|
||||
for obj in objs:
|
||||
home = obj.get_home()
|
||||
text = "object"
|
||||
|
||||
if obj.is_player():
|
||||
text = "player"
|
||||
|
||||
# Obviously, we can't send it back to here.
|
||||
if home.id == self.id:
|
||||
obj.home = default_home
|
||||
obj.save()
|
||||
home = default_home
|
||||
|
||||
# If for some reason it's still None...
|
||||
if not home:
|
||||
functions_general.log_errmsg("Missing default home, %s '%s(#%d)' now has a null location." % (text, obj.name, obj.id))
|
||||
|
||||
if obj.is_player():
|
||||
if obj.is_connected_plr():
|
||||
if home:
|
||||
obj.emit_to("Your current location has ceased to exist, moving you to your home %s(#%d)." % (home.name, home.id))
|
||||
else:
|
||||
# Famous last words: The player should never see this.
|
||||
obj.emit_to("You seem to have found a place that does not exist.")
|
||||
|
||||
# If home is still None, it goes to a null location.
|
||||
obj.move_to(home)
|
||||
obj.save()
|
||||
|
||||
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(attr_object=self).filter(attr_name__iexact=attribute)[0]
|
||||
attrib_obj.attr_value = new_value
|
||||
attrib_obj.save()
|
||||
else:
|
||||
# Attribute object doesn't exist, create it.
|
||||
new_attrib = Attribute()
|
||||
new_attrib.attr_name = attribute
|
||||
new_attrib.attr_value = new_value
|
||||
new_attrib.attr_object = self
|
||||
new_attrib.attr_hidden = False
|
||||
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(attr_object=self).filter(attr_name__iexact=attribute)
|
||||
if attr.count() == 0:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def attribute_namesearch(self, searchstr, exclude_noset=False):
|
||||
"""
|
||||
Searches the object's attributes for name matches against searchstr
|
||||
via regular expressions. Returns a list.
|
||||
|
||||
searchstr: (str) A string (maybe with wildcards) to search for.
|
||||
"""
|
||||
# Retrieve the list of attributes for this object.
|
||||
attrs = Attribute.objects.filter(attr_object=self)
|
||||
# Compile a regular expression that is converted from the user's
|
||||
# wild-carded search string.
|
||||
match_exp = re.compile(functions_general.wildcard_to_regexp(searchstr),
|
||||
re.IGNORECASE)
|
||||
# If the regular expression search returns a match object, add to results.
|
||||
if exclude_noset:
|
||||
return [attr for attr in attrs if match_exp.search(attr.get_name()) and not attr.is_hidden() and not attr.is_noset()]
|
||||
else:
|
||||
return [attr for attr in attrs if match_exp.search(attr.get_name()) and not attr.is_hidden()]
|
||||
|
||||
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 src.flags.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 src.flags.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 self.get_session():
|
||||
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:
|
||||
functions_general.log_errmsg("Object '%s(#%d)' has invalid location: #%s" % (self.name,self.id,self.location_id))
|
||||
return False
|
||||
|
||||
def get_scriptlink(self):
|
||||
"""
|
||||
Returns an object's script parent.
|
||||
"""
|
||||
if not self.scriptlink_cached:
|
||||
if self.is_player():
|
||||
script_to_load = 'player.basicplayer'
|
||||
else:
|
||||
script_to_load = 'basicobject'
|
||||
self.scriptlink_cached = scripthandler.scriptlink(self, self.get_attribute_value('__parent', script_to_load))
|
||||
|
||||
if self.scriptlink_cached:
|
||||
# If the scriptlink variable can't be populated, this will fail
|
||||
# silently and let the exception hit in the scripthandler.
|
||||
return self.scriptlink_cached
|
||||
return None
|
||||
scriptlink = property(fget=get_scriptlink)
|
||||
|
||||
def get_attribute_value(self, attrib, default=False):
|
||||
"""
|
||||
Returns the value of an attribute on an object. You may need to
|
||||
type cast the returned value from this function!
|
||||
|
||||
attrib: (str) The attribute's name.
|
||||
"""
|
||||
if self.has_attribute(attrib):
|
||||
attrib = Attribute.objects.filter(attr_object=self).filter(attr_name=attrib)
|
||||
return attrib[0].attr_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):
|
||||
return Attribute.objects.filter(attr_object=self).filter(attr_name=attrib)
|
||||
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, force_look=True):
|
||||
"""
|
||||
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.
|
||||
force_look: (bool) If true and target is a player, make them 'look'.
|
||||
"""
|
||||
if not quiet:
|
||||
location = self.get_location()
|
||||
if location:
|
||||
location.emit_to_contents("%s has left." %
|
||||
(self.get_name(),), exclude=self)
|
||||
if location.is_player():
|
||||
location.emit_to("%s has left your inventory." %
|
||||
(self.get_name()))
|
||||
|
||||
self.location = target
|
||||
self.save()
|
||||
|
||||
if not quiet:
|
||||
arrival_message = "%s has arrived." % (self.get_name())
|
||||
self.get_location().emit_to_contents(arrival_message, exclude=self)
|
||||
if self.location.is_player():
|
||||
self.location.emit_to("%s is now in your inventory." % (self.get_name()))
|
||||
|
||||
if force_look and self.is_player():
|
||||
self.get_session().execute_cmd('look')
|
||||
|
||||
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 util_object.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, match_type="fuzzy"):
|
||||
"""
|
||||
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 util_object.is_dbref(oname):
|
||||
# First character is a pound sign, looks to be a dbref.
|
||||
return self.dbref_match(oname)
|
||||
elif match_type == "exact":
|
||||
# Exact matching
|
||||
name_chunks = self.name.lower().split(';')
|
||||
for chunk in name_chunks:
|
||||
if oname.lower() == chunk:
|
||||
return True
|
||||
return False
|
||||
else:
|
||||
# Fuzzy matching.
|
||||
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 == defines_global.OTYPE_PLAYER
|
||||
def is_room(self):
|
||||
return self.type == defines_global.OTYPE_ROOM
|
||||
def is_thing(self):
|
||||
return self.type == defines_global.OTYPE_THING
|
||||
def is_exit(self):
|
||||
return self.type == defines_global.OTYPE_EXIT
|
||||
def is_going(self):
|
||||
return self.type == defines_global.OTYPE_GOING
|
||||
def is_garbage(self):
|
||||
return self.type == defines_global.OTYPE_GARBAGE
|
||||
|
||||
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 defines_global.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 defines_global.OBJECT_TYPES[otype][1][0]
|
||||
admin.site.register(Object)
|
||||
|
||||
class CommChannel(models.Model):
|
||||
"""
|
||||
The CommChannel class represents a comsys channel in the vein of MUX/MUSH.
|
||||
"""
|
||||
name = models.CharField(max_length=255)
|
||||
ansi_name = models.CharField(max_length=255)
|
||||
owner = models.ForeignKey(Object, related_name="chan_owner")
|
||||
description = models.CharField(max_length=80)
|
||||
req_grp = models.ManyToManyField(Group, blank=True, null=True)
|
||||
|
||||
objects = CommChannelManager()
|
||||
|
||||
def __str__(self):
|
||||
return "%s" % (self.name,)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-name']
|
||||
permissions = (
|
||||
("emit_commchannel", "May @cemit over channels."),
|
||||
)
|
||||
|
||||
class Admin:
|
||||
list_display = ('name', 'owner')
|
||||
|
||||
def get_name(self):
|
||||
"""
|
||||
Returns a channel's name.
|
||||
"""
|
||||
return self.name
|
||||
|
||||
def get_header(self):
|
||||
"""
|
||||
Returns the channel's header text, or what is shown before each channel
|
||||
message.
|
||||
"""
|
||||
return ansi.parse_ansi(self.ansi_name)
|
||||
|
||||
def get_owner(self):
|
||||
"""
|
||||
Returns a channels' owner.
|
||||
"""
|
||||
return self.owner
|
||||
|
||||
def set_name(self, new_name):
|
||||
"""
|
||||
Rename a channel
|
||||
"""
|
||||
self.name = ansi.parse_ansi(new_name, strip_ansi=True)
|
||||
self.header = "[%s]" % (ansi.parse_ansi(new_name),)
|
||||
self.save()
|
||||
|
||||
def set_header(self, new_header):
|
||||
"""
|
||||
Sets a channel's header text.
|
||||
"""
|
||||
self.header = ansi.parse_ansi(new_header)
|
||||
self.save()
|
||||
|
||||
def set_owner(self, new_owner):
|
||||
"""
|
||||
Sets a channel's owner.
|
||||
"""
|
||||
self.owner = new_owner
|
||||
self.save()
|
||||
admin.site.register(CommChannel)
|
||||
|
||||
class CommChannelMessage(models.Model):
|
||||
"""
|
||||
A single logged channel message.
|
||||
"""
|
||||
channel = models.ForeignKey(CommChannel, related_name="msg_channel")
|
||||
message = models.CharField(max_length=255)
|
||||
date_sent = models.DateTimeField(editable=False, auto_now_add=True)
|
||||
|
||||
objects = CommChannelManager()
|
||||
|
||||
def __str__(self):
|
||||
return "%s: %s" % (self.sender.name, self.message)
|
||||
|
||||
class Meta:
|
||||
ordering = ['-date_sent']
|
||||
|
||||
class Admin:
|
||||
list_display = ('channel', 'message')
|
||||
admin.site.register(CommChannelMessage)
|
||||
5
src/objects/sql/object.sql
Normal file
5
src/objects/sql/object.sql
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
INSERT INTO objects_object VALUES(1,'Wizard','Wizard',1,0,2,1,'',2,'','CONNECTED','2007-04-25');
|
||||
INSERT INTO objects_object VALUES(2,'Limbo','Limbo',1,NULL,NULL,2,'Welcome to your new Evennia-based game. From here you are ready to begin development. If you should need help or would like to participate in community discussions, visit http://evennia.com.',NULL,'','','2007-04-25');
|
||||
INSERT INTO objects_commchannel VALUES(1,'Public','Public',1,'Public Discussion');
|
||||
INSERT INTO objects_commchannel VALUES(2,'Errors','Errors',1,'Error Log');
|
||||
INSERT INTO objects_commchannel VALUES(3,'Info','Info',1,'Info Log');
|
||||
0
src/objects/util/__init__.py
Normal file
0
src/objects/util/__init__.py
Normal file
21
src/objects/util/object.py
Normal file
21
src/objects/util/object.py
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
"""
|
||||
Utility functions for the Object class. These functions should not import
|
||||
any models or modify the database.
|
||||
"""
|
||||
def is_dbref(dbstring):
|
||||
"""
|
||||
Is the input a well-formed dbref number?
|
||||
"""
|
||||
try:
|
||||
number = int(dbstring[1:])
|
||||
except ValueError:
|
||||
return False
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
if not dbstring.startswith("#"):
|
||||
return False
|
||||
elif number < 1:
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
1
src/objects/views.py
Executable file
1
src/objects/views.py
Executable file
|
|
@ -0,0 +1 @@
|
|||
# Create your views here.
|
||||
Loading…
Add table
Add a link
Reference in a new issue