More work on the new js lib, added client-side support for ajax/comet, which will be using JSON and OOB commands, same as websocket version.

This commit is contained in:
Griatch 2016-02-08 18:13:29 +01:00
parent 7fa088c036
commit da52380b0f

View file

@ -25,10 +25,14 @@ functions:
- Evennia.init(options) - Evennia.init(options)
This must be called by the frontend to intialize the library. The This must be called by the frontend to intialize the library. The
argument is an js object with the following possible keys: argument is an js object with the following possible keys:
'connection': Either 'websocket' or 'comet'. 'connection': This defaults to Evennia.WebsocketConnection but
can also be set to Evennia.CometConnection for backwards
compatibility with old browsers. Each connection must have
a 'msg(data)' method that should handle the conversion to
JSON before sending across the wire.
'cmdhandler': An optional custom command handler for 'cmdhandler': An optional custom command handler for
managing outgoing commands from the server. If not managing outgoing commands from the server. If not
supplied, the default will be used. It must have a msg() function. supplied, the default will be used. It must have an emit(data) function.
- Evennia.msg(funcname, [args,...], callback) - Evennia.msg(funcname, [args,...], callback)
Send a command to the server. You can also provide a function Send a command to the server. You can also provide a function
to call with the return of the call (not all commands will return to call with the return of the call (not all commands will return
@ -37,6 +41,7 @@ functions:
Evennia websocket webclient (javascript component) Evennia websocket webclient (javascript component)
The client is composed of two parts: The client is composed of two parts:
@ -52,163 +57,214 @@ messages sent to the client is one of two modes:
*/ */
(function() { (function() {
var cmdid = 0; var cmdid = 0;
var cmdmap = {};
var Evennia = {
debug: true,
// Client -> Evennia.
// Called by the frontend to send a command to Evennia.
//
// Args:
// cmdname (str): String identifier to call
// kwargs (obj): Data argument for calling as cmdname(kwargs)
// callback (func): If given, will be given an eventual return
// value from the backend.
//
send: function (cmdname, kwargs, callback) {
kwargs.cmdid = cmdid++;
var data = kwargs ? [cmdname, kwargs] : [cmdname, {}];
if (typeof callback === 'function') {
this.cmdmap[cmdid] = callback;
}
this.connection.send(JSON.stringify(kwargs));
log('cmd called with following args:', cmd, params, callback);
},
// Evennia -> Client.
// Called by the backend to emit an event to the global emitter
//
// Args:
// event (event): Event received from Evennia
// data (obj):
//
emit: function (cmdname, data) {
if (data.cmdid) {
this.cmdmap[data.cmdid].apply(this, [data]);
delete this.cmdmap[cmddata.cmdid];
}
else {
this.emitter.emit(cmdname, data);
}
},
// Initializer.
// startup Evennia emitter and connection.
//
// Args:
// opts (obj):
// emitter - custom emitter. If not given,
// will use a default emitter. Must have
// an "emit" function.
// connection - This defaults to a WebsocketConnection,
// but could also be the CometConnection or another
// custom protocol. Must have a 'send' method and
// make use of Evennia.emit to return data to Client.
//
init: function (kwargs) {
kwargs = kwargs || {};
this.emitter = kwargs.emitter || new DefaultEmitter();
this.connection = kwargs.connection || new WebsocketConnection();
}
};
// Websocket Connector
//
var WebsocketConnection = function () {
var websocket = new WebSocket(wsurl);
// Handle Websocket open event
this.websocket.onopen = function (event) {
log('Websocket connection openened.');
Evennia.emit('socket:open', event);
};
// Handle Websocket close event
this.websocket.onclose = function (event) {
log('WebSocket connection closed.');
Evennia.emit('socket:close', event);
};
// Handle websocket errors
this.websocket.onerror = function (event) {
log("Websocket error to ", wsurl, event);
Evennia.emit('socket:error', data);
};
// Handle incoming websocket data
this.websocket.onmessage = function (event) {
var data = event.data
if (typeof data !== 'string' && data.length < 0) {
return;
}
// Parse the incoming data, send to emitter
// Incoming data is on the form [cmdname, kwargs]
data = JSON.parse(data);
Evennia.emit(data[0], data[1]]);
};
return websocket;
}
// AJAX/COMET Connector
//
CometConnection = function() {
};
// Basic emitter to distribute data being sent to the client from
// the Server. An alternative can be overridden in Evennia.init.
//
var DefaultEmitter = function () {
var cmdmap = {}; var cmdmap = {};
// Emit data to all listeners tied to a given cmdname var evennia = {
//
// Args:
// cmdname (str): Name of command, used to find
// all listeners to this call; each will be
// called as function(kwargs).
// kwargs (obj): Argument to the listener.
//
var emit = function (cmdname, kwargs) {
log('emit', cmdname, kwargs);
if (this.cmdmap[cmdname]) { debug: true,
this.cmdmap[cmdname].apply(this, kwargs);
// Initialize.
// startup Evennia emitter and connection.
//
// Args:
// opts (obj):
// emitter - custom emitter. If not given,
// will use a default emitter. Must have
// an "emit" function.
// connection - This defaults to a WebsocketConnection,
// but could also be the CometConnection or another
// custom protocol. Must have a 'send' method and
// make use of Evennia.emit to return data to Client.
//
init: function(opts) {
opts = opts || {};
this.emitter = opts.emitter || new DefaultEmitter();
this.connection = opts.connection || new WebsocketConnection();
},
// Client -> Evennia.
// Called by the frontend to send a command to Evennia.
//
// Args:
// cmdname (str): String identifier to call
// kwargs (obj): Data argument for calling as cmdname(kwargs)
// callback (func): If given, will be given an eventual return
// value from the backend.
//
msg: function (cmdname, kwargs, callback) {
kwargs.cmdid = cmdid++;
var data = kwargs ? [cmdname, kwargs] : [cmdname, {}];
if (typeof callback === 'function') {
this.cmdmap[cmdid] = callback;
}
this.connection.msg(data);
log('cmd called with following args:', cmd, params, callback);
},
// Evennia -> Client.
// Called by the backend to emit an event to the global emitter
//
// Args:
// event (event): Event received from Evennia
// data (obj):
//
emit: function (cmdname, data) {
if (data.cmdid) {
this.cmdmap[data.cmdid].apply(this, [data]);
delete this.cmdmap[cmddata.cmdid];
}
else {
this.emitter.emit(cmdname, data);
}
},
}; // end of evennia object
// Basic emitter to distribute data being sent to the client from
// the Server. An alternative can be overridden in Evennia.init.
//
var DefaultEmitter = function () {
var cmdmap = {};
// Emit data to all listeners tied to a given cmdname
//
// Args:
// cmdname (str): Name of command, used to find
// all listeners to this call; each will be
// called as function(kwargs).
// kwargs (obj): Argument to the listener.
//
var emit = function (cmdname, kwargs) {
log('emit', cmdname, kwargs);
if (this.cmdmap[cmdname]) {
this.cmdmap[cmdname].apply(this, kwargs);
};
}; };
}
// Bind listener to event
//
// Args:
// cmdname (str): Name of event to handle.
// listener (function): Function taking one argument,
// to listen to cmdname events.
//
var on = function (cmdname, listener) {
if typeof(listener === 'function') {
this.cmdmap[cmdname] = listener;
};
};
// remove handling of this cmdname
//
// Args:
// cmdname (str): Name of event to handle
//
var off = function (cmdname) {
delete this.cmdmap[cmdname]
};
return {emit:emit, on:on, off:off}
}; };
// Bind listener to event // Websocket Connector
// //
// Args: var WebsocketConnection = function () {
// cmdname (str): Name of event to handle. var websocket = new WebSocket(wsurl);
// listener (function): Function taking one argument, // Handle Websocket open event
// to listen to cmdname events. this.websocket.onopen = function (event) {
// log('Websocket connection openened.');
var on = function (cmdname, listener) { Evennia.emit('socket:open', event);
if typeof(listener === 'function') { };
this.cmdmap[cmdname] = listener; // Handle Websocket close event
} this.websocket.onclose = function (event) {
}; log('WebSocket connection closed.');
Evennia.emit('socket:close', event);
};
// Handle websocket errors
this.websocket.onerror = function (event) {
log("Websocket error to ", wsurl, event);
Evennia.emit('socket:error', data);
};
// Handle incoming websocket data
this.websocket.onmessage = function (event) {
var data = event.data
if (typeof data !== 'string' && data.length < 0) {
return;
}
// Parse the incoming data, send to emitter
// Incoming data is on the form [cmdname, kwargs]
data = JSON.parse(data);
Evennia.emit(data[0], data[1]]);
};
this.websocket.msg = function(data) {
this.websocket.send(JSON.stringify(data));
};
// remove handling of this cmdname return websocket;
// }
// Args:
// cmdname (str): Name of event to handle
//
var off = function (cmdname) {
delete this.cmdmap[cmdname]
}
};
return { // AJAX/COMET Connector
emit: emit, //
on: on, CometConnection = function() {
off: off var client_hash = '0';
};
}; var ajaxcomet = {
// Send Client -> Evennia. Called by Evennia.send.
var msg = function(data) {
$.ajax({type: "POST", url: "/webclientdata",
async: true, cache: false, timeout: 30000,
dataType: "json",
data: {mode:'input', msg: data, 'suid': client_hash},
success: function(data): {},
error: function(req, stat, err): {
log("COMET: Server returned error. " + err)
}
});
};
window.Evennia = Evennia; // Receive Evennia -> Client. This will start an asynchronous
})(); // Long-polling request. It will either timeout or receive data
// from the 'webclientdata' url. Either way a new polling request
// will immediately be started.
var poll = function() {
$.ajax({type: "POST", url: "/webclientdata",
async: true, cache: false, timeout: 30000,
dataType: "json",
data = {mode: 'receive', 'suid': client_hash},
success: function(data) {
Evennia.emit(data[0], data[1])
},
error: function() {
this.poll() // timeout; immediately re-poll
}
});
};
// Initialization will happen when this Connection is created.
// We need to store the client id so Evennia knows to separate
// the clients.
$.ajax({type: "POST", url: "/webclientdata",
async: true, cache: false, timeout: 50000,
datatype: "json",
success: function(data) {
this.client_hash = data.suid;
this.poll();
},
error: function(req, stat, err) {
log("Connection error: " + err);
}
});
};
return ajaxcomet;
};
window.Evennia = evennia;
})(); // end of auto-calling Evennia object defintion
// helper logging function // helper logging function
// Args: // Args:
@ -222,13 +278,13 @@ function log(msg) {
// Called when page has finished loading (kicks the client into gear) // Called when page has finished loading (kicks the client into gear)
$(document).ready(function(){ $(document).ready(function(){
// remove the "no javascript" warning, since we obviously have javascript
$('#noscript').remove();
// a small timeout to stop 'loading' indicator in Chrome // a small timeout to stop 'loading' indicator in Chrome
setTimeout(function () { setTimeout(function () {
log('Evennia initialized...') log('Evennia initialized...')
Evennia.init(); Evennia.init()
}, 500); }, 500);
// set an idle timer to avoid proxy servers to time out on us (every 3 minutes) // set an idle timer to avoid proxy servers to time out on us (every 3 minutes)
setInterval(function() { setInterval(function() {