@colyseus/schema 5.0.11 → 5.0.12

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 (58) hide show
  1. package/build/Metadata.d.ts +20 -12
  2. package/build/annotations.d.ts +23 -10
  3. package/build/codegen/cli.cjs +615 -204
  4. package/build/codegen/cli.cjs.map +1 -1
  5. package/build/codegen/languages/dart.d.ts +20 -0
  6. package/build/codegen/types.d.ts +20 -0
  7. package/build/decoder/Resync.d.ts +3 -3
  8. package/build/encoder/ChangeTree.d.ts +22 -9
  9. package/build/encoder/EncodeDescriptor.d.ts +11 -12
  10. package/build/encoder/StateView.d.ts +26 -2
  11. package/build/encoder/changeTree/inheritedFlags.d.ts +1 -1
  12. package/build/encoder/streaming.d.ts +7 -0
  13. package/build/index.cjs +374 -232
  14. package/build/index.cjs.map +1 -1
  15. package/build/index.d.ts +1 -1
  16. package/build/index.js +374 -232
  17. package/build/index.mjs +373 -231
  18. package/build/index.mjs.map +1 -1
  19. package/build/types/builder.d.ts +31 -22
  20. package/build/types/custom/StreamSchema.d.ts +1 -1
  21. package/build/types/symbols.d.ts +4 -10
  22. package/package.json +1 -1
  23. package/src/Metadata.ts +58 -31
  24. package/src/annotations.ts +56 -32
  25. package/src/codegen/api.ts +2 -1
  26. package/src/codegen/languages/c.ts +21 -3
  27. package/src/codegen/languages/csharp.ts +7 -1
  28. package/src/codegen/languages/dart.ts +274 -0
  29. package/src/codegen/languages/haxe.ts +7 -1
  30. package/src/codegen/languages/lua.ts +16 -4
  31. package/src/codegen/languages/ts.ts +5 -0
  32. package/src/codegen/parser.ts +97 -3
  33. package/src/codegen/types.ts +24 -0
  34. package/src/decoder/Resync.ts +8 -8
  35. package/src/encoder/ChangeRecorder.ts +1 -1
  36. package/src/encoder/ChangeTree.ts +41 -26
  37. package/src/encoder/EncodeDescriptor.ts +17 -38
  38. package/src/encoder/EncodeOperation.ts +3 -1
  39. package/src/encoder/Encoder.ts +97 -21
  40. package/src/encoder/Root.ts +18 -20
  41. package/src/encoder/StateView.ts +102 -12
  42. package/src/encoder/changeTree/inheritedFlags.ts +10 -10
  43. package/src/encoder/changeTree/liveIteration.ts +9 -9
  44. package/src/encoder/streaming.ts +8 -0
  45. package/src/encoding/spec.ts +1 -1
  46. package/src/index.ts +2 -2
  47. package/src/types/builder.ts +35 -31
  48. package/src/types/custom/StreamSchema.ts +1 -1
  49. package/src/types/symbols.ts +4 -11
  50. package/src/bench_bloat.ts +0 -173
  51. package/src/bench_churn.ts +0 -121
  52. package/src/bench_decode.ts +0 -221
  53. package/src/bench_decode_mem.ts +0 -165
  54. package/src/bench_encode.ts +0 -108
  55. package/src/bench_init.ts +0 -150
  56. package/src/bench_static.ts +0 -109
  57. package/src/bench_stream.ts +0 -295
  58. package/src/bench_view_cmp.ts +0 -142
@@ -0,0 +1,20 @@
1
+ import { File, Context } from "../types.js";
2
+ import { GenerateOptions } from "../api.js";
3
+ export declare const name = "Dart/Flutter";
4
+ /**
5
+ * Dart Code Generator
6
+ *
7
+ * Emits typed façades over the `colyseus` Flutter package's runtime: one
8
+ * `SchemaRef` subclass per schema, with typed getters over the shared native
9
+ * handle. Collection getters return `MapSchema<T>` / `ArraySchema<T>`, which
10
+ * also carry the field they came from — that is what
11
+ * `callbacks.onAdd(state.players, ...)` registers against.
12
+ */
13
+ /**
14
+ * Generate individual files for each class/interface/enum
15
+ */
16
+ export declare function generate(context: Context, options: GenerateOptions): File[];
17
+ /**
18
+ * Generate a single bundled file containing all classes, interfaces, and enums
19
+ */
20
+ export declare function renderBundle(context: Context, options: GenerateOptions): File;
@@ -37,11 +37,31 @@ export declare class Enum implements IStructure {
37
37
  properties: Property[];
38
38
  addProperty(property: Property): void;
39
39
  }
40
+ /**
41
+ * Statically-extracted `t.quantized()` options. `wrap` is already normalized
42
+ * from the source's `mode` string; emitters derive `range`/`span` via
43
+ * {@link resolveQuantized} so every language ships identical precomputed values.
44
+ */
45
+ export interface QuantizedProperty {
46
+ min: number;
47
+ max: number;
48
+ bits: 8 | 16 | 32;
49
+ wrap: boolean;
50
+ }
51
+ /**
52
+ * Mirror of the runtime's `resolveQuantize()` scale math (wrap spreads 2^bits
53
+ * steps across [min,max); clamp maps the endpoints onto 0 and 2^bits-1).
54
+ */
55
+ export declare function resolveQuantized(q: QuantizedProperty): {
56
+ range: number;
57
+ span: number;
58
+ };
40
59
  export declare class Property {
41
60
  index: number;
42
61
  name: string;
43
62
  type: string;
44
63
  childType: string;
64
+ quantized?: QuantizedProperty;
45
65
  deprecated?: boolean;
46
66
  }
47
67
  export interface File {
@@ -42,8 +42,8 @@ export declare function resyncTouchEntry(decoder: Decoder, ref: any, operation:
42
42
  /**
43
43
  * Mark a collection as present in the payload — even with zero entries.
44
44
  * The sweep only prunes collections reported here: absence means "not part
45
- * of full-sync" (@transient, view-invisible), where pruning would destroy
46
- * live data. Reflected clients have no @transient metadata, so payload
45
+ * of full-sync" (@patchOnly, view-invisible), where pruning would destroy
46
+ * live data. Reflected clients have no @patchOnly metadata, so payload
47
47
  * presence is the only reliable signal.
48
48
  */
49
49
  export declare function resyncMarkPresent(decoder: Decoder, refId: number): void;
@@ -52,7 +52,7 @@ export declare function resyncMarkPresent(decoder: Decoder, refId: number): void
52
52
  * entry the snapshot did not visit.
53
53
  *
54
54
  * Walks the tree from the root — NOT `root.refs` — for three reasons:
55
- * `@transient` fields are never part of a snapshot and must be left alone;
55
+ * `@patchOnly` fields are never part of a snapshot and must be left alone;
56
56
  * entries of subtrees removed by the sweep itself are left to the GC's
57
57
  * transitive walk (sweeping them directly would double-decrement shared
58
58
  * children); and collections the snapshot never mentions (emptied
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * - parentChain.ts addParent / removeParent / find / has / getAll
10
10
  * - liveIteration.ts forEachLive
11
- * - inheritedFlags.ts filter / unreliable / transient / static inheritance
11
+ * - inheritedFlags.ts filter / unreliable / patchOnly / static inheritance
12
12
  * - treeAttachment.ts setRoot / setParent / forEachChild(+WithCtx)
13
13
  *
14
14
  * Public surface on ChangeTree is unchanged — methods are thin pass-throughs
@@ -51,6 +51,7 @@ export interface ChangeTreeNode {
51
51
  export interface ChangeTreeList {
52
52
  next?: ChangeTreeNode;
53
53
  tail?: ChangeTreeNode;
54
+ nextPosition: number;
54
55
  }
55
56
  export declare function createChangeTreeList(): ChangeTreeList;
56
57
  export interface ParentChain {
@@ -59,7 +60,7 @@ export interface ParentChain {
59
60
  next?: ParentChain;
60
61
  }
61
62
  export declare const IS_FILTERED = 1, IS_VISIBILITY_SHARED = 2, IS_NEW = 4;
62
- export declare const IS_UNRELIABLE = 8, IS_TRANSIENT = 16, IS_STATIC = 32;
63
+ export declare const IS_UNRELIABLE = 8, IS_PATCH_ONLY = 16, IS_FULL_STATE_ONLY = 32;
63
64
  export declare const IS_STREAM_COLLECTION = 64;
64
65
  export declare const NEEDS_RESTAGE = 128;
65
66
  /**
@@ -95,8 +96,8 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
95
96
  refTarget: T;
96
97
  metadata: Metadata;
97
98
  /**
98
- * Per-class cache of encoder fn / filter fn / isSchema / filterBitmask /
99
- * metadata, looked up once at construction. The encode loop reads
99
+ * Per-class cache of encoder fn / filter fn / isSchema / metadata /
100
+ * per-field arrays, looked up once at construction. The encode loop reads
100
101
  * `tree.encDescriptor` and never touches `ref.constructor` again. See
101
102
  * EncodeDescriptor.ts.
102
103
  */
@@ -148,10 +149,10 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
148
149
  set isNew(v: boolean);
149
150
  get isUnreliable(): boolean;
150
151
  set isUnreliable(v: boolean);
151
- get isTransient(): boolean;
152
- set isTransient(v: boolean);
153
- get isStatic(): boolean;
154
- set isStatic(v: boolean);
152
+ get isPatchOnly(): boolean;
153
+ set isPatchOnly(v: boolean);
154
+ get isFullStateOnly(): boolean;
155
+ set isFullStateOnly(v: boolean);
155
156
  get isStreamCollection(): boolean;
156
157
  set isStreamCollection(v: boolean);
157
158
  get needsRestage(): boolean;
@@ -159,7 +160,7 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
159
160
  get hasFilteredFields(): boolean;
160
161
  ensureUnreliableRecorder(): ChangeRecorder;
161
162
  isFieldUnreliable(index: number): boolean;
162
- isFieldStatic(index: number): boolean;
163
+ isFieldFullStateOnly(index: number): boolean;
163
164
  isFieldStream(index: number): boolean;
164
165
  constructor(ref: T);
165
166
  private _opAt;
@@ -218,6 +219,18 @@ export declare class ChangeTree<T extends Ref = any> implements ChangeRecorder {
218
219
  * fields (see annotations.ts), so the per-field unreliable flag here
219
220
  * always means "primitive value updates" — the structural-ADD-routes-
220
221
  * reliable footgun for ref-type fields can't reach this code path.
222
+ *
223
+ * `!isNew` holds an `@unreliable` field on the RELIABLE channel until this
224
+ * tree's own ADD has shipped there. A decoder can only apply a field write
225
+ * to a ref it already knows, so a value emitted before the ADD is dropped —
226
+ * permanently, if the field is never written again. `isNew` clears in
227
+ * `endEncode()`, i.e. after a reliable pass, and recording reliably is
228
+ * itself what enqueues the tree for that pass; the state is self-clearing
229
+ * and no tree can be stranded on the wrong channel. Mirrors `encodeAll`,
230
+ * which has always seeded these fields for late joiners.
231
+ *
232
+ * Ordering matters: `isFieldUnreliable` short-circuits on the class-level
233
+ * `hasAnyUnreliable`, so schemas without the modifier never read `flags`.
221
234
  */
222
235
  private _routeAndRecord;
223
236
  change(index: number, operation?: OPERATION): void;
@@ -7,34 +7,33 @@ export interface EncodeDescriptor {
7
7
  isSchema: boolean;
8
8
  /**
9
9
  * Bit i set iff field i has a @view tag. 0 for collection trees.
10
- * Lets `encodeChangeCb` do a single bitwise op instead of a
11
- * per-field metadata[i]?.tag chase.
10
+ * Lets `encodeChangeCb` do a single bitwise op instead of a per-field
11
+ * metadata[i]?.tag chase. Fields 0–31 only, like the bitmasks below —
12
+ * `encodeChangeCb` reads `tags` past that.
12
13
  */
13
14
  filterBitmask: number;
14
15
  /**
15
16
  * Class-level "any field has the flag" booleans + per-field bitmasks.
16
- * Hot path: per-mutation `_routeAndRecord` calls `isFieldStatic` and
17
+ * Hot path: per-mutation `_routeAndRecord` calls `isFieldFullStateOnly` and
17
18
  * `isFieldUnreliable`. The common case is "no static/unreliable fields
18
19
  * anywhere on this class" (booleans short-circuit before the symbol-keyed
19
20
  * metadata lookup); the secondary common case is "this class has some
20
21
  * such fields and we need to know if THIS field is one" — the bitmask
21
22
  * answers in one bitwise op instead of an `Array.includes` linear scan.
22
23
  *
23
- * Bitmasks cover fields 0–31 only (matches the `filterBitmask` limitation).
24
- * Fields ≥32 fall back to `Metadata.hasXAtIndex` — same handling as the
25
- * filter-bitmask path.
24
+ * Bitmasks cover fields 0–31 only shift counts wrap at 32. Fields ≥32
25
+ * fall back to `Metadata.hasXAtIndex`.
26
26
  */
27
- hasAnyStatic: boolean;
27
+ hasAnyFullStateOnly: boolean;
28
28
  hasAnyUnreliable: boolean;
29
29
  hasAnyStream: boolean;
30
30
  /**
31
- * Class-level "any field carries a `@view` tag" covers fields both
32
- * within and beyond index 31 (unlike `filterBitmask`, which only
33
- * captures the low 32). Read by `ChangeTree.hasFilteredFields` to
34
- * decide whether a parent tree must be included in a view's bootstrap.
31
+ * Class-level "any field carries a `@view` tag". Read by
32
+ * `ChangeTree.hasFilteredFields` to decide whether a parent tree must
33
+ * be included in a view's bootstrap.
35
34
  */
36
35
  hasAnyView: boolean;
37
- staticBitmask: number;
36
+ fullStateOnlyBitmask: number;
38
37
  unreliableBitmask: number;
39
38
  /**
40
39
  * Bit i set iff field i holds a `t.stream(...)` collection. Hot encode
@@ -1,5 +1,9 @@
1
1
  import { ChangeTree, Ref } from "./ChangeTree.js";
2
2
  import { OPERATION } from "../encoding/spec.js";
3
+ import type { StreamSchema } from "../types/custom/StreamSchema.js";
4
+ import type { MapSchema } from "../types/custom/MapSchema.js";
5
+ import type { SetSchema } from "../types/custom/SetSchema.js";
6
+ import type { CollectionSchema } from "../types/custom/CollectionSchema.js";
3
7
  export declare function createView(iterable?: boolean): StateView;
4
8
  export declare class StateView {
5
9
  iterable: boolean;
@@ -145,9 +149,29 @@ export declare class StateView {
145
149
  * enqueued into `_pendingByView` so the priority pass drains them
146
150
  * respecting `maxPerTick`.
147
151
  *
148
- * Idempotent on re-subscribe. Subscribing to an already-subscribed
149
- * collection is a no-op.
152
+ * On a streaming collection, pass a `priority` callback to order THIS
153
+ * client's backlog. It receives only the element, so whatever the
154
+ * client sorts by is captured in the closure — nothing is attached to
155
+ * the view, and both the element and the captured entity stay typed:
156
+ *
157
+ * ```ts
158
+ * onJoin(client) {
159
+ * const player = this.state.players.get(client.sessionId);
160
+ * client.view.subscribe(this.state.enemies, (enemy) =>
161
+ * -((enemy.x - player.x) ** 2 + (enemy.y - player.y) ** 2));
162
+ * }
163
+ * ```
164
+ *
165
+ * A per-view callback overrides the collection's declaration-scope
166
+ * `.priority()` for this client only.
167
+ *
168
+ * Idempotent on re-subscribe: subscribing to an already-subscribed
169
+ * collection is a no-op, EXCEPT that a supplied `priority` always
170
+ * replaces the previous one — re-subscribe to retarget the ordering.
171
+ * Omitting the argument leaves any existing callback in place; pass
172
+ * `null` to drop it and fall back to the declaration-scope callback.
150
173
  */
174
+ subscribe<V>(collection: StreamSchema<V> | MapSchema<V, any> | SetSchema<V> | CollectionSchema<V>, priority?: ((element: V) => number) | null): this;
151
175
  subscribe(collection: Ref): this;
152
176
  /**
153
177
  * End a persistent subscription. Queues DELETE for every already-sent
@@ -6,7 +6,7 @@ import { type ChangeTree, type Ref } from "../ChangeTree.js";
6
6
  */
7
7
  export declare function checkIsFiltered(tree: ChangeTree, parent: Ref, parentIndex: number, _isNewChangeTree: boolean): void;
8
8
  /**
9
- * Inherit filter / unreliable / transient / static classification from
9
+ * Inherit filter / unreliable / patchOnly / static classification from
10
10
  * the parent field's annotation. Collections (MapSchema / ArraySchema /
11
11
  * etc.) inherit these from the Schema field that holds them.
12
12
  *
@@ -30,6 +30,13 @@ export interface StreamableState {
30
30
  * Instance-level override: assign to `stream.priority`.
31
31
  */
32
32
  priority?: (view: any, element: any) => number;
33
+ /**
34
+ * Per-view priority registered by `StateView.subscribe(collection, fn)`.
35
+ * Takes precedence over the declaration-scope `priority` for that view.
36
+ * Receives only the element — the client's own entity is captured in
37
+ * the closure, so nothing has to be attached to the view.
38
+ */
39
+ priorityByView?: Map<number, (element: any) => number>;
33
40
  }
34
41
  export declare function createStreamableState(): StreamableState;
35
42
  /** Allocate `_stream` on first use (idempotent). Returns the state. */