mirror of
https://github.com/evennia/evennia.git
synced 2026-03-26 17:56:32 +01:00
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:
parent
ce2a8e9ffe
commit
28fe2ad3f4
37 changed files with 1622 additions and 555 deletions
|
|
@ -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)
|
||||
|
|
|
|||
124
src/objects/migrations/0004_rename_nick_to_objectnick.py
Normal file
124
src/objects/migrations/0004_rename_nick_to_objectnick.py
Normal 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']
|
||||
121
src/objects/migrations/0005_add_object_default_locks.py
Normal file
121
src/objects/migrations/0005_add_object_default_locks.py
Normal 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']
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
@ -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
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue