@dcl/ecs 7.0.6-4177592674.commit-39cdc99 → 7.0.6-4180146485.commit-9a7dde9
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/dist/components/extended/Animator.d.ts +3 -2
- package/dist/components/extended/Material.d.ts +2 -2
- package/dist/components/extended/MeshCollider.d.ts +2 -2
- package/dist/components/extended/MeshRenderer.d.ts +2 -2
- package/dist/components/generated/global.gen.d.ts +26 -26
- package/dist/components/generated/index.gen.d.ts +32 -31
- package/dist/components/generated/index.gen.js +4 -1
- package/dist/components/generated/pb/decentraland/sdk/components/pointer_events_result.gen.d.ts +1 -9
- package/dist/components/generated/pb/decentraland/sdk/components/pointer_events_result.gen.js +2 -36
- package/dist/components/index.d.ts +9 -9
- package/dist/components/index.js +1 -1
- package/dist/components/{legacy → manual}/Transform.d.ts +2 -2
- package/dist/components/{legacy → manual}/Transform.js +0 -0
- package/dist/components/types.d.ts +1 -1
- package/dist/engine/component.d.ts +74 -27
- package/dist/engine/component.js +7 -240
- package/dist/engine/grow-only-value-set-component-definition.d.ts +8 -0
- package/dist/engine/grow-only-value-set-component-definition.js +132 -0
- package/dist/engine/index.d.ts +1 -2
- package/dist/engine/index.js +18 -1
- package/dist/engine/input.d.ts +3 -3
- package/dist/engine/input.js +75 -68
- package/dist/engine/lww-element-set-component-definition.d.ts +6 -0
- package/dist/engine/lww-element-set-component-definition.js +229 -0
- package/dist/engine/readonly.d.ts +2 -2
- package/dist/engine/types.d.ts +20 -9
- package/dist/engine/types.js +1 -1
- package/dist/runtime/invariant.d.ts +1 -0
- package/dist/runtime/invariant.js +6 -1
- package/dist/runtime/types.d.ts +1 -2
- package/dist/runtime/types.js +0 -1
- package/dist/schemas/ISchema.d.ts +3 -2
- package/dist/schemas/Map.d.ts +0 -1
- package/dist/serialization/crdt/appendValue.d.ts +1 -0
- package/dist/serialization/crdt/appendValue.js +49 -0
- package/dist/serialization/crdt/index.d.ts +1 -0
- package/dist/serialization/crdt/index.js +1 -0
- package/dist/serialization/crdt/message.js +4 -0
- package/dist/serialization/crdt/types.d.ts +24 -3
- package/dist/serialization/crdt/types.js +2 -1
- package/dist/systems/crdt/index.d.ts +1 -1
- package/dist/systems/crdt/index.js +21 -8
- package/dist/systems/events.d.ts +2 -2
- package/package.json +3 -3
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { ISchema } from '../schemas';
|
|
2
|
+
import { PutComponentMessageBody, DeleteComponentMessageBody, CrdtMessageBody } from '../serialization/crdt';
|
|
3
|
+
import { Entity } from './entity';
|
|
4
|
+
export declare function incrementTimestamp(entity: Entity, timestamps: Map<Entity, number>): number;
|
|
5
|
+
export declare function createUpdateLwwFromCrdt(componentId: number, timestamps: Map<Entity, number>, schema: Pick<ISchema<any>, 'serialize' | 'deserialize'>, data: Map<Entity, unknown>): (msg: CrdtMessageBody) => [null | PutComponentMessageBody | DeleteComponentMessageBody, any];
|
|
6
|
+
export declare function createGetCrdtMessagesForLww(componentId: number, timestamps: Map<Entity, number>, dirtyIterator: Set<Entity>, schema: Pick<ISchema<any>, 'serialize'>, data: Map<Entity, unknown>): () => Generator<PutComponentMessageBody | DeleteComponentMessageBody, void, unknown>;
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
import { ReadWriteByteBuffer } from '../serialization/ByteBuffer';
|
|
2
|
+
import { ProcessMessageResultType, CrdtMessageType } from '../serialization/crdt';
|
|
3
|
+
import { dataCompare } from '../systems/crdt/utils';
|
|
4
|
+
import { deepReadonly } from './readonly';
|
|
5
|
+
export function incrementTimestamp(entity, timestamps) {
|
|
6
|
+
const newTimestamp = (timestamps.get(entity) || 0) + 1;
|
|
7
|
+
timestamps.set(entity, newTimestamp);
|
|
8
|
+
return newTimestamp;
|
|
9
|
+
}
|
|
10
|
+
export function createUpdateLwwFromCrdt(componentId, timestamps, schema, data) {
|
|
11
|
+
/**
|
|
12
|
+
* Process the received message only if the lamport number recieved is higher
|
|
13
|
+
* than the stored one. If its lower, we spread it to the network to correct the peer.
|
|
14
|
+
* If they are equal, the bigger raw data wins.
|
|
15
|
+
|
|
16
|
+
* Returns the recieved data if the lamport number was bigger than ours.
|
|
17
|
+
* If it was an outdated message, then we return void
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
function crdtRuleForCurrentState(message) {
|
|
21
|
+
const { entityId, timestamp } = message;
|
|
22
|
+
const currentTimestamp = timestamps.get(entityId);
|
|
23
|
+
// The received message is > than our current value, update our state.components.
|
|
24
|
+
if (currentTimestamp === undefined || currentTimestamp < timestamp) {
|
|
25
|
+
return ProcessMessageResultType.StateUpdatedTimestamp;
|
|
26
|
+
}
|
|
27
|
+
// Outdated Message. Resend our state message through the wire.
|
|
28
|
+
if (currentTimestamp > timestamp) {
|
|
29
|
+
// console.log('2', currentTimestamp, timestamp)
|
|
30
|
+
return ProcessMessageResultType.StateOutdatedTimestamp;
|
|
31
|
+
}
|
|
32
|
+
// Deletes are idempotent
|
|
33
|
+
if (message.type === CrdtMessageType.DELETE_COMPONENT && !data.has(entityId)) {
|
|
34
|
+
return ProcessMessageResultType.NoChanges;
|
|
35
|
+
}
|
|
36
|
+
let currentDataGreater = 0;
|
|
37
|
+
if (data.has(entityId)) {
|
|
38
|
+
const writeBuffer = new ReadWriteByteBuffer();
|
|
39
|
+
schema.serialize(data.get(entityId), writeBuffer);
|
|
40
|
+
currentDataGreater = dataCompare(writeBuffer.toBinary(), message.data || null);
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
currentDataGreater = dataCompare(null, message.data);
|
|
44
|
+
}
|
|
45
|
+
// Same data, same timestamp. Weirdo echo message.
|
|
46
|
+
// console.log('3', currentDataGreater, writeBuffer.toBinary(), (message as any).data || null)
|
|
47
|
+
if (currentDataGreater === 0) {
|
|
48
|
+
return ProcessMessageResultType.NoChanges;
|
|
49
|
+
}
|
|
50
|
+
else if (currentDataGreater > 0) {
|
|
51
|
+
// Current data is greater
|
|
52
|
+
return ProcessMessageResultType.StateOutdatedData;
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
// Curent data is lower
|
|
56
|
+
return ProcessMessageResultType.StateUpdatedData;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return (msg) => {
|
|
60
|
+
/* istanbul ignore next */
|
|
61
|
+
if (msg.type !== CrdtMessageType.PUT_COMPONENT && msg.type !== CrdtMessageType.DELETE_COMPONENT)
|
|
62
|
+
/* istanbul ignore next */
|
|
63
|
+
return [null, data.get(msg.entityId)];
|
|
64
|
+
const action = crdtRuleForCurrentState(msg);
|
|
65
|
+
const entity = msg.entityId;
|
|
66
|
+
switch (action) {
|
|
67
|
+
case ProcessMessageResultType.StateUpdatedData:
|
|
68
|
+
case ProcessMessageResultType.StateUpdatedTimestamp: {
|
|
69
|
+
timestamps.set(entity, msg.timestamp);
|
|
70
|
+
if (msg.type === CrdtMessageType.PUT_COMPONENT) {
|
|
71
|
+
const buf = new ReadWriteByteBuffer(msg.data);
|
|
72
|
+
data.set(entity, schema.deserialize(buf));
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
data.delete(entity);
|
|
76
|
+
}
|
|
77
|
+
return [null, data.get(entity)];
|
|
78
|
+
}
|
|
79
|
+
case ProcessMessageResultType.StateOutdatedTimestamp:
|
|
80
|
+
case ProcessMessageResultType.StateOutdatedData: {
|
|
81
|
+
if (data.has(entity)) {
|
|
82
|
+
const writeBuffer = new ReadWriteByteBuffer();
|
|
83
|
+
schema.serialize(data.get(entity), writeBuffer);
|
|
84
|
+
return [
|
|
85
|
+
{
|
|
86
|
+
type: CrdtMessageType.PUT_COMPONENT,
|
|
87
|
+
componentId,
|
|
88
|
+
data: writeBuffer.toBinary(),
|
|
89
|
+
entityId: entity,
|
|
90
|
+
timestamp: timestamps.get(entity)
|
|
91
|
+
},
|
|
92
|
+
data.get(entity)
|
|
93
|
+
];
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
return [
|
|
97
|
+
{
|
|
98
|
+
type: CrdtMessageType.DELETE_COMPONENT,
|
|
99
|
+
componentId,
|
|
100
|
+
entityId: entity,
|
|
101
|
+
timestamp: timestamps.get(entity)
|
|
102
|
+
},
|
|
103
|
+
undefined
|
|
104
|
+
];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return [null, data.get(entity)];
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
export function createGetCrdtMessagesForLww(componentId, timestamps, dirtyIterator, schema, data) {
|
|
112
|
+
return function* () {
|
|
113
|
+
for (const entity of dirtyIterator) {
|
|
114
|
+
const newTimestamp = incrementTimestamp(entity, timestamps);
|
|
115
|
+
if (data.has(entity)) {
|
|
116
|
+
const writeBuffer = new ReadWriteByteBuffer();
|
|
117
|
+
schema.serialize(data.get(entity), writeBuffer);
|
|
118
|
+
const msg = {
|
|
119
|
+
type: CrdtMessageType.PUT_COMPONENT,
|
|
120
|
+
componentId,
|
|
121
|
+
entityId: entity,
|
|
122
|
+
data: writeBuffer.toBinary(),
|
|
123
|
+
timestamp: newTimestamp
|
|
124
|
+
};
|
|
125
|
+
yield msg;
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
const msg = {
|
|
129
|
+
type: CrdtMessageType.DELETE_COMPONENT,
|
|
130
|
+
componentId,
|
|
131
|
+
entityId: entity,
|
|
132
|
+
timestamp: newTimestamp
|
|
133
|
+
};
|
|
134
|
+
yield msg;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
dirtyIterator.clear();
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @internal
|
|
142
|
+
*/
|
|
143
|
+
export function createComponentDefinitionFromSchema(componentName, componentId, schema) {
|
|
144
|
+
const data = new Map();
|
|
145
|
+
const dirtyIterator = new Set();
|
|
146
|
+
const timestamps = new Map();
|
|
147
|
+
return {
|
|
148
|
+
get componentId() {
|
|
149
|
+
return componentId;
|
|
150
|
+
},
|
|
151
|
+
get componentName() {
|
|
152
|
+
return componentName;
|
|
153
|
+
},
|
|
154
|
+
get componentType() {
|
|
155
|
+
// a getter is used here to prevent accidental changes
|
|
156
|
+
return 0 /* ComponentType.LastWriteWinElementSet */;
|
|
157
|
+
},
|
|
158
|
+
schema,
|
|
159
|
+
has(entity) {
|
|
160
|
+
return data.has(entity);
|
|
161
|
+
},
|
|
162
|
+
deleteFrom(entity, markAsDirty = true) {
|
|
163
|
+
const component = data.get(entity);
|
|
164
|
+
if (data.delete(entity) && markAsDirty) {
|
|
165
|
+
dirtyIterator.add(entity);
|
|
166
|
+
}
|
|
167
|
+
return component || null;
|
|
168
|
+
},
|
|
169
|
+
entityDeleted(entity, markAsDirty) {
|
|
170
|
+
if (data.delete(entity) && markAsDirty) {
|
|
171
|
+
dirtyIterator.add(entity);
|
|
172
|
+
}
|
|
173
|
+
},
|
|
174
|
+
getOrNull(entity) {
|
|
175
|
+
const component = data.get(entity);
|
|
176
|
+
return component ? deepReadonly(component) : null;
|
|
177
|
+
},
|
|
178
|
+
get(entity) {
|
|
179
|
+
const component = data.get(entity);
|
|
180
|
+
if (!component) {
|
|
181
|
+
throw new Error(`[getFrom] Component ${componentName} for entity #${entity} not found`);
|
|
182
|
+
}
|
|
183
|
+
return deepReadonly(component);
|
|
184
|
+
},
|
|
185
|
+
create(entity, value) {
|
|
186
|
+
const component = data.get(entity);
|
|
187
|
+
if (component) {
|
|
188
|
+
throw new Error(`[create] Component ${componentName} for ${entity} already exists`);
|
|
189
|
+
}
|
|
190
|
+
const usedValue = value === undefined ? schema.create() : schema.extend ? schema.extend(value) : value;
|
|
191
|
+
data.set(entity, usedValue);
|
|
192
|
+
dirtyIterator.add(entity);
|
|
193
|
+
return usedValue;
|
|
194
|
+
},
|
|
195
|
+
createOrReplace(entity, value) {
|
|
196
|
+
const usedValue = value === undefined ? schema.create() : schema.extend ? schema.extend(value) : value;
|
|
197
|
+
data.set(entity, usedValue);
|
|
198
|
+
dirtyIterator.add(entity);
|
|
199
|
+
return usedValue;
|
|
200
|
+
},
|
|
201
|
+
getMutableOrNull(entity) {
|
|
202
|
+
const component = data.get(entity);
|
|
203
|
+
if (!component) {
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
dirtyIterator.add(entity);
|
|
207
|
+
return component;
|
|
208
|
+
},
|
|
209
|
+
getMutable(entity) {
|
|
210
|
+
const component = this.getMutableOrNull(entity);
|
|
211
|
+
if (component === null) {
|
|
212
|
+
throw new Error(`[mutable] Component ${componentName} for ${entity} not found`);
|
|
213
|
+
}
|
|
214
|
+
return component;
|
|
215
|
+
},
|
|
216
|
+
*iterator() {
|
|
217
|
+
for (const [entity, component] of data) {
|
|
218
|
+
yield [entity, component];
|
|
219
|
+
}
|
|
220
|
+
},
|
|
221
|
+
*dirtyIterator() {
|
|
222
|
+
for (const entity of dirtyIterator) {
|
|
223
|
+
yield entity;
|
|
224
|
+
}
|
|
225
|
+
},
|
|
226
|
+
getCrdtUpdates: createGetCrdtMessagesForLww(componentId, timestamps, dirtyIterator, schema, data),
|
|
227
|
+
updateFromCrdt: createUpdateLwwFromCrdt(componentId, timestamps, schema, data)
|
|
228
|
+
};
|
|
229
|
+
}
|
|
@@ -2,7 +2,7 @@ import { ComponentDefinition } from './component';
|
|
|
2
2
|
/**
|
|
3
3
|
* @public
|
|
4
4
|
*/
|
|
5
|
-
export type DeepReadonlyMap<K, V> = ReadonlyMap<
|
|
5
|
+
export type DeepReadonlyMap<K, V> = ReadonlyMap<K, DeepReadonly<V>>;
|
|
6
6
|
/**
|
|
7
7
|
* @public
|
|
8
8
|
*/
|
|
@@ -26,4 +26,4 @@ export type ReadonlyComponentSchema<T extends [ComponentDefinition<unknown>, ...
|
|
|
26
26
|
/**
|
|
27
27
|
* @public
|
|
28
28
|
*/
|
|
29
|
-
export type DeepReadonly<T> = T extends ReadonlyPrimitive ? T : T extends Map<infer K, infer V> ? DeepReadonlyMap<K, V> : T extends Set<infer M> ? DeepReadonlySet<M> : DeepReadonlyObject<T>;
|
|
29
|
+
export type DeepReadonly<T> = T extends ReadonlyPrimitive ? T : T extends Array<infer K> ? ReadonlyArray<DeepReadonly<K>> : T extends Map<infer K, infer V> ? DeepReadonlyMap<K, V> : T extends Set<infer M> ? DeepReadonlySet<M> : DeepReadonlyObject<T>;
|
package/dist/engine/types.d.ts
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
import type { ISchema } from '../schemas/ISchema';
|
|
2
2
|
import { MapResult, Spec } from '../schemas/Map';
|
|
3
3
|
import { Transport } from '../systems/crdt/types';
|
|
4
|
-
import { ComponentDefinition } from './component';
|
|
4
|
+
import { ComponentDefinition, GrowOnlyValueSetComponentDefinition, LastWriteWinElementSetComponentDefinition } from './component';
|
|
5
5
|
import { Entity, EntityState } from './entity';
|
|
6
|
+
import { ValueSetOptions } from './grow-only-value-set-component-definition';
|
|
6
7
|
import { ReadonlyComponentSchema } from './readonly';
|
|
7
8
|
import { SystemFn } from './systems';
|
|
9
|
+
export * from './component';
|
|
10
|
+
export { ValueSetOptions };
|
|
8
11
|
/**
|
|
9
12
|
* @public
|
|
10
13
|
*/
|
|
11
14
|
export type Unpacked<T> = T extends (infer U)[] ? U : T;
|
|
12
|
-
/**
|
|
13
|
-
* @public
|
|
14
|
-
*/
|
|
15
|
-
export type ComponentSchema<T extends [ComponentDefinition<any>, ...ComponentDefinition<any>[]]> = {
|
|
16
|
-
[K in keyof T]: T[K] extends ComponentDefinition<any> ? ReturnType<T[K]['getMutable']> : never;
|
|
17
|
-
};
|
|
18
15
|
/**
|
|
19
16
|
* @public
|
|
20
17
|
* Overrides component definition to support partial default values
|
|
21
18
|
*/
|
|
22
|
-
export interface MapComponentDefinition<T> extends
|
|
19
|
+
export interface MapComponentDefinition<T> extends LastWriteWinElementSetComponentDefinition<T> {
|
|
23
20
|
/**
|
|
24
21
|
* Add the current component to an entity, throw an error if the component already exists (use `createOrReplace` instead).
|
|
25
22
|
* - Internal comment: This method adds the <entity,component> to the list to be reviewed next frame
|
|
@@ -128,7 +125,21 @@ export interface IEngine {
|
|
|
128
125
|
* const StateComponent = engine.defineComponentFromSchema("my-lib::VisibleComponent", Schemas.Bool)
|
|
129
126
|
* ```
|
|
130
127
|
*/
|
|
131
|
-
defineComponentFromSchema<T>(componentName: string, spec: ISchema<T>):
|
|
128
|
+
defineComponentFromSchema<T>(componentName: string, spec: ISchema<T>): LastWriteWinElementSetComponentDefinition<T>;
|
|
129
|
+
/**
|
|
130
|
+
* @public
|
|
131
|
+
* Defines a value set component.
|
|
132
|
+
* @param componentName - unique name to identify the component, a hash is calculated for it, it will fail if the hash has collisions.
|
|
133
|
+
* @param spec - An object with schema fields
|
|
134
|
+
* @returns The component definition
|
|
135
|
+
*
|
|
136
|
+
* @example
|
|
137
|
+
* ```ts
|
|
138
|
+
* const StateComponentId = 10023
|
|
139
|
+
* const StateComponent = engine.defineValueSetComponentFromSchema("my-lib::VisibleComponent", Schemas.Int)
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
defineValueSetComponentFromSchema<T>(componentName: string, spec: ISchema<T>, options: ValueSetOptions<T>): GrowOnlyValueSetComponentDefinition<T>;
|
|
132
143
|
/**
|
|
133
144
|
* @public
|
|
134
145
|
* Get the component definition from the component id.
|
package/dist/engine/types.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './component';
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
/* istanbul ignore file */
|
|
2
|
+
export const __DEV__ = (typeof DEBUG === 'boolean' && DEBUG) ||
|
|
3
|
+
(typeof process === 'object' &&
|
|
4
|
+
(process.env?.NODE_ENV !== 'production' || process.env?.NODE_ENV === 'development')) ||
|
|
5
|
+
false;
|
|
1
6
|
export function checkNotThenable(t, error) {
|
|
2
|
-
if (
|
|
7
|
+
if (__DEV__) {
|
|
3
8
|
if (t && typeof t === 'object' && typeof t.then === 'function') {
|
|
4
9
|
throw new Error(error);
|
|
5
10
|
}
|
package/dist/runtime/types.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
export type { SystemFn } from '../engine/systems';
|
|
2
2
|
export type { TransportMessage, ReceiveMessage, Transport } from '../systems/crdt/types';
|
|
3
|
-
export { TransformType, TransformComponent } from '../components/
|
|
3
|
+
export { TransformType, TransformComponent } from '../components/manual/Transform';
|
|
4
4
|
export * from '../engine/component';
|
|
5
5
|
export * from '../schemas/typing';
|
|
6
6
|
export type { MapResult, Spec } from '../schemas/Map';
|
|
7
|
-
export * from '../engine/component';
|
package/dist/runtime/types.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
|
+
import { DeepReadonly } from '../engine/readonly';
|
|
1
2
|
import { ByteBuffer } from '../serialization/ByteBuffer';
|
|
2
3
|
/**
|
|
3
4
|
* @public
|
|
4
5
|
*/
|
|
5
6
|
export interface ISchema<T = any> {
|
|
6
|
-
serialize(value: T
|
|
7
|
+
serialize(value: DeepReadonly<T>, builder: ByteBuffer): void;
|
|
7
8
|
deserialize(reader: ByteBuffer): T;
|
|
8
9
|
create(): T;
|
|
9
|
-
extend?: (base
|
|
10
|
+
extend?: (base: Partial<DeepReadonly<T>> | undefined) => T;
|
|
10
11
|
}
|
package/dist/schemas/Map.d.ts
CHANGED
|
@@ -18,4 +18,3 @@ export type MapResult<T extends Spec> = ToOptional<{
|
|
|
18
18
|
export type MapResultWithOptional<T extends Spec> = ToOptional<{
|
|
19
19
|
[K in keyof T]?: T[K] extends ISchema ? ReturnType<T[K]['deserialize']> : T[K] extends Spec ? MapResult<T[K]> : never;
|
|
20
20
|
}>;
|
|
21
|
-
export type MapSchemaType<T extends Spec> = ISchema<MapResult<T>>;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { CrdtMessageProtocol } from './crdtMessageProtocol';
|
|
2
|
+
import { CrdtMessageType, CRDT_MESSAGE_HEADER_LENGTH } from './types';
|
|
3
|
+
/**
|
|
4
|
+
* @internal
|
|
5
|
+
*/
|
|
6
|
+
export var AppendValueOperation;
|
|
7
|
+
(function (AppendValueOperation) {
|
|
8
|
+
AppendValueOperation.MESSAGE_HEADER_LENGTH = 16;
|
|
9
|
+
/**
|
|
10
|
+
* Call this function for an optimal writing data passing the ByteBuffer
|
|
11
|
+
* already allocated
|
|
12
|
+
*/
|
|
13
|
+
function write(entity, timestamp, componentId, data, buf) {
|
|
14
|
+
// reserve the beginning
|
|
15
|
+
const startMessageOffset = buf.incrementWriteOffset(CRDT_MESSAGE_HEADER_LENGTH + AppendValueOperation.MESSAGE_HEADER_LENGTH);
|
|
16
|
+
// write body
|
|
17
|
+
buf.writeBuffer(data, false);
|
|
18
|
+
const messageLength = buf.currentWriteOffset() - startMessageOffset;
|
|
19
|
+
// Write CrdtMessage header
|
|
20
|
+
buf.setUint32(startMessageOffset, messageLength);
|
|
21
|
+
buf.setUint32(startMessageOffset + 4, CrdtMessageType.APPEND_VALUE);
|
|
22
|
+
// Write ComponentOperation header
|
|
23
|
+
buf.setUint32(startMessageOffset + 8, entity);
|
|
24
|
+
buf.setUint32(startMessageOffset + 12, componentId);
|
|
25
|
+
buf.setUint32(startMessageOffset + 16, timestamp);
|
|
26
|
+
const newLocal = messageLength - AppendValueOperation.MESSAGE_HEADER_LENGTH - CRDT_MESSAGE_HEADER_LENGTH;
|
|
27
|
+
buf.setUint32(startMessageOffset + 20, newLocal);
|
|
28
|
+
}
|
|
29
|
+
AppendValueOperation.write = write;
|
|
30
|
+
function read(buf) {
|
|
31
|
+
const header = CrdtMessageProtocol.readHeader(buf);
|
|
32
|
+
/* istanbul ignore if */
|
|
33
|
+
if (!header) {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
/* istanbul ignore if */
|
|
37
|
+
if (header.type !== CrdtMessageType.APPEND_VALUE) {
|
|
38
|
+
throw new Error('AppendValueOperation tried to read another message type.');
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
...header,
|
|
42
|
+
entityId: buf.readUint32(),
|
|
43
|
+
componentId: buf.readUint32(),
|
|
44
|
+
timestamp: buf.readUint32(),
|
|
45
|
+
data: buf.readBuffer()
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
AppendValueOperation.read = read;
|
|
49
|
+
})(AppendValueOperation || (AppendValueOperation = {}));
|
|
@@ -3,6 +3,7 @@ import { CrdtMessageType } from './types';
|
|
|
3
3
|
import { PutComponentOperation } from './putComponent';
|
|
4
4
|
import { DeleteComponent } from './deleteComponent';
|
|
5
5
|
import { DeleteEntity } from './deleteEntity';
|
|
6
|
+
import { AppendValueOperation } from './appendValue';
|
|
6
7
|
export function readMessage(buf) {
|
|
7
8
|
const header = CrdtMessageProtocol.getHeader(buf);
|
|
8
9
|
if (!header)
|
|
@@ -13,6 +14,9 @@ export function readMessage(buf) {
|
|
|
13
14
|
else if (header.type === CrdtMessageType.DELETE_COMPONENT) {
|
|
14
15
|
return DeleteComponent.read(buf);
|
|
15
16
|
}
|
|
17
|
+
else if (header.type === CrdtMessageType.APPEND_VALUE) {
|
|
18
|
+
return AppendValueOperation.read(buf);
|
|
19
|
+
}
|
|
16
20
|
else if (header.type === CrdtMessageType.DELETE_ENTITY) {
|
|
17
21
|
return DeleteEntity.read(buf);
|
|
18
22
|
}
|
|
@@ -7,7 +7,8 @@ export declare enum CrdtMessageType {
|
|
|
7
7
|
PUT_COMPONENT = 1,
|
|
8
8
|
DELETE_COMPONENT = 2,
|
|
9
9
|
DELETE_ENTITY = 3,
|
|
10
|
-
|
|
10
|
+
APPEND_VALUE = 4,
|
|
11
|
+
MAX_MESSAGE_TYPE = 5
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* Min length = 8 bytes
|
|
@@ -40,6 +41,22 @@ export type PutComponentMessageBody = {
|
|
|
40
41
|
timestamp: number;
|
|
41
42
|
data: Uint8Array;
|
|
42
43
|
};
|
|
44
|
+
/**
|
|
45
|
+
* Min. length = header (8 bytes) + 16 bytes = 24 bytes
|
|
46
|
+
*
|
|
47
|
+
* @param entity - Uint32 number of the entity
|
|
48
|
+
* @param componentId - Uint32 number of id
|
|
49
|
+
* @param timestamp - Uint32 timestamp
|
|
50
|
+
* @param data - Uint8[] data of component => length(4 bytes) + block of bytes[0..length-1]
|
|
51
|
+
* @public
|
|
52
|
+
*/
|
|
53
|
+
export type AppendValueMessageBody = {
|
|
54
|
+
type: CrdtMessageType.APPEND_VALUE;
|
|
55
|
+
entityId: Entity;
|
|
56
|
+
componentId: number;
|
|
57
|
+
timestamp: number;
|
|
58
|
+
data: Uint8Array;
|
|
59
|
+
};
|
|
43
60
|
/**
|
|
44
61
|
* @param entity - Uint32 number of the entity
|
|
45
62
|
* @param componentId - Uint32 number of id
|
|
@@ -60,6 +77,10 @@ export type DeleteEntityMessageBody = {
|
|
|
60
77
|
type: CrdtMessageType.DELETE_ENTITY;
|
|
61
78
|
entityId: Entity;
|
|
62
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* @public
|
|
82
|
+
*/
|
|
83
|
+
export type AppendValueMessage = CrdtMessageHeader & AppendValueMessageBody;
|
|
63
84
|
/**
|
|
64
85
|
* @public
|
|
65
86
|
*/
|
|
@@ -75,11 +96,11 @@ export type DeleteEntityMessage = CrdtMessageHeader & DeleteEntityMessageBody;
|
|
|
75
96
|
/**
|
|
76
97
|
* @public
|
|
77
98
|
*/
|
|
78
|
-
export type CrdtMessage = PutComponentMessage | DeleteComponentMessage | DeleteEntityMessage;
|
|
99
|
+
export type CrdtMessage = PutComponentMessage | DeleteComponentMessage | DeleteEntityMessage | AppendValueMessage;
|
|
79
100
|
/**
|
|
80
101
|
* @public
|
|
81
102
|
*/
|
|
82
|
-
export type CrdtMessageBody = PutComponentMessageBody | DeleteComponentMessageBody | DeleteEntityMessageBody;
|
|
103
|
+
export type CrdtMessageBody = PutComponentMessageBody | DeleteComponentMessageBody | DeleteEntityMessageBody | AppendValueMessageBody;
|
|
83
104
|
export declare enum ProcessMessageResultType {
|
|
84
105
|
/**
|
|
85
106
|
* Typical message and new state set.
|
|
@@ -8,7 +8,8 @@ export var CrdtMessageType;
|
|
|
8
8
|
CrdtMessageType[CrdtMessageType["PUT_COMPONENT"] = 1] = "PUT_COMPONENT";
|
|
9
9
|
CrdtMessageType[CrdtMessageType["DELETE_COMPONENT"] = 2] = "DELETE_COMPONENT";
|
|
10
10
|
CrdtMessageType[CrdtMessageType["DELETE_ENTITY"] = 3] = "DELETE_ENTITY";
|
|
11
|
-
CrdtMessageType[CrdtMessageType["
|
|
11
|
+
CrdtMessageType[CrdtMessageType["APPEND_VALUE"] = 4] = "APPEND_VALUE";
|
|
12
|
+
CrdtMessageType[CrdtMessageType["MAX_MESSAGE_TYPE"] = 5] = "MAX_MESSAGE_TYPE";
|
|
12
13
|
})(CrdtMessageType || (CrdtMessageType = {}));
|
|
13
14
|
/**
|
|
14
15
|
* @public
|
|
@@ -4,4 +4,4 @@ import { CrdtMessageType } from '../../serialization/crdt/types';
|
|
|
4
4
|
/**
|
|
5
5
|
* @public
|
|
6
6
|
*/
|
|
7
|
-
export type OnChangeFunction = (entity: Entity, operation: CrdtMessageType, component?: ComponentDefinition<any
|
|
7
|
+
export type OnChangeFunction = (entity: Entity, operation: CrdtMessageType, component?: ComponentDefinition<any>, componentValue?: any) => void;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EntityState } from '../../engine/entity';
|
|
2
2
|
import { ReadWriteByteBuffer } from '../../serialization/ByteBuffer';
|
|
3
|
-
import { CrdtMessageProtocol } from '../../serialization/crdt';
|
|
3
|
+
import { AppendValueOperation, CrdtMessageProtocol } from '../../serialization/crdt';
|
|
4
4
|
import { DeleteComponent } from '../../serialization/crdt/deleteComponent';
|
|
5
5
|
import { DeleteEntity } from '../../serialization/crdt/deleteEntity';
|
|
6
6
|
import { PutComponentOperation } from '../../serialization/crdt/putComponent';
|
|
@@ -35,7 +35,6 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
35
35
|
if (header.type === CrdtMessageType.DELETE_COMPONENT) {
|
|
36
36
|
const message = DeleteComponent.read(buffer);
|
|
37
37
|
receivedMessages.push({
|
|
38
|
-
...header,
|
|
39
38
|
...message,
|
|
40
39
|
transportId,
|
|
41
40
|
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
@@ -44,7 +43,6 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
44
43
|
else if (header.type === CrdtMessageType.PUT_COMPONENT) {
|
|
45
44
|
const message = PutComponentOperation.read(buffer);
|
|
46
45
|
receivedMessages.push({
|
|
47
|
-
...header,
|
|
48
46
|
...message,
|
|
49
47
|
transportId,
|
|
50
48
|
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
@@ -53,7 +51,14 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
53
51
|
else if (header.type === CrdtMessageType.DELETE_ENTITY) {
|
|
54
52
|
const message = DeleteEntity.read(buffer);
|
|
55
53
|
receivedMessages.push({
|
|
56
|
-
...
|
|
54
|
+
...message,
|
|
55
|
+
transportId,
|
|
56
|
+
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
else if (header.type === CrdtMessageType.APPEND_VALUE) {
|
|
60
|
+
const message = AppendValueOperation.read(buffer);
|
|
61
|
+
receivedMessages.push({
|
|
57
62
|
...message,
|
|
58
63
|
transportId,
|
|
59
64
|
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
@@ -99,13 +104,13 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
99
104
|
}
|
|
100
105
|
const component = engine.getComponentOrNull(msg.componentId);
|
|
101
106
|
if (component) {
|
|
102
|
-
const [conflictMessage] = component.updateFromCrdt(msg);
|
|
107
|
+
const [conflictMessage, value] = component.updateFromCrdt(msg);
|
|
103
108
|
if (conflictMessage) {
|
|
104
109
|
const offset = bufferForOutdated.currentWriteOffset();
|
|
105
110
|
if (conflictMessage.type === CrdtMessageType.PUT_COMPONENT) {
|
|
106
111
|
PutComponentOperation.write(msg.entityId, conflictMessage.timestamp, conflictMessage.componentId, conflictMessage.data, bufferForOutdated);
|
|
107
112
|
}
|
|
108
|
-
else {
|
|
113
|
+
else if (conflictMessage.type === CrdtMessageType.DELETE_COMPONENT) {
|
|
109
114
|
DeleteComponent.write(msg.entityId, component.componentId, conflictMessage.timestamp, bufferForOutdated);
|
|
110
115
|
}
|
|
111
116
|
outdatedMessages.push({
|
|
@@ -116,7 +121,7 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
116
121
|
else {
|
|
117
122
|
// Add message to transport queue to be processed by others transports
|
|
118
123
|
broadcastMessages.push(msg);
|
|
119
|
-
onProcessEntityComponentChange && onProcessEntityComponentChange(msg.entityId, msg.type, component);
|
|
124
|
+
onProcessEntityComponentChange && onProcessEntityComponentChange(msg.entityId, msg.type, component, value);
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
}
|
|
@@ -155,11 +160,19 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
155
160
|
else if (message.type === CrdtMessageType.DELETE_COMPONENT) {
|
|
156
161
|
DeleteComponent.write(message.entityId, component.componentId, message.timestamp, buffer);
|
|
157
162
|
}
|
|
163
|
+
else if (message.type === CrdtMessageType.APPEND_VALUE) {
|
|
164
|
+
AppendValueOperation.write(message.entityId, message.timestamp, message.componentId, message.data, buffer);
|
|
165
|
+
}
|
|
158
166
|
crdtMessages.push({
|
|
159
167
|
...message,
|
|
160
168
|
messageBuffer: buffer.buffer().subarray(offset, buffer.currentWriteOffset())
|
|
161
169
|
});
|
|
162
|
-
|
|
170
|
+
if (onProcessEntityComponentChange) {
|
|
171
|
+
const rawValue = message.type === CrdtMessageType.PUT_COMPONENT || message.type === CrdtMessageType.APPEND_VALUE
|
|
172
|
+
? component.get(message.entityId)
|
|
173
|
+
: undefined;
|
|
174
|
+
onProcessEntityComponentChange(message.entityId, message.type, component, rawValue);
|
|
175
|
+
}
|
|
163
176
|
}
|
|
164
177
|
}
|
|
165
178
|
}
|
package/dist/systems/events.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { InputAction } from '../components/generated/pb/decentraland/sdk/components/common/input_action.gen';
|
|
2
|
-
import {
|
|
2
|
+
import { PBPointerEventsResult } from '../components/generated/pb/decentraland/sdk/components/pointer_events_result.gen';
|
|
3
3
|
import { Entity } from '../engine/entity';
|
|
4
4
|
/**
|
|
5
5
|
* @public
|
|
6
6
|
*/
|
|
7
|
-
export type EventSystemCallback = (event:
|
|
7
|
+
export type EventSystemCallback = (event: PBPointerEventsResult) => void;
|
|
8
8
|
/**
|
|
9
9
|
* @public
|
|
10
10
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dcl/ecs",
|
|
3
|
-
"version": "7.0.6-
|
|
3
|
+
"version": "7.0.6-4180146485.commit-9a7dde9",
|
|
4
4
|
"description": "Decentraland ECS",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"typings": "./dist/index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"dependencies": {
|
|
30
30
|
"@dcl/js-runtime": "file:../js-runtime",
|
|
31
|
-
"@dcl/protocol": "1.0.0-
|
|
31
|
+
"@dcl/protocol": "1.0.0-4177500465.commit-247c87f"
|
|
32
32
|
},
|
|
33
33
|
"files": [
|
|
34
34
|
"dist",
|
|
@@ -40,5 +40,5 @@
|
|
|
40
40
|
"displayName": "ECS",
|
|
41
41
|
"tsconfig": "./tsconfig.json"
|
|
42
42
|
},
|
|
43
|
-
"commit": "
|
|
43
|
+
"commit": "9a7dde903f381c1778ec3c7519e4f312ab9b2ee3"
|
|
44
44
|
}
|