Working on the OOB system, somewhat unstable at the moment.
This commit is contained in:
parent
d59500f574
commit
9ba212c264
6 changed files with 252 additions and 342 deletions
175
src/server/oob_cmds.py
Normal file
175
src/server/oob_cmds.py
Normal file
|
|
@ -0,0 +1,175 @@
|
||||||
|
"""
|
||||||
|
Out-of-band default plugin commands available for OOB handler.
|
||||||
|
|
||||||
|
This module implements commands as defined by the MSDP standard
|
||||||
|
(http://tintin.sourceforge.net/msdp/), but is independent of the
|
||||||
|
actual transfer protocol (webclient, MSDP, GMCP etc).
|
||||||
|
|
||||||
|
This module is pointed to by settings.OOB_PLUGIN_MODULES. All functions
|
||||||
|
(not classes) defined globally in this module will be made available
|
||||||
|
to the oob mechanism.
|
||||||
|
|
||||||
|
oob functions have the following call signature:
|
||||||
|
function(oobhandler, session, *args, **kwargs)
|
||||||
|
|
||||||
|
where oobhandler is a back-reference to the central OOB_HANDLER
|
||||||
|
instance and session is the active session to get return data.
|
||||||
|
|
||||||
|
The function names are not case-sensitive (this allows for names
|
||||||
|
like "LIST" which would otherwise collide with Python builtins).
|
||||||
|
|
||||||
|
A function named _OOB_ERROR will retrieve error strings if it is
|
||||||
|
defined. It will get the error message as its 3rd argument.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
_GA = object.__getattribute__
|
||||||
|
_SA = object.__setattr__
|
||||||
|
_NA_REPORT = lambda o: (None, "N/A")
|
||||||
|
_NA_SEND = lambda o: "N/A"
|
||||||
|
|
||||||
|
#------------------------------------------------------------
|
||||||
|
# All OOB commands must be on the form
|
||||||
|
# cmdname(oobhandler, session, *args, **kwargs)
|
||||||
|
#------------------------------------------------------------
|
||||||
|
|
||||||
|
def _OOB_ERROR(oobhandler, session, errmsg, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
A function with this name is special and is called by the oobhandler when an error
|
||||||
|
occurs already at the execution stage (such as the oob function
|
||||||
|
not being recognized or having the wrong args etc).
|
||||||
|
"""
|
||||||
|
session.msg(oob=("send", {"ERROR": errmsg}))
|
||||||
|
|
||||||
|
|
||||||
|
def ECHO(oobhandler, session, *args, **kwargs):
|
||||||
|
"Test/debug function, simply returning the args and kwargs"
|
||||||
|
session.msg(oob=("echo", args, kwargs))
|
||||||
|
|
||||||
|
|
||||||
|
def SEND(oobhandler, session, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
This function directly returns the value of the given variable to the
|
||||||
|
session.
|
||||||
|
"""
|
||||||
|
print "In SEND:", oobhandler, session, args
|
||||||
|
obj = session.get_puppet_or_player()
|
||||||
|
ret = {}
|
||||||
|
if obj:
|
||||||
|
for name in (a.upper() for a in args if a):
|
||||||
|
try:
|
||||||
|
value = OOB_SENDABLE.get(name, _NA_SEND)(obj)
|
||||||
|
ret[name] = value
|
||||||
|
except Exception, e:
|
||||||
|
ret[name] = str(e)
|
||||||
|
# return result
|
||||||
|
session.msg(oob=("send", ret))
|
||||||
|
|
||||||
|
|
||||||
|
def REPORT(oobhandler, session, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
This creates a tracker instance to track the data given in *args.
|
||||||
|
|
||||||
|
Note that the data name is assumed to be a field is it starts with db_*
|
||||||
|
and an Attribute otherwise.
|
||||||
|
|
||||||
|
"Example of tracking changes to the db_key field and the desc" Attribite:
|
||||||
|
REPORT(oobhandler, session, "CHARACTER_NAME", )
|
||||||
|
"""
|
||||||
|
obj = session.get_puppet_or_player()
|
||||||
|
if obj:
|
||||||
|
for name in (a.upper() for a in args if a):
|
||||||
|
typ, val = OOB_REPORTABLE.get(name, _NA_REPORT)(obj)
|
||||||
|
if typ == "field":
|
||||||
|
oobhandler.track_field(obj, session.sessid, name)
|
||||||
|
elif typ == "attribute":
|
||||||
|
oobhandler.track_attribute(obj, session.sessid, name)
|
||||||
|
|
||||||
|
|
||||||
|
def UNREPORT(oobhandler, session, vartype="prop", *args, **kwargs):
|
||||||
|
"""
|
||||||
|
This removes tracking for the given data given in *args.
|
||||||
|
"""
|
||||||
|
obj = session.get_puppet_or_player()
|
||||||
|
if obj:
|
||||||
|
for name in (a.upper() for a in args if a):
|
||||||
|
typ, val = OOB_REPORTABLE.get(name, _NA_REPORT)
|
||||||
|
if typ == "field":
|
||||||
|
oobhandler.untrack_field(obj, session.sessid, name)
|
||||||
|
else: # assume attribute
|
||||||
|
oobhandler.untrack_attribute(obj, session.sessid, name)
|
||||||
|
|
||||||
|
|
||||||
|
def LIST(oobhandler, session, mode, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
List available properties. Mode is the type of information
|
||||||
|
desired:
|
||||||
|
"COMMANDS" Request an array of commands supported
|
||||||
|
by the server.
|
||||||
|
"LISTS" Request an array of lists supported
|
||||||
|
by the server.
|
||||||
|
"CONFIGURABLE_VARIABLES" Request an array of variables the client
|
||||||
|
can configure.
|
||||||
|
"REPORTABLE_VARIABLES" Request an array of variables the server
|
||||||
|
will report.
|
||||||
|
"REPORTED_VARIABLES" Request an array of variables currently
|
||||||
|
being reported.
|
||||||
|
"SENDABLE_VARIABLES" Request an array of variables the server
|
||||||
|
will send.
|
||||||
|
"""
|
||||||
|
mode = mode.upper()
|
||||||
|
if mode == "COMMANDS":
|
||||||
|
session.msg(oob=("list", ("COMMANDS",
|
||||||
|
"LIST",
|
||||||
|
"REPORT",
|
||||||
|
"UNREPORT",
|
||||||
|
# "RESET",
|
||||||
|
"SEND")))
|
||||||
|
elif mode == "LISTS":
|
||||||
|
session.msg(oob=("list", ("LISTS",
|
||||||
|
"REPORTABLE_VARIABLES",
|
||||||
|
"REPORTED_VARIABLES",
|
||||||
|
# "CONFIGURABLE_VARIABLES",
|
||||||
|
"SENDABLE_VARIABLES")))
|
||||||
|
elif mode == "REPORTABLE_VARIABLES":
|
||||||
|
session.msg(oob=("list", ("REPORTABLE_VARIABLES",) +
|
||||||
|
tuple(key for key in OOB_REPORTABLE.keys())))
|
||||||
|
elif mode == "REPORTED_VARIABLES":
|
||||||
|
session.msg(oob=("list", ("REPORTED_VARIABLES",) +
|
||||||
|
tuple(oobhandler.get_all_tracked(session))))
|
||||||
|
elif mode == "SENDABLE_VARIABLES":
|
||||||
|
session.msg(oob=("list", ("SENDABLE_VARIABLES",) +
|
||||||
|
tuple(key for key in OOB_REPORTABLE.keys())))
|
||||||
|
#elif mode == "CONFIGURABLE_VARIABLES":
|
||||||
|
# pass
|
||||||
|
else:
|
||||||
|
session.msg(oob=("list", ("unsupported mode",)))
|
||||||
|
|
||||||
|
|
||||||
|
# Mapping for how to retrieve each property name.
|
||||||
|
# Each entry should point to a callable that gets the interesting object as
|
||||||
|
# input and returns the relevant value.
|
||||||
|
|
||||||
|
# MSDP recommends the following standard name mappings for general compliance:
|
||||||
|
# "CHARACTER_NAME", "SERVER_ID", "SERVER_TIME", "AFFECTS", "ALIGNMENT", "EXPERIENCE", "EXPERIENCE_MAX", "EXPERIENCE_TNL",
|
||||||
|
# "HEALTH", "HEALTH_MAX", "LEVEL", "RACE", "CLASS", "MANA", "MANA_MAX", "WIMPY", "PRACTICE", "MONEY", "MOVEMENT",
|
||||||
|
# "MOVEMENT_MAX", "HITROLL", "DAMROLL", "AC", "STR", "INT", "WIS", "DEX", "CON", "OPPONENT_HEALTH", "OPPONENT_HEALTH_MAX",
|
||||||
|
# "OPPONENT_LEVEL", "OPPONENT_NAME", "AREA_NAME", "ROOM_EXITS", "ROOM_VNUM", "ROOM_NAME", "WORLD_TIME", "CLIENT_ID",
|
||||||
|
# "CLIENT_VERSION", "PLUGIN_ID", "ANSI_COLORS", "XTERM_256_COLORS", "UTF_8", "SOUND", "MXP", "BUTTON_1", "BUTTON_2",
|
||||||
|
# "BUTTON_3", "BUTTON_4", "BUTTON_5", "GAUGE_1", "GAUGE_2","GAUGE_3", "GAUGE_4", "GAUGE_5"
|
||||||
|
|
||||||
|
OOB_SENDABLE = {
|
||||||
|
"CHARACTER_NAME": lambda o: o.key,
|
||||||
|
"SERVER_ID": lambda o: settings.SERVERNAME,
|
||||||
|
"ROOM_NAME": lambda o: o.db_location.key,
|
||||||
|
"ANSI_COLORS": lambda o: True,
|
||||||
|
"XTERM_256_COLORS": lambda o: True,
|
||||||
|
"UTF_8": lambda o: True
|
||||||
|
}
|
||||||
|
|
||||||
|
# mapping for which properties may be tracked. Each callable should return a tuple (type, value) where
|
||||||
|
# the type is one of "field" or "attribute" depending on what is being tracked.
|
||||||
|
OOB_REPORTABLE = {
|
||||||
|
"CHARACTER_NAME": lambda o: ("field", o.key),
|
||||||
|
"ROOM_NAME": lambda o: ("attribute", o.db_location.key)
|
||||||
|
}
|
||||||
|
|
@ -1,317 +0,0 @@
|
||||||
"""
|
|
||||||
Out-of-band default plugin commands available for OOB handler. This
|
|
||||||
follows the standards defined by the MSDP out-of-band protocol
|
|
||||||
(http://tintin.sourceforge.net/msdp/)
|
|
||||||
|
|
||||||
This module is pointed to by settings.OOB_PLUGIN_MODULES. All functions
|
|
||||||
(not classes) defined globally in this module will be made available
|
|
||||||
to the oob mechanism.
|
|
||||||
|
|
||||||
function execution - the oob protocol can execute a function directly on
|
|
||||||
the server. The available functions must be defined
|
|
||||||
as global functions via settings.OOB_PLUGIN_MODULES.
|
|
||||||
repeat func execution - the oob protocol can request a given function be
|
|
||||||
executed repeatedly at a regular interval. This
|
|
||||||
uses an internal script pool.
|
|
||||||
tracking - the oob protocol can request Evennia to track changes to
|
|
||||||
fields on objects, as well as changes in Attributes. This is
|
|
||||||
done by dynamically adding tracker-objects on entities. The
|
|
||||||
behaviour of those objects can be customized via
|
|
||||||
settings.OOB_PLUGIN_MODULES.
|
|
||||||
|
|
||||||
What goes into the OOB_PLUGIN_MODULES is a list of modules with input
|
|
||||||
for the OOB system.
|
|
||||||
|
|
||||||
oob functions have the following call signature:
|
|
||||||
function(caller, session, *args, **kwargs)
|
|
||||||
|
|
||||||
oob trackers should build upon the OOBTracker class in this module
|
|
||||||
module and implement a minimum of the same functionality.
|
|
||||||
|
|
||||||
a global function oob_error will be used as optional error management.
|
|
||||||
"""
|
|
||||||
from django.conf import settings
|
|
||||||
from src.utils.utils import to_str
|
|
||||||
_GA = object.__getattribute__
|
|
||||||
_SA = object.__setattr__
|
|
||||||
_NA = lambda o: (None, "N/A") # not implemented
|
|
||||||
|
|
||||||
# default properties defined by the MSDP protocol. These are
|
|
||||||
# used by the SEND oob function below. Each entry should point
|
|
||||||
# to a function that takes the relevant object as input and
|
|
||||||
# returns the data it is responsible for. Most of these
|
|
||||||
# are commented out, but kept for reference for each
|
|
||||||
# game to implement.
|
|
||||||
|
|
||||||
OOB_SENDABLE = {
|
|
||||||
## General
|
|
||||||
"CHARACTER_NAME": lambda o: ("db_key", o.key),
|
|
||||||
"SERVER_ID": lambda o: ("settings.SERVERNAME", settings.SERVERNAME),
|
|
||||||
#"SERVER_TIME": _NA,
|
|
||||||
## Character
|
|
||||||
#"AFFECTS": _NA,
|
|
||||||
#"ALIGNMENT": _NA,
|
|
||||||
#"EXPERIENCE": _NA,
|
|
||||||
#"EXPERIENCE_MAX": _NA,
|
|
||||||
#"EXPERIENCE_TNL": _NA,
|
|
||||||
#"HEALTH": _NA,
|
|
||||||
#"HEALTH_MAX": _NA,
|
|
||||||
#"LEVEL": _NA,
|
|
||||||
#"RACE": _NA,
|
|
||||||
#"CLASS": _NA,
|
|
||||||
#"MANA": _NA,
|
|
||||||
#"MANA_MAX": _NA,
|
|
||||||
#"WIMPY": _NA,
|
|
||||||
#"PRACTICE": _NA,
|
|
||||||
#"MONEY": _NA,
|
|
||||||
#"MOVEMENT": _NA,
|
|
||||||
#"MOVEMENT_MAX": _NA,
|
|
||||||
#"HITROLL": _NA,
|
|
||||||
#"DAMROLL": _NA,
|
|
||||||
#"AC": _NA,
|
|
||||||
#"STR": _NA,
|
|
||||||
#"INT": _NA,
|
|
||||||
#"WIS": _NA,
|
|
||||||
#"DEX": _NA,
|
|
||||||
#"CON": _NA,
|
|
||||||
## Combat
|
|
||||||
#"OPPONENT_HEALTH": _NA,
|
|
||||||
#"OPPONENT_HEALTH_MAX": _NA,
|
|
||||||
#"OPPONENT_LEVEL": _NA,
|
|
||||||
#"OPPONENT_NAME": _NA,
|
|
||||||
## World
|
|
||||||
#"AREA_NAME": _NA,
|
|
||||||
#"ROOM_EXITS": _NA,
|
|
||||||
#"ROOM_VNUM": _NA,
|
|
||||||
"ROOM_NAME": lambda o: ("db_location", o.db_location.key),
|
|
||||||
#"WORLD_TIME": _NA,
|
|
||||||
## Configurable variables
|
|
||||||
#"CLIENT_ID": _NA,
|
|
||||||
#"CLIENT_VERSION": _NA,
|
|
||||||
#"PLUGIN_ID": _NA,
|
|
||||||
#"ANSI_COLORS": _NA,
|
|
||||||
#"XTERM_256_COLORS": _NA,
|
|
||||||
#"UTF_8": _NA,
|
|
||||||
#"SOUND": _NA,
|
|
||||||
#"MXP": _NA,
|
|
||||||
## GUI variables
|
|
||||||
#"BUTTON_1": _NA,
|
|
||||||
#"BUTTON_2": _NA,
|
|
||||||
#"BUTTON_3": _NA,
|
|
||||||
#"BUTTON_4": _NA,
|
|
||||||
#"BUTTON_5": _NA,
|
|
||||||
#"GAUGE_1": _NA,
|
|
||||||
#"GAUGE_2": _NA,
|
|
||||||
#"GAUGE_3": _NA,
|
|
||||||
#"GAUGE_4": _NA,
|
|
||||||
#"GAUGE_5": _NA
|
|
||||||
}
|
|
||||||
# mapping for which properties may be tracked
|
|
||||||
OOB_REPORTABLE = OOB_SENDABLE
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# Tracker classes
|
|
||||||
#
|
|
||||||
# Trackers are added to a given object's trackerhandler and
|
|
||||||
# reports back changes when they happen. They are managed using
|
|
||||||
# the oobhandler's track/untrack mechanism
|
|
||||||
#------------------------------------------------------------
|
|
||||||
|
|
||||||
class TrackerBase(object):
|
|
||||||
"""
|
|
||||||
Base class for OOB Tracker objects.
|
|
||||||
"""
|
|
||||||
def __init__(self, oobhandler, *args, **kwargs):
|
|
||||||
self.oobhandler = oobhandler
|
|
||||||
|
|
||||||
def update(self, *args, **kwargs):
|
|
||||||
"Called by tracked objects"
|
|
||||||
pass
|
|
||||||
|
|
||||||
def at_remove(self, *args, **kwargs):
|
|
||||||
"Called when tracker is removed"
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
# Tracker objects stored on objects when using the MSDP report command
|
|
||||||
|
|
||||||
class ReportFieldTracker(TrackerBase):
|
|
||||||
"""
|
|
||||||
Tracker that passively sends data to a stored sessid whenever
|
|
||||||
a named database field changes. The TrackerHandler calls this with
|
|
||||||
the correct arguments.
|
|
||||||
"""
|
|
||||||
def __init__(self, oobhandler, fieldname, sessid, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
name - name of entity to track, such as "db_key"
|
|
||||||
sessid - sessid of session to report to
|
|
||||||
"""
|
|
||||||
self.oobhandler = oobhandler
|
|
||||||
self.fieldname = fieldname
|
|
||||||
self.sessid = sessid
|
|
||||||
|
|
||||||
def update(self, new_value, *args, **kwargs):
|
|
||||||
"Called by cache when updating the tracked entitiy"
|
|
||||||
# use oobhandler to relay data
|
|
||||||
try:
|
|
||||||
# we must never relay objects across the amp, only text data.
|
|
||||||
new_value = new_value.key
|
|
||||||
except AttributeError:
|
|
||||||
new_value = to_str(new_value, force_string=True)
|
|
||||||
# this is a wrapper call for sending oob data back to session
|
|
||||||
self.oobhandler.msg(self.sessid, "report", self.fieldname,
|
|
||||||
new_value, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
class ReportAttributeTracker(TrackerBase):
|
|
||||||
"""
|
|
||||||
Tracker that passively sends data to a stored sessid whenever
|
|
||||||
the Attribute updates. Since the field here is always "db_key",
|
|
||||||
we instead store the name of the attribute to return.
|
|
||||||
"""
|
|
||||||
def __init__(self, oobhandler, fieldname, sessid, attrname, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
attrname - name of attribute to track
|
|
||||||
sessid - sessid of session to report to
|
|
||||||
"""
|
|
||||||
self.oobhandler = oobhandler
|
|
||||||
self.attrname = attrname
|
|
||||||
self.sessid = sessid
|
|
||||||
|
|
||||||
def update(self, new_value, *args, **kwargs):
|
|
||||||
"Called by cache when attribute's db_value field updates"
|
|
||||||
try:
|
|
||||||
new_value = new_value.dbobj
|
|
||||||
except AttributeError:
|
|
||||||
new_value = to_str(new_value, force_string=True)
|
|
||||||
# this is a wrapper call for sending oob data back to session
|
|
||||||
self.oobhandler.msg(self.sessid, "report", self.attrname, new_value, *args, **kwargs)
|
|
||||||
|
|
||||||
|
|
||||||
#------------------------------------------------------------
|
|
||||||
# OOB commands
|
|
||||||
# This defines which internal server commands the OOB handler
|
|
||||||
# makes available to the client. These commands are called
|
|
||||||
# automatically by the OOB mechanism by triggering the
|
|
||||||
# oobhandlers's execute_cmd method with the cmdname and
|
|
||||||
# eventual args/kwargs. All functions defined globally in this
|
|
||||||
# module will be made available to call by the oobhandler. Use
|
|
||||||
# _funcname if you want to exclude one. To allow for python-names
|
|
||||||
# like "list" here, these properties are case-insensitive.
|
|
||||||
#
|
|
||||||
# All OOB commands must be on the form
|
|
||||||
# cmdname(oobhandler, session, *args, **kwargs)
|
|
||||||
#------------------------------------------------------------
|
|
||||||
|
|
||||||
def oob_error(oobhandler, session, errmsg, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
This is a special function called by the oobhandler when an error
|
|
||||||
occurs already at the execution stage (such as the oob function
|
|
||||||
not being recognized or having the wrong args etc).
|
|
||||||
"""
|
|
||||||
session.msg(oob=("send", {"ERROR": errmsg}))
|
|
||||||
|
|
||||||
def LIST(oobhandler, session, mode, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
List available properties. Mode is the type of information
|
|
||||||
desired:
|
|
||||||
"COMMANDS" Request an array of commands supported
|
|
||||||
by the server.
|
|
||||||
"LISTS" Request an array of lists supported
|
|
||||||
by the server.
|
|
||||||
"CONFIGURABLE_VARIABLES" Request an array of variables the client
|
|
||||||
can configure.
|
|
||||||
"REPORTABLE_VARIABLES" Request an array of variables the server
|
|
||||||
will report.
|
|
||||||
"REPORTED_VARIABLES" Request an array of variables currently
|
|
||||||
being reported.
|
|
||||||
"SENDABLE_VARIABLES" Request an array of variables the server
|
|
||||||
will send.
|
|
||||||
"""
|
|
||||||
mode = mode.upper()
|
|
||||||
# the first return argument is treated by the msdp protocol as the
|
|
||||||
# name of the msdp array to return
|
|
||||||
if mode == "COMMANDS":
|
|
||||||
session.msg(oob=("list", ("COMMANDS",
|
|
||||||
"LIST",
|
|
||||||
"REPORT",
|
|
||||||
"UNREPORT",
|
|
||||||
# "RESET",
|
|
||||||
"SEND")))
|
|
||||||
elif mode == "LISTS":
|
|
||||||
session.msg(oob=("list", ("LISTS",
|
|
||||||
"REPORTABLE_VARIABLES",
|
|
||||||
"REPORTED_VARIABLES",
|
|
||||||
# "CONFIGURABLE_VARIABLES",
|
|
||||||
"SENDABLE_VARIABLES")))
|
|
||||||
elif mode == "REPORTABLE_VARIABLES":
|
|
||||||
session.msg(oob=("list", ("REPORTABLE_VARIABLES",) +
|
|
||||||
tuple(key for key in OOB_REPORTABLE.keys())))
|
|
||||||
elif mode == "REPORTED_VARIABLES":
|
|
||||||
session.msg(oob=("list", ("REPORTED_VARIABLES",) +
|
|
||||||
tuple(oobhandler.get_all_tracked(session))))
|
|
||||||
elif mode == "SENDABLE_VARIABLES":
|
|
||||||
session.msg(oob=("list", ("SENDABLE_VARIABLES",) +
|
|
||||||
tuple(key for key in OOB_REPORTABLE.keys())))
|
|
||||||
#elif mode == "CONFIGURABLE_VARIABLES":
|
|
||||||
# pass
|
|
||||||
else:
|
|
||||||
session.msg(oob=("list", ("unsupported mode",)))
|
|
||||||
|
|
||||||
|
|
||||||
def SEND(oobhandler, session, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
This function directly returns the value of the given variable to the
|
|
||||||
session. vartype can be one of
|
|
||||||
"""
|
|
||||||
obj = session.get_puppet_or_player()
|
|
||||||
ret = {}
|
|
||||||
if obj:
|
|
||||||
for name in (a.upper() for a in args if a):
|
|
||||||
try:
|
|
||||||
key, value = OOB_SENDABLE.get(name, _NA)(obj)
|
|
||||||
ret[name] = value
|
|
||||||
except Exception, e:
|
|
||||||
ret[name] = str(e)
|
|
||||||
# return result
|
|
||||||
session.msg(oob=("send", ret))
|
|
||||||
|
|
||||||
|
|
||||||
def REPORT(oobhandler, session, *args, **kwargs):
|
|
||||||
"""
|
|
||||||
This creates a tracker instance to track the data given in *args.
|
|
||||||
vartype is one of "prop" (database fields) or "attr" (attributes)
|
|
||||||
"""
|
|
||||||
obj = session.get_puppet_or_player()
|
|
||||||
if obj:
|
|
||||||
for name in (a.upper() for a in args if a):
|
|
||||||
key, val = OOB_REPORTABLE.get(name, _NA)(obj)
|
|
||||||
if key:
|
|
||||||
if key.startswith("db_"):
|
|
||||||
oobhandler.track_field(obj, session.sessid,
|
|
||||||
key, ReportFieldTracker)
|
|
||||||
else: # assume attribute
|
|
||||||
oobhandler.track_attribute(obj, session.sessid,
|
|
||||||
key, ReportAttributeTracker)
|
|
||||||
|
|
||||||
|
|
||||||
def UNREPORT(oobhandler, session, vartype="prop", *args, **kwargs):
|
|
||||||
"""
|
|
||||||
This removes tracking for the given data given in *args.
|
|
||||||
vartype is one of of "prop" or "attr".
|
|
||||||
"""
|
|
||||||
obj = session.get_puppet_or_player()
|
|
||||||
if obj:
|
|
||||||
for name in (a.upper() for a in args if a):
|
|
||||||
key, val = OOB_REPORTABLE.get(name, _NA)
|
|
||||||
if key:
|
|
||||||
if key.startswith("db_"):
|
|
||||||
oobhandler.untrack_field(obj, session.sessid, key)
|
|
||||||
else: # assume attribute
|
|
||||||
oobhandler.untrack_attribute(obj, session.sessid, key)
|
|
||||||
|
|
||||||
def ECHO(oobhandler, session, *args, **kwargs):
|
|
||||||
"Test function, returning the args, kwargs"
|
|
||||||
args = ["Return echo:"] + list(args)
|
|
||||||
session.msg(oob=("echo", args, kwargs))
|
|
||||||
|
|
@ -45,7 +45,7 @@ from src.server.sessionhandler import SESSIONS
|
||||||
from src.scripts.tickerhandler import Ticker, TickerPool, TickerHandler
|
from src.scripts.tickerhandler import Ticker, TickerPool, TickerHandler
|
||||||
from src.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj, unpack_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 all_from_module, make_iter
|
from src.utils.utils import all_from_module, make_iter, to_str
|
||||||
|
|
||||||
_SA = object.__setattr__
|
_SA = object.__setattr__
|
||||||
_GA = object.__getattribute__
|
_GA = object.__getattribute__
|
||||||
|
|
@ -55,9 +55,9 @@ _DA = object.__delattr__
|
||||||
_OOB_FUNCS = {}
|
_OOB_FUNCS = {}
|
||||||
for mod in make_iter(settings.OOB_PLUGIN_MODULES):
|
for mod in make_iter(settings.OOB_PLUGIN_MODULES):
|
||||||
_OOB_FUNCS.update(dict((key.lower(), func) for key, func in all_from_module(mod).items() if isfunction(func)))
|
_OOB_FUNCS.update(dict((key.lower(), func) for key, func in all_from_module(mod).items() if isfunction(func)))
|
||||||
# get custom error method or use the default
|
|
||||||
_OOB_ERROR = _OOB_FUNCS.get("oob_error", None)
|
|
||||||
|
|
||||||
|
# get custom error method or use the default
|
||||||
|
_OOB_ERROR = _OOB_FUNCS.get("_OOB_ERROR", None)
|
||||||
if not _OOB_ERROR:
|
if not _OOB_ERROR:
|
||||||
# create default oob error message function
|
# create default oob error message function
|
||||||
def oob_error(oobhandler, session, errmsg, *args, **kwargs):
|
def oob_error(oobhandler, session, errmsg, *args, **kwargs):
|
||||||
|
|
@ -131,11 +131,12 @@ class TrackerHandler(object):
|
||||||
logger.log_trace()
|
logger.log_trace()
|
||||||
|
|
||||||
|
|
||||||
# Tracker loaded by the TrackerHandler
|
# On-object Trackers to load with TrackerHandler
|
||||||
|
|
||||||
class TrackerBase(object):
|
class TrackerBase(object):
|
||||||
"""
|
"""
|
||||||
Base class for OOB Tracker objects.
|
Base class for OOB Tracker objects. Inherit from this
|
||||||
|
to define custom trackers.
|
||||||
"""
|
"""
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
pass
|
pass
|
||||||
|
|
@ -148,6 +149,61 @@ class TrackerBase(object):
|
||||||
"Called when tracker is removed"
|
"Called when tracker is removed"
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class ReportFieldTracker(TrackerBase):
|
||||||
|
"""
|
||||||
|
Tracker that passively sends data to a stored sessid whenever
|
||||||
|
a named database field changes. The TrackerHandler calls this with
|
||||||
|
the correct arguments.
|
||||||
|
"""
|
||||||
|
def __init__(self, oobhandler, fieldname, sessid, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
name - name of entity to track, such as "db_key"
|
||||||
|
sessid - sessid of session to report to
|
||||||
|
"""
|
||||||
|
self.oobhandler = oobhandler
|
||||||
|
self.fieldname = fieldname
|
||||||
|
self.sessid = sessid
|
||||||
|
|
||||||
|
def update(self, new_value, *args, **kwargs):
|
||||||
|
"Called by cache when updating the tracked entitiy"
|
||||||
|
# use oobhandler to relay data
|
||||||
|
try:
|
||||||
|
# we must never relay objects across the amp, only text data.
|
||||||
|
new_value = new_value.key
|
||||||
|
except AttributeError:
|
||||||
|
new_value = to_str(new_value, force_string=True)
|
||||||
|
# this is a wrapper call for sending oob data back to session
|
||||||
|
self.oobhandler.msg(self.sessid, "report", self.fieldname,
|
||||||
|
new_value, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class ReportAttributeTracker(TrackerBase):
|
||||||
|
"""
|
||||||
|
Tracker that passively sends data to a stored sessid whenever
|
||||||
|
the Attribute updates. Since the field here is always "db_key",
|
||||||
|
we instead store the name of the attribute to return.
|
||||||
|
"""
|
||||||
|
def __init__(self, oobhandler, fieldname, sessid, attrname, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
attrname - name of attribute to track
|
||||||
|
sessid - sessid of session to report to
|
||||||
|
"""
|
||||||
|
self.oobhandler = oobhandler
|
||||||
|
self.attrname = attrname
|
||||||
|
self.sessid = sessid
|
||||||
|
|
||||||
|
def update(self, new_value, *args, **kwargs):
|
||||||
|
"Called by cache when attribute's db_value field updates"
|
||||||
|
try:
|
||||||
|
new_value = new_value.dbobj
|
||||||
|
except AttributeError:
|
||||||
|
new_value = to_str(new_value, force_string=True)
|
||||||
|
# this is a wrapper call for sending oob data back to session
|
||||||
|
self.oobhandler.msg(self.sessid, "report", self.attrname, new_value, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Ticker of auto-updating objects
|
# Ticker of auto-updating objects
|
||||||
|
|
||||||
class OOBTicker(Ticker):
|
class OOBTicker(Ticker):
|
||||||
|
|
@ -273,7 +329,7 @@ class OOBHandler(object):
|
||||||
sessid = session.sessid
|
sessid = session.sessid
|
||||||
return [key[2].lstrip("db_") for key in self.oob_tracker_storage.keys() if key[1] == sessid]
|
return [key[2].lstrip("db_") for key in self.oob_tracker_storage.keys() if key[1] == sessid]
|
||||||
|
|
||||||
def track_field(self, obj, sessid, field_name, trackerclass):
|
def track_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker):
|
||||||
"""
|
"""
|
||||||
Shortcut wrapper method for specifically tracking a database field.
|
Shortcut wrapper method for specifically tracking a database field.
|
||||||
Takes the tracker class as argument.
|
Takes the tracker class as argument.
|
||||||
|
|
@ -289,7 +345,7 @@ class OOBHandler(object):
|
||||||
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
|
||||||
self._untrack(obj, sessid, field_name)
|
self._untrack(obj, sessid, field_name)
|
||||||
|
|
||||||
def track_attribute(self, obj, sessid, attr_name, trackerclass):
|
def track_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker):
|
||||||
"""
|
"""
|
||||||
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
|
Attribute on an object. Will create a tracker on the Attribute
|
||||||
|
|
@ -332,12 +388,6 @@ class OOBHandler(object):
|
||||||
"""
|
"""
|
||||||
self.tickerhandler.remove(self, obj, interval)
|
self.tickerhandler.remove(self, obj, interval)
|
||||||
|
|
||||||
def msg(self, sessid, funcname, *args, **kwargs):
|
|
||||||
"Shortcut to relay oob data back to portal. Used by oob functions."
|
|
||||||
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 session.msg()
|
# access method - called from session.msg()
|
||||||
|
|
||||||
|
|
@ -347,7 +397,7 @@ class OOBHandler(object):
|
||||||
using *args and **kwargs
|
using *args and **kwargs
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys()
|
#print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys()
|
||||||
oobfunc = _OOB_FUNCS[func_key] # raise traceback if not found
|
oobfunc = _OOB_FUNCS[func_key] # raise traceback if not found
|
||||||
oobfunc(self, session, *args, **kwargs)
|
oobfunc(self, session, *args, **kwargs)
|
||||||
except KeyError,e:
|
except KeyError,e:
|
||||||
|
|
@ -364,5 +414,14 @@ class OOBHandler(object):
|
||||||
else:
|
else:
|
||||||
logger.log_trace(errmsg)
|
logger.log_trace(errmsg)
|
||||||
raise Exception(errmsg)
|
raise Exception(errmsg)
|
||||||
|
|
||||||
|
def msg(self, sessid, funcname, *args, **kwargs):
|
||||||
|
"Shortcut to force-send an OOB message through the oobhandler to a session"
|
||||||
|
session = self.sessionhandler.session_from_sessid(sessid)
|
||||||
|
#print "oobhandler msg:", sessid, session, funcname, args, kwargs
|
||||||
|
if session:
|
||||||
|
session.msg(oob=(funcname, args, kwargs))
|
||||||
|
|
||||||
|
|
||||||
# access object
|
# access object
|
||||||
OOB_HANDLER = OOBHandler()
|
OOB_HANDLER = OOBHandler()
|
||||||
|
|
|
||||||
|
|
@ -103,15 +103,9 @@ class SessionHandler(object):
|
||||||
def oobstruct_parser(self, oobstruct):
|
def oobstruct_parser(self, oobstruct):
|
||||||
"""
|
"""
|
||||||
Helper method for each session to use to parse oob structures
|
Helper method for each session to use to parse oob structures
|
||||||
(The 'oob' kwarg of the msg() method)
|
(The 'oob' kwarg of the msg() method).
|
||||||
((cmdname, (args), {}), ...)
|
|
||||||
|
|
||||||
|
|
||||||
Allowed oob structures are:
|
|
||||||
allowed oob structures are
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Allowed input oob structures are:
|
||||||
cmdname
|
cmdname
|
||||||
((cmdname,), (cmdname,))
|
((cmdname,), (cmdname,))
|
||||||
(cmdname,(arg, ))
|
(cmdname,(arg, ))
|
||||||
|
|
|
||||||
|
|
@ -230,7 +230,7 @@ LOCK_FUNC_MODULES = ("src.locks.lockfuncs",)
|
||||||
# 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_MODULES = ["src.server.oob_msdp"]
|
OOB_PLUGIN_MODULES = ["src.server.oob_cmds"]
|
||||||
|
|
||||||
######################################################################
|
######################################################################
|
||||||
# Default command sets
|
# Default command sets
|
||||||
|
|
|
||||||
|
|
@ -29,8 +29,6 @@ function echo(message) {
|
||||||
doShow("out", "ECHO return: " + message) }
|
doShow("out", "ECHO return: " + message) }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Webclient code
|
// Webclient code
|
||||||
|
|
||||||
function webclient_init(){
|
function webclient_init(){
|
||||||
|
|
@ -95,6 +93,7 @@ function doSend(){
|
||||||
|
|
||||||
if (OOB_debug && outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") {
|
if (OOB_debug && outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") {
|
||||||
// test OOB messaging
|
// test OOB messaging
|
||||||
|
doShow("out", "OOB input: " + outmsg.slice(5))
|
||||||
doOOB(JSON.parse(outmsg.slice(5))); }
|
doOOB(JSON.parse(outmsg.slice(5))); }
|
||||||
else {
|
else {
|
||||||
// normal output
|
// normal output
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue