@replicast/core 1.0.0-rc.6 → 1.0.0-rc.8

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.
Files changed (63) hide show
  1. package/dist/decorators/entity.d.ts +43 -0
  2. package/dist/decorators/entity.d.ts.map +1 -0
  3. package/dist/decorators/entity.js +75 -0
  4. package/dist/decorators/entity.js.map +1 -0
  5. package/dist/decorators/index.d.ts +2 -0
  6. package/dist/decorators/index.d.ts.map +1 -1
  7. package/dist/decorators/index.js +1 -0
  8. package/dist/decorators/index.js.map +1 -1
  9. package/dist/decorators/metadata.d.ts +5 -1
  10. package/dist/decorators/metadata.d.ts.map +1 -1
  11. package/dist/decorators/metadata.js.map +1 -1
  12. package/dist/entity/base-entity.d.ts +19 -0
  13. package/dist/entity/base-entity.d.ts.map +1 -0
  14. package/dist/entity/base-entity.js +18 -0
  15. package/dist/entity/base-entity.js.map +1 -0
  16. package/dist/entity/fields.d.ts +21 -0
  17. package/dist/entity/fields.d.ts.map +1 -0
  18. package/dist/entity/fields.js +67 -0
  19. package/dist/entity/fields.js.map +1 -0
  20. package/dist/entity/registry.d.ts +53 -0
  21. package/dist/entity/registry.d.ts.map +1 -0
  22. package/dist/entity/registry.js +167 -0
  23. package/dist/entity/registry.js.map +1 -0
  24. package/dist/entity/replicator.d.ts +40 -0
  25. package/dist/entity/replicator.d.ts.map +1 -0
  26. package/dist/entity/replicator.js +82 -0
  27. package/dist/entity/replicator.js.map +1 -0
  28. package/dist/entity/types.d.ts +29 -0
  29. package/dist/entity/types.d.ts.map +1 -0
  30. package/dist/entity/types.js +4 -0
  31. package/dist/entity/types.js.map +1 -0
  32. package/dist/entity/visibility.d.ts +11 -0
  33. package/dist/entity/visibility.d.ts.map +1 -0
  34. package/dist/entity/visibility.js +10 -0
  35. package/dist/entity/visibility.js.map +1 -0
  36. package/dist/entity/world.d.ts +67 -0
  37. package/dist/entity/world.d.ts.map +1 -0
  38. package/dist/entity/world.js +183 -0
  39. package/dist/entity/world.js.map +1 -0
  40. package/dist/index.d.ts +15 -1
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +9 -0
  43. package/dist/index.js.map +1 -1
  44. package/dist/store/base-store.d.ts +15 -0
  45. package/dist/store/base-store.d.ts.map +1 -1
  46. package/dist/store/base-store.js +26 -2
  47. package/dist/store/base-store.js.map +1 -1
  48. package/dist/transport/app.d.ts +11 -1
  49. package/dist/transport/app.d.ts.map +1 -1
  50. package/dist/transport/app.js +5 -1
  51. package/dist/transport/app.js.map +1 -1
  52. package/dist/transport/client.d.ts +12 -0
  53. package/dist/transport/client.d.ts.map +1 -1
  54. package/dist/transport/client.js +50 -9
  55. package/dist/transport/client.js.map +1 -1
  56. package/dist/transport/protocol.d.ts +18 -1
  57. package/dist/transport/protocol.d.ts.map +1 -1
  58. package/dist/transport/protocol.js.map +1 -1
  59. package/dist/transport/server.d.ts +18 -0
  60. package/dist/transport/server.d.ts.map +1 -1
  61. package/dist/transport/server.js +111 -10
  62. package/dist/transport/server.js.map +1 -1
  63. package/package.json +1 -1
@@ -0,0 +1,82 @@
1
+ import { snapshotFields, diffFields, ownerIdField } from './fields.js';
2
+ import { isEntityVisible } from './visibility.js';
3
+ /**
4
+ * Tracks, per client, which entities that client has been sent and the fields it last saw, and turns
5
+ * the current world into that client's `{ enter, update, leave }` delta.
6
+ *
7
+ * Cost is O(clients x entities) per flush — deliberate for now; a spatial index hook comes later.
8
+ */
9
+ export class EntityReplicator {
10
+ /** playerId → entityId → last-sent field snapshot. */
11
+ views = new Map();
12
+ /** True when `playerId` currently has `id` in their set (i.e. as of the last flush). */
13
+ hasInView(playerId, id) {
14
+ return this.views.get(playerId)?.has(id) ?? false;
15
+ }
16
+ /** The clients that currently see `id` — the audience for a `@Multicast`. */
17
+ viewersOf(id) {
18
+ const viewers = [];
19
+ for (const [playerId, view] of this.views) {
20
+ if (view.has(id))
21
+ viewers.push(playerId);
22
+ }
23
+ return viewers;
24
+ }
25
+ /** Forget a disconnected client so they re-enter from scratch on reconnect. */
26
+ dropViewer(playerId) {
27
+ this.views.delete(playerId);
28
+ }
29
+ /**
30
+ * Records `id` as already sent to `playerId` with `fields`, without waiting for the next flush —
31
+ * for a caller that must guarantee delivery order (an `@Client`/`@Multicast` send that needs the
32
+ * recipient's mirror to exist before the rpc arrives). The next real `flush()` then sees this id
33
+ * already in `view` and sends only a diff, never a duplicate `enter`.
34
+ */
35
+ recordEnter(playerId, id, fields) {
36
+ let view = this.views.get(playerId);
37
+ if (!view) {
38
+ view = new Map();
39
+ this.views.set(playerId, view);
40
+ }
41
+ view.set(id, fields);
42
+ }
43
+ /**
44
+ * Builds one client's delta and records it as sent. An entity is visible when the client owns it
45
+ * (owners always see their own entities) or the type's `visibleTo` hook allows it.
46
+ */
47
+ computeSync(playerId, entries, world) {
48
+ let view = this.views.get(playerId);
49
+ if (!view) {
50
+ view = new Map();
51
+ this.views.set(playerId, view);
52
+ }
53
+ const sync = { enter: [], update: [], leave: [] };
54
+ const stillVisible = new Set();
55
+ for (const { id, entity, type, plan, visibleTo } of entries) {
56
+ const isOwner = entity[ownerIdField] === playerId;
57
+ if (!isEntityVisible(isOwner, playerId, entity, visibleTo, world))
58
+ continue;
59
+ stillVisible.add(id);
60
+ const fields = snapshotFields(entity, plan, isOwner);
61
+ const previous = view.get(id);
62
+ if (previous === undefined) {
63
+ sync.enter.push({ type, id, fields });
64
+ view.set(id, fields);
65
+ continue;
66
+ }
67
+ const patch = diffFields(previous, fields);
68
+ if (Object.keys(patch).length > 0) {
69
+ sync.update.push({ id, fields: patch });
70
+ view.set(id, fields);
71
+ }
72
+ }
73
+ for (const id of view.keys()) {
74
+ if (!stillVisible.has(id)) {
75
+ sync.leave.push(id);
76
+ view.delete(id);
77
+ }
78
+ }
79
+ return sync;
80
+ }
81
+ }
82
+ //# sourceMappingURL=replicator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"replicator.js","sourceRoot":"","sources":["../../src/entity/replicator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAEtE,OAAO,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAA;AAajD;;;;;GAKG;AACH,MAAM,OAAO,gBAAgB;IAC3B,sDAAsD;IAC9C,KAAK,GAAG,IAAI,GAAG,EAAkD,CAAA;IAEzE,wFAAwF;IACxF,SAAS,CAAC,QAAgB,EAAE,EAAY;QACtC,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,KAAK,CAAA;IACnD,CAAC;IAED,6EAA6E;IAC7E,SAAS,CAAC,EAAY;QACpB,MAAM,OAAO,GAAa,EAAE,CAAA;QAC5B,KAAK,MAAM,CAAC,QAAQ,EAAE,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,CAAC;YAC1C,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAA;QAC1C,CAAC;QACD,OAAO,OAAO,CAAA;IAChB,CAAC;IAED,+EAA+E;IAC/E,UAAU,CAAC,QAAgB;QACzB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAA;IAC7B,CAAC;IAED;;;;;OAKG;IACH,WAAW,CAAC,QAAgB,EAAE,EAAY,EAAE,MAA+B;QACzE,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,EAAE,CAAC;YAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAAC,CAAC;QAC/D,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;IACtB,CAAC;IAED;;;OAGG;IACH,WAAW,CAAC,QAAgB,EAAE,OAAmC,EAAE,KAAsB;QACvF,IAAI,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;QACnC,IAAI,CAAC,IAAI,EAAE,CAAC;YAAC,IAAI,GAAG,IAAI,GAAG,EAAE,CAAC;YAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAA;QAAC,CAAC;QAE/D,MAAM,IAAI,GAAe,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,KAAK,EAAE,EAAE,EAAE,CAAA;QAC7D,MAAM,YAAY,GAAG,IAAI,GAAG,EAAY,CAAA;QAExC,KAAK,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,SAAS,EAAE,IAAI,OAAO,EAAE,CAAC;YAC5D,MAAM,OAAO,GAAI,MAAkC,CAAC,YAAY,CAAC,KAAK,QAAQ,CAAA;YAC9E,IAAI,CAAC,eAAe,CAAC,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,KAAK,CAAC;gBAAE,SAAQ;YAE3E,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YACpB,MAAM,MAAM,GAAG,cAAc,CAAC,MAAM,EAAE,IAAI,EAAE,OAAO,CAAC,CAAA;YACpD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAA;YAC7B,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,MAAM,EAAE,CAAC,CAAA;gBACrC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;gBACpB,SAAQ;YACV,CAAC;YACD,MAAM,KAAK,GAAG,UAAU,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAA;YAC1C,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBAClC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAA;gBACvC,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;YACtB,CAAC;QACH,CAAC;QAED,KAAK,MAAM,EAAE,IAAI,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC;YAC7B,IAAI,CAAC,YAAY,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC;gBAC1B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;gBACnB,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;YACjB,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAA;IACb,CAAC;CACF"}
@@ -0,0 +1,29 @@
1
+ /** Server-assigned entity id. Numeric so it stays compact on the msgpack wire. */
2
+ export type EntityId = number;
3
+ /** Any class usable as a replicated entity type: it must be constructible with no arguments. */
4
+ export type EntityConstructor<T extends object = object> = new () => T;
5
+ /** Read-only view of the server world handed to `visibleTo` / `canCall` hooks. */
6
+ export interface EntityWorldView {
7
+ get(id: EntityId): object | undefined;
8
+ byType<T extends object>(Type: EntityConstructor<T>): T[];
9
+ all(): object[];
10
+ }
11
+ /** One entity entering a client's view: the full replicated field set for that client. */
12
+ export interface EntityEnter {
13
+ type: string;
14
+ id: EntityId;
15
+ fields: Record<string, unknown>;
16
+ }
17
+ /** One entity already in view whose replicated fields changed. */
18
+ export interface EntityUpdate {
19
+ id: EntityId;
20
+ fields: Record<string, unknown>;
21
+ }
22
+ /** A single client's per-flush entity delta. */
23
+ export interface EntitySync {
24
+ enter: EntityEnter[];
25
+ update: EntityUpdate[];
26
+ leave: EntityId[];
27
+ }
28
+ export declare function isEmptySync(sync: EntitySync): boolean;
29
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/entity/types.ts"],"names":[],"mappings":"AAAA,kFAAkF;AAClF,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAA;AAE7B,gGAAgG;AAChG,MAAM,MAAM,iBAAiB,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,UAAU,CAAC,CAAA;AAEtE,kFAAkF;AAClF,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;IACrC,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,CAAA;IACzD,GAAG,IAAI,MAAM,EAAE,CAAA;CAChB;AAED,0FAA0F;AAC1F,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,EAAE,EAAE,QAAQ,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,kEAAkE;AAClE,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,QAAQ,CAAA;IACZ,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC;AAED,gDAAgD;AAChD,MAAM,WAAW,UAAU;IACzB,KAAK,EAAE,WAAW,EAAE,CAAA;IACpB,MAAM,EAAE,YAAY,EAAE,CAAA;IACtB,KAAK,EAAE,QAAQ,EAAE,CAAA;CAClB;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO,CAErD"}
@@ -0,0 +1,4 @@
1
+ export function isEmptySync(sync) {
2
+ return sync.enter.length === 0 && sync.update.length === 0 && sync.leave.length === 0;
3
+ }
4
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/entity/types.ts"],"names":[],"mappings":"AAiCA,MAAM,UAAU,WAAW,CAAC,IAAgB;IAC1C,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,CAAA;AACvF,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { EntityWorldView } from './types.js';
2
+ /** Scopes replication and RPC visibility for an `@Entity` type. Default (undefined): visible to everyone. */
3
+ export type VisibleTo = (viewerId: string, entity: object, world: EntityWorldView) => boolean;
4
+ /**
5
+ * Whether `viewerId` can see `entity`: an owner always can; everyone else defers to `visibleTo`
6
+ * (no hook means visible to everyone). Shared by replication (who gets `enter`/`update`) and by
7
+ * `@Server` RPC authorisation (`EntityWorld.invoke`), so a caller can never act on an entity the
8
+ * server would not otherwise show them.
9
+ */
10
+ export declare function isEntityVisible(isOwner: boolean, viewerId: string, entity: object, visibleTo: VisibleTo | undefined, world: EntityWorldView): boolean;
11
+ //# sourceMappingURL=visibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.d.ts","sourceRoot":"","sources":["../../src/entity/visibility.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEjD,6GAA6G;AAC7G,MAAM,MAAM,SAAS,GAAG,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,eAAe,KAAK,OAAO,CAAA;AAE7F;;;;;GAKG;AACH,wBAAgB,eAAe,CAC7B,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,GAAG,SAAS,EAChC,KAAK,EAAE,eAAe,GACrB,OAAO,CAET"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Whether `viewerId` can see `entity`: an owner always can; everyone else defers to `visibleTo`
3
+ * (no hook means visible to everyone). Shared by replication (who gets `enter`/`update`) and by
4
+ * `@Server` RPC authorisation (`EntityWorld.invoke`), so a caller can never act on an entity the
5
+ * server would not otherwise show them.
6
+ */
7
+ export function isEntityVisible(isOwner, viewerId, entity, visibleTo, world) {
8
+ return isOwner || !visibleTo || visibleTo(viewerId, entity, world);
9
+ }
10
+ //# sourceMappingURL=visibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"visibility.js","sourceRoot":"","sources":["../../src/entity/visibility.ts"],"names":[],"mappings":"AAKA;;;;;GAKG;AACH,MAAM,UAAU,eAAe,CAC7B,OAAgB,EAChB,QAAgB,EAChB,MAAc,EACd,SAAgC,EAChC,KAAsB;IAEtB,OAAO,OAAO,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,CAAC,CAAA;AACpE,CAAC"}
@@ -0,0 +1,67 @@
1
+ import type { EntityConstructor, EntityId, EntitySync, EntityWorldView } from './types.js';
2
+ /** How the world reaches connected clients. Injected so the world stays testable without sockets. */
3
+ export interface EntityTransport {
4
+ /** Push one entity-scoped `@Client` / `@Multicast` call to a client. */
5
+ sendRpc(playerId: string, id: EntityId, method: string, args: unknown[]): void;
6
+ /** Push one client their `{ enter, update, leave }` delta. */
7
+ sendSync(playerId: string, sync: EntitySync): void;
8
+ /** The currently connected players. */
9
+ players(): string[];
10
+ }
11
+ export interface EntityWorldOptions {
12
+ /** Entity classes registered up front (also auto-registered lazily on first spawn). */
13
+ types?: EntityConstructor<object>[];
14
+ transport?: EntityTransport;
15
+ }
16
+ /**
17
+ * Server-side entity registry: spawns entities, runs their `@ServerTick`, authorises their
18
+ * `@Server` calls, and flushes each client's interest-scoped delta.
19
+ */
20
+ export declare class EntityWorld implements EntityWorldView {
21
+ private entities;
22
+ private infoByCtor;
23
+ private replicator;
24
+ private transport;
25
+ private nextId;
26
+ constructor(options?: EntityWorldOptions);
27
+ private register;
28
+ private infoOf;
29
+ /** Registers an entity, assigns its id and owner, and wires its server → client dispatch. */
30
+ spawn<T extends object>(entity: T, options?: {
31
+ ownerId?: string | null;
32
+ }): T;
33
+ despawn(id: EntityId): void;
34
+ get(id: EntityId): object | undefined;
35
+ byType<T extends object>(Type: EntityConstructor<T>): T[];
36
+ all(): object[];
37
+ /** Forget a disconnected client's view so they re-enter from scratch on reconnect. */
38
+ dropViewer(playerId: string): void;
39
+ /** Runs every entity's `@ServerTick` methods. Called by the server's fixed-step scheduler. */
40
+ tick(dt: number): void;
41
+ /** Computes and sends every connected client's delta. Called at the replication rate. */
42
+ flush(): void;
43
+ private replicationEntries;
44
+ /**
45
+ * Runs a client-initiated `@Server` call on one entity after checking the caller is allowed.
46
+ * Rejects on an unknown entity, a method that is not `@Server` on that type, an entity the caller
47
+ * cannot see, or a failed `canCall` — `canCall` alone would otherwise let a `canCall: 'anyone'`
48
+ * entity be driven by a client the `visibleTo` hook was written to hide it from entirely.
49
+ */
50
+ invoke(callerId: string, id: EntityId, method: string, args: unknown[]): Promise<unknown>;
51
+ private canCall;
52
+ /**
53
+ * Replaces `@Client` / `@Multicast` methods on this instance with sends, so the server stays
54
+ * independent of the Vite transform (plain tsx/node works) and tick code can call them directly.
55
+ *
56
+ * Both recompute visibility live against `this.transport.players()` rather than asking the
57
+ * replicator who was already sent an `enter` for this id, because `flush()` runs on its own timer:
58
+ * an entity spawned and called on within the same tick would otherwise have no recorded viewers yet
59
+ * and the rpc would reach nobody. That alone isn't sufficient though — a recipient who is newly
60
+ * visible but has not yet had a flush also has no client-side mirror to run the rpc against, so
61
+ * `sendToRecipient` first catches that one recipient up with a synthetic `enter` (recorded into the
62
+ * replicator so the next real flush sends a diff, not a duplicate enter) before sending the rpc, to
63
+ * guarantee enter-before-rpc per `(player, entity)`.
64
+ */
65
+ private bindClientDispatch;
66
+ }
67
+ //# sourceMappingURL=world.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.d.ts","sourceRoot":"","sources":["../../src/entity/world.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAE1F,qGAAqG;AACrG,MAAM,WAAW,eAAe;IAC9B,wEAAwE;IACxE,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI,CAAA;IAC9E,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAAA;IAClD,uCAAuC;IACvC,OAAO,IAAI,MAAM,EAAE,CAAA;CACpB;AAED,MAAM,WAAW,kBAAkB;IACjC,uFAAuF;IACvF,KAAK,CAAC,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAA;IACnC,SAAS,CAAC,EAAE,eAAe,CAAA;CAC5B;AAeD;;;GAGG;AACH,qBAAa,WAAY,YAAW,eAAe;IACjD,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,UAAU,CAAyB;IAC3C,OAAO,CAAC,SAAS,CAAiB;IAClC,OAAO,CAAC,MAAM,CAAI;gBAEN,OAAO,GAAE,kBAAuB;IAK5C,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,MAAM;IAId,6FAA6F;IAC7F,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,OAAO,GAAE;QAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;KAAO,GAAG,CAAC;IAahF,OAAO,CAAC,EAAE,EAAE,QAAQ,GAAG,IAAI;IAI3B,GAAG,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS;IAIrC,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;IAQzD,GAAG,IAAI,MAAM,EAAE;IAIf,sFAAsF;IACtF,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIlC,8FAA8F;IAC9F,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,IAAI;IAQtB,yFAAyF;IACzF,KAAK,IAAI,IAAI;IAQb,OAAO,CAAC,kBAAkB;IAS1B;;;;;OAKG;IACG,MAAM,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC;IAmB/F,OAAO,CAAC,OAAO;IAOf;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,kBAAkB;CA2B3B"}
@@ -0,0 +1,183 @@
1
+ import { getMethodRoles } from '../decorators/metadata.js';
2
+ import { getEntityHooks, getEntityName, getEntityOptions, isEntityClass } from '../decorators/entity.js';
3
+ import { fieldPlanOf, ownerIdField, snapshotFields } from './fields.js';
4
+ import { EntityReplicator } from './replicator.js';
5
+ import { isEntityVisible } from './visibility.js';
6
+ import { isEmptySync } from './types.js';
7
+ const noopTransport = {
8
+ sendRpc() { }, sendSync() { }, players: () => [],
9
+ };
10
+ /**
11
+ * Server-side entity registry: spawns entities, runs their `@ServerTick`, authorises their
12
+ * `@Server` calls, and flushes each client's interest-scoped delta.
13
+ */
14
+ export class EntityWorld {
15
+ entities = new Map();
16
+ infoByCtor = new Map();
17
+ replicator = new EntityReplicator();
18
+ transport;
19
+ nextId = 1;
20
+ constructor(options = {}) {
21
+ this.transport = options.transport ?? noopTransport;
22
+ for (const Type of options.types ?? [])
23
+ this.register(Type);
24
+ }
25
+ register(Type) {
26
+ const existing = this.infoByCtor.get(Type);
27
+ if (existing)
28
+ return existing;
29
+ if (!isEntityClass(Type)) {
30
+ throw new Error(`${Type.name || 'class'} is not decorated with @Entity`);
31
+ }
32
+ const proto = Type.prototype;
33
+ const hooks = getEntityHooks(proto);
34
+ const roles = getMethodRoles(proto);
35
+ const info = {
36
+ name: getEntityName(Type),
37
+ plan: fieldPlanOf(Type),
38
+ options: getEntityOptions(Type) ?? {},
39
+ serverTicks: Object.keys(hooks).filter(m => hooks[m] === 'serverTick'),
40
+ clientMethods: Object.fromEntries(Object.entries(roles).filter(([, r]) => r === 'client' || r === 'multicast')),
41
+ serverMethods: new Set(Object.keys(roles).filter(m => roles[m] === 'server')),
42
+ };
43
+ this.infoByCtor.set(Type, info);
44
+ return info;
45
+ }
46
+ infoOf(entity) {
47
+ return this.register(Object.getPrototypeOf(entity).constructor);
48
+ }
49
+ /** Registers an entity, assigns its id and owner, and wires its server → client dispatch. */
50
+ spawn(entity, options = {}) {
51
+ const info = this.infoOf(entity);
52
+ const id = this.nextId++;
53
+ const target = entity;
54
+ target['id'] = id;
55
+ target['ownerId'] = options.ownerId ?? null;
56
+ target['isServer'] = true;
57
+ target['hasAuthority'] = true;
58
+ this.entities.set(id, entity);
59
+ this.bindClientDispatch(entity, id, info);
60
+ return entity;
61
+ }
62
+ despawn(id) {
63
+ this.entities.delete(id);
64
+ }
65
+ get(id) {
66
+ return this.entities.get(id);
67
+ }
68
+ byType(Type) {
69
+ const out = [];
70
+ for (const entity of this.entities.values()) {
71
+ if (entity instanceof Type)
72
+ out.push(entity);
73
+ }
74
+ return out;
75
+ }
76
+ all() {
77
+ return [...this.entities.values()];
78
+ }
79
+ /** Forget a disconnected client's view so they re-enter from scratch on reconnect. */
80
+ dropViewer(playerId) {
81
+ this.replicator.dropViewer(playerId);
82
+ }
83
+ /** Runs every entity's `@ServerTick` methods. Called by the server's fixed-step scheduler. */
84
+ tick(dt) {
85
+ for (const entity of [...this.entities.values()]) {
86
+ for (const method of this.infoOf(entity).serverTicks) {
87
+ entity[method].call(entity, dt);
88
+ }
89
+ }
90
+ }
91
+ /** Computes and sends every connected client's delta. Called at the replication rate. */
92
+ flush() {
93
+ const entries = this.replicationEntries();
94
+ for (const playerId of this.transport.players()) {
95
+ const sync = this.replicator.computeSync(playerId, entries, this);
96
+ if (!isEmptySync(sync))
97
+ this.transport.sendSync(playerId, sync);
98
+ }
99
+ }
100
+ replicationEntries() {
101
+ const entries = [];
102
+ for (const [id, entity] of this.entities) {
103
+ const info = this.infoOf(entity);
104
+ entries.push({ id, entity, type: info.name, plan: info.plan, visibleTo: info.options.visibleTo });
105
+ }
106
+ return entries;
107
+ }
108
+ /**
109
+ * Runs a client-initiated `@Server` call on one entity after checking the caller is allowed.
110
+ * Rejects on an unknown entity, a method that is not `@Server` on that type, an entity the caller
111
+ * cannot see, or a failed `canCall` — `canCall` alone would otherwise let a `canCall: 'anyone'`
112
+ * entity be driven by a client the `visibleTo` hook was written to hide it from entirely.
113
+ */
114
+ async invoke(callerId, id, method, args) {
115
+ const entity = this.entities.get(id);
116
+ if (!entity)
117
+ throw new Error(`Unknown entity ${id}`);
118
+ const info = this.infoOf(entity);
119
+ const [typeName, methodName] = method.split('.');
120
+ if (typeName !== info.name || !methodName || !info.serverMethods.has(methodName)) {
121
+ throw new Error(`Unknown entity method: ${method}`);
122
+ }
123
+ const isOwner = entity[ownerIdField] === callerId;
124
+ if (!isEntityVisible(isOwner, callerId, entity, info.options.visibleTo, this)) {
125
+ // Same message as a missing id: a caller with no visibility shouldn't learn the entity exists.
126
+ throw new Error(`Unknown entity ${id}`);
127
+ }
128
+ if (!this.canCall(callerId, entity, info)) {
129
+ throw new Error(`Not allowed: ${method}`);
130
+ }
131
+ return entity[methodName].apply(entity, args);
132
+ }
133
+ canCall(callerId, entity, info) {
134
+ const rule = info.options.canCall ?? 'owner';
135
+ if (rule === 'anyone')
136
+ return true;
137
+ if (rule === 'owner')
138
+ return entity['ownerId'] === callerId;
139
+ return rule(callerId, entity, this);
140
+ }
141
+ /**
142
+ * Replaces `@Client` / `@Multicast` methods on this instance with sends, so the server stays
143
+ * independent of the Vite transform (plain tsx/node works) and tick code can call them directly.
144
+ *
145
+ * Both recompute visibility live against `this.transport.players()` rather than asking the
146
+ * replicator who was already sent an `enter` for this id, because `flush()` runs on its own timer:
147
+ * an entity spawned and called on within the same tick would otherwise have no recorded viewers yet
148
+ * and the rpc would reach nobody. That alone isn't sufficient though — a recipient who is newly
149
+ * visible but has not yet had a flush also has no client-side mirror to run the rpc against, so
150
+ * `sendToRecipient` first catches that one recipient up with a synthetic `enter` (recorded into the
151
+ * replicator so the next real flush sends a diff, not a duplicate enter) before sending the rpc, to
152
+ * guarantee enter-before-rpc per `(player, entity)`.
153
+ */
154
+ bindClientDispatch(entity, id, info) {
155
+ const sendToRecipient = (playerId, isOwner, key, args) => {
156
+ if (!this.replicator.hasInView(playerId, id)) {
157
+ const fields = snapshotFields(entity, info.plan, isOwner);
158
+ this.replicator.recordEnter(playerId, id, fields);
159
+ this.transport.sendSync(playerId, { enter: [{ type: info.name, id, fields }], update: [], leave: [] });
160
+ }
161
+ this.transport.sendRpc(playerId, id, key, args);
162
+ };
163
+ for (const [methodName, role] of Object.entries(info.clientMethods)) {
164
+ const key = `${info.name}.${methodName}`;
165
+ const send = role === 'client'
166
+ ? (...args) => {
167
+ const ownerId = entity['ownerId'];
168
+ if (typeof ownerId === 'string')
169
+ sendToRecipient(ownerId, true, key, args);
170
+ }
171
+ : (...args) => {
172
+ for (const playerId of this.transport.players()) {
173
+ const isOwner = entity[ownerIdField] === playerId;
174
+ if (isEntityVisible(isOwner, playerId, entity, info.options.visibleTo, this)) {
175
+ sendToRecipient(playerId, isOwner, key, args);
176
+ }
177
+ }
178
+ };
179
+ Object.defineProperty(entity, methodName, { value: send, configurable: true, writable: true });
180
+ }
181
+ }
182
+ }
183
+ //# sourceMappingURL=world.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"world.js","sourceRoot":"","sources":["../../src/entity/world.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -2,13 +2,27 @@ export { GlobalStore, OwnerStore, Global, OwnerOnly, ServerOnly } from './decora
2
2
  export { System, Server, Client, Multicast } from './decorators/system.js';
3
3
  export { getStoreType, getPropertyRoles, getMethodRoles, setPropertyRole, setMethodRole } from './decorators/metadata.js';
4
4
  export type { StoreType, PropertyRole, MethodRole } from './decorators/metadata.js';
5
+ export { Entity, Replicated, OwnerReplicated, Quantized, ServerTick, ClientSpawn, ClientDespawn, isEntityClass, getEntityName, getEntityOptions, getEntityHooks, getQuantizedFields, } from './decorators/entity.js';
6
+ export type { EntityOptions, EntityHook, CanCall } from './decorators/entity.js';
7
+ export { BaseEntity } from './entity/base-entity.js';
8
+ export { EntityWorld } from './entity/world.js';
9
+ export type { EntityTransport, EntityWorldOptions } from './entity/world.js';
10
+ export { ClientEntityRegistry } from './entity/registry.js';
11
+ export type { EntityCaller, ClientEntityRegistryOptions } from './entity/registry.js';
12
+ export { EntityReplicator } from './entity/replicator.js';
13
+ export type { ReplicationEntry } from './entity/replicator.js';
14
+ export { fieldPlanOf, snapshotFields, diffFields, quantize } from './entity/fields.js';
15
+ export type { FieldPlan } from './entity/fields.js';
16
+ export { isEntityVisible } from './entity/visibility.js';
17
+ export type { VisibleTo } from './entity/visibility.js';
18
+ export type { EntityId, EntityConstructor, EntityWorldView, EntitySync, EntityEnter, EntityUpdate, } from './entity/types.js';
5
19
  export { BaseStore } from './store/base-store.js';
6
20
  export type { ReplicationBatch } from './store/base-store.js';
7
21
  export type { ConnectRequest, ConnectPayload, ServerContext } from './lifecycle/types.js';
8
22
  export { BaseServerLifecycle } from './lifecycle/base-server.js';
9
23
  export { BaseClientLifecycle } from './lifecycle/base-client.js';
10
24
  export { createRPCServer } from './transport/server.js';
11
- export type { RpcServer } from './transport/server.js';
25
+ export type { RpcServer, TickOptions, ReplicationOptions } from './transport/server.js';
12
26
  export { ConnectionLostError, AuthRejectedError, RpcError, TimeoutError } from './transport/protocol.js';
13
27
  export { WsClient } from './transport/client.js';
14
28
  export type { ConnectionStatus, ConnectionQuality, WsClientOptions, LatencyThresholds } from './transport/client.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAC9F,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAC1E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACzH,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAGnF,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAGhE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACpH,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,YAAY,EAAE,GAAG,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAC9F,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAC1E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AACzH,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAGnF,OAAO,EACL,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EACtF,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,GACnF,MAAM,wBAAwB,CAAA;AAC/B,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAA;AAGhF,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAC/C,YAAY,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAA;AAC5E,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAC3D,YAAY,EAAE,YAAY,EAAE,2BAA2B,EAAE,MAAM,sBAAsB,CAAA;AACrF,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AACtF,YAAY,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,YAAY,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AACvD,YAAY,EACV,QAAQ,EAAE,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,GACpF,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AACjD,YAAY,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AAG7D,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAGzF,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAGhE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AACvD,YAAY,EAAE,SAAS,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAA;AACvF,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAA;AACpH,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAC9C,YAAY,EAAE,GAAG,EAAE,iBAAiB,EAAE,cAAc,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAA"}
package/dist/index.js CHANGED
@@ -2,6 +2,15 @@
2
2
  export { GlobalStore, OwnerStore, Global, OwnerOnly, ServerOnly } from './decorators/store.js';
3
3
  export { System, Server, Client, Multicast } from './decorators/system.js';
4
4
  export { getStoreType, getPropertyRoles, getMethodRoles, setPropertyRole, setMethodRole } from './decorators/metadata.js';
5
+ // Entity decorators
6
+ export { Entity, Replicated, OwnerReplicated, Quantized, ServerTick, ClientSpawn, ClientDespawn, isEntityClass, getEntityName, getEntityOptions, getEntityHooks, getQuantizedFields, } from './decorators/entity.js';
7
+ // Entities
8
+ export { BaseEntity } from './entity/base-entity.js';
9
+ export { EntityWorld } from './entity/world.js';
10
+ export { ClientEntityRegistry } from './entity/registry.js';
11
+ export { EntityReplicator } from './entity/replicator.js';
12
+ export { fieldPlanOf, snapshotFields, diffFields, quantize } from './entity/fields.js';
13
+ export { isEntityVisible } from './entity/visibility.js';
5
14
  // Store
6
15
  export { BaseStore } from './store/base-store.js';
7
16
  // Lifecycle base classes
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAC9F,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAC1E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAGzH,QAAQ;AACR,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAMjD,yBAAyB;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE,wCAAwC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,aAAa;AACb,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,uBAAuB,CAAA;AAC9F,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAC1E,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAGzH,oBAAoB;AACpB,OAAO,EACL,MAAM,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,WAAW,EAAE,aAAa,EACtF,aAAa,EAAE,aAAa,EAAE,gBAAgB,EAAE,cAAc,EAAE,kBAAkB,GACnF,MAAM,wBAAwB,CAAA;AAG/B,WAAW;AACX,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAA;AAE/C,OAAO,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAA;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAEzD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAEtF,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AAMxD,QAAQ;AACR,OAAO,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAA;AAMjD,yBAAyB;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAChE,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAA;AAEhE,wCAAwC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAEvD,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,yBAAyB,CAAA;AACxG,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAEhD,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AAC/C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA"}
@@ -14,7 +14,22 @@ export declare class BaseStore {
14
14
  private _flushScheduled;
15
15
  private _globalSeq;
16
16
  private _ownerSeqs;
17
+ private _listeners;
18
+ /**
19
+ * Legacy single-slot callback. Prefer subscribe(); kept so existing consumers keep working.
20
+ * Both the slot and all subscribe() listeners receive every batch.
21
+ */
17
22
  _onReplication?: (batch: ReplicationBatch) => void;
23
+ /**
24
+ * Listen for replication batches without React. On the client this fires after the
25
+ * incoming snapshot/patch has been applied to this instance, so fields are already current.
26
+ * Returns an unsubscribe function.
27
+ */
28
+ subscribe(listener: (batch: ReplicationBatch) => void): () => void;
29
+ /** Dispatch a batch to the legacy slot and all subscribers. Used by the server flush and the client transport. */
30
+ _emitReplication(batch: ReplicationBatch): void;
31
+ /** True if anything is listening (legacy slot or subscribers). */
32
+ get _hasListeners(): boolean;
18
33
  set(partial: Record<string, unknown>): void;
19
34
  setOwner(key: string, playerId: string, value: unknown): void;
20
35
  getOwner<T>(key: string, playerId: string): T;
@@ -1 +1 @@
1
- {"version":3,"file":"base-store.d.ts","sourceRoot":"","sources":["../../src/store/base-store.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACvC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAA4B;IAE9C,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAElD,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAqB3C,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAgB7D,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC;IAI7C,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAcvD,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,MAAM;CAuCf"}
1
+ {"version":3,"file":"base-store.d.ts","sourceRoot":"","sources":["../../src/store/base-store.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAA;IACX,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC/B;AAED,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC/B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAA;CACvC;AAED,qBAAa,SAAS;IACpB,OAAO,CAAC,UAAU,CAA0C;IAC5D,OAAO,CAAC,YAAY,CAAoB;IACxC,OAAO,CAAC,WAAW,CAAiC;IACpD,OAAO,CAAC,eAAe,CAAQ;IAC/B,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,UAAU,CAA4B;IAE9C,OAAO,CAAC,UAAU,CAA+C;IAEjE;;;OAGG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAElD;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,MAAM,IAAI;IAKlE,kHAAkH;IAClH,gBAAgB,CAAC,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAK/C,kEAAkE;IAClE,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAqB3C,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI;IAgB7D,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,CAAC;IAI7C,WAAW,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAcvD,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,MAAM;CAuCf"}
@@ -6,7 +6,31 @@ export class BaseStore {
6
6
  _flushScheduled = false;
7
7
  _globalSeq = 0;
8
8
  _ownerSeqs = new Map(); // playerId → seq
9
+ _listeners = new Set();
10
+ /**
11
+ * Legacy single-slot callback. Prefer subscribe(); kept so existing consumers keep working.
12
+ * Both the slot and all subscribe() listeners receive every batch.
13
+ */
9
14
  _onReplication;
15
+ /**
16
+ * Listen for replication batches without React. On the client this fires after the
17
+ * incoming snapshot/patch has been applied to this instance, so fields are already current.
18
+ * Returns an unsubscribe function.
19
+ */
20
+ subscribe(listener) {
21
+ this._listeners.add(listener);
22
+ return () => { this._listeners.delete(listener); };
23
+ }
24
+ /** Dispatch a batch to the legacy slot and all subscribers. Used by the server flush and the client transport. */
25
+ _emitReplication(batch) {
26
+ this._onReplication?.(batch);
27
+ for (const l of this._listeners)
28
+ l(batch);
29
+ }
30
+ /** True if anything is listening (legacy slot or subscribers). */
31
+ get _hasListeners() {
32
+ return this._onReplication !== undefined || this._listeners.size > 0;
33
+ }
10
34
  set(partial) {
11
35
  if (getStoreType(Object.getPrototypeOf(this).constructor) === 'owner') {
12
36
  throw new Error('@OwnerStore does not support set(). Use setOwner() for all properties.');
@@ -73,7 +97,7 @@ export class BaseStore {
73
97
  });
74
98
  }
75
99
  _flush() {
76
- if (!this._onReplication) {
100
+ if (!this._hasListeners) {
77
101
  // Nobody listening yet — discard dirty state so it doesn't accumulate
78
102
  // and leak into the first incremental patch after _onReplication is set.
79
103
  // (getSnapshot() provides the full initial state on connect.)
@@ -107,7 +131,7 @@ export class BaseStore {
107
131
  }
108
132
  this._dirtyOwner.clear();
109
133
  }
110
- this._onReplication(batch);
134
+ this._emitReplication(batch);
111
135
  }
112
136
  }
113
137
  //# sourceMappingURL=base-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"base-store.js","sourceRoot":"","sources":["../../src/store/base-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"base-store.js","sourceRoot":"","sources":["../../src/store/base-store.ts"],"names":[],"mappings":"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"}