/*
The MIT License (MIT)
Copyright (c) 2014-2019 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)
Virtual World Framework Apache 2.0 license (https://github.com/NikolaySuslov/livecodingspace/blob/master/licenses/LICENSE_VWF.md)
*/
import { Helpers } from '/helpers.js';
class ReflectorClient {
constructor() {
console.log("reflector client constructor");
this.helpers = new Helpers;
this.socket = undefined;
}
connect(component_uri_or_json_or_object, path){
function isSocketIO07() {
//return ( parseFloat( io.version ) >= 0.7 );
return true
}
try {
let objToRef = this.helpers.reduceSaveObject(path);
var options = {
// The socket is relative to the application path.
// resource: window.location.pathname.slice( 1,
// window.location.pathname.lastIndexOf("/") ),
query: {
pathname: window.location.pathname.slice( 1,
window.location.pathname.lastIndexOf("/") ),
appRoot: "./public",
path: JSON.stringify(objToRef)//JSON.stringify(path)
},
// query: 'pathname=' + window.location.pathname.slice( 1,
// window.location.pathname.lastIndexOf("/") ),
// Use a secure connection when the application comes from https.
secure: window.location.protocol === "https:",
// Don't attempt to reestablish lost connections. The client reloads after a
// disconnection to recreate the application from scratch.
//reconnect: false,
reconnection: false,
upgrade: false,
transports: ['websocket']
};
if ( isSocketIO07() ) {
//window.location.host
var host = window._app.reflectorHost; //localStorage.getItem('lcs_reflector');
//if(!host) host = 'http://localhost:3002'; //window.location.origin;
this.socket = io.connect( host, options );
} else { // Ruby Server -- only supports socket.io 0.6
io.util.merge( options, {
// For socket.io 0.6, specify the port since the default isn't correct when
// using https.
port: window.location.port ||
( window.location.protocol === "https:" ? 443 : 80 ),
// The ruby socket.io server only supports WebSockets. Don't try the others.
transports: [
'websocket',
],
// Increase the timeout because of starvation while loading the scene. The
// server timeout must also be increased. (For socket.io 0.7+, the client
// timeout is controlled by the server.)
transportOptions: {
"websocket": { timeout: 90000 },
},
} );
this.socket = io.connect( undefined, options );
}
} catch ( e ) {
// If a connection to the reflector is not available, then run in single-user mode.
// Messages intended for the reflector will loop directly back to us in this case.
// Start a timer to monitor the incoming queue and dispatch the messages as though
// they were received from the server.
vwf.dispatch();
setInterval( function() {
var fields = {
time: vwf.now + 0.010, // TODO: there will be a slight skew here since the callback intervals won't be exactly 10 ms; increment using the actual delta time; also, support play/pause/stop and different playback rates as with connected mode.
origin: "reflector",
};
vwf.private.queue.insert( fields, true ); // may invoke dispatch(), so call last before returning to the host
}, 10 );
}
if ( this.socket ) {
this.socket.on('connect_error', function(err) {
console.log(err);
var errDiv = document.createElement("div");
errDiv.innerHTML = "
Connection error!" + err + "
";
document.querySelector('body').appendChild(errDiv);
});
this.socket.on( "connect", function() {
vwf.logger.infox( "-socket", "connected" );
if ( isSocketIO07() ) {
vwf.moniker_ = this.id;
} else { //Ruby Server
vwf.moniker_ = this.transport.sessionid;
}
} );
// Configure a handler to receive messages from the server.
// Note that this example code doesn't implement a robust parser capable of handling
// arbitrary text and that the messages should be placed in a dedicated priority
// queue for best performance rather than resorting the queue as each message
// arrives. Additionally, overlapping messages may cause actions to be performed out
// of order in some cases if messages are not processed on a single thread.
this.socket.on( "message", function( message ) {
// vwf.logger.debugx( "-socket", "message", message );
try {
if ( isSocketIO07() ) {
var fields = message;
} else { // Ruby Server - Unpack the arguements
var fields = JSON.parse( message );
}
fields.time = Number( fields.time );
// TODO: other message validation (check node id, others?)
fields.origin = "reflector";
// Update the queue. Messages in the queue are ordered by time, then by order of arrival.
// Time is only advanced if the message has no action, meaning it is a tick.
vwf.private.queue.insert( fields, !fields.action ); // may invoke dispatch(), so call last before returning to the host
// Each message from the server allows us to move time forward. Parse the
// timestamp from the message and call dispatch() to execute all queued
// actions through that time, including the message just received.
// The simulation may perform immediate actions at the current time or it
// may post actions to the queue to be performed in the future. But we only
// move time forward for items arriving in the queue from the reflector.
} catch ( e ) {
vwf.logger.warn( fields.action, fields.node, fields.member, fields.parameters,
"exception performing action:", require( "vwf/utility" ).exceptionMessage( e ) );
}
} );
this.socket.on( "disconnect", function() {
vwf.logger.infox( "-socket", "disconnected" );
// Reload to rejoin the application.
window.location = window.location.href;
} );
this.socket.on( "error", function() {
//Overcome by compatibility.js websockets check
document.querySelector('body').innerHTML = "WebSockets connections are currently being blocked. Please check your proxy server settings.
";
// jQuery('body').html("WebSockets connections are currently being blocked. Please check your proxy server settings.
");
} );
if ( !isSocketIO07() ) {
// Start communication with the reflector.
this.socket.connect(); // TODO: errors can occur here too, particularly if a local client contains the socket.io files but there is no server; do the loopback here instead of earlier in response to new io.Socket.
}
} else if ( component_uri_or_json_or_object ) {
// Load the application. The application is rooted in a single node constructed here
// as an instance of the component passed to initialize(). That component, its
// prototype(s), and its children, and their prototypes and children, flesh out the
// entire application.
// TODO: add note that this is only for a self-determined application; with socket, wait for reflection server to tell us.
// TODO: maybe depends on component_uri_or_json_or_object too; when to override and not connect to reflection server?
this.createNode( component_uri_or_json_or_object, "application" );
} else { // TODO: also do this if component_uri_or_json_or_object was invalid and createNode() failed
// TODO: show a selection dialog
}
}
}
export { ReflectorClient }