Merge from Kelketek's clone. Added fixes to migrations of Tags. Issues with ContentTypes meaning that this revision is not possible to start.
This commit is contained in:
commit
acdea41a67
19 changed files with 601 additions and 468 deletions
|
|
@ -316,6 +316,7 @@ def cmdhandler(called_on, raw_string, testing=False, callertype="session", sessi
|
||||||
else:
|
else:
|
||||||
# fallback to default error text
|
# fallback to default error text
|
||||||
sysarg = _("Command '%s' is not available.") % raw_string
|
sysarg = _("Command '%s' is not available.") % raw_string
|
||||||
|
cmdset.get_all_cmd_keys_and_aliases(caller)
|
||||||
suggestions = string_suggestions(raw_string, cmdset.get_all_cmd_keys_and_aliases(caller), cutoff=0.7, maxnum=3)
|
suggestions = string_suggestions(raw_string, cmdset.get_all_cmd_keys_and_aliases(caller), cutoff=0.7, maxnum=3)
|
||||||
if suggestions:
|
if suggestions:
|
||||||
sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
|
sysarg += _(" Maybe you meant %s?") % utils.list_to_string(suggestions, _('or'), addquote=True)
|
||||||
|
|
@ -332,7 +333,7 @@ def cmdhandler(called_on, raw_string, testing=False, callertype="session", sessi
|
||||||
if syscmd:
|
if syscmd:
|
||||||
# replace system command with custom version
|
# replace system command with custom version
|
||||||
cmd = syscmd
|
cmd = syscmd
|
||||||
cmd.sessid = caller.sessid if callertype=="session" else None
|
cmd.sessid = session.sessid if session else None
|
||||||
sysarg = "%s:%s" % (cmdname, args)
|
sysarg = "%s:%s" % (cmdname, args)
|
||||||
raise ExecSystemCommand(cmd, sysarg)
|
raise ExecSystemCommand(cmd, sysarg)
|
||||||
|
|
||||||
|
|
@ -398,7 +399,7 @@ def cmdhandler(called_on, raw_string, testing=False, callertype="session", sessi
|
||||||
syscmd.cmdstring = syscmd.key
|
syscmd.cmdstring = syscmd.key
|
||||||
syscmd.args = sysarg
|
syscmd.args = sysarg
|
||||||
syscmd.cmdset = cmdset
|
syscmd.cmdset = cmdset
|
||||||
syscmd.sessid = caller.sessid if callertype=="session" else None
|
syscmd.sessid = session.sessid if session else None
|
||||||
syscmd.raw_string = unformatted_raw_string
|
syscmd.raw_string = unformatted_raw_string
|
||||||
|
|
||||||
if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'):
|
if hasattr(syscmd, 'obj') and hasattr(syscmd.obj, 'scripts'):
|
||||||
|
|
|
||||||
|
|
@ -527,7 +527,7 @@ class CmdPerm(MuxCommand):
|
||||||
tstring = ""
|
tstring = ""
|
||||||
if 'del' in switches:
|
if 'del' in switches:
|
||||||
# delete the given permission(s) from object.
|
# delete the given permission(s) from object.
|
||||||
obj.permission.remove(self.rhslist)
|
obj.permissions.remove(self.rhslist)
|
||||||
cstring += "\nPermission(s) %s removed from %s (if they existed)." % (", ".join(self.rhslist), obj.name)
|
cstring += "\nPermission(s) %s removed from %s (if they existed)." % (", ".join(self.rhslist), obj.name)
|
||||||
tstring += "\n%s revokes the permission(s) %s from you." % (caller.name, ", ".join(self.rhslist))
|
tstring += "\n%s revokes the permission(s) %s from you." % (caller.name, ", ".join(self.rhslist))
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ class Migration(DataMigration):
|
||||||
|
|
||||||
def forwards(self, orm):
|
def forwards(self, orm):
|
||||||
"Write your forwards methods here."
|
"Write your forwards methods here."
|
||||||
# Note: Don't use "from appname.models import ModelName".
|
# Note: Don't use "from appname.models import ModelName".
|
||||||
# Use orm.ModelName to refer to models in this application,
|
# Use orm.ModelName to refer to models in this application,
|
||||||
# and orm['appname.ModelName'] for models in other applications.
|
# and orm['appname.ModelName'] for models in other applications.
|
||||||
ChannelDB = orm['comms.ChannelDB']
|
ChannelDB = orm['comms.ChannelDB']
|
||||||
|
|
@ -28,8 +28,12 @@ class Migration(DataMigration):
|
||||||
new_channel.db_attributes.add(keep_log)
|
new_channel.db_attributes.add(keep_log)
|
||||||
for name in [alias.strip() for alias in
|
for name in [alias.strip() for alias in
|
||||||
channel.db_aliases.split(',')]:
|
channel.db_aliases.split(',')]:
|
||||||
tag = Tag(db_key=name, db_category='comm_alias')
|
tag = Tag.objects.filter(db_key=name.lower().strip(), db_category='comm_alias')
|
||||||
tag.save()
|
if tag:
|
||||||
|
tag = tag[0]
|
||||||
|
else:
|
||||||
|
tag = Tag(db_key=name.lower().strip(), db_category='comm_alias')
|
||||||
|
tag.save()
|
||||||
new_channel.db_tags.add(tag)
|
new_channel.db_tags.add(tag)
|
||||||
new_channel.save()
|
new_channel.save()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -342,10 +342,6 @@ class ChannelDB(TypedObject):
|
||||||
_typeclass_paths = settings.COMM_TYPECLASS_PATHS
|
_typeclass_paths = settings.COMM_TYPECLASS_PATHS
|
||||||
_default_typeclass_path = settings.BASE_COMM_TYPECLASS or "src.comms.comms.Comm"
|
_default_typeclass_path = settings.BASE_COMM_TYPECLASS or "src.comms.comms.Comm"
|
||||||
|
|
||||||
class Meta:
|
|
||||||
"Define Django meta options"
|
|
||||||
verbose_name = "Channel"
|
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
TypedObject.__init__(self, *args, **kwargs)
|
TypedObject.__init__(self, *args, **kwargs)
|
||||||
_SA(self, "tags", TagHandler(self, category_prefix="comm_"))
|
_SA(self, "tags", TagHandler(self, category_prefix="comm_"))
|
||||||
|
|
|
||||||
|
|
@ -11,12 +11,16 @@ class Migration(DataMigration):
|
||||||
# Note: Don't use "from appname.models import ModelName".
|
# Note: Don't use "from appname.models import ModelName".
|
||||||
# Use orm.ModelName to refer to models in this application,
|
# Use orm.ModelName to refer to models in this application,
|
||||||
# and orm['appname.ModelName'] for models in other applications.
|
# and orm['appname.ModelName'] for models in other applications.
|
||||||
|
Tag = orm['typeclasses.Tag']
|
||||||
for obj in orm.ObjectDB.objects.all():
|
for obj in orm.ObjectDB.objects.all():
|
||||||
if obj.db_permissions:
|
if obj.db_permissions:
|
||||||
for perm in [perm.strip() for perm in obj.db_permissions.split(",")]:
|
for perm in [perm.strip() for perm in obj.db_permissions.split(",")]:
|
||||||
tag = orm['typeclasses.Tag'].create(db_key=perm, db_category="permissions")
|
tag = Tag.objects.filter(db_key=perm.lower().strip(), db_category="permissions")
|
||||||
tag.save()
|
if tag:
|
||||||
|
tag = tag[0]
|
||||||
|
else:
|
||||||
|
tag = Tag(db_key=perm.lower().strip(), db_category="permissions")
|
||||||
|
tag.save()
|
||||||
obj.db_tags.add(tag)
|
obj.db_tags.add(tag)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,16 @@ class Migration(DataMigration):
|
||||||
# Note: Don't use "from appname.models import ModelName".
|
# Note: Don't use "from appname.models import ModelName".
|
||||||
# Use orm.ModelName to refer to models in this application,
|
# Use orm.ModelName to refer to models in this application,
|
||||||
# and orm['appname.ModelName'] for models in other applications.
|
# and orm['appname.ModelName'] for models in other applications.
|
||||||
|
Tag = orm['typeclasses.Tag']
|
||||||
for obj in orm.PlayerDB.objects.all():
|
for obj in orm.PlayerDB.objects.all():
|
||||||
if obj.db_permissions:
|
if obj.db_permissions:
|
||||||
for perm in [perm.strip() for perm in obj.db_permissions.split(",")]:
|
for perm in [perm.strip() for perm in obj.db_permissions.split(",")]:
|
||||||
tag = orm['typeclasses.Tag'].create(db_key=perm, db_category="permissions")
|
tag = Tag.objects.filter(db_key=perm.lower().strip(), db_category="permissions")
|
||||||
tag.save()
|
if tag:
|
||||||
|
tag = tag[0]
|
||||||
|
else:
|
||||||
|
tag = Tag(db_key=perm.lower().strip(), db_category="permissions")
|
||||||
|
tag.save()
|
||||||
obj.db_tags.add(tag)
|
obj.db_tags.add(tag)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
|
|
|
||||||
|
|
@ -11,11 +11,16 @@ class Migration(DataMigration):
|
||||||
# Note: Don't use "from appname.models import ModelName".
|
# Note: Don't use "from appname.models import ModelName".
|
||||||
# Use orm.ModelName to refer to models in this application,
|
# Use orm.ModelName to refer to models in this application,
|
||||||
# and orm['appname.ModelName'] for models in other applications.
|
# and orm['appname.ModelName'] for models in other applications.
|
||||||
|
Tag = orm['typeclasses.Tag']
|
||||||
for obj in orm.ScriptDB.objects.all():
|
for obj in orm.ScriptDB.objects.all():
|
||||||
if obj.db_permissions:
|
if obj.db_permissions:
|
||||||
for perm in [perm.strip() for perm in obj.db_permissions.split(",")]:
|
for perm in [perm.strip() for perm in obj.db_permissions.split(",")]:
|
||||||
tag = orm['typeclasses.Tag'].create(db_key=perm, db_category="permissions")
|
tag = Tag.objects.filter(db_key=perm.lower().strip(), db_category="permissions")
|
||||||
tag.save()
|
if tag:
|
||||||
|
tag = tag[0]
|
||||||
|
else:
|
||||||
|
tag = Tag(db_key=perm.lower().strip(), db_category="permissions")
|
||||||
|
tag.save()
|
||||||
obj.db_tags.add(tag)
|
obj.db_tags.add(tag)
|
||||||
|
|
||||||
def backwards(self, orm):
|
def backwards(self, orm):
|
||||||
|
|
|
||||||
|
|
@ -240,7 +240,7 @@ class AMPProtocol(amp.AMP):
|
||||||
|
|
||||||
def errback(self, e, info):
|
def errback(self, e, info):
|
||||||
"error handler, to avoid dropping connections on server tracebacks."
|
"error handler, to avoid dropping connections on server tracebacks."
|
||||||
e.trap(Exception)
|
f = e.trap(Exception)
|
||||||
print "AMP Error for %(info)s: %(e)s" % {'info': info, 'e': e.getErrorMessage()}
|
print "AMP Error for %(info)s: %(e)s" % {'info': info, 'e': e.getErrorMessage()}
|
||||||
|
|
||||||
def send_split_msg(self, sessid, msg, data, command):
|
def send_split_msg(self, sessid, msg, data, command):
|
||||||
|
|
@ -286,7 +286,7 @@ class AMPProtocol(amp.AMP):
|
||||||
data comes in multiple chunks; if so (nparts>1) we buffer the data
|
data comes in multiple chunks; if so (nparts>1) we buffer the data
|
||||||
and wait for the remaining parts to arrive before continuing.
|
and wait for the remaining parts to arrive before continuing.
|
||||||
"""
|
"""
|
||||||
#print "msg portal -> server (server side):", sessid, msg
|
#print "msg portal -> server (server side):", sessid, msg, data
|
||||||
global MSGBUFFER
|
global MSGBUFFER
|
||||||
if nparts > 1:
|
if nparts > 1:
|
||||||
# a multipart message
|
# a multipart message
|
||||||
|
|
@ -311,7 +311,7 @@ class AMPProtocol(amp.AMP):
|
||||||
try:
|
try:
|
||||||
return self.callRemote(MsgPortal2Server,
|
return self.callRemote(MsgPortal2Server,
|
||||||
sessid=sessid,
|
sessid=sessid,
|
||||||
msg=msg,
|
msg=to_str(msg) if msg!=None else "",
|
||||||
ipart=0,
|
ipart=0,
|
||||||
nparts=1,
|
nparts=1,
|
||||||
data=dumps(data)).addErrback(self.errback, "MsgPortal2Server")
|
data=dumps(data)).addErrback(self.errback, "MsgPortal2Server")
|
||||||
|
|
@ -351,7 +351,7 @@ class AMPProtocol(amp.AMP):
|
||||||
try:
|
try:
|
||||||
return self.callRemote(MsgServer2Portal,
|
return self.callRemote(MsgServer2Portal,
|
||||||
sessid=sessid,
|
sessid=sessid,
|
||||||
msg=to_str(msg),
|
msg=to_str(msg) if msg!=None else "",
|
||||||
ipart=0,
|
ipart=0,
|
||||||
nparts=1,
|
nparts=1,
|
||||||
data=dumps(data)).addErrback(self.errback, "MsgServer2Portal")
|
data=dumps(data)).addErrback(self.errback, "MsgServer2Portal")
|
||||||
|
|
|
||||||
|
|
@ -22,23 +22,23 @@ oob trackers should inherit from the OOBTracker class in this
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from inspect import isfunction
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
from src.server.sessionhandler import SESSIONS
|
from src.server.sessionhandler import SESSIONS
|
||||||
from src.scripts.scripts import Script
|
from src.scripts.scripts import Script
|
||||||
from src.create import create_script
|
from src.utils.create import create_script
|
||||||
from src.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj
|
from src.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj, unpack_dbobj
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
from src.utils.utils import variable_from_module, to_str
|
from src.utils.utils import all_from_module, to_str, is_iter, make_iter
|
||||||
|
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_DA = object.__delattribute__
|
_DA = object.__delattr__
|
||||||
|
|
||||||
# trackers track property changes and keep returning until they are removed
|
# load from plugin module
|
||||||
_OOB_TRACKERS = variable_from_module(settings.OBB_PLUGIN_MODULE, "OBB_TRACKERS", default={})
|
_OOB_FUNCS = dict((key.lower(), func) for key, func in all_from_module(settings.OOB_PLUGIN_MODULE).items() if isfunction(func))
|
||||||
# functions return immediately
|
_OOB_ERROR = _OOB_FUNCS.get("oob_error", None)
|
||||||
_OOB_FUNCS = variable_from_module(settings.OBB_PLUGIN_MODULE, "OBB_FUNCS", default={})
|
|
||||||
|
|
||||||
|
|
||||||
class TrackerHandler(object):
|
class TrackerHandler(object):
|
||||||
|
|
@ -52,30 +52,34 @@ class TrackerHandler(object):
|
||||||
"""
|
"""
|
||||||
This is initiated and stored on the object as a property _trackerhandler.
|
This is initiated and stored on the object as a property _trackerhandler.
|
||||||
"""
|
"""
|
||||||
self.obj = obj.dbobj
|
try: obj = obj.dbobj
|
||||||
|
except AttributeError: pass
|
||||||
|
self.obj = obj
|
||||||
self.ntrackers = 0
|
self.ntrackers = 0
|
||||||
# initiate store only with valid on-object fieldnames
|
# initiate store only with valid on-object fieldnames
|
||||||
self.tracktargets = dict((key, {}) for key in _GA(_GA(self.obj, "_meta"), "get_all_field_names")())
|
self.tracktargets = dict((key, {}) for key in _GA(_GA(self.obj, "_meta"), "get_all_field_names")())
|
||||||
|
|
||||||
def add(self, fieldname, trackerkey, trackerobj):
|
def add(self, fieldname, tracker):
|
||||||
"""
|
"""
|
||||||
Add tracker to the handler. Raises KeyError if fieldname
|
Add tracker to the handler. Raises KeyError if fieldname
|
||||||
does not exist.
|
does not exist.
|
||||||
"""
|
"""
|
||||||
self.tracktargets[fieldname][trackerkey] = trackerobj
|
trackerkey = tracker.__class__.__name__
|
||||||
|
self.tracktargets[fieldname][trackerkey] = tracker
|
||||||
self.ntrackers += 1
|
self.ntrackers += 1
|
||||||
|
|
||||||
def remove(self, fieldname, trackerkey, *args, **kwargs):
|
def remove(self, fieldname, trackerclass, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Remove tracker from handler. Raises KeyError if tracker
|
Remove tracker from handler. Raises KeyError if tracker
|
||||||
is not found.
|
is not found.
|
||||||
"""
|
"""
|
||||||
oobobj = self.tracktargets[fieldname][trackerkey]
|
trackerkey = trackerclass.__name__
|
||||||
|
tracker = self.tracktargets[fieldname][trackerkey]
|
||||||
try:
|
try:
|
||||||
oobobj.at_delete(*args, **kwargs)
|
tracker.at_delete(*args, **kwargs)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
del oobobj
|
del tracker
|
||||||
self.ntrackers -= 1
|
self.ntrackers -= 1
|
||||||
if self.ntrackers <= 0:
|
if self.ntrackers <= 0:
|
||||||
# if there are no more trackers, clean this handler
|
# if there are no more trackers, clean this handler
|
||||||
|
|
@ -85,9 +89,9 @@ class TrackerHandler(object):
|
||||||
"""
|
"""
|
||||||
Called by the field when it updates to a new value
|
Called by the field when it updates to a new value
|
||||||
"""
|
"""
|
||||||
for trackerobj in self.tracktargets[fieldname].values():
|
for tracker in self.tracktargets[fieldname].values():
|
||||||
try:
|
try:
|
||||||
trackerobj.update(fieldname, new_value)
|
tracker.update(new_value)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
|
|
||||||
|
|
@ -104,84 +108,61 @@ class TrackerBase(object):
|
||||||
"Called when tracker is removed"
|
"Called when tracker is removed"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
# Default tracker OOB class
|
class _RepeaterScript(Script):
|
||||||
|
|
||||||
class OOBTracker(TrackerBase):
|
|
||||||
"""
|
"""
|
||||||
A OOB object that passively sends data to a stored sessid whenever
|
Repeating and subscription-enabled script for triggering OOB
|
||||||
a named database field changes.
|
functions. Maintained in a _RepeaterPool.
|
||||||
"""
|
"""
|
||||||
def __init__(self, fieldname, sessid, *args, **kwargs):
|
def at_script_creation(self):
|
||||||
"""
|
"Called when script is initialized"
|
||||||
name - name of entity to track, such as "db_key"
|
self.key = "oob_func"
|
||||||
track_type - one of "field", "prop" or "attr" for Database fields,
|
self.desc = "OOB functionality script"
|
||||||
non-database Property or Attribute
|
self.persistent = False #oob scripts should always be non-persistent
|
||||||
sessid - sessid of session to report to
|
self.ndb.subscriptions = {}
|
||||||
"""
|
|
||||||
self.fieldname = fieldname
|
|
||||||
self.sessid = sessid
|
|
||||||
|
|
||||||
def update(self, new_value, *args, **kwargs):
|
def at_repeat(self):
|
||||||
"Called by cache when updating the tracked entitiy"
|
"""
|
||||||
SESSIONS.session_from_sessid(self.sessid).msg(oob={"cmdkey":"trackreturn",
|
Calls subscriptions every self.interval seconds
|
||||||
"name":self.fieldname,
|
"""
|
||||||
"value":new_value})
|
for (func_key, sessid, interval, args, kwargs) in self.ndb.subscriptions.values():
|
||||||
|
session = SESSIONS.session_from_sessid(sessid)
|
||||||
|
OOB_HANDLER.execute_cmd(session, func_key, *args, **kwargs)
|
||||||
|
|
||||||
|
def subscribe(self, store_key, sessid, func_key, interval, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Sign up a subscriber to this oobfunction. Subscriber is
|
||||||
|
a database object with a dbref.
|
||||||
|
"""
|
||||||
|
self.ndb.subscriptions[store_key] = (func_key, sessid, interval, args, kwargs)
|
||||||
|
|
||||||
|
def unsubscribe(self, store_key):
|
||||||
|
"""
|
||||||
|
Unsubscribe from oobfunction. Returns True if removal was
|
||||||
|
successful, False otherwise
|
||||||
|
"""
|
||||||
|
self.ndb.subscriptions.pop(store_key, None)
|
||||||
|
|
||||||
class _RepeaterPool(object):
|
class _RepeaterPool(object):
|
||||||
"""
|
"""
|
||||||
This maintains a pool of _RepeaterScript scripts, ordered one per interval. It
|
This maintains a pool of _RepeaterScript scripts, ordered one per interval. It
|
||||||
will automatically cull itself once a given interval's script has no more
|
will automatically cull itself once a given interval's script has no more
|
||||||
subscriptions.
|
subscriptions.
|
||||||
|
|
||||||
|
This is used and accessed from oobhandler.repeat/unrepeat
|
||||||
"""
|
"""
|
||||||
|
|
||||||
class _RepeaterScript(Script):
|
|
||||||
"""
|
|
||||||
Repeating script for triggering OOB functions. Maintained in the pool.
|
|
||||||
"""
|
|
||||||
def at_script_creation(self):
|
|
||||||
"Called when script is initialized"
|
|
||||||
self.key = "oob_func"
|
|
||||||
self.desc = "OOB functionality script"
|
|
||||||
self.persistent = False #oob scripts should always be non-persistent
|
|
||||||
self.ndb.subscriptions = {}
|
|
||||||
|
|
||||||
def at_repeat(self):
|
|
||||||
"""
|
|
||||||
Calls subscriptions every self.interval seconds
|
|
||||||
"""
|
|
||||||
for (func_key, caller, interval, args, kwargs) in self.ndb.subscriptions.values():
|
|
||||||
try:
|
|
||||||
_OOB_FUNCS[func_key](caller, *args, **kwargs)
|
|
||||||
except Exception:
|
|
||||||
logger.log_trace()
|
|
||||||
|
|
||||||
def subscribe(self, store_key, caller, func_key, interval, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
Sign up a subscriber to this oobfunction. Subscriber is
|
|
||||||
a database object with a dbref.
|
|
||||||
"""
|
|
||||||
self.ndb.subscriptions[store_key] = (func_key, caller, interval, args, kwargs)
|
|
||||||
|
|
||||||
def unsubscribe(self, store_key):
|
|
||||||
"""
|
|
||||||
Unsubscribe from oobfunction. Returns True if removal was
|
|
||||||
successful, False otherwise
|
|
||||||
"""
|
|
||||||
self.ndb.subscriptions.pop(store_key, None)
|
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.scripts = {}
|
self.scripts = {}
|
||||||
|
|
||||||
def add(self, store_key, caller, func_key, interval, *args, **kwargs):
|
def add(self, store_key, sessid, func_key, interval, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Add a new tracking
|
Add a new tracking
|
||||||
"""
|
"""
|
||||||
if interval not in self.scripts:
|
if interval not in self.scripts:
|
||||||
# if no existing interval exists, create new script to fill the gap
|
# if no existing interval exists, create new script to fill the gap
|
||||||
new_tracker = create_script(self._RepeaterScript, key="oob_repeater_%is" % interval, interval=interval)
|
new_tracker = create_script(_RepeaterScript, key="oob_repeater_%is" % interval, interval=interval)
|
||||||
self.scripts[interval] = new_tracker
|
self.scripts[interval] = new_tracker
|
||||||
self.scripts[interval].subscribe(store_key, caller, func_key, interval, *args, **kwargs)
|
self.scripts[interval].subscribe(store_key, sessid, func_key, interval, *args, **kwargs)
|
||||||
|
|
||||||
def remove(self, store_key, interval):
|
def remove(self, store_key, interval):
|
||||||
"""
|
"""
|
||||||
|
|
@ -193,14 +174,14 @@ class _RepeaterPool(object):
|
||||||
# no more subscriptions for this interval. Clean out the script.
|
# no more subscriptions for this interval. Clean out the script.
|
||||||
self.scripts[interval].stop()
|
self.scripts[interval].stop()
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
"""
|
||||||
|
Stop all scripts in pool. This is done at server reload since restoring the pool
|
||||||
|
will automatically re-populate the pool.
|
||||||
|
"""
|
||||||
|
for script in self.scripts.values():
|
||||||
|
script.stop()
|
||||||
|
|
||||||
# Default OOB funcs
|
|
||||||
|
|
||||||
def OOB_get_attr_val(caller, attrname):
|
|
||||||
"Get the given attrback from caller"
|
|
||||||
caller.msg(oob={"cmdkey":"get_attr",
|
|
||||||
"name":attrname,
|
|
||||||
"value":to_str(caller.attributes.get(attrname))})
|
|
||||||
|
|
||||||
# Main OOB Handler
|
# Main OOB Handler
|
||||||
|
|
||||||
|
|
@ -214,6 +195,7 @@ class OOBHandler(object):
|
||||||
"""
|
"""
|
||||||
Initialize handler
|
Initialize handler
|
||||||
"""
|
"""
|
||||||
|
self.sessionhandler = SESSIONS
|
||||||
self.oob_tracker_storage = {}
|
self.oob_tracker_storage = {}
|
||||||
self.oob_repeat_storage = {}
|
self.oob_repeat_storage = {}
|
||||||
self.oob_tracker_pool = _RepeaterPool()
|
self.oob_tracker_pool = _RepeaterPool()
|
||||||
|
|
@ -224,9 +206,12 @@ class OOBHandler(object):
|
||||||
ServerConf field
|
ServerConf field
|
||||||
"""
|
"""
|
||||||
if self.oob_tracker_storage:
|
if self.oob_tracker_storage:
|
||||||
|
#print "saved tracker_storage:", self.oob_tracker_storage
|
||||||
ServerConfig.objects.conf(key="oob_tracker_storage", value=dbserialize(self.oob_tracker_storage))
|
ServerConfig.objects.conf(key="oob_tracker_storage", value=dbserialize(self.oob_tracker_storage))
|
||||||
if self.oob_repeat_storage:
|
if self.oob_repeat_storage:
|
||||||
|
#print "saved repeat_storage:", self.oob_repeat_storage
|
||||||
ServerConfig.objects.conf(key="oob_repeat_storage", value=dbserialize(self.oob_repeat_storage))
|
ServerConfig.objects.conf(key="oob_repeat_storage", value=dbserialize(self.oob_repeat_storage))
|
||||||
|
self.oob_tracker_pool.stop()
|
||||||
|
|
||||||
def restore(self):
|
def restore(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -237,88 +222,102 @@ class OOBHandler(object):
|
||||||
tracker_storage = ServerConfig.objects.conf(key="oob_tracker_storage")
|
tracker_storage = ServerConfig.objects.conf(key="oob_tracker_storage")
|
||||||
if tracker_storage:
|
if tracker_storage:
|
||||||
self.oob_tracker_storage = dbunserialize(tracker_storage)
|
self.oob_tracker_storage = dbunserialize(tracker_storage)
|
||||||
for tracker_key, (obj, sessid, fieldname, args, kwargs) in self.oob_tracker_storage.items():
|
#print "recovered from tracker_storage:", self.oob_tracker_storage
|
||||||
self.track(obj, sessid, fieldname, tracker_key, *args, **kwargs)
|
for (obj, sessid, fieldname, trackerclass, args, kwargs) in self.oob_tracker_storage.values():
|
||||||
|
self.track(unpack_dbobj(obj), sessid, fieldname, trackerclass, *args, **kwargs)
|
||||||
|
# make sure to purce the storage
|
||||||
|
ServerConfig.objects.conf(key="oob_tracker_storage", delete=True)
|
||||||
|
|
||||||
repeat_storage = ServerConfig.objects.conf(key="oob_repeat_storage")
|
repeat_storage = ServerConfig.objects.conf(key="oob_repeat_storage")
|
||||||
if repeat_storage:
|
if repeat_storage:
|
||||||
self.oob_repeat_storage = dbunserialize(repeat_storage)
|
self.oob_repeat_storage = dbunserialize(repeat_storage)
|
||||||
for func_key, (caller, func_key, interval, args, kwargs) in self.oob_repeat_storage.items():
|
#print "recovered from repeat_storage:", self.oob_repeat_storage
|
||||||
self.repeat(caller, func_key, interval, *args, **kwargs)
|
for (obj, sessid, func_key, interval, args, kwargs) in self.oob_repeat_storage.values():
|
||||||
|
self.repeat(unpack_dbobj(obj), sessid, func_key, interval, *args, **kwargs)
|
||||||
|
# make sure to purge the storage
|
||||||
|
ServerConfig.objects.conf(key="oob_repeat_storage", delete=True)
|
||||||
|
|
||||||
|
def track(self, obj, sessid, fieldname, trackerclass, *args, **kwargs):
|
||||||
def track(self, obj, sessid, fieldname, tracker_key, *args, **kwargs):
|
|
||||||
"""
|
"""
|
||||||
Create an OOB obj of class _oob_MAPPING[tracker_key] on obj. args,
|
Create an OOB obj of class _oob_MAPPING[tracker_key] on obj. args,
|
||||||
kwargs will be used to initialize the OOB hook before adding
|
kwargs will be used to initialize the OOB hook before adding
|
||||||
it to obj.
|
it to obj.
|
||||||
If property_key is not given, but the OOB has a class property property_name, this
|
If property_key is not given, but the OOB has a class property property_name, this
|
||||||
will be used as the property name when assigning the OOB to
|
will be used as the property name when assigning the OOB to
|
||||||
obj, otherwise tracker_key is ysed as the property name.
|
obj, otherwise tracker_key is used as the property name.
|
||||||
"""
|
"""
|
||||||
oobclass = _OOB_TRACKERS[tracker_key] # raise traceback if not found
|
try: obj = obj.dbobj
|
||||||
|
except AttributeError: pass
|
||||||
|
|
||||||
if not "_trackerhandler" in _GA(obj, "__dict__"):
|
if not "_trackerhandler" in _GA(obj, "__dict__"):
|
||||||
# assign trackerhandler to object
|
# assign trackerhandler to object
|
||||||
_SA(obj, "_trackerhandler", TrackerHandler(obj))
|
_SA(obj, "_trackerhandler", TrackerHandler(obj))
|
||||||
# initialize object
|
# initialize object
|
||||||
oob = oobclass(obj, sessid, fieldname, *args, **kwargs)
|
tracker = trackerclass(self, fieldname, sessid, *args, **kwargs)
|
||||||
_GA(obj, "_trackerhandler").add(oob, fieldname)
|
_GA(obj, "_trackerhandler").add(fieldname, tracker)
|
||||||
|
|
||||||
# store calling arguments as a pickle for retrieval later
|
# store calling arguments as a pickle for retrieval later
|
||||||
storekey = (pack_dbobj(obj), sessid, fieldname)
|
obj_packed = pack_dbobj(obj)
|
||||||
stored = (obj, sessid, fieldname, args, kwargs)
|
storekey = (obj_packed, sessid, fieldname)
|
||||||
|
stored = (obj_packed, sessid, fieldname, trackerclass, args, kwargs)
|
||||||
self.oob_tracker_storage[storekey] = stored
|
self.oob_tracker_storage[storekey] = stored
|
||||||
|
|
||||||
def untrack(self, obj, sessid, fieldname, tracker_key, *args, **kwargs):
|
def untrack(self, obj, sessid, fieldname, trackerclass, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Remove the OOB from obj. If oob implements an
|
Remove the OOB from obj. If oob implements an
|
||||||
at_delete hook, this will be called with args, kwargs
|
at_delete hook, this will be called with args, kwargs
|
||||||
"""
|
"""
|
||||||
|
try: obj = obj.dbobj
|
||||||
|
except AttributeError: pass
|
||||||
|
|
||||||
try:
|
try:
|
||||||
# call at_delete hook
|
# call at_delete hook
|
||||||
_GA(obj, "_trackerhandler").remove(fieldname, tracker_key, *args, **kwargs)
|
_GA(obj, "_trackerhandler").remove(fieldname, trackerclass, *args, **kwargs)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
pass
|
pass
|
||||||
# remove the pickle from storage
|
# remove the pickle from storage
|
||||||
store_key = (pack_dbobj(obj), sessid, fieldname)
|
store_key = (pack_dbobj(obj), sessid, fieldname)
|
||||||
self.oob_tracker_storage.pop(store_key, None)
|
self.oob_tracker_storage.pop(store_key, None)
|
||||||
|
|
||||||
def track_field(self, obj, sessid, field_name, tracker_key="oobtracker"):
|
def track_field(self, obj, sessid, field_name, trackerclass):
|
||||||
"""
|
"""
|
||||||
Shortcut wrapper method for specifically tracking a database field.
|
Shortcut wrapper method for specifically tracking a database field.
|
||||||
Uses OOBTracker by default (change tracker_key to redirect)
|
Takes the tracker class as argument.
|
||||||
Will create a tracker with a property name that the field cache
|
|
||||||
expects
|
|
||||||
"""
|
"""
|
||||||
# all database field names starts with db_*
|
# all database field names starts with db_*
|
||||||
field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name
|
field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name
|
||||||
oob_tracker_name = "_track_%s_change" % field_name # field cache looks for name on this form
|
self.track(obj, sessid, field_name, trackerclass)
|
||||||
self.track(obj, tracker_key, field_name, sessid, property_name=oob_tracker_name)
|
|
||||||
|
|
||||||
def track_attribute(self, obj, sessid, attr_name, tracker_key="oobtracker"):
|
def untrack_field(self, obj, sessid, field_name):
|
||||||
|
"""
|
||||||
|
Shortcut for untracking a database field. Uses OOBTracker by defualt
|
||||||
|
"""
|
||||||
|
field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name
|
||||||
|
self.untrack(obj, sessid, field_name)
|
||||||
|
|
||||||
|
def track_attribute(self, obj, sessid, attr_name, trackerclass):
|
||||||
"""
|
"""
|
||||||
Shortcut wrapper method for specifically tracking the changes of an
|
Shortcut wrapper method for specifically tracking the changes of an
|
||||||
Attribute on an object. Will create a tracker on the Attribute Object and
|
Attribute on an object. Will create a tracker on the Attribute Object and
|
||||||
name in a way the Attribute expects.
|
name in a way the Attribute expects.
|
||||||
"""
|
"""
|
||||||
# get the attribute object if we can
|
# get the attribute object if we can
|
||||||
|
try: obj = obj.dbobj
|
||||||
|
except AttributeError: pass
|
||||||
attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True)
|
attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True)
|
||||||
if attrobj:
|
if attrobj:
|
||||||
oob_tracker_name = "_track_db_value_change"
|
self.track(attrobj, sessid, "db_value", trackerclass, attr_name)
|
||||||
self.track(attrobj, tracker_key, attr_name, sessid, property_name=oob_tracker_name)
|
|
||||||
|
|
||||||
def run(self, func_key, *args, **kwargs):
|
def untrack_attribute(self, obj, sessid, attr_name, trackerclass):
|
||||||
"""
|
"""
|
||||||
Retrieve oobfunc from OOB_FUNCS and execute it immediately
|
Shortcut for deactivating tracking for a given attribute.
|
||||||
using *args and **kwargs
|
|
||||||
"""
|
"""
|
||||||
oobfunc = _OOB_FUNCS[func_key] # raise traceback if not found
|
try: obj = obj.dbobj
|
||||||
try:
|
except AttributeError: pass
|
||||||
oobfunc(*args, **kwargs)
|
attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True)
|
||||||
except Exception:
|
if attrobj:
|
||||||
logger.log_trace()
|
self.untrack(attrobj, sessid, attr_name, trackerclass)
|
||||||
|
|
||||||
def repeat(self, caller, func_key, interval=20, *args, **kwargs):
|
def repeat(self, obj, sessid, func_key, interval=20, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Start a repeating action. Every interval seconds,
|
Start a repeating action. Every interval seconds,
|
||||||
the oobfunc corresponding to func_key is called with
|
the oobfunc corresponding to func_key is called with
|
||||||
|
|
@ -326,20 +325,59 @@ class OOBHandler(object):
|
||||||
"""
|
"""
|
||||||
if not func_key in _OOB_FUNCS:
|
if not func_key in _OOB_FUNCS:
|
||||||
raise KeyError("%s is not a valid OOB function name.")
|
raise KeyError("%s is not a valid OOB function name.")
|
||||||
store_key = (pack_dbobj(caller), func_key, interval)
|
try:
|
||||||
|
obj = obj.dbobj
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
store_obj = pack_dbobj(obj)
|
||||||
|
store_key = (store_obj, sessid, func_key, interval)
|
||||||
# prepare to store
|
# prepare to store
|
||||||
self.oob_repeat_storage[store_key] = (caller, func_key, interval, args, kwargs)
|
self.oob_repeat_storage[store_key] = (store_obj, sessid, func_key, interval, args, kwargs)
|
||||||
self.oob_tracker_pool.add(store_key, caller, func_key, interval, *args, **kwargs)
|
self.oob_tracker_pool.add(store_key, sessid, func_key, interval, *args, **kwargs)
|
||||||
|
|
||||||
def unrepeat(self, caller, func_key, interval=20):
|
def unrepeat(self, obj, sessid, func_key, interval=20):
|
||||||
"""
|
"""
|
||||||
Stop a repeating action
|
Stop a repeating action
|
||||||
"""
|
"""
|
||||||
store_key = (pack_dbobj(caller), func_key, interval)
|
try:
|
||||||
|
obj = obj.dbobj
|
||||||
|
except AttributeError:
|
||||||
|
pass
|
||||||
|
store_key = (pack_dbobj(obj), sessid, func_key, interval)
|
||||||
self.oob_tracker_pool.remove(store_key, interval)
|
self.oob_tracker_pool.remove(store_key, interval)
|
||||||
self.oob_repeat_storage.pop(store_key, None)
|
self.oob_repeat_storage.pop(store_key, None)
|
||||||
|
|
||||||
|
def msg(self, sessid, funcname, *args, **kwargs):
|
||||||
|
"Shortcut to relay oob data back to portal"
|
||||||
|
session = self.sessionhandler.session_from_sessid(sessid)
|
||||||
|
#print "oobhandler msg:", sessid, session, funcname, args, kwargs
|
||||||
|
if session:
|
||||||
|
session.msg(oob=(funcname, args, kwargs))
|
||||||
|
|
||||||
|
# access method - called from msg()
|
||||||
|
|
||||||
|
def execute_cmd(self, session, func_key, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Retrieve oobfunc from OOB_FUNCS and execute it immediately
|
||||||
|
using *args and **kwargs
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
#print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys()
|
||||||
|
oobfunc = _OOB_FUNCS[func_key] # raise traceback if not found
|
||||||
|
oobfunc(self, session, *args, **kwargs)
|
||||||
|
except KeyError,e:
|
||||||
|
errmsg = "OOB Error: function '%s' not recognized: %s" % (func_key, e)
|
||||||
|
if _OOB_ERROR:
|
||||||
|
_OOB_ERROR(self, session, errmsg, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
logger.log_trace(errmsg)
|
||||||
|
raise
|
||||||
|
except Exception, err:
|
||||||
|
errmsg = "OOB Error: Exception in '%s'(%s, %s):\n%s" % (func_key, args, kwargs, err)
|
||||||
|
if _OOB_ERROR:
|
||||||
|
_OOB_ERROR(self, session, errmsg, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
logger.log_trace(errmsg)
|
||||||
|
raise
|
||||||
# access object
|
# access object
|
||||||
OOB_HANDLER = OOBHandler()
|
OOB_HANDLER = OOBHandler()
|
||||||
|
|
|
||||||
|
|
@ -7,12 +7,10 @@ http://tintin.sourceforge.net/msdp/. MSDP manages out-of-band
|
||||||
communication between the client and server, for updating health bars
|
communication between the client and server, for updating health bars
|
||||||
etc.
|
etc.
|
||||||
|
|
||||||
!TODO - this is just a partial implementation and not used by telnet yet.
|
|
||||||
|
|
||||||
"""
|
"""
|
||||||
import re
|
import re
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from src.utils.utils import make_iter, mod_import
|
from src.utils.utils import make_iter, mod_import, to_str
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
|
|
||||||
# MSDP-relevant telnet cmd/opt-codes
|
# MSDP-relevant telnet cmd/opt-codes
|
||||||
|
|
@ -28,94 +26,13 @@ IAC = chr(255)
|
||||||
SB = chr(250)
|
SB = chr(250)
|
||||||
SE = chr(240)
|
SE = chr(240)
|
||||||
|
|
||||||
|
force_str = lambda inp: to_str(inp, force_string=True)
|
||||||
|
|
||||||
# pre-compiled regexes
|
# pre-compiled regexes
|
||||||
regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_ARRAY_OPEN, MSDP_ARRAY_CLOSE)) # return 2-tuple
|
regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_ARRAY_OPEN, MSDP_ARRAY_CLOSE)) # return 2-tuple
|
||||||
regex_table = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_TABLE_OPEN, MSDP_TABLE_CLOSE)) # return 2-tuple (may be nested)
|
regex_table = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_TABLE_OPEN, MSDP_TABLE_CLOSE)) # return 2-tuple (may be nested)
|
||||||
regex_varval = re.compile(r"%s(.*?)%s(.*)" % (MSDP_VAR, MSDP_VAL)) # return 2-tuple
|
regex_var = re.compile(MSDP_VAR)
|
||||||
|
regex_val = re.compile(MSDP_VAL)
|
||||||
# MSDP default definition commands supported by Evennia (can be supplemented with custom commands as well)
|
|
||||||
MSDP_COMMANDS = ("LIST", "REPORT", "RESET", "SEND", "UNREPORT")
|
|
||||||
|
|
||||||
# fallbacks if no custom OOB module is available
|
|
||||||
MSDP_COMMANDS_CUSTOM = {}
|
|
||||||
# MSDP_REPORTABLE is a standard suggestions for making it easy to create generic guis.
|
|
||||||
# this maps MSDP command names to Evennia commands found in OOB_FUNC_MODULE. It
|
|
||||||
# is up to these commands to return data on proper form. This is overloaded if
|
|
||||||
# OOB_REPORTABLE is defined in the custom OOB module below.
|
|
||||||
MSDP_REPORTABLE = {
|
|
||||||
# General
|
|
||||||
"CHARACTER_NAME": "get_character_name",
|
|
||||||
"SERVER_ID": "get_server_id",
|
|
||||||
"SERVER_TIME": "get_server_time",
|
|
||||||
# Character
|
|
||||||
"AFFECTS": "char_affects",
|
|
||||||
"ALIGNMENT": "char_alignment",
|
|
||||||
"EXPERIENCE": "char_experience",
|
|
||||||
"EXPERIENCE_MAX": "char_experience_max",
|
|
||||||
"EXPERIENCE_TNL": "char_experience_tnl",
|
|
||||||
"HEALTH": "char_health",
|
|
||||||
"HEALTH_MAX": "char_health_max",
|
|
||||||
"LEVEL": "char_level",
|
|
||||||
"RACE": "char_race",
|
|
||||||
"CLASS": "char_class",
|
|
||||||
"MANA": "char_mana",
|
|
||||||
"MANA_MAX": "char_mana_max",
|
|
||||||
"WIMPY": "char_wimpy",
|
|
||||||
"PRACTICE": "char_practice",
|
|
||||||
"MONEY": "char_money",
|
|
||||||
"MOVEMENT": "char_movement",
|
|
||||||
"MOVEMENT_MAX": "char_movement_max",
|
|
||||||
"HITROLL": "char_hitroll",
|
|
||||||
"DAMROLL": "char_damroll",
|
|
||||||
"AC": "char_ac",
|
|
||||||
"STR": "char_str",
|
|
||||||
"INT": "char_int",
|
|
||||||
"WIS": "char_wis",
|
|
||||||
"DEX": "char_dex",
|
|
||||||
"CON": "char_con",
|
|
||||||
# Combat
|
|
||||||
"OPPONENT_HEALTH": "opponent_health",
|
|
||||||
"OPPONENT_HEALTH_MAX":"opponent_health_max",
|
|
||||||
"OPPONENT_LEVEL": "opponent_level",
|
|
||||||
"OPPONENT_NAME": "opponent_name",
|
|
||||||
# World
|
|
||||||
"AREA_NAME": "area_name",
|
|
||||||
"ROOM_EXITS": "area_room_exits",
|
|
||||||
"ROOM_NAME": "room_name",
|
|
||||||
"ROOM_VNUM": "room_dbref",
|
|
||||||
"WORLD_TIME": "world_time",
|
|
||||||
# Configurable variables
|
|
||||||
"CLIENT_ID": "client_id",
|
|
||||||
"CLIENT_VERSION": "client_version",
|
|
||||||
"PLUGIN_ID": "plugin_id",
|
|
||||||
"ANSI_COLORS": "ansi_colours",
|
|
||||||
"XTERM_256_COLORS": "xterm_256_colors",
|
|
||||||
"UTF_8": "utf_8",
|
|
||||||
"SOUND": "sound",
|
|
||||||
"MXP": "mxp",
|
|
||||||
# GUI variables
|
|
||||||
"BUTTON_1": "button1",
|
|
||||||
"BUTTON_2": "button2",
|
|
||||||
"BUTTON_3": "button3",
|
|
||||||
"BUTTON_4": "button4",
|
|
||||||
"BUTTON_5": "button5",
|
|
||||||
"GAUGE_1": "gauge1",
|
|
||||||
"GAUGE_2": "gauge2",
|
|
||||||
"GAUGE_3": "gauge3",
|
|
||||||
"GAUGE_4": "gauge4",
|
|
||||||
"GAUGE_5": "gauge5"}
|
|
||||||
MSDP_SENDABLE = MSDP_REPORTABLE
|
|
||||||
|
|
||||||
# try to load custom OOB module
|
|
||||||
OOB_MODULE = None#mod_import(settings.OOB_FUNC_MODULE)
|
|
||||||
if OOB_MODULE:
|
|
||||||
# loading customizations from OOB_FUNC_MODULE if available
|
|
||||||
try: MSDP_REPORTABLE = OOB_MODULE.OOB_REPORTABLE # replaces the default MSDP definitions
|
|
||||||
except AttributeError: pass
|
|
||||||
try: MSDP_SENDABLE = OOB_MODULE.OOB_SENDABLE
|
|
||||||
except AttributeError: MSDP_SENDABLE = MSDP_REPORTABLE
|
|
||||||
try: MSDP_COMMANDS_CUSTOM = OOB_MODULE.OOB_COMMANDS
|
|
||||||
except: pass
|
|
||||||
|
|
||||||
# Msdp object handler
|
# Msdp object handler
|
||||||
|
|
||||||
|
|
@ -132,69 +49,97 @@ class Msdp(object):
|
||||||
"""
|
"""
|
||||||
self.protocol = protocol
|
self.protocol = protocol
|
||||||
self.protocol.protocol_flags['MSDP'] = False
|
self.protocol.protocol_flags['MSDP'] = False
|
||||||
self.protocol.negotiationMap[MSDP] = self.msdp_to_func
|
self.protocol.negotiationMap[MSDP] = self.msdp_to_evennia
|
||||||
self.protocol.will(MSDP).addCallbacks(self.do_msdp, self.no_msdp)
|
self.protocol.will(MSDP).addCallbacks(self.do_msdp, self.no_msdp)
|
||||||
self.msdp_reported = {}
|
self.msdp_reported = {}
|
||||||
|
|
||||||
def no_msdp(self, option):
|
def no_msdp(self, option):
|
||||||
"No msdp supported or wanted"
|
"No msdp supported or wanted"
|
||||||
print "No msdp supported"
|
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def do_msdp(self, option):
|
def do_msdp(self, option):
|
||||||
"""
|
"""
|
||||||
Called when client confirms that it can do MSDP.
|
Called when client confirms that it can do MSDP.
|
||||||
"""
|
"""
|
||||||
print "msdp supported"
|
|
||||||
self.protocol.protocol_flags['MSDP'] = True
|
self.protocol.protocol_flags['MSDP'] = True
|
||||||
|
|
||||||
def parse_msdp(self, args):
|
def evennia_to_msdp(self, cmdname, *args, **kwargs):
|
||||||
"Called with arguments to subnegotiation"
|
|
||||||
|
|
||||||
def func_to_msdp(self, cmdname, data):
|
|
||||||
"""
|
"""
|
||||||
handle return data from cmdname by converting it to
|
handle return data from cmdname by converting it to
|
||||||
a proper msdp structure. data can either be a single value (will be
|
a proper msdp structure. data can either be a single value (will be
|
||||||
converted to a string), a list (will be converted to an MSDP_ARRAY),
|
converted to a string), a list (will be converted to an MSDP_ARRAY),
|
||||||
or a dictionary (will be converted to MSDP_TABLE).
|
or a dictionary (will be converted to MSDP_TABLE).
|
||||||
|
|
||||||
OBS - this supports nested tables and even arrays nested
|
OBS - there is no actual use of arrays and tables in the MSDP
|
||||||
inside tables, as opposed to the receive method. Arrays
|
specification or default commands -- are returns are implemented
|
||||||
cannot hold tables by definition (the table must be named
|
as simple lists or named lists (our name for them here, these
|
||||||
with MSDP_VAR, and an array can only contain MSDP_VALs).
|
un-bounded structures are not named in the specification). So for
|
||||||
|
now, this routine will not explicitly create arrays nor tables,
|
||||||
|
although there are helper methods ready should it be needed in
|
||||||
|
the future.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def make_table(name, datadict, string):
|
def make_table(name, **kwargs):
|
||||||
"build a table that may be nested with other tables or arrays."
|
"build a table that may be nested with other tables or arrays."
|
||||||
string += MSDP_VAR + name + MSDP_VAL + MSDP_TABLE_OPEN
|
string = MSDP_VAR + force_str(name) + MSDP_VAL + MSDP_TABLE_OPEN
|
||||||
for key, val in datadict.items():
|
for key, val in kwargs.items():
|
||||||
if type(val) == type({}):
|
if isinstance(val, dict):
|
||||||
string += make_table(key, val, string)
|
string += make_table(string, key, **val)
|
||||||
elif hasattr(val, '__iter__'):
|
elif hasattr(val, '__iter__'):
|
||||||
string += make_array(key, val, string)
|
string += make_array(string, key, *val)
|
||||||
else:
|
else:
|
||||||
string += MSDP_VAR + key + MSDP_VAL + val
|
string += MSDP_VAR + force_str(key) + MSDP_VAL + force_str(val)
|
||||||
string += MSDP_TABLE_CLOSE
|
string += MSDP_TABLE_CLOSE
|
||||||
return string
|
return stringk
|
||||||
|
|
||||||
def make_array(name, datalist, string):
|
def make_array(name, *args):
|
||||||
"build a simple array. Arrays may not nest tables by definition."
|
"build a array. Arrays may not nest tables by definition."
|
||||||
string += MSDP_VAR + name + MSDP_ARRAY_OPEN
|
string = MSDP_VAR + force_str(name) + MSDP_ARRAY_OPEN
|
||||||
for val in datalist:
|
string += MSDP_VAL.join(force_str(arg) for arg in args)
|
||||||
string += MSDP_VAL + val
|
|
||||||
string += MSDP_ARRAY_CLOSE
|
string += MSDP_ARRAY_CLOSE
|
||||||
return string
|
return string
|
||||||
|
|
||||||
if isinstance(data, dict):
|
def make_list(name, *args):
|
||||||
msdp_string = make_table(cmdname, data, "")
|
"build a simple list - an array without start/end markers"
|
||||||
elif hasattr(data, '__iter__'):
|
string = MSDP_VAR + force_str(name)
|
||||||
msdp_string = make_array(cmdname, data, "")
|
string += MSDP_VAL.join(force_str(arg) for arg in args)
|
||||||
|
return string
|
||||||
|
|
||||||
|
def make_named_list(name, **kwargs):
|
||||||
|
"build a named list - a table without start/end markers"
|
||||||
|
string = MSDP_VAR + force_str(name)
|
||||||
|
for key, val in kwargs.items():
|
||||||
|
string += MSDP_VAR + force_str(key) + MSDP_VAL + force_str(val)
|
||||||
|
return string
|
||||||
|
|
||||||
|
# Default MSDP commands
|
||||||
|
|
||||||
|
print "MSDP outgoing:", cmdname, args, kwargs
|
||||||
|
|
||||||
|
cupper = cmdname.upper()
|
||||||
|
if cupper == "LIST":
|
||||||
|
if args:
|
||||||
|
args = list(args)
|
||||||
|
mode = args.pop(0).upper()
|
||||||
|
self.data_out(make_array(mode, *args))
|
||||||
|
elif cupper == "REPORT":
|
||||||
|
self.data_out(make_list("REPORT", *args))
|
||||||
|
elif cupper == "UNREPORT":
|
||||||
|
self.data_out(make_list("UNREPORT", *args))
|
||||||
|
elif cupper == "RESET":
|
||||||
|
self.data_out(make_list("RESET", *args))
|
||||||
|
elif cupper == "SEND":
|
||||||
|
self.data_out(make_named_list("SEND", **kwargs))
|
||||||
else:
|
else:
|
||||||
msdp_string = MSDP_VAR + cmdname + MSDP_VAL + data
|
# return list or named lists.
|
||||||
return msdp_string
|
msdp_string = ""
|
||||||
|
if args:
|
||||||
|
msdp_string += make_list(cupper, *args)
|
||||||
|
if kwargs:
|
||||||
|
msdp_string += make_named_list(cupper, **kwargs)
|
||||||
|
self.data_out(msdp_string)
|
||||||
|
|
||||||
|
def msdp_to_evennia(self, data):
|
||||||
def msdp_to_func(self, data):
|
|
||||||
"""
|
"""
|
||||||
Handle a client's requested negotiation, converting
|
Handle a client's requested negotiation, converting
|
||||||
it into a function mapping - either one of the MSDP
|
it into a function mapping - either one of the MSDP
|
||||||
|
|
@ -218,143 +163,151 @@ class Msdp(object):
|
||||||
if hasattr(data, "__iter__"):
|
if hasattr(data, "__iter__"):
|
||||||
data = "".join(data)
|
data = "".join(data)
|
||||||
|
|
||||||
logger.log_infomsg("MSDP SUBNEGOTIATION: %s" % data)
|
#logger.log_infomsg("MSDP SUBNEGOTIATION: %s" % data)
|
||||||
|
|
||||||
for table in regex_table.findall(data):
|
for key, table in regex_table.findall(data):
|
||||||
tables[table[0].upper()] = dict(regex_varval.findall(table[1]))
|
tables[key] = {}
|
||||||
for array in regex_array.findall(data):
|
for varval in regex_var.split(table):
|
||||||
arrays[array[0].upper()] = dict(regex_varval.findall(array[1]))
|
parts = regex_val.split(varval)
|
||||||
# get all stand-alone variables, but first we must clean out all tables and arrays (which also contain vars)
|
tables[key].expand({parts[0] : tuple(parts[1:]) if len(parts)>1 else ("",)})
|
||||||
variables = dict((key.upper(), val) for key, val in regex_varval.findall(regex_array.sub("", regex_table.sub("", data))))
|
for key, array in regex_array.findall(data):
|
||||||
|
arrays[key] = []
|
||||||
|
for val in regex_val.split(array):
|
||||||
|
arrays[key].append(val)
|
||||||
|
arrays[key] = tuple(arrays[key])
|
||||||
|
for varval in regex_var.split(regex_array.sub("", regex_table.sub("", data))):
|
||||||
|
# get remaining varvals after cleaning away tables/arrays
|
||||||
|
parts = regex_val.split(varval)
|
||||||
|
variables[parts[0].upper()] = tuple(parts[1:]) if len(parts)>1 else ("", )
|
||||||
|
|
||||||
print "MSDP: table, array, variables:", tables, arrays, variables
|
#print "MSDP: table, array, variables:", tables, arrays, variables
|
||||||
|
|
||||||
ret = ""
|
# all variables sent through msdp to Evennia are considered commands with arguments.
|
||||||
|
# there are three forms of commands possible through msdp:
|
||||||
|
#
|
||||||
|
# VARNAME VAR -> varname(var)
|
||||||
|
# ARRAYNAME VAR VAL VAR VAL VAR VAL ENDARRAY -> arrayname(val,val,val)
|
||||||
|
# TABLENAME TABLE VARNAME VAL VARNAME VAL ENDTABLE -> tablename(varname=val, varname=val)
|
||||||
|
#
|
||||||
|
|
||||||
# default MSDP functions
|
# default MSDP functions
|
||||||
if "LIST" in variables:
|
if "LIST" in variables:
|
||||||
ret += self.func_to_msdp("LIST", self.msdp_cmd_list(variables["LIST"]))
|
self.data_in("list", *variables.pop("LIST"))
|
||||||
del variables["LIST"]
|
|
||||||
if "REPORT" in variables:
|
if "REPORT" in variables:
|
||||||
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*(variables["REPORT"],)))
|
self.data_in("report", *variables.pop("REPORT"))
|
||||||
del variables["REPORT"]
|
|
||||||
if "REPORT" in arrays:
|
if "REPORT" in arrays:
|
||||||
ret += self.func_to_msdp("REPORT", self.msdp_cmd_report(*arrays["REPORT"]))
|
self.data_in("report", *(arrays.pop("REPORT")))
|
||||||
del arrays["REPORT"]
|
if "UNREPORT" in variables:
|
||||||
|
self.data_in("unreport", *(arrays.pop("UNREPORT")))
|
||||||
if "RESET" in variables:
|
if "RESET" in variables:
|
||||||
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*(variables["RESET"],)))
|
self.data_in("reset", *variables.pop("RESET"))
|
||||||
del variables["RESET"]
|
|
||||||
if "RESET" in arrays:
|
if "RESET" in arrays:
|
||||||
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*arrays["RESET"]))
|
self.data_in("reset", *(arrays.pop("RESET")))
|
||||||
del arrays["RESET"]
|
|
||||||
if "SEND" in variables:
|
if "SEND" in variables:
|
||||||
ret += self.func_to_msdp("SEND", self.msdp_cmd_send(*(variables["SEND"],)))
|
self.data_in("send", *variables.pop("SEND"))
|
||||||
del variables["SEND"]
|
|
||||||
if "SEND" in arrays:
|
if "SEND" in arrays:
|
||||||
ret += self.func_to_msdp("SEND",self.msdp_cmd_send(*arrays["SEND"]))
|
self.data_in("send", *(arrays.pop("SEND")))
|
||||||
del arrays["SEND"]
|
|
||||||
|
# if there are anything left consider it a call to a custom function
|
||||||
|
|
||||||
# if there are anything left we look for a custom function
|
|
||||||
for varname, var in variables.items():
|
for varname, var in variables.items():
|
||||||
# a simple function + argument
|
# a simple function + argument
|
||||||
ooc_func = MSDP_COMMANDS_CUSTOM.get(varname.upper())
|
self.data_in(varname, (var,))
|
||||||
if ooc_func:
|
|
||||||
ret += self.func_to_msdp(varname, ooc_func(var))
|
|
||||||
for arrayname, array in arrays.items():
|
for arrayname, array in arrays.items():
|
||||||
# we assume the array are multiple arguments to the function
|
# we assume the array are multiple arguments to the function
|
||||||
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
|
self.data_in(arrayname, *array)
|
||||||
if ooc_func:
|
|
||||||
ret += self.func_to_msdp(arrayname, ooc_func(*array))
|
|
||||||
for tablename, table in tables.items():
|
for tablename, table in tables.items():
|
||||||
# we assume tables are keyword arguments to the function
|
# we assume tables are keyword arguments to the function
|
||||||
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
|
self.data_in(tablename, **table)
|
||||||
if ooc_func:
|
|
||||||
ret += self.func_to_msdp(tablename, ooc_func(**table))
|
|
||||||
|
|
||||||
if ret:
|
def data_out(self, msdp_string):
|
||||||
# send return value if it exists
|
|
||||||
self.msdp_send(ret)
|
|
||||||
ret = IAC + SB + MSDP + ret + IAC + SE
|
|
||||||
#ret = IAC + SB + MSDP + MSDP_VAR + "SEND" + MSDP_VAL + "Testsend" + IAC + SE
|
|
||||||
self.protocol._write(ret)
|
|
||||||
logger.log_infomsg("MSDP_RESULT: %s" % ret)
|
|
||||||
|
|
||||||
def msdp_send(self, msdp_string):
|
|
||||||
"""
|
"""
|
||||||
Return a msdp-valid subnegotiation across the protocol.
|
Return a msdp-valid subnegotiation across the protocol.
|
||||||
"""
|
"""
|
||||||
self.protocol._write(IAC + SB + MSDP + msdp_string + IAC + SE)
|
#print "msdp data_out (without IAC SE):", msdp_string
|
||||||
|
self.protocol ._write(IAC + SB + MSDP + force_str(msdp_string) + IAC + SE)
|
||||||
|
|
||||||
# MSDP Commands
|
def data_in(self, funcname, *args, **kwargs):
|
||||||
# Some given MSDP (varname, value) pairs can also be treated as command + argument.
|
|
||||||
# Generic msdp command map. The argument will be sent to the given command.
|
|
||||||
# See http://tintin.sourceforge.net/msdp/ for definitions of each command.
|
|
||||||
# These are client->server commands.
|
|
||||||
def msdp_cmd_list(self, arg):
|
|
||||||
"""
|
"""
|
||||||
The List command allows for retrieving various info about the server/client
|
Send oob data to Evennia
|
||||||
"""
|
"""
|
||||||
if arg == 'COMMANDS':
|
#print "msdp data_in:", funcname, args, kwargs
|
||||||
return self.func_to_msdp(arg, MSDP_COMMANDS)
|
self.protocol.data_in(text=None, oob=(funcname, args, kwargs))
|
||||||
elif arg == 'LISTS':
|
|
||||||
return self.func_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
|
|
||||||
"REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
|
|
||||||
elif arg == 'CONFIGURABLE_VARIABLES':
|
|
||||||
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
|
|
||||||
elif arg == 'REPORTABLE_VARIABLES':
|
|
||||||
return self.func_to_msdp(arg, MSDP_REPORTABLE.keys())
|
|
||||||
elif arg == 'REPORTED_VARIABLES':
|
|
||||||
# the dynamically set items to report
|
|
||||||
return self.func_to_msdp(arg, self.msdp_reported.keys())
|
|
||||||
elif arg == 'SENDABLE_VARIABLES':
|
|
||||||
return self.func_to_msdp(arg, MSDP_SENDABLE.keys())
|
|
||||||
else:
|
|
||||||
return self.func_to_msdp("LIST", arg)
|
|
||||||
|
|
||||||
# default msdp commands
|
# # MSDP Commands
|
||||||
|
# # Some given MSDP (varname, value) pairs can also be treated as command + argument.
|
||||||
|
# # Generic msdp command map. The argument will be sent to the given command.
|
||||||
|
# # See http://tintin.sourceforge.net/msdp/ for definitions of each command.
|
||||||
|
# # These are client->server commands.
|
||||||
|
# def msdp_cmd_list(self, arg):
|
||||||
|
# """
|
||||||
|
# The List command allows for retrieving various info about the server/client
|
||||||
|
# """
|
||||||
|
# if arg == 'COMMANDS':
|
||||||
|
# return self.evennia_to_msdp(arg, MSDP_COMMANDS)
|
||||||
|
# elif arg == 'LISTS':
|
||||||
|
# return self.evennia_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
|
||||||
|
# "REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
|
||||||
|
# elif arg == 'CONFIGURABLE_VARIABLES':
|
||||||
|
# return self.evennia_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
|
||||||
|
# elif arg == 'REPORTABLE_VARIABLES':
|
||||||
|
# return self.evennia_to_msdp(arg, MSDP_REPORTABLE.keys())
|
||||||
|
# elif arg == 'REPORTED_VARIABLES':
|
||||||
|
# # the dynamically set items to report
|
||||||
|
# return self.evennia_to_msdp(arg, self.msdp_reported.keys())
|
||||||
|
# elif arg == 'SENDABLE_VARIABLES':
|
||||||
|
# return self.evennia_to_msdp(arg, MSDP_SENDABLE.keys())
|
||||||
|
# else:
|
||||||
|
# return self.evennia_to_msdp("LIST", arg)
|
||||||
|
|
||||||
def msdp_cmd_report(self, *arg):
|
# # default msdp commands
|
||||||
"""
|
|
||||||
The report command instructs the server to start reporting a
|
|
||||||
reportable variable to the client.
|
|
||||||
"""
|
|
||||||
try:
|
|
||||||
return MSDP_REPORTABLE[arg](report=True)
|
|
||||||
except Exception:
|
|
||||||
logger.log_trace()
|
|
||||||
|
|
||||||
def msdp_cmd_unreport(self, arg):
|
# def msdp_cmd_report(self, *arg):
|
||||||
"""
|
# """
|
||||||
Unreport a previously reported variable
|
# The report command instructs the server to start reporting a
|
||||||
"""
|
# reportable variable to the client.
|
||||||
try:
|
# """
|
||||||
MSDP_REPORTABLE[arg](report=False)
|
# try:
|
||||||
except Exception:
|
# return MSDP_REPORTABLE[arg](report=True)
|
||||||
self.logger.log_trace()
|
# except Exception:
|
||||||
|
# logger.log_trace()
|
||||||
|
|
||||||
def msdp_cmd_reset(self, arg):
|
# def msdp_cmd_unreport(self, arg):
|
||||||
"""
|
# """
|
||||||
The reset command resets a variable to its initial state.
|
# Unreport a previously reported variable
|
||||||
"""
|
# """
|
||||||
try:
|
# try:
|
||||||
MSDP_REPORTABLE[arg](reset=True)
|
# MSDP_REPORTABLE[arg](report=False)
|
||||||
except Exception:
|
# except Exception:
|
||||||
logger.log_trace()
|
# self.logger.log_trace()
|
||||||
|
|
||||||
def msdp_cmd_send(self, arg):
|
# def msdp_cmd_reset(self, arg):
|
||||||
"""
|
# """
|
||||||
Request the server to send a particular variable
|
# The reset command resets a variable to its initial state.
|
||||||
to the client.
|
# """
|
||||||
|
# try:
|
||||||
|
# MSDP_REPORTABLE[arg](reset=True)
|
||||||
|
# except Exception:
|
||||||
|
# logger.log_trace()
|
||||||
|
|
||||||
arg - this is a list of variables the client wants.
|
# def msdp_cmd_send(self, *args):
|
||||||
"""
|
# """
|
||||||
ret = []
|
# Request the server to send a particular variable
|
||||||
if arg:
|
# to the client.
|
||||||
for var in make_iter(arg):
|
|
||||||
try:
|
# arg - this is a list of variables the client wants.
|
||||||
ret.append(MSDP_REPORTABLE[var.upper()])# (send=True))
|
# """
|
||||||
except Exception:
|
# ret = []
|
||||||
ret.append("ERROR")#logger.log_trace()
|
# for var in make_iter(arg)
|
||||||
return ret
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# for var in make_iter(arg):
|
||||||
|
# try:
|
||||||
|
# ret.append(MSDP_REPORTABLE[var.upper()])# (send=True))
|
||||||
|
# except Exception:
|
||||||
|
# ret.append("ERROR")#logger.log_trace()
|
||||||
|
# return ret
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -127,7 +127,6 @@ class PortalSessionHandler(SessionHandler):
|
||||||
in from the protocol to the server. data is
|
in from the protocol to the server. data is
|
||||||
serialized before passed on.
|
serialized before passed on.
|
||||||
"""
|
"""
|
||||||
#print "portal_data_in:", string
|
|
||||||
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
self.portal.amp_protocol.call_remote_MsgPortal2Server(session.sessid,
|
||||||
msg=text,
|
msg=text,
|
||||||
data=kwargs)
|
data=kwargs)
|
||||||
|
|
@ -138,6 +137,7 @@ class PortalSessionHandler(SessionHandler):
|
||||||
for session in self.sessions.values():
|
for session in self.sessions.values():
|
||||||
session.data_out(message)
|
session.data_out(message)
|
||||||
|
|
||||||
|
|
||||||
def data_out(self, sessid, text=None, **kwargs):
|
def data_out(self, sessid, text=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
Called by server for having the portal relay messages and data
|
Called by server for having the portal relay messages and data
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ from src.server.session import Session
|
||||||
from src.server.portal import ttype, mssp, msdp
|
from src.server.portal import ttype, mssp, msdp
|
||||||
from src.server.portal.mccp import Mccp, mccp_compress, MCCP
|
from src.server.portal.mccp import Mccp, mccp_compress, MCCP
|
||||||
from src.utils import utils, ansi, logger
|
from src.utils import utils, ansi, logger
|
||||||
|
from src.utils.utils import make_iter, is_iter
|
||||||
|
|
||||||
_RE_N = re.compile(r"\{n$")
|
_RE_N = re.compile(r"\{n$")
|
||||||
|
|
||||||
|
|
@ -36,14 +37,13 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
# negotiate ttype (client info)
|
# negotiate ttype (client info)
|
||||||
#self.ttype = ttype.Ttype(self)
|
#self.ttype = ttype.Ttype(self)
|
||||||
# negotiate mssp (crawler communication)
|
# negotiate mssp (crawler communication)
|
||||||
self.mssp = mssp.Mssp(self)
|
#self.mssp = mssp.Mssp(self)
|
||||||
# msdp
|
# msdp
|
||||||
#self.msdp = msdp.Msdp(self)
|
self.msdp = msdp.Msdp(self)
|
||||||
# add this new connection to sessionhandler so
|
# add this new connection to sessionhandler so
|
||||||
# the Server becomes aware of it.
|
# the Server becomes aware of it.
|
||||||
self.sessionhandler.connect(self)
|
self.sessionhandler.connect(self)
|
||||||
|
|
||||||
|
|
||||||
def enableRemote(self, option):
|
def enableRemote(self, option):
|
||||||
"""
|
"""
|
||||||
This sets up the remote-activated options we allow for this protocol.
|
This sets up the remote-activated options we allow for this protocol.
|
||||||
|
|
@ -69,7 +69,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
else:
|
else:
|
||||||
return super(TelnetProtocol, self).disableLocal(option)
|
return super(TelnetProtocol, self).disableLocal(option)
|
||||||
|
|
||||||
|
|
||||||
def connectionLost(self, reason):
|
def connectionLost(self, reason):
|
||||||
"""
|
"""
|
||||||
This is executed when the connection is lost for
|
This is executed when the connection is lost for
|
||||||
|
|
@ -86,13 +85,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
be handled in line mode. Some clients also sends an erroneous
|
be handled in line mode. Some clients also sends an erroneous
|
||||||
line break after IAC, which we must watch out for.
|
line break after IAC, which we must watch out for.
|
||||||
"""
|
"""
|
||||||
#print "dataRcv (%s):" % data,
|
|
||||||
#try:
|
|
||||||
# for b in data:
|
|
||||||
# print ord(b),
|
|
||||||
# print ""
|
|
||||||
#except Exception, e:
|
|
||||||
# print str(e) + ":", str(data)
|
|
||||||
|
|
||||||
if data and data[0] == IAC or self.iaw_mode:
|
if data and data[0] == IAC or self.iaw_mode:
|
||||||
try:
|
try:
|
||||||
|
|
@ -103,8 +95,16 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
else:
|
else:
|
||||||
self.iaw_mode = False
|
self.iaw_mode = False
|
||||||
return
|
return
|
||||||
except Exception:
|
except Exception, err1:
|
||||||
logger.log_trace()
|
conv = ""
|
||||||
|
try:
|
||||||
|
for b in data:
|
||||||
|
conv += " " + repr(ord(b))
|
||||||
|
except Exception, err2:
|
||||||
|
conv = str(err2) + ":", str(data)
|
||||||
|
out = "Telnet Error (%s): %s (%s)" % (err1, data, conv)
|
||||||
|
logger.log_trace(out)
|
||||||
|
return
|
||||||
# if we get to this point the command must end with a linebreak.
|
# if we get to this point the command must end with a linebreak.
|
||||||
# We make sure to add it, to fix some clients messing this up.
|
# We make sure to add it, to fix some clients messing this up.
|
||||||
data = data.rstrip("\r\n") + "\n"
|
data = data.rstrip("\r\n") + "\n"
|
||||||
|
|
@ -131,7 +131,7 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
Telnet method called when data is coming in over the telnet
|
Telnet method called when data is coming in over the telnet
|
||||||
connection. We pass it on to the game engine directly.
|
connection. We pass it on to the game engine directly.
|
||||||
"""
|
"""
|
||||||
self.sessionhandler.data_in(self, string)
|
self.data_in(text=string)
|
||||||
|
|
||||||
|
|
||||||
# Session hooks
|
# Session hooks
|
||||||
|
|
@ -145,11 +145,17 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
self.data_out(reason)
|
self.data_out(reason)
|
||||||
self.connectionLost(reason)
|
self.connectionLost(reason)
|
||||||
|
|
||||||
|
def data_in(self, text=None, **kwargs):
|
||||||
|
"""
|
||||||
|
Data Telnet -> Server
|
||||||
|
"""
|
||||||
|
self.sessionhandler.data_in(self, text=text, **kwargs)
|
||||||
|
|
||||||
def data_out(self, text=None, **kwargs):
|
def data_out(self, text=None, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
Data Evennia -> Player.
|
||||||
generic hook method for engine to call in order to send data
|
generic hook method for engine to call in order to send data
|
||||||
through the telnet connection.
|
through the telnet connection.
|
||||||
Data Evennia -> Player.
|
|
||||||
|
|
||||||
valid telnet kwargs:
|
valid telnet kwargs:
|
||||||
raw=True - pass string through without any ansi processing (i.e. include Evennia
|
raw=True - pass string through without any ansi processing (i.e. include Evennia
|
||||||
|
|
@ -163,6 +169,15 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
self.sendLine(str(e))
|
self.sendLine(str(e))
|
||||||
return
|
return
|
||||||
|
if "oob" in kwargs:
|
||||||
|
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob"))
|
||||||
|
if "MSDP" in self.protocol_flags:
|
||||||
|
for cmdname, args, kwargs in oobstruct:
|
||||||
|
#print "cmdname, args, kwargs:", cmdname, args, kwargs
|
||||||
|
msdp_string = self.msdp.evennia_to_msdp(cmdname, *args, **kwargs)
|
||||||
|
#print "msdp_string:", msdp_string
|
||||||
|
self.msdp.data_out(msdp_string)
|
||||||
|
|
||||||
ttype = self.protocol_flags.get('TTYPE', {})
|
ttype = self.protocol_flags.get('TTYPE', {})
|
||||||
raw = kwargs.get("raw", False)
|
raw = kwargs.get("raw", False)
|
||||||
nomarkup = not (ttype or ttype.get('256 COLORS') or ttype.get('ANSI') or not ttype.get("init_done"))
|
nomarkup = not (ttype or ttype.get('256 COLORS') or ttype.get('ANSI') or not ttype.get("init_done"))
|
||||||
|
|
|
||||||
|
|
@ -214,10 +214,14 @@ class Evennia(object):
|
||||||
[(o.typeclass, o.at_init()) for o in ObjectDB.get_all_cached_instances()]
|
[(o.typeclass, o.at_init()) for o in ObjectDB.get_all_cached_instances()]
|
||||||
[(p.typeclass, p.at_init()) for p in PlayerDB.get_all_cached_instances()]
|
[(p.typeclass, p.at_init()) for p in PlayerDB.get_all_cached_instances()]
|
||||||
|
|
||||||
|
with open(SERVER_RESTART, 'r') as f:
|
||||||
|
mode = f.read()
|
||||||
|
if mode in ('True', 'reload'):
|
||||||
|
from src.server.oobhandler import OOB_HANDLER
|
||||||
|
OOB_HANDLER.restore()
|
||||||
|
|
||||||
if SERVER_STARTSTOP_MODULE:
|
if SERVER_STARTSTOP_MODULE:
|
||||||
# call correct server hook based on start file value
|
# call correct server hook based on start file value
|
||||||
with open(SERVER_RESTART, 'r') as f:
|
|
||||||
mode = f.read()
|
|
||||||
if mode in ('True', 'reload'):
|
if mode in ('True', 'reload'):
|
||||||
# True was the old reload flag, kept for compatibilty
|
# True was the old reload flag, kept for compatibilty
|
||||||
SERVER_STARTSTOP_MODULE.at_server_reload_start()
|
SERVER_STARTSTOP_MODULE.at_server_reload_start()
|
||||||
|
|
@ -280,6 +284,9 @@ class Evennia(object):
|
||||||
yield self.sessions.all_sessions_portal_sync()
|
yield self.sessions.all_sessions_portal_sync()
|
||||||
ServerConfig.objects.conf("server_restart_mode", "reload")
|
ServerConfig.objects.conf("server_restart_mode", "reload")
|
||||||
|
|
||||||
|
from src.server.oobhandler import OOB_HANDLER
|
||||||
|
OOB_HANDLER.save()
|
||||||
|
|
||||||
if SERVER_STARTSTOP_MODULE:
|
if SERVER_STARTSTOP_MODULE:
|
||||||
SERVER_STARTSTOP_MODULE.at_server_reload_stop()
|
SERVER_STARTSTOP_MODULE.at_server_reload_stop()
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,7 @@ from src.server.session import Session
|
||||||
IDLE_COMMAND = settings.IDLE_COMMAND
|
IDLE_COMMAND = settings.IDLE_COMMAND
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_ObjectDB = None
|
_ObjectDB = None
|
||||||
|
_OOB_HANDLER = None
|
||||||
|
|
||||||
# load optional out-of-band function module
|
# load optional out-of-band function module
|
||||||
OOB_PLUGIN_MODULE = settings.OOB_PLUGIN_MODULE
|
OOB_PLUGIN_MODULE = settings.OOB_PLUGIN_MODULE
|
||||||
|
|
@ -136,6 +137,14 @@ class ServerSession(Session):
|
||||||
return self.logged_in and self.puppet
|
return self.logged_in and self.puppet
|
||||||
get_character = get_puppet
|
get_character = get_puppet
|
||||||
|
|
||||||
|
def get_puppet_or_player(self):
|
||||||
|
"""
|
||||||
|
Returns session if not logged in; puppet if one exists, otherwise return the player.
|
||||||
|
"""
|
||||||
|
if self.logged_in:
|
||||||
|
return self.puppet if self.puppet else self.player
|
||||||
|
return None
|
||||||
|
|
||||||
def log(self, message, channel=True):
|
def log(self, message, channel=True):
|
||||||
"""
|
"""
|
||||||
Emits session info to the appropriate outputs and info channels.
|
Emits session info to the appropriate outputs and info channels.
|
||||||
|
|
@ -178,8 +187,14 @@ class ServerSession(Session):
|
||||||
cmdhandler.cmdhandler(self, text, callertype="session", sessid=self.sessid)
|
cmdhandler.cmdhandler(self, text, callertype="session", sessid=self.sessid)
|
||||||
self.update_session_counters()
|
self.update_session_counters()
|
||||||
if "oob" in kwargs:
|
if "oob" in kwargs:
|
||||||
# relay to OOB handler
|
# handle oob instructions
|
||||||
pass
|
global _OOB_HANDLER
|
||||||
|
if not _OOB_HANDLER:
|
||||||
|
from src.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
|
||||||
|
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob", None))
|
||||||
|
for (funcname, args, kwargs) in oobstruct:
|
||||||
|
if funcname:
|
||||||
|
_OOB_HANDLER.execute_cmd(self, funcname, *args, **kwargs)
|
||||||
|
|
||||||
execute_cmd = data_in # alias
|
execute_cmd = data_in # alias
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,7 @@ _ServerSession = None
|
||||||
_ServerConfig = None
|
_ServerConfig = None
|
||||||
_ScriptDB = None
|
_ScriptDB = None
|
||||||
|
|
||||||
|
|
||||||
# AMP signals
|
# AMP signals
|
||||||
PCONN = chr(1) # portal session connect
|
PCONN = chr(1) # portal session connect
|
||||||
PDISCONN = chr(2) # portal session disconnect
|
PDISCONN = chr(2) # portal session disconnect
|
||||||
|
|
@ -96,6 +97,55 @@ class SessionHandler(object):
|
||||||
"""
|
"""
|
||||||
return dict((sessid, sess.get_sync_data()) for sessid, sess in self.sessions.items())
|
return dict((sessid, sess.get_sync_data()) for sessid, sess in self.sessions.items())
|
||||||
|
|
||||||
|
def oobstruct_parser(self, oobstruct):
|
||||||
|
"""
|
||||||
|
Helper method for each session to use to parse oob structures
|
||||||
|
(The 'oob' kwarg of the msg() method)
|
||||||
|
allowed oob structures are
|
||||||
|
cmdname
|
||||||
|
((cmdname,), (cmdname,))
|
||||||
|
(cmdname,(arg, ))
|
||||||
|
(cmdname,(arg1,arg2))
|
||||||
|
(cmdname,{key:val,key2:val2})
|
||||||
|
(cmdname, (args,), {kwargs})
|
||||||
|
((cmdname, (arg1,arg2)), cmdname, (cmdname, (arg1,)))
|
||||||
|
outputs an ordered structure on the form
|
||||||
|
((cmdname, (args,), {kwargs}), ...), where the two last parts of each tuple may be empty
|
||||||
|
"""
|
||||||
|
def _parse(oobstruct):
|
||||||
|
slen = len(oobstruct)
|
||||||
|
if not oobstruct:
|
||||||
|
return tuple(None, (), {})
|
||||||
|
elif not hasattr(oobstruct, "__iter__"):
|
||||||
|
# a singular command name, without arguments or kwargs
|
||||||
|
return (oobstruct.lower(), (), {})
|
||||||
|
# regardless of number of args/kwargs, the first element must be the function name.
|
||||||
|
# we will not catch this error if not, but allow it to propagate.
|
||||||
|
if slen == 1:
|
||||||
|
return (oobstruct[0].lower(), (), {})
|
||||||
|
elif slen == 2:
|
||||||
|
if isinstance(oobstruct[1], dict):
|
||||||
|
# cmdname, {kwargs}
|
||||||
|
return (oobstruct[0].lower(), (), dict(oobstruct[1]))
|
||||||
|
elif isinstance(oobstruct[1], (tuple, list)):
|
||||||
|
# cmdname, (args,)
|
||||||
|
return (oobstruct[0].lower(), tuple(oobstruct[1]), {})
|
||||||
|
else:
|
||||||
|
# cmdname, (args,), {kwargs}
|
||||||
|
return (oobstruct[0].lower(), tuple(oobstruct[1]), dict(oobstruct[2]))
|
||||||
|
|
||||||
|
if hasattr(oobstruct, "__iter__"):
|
||||||
|
# differentiate between (cmdname, cmdname), (cmdname, args, kwargs) and ((cmdname,args,kwargs), (cmdname,args,kwargs), ...)
|
||||||
|
|
||||||
|
if oobstruct and isinstance(oobstruct[0], basestring):
|
||||||
|
return (tuple(_parse(oobstruct)),)
|
||||||
|
else:
|
||||||
|
out = []
|
||||||
|
for oobpart in oobstruct:
|
||||||
|
out.append(_parse(oobpart))
|
||||||
|
return (tuple(out),)
|
||||||
|
return (_parse(oobstruct),)
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
# Server-SessionHandler class
|
# Server-SessionHandler class
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
|
|
@ -357,6 +407,7 @@ class ServerSessionHandler(SessionHandler):
|
||||||
return self.sessions.get(sessid)
|
return self.sessions.get(sessid)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
def announce_all(self, message):
|
def announce_all(self, message):
|
||||||
"""
|
"""
|
||||||
Send message to all connected sessions
|
Send message to all connected sessions
|
||||||
|
|
@ -379,5 +430,4 @@ class ServerSessionHandler(SessionHandler):
|
||||||
if session:
|
if session:
|
||||||
session.data_in(text=text, **kwargs)
|
session.data_in(text=text, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
SESSIONS = ServerSessionHandler()
|
SESSIONS = ServerSessionHandler()
|
||||||
|
|
|
||||||
|
|
@ -199,7 +199,7 @@ MSSP_META_MODULE = ""
|
||||||
# Module holding OOB (Out of Band) hook objects. This allows for customization
|
# Module holding OOB (Out of Band) hook objects. This allows for customization
|
||||||
# and expansion of which hooks OOB protocols are allowed to call on the server
|
# and expansion of which hooks OOB protocols are allowed to call on the server
|
||||||
# protocols for attaching tracker hooks for when various object field change
|
# protocols for attaching tracker hooks for when various object field change
|
||||||
OOB_PLUGIN_MODULE = ""
|
OOB_PLUGIN_MODULE = "src.server.oob_defaults"
|
||||||
# Tuple of modules implementing lock functions. All callable functions
|
# Tuple of modules implementing lock functions. All callable functions
|
||||||
# inside these modules will be available as lock functions.
|
# inside these modules will be available as lock functions.
|
||||||
LOCK_FUNC_MODULES = ("src.locks.lockfuncs",)
|
LOCK_FUNC_MODULES = ("src.locks.lockfuncs",)
|
||||||
|
|
|
||||||
|
|
@ -437,7 +437,7 @@ class NickHandler(AttributeHandler):
|
||||||
with categories nick_<nicktype>
|
with categories nick_<nicktype>
|
||||||
"""
|
"""
|
||||||
def has(self, key, category="inputline"):
|
def has(self, key, category="inputline"):
|
||||||
categry = "nick_%s" % category
|
category = "nick_%s" % category
|
||||||
return super(NickHandler, self).has(key, category=category)
|
return super(NickHandler, self).has(key, category=category)
|
||||||
|
|
||||||
def get(self, key=None, category="inputline", **kwargs):
|
def get(self, key=None, category="inputline", **kwargs):
|
||||||
|
|
@ -462,6 +462,34 @@ class NickHandler(AttributeHandler):
|
||||||
return super(NickHandler, self).all(category=category)
|
return super(NickHandler, self).all(category=category)
|
||||||
return _GA(self.obj, self._m2m_fieldname).filter(db_category__startswith="nick_")
|
return _GA(self.obj, self._m2m_fieldname).filter(db_category__startswith="nick_")
|
||||||
|
|
||||||
|
class NAttributeHandler(object):
|
||||||
|
"""
|
||||||
|
This stand-alone handler manages non-database saved properties by storing them
|
||||||
|
as properties on obj.ndb. It has the same methods as AttributeHandler, but they
|
||||||
|
are much simplified.
|
||||||
|
"""
|
||||||
|
def __init__(self, obj):
|
||||||
|
"initialized on the object"
|
||||||
|
self.ndb = _GA(obj, "ndb")
|
||||||
|
def has(self, key):
|
||||||
|
"Check if object has this attribute or not"
|
||||||
|
return _GA(self.ndb, key) # ndb returns None if not found
|
||||||
|
def get(self, key):
|
||||||
|
"Returns named key value"
|
||||||
|
return _GA(self.ndb, key)
|
||||||
|
def add(self, key, value):
|
||||||
|
"Add new key and value"
|
||||||
|
_SA(self.ndb, key, value)
|
||||||
|
def remove(self, key):
|
||||||
|
"Remove key from storage"
|
||||||
|
_DA(self.ndb, key)
|
||||||
|
def all(self):
|
||||||
|
"List all keys stored"
|
||||||
|
if callable(self.ndb.all):
|
||||||
|
return self.ndb.all()
|
||||||
|
else:
|
||||||
|
return [val for val in self.ndb.__dict__.keys() if not val.startswith('_')]
|
||||||
|
|
||||||
#------------------------------------------------------------
|
#------------------------------------------------------------
|
||||||
#
|
#
|
||||||
# Tags
|
# Tags
|
||||||
|
|
@ -645,6 +673,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
_SA(self, "dbobj", self) # this allows for self-reference
|
_SA(self, "dbobj", self) # this allows for self-reference
|
||||||
_SA(self, "locks", LockHandler(self))
|
_SA(self, "locks", LockHandler(self))
|
||||||
_SA(self, "permissions", PermissionHandler(self))
|
_SA(self, "permissions", PermissionHandler(self))
|
||||||
|
_SA(self, "nattributes", NAttributeHandler(self))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
"""
|
"""
|
||||||
|
|
@ -1148,6 +1177,9 @@ class TypedObject(SharedMemoryModel):
|
||||||
if hperm in perms and hpos > ppos)
|
if hperm in perms and hpos > ppos)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
#
|
||||||
|
# Memory management
|
||||||
|
#
|
||||||
|
|
||||||
def flush_from_cache(self):
|
def flush_from_cache(self):
|
||||||
"""
|
"""
|
||||||
|
|
@ -1157,6 +1189,60 @@ class TypedObject(SharedMemoryModel):
|
||||||
"""
|
"""
|
||||||
self.__class__.flush_cached_instance(self)
|
self.__class__.flush_cached_instance(self)
|
||||||
|
|
||||||
|
#
|
||||||
|
# Attribute storage
|
||||||
|
#
|
||||||
|
|
||||||
|
#@property db
|
||||||
|
def __db_get(self):
|
||||||
|
"""
|
||||||
|
Attribute handler wrapper. Allows for the syntax
|
||||||
|
obj.db.attrname = value
|
||||||
|
and
|
||||||
|
value = obj.db.attrname
|
||||||
|
and
|
||||||
|
del obj.db.attrname
|
||||||
|
and
|
||||||
|
all_attr = obj.db.all (unless there is no attribute named 'all', in which
|
||||||
|
case that will be returned instead).
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
return self._db_holder
|
||||||
|
except AttributeError:
|
||||||
|
class DbHolder(object):
|
||||||
|
"Holder for allowing property access of attributes"
|
||||||
|
def __init__(self, obj):
|
||||||
|
_SA(self, 'obj', obj)
|
||||||
|
_SA(self, "attrhandler", _GA(_GA(self, "obj"), "attributes"))
|
||||||
|
def __getattribute__(self, attrname):
|
||||||
|
if attrname == 'all':
|
||||||
|
# we allow to overload our default .all
|
||||||
|
attr = _GA(self, "attrhandler").get("all")
|
||||||
|
if attr:
|
||||||
|
return attr
|
||||||
|
return _GA(self, 'all')
|
||||||
|
return _GA(self, "attrhandler").get(attrname)
|
||||||
|
def __setattr__(self, attrname, value):
|
||||||
|
_GA(self, "attrhandler").add(attrname, value)
|
||||||
|
def __delattr__(self, attrname):
|
||||||
|
_GA(self, "attrhandler").remove(attrname)
|
||||||
|
def get_all(self):
|
||||||
|
return _GA(self, "attrhandler").all()
|
||||||
|
all = property(get_all)
|
||||||
|
self._db_holder = DbHolder(self)
|
||||||
|
return self._db_holder
|
||||||
|
#@db.setter
|
||||||
|
def __db_set(self, value):
|
||||||
|
"Stop accidentally replacing the db object"
|
||||||
|
string = "Cannot assign directly to db object! "
|
||||||
|
string += "Use db.attr=value instead."
|
||||||
|
raise Exception(string)
|
||||||
|
#@db.deleter
|
||||||
|
def __db_del(self):
|
||||||
|
"Stop accidental deletion."
|
||||||
|
raise Exception("Cannot delete the db object!")
|
||||||
|
db = property(__db_get, __db_set, __db_del)
|
||||||
|
|
||||||
#
|
#
|
||||||
# Non-persistent (ndb) storage
|
# Non-persistent (ndb) storage
|
||||||
#
|
#
|
||||||
|
|
@ -1202,35 +1288,12 @@ class TypedObject(SharedMemoryModel):
|
||||||
raise Exception("Cannot delete the ndb object!")
|
raise Exception("Cannot delete the ndb object!")
|
||||||
ndb = property(__ndb_get, __ndb_set, __ndb_del)
|
ndb = property(__ndb_get, __ndb_set, __ndb_del)
|
||||||
|
|
||||||
#def nattr(self, attribute_name=None, value=None, delete=False):
|
|
||||||
# """
|
|
||||||
# This allows for assigning non-persistent data on the object using
|
|
||||||
# a method call. Will return None if trying to access a non-existing property.
|
|
||||||
# """
|
|
||||||
# if attribute_name == None:
|
|
||||||
# # act as a list method
|
|
||||||
# if callable(self.ndb.all):
|
|
||||||
# return self.ndb.all()
|
|
||||||
# else:
|
|
||||||
# return [val for val in self.ndb.__dict__.keys()
|
|
||||||
# if not val.startswith['_']]
|
|
||||||
# elif delete == True:
|
|
||||||
# if hasattr(self.ndb, attribute_name):
|
|
||||||
# _DA(_GA(self, "ndb"), attribute_name)
|
|
||||||
# elif value == None:
|
|
||||||
# # act as a getter.
|
|
||||||
# if hasattr(self.ndb, attribute_name):
|
|
||||||
# _GA(_GA(self, "ndb"), attribute_name)
|
|
||||||
# else:
|
|
||||||
# return None
|
|
||||||
# else:
|
|
||||||
# # act as a setter
|
|
||||||
# _SA(self.ndb, attribute_name, value)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
# Attribute handler methods - DEPRECATED!
|
# ***** DEPRECATED METHODS BELOW *******
|
||||||
#
|
#
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
@ -1366,56 +1429,31 @@ class TypedObject(SharedMemoryModel):
|
||||||
# creating a new attribute - check access on storing object!
|
# creating a new attribute - check access on storing object!
|
||||||
_GA(self, "attributes").add(attribute_name, value, accessing_obj=accessing_object, default_access=default_access_create)
|
_GA(self, "attributes").add(attribute_name, value, accessing_obj=accessing_object, default_access=default_access_create)
|
||||||
|
|
||||||
#@property
|
def nattr(self, attribute_name=None, value=None, delete=False):
|
||||||
def __db_get(self):
|
|
||||||
"""
|
"""
|
||||||
A second convenience wrapper for the the attribute methods. It
|
This allows for assigning non-persistent data on the object using
|
||||||
allows for the syntax
|
a method call. Will return None if trying to access a non-existing property.
|
||||||
obj.db.attrname = value
|
|
||||||
and
|
|
||||||
value = obj.db.attrname
|
|
||||||
and
|
|
||||||
del obj.db.attrname
|
|
||||||
and
|
|
||||||
all_attr = obj.db.all (unless there is no attribute named 'all', in which
|
|
||||||
case that will be returned instead).
|
|
||||||
"""
|
"""
|
||||||
try:
|
logger.log_depmsg("obj.nattr() is deprecated. Use obj.nattributes instead.")
|
||||||
return self._db_holder
|
if attribute_name == None:
|
||||||
except AttributeError:
|
# act as a list method
|
||||||
class DbHolder(object):
|
if callable(self.ndb.all):
|
||||||
"Holder for allowing property access of attributes"
|
return self.ndb.all()
|
||||||
def __init__(self, obj):
|
else:
|
||||||
_SA(self, 'obj', obj)
|
return [val for val in self.ndb.__dict__.keys()
|
||||||
_SA(self, "attrhandler", _GA(_GA(self, "obj"), "attributes"))
|
if not val.startswith['_']]
|
||||||
def __getattribute__(self, attrname):
|
elif delete == True:
|
||||||
if attrname == 'all':
|
if hasattr(self.ndb, attribute_name):
|
||||||
# we allow to overload our default .all
|
_DA(_GA(self, "ndb"), attribute_name)
|
||||||
attr = _GA(self, "attrhandler").get("all")
|
elif value == None:
|
||||||
if attr:
|
# act as a getter.
|
||||||
return attr
|
if hasattr(self.ndb, attribute_name):
|
||||||
return _GA(self, 'all')
|
_GA(_GA(self, "ndb"), attribute_name)
|
||||||
return _GA(self, "attrhandler").get(attrname)
|
else:
|
||||||
def __setattr__(self, attrname, value):
|
return None
|
||||||
_GA(self, "attrhandler").add(attrname, value)
|
else:
|
||||||
def __delattr__(self, attrname):
|
# act as a setter
|
||||||
_GA(self, "attrhandler").remove(attrname)
|
_SA(self.ndb, attribute_name, value)
|
||||||
def get_all(self):
|
|
||||||
return _GA(self, "attrhandler").all()
|
|
||||||
all = property(get_all)
|
|
||||||
self._db_holder = DbHolder(self)
|
|
||||||
return self._db_holder
|
|
||||||
#@db.setter
|
|
||||||
def __db_set(self, value):
|
|
||||||
"Stop accidentally replacing the db object"
|
|
||||||
string = "Cannot assign directly to db object! "
|
|
||||||
string += "Use db.attr=value instead."
|
|
||||||
raise Exception(string)
|
|
||||||
#@db.deleter
|
|
||||||
def __db_del(self):
|
|
||||||
"Stop accidental deletion."
|
|
||||||
raise Exception("Cannot delete the db object!")
|
|
||||||
db = property(__db_get, __db_set, __db_del)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ def pack_dbobj(item):
|
||||||
# build the internal representation as a tuple ("__packed_dbobj__", key, creation_time, id)
|
# build the internal representation as a tuple ("__packed_dbobj__", key, creation_time, id)
|
||||||
return natural_key and ('__packed_dbobj__', natural_key, _TO_DATESTRING(obj), _GA(obj, "id")) or item
|
return natural_key and ('__packed_dbobj__', natural_key, _TO_DATESTRING(obj), _GA(obj, "id")) or item
|
||||||
|
|
||||||
def _unpack_dbobj(item):
|
def unpack_dbobj(item):
|
||||||
"""
|
"""
|
||||||
Check and convert internal representations back to Django database models.
|
Check and convert internal representations back to Django database models.
|
||||||
The fact that item is a packed dbobj should be checked before this call.
|
The fact that item is a packed dbobj should be checked before this call.
|
||||||
|
|
@ -209,7 +209,9 @@ def _unpack_dbobj(item):
|
||||||
except ObjectDoesNotExist:
|
except ObjectDoesNotExist:
|
||||||
return None
|
return None
|
||||||
# even if we got back a match, check the sanity of the date (some databases may 're-use' the id)
|
# even if we got back a match, check the sanity of the date (some databases may 're-use' the id)
|
||||||
return _TO_DATESTRING(obj.dbobj) == item[2] and obj or None
|
try: dbobj = obj.dbobj
|
||||||
|
except AttributeError: dbobj = obj
|
||||||
|
return _TO_DATESTRING(dbobj) == item[2] and obj or None
|
||||||
|
|
||||||
#
|
#
|
||||||
# Access methods
|
# Access methods
|
||||||
|
|
@ -267,7 +269,7 @@ def from_pickle(data, db_obj=None):
|
||||||
return item
|
return item
|
||||||
elif _IS_PACKED_DBOBJ(item):
|
elif _IS_PACKED_DBOBJ(item):
|
||||||
# this must be checked before tuple
|
# this must be checked before tuple
|
||||||
return _unpack_dbobj(item)
|
return unpack_dbobj(item)
|
||||||
elif dtype == tuple:
|
elif dtype == tuple:
|
||||||
return tuple(process_item(val) for val in item)
|
return tuple(process_item(val) for val in item)
|
||||||
elif dtype == dict:
|
elif dtype == dict:
|
||||||
|
|
@ -289,7 +291,7 @@ def from_pickle(data, db_obj=None):
|
||||||
return item
|
return item
|
||||||
elif _IS_PACKED_DBOBJ(item):
|
elif _IS_PACKED_DBOBJ(item):
|
||||||
# this must be checked before tuple
|
# this must be checked before tuple
|
||||||
return _unpack_dbobj(item)
|
return unpack_dbobj(item)
|
||||||
elif dtype == tuple:
|
elif dtype == tuple:
|
||||||
return tuple(process_tree(val) for val in item)
|
return tuple(process_tree(val) for val in item)
|
||||||
elif dtype == list:
|
elif dtype == list:
|
||||||
|
|
|
||||||
|
|
@ -729,10 +729,10 @@ def mod_import(module):
|
||||||
|
|
||||||
def all_from_module(module):
|
def all_from_module(module):
|
||||||
"""
|
"""
|
||||||
Return all global-level variables from a module
|
Return all global-level variables from a module as a dict
|
||||||
"""
|
"""
|
||||||
mod = mod_import(module)
|
mod = mod_import(module)
|
||||||
return [val for key, val in mod.__dict__.items() if not (key.startswith("_") or ismodule(val))]
|
return dict((key, val) for key, val in mod.__dict__.items() if not (key.startswith("_") or ismodule(val)))
|
||||||
|
|
||||||
def variable_from_module(module, variable=None, default=None):
|
def variable_from_module(module, variable=None, default=None):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue