diff --git a/src/server/oob_cmds.py b/src/server/oob_cmds.py new file mode 100644 index 0000000000..88236104e0 --- /dev/null +++ b/src/server/oob_cmds.py @@ -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) + } diff --git a/src/server/oob_msdp.py b/src/server/oob_msdp.py deleted file mode 100644 index 6b474fde79..0000000000 --- a/src/server/oob_msdp.py +++ /dev/null @@ -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)) diff --git a/src/server/oobhandler.py b/src/server/oobhandler.py index fea7a5642a..aeacfe3c04 100644 --- a/src/server/oobhandler.py +++ b/src/server/oobhandler.py @@ -45,7 +45,7 @@ from src.server.sessionhandler import SESSIONS from src.scripts.tickerhandler import Ticker, TickerPool, TickerHandler from src.utils.dbserialize import dbserialize, dbunserialize, pack_dbobj, unpack_dbobj 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__ _GA = object.__getattribute__ @@ -55,9 +55,9 @@ _DA = object.__delattr__ _OOB_FUNCS = {} 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) +# get custom error method or use the default +_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): @@ -131,11 +131,12 @@ class TrackerHandler(object): logger.log_trace() -# Tracker loaded by the TrackerHandler +# On-object Trackers to load with TrackerHandler 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): pass @@ -148,6 +149,61 @@ class TrackerBase(object): "Called when tracker is removed" 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 class OOBTicker(Ticker): @@ -273,7 +329,7 @@ class OOBHandler(object): sessid = session.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. 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 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 Attribute on an object. Will create a tracker on the Attribute @@ -332,12 +388,6 @@ class OOBHandler(object): """ 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() @@ -347,7 +397,7 @@ class OOBHandler(object): using *args and **kwargs """ 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(self, session, *args, **kwargs) except KeyError,e: @@ -364,5 +414,14 @@ class OOBHandler(object): else: logger.log_trace(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 OOB_HANDLER = OOBHandler() diff --git a/src/server/sessionhandler.py b/src/server/sessionhandler.py index ec69d35c6e..b911226095 100644 --- a/src/server/sessionhandler.py +++ b/src/server/sessionhandler.py @@ -103,15 +103,9 @@ class SessionHandler(object): def oobstruct_parser(self, oobstruct): """ Helper method for each session to use to parse oob structures - (The 'oob' kwarg of the msg() method) - ((cmdname, (args), {}), ...) - - - Allowed oob structures are: - allowed oob structures are - - + (The 'oob' kwarg of the msg() method). + Allowed input oob structures are: cmdname ((cmdname,), (cmdname,)) (cmdname,(arg, )) diff --git a/src/settings_default.py b/src/settings_default.py index 0c470fd3a7..9a6d466a52 100644 --- a/src/settings_default.py +++ b/src/settings_default.py @@ -230,7 +230,7 @@ LOCK_FUNC_MODULES = ("src.locks.lockfuncs",) # 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 # 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 diff --git a/src/web/media/javascript/evennia_websocket_webclient.js b/src/web/media/javascript/evennia_websocket_webclient.js index a5ba1d156a..b604fc92fc 100644 --- a/src/web/media/javascript/evennia_websocket_webclient.js +++ b/src/web/media/javascript/evennia_websocket_webclient.js @@ -29,8 +29,6 @@ function echo(message) { doShow("out", "ECHO return: " + message) } - - // Webclient code function webclient_init(){ @@ -95,6 +93,7 @@ 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))); } else { // normal output