@dcl/playground-assets 7.0.6-3823801200.commit-32470bd → 7.0.6-3830539086.commit-6152fbd

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/index.js CHANGED
@@ -13130,6 +13130,80 @@
13130
13130
 
13131
13131
  var dist = {};
13132
13132
 
13133
+ var gset = {};
13134
+
13135
+ var hasRequiredGset;
13136
+
13137
+ function requireGset () {
13138
+ if (hasRequiredGset) return gset;
13139
+ hasRequiredGset = 1;
13140
+ Object.defineProperty(gset, "__esModule", { value: true });
13141
+ gset.createGSet = void 0;
13142
+ /**
13143
+ *
13144
+ * @returns a new GSet
13145
+ */
13146
+ function createGSet() {
13147
+ const lastVersion = new Map();
13148
+ return {
13149
+ /**
13150
+ *
13151
+ * @param n
13152
+ * @param v
13153
+ * @returns
13154
+ */
13155
+ addTo(n, v) {
13156
+ if (v < 0) {
13157
+ return false;
13158
+ }
13159
+ const currentValue = lastVersion.get(n);
13160
+ // If the version is >=, it means the value it's already in the set
13161
+ if (currentValue !== undefined && currentValue >= v) {
13162
+ return true;
13163
+ }
13164
+ lastVersion.set(n, v);
13165
+ return true;
13166
+ },
13167
+ /**
13168
+ * @returns the set with [number, version] of each value
13169
+ */
13170
+ get() {
13171
+ const arr = [];
13172
+ for (const [n, v] of lastVersion) {
13173
+ for (let i = 0; i <= v; i++) {
13174
+ arr.push([n, i]);
13175
+ }
13176
+ }
13177
+ return arr;
13178
+ },
13179
+ /**
13180
+ * @returns the set with [number, version] of each value
13181
+ */
13182
+ has(n, v) {
13183
+ const currentValue = lastVersion.get(n);
13184
+ // If the version is >=, it means the value it's already in the set
13185
+ if (currentValue !== undefined && currentValue >= v) {
13186
+ return true;
13187
+ }
13188
+ return false;
13189
+ },
13190
+ /**
13191
+ * Warning: this function returns the reference to the internal map,
13192
+ * if you need to mutate some value, make a copy.
13193
+ * For optimization purpose the copy isn't made here.
13194
+ *
13195
+ * @returns the map of number to version
13196
+ */
13197
+ getMap() {
13198
+ return lastVersion;
13199
+ }
13200
+ };
13201
+ }
13202
+ gset.createGSet = createGSet;
13203
+
13204
+ return gset;
13205
+ }
13206
+
13133
13207
  var types = {};
13134
13208
 
13135
13209
  var hasRequiredTypes;
@@ -13137,8 +13211,71 @@
13137
13211
  function requireTypes () {
13138
13212
  if (hasRequiredTypes) return types;
13139
13213
  hasRequiredTypes = 1;
13140
- Object.defineProperty(types, "__esModule", { value: true });
13141
-
13214
+ (function (exports) {
13215
+ Object.defineProperty(exports, "__esModule", { value: true });
13216
+ exports.ProcessMessageResultType = exports.CRDTMessageType = void 0;
13217
+ (function (CRDTMessageType) {
13218
+ CRDTMessageType[CRDTMessageType["CRDTMT_PutComponentData"] = 1] = "CRDTMT_PutComponentData";
13219
+ CRDTMessageType[CRDTMessageType["CRDTMT_DeleteEntity"] = 2] = "CRDTMT_DeleteEntity";
13220
+ })(exports.CRDTMessageType || (exports.CRDTMessageType = {}));
13221
+ (function (ProcessMessageResultType) {
13222
+ /**
13223
+ * Typical message and new state set.
13224
+ * @state CHANGE
13225
+ * @reason Incoming message has a timestamp greater
13226
+ */
13227
+ ProcessMessageResultType[ProcessMessageResultType["StateUpdatedTimestamp"] = 1] = "StateUpdatedTimestamp";
13228
+ /**
13229
+ * Typical message when it is considered old.
13230
+ * @state it does NOT CHANGE.
13231
+ * @reason incoming message has a timestamp lower.
13232
+ */
13233
+ ProcessMessageResultType[ProcessMessageResultType["StateOutdatedTimestamp"] = 2] = "StateOutdatedTimestamp";
13234
+ /**
13235
+ * Weird message, same timestamp and data.
13236
+ * @state it does NOT CHANGE.
13237
+ * @reason consistent state between peers.
13238
+ */
13239
+ ProcessMessageResultType[ProcessMessageResultType["NoChanges"] = 3] = "NoChanges";
13240
+ /**
13241
+ * Less but typical message, same timestamp, resolution by data.
13242
+ * @state it does NOT CHANGE.
13243
+ * @reason incoming message has a LOWER data.
13244
+ */
13245
+ ProcessMessageResultType[ProcessMessageResultType["StateOutdatedData"] = 4] = "StateOutdatedData";
13246
+ /**
13247
+ * Less but typical message, same timestamp, resolution by data.
13248
+ * @state CHANGE.
13249
+ * @reason incoming message has a GREATER data.
13250
+ */
13251
+ ProcessMessageResultType[ProcessMessageResultType["StateUpdatedData"] = 5] = "StateUpdatedData";
13252
+ /**
13253
+ * Entity was previously deleted.
13254
+ * @state it does NOT CHANGE.
13255
+ * @reason The message is considered old.
13256
+ */
13257
+ ProcessMessageResultType[ProcessMessageResultType["EntityWasDeleted"] = 6] = "EntityWasDeleted";
13258
+ /**
13259
+ * Entity should be deleted.
13260
+ * @state CHANGE.
13261
+ * @reason the state is storing old entities
13262
+ */
13263
+ ProcessMessageResultType[ProcessMessageResultType["EntityDeleted"] = 7] = "EntityDeleted";
13264
+ })(exports.ProcessMessageResultType || (exports.ProcessMessageResultType = {}));
13265
+ // we receive LWW, v=6, we have v=5 => we receive with delay the deleteEntity(v=5)
13266
+ // => we should generate the deleteEntity message effects internally with deleteEntity(v=5),
13267
+ // but don't resend the deleteEntity
13268
+ // - (CRDT) addDeletedEntitySet v=5 (with crdt state cleaning) and then LWW v=6
13269
+ // - (engine) engine.deleteEntity v=5
13270
+ // we receive LWW, v=7, we have v=5 => we receive with delay the deleteEntity(v=5), deleteEntity(v=6), ..., N
13271
+ // => we should generate the deleteEntity message effects internally with deleteEntity(v=5),
13272
+ // but don't resend the deleteEntity
13273
+ // - (CRDT) addDeletedEntitySet v=5 (with crdt state cleaning) and then LWW v=6
13274
+ // - (engine) engine.deleteEntity v=5
13275
+ // msg delete entity: it only should be sent by deleter
13276
+ //
13277
+
13278
+ } (types));
13142
13279
  return types;
13143
13280
  }
13144
13281
 
@@ -13163,46 +13300,57 @@
13163
13300
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
13164
13301
  };
13165
13302
  Object.defineProperty(exports, "__esModule", { value: true });
13166
- exports.crdtProtocol = exports.stateIterator = exports.sameData = void 0;
13167
- __exportStar(requireTypes(), exports);
13303
+ exports.crdtProtocol = exports.stateIterator = exports.dataCompare = void 0;
13304
+ const gset_1 = requireGset();
13305
+ const types_1 = requireTypes();
13168
13306
  const globalBuffer = globalThis.Buffer;
13307
+ __exportStar(requireTypes(), exports);
13169
13308
  /**
13170
13309
  * Compare raw data.
13171
13310
  * @internal
13311
+ * @returns 0 if is the same data, 1 if a > b, -1 if b > a
13172
13312
  */
13173
- function sameData(a, b) {
13313
+ function dataCompare(a, b) {
13174
13314
  // At reference level
13175
13315
  if (a === b)
13176
- return true;
13316
+ return 0;
13317
+ if (a === null && b !== null)
13318
+ return -1;
13319
+ if (a !== null && b === null)
13320
+ return 1;
13177
13321
  if (a instanceof Uint8Array && b instanceof Uint8Array) {
13178
- if (a.byteLength !== b.byteLength) {
13179
- return false;
13180
- }
13181
- for (let i = 0; i < a.byteLength; i++) {
13182
- if (a[i] !== b[i]) {
13183
- return false;
13322
+ let res;
13323
+ const n = a.byteLength > b.byteLength ? b.byteLength : a.byteLength;
13324
+ for (let i = 0; i < n; i++) {
13325
+ res = a[i] - b[i];
13326
+ if (res !== 0) {
13327
+ return res > 0 ? 1 : -1;
13184
13328
  }
13185
13329
  }
13186
- return true;
13330
+ res = a.byteLength - b.byteLength;
13331
+ return res > 0 ? 1 : res < 0 ? -1 : 0;
13187
13332
  }
13188
13333
  if (globalBuffer) {
13189
13334
  /* istanbul ignore next */
13190
13335
  if (a instanceof globalBuffer && b instanceof globalBuffer) {
13191
13336
  /* istanbul ignore next */
13192
- return a.equals(b);
13337
+ return a.compare(b);
13193
13338
  }
13194
13339
  }
13195
- return false;
13340
+ if (typeof a === 'string') {
13341
+ return a.localeCompare(b);
13342
+ }
13343
+ return a > b ? 1 : -1;
13196
13344
  }
13197
- exports.sameData = sameData;
13345
+ exports.dataCompare = dataCompare;
13198
13346
  /**
13199
13347
  * State iterator
13200
13348
  * @internal
13201
13349
  */
13202
13350
  function* stateIterator(state) {
13203
- for (const [key1, value1] of state.entries()) {
13204
- for (const [key2, value2] of value1.entries()) {
13205
- yield [key1, key2, value2];
13351
+ for (const [componentId, value1] of state.components.entries()) {
13352
+ for (const [entityId, value2] of value1.entries()) {
13353
+ yield [componentId, entityId, value2];
13206
13354
  }
13207
13355
  }
13208
13356
  }
@@ -13214,40 +13362,70 @@
13214
13362
  * to process and store the new data in case its an update, or
13215
13363
  * to discard and send our local value cause remote it's outdated.
13216
13364
  */
13217
- function crdtProtocol() {
13365
+ function crdtProtocol(entityUtils) {
13218
13366
  /**
13219
13367
  * Local state where we store the latest lamport timestamp
13220
13368
  * and the raw data value
13221
13369
  * @internal
13222
13370
  */
13223
- const state = new Map();
13371
+ const state = {
13372
+ components: new Map(),
13373
+ deletedEntities: (0, gset_1.createGSet)()
13374
+ };
13224
13375
  /**
13225
13376
  * We should call this fn in order to update the state
13226
13377
  * @internal
13227
13378
  */
13228
- function updateState(key1, key2, data, remoteTimestamp) {
13229
- const key1Value = state.get(key1);
13230
- const timestamp = Math.max(remoteTimestamp, key1Value?.get(key2)?.timestamp || 0);
13231
- if (key1Value) {
13232
- key1Value.set(key2, { timestamp, data });
13379
+ function updateState(componentId, entityId, // todo: force type entity
13380
+ data, remoteTimestamp) {
13381
+ const componentIdValue = state.components.get(componentId);
13382
+ const timestamp = Math.max(remoteTimestamp, componentIdValue?.get(entityId)?.timestamp || 0);
13383
+ if (componentIdValue) {
13384
+ componentIdValue.set(entityId, { timestamp, data });
13233
13385
  }
13234
13386
  else {
13235
- const newKey1Value = new Map();
13236
- newKey1Value.set(key2, { timestamp, data });
13237
- state.set(key1, newKey1Value);
13387
+ const componentIdValue = new Map();
13388
+ componentIdValue.set(entityId, { timestamp, data });
13389
+ state.components.set(componentId, componentIdValue);
13238
13390
  }
13239
13391
  return { timestamp, data };
13240
13392
  }
13241
13393
  /**
13242
13394
  * Create an event for the specified key and store the new data and
13243
- * lamport timestmap incremented by one in the state.
13395
+ * lamport timestmap incremented by one in the state.components.
13396
+ * @public
13397
+ */
13398
+ function createComponentDataEvent(componentId, entityId, data) {
13399
+ // Increment the timestamp
13400
+ const timestamp = (state.components.get(componentId)?.get(entityId)?.timestamp || 0) + 1;
13401
+ const msg = {
13402
+ type: types_1.CRDTMessageType.CRDTMT_PutComponentData,
13403
+ componentId,
13404
+ entityId,
13405
+ data,
13406
+ timestamp
13407
+ };
13408
+ const res = processComponentDataMessage(msg);
13409
+ if (res === types_1.ProcessMessageResultType.StateUpdatedTimestamp) {
13410
+ return msg;
13411
+ }
13412
+ else {
13413
+ return null;
13414
+ }
13415
+ }
13416
+ /**
13417
+ * Create an event for the specified key and store the new data and
13418
+ * lamport timestmap incremented by one in the state.components.
13244
13419
  * @public
13245
13420
  */
13246
- function createEvent(key1, key2, data) {
13421
+ function createDeleteEntityEvent(entityId) {
13247
13422
  // Increment the timestamp
13248
- const timestamp = (state.get(key1)?.get(key2)?.timestamp || 0) + 1;
13249
- updateState(key1, key2, data, timestamp);
13250
- return { key1, key2, data, timestamp };
13423
+ const message = {
13424
+ type: types_1.CRDTMessageType.CRDTMT_DeleteEntity,
13425
+ entityId
13426
+ };
13427
+ processDeleteEntityMessage(message);
13428
+ return message;
13251
13429
  }
13252
13430
  /**
13253
13431
  * Process the received message only if the lamport number recieved is higher
@@ -13258,42 +13436,59 @@
13258
13436
  * If it was an outdated message, then we return void
13259
13437
  * @public
13260
13438
  */
13261
- function processMessage(message) {
13262
- const { key1, key2, data, timestamp } = message;
13263
- const current = state.get(key1)?.get(key2);
13264
- // The received message is > than our current value, update our state.
13439
+ function processComponentDataMessage(message) {
13440
+ const [entityNumber, entityVersion] = entityUtils.fromEntityId(message.entityId);
13441
+ if (state.deletedEntities.has(entityNumber, entityVersion)) {
13442
+ return types_1.ProcessMessageResultType.EntityWasDeleted;
13443
+ }
13444
+ const { componentId, entityId, data, timestamp } = message;
13445
+ const current = state.components.get(componentId)?.get(entityId);
13446
+ // The received message is > than our current value, update our state.components.
13265
13447
  if (!current || current.timestamp < timestamp) {
13266
- updateState(key1, key2, data, timestamp);
13267
- return message;
13448
+ updateState(componentId, entityId, data, timestamp);
13449
+ return types_1.ProcessMessageResultType.StateUpdatedTimestamp;
13268
13450
  }
13269
13451
  // Outdated Message. Resend our state message through the wire.
13270
13452
  if (current.timestamp > timestamp) {
13271
- return {
13272
- key1,
13273
- key2,
13274
- data: current.data,
13275
- timestamp: current.timestamp
13276
- };
13453
+ return types_1.ProcessMessageResultType.StateOutdatedData;
13277
13454
  }
13455
+ const currentDataGreater = dataCompare(current.data, data);
13278
13456
  // Same data, same timestamp. Weirdo echo message.
13279
- if (sameData(current.data, data)) {
13280
- return message;
13457
+ if (currentDataGreater === 0) {
13458
+ return types_1.ProcessMessageResultType.NoChanges;
13459
+ // Current data is greater
13281
13460
  }
13282
- // Race condition, same timestamp diff data.
13283
- function compareData(current, data) {
13284
- // Null value wins
13285
- return !current || current > data;
13461
+ else if (currentDataGreater > 0) {
13462
+ return types_1.ProcessMessageResultType.StateOutdatedData;
13463
+ // Curent data is lower
13286
13464
  }
13287
- if (compareData(current.data, data)) {
13288
- return {
13289
- key1,
13290
- key2,
13291
- data: current.data,
13292
- timestamp: current.timestamp
13293
- };
13465
+ else {
13466
+ updateState(componentId, entityId, data, timestamp);
13467
+ return types_1.ProcessMessageResultType.StateUpdatedData;
13294
13468
  }
13295
- updateState(key1, key2, data, timestamp).data;
13296
- return message;
13469
+ }
13470
+ /*
13471
+ * @public
13472
+ */
13473
+ function processMessage(message) {
13474
+ if (message.type === types_1.CRDTMessageType.CRDTMT_PutComponentData) {
13475
+ return processComponentDataMessage(message);
13476
+ }
13477
+ else if (message.type === types_1.CRDTMessageType.CRDTMT_DeleteEntity) {
13478
+ return processDeleteEntityMessage(message);
13479
+ }
13480
+ else {
13481
+ return types_1.ProcessMessageResultType.NoChanges;
13482
+ }
13483
+ }
13484
+ function processDeleteEntityMessage(message) {
13485
+ const { entityId } = message;
13486
+ const [entityNumber, entityVersion] = entityUtils.fromEntityId(message.entityId);
13487
+ state.deletedEntities.addTo(entityNumber, entityVersion);
13488
+ for (const [, payload] of state.components) {
13489
+ payload.delete(entityId);
13490
+ }
13491
+ return types_1.ProcessMessageResultType.EntityDeleted;
13297
13492
  }
13298
13493
  /**
13299
13494
  * Returns the current state
@@ -13306,12 +13501,13 @@
13306
13501
  * Returns the element state of a given element of the LWW-ElementSet
13307
13502
  * @public
13308
13503
  */
13309
- function getElementSetState(key1, key2) {
13310
- return state.get(key1)?.get(key2) || null;
13504
+ function getElementSetState(componentId, entityId) {
13505
+ return state.components.get(componentId)?.get(entityId) || null;
13311
13506
  }
13312
13507
  return {
13313
13508
  getElementSetState,
13314
- createEvent,
13509
+ createComponentDataEvent,
13510
+ createDeleteEntityEvent,
13315
13511
  processMessage,
13316
13512
  getState
13317
13513
  };
@@ -13324,6 +13520,171 @@
13324
13520
 
13325
13521
  var distExports = requireDist();
13326
13522
 
13523
+ var typesExports = requireTypes();
13524
+
13525
+ var gsetExports = requireGset();
13526
+
13527
+ const MAX_U16 = 0xffff;
13528
+ const MASK_UPPER_16_ON_32 = 0xffff0000;
13529
+ const AMOUNT_VERSION_AVAILABLE = MAX_U16 + 1;
13530
+ // This type matches with @dcl/crdt entity type.
13531
+ /**
13532
+ * @public This first 512 entities are reserved by the renderer
13533
+ */
13534
+ const RESERVED_STATIC_ENTITIES = 512;
13535
+ /**
13536
+ * @public
13537
+ */
13538
+ const MAX_ENTITY_NUMBER = MAX_U16;
13539
+ exports.EntityUtils = void 0;
13540
+ (function (EntityUtils) {
13541
+ /**
13542
+ * @returns [entityNumber, entityVersion]
13543
+ */
13544
+ function fromEntityId(entityId) {
13545
+ return [
13546
+ (entityId & MAX_U16) >>> 0,
13547
+ (((entityId & MASK_UPPER_16_ON_32) >> 16) & MAX_U16) >>> 0
13548
+ ];
13549
+ }
13550
+ EntityUtils.fromEntityId = fromEntityId;
13551
+ /**
13552
+ * @returns compound number from entityNumber and entityVerison
13553
+ */
13554
+ function toEntityId(entityNumber, entityVersion) {
13555
+ return (((entityNumber & MAX_U16) | ((entityVersion & MAX_U16) << 16)) >>>
13556
+ 0);
13557
+ }
13558
+ EntityUtils.toEntityId = toEntityId;
13559
+ })(exports.EntityUtils || (exports.EntityUtils = {}));
13560
+ /**
13561
+ * @public
13562
+ */
13563
+ exports.EntityState = void 0;
13564
+ (function (EntityState) {
13565
+ EntityState[EntityState["Unknown"] = 0] = "Unknown";
13566
+ /**
13567
+ * The entity was generated and added to the usedEntities set
13568
+ */
13569
+ EntityState[EntityState["UsedEntity"] = 1] = "UsedEntity";
13570
+ /**
13571
+ * The entity was removed from current engine or remotely
13572
+ */
13573
+ EntityState[EntityState["Removed"] = 2] = "Removed";
13574
+ /**
13575
+ * The entity is reserved number.
13576
+ */
13577
+ EntityState[EntityState["Reserved"] = 3] = "Reserved";
13578
+ })(exports.EntityState || (exports.EntityState = {}));
13579
+ function EntityContainer() {
13580
+ let entityCounter = RESERVED_STATIC_ENTITIES;
13581
+ const usedEntities = new Set();
13582
+ let toRemoveEntities = [];
13583
+ const removedEntities = gsetExports.createGSet();
13584
+ function generateNewEntity() {
13585
+ if (entityCounter > MAX_ENTITY_NUMBER - 1) {
13586
+ throw new Error(`It fails trying to generate an entity out of range ${MAX_ENTITY_NUMBER}.`);
13587
+ }
13588
+ const entityNumber = entityCounter++;
13589
+ const entityVersion = removedEntities.getMap().has(entityNumber)
13590
+ ? removedEntities.getMap().get(entityNumber) + 1
13591
+ : 0;
13592
+ const entity = exports.EntityUtils.toEntityId(entityNumber, entityVersion);
13593
+ usedEntities.add(entity);
13594
+ return entity;
13595
+ }
13596
+ function generateEntity() {
13597
+ // If all entities until `entityCounter` are being used, we need to generate another one
13598
+ if (usedEntities.size + RESERVED_STATIC_ENTITIES >= entityCounter) {
13599
+ return generateNewEntity();
13600
+ }
13601
+ for (const [number, version] of removedEntities.getMap()) {
13602
+ if (version < MAX_U16) {
13603
+ const entity = exports.EntityUtils.toEntityId(number, version + 1);
13604
+ // If the entity is not being used, we can re-use it
13605
+ // If the entity was removed in this tick, we're not counting for the usedEntities, but we have it in the toRemoveEntityArray
13606
+ if (!usedEntities.has(entity) && !toRemoveEntities.includes(entity)) {
13607
+ usedEntities.add(entity);
13608
+ return entity;
13609
+ }
13610
+ }
13611
+ }
13612
+ return generateNewEntity();
13613
+ }
13614
+ function removeEntity(entity) {
13615
+ if (entity < RESERVED_STATIC_ENTITIES)
13616
+ return false;
13617
+ if (usedEntities.has(entity)) {
13618
+ usedEntities.delete(entity);
13619
+ toRemoveEntities.push(entity);
13620
+ }
13621
+ else {
13622
+ updateRemovedEntity(entity);
13623
+ }
13624
+ return true;
13625
+ }
13626
+ function releaseRemovedEntities() {
13627
+ const arr = toRemoveEntities;
13628
+ toRemoveEntities = [];
13629
+ for (const entity of arr) {
13630
+ const [n, v] = exports.EntityUtils.fromEntityId(entity);
13631
+ removedEntities.addTo(n, v);
13632
+ }
13633
+ return arr;
13634
+ }
13635
+ function updateRemovedEntity(entity) {
13636
+ const [n, v] = exports.EntityUtils.fromEntityId(entity);
13637
+ // Update the removed entities map
13638
+ removedEntities.addTo(n, v);
13639
+ // Remove the usedEntities if exist
13640
+ for (let i = 0; i <= v; i++) {
13641
+ usedEntities.delete(exports.EntityUtils.toEntityId(n, i));
13642
+ }
13643
+ return true;
13644
+ }
13645
+ function updateUsedEntity(entity) {
13646
+ const [n, v] = exports.EntityUtils.fromEntityId(entity);
13647
+ const removedVersion = removedEntities.getMap().get(n);
13648
+ if (removedVersion !== undefined && removedVersion >= v) {
13649
+ return false;
13650
+ }
13651
+ // Update
13652
+ if (v > 0) {
13653
+ for (let i = 0; i <= v - 1; i++) {
13654
+ usedEntities.delete(exports.EntityUtils.toEntityId(n, i));
13655
+ }
13656
+ removedEntities.addTo(n, v - 1);
13657
+ }
13658
+ usedEntities.add(entity);
13659
+ return true;
13660
+ }
13661
+ function getEntityState(entity) {
13662
+ const [n, v] = exports.EntityUtils.fromEntityId(entity);
13663
+ if (n < RESERVED_STATIC_ENTITIES) {
13664
+ return exports.EntityState.Reserved;
13665
+ }
13666
+ if (usedEntities.has(entity)) {
13667
+ return exports.EntityState.UsedEntity;
13668
+ }
13669
+ const removedVersion = removedEntities.getMap().get(n);
13670
+ if (removedVersion !== undefined && removedVersion >= v) {
13671
+ return exports.EntityState.Removed;
13672
+ }
13673
+ return exports.EntityState.Unknown;
13674
+ }
13675
+ return {
13676
+ generateEntity,
13677
+ removeEntity,
13678
+ getExistingEntities() {
13679
+ return new Set(usedEntities);
13680
+ },
13681
+ getEntityState,
13682
+ releaseRemovedEntities,
13683
+ updateRemovedEntity,
13684
+ updateUsedEntity
13685
+ };
13686
+ }
13687
+
13327
13688
  var utf8Exports = requireUtf8();
13328
13689
 
13329
13690
  /**
@@ -13583,114 +13944,204 @@
13583
13944
  };
13584
13945
  }
13585
13946
 
13586
- /**
13587
- * The wire message is the top-level message that can be packed
13588
- * inside it can contain a data with another structure or protocol
13589
- *
13590
- * Each wire message has three primitive property that it'll never change
13591
- * ---> length uint32 (message size up to 4,294,967,295)
13592
- * ---> version uint32 (for now just a number which is zero)
13593
- * ---> message type uint32
13594
- * The length indicates how many bytes are above self, the version in
13595
- * combination with message type defines the set of handlers that will be
13596
- * available to process the message
13597
- *
13598
- */
13599
- var WireMessage;
13600
- (function (WireMessage) {
13601
- let Enum;
13602
- (function (Enum) {
13603
- Enum[Enum["RESERVED"] = 0] = "RESERVED";
13604
- // Component Operation
13605
- Enum[Enum["PUT_COMPONENT"] = 1] = "PUT_COMPONENT";
13606
- Enum[Enum["DELETE_COMPONENT"] = 2] = "DELETE_COMPONENT";
13607
- Enum[Enum["MAX_MESSAGE_TYPE"] = 3] = "MAX_MESSAGE_TYPE";
13608
- })(Enum = WireMessage.Enum || (WireMessage.Enum = {}));
13609
- WireMessage.HEADER_LENGTH = 8;
13610
- /**
13611
- * Validate if the message incoming is completed
13612
- * @param buf - ByteBuffer
13613
- */
13614
- function validate(buf) {
13615
- const rem = buf.remainingBytes();
13616
- if (rem < WireMessage.HEADER_LENGTH) {
13617
- return false;
13618
- }
13619
- const messageLength = buf.getUint32(buf.currentReadOffset());
13620
- if (rem < messageLength) {
13621
- return false;
13622
- }
13623
- return true;
13947
+ exports.CrdtMessageType = void 0;
13948
+ (function (CrdtMessageType) {
13949
+ CrdtMessageType[CrdtMessageType["RESERVED"] = 0] = "RESERVED";
13950
+ // Component Operation
13951
+ CrdtMessageType[CrdtMessageType["PUT_COMPONENT"] = 1] = "PUT_COMPONENT";
13952
+ CrdtMessageType[CrdtMessageType["DELETE_COMPONENT"] = 2] = "DELETE_COMPONENT";
13953
+ CrdtMessageType[CrdtMessageType["DELETE_ENTITY"] = 3] = "DELETE_ENTITY";
13954
+ CrdtMessageType[CrdtMessageType["MAX_MESSAGE_TYPE"] = 4] = "MAX_MESSAGE_TYPE";
13955
+ })(exports.CrdtMessageType || (exports.CrdtMessageType = {}));
13956
+ const CRDT_MESSAGE_HEADER_LENGTH = 8;
13957
+
13958
+ exports.DeleteComponent = void 0;
13959
+ (function (DeleteComponent) {
13960
+ // TODO: change timestamp to 32 bit and remove buffer length (-8 bytes)
13961
+ DeleteComponent.MESSAGE_HEADER_LENGTH = 20;
13962
+ /**
13963
+ * Write DeleteComponent message
13964
+ */
13965
+ function write(entity, componentId, timestamp, buf) {
13966
+ // reserve the beginning
13967
+ const messageLength = CRDT_MESSAGE_HEADER_LENGTH + DeleteComponent.MESSAGE_HEADER_LENGTH;
13968
+ const startMessageOffset = buf.incrementWriteOffset(messageLength);
13969
+ // Write CrdtMessage header
13970
+ buf.setUint32(startMessageOffset, messageLength);
13971
+ buf.setUint32(startMessageOffset + 4, exports.CrdtMessageType.DELETE_COMPONENT);
13972
+ // Write ComponentOperation header
13973
+ buf.setUint32(startMessageOffset + 8, entity);
13974
+ buf.setUint32(startMessageOffset + 12, componentId);
13975
+ // TODO: change timestamp to 32bit (-4 bytes)
13976
+ buf.setUint64(startMessageOffset + 16, BigInt(timestamp));
13977
+ // TODO: remove buffer length (-4 bytes)
13978
+ buf.setUint32(startMessageOffset + 24, 0);
13624
13979
  }
13625
- WireMessage.validate = validate;
13626
- function readHeader(buf) {
13627
- if (!validate(buf)) {
13980
+ DeleteComponent.write = write;
13981
+ function read(buf) {
13982
+ const header = CrdtMessageProtocol.readHeader(buf);
13983
+ if (!header) {
13628
13984
  return null;
13629
13985
  }
13630
- return {
13631
- length: buf.readUint32(),
13632
- type: buf.readUint32()
13986
+ if (header.type !== exports.CrdtMessageType.DELETE_COMPONENT) {
13987
+ throw new Error('DeleteComponentOperation tried to read another message type.');
13988
+ }
13989
+ const msg = {
13990
+ ...header,
13991
+ entityId: buf.readUint32(),
13992
+ componentId: buf.readInt32(),
13993
+ timestamp: Number(buf.readUint64())
13633
13994
  };
13995
+ // TODO: remove buffer length
13996
+ buf.incrementReadOffset(4);
13997
+ return msg;
13634
13998
  }
13635
- WireMessage.readHeader = readHeader;
13636
- function getType(component, entity) {
13637
- return component.has(entity) ? Enum.PUT_COMPONENT : Enum.DELETE_COMPONENT;
13638
- }
13639
- WireMessage.getType = getType;
13640
- })(WireMessage || (WireMessage = {}));
13641
- var WireMessage$1 = WireMessage;
13999
+ DeleteComponent.read = read;
14000
+ })(exports.DeleteComponent || (exports.DeleteComponent = {}));
13642
14001
 
13643
- var ComponentOperation;
13644
- (function (ComponentOperation) {
13645
- ComponentOperation.MESSAGE_HEADER_LENGTH = 20;
14002
+ exports.PutComponentOperation = void 0;
14003
+ (function (PutComponentOperation) {
14004
+ PutComponentOperation.MESSAGE_HEADER_LENGTH = 20;
13646
14005
  /**
13647
14006
  * Call this function for an optimal writing data passing the ByteBuffer
13648
14007
  * already allocated
13649
14008
  */
13650
- function write(type, entity, timestamp, componentDefinition, buf) {
14009
+ function write(entity, timestamp, componentDefinition, buf) {
13651
14010
  // reserve the beginning
13652
- const startMessageOffset = buf.incrementWriteOffset(WireMessage$1.HEADER_LENGTH + ComponentOperation.MESSAGE_HEADER_LENGTH);
14011
+ const startMessageOffset = buf.incrementWriteOffset(CRDT_MESSAGE_HEADER_LENGTH + PutComponentOperation.MESSAGE_HEADER_LENGTH);
13653
14012
  // write body
13654
- if (type === WireMessage$1.Enum.PUT_COMPONENT) {
13655
- componentDefinition.writeToByteBuffer(entity, buf);
13656
- }
14013
+ componentDefinition.writeToByteBuffer(entity, buf);
13657
14014
  const messageLength = buf.size() - startMessageOffset;
13658
- // Write WireMessage header
14015
+ // Write CrdtMessage header
13659
14016
  buf.setUint32(startMessageOffset, messageLength);
13660
- buf.setUint32(startMessageOffset + 4, type);
14017
+ buf.setUint32(startMessageOffset + 4, exports.CrdtMessageType.PUT_COMPONENT);
13661
14018
  // Write ComponentOperation header
13662
14019
  buf.setUint32(startMessageOffset + 8, entity);
13663
14020
  buf.setUint32(startMessageOffset + 12, componentDefinition._id);
13664
14021
  buf.setUint64(startMessageOffset + 16, BigInt(timestamp));
13665
- buf.setUint32(startMessageOffset + 24, messageLength - ComponentOperation.MESSAGE_HEADER_LENGTH - WireMessage$1.HEADER_LENGTH);
14022
+ const newLocal = messageLength - PutComponentOperation.MESSAGE_HEADER_LENGTH - CRDT_MESSAGE_HEADER_LENGTH;
14023
+ buf.setUint32(startMessageOffset + 24, newLocal);
13666
14024
  }
13667
- ComponentOperation.write = write;
14025
+ PutComponentOperation.write = write;
13668
14026
  function read(buf) {
13669
- const header = WireMessage$1.readHeader(buf);
14027
+ const header = CrdtMessageProtocol.readHeader(buf);
13670
14028
  if (!header) {
13671
14029
  return null;
13672
14030
  }
13673
- const common = {
14031
+ if (header.type !== exports.CrdtMessageType.PUT_COMPONENT) {
14032
+ throw new Error('PutComponentOperation tried to read another message type.');
14033
+ }
14034
+ return {
13674
14035
  ...header,
13675
- entity: buf.readUint32(),
14036
+ entityId: buf.readUint32(),
13676
14037
  componentId: buf.readInt32(),
13677
- timestamp: Number(buf.readUint64())
14038
+ timestamp: Number(buf.readUint64()),
14039
+ data: buf.readBuffer()
13678
14040
  };
13679
- if (header.type === WireMessage$1.Enum.DELETE_COMPONENT) {
13680
- return common;
14041
+ }
14042
+ PutComponentOperation.read = read;
14043
+ })(exports.PutComponentOperation || (exports.PutComponentOperation = {}));
14044
+
14045
+ exports.DeleteEntity = void 0;
14046
+ (function (DeleteEntity) {
14047
+ DeleteEntity.MESSAGE_HEADER_LENGTH = 4;
14048
+ function write(entity, buf) {
14049
+ // Write CrdtMessage header
14050
+ buf.writeUint32(CRDT_MESSAGE_HEADER_LENGTH + 4);
14051
+ buf.writeUint32(exports.CrdtMessageType.DELETE_ENTITY);
14052
+ // body
14053
+ buf.writeUint32(entity);
14054
+ }
14055
+ DeleteEntity.write = write;
14056
+ function read(buf) {
14057
+ const header = CrdtMessageProtocol.readHeader(buf);
14058
+ if (!header) {
14059
+ return null;
14060
+ }
14061
+ if (header.type !== exports.CrdtMessageType.DELETE_ENTITY) {
14062
+ throw new Error('DeleteComponentOperation tried to read another message type.');
13681
14063
  }
13682
14064
  return {
13683
- ...common,
13684
- data: buf.readBuffer()
14065
+ ...header,
14066
+ entityId: buf.readUint32()
14067
+ };
14068
+ }
14069
+ DeleteEntity.read = read;
14070
+ })(exports.DeleteEntity || (exports.DeleteEntity = {}));
14071
+
14072
+ exports.CrdtMessageProtocol = void 0;
14073
+ (function (CrdtMessageProtocol) {
14074
+ /**
14075
+ * Validate if the message incoming is completed
14076
+ * @param buf - ByteBuffer
14077
+ */
14078
+ function validate(buf) {
14079
+ const rem = buf.remainingBytes();
14080
+ if (rem < CRDT_MESSAGE_HEADER_LENGTH) {
14081
+ return false;
14082
+ }
14083
+ const messageLength = buf.getUint32(buf.currentReadOffset());
14084
+ if (rem < messageLength) {
14085
+ return false;
14086
+ }
14087
+ return true;
14088
+ }
14089
+ CrdtMessageProtocol.validate = validate;
14090
+ /**
14091
+ * Get the current header, consuming the bytes involved.
14092
+ * @param buf - ByteBuffer
14093
+ * @returns header or null if there is no validated message
14094
+ */
14095
+ function readHeader(buf) {
14096
+ if (!validate(buf)) {
14097
+ return null;
14098
+ }
14099
+ return {
14100
+ length: buf.readUint32(),
14101
+ type: buf.readUint32()
13685
14102
  };
13686
14103
  }
13687
- ComponentOperation.read = read;
13688
- })(ComponentOperation || (ComponentOperation = {}));
14104
+ CrdtMessageProtocol.readHeader = readHeader;
14105
+ /**
14106
+ * Get the current header, without consuming the bytes involved.
14107
+ * @param buf - ByteBuffer
14108
+ * @returns header or null if there is no validated message
14109
+ */
14110
+ function getHeader(buf) {
14111
+ if (!validate(buf)) {
14112
+ return null;
14113
+ }
14114
+ const currentOffset = buf.currentReadOffset();
14115
+ return {
14116
+ length: buf.getUint32(currentOffset),
14117
+ type: buf.getUint32(currentOffset + 4)
14118
+ };
14119
+ }
14120
+ CrdtMessageProtocol.getHeader = getHeader;
14121
+ /**
14122
+ * Consume the incoming message without processing it.
14123
+ * @param buf - ByteBuffer
14124
+ * @returns true in case of success or false if there is no valid message.
14125
+ */
14126
+ function consumeMessage(buf) {
14127
+ const header = getHeader(buf);
14128
+ if (!header) {
14129
+ return false;
14130
+ }
14131
+ buf.incrementReadOffset(header.length);
14132
+ return true;
14133
+ }
14134
+ CrdtMessageProtocol.consumeMessage = consumeMessage;
14135
+ })(exports.CrdtMessageProtocol || (exports.CrdtMessageProtocol = {}));
14136
+ var CrdtMessageProtocol = exports.CrdtMessageProtocol;
13689
14137
 
13690
14138
  function crdtSceneSystem(engine, onProcessEntityComponentChange) {
13691
14139
  const transports = [];
13692
14140
  // CRDT Client
13693
- const crdtClient = distExports.crdtProtocol();
14141
+ const crdtClient = distExports.crdtProtocol({
14142
+ toEntityId: exports.EntityUtils.toEntityId,
14143
+ fromEntityId: exports.EntityUtils.fromEntityId
14144
+ });
13694
14145
  // Messages that we received at transport.onMessage waiting to be processed
13695
14146
  const receivedMessages = [];
13696
14147
  // Messages already processed by the engine but that we need to broadcast to other transports.
@@ -13712,21 +14163,47 @@
13712
14163
  const buffer = createByteBuffer({
13713
14164
  reading: { buffer: chunkMessage, currentOffset: 0 }
13714
14165
  });
13715
- while (WireMessage$1.validate(buffer)) {
14166
+ let header;
14167
+ while ((header = CrdtMessageProtocol.getHeader(buffer))) {
13716
14168
  const offset = buffer.currentReadOffset();
13717
- const message = ComponentOperation.read(buffer);
13718
- const { type, entity, componentId, data, timestamp } = message;
13719
- receivedMessages.push({
13720
- type,
13721
- entity,
13722
- componentId,
13723
- data,
13724
- timestamp,
13725
- transportId,
13726
- messageBuffer: buffer
13727
- .buffer()
13728
- .subarray(offset, buffer.currentReadOffset())
13729
- });
14169
+ if (header.type === exports.CrdtMessageType.DELETE_COMPONENT) {
14170
+ const message = exports.DeleteComponent.read(buffer);
14171
+ receivedMessages.push({
14172
+ ...header,
14173
+ ...message,
14174
+ transportId,
14175
+ messageBuffer: buffer
14176
+ .buffer()
14177
+ .subarray(offset, buffer.currentReadOffset())
14178
+ });
14179
+ }
14180
+ else if (header.type === exports.CrdtMessageType.PUT_COMPONENT) {
14181
+ const message = exports.PutComponentOperation.read(buffer);
14182
+ receivedMessages.push({
14183
+ ...header,
14184
+ ...message,
14185
+ transportId,
14186
+ messageBuffer: buffer
14187
+ .buffer()
14188
+ .subarray(offset, buffer.currentReadOffset())
14189
+ });
14190
+ }
14191
+ else if (header.type === exports.CrdtMessageType.DELETE_ENTITY) {
14192
+ const message = exports.DeleteEntity.read(buffer);
14193
+ receivedMessages.push({
14194
+ ...header,
14195
+ ...message,
14196
+ transportId,
14197
+ messageBuffer: buffer
14198
+ .buffer()
14199
+ .subarray(offset, buffer.currentReadOffset())
14200
+ });
14201
+ // Unknown message, we skip it
14202
+ }
14203
+ else {
14204
+ // consume the message
14205
+ buffer.incrementReadOffset(header.length);
14206
+ }
13730
14207
  }
13731
14208
  // TODO: do something if buffler.len>0
13732
14209
  };
@@ -13746,54 +14223,107 @@
13746
14223
  async function receiveMessages() {
13747
14224
  const messagesToProcess = getMessages(receivedMessages);
13748
14225
  const bufferForOutdated = createByteBuffer();
13749
- for (const message of messagesToProcess) {
13750
- const { data, timestamp, componentId, entity, type } = message;
13751
- const crdtMessage = {
13752
- key1: entity,
13753
- key2: componentId,
13754
- data: data || null,
13755
- timestamp: timestamp
13756
- };
13757
- const component = engine.getComponentOrNull(componentId);
13758
- if (component?.isDirty(entity)) {
13759
- crdtClient.createEvent(entity, component._id, component.toBinaryOrNull(entity)?.toBinary() || null);
13760
- }
13761
- const current = crdtClient.processMessage(crdtMessage);
13762
- if (!component) {
13763
- continue;
13764
- }
13765
- // CRDT outdated message. Resend this message to the transport
13766
- // To do this we add this message to a queue that will be processed at the end of the update tick
13767
- if (crdtMessage !== current) {
13768
- const offset = bufferForOutdated.currentWriteOffset();
13769
- const type = WireMessage$1.getType(component, entity);
13770
- const ts = current.timestamp;
13771
- ComponentOperation.write(type, entity, ts, component, bufferForOutdated);
13772
- outdatedMessages.push({
13773
- ...message,
13774
- timestamp: current.timestamp,
13775
- messageBuffer: bufferForOutdated
13776
- .buffer()
13777
- .subarray(offset, bufferForOutdated.currentWriteOffset())
14226
+ const entitiesShouldBeCleaned = [];
14227
+ for (const msg of messagesToProcess) {
14228
+ // TODO: emit delete entity to el onCrdtMessage
14229
+ if (msg.type === exports.CrdtMessageType.DELETE_ENTITY) {
14230
+ crdtClient.processMessage({
14231
+ type: typesExports.CRDTMessageType.CRDTMT_DeleteEntity,
14232
+ entityId: msg.entityId
13778
14233
  });
14234
+ entitiesShouldBeCleaned.push(msg.entityId);
13779
14235
  }
13780
14236
  else {
13781
- // Add message to transport queue to be processed by others transports
13782
- broadcastMessages.push(message);
13783
- // Process CRDT Message
13784
- if (type === WireMessage$1.Enum.DELETE_COMPONENT) {
13785
- component.deleteFrom(entity, false);
14237
+ // TODO: emit pu/delete component to el onCrdtMessage
14238
+ const crdtMessage = {
14239
+ type: typesExports.CRDTMessageType.CRDTMT_PutComponentData,
14240
+ entityId: msg.entityId,
14241
+ componentId: msg.componentId,
14242
+ data: msg.type === exports.CrdtMessageType.PUT_COMPONENT ? msg.data : null,
14243
+ timestamp: msg.timestamp
14244
+ };
14245
+ const entityState = engine.entityContainer.getEntityState(msg.entityId);
14246
+ // Skip updates from removed entityes
14247
+ if (entityState === exports.EntityState.Removed)
14248
+ continue;
14249
+ // Entities with unknown entities should update its entity state
14250
+ if (entityState === exports.EntityState.Unknown) {
14251
+ engine.entityContainer.updateUsedEntity(msg.entityId);
13786
14252
  }
13787
- else {
13788
- const opts = {
13789
- reading: { buffer: message.data, currentOffset: 0 }
13790
- };
13791
- const data = createByteBuffer(opts);
13792
- component.upsertFromBinary(message.entity, data, false);
14253
+ const component = engine.getComponentOrNull(msg.componentId);
14254
+ // The state isn't updated because the dirty was set
14255
+ // out of the block of systems update between `receiveMessage` and `updateState`
14256
+ if (component?.isDirty(msg.entityId)) {
14257
+ crdtClient.createComponentDataEvent(component._id, msg.entityId, component.toBinaryOrNull(msg.entityId)?.toBinary() || null);
13793
14258
  }
13794
- onProcessEntityComponentChange &&
13795
- onProcessEntityComponentChange(entity, component, type);
14259
+ const processResult = crdtClient.processMessage(crdtMessage);
14260
+ if (!component) {
14261
+ continue;
14262
+ }
14263
+ switch (processResult) {
14264
+ case typesExports.ProcessMessageResultType.StateUpdatedTimestamp:
14265
+ case typesExports.ProcessMessageResultType.StateUpdatedData:
14266
+ // Add message to transport queue to be processed by others transports
14267
+ broadcastMessages.push(msg);
14268
+ // Process CRDT Message
14269
+ if (msg.type === exports.CrdtMessageType.DELETE_COMPONENT) {
14270
+ component.deleteFrom(msg.entityId, false);
14271
+ }
14272
+ else {
14273
+ const opts = {
14274
+ reading: { buffer: msg.data, currentOffset: 0 }
14275
+ };
14276
+ const data = createByteBuffer(opts);
14277
+ component.upsertFromBinary(msg.entityId, data, false);
14278
+ }
14279
+ onProcessEntityComponentChange &&
14280
+ onProcessEntityComponentChange(msg.entityId, msg.type, component);
14281
+ break;
14282
+ // CRDT outdated message. Resend this message to the transport
14283
+ // To do this we add this message to a queue that will be processed at the end of the update tick
14284
+ case typesExports.ProcessMessageResultType.StateOutdatedData:
14285
+ case typesExports.ProcessMessageResultType.StateOutdatedTimestamp:
14286
+ const current = crdtClient
14287
+ .getState()
14288
+ .components.get(msg.componentId)
14289
+ ?.get(msg.entityId);
14290
+ if (current) {
14291
+ const offset = bufferForOutdated.currentWriteOffset();
14292
+ const ts = current.timestamp;
14293
+ if (component.has(msg.entityId)) {
14294
+ exports.PutComponentOperation.write(msg.entityId, ts, component, bufferForOutdated);
14295
+ }
14296
+ else {
14297
+ exports.DeleteComponent.write(msg.entityId, component._id, ts, bufferForOutdated);
14298
+ }
14299
+ outdatedMessages.push({
14300
+ ...msg,
14301
+ messageBuffer: bufferForOutdated
14302
+ .buffer()
14303
+ .subarray(offset, bufferForOutdated.currentWriteOffset())
14304
+ });
14305
+ }
14306
+ break;
14307
+ case typesExports.ProcessMessageResultType.NoChanges:
14308
+ case typesExports.ProcessMessageResultType.EntityDeleted:
14309
+ case typesExports.ProcessMessageResultType.EntityWasDeleted:
14310
+ }
14311
+ }
14312
+ }
14313
+ // TODO: emit delete entity to el onCrdtMessage
14314
+ for (const entity of entitiesShouldBeCleaned) {
14315
+ // If we tried to resend outdated message and the entity was deleted before, we avoid sending them.
14316
+ for (let i = outdatedMessages.length - 1; i >= 0; i--) {
14317
+ if (outdatedMessages[i].entityId === entity) {
14318
+ outdatedMessages.splice(i, 1);
14319
+ }
14320
+ }
14321
+ for (const definition of engine.componentsIter()) {
14322
+ definition.deleteFrom(entity, false);
13796
14323
  }
14324
+ engine.entityContainer.updateRemovedEntity(entity);
14325
+ onProcessEntityComponentChange &&
14326
+ onProcessEntityComponentChange(entity, exports.CrdtMessageType.DELETE_ENTITY);
13797
14327
  }
13798
14328
  }
13799
14329
  /**
@@ -13811,15 +14341,20 @@
13811
14341
  entitySet = [];
13812
14342
  dirtyMap.set(component, entitySet);
13813
14343
  }
13814
- entitySet.push(entity);
13815
14344
  // TODO: reuse shared writer to prevent extra allocations of toBinary
13816
14345
  const componentValue = component.toBinaryOrNull(entity)?.toBinary() ?? null;
13817
14346
  // TODO: do not emit event if componentValue equals the value didn't change
13818
- crdtClient.createEvent(entity, component._id, componentValue);
13819
- onProcessEntityComponentChange &&
13820
- onProcessEntityComponentChange(entity, component, componentValue === null
13821
- ? WireMessage$1.Enum.DELETE_COMPONENT
13822
- : WireMessage$1.Enum.PUT_COMPONENT);
14347
+ // if update goes bad, the entity doesn't accept put anymore (it's added to deleted entities set)
14348
+ if (crdtClient.createComponentDataEvent(component._id, entity, componentValue) === null) {
14349
+ component.deleteFrom(entity, false);
14350
+ }
14351
+ else {
14352
+ entitySet.push(entity);
14353
+ onProcessEntityComponentChange &&
14354
+ onProcessEntityComponentChange(entity, componentValue === null
14355
+ ? exports.CrdtMessageType.DELETE_COMPONENT
14356
+ : exports.CrdtMessageType.PUT_COMPONENT, component);
14357
+ }
13823
14358
  }
13824
14359
  }
13825
14360
  return dirtyMap;
@@ -13827,7 +14362,7 @@
13827
14362
  /**
13828
14363
  * Iterates the dirty map and generates crdt messages to be send
13829
14364
  */
13830
- async function sendMessages(dirtyEntities) {
14365
+ async function sendMessages(dirtyEntities, deletedEntities) {
13831
14366
  // CRDT Messages will be the merge between the recieved transport messages and the new crdt messages
13832
14367
  const crdtMessages = getMessages(broadcastMessages);
13833
14368
  const outdatedMessagesBkp = getMessages(outdatedMessages);
@@ -13837,19 +14372,26 @@
13837
14372
  // Component will be always defined here since dirtyMap its an iterator of engine.componentsDefinition
13838
14373
  const { timestamp } = crdtClient
13839
14374
  .getState()
13840
- .get(entity)
13841
- .get(component._id);
14375
+ .components.get(component._id)
14376
+ .get(entity);
13842
14377
  const offset = buffer.currentWriteOffset();
13843
- const type = WireMessage$1.getType(component, entity);
14378
+ const type = component.has(entity)
14379
+ ? exports.CrdtMessageType.PUT_COMPONENT
14380
+ : exports.CrdtMessageType.DELETE_COMPONENT;
13844
14381
  const transportMessage = {
13845
14382
  type,
14383
+ entityId: entity,
13846
14384
  componentId: component._id,
13847
- entity,
13848
14385
  timestamp
13849
14386
  };
13850
14387
  // Avoid creating messages if there is no transport that will handle it
13851
14388
  if (transports.some((t) => t.filter(transportMessage))) {
13852
- ComponentOperation.write(type, entity, timestamp, component, buffer);
14389
+ if (transportMessage.type === exports.CrdtMessageType.PUT_COMPONENT) {
14390
+ exports.PutComponentOperation.write(entity, timestamp, component, buffer);
14391
+ }
14392
+ else {
14393
+ exports.DeleteComponent.write(entity, component._id, timestamp, buffer);
14394
+ }
13853
14395
  crdtMessages.push({
13854
14396
  ...transportMessage,
13855
14397
  messageBuffer: buffer
@@ -13859,6 +14401,19 @@
13859
14401
  }
13860
14402
  }
13861
14403
  }
14404
+ // After all updates, I execute the DeletedEntity messages
14405
+ for (const entityId of deletedEntities) {
14406
+ crdtClient.createDeleteEntityEvent(entityId);
14407
+ const offset = buffer.currentWriteOffset();
14408
+ exports.DeleteEntity.write(entityId, buffer);
14409
+ crdtMessages.push({
14410
+ type: exports.CrdtMessageType.DELETE_ENTITY,
14411
+ entityId,
14412
+ messageBuffer: buffer
14413
+ .buffer()
14414
+ .subarray(offset, buffer.currentWriteOffset())
14415
+ });
14416
+ }
13862
14417
  // Send CRDT messages to transports
13863
14418
  const transportBuffer = createByteBuffer();
13864
14419
  for (const index in transports) {
@@ -13869,8 +14424,11 @@
13869
14424
  // So we can fix their crdt state
13870
14425
  for (const message of outdatedMessagesBkp) {
13871
14426
  if (message.transportId === transportIndex &&
14427
+ // TODO: This is an optimization, the state should converge anyway, whatever the message is sent.
13872
14428
  // Avoid sending multiple messages for the same entity-componentId
13873
- !crdtMessages.find((m) => m.entity === message.entity &&
14429
+ !crdtMessages.find((m) => m.entityId === message.entityId &&
14430
+ // TODO: as any, with multiple type of messages, it should have many checks before the check for similar messages
14431
+ m.componentId &&
13874
14432
  m.componentId === message.componentId)) {
13875
14433
  transportBuffer.writeBuffer(message.messageBuffer, false);
13876
14434
  }
@@ -14058,79 +14616,6 @@
14058
14616
  };
14059
14617
  }
14060
14618
 
14061
- const MAX_U16 = 0xffff;
14062
- const MASK_UPPER_16_ON_32 = 0xffff0000;
14063
- const AMOUNT_VERSION_AVAILABLE = MAX_U16 + 1;
14064
- /**
14065
- * @public This first 512 entities are reserved by the renderer
14066
- */
14067
- const RESERVED_STATIC_ENTITIES = 512;
14068
- /**
14069
- * @public
14070
- */
14071
- const MAX_ENTITY_NUMBER = MAX_U16;
14072
- function EntityContainer() {
14073
- let entityCounter = RESERVED_STATIC_ENTITIES;
14074
- const usedEntities = new Set();
14075
- const removedEntities = new Map();
14076
- function entityVersion(entity) {
14077
- return (((entity & MASK_UPPER_16_ON_32) >> 16) & MAX_U16) >>> 0;
14078
- }
14079
- function entityNumber(entity) {
14080
- return (entity & MAX_U16) >>> 0;
14081
- }
14082
- function entityId(entityNumber, entityVersion) {
14083
- return (((entityNumber & MAX_U16) | ((entityVersion & MAX_U16) << 16)) >>>
14084
- 0);
14085
- }
14086
- function generateNewEntity() {
14087
- if (entityCounter > MAX_ENTITY_NUMBER - 1) {
14088
- throw new Error(`It fails trying to generate an entity out of range ${MAX_ENTITY_NUMBER}.`);
14089
- }
14090
- const entity = entityCounter++;
14091
- usedEntities.add(entity);
14092
- return entity;
14093
- }
14094
- function generateEntity() {
14095
- if (usedEntities.size + RESERVED_STATIC_ENTITIES >= entityCounter) {
14096
- return generateNewEntity();
14097
- }
14098
- for (const [number, version] of removedEntities) {
14099
- if (version < MAX_U16) {
14100
- const entity = entityId(number, version + 1);
14101
- usedEntities.add(entity);
14102
- removedEntities.delete(number);
14103
- return entity;
14104
- }
14105
- }
14106
- return generateNewEntity();
14107
- }
14108
- function removeEntity(entity) {
14109
- const deleted = usedEntities.delete(entity);
14110
- if (deleted) {
14111
- removedEntities.set(entityNumber(entity), entityVersion(entity));
14112
- }
14113
- return deleted;
14114
- }
14115
- return {
14116
- generateEntity() {
14117
- return generateEntity();
14118
- },
14119
- removeEntity(entity) {
14120
- return removeEntity(entity);
14121
- },
14122
- entityExists(entity) {
14123
- return entity < RESERVED_STATIC_ENTITIES || usedEntities.has(entity);
14124
- },
14125
- getExistingEntities() {
14126
- return new Set(usedEntities);
14127
- },
14128
- entityVersion,
14129
- entityNumber,
14130
- entityId
14131
- };
14132
- }
14133
-
14134
14619
  const SYSTEMS_REGULAR_PRIORITY = 100e3;
14135
14620
  function SystemContainer() {
14136
14621
  const systems = [];
@@ -14345,9 +14830,6 @@
14345
14830
  const entity = entityContainer.generateEntity();
14346
14831
  return entity;
14347
14832
  }
14348
- function entityExists(entity) {
14349
- return entityContainer.entityExists(entity);
14350
- }
14351
14833
  function removeEntity(entity) {
14352
14834
  for (const [, component] of componentsDefinition) {
14353
14835
  if (component.has(entity)) {
@@ -14454,8 +14936,6 @@
14454
14936
  }
14455
14937
  }
14456
14938
  return {
14457
- entityExists,
14458
- componentsDefinition,
14459
14939
  addEntity,
14460
14940
  removeEntity,
14461
14941
  addSystem,
@@ -14469,6 +14949,7 @@
14469
14949
  removeComponentDefinition,
14470
14950
  removeEntityWithChildren,
14471
14951
  registerCustomComponent,
14952
+ entityContainer,
14472
14953
  componentsIter
14473
14954
  };
14474
14955
  }
@@ -14485,8 +14966,9 @@
14485
14966
  checkNotThenable(ret, `A system (${system.name || 'anonymous'}) returned a thenable. Systems cannot be async functions. Documentation: https://dcl.gg/sdk/sync-systems`);
14486
14967
  }
14487
14968
  const dirtyEntities = crdtSystem.updateState();
14488
- await crdtSystem.sendMessages(dirtyEntities);
14489
- for (const [_componentId, definition] of engine.componentsDefinition) {
14969
+ const deletedEntites = engine.entityContainer.releaseRemovedEntities();
14970
+ await crdtSystem.sendMessages(dirtyEntities, deletedEntites);
14971
+ for (const definition of engine.componentsIter()) {
14490
14972
  definition.clearDirty();
14491
14973
  }
14492
14974
  }
@@ -14508,10 +14990,10 @@
14508
14990
  RootEntity: 0,
14509
14991
  PlayerEntity: 1,
14510
14992
  CameraEntity: 2,
14511
- entityExists: engine.entityExists,
14993
+ getEntityState: engine.entityContainer.getEntityState,
14512
14994
  addTransport: crdtSystem.addTransport,
14513
14995
  getCrdtState: crdtSystem.getCrdt,
14514
- componentsDefinition: engine.componentsDefinition
14996
+ entityContainer: engine.entityContainer
14515
14997
  };
14516
14998
  }
14517
14999
 
@@ -14597,7 +15079,7 @@
14597
15079
  // @internal
14598
15080
  engine.addSystem(function EventSystem() {
14599
15081
  for (const [entity, event] of eventsMap) {
14600
- if (!engine.entityExists(entity)) {
15082
+ if (engine.getEntityState(entity) === exports.EntityState.Removed) {
14601
15083
  eventsMap.delete(entity);
14602
15084
  continue;
14603
15085
  }
@@ -41150,7 +41632,9 @@
41150
41632
  }
41151
41633
  },
41152
41634
  filter(message) {
41153
- if (!componentIds.includes(message.componentId)) {
41635
+ if ((message.type === exports.CrdtMessageType.PUT_COMPONENT ||
41636
+ message.type === exports.CrdtMessageType.DELETE_COMPONENT) &&
41637
+ !componentIds.includes(message.componentId)) {
41154
41638
  return false;
41155
41639
  }
41156
41640
  return !!message;
@@ -41178,6 +41662,7 @@
41178
41662
  exports.BorderRect = BorderRect;
41179
41663
  exports.Button = Button;
41180
41664
  exports.CANVAS_ROOT_ENTITY = CANVAS_ROOT_ENTITY;
41665
+ exports.CRDT_MESSAGE_HEADER_LENGTH = CRDT_MESSAGE_HEADER_LENGTH;
41181
41666
  exports.CameraMode = CameraMode;
41182
41667
  exports.CameraModeArea = CameraModeArea;
41183
41668
  exports.DEG2RAD = DEG2RAD;