topazcube 0.1.14 → 0.1.16
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/dist/client.cjs +4 -4
- package/dist/client.cjs.map +1 -1
- package/dist/client.d.cts +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/client.js +4 -4
- package/dist/client.js.map +1 -1
- package/dist/server.cjs +184 -234
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.cts +7 -1
- package/dist/server.d.ts +7 -1
- package/dist/server.js +191 -234
- package/dist/server.js.map +1 -1
- package/package.json +2 -1
- package/src/client.ts +4 -4
- package/src/server.ts +6 -6
- package/src/utils.ts +2 -2
package/dist/server.cjs
CHANGED
|
@@ -122,9 +122,9 @@ function clonewo_(obj, excludeStart = "_") {
|
|
|
122
122
|
}
|
|
123
123
|
if (obj instanceof Map) {
|
|
124
124
|
const mapClone = /* @__PURE__ */ new Map();
|
|
125
|
-
|
|
125
|
+
Array.from(obj.entries()).forEach(([key, value]) => {
|
|
126
126
|
mapClone.set(clonewo_(key, excludeStart), clonewo_(value, excludeStart));
|
|
127
|
-
}
|
|
127
|
+
});
|
|
128
128
|
return mapClone;
|
|
129
129
|
}
|
|
130
130
|
let clone;
|
|
@@ -282,6 +282,7 @@ var import_ws = require("ws");
|
|
|
282
282
|
var import_mongodb = require("mongodb");
|
|
283
283
|
var import_gl_matrix = require("gl-matrix");
|
|
284
284
|
import_gl_matrix.glMatrix.setMatrixArrayType(Array);
|
|
285
|
+
var wrtc = require("@roamhq/wrtc");
|
|
285
286
|
var fastPatchProperties = {
|
|
286
287
|
"type": true,
|
|
287
288
|
// string 'enemy'
|
|
@@ -325,6 +326,7 @@ var LITTLE_ENDIAN = (() => {
|
|
|
325
326
|
new DataView(buffer).setInt16(0, 256, true);
|
|
326
327
|
return new Int16Array(buffer)[0] === 256;
|
|
327
328
|
})();
|
|
329
|
+
var MAX_PACKAGE_SIZE = 65400;
|
|
328
330
|
var TopazCubeServer = class {
|
|
329
331
|
name = "TopazCubeServer";
|
|
330
332
|
cycle = 100;
|
|
@@ -618,8 +620,8 @@ var TopazCubeServer = class {
|
|
|
618
620
|
client.peerConnection = null;
|
|
619
621
|
this.log("client connected", client.ID);
|
|
620
622
|
this.clients.push(client);
|
|
621
|
-
client.on("error", () => {
|
|
622
|
-
this._onError(client,
|
|
623
|
+
client.on("error", (...args) => {
|
|
624
|
+
this._onError(client, args);
|
|
623
625
|
});
|
|
624
626
|
client.on("message", (message) => {
|
|
625
627
|
let dec = decode(message);
|
|
@@ -956,258 +958,206 @@ var TopazCubeServer = class {
|
|
|
956
958
|
e["__changed_" + property] = true;
|
|
957
959
|
}
|
|
958
960
|
/*= WEBRTC ===================================================================*/
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
//
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
type: 'ice-candidate',
|
|
979
|
-
candidate: event.candidate, // .toJSON()
|
|
980
|
-
})
|
|
981
|
-
} else {
|
|
982
|
-
//this.log("RTC: ICE candidate gathering complete");
|
|
983
|
-
}
|
|
984
|
-
}
|
|
985
|
-
|
|
986
|
-
peerConnection.onconnectionstatechange = () => {
|
|
987
|
-
//this.log(`RTC: Connection state changed: ${peerConnection.connectionState}`);
|
|
988
|
-
if (peerConnection.connectionState === 'connected') {
|
|
989
|
-
client.webRTCConnected = true
|
|
990
|
-
this.log(`RTC: Connection established with client ${client.ID}`)
|
|
991
|
-
} else if (
|
|
992
|
-
peerConnection.connectionState === 'failed' ||
|
|
993
|
-
peerConnection.connectionState === 'disconnected' ||
|
|
994
|
-
peerConnection.connectionState === 'closed'
|
|
995
|
-
) {
|
|
996
|
-
client.webRTCConnected = false
|
|
997
|
-
this.log(`RTC: Connection failed or closed with client ${client.ID}`)
|
|
998
|
-
}
|
|
961
|
+
async _processOffer(client, data) {
|
|
962
|
+
const peerConnection = new wrtc.RTCPeerConnection({
|
|
963
|
+
iceServers: [
|
|
964
|
+
{ urls: "stun:stun.l.google.com:19302" },
|
|
965
|
+
{ urls: "stun:stun.cloudflare.com:3478" },
|
|
966
|
+
{ urls: "stun:freestun.net:3478" }
|
|
967
|
+
],
|
|
968
|
+
iceCandidatePoolSize: 10
|
|
969
|
+
});
|
|
970
|
+
client.peerConnection = peerConnection;
|
|
971
|
+
peerConnection.onicecandidate = (event) => {
|
|
972
|
+
if (event.candidate) {
|
|
973
|
+
this.send(client, {
|
|
974
|
+
c: "rtc-candidate",
|
|
975
|
+
type: "ice-candidate",
|
|
976
|
+
candidate: event.candidate
|
|
977
|
+
// .toJSON()
|
|
978
|
+
});
|
|
979
|
+
} else {
|
|
999
980
|
}
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
981
|
+
};
|
|
982
|
+
peerConnection.onconnectionstatechange = () => {
|
|
983
|
+
if (peerConnection.connectionState === "connected") {
|
|
984
|
+
client.webRTCConnected = true;
|
|
985
|
+
this.log(`RTC: Connection established with client ${client.ID}`);
|
|
986
|
+
} else if (peerConnection.connectionState === "failed" || peerConnection.connectionState === "disconnected" || peerConnection.connectionState === "closed") {
|
|
987
|
+
client.webRTCConnected = false;
|
|
988
|
+
this.log(`RTC: Connection failed or closed with client ${client.ID}`);
|
|
1003
989
|
}
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
peerConnection.iceConnectionState === 'completed'
|
|
1010
|
-
) {
|
|
1011
|
-
//this.log(`RTC: ICE connection established with client ${client.ID}`);
|
|
1012
|
-
}
|
|
990
|
+
};
|
|
991
|
+
peerConnection.onicegatheringstatechange = () => {
|
|
992
|
+
};
|
|
993
|
+
peerConnection.oniceconnectionstatechange = () => {
|
|
994
|
+
if (peerConnection.iceConnectionState === "connected" || peerConnection.iceConnectionState === "completed") {
|
|
1013
995
|
}
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
} catch (e) {
|
|
1033
|
-
this.error(
|
|
1034
|
-
`RTC: Error sending test message to client ${client.ID}`,
|
|
1035
|
-
e
|
|
1036
|
-
)
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
client.dataChannel.onclose = () => {
|
|
1041
|
-
this.log(`RTC: Data channel closed for client ${client.ID}`)
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
client.dataChannel.onerror = (error: Event) => {
|
|
1045
|
-
this.error(`RTC: Data channel error for client ${client.ID}:`, error)
|
|
996
|
+
};
|
|
997
|
+
try {
|
|
998
|
+
await peerConnection.setRemoteDescription(
|
|
999
|
+
new wrtc.RTCSessionDescription(data)
|
|
1000
|
+
);
|
|
1001
|
+
client.dataChannel = peerConnection.createDataChannel("serverchannel", {
|
|
1002
|
+
ordered: true,
|
|
1003
|
+
maxRetransmits: 1
|
|
1004
|
+
});
|
|
1005
|
+
client.dataChannel.onopen = () => {
|
|
1006
|
+
try {
|
|
1007
|
+
const testData = { c: "test", message: "Hello WebRTC" };
|
|
1008
|
+
this.sendRTC(client, testData);
|
|
1009
|
+
} catch (e) {
|
|
1010
|
+
this.error(
|
|
1011
|
+
`RTC: Error sending test message to client ${client.ID}`,
|
|
1012
|
+
e
|
|
1013
|
+
);
|
|
1046
1014
|
}
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1015
|
+
};
|
|
1016
|
+
client.dataChannel.onclose = () => {
|
|
1017
|
+
this.log(`RTC: Data channel closed for client ${client.ID}`);
|
|
1018
|
+
};
|
|
1019
|
+
client.dataChannel.onerror = (error) => {
|
|
1020
|
+
this.error(`RTC: Data channel error for client ${client.ID}:`, error);
|
|
1021
|
+
};
|
|
1022
|
+
client.dataChannel.onmessage = (event) => {
|
|
1023
|
+
try {
|
|
1024
|
+
const data2 = decode(event.data);
|
|
1025
|
+
this.log(
|
|
1026
|
+
`RTC: Data channel message from client ${client.ID}:`,
|
|
1027
|
+
data2
|
|
1028
|
+
);
|
|
1029
|
+
} catch (error) {
|
|
1030
|
+
this.error(
|
|
1031
|
+
`RTC: Error decoding message from client ${client.ID}:`,
|
|
1032
|
+
error
|
|
1033
|
+
);
|
|
1062
1034
|
}
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1035
|
+
};
|
|
1036
|
+
const answer = await peerConnection.createAnswer();
|
|
1037
|
+
await peerConnection.setLocalDescription(answer);
|
|
1038
|
+
this.send(client, {
|
|
1039
|
+
c: "rtc-answer",
|
|
1040
|
+
type: answer.type,
|
|
1041
|
+
sdp: answer.sdp
|
|
1042
|
+
});
|
|
1043
|
+
} catch (error) {
|
|
1044
|
+
this.error(
|
|
1045
|
+
`RTC: Error processing offer from client ${client.ID}:`,
|
|
1046
|
+
error
|
|
1047
|
+
);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
async _processICECandidate(client, data) {
|
|
1051
|
+
try {
|
|
1052
|
+
if (client.peerConnection && data.candidate) {
|
|
1053
|
+
await client.peerConnection.addIceCandidate(
|
|
1054
|
+
data.candidate
|
|
1055
|
+
//new wrtc.RTCIceCandidate(data.candidate)
|
|
1056
|
+
);
|
|
1057
|
+
} else {
|
|
1079
1058
|
}
|
|
1059
|
+
} catch (error) {
|
|
1060
|
+
this.error(`RTC: Error adding ICE candidate for client ${client.ID}`);
|
|
1080
1061
|
}
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1062
|
+
}
|
|
1063
|
+
_clientRTCOpen(client) {
|
|
1064
|
+
return client.dataChannel !== null && client.dataChannel !== void 0 && client.dataChannel.readyState === "open";
|
|
1065
|
+
}
|
|
1066
|
+
async sendRTC(client, message) {
|
|
1067
|
+
let data = encode(message);
|
|
1068
|
+
if (this.allowCompression) {
|
|
1069
|
+
data = await compress(data);
|
|
1070
|
+
}
|
|
1071
|
+
this.stats.sendRTC += data.byteLength;
|
|
1072
|
+
this.stats._sendRTCUpdate += data.byteLength;
|
|
1073
|
+
let packages = this._splitRTCMessage(data);
|
|
1074
|
+
if (this.simulateLatency) {
|
|
1075
|
+
setTimeout(() => {
|
|
1076
|
+
if (this._clientRTCOpen(client)) {
|
|
1077
|
+
packages.forEach((p) => {
|
|
1078
|
+
client.dataChannel.send(p);
|
|
1079
|
+
});
|
|
1093
1080
|
}
|
|
1094
|
-
}
|
|
1095
|
-
|
|
1081
|
+
}, this.simulateLatency);
|
|
1082
|
+
} else {
|
|
1083
|
+
if (this._clientRTCOpen(client)) {
|
|
1084
|
+
packages.forEach((p) => {
|
|
1085
|
+
client.dataChannel.send(p);
|
|
1086
|
+
});
|
|
1096
1087
|
}
|
|
1097
1088
|
}
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1089
|
+
}
|
|
1090
|
+
async broadcastRTC(message, clients = []) {
|
|
1091
|
+
if (clients.length == 0) {
|
|
1092
|
+
clients = this.clients;
|
|
1101
1093
|
}
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1094
|
+
let t1 = Date.now();
|
|
1095
|
+
let data = encode(message);
|
|
1096
|
+
let dl = data.byteLength;
|
|
1097
|
+
let t2 = Date.now();
|
|
1098
|
+
if (this.allowCompression) {
|
|
1099
|
+
data = await compress(data);
|
|
1100
|
+
}
|
|
1101
|
+
let t3 = Date.now();
|
|
1102
|
+
if (data.length > 16384) {
|
|
1103
|
+
this.log(`BroadcastRTC message ${dl} -> ${data.length} (${(100 * data.length / dl).toFixed()}%) encoding:${t2 - t1}ms compression:${t3 - t1}ms`);
|
|
1104
|
+
}
|
|
1105
|
+
let packages = this._splitRTCMessage(data);
|
|
1106
|
+
for (let client of this.clients) {
|
|
1107
|
+
this.stats.sendRTC += data.byteLength;
|
|
1108
|
+
this.stats._sendRTCUpdate += data.byteLength;
|
|
1113
1109
|
if (this.simulateLatency) {
|
|
1114
1110
|
setTimeout(() => {
|
|
1115
|
-
if (
|
|
1111
|
+
if (client.dataChannel && client.dataChannel.readyState === "open") {
|
|
1116
1112
|
packages.forEach((p) => {
|
|
1117
|
-
client
|
|
1118
|
-
})
|
|
1113
|
+
client?.dataChannel?.send(p);
|
|
1114
|
+
});
|
|
1119
1115
|
}
|
|
1120
|
-
}, this.simulateLatency)
|
|
1116
|
+
}, this.simulateLatency);
|
|
1121
1117
|
} else {
|
|
1122
|
-
if (
|
|
1118
|
+
if (client.dataChannel && client.dataChannel.readyState === "open") {
|
|
1123
1119
|
packages.forEach((p) => {
|
|
1124
|
-
client
|
|
1125
|
-
})
|
|
1120
|
+
client?.dataChannel?.send(p);
|
|
1121
|
+
});
|
|
1126
1122
|
}
|
|
1127
1123
|
}
|
|
1128
1124
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
let
|
|
1137
|
-
let
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
client?.dataChannel?.send(p)
|
|
1158
|
-
})
|
|
1159
|
-
}
|
|
1160
|
-
}, this.simulateLatency)
|
|
1161
|
-
} else {
|
|
1162
|
-
if (client.dataChannel && client.dataChannel.readyState === 'open') {
|
|
1163
|
-
packages.forEach((p) => {
|
|
1164
|
-
client?.dataChannel?.send(p)
|
|
1165
|
-
})
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
}
|
|
1170
|
-
|
|
1171
|
-
_splitRTCMessage(data: Uint8Array): Uint8Array[] {
|
|
1172
|
-
let packages: Uint8Array[]
|
|
1173
|
-
if (data.byteLength > 65535) {
|
|
1174
|
-
const now = Date.now()
|
|
1175
|
-
this.warn(`RTC: Message too large: ${data.byteLength} bytes`)
|
|
1176
|
-
// Split the message into smaller packages
|
|
1177
|
-
packages = [];
|
|
1178
|
-
let offset = 0;
|
|
1179
|
-
let mid = this.update +'-'+ now
|
|
1180
|
-
let seq = 0
|
|
1181
|
-
|
|
1182
|
-
// Create subsequent packages if needed
|
|
1183
|
-
while (offset < data.byteLength) {
|
|
1184
|
-
const remaining = data.byteLength - offset;
|
|
1185
|
-
const chunkSize = Math.min(remaining, MAX_PACKAGE_SIZE);
|
|
1186
|
-
const chunk = new Uint8Array(data.buffer, offset, chunkSize);
|
|
1187
|
-
let cmessage = {
|
|
1188
|
-
c: 'chunk',
|
|
1189
|
-
t: now,
|
|
1190
|
-
mid: mid,
|
|
1191
|
-
seq: seq,
|
|
1192
|
-
ofs: offset,
|
|
1193
|
-
chs: chunkSize,
|
|
1194
|
-
ts: data.byteLength,
|
|
1195
|
-
data: chunk,
|
|
1196
|
-
last: remaining <= MAX_PACKAGE_SIZE,
|
|
1197
|
-
}
|
|
1198
|
-
packages.push(encode(cmessage))
|
|
1199
|
-
offset += chunkSize;
|
|
1200
|
-
seq++;
|
|
1201
|
-
}
|
|
1202
|
-
|
|
1203
|
-
this.log(`RTC: Large message split into ${packages.length} packages`);
|
|
1204
|
-
} else {
|
|
1205
|
-
packages = [data]
|
|
1206
|
-
this.log(`RTC: Message - ${data.byteLength} bytes`)
|
|
1125
|
+
}
|
|
1126
|
+
_splitRTCMessage(data) {
|
|
1127
|
+
let packages;
|
|
1128
|
+
if (data.byteLength > 65535) {
|
|
1129
|
+
const now = Date.now();
|
|
1130
|
+
this.warn(`RTC: Message too large: ${data.byteLength} bytes`);
|
|
1131
|
+
packages = [];
|
|
1132
|
+
let offset = 0;
|
|
1133
|
+
let mid = this.update + "-" + now;
|
|
1134
|
+
let seq = 0;
|
|
1135
|
+
while (offset < data.byteLength) {
|
|
1136
|
+
const remaining = data.byteLength - offset;
|
|
1137
|
+
const chunkSize = Math.min(remaining, MAX_PACKAGE_SIZE);
|
|
1138
|
+
const chunk = new Uint8Array(data.buffer, offset, chunkSize);
|
|
1139
|
+
let cmessage = {
|
|
1140
|
+
c: "chunk",
|
|
1141
|
+
t: now,
|
|
1142
|
+
mid,
|
|
1143
|
+
seq,
|
|
1144
|
+
ofs: offset,
|
|
1145
|
+
chs: chunkSize,
|
|
1146
|
+
ts: data.byteLength,
|
|
1147
|
+
data: chunk,
|
|
1148
|
+
last: remaining <= MAX_PACKAGE_SIZE
|
|
1149
|
+
};
|
|
1150
|
+
packages.push(encode(cmessage));
|
|
1151
|
+
offset += chunkSize;
|
|
1152
|
+
seq++;
|
|
1207
1153
|
}
|
|
1208
|
-
|
|
1154
|
+
this.log(`RTC: Large message split into ${packages.length} packages`);
|
|
1155
|
+
} else {
|
|
1156
|
+
packages = [data];
|
|
1157
|
+
this.log(`RTC: Message - ${data.byteLength} bytes`);
|
|
1209
1158
|
}
|
|
1210
|
-
|
|
1159
|
+
return packages;
|
|
1160
|
+
}
|
|
1211
1161
|
/*= DATABASE =================================================================*/
|
|
1212
1162
|
// properties (of the documents) that starts with __ are not saved to the database.
|
|
1213
1163
|
// __properties are restored on hydration. (for example __physicsBody or __bigObject)
|