topazcube 0.0.3 → 0.1.0

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.
@@ -0,0 +1,481 @@
1
+ import { applyOperation as L } from "fast-json-patch";
2
+ import { FLOAT32_OPTIONS as b, Packr as F } from "msgpackr";
3
+ import { vec3 as I, quat as R } from "gl-matrix";
4
+ const { ALWAYS: M, DECIMAL_ROUND: Y, DECIMAL_FIT: $ } = b;
5
+ let y = new F({
6
+ useFloat32: M
7
+ });
8
+ function S(o) {
9
+ return y.pack(o);
10
+ }
11
+ function w(o) {
12
+ return y.unpack(o);
13
+ }
14
+ function D(o, t, n, s = "", c = !1) {
15
+ if (t === null || typeof t != "object")
16
+ return t;
17
+ function r(l) {
18
+ let i = !0;
19
+ return l.startsWith("_") && (i = !1), c && c[l] && (i = !1), s == "/entities" && (i = !1), i;
20
+ }
21
+ for (const l in t)
22
+ r(l) && (t[l] = D(
23
+ o,
24
+ t[l],
25
+ n,
26
+ s + "/" + l,
27
+ c
28
+ ));
29
+ return new Proxy(t, {
30
+ get(l, i, h) {
31
+ return Reflect.get(l, i, h);
32
+ },
33
+ set(l, i, h) {
34
+ let _, d = s + "/" + String(i);
35
+ return r(i) ? (_ = D(o, h, n, d, c), n(o, "replace", l, d, _)) : _ = h, Reflect.set(l, i, _);
36
+ },
37
+ deleteProperty(l, i) {
38
+ let h = s + "/" + String(i);
39
+ return delete l[i], r(i) && n(o, "delete", l, h, null), !0;
40
+ }
41
+ });
42
+ }
43
+ function W(o) {
44
+ let t = {};
45
+ return o.o ? t.op = {
46
+ a: "add",
47
+ r: "remove",
48
+ d: "delete",
49
+ t: "test"
50
+ }[o.o] : t.op = "replace", t.path = o.p, t.value = o.v, t;
51
+ }
52
+ function B(o, t, n, s) {
53
+ let c = { p: n, v: s };
54
+ return o != "replace" && (c.o = {
55
+ add: "a",
56
+ remove: "r",
57
+ delete: "d",
58
+ test: "t"
59
+ }[o]), c;
60
+ }
61
+ function C(o, t = 0) {
62
+ let n = t;
63
+ return (o[n++] & 127) << 24 | o[n++] << 16 | o[n++] << 8 | o[n];
64
+ }
65
+ function U(o, t = 0) {
66
+ let n = t;
67
+ return o[n++] << 16 | o[n++] << 8 | o[n];
68
+ }
69
+ function O(o, t = 0) {
70
+ let n = t;
71
+ return o[n++] << 8 | o[n];
72
+ }
73
+ function g(o, t = 0) {
74
+ const n = (o[t] & 128) === 128 ? -256 : 256;
75
+ return o[t] &= 127, U(o, t) / n;
76
+ }
77
+ function T(o, t = 0) {
78
+ const n = (o[t] & 128) === 128 ? -65536 : 65536;
79
+ return o[t] &= 127, C(o, t) / n;
80
+ }
81
+ function u(o, t = 0) {
82
+ const n = (o[t] & 128) === 128 ? -4096 : 4096;
83
+ return o[t] &= 127, O(o, t) / n;
84
+ }
85
+ const E = "gzip";
86
+ async function v(o) {
87
+ if (typeof DecompressionStream < "u")
88
+ try {
89
+ const t = new DecompressionStream(E);
90
+ return await new Response(
91
+ new Blob([o]).stream().pipeThrough(t)
92
+ ).arrayBuffer();
93
+ } catch {
94
+ return o;
95
+ }
96
+ else
97
+ throw new Error("DecompressionStream not supported");
98
+ }
99
+ class N {
100
+ constructor({
101
+ url: t,
102
+ // server url
103
+ autoReconnect: n = !0,
104
+ // auto reconnect on disconnect
105
+ allowSync: s = !0,
106
+ // allow sync on connect
107
+ allowWebRTC: c = !1
108
+ }) {
109
+ this.CYCLE = 200, this.url = "", this.documents = {}, this.autoReconnect = !0, this.allowSync = !0, this.allowWebRTC = !1, this.isConnected = !1, this.isConnecting = !1, this.isPatched = !1, this.stats = {
110
+ send: 0,
111
+ rec: 0,
112
+ recRTC: 0,
113
+ sendBps: 0,
114
+ recBps: 0,
115
+ recRTCBps: 0,
116
+ ping: 0,
117
+ stdiff: 0
118
+ // server time difference
119
+ }, this.lastFullState = 0, this.lastPatch = 0, this._chunks = {}, this.le = !0, this._documentChanges = {}, this.ID = 0, this.socket = null, this._peerConnection = null, this._dataChannel = null, this._serverDataChannel = null, this._webRTCConnected = null, this.isInterpolated = !1, this._lastInterpolate = Date.now(), this._lastUpdateId = {}, this._dpos = [0, 0, 0], this._drot = [0, 0, 0, 1], this._sca = [1, 1, 1], this._notifyChanges = !0, this._siv = null, this._loopiv = null, this._updateiv = null, this._pingiv = null, this.url = t, this.autoReconnect = n, this.allowSync = s, this.allowWebRTC = c, this.socket = null, this._startLoop(), console.log("Client initialized");
120
+ }
121
+ /*= UPDATE ===================================================================*/
122
+ _startLoop() {
123
+ this._loopiv && clearInterval(this._loopiv), this._loopiv = setInterval(() => {
124
+ this._loop();
125
+ }, this.CYCLE), this._siv = setInterval(() => {
126
+ this._updateStats();
127
+ }, 1e3), this._pingiv = setInterval(() => {
128
+ this._ping();
129
+ }, 1e4);
130
+ }
131
+ _loop() {
132
+ this.isConnected && this._sendPatches();
133
+ }
134
+ _updateStats() {
135
+ this.stats.recBps = this.stats.rec, this.stats.rec = 0, this.stats.recRTCBps = this.stats.recRTC, this.stats.recRTC = 0, this.stats.sendBps = this.stats.send, this.stats.send = 0;
136
+ }
137
+ _clear() {
138
+ this.stats.sendBps = 0, this.stats.recBps = 0, this.stats.recRTC = 0, this.stats.recRTCBps = 0, this.stats.send = 0, this.stats.rec = 0, this.ID = 0, this.documents = {}, this._documentChanges = {}, this._lastUpdateId = {}, this.lastFullState = 0, this.lastPatch = 0, this._lastInterpolate = 0, this.isPatched = !1, this.le = !0;
139
+ }
140
+ /*= INTERPOLATION ============================================================*/
141
+ // to be called in display rate (like 60fps) to interpolate .position, .rotation and .scale
142
+ interpolate() {
143
+ let t = Date.now(), n = t - this._lastInterpolate;
144
+ if (this._lastInterpolate = t, !(n <= 0 || n > 200)) {
145
+ this.isInterpolated = !0;
146
+ for (let s in this.documents) {
147
+ let r = this.documents[s].entities;
148
+ if (r)
149
+ for (let l in r) {
150
+ let i = r[l];
151
+ if (i._lpostime1 && i._lpostime2) {
152
+ let h = i._lpostime1;
153
+ const d = i._lpostime2 - h, a = t - h, f = Math.max(0, a / d);
154
+ I.lerp(this._dpos, i._lpos1, i._lpos2, f), I.lerp(i.position, i.position, this._dpos, 0.07), i._changed_position = t;
155
+ }
156
+ if (i._lrottime1 && i._lrottime2) {
157
+ let h = i._lrottime1;
158
+ const d = i._lrottime2 - h, a = t - h, f = Math.max(0, a / d);
159
+ R.slerp(this._drot, i._lrot1, i._lrot2, f), R.slerp(i.rotation, i.rotation, this._drot, 0.07), i._changed_rotation = t;
160
+ }
161
+ }
162
+ }
163
+ this.isInterpolated = !1;
164
+ }
165
+ }
166
+ /*= CONNECTION ===============================================================*/
167
+ subscribe(t) {
168
+ this.documents[t] = {}, this.send({ c: "sub", n: t });
169
+ }
170
+ unsubscribe(t) {
171
+ this.send({ c: "unsub", n: t }), delete this.documents[t];
172
+ }
173
+ connect() {
174
+ this.isConnecting || (this.isConnecting = !0, this._clear(), console.log("connecting..."), this.socket = new WebSocket(this.url), this.socket.onmessage = async (t) => {
175
+ let n = await t.data.arrayBuffer();
176
+ this.stats.rec += n.byteLength;
177
+ let s = await v(n), c = w(s);
178
+ this._onMessage(c);
179
+ }, this.socket.onclose = (t) => {
180
+ this._clear(), this.isConnected = !1, this.isConnecting = !1, this.lastFullState = 0, this.socket = null, this.onDisconnect(), this.allowWebRTC && this._destroyWebRTC(), this.autoReconnect && setTimeout(
181
+ () => {
182
+ this._reconnect();
183
+ },
184
+ 500 + Math.random() * 500
185
+ );
186
+ }, this.socket.onerror = (t) => {
187
+ this._clear(), this.isConnected = !1, this.isConnecting = !1, this.lastFullState = 0, this.socket = null, this.onDisconnect(), this.allowWebRTC && this._destroyWebRTC(), this.autoReconnect && setTimeout(
188
+ () => {
189
+ this._reconnect();
190
+ },
191
+ 500 + Math.random() * 500
192
+ );
193
+ }, this.socket.onopen = async (t) => {
194
+ this._clear(), this.isConnecting = !1, this.isConnected = !0, this.lastFullState = 0, this._ping(), this.onConnect(), this.allowWebRTC && await this._initializeWebRTC();
195
+ });
196
+ }
197
+ disconnect() {
198
+ this._clear(), this.isConnected = !1, this.isConnecting = !1, this.lastFullState = 0, this.socket.close(), this.socket = null;
199
+ }
200
+ destroy() {
201
+ this._clear(), this.autoReconnect = !1, this.disconnect(), this.socket = null, clearInterval(this._siv), clearInterval(this._loopiv);
202
+ }
203
+ onConnect() {
204
+ }
205
+ onDisconnect() {
206
+ }
207
+ _reconnect() {
208
+ this.isConnected || this.isConnecting || this.connect();
209
+ }
210
+ _ping() {
211
+ this.isConnected && this.send({ c: "ping", ct: Date.now() });
212
+ }
213
+ /*= MESSAGES =================================================================*/
214
+ onChange(t, n) {
215
+ }
216
+ onMessage(t) {
217
+ }
218
+ send(t) {
219
+ try {
220
+ let n = S(t);
221
+ this.stats.send += n.byteLength, this.socket.send(n);
222
+ } catch (n) {
223
+ console.error("send failed", n);
224
+ }
225
+ }
226
+ get document() {
227
+ let t = Object.keys(this.documents);
228
+ return this.documents[t[0]];
229
+ }
230
+ async _onMessage(t) {
231
+ let n = Date.now();
232
+ if (t.c == "full") {
233
+ console.log("full:", t);
234
+ let s = t.n, c = t.doc;
235
+ this.documents[s] = c, this._decodeFastChanges(t), this.isPatched = !1, this.allowSync && (this.documents[s] = D(
236
+ s,
237
+ this.documents[s],
238
+ this._onDocumentChange.bind(this)
239
+ )), this.isPatched = !1, this.lastFullState = t.t, this.le = t.le, this._notifyChanges && this.onChange(s, this.documents[s]);
240
+ } else if (t.c == "patch") {
241
+ this.lastPatch = t.t;
242
+ let s = t.n;
243
+ if (t.doc) {
244
+ this.isPatched = !0;
245
+ for (let c of t.doc) {
246
+ let r = W(c);
247
+ L(this.documents[s], r);
248
+ }
249
+ this.isPatched = !1;
250
+ }
251
+ this._notifyChanges && this.onChange(s, this.documents[s]);
252
+ } else if (t.c == "chunk") {
253
+ if (this._chunks[t.mid + "-" + t.seq] = t, t.last) {
254
+ let s = 0, c = t.ts, r = new Uint8Array(c);
255
+ for (const l in this._chunks) {
256
+ let i = this._chunks[l];
257
+ if (i.mid == t.mid) {
258
+ let h = i.ofs;
259
+ i.chs, r.set(new Uint8Array(i.data), h), s++, delete this._chunks[l];
260
+ }
261
+ }
262
+ if (s == t.seq + 1)
263
+ try {
264
+ let l = await v(r), i = w(l);
265
+ this._onMessage(i);
266
+ } catch (l) {
267
+ console.error("Error decoding chunks:", l);
268
+ }
269
+ else
270
+ console.warn("missing chunks", s, "of", t.seq + 1);
271
+ }
272
+ } else if (t.c == "fpatch") {
273
+ n = Date.now();
274
+ let s = t.n, c = !0;
275
+ if (!this._lastUpdateId[s])
276
+ this._lastUpdateId[s] = t.u;
277
+ else if (this._lastUpdateId[s] < t.u) {
278
+ let r = t.u - this._lastUpdateId[s] - 1;
279
+ r > 0 && console.warn("Lost " + r + " updates"), this._lastUpdateId[s] = t.u;
280
+ } else this._lastUpdateId[s] > t.u && (console.warn(`Received outdated update ID for document ${s}: ${t.u} < ${this._lastUpdateId[s]}`), c = !1);
281
+ c && this._decodeFastChanges(t);
282
+ } else if (t.c == "pong") {
283
+ this.ID = t.ID, n = Date.now();
284
+ let s = t.ct, c = n - s, r = t.t;
285
+ this.send({ c: "peng", ct: Date.now(), st: r }), this.stats.stdiff = r + c / 2 - n, this.stats.ping = c, console.log("ping", c, "ms", "stdiff", this.stats.stdiff, "ms");
286
+ } else if (t.c != "rtc-offer") if (t.c == "rtc-answer")
287
+ try {
288
+ const s = new RTCSessionDescription({
289
+ type: t.type,
290
+ sdp: t.sdp
291
+ });
292
+ await this._peerConnection.setRemoteDescription(s);
293
+ } catch (s) {
294
+ console.error("RTC: Error setting remote description:", s);
295
+ }
296
+ else if (t.c == "rtc-candidate")
297
+ try {
298
+ this._peerConnection && t.candidate ? await this._peerConnection.addIceCandidate(
299
+ new RTCIceCandidate(t.candidate)
300
+ ) : console.warn(
301
+ "RTC: Received candidate but peerConnection not ready or candidate missing"
302
+ );
303
+ } catch {
304
+ }
305
+ else
306
+ this.onMessage(t);
307
+ }
308
+ _onDocumentChange(t, n, s, c, r) {
309
+ this.isPatched || !this.allowSync || c.indexOf("/_") >= 0 || (this._documentChanges[t] || (this._documentChanges[t] = []), this._documentChanges[t].push(B(n, s, c, r)));
310
+ }
311
+ _sendPatches() {
312
+ for (let t in this._documentChanges) {
313
+ let n = this._documentChanges[t];
314
+ if (n.length == 0)
315
+ continue;
316
+ let s = {
317
+ n: t,
318
+ c: "sync",
319
+ ct: Date.now(),
320
+ p: null
321
+ };
322
+ n.length > 0 && (s.p = n), this.send(s), this._documentChanges[t].length = 0, this._notifyChanges && this.onChange(t, this.documents[t]);
323
+ }
324
+ }
325
+ _decodeFastChanges(t) {
326
+ let n = Date.now(), s = t.n, c = t.fdata;
327
+ if (!c)
328
+ return;
329
+ let r = this.documents[s];
330
+ if (!r)
331
+ return;
332
+ let l = r.entities;
333
+ if (!l)
334
+ return;
335
+ let i = this.documents[s].origin;
336
+ i || (i = [0, 0, 0]);
337
+ for (let h in c) {
338
+ let _ = c[h];
339
+ if (_.dict) {
340
+ let d = _.pdata, a = _.dict, f = {};
341
+ for (let p in a)
342
+ f[a[p]] = p;
343
+ let e = 0;
344
+ for (; e < d.byteLength; ) {
345
+ let p = "" + C(d, e);
346
+ e += 4;
347
+ let k = "" + C(d, e);
348
+ e += 4;
349
+ let m = l[p];
350
+ if (!m)
351
+ continue;
352
+ let P = f[k];
353
+ m[h] = P, m["_changed_" + h] = n;
354
+ }
355
+ } else {
356
+ let d = _.pdata, a = 0;
357
+ for (; a < d.byteLength; ) {
358
+ let f = "" + C(d, a), e = l[f];
359
+ if (!e) {
360
+ h == "position" ? a += 13 : h == "rotation" ? a += 8 : h == "scale" && (a += 16);
361
+ continue;
362
+ }
363
+ a += 4, h == "position" ? (e._lpos2 ? (e._lpos1[0] = e._lpos2[0], e._lpos1[1] = e._lpos2[1], e._lpos1[2] = e._lpos2[2], e._lpostime1 = e._lpostime2) : (e._lpos1 = [0, 0, 0], e._lpos2 = [0, 0, 0]), e._lpostime2 = n, e._lpos2[0] = i[0] + g(d, a), a += 3, e._lpos2[1] = i[1] + g(d, a), a += 3, e._lpos2[2] = i[2] + g(d, a), a += 3, e.position || (e.position = [
364
+ e._lpos2[0],
365
+ e._lpos2[1],
366
+ e._lpos2[2]
367
+ ])) : h == "rotation" ? (e._lrot2 ? (e._lrot1[0] = e._lrot2[0], e._lrot1[1] = e._lrot2[1], e._lrot1[2] = e._lrot2[2], e._lrot1[3] = e._lrot2[3], e._lrottime1 = e._lrottime2) : (e._lrot1 = [0, 0, 0, 1], e._lrot2 = [0, 0, 0, 1]), e._lrottime2 = n, e._lrot2[0] = u(d, a), a += 2, e._lrot2[1] = u(d, a), a += 2, e._lrot2[2] = u(d, a), a += 2, e._lrot2[3] = u(d, a), a += 2, R.normalize(e._lrot2, e._lrot2), e.rotation || (e.rotation = [
368
+ e._lrot2[0],
369
+ e._lrot2[1],
370
+ e._lrot2[2],
371
+ e._lrot2[3]
372
+ ])) : h == "scale" && (e._lsca2 ? (e._lsca1[0] = e._lsca2[0], e._lsca1[1] = e._lsca2[1], e._lsca1[2] = e._lsca2[2], e._lsca1[3] = e._lsca2[3], e._lscatime1 = e._lscatime2) : (e._lsca1 = [0, 0, 0, 1], e._lsca2 = [0, 0, 0, 1]), e._lscatime2 = n, e._lsca2[0] = T(d, a), a += 4, e._lsca2[1] = T(d, a), a += 4, e._lsca2[2] = T(d, a), a += 4, e.sca || (e.sca = [
373
+ e._lsca2[0],
374
+ e._lsca2[1],
375
+ e._lsca2[2]
376
+ ]));
377
+ }
378
+ }
379
+ }
380
+ }
381
+ /*= WEBRTC ===================================================================*/
382
+ sendRTC(t) {
383
+ this._dataChannel && this._dataChannel.readyState === "open" && this._dataChannel.send(S(t));
384
+ }
385
+ _onRTCConnect() {
386
+ console.log("RTC: Connected"), this.send({ c: "test", message: "Hello RTC from client" });
387
+ }
388
+ _onRTCDisconnect() {
389
+ this._webRTCConnected = !0, console.log("RTC: Disconnected");
390
+ }
391
+ async _onRTCMessage(t) {
392
+ this.stats.recRTC += t.byteLength;
393
+ let n = await v(t), s = w(n);
394
+ this._onMessage(s);
395
+ }
396
+ async _initializeWebRTC() {
397
+ this._peerConnection = null;
398
+ try {
399
+ this._peerConnection = new RTCPeerConnection({
400
+ iceServers: [
401
+ { urls: "stun:stun.l.google.com:19302" },
402
+ { urls: "stun:stun.cloudflare.com:3478" },
403
+ { urls: "stun:freestun.net:3478" }
404
+ ],
405
+ iceCandidatePoolSize: 10
406
+ }), this._peerConnection.onicecandidate = (r) => {
407
+ r.candidate && this.send({
408
+ c: "rtc-candidate",
409
+ type: "ice-candidate",
410
+ candidate: r.candidate
411
+ });
412
+ }, this._peerConnection.onconnectionstatechange = () => {
413
+ this._peerConnection.connectionState === "connected" ? this._webRTCConnected = !0 : (this._peerConnection.connectionState === "failed" || this._peerConnection.connectionState === "disconnected" || this._peerConnection.connectionState === "closed") && (this._webRTCConnected = !1);
414
+ }, this._peerConnection.onicegatheringstatechange = () => {
415
+ }, this._peerConnection.oniceconnectionstatechange = () => {
416
+ this._peerConnection.iceConnectionState === "connected" || this._peerConnection.iceConnectionState;
417
+ }, this._dataChannel = this._peerConnection.createDataChannel(
418
+ "clientchannel",
419
+ {
420
+ ordered: !0,
421
+ maxRetransmits: 1
422
+ }
423
+ ), this._dataChannel.onopen = () => {
424
+ this._onRTCConnect();
425
+ }, this._dataChannel.onclose = () => {
426
+ this._onRTCDisconnect();
427
+ }, this._dataChannel.onerror = (r) => {
428
+ console.error("RTC: Client data channel error", r);
429
+ }, this._peerConnection.ondatachannel = (r) => {
430
+ const l = r.channel;
431
+ this._serverDataChannel = l, l.onopen = () => {
432
+ }, l.onclose = () => {
433
+ }, l.onerror = (i) => {
434
+ }, l.onmessage = (i) => {
435
+ this._onRTCMessage(i.data);
436
+ };
437
+ };
438
+ const t = {
439
+ offerToReceiveAudio: !1,
440
+ offerToReceiveVideo: !1,
441
+ iceRestart: !0
442
+ }, n = await this._peerConnection.createOffer(t);
443
+ await this._peerConnection.setLocalDescription(n), await new Promise((r) => setTimeout(r, 100));
444
+ let s = this._peerConnection.localDescription;
445
+ const c = {
446
+ c: "rtc-offer",
447
+ type: s.type,
448
+ sdp: s.sdp
449
+ };
450
+ this.send(c), setTimeout(() => {
451
+ !this._webRTCConnected && this._peerConnection && this._peerConnection.iceConnectionState === "failed" && (console.log("RTC: Attempting ICE restart"), this._restartIce());
452
+ }, 5e3);
453
+ } catch (t) {
454
+ console.error("RTC: error:", t);
455
+ }
456
+ }
457
+ // Add this method to restart ICE if needed
458
+ async _restartIce() {
459
+ try {
460
+ const t = {
461
+ offerToReceiveAudio: !1,
462
+ offerToReceiveVideo: !1,
463
+ iceRestart: !0
464
+ }, n = await this._peerConnection.createOffer(t);
465
+ await this._peerConnection.setLocalDescription(n);
466
+ const s = {
467
+ c: "rtc-offer",
468
+ type: n.type,
469
+ sdp: n.sdp
470
+ };
471
+ this.send(s);
472
+ } catch {
473
+ }
474
+ }
475
+ async _destroyWebRTC() {
476
+ this._peerConnection && (this._peerConnection.dataChannel && this._peerConnection.dataChannel.close(), this._peerConnection.close(), this._peerConnection = null), this._dataChannel && (this._dataChannel.close(), this._dataChannel = null), this._webRTCConnected = !1;
477
+ }
478
+ }
479
+ export {
480
+ N as default
481
+ };