Migrate. Added the "view" access restriction (to make objects invisible). Also changed the input of ObjectDB.objects.object_search() to not require a caller as an argument (this makes it consistent with other search methods). All default systems should have updated to the new call, but if you have custom calls, you need to change them to fit the new syntax (this is only important if explicitly use ObjectDB.objects.object_search; if you just use caller.search you should be fine)

This commit is contained in:
Griatch 2011-04-24 11:26:51 +00:00
parent 28fe2ad3f4
commit 27809694d7
13 changed files with 267 additions and 34 deletions

View file

@ -115,8 +115,8 @@ def get_and_merge_cmdsets(caller):
location = caller.location location = caller.location
if location and not caller_cmdset.no_objs: if location and not caller_cmdset.no_objs:
# Gather all cmdsets stored on objects in the room and # Gather all cmdsets stored on objects in the room and
# also in the caller's inventory # also in the caller's inventory and the location itself
local_objlist = location.contents + caller.contents local_objlist = location.contents + caller.contents + [location]
local_objects_cmdsets = [obj.cmdset.current local_objects_cmdsets = [obj.cmdset.current
for obj in local_objlist for obj in local_objlist
if obj.locks.check(caller, 'call', no_superuser_bypass=True)] if obj.locks.check(caller, 'call', no_superuser_bypass=True)]

View file

@ -272,7 +272,7 @@ class CmdSetHandler(object):
self.permanent_paths.append("") self.permanent_paths.append("")
self.update() self.update()
def add_default(self, cmdset, emit_to_obj=None, permanent=False): def add_default(self, cmdset, emit_to_obj=None, permanent=True):
""" """
Add a new default cmdset. If an old default existed, Add a new default cmdset. If an old default existed,
it is replaced. If permanent is set, a script will be created to it is replaced. If permanent is set, a script will be created to

View file

@ -559,12 +559,19 @@ class CmdDestroy(MuxCommand):
if obj.player and not 'override' in self.switches: if obj.player and not 'override' in self.switches:
string += "\nObject %s is controlled by an active player. Use /override to delete anyway." % objname string += "\nObject %s is controlled by an active player. Use /override to delete anyway." % objname
continue continue
had_exits = hasattr(obj, "exits") and obj.exits
had_objs = hasattr(obj, "contents") and any(obj for obj in obj.contents
if not (hasattr(obj, "exits") and obj not in obj.exits))
# do the deletion # do the deletion
okay = obj.delete() okay = obj.delete()
if not okay: if not okay:
string += "\nERROR: %s not deleted, probably because at_obj_delete() returned False." % objname string += "\nERROR: %s not deleted, probably because at_obj_delete() returned False." % objname
else: else:
string += "\n%s was deleted." % objname string += "\n%s was destroyed." % objname
if had_exits:
string += " Exits to and from %s were destroyed as well." % objname
if had_objs:
string += " Objects inside %s were moved to their homes." % objname
if string: if string:
caller.msg(string.strip()) caller.msg(string.strip())

View file

@ -71,6 +71,9 @@ class CmdLook(MuxCommand):
if not hasattr(looking_at_obj, 'return_appearance'): if not hasattr(looking_at_obj, 'return_appearance'):
# this is likely due to us having a player instead # this is likely due to us having a player instead
looking_at_obj = looking_at_obj.character looking_at_obj = looking_at_obj.character
if not looking_at_obj.access(caller, "view"):
caller.msg("Could not find '%s'." % args)
return
# get object's appearance # get object's appearance
caller.msg(looking_at_obj.return_appearance(caller)) caller.msg(looking_at_obj.return_appearance(caller))
# the object's at_desc() method. # the object's at_desc() method.

View file

@ -197,10 +197,8 @@ class CmdScripts(MuxCommand):
scripts = ScriptDB.objects.get_all_scripts(key=args) scripts = ScriptDB.objects.get_all_scripts(key=args)
if not scripts: if not scripts:
# try to find an object instead. # try to find an object instead.
objects = ObjectDB.objects.object_search(caller, objects = ObjectDB.objects.object_search(args, caller=caller, global_search=True)
args, if objects:
global_search=True)
if objects:
scripts = [] scripts = []
for obj in objects: for obj in objects:
# get all scripts on the object(s) # get all scripts on the object(s)

