@irtio/runtime 0.8.0 → 0.9.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.
- package/dist/{chunk-ZCD2TBXB.js → chunk-ADZZEVJU.js} +228 -15
- package/dist/{chunk-DBGMG2S3.js → chunk-SBDZ45JG.js} +10 -2
- package/dist/{contract-CRilLVdx.d.ts → contract-C5aqs49-.d.ts} +5 -1
- package/dist/index.d.ts +17 -4
- package/dist/index.js +2 -2
- package/dist/{room-YJGz6Caw.d.ts → room-C4RZJ2KO.d.ts} +59 -6
- package/dist/test/index.d.ts +4 -4
- package/dist/test/index.js +3 -3
- package/dist/worker/index.d.ts +1 -1
- package/dist/worker/index.js +2 -2
- package/package.json +4 -4
|
@@ -250,7 +250,7 @@ function createCallProxy(core, clientId) {
|
|
|
250
250
|
}
|
|
251
251
|
);
|
|
252
252
|
}
|
|
253
|
-
function createBroadcastProxy(core) {
|
|
253
|
+
function createBroadcastProxy(core, except) {
|
|
254
254
|
return new Proxy(
|
|
255
255
|
{},
|
|
256
256
|
{
|
|
@@ -259,13 +259,19 @@ function createBroadcastProxy(core) {
|
|
|
259
259
|
return (params) => {
|
|
260
260
|
const desc = descFor(core, prop);
|
|
261
261
|
for (const entry of core.clients.values()) {
|
|
262
|
-
if (entry.connected)
|
|
262
|
+
if (!entry.connected) continue;
|
|
263
|
+
if (except?.has(entry.clientId)) continue;
|
|
264
|
+
sendCall(core, entry.clientId, desc, params ?? {});
|
|
263
265
|
}
|
|
264
266
|
};
|
|
265
267
|
}
|
|
266
268
|
}
|
|
267
269
|
);
|
|
268
270
|
}
|
|
271
|
+
function createBroadcastExceptProxy(core, except) {
|
|
272
|
+
const ids = typeof except === "string" ? [except] : except;
|
|
273
|
+
return createBroadcastProxy(core, new Set(ids));
|
|
274
|
+
}
|
|
269
275
|
function handleReply(core, clientId, payload) {
|
|
270
276
|
let reply;
|
|
271
277
|
try {
|
|
@@ -956,6 +962,7 @@ function decodeMatterBodies(bytes) {
|
|
|
956
962
|
function bodyKey(collection, id) {
|
|
957
963
|
return `${collection}\0${id}`;
|
|
958
964
|
}
|
|
965
|
+
var PLANAR_CHANNELS = /* @__PURE__ */ new Set(["x", "y", "qz", "qw", "vx", "vy", "wz"]);
|
|
959
966
|
var MatterRuntime = class {
|
|
960
967
|
engineKind = "matter2d";
|
|
961
968
|
/** rapier3d only; present so both runtimes satisfy one internal shape. */
|
|
@@ -981,6 +988,15 @@ var MatterRuntime = class {
|
|
|
981
988
|
restore;
|
|
982
989
|
stepMs;
|
|
983
990
|
sleepSynced = /* @__PURE__ */ new Set();
|
|
991
|
+
/**
|
|
992
|
+
* The plain record object each body was built from. See `PhysicsRuntime.sourceRecords`: `add()`
|
|
993
|
+
* installs a new record object, so an id removed and re-added between two reconciles (a pooled
|
|
994
|
+
* projectile) is identifiable by identity, and is the one case where a live body has to be
|
|
995
|
+
* rebuilt so the new row's spawn pose and velocity are honoured.
|
|
996
|
+
*/
|
|
997
|
+
sourceRecords = /* @__PURE__ */ new Map();
|
|
998
|
+
/** `collection.field` pairs already warned about a channel a plane cannot hold. */
|
|
999
|
+
warnedChannels = /* @__PURE__ */ new Set();
|
|
984
1000
|
constructor(core, matter, options) {
|
|
985
1001
|
const config = core.definition.config.physics;
|
|
986
1002
|
if (!config) throw new Error("MatterRuntime: the room config declares no physics");
|
|
@@ -1050,6 +1066,7 @@ var MatterRuntime = class {
|
|
|
1050
1066
|
} else {
|
|
1051
1067
|
this.applyRecordToBody(desc, spec.body, record);
|
|
1052
1068
|
}
|
|
1069
|
+
this.sourceRecords.set(key, record);
|
|
1053
1070
|
return spec.body;
|
|
1054
1071
|
}
|
|
1055
1072
|
// ---- M6 lane F: rewind ----
|
|
@@ -1089,6 +1106,17 @@ var MatterRuntime = class {
|
|
|
1089
1106
|
for (const [channel, field] of physics.channels) {
|
|
1090
1107
|
const raw = record[field];
|
|
1091
1108
|
if (typeof raw !== "number") continue;
|
|
1109
|
+
if (raw !== 0 && !PLANAR_CHANNELS.has(channel)) {
|
|
1110
|
+
const key = `${desc.name}.${field}`;
|
|
1111
|
+
if (!this.warnedChannels.has(key)) {
|
|
1112
|
+
this.warnedChannels.add(key);
|
|
1113
|
+
this.core.log(
|
|
1114
|
+
"warn",
|
|
1115
|
+
`irtio: ${desc.name}.${field} maps body channel ${channel}, which a matter2d room cannot hold (the world is a plane); the ${raw} it was added with was dropped`
|
|
1116
|
+
);
|
|
1117
|
+
}
|
|
1118
|
+
continue;
|
|
1119
|
+
}
|
|
1092
1120
|
applyChannel2d(channel, raw, t);
|
|
1093
1121
|
}
|
|
1094
1122
|
this.applyState(body, {
|
|
@@ -1110,8 +1138,21 @@ var MatterRuntime = class {
|
|
|
1110
1138
|
for (const id of coll.ids()) {
|
|
1111
1139
|
const key = bodyKey(desc.name, id);
|
|
1112
1140
|
live.add(key);
|
|
1113
|
-
if (this.bodies.has(key)) continue;
|
|
1114
1141
|
const record = coll.get(id);
|
|
1142
|
+
const existing = this.bodies.get(key);
|
|
1143
|
+
if (existing) {
|
|
1144
|
+
if (record === void 0) continue;
|
|
1145
|
+
const source = this.sourceRecords.get(key);
|
|
1146
|
+
if (source === void 0) {
|
|
1147
|
+
this.sourceRecords.set(key, record);
|
|
1148
|
+
continue;
|
|
1149
|
+
}
|
|
1150
|
+
if (source === record) continue;
|
|
1151
|
+
this.bodies.delete(key);
|
|
1152
|
+
this.sleepSynced.delete(key);
|
|
1153
|
+
this.sourceRecords.delete(key);
|
|
1154
|
+
this.matter.Composite.remove(this.engine.world, [existing.body, ...existing.constraints]);
|
|
1155
|
+
}
|
|
1115
1156
|
if (record !== void 0) this.create(desc, id, record);
|
|
1116
1157
|
}
|
|
1117
1158
|
}
|
|
@@ -1119,6 +1160,7 @@ var MatterRuntime = class {
|
|
|
1119
1160
|
if (live.has(key)) continue;
|
|
1120
1161
|
this.bodies.delete(key);
|
|
1121
1162
|
this.sleepSynced.delete(key);
|
|
1163
|
+
this.sourceRecords.delete(key);
|
|
1122
1164
|
this.matter.Composite.remove(this.engine.world, [attached.body, ...attached.constraints]);
|
|
1123
1165
|
}
|
|
1124
1166
|
}
|
|
@@ -1349,6 +1391,19 @@ var PhysicsRuntime = class {
|
|
|
1349
1391
|
* world rebuilt from schema state would wake it with a kick). Found by the drift check.
|
|
1350
1392
|
*/
|
|
1351
1393
|
sleepSynced = /* @__PURE__ */ new Set();
|
|
1394
|
+
/**
|
|
1395
|
+
* The plain record object each body was built from, by body key. `add()` always installs a
|
|
1396
|
+
* **new** record object (`normalizeRecord` builds one), so an id that is removed and re-added
|
|
1397
|
+
* between two reconciles — the pooled-projectile shape — is identifiable by identity alone,
|
|
1398
|
+
* which is the only way to see it from here: both edits happened before this module looked.
|
|
1399
|
+
* Without it the old body kept its pose and velocity and the new row's spawn values were lost.
|
|
1400
|
+
*
|
|
1401
|
+
* Unknown means adopt, never rebuild: a world restored from a hibernation blob has bodies with
|
|
1402
|
+
* no recorded source, and rebuilding those would throw away exactly what the blob preserved.
|
|
1403
|
+
*/
|
|
1404
|
+
sourceRecords = /* @__PURE__ */ new Map();
|
|
1405
|
+
/** `collection.field` pairs already warned about a channel value the engine would not take. */
|
|
1406
|
+
warnedChannels = /* @__PURE__ */ new Set();
|
|
1352
1407
|
constructor(core, rapier, options) {
|
|
1353
1408
|
const config = core.definition.config.physics;
|
|
1354
1409
|
if (!config) throw new Error("PhysicsRuntime: the room config declares no physics");
|
|
@@ -1423,10 +1478,50 @@ var PhysicsRuntime = class {
|
|
|
1423
1478
|
}
|
|
1424
1479
|
const body = this.world.createRigidBody(spec.body);
|
|
1425
1480
|
for (const collider of spec.colliders ?? []) this.world.createCollider(collider, body);
|
|
1481
|
+
this.warnLockedChannels(desc, spec, record);
|
|
1426
1482
|
this.applyRecordToBody(desc, body, record);
|
|
1427
1483
|
this.bodies.set(bodyKey2(desc.name, id), body);
|
|
1484
|
+
this.sourceRecords.set(bodyKey2(desc.name, id), record);
|
|
1428
1485
|
return body;
|
|
1429
1486
|
}
|
|
1487
|
+
/**
|
|
1488
|
+
* A spawn value the engine will not take, said once per field.
|
|
1489
|
+
*
|
|
1490
|
+
* Rapier accepts `setLinvel` on an axis the body has disabled and then keeps zero, and it
|
|
1491
|
+
* accepts one on a fixed body and then ignores it — both read back as "the field was applied"
|
|
1492
|
+
* and behave as if it never was. The desc says which, before the body exists, so the check is
|
|
1493
|
+
* on the desc: the usual case is the 2D-in-3D recipe (`enabledTranslations(true, true, false)`)
|
|
1494
|
+
* meeting a row that spawns with a `vz`. Named by field, because the field is what the author
|
|
1495
|
+
* wrote and what they will grep for.
|
|
1496
|
+
*/
|
|
1497
|
+
warnLockedChannels(desc, spec, record) {
|
|
1498
|
+
const physics = desc.physics;
|
|
1499
|
+
if (!physics) return;
|
|
1500
|
+
const b = spec.body;
|
|
1501
|
+
const frozen = b.status !== 0;
|
|
1502
|
+
const enabled = {
|
|
1503
|
+
vx: b.translationsEnabledX,
|
|
1504
|
+
vy: b.translationsEnabledY,
|
|
1505
|
+
vz: b.translationsEnabledZ,
|
|
1506
|
+
wx: b.rotationsEnabledX,
|
|
1507
|
+
wy: b.rotationsEnabledY,
|
|
1508
|
+
wz: b.rotationsEnabledZ
|
|
1509
|
+
};
|
|
1510
|
+
for (const [channel, field] of physics.channels) {
|
|
1511
|
+
const allowed = enabled[channel];
|
|
1512
|
+
if (allowed === void 0) continue;
|
|
1513
|
+
const raw = record[field];
|
|
1514
|
+
if (typeof raw !== "number" || raw === 0) continue;
|
|
1515
|
+
if (allowed && !frozen) continue;
|
|
1516
|
+
const key = `${desc.name}.${field}`;
|
|
1517
|
+
if (this.warnedChannels.has(key)) continue;
|
|
1518
|
+
this.warnedChannels.add(key);
|
|
1519
|
+
this.core.log(
|
|
1520
|
+
"warn",
|
|
1521
|
+
`irtio: ${desc.name}.${field} (body channel ${channel}) was added as ${raw}, but ` + (frozen ? `physics.bodies.${desc.name} builds a non-dynamic body, which never moves under it` : `that degree of freedom is disabled by physics.bodies.${desc.name}`) + " \u2014 the value was dropped"
|
|
1522
|
+
);
|
|
1523
|
+
}
|
|
1524
|
+
}
|
|
1430
1525
|
// ---- M6 lane F: rewind ----
|
|
1431
1526
|
/**
|
|
1432
1527
|
* D72: every tracked body, in the order `sync()` walks them (collection order, then instance
|
|
@@ -1522,8 +1617,21 @@ var PhysicsRuntime = class {
|
|
|
1522
1617
|
for (const id of coll.ids()) {
|
|
1523
1618
|
const key = bodyKey2(desc.name, id);
|
|
1524
1619
|
live.add(key);
|
|
1525
|
-
if (this.bodies.has(key)) continue;
|
|
1526
1620
|
const record = coll.get(id);
|
|
1621
|
+
const existing = this.bodies.get(key);
|
|
1622
|
+
if (existing) {
|
|
1623
|
+
if (record === void 0) continue;
|
|
1624
|
+
const source = this.sourceRecords.get(key);
|
|
1625
|
+
if (source === void 0) {
|
|
1626
|
+
this.sourceRecords.set(key, record);
|
|
1627
|
+
continue;
|
|
1628
|
+
}
|
|
1629
|
+
if (source === record) continue;
|
|
1630
|
+
this.bodies.delete(key);
|
|
1631
|
+
this.sleepSynced.delete(key);
|
|
1632
|
+
this.sourceRecords.delete(key);
|
|
1633
|
+
this.world.removeRigidBody(existing);
|
|
1634
|
+
}
|
|
1527
1635
|
if (record !== void 0) this.create(desc, id, record);
|
|
1528
1636
|
}
|
|
1529
1637
|
}
|
|
@@ -1531,6 +1639,7 @@ var PhysicsRuntime = class {
|
|
|
1531
1639
|
if (live.has(key)) continue;
|
|
1532
1640
|
this.bodies.delete(key);
|
|
1533
1641
|
this.sleepSynced.delete(key);
|
|
1642
|
+
this.sourceRecords.delete(key);
|
|
1534
1643
|
this.world.removeRigidBody(body);
|
|
1535
1644
|
}
|
|
1536
1645
|
}
|
|
@@ -1630,6 +1739,14 @@ function channelValue(channel, t, r, v, w) {
|
|
|
1630
1739
|
}
|
|
1631
1740
|
|
|
1632
1741
|
// src/core/random.ts
|
|
1742
|
+
function seedFromRoomId(roomId) {
|
|
1743
|
+
let h = 2166136261;
|
|
1744
|
+
for (let i = 0; i < roomId.length; i++) {
|
|
1745
|
+
h ^= roomId.charCodeAt(i);
|
|
1746
|
+
h = Math.imul(h, 16777619) >>> 0;
|
|
1747
|
+
}
|
|
1748
|
+
return h === 0 ? 2654435769 : h >>> 0;
|
|
1749
|
+
}
|
|
1633
1750
|
var Mulberry32 = class {
|
|
1634
1751
|
/** Current internal state (u32). Survives hibernation. */
|
|
1635
1752
|
state;
|
|
@@ -1691,6 +1808,16 @@ var Rapier2dRuntime = class {
|
|
|
1691
1808
|
bodies = /* @__PURE__ */ new Map();
|
|
1692
1809
|
/** See `PhysicsRuntime.sleepSynced`: the one extra sync a body owes on the tick it sleeps. */
|
|
1693
1810
|
sleepSynced = /* @__PURE__ */ new Set();
|
|
1811
|
+
/**
|
|
1812
|
+
* See `PhysicsRuntime.sourceRecords`: the plain record object each body was built from. `add()`
|
|
1813
|
+
* always installs a new record object, so an id removed and re-added between two reconciles —
|
|
1814
|
+
* the pooled-projectile shape — is identifiable by identity alone, and its body is rebuilt so
|
|
1815
|
+
* the new row's spawn pose and velocity are what the world gets. Unknown means adopt, never
|
|
1816
|
+
* rebuild: a world restored from a hibernation blob has bodies with no recorded source.
|
|
1817
|
+
*/
|
|
1818
|
+
sourceRecords = /* @__PURE__ */ new Map();
|
|
1819
|
+
/** `collection.field` pairs already warned about a channel value the engine would not take. */
|
|
1820
|
+
warnedChannels = /* @__PURE__ */ new Set();
|
|
1694
1821
|
constructor(core, rapier, options) {
|
|
1695
1822
|
const config = core.definition.config.physics;
|
|
1696
1823
|
if (!config) throw new Error("Rapier2dRuntime: the room config declares no physics");
|
|
@@ -1760,10 +1887,48 @@ var Rapier2dRuntime = class {
|
|
|
1760
1887
|
}
|
|
1761
1888
|
const body = this.world.createRigidBody(spec.body);
|
|
1762
1889
|
for (const collider of spec.colliders ?? []) this.world.createCollider(collider, body);
|
|
1890
|
+
this.warnLockedChannels(desc, spec, record);
|
|
1763
1891
|
this.applyRecordToBody(desc, body, record);
|
|
1764
1892
|
this.bodies.set(bodyKey3(desc.name, id), body);
|
|
1893
|
+
this.sourceRecords.set(bodyKey3(desc.name, id), record);
|
|
1765
1894
|
return body;
|
|
1766
1895
|
}
|
|
1896
|
+
/**
|
|
1897
|
+
* A spawn value the engine will not take, said once per field — the planar half of
|
|
1898
|
+
* `PhysicsRuntime.warnLockedChannels`.
|
|
1899
|
+
*
|
|
1900
|
+
* Rapier accepts `setLinvel` on an axis the body has disabled and then keeps zero, and it
|
|
1901
|
+
* accepts one on a fixed body and then ignores it: both read back as "the field was applied"
|
|
1902
|
+
* and behave as if it never was. The desc says which, before the body exists, so the check is on
|
|
1903
|
+
* the desc. In the plane there are three movable channels — `vx`, `vy` and the one rotation
|
|
1904
|
+
* `wz` — and the desc carries exactly those three flags. Named by field, because the field is
|
|
1905
|
+
* what the author wrote and what they will grep for.
|
|
1906
|
+
*/
|
|
1907
|
+
warnLockedChannels(desc, spec, record) {
|
|
1908
|
+
const physics = desc.physics;
|
|
1909
|
+
if (!physics) return;
|
|
1910
|
+
const b = spec.body;
|
|
1911
|
+
const frozen = b.status !== 0;
|
|
1912
|
+
const enabled = {
|
|
1913
|
+
vx: b.translationsEnabledX,
|
|
1914
|
+
vy: b.translationsEnabledY,
|
|
1915
|
+
wz: b.rotationsEnabled
|
|
1916
|
+
};
|
|
1917
|
+
for (const [channel, field] of physics.channels) {
|
|
1918
|
+
const allowed = enabled[channel];
|
|
1919
|
+
if (allowed === void 0) continue;
|
|
1920
|
+
const raw = record[field];
|
|
1921
|
+
if (typeof raw !== "number" || raw === 0) continue;
|
|
1922
|
+
if (allowed && !frozen) continue;
|
|
1923
|
+
const key = `${desc.name}.${field}`;
|
|
1924
|
+
if (this.warnedChannels.has(key)) continue;
|
|
1925
|
+
this.warnedChannels.add(key);
|
|
1926
|
+
this.core.log(
|
|
1927
|
+
"warn",
|
|
1928
|
+
`irtio: ${desc.name}.${field} (body channel ${channel}) was added as ${raw}, but ` + (frozen ? `physics.bodies.${desc.name} builds a non-dynamic body, which never moves under it` : `that degree of freedom is disabled by physics.bodies.${desc.name}`) + " \u2014 the value was dropped"
|
|
1929
|
+
);
|
|
1930
|
+
}
|
|
1931
|
+
}
|
|
1767
1932
|
// ---- M6 lane F: rewind ----
|
|
1768
1933
|
/**
|
|
1769
1934
|
* D72: every tracked body, in the order `sync()` walks them (collection order, then instance
|
|
@@ -1814,8 +1979,21 @@ var Rapier2dRuntime = class {
|
|
|
1814
1979
|
for (const id of coll.ids()) {
|
|
1815
1980
|
const key = bodyKey3(desc.name, id);
|
|
1816
1981
|
live.add(key);
|
|
1817
|
-
if (this.bodies.has(key)) continue;
|
|
1818
1982
|
const record = coll.get(id);
|
|
1983
|
+
const existing = this.bodies.get(key);
|
|
1984
|
+
if (existing) {
|
|
1985
|
+
if (record === void 0) continue;
|
|
1986
|
+
const source = this.sourceRecords.get(key);
|
|
1987
|
+
if (source === void 0) {
|
|
1988
|
+
this.sourceRecords.set(key, record);
|
|
1989
|
+
continue;
|
|
1990
|
+
}
|
|
1991
|
+
if (source === record) continue;
|
|
1992
|
+
this.bodies.delete(key);
|
|
1993
|
+
this.sleepSynced.delete(key);
|
|
1994
|
+
this.sourceRecords.delete(key);
|
|
1995
|
+
this.world.removeRigidBody(existing);
|
|
1996
|
+
}
|
|
1819
1997
|
if (record !== void 0) this.create(desc, id, record);
|
|
1820
1998
|
}
|
|
1821
1999
|
}
|
|
@@ -1823,6 +2001,7 @@ var Rapier2dRuntime = class {
|
|
|
1823
2001
|
if (live.has(key)) continue;
|
|
1824
2002
|
this.bodies.delete(key);
|
|
1825
2003
|
this.sleepSynced.delete(key);
|
|
2004
|
+
this.sourceRecords.delete(key);
|
|
1826
2005
|
this.world.removeRigidBody(body);
|
|
1827
2006
|
}
|
|
1828
2007
|
}
|
|
@@ -2144,8 +2323,8 @@ function catchUpDirty(ext, plain, fromRole, toRole) {
|
|
|
2144
2323
|
// src/core/snapshot.ts
|
|
2145
2324
|
import { withBuiltins } from "@irtio/protocol";
|
|
2146
2325
|
import { ByteReader as ByteReader3, ByteWriter as ByteWriter3, decodeSnapshot } from "@irtio/schema";
|
|
2147
|
-
var SNAPSHOT_FORMAT_VERSION =
|
|
2148
|
-
var READABLE_SNAPSHOT_VERSIONS = [1, 2];
|
|
2326
|
+
var SNAPSHOT_FORMAT_VERSION = 3;
|
|
2327
|
+
var READABLE_SNAPSHOT_VERSIONS = [1, 2, 3];
|
|
2149
2328
|
function parseHibernationBlob(bytes) {
|
|
2150
2329
|
const r = new ByteReader3(bytes);
|
|
2151
2330
|
const version = r.u8();
|
|
@@ -2156,22 +2335,31 @@ function parseHibernationBlob(bytes) {
|
|
|
2156
2335
|
const rngState = r.u32();
|
|
2157
2336
|
const tick = r.u32();
|
|
2158
2337
|
const mode = r.u8() === 1 ? "event" : "tick";
|
|
2338
|
+
const schemaHash8 = version >= 3 ? r.bytes(8).slice() : void 0;
|
|
2159
2339
|
let physics;
|
|
2160
2340
|
if (version >= 2) {
|
|
2161
2341
|
const section = r.blob();
|
|
2162
2342
|
if (section.length > 0) physics = section;
|
|
2163
2343
|
}
|
|
2164
|
-
return { version, seed, rngState, tick, mode, physics, snapshot: r.rest() };
|
|
2344
|
+
return { version, seed, rngState, tick, mode, schemaHash8, physics, snapshot: r.rest() };
|
|
2345
|
+
}
|
|
2346
|
+
function sameSchemaHash(a, b) {
|
|
2347
|
+
if (!a || !b) return true;
|
|
2348
|
+
if (a.length !== b.length) return false;
|
|
2349
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
|
|
2350
|
+
return true;
|
|
2165
2351
|
}
|
|
2166
2352
|
function writeHibernationBlob(header, snapshot, physics) {
|
|
2167
|
-
const
|
|
2168
|
-
const
|
|
2353
|
+
const hash = header.schemaHash8;
|
|
2354
|
+
const version = hash ? 3 : physics ? 2 : 1;
|
|
2355
|
+
const w = new ByteWriter3(snapshot.length + (physics?.length ?? 0) + 32);
|
|
2169
2356
|
w.u8(version);
|
|
2170
2357
|
w.u32(header.seed >>> 0);
|
|
2171
2358
|
w.u32(header.rngState >>> 0);
|
|
2172
2359
|
w.u32(header.tick >>> 0);
|
|
2173
2360
|
w.u8(header.mode === "event" ? 1 : 0);
|
|
2174
|
-
if (
|
|
2361
|
+
if (hash) w.bytes(hash);
|
|
2362
|
+
if (version >= 2) w.blob(physics ?? new Uint8Array(0));
|
|
2175
2363
|
w.bytes(snapshot);
|
|
2176
2364
|
return w.finish();
|
|
2177
2365
|
}
|
|
@@ -3284,9 +3472,20 @@ function createRoomApi(core, publicUrl) {
|
|
|
3284
3472
|
get id() {
|
|
3285
3473
|
return core.roomId;
|
|
3286
3474
|
},
|
|
3475
|
+
/**
|
|
3476
|
+
* The same tracked state object handlers get. The one caller that has no other way to it is
|
|
3477
|
+
* `physics.setup`, which runs before `onCreate` on a fresh room and after the snapshot is
|
|
3478
|
+
* decoded on a rebuild — see the doc on `Room.state`.
|
|
3479
|
+
*/
|
|
3480
|
+
get state() {
|
|
3481
|
+
return core.anyState;
|
|
3482
|
+
},
|
|
3287
3483
|
get link() {
|
|
3288
3484
|
return link();
|
|
3289
3485
|
},
|
|
3486
|
+
get seed() {
|
|
3487
|
+
return core.seed;
|
|
3488
|
+
},
|
|
3290
3489
|
get tick() {
|
|
3291
3490
|
return core.tick;
|
|
3292
3491
|
},
|
|
@@ -3477,6 +3676,9 @@ function createRoomApi(core, publicUrl) {
|
|
|
3477
3676
|
},
|
|
3478
3677
|
get broadcast() {
|
|
3479
3678
|
return broadcast;
|
|
3679
|
+
},
|
|
3680
|
+
broadcastExcept(clientId) {
|
|
3681
|
+
return createBroadcastExceptProxy(core, clientId);
|
|
3480
3682
|
}
|
|
3481
3683
|
};
|
|
3482
3684
|
const broadcast = createBroadcastProxy(core);
|
|
@@ -3665,8 +3867,13 @@ var RoomCore = class _RoomCore {
|
|
|
3665
3867
|
}
|
|
3666
3868
|
let restored;
|
|
3667
3869
|
let plain;
|
|
3668
|
-
|
|
3669
|
-
|
|
3870
|
+
const candidate = options.restoreFrom ? parseHibernationBlob(options.restoreFrom) : void 0;
|
|
3871
|
+
if (candidate && !sameSchemaHash(candidate.schemaHash8, this.ext.hash8)) {
|
|
3872
|
+
host.log("warn", [`irtio: room ${options.roomId}: schema changed, snapshot discarded`]);
|
|
3873
|
+
} else if (candidate) {
|
|
3874
|
+
restored = candidate;
|
|
3875
|
+
}
|
|
3876
|
+
if (restored) {
|
|
3670
3877
|
plain = decodeSnapshot2(this.ext, restored.snapshot).state;
|
|
3671
3878
|
const presence = plainEntity(plain, PRESENCE_COLLECTION);
|
|
3672
3879
|
for (const id of [...presence.ids()]) presence.remove(id);
|
|
@@ -3681,7 +3888,7 @@ var RoomCore = class _RoomCore {
|
|
|
3681
3888
|
this.plain = plain;
|
|
3682
3889
|
this.tracked = track(this.ext, plain);
|
|
3683
3890
|
this.anyState = this.tracked.state;
|
|
3684
|
-
this.seed = restored?.seed ?? options.seed ??
|
|
3891
|
+
this.seed = restored?.seed ?? options.seed ?? seedFromRoomId(this.roomId);
|
|
3685
3892
|
this.rng = new Mulberry32(restored?.rngState ?? this.seed);
|
|
3686
3893
|
this.tick = restored?.tick ?? 0;
|
|
3687
3894
|
const self = this;
|
|
@@ -4025,7 +4232,13 @@ var RoomCore = class _RoomCore {
|
|
|
4025
4232
|
snapshot() {
|
|
4026
4233
|
const physics = this.physics ? encodeSection(this.physics) : void 0;
|
|
4027
4234
|
return writeHibernationBlob(
|
|
4028
|
-
{
|
|
4235
|
+
{
|
|
4236
|
+
seed: this.seed,
|
|
4237
|
+
rngState: this.rng.state,
|
|
4238
|
+
tick: this.tick,
|
|
4239
|
+
mode: this.mode,
|
|
4240
|
+
schemaHash8: this.ext.hash8
|
|
4241
|
+
},
|
|
4029
4242
|
encodeSnapshot2(this.ext, this.plain, { tick: this.tick }),
|
|
4030
4243
|
physics
|
|
4031
4244
|
);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
parseHibernationBlob,
|
|
3
3
|
writeHibernationBlob
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-ADZZEVJU.js";
|
|
5
5
|
|
|
6
6
|
// src/migrate.ts
|
|
7
7
|
import { PRESENCE_COLLECTION, withBuiltins } from "@irtio/protocol";
|
|
@@ -53,7 +53,15 @@ function migrateSnapshot(blob, chain, options) {
|
|
|
53
53
|
}
|
|
54
54
|
plain = fromMigrationState(withBuiltins(schema), state, last.version);
|
|
55
55
|
const bytes = writeHibernationBlob(
|
|
56
|
-
{
|
|
56
|
+
{
|
|
57
|
+
seed: parsed.seed,
|
|
58
|
+
rngState: parsed.rngState,
|
|
59
|
+
tick: parsed.tick,
|
|
60
|
+
mode: parsed.mode,
|
|
61
|
+
// Stamped with the *target* schema: these bytes are now that schema's, and a room waking on
|
|
62
|
+
// it must not see them as a mismatch.
|
|
63
|
+
schemaHash8: withBuiltins(schema).hash8
|
|
64
|
+
},
|
|
57
65
|
encodeSnapshot(withBuiltins(schema), plain, { tick: parsed.tick })
|
|
58
66
|
);
|
|
59
67
|
return {
|
|
@@ -302,7 +302,11 @@ interface RoomHost {
|
|
|
302
302
|
}
|
|
303
303
|
interface RoomCoreOptions {
|
|
304
304
|
readonly roomId: string;
|
|
305
|
-
/**
|
|
305
|
+
/**
|
|
306
|
+
* Seed for `room.random()`. Defaults to a stable 32-bit hash of `roomId`, so two rooms of the
|
|
307
|
+
* same game generate different worlds; pass a number to pin the sequence instead. A room
|
|
308
|
+
* restored from a snapshot ignores both and keeps the seed it was created with.
|
|
309
|
+
*/
|
|
306
310
|
readonly seed?: number;
|
|
307
311
|
/** Base URL for `room.link` (`<publicUrl>?room=<id>`); default `http://localhost/`. */
|
|
308
312
|
readonly publicUrl?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
export { D as DEFAULT_TIMELINE_MAX_RECORDS, a as DEFAULT_TIMELINE_MAX_TICKS, E as EVENT_RING_SIZE, H as HOST_CALL_TIMEOUT_MS, b as HostCall, c as HostCallResult, J as JoinOptions, d as JoinResult, L as LogLevel, R as RoomCoreApi, e as RoomCoreOptions, f as RoomEvent, g as RoomEventKind, h as RoomFullError, i as RoomHost, j as RoomInspection, k as RoomStats, T as TimelineDump, l as TimelineFrame, m as TimelineRecorder, n as TimelineRecorderOptions, o as inspectState } from './contract-
|
|
2
|
-
export { C as CRASH_AFTER_THROWS, M as MAX_CATCHUP, a as MatterBodyRecord, b as MatterSection, c as Mulberry32, P as PhysicsEngineTag, d as PhysicsSection, R as RoomCore, e as decodeMatterBodies, f as decodeMatterSectionEnvelope, g as decodePhysicsSection, h as decodeRapier2dSectionEnvelope, i as encodeMatterBodies, j as encodeMatterSectionEnvelope, k as encodePhysicsSection, l as encodeRapier2dSectionEnvelope, m as initMatter, n as initPhysics, o as initRapier2d, p as loadedMatter, q as loadedPhysics, r as loadedRapier2d, s as physicsSectionEngine, t as resetMatterForTests, u as resetPhysicsForTests, v as resetRapier2dForTests } from './room-
|
|
1
|
+
export { D as DEFAULT_TIMELINE_MAX_RECORDS, a as DEFAULT_TIMELINE_MAX_TICKS, E as EVENT_RING_SIZE, H as HOST_CALL_TIMEOUT_MS, b as HostCall, c as HostCallResult, J as JoinOptions, d as JoinResult, L as LogLevel, R as RoomCoreApi, e as RoomCoreOptions, f as RoomEvent, g as RoomEventKind, h as RoomFullError, i as RoomHost, j as RoomInspection, k as RoomStats, T as TimelineDump, l as TimelineFrame, m as TimelineRecorder, n as TimelineRecorderOptions, o as inspectState } from './contract-C5aqs49-.js';
|
|
2
|
+
export { C as CRASH_AFTER_THROWS, M as MAX_CATCHUP, a as MatterBodyRecord, b as MatterSection, c as Mulberry32, P as PhysicsEngineTag, d as PhysicsSection, R as RoomCore, e as decodeMatterBodies, f as decodeMatterSectionEnvelope, g as decodePhysicsSection, h as decodeRapier2dSectionEnvelope, i as encodeMatterBodies, j as encodeMatterSectionEnvelope, k as encodePhysicsSection, l as encodeRapier2dSectionEnvelope, m as initMatter, n as initPhysics, o as initRapier2d, p as loadedMatter, q as loadedPhysics, r as loadedRapier2d, s as physicsSectionEngine, t as resetMatterForTests, u as resetPhysicsForTests, v as resetRapier2dForTests } from './room-C4RZJ2KO.js';
|
|
3
3
|
import { CollectionDesc, AnySchema, PlainState, DirtySet } from '@irtio/schema';
|
|
4
4
|
import '@irtio/protocol';
|
|
5
5
|
import '@irtio/server';
|
|
@@ -70,9 +70,16 @@ declare function catchUpDirty(ext: AnySchema, plain: PlainState, fromRole: strin
|
|
|
70
70
|
* u32 rng state
|
|
71
71
|
* u32 tick
|
|
72
72
|
* u8 mode (1 = event, 0 = tick)
|
|
73
|
+
* [v3] 8 bytes schema hash (`withBuiltins(schema).hash8`)
|
|
73
74
|
* [v2] blob physics section (varint length + bytes; length 0 = no world)
|
|
74
75
|
* ... encodeSnapshot(withBuiltins(schema), state)
|
|
75
76
|
*
|
|
77
|
+
* **v3** adds the schema hash. Before it, restoring a snapshot written under a different schema
|
|
78
|
+
* fed the old bytes to the new decoder and surfaced as a `TypeError` from deep inside the string
|
|
79
|
+
* decoder; the room could only be started again by hand-deleting `.irtio/snapshots`. The hash is
|
|
80
|
+
* in the envelope (not only in the codec payload) so the check costs no decode: a mismatch is
|
|
81
|
+
* seen, logged and the snapshot discarded before anything is parsed against the wrong schema.
|
|
82
|
+
*
|
|
76
83
|
* **v2 (week 9, D22)** adds the physics section: the Rapier world snapshot plus the entity↔handle
|
|
77
84
|
* map (`core/physics.ts` encodes it). It rides *inside* this blob rather than beside it so that
|
|
78
85
|
* wake is atomic — state and world always come back from the same bytes, and every store, cache
|
|
@@ -89,8 +96,8 @@ declare function catchUpDirty(ext: AnySchema, plain: PlainState, fromRole: strin
|
|
|
89
96
|
* instead of parsing bytes it may not yet know how to read.
|
|
90
97
|
*/
|
|
91
98
|
|
|
92
|
-
/** Written when the
|
|
93
|
-
declare const SNAPSHOT_FORMAT_VERSION =
|
|
99
|
+
/** Written when the header carries a schema hash (every room does); older versions still read. */
|
|
100
|
+
declare const SNAPSHOT_FORMAT_VERSION = 3;
|
|
94
101
|
/** Versions this build can parse. */
|
|
95
102
|
declare const READABLE_SNAPSHOT_VERSIONS: readonly number[];
|
|
96
103
|
type SnapshotMode = 'tick' | 'event';
|
|
@@ -99,6 +106,12 @@ interface SnapshotHeader {
|
|
|
99
106
|
readonly rngState: number;
|
|
100
107
|
readonly tick: number;
|
|
101
108
|
readonly mode: SnapshotMode;
|
|
109
|
+
/**
|
|
110
|
+
* `withBuiltins(schema).hash8` of the schema these bytes were written under. Omitted only by a
|
|
111
|
+
* writer that has no schema in hand; a restore that finds it absent cannot check and proceeds
|
|
112
|
+
* as pre-v3 builds did.
|
|
113
|
+
*/
|
|
114
|
+
readonly schemaHash8?: Uint8Array | undefined;
|
|
102
115
|
}
|
|
103
116
|
interface ParsedSnapshot extends SnapshotHeader {
|
|
104
117
|
/** The blob's own format version (1 or 2). */
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
fromMigrationState,
|
|
3
3
|
migrateSnapshot,
|
|
4
4
|
toMigrationState
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SBDZ45JG.js";
|
|
6
6
|
import {
|
|
7
7
|
CRASH_AFTER_THROWS,
|
|
8
8
|
DEFAULT_TIMELINE_MAX_RECORDS,
|
|
@@ -47,7 +47,7 @@ import {
|
|
|
47
47
|
visibleNames,
|
|
48
48
|
visibleTo,
|
|
49
49
|
writeHibernationBlob
|
|
50
|
-
} from "./chunk-
|
|
50
|
+
} from "./chunk-ADZZEVJU.js";
|
|
51
51
|
export {
|
|
52
52
|
CRASH_AFTER_THROWS,
|
|
53
53
|
DEFAULT_TIMELINE_MAX_RECORDS,
|
|
@@ -1,12 +1,8 @@
|
|
|
1
1
|
import { AttributeOptions, ProfileSnapshot } from '@irtio/protocol';
|
|
2
2
|
import { DirtySet, AnySchema, PlainState, Tracked, State } from '@irtio/schema';
|
|
3
3
|
import { RoomDefinition, RoomMode, Room, RewindView, Ctx, MatterModule, MatterEngine, MatterBody, RapierModule, RapierWorld, RapierRigidBody, Rapier2dModule, Rapier2dWorld, Rapier2dRigidBody, ResolvedRoomConfig, LeaveReason } from '@irtio/server';
|
|
4
|
-
import { i as RoomHost, k as RoomStats, L as LogLevel, R as RoomCoreApi, e as RoomCoreOptions, g as RoomEventKind, n as TimelineRecorderOptions, T as TimelineDump, j as RoomInspection, J as JoinOptions, d as JoinResult, c as HostCallResult } from './contract-
|
|
4
|
+
import { i as RoomHost, k as RoomStats, L as LogLevel, R as RoomCoreApi, e as RoomCoreOptions, g as RoomEventKind, n as TimelineRecorderOptions, T as TimelineDump, j as RoomInspection, J as JoinOptions, d as JoinResult, c as HostCallResult } from './contract-C5aqs49-.js';
|
|
5
5
|
|
|
6
|
-
/**
|
|
7
|
-
* `room.random()` — mulberry32 over a u32 seed. Deterministic, tiny, and serializable: the
|
|
8
|
-
* generator's whole state is one u32, so `serialize()`/`restore()` round-trip it exactly.
|
|
9
|
-
*/
|
|
10
6
|
declare class Mulberry32 {
|
|
11
7
|
/** Current internal state (u32). Survives hibernation. */
|
|
12
8
|
state: number;
|
|
@@ -139,6 +135,8 @@ interface RoomInternals {
|
|
|
139
135
|
readonly ext: AnySchema;
|
|
140
136
|
readonly host: RoomHost;
|
|
141
137
|
readonly roomId: string;
|
|
138
|
+
/** The resolved RNG seed: explicit option, else derived from the room id; restored on wake. */
|
|
139
|
+
readonly seed: number;
|
|
142
140
|
readonly mode: RoomMode;
|
|
143
141
|
/** The plain state the tracked proxies wrap (what the codec reads). */
|
|
144
142
|
readonly plain: PlainState;
|
|
@@ -346,6 +344,15 @@ declare class MatterRuntime {
|
|
|
346
344
|
private readonly restore;
|
|
347
345
|
private readonly stepMs;
|
|
348
346
|
private readonly sleepSynced;
|
|
347
|
+
/**
|
|
348
|
+
* The plain record object each body was built from. See `PhysicsRuntime.sourceRecords`: `add()`
|
|
349
|
+
* installs a new record object, so an id removed and re-added between two reconciles (a pooled
|
|
350
|
+
* projectile) is identifiable by identity, and is the one case where a live body has to be
|
|
351
|
+
* rebuilt so the new row's spawn pose and velocity are honoured.
|
|
352
|
+
*/
|
|
353
|
+
private readonly sourceRecords;
|
|
354
|
+
/** `collection.field` pairs already warned about a channel a plane cannot hold. */
|
|
355
|
+
private readonly warnedChannels;
|
|
349
356
|
constructor(core: RoomInternals, matter: MatterModule, options: MatterRuntimeOptions);
|
|
350
357
|
get timestep(): number;
|
|
351
358
|
runSetup(room: Room): void;
|
|
@@ -488,6 +495,19 @@ declare class PhysicsRuntime {
|
|
|
488
495
|
* world rebuilt from schema state would wake it with a kick). Found by the drift check.
|
|
489
496
|
*/
|
|
490
497
|
private readonly sleepSynced;
|
|
498
|
+
/**
|
|
499
|
+
* The plain record object each body was built from, by body key. `add()` always installs a
|
|
500
|
+
* **new** record object (`normalizeRecord` builds one), so an id that is removed and re-added
|
|
501
|
+
* between two reconciles — the pooled-projectile shape — is identifiable by identity alone,
|
|
502
|
+
* which is the only way to see it from here: both edits happened before this module looked.
|
|
503
|
+
* Without it the old body kept its pose and velocity and the new row's spawn values were lost.
|
|
504
|
+
*
|
|
505
|
+
* Unknown means adopt, never rebuild: a world restored from a hibernation blob has bodies with
|
|
506
|
+
* no recorded source, and rebuilding those would throw away exactly what the blob preserved.
|
|
507
|
+
*/
|
|
508
|
+
private readonly sourceRecords;
|
|
509
|
+
/** `collection.field` pairs already warned about a channel value the engine would not take. */
|
|
510
|
+
private readonly warnedChannels;
|
|
491
511
|
constructor(core: RoomInternals, rapier: RapierModule, options: PhysicsRuntimeOptions);
|
|
492
512
|
get timestep(): number;
|
|
493
513
|
/** Runs the room's `setup` — static geometry — on a world that was built rather than restored. */
|
|
@@ -496,6 +516,17 @@ declare class PhysicsRuntime {
|
|
|
496
516
|
/** The body behind an instance, created on demand so a handler's own `add` is usable at once. */
|
|
497
517
|
bodyFor(collection: string, id: string): RapierRigidBody | undefined;
|
|
498
518
|
private create;
|
|
519
|
+
/**
|
|
520
|
+
* A spawn value the engine will not take, said once per field.
|
|
521
|
+
*
|
|
522
|
+
* Rapier accepts `setLinvel` on an axis the body has disabled and then keeps zero, and it
|
|
523
|
+
* accepts one on a fixed body and then ignores it — both read back as "the field was applied"
|
|
524
|
+
* and behave as if it never was. The desc says which, before the body exists, so the check is
|
|
525
|
+
* on the desc: the usual case is the 2D-in-3D recipe (`enabledTranslations(true, true, false)`)
|
|
526
|
+
* meeting a row that spawns with a `vz`. Named by field, because the field is what the author
|
|
527
|
+
* wrote and what they will grep for.
|
|
528
|
+
*/
|
|
529
|
+
private warnLockedChannels;
|
|
499
530
|
/**
|
|
500
531
|
* D72: every tracked body, in the order `sync()` walks them (collection order, then instance
|
|
501
532
|
* order). Read-only: it hands out the live bodies and nothing else, and the map stays private.
|
|
@@ -585,6 +616,16 @@ declare class Rapier2dRuntime {
|
|
|
585
616
|
private readonly bodies;
|
|
586
617
|
/** See `PhysicsRuntime.sleepSynced`: the one extra sync a body owes on the tick it sleeps. */
|
|
587
618
|
private readonly sleepSynced;
|
|
619
|
+
/**
|
|
620
|
+
* See `PhysicsRuntime.sourceRecords`: the plain record object each body was built from. `add()`
|
|
621
|
+
* always installs a new record object, so an id removed and re-added between two reconciles —
|
|
622
|
+
* the pooled-projectile shape — is identifiable by identity alone, and its body is rebuilt so
|
|
623
|
+
* the new row's spawn pose and velocity are what the world gets. Unknown means adopt, never
|
|
624
|
+
* rebuild: a world restored from a hibernation blob has bodies with no recorded source.
|
|
625
|
+
*/
|
|
626
|
+
private readonly sourceRecords;
|
|
627
|
+
/** `collection.field` pairs already warned about a channel value the engine would not take. */
|
|
628
|
+
private readonly warnedChannels;
|
|
588
629
|
constructor(core: RoomInternals, rapier: Rapier2dModule, options: Rapier2dRuntimeOptions);
|
|
589
630
|
get timestep(): number;
|
|
590
631
|
/** Runs the room's `setup` — static geometry — on a world that was built rather than restored. */
|
|
@@ -593,6 +634,18 @@ declare class Rapier2dRuntime {
|
|
|
593
634
|
/** The body behind an instance, created on demand so a handler's own `add` is usable at once. */
|
|
594
635
|
bodyFor(collection: string, id: string): Rapier2dRigidBody | undefined;
|
|
595
636
|
private create;
|
|
637
|
+
/**
|
|
638
|
+
* A spawn value the engine will not take, said once per field — the planar half of
|
|
639
|
+
* `PhysicsRuntime.warnLockedChannels`.
|
|
640
|
+
*
|
|
641
|
+
* Rapier accepts `setLinvel` on an axis the body has disabled and then keeps zero, and it
|
|
642
|
+
* accepts one on a fixed body and then ignores it: both read back as "the field was applied"
|
|
643
|
+
* and behave as if it never was. The desc says which, before the body exists, so the check is on
|
|
644
|
+
* the desc. In the plane there are three movable channels — `vx`, `vy` and the one rotation
|
|
645
|
+
* `wz` — and the desc carries exactly those three flags. Named by field, because the field is
|
|
646
|
+
* what the author wrote and what they will grep for.
|
|
647
|
+
*/
|
|
648
|
+
private warnLockedChannels;
|
|
596
649
|
/**
|
|
597
650
|
* D72: every tracked body, in the order `sync()` walks them (collection order, then instance
|
|
598
651
|
* order). The rapier2d third of the same read-only accessor the other two runtimes carry.
|
|
@@ -668,7 +721,7 @@ declare class RoomCore<S extends AnySchema = AnySchema> implements RoomCoreApi<S
|
|
|
668
721
|
* it again over its next `history` ticks.
|
|
669
722
|
*/
|
|
670
723
|
private readonly rewindState;
|
|
671
|
-
|
|
724
|
+
readonly seed: number;
|
|
672
725
|
private readonly api;
|
|
673
726
|
private readonly internals;
|
|
674
727
|
private started;
|
package/dist/test/index.d.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { R as RoomCore } from '../room-
|
|
2
|
-
export { m as initMatter, n as initPhysics, o as initRapier2d } from '../room-
|
|
1
|
+
import { R as RoomCore } from '../room-C4RZJ2KO.js';
|
|
2
|
+
export { m as initMatter, n as initPhysics, o as initRapier2d } from '../room-C4RZJ2KO.js';
|
|
3
3
|
import { ErrorCodeName, FrameType } from '@irtio/protocol';
|
|
4
4
|
import { NpcConfig, LeaveReason, Room, RoomDefinition } from '@irtio/server';
|
|
5
|
-
import { i as RoomHost, L as LogLevel, R as RoomCoreApi, b as HostCall, c as HostCallResult, k as RoomStats } from '../contract-
|
|
5
|
+
import { i as RoomHost, L as LogLevel, R as RoomCoreApi, b as HostCall, c as HostCallResult, k as RoomStats } from '../contract-C5aqs49-.js';
|
|
6
6
|
import { AnySchema, PlainState, EntityCollection, State } from '@irtio/schema';
|
|
7
7
|
|
|
8
8
|
/**
|
|
@@ -331,7 +331,7 @@ interface FakeClient<S extends AnySchema = AnySchema> {
|
|
|
331
331
|
*/
|
|
332
332
|
|
|
333
333
|
interface HarnessOptions {
|
|
334
|
-
/** `room.random()` seed;
|
|
334
|
+
/** `room.random()` seed; defaults to a hash of `roomId`, exactly as `RoomCore` does. */
|
|
335
335
|
readonly seed?: number;
|
|
336
336
|
/** Base URL for `room.link`. */
|
|
337
337
|
readonly publicUrl?: string;
|
package/dist/test/index.js
CHANGED
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
initPhysics,
|
|
6
6
|
initRapier2d,
|
|
7
7
|
visibleNames
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-ADZZEVJU.js";
|
|
9
9
|
|
|
10
10
|
// src/test/clock.ts
|
|
11
11
|
var FakeClock = class {
|
|
@@ -677,7 +677,7 @@ var Harness = class {
|
|
|
677
677
|
this.host.onSend = (clientId, frame) => this.deliver(clientId, frame);
|
|
678
678
|
this.coreRef = new RoomCore(definition, this.host, {
|
|
679
679
|
roomId: options.roomId ?? "test-room",
|
|
680
|
-
seed: options.seed
|
|
680
|
+
...options.seed !== void 0 ? { seed: options.seed } : {},
|
|
681
681
|
...options.publicUrl !== void 0 ? { publicUrl: options.publicUrl } : {},
|
|
682
682
|
...options.restoreFrom !== void 0 ? { restoreFrom: options.restoreFrom } : {}
|
|
683
683
|
});
|
|
@@ -963,7 +963,7 @@ var Harness = class {
|
|
|
963
963
|
this.nextClientId = 1;
|
|
964
964
|
this.coreRef = RoomCore.restore(this.definition, bytes, this.host, {
|
|
965
965
|
roomId: this.options.roomId ?? "test-room",
|
|
966
|
-
seed: this.options.seed
|
|
966
|
+
...this.options.seed !== void 0 ? { seed: this.options.seed } : {},
|
|
967
967
|
...this.options.publicUrl !== void 0 ? { publicUrl: this.options.publicUrl } : {}
|
|
968
968
|
});
|
|
969
969
|
this.coreRef.start();
|
package/dist/worker/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { L as LogLevel, T as TimelineDump, k as RoomStats, b as HostCall, c as HostCallResult, R as RoomCoreApi, i as RoomHost } from '../contract-
|
|
1
|
+
import { L as LogLevel, T as TimelineDump, k as RoomStats, b as HostCall, c as HostCallResult, R as RoomCoreApi, i as RoomHost } from '../contract-C5aqs49-.js';
|
|
2
2
|
import { ErrorCodeName, ProfileSnapshot } from '@irtio/protocol';
|
|
3
3
|
import { NpcConfig, LeaveReason } from '@irtio/server';
|
|
4
4
|
import '@irtio/schema';
|
package/dist/worker/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
migrateSnapshot
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-SBDZ45JG.js";
|
|
4
4
|
import {
|
|
5
5
|
RoomCore,
|
|
6
6
|
RoomFullError,
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
initRapier2d,
|
|
10
10
|
onFirstRapierStep,
|
|
11
11
|
rapierHasStepped
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-ADZZEVJU.js";
|
|
13
13
|
|
|
14
14
|
// src/worker/index.ts
|
|
15
15
|
import { getHeapStatistics } from "v8";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@irtio/runtime",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "irtio room runtime: RoomCore, worker_threads host, in-process test harness",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"publishConfig": {
|
|
@@ -31,9 +31,9 @@
|
|
|
31
31
|
"@dimforge/rapier2d-compat": "0.20.0",
|
|
32
32
|
"@dimforge/rapier3d-compat": "0.20.0",
|
|
33
33
|
"matter-js": "0.20.0",
|
|
34
|
-
"@irtio/
|
|
35
|
-
"@irtio/
|
|
36
|
-
"@irtio/
|
|
34
|
+
"@irtio/schema": "0.9.0",
|
|
35
|
+
"@irtio/server": "0.9.0",
|
|
36
|
+
"@irtio/protocol": "0.9.0"
|
|
37
37
|
},
|
|
38
38
|
"devDependencies": {
|
|
39
39
|
"@types/matter-js": "0.20.2"
|