Run Migrate. Implemented a full separation between Player and Character - Players (OOC entities) can now also hold cmdsets and execute commands. This means that "disconnecting" from a Character becomes possible, putting the Player in an "OOC" state outside the game. This overall makes the game much more stable since there used to be issues if the character was destroyed. Having an OOC set also avoids the previous problem of @puppeting into an object that didn't have any cmdset of its own - you couldn't get back out! A new default OOC-Cmdset handles commands available to a player while OOC. Commands in this set are applied with a low priority, allowing "IC" mode to give precedence if desired.

This change meant several changes to the lock and permission functionality, since it becomes important if permissions are assigned on the Player or on their Character (lock functions pperm() and pid() etc check on Player rather than Character). This has the boon of allowing Admins to switch and play/test the game as a "Low access" character as they like.

Plenty of bug fixes and adjustments. Migrations should make sure to move over all data properly.
This commit is contained in:
Griatch 2011-04-23 11:54:08 +00:00
parent ce2a8e9ffe
commit 28fe2ad3f4
37 changed files with 1622 additions and 555 deletions

View file

@ -9,11 +9,8 @@ from src.typeclasses.managers import returns_typeclass, returns_typeclass_list
from src.utils import utils
# Try to use a custom way to parse id-tagged multimatches.
IDPARSER_PATH = getattr(settings, 'ALTERNATE_OBJECT_SEARCH_MULTIMATCH_PARSER', 'src.objects.object_search_funcs')
if not IDPARSER_PATH:
# can happen if variable is set to "" in settings
IDPARSER_PATH = 'src.objects.object_search_funcs'
exec("from %s import object_multimatch_parser as IDPARSER" % IDPARSER_PATH)
AT_MULTIMATCH_INPUT = utils.mod_import(*settings.SEARCH_AT_MULTIMATCH_INPUT.rsplit('.', 1))
class ObjectManager(TypedObjectManager):
"""
@ -172,7 +169,10 @@ class ObjectManager(TypedObjectManager):
global_search=False,
attribute_name=None, location=None):
"""
Search as an object and return results.
Search as an object and return results. The result is always an Object.
If * is appended (player search, a Character controlled by this Player
is looked for. The Character is returned, not the Player. Use player_search
to find Player objects.
character: (Object) The object performing the search.
ostring: (string) The string to compare names against.
@ -187,7 +187,7 @@ class ObjectManager(TypedObjectManager):
if not ostring or not character:
return None
if not location:
if not location and hasattr(character, "location"):
location = character.location
# Easiest case - dbref matching (always exact)
@ -204,16 +204,17 @@ class ObjectManager(TypedObjectManager):
if character and ostring in ['me', 'self']:
return [character]
if character and ostring in ['*me', '*self']:
return [character.player]
return [character]
# Test if we are looking for a player object
# Test if we are looking for an object controlled by a
# specific player
if utils.to_unicode(ostring).startswith("*"):
# Player search - try to find obj by its player's name
player_match = self.get_object_with_player(ostring)
if player_match is not None:
return [player_match.player]
return [player_match]
# Search for keys, aliases or other attributes
search_locations = [None] # this means a global search
@ -246,7 +247,7 @@ class ObjectManager(TypedObjectManager):
matches = local_and_global_search(ostring, exact=True)
if not matches:
# if we have no match, check if we are dealing with an "N-keyword" query - if so, strip it.
match_number, ostring = IDPARSER(ostring)
match_number, ostring = AT_MULTIMATCH_INPUT(ostring)
if match_number != None and ostring:
# Run search again, without match number:
matches = local_and_global_search(ostring, exact=True)

View file

@ -0,0 +1,124 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models, utils
class Migration(SchemaMigration):
def forwards(self, orm):
try:
# if we migrate, we just rename the table. This will move over all values too.
db.rename_table("objects_nick", "objects_objectnick")
except utils.DatabaseError:
# this happens if we start from scratch. In that case the old
# database table doesn't exist, so we just create the new one.
# Adding model 'ObjectNick'
db.create_table('objects_objectnick', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('db_nick', self.gf('django.db.models.fields.CharField')(max_length=255, db_index=True)),
('db_real', self.gf('django.db.models.fields.TextField')()),
('db_type', self.gf('django.db.models.fields.CharField')(default='inputline', max_length=16, null=True, blank=True)),
('db_obj', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['objects.ObjectDB'])),
))
db.send_create_signal('objects', ['ObjectNick'])
# Adding unique constraint on 'ObjectNick', fields ['db_nick', 'db_type', 'db_obj']
db.create_unique('objects_objectnick', ['db_nick', 'db_type', 'db_obj_id'])
def backwards(self, orm):
raise RuntimeError("This migration cannot be reversed.")
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.alias': {
'Meta': {'object_name': 'Alias'},
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objattribute': {
'Meta': {'object_name': 'ObjAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectnick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'ObjectNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['objects']

View file

@ -0,0 +1,121 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models, utils
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
# we need to add a default lock string to all objects, then a separate set to Characters.
lockstring1 = 'control:id(1);get:all();edit:perm(Wizards);examine:perm(Builders);call:true();puppet:id(#4) or perm(Immortals) or pperm(Immortals);delete:id(1) or perm(Wizards)'
lockstring2 = 'control:id(#3) or perm(Immortals);get:perm(Wizards);edit:perm(Wizards);examine:perm(Builders);call:false();puppet:id(%i) or pid(%i) or perm(Immortals) or pperm(Immortals);delete:perm(Wizards)'
try:
for obj in orm.ObjectDB.objects.all().exclude(db_player__isnull=False):
obj.db_lock_storage = lockstring1
obj.save()
for obj in orm.ObjectDB.objects.filter(db_player__isnull=False):
obj.db_lock_storage = lockstring2 % (obj.id, obj.db_player.id)
obj.save()
except utils.DatabaseError:
# running from scatch. In this case we just ignore this.
pass
def backwards(self, orm):
"Write your backwards methods here."
raise RuntimeError("You cannot reverse this migration.")
models = {
'auth.group': {
'Meta': {'object_name': 'Group'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
'auth.permission': {
'Meta': {'ordering': "('content_type__app_label', 'content_type__model', 'codename')", 'unique_together': "(('content_type', 'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['contenttypes.ContentType']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'objects.alias': {
'Meta': {'object_name': 'Alias'},
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objattribute': {
'Meta': {'object_name': 'ObjAttribute'},
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_value': ('django.db.models.fields.TextField', [], {'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectdb': {
'Meta': {'object_name': 'ObjectDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_destination': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'destinations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_home': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'homes_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_location': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'locations_set'", 'null': 'True', 'to': "orm['objects.ObjectDB']"}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_player': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['players.PlayerDB']", 'null': 'True', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'objects.objectnick': {
'Meta': {'unique_together': "(('db_nick', 'db_type', 'db_obj'),)", 'object_name': 'ObjectNick'},
'db_nick': ('django.db.models.fields.CharField', [], {'max_length': '255', 'db_index': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']"}),
'db_real': ('django.db.models.fields.TextField', [], {}),
'db_type': ('django.db.models.fields.CharField', [], {'default': "'inputline'", 'max_length': '16', 'null': 'True', 'blank': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'})
},
'players.playerdb': {
'Meta': {'object_name': 'PlayerDB'},
'db_cmdset_storage': ('django.db.models.fields.TextField', [], {'null': 'True'}),
'db_date_created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'db_key': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'db_lock_storage': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'db_obj': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['objects.ObjectDB']", 'null': 'True'}),
'db_permissions': ('django.db.models.fields.CharField', [], {'max_length': '512', 'blank': 'True'}),
'db_typeclass_path': ('django.db.models.fields.CharField', [], {'max_length': '255', 'null': 'True'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['auth.User']", 'unique': 'True'})
}
}
complete_apps = ['objects']

View file

@ -16,25 +16,24 @@ transparently through the decorating TypeClass.
from django.db import models
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from src.utils.idmapper.models import SharedMemoryModel
from src.typeclasses.models import Attribute, TypedObject
from src.typeclasses.models import Attribute, TypedObject, TypeNick, TypeNickHandler
from src.typeclasses.typeclass import TypeClass
from src.objects.manager import ObjectManager
from src.players.models import PlayerDB
from src.server.models import ServerConfig
from src.commands.cmdsethandler import CmdSetHandler
from src.commands import cmdhandler
from src.scripts.scripthandler import ScriptHandler
from src.utils import logger
from src.utils.utils import is_iter, to_unicode
from src.utils.utils import is_iter, to_unicode, to_str, mod_import
#PlayerDB = ContentType.objects.get(app_label="players", model="playerdb").model_class()
FULL_PERSISTENCE = settings.FULL_PERSISTENCE
try:
HANDLE_SEARCH_ERRORS = __import__(
settings.ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER).handle_search_errors, fromlist=[None]
except Exception:
from src.objects.object_search_funcs \
import handle_search_errors as HANDLE_SEARCH_ERRORS
AT_SEARCH_RESULT = mod_import(*settings.SEARCH_AT_RESULT.rsplit('.', 1))
#------------------------------------------------------------
#
@ -80,80 +79,33 @@ class Alias(SharedMemoryModel):
#------------------------------------------------------------
#
# Nick
# Object Nicks
#
#------------------------------------------------------------
class Nick(SharedMemoryModel):
class ObjectNick(TypeNick):
"""
This model holds whichever alternate names this object
has for OTHER objects, but also for arbitrary strings,
channels, players etc. Setting a nick does not affect
the nicknamed object at all (as opposed to Aliases above),
and only this object will be able to refer to the nicknamed
object by the given nick.
The default nick types used by Evennia are:
inputline (default) - match against all input
player - match against player searches
obj - match against object searches
channel - used to store own names for channels
"""
db_nick = models.CharField(max_length=255, db_index=True) # the nick
db_real = models.TextField() # the aliased string
db_type = models.CharField(default="inputline", max_length=16, null=True, blank=True) # the type of nick
db_obj = models.ForeignKey("ObjectDB")
class Meta:
"Define Django meta options"
verbose_name = "Nickname"
verbose_name_plural = "Nicknames"
verbose_name = "Nickname for Objects"
verbose_name_plural = "Nicknames Objects"
unique_together = ("db_nick", "db_type", "db_obj")
class NickHandler(object):
class ObjectNickHandler(TypeNickHandler):
"""
Handles nick access and setting. Accessed through ObjectDB.nicks
"""
NickClass = ObjectNick
def __init__(self, obj):
"Setup"
self.obj = obj
def add(self, nick, realname, nick_type="inputline"):
"We want to assign a new nick"
if not nick or not nick.strip():
return
nick = nick.strip()
real = realname.strip()
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
if query.count():
old_nick = query[0]
old_nick.db_real = real
old_nick.save()
else:
new_nick = Nick(db_nick=nick, db_real=real, db_type=nick_type, db_obj=self.obj)
new_nick.save()
def delete(self, nick, nick_type="inputline"):
"Removes a nick"
nick = nick.strip()
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
if query.count():
# remove the found nick(s)
query.delete()
def get(self, nick=None, nick_type="inputline"):
if nick:
query = Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type)
query = query.values_list("db_real", flat=True)
if query.count():
return query[0]
else:
return nick
else:
return Nick.objects.filter(db_obj=self.obj)
def has(self, nick, nick_type="inputline"):
"Returns true/false if this nick is defined or not"
return Nick.objects.filter(db_obj=self.obj, db_nick__iexact=nick, db_type__iexact=nick_type).count()
#------------------------------------------------------------
#
@ -239,7 +191,7 @@ class ObjectDB(TypedObject):
self.cmdset.update(init_mode=True)
self.scripts = ScriptHandler(self)
self.scripts.validate(init_mode=True)
self.nicks = NickHandler(self)
self.nicks = ObjectNickHandler(self)
# Wrapper properties to easily set database fields. These are
# @property decorators that allows to access these fields using
@ -312,7 +264,7 @@ class ObjectDB(TypedObject):
loc = location
elif ObjectDB.objects.dbref(location):
# location is a dbref; search
loc = ObjectDB.objects.dbref_search(location)
loc = ObjectDB.objects.dbref_search(ocation)
if loc and hasattr(loc,'dbobj'):
loc = loc.dbobj
else:
@ -524,14 +476,11 @@ class ObjectDB(TypedObject):
global_search=False,
attribute_name=None,
use_nicks=False, location=None,
ignore_errors=False):
ignore_errors=False, player=False):
"""
Perform a standard object search in the database, handling
multiple results and lack thereof gracefully.
if local_only AND search_self are both false, a global
search is done instead.
ostring: (str) The string to match object names against.
Obs - To find a player, append * to the
start of ostring.
@ -544,7 +493,16 @@ class ObjectDB(TypedObject):
ignore_errors : Don't display any error messages even
if there are none/multiple matches -
just return the result as a list.
player : Don't search for an Object but a Player.
This will also find players that don't
currently have a character.
Use *<string> to search for objects controlled by a specific
player. Note that the object controlled by the player will be
returned, not the player object itself. This also means that
this will not find Players without a character. Use the keyword
player=True to find player objects.
Note - for multiple matches, the engine accepts a number
linked to the key in order to separate the matches from
each other without showing the dbref explicitly. Default
@ -554,22 +512,30 @@ class ObjectDB(TypedObject):
etc.
"""
if use_nicks:
if ostring.startswith('*'):
if ostring.startswith('*') or player:
# player nick replace
ostring = "*%s" % self.nicks.get(ostring.lstrip('*'), nick_type="player")
ostring = self.nicks.get(ostring.lstrip('*'), nick_type="player")
if not player:
ostring = "*%s" % ostring
else:
# object nick replace
ostring = self.nicks.get(ostring, nick_type="object")
results = ObjectDB.objects.object_search(self, ostring,
global_search=global_search,
attribute_name=attribute_name,
location=location)
if player:
if ostring in ("me", "self", "*me", "*self"):
results = [self.player]
else:
results = PlayerDB.objects.player_search(ostring.lstrip('*'))
else:
results = ObjectDB.objects.object_search(self, ostring,
global_search=global_search,
attribute_name=attribute_name,
location=location)
if ignore_errors:
return results
return HANDLE_SEARCH_ERRORS(self, ostring, results, global_search)
# this import is cache after the first call.
return AT_SEARCH_RESULT(self, ostring, results, global_search)
#
# Execution/action methods
@ -588,7 +554,7 @@ class ObjectDB(TypedObject):
raw_list = raw_string.split(None)
raw_list = [" ".join(raw_list[:i+1]) for i in range(len(raw_list)) if raw_list[:i+1]]
for nick in Nick.objects.filter(db_obj=self, db_type__in=("inputline","channel")):
for nick in ObjectNick.objects.filter(db_obj=self, db_type__in=("inputline","channel")):
if nick.db_nick in raw_list:
raw_string = raw_string.replace(nick.db_nick, nick.db_real, 1)
break
@ -605,7 +571,7 @@ class ObjectDB(TypedObject):
"""
# This is an important function that must always work.
# we use a different __getattribute__ to avoid recursive loops.
if object.__getattribute__(self, 'player'):
object.__getattribute__(self, 'player').msg(message, from_obj, data)
@ -796,9 +762,15 @@ class ObjectDB(TypedObject):
return False
# See if we need to kick the player off.
for session in self.sessions:
session.msg("Your character %s has been destroyed. Goodbye." % self.name)
session.session_disconnect()
session.msg("Your character %s has been destroyed." % self.name)
#session.session_disconnect()
# sever the connection (important!)
if object.__getattribute__(self, 'player') and self.player:
self.player.character = None
self.player = None
# if self.player:
# self.player.user.is_active = False
@ -811,6 +783,3 @@ class ObjectDB(TypedObject):
# Perform the deletion of the object
super(ObjectDB, self).delete()
return True
# Deferred import to avoid circular import errors.
from src.commands import cmdhandler

View file

@ -1,102 +0,0 @@
"""
Default functions for formatting and processing object searches.
This is in its own module due to them being possible to
replace from the settings file by use of setting the variables
ALTERNATE_OBJECT_SEARCH_ERROR_HANDLER
ALTERNATE_OBJECT_SEARCH_MULTIMATCH_PARSER
Both the replacing functions must have the same name and same input/output
as the ones in this module.
"""
def handle_search_errors(emit_to_obj, ostring, results, global_search=False):
"""
Takes a search result (a list) and
formats eventual errors.
emit_to_obj - object to receive feedback.
ostring - original search string
results - list of object matches, if any
global_search - if this was a global_search or not
(if it is, there might be an idea of supplying
dbrefs instead of only numbers)
"""
if not results:
emit_to_obj.msg("Could not find '%s'." % ostring)
return None
if len(results) > 1:
# we have more than one match. We will display a
# list of the form 1-objname, 2-objname etc.
# check if the emit_to_object may se dbrefs
show_dbref = global_search and \
emit_to_obj.check_permstring('Builders')
string = "More than one match for '%s'" % ostring
string += " (please narrow target):"
for num, result in enumerate(results):
invtext = ""
dbreftext = ""
if result.location == emit_to_obj:
invtext = " (carried)"
if show_dbref:
dbreftext = "(#%i)" % result.id
string += "\n %i-%s%s%s" % (num+1, result.name,
dbreftext, invtext)
emit_to_obj.msg(string.strip())
return None
else:
return results[0]
def object_multimatch_parser(ostring):
"""
Parse number-identifiers.
Sometimes it can happen that there are several objects in the room
all with exactly the same key/identifier. Showing dbrefs to
separate them is not suitable for all types of games since it's
unique to that object (and e.g. in rp-games the object might not
want to be identified like that). Instead Evennia allows for
dbref-free matching by letting the user number which of the
objects in a multi-match they want.
Ex for use in game session:
> look
You see: ball, ball, ball and ball.
> get ball
There where multiple matches for ball:
1-ball
2-ball
3-ball
4-ball
> get 3-ball
You get the ball.
The actual feedback upon multiple matches has to be
handled by the searching command. The syntax shown above is the
default.
For replacing, the method must be named the same and
take the searchstring as argument and
return a tuple (int, string) where int is the identifier
matching which of the results (in order) should be used to
pick out the right match from the multimatch). Note
that the engine assumes this number to start with 1 (i.e. not
zero as in normal Python).
"""
if not isinstance(ostring, basestring):
return (None, ostring)
if not '-' in ostring:
return (None, ostring)
try:
index = ostring.find('-')
number = int(ostring[:index])-1
return (number, ostring[index+1:])
except ValueError:
#not a number; this is not an identifier.
return (None, ostring)
except IndexError:
return (None, ostring)

View file

@ -49,6 +49,9 @@ class Object(TypeClass):
"""
This sets up the default properties of an Object,
just before the more general at_object_creation.
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
"""
# the default security setup fallback for a generic
# object. Overload in child for a custom setup. Also creation
@ -63,6 +66,7 @@ class Object(TypeClass):
self.locks.add("delete:perm(Wizards)") # delete object
self.locks.add("get:all()") # pick up object
self.locks.add("call:true()") # allow to call commands on this object
self.locks.add("puppet:id(%s) or perm(Immortals) or pperm(Immortals)" % dbref) # restricts puppeting of this object
def at_object_creation(self):
"""
@ -310,9 +314,11 @@ class Character(Object):
def basetype_setup(self):
"""
Setup character-specific security
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
"""
super(Character, self).basetype_setup()
self.locks.add("puppet:id(%s) or perm(Immortals)" % self.dbobj.dbref) # who may become this object's player
self.locks.add("get:false()") # noone can pick up the character
self.locks.add("call:false()") # no commands can be called on character
@ -348,9 +354,13 @@ class Room(Object):
"""
Simple setup, shown as an example
(since default is None anyway)
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
"""
super(Room, self).basetype_setup()
self.locks.add("puppet:false()") # would be weird to puppet a room ...
self.locks.add("get:false()")
super(Room, self).basetype_setup()
@ -371,9 +381,13 @@ class Exit(Object):
def basetype_setup(self):
"""
Setup exit-security
Don't change this, instead edit at_object_creation() to
overload the defaults (it is called after this one).
"""
# the lock is open to all by default
super(Exit, self).basetype_setup()
self.locks.add("puppet:false()") # would be weird to puppet an exit ...
self.locks.add("traverse:all()") # who can pass through exit
self.locks.add("get:false()") # noone can pick up the exit