@colyseus/schema 3.0.19 → 3.0.21
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 +103 -62
- package/build/cjs/index.js.map +1 -1
- package/build/esm/index.mjs +103 -62
- package/build/esm/index.mjs.map +1 -1
- package/build/umd/index.js +103 -62
- package/lib/Reflection.js +69 -41
- package/lib/Reflection.js.map +1 -1
- package/lib/encoder/Encoder.js +6 -4
- package/lib/encoder/Encoder.js.map +1 -1
- package/lib/encoder/StateView.js +7 -8
- package/lib/encoder/StateView.js.map +1 -1
- package/lib/types/TypeContext.d.ts +2 -0
- package/lib/types/TypeContext.js +9 -5
- package/lib/types/TypeContext.js.map +1 -1
- package/lib/types/custom/CollectionSchema.d.ts +5 -1
- package/lib/types/custom/CollectionSchema.js +6 -2
- package/lib/types/custom/CollectionSchema.js.map +1 -1
- package/lib/types/custom/MapSchema.js +0 -1
- package/lib/types/custom/MapSchema.js.map +1 -1
- package/lib/types/custom/SetSchema.d.ts +5 -1
- package/lib/types/custom/SetSchema.js +6 -2
- package/lib/types/custom/SetSchema.js.map +1 -1
- package/package.json +1 -1
- package/src/Reflection.ts +79 -46
- package/src/encoder/Encoder.ts +6 -4
- package/src/encoder/StateView.ts +3 -2
- package/src/types/TypeContext.ts +10 -5
- package/src/types/custom/CollectionSchema.ts +9 -3
- package/src/types/custom/MapSchema.ts +1 -1
- package/src/types/custom/SetSchema.ts +8 -3
package/src/Reflection.ts
CHANGED
|
@@ -44,71 +44,104 @@ export class Reflection extends Schema {
|
|
|
44
44
|
const rootType = context.schemas.get(encoder.state.constructor);
|
|
45
45
|
if (rootType > 0) { reflection.rootType = rootType; }
|
|
46
46
|
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
47
|
+
const includedTypeIds = new Set<number>();
|
|
48
|
+
const pendingReflectionTypes: { [typeid: number]: ReflectionType[] } = {};
|
|
49
|
+
|
|
50
|
+
// add type to reflection in a way that respects inheritance
|
|
51
|
+
// (parent types should be added before their children)
|
|
52
|
+
const addType = (type: ReflectionType) => {
|
|
53
|
+
if (type.extendsId === undefined || includedTypeIds.has(type.extendsId)) {
|
|
54
|
+
includedTypeIds.add(type.id);
|
|
55
|
+
|
|
56
|
+
reflection.types.push(type);
|
|
57
|
+
|
|
58
|
+
const deps = pendingReflectionTypes[type.id];
|
|
59
|
+
if (deps !== undefined) {
|
|
60
|
+
delete pendingReflectionTypes[type.id];
|
|
61
|
+
deps.forEach((childType) => addType(childType));
|
|
55
62
|
}
|
|
63
|
+
} else {
|
|
64
|
+
if (pendingReflectionTypes[type.extendsId] === undefined) {
|
|
65
|
+
pendingReflectionTypes[type.extendsId] = [];
|
|
66
|
+
}
|
|
67
|
+
pendingReflectionTypes[type.extendsId].push(type);
|
|
68
|
+
}
|
|
69
|
+
};
|
|
56
70
|
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
context.schemas.forEach((typeid, klass) => {
|
|
72
|
+
const type = new ReflectionType();
|
|
73
|
+
type.id = Number(typeid);
|
|
59
74
|
|
|
60
|
-
|
|
75
|
+
// support inheritance
|
|
76
|
+
const inheritFrom = Object.getPrototypeOf(klass);
|
|
77
|
+
if (inheritFrom !== Schema) {
|
|
78
|
+
type.extendsId = context.schemas.get(inheritFrom);
|
|
79
|
+
}
|
|
61
80
|
|
|
62
|
-
|
|
81
|
+
const metadata = klass[Symbol.metadata];
|
|
63
82
|
|
|
64
|
-
|
|
65
|
-
|
|
83
|
+
//
|
|
84
|
+
// FIXME: this is a workaround for inherited types without additional fields
|
|
85
|
+
// if metadata is the same reference as the parent class - it means the class has no own metadata
|
|
86
|
+
//
|
|
87
|
+
if (metadata !== inheritFrom[Symbol.metadata]) {
|
|
88
|
+
for (const fieldIndex in metadata) {
|
|
89
|
+
const index = Number(fieldIndex);
|
|
90
|
+
const fieldName = metadata[index].name;
|
|
66
91
|
|
|
67
|
-
|
|
68
|
-
|
|
92
|
+
// skip fields from parent classes
|
|
93
|
+
if (!Object.prototype.hasOwnProperty.call(metadata, fieldName)) {
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const reflectionField = new ReflectionField();
|
|
98
|
+
reflectionField.name = fieldName;
|
|
99
|
+
|
|
100
|
+
let fieldType: string;
|
|
69
101
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
fieldType = "ref";
|
|
75
|
-
childTypeSchema = type as typeof Schema;
|
|
102
|
+
const field = metadata[index];
|
|
103
|
+
|
|
104
|
+
if (typeof (field.type) === "string") {
|
|
105
|
+
fieldType = field.type;
|
|
76
106
|
|
|
77
107
|
} else {
|
|
78
|
-
|
|
108
|
+
let childTypeSchema: typeof Schema;
|
|
79
109
|
|
|
80
|
-
|
|
81
|
-
|
|
110
|
+
//
|
|
111
|
+
// TODO: refactor below.
|
|
112
|
+
//
|
|
113
|
+
if (Schema.is(field.type)) {
|
|
114
|
+
fieldType = "ref";
|
|
115
|
+
childTypeSchema = field.type as typeof Schema;
|
|
82
116
|
|
|
83
117
|
} else {
|
|
84
|
-
|
|
118
|
+
fieldType = Object.keys(field.type)[0];
|
|
119
|
+
|
|
120
|
+
if (typeof (field.type[fieldType]) === "string") {
|
|
121
|
+
fieldType += ":" + field.type[fieldType]; // array:string
|
|
122
|
+
|
|
123
|
+
} else {
|
|
124
|
+
childTypeSchema = field.type[fieldType];
|
|
125
|
+
}
|
|
85
126
|
}
|
|
127
|
+
|
|
128
|
+
reflectionField.referencedType = (childTypeSchema)
|
|
129
|
+
? context.getTypeId(childTypeSchema)
|
|
130
|
+
: -1;
|
|
86
131
|
}
|
|
87
132
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
: -1;
|
|
133
|
+
reflectionField.type = fieldType;
|
|
134
|
+
type.fields.push(reflectionField);
|
|
91
135
|
}
|
|
92
|
-
|
|
93
|
-
field.type = fieldType;
|
|
94
|
-
currentType.fields.push(field);
|
|
95
136
|
}
|
|
96
137
|
|
|
97
|
-
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
for (let typeid in context.types) {
|
|
101
|
-
const klass = context.types[typeid];
|
|
102
|
-
const type = new ReflectionType();
|
|
103
|
-
type.id = Number(typeid);
|
|
104
|
-
|
|
105
|
-
// support inheritance
|
|
106
|
-
const inheritFrom = Object.getPrototypeOf(klass);
|
|
107
|
-
if (inheritFrom !== Schema) {
|
|
108
|
-
type.extendsId = context.schemas.get(inheritFrom);
|
|
109
|
-
}
|
|
138
|
+
addType(type);
|
|
139
|
+
});
|
|
110
140
|
|
|
111
|
-
|
|
141
|
+
// in case there are types that were not added due to inheritance
|
|
142
|
+
for (const typeid in pendingReflectionTypes) {
|
|
143
|
+
pendingReflectionTypes[typeid].forEach((type) =>
|
|
144
|
+
reflection.types.push(type))
|
|
112
145
|
}
|
|
113
146
|
|
|
114
147
|
const buf = reflectionEncoder.encodeAll(it);
|
package/src/encoder/Encoder.ts
CHANGED
|
@@ -23,10 +23,12 @@ export class Encoder<T extends Schema = any> {
|
|
|
23
23
|
|
|
24
24
|
constructor(state: T) {
|
|
25
25
|
//
|
|
26
|
-
//
|
|
27
|
-
// (to avoid creating a new context for every new room)
|
|
26
|
+
// Use .cache() here to avoid re-creating a new context for every new room instance.
|
|
28
27
|
//
|
|
29
|
-
this
|
|
28
|
+
// We may need to make this optional in case of dynamically created
|
|
29
|
+
// schemas - which would lead to memory leaks
|
|
30
|
+
//
|
|
31
|
+
this.context = TypeContext.cache(state.constructor as typeof Schema);
|
|
30
32
|
this.root = new Root(this.context);
|
|
31
33
|
|
|
32
34
|
this.setState(state);
|
|
@@ -64,7 +66,7 @@ export class Encoder<T extends Schema = any> {
|
|
|
64
66
|
view.invisible.add(changeTree);
|
|
65
67
|
continue; // skip this change tree
|
|
66
68
|
|
|
67
|
-
} else
|
|
69
|
+
} else {
|
|
68
70
|
view.invisible.delete(changeTree); // remove from invisible list
|
|
69
71
|
}
|
|
70
72
|
}
|
package/src/encoder/StateView.ts
CHANGED
|
@@ -90,6 +90,7 @@ export class StateView {
|
|
|
90
90
|
const op = changeTree.indexedOperations[index] ?? OPERATION.ADD;
|
|
91
91
|
const tagAtIndex = metadata?.[index].tag;
|
|
92
92
|
if (
|
|
93
|
+
!changeTree.isNew && // new structures will be added as part of .encode() call, no need to force it to .encodeView()
|
|
93
94
|
(
|
|
94
95
|
isInvisible || // if "invisible", include all
|
|
95
96
|
tagAtIndex === undefined || // "all change" with no tag
|
|
@@ -132,8 +133,8 @@ export class StateView {
|
|
|
132
133
|
this.addParentOf(changeTree, tag);
|
|
133
134
|
}
|
|
134
135
|
|
|
135
|
-
// parent is already available, no need to add it!
|
|
136
|
-
if (!this.invisible.has(changeTree)) { return; }
|
|
136
|
+
// // parent is already available, no need to add it!
|
|
137
|
+
// if (!this.invisible.has(changeTree)) { return; }
|
|
137
138
|
}
|
|
138
139
|
|
|
139
140
|
// add parent's tag properties
|
package/src/types/TypeContext.ts
CHANGED
|
@@ -14,6 +14,7 @@ export class TypeContext {
|
|
|
14
14
|
* Keeps track of which classes extends which. (parent -> children)
|
|
15
15
|
*/
|
|
16
16
|
static inheritedTypes = new Map<typeof Schema, Set<typeof Schema>>();
|
|
17
|
+
static cachedContexts = new Map<typeof Schema, TypeContext>();
|
|
17
18
|
|
|
18
19
|
static register(target: typeof Schema) {
|
|
19
20
|
const parent = Object.getPrototypeOf(target);
|
|
@@ -27,13 +28,17 @@ export class TypeContext {
|
|
|
27
28
|
}
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
static cache (rootClass: typeof Schema) {
|
|
32
|
+
let context = TypeContext.cachedContexts.get(rootClass);
|
|
33
|
+
if (!context) {
|
|
34
|
+
context = new TypeContext(rootClass);
|
|
35
|
+
TypeContext.cachedContexts.set(rootClass, context);
|
|
36
|
+
}
|
|
37
|
+
return context;
|
|
38
|
+
}
|
|
39
|
+
|
|
30
40
|
constructor(rootClass?: typeof Schema) {
|
|
31
41
|
if (rootClass) {
|
|
32
|
-
//
|
|
33
|
-
// TODO:
|
|
34
|
-
// cache "discoverTypes" results for each rootClass
|
|
35
|
-
// to avoid re-discovering types for each new context/room
|
|
36
|
-
//
|
|
37
42
|
this.discoverTypes(rootClass);
|
|
38
43
|
}
|
|
39
44
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex } from "../symbols";
|
|
1
|
+
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd } from "../symbols";
|
|
2
2
|
import { ChangeTree } from "../../encoder/ChangeTree";
|
|
3
3
|
import { OPERATION } from "../../encoding/spec";
|
|
4
4
|
import { registerType } from "../registry";
|
|
@@ -10,8 +10,10 @@ import type { StateView } from "../../encoder/StateView";
|
|
|
10
10
|
type K = number; // TODO: allow to specify K generic on MapSchema.
|
|
11
11
|
|
|
12
12
|
export class CollectionSchema<V=any> implements Collection<K, V>{
|
|
13
|
+
|
|
13
14
|
protected $items: Map<number, V> = new Map<number, V>();
|
|
14
15
|
protected $indexes: Map<number, number> = new Map<number, number>();
|
|
16
|
+
protected deletedItems: { [field: string]: V } = {};
|
|
15
17
|
|
|
16
18
|
protected $refId: number = 0;
|
|
17
19
|
|
|
@@ -31,7 +33,7 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
|
|
|
31
33
|
return (
|
|
32
34
|
!view ||
|
|
33
35
|
typeof (ref[$childType]) === "string" ||
|
|
34
|
-
view.items.has(ref[$getByIndex](index)[$changes])
|
|
36
|
+
view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
|
|
35
37
|
);
|
|
36
38
|
}
|
|
37
39
|
|
|
@@ -101,7 +103,7 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
|
|
|
101
103
|
return false;
|
|
102
104
|
}
|
|
103
105
|
|
|
104
|
-
this[$changes].delete(index);
|
|
106
|
+
this.deletedItems[index] = this[$changes].delete(index);
|
|
105
107
|
this.$indexes.delete(index);
|
|
106
108
|
|
|
107
109
|
return this.$items.delete(index);
|
|
@@ -162,6 +164,10 @@ export class CollectionSchema<V=any> implements Collection<K, V>{
|
|
|
162
164
|
this.$indexes.delete(index);
|
|
163
165
|
}
|
|
164
166
|
|
|
167
|
+
protected [$onEncodeEnd]() {
|
|
168
|
+
this.deletedItems = {};
|
|
169
|
+
}
|
|
170
|
+
|
|
165
171
|
toArray() {
|
|
166
172
|
return Array.from(this.$items.values());
|
|
167
173
|
}
|
|
@@ -143,7 +143,7 @@ export class MapSchema<V=any, K extends string = string> implements Map<K, V>, C
|
|
|
143
143
|
delete(key: K) {
|
|
144
144
|
const index = this[$changes].indexes[key];
|
|
145
145
|
|
|
146
|
-
this.deletedItems[index] = this[$changes].delete(index)
|
|
146
|
+
this.deletedItems[index] = this[$changes].delete(index);
|
|
147
147
|
|
|
148
148
|
return this.$items.delete(key);
|
|
149
149
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { OPERATION } from "../../encoding/spec";
|
|
2
2
|
import { registerType } from "../registry";
|
|
3
|
-
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex } from "../symbols";
|
|
3
|
+
import { $changes, $childType, $decoder, $deleteByIndex, $encoder, $filter, $getByIndex, $onEncodeEnd } from "../symbols";
|
|
4
4
|
import { Collection } from "../HelperTypes";
|
|
5
5
|
import { ChangeTree } from "../../encoder/ChangeTree";
|
|
6
6
|
import { encodeKeyValueOperation } from "../../encoder/EncodeOperation";
|
|
@@ -11,6 +11,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
|
|
|
11
11
|
|
|
12
12
|
protected $items: Map<number, V> = new Map<number, V>();
|
|
13
13
|
protected $indexes: Map<number, number> = new Map<number, number>();
|
|
14
|
+
protected deletedItems: { [field: string]: V } = {};
|
|
14
15
|
|
|
15
16
|
protected $refId: number = 0;
|
|
16
17
|
|
|
@@ -30,7 +31,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
|
|
|
30
31
|
return (
|
|
31
32
|
!view ||
|
|
32
33
|
typeof (ref[$childType]) === "string" ||
|
|
33
|
-
view.items.has(ref[$getByIndex](index)[$changes])
|
|
34
|
+
view.items.has((ref[$getByIndex](index) ?? ref.deletedItems[index])[$changes])
|
|
34
35
|
);
|
|
35
36
|
}
|
|
36
37
|
|
|
@@ -98,7 +99,7 @@ export class SetSchema<V=any> implements Collection<number, V> {
|
|
|
98
99
|
return false;
|
|
99
100
|
}
|
|
100
101
|
|
|
101
|
-
this[$changes].delete(index);
|
|
102
|
+
this.deletedItems[index] = this[$changes].delete(index);
|
|
102
103
|
this.$indexes.delete(index);
|
|
103
104
|
|
|
104
105
|
return this.$items.delete(index);
|
|
@@ -172,6 +173,10 @@ export class SetSchema<V=any> implements Collection<number, V> {
|
|
|
172
173
|
this.$indexes.delete(index);
|
|
173
174
|
}
|
|
174
175
|
|
|
176
|
+
protected [$onEncodeEnd]() {
|
|
177
|
+
this.deletedItems = {};
|
|
178
|
+
}
|
|
179
|
+
|
|
175
180
|
toArray() {
|
|
176
181
|
return Array.from(this.$items.values());
|
|
177
182
|
}
|