View file

@ -226,9 +226,9 @@ def pid(accessing_obj, accessed_obj, *args, **kwargs):
def attr(accessing_obj, accessed_obj, *args, **kwargs): def attr(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
has_attr(attrname) attr(attrname)
has_attr(attrname, value) attr(attrname, value)
has_attr(attrname, value, compare=type) attr(attrname, value, compare=type)
where compare's type is one of (eq,gt,lt,ge,le,ne) and signifies where compare's type is one of (eq,gt,lt,ge,le,ne) and signifies
how the value should be compared with one on accessing_obj (so how the value should be compared with one on accessing_obj (so
@ -288,6 +288,37 @@ def attr(accessing_obj, accessed_obj, *args, **kwargs):
return True return True
return False return False
def objattr(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
objattr(attrname)
objattr(attrname, value)
objattr(attrname, value, compare=type)
Works like attr, except it looks for an attribute on
accessing_obj.obj, if such an entity exists. Suitable
for commands.
"""
if hasattr(accessing_obj, "obj"):
return attr(accessing_obj.obj, accessed_obj, *args, **kwargs)
def locattr(accessing_obj, accessed_obj, *args, **kwargs):
"""
Usage:
locattr(attrname)
locattr(attrname, value)
locattr(attrname, value, compare=type)
Works like attr, except it looks for an attribute on
accessing_obj.location, if such an entity exists. Suitable
for commands.
"""
if hasattr(accessing_obj, "location"):
return attr(accessing_obj.location, accessed_obj, *args, **kwargs)
def attr_eq(accessing_obj, accessed_obj, *args, **kwargs): def attr_eq(accessing_obj, accessed_obj, *args, **kwargs):
""" """
Usage: Usage:
@ -351,6 +382,28 @@ def holds(accessing_obj, accessed_obj, objid, *args, **kwargs):
objid = objid.lower() objid = objid.lower()
return any((True for obj in contains if obj.name.lower() == objid)) return any((True for obj in contains if obj.name.lower() == objid))
def carried(accessing_obj, accessed_obj):
"""
Usage:
carried()
This is passed if accessed_obj is carried by accessing_obj (that is,
accessed_obj.location == accessing_obj)
"""
return hasattr(accessed_obj, "location") and accessed_obj.location == accessing_obj
def objcarried(accessing_obj, accessed_obj):
"""
Usage:
objcarried()
Like carried, except this lock looks for a property "obj" on the accessed_obj
and tries to determing if *this* is carried by accessing_obj. This works well
for commands and scripts.
"""
return hasattr(accessed_obj, "obj") and accessed_obj.obj and \
hasattr(accessed_obj.obj, "location") and accessed_obj.obj.location == accessing_obj
def superuser(*args, **kwargs): def superuser(*args, **kwargs):
""" """
Only accepts an accesing_obj that is superuser (e.g. user #1) Only accepts an accesing_obj that is superuser (e.g. user #1)

View file

@ -11,15 +11,30 @@ class ExitCommand(command.Command):
is_exit = True is_exit = True
locks = "cmd:all()" # should always be set to this. locks = "cmd:all()" # should always be set to this.
destination = None destination = None
obj = None obj = None
def func(self): def func(self):
"Default exit traverse if no syscommand is defined." "Default exit traverse if no syscommand is defined."
if self.obj.access(self.caller, 'traverse'): if self.obj.access(self.caller, 'traverse'):
self.caller.move_to(self.destination) # we may traverse the exit.
old_location = None
if hasattr(self.caller, "location"):
old_location = self.caller.location
# call pre/post hooks and move object.
self.obj.at_before_traverse(self.caller)
self.caller.move_to(self.destination)
self.obj.at_after_traverse(self.caller, old_location)
else: else:
self.caller.msg("You cannot enter.") if self.obj.db.err_traverse:
# if exit has a better error message, let's use it.
self.caller.msg(self.obj.db.err_traverse)
else:
# No shorthand error message. Call hook.
self.obj.at_fail_traverse(self.caller)
class ExitHandler(object): class ExitHandler(object):
""" """

View file

@ -165,18 +165,18 @@ class ObjectManager(TypedObjectManager):
return eval("self.filter(db_location__id=location.id)%s" % estring) return eval("self.filter(db_location__id=location.id)%s" % estring)
@returns_typeclass_list @returns_typeclass_list
def object_search(self, character, ostring, def object_search(self, ostring, caller=None,
global_search=False, global_search=False,
attribute_name=None, location=None): attribute_name=None, location=None):
""" """
Search as an object and return results. The result is always an Object. Search as an object and return results. The result is always an Object.
If * is appended (player search, a Character controlled by this Player If * is appended (player search, a Character controlled by this Player
is looked for. The Character is returned, not the Player. Use player_search is looked for. The Character is returned, not the Player. Use player_search
to find Player objects. to find Player objects. Always returns a list.
character: (Object) The object performing the search.
ostring: (string) The string to compare names against. ostring: (string) The string to compare names against.
Can be a dbref. If name is appended by *, a player is searched for. Can be a dbref. If name is appended by *, a player is searched for.
caller: (Object) The object performing the search.
global_search: Search all objects, not just the current location/inventory global_search: Search all objects, not just the current location/inventory
attribute_name: (string) Which attribute to search in each object. attribute_name: (string) Which attribute to search in each object.
If None, the default 'key' attribute is used. If None, the default 'key' attribute is used.
@ -184,11 +184,8 @@ class ObjectManager(TypedObjectManager):
""" """
#ostring = str(ostring).strip() #ostring = str(ostring).strip()
if not ostring or not character: if not ostring:
return None return []
if not location and hasattr(character, "location"):
location = character.location
# Easiest case - dbref matching (always exact) # Easiest case - dbref matching (always exact)
dbref = self.dbref(ostring) dbref = self.dbref(ostring)
@ -197,14 +194,17 @@ class ObjectManager(TypedObjectManager):
if dbref_match: if dbref_match:
return [dbref_match] return [dbref_match]
if not location and caller and hasattr(caller, "location"):
location = caller.location
# Test some common self-references # Test some common self-references
if location and ostring == 'here': if location and ostring == 'here':
return [location] return [location]
if character and ostring in ['me', 'self']: if caller and ostring in ['me', 'self']:
return [character] return [caller]
if character and ostring in ['*me', '*self']: if caller and ostring in ['*me', '*self']:
return [character] return [caller]
# Test if we are looking for an object controlled by a # Test if we are looking for an object controlled by a
# specific player # specific player
@ -224,8 +224,10 @@ class ObjectManager(TypedObjectManager):
or ostring.lower() in [alias.lower() for alias in location.aliases]): or ostring.lower() in [alias.lower() for alias in location.aliases]):
return [location] return [location]
# otherwise, setup the locations to search in # otherwise, setup the locations to search in
search_locations = [character, location] search_locations = [location]
if caller:
search_locations.append(caller)
def local_and_global_search(ostring, exact=False): def local_and_global_search(ostring, exact=False):
"Helper method for searching objects" "Helper method for searching objects"
matches = [] matches = []

View file

@ -0,0 +1,119 @@
# encoding: utf-8
import datetime
from south.db import db
from south.v2 import DataMigration
from django.db import models
class Migration(DataMigration):
def forwards(self, orm):
"Write your forwards methods here."
lockstring1 = 'control:id(1);get:all();edit:perm(Wizards);view:all();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);view:all();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

@ -527,7 +527,7 @@ class ObjectDB(TypedObject):
else: else:
results = PlayerDB.objects.player_search(ostring.lstrip('*')) results = PlayerDB.objects.player_search(ostring.lstrip('*'))
else: else:
results = ObjectDB.objects.object_search(self, ostring, results = ObjectDB.objects.object_search(ostring, caller=self,
global_search=global_search, global_search=global_search,
attribute_name=attribute_name, attribute_name=attribute_name,
location=location) location=location)
@ -658,8 +658,8 @@ class ObjectDB(TypedObject):
except Exception: except Exception:
emit_to_obj.msg(errtxt % "at_announce_move()" ) emit_to_obj.msg(errtxt % "at_announce_move()" )
logger.log_trace() logger.log_trace()
return False return False
# Perform move # Perform move
try: try:
self.location = destination self.location = destination

View file

@ -62,6 +62,7 @@ class Object(TypeClass):
self.locks.add("control:id(%s) or perm(Immortals)" % dbref) # edit locks/permissions, delete self.locks.add("control:id(%s) or perm(Immortals)" % dbref) # edit locks/permissions, delete
self.locks.add("examine:perm(Builders)") # examine properties self.locks.add("examine:perm(Builders)") # examine properties
self.locks.add("view:all()") # look at object (visibility)
self.locks.add("edit:perm(Wizards)") # edit properties/attributes self.locks.add("edit:perm(Wizards)") # edit properties/attributes
self.locks.add("delete:perm(Wizards)") # delete object self.locks.add("delete:perm(Wizards)") # delete object
self.locks.add("get:all()") # pick up object self.locks.add("get:all()") # pick up object
@ -185,7 +186,33 @@ class Object(TypeClass):
source_location - where moved_object came from. source_location - where moved_object came from.
""" """
pass pass
def at_before_traverse(self, traversing_object):
"""
Called just before an object uses this object to
traverse to another object (i.e. this object is a type of Exit)
The target location should normally be available as self.destination.
"""
pass
def at_after_traverse(self, traversing_object, source_location):
"""
Called just after an object successfully used this object to
traverse to another object (i.e. this object is a type of Exit)
The target location should normally be available as self.destination.
"""
pass
def at_failed_traverse(self, traversing_object):
"""
This is called if an object fails to traverse this object for some
reason. It will not be called if the attribute err_traverse is defined,
that attribute will then be echoed back instead.
"""
pass
# hooks called by the default cmdset. # hooks called by the default cmdset.
@ -414,3 +441,12 @@ class Exit(Object):
""" """
EXITHANDLER.clear(self.dbobj) EXITHANDLER.clear(self.dbobj)
return True return True
def at_failed_traverse(self, traversing_object):
"""
This is called if an object fails to traverse this object for some
reason. It will not be called if the attribute "err_traverse" is defined,
that attribute will then be echoed back instead as a convenient shortcut.
"""
traversing_object.msg("You cannot enter %s." % self.key)

View file

@ -377,7 +377,7 @@ class PlayerDB(TypedObject):
else: else:
# more limited player-only search. Still returns an Object. # more limited player-only search. Still returns an Object.
ObjectDB = ContentType.objects.get(app_label="objects", model="objectdb").model_class() ObjectDB = ContentType.objects.get(app_label="objects", model="objectdb").model_class()
matches = ObjectDB.objects.object_search(self, ostring, global_search=global_search) matches = ObjectDB.objects.object_search(ostring, caller=self, global_search=global_search)
# deal with results # deal with results
matches = AT_SEARCH_RESULT(self, ostring, matches, global_search=global_search) matches = AT_SEARCH_RESULT(self, ostring, matches, global_search=global_search)
return matches return matches

View file

@ -44,15 +44,15 @@ HelpEntry = ContentType.objects.get(app_label="help", model="helpentry").model_c
# is reachable from within each command class # is reachable from within each command class
# by using self.caller.search()! # by using self.caller.search()!
# #
# def object_search(self, character, ostring, # def object_search(self, ostring, caller=None,
# global_search=False, # global_search=False,
# attribute_name=None): # attribute_name=None):
# """ # """
# Search as an object and return results. # Search as an object and return results.
# #
# character: (Object) The object performing the search.
# ostring: (string) The string to compare names against. # ostring: (string) The string to compare names against.
# Can be a dbref. If name is appended by *, a player is searched for. # Can be a dbref. If name is appended by *, a player is searched for.
# caller: (Object) The object performing the search.
# global_search: Search all objects, not just the current location/inventory # global_search: Search all objects, not just the current location/inventory
# attribute_name: (string) Which attribute to search in each object. # attribute_name: (string) Which attribute to search in each object.
# If None, the default 'name' attribute is used. # If None, the default 'name' attribute is used.