-
Notifications
You must be signed in to change notification settings - Fork 0
/
core-visitors.ts
108 lines (102 loc) · 4.42 KB
/
core-visitors.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { Add, Colyseus, Container, Events, Remove, Replace, Traverse, Visitor } from './types';
import { ArraySchema, MapSchema, Schema } from '@colyseus/schema';
import { CallbacksCleanup } from './destructors';
import { getFieldsList } from './internals-extract';
export const handleSchema = Object.freeze({
cache: new CallbacksCleanup(),
visit(traverse: Traverse, state: Container, events: Events, namespace: string) {
if (!(state instanceof Schema)) {
return false;
}
const destructors = this.cache.resetDestructors(state);
for (const field of getFieldsList(state)) {
const fieldNamespace = `${namespace}/${field as string}`;
const d = state.listen(field, (value, previousValue) => {
if (value === previousValue) return;
events.emit(fieldNamespace, Replace(fieldNamespace, value as Colyseus));
traverse(value as Colyseus, events, fieldNamespace);
});
destructors.add(d);
}
this.cache.recheckCallbacks(state);
return true;
},
});
export const handleArraySchema = Object.freeze({
cache: new CallbacksCleanup(),
visit(traverse: Traverse, state: Container, events: Events, namespace: string) {
if (!(state instanceof ArraySchema)) {
return false;
}
const knownKeys = new Set<number>(); // for ignoring first and last onChange
const destructors = this.cache.resetDestructors(state);
destructors.add(
state.onAdd((value: Colyseus, field: number) => {
const fieldNamespace = `${namespace}/${field}`;
events.emit(namespace, Add(fieldNamespace, value));
traverse(value, events, fieldNamespace);
})
);
destructors.add(
state.onChange((value: Colyseus, field: number) => {
if (knownKeys.has(field)) {
const fieldNamespace = `${namespace}/${field}`;
events.emit(fieldNamespace, Replace(fieldNamespace, value));
traverse(value, events, fieldNamespace);
} else {
knownKeys.add(field);
}
})
);
destructors.add(
state.onRemove((_, field: number) => {
knownKeys.delete(field);
const fieldNamespace = `${namespace}/${field}`;
events.emit(namespace, Remove(fieldNamespace));
})
);
this.cache.recheckCallbacks(state);
return true;
},
});
export const handleMapSchema = Object.freeze({
cache: new CallbacksCleanup(),
visit(traverse: Traverse, state: Container, events: Events, namespace: string) {
// Check if it is going to handle the state object, and return `false` if not.
if (!(state instanceof MapSchema)) {
return false;
}
const knownKeys = new Set<string>(); // for ignoring first and last onChange
const destructors = this.cache.resetDestructors(state);
// Hook on new elements and register destructors
destructors.add(
state.onAdd((value: Colyseus, field: string) => {
const fieldNamespace = `${namespace}/${field}`; // path to the new element
events.emit(namespace, Add(fieldNamespace, value)); // emit the add event
traverse(value, events, fieldNamespace); // call the traverse function on the new value
})
);
destructors.add(
state.onChange((value: Colyseus, field: string) => {
if (knownKeys.has(field)) {
const fieldNamespace = `${namespace}/${field}`;
events.emit(fieldNamespace, Replace(fieldNamespace, value));
traverse(value, events, fieldNamespace);
} else {
knownKeys.add(field);
}
})
);
destructors.add(
state.onRemove((_, field: string) => {
knownKeys.delete(field);
const fieldNamespace = `${namespace}/${field}`;
events.emit(namespace, Remove(fieldNamespace));
})
);
this.cache.recheckCallbacks(state);
return true;
},
});
// TODO handle CollectionSchema and andSetSchema
export const coreVisitors: ReadonlyArray<Visitor> = Object.freeze([handleSchema, handleArraySchema, handleMapSchema]);