reflectorClient.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214
  1. /*
  2. The MIT License (MIT)
  3. Copyright (c) 2014-2019 Nikolai Suslov and the Krestianstvo.org project contributors. (https://github.com/NikolaySuslov/livecodingspace/blob/master/LICENSE.md)
  4. Virtual World Framework Apache 2.0 license (https://github.com/NikolaySuslov/livecodingspace/blob/master/licenses/LICENSE_VWF.md)
  5. */
  6. import { Helpers } from '/core/helpers.js';
  7. class ReflectorClient {
  8. constructor() {
  9. console.log("reflector client constructor");
  10. this.helpers = new Helpers;
  11. this.socket = undefined;
  12. }
  13. connect(component_uri_or_json_or_object, path){
  14. function isSocketIO07() {
  15. //return ( parseFloat( io.version ) >= 0.7 );
  16. return true
  17. }
  18. try {
  19. let objToRef = this.helpers.reduceSaveObject(path);
  20. var options = {
  21. // The socket is relative to the application path.
  22. // resource: window.location.pathname.slice( 1,
  23. // window.location.pathname.lastIndexOf("/") ),
  24. query: {
  25. pathname: window.location.pathname.slice( 1,
  26. window.location.pathname.lastIndexOf("/") ),
  27. appRoot: "./public",
  28. path: JSON.stringify(objToRef)//JSON.stringify(path)
  29. },
  30. // query: 'pathname=' + window.location.pathname.slice( 1,
  31. // window.location.pathname.lastIndexOf("/") ),
  32. // Use a secure connection when the application comes from https.
  33. secure: window.location.protocol === "https:",
  34. // Don't attempt to reestablish lost connections. The client reloads after a
  35. // disconnection to recreate the application from scratch.
  36. //reconnect: false,
  37. reconnection: false,
  38. upgrade: false,
  39. transports: ['websocket']
  40. };
  41. if ( isSocketIO07() ) {
  42. //window.location.host
  43. var host = window._app.reflectorHost; //localStorage.getItem('lcs_reflector');
  44. //if(!host) host = 'http://localhost:3002'; //window.location.origin;
  45. this.socket = io.connect( host, options );
  46. }
  47. } catch ( e ) {
  48. // If a connection to the reflector is not available, then run in single-user mode.
  49. // Messages intended for the reflector will loop directly back to us in this case.
  50. // Start a timer to monitor the incoming queue and dispatch the messages as though
  51. // they were received from the server.
  52. // this.dispatch();
  53. // setInterval( function() {
  54. // var fields = {
  55. // 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.
  56. // origin: "reflector",
  57. // };
  58. // _app.virtualTime.insert( fields, true ); // may invoke dispatch(), so call last before returning to the host
  59. // }, 10 );
  60. }
  61. if ( this.socket ) {
  62. this.socket.on('connect_error', function(err) {
  63. console.log(err);
  64. var errDiv = document.createElement("div");
  65. errDiv.innerHTML = "<div class='vwf-err' style='z-index: 10; position: absolute; top: 80px; right: 50px'>Connection error!" + err + "</div>";
  66. document.querySelector('body').appendChild(errDiv);
  67. });
  68. this.socket.on( "connect", function() {
  69. vwf.logger.infox( "-socket", "connected" );
  70. if ( isSocketIO07() ) {
  71. vwf.moniker_ = this.id;
  72. } else { //Ruby Server
  73. vwf.moniker_ = this.transport.sessionid;
  74. }
  75. } );
  76. // Configure a handler to receive messages from the server.
  77. // Note that this example code doesn't implement a robust parser capable of handling
  78. // arbitrary text and that the messages should be placed in a dedicated priority
  79. // queue for best performance rather than resorting the queue as each message
  80. // arrives. Additionally, overlapping messages may cause actions to be performed out
  81. // of order in some cases if messages are not processed on a single thread.
  82. this.socket.on( "message", function( message ) {
  83. // vwf.logger.debugx( "-socket", "message", message );
  84. try {
  85. if ( isSocketIO07() ) {
  86. var fields = message;
  87. } else { // Ruby Server - Unpack the arguements
  88. var fields = JSON.parse( message );
  89. }
  90. fields.time = Number( fields.time );
  91. // TODO: other message validation (check node id, others?)
  92. fields.origin = "reflector";
  93. // Update the queue. Messages in the queue are ordered by time, then by order of arrival.
  94. // Time is only advanced if the message has no action, meaning it is a tick.
  95. if(_app.config.streamMsg) {
  96. vwf.virtualTime.streamAdapter.induce(fields);
  97. } else {
  98. vwf.virtualTime.insert( fields, !fields.action );
  99. }
  100. //
  101. // may invoke dispatch(), so call last before returning to the host
  102. // Each message from the server allows us to move time forward. Parse the
  103. // timestamp from the message and call dispatch() to execute all queued
  104. // actions through that time, including the message just received.
  105. // The simulation may perform immediate actions at the current time or it
  106. // may post actions to the queue to be performed in the future. But we only
  107. // move time forward for items arriving in the queue from the reflector.
  108. } catch ( e ) {
  109. vwf.logger.warn( fields.action, fields.node, fields.member, fields.parameters,
  110. "exception performing action:", vwf.utility.exceptionMessage( e ) );
  111. }
  112. } );
  113. this.socket.on( "disconnect", function() {
  114. vwf.logger.infox( "-socket", "disconnected" );
  115. // Reload to rejoin the application.
  116. window.location = window.location.href;
  117. } );
  118. this.socket.on( "error", function() {
  119. //Overcome by compatibility.js websockets check
  120. document.querySelector('body').innerHTML = "<div class='vwf-err'>WebSockets connections are currently being blocked. Please check your proxy server settings.</div>";
  121. // jQuery('body').html("<div class='vwf-err'>WebSockets connections are currently being blocked. Please check your proxy server settings.</div>");
  122. } );
  123. if ( !isSocketIO07() ) {
  124. // Start communication with the reflector.
  125. 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.
  126. }
  127. } else if ( component_uri_or_json_or_object ) {
  128. // Load the application. The application is rooted in a single node constructed here
  129. // as an instance of the component passed to initialize(). That component, its
  130. // prototype(s), and its children, and their prototypes and children, flesh out the
  131. // entire application.
  132. // TODO: add note that this is only for a self-determined application; with socket, wait for reflection server to tell us.
  133. // TODO: maybe depends on component_uri_or_json_or_object too; when to override and not connect to reflection server?
  134. this.createNode( component_uri_or_json_or_object, "application" );
  135. } else { // TODO: also do this if component_uri_or_json_or_object was invalid and createNode() failed
  136. // TODO: show a selection dialog
  137. }
  138. }
  139. }
  140. export { ReflectorClient }