First, untested version of the OOBhandler mechanism.
This commit is contained in:
parent
90a64a3780
commit
b0b0fa7983
9 changed files with 454 additions and 106 deletions
|
|
@ -396,6 +396,13 @@ class DoNothing(Script):
|
||||||
self.key = "sys_do_nothing"
|
self.key = "sys_do_nothing"
|
||||||
self.desc = _("This is an empty placeholder script.")
|
self.desc = _("This is an empty placeholder script.")
|
||||||
|
|
||||||
|
class Store(Script):
|
||||||
|
"Simple storage script"
|
||||||
|
def at_script_creation(self):
|
||||||
|
"Setup the script"
|
||||||
|
self.key = "sys_storage"
|
||||||
|
self.desc = _("This is a generic storage container.")
|
||||||
|
|
||||||
class CheckSessions(Script):
|
class CheckSessions(Script):
|
||||||
"Check sessions regularly."
|
"Check sessions regularly."
|
||||||
def at_script_creation(self):
|
def at_script_creation(self):
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ Central caching module.
|
||||||
|
|
||||||
from sys import getsizeof
|
from sys import getsizeof
|
||||||
from collections import defaultdict
|
from collections import defaultdict
|
||||||
from weakref import WeakKeyDictionary
|
|
||||||
|
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
|
|
@ -16,6 +15,14 @@ _ATTR_CACHE = defaultdict(dict)
|
||||||
_FIELD_CACHE = defaultdict(dict)
|
_FIELD_CACHE = defaultdict(dict)
|
||||||
_PROP_CACHE = defaultdict(dict)
|
_PROP_CACHE = defaultdict(dict)
|
||||||
|
|
||||||
|
# OOB hooks
|
||||||
|
_OOB_FIELD_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
_OOB_PROP_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
_OOB_ATTR_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
_OOB_NDB_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
_OOB_CUSTOM_UPDATE_HOOKS = defaultdict(dict)
|
||||||
|
|
||||||
|
_OOB_HANDLER = None # set by oob handler when it initializes
|
||||||
|
|
||||||
def get_cache_sizes():
|
def get_cache_sizes():
|
||||||
"""
|
"""
|
||||||
|
|
@ -46,16 +53,71 @@ def hashid(obj):
|
||||||
try:
|
try:
|
||||||
hid = _GA(obj, "_hashid")
|
hid = _GA(obj, "_hashid")
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
date, idnum = _GA(obj, "db_date_created"), _GA(obj, "id")
|
try:
|
||||||
if not idnum or not date:
|
date, idnum = _GA(obj, "db_date_created"), _GA(obj, "id")
|
||||||
# this will happen if setting properties on an object
|
if not idnum or not date:
|
||||||
# which is not yet saved
|
# this will happen if setting properties on an object
|
||||||
return None
|
# which is not yet saved
|
||||||
|
return None
|
||||||
|
except AttributeError:
|
||||||
|
# this happens if hashing something like ndb. We have to
|
||||||
|
# rely on memory adressing in this case.
|
||||||
|
date, idnum = "Nondb", id(obj)
|
||||||
# build the hashid
|
# build the hashid
|
||||||
hid = "%s-%s-#%s" % (_GA(obj, "__class__"), date, idnum)
|
hid = "%s-%s-#%s" % (_GA(obj, "__class__"), date, idnum)
|
||||||
_SA(obj, "_hashid", hid)
|
_SA(obj, "_hashid", hid)
|
||||||
return hid
|
return hid
|
||||||
|
|
||||||
|
# oob helper functions
|
||||||
|
def register_oob_update_hook(obj,name, entity="field"):
|
||||||
|
"""
|
||||||
|
Register hook function to be called when field/property/db/ndb is updated.
|
||||||
|
Given function will be called with function(obj, entityname, newvalue, *args, **kwargs)
|
||||||
|
entity - one of "field", "property", "db", "ndb" or "custom"
|
||||||
|
"""
|
||||||
|
hid = hashid(obj)
|
||||||
|
if hid:
|
||||||
|
if entity == "field":
|
||||||
|
global _OOB_FIELD_UPDATE_HOOKS
|
||||||
|
_OOB_FIELD_UPDATE_HOOKS[hid][name] = True
|
||||||
|
elif entity == "property":
|
||||||
|
global _OOB_PROP_UPDATE_HOOKS
|
||||||
|
_OOB_PROP_UPDATE_HOOKS[hid][name] = True
|
||||||
|
elif entity == "db":
|
||||||
|
global _OOB_ATTR_UPDATE_HOOKS
|
||||||
|
_OOB_ATTR_UPDATE_HOOKS[hid][name] = True
|
||||||
|
elif entity == "ndb":
|
||||||
|
global _OOB_NDB_UPDATE_HOOKS
|
||||||
|
_OOB_NDB_UPDATE_HOOKS[hid][name] = True
|
||||||
|
elif entity == "custom":
|
||||||
|
global _OOB_CUSTOM_UPDATE_HOOKS
|
||||||
|
_OOB_CUSTOM_UPDATE_HOOKS[hid][name] = True
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
return hid
|
||||||
|
|
||||||
|
def unregister_oob_update_hook(obj, name, entity="property"):
|
||||||
|
"""
|
||||||
|
Un-register a report hook
|
||||||
|
"""
|
||||||
|
hid = hashid(obj)
|
||||||
|
if hid:
|
||||||
|
global _OOB_FIELD_UPDATE_HOOKS,_OOB_PROP_UPDATE_HOOKS, _OOB_ATTR_UPDATE_HOOKS
|
||||||
|
global _OOB_CUSTOM_UPDATE_HOOKS, _OOB_NDB_UPDATE_HOOKS
|
||||||
|
if entity == "field" and name in _OOB_FIELD_UPDATE_HOOKS:
|
||||||
|
del _OOB_FIELD_UPDATE_HOOKS[hid][name]
|
||||||
|
elif entity == "property" and name in _OOB_PROP_UPDATE_HOOKS:
|
||||||
|
del _OOB_PROP_UPDATE_HOOKS[hid][name]
|
||||||
|
elif entity == "db" and name in _OOB_ATTR_UPDATE_HOOKS:
|
||||||
|
del _OOB_ATTR_UPDATE_HOOKS[hid][name]
|
||||||
|
elif entity == "ndb" and name in _OOB_NDB_UPDATE_HOOKS:
|
||||||
|
del _OOB_NDB_UPDATE_HOOKS[hid][name]
|
||||||
|
elif entity == "custom" and name in _OOB_CUSTOM_UPDATE_HOOKS:
|
||||||
|
del _OOB_CUSTOM_UPDATE_HOOKS[hid][name]
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
return hid
|
||||||
|
|
||||||
# on-object database field cache
|
# on-object database field cache
|
||||||
def get_field_cache(obj, name):
|
def get_field_cache(obj, name):
|
||||||
"On-model Cache handler."
|
"On-model Cache handler."
|
||||||
|
|
@ -78,6 +140,9 @@ def set_field_cache(obj, name, val):
|
||||||
if hid:
|
if hid:
|
||||||
global _FIELD_CACHE
|
global _FIELD_CACHE
|
||||||
_FIELD_CACHE[hid][name] = val
|
_FIELD_CACHE[hid][name] = val
|
||||||
|
# oob hook functionality
|
||||||
|
if _OOB_FIELD_UPDATE_HOOKS[hid].get(name):
|
||||||
|
_OOB_HANDLER.update(hid, name, val)
|
||||||
|
|
||||||
def del_field_cache(obj, name):
|
def del_field_cache(obj, name):
|
||||||
"On-model cache deleter"
|
"On-model cache deleter"
|
||||||
|
|
@ -110,7 +175,7 @@ def get_prop_cache(obj, name, default=None):
|
||||||
hid = hashid(obj)
|
hid = hashid(obj)
|
||||||
if hid:
|
if hid:
|
||||||
try:
|
try:
|
||||||
return _PROP_CACHE[hid][name]
|
val = _PROP_CACHE[hid][name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
return default
|
return default
|
||||||
_PROP_CACHE[hid][name] = val
|
_PROP_CACHE[hid][name] = val
|
||||||
|
|
@ -123,6 +188,11 @@ def set_prop_cache(obj, name, val):
|
||||||
if hid:
|
if hid:
|
||||||
global _PROP_CACHE
|
global _PROP_CACHE
|
||||||
_PROP_CACHE[hid][name] = val
|
_PROP_CACHE[hid][name] = val
|
||||||
|
# oob hook functionality
|
||||||
|
oob_hook = _OOB_PROP_UPDATE_HOOKS[hid].get(name)
|
||||||
|
if oob_hook:
|
||||||
|
oob_hook[0](obj.typeclass, name, val, *oob_hook[1], **oob_hook[2])
|
||||||
|
|
||||||
|
|
||||||
def del_prop_cache(obj, name):
|
def del_prop_cache(obj, name):
|
||||||
"On-model cache deleter"
|
"On-model cache deleter"
|
||||||
|
|
@ -130,7 +200,7 @@ def del_prop_cache(obj, name):
|
||||||
del _PROP_CACHE[hashid(obj)][name]
|
del _PROP_CACHE[hashid(obj)][name]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
def flush_field_cache(obj=None):
|
def flush_prop_cache(obj=None):
|
||||||
"On-model cache resetter"
|
"On-model cache resetter"
|
||||||
hid = hashid(obj)
|
hid = hashid(obj)
|
||||||
global _PROP_CACHE
|
global _PROP_CACHE
|
||||||
|
|
@ -152,10 +222,14 @@ def set_attr_cache(obj, attrname, attrobj):
|
||||||
"""
|
"""
|
||||||
Cache an attribute object
|
Cache an attribute object
|
||||||
"""
|
"""
|
||||||
global _ATTR_CACHE
|
|
||||||
hid = hashid(obj)
|
hid = hashid(obj)
|
||||||
if hid:
|
if hid:
|
||||||
|
global _ATTR_CACHE
|
||||||
_ATTR_CACHE[hid][attrname] = attrobj
|
_ATTR_CACHE[hid][attrname] = attrobj
|
||||||
|
# oob hook functionality
|
||||||
|
oob_hook = _OOB_ATTR_UPDATE_HOOKS[hid].get(attrname)
|
||||||
|
if oob_hook:
|
||||||
|
oob_hook[0](obj.typeclass, attrname, attrobj.value, *oob_hook[1], **oob_hook[2])
|
||||||
|
|
||||||
def del_attr_cache(obj, attrname):
|
def del_attr_cache(obj, attrname):
|
||||||
"""
|
"""
|
||||||
|
|
@ -177,3 +251,26 @@ def flush_attr_cache(obj=None):
|
||||||
else:
|
else:
|
||||||
# clean cache completely
|
# clean cache completely
|
||||||
_ATTR_CACHE = defaultdict(dict)
|
_ATTR_CACHE = defaultdict(dict)
|
||||||
|
|
||||||
|
|
||||||
|
def call_ndb_hooks(obj, attrname, value):
|
||||||
|
"""
|
||||||
|
No caching is done of ndb here, but
|
||||||
|
we use this as a way to call OOB hooks.
|
||||||
|
"""
|
||||||
|
hid = hashid(obj)
|
||||||
|
if hid:
|
||||||
|
oob_hook = _OOB_NDB_UPDATE_HOOKS[hid].get(attrname)
|
||||||
|
if oob_hook:
|
||||||
|
oob_hook[0](obj.typeclass, attrname, value, *oob_hook[1], **oob_hook[2])
|
||||||
|
|
||||||
|
def call_custom_hooks(obj, attrname, value):
|
||||||
|
"""
|
||||||
|
Custom handler for developers adding their own oob hooks, e.g. to
|
||||||
|
custom typeclass properties.
|
||||||
|
"""
|
||||||
|
hid = hashid(obj)
|
||||||
|
if hid:
|
||||||
|
oob_hook = _OOB_CUSTOM_UPDATE_HOOKS[hid].get(attrname)
|
||||||
|
if oob_hook:
|
||||||
|
oob_hook[0](obj.typeclass, attrname, value, *oob_hook[1], **oob_hook[2])
|
||||||
|
|
|
||||||
|
|
@ -107,7 +107,7 @@ class ServerConfig(SharedMemoryModel):
|
||||||
def __unicode__(self):
|
def __unicode__(self):
|
||||||
return "%s : %s" % (self.key, self.value)
|
return "%s : %s" % (self.key, self.value)
|
||||||
|
|
||||||
def store(key, value):
|
def store(self, key, value):
|
||||||
"""
|
"""
|
||||||
Wrap the storage (handles pickling)
|
Wrap the storage (handles pickling)
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -15,13 +15,6 @@ from django.conf import settings
|
||||||
from src.utils.utils import make_iter, mod_import
|
from src.utils.utils import make_iter, mod_import
|
||||||
from src.utils import logger
|
from src.utils import logger
|
||||||
|
|
||||||
# custom functions
|
|
||||||
OOC_MODULE = mod_import(settings.OOB_FUNC_MODULE)
|
|
||||||
OOC_FUNCS = dict((key.upper(), var) for key, var in OOC_MODULE if not key.startswith('__') and callable(var))
|
|
||||||
|
|
||||||
# MSDP commands supported by Evennia
|
|
||||||
MSDP_COMMANDS = ("LIST", "REPORT", "RESET", "SEND", "UNREPORT")
|
|
||||||
|
|
||||||
# MSDP-relevant telnet cmd/opt-codes
|
# MSDP-relevant telnet cmd/opt-codes
|
||||||
MSDP = chr(69)
|
MSDP = chr(69)
|
||||||
MSDP_VAR = chr(1)
|
MSDP_VAR = chr(1)
|
||||||
|
|
@ -36,6 +29,92 @@ regex_array = re.compile(r"%s(.*?)%s%s(.*?)%s" % (MSDP_VAR, MSDP_VAL, MSDP_ARRAY
|
||||||
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_varval = re.compile(r"%s(.*?)%s(.*?)" % (MSDP_VAR, MSDP_VAL)) # return 2-tuple
|
||||||
|
|
||||||
|
# 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 = 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
|
||||||
|
|
||||||
class Msdp(object):
|
class Msdp(object):
|
||||||
"""
|
"""
|
||||||
Implements the MSDP protocol.
|
Implements the MSDP protocol.
|
||||||
|
|
@ -51,6 +130,7 @@ class Msdp(object):
|
||||||
self.protocol.protocol_FLAGS['MSDP'] = False
|
self.protocol.protocol_FLAGS['MSDP'] = False
|
||||||
self.protocol.negotiationMap['MSDP'] = self.parse_msdp
|
self.protocol.negotiationMap['MSDP'] = self.parse_msdp
|
||||||
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 = {}
|
||||||
|
|
||||||
def no_msdp(self, option):
|
def no_msdp(self, option):
|
||||||
"No msdp supported or wanted"
|
"No msdp supported or wanted"
|
||||||
|
|
@ -110,7 +190,7 @@ class Msdp(object):
|
||||||
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
|
||||||
default functions (LIST, SEND etc) or a custom one
|
default functions (LIST, SEND etc) or a custom one
|
||||||
in OOC_FUNCS dictionary. command names are case-insensitive.
|
in OOB_FUNCS dictionary. command names are case-insensitive.
|
||||||
|
|
||||||
varname, var --> mapped to function varname(var)
|
varname, var --> mapped to function varname(var)
|
||||||
arrayname, array --> mapped to function arrayname(*array)
|
arrayname, array --> mapped to function arrayname(*array)
|
||||||
|
|
@ -133,6 +213,7 @@ class Msdp(object):
|
||||||
variables = dict((key.upper(), val) for key, val in regex_varval(regex_array.sub("", regex_table.sub("", data))))
|
variables = dict((key.upper(), val) for key, val in regex_varval(regex_array.sub("", regex_table.sub("", data))))
|
||||||
|
|
||||||
ret = ""
|
ret = ""
|
||||||
|
|
||||||
# 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"]))
|
ret += self.func_to_msdp("LIST", self.msdp_cmd_list(variables["LIST"]))
|
||||||
|
|
@ -150,7 +231,7 @@ class Msdp(object):
|
||||||
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*arrays["RESET"]))
|
ret += self.func_to_msdp("RESET", self.msdp_cmd_reset(*arrays["RESET"]))
|
||||||
del arrays["RESET"]
|
del arrays["RESET"]
|
||||||
if "SEND" in variables:
|
if "SEND" in variables:
|
||||||
ret += self.func_to_msdp("SEND", self.msdp_cmd_send((*variables["SEND"],)))
|
ret += self.func_to_msdp("SEND", self.msdp_cmd_send(*(variables["SEND"],)))
|
||||||
del variables["SEND"]
|
del variables["SEND"]
|
||||||
if "SEND" in arrays:
|
if "SEND" in arrays:
|
||||||
ret += self.func_to_msdp("SEND",self.msdp_cmd_send(*arrays["SEND"]))
|
ret += self.func_to_msdp("SEND",self.msdp_cmd_send(*arrays["SEND"]))
|
||||||
|
|
@ -159,17 +240,17 @@ class Msdp(object):
|
||||||
# if there are anything left we look for 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 = OOC_FUNCS.get(varname.upper())
|
ooc_func = MSDP_COMMANDS_CUSTOM.get(varname.upper())
|
||||||
if ooc_func:
|
if ooc_func:
|
||||||
ret += self.func_to_msdp(varname, ooc_func(var))
|
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 = OOC_FUNCS.get(arrayname.upper())
|
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
|
||||||
if ooc_func:
|
if ooc_func:
|
||||||
ret += self.func_to_msdp(arrayname, ooc_func(*array))
|
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 = OOC_FUNCS.get(arrayname.upper())
|
ooc_func = MSDP_COMMANDS_CUSTOM.get(arrayname.upper())
|
||||||
if ooc_func:
|
if ooc_func:
|
||||||
ret += self.func_to_msdp(tablename, ooc_func(**table))
|
ret += self.func_to_msdp(tablename, ooc_func(**table))
|
||||||
return ret
|
return ret
|
||||||
|
|
@ -184,28 +265,31 @@ class Msdp(object):
|
||||||
The List command allows for retrieving various info about the server/client
|
The List command allows for retrieving various info about the server/client
|
||||||
"""
|
"""
|
||||||
if arg == 'COMMANDS':
|
if arg == 'COMMANDS':
|
||||||
return self.func_to_msdp(arg, MSDP_COMMANDS))
|
return self.func_to_msdp(arg, MSDP_COMMANDS)
|
||||||
elif arg == 'LISTS':
|
elif arg == 'LISTS':
|
||||||
return self.func_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
|
return self.func_to_msdp(arg, ("COMMANDS", "LISTS", "CONFIGURABLE_VARIABLES",
|
||||||
"REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
|
"REPORTED_VARIABLES", "SENDABLE_VARIABLES"))
|
||||||
elif arg == 'CONFIGURABLE_VARIABLES':
|
elif arg == 'CONFIGURABLE_VARIABLES':
|
||||||
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
|
return self.func_to_msdp(arg, ("CLIENT_NAME", "CLIENT_VERSION", "PLUGIN_ID"))
|
||||||
elif arg == 'REPORTABLE_VARIABLES':
|
elif arg == 'REPORTABLE_VARIABLES':
|
||||||
return self.func_to_msdp(arg, self.MSDP_REPORTABLE.keys())
|
return self.func_to_msdp(arg, MSDP_REPORTABLE.keys())
|
||||||
elif arg == 'REPORTED_VARIABLES':
|
elif arg == 'REPORTED_VARIABLES':
|
||||||
return self.func_to_msdp(arg, self.MSDP_REPORTED.keys())
|
# the dynamically set items to report
|
||||||
|
return self.func_to_msdp(arg, self.msdp_reported.keys())
|
||||||
elif arg == 'SENDABLE_VARIABLES':
|
elif arg == 'SENDABLE_VARIABLES':
|
||||||
return self.func_to_msdp(arg, self.MSDP_SEND.keys())
|
return self.func_to_msdp(arg, MSDP_SENDABLE.keys())
|
||||||
else:
|
else:
|
||||||
return self.func_to_msdp("LIST", arg)
|
return self.func_to_msdp("LIST", arg)
|
||||||
|
|
||||||
|
# default msdp commands
|
||||||
|
|
||||||
def msdp_cmd_report(self, *arg):
|
def msdp_cmd_report(self, *arg):
|
||||||
"""
|
"""
|
||||||
The report command instructs the server to start reporting a
|
The report command instructs the server to start reporting a
|
||||||
reportable variable to the client.
|
reportable variable to the client.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.MSDP_REPORTABLE[arg](report=True)
|
MSDP_REPORTABLE[arg](report=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
|
|
||||||
|
|
@ -214,7 +298,7 @@ class Msdp(object):
|
||||||
Unreport a previously reported variable
|
Unreport a previously reported variable
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.MSDP_REPORTABLE[arg](report=False)
|
MSDP_REPORTABLE[arg](report=False)
|
||||||
except Exception:
|
except Exception:
|
||||||
self.logger.log_trace()
|
self.logger.log_trace()
|
||||||
|
|
||||||
|
|
@ -223,7 +307,7 @@ class Msdp(object):
|
||||||
The reset command resets a variable to its initial state.
|
The reset command resets a variable to its initial state.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
self.MSDP_REPORTABLE[arg](reset=True)
|
MSDP_REPORTABLE[arg](reset=True)
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
|
|
||||||
|
|
@ -237,79 +321,9 @@ class Msdp(object):
|
||||||
ret = []
|
ret = []
|
||||||
for var in make_iter(arg):
|
for var in make_iter(arg):
|
||||||
try:
|
try:
|
||||||
ret.append(self.MSDP_REPORTABLE[arg](send=True))
|
ret.append(MSDP_REPORTABLE[arg](send=True))
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
# MSDP_MAP 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.
|
|
||||||
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"}
|
|
||||||
|
|
|
||||||
224
src/server/oobhandler.py
Normal file
224
src/server/oobhandler.py
Normal file
|
|
@ -0,0 +1,224 @@
|
||||||
|
"""
|
||||||
|
OOB - Out-of-band central handler
|
||||||
|
|
||||||
|
This module presents a central API for requesting data from objects in
|
||||||
|
Evennia via OOB negotiation. It is meant specifically to be imported
|
||||||
|
and used by the module defined in settings.OOB_MODULE.
|
||||||
|
|
||||||
|
Import src.server.oobhandler and use the methods in OOBHANDLER.
|
||||||
|
|
||||||
|
The actual client protocol (MSDP, GMCP, whatever) does not matter at
|
||||||
|
this level, serialization is assumed to happen at the protocol level
|
||||||
|
only.
|
||||||
|
|
||||||
|
This module offers the following basic functionality:
|
||||||
|
|
||||||
|
track_passive - retrieve field, property, db/ndb attribute from an object, then continue reporting
|
||||||
|
changes henceforth. This is done efficiently and on-demand using hooks. This should be
|
||||||
|
used preferentially since it's very resource efficient.
|
||||||
|
track_active - this is an active reporting mechanism making use of a Script. This should normally
|
||||||
|
only be used if:
|
||||||
|
1) you want changes to be reported SLOWER than the actual rate of update (such
|
||||||
|
as only wanting to show an average of change over time)
|
||||||
|
2) the data you are reporting is NOT stored as a field/property/db/ndb on an object (such
|
||||||
|
as some sort of server statistic calculated on the fly).
|
||||||
|
|
||||||
|
Trivial operations such as get/setting individual properties one time is best done directly from
|
||||||
|
the OOB_MODULE functions.
|
||||||
|
|
||||||
|
"""
|
||||||
|
|
||||||
|
from collections import defaultdict
|
||||||
|
from src.scripts.objects import ScriptDB
|
||||||
|
from src.scripts.script import Script
|
||||||
|
from src.server import caches
|
||||||
|
from src.server.caches import hashid
|
||||||
|
from src.utils import logger, create
|
||||||
|
|
||||||
|
class _OOBTracker(Script):
|
||||||
|
"""
|
||||||
|
Active tracker script, handles subscriptions
|
||||||
|
"""
|
||||||
|
def at_script_creation(self):
|
||||||
|
"Called at script creation"
|
||||||
|
self.key = "oob_tracking_30" # default to 30 second interval
|
||||||
|
self.desc = "Active tracking of oob data"
|
||||||
|
self.interval = 30
|
||||||
|
self.persistent = False
|
||||||
|
self.start_delay = True
|
||||||
|
# holds dictionary of key:(function, (args,), {kwargs}) to call
|
||||||
|
self.db.subs = {}
|
||||||
|
|
||||||
|
def track(self, key, func, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Add sub to track. func(*args, **kwargs) will be called at self.interval.
|
||||||
|
key is a unique identifier for removing the tracking later.
|
||||||
|
"""
|
||||||
|
self.subs[key] = (func, args, kwargs)
|
||||||
|
|
||||||
|
def untrack(self, key):
|
||||||
|
"""
|
||||||
|
Clear a tracking. Return True if untracked successfully, None if
|
||||||
|
no previous track was found.
|
||||||
|
"""
|
||||||
|
if key in self.subs:
|
||||||
|
del self.subs[key]
|
||||||
|
if not self.subs:
|
||||||
|
# we have no more subs. Stop this script.
|
||||||
|
self.stop()
|
||||||
|
return True
|
||||||
|
|
||||||
|
def at_repeat(self):
|
||||||
|
"""
|
||||||
|
Loops through all subs, calling their given function
|
||||||
|
"""
|
||||||
|
for func, args, kwargs in self.subs:
|
||||||
|
try:
|
||||||
|
func(*args, **kwargs)
|
||||||
|
except Exception:
|
||||||
|
logger.log_trace()
|
||||||
|
|
||||||
|
class _OOBStore(Script):
|
||||||
|
"""
|
||||||
|
Store OOB data between restarts
|
||||||
|
"""
|
||||||
|
def at_script_creation(self):
|
||||||
|
"Called at script creation"
|
||||||
|
self.key = "oob_save_store"
|
||||||
|
self.desc = "Stores OOB data"
|
||||||
|
self.persistent = True
|
||||||
|
def save_oob_data(self, data):
|
||||||
|
self.db.store = data
|
||||||
|
def get_oob_data(self):
|
||||||
|
return self.db.store
|
||||||
|
|
||||||
|
class OOBhandler(object):
|
||||||
|
"""
|
||||||
|
Main Out-of-band handler
|
||||||
|
"""
|
||||||
|
def __init__(self):
|
||||||
|
"initialization"
|
||||||
|
self.track_passive_subs = defaultdict(dict)
|
||||||
|
scripts = ScriptDB.objects.filter(db_key__startswith="oob_tracking_")
|
||||||
|
self.track_active_subs = dict((s.interval, s) for s in scripts)
|
||||||
|
# set reference on caches module
|
||||||
|
caches._OOB_HANDLER = self
|
||||||
|
|
||||||
|
def track_passive(self, tracker, tracked, name, function, entity="db", *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Passively track changes to an object property,
|
||||||
|
attribute or non-db-attribute. Uses cache hooks to
|
||||||
|
do this on demand, without active tracking.
|
||||||
|
|
||||||
|
tracker - object who is tracking
|
||||||
|
tracked - object being tracked
|
||||||
|
name - field/property/attribute/ndb nam to watch
|
||||||
|
function - function object to call when entity update. When entitye <name>
|
||||||
|
is updated, this function will be called with called
|
||||||
|
with function(obj, name, new_value, *args, **kwargs)
|
||||||
|
*args - additional, optional arguments to send to function
|
||||||
|
entity (keyword) - the type of entity to track. One of
|
||||||
|
"property", "db", "ndb" or "custom" ("property" includes both
|
||||||
|
changes to database fields and cached on-model properties)
|
||||||
|
**kwargs - additional, optionak keywords to send to function
|
||||||
|
|
||||||
|
Only entities that are being -cached- can be tracked. For custom
|
||||||
|
on-typeclass properties, a custom hook needs to be created, calling
|
||||||
|
the update() function in this module whenever the tracked entity changes.
|
||||||
|
"""
|
||||||
|
|
||||||
|
# always store database object (in case typeclass changes along the way)
|
||||||
|
try: tracker = tracker.dbobj
|
||||||
|
except AttributeError: pass
|
||||||
|
try: tracked = tracked.dbobj
|
||||||
|
except AttributeError: pass
|
||||||
|
|
||||||
|
thid = hashid(tracked)
|
||||||
|
if not thid:
|
||||||
|
return
|
||||||
|
oob_call = (function, tracked, name, args, kwargs)
|
||||||
|
if thid not in self.track_passive_subs:
|
||||||
|
if entity in ("db", "ndb", "custom"):
|
||||||
|
caches.register_oob_update_hook(tracked, name, entity=entity)
|
||||||
|
elif entity == "property":
|
||||||
|
# track property/field. We must first determine which cache to use.
|
||||||
|
if hasattr(tracked, 'db_%s' % name.lstrip("db_")):
|
||||||
|
hid = caches.register_oob_update_hook(tracked, name, entity="field")
|
||||||
|
else:
|
||||||
|
hid = caches.register_oob_update_hook(tracked, name, entity="property")
|
||||||
|
if not self.track_pass_subs[hid][name]:
|
||||||
|
self.track_pass_subs[hid][name] = {tracker:oob_call}
|
||||||
|
else:
|
||||||
|
self.track_passive_subs[hid][name][tracker] = oob_call
|
||||||
|
|
||||||
|
def untrack_passive(self, tracker, tracked, name, entity="db"):
|
||||||
|
"""
|
||||||
|
Remove passive tracking from an object's entity.
|
||||||
|
entity - one of "property", "db", "ndb" or "custom"
|
||||||
|
"""
|
||||||
|
try: tracked = tracked.dbobj
|
||||||
|
except AttributeError: pass
|
||||||
|
|
||||||
|
thid = hashid(tracked)
|
||||||
|
if not thid:
|
||||||
|
return
|
||||||
|
if len(self.track_passive_subs[thid][name]) == 1:
|
||||||
|
if entity in ("db", "ndb", "custom"):
|
||||||
|
caches.unregister_oob_update_hook(tracked, name, entity=entity)
|
||||||
|
elif entity == "property":
|
||||||
|
if hasattr(tracked, 'db_%s' % name.lstrip("db_")):
|
||||||
|
caches.unregister_oob_update_hook(tracked, name, entity="field")
|
||||||
|
else:
|
||||||
|
caches.unregister_oob_update_hook(tracked, name, entity="property")
|
||||||
|
|
||||||
|
try: del self.track_passive_subs[thid][name][tracker]
|
||||||
|
except (KeyError, TypeError): pass
|
||||||
|
|
||||||
|
def update(self, hid, name, new_val):
|
||||||
|
"""
|
||||||
|
This is called by the caches when the tracked object when its property/field/etc is updated,
|
||||||
|
to inform the oob handler and all subscribing to this particular entity has been updated with new_val.
|
||||||
|
"""
|
||||||
|
# tell all tracking objects of the update
|
||||||
|
for tracker, oob in self.track_passive_subs[hid][name].items():
|
||||||
|
try:
|
||||||
|
# function(tracker, tracked, key, new_value, *args, **kwargs)
|
||||||
|
oob[0](tracker, oob[1], oob[2], new_val, *oob[3], **oob[4])
|
||||||
|
except Exception:
|
||||||
|
logger.log_trace()
|
||||||
|
|
||||||
|
# Track (active/proactive tracking)
|
||||||
|
|
||||||
|
# creating and storing tracker scripts
|
||||||
|
def track_active(self, key, func, interval=30, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
Create a tracking, re-use script with same interval if available,
|
||||||
|
otherwise create a new one.
|
||||||
|
|
||||||
|
args:
|
||||||
|
key - interval-unique identifier needed for removing tracking later
|
||||||
|
func - function to call at interval seconds
|
||||||
|
(all other args become argjs into func)
|
||||||
|
keywords:
|
||||||
|
interval (default 30s) - how often to update tracker
|
||||||
|
(all other kwargs become kwargs into func)
|
||||||
|
"""
|
||||||
|
if interval in self.track_active_subs:
|
||||||
|
# tracker with given interval found. Add to its subs
|
||||||
|
self.track_active_subs[interval].track(key, func, *args, **kwargs)
|
||||||
|
else:
|
||||||
|
# create new tracker with given interval
|
||||||
|
new_tracker = create.create_script(_OOBTracker, key="oob_tracking_%i" % interval, interval=interval)
|
||||||
|
new_tracker.track(key, func, *args, **kwargs)
|
||||||
|
self.track_active_subs[interval] = new_tracker
|
||||||
|
|
||||||
|
def untrack_active(self, key, interval):
|
||||||
|
"""
|
||||||
|
Remove tracking for a given interval and key
|
||||||
|
"""
|
||||||
|
tracker = self.track_active_subs.get(interval)
|
||||||
|
if tracker:
|
||||||
|
tracker.untrack(key)
|
||||||
|
|
||||||
|
# handler object
|
||||||
|
OOBHANDLER = OOBhandler()
|
||||||
|
|
@ -142,7 +142,7 @@ class Evennia(object):
|
||||||
if len(mismatches): # can't use any() since mismatches may be [0] which reads as False for any()
|
if len(mismatches): # can't use any() since mismatches may be [0] which reads as False for any()
|
||||||
# we have a changed default. Import relevant objects and run the update
|
# we have a changed default. Import relevant objects and run the update
|
||||||
from src.objects.models import ObjectDB
|
from src.objects.models import ObjectDB
|
||||||
from src.players.models import PlayerDB
|
#from src.players.models import PlayerDB
|
||||||
for i, prev, curr in ((i, tup[0], tup[1]) for i, tup in enumerate(settings_compare) if i in mismatches):
|
for i, prev, curr in ((i, tup[0], tup[1]) for i, tup in enumerate(settings_compare) if i in mismatches):
|
||||||
# update the database
|
# update the database
|
||||||
print " one or more default cmdset/typeclass settings changed. Updating defaults stored in database ..."
|
print " one or more default cmdset/typeclass settings changed. Updating defaults stored in database ..."
|
||||||
|
|
@ -188,7 +188,7 @@ class Evennia(object):
|
||||||
Called every server start
|
Called every server start
|
||||||
"""
|
"""
|
||||||
from src.objects.models import ObjectDB
|
from src.objects.models import ObjectDB
|
||||||
from src.players.models import PlayerDB
|
#from src.players.models import PlayerDB
|
||||||
|
|
||||||
#update eventual changed defaults
|
#update eventual changed defaults
|
||||||
self.update_defaults()
|
self.update_defaults()
|
||||||
|
|
@ -264,7 +264,7 @@ class Evennia(object):
|
||||||
# call shutdown hooks on all cached objects
|
# call shutdown hooks on all cached objects
|
||||||
|
|
||||||
from src.objects.models import ObjectDB
|
from src.objects.models import ObjectDB
|
||||||
from src.players.models import PlayerDB
|
#from src.players.models import PlayerDB
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
|
|
||||||
if mode == 'reload':
|
if mode == 'reload':
|
||||||
|
|
|
||||||
|
|
@ -228,6 +228,8 @@ class ServerSession(Session):
|
||||||
example:
|
example:
|
||||||
data = {"get_hp": ([], {}),
|
data = {"get_hp": ([], {}),
|
||||||
"update_counter", (["counter1"], {"now":True}) }
|
"update_counter", (["counter1"], {"now":True}) }
|
||||||
|
|
||||||
|
All functions will be called with a back-reference to this session as first argument.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
outdata = {}
|
outdata = {}
|
||||||
|
|
@ -243,7 +245,7 @@ class ServerSession(Session):
|
||||||
func = OOB_FUNC_MODULE.__dict__.get(funcname, None)
|
func = OOB_FUNC_MODULE.__dict__.get(funcname, None)
|
||||||
if func:
|
if func:
|
||||||
try:
|
try:
|
||||||
outdata[funcname] = func(entity, *argtuple[0], **argtuple[1])
|
outdata[funcname] = func(self, entity, *argtuple[0], **argtuple[1])
|
||||||
except Exception:
|
except Exception:
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
else:
|
else:
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ class TelnetProtocol(Telnet, StatefulTelnetProtocol, Session):
|
||||||
self.mccp = Mccp(self)
|
self.mccp = Mccp(self)
|
||||||
# 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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,7 @@ from src.utils.idmapper.models import SharedMemoryModel
|
||||||
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
from src.server.caches import get_field_cache, set_field_cache, del_field_cache
|
||||||
from src.server.caches import get_attr_cache, set_attr_cache, del_attr_cache
|
from src.server.caches import get_attr_cache, set_attr_cache, del_attr_cache
|
||||||
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
from src.server.caches import get_prop_cache, set_prop_cache, del_prop_cache
|
||||||
|
from src.server.caches import call_ndb_hooks
|
||||||
from src.server.models import ServerConfig
|
from src.server.models import ServerConfig
|
||||||
from src.typeclasses import managers
|
from src.typeclasses import managers
|
||||||
from src.locks.lockhandler import LockHandler
|
from src.locks.lockhandler import LockHandler
|
||||||
|
|
@ -1584,7 +1585,7 @@ class TypedObject(SharedMemoryModel):
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
# act as a setter
|
# act as a setter
|
||||||
_SA(self.db, attribute_name, value)
|
_SA(self.ndb, attribute_name, value)
|
||||||
|
|
||||||
#@property
|
#@property
|
||||||
def __ndb_get(self):
|
def __ndb_get(self):
|
||||||
|
|
@ -1609,6 +1610,10 @@ class TypedObject(SharedMemoryModel):
|
||||||
return _GA(self, key)
|
return _GA(self, key)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
return None
|
return None
|
||||||
|
def __setattr__(self, key, value):
|
||||||
|
# hook the oob handler here
|
||||||
|
call_ndb_hooks(self, key, value)
|
||||||
|
_SA(self, key, value)
|
||||||
self._ndb_holder = NdbHolder()
|
self._ndb_holder = NdbHolder()
|
||||||
return self._ndb_holder
|
return self._ndb_holder
|
||||||
#@ndb.setter
|
#@ndb.setter
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue