Fixed and refactored OOB system and tested with new websocket client

This commit is contained in:
Griatch 2014-06-28 17:38:21 +02:00
parent 9ba212c264
commit c60a5fdea1
10 changed files with 209 additions and 128 deletions

View file

@ -144,9 +144,9 @@ class ObjectDB(TypedObject):
# make sure to sync the contents cache when initializing
#_GA(self, "contents_update")()
def _at_db_player_presave(self):
def _at_db_player_postsave(self):
"""
This hook is called automatically just before the player field is saved.
This hook is called automatically after the player field is saved.
"""
# we need to re-cache this for superusers to bypass.
self.locks.cache_lock_bypass(self)

View file

@ -80,28 +80,28 @@ def hashid(obj, suffix=""):
#------------------------------------------------------------
# callback to field pre_save signal (connected in src.server.server)
def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwargs):
"""
Called at the beginning of the field save operation. The save method
must be called with the update_fields keyword in order to be most efficient.
This method should NOT save; rather it is the save() that triggers this
function. Its main purpose is to allow to plug-in a save handler and oob
handlers.
"""
if raw:
return
if update_fields:
# this is a list of strings at this point. We want field objects
update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields)
else:
# meta.fields are already field objects; get them all
update_fields = _GA(_GA(instance, "_meta"), "fields")
for field in update_fields:
fieldname = field.name
handlername = "_at_%s_presave" % fieldname
handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None
if callable(handler):
handler()
#def field_pre_save(sender, instance=None, update_fields=None, raw=False, **kwargs):
# """
# Called at the beginning of the field save operation. The save method
# must be called with the update_fields keyword in order to be most efficient.
# This method should NOT save; rather it is the save() that triggers this
# function. Its main purpose is to allow to plug-in a save handler and oob
# handlers.
# """
# if raw:
# return
# if update_fields:
# # this is a list of strings at this point. We want field objects
# update_fields = (_GA(_GA(instance, "_meta"), "get_field_by_name")(field)[0] for field in update_fields)
# else:
# # meta.fields are already field objects; get them all
# update_fields = _GA(_GA(instance, "_meta"), "fields")
# for field in update_fields:
# fieldname = field.name
# handlername = "_at_%s_presave" % fieldname
# handler = _GA(instance, handlername) if handlername in _GA(sender, '__dict__') else None
# if callable(handler):
# handler()
def field_post_save(sender, instance=None, update_fields=None, raw=False, **kwargs):

View file

@ -18,14 +18,19 @@ 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
A function named OOB_ERROR will retrieve error strings if it is
defined. It will get the error message as its 3rd argument.
Data is usually returned via
session.msg(oob=(cmdname, (args,), {kwargs}))
Note that args, kwargs must be iterable/dict, non-iterables will
be interpreted as a new command name.
"""
from django.conf import settings
_GA = object.__getattribute__
_SA = object.__setattr__
_NA_REPORT = lambda o: (None, "N/A")
_NA_SEND = lambda o: "N/A"
#------------------------------------------------------------
@ -33,26 +38,25 @@ _NA_SEND = lambda o: "N/A"
# cmdname(oobhandler, session, *args, **kwargs)
#------------------------------------------------------------
def _OOB_ERROR(oobhandler, session, errmsg, *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}))
session.msg(oob=("err", ("ERROR " + errmsg,)))
def ECHO(oobhandler, session, *args, **kwargs):
"Test/debug function, simply returning the args and kwargs"
session.msg(oob=("echo", args, kwargs))
##OOB{"SEND":"CHARACTER_NAME"}
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:
@ -62,14 +66,18 @@ def SEND(oobhandler, session, *args, **kwargs):
ret[name] = value
except Exception, e:
ret[name] = str(e)
# return result
session.msg(oob=("send", ret))
session.msg(oob=("send", ret))
else:
session.msg(oob=("err", ("You must log in first.",)))
##OOB{"REPORT":"TEST"}
def REPORT(oobhandler, session, *args, **kwargs):
"""
This creates a tracker instance to track the data given in *args.
The tracker will return with a oob structure
oob={"report":["attrfieldname", (args,), {kwargs}}
Note that the data name is assumed to be a field is it starts with db_*
and an Attribute otherwise.
@ -79,27 +87,37 @@ def REPORT(oobhandler, session, *args, **kwargs):
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)
trackname = OOB_REPORTABLE.get(name, None)
if not trackname:
session.msg(oob=("err", ("No Reportable property '%s'. Use LIST REPORTABLE_VARIABLES." % trackname,)))
elif trackname.startswith("db_"):
oobhandler.track_field(obj, session.sessid, trackname)
else:
oobhandler.track_attribute(obj, session.sessid, trackname)
else:
session.msg(oob=("err", ("You must log in first.",)))
def UNREPORT(oobhandler, session, vartype="prop", *args, **kwargs):
##OOB{"UNREPORT": "TEST"}
def UNREPORT(oobhandler, session, *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)
trackname = OOB_REPORTABLE.get(name, None)
if not trackname:
session.msg(oob=("err", ("No Un-Reportable property '%s'. Use LIST REPORTED_VALUES." % name,)))
elif trackname.startswith("db_"):
oobhandler.untrack_field(obj, session.sessid, trackname)
else: # assume attribute
oobhandler.untrack_attribute(obj, session.sessid, name)
oobhandler.untrack_attribute(obj, session.sessid, trackname)
else:
session.msg(oob=("err", ("You must log in first.",)))
##OOB{"LIST":"COMMANDS"}
def LIST(oobhandler, session, mode, *args, **kwargs):
"""
List available properties. Mode is the type of information
@ -135,15 +153,42 @@ def LIST(oobhandler, session, mode, *args, **kwargs):
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))))
# we need to check so as to use the right return value depending on if it is
# an Attribute (identified by tracking the db_value field) or a normal database field
reported = oobhandler.get_all_tracked(session)
reported = [stored[2] if stored[2] != "db_value" else stored[4][0] for stored in reported]
session.msg(oob=("list", ["REPORTED_VARIABLES"] + reported))
elif mode == "SENDABLE_VARIABLES":
session.msg(oob=("list", ("SENDABLE_VARIABLES",) +
tuple(key for key in OOB_REPORTABLE.keys())))
#elif mode == "CONFIGURABLE_VARIABLES":
# pass
elif mode == "CONFIGURABLE_VARIABLES":
# Not implemented (game specific)
pass
else:
session.msg(oob=("list", ("unsupported mode",)))
session.msg(oob=("err", ("LIST", "Unsupported mode",)))
def _repeat_callback(oobhandler, session, *args, **kwargs):
"Set up by REPEAT"
session.msg(oob=("repeat", ("Repeat!",)))
##OOB{"REPEAT":10}
def REPEAT(oobhandler, session, interval, *args, **kwargs):
"""
Test command for the repeat functionality. Note that the args/kwargs
must not be db objects (or anything else non-picklable), rather use
dbrefs if so needed. The callback must be defined globally and
will be called as
callback(oobhandler, session, *args, **kwargs)
"""
oobhandler.repeat(None, session.sessid, interval, _repeat_callback, *args, **kwargs)
##OOB{"UNREPEAT":10}
def UNREPEAT(oobhandler, session, interval):
"""
Disable repeating callback
"""
oobhandler.unrepeat(None, session.sessid, interval)
# Mapping for how to retrieve each property name.
@ -167,9 +212,10 @@ OOB_SENDABLE = {
"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.
# mapping for which properties may be tracked. Each value points either to a database field
# (starting with db_*) or an Attribute name.
OOB_REPORTABLE = {
"CHARACTER_NAME": lambda o: ("field", o.key),
"ROOM_NAME": lambda o: ("attribute", o.db_location.key)
"CHARACTER_NAME": "db_key",
"ROOM_NAME": "db_location",
"TEST" : "test"
}

View file

@ -36,7 +36,6 @@ messages.
from inspect import isfunction
from twisted.internet.defer import inlineCallbacks
from twisted.internet.task import LoopingCall
from django.conf import settings
from src.server.models import ServerConfig
from src.server.sessionhandler import SESSIONS
@ -57,12 +56,12 @@ 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)))
# get custom error method or use the default
_OOB_ERROR = _OOB_FUNCS.get("_OOB_ERROR", None)
_OOB_ERROR = _OOB_FUNCS.get("oob_error", None)
if not _OOB_ERROR:
# create default oob error message function
def oob_error(oobhandler, session, errmsg, *args, **kwargs):
"Error wrapper"
session.msg(oob=("send", {"ERROR": errmsg}))
session.msg(oob=("err", ("ERROR ", errmsg)))
_OOB_ERROR = oob_error
@ -105,16 +104,16 @@ class TrackerHandler(object):
def remove(self, fieldname, trackerclass, *args, **kwargs):
"""
Remove tracker from handler. Raises KeyError if tracker
is not found.
Remove identified tracker from TrackerHandler.
Raises KeyError if tracker is not found.
"""
trackerkey = trackerclass.__name__
tracker = self.tracktargets[fieldname][trackerkey]
try:
tracker.at_delete(*args, **kwargs)
tracker.at_remove(*args, **kwargs)
except Exception:
logger.log_trace()
del tracker
del self.tracktargets[fieldname][trackerkey]
self.ntrackers -= 1
if self.ntrackers <= 0:
# if there are no more trackers, clean this handler
@ -173,9 +172,9 @@ class ReportFieldTracker(TrackerBase):
new_value = new_value.key
except AttributeError:
new_value = to_str(new_value, force_string=True)
kwargs[self.fieldname] = new_value
# this is a wrapper call for sending oob data back to session
self.oobhandler.msg(self.sessid, "report", self.fieldname,
new_value, *args, **kwargs)
self.oobhandler.msg(self.sessid, "report", *args, **kwargs)
class ReportAttributeTracker(TrackerBase):
@ -199,8 +198,9 @@ class ReportAttributeTracker(TrackerBase):
new_value = new_value.dbobj
except AttributeError:
new_value = to_str(new_value, force_string=True)
kwargs[self.attrname] = new_value
# this is a wrapper call for sending oob data back to session
self.oobhandler.msg(self.sessid, "report", self.attrname, new_value, *args, **kwargs)
self.oobhandler.msg(self.sessid, "report", *args, **kwargs)
@ -208,25 +208,21 @@ class ReportAttributeTracker(TrackerBase):
class OOBTicker(Ticker):
"""
Version of Ticker that calls OOB_FUNC rather than trying to call
Version of Ticker that executes an executable rather than trying to call
a hook method.
"""
@inlineCallbacks
def _callback(self, oobhandler, sessions):
def _callback(self):
"See original for more info"
for key, (_, args, kwargs) in self.subscriptions.items():
session = sessions.session_from_sessid(kwargs.get("sessid"))
# args = (sessid, callback_function)
session = SESSIONS.session_from_sessid(args[0])
try:
oobhandler.execute_cmd(session, kwargs.get("func_key"), *args, **kwargs)
# execute the oob callback
yield args[1](OOB_HANDLER, session, *args[2:], **kwargs)
except Exception:
logger.log_trace()
def __init__(self, interval):
"Sets up the Ticker"
self.interval = interval
self.subscriptions = {}
self.task = LoopingCall(self._callback, OOB_HANDLER, SESSIONS)
class OOBTickerPool(TickerPool):
ticker_class = OOBTicker
@ -270,9 +266,9 @@ class OOBHandler(object):
tracker_storage = ServerConfig.objects.conf(key="oob_tracker_storage")
if tracker_storage:
self.oob_tracker_storage = dbunserialize(tracker_storage)
#print "recovered from tracker_storage:", self.oob_tracker_storage
for (obj, sessid, fieldname, trackerclass, args, kwargs) in self.oob_tracker_storage.values():
self.track(unpack_dbobj(obj), sessid, fieldname, trackerclass, *args, **kwargs)
#print "restoring tracking:",obj, sessid, fieldname, trackerclass
self._track(unpack_dbobj(obj), sessid, fieldname, trackerclass, *args, **kwargs)
# make sure to purge the storage
ServerConfig.objects.conf(key="oob_tracker_storage", delete=True)
self.tickerhandler.restore()
@ -302,6 +298,7 @@ class OOBHandler(object):
storekey = (obj_packed, sessid, propname)
stored = (obj_packed, sessid, propname, trackerclass, args, kwargs)
self.oob_tracker_storage[storekey] = stored
#print "_track:", obj, id(obj), obj.__dict__
def _untrack(self, obj, sessid, propname, trackerclass, *args, **kwargs):
"""
@ -312,9 +309,8 @@ class OOBHandler(object):
obj = obj.dbobj
except AttributeError:
pass
try:
# call at_delete hook
# call at_remove hook on the trackerclass
_GA(obj, "_trackerhandler").remove(propname, trackerclass, *args, **kwargs)
except AttributeError:
pass
@ -327,7 +323,7 @@ class OOBHandler(object):
Get the names of all variables this session is tracking.
"""
sessid = session.sessid
return [key[2].lstrip("db_") for key in self.oob_tracker_storage.keys() if key[1] == sessid]
return [stored for key, stored in self.oob_tracker_storage.items() if key[1] == sessid]
def track_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker):
"""
@ -336,14 +332,14 @@ class OOBHandler(object):
"""
# all database field names starts with db_*
field_name = field_name if field_name.startswith("db_") else "db_%s" % field_name
self._track(obj, sessid, field_name, trackerclass)
self._track(obj, sessid, field_name, trackerclass, field_name)
def untrack_field(self, obj, sessid, field_name):
def untrack_field(self, obj, sessid, field_name, trackerclass=ReportFieldTracker):
"""
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)
self._untrack(obj, sessid, field_name, trackerclass)
def track_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker):
"""
@ -353,14 +349,15 @@ class OOBHandler(object):
"""
# get the attribute object if we can
try:
obj = obj.dbobj
attrobj = obj.dbobj
except AttributeError:
pass
attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True)
attrobj = obj.attributes.get(attr_name, return_obj=True)
#print "track_attribute attrobj:", attrobj, id(attrobj)
if attrobj:
self._track(attrobj, sessid, "db_value", trackerclass, attr_name)
def untrack_attribute(self, obj, sessid, attr_name, trackerclass):
def untrack_attribute(self, obj, sessid, attr_name, trackerclass=ReportAttributeTracker):
"""
Shortcut for deactivating tracking for a given attribute.
"""
@ -368,25 +365,24 @@ class OOBHandler(object):
obj = obj.dbobj
except AttributeError:
pass
attrobj = _GA(obj, "attributes").get(attr_name, return_obj=True)
attrobj = obj.attributes.get(attr_name, return_obj=True)
if attrobj:
self._untrack(attrobj, sessid, attr_name, trackerclass)
self._untrack(attrobj, sessid, "db_value", trackerclass, attr_name)
def repeat(self, obj, sessid, func_key, interval=20, *args, **kwargs):
def repeat(self, obj, sessid, interval=20, callback=None, *args, **kwargs):
"""
Start a repeating action. Every interval seconds,
the oobfunc corresponding to func_key is called with
args and kwargs.
Start a repeating action. Every interval seconds, trigger
callback(*args, **kwargs). The callback is called with
args and kwargs; note that *args and **kwargs may not contain
anything un-picklable (use dbrefs if wanting to use objects).
"""
if not func_key in _OOB_FUNCS:
raise KeyError("%s is not a valid OOB function name.")
self.tickerhandler.add(self, obj, interval, func_key=func_key, sessid=sessid, *args, **kwargs)
self.tickerhandler.add(obj, interval, sessid, callback, *args, **kwargs)
def unrepeat(self, obj, sessid, func_key, interval=20):
def unrepeat(self, obj, sessid, interval=20):
"""
Stop a repeating action
"""
self.tickerhandler.remove(self, obj, interval)
self.tickerhandler.remove(obj, interval)
# access method - called from session.msg()
@ -396,23 +392,26 @@ class OOBHandler(object):
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)
oobfunc = _OOB_FUNCS.get(func_key, None)
if not oobfunc:
# function not found
errmsg = "OOB Error: function '%s' not recognized." % func_key
if _OOB_ERROR:
_OOB_ERROR(self, session, errmsg, *args, **kwargs)
logger.log_trace()
else:
logger.log_trace(errmsg)
raise KeyError(errmsg)
return
# execute the found function
try:
#print "OOB execute_cmd:", session, func_key, args, kwargs, _OOB_FUNCS.keys()
oobfunc(self, session, *args, **kwargs)
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)
logger.log_trace(errmsg)
raise Exception(errmsg)
def msg(self, sessid, funcname, *args, **kwargs):

View file

@ -31,7 +31,7 @@ import json
from twisted.internet.protocol import Protocol
from src.server.session import Session
from src.utils.logger import log_trace
from src.utils.utils import to_str
from src.utils.utils import to_str, make_iter
from src.utils.text2html import parse_html
@ -85,7 +85,8 @@ class WebSocketClient(Protocol, Session):
try:
oobdata = json.loads(string)
for (key, args) in oobdata.items():
self.data_in(text=None, oob=(key, args))
#print "oob data in:", (key, args)
self.data_in(text=None, oob=(key, make_iter(args)))
except Exception:
log_trace("Websocket malformed OOB request: %s" % string)
else:
@ -119,6 +120,7 @@ class WebSocketClient(Protocol, Session):
self.sendLine(str(e))
if "oob" in kwargs:
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob"))
#print "oob data_out:", "OOB" + json.dumps(oobstruct)
self.sendLine("OOB" + json.dumps(oobstruct))
raw = kwargs.get("raw", False)
nomarkup = kwargs.get("nomarkup", False)

View file

@ -33,9 +33,9 @@ from src.server.sessionhandler import SESSIONS
# setting up server-side field cache
from django.db.models.signals import post_save
from src.server.caches import field_pre_save
from src.server.caches import field_post_save
#pre_save.connect(field_pre_save, dispatch_uid="fieldcache")
post_save.connect(field_pre_save, dispatch_uid="fieldcache")
post_save.connect(field_post_save, dispatch_uid="fieldcache")
#from src.server.caches import post_attr_update
#from django.db.models.signals import m2m_changed

View file

@ -206,6 +206,7 @@ class ServerSession(Session):
if not _OOB_HANDLER:
from src.server.oobhandler import OOB_HANDLER as _OOB_HANDLER
oobstruct = self.sessionhandler.oobstruct_parser(kwargs.pop("oob", None))
#print "session.data_in: oobstruct:",oobstruct
for (funcname, args, kwargs) in oobstruct:
if funcname:
_OOB_HANDLER.execute_cmd(self, funcname, *args, **kwargs)

View file

@ -136,14 +136,17 @@ class SessionHandler(object):
elif isinstance(oobstruct[1], (tuple, list)):
# cmdname, (args,)
return (oobstruct[0].lower(), list(oobstruct[1]), {})
else:
# cmdname, cmdname
return ((oobstruct[0].lower(), (), {}), (oobstruct[1].lower(), (), {}))
else:
# cmdname, (args,), {kwargs}
return (oobstruct[0].lower(), list(oobstruct[1]), dict(oobstruct[2]))
if hasattr(oobstruct, "__iter__"):
# differentiate between (cmdname, cmdname),
# (cmdname, args, kwargs) and ((cmdname,args,kwargs),
# (cmdname,args,kwargs), ...)
# (cmdname, (args), {kwargs}) and ((cmdname,(args),{kwargs}),
# (cmdname,(args),{kwargs}), ...)
if oobstruct and isinstance(oobstruct[0], basestring):
return (list(_parse(oobstruct)),)

