topazcube 0.1.21 → 0.1.23

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/src/client.ts CHANGED
@@ -32,6 +32,7 @@ interface ConstructorParams {
32
32
  autoReconnect?: boolean; // auto reconnect on disconnect
33
33
  allowSync?: boolean; // allow sync on connect
34
34
  allowWebRTC?: boolean;
35
+ DEBUG?: boolean;
35
36
  }
36
37
 
37
38
  interface Message {
@@ -180,7 +181,7 @@ declare global {
180
181
  }
181
182
 
182
183
  export default class TopazCubeClient {
183
- debug = false
184
+ DEBUG = false
184
185
  CYCLE = 200 // update/patch rate in ms
185
186
  url = ''
186
187
  documents: { [key: string]: Document } = {}
@@ -211,6 +212,9 @@ export default class TopazCubeClient {
211
212
  ID = 0
212
213
  socket: WebSocket | null = null
213
214
  _peerConnection: RTCPeerConnection | null = null
215
+ _candidates: RTCIceCandidate[] = []
216
+ _remoteCandidates: RTCIceCandidateInit[] = []
217
+ _offerSent: boolean = false
214
218
  _dataChannel: RTCDataChannel | null = null // our data channel
215
219
  _serverDataChannel: RTCDataChannel | null = null // server data channel
216
220
  _webRTCConnected: boolean = false
@@ -233,14 +237,32 @@ export default class TopazCubeClient {
233
237
  autoReconnect = true, // auto reconnect on disconnect
234
238
  allowSync = true, // allow sync on connect
235
239
  allowWebRTC = false,
240
+ DEBUG = false
236
241
  }: ConstructorParams) {
237
242
  this.url = url
238
243
  this.autoReconnect = autoReconnect
239
244
  this.allowSync = allowSync
240
245
  this.allowWebRTC = allowWebRTC
241
246
  this.socket = null
247
+ this.DEBUG = DEBUG
242
248
  this._startLoop()
243
- console.log('Client initialized')
249
+ this.log('Client initialized')
250
+ }
251
+
252
+ log(...args: any[]) {
253
+ if (this.DEBUG) {
254
+ console.log(...args);
255
+ }
256
+ }
257
+
258
+ warn(...args: any[]) {
259
+ if (this.DEBUG) {
260
+ console.warn(...args);
261
+ }
262
+ }
263
+
264
+ error(...args: any[]) {
265
+ console.error(...args);
244
266
  }
245
267
 
246
268
  /*= UPDATE ===================================================================*/
@@ -314,21 +336,34 @@ export default class TopazCubeClient {
314
336
  let t2 = e._lpostime2
315
337
  const interval = t2 - t1;
316
338
  const elapsed = now - t1;
317
- const alpha = Math.max(0, elapsed / interval)
318
- vec3.lerp(this._dpos, e._lpos1!, e._lpos2!, alpha)
319
- vec3.lerp(e.position!, e.position!, this._dpos, 0.07)
320
- e._changed_position = now
339
+ e.pelapsed = elapsed
340
+ /*if (elapsed > 5000) {
341
+ } else */if (elapsed > 1000) {
342
+ vec3.copy(e.position!, e._lpos2!)
343
+ e._changed_position = now
344
+ } else {
345
+ const alpha = Math.max(0, elapsed / interval)
346
+ vec3.lerp(this._dpos, e._lpos1!, e._lpos2!, alpha)
347
+ vec3.lerp(e.position!, e.position!, this._dpos, 0.07)
348
+ e._changed_position = now
349
+ }
321
350
  }
322
351
  if (e._lrottime1 && e._lrottime2) {
323
-
324
352
  let t1 = e._lrottime1
325
353
  let t2 = e._lrottime2
326
354
  const interval = t2 - t1;
327
355
  const elapsed = now - t1;
328
- const alpha = Math.max(0, elapsed / interval)
329
- quat.slerp(this._drot, e._lrot1!, e._lrot2!, alpha)
330
- quat.slerp(e.rotation!, e.rotation!, this._drot, 0.07)
331
- e._changed_rotation = now
356
+ e.relapsed = elapsed
357
+ /*if (elapsed > 5000) {
358
+ } else */if (elapsed > 1000) {
359
+ quat.copy(e.rotation!, e._lrot2!)
360
+ e._changed_rotation = now
361
+ } else {
362
+ const alpha = Math.max(0, elapsed / interval)
363
+ quat.slerp(this._drot, e._lrot1!, e._lrot2!, alpha)
364
+ quat.slerp(e.rotation!, e.rotation!, this._drot, 0.07)
365
+ e._changed_rotation = now
366
+ }
332
367
  }
333
368
 
334
369
  }
@@ -354,7 +389,7 @@ export default class TopazCubeClient {
354
389
  }
355
390
  this.isConnecting = true
356
391
  this._clear()
357
- console.log('connecting...')
392
+ this.log('connecting...')
358
393
 
359
394
  this.socket = new WebSocket(this.url)
360
395
 
@@ -475,7 +510,7 @@ export default class TopazCubeClient {
475
510
  this.socket.send(enc)
476
511
  }
477
512
  } catch (e) {
478
- console.error('send failed', e)
513
+ this.error('send failed', e)
479
514
  }
