@irtio/server 0.9.0 → 0.10.1

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.d.ts CHANGED
@@ -441,10 +441,41 @@ interface RapierPhysicsConfig<S extends AnySchema> {
441
441
  setup?(world: RapierWorld, rapier: RapierModule, room: Room<S>): void;
442
442
  readonly bodies: BodyFactories<S>;
443
443
  /** D72: how many ticks of body poses to keep for `room.rewind`. See {@link HISTORY_DOC}. */
444
- readonly history?: number;
444
+ readonly history?: HistoryConfig;
445
445
  }
446
+ /**
447
+ * D72+: one physics collection's declared history channels.
448
+ *
449
+ * Method syntax for `read`, so a hook written against its own instance type is accepted: the
450
+ * values really passed are the schema's records for that collection. Same precedent as
451
+ * {@link Matter2dIntentHook}.
452
+ */
453
+ interface HistoryChannelSpec {
454
+ /** How many f32s per entity per tick, 1..{@link HISTORY_MAX_CHANNELS}. */
455
+ readonly count: number;
456
+ /**
457
+ * Fills `out` (length `count`, pre-zeroed) from the instance's record, once per body per tick.
458
+ * `instance` is a read-only view: writes through it do not dirty synced state.
459
+ */
460
+ read(instance: Record<string, unknown>, out: Float32Array): void;
461
+ }
462
+ /**
463
+ * D72: the `physics.history` declaration. A bare number is a depth in ticks and means exactly what
464
+ * it has always meant; the object form adds per-collection channels on the same ring and the same
465
+ * lifecycle. Keys of `channels` name physics collections.
466
+ */
467
+ type HistoryConfig = number | {
468
+ readonly depth: number;
469
+ readonly channels?: Readonly<Record<string, HistoryChannelSpec>>;
470
+ };
446
471
  /** D72: the deepest history a room may declare, in ticks. One second at the maximum tick rate. */
447
472
  declare const HISTORY_MAX_TICKS = 240;
473
+ /**
474
+ * D72+: the most history channels one physics collection may declare. Enough for a crouch phase, a
475
+ * run phase and a few bone parameters; a room wanting a full skeleton per tick should keep its own
476
+ * ring rather than push this up.
477
+ */
478
+ declare const HISTORY_MAX_CHANNELS = 8;
448
479
  /**
449
480
  * D72: what `room.rewind(tick, fn)` hands `fn`.
450
481
  *
@@ -466,6 +497,14 @@ interface RewindView {
466
497
  * remember", and a room that would rather refuse a clamped answer can read this and do so.
467
498
  */
468
499
  readonly clamped: boolean;
500
+ /**
501
+ * D72+: the declared history channels for one entity at the answered tick, in declaration order,
502
+ * length `count`. `undefined` when the collection declares no channels or when the entity was not
503
+ * present at that tick.
504
+ *
505
+ * The array is a view into the ring: read it inside `fn`, do not hold it and do not write to it.
506
+ */
507
+ channels(collection: string, id: string): Float32Array | undefined;
469
508
  /** rapier3d rooms: a scratch world to `castRay` / `castShape` / `intersectionsWithShape` on. */
470
509
  readonly rapier?: {
471
510
  readonly world: RapierWorld;
@@ -546,7 +585,7 @@ interface Matter2dPhysicsConfig<S extends AnySchema> {
546
585
  */
547
586
  readonly intents?: Readonly<Partial<Record<PhysicsKeys<S> & string, Matter2dIntentHook>>>;
548
587
  /** D72: how many ticks of body poses to keep for `room.rewind`. See {@link HISTORY_DOC}. */
549
- readonly history?: number;
588
+ readonly history?: HistoryConfig;
550
589
  }
551
590
  /**
552
591
  * The rapier2d counterpart to {@link Matter2dIntentHook}: one step of steering for one body, from
@@ -595,7 +634,7 @@ interface Rapier2dPhysicsConfig<S extends AnySchema> {
595
634
  */
596
635
  readonly intents?: Readonly<Partial<Record<PhysicsKeys<S> & string, Rapier2dIntentHook>>>;
597
636
  /** D72: how many ticks of body poses to keep for `room.rewind`. See {@link HISTORY_DOC}. */
598
- readonly history?: number;
637
+ readonly history?: HistoryConfig;
599
638
  }
600
639
  /** `room.physicsRapier2d` in a rapier2d room. */