View file

@ -70,7 +70,7 @@ _DA = object.__delattr__
#------------------------------------------------------------
#class Attribute(SharedMemoryModel):
class Attribute(WeakSharedMemoryModel):
class Attribute(SharedMemoryModel):
"""
Abstract django model.
@ -173,13 +173,6 @@ class Attribute(WeakSharedMemoryModel):
"""
self.db_value = to_pickle(new_value)
self.save(update_fields=["db_value"])
try:
# eventual OOB hook
#self._track_db_value_change.update(self.cached_value)
self._track_db_value_change.update(self.new_value)
except AttributeError:
pass
return
#@value.deleter
def __value_del(self):

View file

@ -20,13 +20,38 @@ var OOB_debug = true
// Custom OOB functions
// functions defined here can be called by name by the server. For
// example the OOB{"echo":arguments} will trigger a function named
// echo(arguments).
// example the OOB{"echo":(args),{kwargs}} will trigger a function named
// echo(args, kwargs).
function echo(message) {
function echo(args, kwargs) {
// example echo function.
doShow("out", "ECHO return: " + message) }
doShow("out", "ECHO return: " + args) }
function list (args, kwargs) {
// show in main window
doShow("out", args) }
function send (args, kwargs) {
// show in main window. SEND returns kwargs {name:value}.
for (sendvalue in kwargs) {
doShow("out", sendvalue + " = " + kwargs[sendvalue]);}
}
function report (args, kwargs) {
// show in main window. REPORT returns kwargs
// {attrfieldname:value}
for (name in kwargs) {
doShow("out", name + " = " + kwargs[name]) }
}
function repeat (args, kwargs) {
// called by repeating oob funcs
doShow("out", args) }
function err (args, kwargs) {
// display error
doShow("err", args) }
// Webclient code
@ -61,15 +86,20 @@ function onMessage(evt) {
var inmsg = evt.data
if (inmsg.length > 3 && inmsg.substr(0, 3) == "OOB") {
// dynamically call oob methods, if available
try {var oobarray = JSON.parse(inmsg.slice(3));} // everything after OOB }
try {
var oobarray = JSON.parse(inmsg.slice(3));} // everything after OOB }
catch(err) {
// not JSON packed - a normal text
doShow('out', err + " " + inmsg);
doShow('out', inmsg);
return;
}
for (var ind in oobarray) {
try { window[oobarray[ind][0]](oobarray[ind][1]) }
catch(err) { doShow("err", "Could not execute OOB function " + oobtuple[0] + "(" + oobtuple[1] + ")!") }
if (typeof oobarray != "undefined") {
for (var ind in oobarray) {
try {
window[oobarray[ind][0]](oobarray[ind][1], oobarray[ind][2]) }
catch(err) {
doShow("err", "Could not execute js OOB function '" + oobarray[ind][0] + "(" + oobarray[ind][1] + oobarray[ind][2] + ")'") }
}
}
}
else {
@ -93,8 +123,15 @@ function doSend(){
if (OOB_debug && outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") {
// test OOB messaging
doShow("out", "OOB input: " + outmsg.slice(5))
doOOB(JSON.parse(outmsg.slice(5))); }
try {
doShow("out", "OOB input: " + outmsg.slice(5));
if (outmsg.length == 5) {
doShow("err", "OOB testing syntax: ##OOB{\"cmdname:args, ...}"); }
else {
doOOB(JSON.parse(outmsg.slice(5))); } }
catch(err) {
doShow("err", err) }
}
else {
// normal output
websocket.send(outmsg); }