teleportxr 1.0.48 → 1.0.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/client/client.js +70 -7
- package/connections/webrtcconnection.js +19 -2
- package/package.json +1 -1
- package/signaling.js +17 -11
package/client/client.js
CHANGED
|
@@ -11,6 +11,11 @@ const resources= require("../scene/resources.js");
|
|
|
11
11
|
const { BackgroundMode } = require("../core/core.js");
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
// Maximum number of times an unacknowledged origin/lighting command will be
|
|
15
|
+
// resent before we give up and log a disconnect. With a 3s ack timeout this
|
|
16
|
+
// gives roughly MAX_ACK_RESENDS*3 seconds before we stop trying.
|
|
17
|
+
const MAX_ACK_RESENDS = 5;
|
|
18
|
+
|
|
14
19
|
class OriginState
|
|
15
20
|
{
|
|
16
21
|
constructor() {
|
|
@@ -20,6 +25,8 @@ class OriginState
|
|
|
20
25
|
this.acknowledged=false;
|
|
21
26
|
this.serverTimeSentUs=BigInt(0);
|
|
22
27
|
this.valid_counter=BigInt(0);
|
|
28
|
+
this.resendCount=0;
|
|
29
|
+
this.givenUp=false;
|
|
23
30
|
}
|
|
24
31
|
};
|
|
25
32
|
|
|
@@ -30,6 +37,8 @@ class LightingState
|
|
|
30
37
|
this.acknowledged=false;
|
|
31
38
|
this.serverTimeSentUs=BigInt(0);
|
|
32
39
|
this.clientDynamicLighting=new core.ClientDynamicLighting();
|
|
40
|
+
this.resendCount=0;
|
|
41
|
+
this.givenUp=false;
|
|
33
42
|
}
|
|
34
43
|
};
|
|
35
44
|
|
|
@@ -64,9 +73,24 @@ class Client {
|
|
|
64
73
|
this.webRtcConnected=false;
|
|
65
74
|
}
|
|
66
75
|
}
|
|
67
|
-
receivedMessageReliable(id,
|
|
76
|
+
receivedMessageReliable(id,pkt)
|
|
68
77
|
{
|
|
69
|
-
|
|
78
|
+
var dataView=new DataView(pkt.data,0,1);
|
|
79
|
+
const messageType=dataView.getUint8(0);
|
|
80
|
+
switch(messageType){
|
|
81
|
+
case message.MessagePayloadType.ReceivedResources:
|
|
82
|
+
this.receiveReceivedResourcesMessage(pkt.data);
|
|
83
|
+
return;
|
|
84
|
+
case message.MessagePayloadType.Acknowledgement:
|
|
85
|
+
this.ReceiveAcknowledgement(pkt.data);
|
|
86
|
+
return;
|
|
87
|
+
case message.MessagePayloadType.ControllerPoses:
|
|
88
|
+
this.ReceiveNodePoses(pkt.data);
|
|
89
|
+
return;
|
|
90
|
+
default:
|
|
91
|
+
console.log('Client receivedMessageReliable ch.'+id+' unknown messageType '+messageType+'.');
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
70
94
|
}
|
|
71
95
|
// Is the Buffer bf too small to contain a type tp?
|
|
72
96
|
checkTooSmall(tp,bf) {
|
|
@@ -131,8 +155,14 @@ class Client {
|
|
|
131
155
|
this.currentOriginState.acknowledged = false;
|
|
132
156
|
this.currentOriginState.serverTimeSentUs = BigInt(0);
|
|
133
157
|
this.currentOriginState.ackId = 0;
|
|
158
|
+
this.currentOriginState.resendCount = 0;
|
|
159
|
+
this.currentOriginState.givenUp = false;
|
|
134
160
|
this.currentLightingState = new LightingState();
|
|
135
161
|
this.setupCommand.float32_draw_distance=10.0;
|
|
162
|
+
// The C++ client uses startTimestamp_utc_unix_us as the session epoch
|
|
163
|
+
// against which subsequent message timestamps are measured. Without it
|
|
164
|
+
// some message handlers will not progress.
|
|
165
|
+
this.setupCommand.int64_startTimestamp_utc_unix_us = BigInt(core.getStartTimeUnixUs());
|
|
136
166
|
if(this.scene)
|
|
137
167
|
{
|
|
138
168
|
if(this.scene.backgroundTexturePath&&this.scene.backgroundTexturePath!="")
|
|
@@ -182,7 +212,14 @@ class Client {
|
|
|
182
212
|
SendCommand(command){
|
|
183
213
|
let array=core.encodeToUint8Array(command);
|
|
184
214
|
console.log("SendCommand: encoded to", array.length, "bytes, expected", command.size(), "bytes");
|
|
185
|
-
|
|
215
|
+
// Once the WebRTC reliable data channel is open, route commands through it
|
|
216
|
+
// (matches the C++ server's sendCommand path). Bootstrap commands sent
|
|
217
|
+
// before the channel is open (SetupCommand, AcknowledgeHandshakeCommand)
|
|
218
|
+
// fall back to the signaling WebSocket.
|
|
219
|
+
if(this.webRtcConnection && this.webRtcConnection.isReliableOpen())
|
|
220
|
+
this.webRtcConnection.sendReliable(array);
|
|
221
|
+
else
|
|
222
|
+
this.signalingSend(array);
|
|
186
223
|
}
|
|
187
224
|
// We call StartStreaming once the SetupCommand has been acknowledged.
|
|
188
225
|
StartStreaming()
|
|
@@ -220,10 +257,12 @@ class Client {
|
|
|
220
257
|
if(msg.uint64_ackId==this.currentOriginState.ackId)
|
|
221
258
|
{
|
|
222
259
|
this.currentOriginState.acknowledged=true;
|
|
260
|
+
this.currentOriginState.resendCount=0;
|
|
223
261
|
}
|
|
224
262
|
if(msg.uint64_ackId==this.currentLightingState.ackId)
|
|
225
263
|
{
|
|
226
264
|
this.currentLightingState.acknowledged=true;
|
|
265
|
+
this.currentLightingState.resendCount=0;
|
|
227
266
|
}
|
|
228
267
|
}
|
|
229
268
|
ReceiveNodePoses(data)
|
|
@@ -298,16 +337,16 @@ class Client {
|
|
|
298
337
|
{
|
|
299
338
|
this.SendTexture(uid);
|
|
300
339
|
}
|
|
301
|
-
if(!this.currentOriginState.acknowledged)
|
|
340
|
+
if(!this.currentOriginState.acknowledged && !this.currentOriginState.givenUp)
|
|
302
341
|
this.SendOrigin();
|
|
303
|
-
if(!this.currentLightingState.acknowledged)
|
|
342
|
+
if(!this.currentLightingState.acknowledged && !this.currentLightingState.givenUp)
|
|
304
343
|
this.SendLighting();
|
|
305
344
|
}
|
|
306
345
|
SendOrigin()
|
|
307
346
|
{
|
|
308
347
|
let time_now_us=core.getTimestampUs();
|
|
309
348
|
let originAckWaitTimeUs=BigInt(3000000);// three seconds
|
|
310
|
-
if(this.setupCommand.
|
|
349
|
+
if(this.setupCommand.int64_startTimestamp_utc_unix_us==BigInt(0))
|
|
311
350
|
{
|
|
312
351
|
console.log("Start timestamp is not set, so not sending origin.");
|
|
313
352
|
return ;
|
|
@@ -329,6 +368,18 @@ class Client {
|
|
|
329
368
|
console.log("Origin client has is 0, so not sending origin.");
|
|
330
369
|
return;
|
|
331
370
|
}
|
|
371
|
+
// A previous send timed out without an ack. Bound the resend loop so
|
|
372
|
+
// we don't spam the client forever if it never acknowledges.
|
|
373
|
+
if(this.currentOriginState.serverTimeSentUs!=BigInt(0))
|
|
374
|
+
{
|
|
375
|
+
this.currentOriginState.resendCount++;
|
|
376
|
+
if(this.currentOriginState.resendCount>MAX_ACK_RESENDS)
|
|
377
|
+
{
|
|
378
|
+
console.log("Client "+this.clientID+": gave up resending SetOriginNodeCommand after "+this.currentOriginState.resendCount+" attempts; treating client as disconnected for origin.");
|
|
379
|
+
this.currentOriginState.givenUp=true;
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
332
383
|
this.currentOriginState.valid_counter++;
|
|
333
384
|
this.geometryService.SetOriginNode(this.currentOriginState.originClientHas);
|
|
334
385
|
var setp=new command.SetOriginNodeCommand();
|
|
@@ -350,7 +401,7 @@ class Client {
|
|
|
350
401
|
{
|
|
351
402
|
let time_now_us=core.getTimestampUs();
|
|
352
403
|
let ackWaitTimeUs=BigInt(3000000);// three seconds
|
|
353
|
-
if(this.setupCommand.
|
|
404
|
+
if(this.setupCommand.int64_startTimestamp_utc_unix_us==BigInt(0))
|
|
354
405
|
{
|
|
355
406
|
console.log("Start timestamp is not set, so not sending lighting.");
|
|
356
407
|
return;
|
|
@@ -367,6 +418,18 @@ class Client {
|
|
|
367
418
|
console.log("WebRTC connection is not established, so not sending lighting.");
|
|
368
419
|
return;
|
|
369
420
|
}
|
|
421
|
+
// A previous send timed out without an ack. Bound the resend loop so
|
|
422
|
+
// we don't spam the client forever if it never acknowledges.
|
|
423
|
+
if(this.currentLightingState.serverTimeSentUs!=BigInt(0))
|
|
424
|
+
{
|
|
425
|
+
this.currentLightingState.resendCount++;
|
|
426
|
+
if(this.currentLightingState.resendCount>MAX_ACK_RESENDS)
|
|
427
|
+
{
|
|
428
|
+
console.log("Client "+this.clientID+": gave up resending SetLightingCommand after "+this.currentLightingState.resendCount+" attempts; treating client as disconnected for lighting.");
|
|
429
|
+
this.currentLightingState.givenUp=true;
|
|
430
|
+
return;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
370
433
|
|
|
371
434
|
var setl =new command.SetLightingCommand();
|
|
372
435
|
setl.uint64_ackId =this.next_ack_id++;
|
|
@@ -208,7 +208,13 @@ class WebRtcConnection extends EventEmitter
|
|
|
208
208
|
if (this.peerConnection !== pc) return;
|
|
209
209
|
await pc.setLocalDescription(offer);
|
|
210
210
|
if (this.peerConnection !== pc) return;
|
|
211
|
-
|
|
211
|
+
// Use pc.localDescription.sdp rather than the createOffer() result: the
|
|
212
|
+
// ICE ufrag/pwd in the SDP returned by createOffer() are provisional and
|
|
213
|
+
// libwebrtc may activate a transport with different credentials in
|
|
214
|
+
// setLocalDescription(), which would cause STUN ufrag check failures on
|
|
215
|
+
// the remote peer.
|
|
216
|
+
const localSdp = (pc.localDescription && pc.localDescription.sdp) || offer.sdp;
|
|
217
|
+
var message = '{"teleport-signal-type":"offer","sdp":"'+localSdp+'"}';
|
|
212
218
|
this.sendConfigMessage(this.id,message);
|
|
213
219
|
await this.waitUntilIceGatheringStateComplete(pc, this.options);
|
|
214
220
|
} catch (error)
|
|
@@ -343,12 +349,23 @@ class WebRtcConnection extends EventEmitter
|
|
|
343
349
|
this.emit('closed');
|
|
344
350
|
};
|
|
345
351
|
sendGeometry(buffer) {
|
|
346
|
-
try {
|
|
352
|
+
try {
|
|
347
353
|
this.geometryDataChannel.send(buffer);
|
|
348
354
|
}
|
|
349
355
|
catch(exception) {
|
|
350
356
|
console.error('datachannel.sendGeometry exception: '+exception.message);
|
|
351
357
|
}
|
|
358
|
+
}
|
|
359
|
+
isReliableOpen() {
|
|
360
|
+
return !!(this.reliableDataChannel && this.reliableDataChannel.readyState === 'open');
|
|
361
|
+
}
|
|
362
|
+
sendReliable(buffer) {
|
|
363
|
+
try {
|
|
364
|
+
this.reliableDataChannel.send(buffer);
|
|
365
|
+
}
|
|
366
|
+
catch(exception) {
|
|
367
|
+
console.error('datachannel.sendReliable exception: '+exception.message);
|
|
368
|
+
}
|
|
352
369
|
}
|
|
353
370
|
beforeOffer() {
|
|
354
371
|
|
package/package.json
CHANGED
package/signaling.js
CHANGED
|
@@ -53,6 +53,7 @@ var webRtcConnectionManager = null;
|
|
|
53
53
|
var newClient=null;
|
|
54
54
|
var disconnectClient=null;
|
|
55
55
|
var clientHostHeader = ""; // Store the first client's host header for resource URLs
|
|
56
|
+
var clientProtoHeader = ""; // Store the first client's X-Forwarded-Proto header
|
|
56
57
|
function startStreaming(signalingClient) {
|
|
57
58
|
signalingClient.ChangeSignalingState(SignalingState.ACCEPTED);
|
|
58
59
|
// And we send the WebSockets connect-response.
|
|
@@ -185,20 +186,20 @@ function OnWebSocket(ws, req) {
|
|
|
185
186
|
signalingClient.ip_addr_port.toString()
|
|
186
187
|
);
|
|
187
188
|
|
|
188
|
-
// Capture the Host
|
|
189
|
-
//
|
|
189
|
+
// Capture the Host and X-Forwarded-Proto headers from the first client connection
|
|
190
|
+
// for resource URL auto-detection.
|
|
190
191
|
if (!clientHostHeader && req.headers) {
|
|
191
|
-
//
|
|
192
|
-
const xForwardedHost
|
|
193
|
-
const
|
|
194
|
-
|
|
192
|
+
// Prefer X-Forwarded-Host / X-Forwarded-Proto set by a reverse proxy.
|
|
193
|
+
const xForwardedHost = req.headers['x-forwarded-host'];
|
|
194
|
+
const xForwardedProto = req.headers['x-forwarded-proto'];
|
|
195
|
+
const hostHeader = req.headers['host'];
|
|
196
|
+
clientHostHeader = xForwardedHost || hostHeader || '';
|
|
197
|
+
clientProtoHeader = xForwardedProto || '';
|
|
195
198
|
if (clientHostHeader) {
|
|
196
199
|
console.log("Auto-detected resource server host from client connection: " + clientHostHeader);
|
|
197
|
-
if (xForwardedHost)
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
console.log(" (from Host: " + hostHeader + ")");
|
|
201
|
-
}
|
|
200
|
+
if (xForwardedHost) console.log(" (from X-Forwarded-Host: " + xForwardedHost + ")");
|
|
201
|
+
else console.log(" (from Host: " + hostHeader + ")");
|
|
202
|
+
if (xForwardedProto) console.log(" (from X-Forwarded-Proto: " + xForwardedProto + ")");
|
|
202
203
|
} else {
|
|
203
204
|
console.log("WARNING: Could not auto-detect host from request headers");
|
|
204
205
|
console.log(" Available headers: " + JSON.stringify(req.headers));
|
|
@@ -300,3 +301,8 @@ exports.signalingClients = signalingClients;
|
|
|
300
301
|
exports.getClientHostHeader = function () {
|
|
301
302
|
return clientHostHeader;
|
|
302
303
|
};
|
|
304
|
+
|
|
305
|
+
// Export function to retrieve the auto-detected X-Forwarded-Proto header
|
|
306
|
+
exports.getClientProtoHeader = function () {
|
|
307
|
+
return clientProtoHeader;
|
|
308
|
+
};
|