601
640
  interface Rapier2dRoomApi<S extends AnySchema> {
@@ -1421,4 +1460,4 @@ declare function defineRoom<S extends AnySchema>(schema: S, config: RoomConfig<S
1421
1460
  /** Type guard for what a bundle's default export should be. */
1422
1461
  declare function isRoomDefinition(v: unknown): v is RoomDefinition;
1423
1462
 
1424
- export { type Behaviour, type BodyFactories, type BodySpec, type BusConfig, type BusEvent, type BusMessage, type ClientInfo, type Ctx, DEFAULTS, HISTORY_MAX_TICKS, LOBBY_MEMBERS, LOBBY_MIN_PLAYERS, LOBBY_STATE, type LeaderboardSubmitOptions, type LeaveReason, type LobbyConfig, type LobbyPhase, type LobbyStart, MAX_AWAKE_MAX, MEMORY_MB_MAX, MEMORY_MB_MIN, type Matter2dBodyFactories, type Matter2dBodySpec, type Matter2dIntentHook, type Matter2dPhysicsConfig, type Matter2dRoomApi, type MatterBody, type MatterConstraint, type MatterEngine, type MatterModule, type MessageTarget, type Npc, type NpcCollection, type NpcConfig, type NpcHandle, type NpcRoom, type NpcScript, type NpcScriptBrain, type NpcState, type PatrolStep, type PhysicsConfig, type PhysicsRoomApi, type PlayerKv, RETENTION_MAX_MS, RETENTION_MIN_MS, RETENTION_RE, ROOM_DEFINITION_VERSION, type Rapier2dBodyFactories, type Rapier2dBodySpec, type Rapier2dCollider, type Rapier2dColliderDesc, type Rapier2dIntentHook, type Rapier2dModule, type Rapier2dPhysicsConfig, type Rapier2dRigidBody, type Rapier2dRigidBodyDesc, type Rapier2dRoomApi, type Rapier2dWorld, type RapierCollider, type RapierColliderDesc, type RapierModule, type RapierPhysicsConfig, type RapierRigidBody, type RapierRigidBodyDesc, type RapierWorld, type ResolvedRoomConfig, type RewindView, type Room, type RoomBackfill, type RoomBus, type RoomConfig, type RoomConfigBase, type RoomDefinition, type RoomLeaderboard, type RoomLobby, type RoomMode, type RoomRatingResult, type RoomRatings, type RpcImplementations, type TimerHandle, type TypedMessage, type Validators, type Vec2, type Vector2, type Vector3, type WanderState, arrive, choose, defineRoom, flee, isRoomDefinition, lobbyCollections, lobbyConfigProblem, lobbyMemberEntity, lobbyStateSingleton, newWander, patrol, schemaHasLobby, seek, wander };
1463
+ export { type Behaviour, type BodyFactories, type BodySpec, type BusConfig, type BusEvent, type BusMessage, type ClientInfo, type Ctx, DEFAULTS, HISTORY_MAX_CHANNELS, HISTORY_MAX_TICKS, type HistoryChannelSpec, type HistoryConfig, LOBBY_MEMBERS, LOBBY_MIN_PLAYERS, LOBBY_STATE, type LeaderboardSubmitOptions, type LeaveReason, type LobbyConfig, type LobbyPhase, type LobbyStart, MAX_AWAKE_MAX, MEMORY_MB_MAX, MEMORY_MB_MIN, type Matter2dBodyFactories, type Matter2dBodySpec, type Matter2dIntentHook, type Matter2dPhysicsConfig, type Matter2dRoomApi, type MatterBody, type MatterConstraint, type MatterEngine, type MatterModule, type MessageTarget, type Npc, type NpcCollection, type NpcConfig, type NpcHandle, type NpcRoom, type NpcScript, type NpcScriptBrain, type NpcState, type PatrolStep, type PhysicsConfig, type PhysicsRoomApi, type PlayerKv, RETENTION_MAX_MS, RETENTION_MIN_MS, RETENTION_RE, ROOM_DEFINITION_VERSION, type Rapier2dBodyFactories, type Rapier2dBodySpec, type Rapier2dCollider, type Rapier2dColliderDesc, type Rapier2dIntentHook, type Rapier2dModule, type Rapier2dPhysicsConfig, type Rapier2dRigidBody, type Rapier2dRigidBodyDesc, type Rapier2dRoomApi, type Rapier2dWorld, type RapierCollider, type RapierColliderDesc, type RapierModule, type RapierPhysicsConfig, type RapierRigidBody, type RapierRigidBodyDesc, type RapierWorld, type ResolvedRoomConfig, type RewindView, type Room, type RoomBackfill, type RoomBus, type RoomConfig, type RoomConfigBase, type RoomDefinition, type RoomLeaderboard, type RoomLobby, type RoomMode, type RoomRatingResult, type RoomRatings, type RpcImplementations, type TimerHandle, type TypedMessage, type Validators, type Vec2, type Vector2, type Vector3, type WanderState, arrive, choose, defineRoom, flee, isRoomDefinition, lobbyCollections, lobbyConfigProblem, lobbyMemberEntity, lobbyStateSingleton, newWander, patrol, schemaHasLobby, seek, wander };
package/dist/index.js CHANGED
@@ -71,6 +71,7 @@ function lobbyConfigProblem(value) {
71
71
 
72
72
  // src/physics.ts
73
73
  var HISTORY_MAX_TICKS = 240;
74
+ var HISTORY_MAX_CHANNELS = 8;
74
75
 
75
76
  // src/npc.ts
76
77
  var ZERO = { x: 0, y: 0 };
@@ -317,11 +318,45 @@ function checkPhysics(schema, config, mode) {
317
318
  }
318
319
  const history = physics.history;
319
320
  if (history !== void 0) {
320
- if (!Number.isInteger(history) || history < 0 || history > HISTORY_MAX_TICKS) {
321
+ const depth = typeof history === "object" && history !== null ? history.depth : history;
322
+ if (!Number.isInteger(depth) || depth < 0 || depth > HISTORY_MAX_TICKS) {
323
+ const field = typeof history === "object" && history !== null ? "physics.history.depth" : "physics.history";
321
324
  throw new Error(
322
- `defineRoom: physics.history must be an integer in 0..${HISTORY_MAX_TICKS} ticks (0 or absent means no history and no cost), got ${String(history)}`
325
+ `defineRoom: ${field} must be an integer in 0..${HISTORY_MAX_TICKS} ticks (0 or absent means no history and no cost), got ${String(depth)}`
323
326
  );
324
327
  }
328
+ const channels = typeof history === "object" && history !== null ? history.channels : void 0;
329
+ if (channels !== void 0) {
330
+ if (typeof channels !== "object" || channels === null) {
331
+ throw new Error(
332
+ `defineRoom: physics.history.channels must be an object keyed by physics collection, got ${String(channels)}`
333
+ );
334
+ }
335
+ const collections = Object.keys(physics.bodies ?? {});
336
+ for (const [name, spec] of Object.entries(channels)) {
337
+ if (!collections.includes(name)) {
338
+ throw new Error(
339
+ `defineRoom: physics.history.channels.${name} names no physics collection`
340
+ );
341
+ }
342
+ if (typeof spec !== "object" || spec === null) {
343
+ throw new Error(
344
+ `defineRoom: physics.history.channels.${name} must be { count, read }, got ${String(spec)}`
345
+ );
346
+ }
347
+ const count = spec.count;
348
+ if (!Number.isInteger(count) || count < 1 || count > HISTORY_MAX_CHANNELS) {
349
+ throw new Error(
350
+ `defineRoom: physics.history.channels.${name}.count must be an integer in 1..${HISTORY_MAX_CHANNELS}, got ${String(count)}`
351
+ );
352
+ }
353
+ if (typeof spec.read !== "function") {
354
+ throw new Error(
355
+ `defineRoom: physics.history.channels.${name}.read must be a function (instance, out) => void`
356
+ );
357
+ }
358
+ }
359
+ }
325
360
  }
326
361
  assertSyncHandler(physics.setup, "physics.setup");
327
362
  const bodies = physics.bodies ?? {};
@@ -367,6 +402,7 @@ function isRoomDefinition(v) {
367
402
  }
368
403
  export {
369
404
  DEFAULTS,
405
+ HISTORY_MAX_CHANNELS,
370
406
  HISTORY_MAX_TICKS,
371
407
  LOBBY_MEMBERS,
372
408
  LOBBY_MIN_PLAYERS,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@irtio/server",
3
- "version": "0.9.0",
3
+ "version": "0.10.1",
4
4
  "description": "irtio room-file API: defineRoom, handler and ctx types",
5
5
  "license": "MIT",
6
6
  "publishConfig": {
@@ -20,7 +20,7 @@
20
20
  "dist"
21
21
  ],
22
22
  "dependencies": {
23
- "@irtio/schema": "0.9.0"
23
+ "@irtio/schema": "0.10.1"
24
24
  },
25
25
  "peerDependencies": {
26
26
  "@dimforge/rapier2d-compat": ">=0.20.0",