mirror of
https://github.com/evennia/evennia.git
synced 2026-03-31 13:07:16 +02:00
First version of working websocket protocol.
This commit is contained in:
parent
01d15f13ee
commit
ef0a154a61
5 changed files with 53 additions and 36 deletions
|
|
@ -210,7 +210,6 @@ def oob_error(oobhandler, session, errmsg, *args, **kwargs):
|
|||
"""
|
||||
session.msg(oob=("send", {"ERROR": errmsg}))
|
||||
|
||||
|
||||
def list(oobhandler, session, mode, *args, **kwargs):
|
||||
"""
|
||||
List available properties. Mode is the type of information
|
||||
|
|
|
|||
|
|
@ -1,31 +1,36 @@
|
|||
"""
|
||||
OOBHandler - Out Of Band Handler
|
||||
|
||||
The OOBHandler is called directly by out-of-band protocols. It supplies three
|
||||
pieces of functionality:
|
||||
The OOBHandler.execute_cmd is called by the sessionhandler when it detects
|
||||
an OOB instruction (exactly how this looked depends on the protocol; at this
|
||||
point all oob calls should look the same)
|
||||
|
||||
The handler pieces of functionality:
|
||||
|
||||
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.
|
||||
as global functions in 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.
|
||||
behaviour of those objects can be customized by adding new
|
||||
tracker classes in settings.OOB_PLUGIN_MODULES.
|
||||
|
||||
What goes into the OOB_PLUGIN_MODULES is a list of modules with input
|
||||
for the OOB system.
|
||||
What goes into the OOB_PLUGIN_MODULES is a (list of) modules that contains
|
||||
the working server-side code available to the OOB system: oob functions and
|
||||
tracker classes.
|
||||
|
||||
oob functions have the following call signature:
|
||||
function(caller, *args, **kwargs)
|
||||
function(caller, session, *args, **kwargs)
|
||||
|
||||
oob trackers should inherit from the OOBTracker class in this
|
||||
module and implement a minimum of the same functionality.
|
||||
oob trackers should inherit from the OOBTracker class (in this
|
||||
module) and implement a minimum of the same functionality.
|
||||
|
||||
a global function oob_error will be used as optional error management.
|
||||
If a function named "oob_error" is given, this will be called with error
|
||||
messages.
|
||||
|
||||
"""
|
||||
|
||||
|
|
@ -46,12 +51,18 @@ _SA = object.__setattr__
|
|||
_GA = object.__getattribute__
|
||||
_DA = object.__delattr__
|
||||
|
||||
# load from plugin module
|
||||
# load resources from plugin module
|
||||
_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)
|
||||
|
||||
if not _OOB_ERROR:
|
||||
# create default oob error message function
|
||||
def oob_error(oobhandler, session, errmsg, *args, **kwargs):
|
||||
session.msg(oob=("send", {"ERROR": errmsg}))
|
||||
_OOB_ERROR = oob_error
|
||||
|
||||
class TrackerHandler(object):
|
||||
"""
|
||||
|
|
@ -444,13 +455,13 @@ class OOBHandler(object):
|
|||
_OOB_ERROR(self, session, errmsg, *args, **kwargs)
|
||||
else:
|
||||
logger.log_trace(errmsg)
|
||||
raise
|
||||
raise KeyError(errmsg)
|
||||
except Exception, err:
|
||||
errmsg = "OOB Error: Exception in '%s'(%s, %s):\n%s" % (func_key, args, kwargs, err)
|
||||
if _OOB_ERROR:
|
||||
_OOB_ERROR(self, session, errmsg, *args, **kwargs)
|
||||
else:
|
||||
logger.log_trace(errmsg)
|
||||
raise
|
||||
raise Exception(errmsg)
|
||||
# access object
|
||||
OOB_HANDLER = OOBHandler()
|
||||
|
|
|
|||
|
|
@ -285,10 +285,10 @@ if WEBSOCKET_ENABLED:
|
|||
ifacestr = "-%s" % interface
|
||||
for port in WEBSOCKET_PORTS:
|
||||
pstring = "%s:%s" % (ifacestr, port)
|
||||
factory = WebSocketFactory(protocol.ServerFactory())
|
||||
factory = protocol.ServerFactory()
|
||||
factory.protocol = websocket.WebSocketProtocol
|
||||
factory.sessionhandler = PORTAL_SESSIONS
|
||||
websocket_service = internet.TCPServer(port, factory, interface=interface)
|
||||
websocket_service = internet.TCPServer(port, WebSocketFactory(factory), interface=interface)
|
||||
websocket_service.setName('EvenniaWebSocket%s' % pstring)
|
||||
PORTAL.services.addService(websocket_service)
|
||||
|
||||
|
|
|
|||
|
|
@ -9,13 +9,22 @@ Thanks to Ricard Pillosu whose Evennia plugin inspired this module.
|
|||
Communication over the websocket interface is done with normal text
|
||||
communication. A special case is OOB-style communication; to do this
|
||||
the client must send data on the following form:
|
||||
OOB(oobfunc, args, kwargs)
|
||||
or
|
||||
OOB[(oobfunc, args, kwargs), ...]
|
||||
|
||||
OOB{oobfunc:[[args], {kwargs}], ...}
|
||||
|
||||
where the tuple/list is sent json-encoded. The initial OOB-prefix
|
||||
is used to identify this type of communication, all other data
|
||||
is considered plain text (command input).
|
||||
|
||||
Example of call from javascript client:
|
||||
|
||||
websocket = new WeSocket("ws://localhost:8021")
|
||||
var msg1 = "WebSocket Test"
|
||||
websocket.send(msg1)
|
||||
var msg2 = JSON.stringify({"testfunc":[[1,2,3], {"kwarg":"val"}]})
|
||||
websocket.send("OOB" + msg2)
|
||||
websocket.close()
|
||||
|
||||
"""
|
||||
import json
|
||||
from twisted.internet.protocol import Protocol
|
||||
|
|
@ -52,7 +61,7 @@ class WebSocketProtocol(Protocol, Session):
|
|||
the disconnect method
|
||||
"""
|
||||
self.sessionhandler.disconnect(self)
|
||||
self.transport.loseconnection()
|
||||
self.transport.close()
|
||||
|
||||
def dataReceived(self, string):
|
||||
"""
|
||||
|
|
@ -62,11 +71,8 @@ class WebSocketProtocol(Protocol, Session):
|
|||
Type of data is identified by a 3-character
|
||||
prefix.
|
||||
OOB - This is an Out-of-band instruction. If so,
|
||||
the remaining string should either be
|
||||
a json packed tuple (oobfuncname, args, kwargs)
|
||||
or a json-packed list of tuples
|
||||
[(oobfuncname, args, kwargs), ...] to send to
|
||||
the OOBhandler.
|
||||
the remaining string should be a json-packed
|
||||
string on the form {oobfuncname: [[args], {kwargs}], ...}
|
||||
any other prefix (or lack of prefix) is considered
|
||||
plain text data, to be treated like a game
|
||||
input command.
|
||||
|
|
@ -75,19 +81,20 @@ class WebSocketProtocol(Protocol, Session):
|
|||
string = string[3:]
|
||||
try:
|
||||
oobdata = json.loads(string)
|
||||
if isinstance(oobdata, list):
|
||||
for oobtuple in oobdata:
|
||||
self.data_in(oob=oobtuple)
|
||||
elif isinstance(oobdata, tuple):
|
||||
self.data_in(oob=oobtuple)
|
||||
else:
|
||||
raise RuntimeError("OOB data is not list or tuple.")
|
||||
except:
|
||||
log_trace("Websocket malformed OOB request: %s" % oobdata)
|
||||
for (key, argstuple) in oobdata.items():
|
||||
args = argstuple[0] if argstuple else []
|
||||
kwargs = argstuple[1] if len(argstuple) > 1 else {}
|
||||
self.data_in(oob=(key, args, kwargs))
|
||||
except Exception:
|
||||
log_trace("Websocket malformed OOB request: %s" % string)
|
||||
else:
|
||||
# plain text input
|
||||
self.data_in(text=string)
|
||||
|
||||
def sendLine(self, line):
|
||||
"send data to client"
|
||||
return self.transport.write(line)
|
||||
|
||||
def data_in(self, text=None, **kwargs):
|
||||
"""
|
||||
Data Websocket -> Server
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ WEBSOCKET_ENABLED = False
|
|||
# Ports to use for Websockets
|
||||
WEBSOCKET_PORTS = [8021]
|
||||
# Interface addresses to listen to. If 0.0.0.0, listen to all. Use :: for IPv6.
|
||||
WEBSOCKET_INTERFACES = ['0.0.0.0.']
|
||||
WEBSOCKET_INTERFACES = ['0.0.0.0']
|
||||
# The path that contains this settings.py file (no trailing slash).
|
||||
BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
# Path to the src directory containing the bulk of the codebase's code.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue