@dcl/sdk 7.6.3-11407444266.commit-d9e3bec → 7.6.3-11411638449.commit-c070ea9

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.
@@ -1,2 +1,2 @@
1
- declare const getChildren: (parent: import("@dcl/ecs").Entity) => Iterable<import("@dcl/ecs").Entity>, syncEntity: (entityId: import("@dcl/ecs").Entity, componentIds: number[], entityEnumId?: number | undefined) => void, parentEntity: (entity: import("@dcl/ecs").Entity, parent: import("@dcl/ecs").Entity) => void, getParent: (child: import("@dcl/ecs").Entity) => import("@dcl/ecs").Entity | undefined, myProfile: import("./message-bus-sync").IProfile, removeParent: (entity: import("@dcl/ecs").Entity) => void, getFirstChild: (entity: import("@dcl/ecs").Entity) => import("@dcl/ecs").Entity;
2
- export { getFirstChild, getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent };
1
+ declare const getChildren: (parent: import("@dcl/ecs").Entity) => Iterable<import("@dcl/ecs").Entity>, syncEntity: (entityId: import("@dcl/ecs").Entity, componentIds: number[], entityEnumId?: number | undefined) => void, parentEntity: (entity: import("@dcl/ecs").Entity, parent: import("@dcl/ecs").Entity) => void, getParent: (child: import("@dcl/ecs").Entity) => import("@dcl/ecs").Entity | undefined, myProfile: import("./message-bus-sync").IProfile, removeParent: (entity: import("@dcl/ecs").Entity) => void, getFirstChild: (entity: import("@dcl/ecs").Entity) => import("@dcl/ecs").Entity, isStateSyncronized: () => boolean;
2
+ export { getFirstChild, getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, isStateSyncronized };
package/network/index.js CHANGED
@@ -3,6 +3,6 @@ import { engine } from '@dcl/ecs';
3
3
  import { addSyncTransport } from './message-bus-sync';
4
4
  import { getUserData } from '~system/UserIdentity';
5
5
  // initialize sync transport for sdk engine
6
- const { getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, getFirstChild } = addSyncTransport(engine, sendBinary, getUserData);
7
- export { getFirstChild, getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent };
8
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbmV0d29yay9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0NBQWtDLENBQUE7QUFDN0QsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUNqQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQTtBQUNyRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUE7QUFFbEQsMkNBQTJDO0FBQzNDLE1BQU0sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsR0FBRyxnQkFBZ0IsQ0FDbkgsTUFBTSxFQUNOLFVBQVUsRUFDVixXQUFXLENBQ1osQ0FBQTtBQUVELE9BQU8sRUFBRSxhQUFhLEVBQUUsV0FBVyxFQUFFLFVBQVUsRUFBRSxZQUFZLEVBQUUsU0FBUyxFQUFFLFNBQVMsRUFBRSxZQUFZLEVBQUUsQ0FBQSIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IHNlbmRCaW5hcnkgfSBmcm9tICd+c3lzdGVtL0NvbW11bmljYXRpb25zQ29udHJvbGxlcidcbmltcG9ydCB7IGVuZ2luZSB9IGZyb20gJ0BkY2wvZWNzJ1xuaW1wb3J0IHsgYWRkU3luY1RyYW5zcG9ydCB9IGZyb20gJy4vbWVzc2FnZS1idXMtc3luYydcbmltcG9ydCB7IGdldFVzZXJEYXRhIH0gZnJvbSAnfnN5c3RlbS9Vc2VySWRlbnRpdHknXG5cbi8vIGluaXRpYWxpemUgc3luYyB0cmFuc3BvcnQgZm9yIHNkayBlbmdpbmVcbmNvbnN0IHsgZ2V0Q2hpbGRyZW4sIHN5bmNFbnRpdHksIHBhcmVudEVudGl0eSwgZ2V0UGFyZW50LCBteVByb2ZpbGUsIHJlbW92ZVBhcmVudCwgZ2V0Rmlyc3RDaGlsZCB9ID0gYWRkU3luY1RyYW5zcG9ydChcbiAgZW5naW5lLFxuICBzZW5kQmluYXJ5LFxuICBnZXRVc2VyRGF0YVxuKVxuXG5leHBvcnQgeyBnZXRGaXJzdENoaWxkLCBnZXRDaGlsZHJlbiwgc3luY0VudGl0eSwgcGFyZW50RW50aXR5LCBnZXRQYXJlbnQsIG15UHJvZmlsZSwgcmVtb3ZlUGFyZW50IH1cbiJdfQ==
6
+ const { getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, getFirstChild, isStateSyncronized } = addSyncTransport(engine, sendBinary, getUserData);
7
+ export { getFirstChild, getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, isStateSyncronized };
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvbmV0d29yay9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sa0NBQWtDLENBQUE7QUFDN0QsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUNqQyxPQUFPLEVBQUUsZ0JBQWdCLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQTtBQUNyRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sc0JBQXNCLENBQUE7QUFFbEQsMkNBQTJDO0FBQzNDLE1BQU0sRUFBRSxXQUFXLEVBQUUsVUFBVSxFQUFFLFlBQVksRUFBRSxTQUFTLEVBQUUsU0FBUyxFQUFFLFlBQVksRUFBRSxhQUFhLEVBQUUsa0JBQWtCLEVBQUUsR0FDcEgsZ0JBQWdCLENBQUMsTUFBTSxFQUFFLFVBQVUsRUFBRSxXQUFXLENBQUMsQ0FBQTtBQUVuRCxPQUFPLEVBQUUsYUFBYSxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsWUFBWSxFQUFFLFNBQVMsRUFBRSxTQUFTLEVBQUUsWUFBWSxFQUFFLGtCQUFrQixFQUFFLENBQUEiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBzZW5kQmluYXJ5IH0gZnJvbSAnfnN5c3RlbS9Db21tdW5pY2F0aW9uc0NvbnRyb2xsZXInXG5pbXBvcnQgeyBlbmdpbmUgfSBmcm9tICdAZGNsL2VjcydcbmltcG9ydCB7IGFkZFN5bmNUcmFuc3BvcnQgfSBmcm9tICcuL21lc3NhZ2UtYnVzLXN5bmMnXG5pbXBvcnQgeyBnZXRVc2VyRGF0YSB9IGZyb20gJ35zeXN0ZW0vVXNlcklkZW50aXR5J1xuXG4vLyBpbml0aWFsaXplIHN5bmMgdHJhbnNwb3J0IGZvciBzZGsgZW5naW5lXG5jb25zdCB7IGdldENoaWxkcmVuLCBzeW5jRW50aXR5LCBwYXJlbnRFbnRpdHksIGdldFBhcmVudCwgbXlQcm9maWxlLCByZW1vdmVQYXJlbnQsIGdldEZpcnN0Q2hpbGQsIGlzU3RhdGVTeW5jcm9uaXplZCB9ID1cbiAgYWRkU3luY1RyYW5zcG9ydChlbmdpbmUsIHNlbmRCaW5hcnksIGdldFVzZXJEYXRhKVxuXG5leHBvcnQgeyBnZXRGaXJzdENoaWxkLCBnZXRDaGlsZHJlbiwgc3luY0VudGl0eSwgcGFyZW50RW50aXR5LCBnZXRQYXJlbnQsIG15UHJvZmlsZSwgcmVtb3ZlUGFyZW50LCBpc1N0YXRlU3luY3Jvbml6ZWQgfVxuIl19
@@ -8,6 +8,7 @@ export type IProfile = {
8
8
  };
