mirror of
https://github.com/evennia/evennia.git
synced 2026-04-04 23:17:17 +02:00
Updated to a working websocket implementation of webclient.
This commit is contained in:
parent
ca1e36da5f
commit
d59500f574
6 changed files with 133 additions and 121 deletions
|
|
@ -42,20 +42,21 @@ TELNET_PORTS = settings.TELNET_PORTS
|
|||
SSL_PORTS = settings.SSL_PORTS
|
||||
SSH_PORTS = settings.SSH_PORTS
|
||||
WEBSERVER_PORTS = settings.WEBSERVER_PORTS
|
||||
WEBSOCKET_PORTS = settings.WEBSOCKET_PORTS
|
||||
WEBSOCKET_CLIENT_PORT = settings.WEBSOCKET_CLIENT_PORT
|
||||
|
||||
TELNET_INTERFACES = settings.TELNET_INTERFACES
|
||||
SSL_INTERFACES = settings.SSL_INTERFACES
|
||||
SSH_INTERFACES = settings.SSH_INTERFACES
|
||||
WEBSERVER_INTERFACES = settings.WEBSERVER_INTERFACES
|
||||
WEBSOCKET_INTERFACES = settings.WEBSOCKET_INTERFACES
|
||||
WEBSOCKET_CLIENT_INTERFACE = settings.WEBSOCKET_CLIENT_INTERFACE
|
||||
WEBSOCKET_CLIENT_URL = settings.WEBSOCKET_CLIENT_URL
|
||||
|
||||
TELNET_ENABLED = settings.TELNET_ENABLED and TELNET_PORTS and TELNET_INTERFACES
|
||||
SSL_ENABLED = settings.SSL_ENABLED and SSL_PORTS and SSL_INTERFACES
|
||||
SSH_ENABLED = settings.SSH_ENABLED and SSH_PORTS and SSH_INTERFACES
|
||||
WEBSERVER_ENABLED = settings.WEBSERVER_ENABLED and WEBSERVER_PORTS and WEBSERVER_INTERFACES
|
||||
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||
WEBSOCKET_ENABLED = settings.WEBSOCKET_ENABLED and WEBSOCKET_PORTS and WEBSOCKET_INTERFACES
|
||||
WEBSOCKET_CLIENT_ENABLED = settings.WEBSOCKET_CLIENT_ENABLED and WEBSOCKET_CLIENT_PORT and WEBSOCKET_CLIENT_INTERFACE
|
||||
|
||||
AMP_HOST = settings.AMP_HOST
|
||||
AMP_PORT = settings.AMP_PORT
|
||||
|
|
@ -257,16 +258,31 @@ if WEBSERVER_ENABLED:
|
|||
if WEBCLIENT_ENABLED:
|
||||
# create ajax client processes at /webclientdata
|
||||
from src.server.portal.webclient import WebClient
|
||||
|
||||
webclient = WebClient()
|
||||
webclient.sessionhandler = PORTAL_SESSIONS
|
||||
web_root.putChild("webclientdata", webclient)
|
||||
webclientstr = "/client"
|
||||
webclientstr = "\n + client (ajax only)"
|
||||
|
||||
#from src.server.portal import websocket
|
||||
#factory = protocol.ServerFactory()
|
||||
#websocketclient = websocket.WebSocketProtocol()
|
||||
#websocketclient.sessionhandler = PORTAL_SESSIONS
|
||||
#web_root.putChild("websocket", websocketclient)
|
||||
if WEBSOCKET_CLIENT_ENABLED:
|
||||
# start websocket client port for the webclient
|
||||
from src.server.portal import websocket_client
|
||||
from src.utils.txws import WebSocketFactory
|
||||
|
||||
interface = WEBSOCKET_CLIENT_INTERFACE
|
||||
port = WEBSOCKET_CLIENT_PORT
|
||||
ifacestr = ""
|
||||
if interface not in ('0.0.0.0', '::'):
|
||||
ifacestr = "-%s" % interface
|
||||
pstring = "%s:%s" % (ifacestr, port)
|
||||
factory = protocol.ServerFactory()
|
||||
factory.protocol = websocket_client.WebSocketClient
|
||||
factory.sessionhandler = PORTAL_SESSIONS
|
||||
websocket_service = internet.TCPServer(port, WebSocketFactory(factory), interface=interface)
|
||||
websocket_service.setName('EvenniaWebSocket%s' % pstring)
|
||||
PORTAL.services.addService(websocket_service)
|
||||
|
||||
webclientstr = webclientstr[:-11] + "(%s:%s)" % (WEBSOCKET_CLIENT_URL, port)
|
||||
|
||||
web_root = server.Site(web_root, logPath=settings.HTTP_LOG_FILE)
|
||||
proxy_service = internet.TCPServer(proxyport,
|
||||
|
|
@ -274,30 +290,8 @@ if WEBSERVER_ENABLED:
|
|||
interface=interface)
|
||||
proxy_service.setName('EvenniaWebProxy%s' % pstring)
|
||||
PORTAL.services.addService(proxy_service)
|
||||
print " webproxy%s%s:%s (<-> %s)" % (webclientstr, ifacestr, proxyport, serverport)
|
||||
print " webproxy%s:%s (<-> %s)%s" % (ifacestr, proxyport, serverport, webclientstr)
|
||||
|
||||
if WEBSOCKET_ENABLED:
|
||||
# websocket support is experimental!
|
||||
|
||||
# start websocket ports for real-time web communication
|
||||
|
||||
from src.server.portal import websocket
|
||||
from src.utils.txws import WebSocketFactory
|
||||
|
||||
for interface in WEBSOCKET_INTERFACES:
|
||||
ifacestr = ""
|
||||
if interface not in ('0.0.0.0', '::') or len(WEBSOCKET_INTERFACES) > 1:
|
||||
ifacestr = "-%s" % interface
|
||||
for port in WEBSOCKET_PORTS:
|
||||
pstring = "%s:%s" % (ifacestr, port)
|
||||
factory = protocol.ServerFactory()
|
||||
factory.protocol = websocket.WebSocketProtocol
|
||||
factory.sessionhandler = PORTAL_SESSIONS
|
||||
websocket_service = internet.TCPServer(port, WebSocketFactory(factory), interface=interface)
|
||||
websocket_service.setName('EvenniaWebSocket%s' % pstring)
|
||||
PORTAL.services.addService(websocket_service)
|
||||
|
||||
print ' websocket%s: %s' % (ifacestr, port)
|
||||
|
||||
for plugin_module in PORTAL_SERVICES_PLUGIN_MODULES:
|
||||
# external plugin services to start
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
"""
|
||||
Websockets Protocol
|
||||
Websocket-webclient
|
||||
|
||||
This implements WebSockets (http://en.wikipedia.org/wiki/WebSocket)
|
||||
by use of the txws implementation (https://github.com/MostAwesomeDude/txWS).
|
||||
This implements a webclient with WebSockets (http://en.wikipedia.org/wiki/WebSocket)
|
||||
by use of the txws implementation (https://github.com/MostAwesomeDude/txWS). It is
|
||||
used together with src/web/media/javascript/evennia_websocket_webclient.js.
|
||||
|
||||
Thanks to Ricard Pillosu whose Evennia plugin inspired this module.
|
||||
|
||||
|
|
@ -10,13 +11,13 @@ 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}], ...}
|
||||
OOB{"func1":[args], "func2":[args], ...}
|
||||
|
||||
where the tuple/list is sent json-encoded. The initial OOB-prefix
|
||||
where the dict is 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:
|
||||
Example of call from a javascript client:
|
||||
|
||||
websocket = new WeSocket("ws://localhost:8021")
|
||||
var msg1 = "WebSocket Test"
|
||||
|
|
@ -33,9 +34,10 @@ from src.utils.logger import log_trace
|
|||
from src.utils.utils import to_str
|
||||
from src.utils.text2html import parse_html
|
||||
|
||||
class WebSocketProtocol(Protocol, Session):
|
||||
|
||||
class WebSocketClient(Protocol, Session):
|
||||
"""
|
||||
This is called when the connection is first established
|
||||
Implements the server-side of the Websocket connection.
|
||||
"""
|
||||
|
||||
def connectionMade(self):
|
||||
|
|
@ -58,18 +58,22 @@ UPSTREAM_IPS = ['127.0.0.1']
|
|||
# with server load. Set the minimum and maximum number of threads it
|
||||
# may use as (min, max) (must be > 0)
|
||||
WEBSERVER_THREADPOOL_LIMITS = (1, 20)
|
||||
# Start the evennia ajax client on /webclient
|
||||
# (the webserver must also be running)
|
||||
# Start the evennia webclient. This requires the webserver to be running and
|
||||
# offers the fallback ajax-based webclient backbone for browsers not supporting
|
||||
# the websocket one.
|
||||
WEBCLIENT_ENABLED = True
|
||||
# Activate Websocket support. If this is on, the default webclient will use this
|
||||
# before going for the ajax implementation
|
||||
WEBSOCKET_ENABLED = True
|
||||
# Ports to use for Websockets. If this is changed, you must also update
|
||||
# src/web/media/javascript/evennia_websocket_webclient.js to match.
|
||||
WEBSOCKET_PORTS = [8001]
|
||||
# Activate Websocket support for modern browsers. If this is on, the
|
||||
# default webclient will use this and only use the ajax version of the browser
|
||||
# is too old to support websockets. Requires WEBCLIENT_ENABLED.
|
||||
WEBSOCKET_CLIENT_ENABLED = True
|
||||
# Server-side websocket port to open for the webclient.
|
||||
WEBSOCKET_CLIENT_PORT = 8001
|
||||
# Interface addresses to listen to. If 0.0.0.0, listen to all. Use :: for IPv6.
|
||||
WEBSOCKET_INTERFACES = ['0.0.0.0']
|
||||
# Activate SSH protocol (SecureShell)
|
||||
WEBSOCKET_CLIENT_INTERFACE = '0.0.0.0'
|
||||
# Actual URL for webclient component to reach the websocket. The first
|
||||
# port number in the WEBSOCKET_PORTS list will be automatically appended.
|
||||
WEBSOCKET_CLIENT_URL = "ws://localhost"
|
||||
# Activate SSH protocol communication (SecureShell)
|
||||
SSH_ENABLED = False
|
||||
# Ports to use for SSH
|
||||
SSH_PORTS = [8022]
|
||||
|
|
|
|||
|
|
@ -2,30 +2,39 @@
|
|||
|
||||
Evennia websocket webclient (javascript component)
|
||||
|
||||
The client is composed of several parts:
|
||||
templates/webclient.html - the main page
|
||||
webclient/views.py - the django view serving the template (based on urls.py pattern)
|
||||
src/server/portal/websockets.py - the server component talking to the client
|
||||
The client is composed of two parts:
|
||||
src/server/portal/websocket_client.py - the portal-side component
|
||||
this file - the javascript component handling dynamic content
|
||||
|
||||
This implements an mud client for use with Evennia, using jQuery
|
||||
for simplicity.
|
||||
|
||||
messages sent to the client is one of three modes:
|
||||
OOB(func,(args), func,(args), ...) - OOB command executions
|
||||
text - any other text is considered a normal text to echo
|
||||
messages sent to the client is one of two modes:
|
||||
OOB("func1",args, "func2",args, ...) - OOB command executions, this will
|
||||
call unique javascript functions
|
||||
func1(args), func2(args) etc.
|
||||
text - any other text is considered a normal text output in the main output window.
|
||||
|
||||
*/
|
||||
|
||||
// jQuery must be imported by the calling html page before this script
|
||||
// There are plenty of help on using the jQuery library on http://jquery.com/
|
||||
// If on, allows client user to send OOB messages to server by
|
||||
// prepending with ##OOB{}, for example ##OOB{"echo":[1,2,3,4]}
|
||||
var OOB_debug = true
|
||||
|
||||
// Server communications
|
||||
// Set this to the value matching settings.WEBSOCKET_PORTS
|
||||
var wsurl = "ws://localhost:8001";
|
||||
// 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).
|
||||
|
||||
|
||||
function echo(message) {
|
||||
// example echo function.
|
||||
doShow("out", "ECHO return: " + message) }
|
||||
|
||||
|
||||
|
||||
|
||||
// Webclient code
|
||||
|
||||
function webclient_init(){
|
||||
// initializing the client once the html page has loaded
|
||||
// called when client is just initializing
|
||||
websocket = new WebSocket(wsurl);
|
||||
websocket.onopen = function(evt) { onOpen(evt) };
|
||||
websocket.onclose = function(evt) { onClose(evt) };
|
||||
|
|
@ -34,91 +43,100 @@ function webclient_init(){
|
|||
}
|
||||
|
||||
function onOpen(evt) {
|
||||
// client is just connecting
|
||||
// called when client is first connecting
|
||||
$("#connecting").remove(); // remove the "connecting ..." message
|
||||
msg_display("sys", "Using websockets - connected to " + wsurl + ".")
|
||||
doShow("sys", "Using websockets - connected to " + wsurl + ".")
|
||||
|
||||
setTimeout(function () {
|
||||
$("#playercount").fadeOut('slow', webclient_set_sizes);
|
||||
$("#playercount").fadeOut('slow', doSetSizes);
|
||||
}, 10000);
|
||||
}
|
||||
|
||||
function onClose(evt) {
|
||||
// client is closing
|
||||
// called when client is closing
|
||||
CLIENT_HASH = 0;
|
||||
alert("Mud client connection was closed cleanly.");
|
||||
}
|
||||
|
||||
function onMessage(evt) {
|
||||
// outgoing message from server
|
||||
// called when the Evennia is sending data to client
|
||||
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 }
|
||||
catch(err) {
|
||||
// not JSON packed - a normal text
|
||||
msg_display('out', err + " " + inmsg);
|
||||
doShow('out', err + " " + inmsg);
|
||||
return;
|
||||
}
|
||||
for (var ind in oobarray) {
|
||||
try { window[oobarray[ind][0]](oobarray[ind][1]) }
|
||||
catch(err) { msg_display("err", "Could not execute OOB function " + oobtuple[0] + "(" + oobtuple[1] + ")!") }
|
||||
catch(err) { doShow("err", "Could not execute OOB function " + oobtuple[0] + "(" + oobtuple[1] + ")!") }
|
||||
}
|
||||
}
|
||||
else {
|
||||
// normal message
|
||||
msg_display('out', inmsg); }
|
||||
doShow('out', inmsg); }
|
||||
}
|
||||
|
||||
function onError(evt) {
|
||||
// client error message
|
||||
msg_display('err', "Error: Server returned an error. Try reloading the page.");
|
||||
// called on a server error
|
||||
doShow('err', "Error: Server returned an error. Try reloading the page.");
|
||||
}
|
||||
|
||||
function doSend(){
|
||||
// sending data from client to server
|
||||
// relays data from client to Evennia.
|
||||
// If OOB_debug is set, allows OOB test data on the
|
||||
// form ##OOB{func:args}
|
||||
outmsg = $("#inputfield").val();
|
||||
history_add(outmsg);
|
||||
HISTORY_POS = 0;
|
||||
$('#inputform')[0].reset(); // clear input field
|
||||
|
||||
if (outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") {
|
||||
if (OOB_debug && outmsg.length > 4 && outmsg.substr(0, 5) == "##OOB") {
|
||||
// test OOB messaging
|
||||
doOOB(JSON.parse(outmsg.slice(5))); }
|
||||
else {
|
||||
// normal output
|
||||
websocket.send(outmsg); }
|
||||
}
|
||||
|
||||
function doOOB(oobdict){
|
||||
// Handle OOB communication from client side
|
||||
// Takes a dict on form {funcname:[args], funcname: [args], ... ]
|
||||
msg_display("out", "into doOOB: " + oobdict)
|
||||
msg_display("out", "stringify: " + JSON.stringify(oobdict))
|
||||
// Send OOB data from client to Evennia.
|
||||
// Takes input on form {funcname:[args], funcname: [args], ... }
|
||||
var oobmsg = JSON.stringify(oobdict);
|
||||
websocket.send("OOB" + oobmsg);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// OOB functions
|
||||
//
|
||||
|
||||
function echo(message) {
|
||||
msg_display("out", "ECHO return: " + message) }
|
||||
|
||||
//
|
||||
// Display messages
|
||||
|
||||
function msg_display(type, msg){
|
||||
// Add a div to the message window.
|
||||
function doShow(type, msg){
|
||||
// Add msg to the main output window.
|
||||
// type gives the class of div to use.
|
||||
// The default types are
|
||||
// "out" (normal output) or "err" (red error message)
|
||||
$("#messagewindow").append(
|
||||
"<div class='msg "+ type +"'>"+ msg +"</div>");
|
||||
// scroll message window to bottom
|
||||
$('#messagewindow').animate({scrollTop: $('#messagewindow')[0].scrollHeight});
|
||||
}
|
||||
|
||||
// Input history mechanism
|
||||
|
||||
function doSetSizes() {
|
||||
// Sets the size of the message window
|
||||
var win_h = $(document).height();
|
||||
//var win_w = $('#wrapper').width();
|
||||
var inp_h = $('#inputform').outerHeight(true);
|
||||
//var inp_w = $('#inputsend').outerWidth(true);
|
||||
|
||||
$("#messagewindow").css({'height': win_h - inp_h - 1});
|
||||
//$("#inputfield").css({'width': win_w - inp_w - 20});
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Input code
|
||||
//
|
||||
|
||||
// Input history
|
||||
|
||||
var HISTORY_MAX_LENGTH = 21
|
||||
var HISTORY = new Array();
|
||||
|
|
@ -223,6 +241,8 @@ $.fn.appendCaret = function() {
|
|||
});
|
||||
};
|
||||
|
||||
// Input jQuery callbacks
|
||||
|
||||
$(document).keydown( function(event) {
|
||||
// Get the pressed key (normalized by jQuery)
|
||||
var code = event.which,
|
||||
|
|
@ -233,7 +253,7 @@ $(document).keydown( function(event) {
|
|||
|
||||
// Special keys recognized by client
|
||||
|
||||
//msg_display("out", "key code pressed: " + code); // debug
|
||||
//doShow("out", "key code pressed: " + code); // debug
|
||||
|
||||
if (code == 13) { // Enter Key
|
||||
doSend();
|
||||
|
|
@ -252,36 +272,24 @@ $(document).keydown( function(event) {
|
|||
// handler to avoid double-clicks until the ajax request finishes
|
||||
//$("#inputsend").one("click", webclient_input)
|
||||
|
||||
function webclient_set_sizes() {
|
||||
// Sets the size of the message window
|
||||
var win_h = $(document).height();
|
||||
//var win_w = $('#wrapper').width();
|
||||
var inp_h = $('#inputform').outerHeight(true);
|
||||
//var inp_w = $('#inputsend').outerWidth(true);
|
||||
// Callback function - called when the browser window resizes
|
||||
$(window).resize(doSetSizes);
|
||||
|
||||
$("#messagewindow").css({'height': win_h - inp_h - 1});
|
||||
//$("#inputfield").css({'width': win_w - inp_w - 20});
|
||||
}
|
||||
|
||||
|
||||
// Callback function - called when page has finished loading (gets things going)
|
||||
// Callback function - called when page is closed or moved away from.
|
||||
//$(window).bind("beforeunload", webclient_close);
|
||||
//
|
||||
// Callback function - called when page has finished loading (kicks the client into gear)
|
||||
$(document).ready(function(){
|
||||
// remove the "no javascript" warning, since we obviously have javascript
|
||||
$('#noscript').remove();
|
||||
// set sizes of elements and reposition them
|
||||
webclient_set_sizes();
|
||||
doSetSizes();
|
||||
// a small timeout to stop 'loading' indicator in Chrome
|
||||
setTimeout(function () {
|
||||
webclient_init();
|
||||
}, 500);
|
||||
// set an idle timer to avoid proxy servers to time out on us (every 3 minutes)
|
||||
setInterval(function() {
|
||||
webclient_input("idle", true);
|
||||
websocket.send("idle");
|
||||
}, 60000*3);
|
||||
});
|
||||
|
||||
// Callback function - called when the browser window resizes
|
||||
$(window).resize(webclient_set_sizes);
|
||||
|
||||
// Callback function - called when page is closed or moved away from.
|
||||
//$(window).bind("beforeunload", webclient_close);
|
||||
|
|
|
|||
|
|
@ -19,9 +19,10 @@
|
|||
<script language="javascript" type="text/javascript">
|
||||
if ("WebSocket" in window) {
|
||||
<!-- Importing the Evennia websocket webclient component (requires jQuery) -->
|
||||
var wsurl = "{{websocket_url}}";
|
||||
document.write("\<script src=\"/media/javascript/evennia_websocket_webclient.js\" type=\"text/javascript\" charset=\"utf-8\"\>\</script\>")}
|
||||
else {
|
||||
<!-- Importing the Evennia ajax webclient component (requires jQuery) -->
|
||||
<!-- No websocket support in browser. Importing the Evennia ajax webclient component (requires jQuery) -->
|
||||
document.write("\<script src=\"/media/javascript/evennia_ajax_webclient.js\" type=\"text/javascript\" charset=\"utf-8\"\>\</script\>")}
|
||||
</script>
|
||||
{% else %}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@
|
|||
# tuple.
|
||||
#
|
||||
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from src.utils.utils import get_evennia_version
|
||||
|
||||
|
|
@ -30,6 +29,9 @@ WEBSITE = ['Flatpages', 'News', 'Sites']
|
|||
|
||||
|
||||
# The main context processor function
|
||||
WEBCLIENT_ENABLED = settings.WEBCLIENT_ENABLED
|
||||
WEBSOCKET_CLIENT_ENABLED = settings.WEBSOCKET_CLIENT_ENABLED
|
||||
WSURL = "%s:%s" % (settings.WEBSOCKET_CLIENT_URL, settings.WEBSOCKET_CLIENT_PORT)
|
||||
|
||||
def general_context(request):
|
||||
"""
|
||||
|
|
@ -44,6 +46,7 @@ def general_context(request):
|
|||
'evennia_setupapps': GAME_SETUP,
|
||||
'evennia_connectapps': CONNECTIONS,
|
||||
'evennia_websiteapps':WEBSITE,
|
||||
"webclient_enabled" : settings.WEBCLIENT_ENABLED,
|
||||
"websocket_enabled" : settings.WEBSOCKET_ENABLED
|
||||
"webclient_enabled" : WEBCLIENT_ENABLED,
|
||||
"websocket_enabled" : WEBSOCKET_CLIENT_ENABLED,
|
||||
"websocket_url" : WSURL
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue