@dcl/ecs 7.3.31 → 7.3.32

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 (59) hide show
  1. package/dist/components/index.d.ts +10 -0
  2. package/dist/components/index.js +12 -0
  3. package/dist/components/manual/NetworkEntity.d.ts +12 -0
  4. package/dist/components/manual/NetworkEntity.js +9 -0
  5. package/dist/components/manual/NetworkParent.d.ts +12 -0
  6. package/dist/components/manual/NetworkParent.js +9 -0
  7. package/dist/components/types.d.ts +2 -0
  8. package/dist/engine/index.js +8 -21
  9. package/dist/engine/lww-element-set-component-definition.js +5 -2
  10. package/dist/engine/types.d.ts +0 -12
  11. package/dist/index.d.ts +11 -1
  12. package/dist/index.js +10 -0
  13. package/dist/runtime/helpers/tree.d.ts +1 -1
  14. package/dist/runtime/helpers/tree.js +25 -2
  15. package/dist/serialization/crdt/index.d.ts +3 -0
  16. package/dist/serialization/crdt/index.js +3 -0
  17. package/dist/serialization/crdt/message.js +12 -0
  18. package/dist/serialization/crdt/network/deleteComponentNetwork.d.ts +14 -0
  19. package/dist/serialization/crdt/network/deleteComponentNetwork.js +43 -0
  20. package/dist/serialization/crdt/network/deleteEntityNetwork.d.ts +11 -0
  21. package/dist/serialization/crdt/network/deleteEntityNetwork.js +33 -0
  22. package/dist/serialization/crdt/network/putComponentNetwork.d.ts +15 -0
  23. package/dist/serialization/crdt/network/putComponentNetwork.js +49 -0
  24. package/dist/serialization/crdt/network/utils.d.ts +9 -0
  25. package/dist/serialization/crdt/network/utils.js +62 -0
  26. package/dist/serialization/crdt/types.d.ts +48 -3
  27. package/dist/serialization/crdt/types.js +5 -1
  28. package/dist/systems/crdt/index.js +111 -69
  29. package/dist/systems/crdt/types.d.ts +9 -1
  30. package/dist-cjs/components/index.d.ts +10 -0
  31. package/dist-cjs/components/index.js +15 -1
  32. package/dist-cjs/components/manual/NetworkEntity.d.ts +12 -0
  33. package/dist-cjs/components/manual/NetworkEntity.js +11 -0
  34. package/dist-cjs/components/manual/NetworkParent.d.ts +12 -0
  35. package/dist-cjs/components/manual/NetworkParent.js +11 -0
  36. package/dist-cjs/components/types.d.ts +2 -0
  37. package/dist-cjs/engine/index.js +8 -21
  38. package/dist-cjs/engine/lww-element-set-component-definition.js +5 -2
  39. package/dist-cjs/engine/types.d.ts +0 -12
  40. package/dist-cjs/index.d.ts +11 -1
  41. package/dist-cjs/index.js +11 -1
  42. package/dist-cjs/runtime/helpers/tree.d.ts +1 -1
  43. package/dist-cjs/runtime/helpers/tree.js +25 -2
  44. package/dist-cjs/serialization/crdt/index.d.ts +3 -0
  45. package/dist-cjs/serialization/crdt/index.js +3 -0
  46. package/dist-cjs/serialization/crdt/message.js +12 -0
  47. package/dist-cjs/serialization/crdt/network/deleteComponentNetwork.d.ts +14 -0
  48. package/dist-cjs/serialization/crdt/network/deleteComponentNetwork.js +46 -0
  49. package/dist-cjs/serialization/crdt/network/deleteEntityNetwork.d.ts +11 -0
  50. package/dist-cjs/serialization/crdt/network/deleteEntityNetwork.js +36 -0
  51. package/dist-cjs/serialization/crdt/network/putComponentNetwork.d.ts +15 -0
  52. package/dist-cjs/serialization/crdt/network/putComponentNetwork.js +52 -0
  53. package/dist-cjs/serialization/crdt/network/utils.d.ts +9 -0
  54. package/dist-cjs/serialization/crdt/network/utils.js +69 -0
  55. package/dist-cjs/serialization/crdt/types.d.ts +48 -3
  56. package/dist-cjs/serialization/crdt/types.js +5 -1
  57. package/dist-cjs/systems/crdt/index.js +133 -68
  58. package/dist-cjs/systems/crdt/types.d.ts +9 -1
  59. package/package.json +2 -2
@@ -1,4 +1,27 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  exports.crdtSceneSystem = void 0;
4
27
  const entity_1 = require("../../engine/entity");
@@ -8,17 +31,22 @@ const deleteComponent_1 = require("../../serialization/crdt/deleteComponent");
8
31
  const deleteEntity_1 = require("../../serialization/crdt/deleteEntity");
9
32
  const putComponent_1 = require("../../serialization/crdt/putComponent");
10
33
  const types_1 = require("../../serialization/crdt/types");
34
+ const putComponentNetwork_1 = require("../../serialization/crdt/network/putComponentNetwork");
35
+ const components_1 = require("../../components");
36
+ const networkUtils = __importStar(require("../../serialization/crdt/network/utils"));
11
37
  /**
12
38
  * @internal
13
39
  */
14
40
  function crdtSceneSystem(engine, onProcessEntityComponentChange) {
15
41
  const transports = [];
42
+ // Components that we used on this system
43
+ const NetworkEntity = (0, components_1.NetworkEntity)(engine);
44
+ const NetworkParent = (0, components_1.NetworkParent)(engine);
45
+ const Transform = (0, components_1.Transform)(engine);
16
46
  // Messages that we received at transport.onMessage waiting to be processed
17
47
  const receivedMessages = [];
18
48
  // Messages already processed by the engine but that we need to broadcast to other transports.
19
49
  const broadcastMessages = [];
20
- // Messages receieved by a transport that were outdated. We need to correct them
21
- const outdatedMessages = [];
22
50
  /**
23
51
  *
24
52
  * @param transportId tranport id to identiy messages
@@ -35,45 +63,41 @@ function crdtSceneSystem(engine, onProcessEntityComponentChange) {
35
63
  let header;
36
64
  while ((header = crdt_1.CrdtMessageProtocol.getHeader(buffer))) {
37
65
  const offset = buffer.currentReadOffset();
66
+ let message = undefined;
38
67
  if (header.type === types_1.CrdtMessageType.DELETE_COMPONENT) {
39
- const message = deleteComponent_1.DeleteComponent.read(buffer);
40
- receivedMessages.push({
41
- ...message,
42
- transportId,
43
- messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
44
- });
68
+ message = deleteComponent_1.DeleteComponent.read(buffer);
69
+ }
70
+ else if (header.type === types_1.CrdtMessageType.DELETE_COMPONENT_NETWORK) {
71
+ message = crdt_1.DeleteComponentNetwork.read(buffer);
45
72
  }
46
73
  else if (header.type === types_1.CrdtMessageType.PUT_COMPONENT) {
47
- const message = putComponent_1.PutComponentOperation.read(buffer);
48
- receivedMessages.push({
49
- ...message,
50
- transportId,
51
- messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
52
- });
74
+ message = putComponent_1.PutComponentOperation.read(buffer);
75
+ }
76
+ else if (header.type === types_1.CrdtMessageType.PUT_COMPONENT_NETWORK) {
77
+ message = putComponentNetwork_1.PutNetworkComponentOperation.read(buffer);
53
78
  }
54
79
  else if (header.type === types_1.CrdtMessageType.DELETE_ENTITY) {
55
- const message = deleteEntity_1.DeleteEntity.read(buffer);
56
- receivedMessages.push({
57
- ...message,
58
- transportId,
59
- messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
60
- });
80
+ message = deleteEntity_1.DeleteEntity.read(buffer);
81
+ }
82
+ else if (header.type === types_1.CrdtMessageType.DELETE_ENTITY_NETWORK) {
83
+ message = crdt_1.DeleteEntityNetwork.read(buffer);
61
84
  }
62
85
  else if (header.type === types_1.CrdtMessageType.APPEND_VALUE) {
63
- const message = crdt_1.AppendValueOperation.read(buffer);
64
- receivedMessages.push({
65
- ...message,
66
- transportId,
67
- messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
68
- });
86
+ message = crdt_1.AppendValueOperation.read(buffer);
69
87
  // Unknown message, we skip it
70
88
  }
71
89
  else {
72
90
  // consume the message
73
91
  buffer.incrementReadOffset(header.length);
74
92
  }
93
+ if (message) {
94
+ receivedMessages.push({
95
+ ...message,
96
+ transportId,
97
+ messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
98
+ });
99
+ }
75
100
  }
76
- // TODO: do something if buffler.len>0
77
101
  };
78
102
  }
79
103
  /**
@@ -84,46 +108,61 @@ function crdtSceneSystem(engine, onProcessEntityComponentChange) {
84
108
  const messagesToProcess = value.splice(0, value.length);
85
109
  return messagesToProcess;
86
110
  }
111
+ /**
112
+ * Find the local entityId associated to the network component message.
113
+ * It's a mapping Network -> to Local
114
+ * If it's not a network message, return the entityId received by the message
115
+ */
116
+ function findNetworkId(msg) {
117
+ const hasNetworkId = 'networkId' in msg;
118
+ if (hasNetworkId) {
119
+ for (const [entityId, network] of engine.getEntitiesWith(NetworkEntity)) {
120
+ if (network.networkId === msg.networkId && network.entityId === msg.entityId) {
121
+ return { entityId, network };
122
+ }
123
+ }
124
+ }
125
+ return { entityId: msg.entityId };
126
+ }
87
127
  /**
88
128
  * This fn will be called on every tick.
89
129
  * Process all the messages queue received by the transport
90
130
  */
91
131
  async function receiveMessages() {
92
132
  const messagesToProcess = getMessages(receivedMessages);
93
- const bufferForOutdated = new ByteBuffer_1.ReadWriteByteBuffer();
94
133
  const entitiesShouldBeCleaned = [];
95
134
  for (const msg of messagesToProcess) {
96
- if (msg.type === types_1.CrdtMessageType.DELETE_ENTITY) {
97
- entitiesShouldBeCleaned.push(msg.entityId);
135
+ let { entityId, network } = findNetworkId(msg);
136
+ // We receive a new Entity. Create the localEntity and map it to the NetworkEntity component
137
+ if (networkUtils.isNetworkMessage(msg) && !network) {
138
+ entityId = engine.addEntity();
139
+ network = { entityId: msg.entityId, networkId: msg.networkId };
140
+ NetworkEntity.createOrReplace(entityId, network);
141
+ }
142
+ if (msg.type === types_1.CrdtMessageType.DELETE_ENTITY || msg.type === types_1.CrdtMessageType.DELETE_ENTITY_NETWORK) {
143
+ entitiesShouldBeCleaned.push(entityId);
98
144
  broadcastMessages.push(msg);
99
145
  }
100
146
  else {
101
- const entityState = engine.entityContainer.getEntityState(msg.entityId);
147
+ const entityState = engine.entityContainer.getEntityState(entityId);
102
148
  // Skip updates from removed entityes
103
149
  if (entityState === entity_1.EntityState.Removed)
104
150
  continue;
105
151
  // Entities with unknown entities should update its entity state
106
152
  if (entityState === entity_1.EntityState.Unknown) {
107
- engine.entityContainer.updateUsedEntity(msg.entityId);
153
+ engine.entityContainer.updateUsedEntity(entityId);
108
154
  }
109
155
  const component = engine.getComponentOrNull(msg.componentId);
110
156
  /* istanbul ignore else */
111
157
  if (component) {
112
- const [conflictMessage, value] = component.updateFromCrdt(msg);
113
- if (conflictMessage) {
114
- const offset = bufferForOutdated.currentWriteOffset();
115
- if (conflictMessage.type === types_1.CrdtMessageType.PUT_COMPONENT) {
116
- putComponent_1.PutComponentOperation.write(msg.entityId, conflictMessage.timestamp, conflictMessage.componentId, conflictMessage.data, bufferForOutdated);
117
- }
118
- else if (conflictMessage.type === types_1.CrdtMessageType.DELETE_COMPONENT) {
119
- deleteComponent_1.DeleteComponent.write(msg.entityId, component.componentId, conflictMessage.timestamp, bufferForOutdated);
120
- }
121
- outdatedMessages.push({
122
- ...msg,
123
- messageBuffer: bufferForOutdated.buffer().subarray(offset, bufferForOutdated.currentWriteOffset())
124
- });
158
+ if (msg.type === types_1.CrdtMessageType.PUT_COMPONENT &&
159
+ component.componentId === Transform.componentId &&
160
+ NetworkEntity.has(entityId) &&
161
+ NetworkParent.has(entityId)) {
162
+ msg.data = networkUtils.fixTransformParent(msg);
125
163
  }
126
- else {
164
+ const [conflictMessage, value] = component.updateFromCrdt({ ...msg, entityId });
165
+ if (!conflictMessage) {
127
166
  // Add message to transport queue to be processed by others transports
128
167
  broadcastMessages.push(msg);
129
168
  onProcessEntityComponentChange && onProcessEntityComponentChange(msg.entityId, msg.type, component, value);
@@ -137,12 +176,6 @@ function crdtSceneSystem(engine, onProcessEntityComponentChange) {
137
176
  }
138
177
  // the last stage of the syncrhonization is to delete the entities
139
178
  for (const entity of entitiesShouldBeCleaned) {
140
- // If we tried to resend outdated message and the entity was deleted before, we avoid sending them.
141
- for (let i = outdatedMessages.length - 1; i >= 0; i--) {
142
- if (outdatedMessages[i].entityId === entity && outdatedMessages[i].type !== types_1.CrdtMessageType.DELETE_ENTITY) {
143
- outdatedMessages.splice(i, 1);
144
- }
145
- }
146
179
  for (const definition of engine.componentsIter()) {
147
180
  // TODO: check this with pato/pravus
148
181
  definition.entityDeleted(entity, true);
@@ -157,7 +190,6 @@ function crdtSceneSystem(engine, onProcessEntityComponentChange) {
157
190
  async function sendMessages(entitiesDeletedThisTick) {
158
191
  // CRDT Messages will be the merge between the recieved transport messages and the new crdt messages
159
192
  const crdtMessages = getMessages(broadcastMessages);
160
- const outdatedMessagesBkp = getMessages(outdatedMessages);
161
193
  const buffer = new ByteBuffer_1.ReadWriteByteBuffer();
162
194
  for (const component of engine.componentsIter()) {
163
195
  for (const message of component.getCrdtUpdates()) {
@@ -202,25 +234,58 @@ function crdtSceneSystem(engine, onProcessEntityComponentChange) {
202
234
  for (const index in transports) {
203
235
  const transportIndex = Number(index);
204
236
  const transport = transports[transportIndex];
237
+ const isRendererTransport = transport.type === 'renderer';
238
+ const isNetworkTransport = transport.type === 'network';
205
239
  transportBuffer.resetBuffer();
206
- // First we need to send all the messages that were outdated from a transport
207
- // So we can fix their crdt state
208
- for (const message of outdatedMessagesBkp) {
209
- if (message.transportId === transportIndex &&
210
- // TODO: This is an optimization, the state should converge anyway, whatever the message is sent.
211
- // Avoid sending multiple messages for the same entity-componentId
212
- !crdtMessages.find((m) => m.entityId === message.entityId &&
213
- // TODO: as any, with multiple type of messages, it should have many checks before the check for similar messages
214
- m.componentId &&
215
- m.componentId === message.componentId)) {
216
- transportBuffer.writeBuffer(message.messageBuffer, false);
217
- }
218
- }
240
+ const buffer = new ByteBuffer_1.ReadWriteByteBuffer();
219
241
  // Then we send all the new crdtMessages that the transport needs to process
220
242
  for (const message of crdtMessages) {
221
- if (message.transportId !== transportIndex && transport.filter(message)) {
222
- transportBuffer.writeBuffer(message.messageBuffer, false);
243
+ // Avoid echo messages
244
+ if (message.transportId === transportIndex)
245
+ continue;
246
+ // Redundant message for the transport
247
+ if (!transport.filter(message))
248
+ continue;
249
+ const { entityId } = findNetworkId(message);
250
+ const transformNeedsFix = 'componentId' in message &&
251
+ message.componentId === Transform.componentId &&
252
+ Transform.has(entityId) &&
253
+ NetworkParent.has(entityId) &&
254
+ NetworkEntity.has(entityId);
255
+ // If there was a LOCAL change in the transform. Add the parent to that transform
256
+ if (isRendererTransport && message.type === types_1.CrdtMessageType.PUT_COMPONENT && transformNeedsFix) {
257
+ const parent = findNetworkId(NetworkParent.get(entityId));
258
+ const transformData = networkUtils.fixTransformParent(message, Transform.get(entityId), parent.entityId);
259
+ const offset = buffer.currentWriteOffset();
260
+ putComponent_1.PutComponentOperation.write(entityId, message.timestamp, message.componentId, transformData, buffer);
261
+ transportBuffer.writeBuffer(buffer.buffer().subarray(offset, buffer.currentWriteOffset()), false);
262
+ continue;
263
+ }
264
+ if (isRendererTransport && networkUtils.isNetworkMessage(message)) {
265
+ // If it's the renderer transport and its a NetworkMessage, we need to fix the entityId field and convert it to a known Message.
266
+ // PUT_NETWORK_COMPONENT -> PUT_COMPONENT
267
+ let transformData = 'data' in message ? message.data : new Uint8Array();
268
+ if (transformNeedsFix) {
269
+ const parent = findNetworkId(NetworkParent.get(entityId));
270
+ transformData = networkUtils.fixTransformParent(message, Transform.get(entityId), parent.entityId);
271
+ }
272
+ networkUtils.networkMessageToLocal({ ...message, data: transformData }, entityId, buffer, transportBuffer);
273
+ // Iterate the next message
274
+ continue;
275
+ }
276
+ // If its a network transport and its a PUT_COMPONENT that has a NetworkEntity component, we need to send this message
277
+ // through comms with the EntityID and NetworkID from ther NetworkEntity so everyone can recieve this message and map to their custom entityID.
278
+ if (isNetworkTransport && !networkUtils.isNetworkMessage(message)) {
279
+ const networkData = NetworkEntity.getOrNull(message.entityId);
280
+ // If it has networkData convert the message to PUT_NETWORK_COMPONENT.
281
+ if (networkData) {
282
+ networkUtils.localMessageToNetwork(message, networkData, buffer, transportBuffer);
283
+ // Iterate the next message
284
+ continue;
285
+ }
223
286
  }
287
+ // Common message
288
+ transportBuffer.writeBuffer(message.messageBuffer, false);
224
289
  }
225
290
  const message = transportBuffer.currentWriteOffset() ? transportBuffer.toBinary() : new Uint8Array([]);
226
291
  await transport.send(message);
@@ -1,4 +1,4 @@
1
- import { CrdtMessageBody } from '../../serialization/crdt/types';
1
+ import { CrdtMessageBody, CrdtNetworkMessageBody } from '../../serialization/crdt/types';
2
2
  /**
3
3
  * @public
4
4
  */
@@ -6,6 +6,13 @@ export type ReceiveMessage = CrdtMessageBody & {
6
6
  transportId?: number;
7
7
  messageBuffer: Uint8Array;
8
8
  };
9
+ /**
10
+ * @public
11
+ */
12
+ export type ReceiveNetworkMessage = CrdtNetworkMessageBody & {
13
+ transportId?: number;
14
+ messageBuffer: Uint8Array;
15
+ };
9
16
  /**
10
17
  * @public
11
18
  */
@@ -17,4 +24,5 @@ export type Transport = {
17
24
  send(message: Uint8Array): Promise<void>;
18
25
  onmessage?(message: Uint8Array): void;
19
26
  filter(message: Omit<TransportMessage, 'messageBuffer'>): boolean;
27
+ type?: string;
20
28
  };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dcl/ecs",
3
3
  "description": "Decentraland ECS",
4
- "version": "7.3.31",
4
+ "version": "7.3.32",
5
5
  "author": "DCL",
6
6
  "bugs": "https://github.com/decentraland/ecs/issues",
7
7
  "files": [
@@ -33,5 +33,5 @@
33
33
  },
34
34
  "types": "./dist/index.d.ts",
35
35
  "typings": "./dist/index.d.ts",
36
- "commit": "5f044c65cc86498ef4a73f87e0082c070b9730f5"
36
+ "commit": "11a21816f9bf404c90ba02782ad7e359262a909d"
37
37
  }