9
9
  export declare function addSyncTransport(engine: IEngine, sendBinary: (msg: SendBinaryRequest) => Promise<SendBinaryResponse>, getUserData: (value: GetUserDataRequest) => Promise<GetUserDataResponse>): {
10
10
  myProfile: IProfile;
11
+ isStateSyncronized: () => boolean;
11
12
  syncEntity: (entityId: import("@dcl/ecs").Entity, componentIds: number[], entityEnumId?: number | undefined) => void;
12
13
  getChildren: (parent: import("@dcl/ecs").Entity) => Iterable<import("@dcl/ecs").Entity>;
13
14
  getParent: (child: import("@dcl/ecs").Entity) => import("@dcl/ecs").Entity | undefined;
@@ -1,4 +1,4 @@
1
- import { RealmInfo } from '@dcl/ecs';
1
+ import { RealmInfo, PlayerIdentityData } from '@dcl/ecs';
2
2
  import { syncFilter } from './filter';
3
3
  import { engineToCrdt } from './state';
4
4
  import { BinaryMessageBus, CommsMessage, decodeString, encodeString } from './binary-message-bus';
@@ -22,6 +22,8 @@ export function addSyncTransport(engine, sendBinary, getUserData) {
22
22
  pendingMessageBusMessagesToSend.length = 0;
23
23
  return messages;
24
24
  }
25
+ const players = definePlayerHelper(engine);
26
+ let stateIsSyncronized = false;
25
27
  let transportInitialzed = false;
26
28
  // Add Sync Transport
27
29
  const transport = {
@@ -41,55 +43,89 @@ export function addSyncTransport(engine, sendBinary, getUserData) {
41
43
  };
42
44
  engine.addTransport(transport);
43
45
  // End add sync transport
44
- // If we dont have any state initialized, and recieve a state message.
46
+ // Receive & Process CRDT_STATE
45
47
  binaryMessageBus.on(CommsMessage.RES_CRDT_STATE, (value) => {
46
48
  const { sender, data } = decodeCRDTState(value);
47
49
  if (sender !== myProfile.userId)
48
50
  return;
49
51
  DEBUG_NETWORK_MESSAGES() && console.log('[Processing CRDT State]', data.byteLength);
50
52
  transport.onmessage(data);
53
+ stateIsSyncronized = true;
51
54
  });
52
- binaryMessageBus.on(CommsMessage.REQ_CRDT_STATE, (message, userId) => {
55
+ // Answer to REQ_CRDT_STATE
56
+ binaryMessageBus.on(CommsMessage.REQ_CRDT_STATE, async (message, userId) => {
57
+ console.log(`Sending CRDT State to: ${userId}`);
53
58
  transport.onmessage(message);
54
59
  binaryMessageBus.emit(CommsMessage.RES_CRDT_STATE, encodeCRDTState(userId, engineToCrdt(engine)));
55
60
  });
56
- const players = definePlayerHelper(engine);
57
- let requestCrdtStateWhenConnected = false;
58
- players.onEnterScene((player) => {
59
- DEBUG_NETWORK_MESSAGES() && console.log('[onEnterScene]', player.userId);
60
- if (player.userId === myProfile.userId && !requestCrdtStateWhenConnected) {
61
- if (RealmInfo.getOrNull(engine.RootEntity)?.isConnectedSceneRoom) {
62
- DEBUG_NETWORK_MESSAGES() && console.log('Requesting state');
63
- binaryMessageBus.emit(CommsMessage.REQ_CRDT_STATE, engineToCrdt(engine));
61
+ // Process CRDT messages here
62
+ binaryMessageBus.on(CommsMessage.CRDT, (value) => {
63
+ DEBUG_NETWORK_MESSAGES() &&
64
+ console.log(Array.from(serializeCrdtMessages('[NetworkMessage received]:', value, engine)));
65
+ transport.onmessage(value);
66
+ });
67
+ async function requestState(retryCount = 1) {
68
+ let players = Array.from(engine.getEntitiesWith(PlayerIdentityData));
69
+ DEBUG_NETWORK_MESSAGES() && console.log(`Requesting state. Players connected: ${players.length - 1}`);
70
+ if (!RealmInfo.getOrNull(engine.RootEntity)?.isConnectedSceneRoom) {
71
+ DEBUG_NETWORK_MESSAGES() && console.log(`Aborting Requesting state?. Disconnected`);
72
+ return;
73
+ }
74
+ binaryMessageBus.emit(CommsMessage.REQ_CRDT_STATE, engineToCrdt(engine));
75
+ // Wait ~5s for the response.
76
+ await sleep(5000);
77
+ players = Array.from(engine.getEntitiesWith(PlayerIdentityData));
78
+ if (!stateIsSyncronized) {
79
+ if (players.length > 1 && retryCount <= 2) {
80
+ DEBUG_NETWORK_MESSAGES() &&
81
+ console.log(`Requesting state again ${retryCount} (no response). Players connected: ${players.length - 1}`);
82
+ void requestState(retryCount + 1);
64
83
  }
65
84
  else {
66
- DEBUG_NETWORK_MESSAGES() && console.log('Waiting to be conneted');
67
- requestCrdtStateWhenConnected = true;
85
+ DEBUG_NETWORK_MESSAGES() && console.log('No active players. State syncronized');
86
+ stateIsSyncronized = true;
68
87
  }
69
88
  }
89
+ }
90
+ players.onEnterScene((player) => {
91
+ DEBUG_NETWORK_MESSAGES() && console.log('[onEnterScene]', player.userId);
70
92
  });
93
+ // Asks for the REQ_CRDT_STATE when its connected to comms
71
94
  RealmInfo.onChange(engine.RootEntity, (value) => {
72
- if (value?.isConnectedSceneRoom && requestCrdtStateWhenConnected) {
73
- DEBUG_NETWORK_MESSAGES() && console.log('Requesting state.');
74
- requestCrdtStateWhenConnected = false;
75
- binaryMessageBus.emit(CommsMessage.REQ_CRDT_STATE, engineToCrdt(engine));
95
+ if (!value?.isConnectedSceneRoom) {
96
+ DEBUG_NETWORK_MESSAGES() && console.log('Disconnected from comms');
97
+ stateIsSyncronized = false;
98
+ }
99
+ if (value?.isConnectedSceneRoom) {
100
+ DEBUG_NETWORK_MESSAGES() && console.log('Connected to comms');
101
+ }
102
+ if (value?.isConnectedSceneRoom && !stateIsSyncronized) {
103
+ void requestState();
76
104
  }
77
105
  });
78
106
  players.onLeaveScene((userId) => {
79
107
  DEBUG_NETWORK_MESSAGES() && console.log('[onLeaveScene]', userId);
80
- if (userId === myProfile.userId) {
81
- requestCrdtStateWhenConnected = false;
82
- }
83
- });
84
- // Process CRDT messages here
85
- binaryMessageBus.on(CommsMessage.CRDT, (value) => {
86
- DEBUG_NETWORK_MESSAGES() &&
87
- console.log(Array.from(serializeCrdtMessages('[NetworkMessage received]:', value, engine)));
88
- transport.onmessage(value);
89
108
  });
109
+ function isStateSyncronized() {
110
+ return stateIsSyncronized;
111
+ }
112
+ function sleep(ms) {
113
+ return new Promise((resolve) => {
114
+ let timer = 0;
115
+ function sleepSystem(dt) {
116
+ timer += dt;
117
+ if (timer * 1000 >= ms) {
118
+ engine.removeSystem(sleepSystem);
119
+ resolve();
120
+ }
121
+ }
122
+ engine.addSystem(sleepSystem);
123
+ });
124
+ }
90
125
  return {
91
126
  ...entityDefinitions,
92
- myProfile
127
+ myProfile,
128
+ isStateSyncronized
93
129
  };
94
130
  }
95
131
  /**
@@ -121,4 +157,4 @@ function encodeCRDTState(address, data) {
121
157
  serializedMessage.set(data, addressBuffer.byteLength + 1);
122
158
  return serializedMessage;
123
159
  }
124
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVzc2FnZS1idXMtc3luYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9uZXR3b3JrL21lc3NhZ2UtYnVzLXN5bmMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFzQixTQUFTLEVBQUUsTUFBTSxVQUFVLENBQUE7QUFHeEQsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUNyQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQ3RDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxZQUFZLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxNQUFNLHNCQUFzQixDQUFBO0FBQ2pHLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxTQUFTLENBQUE7QUFDdEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUV4QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDL0MsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sK0JBQStCLENBQUE7QUFHckUsK0NBQStDO0FBQy9DLE1BQU0sVUFBVSxnQkFBZ0IsQ0FDOUIsTUFBZSxFQUNmLFVBQW1FLEVBQ25FLFdBQXdFO0lBRXhFLE1BQU0sc0JBQXNCLEdBQUcsR0FBRyxFQUFFLENBQUUsVUFBa0IsQ0FBQyxzQkFBc0IsSUFBSSxJQUFJLENBQUE7SUFDdkYsZUFBZTtJQUNmLE1BQU0sU0FBUyxHQUFhLEVBQWMsQ0FBQTtJQUMxQyxZQUFZLENBQUMsU0FBVSxFQUFFLFdBQVcsQ0FBQyxDQUFBO0lBRXJDLGVBQWU7SUFDZixNQUFNLGlCQUFpQixHQUFHLFdBQVcsQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUE7SUFFeEQsbUVBQW1FO0lBQ25FLE1BQU0sK0JBQStCLEdBQWlCLEVBQUUsQ0FBQTtJQUN4RCxNQUFNLGdCQUFnQixHQUFHLGdCQUFnQixDQUFDLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQywrQkFB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VSxDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQ3ZELGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxJQUFJLFVBQVUsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFBO0lBQ3BFLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDdkMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxhQUFhLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBQ3pELE9BQU8saUJBQWlCLENBQUE7QUFDMUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IElFbmdpbmUsIFRyYW5zcG9ydCwgUmVhbG1JbmZvIH0gZnJvbSAnQGRjbC9lY3MnXG5pbXBvcnQgeyB0eXBlIFNlbmRCaW5hcnlSZXF1ZXN0LCB0eXBlIFNlbmRCaW5hcnlSZXNwb25zZSB9IGZyb20gJ35zeXN0ZW0vQ29tbXVuaWNhdGlvbnNDb250cm9sbGVyJ1xuXG5pbXBvcnQgeyBzeW5jRmlsdGVyIH0gZnJvbSAnLi9maWx0ZXInXG5pbXBvcnQgeyBlbmdpbmVUb0NyZHQgfSBmcm9tICcuL3N0YXRlJ1xuaW1wb3J0IHsgQmluYXJ5TWVzc2FnZUJ1cywgQ29tbXNNZXNzYWdlLCBkZWNvZGVTdHJpbmcsIGVuY29kZVN0cmluZyB9IGZyb20gJy4vYmluYXJ5LW1lc3NhZ2UtYnVzJ1xuaW1wb3J0IHsgZmV0Y2hQcm9maWxlIH0gZnJvbSAnLi91dGlscydcbmltcG9ydCB7IGVudGl0eVV0aWxzIH0gZnJvbSAnLi9lbnRpdGllcydcbmltcG9ydCB7IEdldFVzZXJEYXRhUmVxdWVzdCwgR2V0VXNlckRhdGFSZXNwb25zZSB9IGZyb20gJ35zeXN0ZW0vVXNlcklkZW50aXR5J1xuaW1wb3J0IHsgZGVmaW5lUGxheWVySGVscGVyIH0gZnJvbSAnLi4vcGxheWVycydcbmltcG9ydCB7IHNlcmlhbGl6ZUNyZHRNZXNzYWdlcyB9IGZyb20gJy4uL2ludGVybmFsL3RyYW5zcG9ydHMvbG9nZ2VyJ1xuXG5leHBvcnQgdHlwZSBJUHJvZmlsZSA9IHsgbmV0d29ya0lkOiBudW1iZXI7IHVzZXJJZDogc3RyaW5nIH1cbi8vIHVzZXIgdGhhdCB3ZSBhc2tlZCBmb3IgdGhlIGluaXRhbCBjcmR0IHN0YXRlXG5leHBvcnQgZnVuY3Rpb24gYWRkU3luY1RyYW5zcG9ydChcbiAgZW5naW5lOiBJRW5naW5lLFxuICBzZW5kQmluYXJ5OiAobXNnOiBTZW5kQmluYXJ5UmVxdWVzdCkgPT4gUHJvbWlzZTxTZW5kQmluYXJ5UmVzcG9uc2U+LFxuICBnZXRVc2VyRGF0YTogKHZhbHVlOiBHZXRVc2VyRGF0YVJlcXVlc3QpID0+IFByb21pc2U8R2V0VXNlckRhdGFSZXNwb25zZT5cbikge1xuICBjb25zdCBERUJVR19ORVRXT1JLX01FU1NBR0VTID0gKCkgPT4gKGdsb2JhbFRoaXMgYXMgYW55KS5ERUJVR19ORVRXT1JLX01FU1NBR0VTID8/IHRydWVcbiAgLy8gUHJvZmlsZSBJbmZvXG4gIGNvbnN0IG15UHJvZmlsZTogSVByb2ZpbGUgPSB7fSBhcyBJUHJvZmlsZVxuICBmZXRjaFByb2ZpbGUobXlQcm9maWxlISwgZ2V0VXNlckRhdGEpXG5cbiAgLy8gRW50aXR5IHV0aWxzXG4gIGNvbnN0IGVudGl0eURlZmluaXRpb25zID0gZW50aXR5VXRpbHMoZW5naW5lLCBteVByb2ZpbGUpXG5cbiAgLy8gTGlzdCBvZiBNZXNzYWdlQnVzcyBtZXNzc2FnZXMgdG8gYmUgc2VudCBvbiBldmVyeSBmcmFtZSB0byBjb21tc1xuICBjb25zdCBwZW5kaW5nTWVzc2FnZUJ1c01lc3NhZ2VzVG9TZW5kOiBVaW50OEFycmF5W10gPSBbXVxuICBjb25zdCBiaW5hcnlNZXNzYWdlQnVzID0gQmluYXJ5TWVzc2FnZUJ1cygobWVzc2FnZSkgPT4gcGVuZGluZ01lc3NhZ2VCdXNNZXNzYWdlc1RvU2VuZC5wdXNoKG1lc3NhZ2UpKVxuXG4gIGZ1bmN0aW9uIGdldE1lc3NhZ2VzVG9TZW5kKCkge1xuICAgIGNvbnN0IG1lc3NhZ2VzID0gWy4uLnBlbmRpbmdNZXNzYWdlQnVzTWVzc2FnZXNUb1NlbmRdXG4gICAgcGVuZGluZ01lc3NhZ2VCdXNNZXNzYWdlc1RvU2VuZC5sZW5ndGggPSAwXG4gICAgcmV0dXJuIG1lc3NhZ2VzXG4gIH1cblxuICBsZXQgdHJhbnNwb3J0SW5pdGlhbHplZCA9IGZhbHNlXG4gIC8vIEFkZCBTeW5jIFRyYW5zcG9ydFxuICBjb25zdCB0cmFuc3BvcnQ6IFRyYW5zcG9ydCA9IHtcbiAgICBmaWx0ZXI6IHN5bmNGaWx0ZXIoZW5naW5lKSxcbiAgICBzZW5kOiBhc3luYyAobWVzc2FnZTogVWludDhBcnJheSkgPT4ge1xuICAgICAgaWYgKG1lc3NhZ2UuYnl0ZUxlbmd0aCAmJiB0cmFuc3BvcnRJbml0aWFsemVkKSB7XG4gICAgICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJlxuICAgICAgICAgIGNvbnNvbGUubG9nKC4uLkFycmF5LmZyb20oc2VyaWFsaXplQ3JkdE1lc3NhZ2VzKCdbTmV0d29ya01lc3NhZ2Ugc2VudF06JywgbWVzc2FnZSwgZW5naW5lKSkpXG4gICAgICAgIGJpbmFyeU1lc3NhZ2VCdXMuZW1pdChDb21tc01lc3NhZ2UuQ1JEVCwgbWVzc2FnZSlcbiAgICAgIH1cbiAgICAgIGNvbnN0IG1lc3NhZ2VzID0gZ2V0TWVzc2FnZXNUb1NlbmQoKVxuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBzZW5kQmluYXJ5KHsgZGF0YTogbWVzc2FnZXMgfSlcbiAgICAgIGJpbmFyeU1lc3NhZ2VCdXMuX19wcm9jZXNzTWVzc2FnZXMocmVzcG9uc2UuZGF0YSlcbiAgICAgIHRyYW5zcG9ydEluaXRpYWx6ZWQgPSB0cnVlXG4gICAgfSxcbiAgICB0eXBlOiAnbmV0d29yaydcbiAgfVxuICBlbmdpbmUuYWRkVHJhbnNwb3J0KHRyYW5zcG9ydClcbiAgLy8gRW5kIGFkZCBzeW5jIHRyYW5zcG9ydFxuXG4gIC8vIElmIHdlIGRvbnQgaGF2ZSBhbnkgc3RhdGUgaW5pdGlhbGl6ZWQsIGFuZCByZWNpZXZlIGEgc3RhdGUgbWVzc2FnZS5cbiAgYmluYXJ5TWVzc2FnZUJ1cy5vbihDb21tc01lc3NhZ2UuUkVTX0NSRFRfU1RBVEUsICh2YWx1ZSkgPT4ge1xuICAgIGNvbnN0IHsgc2VuZGVyLCBkYXRhIH0gPSBkZWNvZGVDUkRUU3RhdGUodmFsdWUpXG4gICAgaWYgKHNlbmRlciAhPT0gbXlQcm9maWxlLnVzZXJJZCkgcmV0dXJuXG4gICAgREVCVUdfTkVUV09SS19NRVNTQUdFUygpICYmIGNvbnNvbGUubG9nKCdbUHJvY2Vzc2luZyBDUkRUIFN0YXRlXScsIGRhdGEuYnl0ZUxlbmd0aClcbiAgICB0cmFuc3BvcnQub25tZXNzYWdlIShkYXRhKVxuICB9KVxuXG4gIGJpbmFyeU1lc3NhZ2VCdXMub24oQ29tbXNNZXNzYWdlLlJFUV9DUkRUX1NUQVRFLCAobWVzc2FnZSwgdXNlcklkKSA9PiB7XG4gICAgdHJhbnNwb3J0Lm9ubWVzc2FnZSEobWVzc2FnZSlcbiAgICBiaW5hcnlNZXNzYWdlQnVzLmVtaXQoQ29tbXNNZXNzYWdlLlJFU19DUkRUX1NUQVRFLCBlbmNvZGVDUkRUU3RhdGUodXNlcklkLCBlbmdpbmVUb0NyZHQoZW5naW5lKSkpXG4gIH0pXG5cbiAgY29uc3QgcGxheWVycyA9IGRlZmluZVBsYXllckhlbHBlcihlbmdpbmUpXG5cbiAgbGV0IHJlcXVlc3RDcmR0U3RhdGVXaGVuQ29ubmVjdGVkID0gZmFsc2VcblxuICBwbGF5ZXJzLm9uRW50ZXJTY2VuZSgocGxheWVyKSA9PiB7XG4gICAgREVCVUdfTkVUV09SS19NRVNTQUdFUygpICYmIGNvbnNvbGUubG9nKCdbb25FbnRlclNjZW5lXScsIHBsYXllci51c2VySWQpXG4gICAgaWYgKHBsYXllci51c2VySWQgPT09IG15UHJvZmlsZS51c2VySWQgJiYgIXJlcXVlc3RDcmR0U3RhdGVXaGVuQ29ubmVjdGVkKSB7XG4gICAgICBpZiAoUmVhbG1JbmZvLmdldE9yTnVsbChlbmdpbmUuUm9vdEVudGl0eSk/LmlzQ29ubmVjdGVkU2NlbmVSb29tKSB7XG4gICAgICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJiBjb25zb2xlLmxvZygnUmVxdWVzdGluZyBzdGF0ZScpXG4gICAgICAgIGJpbmFyeU1lc3NhZ2VCdXMuZW1pdChDb21tc01lc3NhZ2UuUkVRX0NSRFRfU1RBVEUsIGVuZ2luZVRvQ3JkdChlbmdpbmUpKVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgREVCVUdfTkVUV09SS19NRVNTQUdFUygpICYmIGNvbnNvbGUubG9nKCdXYWl0aW5nIHRvIGJlIGNvbm5ldGVkJylcbiAgICAgICAgcmVxdWVzdENyZHRTdGF0ZVdoZW5Db25uZWN0ZWQgPSB0cnVlXG4gICAgICB9XG4gICAgfVxuICB9KVxuXG4gIFJlYWxtSW5mby5vbkNoYW5nZShlbmdpbmUuUm9vdEVudGl0eSwgKHZhbHVlKSA9PiB7XG4gICAgaWYgKHZhbHVlPy5pc0Nvbm5lY3RlZFNjZW5lUm9vbSAmJiByZXF1ZXN0Q3JkdFN0YXRlV2hlbkNvbm5lY3RlZCkge1xuICAgICAgREVCVUdfTkVUV09SS19NRVNTQUdFUygpICYmIGNvbnNvbGUubG9nKCdSZXF1ZXN0aW5nIHN0YXRlLicpXG4gICAgICByZXF1ZXN0Q3JkdFN0YXRlV2hlbkNvbm5lY3RlZCA9IGZhbHNlXG4gICAgICBiaW5hcnlNZXNzYWdlQnVzLmVtaXQoQ29tbXNNZXNzYWdlLlJFUV9DUkRUX1NUQVRFLCBlbmdpbmVUb0NyZHQoZW5naW5lKSlcbiAgICB9XG4gIH0pXG5cbiAgcGxheWVycy5vbkxlYXZlU2NlbmUoKHVzZXJJZCkgPT4ge1xuICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJiBjb25zb2xlLmxvZygnW29uTGVhdmVTY2VuZV0nLCB1c2VySWQpXG4gICAgaWYgKHVzZXJJZCA9PT0gbXlQcm9maWxlLnVzZXJJZCkge1xuICAgICAgcmVxdWVzdENyZHRTdGF0ZVdoZW5Db25uZWN0ZWQgPSBmYWxzZVxuICAgIH1cbiAgfSlcblxuICAvLyBQcm9jZXNzIENSRFQgbWVzc2FnZXMgaGVyZVxuICBiaW5hcnlNZXNzYWdlQnVzLm9uKENvbW1zTWVzc2FnZS5DUkRULCAodmFsdWUpID0+IHtcbiAgICBERUJVR19ORVRXT1JLX01FU1NBR0VTKCkgJiZcbiAgICAgIGNvbnNvbGUubG9nKEFycmF5LmZyb20oc2VyaWFsaXplQ3JkdE1lc3NhZ2VzKCdbTmV0d29ya01lc3NhZ2UgcmVjZWl2ZWRdOicsIHZhbHVlLCBlbmdpbmUpKSlcbiAgICB0cmFuc3BvcnQub25tZXNzYWdlISh2YWx1ZSlcbiAgfSlcblxuICByZXR1cm4ge1xuICAgIC4uLmVudGl0eURlZmluaXRpb25zLFxuICAgIG15UHJvZmlsZVxuICB9XG59XG5cbi8qKlxuICogTWVzc2FnZXMgUHJvdG9jb2wgRW5jb2RpbmdcbiAqXG4gKiBDUkRUOiBQbGFpbiBVaW50OEFycmF5XG4gKlxuICogQ1JEVF9TVEFURV9SRVMgeyBzZW5kZXI6IHN0cmluZywgZGF0YTogVWludDhBcnJheX1cbiAqL1xuZnVuY3Rpb24gZGVjb2RlQ1JEVFN0YXRlKGRhdGE6IFVpbnQ4QXJyYXkpIHtcbiAgbGV0IG9mZnNldCA9IDBcbiAgY29uc3QgciA9IG5ldyBVaW50OEFycmF5KGRhdGEpXG4gIGNvbnN0IHZpZXcgPSBuZXcgRGF0YVZpZXcoci5idWZmZXIpXG4gIGNvbnN0IHNlbmRlckxlbmd0aCA9IHZpZXcuZ2V0VWludDgob2Zmc2V0KVxuICBvZmZzZXQgKz0gMVxuICBjb25zdCBzZW5kZXIgPSBkZWNvZGVTdHJpbmcoZGF0YS5zdWJhcnJheSgxLCBzZW5kZXJMZW5ndGggKyAxKSlcbiAgb2Zmc2V0ICs9IHNlbmRlckxlbmd0aFxuICBjb25zdCBzdGF0ZSA9IHIuc3ViYXJyYXkob2Zmc2V0KVxuXG4gIHJldHVybiB7IHNlbmRlciwgZGF0YTogc3RhdGUgfVxufVxuXG5mdW5jdGlvbiBlbmNvZGVDUkRUU3RhdGUoYWRkcmVzczogc3RyaW5nLCBkYXRhOiBVaW50OEFycmF5KSB7XG4gIC8vIGFkZHJlc3MgdG8gdWludDhhcnJheVxuICBjb25zdCBhZGRyZXNzQnVmZmVyID0gZW5jb2RlU3RyaW5nKGFkZHJlc3MpXG4gIGNvbnN0IGFkZHJlc3NPZmZzZXQgPSAxXG4gIGNvbnN0IG1lc3NhZ2VMZW5ndGggPSBhZGRyZXNzT2Zmc2V0ICsgYWRkcmVzc0J1ZmZlci5ieXRlTGVuZ3RoICsgZGF0YS5ieXRlTGVuZ3RoXG5cbiAgY29uc3Qgc2VyaWFsaXplZE1lc3NhZ2UgPSBuZXcgVWludDhBcnJheShtZXNzYWdlTGVuZ3RoKVxuICBzZXJpYWxpemVkTWVzc2FnZS5zZXQobmV3IFVpbnQ4QXJyYXkoW2FkZHJlc3NCdWZmZXIuYnl0ZUxlbmd0aF0pLCAwKVxuICBzZXJpYWxpemVkTWVzc2FnZS5zZXQoYWRkcmVzc0J1ZmZlciwgMSlcbiAgc2VyaWFsaXplZE1lc3NhZ2Uuc2V0KGRhdGEsIGFkZHJlc3NCdWZmZXIuYnl0ZUxlbmd0aCArIDEpXG4gIHJldHVybiBzZXJpYWxpemVkTWVzc2FnZVxufVxuIl19
160
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibWVzc2FnZS1idXMtc3luYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9uZXR3b3JrL21lc3NhZ2UtYnVzLXN5bmMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFzQixTQUFTLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxVQUFVLENBQUE7QUFHNUUsT0FBTyxFQUFFLFVBQVUsRUFBRSxNQUFNLFVBQVUsQ0FBQTtBQUNyQyxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sU0FBUyxDQUFBO0FBQ3RDLE9BQU8sRUFBRSxnQkFBZ0IsRUFBRSxZQUFZLEVBQUUsWUFBWSxFQUFFLFlBQVksRUFBRSxNQUFNLHNCQUFzQixDQUFBO0FBQ2pHLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSxTQUFTLENBQUE7QUFDdEMsT0FBTyxFQUFFLFdBQVcsRUFBRSxNQUFNLFlBQVksQ0FBQTtBQUV4QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSxZQUFZLENBQUE7QUFDL0MsT0FBTyxFQUFFLHFCQUFxQixFQUFFLE1BQU0sK0JBQStCLENBQUE7QUFHckUsK0NBQStDO0FBQy9DLE1BQU0sVUFBVSxnQkFBZ0IsQ0FDOUIsTUFBZSxFQUNmLFVBQW1FLEVBQ25FLFdBQXdFO0lBRXhFLE1BQU0sc0JBQXNCLEdBQUcsR0FBRyxFQUFFLENBQUUsVUFBa0IsQ0FBQyxzQkFBc0IsSUFBSSxJQUFJLENBQUE7SUFDdkYsZUFBZTtJQUNmLE1BQU0sU0FBUyxHQUFhLEVBQWMsQ0FBQTtJQUMxQyxZQUFZLENBQUMsU0FBVSxFQUFFLFdBQVcsQ0FBQyxDQUFBO0lBRXJDLGVBQWU7SUFDZixNQUFNLGlCQUFpQixHQUFHLFdBQVcsQ0FBQyxNQUFNLEVBQUUsU0FBUyxDQUFDLENBQUE7SUFFeEQsbUVBQW1FO0lBQ25FLE1BQU0sK0JBQStCLEdBQWlCLEVBQUUsQ0FBQTtJQUN4RCxNQUFNLGdCQUFnQixHQUFHLGdCQUFnQixDQUFDLENBQUMsT0FBTyxFQUFFLEVB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SUFDeEQsd0JBQXdCO0lBQ3hCLE1BQU0sYUFBYSxHQUFHLFlBQVksQ0FBQyxPQUFPLENBQUMsQ0FBQTtJQUMzQyxNQUFNLGFBQWEsR0FBRyxDQUFDLENBQUE7SUFDdkIsTUFBTSxhQUFhLEdBQUcsYUFBYSxHQUFHLGFBQWEsQ0FBQyxVQUFVLEdBQUcsSUFBSSxDQUFDLFVBQVUsQ0FBQTtJQUVoRixNQUFNLGlCQUFpQixHQUFHLElBQUksVUFBVSxDQUFDLGFBQWEsQ0FBQyxDQUFBO0lBQ3ZELGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxJQUFJLFVBQVUsQ0FBQyxDQUFDLGFBQWEsQ0FBQyxVQUFVLENBQUMsQ0FBQyxFQUFFLENBQUMsQ0FBQyxDQUFBO0lBQ3BFLGlCQUFpQixDQUFDLEdBQUcsQ0FBQyxhQUFhLEVBQUUsQ0FBQyxDQUFDLENBQUE7SUFDdkMsaUJBQWlCLENBQUMsR0FBRyxDQUFDLElBQUksRUFBRSxhQUFhLENBQUMsVUFBVSxHQUFHLENBQUMsQ0FBQyxDQUFBO0lBQ3pELE9BQU8saUJBQWlCLENBQUE7QUFDMUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IElFbmdpbmUsIFRyYW5zcG9ydCwgUmVhbG1JbmZvLCBQbGF5ZXJJZGVudGl0eURhdGEgfSBmcm9tICdAZGNsL2VjcydcbmltcG9ydCB7IHR5cGUgU2VuZEJpbmFyeVJlcXVlc3QsIHR5cGUgU2VuZEJpbmFyeVJlc3BvbnNlIH0gZnJvbSAnfnN5c3RlbS9Db21tdW5pY2F0aW9uc0NvbnRyb2xsZXInXG5cbmltcG9ydCB7IHN5bmNGaWx0ZXIgfSBmcm9tICcuL2ZpbHRlcidcbmltcG9ydCB7IGVuZ2luZVRvQ3JkdCB9IGZyb20gJy4vc3RhdGUnXG5pbXBvcnQgeyBCaW5hcnlNZXNzYWdlQnVzLCBDb21tc01lc3NhZ2UsIGRlY29kZVN0cmluZywgZW5jb2RlU3RyaW5nIH0gZnJvbSAnLi9iaW5hcnktbWVzc2FnZS1idXMnXG5pbXBvcnQgeyBmZXRjaFByb2ZpbGUgfSBmcm9tICcuL3V0aWxzJ1xuaW1wb3J0IHsgZW50aXR5VXRpbHMgfSBmcm9tICcuL2VudGl0aWVzJ1xuaW1wb3J0IHsgR2V0VXNlckRhdGFSZXF1ZXN0LCBHZXRVc2VyRGF0YVJlc3BvbnNlIH0gZnJvbSAnfnN5c3RlbS9Vc2VySWRlbnRpdHknXG5pbXBvcnQgeyBkZWZpbmVQbGF5ZXJIZWxwZXIgfSBmcm9tICcuLi9wbGF5ZXJzJ1xuaW1wb3J0IHsgc2VyaWFsaXplQ3JkdE1lc3NhZ2VzIH0gZnJvbSAnLi4vaW50ZXJuYWwvdHJhbnNwb3J0cy9sb2dnZXInXG5cbmV4cG9ydCB0eXBlIElQcm9maWxlID0geyBuZXR3b3JrSWQ6IG51bWJlcjsgdXNlcklkOiBzdHJpbmcgfVxuLy8gdXNlciB0aGF0IHdlIGFza2VkIGZvciB0aGUgaW5pdGFsIGNyZHQgc3RhdGVcbmV4cG9ydCBmdW5jdGlvbiBhZGRTeW5jVHJhbnNwb3J0KFxuICBlbmdpbmU6IElFbmdpbmUsXG4gIHNlbmRCaW5hcnk6IChtc2c6IFNlbmRCaW5hcnlSZXF1ZXN0KSA9PiBQcm9taXNlPFNlbmRCaW5hcnlSZXNwb25zZT4sXG4gIGdldFVzZXJEYXRhOiAodmFsdWU6IEdldFVzZXJEYXRhUmVxdWVzdCkgPT4gUHJvbWlzZTxHZXRVc2VyRGF0YVJlc3BvbnNlPlxuKSB7XG4gIGNvbnN0IERFQlVHX05FVFdPUktfTUVTU0FHRVMgPSAoKSA9PiAoZ2xvYmFsVGhpcyBhcyBhbnkpLkRFQlVHX05FVFdPUktfTUVTU0FHRVMgPz8gdHJ1ZVxuICAvLyBQcm9maWxlIEluZm9cbiAgY29uc3QgbXlQcm9maWxlOiBJUHJvZmlsZSA9IHt9IGFzIElQcm9maWxlXG4gIGZldGNoUHJvZmlsZShteVByb2ZpbGUhLCBnZXRVc2VyRGF0YSlcblxuICAvLyBFbnRpdHkgdXRpbHNcbiAgY29uc3QgZW50aXR5RGVmaW5pdGlvbnMgPSBlbnRpdHlVdGlscyhlbmdpbmUsIG15UHJvZmlsZSlcblxuICAvLyBMaXN0IG9mIE1lc3NhZ2VCdXNzIG1lc3NzYWdlcyB0byBiZSBzZW50IG9uIGV2ZXJ5IGZyYW1lIHRvIGNvbW1zXG4gIGNvbnN0IHBlbmRpbmdNZXNzYWdlQnVzTWVzc2FnZXNUb1NlbmQ6IFVpbnQ4QXJyYXlbXSA9IFtdXG4gIGNvbnN0IGJpbmFyeU1lc3NhZ2VCdXMgPSBCaW5hcnlNZXNzYWdlQnVzKChtZXNzYWdlKSA9PiBwZW5kaW5nTWVzc2FnZUJ1c01lc3NhZ2VzVG9TZW5kLnB1c2gobWVzc2FnZSkpXG5cbiAgZnVuY3Rpb24gZ2V0TWVzc2FnZXNUb1NlbmQoKSB7XG4gICAgY29uc3QgbWVzc2FnZXMgPSBbLi4ucGVuZGluZ01lc3NhZ2VCdXNNZXNzYWdlc1RvU2VuZF1cbiAgICBwZW5kaW5nTWVzc2FnZUJ1c01lc3NhZ2VzVG9TZW5kLmxlbmd0aCA9IDBcbiAgICByZXR1cm4gbWVzc2FnZXNcbiAgfVxuICBjb25zdCBwbGF5ZXJzID0gZGVmaW5lUGxheWVySGVscGVyKGVuZ2luZSlcblxuICBsZXQgc3RhdGVJc1N5bmNyb25pemVkID0gZmFsc2VcbiAgbGV0IHRyYW5zcG9ydEluaXRpYWx6ZWQgPSBmYWxzZVxuXG4gIC8vIEFkZCBTeW5jIFRyYW5zcG9ydFxuICBjb25zdCB0cmFuc3BvcnQ6IFRyYW5zcG9ydCA9IHtcbiAgICBmaWx0ZXI6IHN5bmNGaWx0ZXIoZW5naW5lKSxcbiAgICBzZW5kOiBhc3luYyAobWVzc2FnZTogVWludDhBcnJheSkgPT4ge1xuICAgICAgaWYgKG1lc3NhZ2UuYnl0ZUxlbmd0aCAmJiB0cmFuc3BvcnRJbml0aWFsemVkKSB7XG4gICAgICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJlxuICAgICAgICAgIGNvbnNvbGUubG9nKC4uLkFycmF5LmZyb20oc2VyaWFsaXplQ3JkdE1lc3NhZ2VzKCdbTmV0d29ya01lc3NhZ2Ugc2VudF06JywgbWVzc2FnZSwgZW5naW5lKSkpXG4gICAgICAgIGJpbmFyeU1lc3NhZ2VCdXMuZW1pdChDb21tc01lc3NhZ2UuQ1JEVCwgbWVzc2FnZSlcbiAgICAgIH1cbiAgICAgIGNvbnN0IG1lc3NhZ2VzID0gZ2V0TWVzc2FnZXNUb1NlbmQoKVxuICAgICAgY29uc3QgcmVzcG9uc2UgPSBhd2FpdCBzZW5kQmluYXJ5KHsgZGF0YTogbWVzc2FnZXMgfSlcbiAgICAgIGJpbmFyeU1lc3NhZ2VCdXMuX19wcm9jZXNzTWVzc2FnZXMocmVzcG9uc2UuZGF0YSlcbiAgICAgIHRyYW5zcG9ydEluaXRpYWx6ZWQgPSB0cnVlXG4gICAgfSxcbiAgICB0eXBlOiAnbmV0d29yaydcbiAgfVxuICBlbmdpbmUuYWRkVHJhbnNwb3J0KHRyYW5zcG9ydClcbiAgLy8gRW5kIGFkZCBzeW5jIHRyYW5zcG9ydFxuXG4gIC8vIFJlY2VpdmUgJiBQcm9jZXNzIENSRFRfU1RBVEVcbiAgYmluYXJ5TWVzc2FnZUJ1cy5vbihDb21tc01lc3NhZ2UuUkVTX0NSRFRfU1RBVEUsICh2YWx1ZSkgPT4ge1xuICAgIGNvbnN0IHsgc2VuZGVyLCBkYXRhIH0gPSBkZWNvZGVDUkRUU3RhdGUodmFsdWUpXG4gICAgaWYgKHNlbmRlciAhPT0gbXlQcm9maWxlLnVzZXJJZCkgcmV0dXJuXG4gICAgREVCVUdfTkVUV09SS19NRVNTQUdFUygpICYmIGNvbnNvbGUubG9nKCdbUHJvY2Vzc2luZyBDUkRUIFN0YXRlXScsIGRhdGEuYnl0ZUxlbmd0aClcbiAgICB0cmFuc3BvcnQub25tZXNzYWdlIShkYXRhKVxuICAgIHN0YXRlSXNTeW5jcm9uaXplZCA9IHRydWVcbiAgfSlcblxuICAvLyBBbnN3ZXIgdG8gUkVRX0NSRFRfU1RBVEVcbiAgYmluYXJ5TWVzc2FnZUJ1cy5vbihDb21tc01lc3NhZ2UuUkVRX0NSRFRfU1RBVEUsIGFzeW5jIChtZXNzYWdlLCB1c2VySWQpID0+IHtcbiAgICBjb25zb2xlLmxvZyhgU2VuZGluZyBDUkRUIFN0YXRlIHRvOiAke3VzZXJJZH1gKVxuICAgIHRyYW5zcG9ydC5vbm1lc3NhZ2UhKG1lc3NhZ2UpXG4gICAgYmluYXJ5TWVzc2FnZUJ1cy5lbWl0KENvbW1zTWVzc2FnZS5SRVNfQ1JEVF9TVEFURSwgZW5jb2RlQ1JEVFN0YXRlKHVzZXJJZCwgZW5naW5lVG9DcmR0KGVuZ2luZSkpKVxuICB9KVxuXG4gIC8vIFByb2Nlc3MgQ1JEVCBtZXNzYWdlcyBoZXJlXG4gIGJpbmFyeU1lc3NhZ2VCdXMub24oQ29tbXNNZXNzYWdlLkNSRFQsICh2YWx1ZSkgPT4ge1xuICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJlxuICAgICAgY29uc29sZS5sb2coQXJyYXkuZnJvbShzZXJpYWxpemVDcmR0TWVzc2FnZXMoJ1tOZXR3b3JrTWVzc2FnZSByZWNlaXZlZF06JywgdmFsdWUsIGVuZ2luZSkpKVxuICAgIHRyYW5zcG9ydC5vbm1lc3NhZ2UhKHZhbHVlKVxuICB9KVxuXG4gIGFzeW5jIGZ1bmN0aW9uIHJlcXVlc3RTdGF0ZShyZXRyeUNvdW50OiBudW1iZXIgPSAxKSB7XG4gICAgbGV0IHBsYXllcnMgPSBBcnJheS5mcm9tKGVuZ2luZS5nZXRFbnRpdGllc1dpdGgoUGxheWVySWRlbnRpdHlEYXRhKSlcbiAgICBERUJVR19ORVRXT1JLX01FU1NBR0VTKCkgJiYgY29uc29sZS5sb2coYFJlcXVlc3Rpbmcgc3RhdGUuIFBsYXllcnMgY29ubmVjdGVkOiAke3BsYXllcnMubGVuZ3RoIC0gMX1gKVxuXG4gICAgaWYgKCFSZWFsbUluZm8uZ2V0T3JOdWxsKGVuZ2luZS5Sb290RW50aXR5KT8uaXNDb25uZWN0ZWRTY2VuZVJvb20pIHtcbiAgICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJiBjb25zb2xlLmxvZyhgQWJvcnRpbmcgUmVxdWVzdGluZyBzdGF0ZT8uIERpc2Nvbm5lY3RlZGApXG4gICAgICByZXR1cm5cbiAgICB9XG5cbiAgICBiaW5hcnlNZXNzYWdlQnVzLmVtaXQoQ29tbXNNZXNzYWdlLlJFUV9DUkRUX1NUQVRFLCBlbmdpbmVUb0NyZHQoZW5naW5lKSlcblxuICAgIC8vIFdhaXQgfjVzIGZvciB0aGUgcmVzcG9uc2UuXG4gICAgYXdhaXQgc2xlZXAoNTAwMClcblxuICAgIHBsYXllcnMgPSBBcnJheS5mcm9tKGVuZ2luZS5nZXRFbnRpdGllc1dpdGgoUGxheWVySWRlbnRpdHlEYXRhKSlcblxuICAgIGlmICghc3RhdGVJc1N5bmNyb25pemVkKSB7XG4gICAgICBpZiAocGxheWVycy5sZW5ndGggPiAxICYmIHJldHJ5Q291bnQgPD0gMikge1xuICAgICAgICBERUJVR19ORVRXT1JLX01FU1NBR0VTKCkgJiZcbiAgICAgICAgICBjb25zb2xlLmxvZyhgUmVxdWVzdGluZyBzdGF0ZSBhZ2FpbiAke3JldHJ5Q291bnR9IChubyByZXNwb25zZSkuIFBsYXllcnMgY29ubmVjdGVkOiAke3BsYXllcnMubGVuZ3RoIC0gMX1gKVxuICAgICAgICB2b2lkIHJlcXVlc3RTdGF0ZShyZXRyeUNvdW50ICsgMSlcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJiBjb25zb2xlLmxvZygnTm8gYWN0aXZlIHBsYXllcnMuIFN0YXRlIHN5bmNyb25pemVkJylcbiAgICAgICAgc3RhdGVJc1N5bmNyb25pemVkID0gdHJ1ZVxuICAgICAgfVxuICAgIH1cbiAgfVxuXG4gIHBsYXllcnMub25FbnRlclNjZW5lKChwbGF5ZXIpID0+IHtcbiAgICBERUJVR19ORVRXT1JLX01FU1NBR0VTKCkgJiYgY29uc29sZS5sb2coJ1tvbkVudGVyU2NlbmVdJywgcGxheWVyLnVzZXJJZClcbiAgfSlcblxuICAvLyBBc2tzIGZvciB0aGUgUkVRX0NSRFRfU1RBVEUgd2hlbiBpdHMgY29ubmVjdGVkIHRvIGNvbW1zXG4gIFJlYWxtSW5mby5vbkNoYW5nZShlbmdpbmUuUm9vdEVudGl0eSwgKHZhbHVlKSA9PiB7XG4gICAgaWYgKCF2YWx1ZT8uaXNDb25uZWN0ZWRTY2VuZVJvb20pIHtcbiAgICAgIERFQlVHX05FVFdPUktfTUVTU0FHRVMoKSAmJiBjb25zb2xlLmxvZygnRGlzY29ubmVjdGVkIGZyb20gY29tbXMnKVxuICAgICAgc3RhdGVJc1N5bmNyb25pemVkID0gZmFsc2VcbiAgICB9XG5cbiAgICBpZiAodmFsdWU/LmlzQ29ubmVjdGVkU2NlbmVSb29tKSB7XG4gICAgICBERUJVR19ORVRXT1JLX01FU1NBR0VTKCkgJiYgY29uc29sZS5sb2coJ0Nvbm5lY3RlZCB0byBjb21tcycpXG4gICAgfVxuXG4gICAgaWYgKHZhbHVlPy5pc0Nvbm5lY3RlZFNjZW5lUm9vbSAmJiAhc3RhdGVJc1N5bmNyb25pemVkKSB7XG4gICAgICB2b2lkIHJlcXVlc3RTdGF0ZSgpXG4gICAgfVxuICB9KVxuXG4gIHBsYXllcnMub25MZWF2ZVNjZW5lKCh1c2VySWQpID0+IHtcbiAgICBERUJVR19ORVRXT1JLX01FU1NBR0VTKCkgJiYgY29uc29sZS5sb2coJ1tvbkxlYXZlU2NlbmVdJywgdXNlcklkKVxuICB9KVxuXG4gIGZ1bmN0aW9uIGlzU3RhdGVTeW5jcm9uaXplZCgpIHtcbiAgICByZXR1cm4gc3RhdGVJc1N5bmNyb25pemVkXG4gIH1cblxuICBmdW5jdGlvbiBzbGVlcChtczogbnVtYmVyKSB7XG4gICAgcmV0dXJuIG5ldyBQcm9taXNlPHZvaWQ+KChyZXNvbHZlKSA9PiB7XG4gICAgICBsZXQgdGltZXIgPSAwXG4gICAgICBmdW5jdGlvbiBzbGVlcFN5c3RlbShkdDogbnVtYmVyKSB7XG4gICAgICAgIHRpbWVyICs9IGR0XG4gICAgICAgIGlmICh0aW1lciAqIDEwMDAgPj0gbXMpIHtcbiAgICAgICAgICBlbmdpbmUucmVtb3ZlU3lzdGVtKHNsZWVwU3lzdGVtKVxuICAgICAgICAgIHJlc29sdmUoKVxuICAgICAgICB9XG4gICAgICB9XG4gICAgICBlbmdpbmUuYWRkU3lzdGVtKHNsZWVwU3lzdGVtKVxuICAgIH0pXG4gIH1cblxuICByZXR1cm4ge1xuICAgIC4uLmVudGl0eURlZmluaXRpb25zLFxuICAgIG15UHJvZmlsZSxcbiAgICBpc1N0YXRlU3luY3Jvbml6ZWRcbiAgfVxufVxuXG4vKipcbiAqIE1lc3NhZ2VzIFByb3RvY29sIEVuY29kaW5nXG4gKlxuICogQ1JEVDogUGxhaW4gVWludDhBcnJheVxuICpcbiAqIENSRFRfU1RBVEVfUkVTIHsgc2VuZGVyOiBzdHJpbmcsIGRhdGE6IFVpbnQ4QXJyYXl9XG4gKi9cbmZ1bmN0aW9uIGRlY29kZUNSRFRTdGF0ZShkYXRhOiBVaW50OEFycmF5KSB7XG4gIGxldCBvZmZzZXQgPSAwXG4gIGNvbnN0IHIgPSBuZXcgVWludDhBcnJheShkYXRhKVxuICBjb25zdCB2aWV3ID0gbmV3IERhdGFWaWV3KHIuYnVmZmVyKVxuICBjb25zdCBzZW5kZXJMZW5ndGggPSB2aWV3LmdldFVpbnQ4KG9mZnNldClcbiAgb2Zmc2V0ICs9IDFcbiAgY29uc3Qgc2VuZGVyID0gZGVjb2RlU3RyaW5nKGRhdGEuc3ViYXJyYXkoMSwgc2VuZGVyTGVuZ3RoICsgMSkpXG4gIG9mZnNldCArPSBzZW5kZXJMZW5ndGhcbiAgY29uc3Qgc3RhdGUgPSByLnN1YmFycmF5KG9mZnNldClcblxuICByZXR1cm4geyBzZW5kZXIsIGRhdGE6IHN0YXRlIH1cbn1cblxuZnVuY3Rpb24gZW5jb2RlQ1JEVFN0YXRlKGFkZHJlc3M6IHN0cmluZywgZGF0YTogVWludDhBcnJheSkge1xuICAvLyBhZGRyZXNzIHRvIHVpbnQ4YXJyYXlcbiAgY29uc3QgYWRkcmVzc0J1ZmZlciA9IGVuY29kZVN0cmluZyhhZGRyZXNzKVxuICBjb25zdCBhZGRyZXNzT2Zmc2V0ID0gMVxuICBjb25zdCBtZXNzYWdlTGVuZ3RoID0gYWRkcmVzc09mZnNldCArIGFkZHJlc3NCdWZmZXIuYnl0ZUxlbmd0aCArIGRhdGEuYnl0ZUxlbmd0aFxuXG4gIGNvbnN0IHNlcmlhbGl6ZWRNZXNzYWdlID0gbmV3IFVpbnQ4QXJyYXkobWVzc2FnZUxlbmd0aClcbiAgc2VyaWFsaXplZE1lc3NhZ2Uuc2V0KG5ldyBVaW50OEFycmF5KFthZGRyZXNzQnVmZmVyLmJ5dGVMZW5ndGhdKSwgMClcbiAgc2VyaWFsaXplZE1lc3NhZ2Uuc2V0KGFkZHJlc3NCdWZmZXIsIDEpXG4gIHNlcmlhbGl6ZWRNZXNzYWdlLnNldChkYXRhLCBhZGRyZXNzQnVmZmVyLmJ5dGVMZW5ndGggKyAxKVxuICByZXR1cm4gc2VyaWFsaXplZE1lc3NhZ2Vcbn1cbiJdfQ==
package/package.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@dcl/sdk",
3
3
  "description": "",
4
- "version": "7.6.3-11407444266.commit-d9e3bec",
4
+ "version": "7.6.3-11411638449.commit-c070ea9",
5
5
  "author": "Decentraland",
6
6
  "dependencies": {
7
- "@dcl/ecs": "7.6.3-11407444266.commit-d9e3bec",
7
+ "@dcl/ecs": "7.6.3-11411638449.commit-c070ea9",
8
8
  "@dcl/ecs-math": "2.0.2",
9
9
  "@dcl/explorer": "1.0.164509-20240802172549.commit-fb95b9b",
10
- "@dcl/js-runtime": "7.6.3-11407444266.commit-d9e3bec",
11
- "@dcl/react-ecs": "7.6.3-11407444266.commit-d9e3bec",
12
- "@dcl/sdk-commands": "7.6.3-11407444266.commit-d9e3bec",
10
+ "@dcl/js-runtime": "7.6.3-11411638449.commit-c070ea9",
11
+ "@dcl/react-ecs": "7.6.3-11411638449.commit-c070ea9",
12
+ "@dcl/sdk-commands": "7.6.3-11411638449.commit-c070ea9",
13
13
  "text-encoding": "0.7.0"
14
14
  },
15
15
  "keywords": [],
@@ -35,5 +35,5 @@
35
35
  },
36
36
  "types": "./index.d.ts",
37
37
  "typings": "./index.d.ts",
38
- "commit": "d9e3bec5637b6cef47ac088cda87fbdd3c8b9aff"
38
+ "commit": "c070ea9e2c24483892875d921f621668a85f3a9a"
39
39
  }
@@ -4,10 +4,7 @@ import { addSyncTransport } from './message-bus-sync'
4
4
  import { getUserData } from '~system/UserIdentity'
5
5
 
6
6
  // initialize sync transport for sdk engine
7
- const { getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, getFirstChild } = addSyncTransport(
8
- engine,
9
- sendBinary,
10
- getUserData
11
- )
7
+ const { getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, getFirstChild, isStateSyncronized } =
8
+ addSyncTransport(engine, sendBinary, getUserData)
12
9
 
13
- export { getFirstChild, getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent }
10
+ export { getFirstChild, getChildren, syncEntity, parentEntity, getParent, myProfile, removeParent, isStateSyncronized }
@@ -1,4 +1,4 @@
1
- import { IEngine, Transport, RealmInfo } from '@dcl/ecs'
1
+ import { IEngine, Transport, RealmInfo, PlayerIdentityData } from '@dcl/ecs'
2
2
  import { type SendBinaryRequest, type SendBinaryResponse } from '~system/CommunicationsController'
3
3
 
4
4
  import { syncFilter } from './filter'
@@ -34,8 +34,11 @@ export function addSyncTransport(
34
34
  pendingMessageBusMessagesToSend.length = 0
35
35
  return messages
36
36
  }
37
+ const players = definePlayerHelper(engine)
37
38
 
39
+ let stateIsSyncronized = false
38
40
  let transportInitialzed = false
41
+
39
42
  // Add Sync Transport
40
43
  const transport: Transport = {
41
44
  filter: syncFilter(engine),
@@ -55,61 +58,103 @@ export function addSyncTransport(
55
58
  engine.addTransport(transport)
56
59
  // End add sync transport
57
60
 
58
- // If we dont have any state initialized, and recieve a state message.
61
+ // Receive & Process CRDT_STATE
59
62
  binaryMessageBus.on(CommsMessage.RES_CRDT_STATE, (value) => {
60
63
  const { sender, data } = decodeCRDTState(value)
61
64
  if (sender !== myProfile.userId) return
62
65
  DEBUG_NETWORK_MESSAGES() && console.log('[Processing CRDT State]', data.byteLength)
63
66
  transport.onmessage!(data)
67
+ stateIsSyncronized = true
64
68
  })
65
69
 
66
- binaryMessageBus.on(CommsMessage.REQ_CRDT_STATE, (message, userId) => {
70
+ // Answer to REQ_CRDT_STATE
71
+ binaryMessageBus.on(CommsMessage.REQ_CRDT_STATE, async (message, userId) => {
72
+ console.log(`Sending CRDT State to: ${userId}`)
67
73
  transport.onmessage!(message)
68
74
  binaryMessageBus.emit(CommsMessage.RES_CRDT_STATE, encodeCRDTState(userId, engineToCrdt(engine)))
69
75
  })
70
76
 
71
- const players = definePlayerHelper(engine)
77
+ // Process CRDT messages here
78
+ binaryMessageBus.on(CommsMessage.CRDT, (value) => {
79
+ DEBUG_NETWORK_MESSAGES() &&
80
+ console.log(Array.from(serializeCrdtMessages('[NetworkMessage received]:', value, engine)))
81
+ transport.onmessage!(value)
82
+ })
72
83
 
73
- let requestCrdtStateWhenConnected = false
84
+ async function requestState(retryCount: number = 1) {
85
+ let players = Array.from(engine.getEntitiesWith(PlayerIdentityData))
86
+ DEBUG_NETWORK_MESSAGES() && console.log(`Requesting state. Players connected: ${players.length - 1}`)
74
87
 
75
- players.onEnterScene((player) => {
76
- DEBUG_NETWORK_MESSAGES() && console.log('[onEnterScene]', player.userId)
77
- if (player.userId === myProfile.userId && !requestCrdtStateWhenConnected) {
78
- if (RealmInfo.getOrNull(engine.RootEntity)?.isConnectedSceneRoom) {
79
- DEBUG_NETWORK_MESSAGES() && console.log('Requesting state')
80
- binaryMessageBus.emit(CommsMessage.REQ_CRDT_STATE, engineToCrdt(engine))
88
+ if (!RealmInfo.getOrNull(engine.RootEntity)?.isConnectedSceneRoom) {
89
+ DEBUG_NETWORK_MESSAGES() && console.log(`Aborting Requesting state?. Disconnected`)
90
+ return
91
+ }
92
+
93
+ binaryMessageBus.emit(CommsMessage.REQ_CRDT_STATE, engineToCrdt(engine))
94
+
95
+ // Wait ~5s for the response.
96
+ await sleep(5000)
97
+
98
+ players = Array.from(engine.getEntitiesWith(PlayerIdentityData))
99
+
100
+ if (!stateIsSyncronized) {
101
+ if (players.length > 1 && retryCount <= 2) {
102
+ DEBUG_NETWORK_MESSAGES() &&
103
+ console.log(`Requesting state again ${retryCount} (no response). Players connected: ${players.length - 1}`)
104
+ void requestState(retryCount + 1)
81
105
  } else {
82
- DEBUG_NETWORK_MESSAGES() && console.log('Waiting to be conneted')
83
- requestCrdtStateWhenConnected = true
106
+ DEBUG_NETWORK_MESSAGES() && console.log('No active players. State syncronized')
107
+ stateIsSyncronized = true
84
108
  }
85
109
  }
110
+ }
111
+
112
+ players.onEnterScene((player) => {
113
+ DEBUG_NETWORK_MESSAGES() && console.log('[onEnterScene]', player.userId)
86
114
  })
87
115
 
116
+ // Asks for the REQ_CRDT_STATE when its connected to comms
88
117
  RealmInfo.onChange(engine.RootEntity, (value) => {
89
- if (value?.isConnectedSceneRoom && requestCrdtStateWhenConnected) {
90
- DEBUG_NETWORK_MESSAGES() && console.log('Requesting state.')
91
- requestCrdtStateWhenConnected = false
92
- binaryMessageBus.emit(CommsMessage.REQ_CRDT_STATE, engineToCrdt(engine))
118
+ if (!value?.isConnectedSceneRoom) {
119
+ DEBUG_NETWORK_MESSAGES() && console.log('Disconnected from comms')
120
+ stateIsSyncronized = false
121
+ }
122
+
123
+ if (value?.isConnectedSceneRoom) {
124
+ DEBUG_NETWORK_MESSAGES() && console.log('Connected to comms')
125
+ }
126
+
127
+ if (value?.isConnectedSceneRoom && !stateIsSyncronized) {
128
+ void requestState()
93
129
  }
94
130
  })
95
131
 
96
132
  players.onLeaveScene((userId) => {
97
133
  DEBUG_NETWORK_MESSAGES() && console.log('[onLeaveScene]', userId)
98
- if (userId === myProfile.userId) {
99
- requestCrdtStateWhenConnected = false
100
- }
101
134
  })
102
135
 
103
- // Process CRDT messages here
104
- binaryMessageBus.on(CommsMessage.CRDT, (value) => {
105
- DEBUG_NETWORK_MESSAGES() &&
106
- console.log(Array.from(serializeCrdtMessages('[NetworkMessage received]:', value, engine)))
107
- transport.onmessage!(value)
108
- })
136
+ function isStateSyncronized() {
137
+ return stateIsSyncronized
138
+ }
139
+
140
+ function sleep(ms: number) {
141
+ return new Promise<void>((resolve) => {
142
+ let timer = 0
143
+ function sleepSystem(dt: number) {
144
+ timer += dt
145
+ if (timer * 1000 >= ms) {
146
+ engine.removeSystem(sleepSystem)
147
+ resolve()
148
+ }
149
+ }
150
+ engine.addSystem(sleepSystem)
151
+ })
152
+ }
109
153
 
110
154
  return {
111
155
  ...entityDefinitions,
112
- myProfile
156
+ myProfile,
157
+ isStateSyncronized
113
158
  }
114
159
  }
115
160