@replicast/core 1.0.0-rc.1 → 1.0.0-rc.10
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/decorators/entity.d.ts +43 -0
- package/dist/decorators/entity.d.ts.map +1 -0
- package/dist/decorators/entity.js +75 -0
- package/dist/decorators/entity.js.map +1 -0
- package/dist/decorators/index.d.ts +2 -0
- package/dist/decorators/index.d.ts.map +1 -1
- package/dist/decorators/index.js +1 -0
- package/dist/decorators/index.js.map +1 -1
- package/dist/decorators/metadata.d.ts +5 -1
- package/dist/decorators/metadata.d.ts.map +1 -1
- package/dist/decorators/metadata.js.map +1 -1
- package/dist/entity/base-entity.d.ts +19 -0
- package/dist/entity/base-entity.d.ts.map +1 -0
- package/dist/entity/base-entity.js +18 -0
- package/dist/entity/base-entity.js.map +1 -0
- package/dist/entity/fields.d.ts +21 -0
- package/dist/entity/fields.d.ts.map +1 -0
- package/dist/entity/fields.js +67 -0
- package/dist/entity/fields.js.map +1 -0
- package/dist/entity/registry.d.ts +53 -0
- package/dist/entity/registry.d.ts.map +1 -0
- package/dist/entity/registry.js +167 -0
- package/dist/entity/registry.js.map +1 -0
- package/dist/entity/replicator.d.ts +40 -0
- package/dist/entity/replicator.d.ts.map +1 -0
- package/dist/entity/replicator.js +82 -0
- package/dist/entity/replicator.js.map +1 -0
- package/dist/entity/types.d.ts +29 -0
- package/dist/entity/types.d.ts.map +1 -0
- package/dist/entity/types.js +4 -0
- package/dist/entity/types.js.map +1 -0
- package/dist/entity/visibility.d.ts +11 -0
- package/dist/entity/visibility.d.ts.map +1 -0
- package/dist/entity/visibility.js +10 -0
- package/dist/entity/visibility.js.map +1 -0
- package/dist/entity/world.d.ts +67 -0
- package/dist/entity/world.d.ts.map +1 -0
- package/dist/entity/world.js +183 -0
- package/dist/entity/world.js.map +1 -0
- package/dist/index.d.ts +18 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/types.d.ts +2 -0
- package/dist/lifecycle/types.d.ts.map +1 -1
- package/dist/store/base-store.d.ts +24 -4
- package/dist/store/base-store.d.ts.map +1 -1
- package/dist/store/base-store.js +47 -20
- package/dist/store/base-store.js.map +1 -1
- package/dist/transport/app.d.ts +53 -0
- package/dist/transport/app.d.ts.map +1 -0
- package/dist/transport/app.js +53 -0
- package/dist/transport/app.js.map +1 -0
- package/dist/transport/client.d.ts +13 -1
- package/dist/transport/client.d.ts.map +1 -1
- package/dist/transport/client.js +52 -11
- package/dist/transport/client.js.map +1 -1
- package/dist/transport/context-browser.d.ts +6 -0
- package/dist/transport/context-browser.d.ts.map +1 -0
- package/dist/transport/context-browser.js +11 -0
- package/dist/transport/context-browser.js.map +1 -0
- package/dist/transport/context.d.ts +5 -0
- package/dist/transport/context.d.ts.map +1 -0
- package/dist/transport/context.js +9 -0
- package/dist/transport/context.js.map +1 -0
- package/dist/transport/protocol.d.ts +18 -1
- package/dist/transport/protocol.d.ts.map +1 -1
- package/dist/transport/protocol.js.map +1 -1
- package/dist/transport/server.d.ts +23 -1
- package/dist/transport/server.d.ts.map +1 -1
- package/dist/transport/server.js +176 -15
- package/dist/transport/server.js.map +1 -1
- package/package.json +3 -2
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { EntityWorldView } from '../entity/types.js';
|
|
2
|
+
/** Client lifecycle / server scheduler hooks an entity method can carry. */
|
|
3
|
+
export type EntityHook = 'serverTick' | 'clientSpawn' | 'clientDespawn';
|
|
4
|
+
/** Who may invoke an entity's `@Server` methods: its owner, anyone, or a custom predicate. */
|
|
5
|
+
export type CanCall<T> = 'owner' | 'anyone' | ((callerId: string, entity: T, world: EntityWorldView) => boolean);
|
|
6
|
+
export interface EntityOptions<T = any> {
|
|
7
|
+
/** Wire name for this type. Defaults to the class name (or the Vite plugin's `__replicastName` stamp). */
|
|
8
|
+
name?: string;
|
|
9
|
+
/** Scopes replication. Return false to hide the entity from that viewer. Default: visible to everyone. */
|
|
10
|
+
visibleTo?: (viewerId: string, entity: T, world: EntityWorldView) => boolean;
|
|
11
|
+
/** Authorisation for `@Server` methods on this entity. Default: `'owner'`. */
|
|
12
|
+
canCall?: CanCall<T>;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Registers a class as a replicated entity type. One class per entity type, written once and run on
|
|
16
|
+
* both sides; the field and method decorators below say which parts travel where.
|
|
17
|
+
*/
|
|
18
|
+
export declare function Entity<T extends object = object>(options?: EntityOptions<T>): (_value: abstract new (...args: any[]) => T, context: ClassDecoratorContext) => void;
|
|
19
|
+
/** Replicated to every client that can see the entity; diffed per flush. */
|
|
20
|
+
export declare function Replicated(_value: undefined, context: ClassFieldDecoratorContext): void;
|
|
21
|
+
/** Replicated only to the client identified by `entity.ownerId`. */
|
|
22
|
+
export declare function OwnerReplicated(_value: undefined, context: ClassFieldDecoratorContext): void;
|
|
23
|
+
/**
|
|
24
|
+
* Rounds a numeric field to `step` on the server before diffing, so float noise does not produce
|
|
25
|
+
* updates. A pure modifier: combine with `@Replicated` / `@OwnerReplicated` to actually replicate.
|
|
26
|
+
*/
|
|
27
|
+
export declare function Quantized(step: number): (_value: undefined, context: ClassFieldDecoratorContext) => void;
|
|
28
|
+
/** Runs on the server each sim tick with `dt` (seconds). Absent from the client bundle. */
|
|
29
|
+
export declare function ServerTick(_value: Function, context: ClassMethodDecoratorContext): void;
|
|
30
|
+
/** Runs on the client when the entity enters this viewer's set, after its fields are assigned. */
|
|
31
|
+
export declare function ClientSpawn(_value: Function, context: ClassMethodDecoratorContext): void;
|
|
32
|
+
/** Runs on the client when the entity leaves this viewer's set, before it is dropped. */
|
|
33
|
+
export declare function ClientDespawn(_value: Function, context: ClassMethodDecoratorContext): void;
|
|
34
|
+
export declare function isEntityClass(cls: Function): boolean;
|
|
35
|
+
export declare function getEntityOptions<T = any>(cls: Function): EntityOptions<T> | undefined;
|
|
36
|
+
/**
|
|
37
|
+
* Wire name for an entity type. Read lazily so the Vite plugin's `static __replicastName` stamp wins
|
|
38
|
+
* over `cls.name`, which bundlers and minifiers may rename.
|
|
39
|
+
*/
|
|
40
|
+
export declare function getEntityName(cls: Function): string;
|
|
41
|
+
export declare function getQuantizedFields(proto: object): Record<string, number>;
|
|
42
|
+
export declare function getEntityHooks(proto: object): Record<string, EntityHook>;
|
|
43
|
+
//# sourceMappingURL=entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity.d.ts","sourceRoot":"","sources":["../../src/decorators/entity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAMzD,4EAA4E;AAC5E,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,aAAa,GAAG,eAAe,CAAA;AAEvE,8FAA8F;AAC9F,MAAM,MAAM,OAAO,CAAC,CAAC,IAAI,OAAO,GAAG,QAAQ,GAAG,CAAC,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,eAAe,KAAK,OAAO,CAAC,CAAA;AAEhH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,GAAG;IACpC,0GAA0G;IAC1G,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,0GAA0G;IAC1G,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,KAAK,EAAE,eAAe,KAAK,OAAO,CAAA;IAC5E,8EAA8E;IAC9E,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;CACrB;AAMD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,EAAE,OAAO,GAAE,aAAa,CAAC,CAAC,CAAM,IAC7D,QAAQ,QAAQ,MAAM,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,CAAC,EAAE,SAAS,qBAAqB,KAAG,IAAI,CAGnG;AAED,4EAA4E;AAC5E,wBAAgB,UAAU,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,GAAG,IAAI,CAEvF;AAED,oEAAoE;AACpE,wBAAgB,eAAe,CAAC,MAAM,EAAE,SAAS,EAAE,OAAO,EAAE,0BAA0B,GAAG,IAAI,CAE5F;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,IACnB,QAAQ,SAAS,EAAE,SAAS,0BAA0B,KAAG,IAAI,CAK/E;AAQD,2FAA2F;AAC3F,wBAAgB,UAAU,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAEvF;AAED,kGAAkG;AAClG,wBAAgB,WAAW,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAExF;AAED,yFAAyF;AACzF,wBAAgB,aAAa,CAAC,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAE1F;AAED,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,OAAO,CAEpD;AAED,wBAAgB,gBAAgB,CAAC,CAAC,GAAG,GAAG,EAAE,GAAG,EAAE,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,GAAG,SAAS,CAErF;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,QAAQ,GAAG,MAAM,CAInD;AAED,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAExE;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAExE"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { setPropertyRole } from './metadata.js';
|
|
2
|
+
const entityOptionsKey = Symbol.for('replicast:entityOptions');
|
|
3
|
+
const quantizedFieldsKey = Symbol.for('replicast:quantizedFields');
|
|
4
|
+
const entityHooksKey = Symbol.for('replicast:entityHooks');
|
|
5
|
+
function meta(cls) {
|
|
6
|
+
return cls[Symbol.metadata];
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Registers a class as a replicated entity type. One class per entity type, written once and run on
|
|
10
|
+
* both sides; the field and method decorators below say which parts travel where.
|
|
11
|
+
*/
|
|
12
|
+
export function Entity(options = {}) {
|
|
13
|
+
return function (_value, context) {
|
|
14
|
+
;
|
|
15
|
+
context.metadata[entityOptionsKey] = options;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** Replicated to every client that can see the entity; diffed per flush. */
|
|
19
|
+
export function Replicated(_value, context) {
|
|
20
|
+
setPropertyRole(context.metadata, String(context.name), 'replicated');
|
|
21
|
+
}
|
|
22
|
+
/** Replicated only to the client identified by `entity.ownerId`. */
|
|
23
|
+
export function OwnerReplicated(_value, context) {
|
|
24
|
+
setPropertyRole(context.metadata, String(context.name), 'ownerReplicated');
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Rounds a numeric field to `step` on the server before diffing, so float noise does not produce
|
|
28
|
+
* updates. A pure modifier: combine with `@Replicated` / `@OwnerReplicated` to actually replicate.
|
|
29
|
+
*/
|
|
30
|
+
export function Quantized(step) {
|
|
31
|
+
return function (_value, context) {
|
|
32
|
+
const m = context.metadata;
|
|
33
|
+
const existing = m[quantizedFieldsKey] ?? {};
|
|
34
|
+
m[quantizedFieldsKey] = { ...existing, [String(context.name)]: step };
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
function setHook(context, hook) {
|
|
38
|
+
const m = context.metadata;
|
|
39
|
+
const existing = m[entityHooksKey] ?? {};
|
|
40
|
+
m[entityHooksKey] = { ...existing, [String(context.name)]: hook };
|
|
41
|
+
}
|
|
42
|
+
/** Runs on the server each sim tick with `dt` (seconds). Absent from the client bundle. */
|
|
43
|
+
export function ServerTick(_value, context) {
|
|
44
|
+
setHook(context, 'serverTick');
|
|
45
|
+
}
|
|
46
|
+
/** Runs on the client when the entity enters this viewer's set, after its fields are assigned. */
|
|
47
|
+
export function ClientSpawn(_value, context) {
|
|
48
|
+
setHook(context, 'clientSpawn');
|
|
49
|
+
}
|
|
50
|
+
/** Runs on the client when the entity leaves this viewer's set, before it is dropped. */
|
|
51
|
+
export function ClientDespawn(_value, context) {
|
|
52
|
+
setHook(context, 'clientDespawn');
|
|
53
|
+
}
|
|
54
|
+
export function isEntityClass(cls) {
|
|
55
|
+
return meta(cls)?.[entityOptionsKey] !== undefined;
|
|
56
|
+
}
|
|
57
|
+
export function getEntityOptions(cls) {
|
|
58
|
+
return meta(cls)?.[entityOptionsKey];
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Wire name for an entity type. Read lazily so the Vite plugin's `static __replicastName` stamp wins
|
|
62
|
+
* over `cls.name`, which bundlers and minifiers may rename.
|
|
63
|
+
*/
|
|
64
|
+
export function getEntityName(cls) {
|
|
65
|
+
return cls.__replicastName
|
|
66
|
+
?? getEntityOptions(cls)?.name
|
|
67
|
+
?? cls.name;
|
|
68
|
+
}
|
|
69
|
+
export function getQuantizedFields(proto) {
|
|
70
|
+
return meta(proto.constructor)?.[quantizedFieldsKey] ?? {};
|
|
71
|
+
}
|
|
72
|
+
export function getEntityHooks(proto) {
|
|
73
|
+
return meta(proto.constructor)?.[entityHooksKey] ?? {};
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"entity.js","sourceRoot":"","sources":["../../src/decorators/entity.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { GlobalStore, OwnerStore, Global, OwnerOnly, ServerOnly } from './store.js';
|
|
2
2
|
export { System, Server, Client, Multicast, isSystemKey, isServerClassKey, isClientClassKey } from './system.js';
|
|
3
|
+
export { Entity, Replicated, OwnerReplicated, Quantized, ServerTick, ClientSpawn, ClientDespawn, isEntityClass, getEntityName, getEntityOptions, getEntityHooks, getQuantizedFields, } from './entity.js';
|
|
4
|
+
export type { EntityOptions, EntityHook, CanCall } from './entity.js';
|
|
3
5
|
export { getStoreType, getPropertyRoles, getMethodRoles } from './metadata.js';
|
|
4
6
|
export type { StoreType, PropertyRole, MethodRole } from './metadata.js';
|
|
5
7
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAChH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9E,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAChH,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,aAAa,CAAA;AACpB,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,OAAO,EAAE,MAAM,aAAa,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAC9E,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA"}
|
package/dist/decorators/index.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export { GlobalStore, OwnerStore, Global, OwnerOnly, ServerOnly } from './store.js';
|
|
2
2
|
export { System, Server, Client, Multicast, isSystemKey, isServerClassKey, isClientClassKey } from './system.js';
|
|
3
|
+
export { Entity, Replicated, OwnerReplicated, Quantized, ServerTick, ClientSpawn, ClientDespawn, isEntityClass, getEntityName, getEntityOptions, getEntityHooks, getQuantizedFields, } from './entity.js';
|
|
3
4
|
export { getStoreType, getPropertyRoles, getMethodRoles } from './metadata.js';
|
|
4
5
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAChH,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AACnF,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAA;AAChH,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,aAAa,CAAA;AAEpB,OAAO,EAAE,YAAY,EAAE,gBAAgB,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA"}
|
|
@@ -2,7 +2,11 @@ export declare const storeTypeKey: unique symbol;
|
|
|
2
2
|
export declare const propertyRolesKey: unique symbol;
|
|
3
3
|
export declare const methodRolesKey: unique symbol;
|
|
4
4
|
export type StoreType = 'global' | 'owner';
|
|
5
|
-
|
|
5
|
+
/**
|
|
6
|
+
* Where a decorated field is allowed to travel. `global` / `ownerOnly` are store roles;
|
|
7
|
+
* `replicated` / `ownerReplicated` are the entity equivalents. `serverOnly` is shared by both.
|
|
8
|
+
*/
|
|
9
|
+
export type PropertyRole = 'global' | 'ownerOnly' | 'serverOnly' | 'replicated' | 'ownerReplicated';
|
|
6
10
|
export type MethodRole = 'server' | 'client' | 'multicast';
|
|
7
11
|
export declare function getStoreType(cls: Function): StoreType | undefined;
|
|
8
12
|
export declare function setStoreType(metadata: DecoratorMetadata, type: StoreType): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/decorators/metadata.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,eAAgC,CAAA;AACzD,eAAO,MAAM,gBAAgB,eAAoC,CAAA;AACjE,eAAO,MAAM,cAAc,eAAkC,CAAA;AAE7D,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;AAC1C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../src/decorators/metadata.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,eAAgC,CAAA;AACzD,eAAO,MAAM,gBAAgB,eAAoC,CAAA;AACjE,eAAO,MAAM,cAAc,eAAkC,CAAA;AAE7D,MAAM,MAAM,SAAS,GAAG,QAAQ,GAAG,OAAO,CAAA;AAC1C;;;GAGG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,WAAW,GAAG,YAAY,GAAG,YAAY,GAAG,iBAAiB,CAAA;AACnG,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAA;AAE1D,wBAAgB,YAAY,CAAC,GAAG,EAAE,QAAQ,GAAG,SAAS,GAAG,SAAS,CAGjE;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,iBAAiB,EAAE,IAAI,EAAE,SAAS,GAAG,IAAI,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,YAAY,CAAC,CAG5E;AAED,wBAAgB,eAAe,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,YAAY,GAAG,IAAI,CAIlG;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAGxE;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,iBAAiB,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAI9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/decorators/metadata.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,CAAC;AAAC,MAAc,CAAC,QAAQ,KAAK,MAAM,CAAC,iBAAiB,CAAC,CAAA;AAEvD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAA;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAA;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../src/decorators/metadata.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,CAAC;AAAC,MAAc,CAAC,QAAQ,KAAK,MAAM,CAAC,iBAAiB,CAAC,CAAA;AAEvD,MAAM,CAAC,MAAM,YAAY,GAAG,MAAM,CAAC,qBAAqB,CAAC,CAAA;AACzD,MAAM,CAAC,MAAM,gBAAgB,GAAG,MAAM,CAAC,yBAAyB,CAAC,CAAA;AACjE,MAAM,CAAC,MAAM,cAAc,GAAG,MAAM,CAAC,uBAAuB,CAAC,CAAA;AAU7D,MAAM,UAAU,YAAY,CAAC,GAAa;IACxC,MAAM,IAAI,GAAI,GAAW,CAAC,MAAM,CAAC,QAAQ,CAA+C,CAAA;IACxF,OAAO,IAAI,EAAE,CAAC,YAAY,CAA0B,CAAA;AACtD,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,QAA2B,EAAE,IAAe;IACtE,QAAoC,CAAC,YAAY,CAAC,GAAG,IAAI,CAAA;AAC5D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,MAAM,IAAI,GAAI,KAAa,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAA+C,CAAA;IACtG,OAAQ,IAAI,EAAE,CAAC,gBAAgB,CAAkC,IAAI,EAAE,CAAA;AACzE,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,QAA2B,EAAE,GAAW,EAAE,IAAkB;IAC1F,MAAM,CAAC,GAAG,QAAmC,CAAA;IAC7C,MAAM,QAAQ,GAAI,CAAC,CAAC,gBAAgB,CAAkC,IAAI,EAAE,CAAA;IAC5E,CAAC,CAAC,gBAAgB,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAA;AACpD,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAa;IAC1C,MAAM,IAAI,GAAI,KAAa,CAAC,WAAW,CAAC,MAAM,CAAC,QAAQ,CAA+C,CAAA;IACtG,OAAQ,IAAI,EAAE,CAAC,cAAc,CAAgC,IAAI,EAAE,CAAA;AACrE,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,QAA2B,EAAE,GAAW,EAAE,IAAgB;IACtF,MAAM,CAAC,GAAG,QAAmC,CAAA;IAC7C,MAAM,QAAQ,GAAI,CAAC,CAAC,cAAc,CAAgC,IAAI,EAAE,CAAA;IACxE,CAAC,CAAC,cAAc,CAAC,GAAG,EAAE,GAAG,QAAQ,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,CAAA;AAClD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { EntityId } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Optional base for `@Entity` classes. The runtime assigns these on spawn (server) and on enter
|
|
4
|
+
* (client), so extending is not required — it just makes them visible to TypeScript.
|
|
5
|
+
*
|
|
6
|
+
* Entity constructors must take no arguments: the client mirror creates the instance first and
|
|
7
|
+
* assigns replicated fields afterwards.
|
|
8
|
+
*/
|
|
9
|
+
export declare class BaseEntity {
|
|
10
|
+
/** Server-assigned id. 0 until the entity is spawned. */
|
|
11
|
+
id: EntityId;
|
|
12
|
+
/** The player this entity belongs to, or null. Replicated to every viewer. */
|
|
13
|
+
ownerId: string | null;
|
|
14
|
+
/** True in the server runtime, false on a client mirror. */
|
|
15
|
+
isServer: boolean;
|
|
16
|
+
/** True on the server; on a client, true only for entities this client owns. */
|
|
17
|
+
hasAuthority: boolean;
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=base-entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-entity.d.ts","sourceRoot":"","sources":["../../src/entity/base-entity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAA;AAE1C;;;;;;GAMG;AACH,qBAAa,UAAU;IACrB,yDAAyD;IACzD,EAAE,EAAE,QAAQ,CAAI;IAChB,8EAA8E;IAC9E,OAAO,EAAE,MAAM,GAAG,IAAI,CAAO;IAC7B,4DAA4D;IAC5D,QAAQ,EAAE,OAAO,CAAQ;IACzB,gFAAgF;IAChF,YAAY,EAAE,OAAO,CAAQ;CAC9B"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Optional base for `@Entity` classes. The runtime assigns these on spawn (server) and on enter
|
|
3
|
+
* (client), so extending is not required — it just makes them visible to TypeScript.
|
|
4
|
+
*
|
|
5
|
+
* Entity constructors must take no arguments: the client mirror creates the instance first and
|
|
6
|
+
* assigns replicated fields afterwards.
|
|
7
|
+
*/
|
|
8
|
+
export class BaseEntity {
|
|
9
|
+
/** Server-assigned id. 0 until the entity is spawned. */
|
|
10
|
+
id = 0;
|
|
11
|
+
/** The player this entity belongs to, or null. Replicated to every viewer. */
|
|
12
|
+
ownerId = null;
|
|
13
|
+
/** True in the server runtime, false on a client mirror. */
|
|
14
|
+
isServer = false;
|
|
15
|
+
/** True on the server; on a client, true only for entities this client owns. */
|
|
16
|
+
hasAuthority = false;
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=base-entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"base-entity.js","sourceRoot":"","sources":["../../src/entity/base-entity.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,UAAU;IACrB,yDAAyD;IACzD,EAAE,GAAa,CAAC,CAAA;IAChB,8EAA8E;IAC9E,OAAO,GAAkB,IAAI,CAAA;IAC7B,4DAA4D;IAC5D,QAAQ,GAAY,KAAK,CAAA;IACzB,gFAAgF;IAChF,YAAY,GAAY,KAAK,CAAA;CAC9B"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { EntityConstructor } from './types.js';
|
|
2
|
+
/** Which fields of an entity type travel to whom, and how they are rounded first. */
|
|
3
|
+
export interface FieldPlan {
|
|
4
|
+
replicated: string[];
|
|
5
|
+
ownerReplicated: string[];
|
|
6
|
+
quantized: Record<string, number>;
|
|
7
|
+
}
|
|
8
|
+
/** `ownerId` is a replicated built-in: `@OwnerReplicated` fields and `@Client` calls key off it. */
|
|
9
|
+
export declare const ownerIdField = "ownerId";
|
|
10
|
+
/** Derives the field plan for an entity type. Pure — callers cache it per registered type. */
|
|
11
|
+
export declare function fieldPlanOf(Type: EntityConstructor<object> | Function): FieldPlan;
|
|
12
|
+
/** Rounds a numeric value to `step` so float noise does not produce updates. */
|
|
13
|
+
export declare function quantize(value: unknown, step: number | undefined): unknown;
|
|
14
|
+
/**
|
|
15
|
+
* The replicated field set of one entity as one viewer should see it. `isOwner` adds the
|
|
16
|
+
* `@OwnerReplicated` fields.
|
|
17
|
+
*/
|
|
18
|
+
export declare function snapshotFields(entity: object, plan: FieldPlan, isOwner: boolean): Record<string, unknown>;
|
|
19
|
+
/** Keys of `next` whose value changed since `prev`, plus keys that disappeared (as `undefined`). */
|
|
20
|
+
export declare function diffFields(prev: Record<string, unknown>, next: Record<string, unknown>): Record<string, unknown>;
|
|
21
|
+
//# sourceMappingURL=fields.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.d.ts","sourceRoot":"","sources":["../../src/entity/fields.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD,qFAAqF;AACrF,MAAM,WAAW,SAAS;IACxB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,eAAe,EAAE,MAAM,EAAE,CAAA;IACzB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAClC;AAED,oGAAoG;AACpG,eAAO,MAAM,YAAY,YAAY,CAAA;AAErC,8FAA8F;AAC9F,wBAAgB,WAAW,CAAC,IAAI,EAAE,iBAAiB,CAAC,MAAM,CAAC,GAAG,QAAQ,GAAG,SAAS,CAUjF;AAED,gFAAgF;AAChF,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAG1E;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAQzG;AAgBD,oGAAoG;AACpG,wBAAgB,UAAU,CACxB,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CASzB"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { getPropertyRoles } from '../decorators/metadata.js';
|
|
2
|
+
import { getQuantizedFields } from '../decorators/entity.js';
|
|
3
|
+
/** `ownerId` is a replicated built-in: `@OwnerReplicated` fields and `@Client` calls key off it. */
|
|
4
|
+
export const ownerIdField = 'ownerId';
|
|
5
|
+
/** Derives the field plan for an entity type. Pure — callers cache it per registered type. */
|
|
6
|
+
export function fieldPlanOf(Type) {
|
|
7
|
+
const proto = Type.prototype;
|
|
8
|
+
const roles = getPropertyRoles(proto);
|
|
9
|
+
const replicated = [];
|
|
10
|
+
const ownerReplicated = [];
|
|
11
|
+
for (const [key, role] of Object.entries(roles)) {
|
|
12
|
+
if (role === 'replicated')
|
|
13
|
+
replicated.push(key);
|
|
14
|
+
else if (role === 'ownerReplicated')
|
|
15
|
+
ownerReplicated.push(key);
|
|
16
|
+
}
|
|
17
|
+
return { replicated, ownerReplicated, quantized: getQuantizedFields(proto) };
|
|
18
|
+
}
|
|
19
|
+
/** Rounds a numeric value to `step` so float noise does not produce updates. */
|
|
20
|
+
export function quantize(value, step) {
|
|
21
|
+
if (step === undefined || step <= 0 || typeof value !== 'number' || !Number.isFinite(value))
|
|
22
|
+
return value;
|
|
23
|
+
return Math.round(value / step) * step;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* The replicated field set of one entity as one viewer should see it. `isOwner` adds the
|
|
27
|
+
* `@OwnerReplicated` fields.
|
|
28
|
+
*/
|
|
29
|
+
export function snapshotFields(entity, plan, isOwner) {
|
|
30
|
+
const source = entity;
|
|
31
|
+
const out = { [ownerIdField]: source[ownerIdField] ?? null };
|
|
32
|
+
for (const key of plan.replicated)
|
|
33
|
+
out[key] = quantize(source[key], plan.quantized[key]);
|
|
34
|
+
if (isOwner) {
|
|
35
|
+
for (const key of plan.ownerReplicated)
|
|
36
|
+
out[key] = quantize(source[key], plan.quantized[key]);
|
|
37
|
+
}
|
|
38
|
+
return out;
|
|
39
|
+
}
|
|
40
|
+
/** Shallow value equality: primitives by Object.is, arrays and plain objects one level deep. */
|
|
41
|
+
function valuesEqual(a, b) {
|
|
42
|
+
if (Object.is(a, b))
|
|
43
|
+
return true;
|
|
44
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
45
|
+
return a.length === b.length && a.every((v, i) => Object.is(v, b[i]));
|
|
46
|
+
}
|
|
47
|
+
if (typeof a === 'object' && typeof b === 'object' && a !== null && b !== null) {
|
|
48
|
+
const ka = Object.keys(a), kb = Object.keys(b);
|
|
49
|
+
return ka.length === kb.length
|
|
50
|
+
&& ka.every(k => Object.is(a[k], b[k]));
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
/** Keys of `next` whose value changed since `prev`, plus keys that disappeared (as `undefined`). */
|
|
55
|
+
export function diffFields(prev, next) {
|
|
56
|
+
const patch = {};
|
|
57
|
+
for (const [key, value] of Object.entries(next)) {
|
|
58
|
+
if (!valuesEqual(prev[key], value))
|
|
59
|
+
patch[key] = value;
|
|
60
|
+
}
|
|
61
|
+
for (const key of Object.keys(prev)) {
|
|
62
|
+
if (!(key in next))
|
|
63
|
+
patch[key] = undefined;
|
|
64
|
+
}
|
|
65
|
+
return patch;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=fields.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fields.js","sourceRoot":"","sources":["../../src/entity/fields.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { EntityConstructor, EntityId, EntitySync } from './types.js';
|
|
2
|
+
/** The slice of the transport a mirrored entity needs for its `@Server` calls. */
|
|
3
|
+
export interface EntityCaller {
|
|
4
|
+
callEntity(id: EntityId, method: string, args: unknown[]): Promise<unknown> | void;
|
|
5
|
+
}
|
|
6
|
+
export interface ClientEntityRegistryOptions {
|
|
7
|
+
types: EntityConstructor<object>[];
|
|
8
|
+
/** Reads the local playerId lazily — it is only known after the connection is authenticated. */
|
|
9
|
+
getPlayerId: () => string | null;
|
|
10
|
+
client?: EntityCaller;
|
|
11
|
+
}
|
|
12
|
+
type Listener<T extends object = object> = (entity: T) => void;
|
|
13
|
+
/**
|
|
14
|
+
* The client's mirror of the entities it can currently see. Creates the same class on `enter`,
|
|
15
|
+
* applies `update` patches in place, and calls `@ClientSpawn` / `@ClientDespawn` as entities join
|
|
16
|
+
* and leave this viewer's set.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ClientEntityRegistry {
|
|
19
|
+
private options;
|
|
20
|
+
private entities;
|
|
21
|
+
private infoByName;
|
|
22
|
+
private infoByCtor;
|
|
23
|
+
private spawnListeners;
|
|
24
|
+
private despawnListeners;
|
|
25
|
+
private subscribers;
|
|
26
|
+
private byTypeCache;
|
|
27
|
+
/** Per-type version, bumped only for types actually touched by a sync — keeps an update to one
|
|
28
|
+
* type from invalidating every other type's `byType()` cache (and its `useEntities()` callers). */
|
|
29
|
+
private typeVersions;
|
|
30
|
+
private version;
|
|
31
|
+
constructor(options: ClientEntityRegistryOptions);
|
|
32
|
+
get(id: EntityId): object | undefined;
|
|
33
|
+
all(): object[];
|
|
34
|
+
/** Entities of one type. The array is referentially stable until that type's entities change. */
|
|
35
|
+
byType<T extends object>(Type: EntityConstructor<T>): T[];
|
|
36
|
+
private touchType;
|
|
37
|
+
private hasAuthorityFor;
|
|
38
|
+
/** Called when an entity of `Type` enters this viewer's set, after its fields are assigned. */
|
|
39
|
+
onSpawn<T extends object>(Type: EntityConstructor<T>, listener: Listener<T>): () => void;
|
|
40
|
+
/** Called when an entity of `Type` leaves this viewer's set, before it is dropped. */
|
|
41
|
+
onDespawn<T extends object>(Type: EntityConstructor<T>, listener: Listener<T>): () => void;
|
|
42
|
+
/** Fires once per applied sync, after the whole batch is in place. For game loops and React. */
|
|
43
|
+
subscribe(listener: () => void): () => void;
|
|
44
|
+
/** Applies one `{ enter, update, leave }` delta from the server. */
|
|
45
|
+
applySync(sync: EntitySync): void;
|
|
46
|
+
/** Dispatches a server-pushed `@Client` / `@Multicast` call onto the mirrored entity. */
|
|
47
|
+
applyRpc(id: EntityId, method: string, args: unknown[]): void;
|
|
48
|
+
/** Despawns every mirror — used when the connection drops so a reconnect starts clean. */
|
|
49
|
+
clear(): void;
|
|
50
|
+
private remove;
|
|
51
|
+
}
|
|
52
|
+
export {};
|
|
53
|
+
//# sourceMappingURL=registry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.d.ts","sourceRoot":"","sources":["../../src/entity/registry.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,iBAAiB,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAA;AAEzE,kFAAkF;AAClF,MAAM,WAAW,YAAY;IAC3B,UAAU,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAAA;CACnF;AAED,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,iBAAiB,CAAC,MAAM,CAAC,EAAE,CAAA;IAClC,gGAAgG;IAChG,WAAW,EAAE,MAAM,MAAM,GAAG,IAAI,CAAA;IAChC,MAAM,CAAC,EAAE,YAAY,CAAA;CACtB;AAQD,KAAK,QAAQ,CAAC,CAAC,SAAS,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,EAAE,CAAC,KAAK,IAAI,CAAA;AAE9D;;;;GAIG;AACH,qBAAa,oBAAoB;IAanB,OAAO,CAAC,OAAO;IAZ3B,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,UAAU,CAA8B;IAChD,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,cAAc,CAAqC;IAC3D,OAAO,CAAC,gBAAgB,CAAqC;IAC7D,OAAO,CAAC,WAAW,CAAwB;IAC3C,OAAO,CAAC,WAAW,CAA2D;IAC9E;wGACoG;IACpG,OAAO,CAAC,YAAY,CAA8B;IAClD,OAAO,CAAC,OAAO,CAAI;gBAEC,OAAO,EAAE,2BAA2B;IAqBxD,GAAG,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,GAAG,SAAS;IAIrC,GAAG,IAAI,MAAM,EAAE;IAIf,iGAAiG;IACjG,MAAM,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE;IASzD,OAAO,CAAC,SAAS;IAKjB,OAAO,CAAC,eAAe;IAIvB,+FAA+F;IAC/F,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAIxF,sFAAsF;IACtF,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI;IAI1F,gGAAgG;IAChG,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;IAK3C,oEAAoE;IACpE,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;IAsCjC,yFAAyF;IACzF,QAAQ,CAAC,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,IAAI;IAO7D,0FAA0F;IAC1F,KAAK,IAAI,IAAI;IAOb,OAAO,CAAC,MAAM;CASf"}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { getEntityHooks, getEntityName } from '../decorators/entity.js';
|
|
2
|
+
import { isEmptySync } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The client's mirror of the entities it can currently see. Creates the same class on `enter`,
|
|
5
|
+
* applies `update` patches in place, and calls `@ClientSpawn` / `@ClientDespawn` as entities join
|
|
6
|
+
* and leave this viewer's set.
|
|
7
|
+
*/
|
|
8
|
+
export class ClientEntityRegistry {
|
|
9
|
+
options;
|
|
10
|
+
entities = new Map();
|
|
11
|
+
infoByName = new Map();
|
|
12
|
+
infoByCtor = new Map();
|
|
13
|
+
spawnListeners = new Map();
|
|
14
|
+
despawnListeners = new Map();
|
|
15
|
+
subscribers = new Set();
|
|
16
|
+
byTypeCache = new Map();
|
|
17
|
+
/** Per-type version, bumped only for types actually touched by a sync — keeps an update to one
|
|
18
|
+
* type from invalidating every other type's `byType()` cache (and its `useEntities()` callers). */
|
|
19
|
+
typeVersions = new Map();
|
|
20
|
+
version = 0;
|
|
21
|
+
constructor(options) {
|
|
22
|
+
this.options = options;
|
|
23
|
+
for (const Type of options.types) {
|
|
24
|
+
const proto = Type.prototype;
|
|
25
|
+
const hooks = getEntityHooks(proto);
|
|
26
|
+
const name = getEntityName(Type);
|
|
27
|
+
const existing = this.infoByName.get(name);
|
|
28
|
+
if (existing && existing.Type !== Type) {
|
|
29
|
+
throw new Error(`[replicast] two @Entity classes are both named "${name}" — give one an explicit @Entity({ name }) option`);
|
|
30
|
+
}
|
|
31
|
+
const info = {
|
|
32
|
+
Type,
|
|
33
|
+
spawnHooks: Object.keys(hooks).filter(m => hooks[m] === 'clientSpawn'),
|
|
34
|
+
despawnHooks: Object.keys(hooks).filter(m => hooks[m] === 'clientDespawn'),
|
|
35
|
+
};
|
|
36
|
+
this.infoByName.set(name, info);
|
|
37
|
+
this.infoByCtor.set(Type, info);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
get(id) {
|
|
41
|
+
return this.entities.get(id);
|
|
42
|
+
}
|
|
43
|
+
all() {
|
|
44
|
+
return [...this.entities.values()];
|
|
45
|
+
}
|
|
46
|
+
/** Entities of one type. The array is referentially stable until that type's entities change. */
|
|
47
|
+
byType(Type) {
|
|
48
|
+
const version = this.typeVersions.get(Type) ?? 0;
|
|
49
|
+
const cached = this.byTypeCache.get(Type);
|
|
50
|
+
if (cached?.version === version)
|
|
51
|
+
return cached.list;
|
|
52
|
+
const list = this.all().filter(e => e instanceof Type);
|
|
53
|
+
this.byTypeCache.set(Type, { version, list });
|
|
54
|
+
return list;
|
|
55
|
+
}
|
|
56
|
+
touchType(entity) {
|
|
57
|
+
const Type = Object.getPrototypeOf(entity).constructor;
|
|
58
|
+
this.typeVersions.set(Type, (this.typeVersions.get(Type) ?? 0) + 1);
|
|
59
|
+
}
|
|
60
|
+
hasAuthorityFor(entity) {
|
|
61
|
+
return entity['ownerId'] != null && entity['ownerId'] === this.options.getPlayerId();
|
|
62
|
+
}
|
|
63
|
+
/** Called when an entity of `Type` enters this viewer's set, after its fields are assigned. */
|
|
64
|
+
onSpawn(Type, listener) {
|
|
65
|
+
return addListener(this.spawnListeners, Type, listener);
|
|
66
|
+
}
|
|
67
|
+
/** Called when an entity of `Type` leaves this viewer's set, before it is dropped. */
|
|
68
|
+
onDespawn(Type, listener) {
|
|
69
|
+
return addListener(this.despawnListeners, Type, listener);
|
|
70
|
+
}
|
|
71
|
+
/** Fires once per applied sync, after the whole batch is in place. For game loops and React. */
|
|
72
|
+
subscribe(listener) {
|
|
73
|
+
this.subscribers.add(listener);
|
|
74
|
+
return () => { this.subscribers.delete(listener); };
|
|
75
|
+
}
|
|
76
|
+
/** Applies one `{ enter, update, leave }` delta from the server. */
|
|
77
|
+
applySync(sync) {
|
|
78
|
+
if (isEmptySync(sync))
|
|
79
|
+
return;
|
|
80
|
+
for (const { type, id, fields } of sync.enter) {
|
|
81
|
+
const info = this.infoByName.get(type);
|
|
82
|
+
if (!info) {
|
|
83
|
+
console.warn(`[replicast] received an unknown entity type "${type}" — register it in createApp({ entities })`);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const entity = new info.Type();
|
|
87
|
+
entity['id'] = id;
|
|
88
|
+
entity['isServer'] = false;
|
|
89
|
+
if (this.options.client)
|
|
90
|
+
entity['__wsClient'] = this.options.client;
|
|
91
|
+
Object.assign(entity, fields);
|
|
92
|
+
entity['hasAuthority'] = this.hasAuthorityFor(entity);
|
|
93
|
+
this.entities.set(id, entity);
|
|
94
|
+
this.touchType(entity);
|
|
95
|
+
callHooks(entity, info.spawnHooks);
|
|
96
|
+
notify(this.spawnListeners, entity);
|
|
97
|
+
}
|
|
98
|
+
for (const { id, fields } of sync.update) {
|
|
99
|
+
const entity = this.entities.get(id);
|
|
100
|
+
if (entity) {
|
|
101
|
+
Object.assign(entity, fields);
|
|
102
|
+
// ownerId is a replicated built-in and can change on an already-mirrored entity
|
|
103
|
+
// (e.g. a ship changes hands) — hasAuthority must track it, not just the initial enter.
|
|
104
|
+
entity['hasAuthority'] = this.hasAuthorityFor(entity);
|
|
105
|
+
this.touchType(entity);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
for (const id of sync.leave)
|
|
109
|
+
this.remove(id);
|
|
110
|
+
this.version++;
|
|
111
|
+
for (const listener of this.subscribers)
|
|
112
|
+
listener();
|
|
113
|
+
}
|
|
114
|
+
/** Dispatches a server-pushed `@Client` / `@Multicast` call onto the mirrored entity. */
|
|
115
|
+
applyRpc(id, method, args) {
|
|
116
|
+
const entity = this.entities.get(id);
|
|
117
|
+
const methodName = method.split('.')[1];
|
|
118
|
+
if (!entity || !methodName || typeof entity[methodName] !== 'function')
|
|
119
|
+
return;
|
|
120
|
+
entity[methodName].apply(entity, args);
|
|
121
|
+
}
|
|
122
|
+
/** Despawns every mirror — used when the connection drops so a reconnect starts clean. */
|
|
123
|
+
clear() {
|
|
124
|
+
if (this.entities.size === 0)
|
|
125
|
+
return;
|
|
126
|
+
for (const id of [...this.entities.keys()])
|
|
127
|
+
this.remove(id);
|
|
128
|
+
this.version++;
|
|
129
|
+
for (const listener of this.subscribers)
|
|
130
|
+
listener();
|
|
131
|
+
}
|
|
132
|
+
remove(id) {
|
|
133
|
+
const entity = this.entities.get(id);
|
|
134
|
+
if (!entity)
|
|
135
|
+
return;
|
|
136
|
+
const info = this.infoByCtor.get(Object.getPrototypeOf(entity).constructor);
|
|
137
|
+
callHooks(entity, info?.despawnHooks ?? []);
|
|
138
|
+
this.entities.delete(id);
|
|
139
|
+
this.touchType(entity);
|
|
140
|
+
notify(this.despawnListeners, entity);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
function addListener(map, Type, listener) {
|
|
144
|
+
let set = map.get(Type);
|
|
145
|
+
if (!set) {
|
|
146
|
+
set = new Set();
|
|
147
|
+
map.set(Type, set);
|
|
148
|
+
}
|
|
149
|
+
set.add(listener);
|
|
150
|
+
return () => { set.delete(listener); };
|
|
151
|
+
}
|
|
152
|
+
function notify(map, entity) {
|
|
153
|
+
for (const [Type, listeners] of map) {
|
|
154
|
+
if (!(entity instanceof Type))
|
|
155
|
+
continue;
|
|
156
|
+
for (const listener of listeners)
|
|
157
|
+
listener(entity);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
function callHooks(entity, methods) {
|
|
161
|
+
for (const method of methods) {
|
|
162
|
+
const fn = entity[method];
|
|
163
|
+
if (typeof fn === 'function')
|
|
164
|
+
fn.call(entity);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/entity/registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import type { FieldPlan } from './fields.js';
|
|
2
|
+
import type { VisibleTo } from './visibility.js';
|
|
3
|
+
import type { EntityId, EntitySync, EntityWorldView } from './types.js';
|
|
4
|
+
/** One live entity as the replicator needs to see it: identity, wire type, field plan, scoping. */
|
|
5
|
+
export interface ReplicationEntry {
|
|
6
|
+
id: EntityId;
|
|
7
|
+
entity: object;
|
|
8
|
+
type: string;
|
|
9
|
+
plan: FieldPlan;
|
|
10
|
+
visibleTo?: VisibleTo;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Tracks, per client, which entities that client has been sent and the fields it last saw, and turns
|
|
14
|
+
* the current world into that client's `{ enter, update, leave }` delta.
|
|
15
|
+
*
|
|
16
|
+
* Cost is O(clients x entities) per flush — deliberate for now; a spatial index hook comes later.
|
|
17
|
+
*/
|
|
18
|
+
export declare class EntityReplicator {
|
|
19
|
+
/** playerId → entityId → last-sent field snapshot. */
|
|
20
|
+
private views;
|
|
21
|
+
/** True when `playerId` currently has `id` in their set (i.e. as of the last flush). */
|
|
22
|
+
hasInView(playerId: string, id: EntityId): boolean;
|
|
23
|
+
/** The clients that currently see `id` — the audience for a `@Multicast`. */
|
|
24
|
+
viewersOf(id: EntityId): string[];
|
|
25
|
+
/** Forget a disconnected client so they re-enter from scratch on reconnect. */
|
|
26
|
+
dropViewer(playerId: string): void;
|
|
27
|
+
/**
|
|
28
|
+
* Records `id` as already sent to `playerId` with `fields`, without waiting for the next flush —
|
|
29
|
+
* for a caller that must guarantee delivery order (an `@Client`/`@Multicast` send that needs the
|
|
30
|
+
* recipient's mirror to exist before the rpc arrives). The next real `flush()` then sees this id
|
|
31
|
+
* already in `view` and sends only a diff, never a duplicate `enter`.
|
|
32
|
+
*/
|
|
33
|
+
recordEnter(playerId: string, id: EntityId, fields: Record<string, unknown>): void;
|
|
34
|
+
/**
|
|
35
|
+
* Builds one client's delta and records it as sent. An entity is visible when the client owns it
|
|
36
|
+
* (owners always see their own entities) or the type's `visibleTo` hook allows it.
|
|
37
|
+
*/
|
|
38
|
+
computeSync(playerId: string, entries: Iterable<ReplicationEntry>, world: EntityWorldView): EntitySync;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=replicator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"replicator.d.ts","sourceRoot":"","sources":["../../src/entity/replicator.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAChD,OAAO,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,eAAe,EAAE,MAAM,YAAY,CAAA;AAEvE,mGAAmG;AACnG,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,QAAQ,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,SAAS,CAAA;IACf,SAAS,CAAC,EAAE,SAAS,CAAA;CACtB;AAED;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,sDAAsD;IACtD,OAAO,CAAC,KAAK,CAA4D;IAEzE,wFAAwF;IACxF,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,GAAG,OAAO;IAIlD,6EAA6E;IAC7E,SAAS,CAAC,EAAE,EAAE,QAAQ,GAAG,MAAM,EAAE;IAQjC,+EAA+E;IAC/E,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAIlC;;;;;OAKG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI;IAMlF;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,CAAC,gBAAgB,CAAC,EAAE,KAAK,EAAE,eAAe,GAAG,UAAU;CAmCvG"}
|