480
515
  }
481
516
 
@@ -487,7 +522,7 @@ export default class TopazCubeClient {
487
522
  async _onMessage(message: Message) {
488
523
  let time = Date.now()
489
524
  if (message.c == 'full') {
490
- //console.log('full:', message)
525
+ //this.log('full:', message)
491
526
  let name:string = ''+message.n
492
527
  let doc = message.doc
493
528
  this.documents[name] = doc
@@ -517,7 +552,7 @@ export default class TopazCubeClient {
517
552
  try {
518
553
  applyOperation(this.documents[name], dop)
519
554
  } catch (e) {
520
- console.error('applyOperation failed for', name, 'with op', dop, e)
555
+ this.error('applyOperation failed for', name, 'with op', dop, e)
521
556
  }
522
557
  }
523
558
  this.isPatched = false
@@ -526,7 +561,7 @@ export default class TopazCubeClient {
526
561
  this.onChange(name, this.documents[name], message.doc)
527
562
  }
528
563
  } else if (message.c == 'chunk') {
529
- //console.log('chunk', message)
564
+ //this.log('chunk', message)
530
565
  this._chunks[message.mid+'-'+message.seq] = message
531
566
  if (message.last) {
532
567
  let cfound = 0
@@ -542,25 +577,25 @@ export default class TopazCubeClient {
542
577
  delete this._chunks[cid]
543
578
  }
544
579
  }
545
- //console.log('found chunks ', cfound, 'of', message.seq + 1)
580
+ //this.log('found chunks ', cfound, 'of', message.seq + 1)
546
581
  if (cfound == message.seq + 1) {
547
582
  try {
548
583
  let cdec = await decompress(cdata)
549
584
  let cdecu = new Uint8Array(cdec)
550
585
  let nmessage = decode(cdecu)
551
- //console.log('decoded message', nmessage)
586
+ //this.log('decoded message', nmessage)
552
587
  this._onMessage(nmessage)
553
588
  } catch (error) {
554
- console.error('Error decoding chunks:', error)
589
+ this.error('Error decoding chunks:', error)
555
590
  }
556
591
  } else {
557
- console.warn('missing chunks', cfound, 'of', message.seq + 1)
592
+ this.warn('missing chunks', cfound, 'of', message.seq + 1)
558
593
  }
559
594
  }
560
595
  } else if (message.c == 'fpatch') {
561
596
  time = Date.now()
562
597
  let name = message.n
563
- //console.log('fpatch', message)
598
+ //this.log('fpatch', message)
564
599
  let doPatch = true
565
600
  if (!this._lastUpdateId[name]) {
566
601
  this._lastUpdateId[name] = message.u
@@ -568,13 +603,13 @@ export default class TopazCubeClient {
568
603
  if (this._lastUpdateId[name] < message.u) {
569
604
  let lp = message.u - this._lastUpdateId[name] - 1
570
605
  if (lp > 0) {
571
- console.warn('Lost ' + lp + ' updates')
606
+ this.warn('Lost ' + lp + ' updates')
572
607
  }
573
608
  this._lastUpdateId[name] = message.u
574
609
  } else if (this._lastUpdateId[name] > message.u) {
575
610
  // Handle the case where the server's update ID is older than the client's
576
611
  // This could be due to a network issue or a clock skew
577
- console.warn(`Received outdated update ID for document ${name}: ${message.u} < ${this._lastUpdateId[name]}`)
612
+ this.warn(`Received outdated update ID for document ${name}: ${message.u} < ${this._lastUpdateId[name]}`)
578
613
  doPatch = false
579
614
  }
580
615
  }
@@ -590,12 +625,12 @@ export default class TopazCubeClient {
590
625
  this.send({ c: 'peng', ct: Date.now(), st: stime })
591
626
  this.stats.stdiff = stime + ping / 2 - time
592
627
  this.stats.ping = ping
593
- console.log('ping', ping, 'ms', 'stdiff', this.stats.stdiff, 'ms')
628
+ this.log('ping', ping, 'ms', 'stdiff', this.stats.stdiff, 'ms')
594
629
  } else if (message.c == 'rtc-offer') {
595
- //console.log("RTC: offer received:", message);
630
+ this.log("RTC: offer received:", message);
596
631
  // You might need to handle this if the server sends offers
597
632
  } else if (message.c == 'rtc-answer') {
598
- //console.log("RTC: answer received:", message);
633
+ this.log("RTC: answer received:", message);
599
634
  try {
600
635
  const sessionDesc = new RTCSessionDescription({
601
636
  type: message.type,
@@ -604,39 +639,36 @@ export default class TopazCubeClient {
604
639
  if (this._peerConnection) {
605
640
  await this._peerConnection.setRemoteDescription(sessionDesc)
606
641
  }
607
- //console.log("RTC: Remote description set successfully");
642
+ this.log("RTC: Remote description set successfully");
608
643
 
609
644
  // Log the current state after setting remote description
610
- //console.log("RTC: Current connection state:", this._peerConnection.connectionState);
611
- //console.log("RTC: Current ICE connection state:", this._peerConnection.iceConnectionState);
645
+ //
646
+ //this.log("RTC: Current connection state:", this._peerConnection.connectionState);
647
+ //this.log("RTC: Current ICE connection state:", this._peerConnection.iceConnectionState);
648
+ for (let candidate of this._remoteCandidates) {
649
+ try {
650
+ await this._peerConnection?.addIceCandidate(candidate);
651
+ this.log("RTC: Added remote ICE candidate:", candidate);
652
+ } catch (error) {
653
+ this.error("RTC: Error adding remote ICE candidate:", error);
654
+ }
655
+ }
612
656
  } catch (error) {
613
- console.error('RTC: Error setting remote description:', error)
657
+ this.error('RTC: Error setting remote description:', error)
614
658
  }
615
659
  } else if (message.c == 'rtc-candidate') {
616
- //console.log("RTC: candidate received", message);
617
- try {
660
+ this.log("RTC: candidate received", message);
618
661
  if (this._peerConnection && message.candidate) {
619
- await this._peerConnection.addIceCandidate(
620
- //new RTCIceCandidate(message.candidate)
621
- message.candidate
622
- )
623
- //console.log("RTC: ICE candidate added successfully");
624
- } else {
625
- console.warn(
626
- 'RTC: Received candidate but peerConnection not ready or candidate missing'
627
- )
662
+ this._remoteCandidates.push(message.candidate);
628
663
  }
629
- } catch (error) {
630
- //console.error("RTC: Error adding ICE candidate:", error);
631
- }
632
664
  } else {
633
665
  this.onMessage(message)
634
666
  }
635
667
  }
636
668
 
637
669
  _onDocumentChange(name: string, op: any, target: any, path: string, value: any) {
638
- if (this.debug) {
639
- console.log('Document change:', name, op, target, path, value)
670
+ if (this.DEBUG) {
671
+ this.log('Document change:', name, op, target, path, value)
640
672
  }
641
673
  if (this.isPatched || !this.allowSync) {
642
674
  return
@@ -712,12 +744,12 @@ export default class TopazCubeClient {
712
744
  offset += 4
713
745
  let e: Entity = entities[id]
714
746
  if (!e) {
715
- //console.log('Entity not found:', id)
747
+ //this.log('Entity not found:', id)
716
748
  continue
717
749
  }
718
750
  let value = rdict[did]
719
751
  e[key] = value
720
- //console.log('FCHANGE', key, id, did, value, rdict)
752
+ //this.log('FCHANGE', key, id, did, value, rdict)
721
753
  e['_changed_'+key] = time
722
754
  }
723
755
  } else {
@@ -830,13 +862,13 @@ export default class TopazCubeClient {
830
862
  }
831
863
 
832
864
  _onRTCConnect() {
833
- console.log('RTC: Connected')
865
+ this.log('RTC: Connected')
834
866
  this.send({ c: 'test', message: 'Hello RTC from client' })
835
867
  }
836
868
 
837
869
  _onRTCDisconnect() {
838
870
  this._webRTCConnected = false
839
- console.log('RTC: Disconnected')
871
+ this.log('RTC: Disconnected')
840
872
  }
841
873
 
842
874
  async _onRTCMessage(data: ArrayBuffer) {
@@ -849,8 +881,11 @@ export default class TopazCubeClient {
849
881
  }
850
882
 
851
883
  async _initializeWebRTC() {
852
- //console.log("RTC: _initializeWebRTC")
884
+ //this.log("RTC: _initializeWebRTC")
853
885
  this._peerConnection = null
886
+ this._candidates = []
887
+ this._remoteCandidates = []
888
+ this._offerSent = false
854
889
  try {
855
890
  // Create RTCPeerConnection with more comprehensive STUN server list
856
891
  this._peerConnection = new RTCPeerConnection({
@@ -862,27 +897,24 @@ export default class TopazCubeClient {
862
897
  iceCandidatePoolSize: 10,
863
898
  })
864
899
 
865
- //console.log("RTC: peerConnection created", this._peerConnection)
900
+ //this.log("RTC: peerConnection created", this._peerConnection)
866
901
 
867
902
  // Handle ICE candidates
868
903
  this._peerConnection.onicecandidate = (event: RTCPeerConnectionIceEvent) => {
869
- //console.log("RTC: onicecandidate", event.candidate)
904
+ //this.log("RTC: onicecandidate", event.candidate)
870
905
  if (event.candidate) {
871
- this.send({
872
- c: 'rtc-candidate',
873
- type: 'ice-candidate',
874
- candidate: event.candidate,
875
- })
906
+ this._candidates.push(event.candidate)
876
907
  } else {
877
- //console.log("RTC: ICE candidate gathering complete")
908
+ this.log("RTC: ICE candidate gathering complete")
878
909
  }
879
910
  }
880
911
 
881
912
  // Log connection state changes
882
913
  this._peerConnection.onconnectionstatechange = () => {
883
- //console.log(`RTC: Connection state changed: ${this._peerConnection.connectionState}`)
914
+ //this.log(`RTC: Connection state changed: ${this._peerConnection.connectionState}`)
884
915
  if (this._peerConnection && this._peerConnection.connectionState === 'connected') {
885
916
  this._webRTCConnected = true
917
+ this.log('RTC: Peer connection established!')
886
918
  } else if (
887
919
  this._peerConnection && (
888
920
  this._peerConnection.connectionState === 'failed' ||
@@ -890,15 +922,25 @@ export default class TopazCubeClient {
890
922
  this._peerConnection.connectionState === 'closed')
891
923
  ) {
892
924
  this._webRTCConnected = false
925
+ this.log('RTC: Peer connection closed or failed')
893
926
  }
894
927
  }
895
928
 
896
929
  this._peerConnection.onicegatheringstatechange = () => {
897
- //console.log(`RTC: ICE gathering state: ${this._peerConnection.iceGatheringState}`)
930
+ this.log(`RTC: ICE gathering state. _candidates:`, this._candidates.length, this._peerConnection?.iceGatheringState)
931
+ if (this._peerConnection?.iceGatheringState == 'complete' && this._offerSent) {
932
+ for (let candidate of this._candidates) {
933
+ this.send({
934
+ c: 'rtc-candidate',
935
+ type: 'ice-candidate',
936
+ candidate: candidate,
937
+ })
938
+ }
939
+ }
898
940
  }
899
941
 
900
942
  this._peerConnection.oniceconnectionstatechange = () => {
901
- //console.log(`RTC: ICE connection state: ${this._peerConnection.iceConnectionState}`)
943
+ //this.log(`RTC: ICE connection state: ${this._peerConnection.iceConnectionState}`)
902
944
 
903
945
  // This is critical - when ICE succeeds, the connection should be established
904
946
  if (
@@ -906,7 +948,7 @@ export default class TopazCubeClient {
906
948
  this._peerConnection.iceConnectionState === 'connected' ||
907
949
  this._peerConnection.iceConnectionState === 'completed')
908
950
  ) {
909
- //console.log("RTC: ICE connection established!")
951
+ //this.log("RTC: ICE connection established!")
910
952
  }
911
953
  }
912
954
 
@@ -928,25 +970,25 @@ export default class TopazCubeClient {
928
970
  }
929
971
 
930
972
  this._dataChannel.onerror = (_error: Event) => {
931
- console.error('RTC: Client data channel error', _error)
973
+ this.error('RTC: Client data channel error', _error)
932
974
  }
933
975
 
934
976
  // Handle data channels created by the server
935
977
  this._peerConnection.ondatachannel = (event: RTCDataChannelEvent) => {
936
- //console.log("RTC: Server data channel received", event.channel.label);
978
+ //this.log("RTC: Server data channel received", event.channel.label);
937
979
  const dataChannel = event.channel
938
980
  this._serverDataChannel = dataChannel
939
981
 
940
982
  dataChannel.onopen = () => {
941
- //console.log("RTC: Server data channel open");
983
+ //this.log("RTC: Server data channel open");
942
984
  }
943
985
 
944
986
  dataChannel.onclose = () => {
945
- //console.log("RTC: Server data channel closed");
987
+ //this.log("RTC: Server data channel closed");
946
988
  }
947
989
 
948
990
  dataChannel.onerror = (_error: Event) => {
949
- //console.error("RTC: Server data channel error", error);
991
+ //this.error("RTC: Server data channel error", error);
950
992
  }
951
993
 
952
994
  dataChannel.onmessage = (event: MessageEvent) => {
@@ -962,14 +1004,14 @@ export default class TopazCubeClient {
962
1004
  }
963
1005
 
964
1006
  const offer = await this._peerConnection.createOffer(offerOptions)
965
- //console.log("RTC: our offer:", offer);
1007
+ //this.log("RTC: our offer:", offer);
966
1008
  await this._peerConnection.setLocalDescription(offer)
967
1009
 
968
1010
  // Wait a moment to ensure the local description is set
969
1011
  await new Promise((resolve) => setTimeout(resolve, 100))
970
1012
 
971
1013
  let ld = this._peerConnection.localDescription
972
- //console.log("RTC: our localDescription", ld);
1014
+ //this.log("RTC: our localDescription", ld);
973
1015
 
974
1016
  if (ld) {
975
1017
  const offerPayload = {
@@ -977,28 +1019,29 @@ export default class TopazCubeClient {
977
1019
  type: ld.type,
978
1020
  sdp: ld.sdp,
979
1021
  }
980
- //console.log("RTC: our offer payload", offerPayload);
1022
+ //this.log("RTC: our offer payload", offerPayload);
981
1023
  this.send(offerPayload)
1024
+ this._offerSent = true
982
1025
  }
983
1026
 
984
1027
  // Set a timeout to check connection status
985
1028
  setTimeout(() => {
986
1029
  if (!this._webRTCConnected && this._peerConnection) {
987
1030
  /*
988
- console.log("RTC: Connection not established after timeout, current states:");
989
- console.log("Connection state:", this._peerConnection.connectionState);
990
- console.log("ICE connection state:", this._peerConnection.iceConnectionState);
991
- console.log("ICE gathering state:", this._peerConnection.iceGatheringState);
1031
+ this.log("RTC: Connection not established after timeout, current states:");
1032
+ this.log("Connection state:", this._peerConnection.connectionState);
1033
+ this.log("ICE connection state:", this._peerConnection.iceConnectionState);
1034
+ this.log("ICE gathering state:", this._peerConnection.iceGatheringState);
992
1035
  */
993
1036
  // Attempt to restart ICE if needed
994
1037
  if (this._peerConnection.iceConnectionState === 'failed') {
995
- console.log('RTC: Attempting ICE restart')
1038
+ this.log('RTC: Attempting ICE restart')
996
1039
  this._restartIce()
997
1040
  }
998
1041
  }
999
1042
  }, 5000)
1000
1043
  } catch (error) {
1001
- console.error('RTC: error:', error)
1044
+ this.error('RTC: error:', error)
1002
1045
  }
1003
1046
  }
1004
1047
 
@@ -1019,10 +1062,10 @@ export default class TopazCubeClient {
1019
1062
  type: offer?.type,
1020
1063
  sdp: offer?.sdp,
1021
1064
  }
1022
- //console.log("RTC: ICE restart offer payload", offerPayload);
1065
+ //this.log("RTC: ICE restart offer payload", offerPayload);
1023
1066
  this.send(offerPayload)
1024
1067
  } catch (error) {
1025
- //console.error("RTC: Error during ICE restart:", error);
1068
+ //this.error("RTC: Error during ICE restart:", error);
1026
1069
  }
1027
1070
  }
1028
1071
 
package/src/server.ts CHANGED
@@ -19,7 +19,7 @@ import fastjsonpatch from 'fast-json-patch'
19
19
  import { WebSocketServer, WebSocket } from 'ws'
20
20
  import { MongoClient, Db } from 'mongodb'
21
21
  import { glMatrix, vec3, quat } from 'gl-matrix'
22
- // WebRTC implementation loaded conditionally when needed
22
+ import { RTCPeerConnection, RTCDataChannel, RTCSessionDescription } from "werift";
23
23
 
24
24
  glMatrix.setMatrixArrayType(Array)
25
25
 
@@ -73,6 +73,7 @@ interface StatsType {
73
73
  }
74
74
 
75
75
  export default class TopazCubeServer {
76
+ DEBUG = false
76
77
  name = 'TopazCubeServer'
77
78
  cycle = 100
78
79
  patchCycleDivider = 1
@@ -91,7 +92,6 @@ export default class TopazCubeServer {
91
92
  allowFastPatch = false
92
93
  allowCompression = false
93
94
  simulateLatency = 0
94
-
95
95
  _lastUID = 100
96
96
  clients: ClientType[] = []
97
97
  documents: Record<string, any> = {}
@@ -118,11 +118,15 @@ export default class TopazCubeServer {
118
118
  _exited = false
119
119
 
120
120
  log(...args: any[]) {
121
+ if (this.DEBUG) {
121
122
  console.log(this.name + ':', ...args);
123
+ }
122
124
  }
123
125
 
124
126
  warn(...args: any[]) {
127
+ if (this.DEBUG) {
125
128
  console.warn(this.name + ':', ...args);
129
+ }
126
130
  }
127
131
 
128
132
  error(...args: any[]) {
@@ -145,6 +149,7 @@ export default class TopazCubeServer {
145
149
  allowFastPatch = false,
146
150
  allowCompression = false,
147
151
  simulateLatency = 0,
152
+ DEBUG = false
148
153
  }: {
149
154
  name?: string
150
155
  cycle?: number
@@ -161,6 +166,7 @@ export default class TopazCubeServer {
161
166
  allowFastPatch?: boolean
162
167
  allowCompression?: boolean
163
168
  simulateLatency?: number
169
+ DEBUG?: boolean
164
170
  } = {}) {
165
171
  this.name = name
166
172
  this.cycle = cycle
@@ -177,6 +183,7 @@ export default class TopazCubeServer {
177
183
  this.allowFastPatch = allowFastPatch
178
184
  this.allowCompression = allowCompression
179
185
  this.simulateLatency = simulateLatency
186
+ this.DEBUG = DEBUG
180
187
 
181
188
  this._initDB()
182
189
 
@@ -786,7 +793,7 @@ export default class TopazCubeServer {
786
793
  n: name,
787
794
  fdata: changes
788
795
  }
789
- //this.broadcastRTC(record, sus)
796
+ this.broadcastRTC(record, sus)
790
797
  let t3 = Date.now()
791
798
  this.log(`_sendPatches: ${name} encode_changes: ${t2-t1}ms broadcast:${t3-t2}ms`)
792
799
  }
@@ -813,19 +820,6 @@ export default class TopazCubeServer {
813
820
 
814
821
  /*= WEBRTC ===================================================================*/
815
822
 
816
- private _wrtc: any = null
817
-
818
- private async _loadWebRTC(): Promise<any> {
819
- if (!this._wrtc) {
820
- try {
821
- this._wrtc = await import('@roamhq/wrtc')
822
- } catch (error) {
823
- this.error('WebRTC module not available:', error)
824
- throw new Error('WebRTC functionality requires @roamhq/wrtc and platform-specific binary packages')
825
- }
826
- }
827
- return this._wrtc
828
- }
829
823
 
830
824
  async _processOffer(client: ClientType, data: any): Promise<void> {
831
825
  if (!this.allowWebRTC) {
@@ -833,34 +827,34 @@ export default class TopazCubeServer {
833
827
  return
834
828
  }
835
829
 
836
- const wrtc = await this._loadWebRTC()
837
- //this.log("RTC: Offer received", data);
838
- const peerConnection = new (wrtc as any).RTCPeerConnection({
830
+ this.log("RTC: Processing offer from client", client.ID, data);
831
+
832
+ const peerConnection = new RTCPeerConnection({
839
833
  iceServers: [
840
834
  { urls: 'stun:stun.l.google.com:19302' },
841
835
  { urls: 'stun:stun.cloudflare.com:3478' },
842
836
  { urls: 'stun:freestun.net:3478' },
843
837
  ],
844
- iceCandidatePoolSize: 10,
838
+ //iceCandidatePoolSize: 10,
845
839
  })
846
840
 
847
841
  client.peerConnection = peerConnection
848
842
 
849
843
  peerConnection.onicecandidate = (event: any) => {
850
844
  if (event.candidate) {
851
- //this.log("RTC: ICE candidate generated", event.candidate.candidate.substring(0, 50) + "...");
845
+ this.log("RTC: ICE candidate generated", event.candidate.candidate.substring(0, 50) + "...");
852
846
  this.send(client, {
853
847
  c: 'rtc-candidate',
854
848
  type: 'ice-candidate',
855
849
  candidate: event.candidate, // .toJSON()
856
850
  })
857
851
  } else {
858
- //this.log("RTC: ICE candidate gathering complete");
852
+ this.log("RTC: ICE candidate gathering complete");
859
853
  }
860
854
  }
861
855
 
862
856
  peerConnection.onconnectionstatechange = () => {
863
- //this.log(`RTC: Connection state changed: ${peerConnection.connectionState}`);
857
+ this.log(`RTC: Connection state changed: ${peerConnection.connectionState}`);
864
858
  if (peerConnection.connectionState === 'connected') {
865
859
  client.webRTCConnected = true
866
860
  this.log(`RTC: Connection established with client ${client.ID}`)
@@ -875,24 +869,26 @@ export default class TopazCubeServer {
875
869
  }
876
870
 
877
871
  peerConnection.onicegatheringstatechange = () => {
878
- //this.log(`RTC: ICE gathering state: ${peerConnection.iceGatheringState}`);
872
+ this.log(`RTC: ICE gathering state: ${peerConnection.iceGatheringState}`);
879
873
  }
880
874
 
881
875
  peerConnection.oniceconnectionstatechange = () => {
882
- //this.log(`RTC: ICE connection state: ${peerConnection.iceConnectionState}`);
876
+ this.log(`RTC: ICE connection state: ${peerConnection.iceConnectionState}`);
883
877
  if (
884
878
  peerConnection.iceConnectionState === 'connected' ||
885
879
  peerConnection.iceConnectionState === 'completed'
886
880
  ) {
887
- //this.log(`RTC: ICE connection established with client ${client.ID}`);
881
+ this.log(`RTC: ICE connection established with client ${client.ID}`);
888
882
  }
889
883
  }
890
884
 
891
885
  try {
886
+ this.log("RTC: Remote description set from data", data);
892
887
  await peerConnection.setRemoteDescription(
893
- new (wrtc as any).RTCSessionDescription(data)
888
+ //data
889
+ new RTCSessionDescription(data.sdp, data.type)
894
890
  )
895
- //this.log("RTC: Remote description set successfully");
891
+ this.log("RTC: Remote description set successfully");
896
892
 
897
893
  client.dataChannel = peerConnection.createDataChannel('serverchannel', {
898
894
  ordered: true,
@@ -900,7 +896,7 @@ export default class TopazCubeServer {
900
896
  })
901
897
 
902
898
  client.dataChannel.onopen = () => {
903
- //this.log(`RTC: Data channel opened for client ${client.ID}`);
899
+ this.log(`RTC: Data channel opened for client ${client.ID}`);
904
900
  // Try sending a test message
905
901
  try {
906
902
  const testData = { c: 'test', message: 'Hello WebRTC' }
@@ -941,7 +937,7 @@ export default class TopazCubeServer {
941
937
  const answer = await peerConnection.createAnswer()
942
938
  await peerConnection.setLocalDescription(answer)
943
939
 
944
- //this.log(`RTC: Sending answer to client ${client.ID}`);
940
+ this.log(`RTC: Sending answer to client ${client.ID}`);
945
941
  this.send(client, {
946
942
  c: 'rtc-answer',
947
943
  type: answer.type,
@@ -956,16 +952,19 @@ export default class TopazCubeServer {
956
952
  }
957
953
 
958
954
  async _processICECandidate(client: ClientType, data: any): Promise<void> {
959
- //this.log(`RTC: Processing ICE candidate from client ${client.ID}`);
955
+ this.log(`RTC: Processing ICE candidate from client ${client.ID}`);
960
956
  try {
957
+ if (data.candidate && typeof(data.candidate) == 'object') {
958
+ data.candidate = data.candidate.candidate
959
+ }
961
960
  if (client.peerConnection && data.candidate) {
962
961
  await client.peerConnection.addIceCandidate(
963
962
  data.candidate
964
963
  //new wrtc.RTCIceCandidate(data.candidate)
965
964
  )
966
- //this.log(`RTC: ICE candidate added successfully for client ${client.ID}`);
965
+ this.log(`RTC: ICE candidate added successfully for client ${client.ID}`);
967
966
  } else {
968
- //this.warn(`RTC: Cannot add ICE candidate for client ${client.ID} - peerConnection not ready or candidate missing`);
967
+ this.warn(`RTC: Cannot add ICE candidate for client ${client.ID} - peerConnection not ready or candidate missing`, client.peerConnection, data);
969
968
  }
970
969
  } catch (error) {
971
970
  this.error(`RTC: Error adding ICE candidate for client ${client.ID}`)