@dcl/playground-assets 7.0.5 → 7.0.6-3713220614.commit-d02ef9f

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
@@ -12828,7 +12828,8 @@
12828
12828
  }
12829
12829
  // Race condition, same timestamp diff data.
12830
12830
  function compareData(current, data) {
12831
- return current > data;
12831
+ // Null value wins
12832
+ return !current || current > data;
12832
12833
  }
12833
12834
  if (compareData(current.data, data)) {
12834
12835
  return {
@@ -13130,13 +13131,14 @@
13130
13131
  */
13131
13132
  var WireMessage;
13132
13133
  (function (WireMessage) {
13134
+ let Enum;
13133
13135
  (function (Enum) {
13134
13136
  Enum[Enum["RESERVED"] = 0] = "RESERVED";
13135
13137
  // Component Operation
13136
13138
  Enum[Enum["PUT_COMPONENT"] = 1] = "PUT_COMPONENT";
13137
13139
  Enum[Enum["DELETE_COMPONENT"] = 2] = "DELETE_COMPONENT";
13138
13140
  Enum[Enum["MAX_MESSAGE_TYPE"] = 3] = "MAX_MESSAGE_TYPE";
13139
- })(WireMessage.Enum || (WireMessage.Enum = {}));
13141
+ })(Enum = WireMessage.Enum || (WireMessage.Enum = {}));
13140
13142
  WireMessage.HEADER_LENGTH = 8;
13141
13143
  /**
13142
13144
  * Validate if the message incoming is completed
@@ -13164,6 +13166,10 @@
13164
13166
  };
13165
13167
  }
13166
13168
  WireMessage.readHeader = readHeader;
13169
+ function getType(component, entity) {
13170
+ return component.has(entity) ? Enum.PUT_COMPONENT : Enum.DELETE_COMPONENT;
13171
+ }
13172
+ WireMessage.getType = getType;
13167
13173
  })(WireMessage || (WireMessage = {}));
13168
13174
  var WireMessage$1 = WireMessage;
13169
13175
 
@@ -13221,14 +13227,15 @@
13221
13227
  // Messages that we received at transport.onMessage waiting to be processed
13222
13228
  const receivedMessages = [];
13223
13229
  // Messages already processed by the engine but that we need to broadcast to other transports.
13224
- const transportMessages = [];
13225
- // Map of entities already processed at least once
13230
+ const broadcastMessages = [];
13231
+ // Messages receieved by a transport that were outdated. We need to correct them
13232
+ const outdatedMessages = [];
13226
13233
  /**
13227
13234
  *
13228
- * @param transportType tranport id to identiy messages
13235
+ * @param transportId tranport id to identiy messages
13229
13236
  * @returns a function to process received messages
13230
13237
  */
13231
- function parseChunkMessage(transportType) {
13238
+ function parseChunkMessage(transportId) {
13232
13239
  /**
13233
13240
  * Receives a chunk of binary messages and stores all the valid
13234
13241
  * Component Operation Messages at messages queue
@@ -13248,7 +13255,7 @@
13248
13255
  componentId,
13249
13256
  data,
13250
13257
  timestamp,
13251
- transportType,
13258
+ transportId,
13252
13259
  messageBuffer: buffer
13253
13260
  .buffer()
13254
13261
  .subarray(offset, buffer.currentReadOffset())
@@ -13262,93 +13269,117 @@
13262
13269
  * @returns messages recieved by the transport to process on the next tick
13263
13270
  */
13264
13271
  function getMessages(value) {
13265
- const messagesToProcess = Array.from(value);
13266
- value.length = 0;
13272
+ const messagesToProcess = value.splice(0, value.length);
13267
13273
  return messagesToProcess;
13268
13274
  }
13269
13275
  /**
13270
13276
  * This fn will be called on every tick.
13271
13277
  * Process all the messages queue received by the transport
13272
13278
  */
13273
- function receiveMessages() {
13279
+ async function receiveMessages() {
13274
13280
  const messagesToProcess = getMessages(receivedMessages);
13275
- for (const transport of transports) {
13276
- const buffer = createByteBuffer();
13277
- for (const message of messagesToProcess) {
13278
- const { data, timestamp, componentId, entity, type } = message;
13279
- const crdtMessage = {
13280
- key1: entity,
13281
- key2: componentId,
13282
- data: data || null,
13283
- timestamp: timestamp
13284
- };
13285
- const component = engine.getComponentOrNull(componentId);
13286
- const current = crdtClient.processMessage(crdtMessage);
13287
- /* istanbul ignore next */
13288
- if (!component)
13289
- // TODO: TEST
13290
- continue;
13291
- // CRDT outdated message. Resend this message through the wire
13292
- if (crdtMessage !== current) {
13293
- const type = component.has(entity)
13294
- ? WireMessage$1.Enum.PUT_COMPONENT
13295
- : WireMessage$1.Enum.DELETE_COMPONENT;
13296
- ComponentOperation.write(type, entity, current.timestamp, component, buffer);
13281
+ const bufferForOutdated = createByteBuffer();
13282
+ for (const message of messagesToProcess) {
13283
+ const { data, timestamp, componentId, entity, type } = message;
13284
+ const crdtMessage = {
13285
+ key1: entity,
13286
+ key2: componentId,
13287
+ data: data || null,
13288
+ timestamp: timestamp
13289
+ };
13290
+ const component = engine.getComponentOrNull(componentId);
13291
+ if (component?.isDirty(entity)) {
13292
+ crdtClient.createEvent(entity, component._id, component.toBinaryOrNull(entity)?.toBinary() || null);
13293
+ }
13294
+ const current = crdtClient.processMessage(crdtMessage);
13295
+ if (!component) {
13296
+ continue;
13297
+ }
13298
+ // CRDT outdated message. Resend this message to the transport
13299
+ // To do this we add this message to a queue that will be processed at the end of the update tick
13300
+ if (crdtMessage !== current) {
13301
+ const offset = bufferForOutdated.currentWriteOffset();
13302
+ const type = WireMessage$1.getType(component, entity);
13303
+ const ts = current.timestamp;
13304
+ ComponentOperation.write(type, entity, ts, component, bufferForOutdated);
13305
+ outdatedMessages.push({
13306
+ ...message,
13307
+ timestamp: current.timestamp,
13308
+ messageBuffer: bufferForOutdated
13309
+ .buffer()
13310
+ .subarray(offset, bufferForOutdated.currentWriteOffset())
13311
+ });
13312
+ }
13313
+ else {
13314
+ // Add message to transport queue to be processed by others transports
13315
+ broadcastMessages.push(message);
13316
+ // Process CRDT Message
13317
+ if (type === WireMessage$1.Enum.DELETE_COMPONENT) {
13318
+ component.deleteFrom(entity, false);
13297
13319
  }
13298
13320
  else {
13299
- // Process CRDT Message
13300
- if (type === WireMessage$1.Enum.DELETE_COMPONENT) {
13301
- component.deleteFrom(entity);
13302
- }
13303
- else {
13304
- const opts = {
13305
- reading: { buffer: message.data, currentOffset: 0 }
13306
- };
13307
- const bb = createByteBuffer(opts);
13308
- // Update engine component
13309
- component.upsertFromBinary(message.entity, bb);
13310
- component.clearDirty();
13311
- }
13312
- // Add message to transport queue to be processed by others transports
13313
- transportMessages.push(message);
13321
+ const opts = {
13322
+ reading: { buffer: message.data, currentOffset: 0 }
13323
+ };
13324
+ const data = createByteBuffer(opts);
13325
+ component.upsertFromBinary(message.entity, data, false);
13326
+ }
13327
+ }
13328
+ }
13329
+ }
13330
+ function getDirtyMap() {
13331
+ const dirtySet = new Map();
13332
+ for (const [componentId, definition] of engine.componentsDefinition) {
13333
+ for (const entity of definition.dirtyIterator()) {
13334
+ if (!dirtySet.has(entity)) {
13335
+ dirtySet.set(entity, new Set());
13314
13336
  }
13337
+ dirtySet.get(entity).add(componentId);
13315
13338
  }
13316
- if (buffer.size()) {
13317
- transport.send(buffer.toBinary());
13339
+ }
13340
+ return dirtySet;
13341
+ }
13342
+ /**
13343
+ * Updates CRDT state of the current engine dirty components
13344
+ */
13345
+ function updateState() {
13346
+ const dirtyEntities = getDirtyMap();
13347
+ for (const [entity, componentsId] of getDirtyMap()) {
13348
+ for (const componentId of componentsId) {
13349
+ const component = engine.getComponent(componentId);
13350
+ const componentValue = component.toBinaryOrNull(entity)?.toBinary() ?? null;
13351
+ crdtClient.createEvent(entity, componentId, componentValue);
13318
13352
  }
13319
13353
  }
13354
+ return dirtyEntities;
13320
13355
  }
13321
13356
  /**
13322
13357
  * Iterates the dirty map and generates crdt messages to be send
13323
- * @param dirtyMap a map of { entities: [componentId] }
13324
13358
  */
13325
- function createMessages(dirtyMap) {
13359
+ async function sendMessages(dirtyEntities) {
13326
13360
  // CRDT Messages will be the merge between the recieved transport messages and the new crdt messages
13327
- const crdtMessages = getMessages(transportMessages);
13361
+ const crdtMessages = getMessages(broadcastMessages);
13362
+ const outdatedMessagesBkp = getMessages(outdatedMessages);
13328
13363
  const buffer = createByteBuffer();
13329
- for (const [entity, componentsId] of dirtyMap) {
13364
+ for (const [entity, componentsId] of dirtyEntities) {
13330
13365
  for (const componentId of componentsId) {
13331
- const component = engine.getComponentOrNull(componentId);
13332
- /* istanbul ignore next */
13333
- if (!component)
13334
- // TODO: test coverage
13335
- continue;
13336
- const entityComponent = component.has(entity)
13337
- ? component.toBinary(entity).toBinary()
13338
- : null;
13339
- const event = crdtClient.createEvent(entity, componentId, entityComponent);
13366
+ // Component will be always defined here since dirtyMap its an iterator of engine.componentsDefinition
13367
+ const component = engine.getComponent(componentId);
13368
+ const { timestamp } = crdtClient
13369
+ .getState()
13370
+ .get(entity)
13371
+ .get(componentId);
13340
13372
  const offset = buffer.currentWriteOffset();
13341
- const type = component.has(entity)
13342
- ? WireMessage$1.Enum.PUT_COMPONENT
13343
- : WireMessage$1.Enum.DELETE_COMPONENT;
13373
+ const type = WireMessage$1.getType(component, entity);
13344
13374
  const transportMessage = {
13345
13375
  type,
13346
13376
  componentId,
13347
13377
  entity,
13348
- timestamp: event.timestamp
13378
+ timestamp
13349
13379
  };
13380
+ // Avoid creating messages if there is no transport that will handle it
13350
13381
  if (transports.some((t) => t.filter(transportMessage))) {
13351
- ComponentOperation.write(type, entity, event.timestamp, component, buffer);
13382
+ ComponentOperation.write(type, entity, timestamp, component, buffer);
13352
13383
  crdtMessages.push({
13353
13384
  ...transportMessage,
13354
13385
  messageBuffer: buffer
@@ -13358,33 +13389,56 @@
13358
13389
  }
13359
13390
  }
13360
13391
  }
13361
- // Send messages to transports
13392
+ // Send CRDT messages to transports
13362
13393
  const transportBuffer = createByteBuffer();
13363
- for (const transport of transports) {
13394
+ for (const index in transports) {
13395
+ const transportIndex = Number(index);
13396
+ const transport = transports[transportIndex];
13364
13397
  transportBuffer.resetBuffer();
13365
- for (const message of crdtMessages) {
13366
- if (transport.filter(message)) {
13398
+ // First we need to send all the messages that were outdated from a transport
13399
+ // So we can fix their crdt state
13400
+ for (const message of outdatedMessagesBkp) {
13401
+ if (message.transportId === transportIndex &&
13402
+ // Avoid sending multiple messages for the same entity-componentId
13403
+ !crdtMessages.find((m) => m.entity === message.entity &&
13404
+ m.componentId === message.componentId)) {
13367
13405
  transportBuffer.writeBuffer(message.messageBuffer, false);
13368
13406
  }
13369
13407
  }
13370
- if (transportBuffer.size()) {
13371
- transport.send(transportBuffer.toBinary());
13372
- }
13373
- else {
13374
- transport.send(new Uint8Array([]));
13408
+ // Then we send all the new crdtMessages that the transport needs to process
13409
+ for (const message of crdtMessages) {
13410
+ if (message.transportId !== transportIndex &&
13411
+ transport.filter(message)) {
13412
+ transportBuffer.writeBuffer(message.messageBuffer, false);
13413
+ }
13375
13414
  }
13415
+ const message = transportBuffer.size()
13416
+ ? transportBuffer.toBinary()
13417
+ : new Uint8Array([]);
13418
+ await transport.send(message);
13376
13419
  }
13377
13420
  }
13421
+ /**
13422
+ * @public
13423
+ * Add a transport to the crdt system
13424
+ */
13378
13425
  function addTransport(transport) {
13379
- transports.push(transport);
13380
- transport.onmessage = parseChunkMessage(transport.type);
13381
- // TODO: pull messages from transport
13382
- // TODO: send entities to transport
13426
+ const id = transports.push(transport) - 1;
13427
+ transport.onmessage = parseChunkMessage(id);
13428
+ }
13429
+ /**
13430
+ * @public
13431
+ * @returns returns the crdt state
13432
+ */
13433
+ function getCrdt() {
13434
+ return crdtClient.getState();
13383
13435
  }
13384
13436
  return {
13385
- createMessages,
13437
+ getCrdt,
13438
+ sendMessages,
13386
13439
  receiveMessages,
13387
- addTransport
13440
+ addTransport,
13441
+ updateState
13388
13442
  };
13389
13443
  }
13390
13444
 
@@ -13431,10 +13485,15 @@
13431
13485
  has(entity) {
13432
13486
  return data.has(entity);
13433
13487
  },
13434
- deleteFrom(entity) {
13488
+ deleteFrom(entity, markAsDirty = true) {
13435
13489
  const component = data.get(entity);
13436
13490
  data.delete(entity);
13437
- dirtyIterator.add(entity);
13491
+ if (markAsDirty) {
13492
+ dirtyIterator.add(entity);
13493
+ }
13494
+ else {
13495
+ dirtyIterator.delete(entity);
13496
+ }
13438
13497
  return component || null;
13439
13498
  },
13440
13499
  getOrNull(entity) {
@@ -13498,6 +13557,15 @@
13498
13557
  spec.serialize(component, writeBuffer);
13499
13558
  return writeBuffer;
13500
13559
  },
13560
+ toBinaryOrNull(entity) {
13561
+ const component = data.get(entity);
13562
+ if (!component) {
13563
+ return null;
13564
+ }
13565
+ const writeBuffer = createByteBuffer();
13566
+ spec.serialize(component, writeBuffer);
13567
+ return writeBuffer;
13568
+ },
13501
13569
  writeToByteBuffer(entity, buffer) {
13502
13570
  const component = data.get(entity);
13503
13571
  if (!component) {
@@ -13505,19 +13573,27 @@
13505
13573
  }
13506
13574
  spec.serialize(component, buffer);
13507
13575
  },
13508
- updateFromBinary(entity, buffer) {
13576
+ updateFromBinary(entity, buffer, markAsDirty = true) {
13509
13577
  const component = data.get(entity);
13510
13578
  if (!component) {
13511
13579
  throw new Error(`[updateFromBinary] Component ${componentId} for ${entity} not found`);
13512
13580
  }
13513
- return this.upsertFromBinary(entity, buffer);
13581
+ return this.upsertFromBinary(entity, buffer, markAsDirty);
13514
13582
  },
13515
- upsertFromBinary(entity, buffer) {
13583
+ upsertFromBinary(entity, buffer, markAsDirty = true) {
13516
13584
  const newValue = spec.deserialize(buffer);
13517
13585
  data.set(entity, newValue);
13518
- dirtyIterator.add(entity);
13586
+ if (markAsDirty) {
13587
+ dirtyIterator.add(entity);
13588
+ }
13589
+ else {
13590
+ dirtyIterator.delete(entity);
13591
+ }
13519
13592
  return newValue;
13520
13593
  },
13594
+ deserialize(buffer) {
13595
+ return spec.deserialize(buffer);
13596
+ },
13521
13597
  clearDirty() {
13522
13598
  dirtyIterator.clear();
13523
13599
  }
@@ -13855,6 +13931,14 @@
13855
13931
  }
13856
13932
  return entityContainer.removeEntity(entity);
13857
13933
  }
13934
+ function registerCustomComponent(component, componentId) {
13935
+ const prev = componentsDefinition.get(componentId);
13936
+ if (prev) {
13937
+ throw new Error(`Component number ${componentId} was already registered.`);
13938
+ }
13939
+ componentsDefinition.set(componentId, component);
13940
+ return component;
13941
+ }
13858
13942
  function defineComponentFromSchema(spec, componentId, constructorDefault) {
13859
13943
  const prev = componentsDefinition.get(componentId);
13860
13944
  if (prev) {
@@ -13906,6 +13990,24 @@
13906
13990
  function removeComponentDefinition(componentId) {
13907
13991
  componentsDefinition.delete(componentId);
13908
13992
  }
13993
+ const Transform = Transform$1({ defineComponentFromSchema });
13994
+ function* getTreeEntityArray(firstEntity, proccesedEntities) {
13995
+ // This avoid infinite loop when there is a cyclic parenting
13996
+ if (proccesedEntities.find((value) => firstEntity === value))
13997
+ return;
13998
+ proccesedEntities.push(firstEntity);
13999
+ for (const [entity, value] of getEntitiesWith(Transform)) {
14000
+ if (value.parent === firstEntity) {
14001
+ yield* getTreeEntityArray(entity, proccesedEntities);
14002
+ }
14003
+ }
14004
+ yield firstEntity;
14005
+ }
14006
+ function removeEntityWithChildren(firstEntity) {
14007
+ for (const entity of getTreeEntityArray(firstEntity, [])) {
14008
+ removeEntity(entity);
14009
+ }
14010
+ }
13909
14011
  return {
13910
14012
  entityExists,
13911
14013
  componentsDefinition,
@@ -13919,7 +14021,9 @@
13919
14021
  getEntitiesWith,
13920
14022
  getComponent,
13921
14023
  getComponentOrNull,
13922
- removeComponentDefinition
14024
+ removeComponentDefinition,
14025
+ removeEntityWithChildren,
14026
+ registerCustomComponent
13923
14027
  };
13924
14028
  }
13925
14029
  /**
@@ -13928,55 +14032,27 @@
13928
14032
  function Engine() {
13929
14033
  const engine = preEngine();
13930
14034
  const crdtSystem = crdtSceneSystem(engine);
13931
- function update(dt) {
13932
- crdtSystem.receiveMessages();
14035
+ async function update(dt) {
14036
+ await crdtSystem.receiveMessages();
13933
14037
  for (const system of engine.getSystems()) {
13934
14038
  const ret = system.fn(dt);
13935
14039
  checkNotThenable(ret, `A system (${system.name || 'anonymous'}) returned a thenable. Systems cannot be async functions. Documentation: https://dcl.gg/sdk/sync-systems`);
13936
14040
  }
13937
- // TODO: Perf tip
13938
- // Should we add some dirtyIteratorSet at engine level so we dont have
13939
- // to iterate all the component definitions to get the dirty ones ?
13940
- const dirtySet = new Map();
13941
- for (const [componentId, definition] of engine.componentsDefinition) {
13942
- for (const entity of definition.dirtyIterator()) {
13943
- if (!dirtySet.has(entity)) {
13944
- dirtySet.set(entity, new Set());
13945
- }
13946
- dirtySet.get(entity).add(componentId);
13947
- }
13948
- }
13949
- crdtSystem.createMessages(dirtySet);
14041
+ const dirtyEntities = crdtSystem.updateState();
14042
+ await crdtSystem.sendMessages(dirtyEntities);
13950
14043
  for (const [_componentId, definition] of engine.componentsDefinition) {
13951
14044
  definition.clearDirty();
13952
14045
  }
13953
14046
  }
13954
- const Transform = Transform$1(engine);
13955
- function* getTreeEntityArray(firstEntity, proccesedEntities) {
13956
- // This avoid infinite loop when there is a cyclic parenting
13957
- if (proccesedEntities.find((value) => firstEntity === value))
13958
- return;
13959
- proccesedEntities.push(firstEntity);
13960
- for (const [entity, value] of engine.getEntitiesWith(Transform)) {
13961
- if (value.parent === firstEntity) {
13962
- yield* getTreeEntityArray(entity, proccesedEntities);
13963
- }
13964
- }
13965
- yield firstEntity;
13966
- }
13967
- function removeEntityWithChildren(firstEntity) {
13968
- for (const entity of getTreeEntityArray(firstEntity, [])) {
13969
- engine.removeEntity(entity);
13970
- }
13971
- }
13972
14047
  return {
13973
14048
  addEntity: engine.addEntity,
13974
14049
  removeEntity: engine.removeEntity,
13975
- removeEntityWithChildren,
14050
+ removeEntityWithChildren: engine.removeEntityWithChildren,
13976
14051
  addSystem: engine.addSystem,
13977
14052
  removeSystem: engine.removeSystem,
13978
14053
  defineComponent: engine.defineComponent,
13979
14054
  defineComponentFromSchema: engine.defineComponentFromSchema,
14055
+ registerCustomComponent: engine.registerCustomComponent,
13980
14056
  getEntitiesWith: engine.getEntitiesWith,
13981
14057
  getComponent: engine.getComponent,
13982
14058
  getComponentOrNull: engine.getComponentOrNull,
@@ -13986,7 +14062,9 @@
13986
14062
  PlayerEntity: 1,
13987
14063
  CameraEntity: 2,
13988
14064
  entityExists: engine.entityExists,
13989
- addTransport: crdtSystem.addTransport
14065
+ addTransport: crdtSystem.addTransport,
14066
+ getCrdtState: crdtSystem.getCrdt,
14067
+ componentsDefinition: engine.componentsDefinition
13990
14068
  };
13991
14069
  }
13992
14070
 
@@ -40393,19 +40471,17 @@
40393
40471
  }
40394
40472
  }
40395
40473
  }
40396
- const type = 'renderer';
40397
40474
  const rendererTransport = {
40398
- type,
40399
- send(message) {
40400
- sendToRenderer(message).catch((error) => {
40475
+ async send(message) {
40476
+ try {
40477
+ await sendToRenderer(message);
40478
+ }
40479
+ catch (error) {
40401
40480
  console.error(error);
40402
40481
  debugger;
40403
- });
40482
+ }
40404
40483
  },
40405
40484
  filter(message) {
40406
- if (message.transportType === type) {
40407
- return false;
40408
- }
40409
40485
  if (!componentIds.includes(message.componentId)) {
40410
40486
  return false;
40411
40487
  }
@@ -40418,7 +40494,7 @@
40418
40494
  engine.addTransport(createRendererTransport({ crdtSendToRenderer: EngineApi.crdtSendToRenderer }));
40419
40495
  setSubscribeFunction(EngineApi.subscribe);
40420
40496
  async function onUpdate(deltaTime) {
40421
- engine.update(deltaTime);
40497
+ await engine.update(deltaTime);
40422
40498
  await pollEvents(EngineApi.sendBatch);
40423
40499
  }
40424
40500