@colyseus/schema 3.0.25 → 3.0.27
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/build/cjs/index.js +81 -38
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +81 -38
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +81 -40
- package/lib/encoder/ChangeTree.d.ts +3 -2
- package/lib/encoder/ChangeTree.js +47 -13
- package/lib/encoder/ChangeTree.js.map +1 -1
- package/lib/encoder/EncodeOperation.js +1 -0
- package/lib/encoder/EncodeOperation.js.map +1 -1
- package/lib/encoder/Encoder.d.ts +2 -1
- package/lib/encoder/Encoder.js +3 -3
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/index.d.ts +1 -1
- package/lib/index.js.map +1 -1
- package/lib/types/custom/ArraySchema.js +47 -22
- package/lib/types/custom/ArraySchema.js.map +1 -1
- package/package.json +1 -1
- package/src/encoder/ChangeTree.ts +54 -15
- package/src/encoder/EncodeOperation.ts +2 -0
- package/src/encoder/Encoder.ts +5 -5
- package/src/index.ts +1 -1
- package/src/types/custom/ArraySchema.ts +56 -25
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import * as util from "util";
|
|
2
|
+
|
|
1
3
|
import { OPERATION } from "../encoding/spec";
|
|
2
4
|
import { Schema } from "../Schema";
|
|
3
5
|
import { $changes, $childType, $decoder, $onEncodeEnd, $encoder, $getByIndex, $refTypeFieldIndexes, $viewFieldIndexes } from "../types/symbols";
|
|
@@ -39,7 +41,7 @@ export interface IndexedOperations {
|
|
|
39
41
|
export interface ChangeSet {
|
|
40
42
|
// field index -> operation index
|
|
41
43
|
indexes: { [index: number]: number };
|
|
42
|
-
operations:
|
|
44
|
+
operations: number[];
|
|
43
45
|
queueRootIndex?: number; // index of ChangeTree structure in `root.changes` or `root.filteredChanges`
|
|
44
46
|
}
|
|
45
47
|
|
|
@@ -56,14 +58,43 @@ export function setOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
|
56
58
|
}
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
export function deleteOperationAtIndex(changeSet: ChangeSet, index: number) {
|
|
60
|
-
|
|
61
|
-
if (operationsIndex
|
|
62
|
-
|
|
61
|
+
export function deleteOperationAtIndex(changeSet: ChangeSet, index: number | string) {
|
|
62
|
+
let operationsIndex = changeSet.indexes[index];
|
|
63
|
+
if (operationsIndex === undefined) {
|
|
64
|
+
//
|
|
65
|
+
// if index is not found, we need to find the last operation
|
|
66
|
+
// FIXME: this is not very efficient
|
|
67
|
+
//
|
|
68
|
+
// > See "should allow consecutive splices (same place)" tests
|
|
69
|
+
//
|
|
70
|
+
operationsIndex = Object.values(changeSet.indexes).at(-1);
|
|
71
|
+
index = Object.entries(changeSet.indexes).find(([_, value]) => value === operationsIndex)?.[0];
|
|
63
72
|
}
|
|
73
|
+
changeSet.operations[operationsIndex] = undefined;
|
|
64
74
|
delete changeSet.indexes[index];
|
|
65
75
|
}
|
|
66
76
|
|
|
77
|
+
export function debugChangeSet(label: string, changeSet: ChangeSet) {
|
|
78
|
+
let indexes: string[] = [];
|
|
79
|
+
let operations: string[] = [];
|
|
80
|
+
|
|
81
|
+
for (const index in changeSet.indexes) {
|
|
82
|
+
indexes.push(`\t${util.inspect(index, { colors: true })} => [${util.inspect(changeSet.indexes[index], { colors: true })}]`);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
for (let i = 0; i < changeSet.operations.length; i++) {
|
|
86
|
+
const index = changeSet.operations[i];
|
|
87
|
+
if (index !== undefined) {
|
|
88
|
+
operations.push(`\t[${util.inspect(i, { colors: true })}] => ${util.inspect(index, { colors: true })}`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
console.log(`${label} =>\nindexes (${Object.keys(changeSet.indexes).length}) {`);
|
|
93
|
+
console.log(indexes.join("\n"), "\n}");
|
|
94
|
+
console.log(`operations (${changeSet.operations.filter(op => op !== undefined).length}) {`);
|
|
95
|
+
console.log(operations.join("\n"), "\n}");
|
|
96
|
+
}
|
|
97
|
+
|
|
67
98
|
export function enqueueChangeTree(
|
|
68
99
|
root: Root,
|
|
69
100
|
changeTree: ChangeTree,
|
|
@@ -267,7 +298,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
267
298
|
const newIndexes = {};
|
|
268
299
|
for (const index in this.indexedOperations) {
|
|
269
300
|
newIndexedOperations[Number(index) + shiftIndex] = this.indexedOperations[index];
|
|
270
|
-
newIndexes[Number(index) + shiftIndex] = changeSet[index];
|
|
301
|
+
newIndexes[Number(index) + shiftIndex] = changeSet.indexes[index];
|
|
271
302
|
}
|
|
272
303
|
this.indexedOperations = newIndexedOperations;
|
|
273
304
|
changeSet.indexes = newIndexes;
|
|
@@ -292,15 +323,24 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
292
323
|
|
|
293
324
|
private _shiftAllChangeIndexes(shiftIndex: number, startIndex: number = 0, changeSet: ChangeSet) {
|
|
294
325
|
const newIndexes = {};
|
|
295
|
-
|
|
326
|
+
let newKey = 0;
|
|
296
327
|
for (const key in changeSet.indexes) {
|
|
297
|
-
|
|
298
|
-
if (index > startIndex) {
|
|
299
|
-
newIndexes[Number(key) + shiftIndex] = index;
|
|
300
|
-
} else {
|
|
301
|
-
newIndexes[key] = index;
|
|
302
|
-
}
|
|
328
|
+
newIndexes[newKey++] = changeSet.indexes[key];
|
|
303
329
|
}
|
|
330
|
+
|
|
331
|
+
// const newIndexes = {};
|
|
332
|
+
// let newKey = 0;
|
|
333
|
+
// for (const key in changeSet.indexes) {
|
|
334
|
+
// const index = changeSet.indexes[key];
|
|
335
|
+
// newIndexes[newKey++] = changeSet.indexes[key];
|
|
336
|
+
// console.log("...shiftAllChangeIndexes", { index: key, targetIndex: index, startIndex, shiftIndex });
|
|
337
|
+
// if (index > startIndex) {
|
|
338
|
+
// newIndexes[Number(key) + shiftIndex] = index;
|
|
339
|
+
// } else {
|
|
340
|
+
// newIndexes[Number(key)] = index;
|
|
341
|
+
// }
|
|
342
|
+
// }
|
|
343
|
+
|
|
304
344
|
changeSet.indexes = newIndexes;
|
|
305
345
|
|
|
306
346
|
for (let i = 0; i < changeSet.operations.length; i++) {
|
|
@@ -372,6 +412,7 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
372
412
|
|
|
373
413
|
this.indexedOperations[index] = operation ?? OPERATION.DELETE;
|
|
374
414
|
setOperationAtIndex(changeSet, index);
|
|
415
|
+
deleteOperationAtIndex(this.allChanges, allChangesIndex);
|
|
375
416
|
|
|
376
417
|
const previousValue = this.getValue(index);
|
|
377
418
|
|
|
@@ -390,8 +431,6 @@ export class ChangeTree<T extends Ref=any> {
|
|
|
390
431
|
this.root?.remove(previousValue[$changes]);
|
|
391
432
|
}
|
|
392
433
|
|
|
393
|
-
deleteOperationAtIndex(this.allChanges, allChangesIndex);
|
|
394
|
-
|
|
395
434
|
//
|
|
396
435
|
// FIXME: this is looking a ugly and repeated
|
|
397
436
|
//
|
|
@@ -217,6 +217,8 @@ export const encodeArray: EncodeOperation = function (
|
|
|
217
217
|
const type = changeTree.getType(field);
|
|
218
218
|
const value = changeTree.getValue(field, isEncodeAll);
|
|
219
219
|
|
|
220
|
+
// console.log({ type, field, value });
|
|
221
|
+
|
|
220
222
|
// console.log("encodeArray -> ", {
|
|
221
223
|
// ref: changeTree.ref.constructor.name,
|
|
222
224
|
// field,
|
package/src/encoder/Encoder.ts
CHANGED
|
@@ -10,7 +10,7 @@ import { Root } from "./Root";
|
|
|
10
10
|
|
|
11
11
|
import type { StateView } from "./StateView";
|
|
12
12
|
import type { Metadata } from "../Metadata";
|
|
13
|
-
import type { ChangeTree } from "./ChangeTree";
|
|
13
|
+
import type { ChangeSetName, ChangeTree } from "./ChangeTree";
|
|
14
14
|
|
|
15
15
|
export class Encoder<T extends Schema = any> {
|
|
16
16
|
static BUFFER_SIZE = (typeof(Buffer) !== "undefined") && Buffer.poolSize || 8 * 1024; // 8KB
|
|
@@ -48,7 +48,7 @@ export class Encoder<T extends Schema = any> {
|
|
|
48
48
|
it: Iterator = { offset: 0 },
|
|
49
49
|
view?: StateView,
|
|
50
50
|
buffer = this.sharedBuffer,
|
|
51
|
-
changeSetName:
|
|
51
|
+
changeSetName: ChangeSetName = "changes",
|
|
52
52
|
isEncodeAll = changeSetName === "allChanges",
|
|
53
53
|
initialOffset = it.offset // cache current offset in case we need to resize the buffer
|
|
54
54
|
): Buffer {
|
|
@@ -70,11 +70,11 @@ export class Encoder<T extends Schema = any> {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
-
const
|
|
73
|
+
const changeSet = changeTree[changeSetName];
|
|
74
74
|
const ref = changeTree.ref;
|
|
75
75
|
|
|
76
76
|
// TODO: avoid iterating over change tree if no changes were made
|
|
77
|
-
const numChanges =
|
|
77
|
+
const numChanges = changeSet.operations.length;
|
|
78
78
|
if (numChanges === 0) { continue; }
|
|
79
79
|
|
|
80
80
|
const ctor = ref.constructor;
|
|
@@ -90,7 +90,7 @@ export class Encoder<T extends Schema = any> {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
for (let j = 0; j < numChanges; j++) {
|
|
93
|
-
const fieldIndex =
|
|
93
|
+
const fieldIndex = changeSet.operations[j];
|
|
94
94
|
|
|
95
95
|
const operation = (fieldIndex < 0)
|
|
96
96
|
? Math.abs(fieldIndex) // "pure" operation without fieldIndex (e.g. CLEAR, REVERSE, etc.)
|
package/src/index.ts
CHANGED
|
@@ -50,7 +50,7 @@ export { getRawChangesCallback } from "./decoder/strategy/RawChanges";
|
|
|
50
50
|
|
|
51
51
|
export { Encoder } from "./encoder/Encoder";
|
|
52
52
|
export { encodeSchemaOperation, encodeArray, encodeKeyValueOperation } from "./encoder/EncodeOperation";
|
|
53
|
-
export { ChangeTree, Ref } from "./encoder/ChangeTree";
|
|
53
|
+
export { ChangeTree, Ref, type ChangeSetName, type ChangeSet} from "./encoder/ChangeTree";
|
|
54
54
|
export { StateView } from "./encoder/StateView";
|
|
55
55
|
|
|
56
56
|
export { Decoder } from "./decoder/Decoder";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { $changes, $childType, $decoder, $deleteByIndex, $onEncodeEnd, $encoder, $filter, $getByIndex, $onDecodeEnd } from "../symbols";
|
|
2
2
|
import type { Schema } from "../../Schema";
|
|
3
|
-
import { ChangeTree, setOperationAtIndex } from "../../encoder/ChangeTree";
|
|
3
|
+
import { ChangeTree, debugChangeSet, deleteOperationAtIndex, enqueueChangeTree, setOperationAtIndex } from "../../encoder/ChangeTree";
|
|
4
4
|
import { OPERATION } from "../../encoding/spec";
|
|
5
5
|
import { registerType } from "../registry";
|
|
6
6
|
import { Collection } from "../HelperTypes";
|
|
@@ -229,9 +229,6 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
|
|
|
229
229
|
|
|
230
230
|
this[$changes].delete(index, undefined, this.items.length - 1);
|
|
231
231
|
|
|
232
|
-
// this.tmpItems[index] = undefined;
|
|
233
|
-
// this.tmpItems.pop();
|
|
234
|
-
|
|
235
232
|
this.deletedIndexes[index] = true;
|
|
236
233
|
|
|
237
234
|
return this.items.pop();
|
|
@@ -360,11 +357,13 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
|
|
|
360
357
|
if (this.items.length === 0) { return undefined; }
|
|
361
358
|
|
|
362
359
|
// const index = Number(Object.keys(changeTree.indexes)[0]);
|
|
363
|
-
const index = this.tmpItems.findIndex((item, i) => item === this.items[0]);
|
|
364
360
|
const changeTree = this[$changes];
|
|
365
361
|
|
|
366
|
-
|
|
367
|
-
|
|
362
|
+
const index = this.tmpItems.findIndex(item => item === this.items[0]);
|
|
363
|
+
const allChangesIndex = this.items.findIndex(item => item === this.items[0]);
|
|
364
|
+
|
|
365
|
+
changeTree.delete(index, OPERATION.DELETE, allChangesIndex);
|
|
366
|
+
changeTree.shiftAllChangeIndexes(-1, allChangesIndex);
|
|
368
367
|
|
|
369
368
|
// this.deletedIndexes[index] = true;
|
|
370
369
|
|
|
@@ -410,38 +409,63 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
|
|
|
410
409
|
*/
|
|
411
410
|
splice(
|
|
412
411
|
start: number,
|
|
413
|
-
deleteCount
|
|
412
|
+
deleteCount?: number,
|
|
414
413
|
...insertItems: V[]
|
|
415
414
|
): V[] {
|
|
416
415
|
const changeTree = this[$changes];
|
|
417
416
|
|
|
417
|
+
const itemsLength = this.items.length;
|
|
418
418
|
const tmpItemsLength = this.tmpItems.length;
|
|
419
419
|
const insertCount = insertItems.length;
|
|
420
420
|
|
|
421
421
|
// build up-to-date list of indexes, excluding removed values.
|
|
422
422
|
const indexes: number[] = [];
|
|
423
423
|
for (let i = 0; i < tmpItemsLength; i++) {
|
|
424
|
-
// if (this.tmpItems[i] !== undefined) {
|
|
425
424
|
if (this.deletedIndexes[i] !== true) {
|
|
426
425
|
indexes.push(i);
|
|
427
426
|
}
|
|
428
427
|
}
|
|
429
428
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
429
|
+
if (itemsLength > start) {
|
|
430
|
+
// if deleteCount is not provided, delete all items from start to end
|
|
431
|
+
if (deleteCount === undefined) {
|
|
432
|
+
deleteCount = itemsLength - start;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
//
|
|
436
|
+
// delete operations at correct index
|
|
437
|
+
//
|
|
438
|
+
for (let i = start; i < start + deleteCount; i++) {
|
|
439
|
+
const index = indexes[i];
|
|
440
|
+
changeTree.delete(index, OPERATION.DELETE);
|
|
441
|
+
this.deletedIndexes[index] = true;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
} else {
|
|
445
|
+
// not enough items to delete
|
|
446
|
+
deleteCount = 0;
|
|
436
447
|
}
|
|
437
448
|
|
|
438
|
-
//
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
449
|
+
// insert operations
|
|
450
|
+
if (insertCount > 0) {
|
|
451
|
+
if (insertCount > deleteCount) {
|
|
452
|
+
console.error("Inserting more elements than deleting during ArraySchema#splice()");
|
|
453
|
+
throw new Error("ArraySchema#splice(): insertCount must be equal or lower than deleteCount.");
|
|
454
|
+
}
|
|
442
455
|
|
|
443
|
-
|
|
444
|
-
|
|
456
|
+
for (let i = 0; i < insertCount; i++) {
|
|
457
|
+
const addIndex = (indexes[start] ?? itemsLength) + i;
|
|
458
|
+
|
|
459
|
+
changeTree.indexedOperation(
|
|
460
|
+
addIndex,
|
|
461
|
+
(this.deletedIndexes[addIndex])
|
|
462
|
+
? OPERATION.DELETE_AND_ADD
|
|
463
|
+
: OPERATION.ADD
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
// set value's parent/root
|
|
467
|
+
insertItems[i][$changes]?.setParent(this, changeTree.root, addIndex);
|
|
468
|
+
}
|
|
445
469
|
}
|
|
446
470
|
|
|
447
471
|
//
|
|
@@ -450,6 +474,17 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
|
|
|
450
474
|
//
|
|
451
475
|
if (deleteCount > insertCount) {
|
|
452
476
|
changeTree.shiftAllChangeIndexes(-(deleteCount - insertCount), indexes[start + insertCount]);
|
|
477
|
+
// debugChangeSet("AFTER SHIFT indexes", changeTree.allChanges);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
//
|
|
481
|
+
// FIXME: this code block is duplicated on ChangeTree
|
|
482
|
+
//
|
|
483
|
+
if (changeTree.filteredChanges !== undefined) {
|
|
484
|
+
enqueueChangeTree(changeTree.root, changeTree, 'filteredChanges');
|
|
485
|
+
|
|
486
|
+
} else {
|
|
487
|
+
enqueueChangeTree(changeTree.root, changeTree, 'changes');
|
|
453
488
|
}
|
|
454
489
|
|
|
455
490
|
return this.items.splice(start, deleteCount, ...insertItems);
|
|
@@ -765,10 +800,6 @@ export class ArraySchema<V = any> implements Array<V>, Collection<number, V> {
|
|
|
765
800
|
: this.deletedIndexes[index]
|
|
766
801
|
? this.items[index]
|
|
767
802
|
: this.tmpItems[index] || this.items[index];
|
|
768
|
-
|
|
769
|
-
// return (isEncodeAll)
|
|
770
|
-
// ? this.items[index]
|
|
771
|
-
// : this.tmpItems[index] ?? this.items[index];
|
|
772
803
|
}
|
|
773
804
|
|
|
774
805
|
protected [$deleteByIndex](index: number) {
|