mirror of
https://github.com/evennia/evennia.git
synced 2026-03-31 04:57:16 +02:00
Allow for webclient reconnect without a page refresh
- Refactored WebSocket client to not return websocket object directly - Changed AjaxCometConnection to emit close events in a way that mirrors WebsocketConnection more closely. - Added reconnect confirmation dialog.
This commit is contained in:
parent
039eb8c824
commit
ef05816b33
2 changed files with 163 additions and 62 deletions
|
|
@ -92,6 +92,24 @@ An "emitter" object must have a function
|
|||
}
|
||||
log('Evennia initialized.')
|
||||
},
|
||||
|
||||
// Connect to the Evennia server.
|
||||
// Re-establishes the connection after it is lost.
|
||||
//
|
||||
connect: function() {
|
||||
if (this.connection.isOpen()) {
|
||||
// Already connected.
|
||||
return;
|
||||
}
|
||||
this.connection.connect();
|
||||
log('Evenna reconnecting.')
|
||||
},
|
||||
|
||||
// Returns true if the connection is open.
|
||||
//
|
||||
isConnected: function () {
|
||||
return this.connection.isOpen();
|
||||
},
|
||||
|
||||
// client -> Evennia.
|
||||
// called by the frontend to send a command to Evennia.
|
||||
|
|
@ -196,55 +214,78 @@ An "emitter" object must have a function
|
|||
var WebsocketConnection = function () {
|
||||
log("Trying websocket ...");
|
||||
var open = false;
|
||||
var websocket = new WebSocket(wsurl);
|
||||
// Handle Websocket open event
|
||||
websocket.onopen = function (event) {
|
||||
open = true;
|
||||
Evennia.emit('connection_open', ["websocket"], event);
|
||||
};
|
||||
// Handle Websocket close event
|
||||
websocket.onclose = function (event) {
|
||||
if (open) {
|
||||
// only emit if websocket was ever open at all
|
||||
Evennia.emit('connection_close', ["websocket"], event);
|
||||
}
|
||||
open = false;
|
||||
};
|
||||
// Handle websocket errors
|
||||
websocket.onerror = function (event) {
|
||||
if (websocket.readyState === websocket.CLOSED) {
|
||||
log("Websocket failed. Falling back to Ajax...");
|
||||
if (open) {
|
||||
// only emit if websocket was ever open at all.
|
||||
Evennia.emit('connection_error', ["websocket"], event);
|
||||
}
|
||||
open = false;
|
||||
Evennia.connection = AjaxCometConnection();
|
||||
}
|
||||
};
|
||||
// Handle incoming websocket data [cmdname, args, kwargs]
|
||||
websocket.onmessage = function (event) {
|
||||
var data = event.data;
|
||||
if (typeof data !== 'string' && data.length < 0) {
|
||||
var ever_open = false;
|
||||
var websocket = null;
|
||||
var wsurl = window.wsurl;
|
||||
|
||||
var connect = function() {
|
||||
if (websocket && websocket.readyState != websocket.CLOSED) {
|
||||
// No-op if a connection is already open.
|
||||
return;
|
||||
}
|
||||
// Parse the incoming data, send to emitter
|
||||
// Incoming data is on the form [cmdname, args, kwargs]
|
||||
data = JSON.parse(data);
|
||||
Evennia.emit(data[0], data[1], data[2]);
|
||||
};
|
||||
websocket.msg = function(data) {
|
||||
websocket = new WebSocket(wsurl);
|
||||
|
||||
// Handle Websocket open event
|
||||
websocket.onopen = function (event) {
|
||||
open = true;
|
||||
ever_open = true;
|
||||
Evennia.emit('connection_open', ["websocket"], event);
|
||||
};
|
||||
// Handle Websocket close event
|
||||
websocket.onclose = function (event) {
|
||||
// only emit if websocket was ever open at all
|
||||
Evennia.emit('connection_close', ["websocket"], event);
|
||||
open = false;
|
||||
};
|
||||
// Handle websocket errors
|
||||
websocket.onerror = function (event) {
|
||||
if (websocket.readyState === websocket.CLOSED) {
|
||||
if (ever_open) {
|
||||
// only emit if websocket was ever open at all.
|
||||
log("Websocket failed.")
|
||||
Evennia.emit('connection_error', ["websocket"], event);
|
||||
}
|
||||
else {
|
||||
// only fall back to AJAX if we never got an open socket.
|
||||
log("Websocket failed. Falling back to Ajax...");
|
||||
Evennia.connection = AjaxCometConnection();
|
||||
}
|
||||
open = false;
|
||||
}
|
||||
};
|
||||
// Handle incoming websocket data [cmdname, args, kwargs]
|
||||
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, args, kwargs]
|
||||
data = JSON.parse(data);
|
||||
Evennia.emit(data[0], data[1], data[2]);
|
||||
};
|
||||
}
|
||||
|
||||
var msg = function(data) {
|
||||
// send data across the wire. Make sure to json it.
|
||||
websocket.send(JSON.stringify(data));
|
||||
};
|
||||
websocket.close = function() {
|
||||
|
||||
var close = function() {
|
||||
// tell the server this connection is closing (usually
|
||||
// tied to when the client window is closed). This
|
||||
// Makes use of a websocket-protocol specific instruction.
|
||||
websocket.send(JSON.stringify(["websocket_close", [], {}]));
|
||||
open = false;
|
||||
}
|
||||
return websocket;
|
||||
|
||||
var isOpen = function() {
|
||||
return open;
|
||||
}
|
||||
|
||||
connect();
|
||||
|
||||
return {connect: connect, msg: msg, close: close, isOpen: isOpen};
|
||||
};
|
||||
|
||||
// AJAX/COMET Connector
|
||||
|
|
@ -252,6 +293,8 @@ An "emitter" object must have a function
|
|||
var AjaxCometConnection = function() {
|
||||
log("Trying ajax ...");
|
||||
var client_hash = '0';
|
||||
var stop_polling = false;
|
||||
var is_closing = false;
|
||||
|
||||
// initialize connection and get hash
|
||||
var init = function() {
|
||||
|
|
@ -264,11 +307,15 @@ An "emitter" object must have a function
|
|||
data = JSON.parse(data);
|
||||
log ("connection_open", ["AJAX/COMET"], data);
|
||||
client_hash = data.suid;
|
||||
stop_polling = false;
|
||||
poll();
|
||||
},
|
||||
error: function(req, stat, err) {
|
||||
Evennia.emit("connection_error", ["AJAX/COMET init error"], err);
|
||||
// Also emit a close event so that the COMET API mirrors the websocket API.
|
||||
Evennia.emit("connection_close", ["AJAX/COMET init close"], err);
|
||||
log("AJAX/COMET: Connection error: " + err);
|
||||
stop_polling = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -280,10 +327,13 @@ An "emitter" object must have a function
|
|||
dataType: "json",
|
||||
data: {mode: inmode == null ? 'input' : inmode,
|
||||
data: JSON.stringify(data), 'suid': client_hash},
|
||||
success: function(req, stat, err) {},
|
||||
success: function(req, stat, err) {
|
||||
stop_polling = false;
|
||||
},
|
||||
error: function(req, stat, err) {
|
||||
Evennia.emit("connection_error", ["AJAX/COMET send error"], err);
|
||||
log("AJAX/COMET: Server returned error.",req,stat,err);
|
||||
stop_polling = true;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
|
@ -294,7 +344,7 @@ An "emitter" object must have a function
|
|||
// will immediately be started.
|
||||
var poll = function() {
|
||||
$.ajax({type: "POST", url: "/webclientdata",
|
||||
async: true, cache: false, timeout: 30000,
|
||||
async: true, cache: false, timeout: 60000,
|
||||
dataType: "json",
|
||||
data: {mode: 'receive', 'suid': client_hash},
|
||||
success: function(data) {
|
||||
|
|
@ -306,43 +356,77 @@ An "emitter" object must have a function
|
|||
// not a keepalive
|
||||
Evennia.emit(data[0], data[1], data[2]);
|
||||
}
|
||||
stop_polling = false;
|
||||
poll(); // immiately start a new request
|
||||
},
|
||||
error: function(req, stat, err) {
|
||||
poll() // timeout; immediately re-poll
|
||||
// don't trigger an emit event here,
|
||||
// this is normal for ajax/comet
|
||||
if (stat !== "timeout") {
|
||||
// Any other error besides a timeout is abnormal
|
||||
Evennia.emit("connection_error", ["AJAX/COMET receive error"], err);
|
||||
log("AJAX/COMET: Server returned error on receive.",req,stat,err);
|
||||
stop_polling = true;
|
||||
}
|
||||
else {
|
||||
// We'd expect to see a keepalive message rather than
|
||||
// a timeout. Ping the server to see if it's still there.
|
||||
msg("idle");
|
||||
}
|
||||
|
||||
if (stop_polling) {
|
||||
// An error of some kind occurred.
|
||||
// Close the connection, if possible.
|
||||
close();
|
||||
}
|
||||
else {
|
||||
poll(); // timeout; immediately re-poll
|
||||
// don't trigger an emit event here,
|
||||
// this is normal for ajax/comet
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Kill the connection and do house cleaning on the server.
|
||||
|
||||
// Kill the connection and do house cleaning on the server.
|
||||
var close = function webclient_close(){
|
||||
if (is_closing || client_hash === '0') {
|
||||
// Already closed or trying to close.
|
||||
return;
|
||||
}
|
||||
stop_polling = true;
|
||||
is_closing = true;
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/webclientdata",
|
||||
async: false,
|
||||
async: true,
|
||||
cache: false,
|
||||
timeout: 50000,
|
||||
dataType: "json",
|
||||
data: {mode: 'close', 'suid': client_hash},
|
||||
|
||||
success: function(data){
|
||||
is_closing = false;
|
||||
client_hash = '0';
|
||||
Evennia.emit("connection_close", ["AJAX/COMET"], {});
|
||||
log("AJAX/COMET connection closed cleanly.")
|
||||
},
|
||||
error: function(req, stat, err){
|
||||
Evennia.emit("connection_err", ["AJAX/COMET close error"], err);
|
||||
is_closing = false;
|
||||
Evennia.emit("connection_error", ["AJAX/COMET close error"], err);
|
||||
// Also emit a close event so that the COMET API mirrors the websocket API.
|
||||
Evennia.emit("connection_close", ["AJAX/COMET close unclean"], err);
|
||||
client_hash = '0';
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var isOpen = function () {
|
||||
return !(is_closing || client_hash === '0');
|
||||
}
|
||||
|
||||
// init
|
||||
init();
|
||||
|
||||
return {msg: msg, poll: poll, close: close};
|
||||
return {connect: init, msg: msg, close: close, isOpen: isOpen};
|
||||
};
|
||||
|
||||
window.Evennia = Evennia;
|
||||
|
|
|
|||
|
|
@ -76,6 +76,15 @@ var input_history = function() {
|
|||
|
||||
// Grab text from inputline and send to Evennia
|
||||
function doSendText() {
|
||||
if (!Evennia.isConnected()) {
|
||||
var reconnect = confirm("Not currently connected. Reconnect?");
|
||||
if (reconnect) {
|
||||
onText(["Attempting to reconnnect..."], {cls: "sys"});
|
||||
Evennia.connect();
|
||||
}
|
||||
// Don't try to send anything until the connection is back.
|
||||
return;
|
||||
}
|
||||
var inputfield = $("#inputfield");
|
||||
var outtext = inputfield.val();
|
||||
if (outtext.length > 7 && outtext.substr(0, 7) == "##send ") {
|
||||
|
|
@ -129,6 +138,13 @@ function onKeydown (event) {
|
|||
}
|
||||
};
|
||||
|
||||
function onKeyPress (event) {
|
||||
// Prevent carriage returns inside the input area.
|
||||
if (event.which === 13) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
var resizeInputField = function () {
|
||||
var min_height = 50;
|
||||
var max_height = 300;
|
||||
|
|
@ -209,7 +225,7 @@ function onConnectionClose(conn_name, evt) {
|
|||
function onConnectionError(conn_name, evt) {
|
||||
if (conn_name[0].lastIndexOf("AJAX/COMET", 0) === 0) {
|
||||
// only display anything if the error is in AJAX/COMET
|
||||
onText(["The connection was closed or lost."], {'cls': 'err'});
|
||||
//onText(["The connection was closed or lost."], {'cls': 'err'});
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -230,21 +246,22 @@ function onDefault(cmdname, args, kwargs) {
|
|||
// Register Events
|
||||
//
|
||||
|
||||
// Event when client window changes
|
||||
$(window).bind("resize", doWindowResize);
|
||||
$("#inputfield").bind("resize", doWindowResize);
|
||||
|
||||
// Event when any key is pressed
|
||||
$(document).keydown(onKeydown)
|
||||
.bind("keyup", resizeInputField)
|
||||
.bind("paste", resizeInputField)
|
||||
.bind("cut", resizeInputField);
|
||||
|
||||
// Pressing the send button
|
||||
$("#inputsend").bind("click", doSendText);
|
||||
|
||||
// Event when client finishes loading
|
||||
$(document).ready(function() {
|
||||
// Event when client window changes
|
||||
$(window).bind("resize", doWindowResize);
|
||||
$("#inputfield").bind("resize", doWindowResize)
|
||||
.keypress(onKeyPress)
|
||||
.bind("paste", resizeInputField)
|
||||
.bind("cut", resizeInputField);
|
||||
|
||||
// Event when any key is pressed
|
||||
$(document).keydown(onKeydown)
|
||||
.keyup(resizeInputField);
|
||||
|
||||
// Pressing the send button
|
||||
$("#inputsend").bind("click", doSendText);
|
||||
|
||||
// This is safe to call, it will always only
|
||||
// initialize once.
|
||||
Evennia.init();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue