@colyseus/schema 3.0.0-alpha.5 → 3.0.0-alpha.50

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 (148) hide show
  1. package/README.md +148 -62
  2. package/bin/schema-debug +94 -0
  3. package/build/cjs/index.js +2234 -1510
  4. package/build/cjs/index.js.map +1 -1
  5. package/build/esm/index.mjs +2231 -1509
  6. package/build/esm/index.mjs.map +1 -1
  7. package/build/umd/index.js +2233 -1509
  8. package/lib/Metadata.d.ts +21 -9
  9. package/lib/Metadata.js +169 -32
  10. package/lib/Metadata.js.map +1 -1
  11. package/lib/Reflection.d.ts +19 -4
  12. package/lib/Reflection.js +66 -31
  13. package/lib/Reflection.js.map +1 -1
  14. package/lib/Schema.d.ts +12 -5
  15. package/lib/Schema.js +57 -56
  16. package/lib/Schema.js.map +1 -1
  17. package/lib/annotations.d.ts +31 -34
  18. package/lib/annotations.js +110 -160
  19. package/lib/annotations.js.map +1 -1
  20. package/lib/bench_encode.d.ts +1 -0
  21. package/lib/bench_encode.js +130 -0
  22. package/lib/bench_encode.js.map +1 -0
  23. package/lib/codegen/api.js +1 -2
  24. package/lib/codegen/api.js.map +1 -1
  25. package/lib/codegen/languages/cpp.js +1 -2
  26. package/lib/codegen/languages/cpp.js.map +1 -1
  27. package/lib/codegen/languages/csharp.js +2 -46
  28. package/lib/codegen/languages/csharp.js.map +1 -1
  29. package/lib/codegen/languages/haxe.js +4 -2
  30. package/lib/codegen/languages/haxe.js.map +1 -1
  31. package/lib/codegen/languages/java.js +1 -2
  32. package/lib/codegen/languages/java.js.map +1 -1
  33. package/lib/codegen/languages/js.js +1 -2
  34. package/lib/codegen/languages/js.js.map +1 -1
  35. package/lib/codegen/languages/lua.js +23 -25
  36. package/lib/codegen/languages/lua.js.map +1 -1
  37. package/lib/codegen/languages/ts.js +1 -2
  38. package/lib/codegen/languages/ts.js.map +1 -1
  39. package/lib/codegen/parser.js +85 -3
  40. package/lib/codegen/parser.js.map +1 -1
  41. package/lib/codegen/types.js +6 -3
  42. package/lib/codegen/types.js.map +1 -1
  43. package/lib/debug.d.ts +1 -0
  44. package/lib/debug.js +51 -0
  45. package/lib/debug.js.map +1 -0
  46. package/lib/decoder/DecodeOperation.d.ts +3 -4
  47. package/lib/decoder/DecodeOperation.js +37 -19
  48. package/lib/decoder/DecodeOperation.js.map +1 -1
  49. package/lib/decoder/Decoder.d.ts +6 -7
  50. package/lib/decoder/Decoder.js +14 -14
  51. package/lib/decoder/Decoder.js.map +1 -1
  52. package/lib/decoder/ReferenceTracker.js +4 -2
  53. package/lib/decoder/ReferenceTracker.js.map +1 -1
  54. package/lib/decoder/strategy/RawChanges.js +1 -2
  55. package/lib/decoder/strategy/RawChanges.js.map +1 -1
  56. package/lib/decoder/strategy/StateCallbacks.d.ts +44 -11
  57. package/lib/decoder/strategy/StateCallbacks.js +75 -65
  58. package/lib/decoder/strategy/StateCallbacks.js.map +1 -1
  59. package/lib/encoder/ChangeTree.d.ts +28 -20
  60. package/lib/encoder/ChangeTree.js +242 -188
  61. package/lib/encoder/ChangeTree.js.map +1 -1
  62. package/lib/encoder/EncodeOperation.d.ts +3 -6
  63. package/lib/encoder/EncodeOperation.js +51 -65
  64. package/lib/encoder/EncodeOperation.js.map +1 -1
  65. package/lib/encoder/Encoder.d.ts +9 -8
  66. package/lib/encoder/Encoder.js +134 -85
  67. package/lib/encoder/Encoder.js.map +1 -1
  68. package/lib/encoder/Root.d.ts +22 -0
  69. package/lib/encoder/Root.js +81 -0
  70. package/lib/encoder/Root.js.map +1 -0
  71. package/lib/encoder/StateView.d.ts +7 -7
  72. package/lib/encoder/StateView.js +72 -74
  73. package/lib/encoder/StateView.js.map +1 -1
  74. package/lib/encoding/assert.d.ts +7 -6
  75. package/lib/encoding/assert.js +13 -5
  76. package/lib/encoding/assert.js.map +1 -1
  77. package/lib/encoding/decode.d.ts +36 -19
  78. package/lib/encoding/decode.js +54 -84
  79. package/lib/encoding/decode.js.map +1 -1
  80. package/lib/encoding/encode.d.ts +36 -17
  81. package/lib/encoding/encode.js +82 -68
  82. package/lib/encoding/encode.js.map +1 -1
  83. package/lib/encoding/spec.d.ts +4 -5
  84. package/lib/encoding/spec.js +1 -2
  85. package/lib/encoding/spec.js.map +1 -1
  86. package/lib/index.d.ts +10 -9
  87. package/lib/index.js +24 -17
  88. package/lib/index.js.map +1 -1
  89. package/lib/types/HelperTypes.d.ts +34 -2
  90. package/lib/types/HelperTypes.js.map +1 -1
  91. package/lib/types/TypeContext.d.ts +29 -0
  92. package/lib/types/TypeContext.js +151 -0
  93. package/lib/types/TypeContext.js.map +1 -0
  94. package/lib/types/custom/ArraySchema.d.ts +2 -2
  95. package/lib/types/custom/ArraySchema.js +33 -22
  96. package/lib/types/custom/ArraySchema.js.map +1 -1
  97. package/lib/types/custom/CollectionSchema.js +1 -0
  98. package/lib/types/custom/CollectionSchema.js.map +1 -1
  99. package/lib/types/custom/MapSchema.d.ts +17 -15
  100. package/lib/types/custom/MapSchema.js +12 -4
  101. package/lib/types/custom/MapSchema.js.map +1 -1
  102. package/lib/types/custom/SetSchema.js +1 -0
  103. package/lib/types/custom/SetSchema.js.map +1 -1
  104. package/lib/types/registry.d.ts +8 -1
  105. package/lib/types/registry.js +23 -6
  106. package/lib/types/registry.js.map +1 -1
  107. package/lib/types/symbols.d.ts +8 -5
  108. package/lib/types/symbols.js +9 -6
  109. package/lib/types/symbols.js.map +1 -1
  110. package/lib/types/utils.js +1 -2
  111. package/lib/types/utils.js.map +1 -1
  112. package/lib/utils.js +9 -7
  113. package/lib/utils.js.map +1 -1
  114. package/package.json +19 -18
  115. package/src/Metadata.ts +190 -42
  116. package/src/Reflection.ts +76 -38
  117. package/src/Schema.ts +72 -70
  118. package/src/annotations.ts +156 -202
  119. package/src/bench_encode.ts +108 -0
  120. package/src/codegen/languages/csharp.ts +1 -47
  121. package/src/codegen/languages/haxe.ts +4 -0
  122. package/src/codegen/languages/lua.ts +19 -27
  123. package/src/codegen/parser.ts +107 -0
  124. package/src/codegen/types.ts +1 -0
  125. package/src/debug.ts +55 -0
  126. package/src/decoder/DecodeOperation.ts +46 -18
  127. package/src/decoder/Decoder.ts +17 -15
  128. package/src/decoder/ReferenceTracker.ts +5 -3
  129. package/src/decoder/strategy/StateCallbacks.ts +153 -82
  130. package/src/encoder/ChangeTree.ts +282 -209
  131. package/src/encoder/EncodeOperation.ts +78 -78
  132. package/src/encoder/Encoder.ts +161 -96
  133. package/src/encoder/Root.ts +93 -0
  134. package/src/encoder/StateView.ts +80 -88
  135. package/src/encoding/assert.ts +17 -8
  136. package/src/encoding/decode.ts +73 -93
  137. package/src/encoding/encode.ts +99 -65
  138. package/src/encoding/spec.ts +3 -5
  139. package/src/index.ts +12 -20
  140. package/src/types/HelperTypes.ts +54 -2
  141. package/src/types/TypeContext.ts +175 -0
  142. package/src/types/custom/ArraySchema.ts +49 -19
  143. package/src/types/custom/CollectionSchema.ts +1 -0
  144. package/src/types/custom/MapSchema.ts +30 -17
  145. package/src/types/custom/SetSchema.ts +1 -0
  146. package/src/types/registry.ts +22 -3
  147. package/src/types/symbols.ts +10 -7
  148. package/src/utils.ts +7 -3
@@ -1,43 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Encoder = void 0;
4
- const annotations_1 = require("../annotations");
4
+ const TypeContext_1 = require("../types/TypeContext");
5
5
  const symbols_1 = require("../types/symbols");
6
- const encode = require("../encoding/encode");
6
+ const encode_1 = require("../encoding/encode");
7
7
  const spec_1 = require("../encoding/spec");
8
- const ChangeTree_1 = require("./ChangeTree");
8
+ const Root_1 = require("./Root");
9
9
  const utils_1 = require("../utils");
10
10
  class Encoder {
11
11
  static { this.BUFFER_SIZE = 8 * 1024; } // 8KB
12
- constructor(root) {
12
+ constructor(state) {
13
13
  this.sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);
14
- this.setRoot(root);
15
14
  //
16
15
  // TODO: cache and restore "Context" based on root schema
17
16
  // (to avoid creating a new context for every new room)
18
17
  //
19
- this.context = new annotations_1.TypeContext(root.constructor);
18
+ this.context = new TypeContext_1.TypeContext(state.constructor);
19
+ this.root = new Root_1.Root(this.context);
20
+ this.setState(state);
20
21
  // console.log(">>>>>>>>>>>>>>>> Encoder types");
21
22
  // this.context.schemas.forEach((id, schema) => {
22
23
  // console.log("type:", id, schema.name, Object.keys(schema[Symbol.metadata]));
23
24
  // });
24
25
  }
25
- setRoot(state) {
26
- this.root = new ChangeTree_1.Root();
26
+ setState(state) {
27
27
  this.state = state;
28
- state[symbols_1.$changes].setRoot(this.root);
28
+ this.state[symbols_1.$changes].setRoot(this.root);
29
29
  }
30
- encode(it = { offset: 0 }, view, bytes = this.sharedBuffer, changeTrees = this.root.changes) {
31
- const initialOffset = it.offset; // cache current offset in case we need to resize the buffer
32
- const isEncodeAll = this.root.allChanges === changeTrees;
30
+ encode(it = { offset: 0 }, view, buffer = this.sharedBuffer, changeSetName = "changes", isEncodeAll = changeSetName === "allChanges", initialOffset = it.offset // cache current offset in case we need to resize the buffer
31
+ ) {
33
32
  const hasView = (view !== undefined);
34
33
  const rootChangeTree = this.state[symbols_1.$changes];
35
- const changeTreesIterator = changeTrees.entries();
36
- for (const [changeTree, changes] of changeTreesIterator) {
34
+ const shouldDiscardChanges = !isEncodeAll && !hasView;
35
+ const changeTrees = this.root[changeSetName];
36
+ for (let i = 0, numChangeTrees = changeTrees.length; i < numChangeTrees; i++) {
37
+ const changeTree = changeTrees[i];
38
+ const operations = changeTree[changeSetName];
37
39
  const ref = changeTree.ref;
38
- const ctor = ref['constructor'];
40
+ const ctor = ref.constructor;
39
41
  const encoder = ctor[symbols_1.$encoder];
40
42
  const filter = ctor[symbols_1.$filter];
43
+ const metadata = ctor[Symbol.metadata];
41
44
  if (hasView) {
42
45
  if (!view.items.has(changeTree)) {
43
46
  view.invisible.add(changeTree);
@@ -48,12 +51,18 @@ class Encoder {
48
51
  }
49
52
  }
50
53
  // skip root `refId` if it's the first change tree
51
- if (it.offset !== initialOffset || changeTree !== rootChangeTree) {
52
- bytes[it.offset++] = spec_1.SWITCH_TO_STRUCTURE & 255;
53
- encode.number(bytes, changeTree.refId, it);
54
+ // (unless it "hasView", which will need to revisit the root)
55
+ if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {
56
+ buffer[it.offset++] = spec_1.SWITCH_TO_STRUCTURE & 255;
57
+ encode_1.encode.number(buffer, changeTree.refId, it);
54
58
  }
55
- const changesIterator = changes.entries();
56
- for (const [fieldIndex, operation] of changesIterator) {
59
+ for (let j = 0, numChanges = operations.operations.length; j < numChanges; j++) {
60
+ const fieldIndex = operations.operations[j];
61
+ const operation = (fieldIndex < 0)
62
+ ? Math.abs(fieldIndex) // "pure" operation without fieldIndex (e.g. CLEAR, REVERSE, etc.)
63
+ : (isEncodeAll)
64
+ ? spec_1.OPERATION.ADD
65
+ : changeTree.indexedOperations[fieldIndex];
57
66
  //
58
67
  // first pass (encodeAll), identify "filtered" operations without encoding them
59
68
  // they will be encoded per client, based on their view.
@@ -61,93 +70,109 @@ class Encoder {
61
70
  // TODO: how can we optimize filtering out "encode all" operations?
62
71
  // TODO: avoid checking if no view tags were defined
63
72
  //
64
- if (filter && !filter(ref, fieldIndex, view)) {
65
- // console.log("SKIP FIELD:", { ref: changeTree.ref.constructor.name, fieldIndex, })
73
+ if (fieldIndex === undefined || operation === undefined || (filter && !filter(ref, fieldIndex, view))) {
66
74
  // console.log("ADD AS INVISIBLE:", fieldIndex, changeTree.ref.constructor.name)
67
75
  // view?.invisible.add(changeTree);
68
76
  continue;
69
77
  }
70
- // console.log("WILL ENCODE", {
71
- // ref: changeTree.ref.constructor.name,
72
- // fieldIndex,
73
- // operation: OPERATION[operation],
74
- // });
75
- encoder(this, bytes, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);
78
+ encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView, metadata);
76
79
  }
80
+ // if (shouldDiscardChanges) {
81
+ // changeTree.discard();
82
+ // changeTree.isNew = false; // Not a new instance anymore
83
+ // }
77
84
  }
78
- if (it.offset > bytes.byteLength) {
79
- const newSize = (0, utils_1.getNextPowerOf2)(this.sharedBuffer.byteLength * 2);
80
- console.warn("@colyseus/schema encode buffer overflow. Current buffer size: " + bytes.byteLength + ", encoding offset: " + it.offset + ", new size: " + newSize);
85
+ if (it.offset > buffer.byteLength) {
86
+ const newSize = (0, utils_1.getNextPowerOf2)(buffer.byteLength * 2);
87
+ console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:
88
+
89
+ import { Encoder } from "@colyseus/schema";
90
+ Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB
91
+ `);
81
92
  //
82
93
  // resize buffer and re-encode (TODO: can we avoid re-encoding here?)
83
94
  //
84
- this.sharedBuffer = Buffer.allocUnsafeSlow(newSize);
85
- return this.encode({ offset: initialOffset }, view);
95
+ buffer = Buffer.alloc(newSize);
96
+ // assign resized buffer to local sharedBuffer
97
+ if (buffer === this.sharedBuffer) {
98
+ this.sharedBuffer = buffer;
99
+ }
100
+ return this.encode({ offset: initialOffset }, view, buffer, changeSetName, isEncodeAll);
86
101
  }
87
102
  else {
88
103
  //
89
104
  // only clear changes after making sure buffer resize is not required.
90
105
  //
91
- if (!isEncodeAll && !hasView) {
106
+ if (shouldDiscardChanges) {
92
107
  //
93
- // FIXME: avoid iterating over change trees twice.
108
+ // TODO: avoid iterating over change trees twice.
94
109
  //
95
- this.onEndEncode(changeTrees);
110
+ for (let i = 0, numChangeTrees = changeTrees.length; i < numChangeTrees; i++) {
111
+ const changeTree = changeTrees[i];
112
+ changeTree.discard();
113
+ changeTree.isNew = false; // Not a new instance anymore
114
+ }
96
115
  }
97
- // return bytes;
98
- return bytes.slice(0, it.offset);
116
+ return buffer.subarray(0, it.offset);
99
117
  }
100
118
  }
101
- encodeAll(it = { offset: 0 }) {
102
- // console.log(`encodeAll(), this.$root.allChanges (${this.$root.allChanges.size})`);
103
- // Array.from(this.$root.allChanges.entries()).map((item) => {
104
- // console.log("->", item[0].refId, item[0].ref.toJSON());
105
- // });
106
- return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);
119
+ encodeAll(it = { offset: 0 }, buffer = this.sharedBuffer) {
120
+ return this.encode(it, undefined, buffer, "allChanges", true);
107
121
  }
108
122
  encodeAllView(view, sharedOffset, it, bytes = this.sharedBuffer) {
109
123
  const viewOffset = it.offset;
110
- // console.log(`encodeAllView(), this.$root.allFilteredChanges (${this.$root.allFilteredChanges.size})`);
111
- // this.debugAllFilteredChanges();
112
124
  // try to encode "filtered" changes
113
- this.encode(it, view, bytes, this.root.allFilteredChanges);
125
+ this.encode(it, view, bytes, "allFilteredChanges", true, viewOffset);
114
126
  return Buffer.concat([
115
- bytes.slice(0, sharedOffset),
116
- bytes.slice(viewOffset, it.offset)
127
+ bytes.subarray(0, sharedOffset),
128
+ bytes.subarray(viewOffset, it.offset)
117
129
  ]);
118
130
  }
119
- // debugAllFilteredChanges() {
120
- // Array.from(this.$root.allFilteredChanges.entries()).map((item) => {
121
- // console.log("->", { refId: item[0].refId }, item[0].ref.toJSON());
122
- // if (Array.isArray(item[0].ref.toJSON())) {
123
- // item[1].forEach((op, key) => {
124
- // console.log(" ->", { key, op: OPERATION[op] });
125
- // })
126
- // }
127
- // });
128
- // }
131
+ debugChanges(field) {
132
+ const rootChangeSet = (typeof (field) === "string")
133
+ ? this.root[field]
134
+ : field;
135
+ rootChangeSet.forEach((changeTree) => {
136
+ const changeSet = changeTree[field];
137
+ const metadata = changeTree.ref.constructor[Symbol.metadata];
138
+ console.log("->", { ref: changeTree.ref.constructor.name, refId: changeTree.refId, changes: Object.keys(changeSet).length });
139
+ for (const index in changeSet) {
140
+ const op = changeSet[index];
141
+ console.log(" ->", {
142
+ index,
143
+ field: metadata?.[index],
144
+ op: spec_1.OPERATION[op],
145
+ });
146
+ }
147
+ });
148
+ }
129
149
  encodeView(view, sharedOffset, it, bytes = this.sharedBuffer) {
130
150
  const viewOffset = it.offset;
131
- // try to encode "filtered" changes
132
- this.encode(it, view, bytes, this.root.filteredChanges);
133
151
  // encode visibility changes (add/remove for this view)
134
- const viewChangesIterator = view.changes.entries();
135
- for (const [changeTree, changes] of viewChangesIterator) {
136
- if (changes.size === 0) {
137
- // FIXME: avoid having empty changes if no changes were made
138
- // console.log("changes.size === 0", changeTree.ref.constructor.name);
152
+ const refIds = Object.keys(view.changes);
153
+ for (let i = 0, numRefIds = refIds.length; i < numRefIds; i++) {
154
+ const refId = refIds[i];
155
+ const changes = view.changes[refId];
156
+ const changeTree = this.root.changeTrees[refId];
157
+ if (changeTree === undefined ||
158
+ Object.keys(changes).length === 0 // FIXME: avoid having empty changes if no changes were made
159
+ ) {
160
+ // console.log("changes.size === 0, skip", changeTree.ref.constructor.name);
139
161
  continue;
140
162
  }
141
163
  const ref = changeTree.ref;
142
- const ctor = ref['constructor'];
164
+ const ctor = ref.constructor;
143
165
  const encoder = ctor[symbols_1.$encoder];
166
+ const metadata = ctor[Symbol.metadata];
144
167
  bytes[it.offset++] = spec_1.SWITCH_TO_STRUCTURE & 255;
145
- encode.number(bytes, changeTree.refId, it);
146
- const changesIterator = changes.entries();
147
- for (const [fieldIndex, operation] of changesIterator) {
168
+ encode_1.encode.number(bytes, changeTree.refId, it);
169
+ const keys = Object.keys(changes);
170
+ for (let i = 0, numChanges = keys.length; i < numChanges; i++) {
171
+ const key = keys[i];
172
+ const operation = changes[key];
148
173
  // isEncodeAll = false
149
174
  // hasView = true
150
- encoder(this, bytes, changeTree, fieldIndex, operation, it, false, true);
175
+ encoder(this, bytes, changeTree, Number(key), operation, it, false, true, metadata);
151
176
  }
152
177
  }
153
178
  //
@@ -155,38 +180,62 @@ class Encoder {
155
180
  // (to allow re-using StateView's for multiple clients)
156
181
  //
157
182
  // clear "view" changes after encoding
158
- view.changes.clear();
183
+ view.changes = {};
184
+ // try to encode "filtered" changes
185
+ this.encode(it, view, bytes, "filteredChanges", false, viewOffset);
159
186
  return Buffer.concat([
160
- bytes.slice(0, sharedOffset),
161
- bytes.slice(viewOffset, it.offset)
187
+ bytes.subarray(0, sharedOffset),
188
+ bytes.subarray(viewOffset, it.offset)
162
189
  ]);
163
190
  }
164
191
  onEndEncode(changeTrees = this.root.changes) {
165
- const changeTreesIterator = changeTrees.entries();
166
- for (const [changeTree, _] of changeTreesIterator) {
167
- changeTree.endEncode();
168
- }
192
+ // changeTrees.forEach(function(changeTree) {
193
+ // changeTree.endEncode();
194
+ // });
195
+ // for (const refId in changeTrees) {
196
+ // const changeTree = this.root.changeTrees[refId];
197
+ // changeTree.endEncode();
198
+ // // changeTree.changes.clear();
199
+ // // // ArraySchema and MapSchema have a custom "encode end" method
200
+ // // changeTree.ref[$onEncodeEnd]?.();
201
+ // // // Not a new instance anymore
202
+ // // delete changeTree[$isNew];
203
+ // }
169
204
  }
170
205
  discardChanges() {
171
206
  // discard shared changes
172
- if (this.root.changes.size > 0) {
173
- this.onEndEncode(this.root.changes);
174
- this.root.changes.clear();
207
+ let length = this.root.changes.length;
208
+ if (length > 0) {
209
+ while (length--) {
210
+ this.root.changes[length]?.endEncode();
211
+ }
212
+ this.root.changes.length = 0;
175
213
  }
176
214
  // discard filtered changes
177
- if (this.root.filteredChanges.size > 0) {
178
- this.onEndEncode(this.root.filteredChanges);
179
- this.root.filteredChanges.clear();
215
+ length = this.root.filteredChanges.length;
216
+ if (length > 0) {
217
+ while (length--) {
218
+ this.root.filteredChanges[length]?.endEncode();
219
+ }
220
+ this.root.filteredChanges.length = 0;
180
221
  }
181
222
  }
182
223
  tryEncodeTypeId(bytes, baseType, targetType, it) {
183
224
  const baseTypeId = this.context.getTypeId(baseType);
184
225
  const targetTypeId = this.context.getTypeId(targetType);
226
+ if (targetTypeId === undefined) {
227
+ console.warn(`@colyseus/schema WARNING: Class "${targetType.name}" is not registered on TypeRegistry - Please either tag the class with @entity or define a @type() field.`);
228
+ return;
229
+ }
185
230
  if (baseTypeId !== targetTypeId) {
186
231
  bytes[it.offset++] = spec_1.TYPE_ID & 255;
187
- encode.number(bytes, targetTypeId, it);
232
+ encode_1.encode.number(bytes, targetTypeId, it);
188
233
  }
189
234
  }
235
+ get hasChanges() {
236
+ return (this.root.changes.length > 0 ||
237
+ this.root.filteredChanges.length > 0);
238
+ }
190
239
  }
191
240
  exports.Encoder = Encoder;
192
241
  //# sourceMappingURL=Encoder.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"Encoder.js","sourceRoot":"","sources":["../../src/encoder/Encoder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Schema } from \"../Schema\";\nimport { TypeContext } from \"../annotations\";\nimport { $changes, $encoder, $filter } from \"../types/symbols\";\n\nimport * as encode from \"../encoding/encode\";\nimport type { Iterator } from \"../encoding/decode\";\n\nimport { SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';\nimport { Root } from \"./ChangeTree\";\nimport { getNextPowerOf2 } from \"../utils\";\nimport type { StateView } from \"./StateView\";\n\nexport class Encoder<T extends Schema = any> {\n static BUFFER_SIZE = 8 * 1024;// 8KB\n sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);\n\n context: TypeContext;\n state: T;\n\n root: Root;\n\n constructor(root: T) {\n this.setRoot(root);\n\n //\n // TODO: cache and restore \"Context\" based on root schema\n // (to avoid creating a new context for every new room)\n //\n this.context = new TypeContext(root.constructor as typeof Schema);\n\n // console.log(\">>>>>>>>>>>>>>>> Encoder types\");\n // this.context.schemas.forEach((id, schema) => {\n // console.log(\"type:\", id, schema.name, Object.keys(schema[Symbol.metadata]));\n // });\n }\n\n protected setRoot(state: T) {\n this.root = new Root();\n this.state = state;\n state[$changes].setRoot(this.root);\n }\n\n encode(\n it: Iterator = { offset: 0 },\n view?: StateView,\n bytes = this.sharedBuffer,\n changeTrees = this.root.changes\n ): Buffer {\n const initialOffset = it.offset; // cache current offset in case we need to resize the buffer\n\n const isEncodeAll = this.root.allChanges === changeTrees;\n const hasView = (view !== undefined);\n const rootChangeTree = this.state[$changes];\n\n const changeTreesIterator = changeTrees.entries();\n\n for (const [changeTree, changes] of changeTreesIterator) {\n const ref = changeTree.ref;\n\n const ctor = ref['constructor'];\n const encoder = ctor[$encoder];\n const filter = ctor[$filter];\n\n if (hasView) {\n if (!view.items.has(changeTree)) {\n view.invisible.add(changeTree);\n continue; // skip this change tree\n\n } else if (view.invisible.has(changeTree)) {\n view.invisible.delete(changeTree); // remove from invisible list\n }\n }\n\n // skip root `refId` if it's the first change tree\n if (it.offset !== initialOffset || changeTree !== rootChangeTree) {\n bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;\n encode.number(bytes, changeTree.refId, it);\n }\n\n const changesIterator = changes.entries();\n\n for (const [fieldIndex, operation] of changesIterator) {\n //\n // first pass (encodeAll), identify \"filtered\" operations without encoding them\n // they will be encoded per client, based on their view.\n //\n // TODO: how can we optimize filtering out \"encode all\" operations?\n // TODO: avoid checking if no view tags were defined\n //\n if (filter && !filter(ref, fieldIndex, view)) {\n // console.log(\"SKIP FIELD:\", { ref: changeTree.ref.constructor.name, fieldIndex, })\n\n // console.log(\"ADD AS INVISIBLE:\", fieldIndex, changeTree.ref.constructor.name)\n // view?.invisible.add(changeTree);\n continue;\n }\n\n // console.log(\"WILL ENCODE\", {\n // ref: changeTree.ref.constructor.name,\n // fieldIndex,\n // operation: OPERATION[operation],\n // });\n\n encoder(this, bytes, changeTree, fieldIndex, operation, it, isEncodeAll, hasView);\n }\n }\n\n if (it.offset > bytes.byteLength) {\n const newSize = getNextPowerOf2(this.sharedBuffer.byteLength * 2);\n console.warn(\"@colyseus/schema encode buffer overflow. Current buffer size: \" + bytes.byteLength + \", encoding offset: \" + it.offset + \", new size: \" + newSize);\n\n //\n // resize buffer and re-encode (TODO: can we avoid re-encoding here?)\n //\n this.sharedBuffer = Buffer.allocUnsafeSlow(newSize);\n return this.encode({ offset: initialOffset }, view);\n\n } else {\n //\n // only clear changes after making sure buffer resize is not required.\n //\n if (!isEncodeAll && !hasView) {\n //\n // FIXME: avoid iterating over change trees twice.\n //\n this.onEndEncode(changeTrees);\n }\n\n // return bytes;\n return bytes.slice(0, it.offset);\n }\n }\n\n encodeAll(it: Iterator = { offset: 0 }) {\n // console.log(`encodeAll(), this.$root.allChanges (${this.$root.allChanges.size})`);\n\n // Array.from(this.$root.allChanges.entries()).map((item) => {\n // console.log(\"->\", item[0].refId, item[0].ref.toJSON());\n // });\n\n return this.encode(it, undefined, this.sharedBuffer, this.root.allChanges);\n }\n\n encodeAllView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {\n const viewOffset = it.offset;\n\n // console.log(`encodeAllView(), this.$root.allFilteredChanges (${this.$root.allFilteredChanges.size})`);\n // this.debugAllFilteredChanges();\n\n // try to encode \"filtered\" changes\n this.encode(it, view, bytes, this.root.allFilteredChanges);\n\n return Buffer.concat([\n bytes.slice(0, sharedOffset),\n bytes.slice(viewOffset, it.offset)\n ]);\n }\n\n\n // debugAllFilteredChanges() {\n // Array.from(this.$root.allFilteredChanges.entries()).map((item) => {\n // console.log(\"->\", { refId: item[0].refId }, item[0].ref.toJSON());\n // if (Array.isArray(item[0].ref.toJSON())) {\n // item[1].forEach((op, key) => {\n // console.log(\" ->\", { key, op: OPERATION[op] });\n // })\n // }\n // });\n // }\n\n encodeView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {\n const viewOffset = it.offset;\n\n // try to encode \"filtered\" changes\n this.encode(it, view, bytes, this.root.filteredChanges);\n\n // encode visibility changes (add/remove for this view)\n const viewChangesIterator = view.changes.entries();\n for (const [changeTree, changes] of viewChangesIterator) {\n if (changes.size === 0) {\n // FIXME: avoid having empty changes if no changes were made\n // console.log(\"changes.size === 0\", changeTree.ref.constructor.name);\n continue;\n }\n\n const ref = changeTree.ref;\n\n const ctor = ref['constructor'];\n const encoder = ctor[$encoder];\n\n bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;\n encode.number(bytes, changeTree.refId, it);\n\n const changesIterator = changes.entries();\n\n for (const [fieldIndex, operation] of changesIterator) {\n // isEncodeAll = false\n // hasView = true\n encoder(this, bytes, changeTree, fieldIndex, operation, it, false, true);\n }\n }\n\n //\n // TODO: only clear view changes after all views are encoded\n // (to allow re-using StateView's for multiple clients)\n //\n // clear \"view\" changes after encoding\n view.changes.clear();\n\n return Buffer.concat([\n bytes.slice(0, sharedOffset),\n bytes.slice(viewOffset, it.offset)\n ]);\n }\n\n onEndEncode(changeTrees = this.root.changes) {\n const changeTreesIterator = changeTrees.entries();\n for (const [changeTree, _] of changeTreesIterator) {\n changeTree.endEncode();\n }\n }\n\n discardChanges() {\n // discard shared changes\n if (this.root.changes.size > 0) {\n this.onEndEncode(this.root.changes);\n this.root.changes.clear();\n }\n // discard filtered changes\n if (this.root.filteredChanges.size > 0) {\n this.onEndEncode(this.root.filteredChanges);\n this.root.filteredChanges.clear();\n }\n }\n\n tryEncodeTypeId (bytes: Buffer, baseType: typeof Schema, targetType: typeof Schema, it: Iterator) {\n const baseTypeId = this.context.getTypeId(baseType);\n const targetTypeId = this.context.getTypeId(targetType);\n\n if (baseTypeId !== targetTypeId) {\n bytes[it.offset++] = TYPE_ID & 255;\n encode.number(bytes, targetTypeId, it);\n }\n }\n}"]}
1
+ {"version":3,"file":"Encoder.js","sourceRoot":"","sources":["../../src/encoder/Encoder.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Schema } from \"../Schema\";\nimport { TypeContext } from \"../types/TypeContext\";\nimport { $changes, $encoder, $filter, $onEncodeEnd } from \"../types/symbols\";\n\nimport { encode } from \"../encoding/encode\";\nimport type { Iterator } from \"../encoding/decode\";\n\nimport { OPERATION, SWITCH_TO_STRUCTURE, TYPE_ID } from '../encoding/spec';\nimport { Root } from \"./Root\";\nimport { getNextPowerOf2 } from \"../utils\";\n\nimport type { StateView } from \"./StateView\";\nimport type { Metadata } from \"../Metadata\";\n\nexport class Encoder<T extends Schema = any> {\n static BUFFER_SIZE = 8 * 1024;// 8KB\n sharedBuffer = Buffer.allocUnsafeSlow(Encoder.BUFFER_SIZE);\n\n context: TypeContext;\n state: T;\n\n root: Root;\n\n constructor(state: T) {\n //\n // TODO: cache and restore \"Context\" based on root schema\n // (to avoid creating a new context for every new room)\n //\n this.context = new TypeContext(state.constructor as typeof Schema);\n this.root = new Root(this.context);\n\n this.setState(state);\n\n // console.log(\">>>>>>>>>>>>>>>> Encoder types\");\n // this.context.schemas.forEach((id, schema) => {\n // console.log(\"type:\", id, schema.name, Object.keys(schema[Symbol.metadata]));\n // });\n }\n\n protected setState(state: T) {\n this.state = state;\n this.state[$changes].setRoot(this.root);\n }\n\n encode(\n it: Iterator = { offset: 0 },\n view?: StateView,\n buffer = this.sharedBuffer,\n changeSetName: \"changes\" | \"allChanges\" | \"filteredChanges\" | \"allFilteredChanges\" = \"changes\",\n isEncodeAll = changeSetName === \"allChanges\",\n initialOffset = it.offset // cache current offset in case we need to resize the buffer\n ): Buffer {\n const hasView = (view !== undefined);\n const rootChangeTree = this.state[$changes];\n\n const shouldDiscardChanges = !isEncodeAll && !hasView;\n const changeTrees = this.root[changeSetName];\n\n for (let i = 0, numChangeTrees = changeTrees.length; i < numChangeTrees; i++) {\n const changeTree = changeTrees[i];\n\n const operations = changeTree[changeSetName];\n const ref = changeTree.ref;\n\n const ctor = ref.constructor;\n const encoder = ctor[$encoder];\n const filter = ctor[$filter];\n const metadata = ctor[Symbol.metadata];\n\n if (hasView) {\n if (!view.items.has(changeTree)) {\n view.invisible.add(changeTree);\n continue; // skip this change tree\n\n } else if (view.invisible.has(changeTree)) {\n view.invisible.delete(changeTree); // remove from invisible list\n }\n }\n\n // skip root `refId` if it's the first change tree\n // (unless it \"hasView\", which will need to revisit the root)\n if (hasView || it.offset > initialOffset || changeTree !== rootChangeTree) {\n buffer[it.offset++] = SWITCH_TO_STRUCTURE & 255;\n encode.number(buffer, changeTree.refId, it);\n }\n\n for (let j = 0, numChanges = operations.operations.length; j < numChanges; j++) {\n const fieldIndex = operations.operations[j];\n\n const operation = (fieldIndex < 0)\n ? Math.abs(fieldIndex) // \"pure\" operation without fieldIndex (e.g. CLEAR, REVERSE, etc.)\n : (isEncodeAll)\n ? OPERATION.ADD\n : changeTree.indexedOperations[fieldIndex];\n\n //\n // first pass (encodeAll), identify \"filtered\" operations without encoding them\n // they will be encoded per client, based on their view.\n //\n // TODO: how can we optimize filtering out \"encode all\" operations?\n // TODO: avoid checking if no view tags were defined\n //\n if (fieldIndex === undefined || operation === undefined || (filter && !filter(ref, fieldIndex, view))) {\n // console.log(\"ADD AS INVISIBLE:\", fieldIndex, changeTree.ref.constructor.name)\n // view?.invisible.add(changeTree);\n continue;\n }\n\n encoder(this, buffer, changeTree, fieldIndex, operation, it, isEncodeAll, hasView, metadata);\n }\n\n // if (shouldDiscardChanges) {\n // changeTree.discard();\n // changeTree.isNew = false; // Not a new instance anymore\n // }\n }\n\n if (it.offset > buffer.byteLength) {\n const newSize = getNextPowerOf2(buffer.byteLength * 2);\n console.warn(`@colyseus/schema buffer overflow. Encoded state is higher than default BUFFER_SIZE. Use the following to increase default BUFFER_SIZE:\n\n import { Encoder } from \"@colyseus/schema\";\n Encoder.BUFFER_SIZE = ${Math.round(newSize / 1024)} * 1024; // ${Math.round(newSize / 1024)} KB\n`);\n\n //\n // resize buffer and re-encode (TODO: can we avoid re-encoding here?)\n //\n buffer = Buffer.alloc(newSize);\n\n // assign resized buffer to local sharedBuffer\n if (buffer === this.sharedBuffer) {\n this.sharedBuffer = buffer;\n }\n\n return this.encode({ offset: initialOffset }, view, buffer, changeSetName, isEncodeAll);\n\n } else {\n //\n // only clear changes after making sure buffer resize is not required.\n //\n if (shouldDiscardChanges) {\n //\n // TODO: avoid iterating over change trees twice.\n //\n for (let i = 0, numChangeTrees = changeTrees.length; i < numChangeTrees; i++) {\n const changeTree = changeTrees[i];\n changeTree.discard();\n changeTree.isNew = false; // Not a new instance anymore\n }\n }\n\n return buffer.subarray(0, it.offset);\n }\n }\n\n encodeAll(it: Iterator = { offset: 0 }, buffer: Buffer = this.sharedBuffer) {\n return this.encode(it, undefined, buffer, \"allChanges\", true);\n }\n\n encodeAllView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {\n const viewOffset = it.offset;\n\n // try to encode \"filtered\" changes\n this.encode(it, view, bytes, \"allFilteredChanges\", true, viewOffset);\n\n return Buffer.concat([\n bytes.subarray(0, sharedOffset),\n bytes.subarray(viewOffset, it.offset)\n ]);\n }\n\n debugChanges(field: \"changes\" | \"allFilteredChanges\" | \"allChanges\" | \"filteredChanges\") {\n const rootChangeSet = (typeof (field) === \"string\")\n ? this.root[field]\n : field;\n\n rootChangeSet.forEach((changeTree) => {\n const changeSet = changeTree[field];\n\n const metadata: Metadata = changeTree.ref.constructor[Symbol.metadata];\n console.log(\"->\", { ref: changeTree.ref.constructor.name, refId: changeTree.refId, changes: Object.keys(changeSet).length });\n for (const index in changeSet) {\n const op = changeSet[index];\n console.log(\" ->\", {\n index,\n field: metadata?.[index],\n op: OPERATION[op],\n });\n }\n });\n }\n\n encodeView(view: StateView, sharedOffset: number, it: Iterator, bytes = this.sharedBuffer) {\n const viewOffset = it.offset;\n\n // encode visibility changes (add/remove for this view)\n const refIds = Object.keys(view.changes);\n\n for (let i = 0, numRefIds = refIds.length; i < numRefIds; i++) {\n const refId = refIds[i];\n const changes = view.changes[refId];\n const changeTree = this.root.changeTrees[refId];\n\n if (\n changeTree === undefined ||\n Object.keys(changes).length === 0 // FIXME: avoid having empty changes if no changes were made\n ) {\n // console.log(\"changes.size === 0, skip\", changeTree.ref.constructor.name);\n continue;\n }\n\n const ref = changeTree.ref;\n\n const ctor = ref.constructor;\n const encoder = ctor[$encoder];\n const metadata = ctor[Symbol.metadata];\n\n bytes[it.offset++] = SWITCH_TO_STRUCTURE & 255;\n encode.number(bytes, changeTree.refId, it);\n\n const keys = Object.keys(changes);\n for (let i = 0, numChanges = keys.length; i < numChanges; i++) {\n const key = keys[i];\n const operation = changes[key];\n\n // isEncodeAll = false\n // hasView = true\n encoder(this, bytes, changeTree, Number(key), operation, it, false, true, metadata);\n }\n }\n\n //\n // TODO: only clear view changes after all views are encoded\n // (to allow re-using StateView's for multiple clients)\n //\n // clear \"view\" changes after encoding\n view.changes = {};\n\n // try to encode \"filtered\" changes\n this.encode(it, view, bytes, \"filteredChanges\", false, viewOffset);\n\n return Buffer.concat([\n bytes.subarray(0, sharedOffset),\n bytes.subarray(viewOffset, it.offset)\n ]);\n }\n\n onEndEncode(changeTrees = this.root.changes) {\n // changeTrees.forEach(function(changeTree) {\n // changeTree.endEncode();\n // });\n\n\n // for (const refId in changeTrees) {\n // const changeTree = this.root.changeTrees[refId];\n // changeTree.endEncode();\n\n // // changeTree.changes.clear();\n\n // // // ArraySchema and MapSchema have a custom \"encode end\" method\n // // changeTree.ref[$onEncodeEnd]?.();\n\n // // // Not a new instance anymore\n // // delete changeTree[$isNew];\n // }\n }\n\n discardChanges() {\n // discard shared changes\n let length = this.root.changes.length;\n if (length > 0) {\n while (length--) {\n this.root.changes[length]?.endEncode();\n }\n this.root.changes.length = 0;\n }\n\n // discard filtered changes\n length = this.root.filteredChanges.length;\n if (length > 0) {\n while (length--) {\n this.root.filteredChanges[length]?.endEncode();\n }\n this.root.filteredChanges.length = 0;\n }\n }\n\n tryEncodeTypeId (bytes: Buffer, baseType: typeof Schema, targetType: typeof Schema, it: Iterator) {\n const baseTypeId = this.context.getTypeId(baseType);\n const targetTypeId = this.context.getTypeId(targetType);\n\n if (targetTypeId === undefined) {\n console.warn(`@colyseus/schema WARNING: Class \"${targetType.name}\" is not registered on TypeRegistry - Please either tag the class with @entity or define a @type() field.`);\n return;\n }\n\n if (baseTypeId !== targetTypeId) {\n bytes[it.offset++] = TYPE_ID & 255;\n encode.number(bytes, targetTypeId, it);\n }\n }\n\n get hasChanges() {\n return (\n this.root.changes.length > 0 ||\n this.root.filteredChanges.length > 0\n );\n }\n}\n"]}
@@ -0,0 +1,22 @@
1
+ import { TypeContext } from "../types/TypeContext";
2
+ import { ChangeTree } from "./ChangeTree";
3
+ export declare class Root {
4
+ types: TypeContext;
5
+ protected nextUniqueId: number;
6
+ refCount: {
7
+ [id: number]: number;
8
+ };
9
+ changeTrees: {
10
+ [refId: number]: ChangeTree;
11
+ };
12
+ allChanges: ChangeTree[];
13
+ allFilteredChanges: ChangeTree[];
14
+ changes: ChangeTree[];
15
+ filteredChanges: ChangeTree[];
16
+ constructor(types: TypeContext);
17
+ getNextUniqueId(): number;
18
+ add(changeTree: ChangeTree): boolean;
19
+ remove(changeTree: ChangeTree): number;
20
+ removeChangeFromChangeSet(changeSetName: "allChanges" | "changes" | "filteredChanges" | "allFilteredChanges", changeTree: ChangeTree): void;
21
+ clear(): void;
22
+ }
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Root = void 0;
4
+ const spec_1 = require("../encoding/spec");
5
+ const utils_1 = require("../types/utils");
6
+ const ChangeTree_1 = require("./ChangeTree");
7
+ class Root {
8
+ constructor(types) {
9
+ this.types = types;
10
+ this.nextUniqueId = 0;
11
+ this.refCount = {};
12
+ this.changeTrees = {};
13
+ // all changes
14
+ this.allChanges = [];
15
+ this.allFilteredChanges = []; // TODO: do not initialize it if filters are not used
16
+ // pending changes to be encoded
17
+ this.changes = [];
18
+ this.filteredChanges = []; // TODO: do not initialize it if filters are not used
19
+ }
20
+ getNextUniqueId() {
21
+ return this.nextUniqueId++;
22
+ }
23
+ add(changeTree) {
24
+ // FIXME: move implementation of `ensureRefId` to `Root` class
25
+ changeTree.ensureRefId();
26
+ const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);
27
+ if (isNewChangeTree) {
28
+ this.changeTrees[changeTree.refId] = changeTree;
29
+ }
30
+ const previousRefCount = this.refCount[changeTree.refId];
31
+ if (previousRefCount === 0) {
32
+ //
33
+ // When a ChangeTree is re-added, it means that it was previously removed.
34
+ // We need to re-add all changes to the `changes` map.
35
+ //
36
+ const ops = changeTree.allChanges.operations;
37
+ let len = ops.length;
38
+ while (len--) {
39
+ changeTree.indexedOperations[ops[len]] = spec_1.OPERATION.ADD;
40
+ (0, ChangeTree_1.setOperationAtIndex)(changeTree.changes, len);
41
+ }
42
+ }
43
+ this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;
44
+ return isNewChangeTree;
45
+ }
46
+ remove(changeTree) {
47
+ const refCount = (this.refCount[changeTree.refId]) - 1;
48
+ if (refCount <= 0) {
49
+ //
50
+ // Only remove "root" reference if it's the last reference
51
+ //
52
+ changeTree.root = undefined;
53
+ delete this.changeTrees[changeTree.refId];
54
+ this.removeChangeFromChangeSet("allChanges", changeTree);
55
+ this.removeChangeFromChangeSet("changes", changeTree);
56
+ if (changeTree.filteredChanges) {
57
+ this.removeChangeFromChangeSet("allFilteredChanges", changeTree);
58
+ this.removeChangeFromChangeSet("filteredChanges", changeTree);
59
+ }
60
+ this.refCount[changeTree.refId] = 0;
61
+ }
62
+ else {
63
+ this.refCount[changeTree.refId] = refCount;
64
+ }
65
+ changeTree.forEachChild((child, _) => this.remove(child));
66
+ return refCount;
67
+ }
68
+ removeChangeFromChangeSet(changeSetName, changeTree) {
69
+ const changeSet = this[changeSetName];
70
+ const index = changeSet.indexOf(changeTree);
71
+ if (index !== -1) {
72
+ (0, utils_1.spliceOne)(changeSet, index);
73
+ // changeSet[index] = undefined;
74
+ }
75
+ }
76
+ clear() {
77
+ this.changes.length = 0;
78
+ }
79
+ }
80
+ exports.Root = Root;
81
+ //# sourceMappingURL=Root.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Root.js","sourceRoot":"","sources":["../../src/encoder/Root.ts"],"names":[],"mappings":"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","sourcesContent":["import { OPERATION } from \"../encoding/spec\";\nimport { TypeContext } from \"../types/TypeContext\";\nimport { spliceOne } from \"../types/utils\";\nimport { ChangeTree, setOperationAtIndex } from \"./ChangeTree\";\n\nexport class Root {\n protected nextUniqueId: number = 0;\n\n refCount: {[id: number]: number} = {};\n changeTrees: {[refId: number]: ChangeTree} = {};\n\n // all changes\n allChanges: ChangeTree[] = [];\n allFilteredChanges: ChangeTree[] = [];// TODO: do not initialize it if filters are not used\n\n // pending changes to be encoded\n changes: ChangeTree[] = [];\n filteredChanges: ChangeTree[] = [];// TODO: do not initialize it if filters are not used\n\n constructor(public types: TypeContext) { }\n\n getNextUniqueId() {\n return this.nextUniqueId++;\n }\n\n add(changeTree: ChangeTree) {\n // FIXME: move implementation of `ensureRefId` to `Root` class\n changeTree.ensureRefId();\n\n const isNewChangeTree = (this.changeTrees[changeTree.refId] === undefined);\n if (isNewChangeTree) { this.changeTrees[changeTree.refId] = changeTree; }\n\n const previousRefCount = this.refCount[changeTree.refId];\n if (previousRefCount === 0) {\n //\n // When a ChangeTree is re-added, it means that it was previously removed.\n // We need to re-add all changes to the `changes` map.\n //\n const ops = changeTree.allChanges.operations;\n let len = ops.length;\n while (len--) {\n changeTree.indexedOperations[ops[len]] = OPERATION.ADD;\n setOperationAtIndex(changeTree.changes, len);\n }\n }\n\n this.refCount[changeTree.refId] = (previousRefCount || 0) + 1;\n\n return isNewChangeTree;\n }\n\n remove(changeTree: ChangeTree) {\n const refCount = (this.refCount[changeTree.refId]) - 1;\n\n if (refCount <= 0) {\n //\n // Only remove \"root\" reference if it's the last reference\n //\n changeTree.root = undefined;\n delete this.changeTrees[changeTree.refId];\n\n this.removeChangeFromChangeSet(\"allChanges\", changeTree);\n this.removeChangeFromChangeSet(\"changes\", changeTree);\n\n if (changeTree.filteredChanges) {\n this.removeChangeFromChangeSet(\"allFilteredChanges\", changeTree);\n this.removeChangeFromChangeSet(\"filteredChanges\", changeTree);\n }\n\n this.refCount[changeTree.refId] = 0;\n\n } else {\n this.refCount[changeTree.refId] = refCount;\n }\n\n changeTree.forEachChild((child, _) => this.remove(child));\n\n return refCount;\n }\n\n removeChangeFromChangeSet(changeSetName: \"allChanges\" | \"changes\" | \"filteredChanges\" | \"allFilteredChanges\", changeTree: ChangeTree) {\n const changeSet = this[changeSetName];\n const index = changeSet.indexOf(changeTree);\n if (index !== -1) {\n spliceOne(changeSet, index);\n // changeSet[index] = undefined;\n }\n }\n\n clear() {\n this.changes.length = 0;\n }\n}\n"]}
@@ -1,7 +1,5 @@
1
- import { ChangeTree, Ref } from "./ChangeTree";
2
- import { OPERATION } from "../encoding/spec";
3
- import type { Schema } from "../Schema";
4
- export declare function createView(root: Schema): void;
1
+ import { ChangeTree, IndexedOperations, Ref } from "./ChangeTree";
2
+ export declare function createView(): StateView;
5
3
  export declare class StateView {
6
4
  /**
7
5
  * List of ChangeTree's that are visible to this view
@@ -16,8 +14,10 @@ export declare class StateView {
16
14
  * Manual "ADD" operations for changes per ChangeTree, specific to this view.
17
15
  * (This is used to force encoding a property, even if it was not changed)
18
16
  */
19
- changes: Map<ChangeTree<any>, Map<number, OPERATION>>;
20
- add(obj: Ref, tag?: number): this;
21
- protected addParent(changeTree: ChangeTree, tag: number): void;
17
+ changes: {
18
+ [refId: number]: IndexedOperations;
19
+ };
20
+ add(obj: Ref, tag?: number, checkIncludeParent?: boolean): this;
21
+ protected addParent(changeTree: ChangeTree, parentIndex: number, tag: number): void;
22
22
  remove(obj: Ref, tag?: number): this;
23
23
  }