@irtio/client 0.7.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/index.js CHANGED
@@ -8,7 +8,7 @@ import {
8
8
  SMOOTHING_HALF_LIFE_MS,
9
9
  SMOOTHING_SNAP_UNITS,
10
10
  emptyPredictionStats
11
- } from "./chunk-XWVXZRBS.js";
11
+ } from "./chunk-5OHONPVG.js";
12
12
 
13
13
  // src/index.ts
14
14
  import { EMPTY_PROFILE as EMPTY_PROFILE2 } from "@irtio/protocol";
@@ -1065,6 +1065,7 @@ var DEFAULT_WRITE_INTERVAL_MS = 50;
1065
1065
  var PING_INTERVAL_MS = 2e3;
1066
1066
  var RTT_ALPHA = 0.3;
1067
1067
  var CALL_TIMEOUT_MS = 1e4;
1068
+ var LEAVE_CLOSE_TIMEOUT_MS = 1e3;
1068
1069
  var BACKOFF_START_MS = 250;
1069
1070
  var BACKOFF_MAX_MS = 5e3;
1070
1071
  function isThenable(v) {
@@ -1117,7 +1118,7 @@ var Session = class {
1117
1118
  const physics2d = options.physics2d;
1118
1119
  if (physics && hasPhysics) {
1119
1120
  this.predictionRequested = true;
1120
- void import("./physics-RT5T36P5.js").then(({ PhysicsPredictor }) => {
1121
+ void import("./physics-D65WIHPR.js").then(({ PhysicsPredictor }) => {
1121
1122
  if (this.left) return;
1122
1123
  attach(
1123
1124
  new PhysicsPredictor(
@@ -1130,15 +1131,32 @@ var Session = class {
1130
1131
  )
1131
1132
  );
1132
1133
  });
1133
- } else if (physics2d && hasPhysics) {
1134
+ } else if (physics2d !== void 0 && physics2d.engine === "rapier2d" && hasPhysics) {
1135
+ const rapier2d = physics2d;
1134
1136
  this.predictionRequested = true;
1135
- void import("./physics2d-HOFMWPZV.js").then(({ Physics2dPredictor }) => {
1137
+ void import("./physics-rapier2d-O53E6BHO.js").then(({ Rapier2dPredictor }) => {
1138
+ if (this.left) return;
1139
+ attach(
1140
+ new Rapier2dPredictor(
1141
+ this.ext,
1142
+ this.store,
1143
+ rapier2d,
1144
+ () => this.me,
1145
+ () => this.rtt,
1146
+ () => this.tickIntervalMs
1147
+ )
1148
+ );
1149
+ });
1150
+ } else if (physics2d !== void 0 && physics2d.engine !== "rapier2d" && hasPhysics) {
1151
+ const matter2d = physics2d;
1152
+ this.predictionRequested = true;
1153
+ void import("./physics2d-GIL6YYAY.js").then(({ Physics2dPredictor }) => {
1136
1154
  if (this.left) return;
1137
1155
  attach(
1138
1156
  new Physics2dPredictor(
1139
1157
  this.ext,
1140
1158
  this.store,
1141
- physics2d,
1159
+ matter2d,
1142
1160
  () => this.me,
1143
1161
  () => this.rtt,
1144
1162
  () => this.tickIntervalMs
@@ -1362,18 +1380,45 @@ var Session = class {
1362
1380
  });
1363
1381
  this.send(FrameType.HELLO, hello);
1364
1382
  }
1365
- /** Leaves for good: no reconnect, pending calls reject, the socket closes. */
1383
+ /**
1384
+ * Leaves for good: no reconnect, pending calls reject, the socket closes.
1385
+ *
1386
+ * The promise resolves when the socket reports its close (or immediately when there was
1387
+ * nothing open), so `await room.leave()` is a real wait rather than a guessed timeout. A
1388
+ * transport that never reports one is bounded by `LEAVE_CLOSE_TIMEOUT_MS` so the caller cannot
1389
+ * hang on it.
1390
+ */
1366
1391
  leave() {
1367
- if (this.left) return;
1392
+ if (this.left) return Promise.resolve();
1368
1393
  this.left = true;
1369
1394
  this.stopTimers();
1370
1395
  this.predictor?.free();
1371
1396
  this.rejectPending(new Error("irtio: left the room"));
1372
1397
  this.setStatus("closed");
1373
1398
  if (this.socket && this.joined) this.send(FrameType.LEAVE, new Uint8Array(0));
1374
- this.socket?.close();
1399
+ const socket = this.socket;
1375
1400
  this.socket = void 0;
1376
1401
  this.socketOpen = false;
1402
+ if (!socket) {
1403
+ return Promise.resolve();
1404
+ }
1405
+ return new Promise((resolve) => {
1406
+ let settled = false;
1407
+ let cancelTimeout;
1408
+ const finish = () => {
1409
+ if (settled) return;
1410
+ settled = true;
1411
+ cancelTimeout?.();
1412
+ resolve();
1413
+ };
1414
+ const previous = socket.onclose;
1415
+ socket.onclose = (info) => {
1416
+ previous?.(info);
1417
+ finish();
1418
+ };
1419
+ socket.close();
1420
+ if (!settled) cancelTimeout = this.scheduler.setTimeout(finish, LEAVE_CLOSE_TIMEOUT_MS);
1421
+ });
1377
1422
  }
1378
1423
  fatal(error) {
1379
1424
  this.left = true;
@@ -2191,6 +2236,233 @@ function isRoomFull(err) {
2191
2236
  const message = err instanceof Error ? err.message : String(err);
2192
2237
  return message.startsWith("E_ROOM_FULL");
2193
2238
  }
2239
+ async function findPublic(project, options = {}) {
2240
+ const controlUrl = (options.controlUrl ?? DEFAULT_CONTROL_URL).replace(/\/+$/, "");
2241
+ const fetchImpl = options.fetch ?? fetch;
2242
+ let res;
2243
+ try {
2244
+ res = await fetchImpl(`${controlUrl}/match`, {
2245
+ method: "POST",
2246
+ headers: { "content-type": "application/json" },
2247
+ body: JSON.stringify({
2248
+ project,
2249
+ mode: "public",
2250
+ ...options.queue !== void 0 ? { queue: options.queue } : {},
2251
+ ...options.identity !== void 0 ? { identity: options.identity } : {},
2252
+ ...options.exclude !== void 0 ? { exclude: options.exclude } : {}
2253
+ })
2254
+ });
2255
+ } catch (err) {
2256
+ throw new MatchError(
2257
+ "E_MATCH_UNREACHABLE",
2258
+ `cannot reach the matchmaker at ${controlUrl}: ${err instanceof Error ? err.message : String(err)}`
2259
+ );
2260
+ }
2261
+ const text = await res.text().catch(() => "");
2262
+ let body = {};
2263
+ try {
2264
+ body = JSON.parse(text);
2265
+ } catch {
2266
+ }
2267
+ if (!res.ok) {
2268
+ throw new MatchError(
2269
+ typeof body.code === "string" ? body.code : `E_HTTP_${res.status}`,
2270
+ typeof body.message === "string" ? body.message : `match failed with status ${res.status}`
2271
+ );
2272
+ }
2273
+ if (body.status !== "matched" || typeof body.room !== "string") {
2274
+ throw new MatchError("E_MATCH_MALFORMED", "the matchmaker answered something unexpected");
2275
+ }
2276
+ return {
2277
+ room: body.room,
2278
+ queue: typeof body.queue === "string" ? body.queue : "default",
2279
+ size: typeof body.size === "number" ? body.size : 0,
2280
+ created: body.created === true
2281
+ };
2282
+ }
2283
+ async function joinPublic(schema, options = {}) {
2284
+ const project = schema.project;
2285
+ const key = options.key ?? (typeof project === "string" ? project : void 0);
2286
+ if (key === void 0 || key === "") {
2287
+ throw new MatchError(
2288
+ "E_MATCH_NO_PROJECT",
2289
+ "joinPublic needs a project key: build your schema with `irtio` so it carries one, or pass { key }"
2290
+ );
2291
+ }
2292
+ const identity = options.identity === true ? new Identity({
2293
+ project: key,
2294
+ controlUrl: options.controlUrl,
2295
+ fetch: options.fetch
2296
+ }) : void 0;
2297
+ const ask = async (exclude) => findPublic(key, {
2298
+ ...options.queue !== void 0 ? { queue: options.queue } : {},
2299
+ ...options.controlUrl !== void 0 ? { controlUrl: options.controlUrl } : {},
2300
+ ...options.fetch !== void 0 ? { fetch: options.fetch } : {},
2301
+ ...identity !== void 0 ? { identity: await identity.ensure() } : {},
2302
+ ...exclude !== void 0 ? { exclude } : {}
2303
+ });
2304
+ const ticket = await ask();
2305
+ try {
2306
+ return await joinRoom(schema, { ...options, room: ticket.room });
2307
+ } catch (err) {
2308
+ if (!isRetryablePublicJoin(err)) throw err;
2309
+ const second = await ask(ticket.room);
2310
+ return joinRoom(schema, { ...options, room: second.room });
2311
+ }
2312
+ }
2313
+ function isRetryablePublicJoin(err) {
2314
+ const message = err instanceof Error ? err.message : String(err);
2315
+ return message.startsWith("E_ROOM_FULL") || message.startsWith("E_ROOM_NOT_FOUND");
2316
+ }
2317
+
2318
+ // src/lobby.ts
2319
+ var LOBBY_STATE = "irtLobby";
2320
+ var LOBBY_MEMBERS = "irtLobbyMembers";
2321
+ var LOBBY_POLL_MS = 120;
2322
+ function hasLobby(room) {
2323
+ const state = room?.state;
2324
+ return typeof state === "object" && state !== null && state[LOBBY_STATE] !== void 0 && state[LOBBY_MEMBERS] !== void 0;
2325
+ }
2326
+ function lobbyOf(room, options = {}) {
2327
+ if (!hasLobby(room)) return void 0;
2328
+ const r = room;
2329
+ const singleton = () => r.state[LOBBY_STATE] ?? {};
2330
+ const members = () => r.state[LOBBY_MEMBERS];
2331
+ const players = () => r.clients.map((c) => ({
2332
+ clientId: c.clientId,
2333
+ ready: members().get(c.clientId)?.ready === true,
2334
+ connected: c.connected !== false,
2335
+ me: c.clientId === r.me
2336
+ }));
2337
+ const signature = () => {
2338
+ const s = singleton();
2339
+ let out = `${s.phase ?? ""}|${s.public === true ? 1 : 0}|${s.readyUi === true ? 1 : 0}|${s.capacity ?? 0}`;
2340
+ for (const p of players()) out += `|${p.clientId}${p.ready ? 1 : 0}${p.connected ? 1 : 0}`;
2341
+ return out;
2342
+ };
2343
+ return {
2344
+ get phase() {
2345
+ return singleton().phase === "started" ? "started" : "lobby";
2346
+ },
2347
+ get players() {
2348
+ return players();
2349
+ },
2350
+ get public() {
2351
+ return singleton().public === true;
2352
+ },
2353
+ get readyUi() {
2354
+ return singleton().readyUi === true;
2355
+ },
2356
+ get capacity() {
2357
+ return singleton().capacity ?? 0;
2358
+ },
2359
+ ready(value) {
2360
+ const mine = members().get(r.me);
2361
+ if (!mine) return;
2362
+ mine.ready = value === true;
2363
+ r.flush();
2364
+ },
2365
+ ...options.setPublic !== void 0 ? { setPublic: options.setPublic } : {},
2366
+ on(event, cb) {
2367
+ if (event !== "change") return () => {
2368
+ };
2369
+ let last = signature();
2370
+ const sample = () => {
2371
+ const next = signature();
2372
+ if (next === last) return;
2373
+ last = next;
2374
+ cb();
2375
+ };
2376
+ const offClients = r.on("clients", sample);
2377
+ const timer = setInterval(sample, LOBBY_POLL_MS);
2378
+ timer.unref?.();
2379
+ return () => {
2380
+ offClients();
2381
+ clearInterval(timer);
2382
+ };
2383
+ }
2384
+ };
2385
+ }
2386
+ function attachable(room, view) {
2387
+ return {
2388
+ get id() {
2389
+ return room.id;
2390
+ },
2391
+ get link() {
2392
+ return room.link;
2393
+ },
2394
+ get status() {
2395
+ return room.status;
2396
+ },
2397
+ get clients() {
2398
+ return room.clients;
2399
+ },
2400
+ get maxClients() {
2401
+ return room.maxClients;
2402
+ },
2403
+ get rtt() {
2404
+ return room.rtt;
2405
+ },
2406
+ on: (event, cb) => room.on(event, cb),
2407
+ ...view !== void 0 ? { lobby: view } : {}
2408
+ };
2409
+ }
2410
+ function attachLobby(element, schema, options = {}) {
2411
+ let detachRoom;
2412
+ let busy = false;
2413
+ const fail = (err) => {
2414
+ element.removeAttribute("searching");
2415
+ if (options.onError) {
2416
+ options.onError(err);
2417
+ return;
2418
+ }
2419
+ queueMicrotask(() => {
2420
+ throw err;
2421
+ });
2422
+ };
2423
+ const landed = (room) => {
2424
+ element.removeAttribute("searching");
2425
+ const view = lobbyOf(room, {
2426
+ ...options.setPublic !== void 0 ? { setPublic: (value) => options.setPublic?.(room, value) } : {}
2427
+ });
2428
+ detachRoom = element.attach(
2429
+ attachable(room, view)
2430
+ );
2431
+ options.onRoom?.(room);
2432
+ };
2433
+ const run = async (make) => {
2434
+ if (busy) return;
2435
+ busy = true;
2436
+ try {
2437
+ landed(await make());
2438
+ } catch (err) {
2439
+ fail(err);
2440
+ } finally {
2441
+ busy = false;
2442
+ }
2443
+ };
2444
+ const onPrivate = () => {
2445
+ void run(() => joinRoom(schema, { ...options.join ?? {} }));
2446
+ };
2447
+ const onQuickMatch = (event) => {
2448
+ const detail = event.detail;
2449
+ element.setAttribute("searching", "");
2450
+ void run(
2451
+ () => joinPublic(schema, {
2452
+ ...options.join ?? {},
2453
+ ...options.match ?? {},
2454
+ ...detail?.queue !== void 0 ? { queue: detail.queue } : {}
2455
+ })
2456
+ );
2457
+ };
2458
+ element.addEventListener("private", onPrivate);
2459
+ element.addEventListener("quickmatch", onQuickMatch);
2460
+ return () => {
2461
+ element.removeEventListener("private", onPrivate);
2462
+ element.removeEventListener("quickmatch", onQuickMatch);
2463
+ detachRoom?.();
2464
+ };
2465
+ }
2194
2466
 
2195
2467
  // src/voice.ts
2196
2468
  import {
@@ -2621,6 +2893,8 @@ function makeRoom(session) {
2621
2893
  messages,
2622
2894
  stats,
2623
2895
  on: (event, cb) => session.on(event, cb),
2896
+ onAdd: ((collection, cb) => session.store.onEntityAdd(collection, cb)),
2897
+ onRemove: ((collection, cb) => session.store.onEntityRemove(collection, cb)),
2624
2898
  flush: () => session.flush(),
2625
2899
  leave: () => session.leave()
2626
2900
  };
@@ -2693,6 +2967,9 @@ export {
2693
2967
  INTERNAL_VOICE_TRACKS,
2694
2968
  Identity,
2695
2969
  IdentityError,
2970
+ LOBBY_MEMBERS,
2971
+ LOBBY_POLL_MS,
2972
+ LOBBY_STATE,
2696
2973
  MAX_IDENTITY_RETRY_WAIT_MS,
2697
2974
  MAX_PREDICTED_BODIES,
2698
2975
  MAX_PROXY_BODIES,
@@ -2704,14 +2981,20 @@ export {
2704
2981
  SMOOTHING_HALF_LIFE_MS,
2705
2982
  SMOOTHING_SNAP_UNITS,
2706
2983
  Session,
2984
+ attachLobby,
2985
+ attachable,
2707
2986
  createParty,
2708
2987
  defaultScheduler,
2709
2988
  findMatch,
2989
+ findPublic,
2990
+ hasLobby,
2710
2991
  identityStorageKey,
2992
+ joinPublic,
2711
2993
  joinRelay,
2712
2994
  joinRoom,
2713
2995
  joinVoice,
2714
2996
  linkForUrl,
2997
+ lobbyOf,
2715
2998
  matchRoom,
2716
2999
  resolveUrl,
2717
3000
  roomIdFrom,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Predictor
3
- } from "./chunk-DABQDR3S.js";
4
- import "./chunk-XWVXZRBS.js";
3
+ } from "./chunk-VZFJXPVF.js";
4
+ import "./chunk-5OHONPVG.js";
5
5
 
6
6
  // src/physics.ts
7
7
  function readPose(body, into) {
@@ -0,0 +1,186 @@
1
+ import {
2
+ Predictor
3
+ } from "./chunk-VZFJXPVF.js";
4
+ import "./chunk-5OHONPVG.js";
5
+
6
+ // src/physics-rapier2d.ts
7
+ import {
8
+ angleFrom2d,
9
+ applyChannel2d,
10
+ channelOf2d
11
+ } from "@irtio/schema";
12
+ var engine;
13
+ var loading;
14
+ async function loadEngine() {
15
+ if (engine) return engine;
16
+ loading ??= (async () => {
17
+ const mod = await import("@dimforge/rapier2d-compat");
18
+ const ns = mod.default ?? mod;
19
+ await ns.init();
20
+ engine = ns;
21
+ return ns;
22
+ })();
23
+ return loading;
24
+ }
25
+ var Rapier2dAdapter = class {
26
+ constructor(options) {
27
+ this.options = options;
28
+ }
29
+ options;
30
+ engineName = "@dimforge/rapier2d-compat";
31
+ optionName = "physics2d";
32
+ rapier;
33
+ world;
34
+ timestepSeconds = 1 / 30;
35
+ /** Reused by `readPose`, which runs per body per step and must not allocate. */
36
+ state = {
37
+ x: 0,
38
+ y: 0,
39
+ angle: 0,
40
+ vx: 0,
41
+ vy: 0,
42
+ angularVelocity: 0
43
+ };
44
+ /** `applyChannel2d`'s target shape, likewise reused. */
45
+ channels = { x: 0, y: 0, qz: 0, qw: 1, vx: 0, vy: 0, wz: 0 };
46
+ /** `moveKinematic`'s translation argument, reused: it runs per proxy per step (D71). */
47
+ moveTarget = { x: 0, y: 0 };
48
+ get timestep() {
49
+ return this.options.timestep;
50
+ }
51
+ async start(timestepSeconds) {
52
+ const rapier = await loadEngine();
53
+ this.rapier = rapier;
54
+ this.timestepSeconds = timestepSeconds;
55
+ const world = new rapier.World({ x: this.options.gravity.x, y: this.options.gravity.y });
56
+ world.timestep = timestepSeconds;
57
+ if (this.options.setup) this.options.setup(world, rapier);
58
+ this.world = world;
59
+ }
60
+ free() {
61
+ this.world?.free();
62
+ this.world = void 0;
63
+ }
64
+ hasFactory(collection) {
65
+ return this.options.bodies?.[collection] !== void 0;
66
+ }
67
+ createBody(desc, id, record, _warn) {
68
+ const world = this.world;
69
+ const rapier = this.rapier;
70
+ const factory = this.options.bodies?.[desc.name];
71
+ if (!world || !rapier || !factory) return void 0;
72
+ const spec = factory(rapier, record, id);
73
+ if (!spec || !spec.body) return void 0;
74
+ const body = world.createRigidBody(spec.body);
75
+ for (const collider of spec.colliders ?? []) world.createCollider(collider, body);
76
+ return body;
77
+ }
78
+ removeBody(body) {
79
+ this.world?.removeRigidBody(body);
80
+ }
81
+ /**
82
+ * D71: a proxy is a `KinematicPositionBased` body, exactly as in 3D — driven by the position it
83
+ * is told to be at next, never by a force, an impulse or a contact, with Rapier deriving the
84
+ * velocity its contacts see from the move itself. The body is *switched* rather than built
85
+ * kinematic, so its collider and mass properties are the ones the shared factory produced.
86
+ */
87
+ makeKinematic(handle) {
88
+ const rapier = this.rapier;
89
+ if (!rapier) return;
90
+ handle.setBodyType(rapier.RigidBodyType.KinematicPositionBased, true);
91
+ }
92
+ moveKinematic(handle, pose) {
93
+ const body = handle;
94
+ const to = this.moveTarget;
95
+ to.x = pose.t.x;
96
+ to.y = pose.t.y;
97
+ body.setNextKinematicTranslation(to);
98
+ body.setNextKinematicRotation(angleFrom2d(pose.r.z, pose.r.w));
99
+ }
100
+ step() {
101
+ this.world?.step();
102
+ }
103
+ /**
104
+ * Server record → body state, through `applyChannel2d` so the plane-to-channel mapping stays
105
+ * written once, and in `Rapier2dRuntime.applyRecordToBody`'s order — translation, rotation,
106
+ * linear velocity, spin — so the two sides write the same body the same way. Every setter takes
107
+ * `wakeUp = true`: a rebase writes state a sleeping body would otherwise ignore.
108
+ */
109
+ applyRecord(handle, channels, record) {
110
+ const body = handle;
111
+ const t = this.channels;
112
+ const pos = body.translation();
113
+ const vel = body.linvel();
114
+ const angle = body.rotation();
115
+ t.x = pos.x;
116
+ t.y = pos.y;
117
+ t.qz = Math.sin(angle / 2);
118
+ t.qw = Math.cos(angle / 2);
119
+ t.vx = vel.x;
120
+ t.vy = vel.y;
121
+ t.wz = body.angvel();
122
+ for (const [channel, field] of channels) {
123
+ const raw = record[field];
124
+ if (typeof raw !== "number") continue;
125
+ applyChannel2d(channel, raw, t);
126
+ }
127
+ body.setTranslation({ x: t.x, y: t.y }, true);
128
+ body.setRotation(angleFrom2d(t.qz, t.qw), true);
129
+ body.setLinvel({ x: t.vx, y: t.vy }, true);
130
+ body.setAngvel(t.wz, true);
131
+ }
132
+ /**
133
+ * Body state → a 3D pose, through `channelOf2d`: `t = (x, y, 0)`, `r` a quaternion about Z,
134
+ * `v = (vx, vy, 0)`, `w = (0, 0, spin)`.
135
+ */
136
+ readPose(handle, into) {
137
+ const body = handle;
138
+ const s = this.state;
139
+ const pos = body.translation();
140
+ const vel = body.linvel();
141
+ s.x = pos.x;
142
+ s.y = pos.y;
143
+ s.angle = body.rotation();
144
+ s.vx = vel.x;
145
+ s.vy = vel.y;
146
+ s.angularVelocity = body.angvel();
147
+ const b = s;
148
+ into.t.x = channelOf2d("x", b);
149
+ into.t.y = channelOf2d("y", b);
150
+ into.t.z = channelOf2d("z", b);
151
+ into.r.x = channelOf2d("qx", b);
152
+ into.r.y = channelOf2d("qy", b);
153
+ into.r.z = channelOf2d("qz", b);
154
+ into.r.w = channelOf2d("qw", b);
155
+ into.v.x = channelOf2d("vx", b);
156
+ into.v.y = channelOf2d("vy", b);
157
+ into.v.z = channelOf2d("vz", b);
158
+ into.w.x = channelOf2d("wx", b);
159
+ into.w.y = channelOf2d("wy", b);
160
+ into.w.z = channelOf2d("wz", b);
161
+ }
162
+ applyIntent(collection, body, instance) {
163
+ const hook = this.options.intents?.[collection];
164
+ const world = this.world;
165
+ const rapier = this.rapier;
166
+ if (!hook || !world || !rapier) return;
167
+ hook(body, instance, rapier, world, this.timestepSeconds);
168
+ }
169
+ // No `settle`. The world applies gravity; see the module docblock.
170
+ /** Rapier velocities are per second, so one tick of error is `epsilon` when `dv = eps / dt`. */
171
+ velocityTolerance(epsilon, timestepSeconds) {
172
+ return epsilon / timestepSeconds;
173
+ }
174
+ };
175
+ var Rapier2dPredictor = class extends Predictor {
176
+ constructor(ext, store, options, meOf, rttOf, tickIntervalOf, log = (m) => console.warn(m)) {
177
+ super(ext, store, new Rapier2dAdapter(options), options, meOf, rttOf, tickIntervalOf, log);
178
+ }
179
+ };
180
+ function createRapier2dAdapter(options) {
181
+ return new Rapier2dAdapter(options);
182
+ }
183
+ export {
184
+ Rapier2dPredictor,
185
+ createRapier2dAdapter
186
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  Predictor
3
- } from "./chunk-DABQDR3S.js";
4
- import "./chunk-XWVXZRBS.js";
3
+ } from "./chunk-VZFJXPVF.js";
4
+ import "./chunk-5OHONPVG.js";
5
5
 
6
6
  // src/physics2d.ts
7
7
  import {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@irtio/client",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "irtio client SDK: joinRoom, owned-write batching, corrections, typed RPCs, presence, reconnection",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -20,14 +20,18 @@
20
20
  ],
21
21
  "dependencies": {
22
22
  "mediasoup-client": "^3.23.1",
23
- "@irtio/protocol": "0.7.0",
24
- "@irtio/schema": "0.7.0"
23
+ "@irtio/protocol": "0.9.0",
24
+ "@irtio/schema": "0.9.0"
25
25
  },
26
26
  "peerDependencies": {
27
+ "@dimforge/rapier2d-compat": ">=0.20.0",
27
28
  "@dimforge/rapier3d-compat": ">=0.20.0",
28
29
  "matter-js": ">=0.20.0"
29
30
  },
30
31
  "peerDependenciesMeta": {
32
+ "@dimforge/rapier2d-compat": {
33
+ "optional": true
34
+ },
31
35
  "@dimforge/rapier3d-compat": {
32
36
  "optional": true
33
37
  },
@@ -39,6 +43,7 @@
39
43
  "@dimforge/rapier3d-compat": "0.20.0",
40
44
  "@types/matter-js": "0.20.2",
41
45
  "matter-js": "0.20.0",
46
+ "@dimforge/rapier2d-compat": "0.20.0",
42
47
  "@irtio/sfu": "0.0.0"
43
48
  },
44
49
  "scripts": {