@dcl/playground-assets 7.0.6-4138167187.commit-c9d306a → 7.0.6-4153633895.commit-4aad233
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/alpha.d.ts +110 -1
- package/dist/beta.d.ts +110 -1
- package/dist/index.bundled.d.ts +110 -1
- package/dist/index.js +344 -585
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/dist/playground/sdk/dcl-sdk.package.json +2 -2
- package/dist/playground-assets.d.ts +110 -1
- package/etc/playground-assets.api.json +990 -67
- package/etc/playground-assets.api.md +66 -2
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -13011,7 +13011,8 @@
|
|
|
13011
13011
|
return {
|
|
13012
13012
|
position: { x: 0, y: 0, z: 0 },
|
|
13013
13013
|
scale: { x: 1, y: 1, z: 1 },
|
|
13014
|
-
rotation: { x: 0, y: 0, z: 0, w: 1 }
|
|
13014
|
+
rotation: { x: 0, y: 0, z: 0, w: 1 },
|
|
13015
|
+
parent: 0
|
|
13015
13016
|
};
|
|
13016
13017
|
},
|
|
13017
13018
|
extend(value) {
|
|
@@ -13019,6 +13020,7 @@
|
|
|
13019
13020
|
position: { x: 0, y: 0, z: 0 },
|
|
13020
13021
|
scale: { x: 1, y: 1, z: 1 },
|
|
13021
13022
|
rotation: { x: 0, y: 0, z: 0, w: 1 },
|
|
13023
|
+
parent: 0,
|
|
13022
13024
|
...value
|
|
13023
13025
|
};
|
|
13024
13026
|
}
|
|
@@ -13667,402 +13669,57 @@
|
|
|
13667
13669
|
Schemas.Optional = IOptional;
|
|
13668
13670
|
})(exports.Schemas || (exports.Schemas = {}));
|
|
13669
13671
|
|
|
13670
|
-
|
|
13671
|
-
|
|
13672
|
-
|
|
13673
|
-
|
|
13674
|
-
|
|
13675
|
-
|
|
13676
|
-
|
|
13677
|
-
|
|
13678
|
-
|
|
13679
|
-
|
|
13680
|
-
|
|
13681
|
-
|
|
13682
|
-
|
|
13683
|
-
|
|
13684
|
-
|
|
13685
|
-
|
|
13686
|
-
|
|
13687
|
-
|
|
13688
|
-
|
|
13689
|
-
|
|
13690
|
-
|
|
13691
|
-
|
|
13692
|
-
|
|
13693
|
-
|
|
13694
|
-
|
|
13695
|
-
|
|
13696
|
-
|
|
13697
|
-
|
|
13698
|
-
|
|
13699
|
-
|
|
13700
|
-
|
|
13701
|
-
|
|
13702
|
-
|
|
13703
|
-
|
|
13704
|
-
|
|
13705
|
-
|
|
13706
|
-
|
|
13707
|
-
|
|
13708
|
-
|
|
13709
|
-
|
|
13710
|
-
|
|
13711
|
-
|
|
13712
|
-
|
|
13713
|
-
|
|
13714
|
-
|
|
13715
|
-
|
|
13716
|
-
|
|
13717
|
-
|
|
13718
|
-
|
|
13719
|
-
* @returns the set with [number, version] of each value
|
|
13720
|
-
*/
|
|
13721
|
-
has(n, v) {
|
|
13722
|
-
const currentValue = lastVersion.get(n);
|
|
13723
|
-
// If the version is >=, it means the value it's already in the set
|
|
13724
|
-
if (currentValue !== undefined && currentValue >= v) {
|
|
13725
|
-
return true;
|
|
13726
|
-
}
|
|
13727
|
-
return false;
|
|
13728
|
-
},
|
|
13729
|
-
/**
|
|
13730
|
-
* Warning: this function returns the reference to the internal map,
|
|
13731
|
-
* if you need to mutate some value, make a copy.
|
|
13732
|
-
* For optimization purpose the copy isn't made here.
|
|
13733
|
-
*
|
|
13734
|
-
* @returns the map of number to version
|
|
13735
|
-
*/
|
|
13736
|
-
getMap() {
|
|
13737
|
-
return lastVersion;
|
|
13738
|
-
}
|
|
13739
|
-
};
|
|
13740
|
-
}
|
|
13741
|
-
gset.createVersionGSet = createVersionGSet;
|
|
13742
|
-
|
|
13743
|
-
return gset;
|
|
13744
|
-
}
|
|
13745
|
-
|
|
13746
|
-
var types = {};
|
|
13747
|
-
|
|
13748
|
-
var hasRequiredTypes;
|
|
13749
|
-
|
|
13750
|
-
function requireTypes () {
|
|
13751
|
-
if (hasRequiredTypes) return types;
|
|
13752
|
-
hasRequiredTypes = 1;
|
|
13753
|
-
(function (exports) {
|
|
13754
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13755
|
-
exports.ProcessMessageResultType = exports.CRDTMessageType = void 0;
|
|
13756
|
-
(function (CRDTMessageType) {
|
|
13757
|
-
CRDTMessageType[CRDTMessageType["CRDTMT_PutComponentData"] = 1] = "CRDTMT_PutComponentData";
|
|
13758
|
-
CRDTMessageType[CRDTMessageType["CRDTMT_DeleteEntity"] = 2] = "CRDTMT_DeleteEntity";
|
|
13759
|
-
})(exports.CRDTMessageType || (exports.CRDTMessageType = {}));
|
|
13760
|
-
(function (ProcessMessageResultType) {
|
|
13761
|
-
/**
|
|
13762
|
-
* Typical message and new state set.
|
|
13763
|
-
* @state CHANGE
|
|
13764
|
-
* @reason Incoming message has a timestamp greater
|
|
13765
|
-
*/
|
|
13766
|
-
ProcessMessageResultType[ProcessMessageResultType["StateUpdatedTimestamp"] = 1] = "StateUpdatedTimestamp";
|
|
13767
|
-
/**
|
|
13768
|
-
* Typical message when it is considered old.
|
|
13769
|
-
* @state it does NOT CHANGE.
|
|
13770
|
-
* @reason incoming message has a timestamp lower.
|
|
13771
|
-
*/
|
|
13772
|
-
ProcessMessageResultType[ProcessMessageResultType["StateOutdatedTimestamp"] = 2] = "StateOutdatedTimestamp";
|
|
13773
|
-
/**
|
|
13774
|
-
* Weird message, same timestamp and data.
|
|
13775
|
-
* @state it does NOT CHANGE.
|
|
13776
|
-
* @reason consistent state between peers.
|
|
13777
|
-
*/
|
|
13778
|
-
ProcessMessageResultType[ProcessMessageResultType["NoChanges"] = 3] = "NoChanges";
|
|
13779
|
-
/**
|
|
13780
|
-
* Less but typical message, same timestamp, resolution by data.
|
|
13781
|
-
* @state it does NOT CHANGE.
|
|
13782
|
-
* @reason incoming message has a LOWER data.
|
|
13783
|
-
*/
|
|
13784
|
-
ProcessMessageResultType[ProcessMessageResultType["StateOutdatedData"] = 4] = "StateOutdatedData";
|
|
13785
|
-
/**
|
|
13786
|
-
* Less but typical message, same timestamp, resolution by data.
|
|
13787
|
-
* @state CHANGE.
|
|
13788
|
-
* @reason incoming message has a GREATER data.
|
|
13789
|
-
*/
|
|
13790
|
-
ProcessMessageResultType[ProcessMessageResultType["StateUpdatedData"] = 5] = "StateUpdatedData";
|
|
13791
|
-
/**
|
|
13792
|
-
* Entity was previously deleted.
|
|
13793
|
-
* @state it does NOT CHANGE.
|
|
13794
|
-
* @reason The message is considered old.
|
|
13795
|
-
*/
|
|
13796
|
-
ProcessMessageResultType[ProcessMessageResultType["EntityWasDeleted"] = 6] = "EntityWasDeleted";
|
|
13797
|
-
/**
|
|
13798
|
-
* Entity should be deleted.
|
|
13799
|
-
* @state CHANGE.
|
|
13800
|
-
* @reason the state is storing old entities
|
|
13801
|
-
*/
|
|
13802
|
-
ProcessMessageResultType[ProcessMessageResultType["EntityDeleted"] = 7] = "EntityDeleted";
|
|
13803
|
-
})(exports.ProcessMessageResultType || (exports.ProcessMessageResultType = {}));
|
|
13804
|
-
// we receive LWW, v=6, we have v=5 => we receive with delay the deleteEntity(v=5)
|
|
13805
|
-
// => we should generate the deleteEntity message effects internally with deleteEntity(v=5),
|
|
13806
|
-
// but don't resend the deleteEntity
|
|
13807
|
-
// - (CRDT) addDeletedEntitySet v=5 (with crdt state cleaning) and then LWW v=6
|
|
13808
|
-
// - (engine) engine.deleteEntity v=5
|
|
13809
|
-
// we receive LWW, v=7, we have v=5 => we receive with delay the deleteEntity(v=5), deleteEntity(v=6), ..., N
|
|
13810
|
-
// => we should generate the deleteEntity message effects internally with deleteEntity(v=5),
|
|
13811
|
-
// but don't resend the deleteEntity
|
|
13812
|
-
// - (CRDT) addDeletedEntitySet v=5 (with crdt state cleaning) and then LWW v=6
|
|
13813
|
-
// - (engine) engine.deleteEntity v=5
|
|
13814
|
-
// msg delete entity: it only should be sent by deleter
|
|
13815
|
-
//
|
|
13816
|
-
|
|
13817
|
-
} (types));
|
|
13818
|
-
return types;
|
|
13819
|
-
}
|
|
13820
|
-
|
|
13821
|
-
var hasRequiredDist;
|
|
13822
|
-
|
|
13823
|
-
function requireDist () {
|
|
13824
|
-
if (hasRequiredDist) return dist;
|
|
13825
|
-
hasRequiredDist = 1;
|
|
13826
|
-
(function (exports) {
|
|
13827
|
-
var __createBinding = (commonjsGlobal && commonjsGlobal.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
13828
|
-
if (k2 === undefined) k2 = k;
|
|
13829
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
13830
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13831
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
13832
|
-
}
|
|
13833
|
-
Object.defineProperty(o, k2, desc);
|
|
13834
|
-
}) : (function(o, m, k, k2) {
|
|
13835
|
-
if (k2 === undefined) k2 = k;
|
|
13836
|
-
o[k2] = m[k];
|
|
13837
|
-
}));
|
|
13838
|
-
var __exportStar = (commonjsGlobal && commonjsGlobal.__exportStar) || function(m, exports) {
|
|
13839
|
-
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
13840
|
-
};
|
|
13841
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13842
|
-
exports.crdtProtocol = exports.stateIterator = exports.dataCompare = void 0;
|
|
13843
|
-
const gset_1 = requireGset();
|
|
13844
|
-
const types_1 = requireTypes();
|
|
13845
|
-
const globalBuffer = globalThis.Buffer;
|
|
13846
|
-
__exportStar(requireTypes(), exports);
|
|
13847
|
-
/**
|
|
13848
|
-
* Compare raw data.
|
|
13849
|
-
* @internal
|
|
13850
|
-
* @returns 0 if is the same data, 1 if a > b, -1 if b > a
|
|
13851
|
-
*/
|
|
13852
|
-
function dataCompare(a, b) {
|
|
13853
|
-
// At reference level
|
|
13854
|
-
if (a === b)
|
|
13855
|
-
return 0;
|
|
13856
|
-
if (a === null && b !== null)
|
|
13857
|
-
return -1;
|
|
13858
|
-
if (a !== null && b === null)
|
|
13859
|
-
return 1;
|
|
13860
|
-
if (a instanceof Uint8Array && b instanceof Uint8Array) {
|
|
13861
|
-
let res;
|
|
13862
|
-
const n = a.byteLength > b.byteLength ? b.byteLength : a.byteLength;
|
|
13863
|
-
for (let i = 0; i < n; i++) {
|
|
13864
|
-
res = a[i] - b[i];
|
|
13865
|
-
if (res !== 0) {
|
|
13866
|
-
return res > 0 ? 1 : -1;
|
|
13867
|
-
}
|
|
13868
|
-
}
|
|
13869
|
-
res = a.byteLength - b.byteLength;
|
|
13870
|
-
return res > 0 ? 1 : res < 0 ? -1 : 0;
|
|
13871
|
-
}
|
|
13872
|
-
if (globalBuffer) {
|
|
13873
|
-
/* istanbul ignore next */
|
|
13874
|
-
if (a instanceof globalBuffer && b instanceof globalBuffer) {
|
|
13875
|
-
/* istanbul ignore next */
|
|
13876
|
-
return a.compare(b);
|
|
13877
|
-
}
|
|
13878
|
-
}
|
|
13879
|
-
if (typeof a === 'string') {
|
|
13880
|
-
return a.localeCompare(b);
|
|
13881
|
-
}
|
|
13882
|
-
return a > b ? 1 : -1;
|
|
13883
|
-
}
|
|
13884
|
-
exports.dataCompare = dataCompare;
|
|
13885
|
-
/**
|
|
13886
|
-
* State iterator
|
|
13887
|
-
* @internal
|
|
13888
|
-
*/
|
|
13889
|
-
function* stateIterator(state) {
|
|
13890
|
-
for (const [componentId, value1] of state.components.entries()) {
|
|
13891
|
-
for (const [entityId, value2] of value1.entries()) {
|
|
13892
|
-
yield [componentId, entityId, value2];
|
|
13893
|
-
}
|
|
13894
|
-
}
|
|
13895
|
-
}
|
|
13896
|
-
exports.stateIterator = stateIterator;
|
|
13897
|
-
/**
|
|
13898
|
-
* @public
|
|
13899
|
-
* CRDT protocol.
|
|
13900
|
-
* Stores the latest state, and decides whenever we have
|
|
13901
|
-
* to process and store the new data in case its an update, or
|
|
13902
|
-
* to discard and send our local value cause remote it's outdated.
|
|
13903
|
-
*/
|
|
13904
|
-
function crdtProtocol(entityUtils) {
|
|
13905
|
-
/**
|
|
13906
|
-
* Local state where we store the latest lamport timestamp
|
|
13907
|
-
* and the raw data value
|
|
13908
|
-
* @internal
|
|
13909
|
-
*/
|
|
13910
|
-
const state = {
|
|
13911
|
-
components: new Map(),
|
|
13912
|
-
deletedEntities: (0, gset_1.createVersionGSet)()
|
|
13913
|
-
};
|
|
13914
|
-
/**
|
|
13915
|
-
* We should call this fn in order to update the state
|
|
13916
|
-
* @internal
|
|
13917
|
-
*/
|
|
13918
|
-
function updateState(componentId, entityId, // todo: force type entity
|
|
13919
|
-
data, remoteTimestamp) {
|
|
13920
|
-
const componentIdValue = state.components.get(componentId);
|
|
13921
|
-
const timestamp = Math.max(remoteTimestamp, componentIdValue?.get(entityId)?.timestamp || 0);
|
|
13922
|
-
if (componentIdValue) {
|
|
13923
|
-
componentIdValue.set(entityId, { timestamp, data });
|
|
13924
|
-
}
|
|
13925
|
-
else {
|
|
13926
|
-
const componentIdValue = new Map();
|
|
13927
|
-
componentIdValue.set(entityId, { timestamp, data });
|
|
13928
|
-
state.components.set(componentId, componentIdValue);
|
|
13929
|
-
}
|
|
13930
|
-
return { timestamp, data };
|
|
13931
|
-
}
|
|
13932
|
-
/**
|
|
13933
|
-
* Create an event for the specified key and store the new data and
|
|
13934
|
-
* lamport timestmap incremented by one in the state.components.
|
|
13935
|
-
* @public
|
|
13936
|
-
*/
|
|
13937
|
-
function createComponentDataEvent(componentId, entityId, data) {
|
|
13938
|
-
// Increment the timestamp
|
|
13939
|
-
const timestamp = (state.components.get(componentId)?.get(entityId)?.timestamp || 0) + 1;
|
|
13940
|
-
const msg = {
|
|
13941
|
-
type: types_1.CRDTMessageType.CRDTMT_PutComponentData,
|
|
13942
|
-
componentId,
|
|
13943
|
-
entityId,
|
|
13944
|
-
data,
|
|
13945
|
-
timestamp
|
|
13946
|
-
};
|
|
13947
|
-
const res = processComponentDataMessage(msg);
|
|
13948
|
-
if (res === types_1.ProcessMessageResultType.StateUpdatedTimestamp) {
|
|
13949
|
-
return msg;
|
|
13950
|
-
}
|
|
13951
|
-
else {
|
|
13952
|
-
return null;
|
|
13953
|
-
}
|
|
13954
|
-
}
|
|
13955
|
-
/**
|
|
13956
|
-
* Create an event for the specified key and store the new data and
|
|
13957
|
-
* lamport timestmap incremented by one in the state.components.
|
|
13958
|
-
* @public
|
|
13959
|
-
*/
|
|
13960
|
-
function createDeleteEntityEvent(entityId) {
|
|
13961
|
-
// Increment the timestamp
|
|
13962
|
-
const message = {
|
|
13963
|
-
type: types_1.CRDTMessageType.CRDTMT_DeleteEntity,
|
|
13964
|
-
entityId
|
|
13965
|
-
};
|
|
13966
|
-
processDeleteEntityMessage(message);
|
|
13967
|
-
return message;
|
|
13968
|
-
}
|
|
13969
|
-
/**
|
|
13970
|
-
* Process the received message only if the lamport number recieved is higher
|
|
13971
|
-
* than the stored one. If its lower, we spread it to the network to correct the peer.
|
|
13972
|
-
* If they are equal, the bigger raw data wins.
|
|
13973
|
-
|
|
13974
|
-
* Returns the recieved data if the lamport number was bigger than ours.
|
|
13975
|
-
* If it was an outdated message, then we return void
|
|
13976
|
-
* @public
|
|
13977
|
-
*/
|
|
13978
|
-
function processComponentDataMessage(message) {
|
|
13979
|
-
const [entityNumber, entityVersion] = entityUtils.fromEntityId(message.entityId);
|
|
13980
|
-
if (state.deletedEntities.has(entityNumber, entityVersion)) {
|
|
13981
|
-
return types_1.ProcessMessageResultType.EntityWasDeleted;
|
|
13982
|
-
}
|
|
13983
|
-
const { componentId, entityId, data, timestamp } = message;
|
|
13984
|
-
const current = state.components.get(componentId)?.get(entityId);
|
|
13985
|
-
// The received message is > than our current value, update our state.components.
|
|
13986
|
-
if (!current || current.timestamp < timestamp) {
|
|
13987
|
-
updateState(componentId, entityId, data, timestamp);
|
|
13988
|
-
return types_1.ProcessMessageResultType.StateUpdatedTimestamp;
|
|
13989
|
-
}
|
|
13990
|
-
// Outdated Message. Resend our state message through the wire.
|
|
13991
|
-
if (current.timestamp > timestamp) {
|
|
13992
|
-
return types_1.ProcessMessageResultType.StateOutdatedTimestamp;
|
|
13993
|
-
}
|
|
13994
|
-
const currentDataGreater = dataCompare(current.data, data);
|
|
13995
|
-
// Same data, same timestamp. Weirdo echo message.
|
|
13996
|
-
if (currentDataGreater === 0) {
|
|
13997
|
-
return types_1.ProcessMessageResultType.NoChanges;
|
|
13998
|
-
// Current data is greater
|
|
13999
|
-
}
|
|
14000
|
-
else if (currentDataGreater > 0) {
|
|
14001
|
-
return types_1.ProcessMessageResultType.StateOutdatedData;
|
|
14002
|
-
// Curent data is lower
|
|
14003
|
-
}
|
|
14004
|
-
else {
|
|
14005
|
-
updateState(componentId, entityId, data, timestamp);
|
|
14006
|
-
return types_1.ProcessMessageResultType.StateUpdatedData;
|
|
14007
|
-
}
|
|
14008
|
-
}
|
|
14009
|
-
/*
|
|
14010
|
-
* @public
|
|
14011
|
-
*/
|
|
14012
|
-
function processMessage(message) {
|
|
14013
|
-
if (message.type === types_1.CRDTMessageType.CRDTMT_PutComponentData) {
|
|
14014
|
-
return processComponentDataMessage(message);
|
|
14015
|
-
}
|
|
14016
|
-
else if (message.type === types_1.CRDTMessageType.CRDTMT_DeleteEntity) {
|
|
14017
|
-
return processDeleteEntityMessage(message);
|
|
14018
|
-
}
|
|
14019
|
-
else {
|
|
14020
|
-
return types_1.ProcessMessageResultType.NoChanges;
|
|
14021
|
-
}
|
|
14022
|
-
}
|
|
14023
|
-
function processDeleteEntityMessage(message) {
|
|
14024
|
-
const { entityId } = message;
|
|
14025
|
-
const [entityNumber, entityVersion] = entityUtils.fromEntityId(message.entityId);
|
|
14026
|
-
state.deletedEntities.addTo(entityNumber, entityVersion);
|
|
14027
|
-
for (const [, payload] of state.components) {
|
|
14028
|
-
payload.delete(entityId);
|
|
14029
|
-
}
|
|
14030
|
-
return types_1.ProcessMessageResultType.EntityDeleted;
|
|
14031
|
-
}
|
|
14032
|
-
/**
|
|
14033
|
-
* Returns the current state
|
|
14034
|
-
* @public
|
|
14035
|
-
*/
|
|
14036
|
-
function getState() {
|
|
14037
|
-
return state;
|
|
14038
|
-
}
|
|
14039
|
-
/**
|
|
14040
|
-
* Returns the element state of a given element of the LWW-ElementSet
|
|
14041
|
-
* @public
|
|
14042
|
-
*/
|
|
14043
|
-
function getElementSetState(componentId, entityId) {
|
|
14044
|
-
return state.components.get(componentId)?.get(entityId) || null;
|
|
14045
|
-
}
|
|
14046
|
-
return {
|
|
14047
|
-
getElementSetState,
|
|
14048
|
-
createComponentDataEvent,
|
|
14049
|
-
createDeleteEntityEvent,
|
|
14050
|
-
processMessage,
|
|
14051
|
-
getState
|
|
14052
|
-
};
|
|
14053
|
-
}
|
|
14054
|
-
exports.crdtProtocol = crdtProtocol;
|
|
14055
|
-
|
|
14056
|
-
} (dist));
|
|
14057
|
-
return dist;
|
|
13672
|
+
/**
|
|
13673
|
+
*
|
|
13674
|
+
* @returns a new GSet
|
|
13675
|
+
*/
|
|
13676
|
+
function createVersionGSet() {
|
|
13677
|
+
const lastVersion = new Map();
|
|
13678
|
+
return {
|
|
13679
|
+
/**
|
|
13680
|
+
*
|
|
13681
|
+
* @param number
|
|
13682
|
+
* @param version
|
|
13683
|
+
* @returns
|
|
13684
|
+
*/
|
|
13685
|
+
addTo(number, version) {
|
|
13686
|
+
/* istanbul ignore next */
|
|
13687
|
+
if (version < 0) {
|
|
13688
|
+
/* istanbul ignore next */
|
|
13689
|
+
return false;
|
|
13690
|
+
}
|
|
13691
|
+
const currentValue = lastVersion.get(number);
|
|
13692
|
+
// If the version is >=, it means the value it's already in the set
|
|
13693
|
+
if (currentValue !== undefined && currentValue >= version) {
|
|
13694
|
+
return true;
|
|
13695
|
+
}
|
|
13696
|
+
lastVersion.set(number, version);
|
|
13697
|
+
return true;
|
|
13698
|
+
},
|
|
13699
|
+
/**
|
|
13700
|
+
* @returns the set with [number, version] of each value
|
|
13701
|
+
*/
|
|
13702
|
+
has(n, v) {
|
|
13703
|
+
const currentValue = lastVersion.get(n);
|
|
13704
|
+
// If the version is >=, it means the value it's already in the set
|
|
13705
|
+
if (currentValue !== undefined && currentValue >= v) {
|
|
13706
|
+
return true;
|
|
13707
|
+
}
|
|
13708
|
+
return false;
|
|
13709
|
+
},
|
|
13710
|
+
/**
|
|
13711
|
+
* Warning: this function returns the reference to the internal map,
|
|
13712
|
+
* if you need to mutate some value, make a copy.
|
|
13713
|
+
* For optimization purpose the copy isn't made here.
|
|
13714
|
+
*
|
|
13715
|
+
* @returns the map of number to version
|
|
13716
|
+
*/
|
|
13717
|
+
getMap() {
|
|
13718
|
+
return lastVersion;
|
|
13719
|
+
}
|
|
13720
|
+
};
|
|
14058
13721
|
}
|
|
14059
13722
|
|
|
14060
|
-
var distExports = requireDist();
|
|
14061
|
-
|
|
14062
|
-
var typesExports = requireTypes();
|
|
14063
|
-
|
|
14064
|
-
var gsetExports = requireGset();
|
|
14065
|
-
|
|
14066
13723
|
/**
|
|
14067
13724
|
* @internal
|
|
14068
13725
|
*/
|
|
@@ -14128,7 +13785,7 @@
|
|
|
14128
13785
|
let entityCounter = RESERVED_STATIC_ENTITIES;
|
|
14129
13786
|
const usedEntities = new Set();
|
|
14130
13787
|
let toRemoveEntities = [];
|
|
14131
|
-
const removedEntities =
|
|
13788
|
+
const removedEntities = createVersionGSet();
|
|
14132
13789
|
function generateNewEntity() {
|
|
14133
13790
|
if (entityCounter > MAX_ENTITY_NUMBER - 1) {
|
|
14134
13791
|
throw new Error(`It fails trying to generate an entity out of range ${MAX_ENTITY_NUMBER}.`);
|
|
@@ -14173,10 +13830,12 @@
|
|
|
14173
13830
|
}
|
|
14174
13831
|
function releaseRemovedEntities() {
|
|
14175
13832
|
const arr = toRemoveEntities;
|
|
14176
|
-
|
|
14177
|
-
|
|
14178
|
-
const
|
|
14179
|
-
|
|
13833
|
+
if (arr.length) {
|
|
13834
|
+
toRemoveEntities = [];
|
|
13835
|
+
for (const entity of arr) {
|
|
13836
|
+
const [n, v] = exports.EntityUtils.fromEntityId(entity);
|
|
13837
|
+
removedEntities.addTo(n, v);
|
|
13838
|
+
}
|
|
14180
13839
|
}
|
|
14181
13840
|
return arr;
|
|
14182
13841
|
}
|
|
@@ -14192,10 +13851,9 @@
|
|
|
14192
13851
|
}
|
|
14193
13852
|
function updateUsedEntity(entity) {
|
|
14194
13853
|
const [n, v] = exports.EntityUtils.fromEntityId(entity);
|
|
14195
|
-
|
|
14196
|
-
if (
|
|
13854
|
+
// if the entity was removed then abort fast
|
|
13855
|
+
if (removedEntities.has(n, v))
|
|
14197
13856
|
return false;
|
|
14198
|
-
}
|
|
14199
13857
|
// Update
|
|
14200
13858
|
if (v > 0) {
|
|
14201
13859
|
for (let i = 0; i <= v - 1; i++) {
|
|
@@ -14489,9 +14147,66 @@
|
|
|
14489
14147
|
CrdtMessageType[CrdtMessageType["MAX_MESSAGE_TYPE"] = 4] = "MAX_MESSAGE_TYPE";
|
|
14490
14148
|
})(exports.CrdtMessageType || (exports.CrdtMessageType = {}));
|
|
14491
14149
|
/**
|
|
14492
|
-
* @
|
|
14150
|
+
* @public
|
|
14493
14151
|
*/
|
|
14494
14152
|
const CRDT_MESSAGE_HEADER_LENGTH = 8;
|
|
14153
|
+
exports.ProcessMessageResultType = void 0;
|
|
14154
|
+
(function (ProcessMessageResultType) {
|
|
14155
|
+
/**
|
|
14156
|
+
* Typical message and new state set.
|
|
14157
|
+
* @state CHANGE
|
|
14158
|
+
* @reason Incoming message has a timestamp greater
|
|
14159
|
+
*/
|
|
14160
|
+
ProcessMessageResultType[ProcessMessageResultType["StateUpdatedTimestamp"] = 1] = "StateUpdatedTimestamp";
|
|
14161
|
+
/**
|
|
14162
|
+
* Typical message when it is considered old.
|
|
14163
|
+
* @state it does NOT CHANGE.
|
|
14164
|
+
* @reason incoming message has a timestamp lower.
|
|
14165
|
+
*/
|
|
14166
|
+
ProcessMessageResultType[ProcessMessageResultType["StateOutdatedTimestamp"] = 2] = "StateOutdatedTimestamp";
|
|
14167
|
+
/**
|
|
14168
|
+
* Weird message, same timestamp and data.
|
|
14169
|
+
* @state it does NOT CHANGE.
|
|
14170
|
+
* @reason consistent state between peers.
|
|
14171
|
+
*/
|
|
14172
|
+
ProcessMessageResultType[ProcessMessageResultType["NoChanges"] = 3] = "NoChanges";
|
|
14173
|
+
/**
|
|
14174
|
+
* Less but typical message, same timestamp, resolution by data.
|
|
14175
|
+
* @state it does NOT CHANGE.
|
|
14176
|
+
* @reason incoming message has a LOWER data.
|
|
14177
|
+
*/
|
|
14178
|
+
ProcessMessageResultType[ProcessMessageResultType["StateOutdatedData"] = 4] = "StateOutdatedData";
|
|
14179
|
+
/**
|
|
14180
|
+
* Less but typical message, same timestamp, resolution by data.
|
|
14181
|
+
* @state CHANGE.
|
|
14182
|
+
* @reason incoming message has a GREATER data.
|
|
14183
|
+
*/
|
|
14184
|
+
ProcessMessageResultType[ProcessMessageResultType["StateUpdatedData"] = 5] = "StateUpdatedData";
|
|
14185
|
+
/**
|
|
14186
|
+
* Entity was previously deleted.
|
|
14187
|
+
* @state it does NOT CHANGE.
|
|
14188
|
+
* @reason The message is considered old.
|
|
14189
|
+
*/
|
|
14190
|
+
ProcessMessageResultType[ProcessMessageResultType["EntityWasDeleted"] = 6] = "EntityWasDeleted";
|
|
14191
|
+
/**
|
|
14192
|
+
* Entity should be deleted.
|
|
14193
|
+
* @state CHANGE.
|
|
14194
|
+
* @reason the state is storing old entities
|
|
14195
|
+
*/
|
|
14196
|
+
ProcessMessageResultType[ProcessMessageResultType["EntityDeleted"] = 7] = "EntityDeleted";
|
|
14197
|
+
})(exports.ProcessMessageResultType || (exports.ProcessMessageResultType = {}));
|
|
14198
|
+
// we receive LWW, v=6, we have v=5 => we receive with delay the deleteEntity(v=5)
|
|
14199
|
+
// => we should generate the deleteEntity message effects internally with deleteEntity(v=5),
|
|
14200
|
+
// but don't resend the deleteEntity
|
|
14201
|
+
// - (CRDT) addDeletedEntitySet v=5 (with crdt state cleaning) and then LWW v=6
|
|
14202
|
+
// - (engine) engine.deleteEntity v=5
|
|
14203
|
+
// we receive LWW, v=7, we have v=5 => we receive with delay the deleteEntity(v=5), deleteEntity(v=6), ..., N
|
|
14204
|
+
// => we should generate the deleteEntity message effects internally with deleteEntity(v=5),
|
|
14205
|
+
// but don't resend the deleteEntity
|
|
14206
|
+
// - (CRDT) addDeletedEntitySet v=5 (with crdt state cleaning) and then LWW v=6
|
|
14207
|
+
// - (engine) engine.deleteEntity v=5
|
|
14208
|
+
// msg delete entity: it only should be sent by deleter
|
|
14209
|
+
//
|
|
14495
14210
|
|
|
14496
14211
|
/**
|
|
14497
14212
|
* @internal
|
|
@@ -14642,18 +14357,18 @@
|
|
|
14642
14357
|
* Call this function for an optimal writing data passing the ByteBuffer
|
|
14643
14358
|
* already allocated
|
|
14644
14359
|
*/
|
|
14645
|
-
function write(entity, timestamp,
|
|
14360
|
+
function write(entity, timestamp, componentId, data, buf) {
|
|
14646
14361
|
// reserve the beginning
|
|
14647
14362
|
const startMessageOffset = buf.incrementWriteOffset(CRDT_MESSAGE_HEADER_LENGTH + PutComponentOperation.MESSAGE_HEADER_LENGTH);
|
|
14648
14363
|
// write body
|
|
14649
|
-
|
|
14364
|
+
buf.writeBuffer(data, false);
|
|
14650
14365
|
const messageLength = buf.currentWriteOffset() - startMessageOffset;
|
|
14651
14366
|
// Write CrdtMessage header
|
|
14652
14367
|
buf.setUint32(startMessageOffset, messageLength);
|
|
14653
14368
|
buf.setUint32(startMessageOffset + 4, exports.CrdtMessageType.PUT_COMPONENT);
|
|
14654
14369
|
// Write ComponentOperation header
|
|
14655
14370
|
buf.setUint32(startMessageOffset + 8, entity);
|
|
14656
|
-
buf.setUint32(startMessageOffset + 12,
|
|
14371
|
+
buf.setUint32(startMessageOffset + 12, componentId);
|
|
14657
14372
|
buf.setUint32(startMessageOffset + 16, timestamp);
|
|
14658
14373
|
const newLocal = messageLength - PutComponentOperation.MESSAGE_HEADER_LENGTH - CRDT_MESSAGE_HEADER_LENGTH;
|
|
14659
14374
|
buf.setUint32(startMessageOffset + 20, newLocal);
|
|
@@ -14683,11 +14398,6 @@
|
|
|
14683
14398
|
*/
|
|
14684
14399
|
function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
14685
14400
|
const transports = [];
|
|
14686
|
-
// CRDT Client
|
|
14687
|
-
const crdtClient = distExports.crdtProtocol({
|
|
14688
|
-
toEntityId: exports.EntityUtils.toEntityId,
|
|
14689
|
-
fromEntityId: exports.EntityUtils.fromEntityId
|
|
14690
|
-
});
|
|
14691
14401
|
// Messages that we received at transport.onMessage waiting to be processed
|
|
14692
14402
|
const receivedMessages = [];
|
|
14693
14403
|
// Messages already processed by the engine but that we need to broadcast to other transports.
|
|
@@ -14764,20 +14474,9 @@
|
|
|
14764
14474
|
const entitiesShouldBeCleaned = [];
|
|
14765
14475
|
for (const msg of messagesToProcess) {
|
|
14766
14476
|
if (msg.type === exports.CrdtMessageType.DELETE_ENTITY) {
|
|
14767
|
-
crdtClient.processMessage({
|
|
14768
|
-
type: typesExports.CRDTMessageType.CRDTMT_DeleteEntity,
|
|
14769
|
-
entityId: msg.entityId
|
|
14770
|
-
});
|
|
14771
14477
|
entitiesShouldBeCleaned.push(msg.entityId);
|
|
14772
14478
|
}
|
|
14773
14479
|
else {
|
|
14774
|
-
const crdtMessage = {
|
|
14775
|
-
type: typesExports.CRDTMessageType.CRDTMT_PutComponentData,
|
|
14776
|
-
entityId: msg.entityId,
|
|
14777
|
-
componentId: msg.componentId,
|
|
14778
|
-
data: msg.type === exports.CrdtMessageType.PUT_COMPONENT ? msg.data : null,
|
|
14779
|
-
timestamp: msg.timestamp
|
|
14780
|
-
};
|
|
14781
14480
|
const entityState = engine.entityContainer.getEntityState(msg.entityId);
|
|
14782
14481
|
// Skip updates from removed entityes
|
|
14783
14482
|
if (entityState === exports.EntityState.Removed)
|
|
@@ -14787,144 +14486,73 @@
|
|
|
14787
14486
|
engine.entityContainer.updateUsedEntity(msg.entityId);
|
|
14788
14487
|
}
|
|
14789
14488
|
const component = engine.getComponentOrNull(msg.componentId);
|
|
14790
|
-
|
|
14791
|
-
|
|
14792
|
-
|
|
14793
|
-
|
|
14794
|
-
|
|
14795
|
-
|
|
14796
|
-
if (!component) {
|
|
14797
|
-
continue;
|
|
14798
|
-
}
|
|
14799
|
-
switch (processResult) {
|
|
14800
|
-
case typesExports.ProcessMessageResultType.StateUpdatedTimestamp:
|
|
14801
|
-
case typesExports.ProcessMessageResultType.StateUpdatedData:
|
|
14802
|
-
// Add message to transport queue to be processed by others transports
|
|
14803
|
-
broadcastMessages.push(msg);
|
|
14804
|
-
// Process CRDT Message
|
|
14805
|
-
if (msg.type === exports.CrdtMessageType.DELETE_COMPONENT) {
|
|
14806
|
-
component.deleteFrom(msg.entityId, false);
|
|
14489
|
+
if (component) {
|
|
14490
|
+
const [conflictMessage] = component.updateFromCrdt(msg);
|
|
14491
|
+
if (conflictMessage) {
|
|
14492
|
+
const offset = bufferForOutdated.currentWriteOffset();
|
|
14493
|
+
if (conflictMessage.type === exports.CrdtMessageType.PUT_COMPONENT) {
|
|
14494
|
+
exports.PutComponentOperation.write(msg.entityId, conflictMessage.timestamp, conflictMessage.componentId, conflictMessage.data, bufferForOutdated);
|
|
14807
14495
|
}
|
|
14808
14496
|
else {
|
|
14809
|
-
|
|
14810
|
-
component.upsertFromBinary(msg.entityId, data, false);
|
|
14497
|
+
exports.DeleteComponent.write(msg.entityId, component.componentId, conflictMessage.timestamp, bufferForOutdated);
|
|
14811
14498
|
}
|
|
14499
|
+
outdatedMessages.push({
|
|
14500
|
+
...msg,
|
|
14501
|
+
messageBuffer: bufferForOutdated.buffer().subarray(offset, bufferForOutdated.currentWriteOffset())
|
|
14502
|
+
});
|
|
14503
|
+
}
|
|
14504
|
+
else {
|
|
14505
|
+
// Add message to transport queue to be processed by others transports
|
|
14506
|
+
broadcastMessages.push(msg);
|
|
14812
14507
|
onProcessEntityComponentChange && onProcessEntityComponentChange(msg.entityId, msg.type, component);
|
|
14813
|
-
|
|
14814
|
-
// CRDT outdated message. Resend this message to the transport
|
|
14815
|
-
// To do this we add this message to a queue that will be processed at the end of the update tick
|
|
14816
|
-
case typesExports.ProcessMessageResultType.StateOutdatedData:
|
|
14817
|
-
case typesExports.ProcessMessageResultType.StateOutdatedTimestamp:
|
|
14818
|
-
const current = crdtClient.getState().components.get(msg.componentId)?.get(msg.entityId);
|
|
14819
|
-
if (current) {
|
|
14820
|
-
const offset = bufferForOutdated.currentWriteOffset();
|
|
14821
|
-
const ts = current.timestamp;
|
|
14822
|
-
if (component.has(msg.entityId)) {
|
|
14823
|
-
exports.PutComponentOperation.write(msg.entityId, ts, component, bufferForOutdated);
|
|
14824
|
-
}
|
|
14825
|
-
else {
|
|
14826
|
-
exports.DeleteComponent.write(msg.entityId, component.componentId, ts, bufferForOutdated);
|
|
14827
|
-
}
|
|
14828
|
-
outdatedMessages.push({
|
|
14829
|
-
...msg,
|
|
14830
|
-
messageBuffer: bufferForOutdated.buffer().subarray(offset, bufferForOutdated.currentWriteOffset())
|
|
14831
|
-
});
|
|
14832
|
-
}
|
|
14833
|
-
break;
|
|
14834
|
-
case typesExports.ProcessMessageResultType.NoChanges:
|
|
14835
|
-
case typesExports.ProcessMessageResultType.EntityDeleted:
|
|
14836
|
-
case typesExports.ProcessMessageResultType.EntityWasDeleted:
|
|
14508
|
+
}
|
|
14837
14509
|
}
|
|
14838
14510
|
}
|
|
14839
14511
|
}
|
|
14512
|
+
// the last stage of the syncrhonization is to delete the entities
|
|
14840
14513
|
for (const entity of entitiesShouldBeCleaned) {
|
|
14841
14514
|
// If we tried to resend outdated message and the entity was deleted before, we avoid sending them.
|
|
14842
14515
|
for (let i = outdatedMessages.length - 1; i >= 0; i--) {
|
|
14843
|
-
if (outdatedMessages[i].entityId === entity) {
|
|
14516
|
+
if (outdatedMessages[i].entityId === entity && outdatedMessages[i].type !== exports.CrdtMessageType.DELETE_ENTITY) {
|
|
14844
14517
|
outdatedMessages.splice(i, 1);
|
|
14845
14518
|
}
|
|
14846
14519
|
}
|
|
14847
14520
|
for (const definition of engine.componentsIter()) {
|
|
14848
|
-
definition.
|
|
14521
|
+
definition.entityDeleted(entity, false);
|
|
14849
14522
|
}
|
|
14850
14523
|
engine.entityContainer.updateRemovedEntity(entity);
|
|
14851
14524
|
onProcessEntityComponentChange && onProcessEntityComponentChange(entity, exports.CrdtMessageType.DELETE_ENTITY);
|
|
14852
14525
|
}
|
|
14853
14526
|
}
|
|
14854
|
-
/**
|
|
14855
|
-
* Updates CRDT state of the current engine dirty components
|
|
14856
|
-
*
|
|
14857
|
-
* TODO: optimize this function allocations using a bitmap
|
|
14858
|
-
* TODO: unify this function with sendMessages
|
|
14859
|
-
*/
|
|
14860
|
-
function updateState() {
|
|
14861
|
-
const dirtyMap = new Map();
|
|
14862
|
-
for (const component of engine.componentsIter()) {
|
|
14863
|
-
let entitySet = null;
|
|
14864
|
-
for (const entity of component.dirtyIterator()) {
|
|
14865
|
-
if (!entitySet) {
|
|
14866
|
-
entitySet = [];
|
|
14867
|
-
dirtyMap.set(component, entitySet);
|
|
14868
|
-
}
|
|
14869
|
-
// TODO: reuse shared writer to prevent extra allocations of toBinary
|
|
14870
|
-
const componentValue = component.toBinaryOrNull(entity)?.toBinary() ?? null;
|
|
14871
|
-
// TODO: do not emit event if componentValue equals the value didn't change
|
|
14872
|
-
// if update goes bad, the entity doesn't accept put anymore (it's added to deleted entities set)
|
|
14873
|
-
if (crdtClient.createComponentDataEvent(component.componentId, entity, componentValue) === null) {
|
|
14874
|
-
component.deleteFrom(entity, false);
|
|
14875
|
-
}
|
|
14876
|
-
else {
|
|
14877
|
-
entitySet.push(entity);
|
|
14878
|
-
onProcessEntityComponentChange &&
|
|
14879
|
-
onProcessEntityComponentChange(entity, componentValue === null ? exports.CrdtMessageType.DELETE_COMPONENT : exports.CrdtMessageType.PUT_COMPONENT, component);
|
|
14880
|
-
}
|
|
14881
|
-
}
|
|
14882
|
-
}
|
|
14883
|
-
return dirtyMap;
|
|
14884
|
-
}
|
|
14885
14527
|
/**
|
|
14886
14528
|
* Iterates the dirty map and generates crdt messages to be send
|
|
14887
14529
|
*/
|
|
14888
|
-
async function sendMessages(
|
|
14530
|
+
async function sendMessages(entitiesDeletedThisTick) {
|
|
14889
14531
|
// CRDT Messages will be the merge between the recieved transport messages and the new crdt messages
|
|
14890
14532
|
const crdtMessages = getMessages(broadcastMessages);
|
|
14891
14533
|
const outdatedMessagesBkp = getMessages(outdatedMessages);
|
|
14892
14534
|
const buffer = new ReadWriteByteBuffer();
|
|
14893
|
-
for (const
|
|
14894
|
-
for (const
|
|
14895
|
-
// Component will be always defined here since dirtyMap its an iterator of engine.componentsDefinition
|
|
14896
|
-
const { timestamp } = crdtClient
|
|
14897
|
-
.getState()
|
|
14898
|
-
.components.get(component.componentId)
|
|
14899
|
-
.get(entity);
|
|
14535
|
+
for (const component of engine.componentsIter()) {
|
|
14536
|
+
for (const message of component.getCrdtUpdates()) {
|
|
14900
14537
|
const offset = buffer.currentWriteOffset();
|
|
14901
|
-
const type = component.has(entity)
|
|
14902
|
-
? exports.CrdtMessageType.PUT_COMPONENT
|
|
14903
|
-
: exports.CrdtMessageType.DELETE_COMPONENT;
|
|
14904
|
-
const transportMessage = {
|
|
14905
|
-
type,
|
|
14906
|
-
entityId: entity,
|
|
14907
|
-
componentId: component.componentId,
|
|
14908
|
-
timestamp
|
|
14909
|
-
};
|
|
14910
14538
|
// Avoid creating messages if there is no transport that will handle it
|
|
14911
|
-
if (transports.some((t) => t.filter(
|
|
14912
|
-
if (
|
|
14913
|
-
exports.PutComponentOperation.write(
|
|
14539
|
+
if (transports.some((t) => t.filter(message))) {
|
|
14540
|
+
if (message.type === exports.CrdtMessageType.PUT_COMPONENT) {
|
|
14541
|
+
exports.PutComponentOperation.write(message.entityId, message.timestamp, message.componentId, message.data, buffer);
|
|
14914
14542
|
}
|
|
14915
|
-
else {
|
|
14916
|
-
exports.DeleteComponent.write(
|
|
14543
|
+
else if (message.type === exports.CrdtMessageType.DELETE_COMPONENT) {
|
|
14544
|
+
exports.DeleteComponent.write(message.entityId, component.componentId, message.timestamp, buffer);
|
|
14917
14545
|
}
|
|
14918
14546
|
crdtMessages.push({
|
|
14919
|
-
...
|
|
14547
|
+
...message,
|
|
14920
14548
|
messageBuffer: buffer.buffer().subarray(offset, buffer.currentWriteOffset())
|
|
14921
14549
|
});
|
|
14550
|
+
onProcessEntityComponentChange && onProcessEntityComponentChange(message.entityId, message.type, component);
|
|
14922
14551
|
}
|
|
14923
14552
|
}
|
|
14924
14553
|
}
|
|
14925
14554
|
// After all updates, I execute the DeletedEntity messages
|
|
14926
|
-
for (const entityId of
|
|
14927
|
-
crdtClient.createDeleteEntityEvent(entityId);
|
|
14555
|
+
for (const entityId of entitiesDeletedThisTick) {
|
|
14928
14556
|
const offset = buffer.currentWriteOffset();
|
|
14929
14557
|
exports.DeleteEntity.write(entityId, buffer);
|
|
14930
14558
|
crdtMessages.push({
|
|
@@ -14932,6 +14560,7 @@
|
|
|
14932
14560
|
entityId,
|
|
14933
14561
|
messageBuffer: buffer.buffer().subarray(offset, buffer.currentWriteOffset())
|
|
14934
14562
|
});
|
|
14563
|
+
onProcessEntityComponentChange && onProcessEntityComponentChange(entityId, exports.CrdtMessageType.DELETE_ENTITY);
|
|
14935
14564
|
}
|
|
14936
14565
|
// Send CRDT messages to transports
|
|
14937
14566
|
const transportBuffer = new ReadWriteByteBuffer();
|
|
@@ -14970,22 +14599,53 @@
|
|
|
14970
14599
|
const id = transports.push(transport) - 1;
|
|
14971
14600
|
transport.onmessage = parseChunkMessage(id);
|
|
14972
14601
|
}
|
|
14973
|
-
/**
|
|
14974
|
-
* @public
|
|
14975
|
-
* @returns returns the crdt state
|
|
14976
|
-
*/
|
|
14977
|
-
function getCrdt() {
|
|
14978
|
-
return crdtClient.getState();
|
|
14979
|
-
}
|
|
14980
14602
|
return {
|
|
14981
|
-
getCrdt,
|
|
14982
14603
|
sendMessages,
|
|
14983
14604
|
receiveMessages,
|
|
14984
|
-
addTransport
|
|
14985
|
-
updateState
|
|
14605
|
+
addTransport
|
|
14986
14606
|
};
|
|
14987
14607
|
}
|
|
14988
14608
|
|
|
14609
|
+
var CrdtUtils;
|
|
14610
|
+
(function (CrdtUtils) {
|
|
14611
|
+
(function (SynchronizedEntityType) {
|
|
14612
|
+
// synchronizes entities with the NetworkSynchronized component only, used for networked games
|
|
14613
|
+
SynchronizedEntityType[SynchronizedEntityType["NETWORKED"] = 0] = "NETWORKED";
|
|
14614
|
+
// synchronizes entities needed by the renderer
|
|
14615
|
+
SynchronizedEntityType[SynchronizedEntityType["RENDERER"] = 1] = "RENDERER";
|
|
14616
|
+
})(CrdtUtils.SynchronizedEntityType || (CrdtUtils.SynchronizedEntityType = {}));
|
|
14617
|
+
})(CrdtUtils || (CrdtUtils = {}));
|
|
14618
|
+
/**
|
|
14619
|
+
* Compare raw data.
|
|
14620
|
+
* @internal
|
|
14621
|
+
* @returns 0 if is the same data, 1 if a > b, -1 if b > a
|
|
14622
|
+
*/
|
|
14623
|
+
function dataCompare(a, b) {
|
|
14624
|
+
// At reference level
|
|
14625
|
+
if (a === b)
|
|
14626
|
+
return 0;
|
|
14627
|
+
if (a === null && b !== null)
|
|
14628
|
+
return -1;
|
|
14629
|
+
if (a !== null && b === null)
|
|
14630
|
+
return 1;
|
|
14631
|
+
if (a instanceof Uint8Array && b instanceof Uint8Array) {
|
|
14632
|
+
let res;
|
|
14633
|
+
const n = a.byteLength > b.byteLength ? b.byteLength : a.byteLength;
|
|
14634
|
+
for (let i = 0; i < n; i++) {
|
|
14635
|
+
res = a[i] - b[i];
|
|
14636
|
+
if (res !== 0) {
|
|
14637
|
+
return res > 0 ? 1 : -1;
|
|
14638
|
+
}
|
|
14639
|
+
}
|
|
14640
|
+
res = a.byteLength - b.byteLength;
|
|
14641
|
+
return res > 0 ? 1 : res < 0 ? -1 : 0;
|
|
14642
|
+
}
|
|
14643
|
+
if (typeof a === 'string') {
|
|
14644
|
+
return a.localeCompare(b);
|
|
14645
|
+
}
|
|
14646
|
+
return a > b ? 1 : -1;
|
|
14647
|
+
}
|
|
14648
|
+
|
|
14989
14649
|
/**
|
|
14990
14650
|
* @internal
|
|
14991
14651
|
*/
|
|
@@ -14993,12 +14653,148 @@
|
|
|
14993
14653
|
return Object.freeze({ ...val });
|
|
14994
14654
|
}
|
|
14995
14655
|
|
|
14656
|
+
function incrementTimestamp(entity, timestamps) {
|
|
14657
|
+
const newTimestamp = (timestamps.get(entity) || 0) + 1;
|
|
14658
|
+
timestamps.set(entity, newTimestamp);
|
|
14659
|
+
return newTimestamp;
|
|
14660
|
+
}
|
|
14661
|
+
function createUpdateFromCrdt(componentId, timestamps, schema, data) {
|
|
14662
|
+
/**
|
|
14663
|
+
* Process the received message only if the lamport number recieved is higher
|
|
14664
|
+
* than the stored one. If its lower, we spread it to the network to correct the peer.
|
|
14665
|
+
* If they are equal, the bigger raw data wins.
|
|
14666
|
+
|
|
14667
|
+
* Returns the recieved data if the lamport number was bigger than ours.
|
|
14668
|
+
* If it was an outdated message, then we return void
|
|
14669
|
+
* @public
|
|
14670
|
+
*/
|
|
14671
|
+
function crdtRuleForCurrentState(message) {
|
|
14672
|
+
const { entityId, timestamp } = message;
|
|
14673
|
+
const currentTimestamp = timestamps.get(entityId);
|
|
14674
|
+
// The received message is > than our current value, update our state.components.
|
|
14675
|
+
if (currentTimestamp === undefined || currentTimestamp < timestamp) {
|
|
14676
|
+
return exports.ProcessMessageResultType.StateUpdatedTimestamp;
|
|
14677
|
+
}
|
|
14678
|
+
// Outdated Message. Resend our state message through the wire.
|
|
14679
|
+
if (currentTimestamp > timestamp) {
|
|
14680
|
+
// console.log('2', currentTimestamp, timestamp)
|
|
14681
|
+
return exports.ProcessMessageResultType.StateOutdatedTimestamp;
|
|
14682
|
+
}
|
|
14683
|
+
// Deletes are idempotent
|
|
14684
|
+
if (message.type === exports.CrdtMessageType.DELETE_COMPONENT && !data.has(entityId)) {
|
|
14685
|
+
return exports.ProcessMessageResultType.NoChanges;
|
|
14686
|
+
}
|
|
14687
|
+
let currentDataGreater = 0;
|
|
14688
|
+
if (data.has(entityId)) {
|
|
14689
|
+
const writeBuffer = new ReadWriteByteBuffer();
|
|
14690
|
+
schema.serialize(data.get(entityId), writeBuffer);
|
|
14691
|
+
currentDataGreater = dataCompare(writeBuffer.toBinary(), message.data || null);
|
|
14692
|
+
}
|
|
14693
|
+
else {
|
|
14694
|
+
currentDataGreater = dataCompare(null, message.data);
|
|
14695
|
+
}
|
|
14696
|
+
// Same data, same timestamp. Weirdo echo message.
|
|
14697
|
+
// console.log('3', currentDataGreater, writeBuffer.toBinary(), (message as any).data || null)
|
|
14698
|
+
if (currentDataGreater === 0) {
|
|
14699
|
+
return exports.ProcessMessageResultType.NoChanges;
|
|
14700
|
+
}
|
|
14701
|
+
else if (currentDataGreater > 0) {
|
|
14702
|
+
// Current data is greater
|
|
14703
|
+
return exports.ProcessMessageResultType.StateOutdatedData;
|
|
14704
|
+
}
|
|
14705
|
+
else {
|
|
14706
|
+
// Curent data is lower
|
|
14707
|
+
return exports.ProcessMessageResultType.StateUpdatedData;
|
|
14708
|
+
}
|
|
14709
|
+
}
|
|
14710
|
+
return (msg) => {
|
|
14711
|
+
/* istanbul ignore next */
|
|
14712
|
+
if (msg.type !== exports.CrdtMessageType.PUT_COMPONENT && msg.type !== exports.CrdtMessageType.DELETE_COMPONENT)
|
|
14713
|
+
/* istanbul ignore next */
|
|
14714
|
+
return [null, data.get(msg.entityId)];
|
|
14715
|
+
const action = crdtRuleForCurrentState(msg);
|
|
14716
|
+
const entity = msg.entityId;
|
|
14717
|
+
switch (action) {
|
|
14718
|
+
case exports.ProcessMessageResultType.StateUpdatedData:
|
|
14719
|
+
case exports.ProcessMessageResultType.StateUpdatedTimestamp: {
|
|
14720
|
+
timestamps.set(entity, msg.timestamp);
|
|
14721
|
+
if (msg.type === exports.CrdtMessageType.PUT_COMPONENT) {
|
|
14722
|
+
const buf = new ReadWriteByteBuffer(msg.data);
|
|
14723
|
+
data.set(entity, schema.deserialize(buf));
|
|
14724
|
+
}
|
|
14725
|
+
else {
|
|
14726
|
+
data.delete(entity);
|
|
14727
|
+
}
|
|
14728
|
+
return [null, data.get(entity)];
|
|
14729
|
+
}
|
|
14730
|
+
case exports.ProcessMessageResultType.StateOutdatedTimestamp:
|
|
14731
|
+
case exports.ProcessMessageResultType.StateOutdatedData: {
|
|
14732
|
+
if (data.has(entity)) {
|
|
14733
|
+
const writeBuffer = new ReadWriteByteBuffer();
|
|
14734
|
+
schema.serialize(data.get(entity), writeBuffer);
|
|
14735
|
+
return [
|
|
14736
|
+
{
|
|
14737
|
+
type: exports.CrdtMessageType.PUT_COMPONENT,
|
|
14738
|
+
componentId,
|
|
14739
|
+
data: writeBuffer.toBinary(),
|
|
14740
|
+
entityId: entity,
|
|
14741
|
+
timestamp: timestamps.get(entity)
|
|
14742
|
+
},
|
|
14743
|
+
data.get(entity)
|
|
14744
|
+
];
|
|
14745
|
+
}
|
|
14746
|
+
else {
|
|
14747
|
+
return [
|
|
14748
|
+
{
|
|
14749
|
+
type: exports.CrdtMessageType.DELETE_COMPONENT,
|
|
14750
|
+
componentId,
|
|
14751
|
+
entityId: entity,
|
|
14752
|
+
timestamp: timestamps.get(entity)
|
|
14753
|
+
},
|
|
14754
|
+
undefined
|
|
14755
|
+
];
|
|
14756
|
+
}
|
|
14757
|
+
}
|
|
14758
|
+
}
|
|
14759
|
+
return [null, data.get(entity)];
|
|
14760
|
+
};
|
|
14761
|
+
}
|
|
14762
|
+
function createGetCrdtMessages(componentId, timestamps, dirtyIterator, schema, data) {
|
|
14763
|
+
return function* () {
|
|
14764
|
+
for (const entity of dirtyIterator) {
|
|
14765
|
+
const newTimestamp = incrementTimestamp(entity, timestamps);
|
|
14766
|
+
if (data.has(entity)) {
|
|
14767
|
+
const writeBuffer = new ReadWriteByteBuffer();
|
|
14768
|
+
schema.serialize(data.get(entity), writeBuffer);
|
|
14769
|
+
const msg = {
|
|
14770
|
+
type: exports.CrdtMessageType.PUT_COMPONENT,
|
|
14771
|
+
componentId,
|
|
14772
|
+
entityId: entity,
|
|
14773
|
+
data: writeBuffer.toBinary(),
|
|
14774
|
+
timestamp: newTimestamp
|
|
14775
|
+
};
|
|
14776
|
+
yield msg;
|
|
14777
|
+
}
|
|
14778
|
+
else {
|
|
14779
|
+
const msg = {
|
|
14780
|
+
type: exports.CrdtMessageType.DELETE_COMPONENT,
|
|
14781
|
+
componentId,
|
|
14782
|
+
entityId: entity,
|
|
14783
|
+
timestamp: newTimestamp
|
|
14784
|
+
};
|
|
14785
|
+
yield msg;
|
|
14786
|
+
}
|
|
14787
|
+
}
|
|
14788
|
+
dirtyIterator.clear();
|
|
14789
|
+
};
|
|
14790
|
+
}
|
|
14996
14791
|
/**
|
|
14997
14792
|
* @internal
|
|
14998
14793
|
*/
|
|
14999
14794
|
function createComponentDefinitionFromSchema(componentName, componentId, schema) {
|
|
15000
14795
|
const data = new Map();
|
|
15001
14796
|
const dirtyIterator = new Set();
|
|
14797
|
+
const timestamps = new Map();
|
|
15002
14798
|
return {
|
|
15003
14799
|
get componentId() {
|
|
15004
14800
|
return componentId;
|
|
@@ -15017,15 +14813,16 @@
|
|
|
15017
14813
|
},
|
|
15018
14814
|
deleteFrom(entity, markAsDirty = true) {
|
|
15019
14815
|
const component = data.get(entity);
|
|
15020
|
-
data.delete(entity)
|
|
15021
|
-
if (markAsDirty) {
|
|
14816
|
+
if (data.delete(entity) && markAsDirty) {
|
|
15022
14817
|
dirtyIterator.add(entity);
|
|
15023
14818
|
}
|
|
15024
|
-
else {
|
|
15025
|
-
dirtyIterator.delete(entity);
|
|
15026
|
-
}
|
|
15027
14819
|
return component || null;
|
|
15028
14820
|
},
|
|
14821
|
+
entityDeleted(entity, markAsDirty) {
|
|
14822
|
+
if (data.delete(entity) && markAsDirty) {
|
|
14823
|
+
dirtyIterator.add(entity);
|
|
14824
|
+
}
|
|
14825
|
+
},
|
|
15029
14826
|
getOrNull(entity) {
|
|
15030
14827
|
const component = data.get(entity);
|
|
15031
14828
|
return component ? deepReadonly(component) : null;
|
|
@@ -15078,6 +14875,7 @@
|
|
|
15078
14875
|
yield entity;
|
|
15079
14876
|
}
|
|
15080
14877
|
},
|
|
14878
|
+
getCrdtUpdates: createGetCrdtMessages(componentId, timestamps, dirtyIterator, schema, data),
|
|
15081
14879
|
toBinary(entity) {
|
|
15082
14880
|
const component = data.get(entity);
|
|
15083
14881
|
if (!component) {
|
|
@@ -15087,45 +14885,9 @@
|
|
|
15087
14885
|
schema.serialize(component, writeBuffer);
|
|
15088
14886
|
return writeBuffer;
|
|
15089
14887
|
},
|
|
15090
|
-
|
|
15091
|
-
const component = data.get(entity);
|
|
15092
|
-
if (!component) {
|
|
15093
|
-
return null;
|
|
15094
|
-
}
|
|
15095
|
-
const writeBuffer = new ReadWriteByteBuffer();
|
|
15096
|
-
schema.serialize(component, writeBuffer);
|
|
15097
|
-
return writeBuffer;
|
|
15098
|
-
},
|
|
15099
|
-
writeToByteBuffer(entity, buffer) {
|
|
15100
|
-
const component = data.get(entity);
|
|
15101
|
-
if (!component) {
|
|
15102
|
-
throw new Error(`[writeToByteBuffer] Component ${componentName} for entity #${entity} not found`);
|
|
15103
|
-
}
|
|
15104
|
-
schema.serialize(component, buffer);
|
|
15105
|
-
},
|
|
15106
|
-
updateFromBinary(entity, buffer, markAsDirty = true) {
|
|
15107
|
-
const component = data.get(entity);
|
|
15108
|
-
if (!component) {
|
|
15109
|
-
throw new Error(`[updateFromBinary] Component ${componentName} for ${entity} not found`);
|
|
15110
|
-
}
|
|
15111
|
-
return this.upsertFromBinary(entity, buffer, markAsDirty);
|
|
15112
|
-
},
|
|
15113
|
-
upsertFromBinary(entity, buffer, markAsDirty = true) {
|
|
15114
|
-
const newValue = schema.deserialize(buffer);
|
|
15115
|
-
data.set(entity, newValue);
|
|
15116
|
-
if (markAsDirty) {
|
|
15117
|
-
dirtyIterator.add(entity);
|
|
15118
|
-
}
|
|
15119
|
-
else {
|
|
15120
|
-
dirtyIterator.delete(entity);
|
|
15121
|
-
}
|
|
15122
|
-
return newValue;
|
|
15123
|
-
},
|
|
14888
|
+
updateFromCrdt: createUpdateFromCrdt(componentId, timestamps, schema, data),
|
|
15124
14889
|
deserialize(buffer) {
|
|
15125
14890
|
return schema.deserialize(buffer);
|
|
15126
|
-
},
|
|
15127
|
-
clearDirty() {
|
|
15128
|
-
dirtyIterator.clear();
|
|
15129
14891
|
}
|
|
15130
14892
|
};
|
|
15131
14893
|
}
|
|
@@ -15346,9 +15108,7 @@
|
|
|
15346
15108
|
}
|
|
15347
15109
|
function removeEntity(entity) {
|
|
15348
15110
|
for (const [, component] of componentsDefinition) {
|
|
15349
|
-
|
|
15350
|
-
component.deleteFrom(entity);
|
|
15351
|
-
}
|
|
15111
|
+
component.entityDeleted(entity, true);
|
|
15352
15112
|
}
|
|
15353
15113
|
return entityContainer.removeEntity(entity);
|
|
15354
15114
|
}
|
|
@@ -15504,12 +15264,9 @@
|
|
|
15504
15264
|
const ret = system.fn(dt);
|
|
15505
15265
|
checkNotThenable(ret, `A system (${system.name || 'anonymous'}) returned a thenable. Systems cannot be async functions. Documentation: https://dcl.gg/sdk/sync-systems`);
|
|
15506
15266
|
}
|
|
15507
|
-
|
|
15267
|
+
// get the deleted entities to send the DeleteEntity CRDT commands
|
|
15508
15268
|
const deletedEntites = partialEngine.entityContainer.releaseRemovedEntities();
|
|
15509
|
-
await crdtSystem.sendMessages(
|
|
15510
|
-
for (const definition of partialEngine.componentsIter()) {
|
|
15511
|
-
definition.clearDirty();
|
|
15512
|
-
}
|
|
15269
|
+
await crdtSystem.sendMessages(deletedEntites);
|
|
15513
15270
|
}
|
|
15514
15271
|
return {
|
|
15515
15272
|
addEntity: partialEngine.addEntity,
|
|
@@ -15532,7 +15289,6 @@
|
|
|
15532
15289
|
CameraEntity: 2,
|
|
15533
15290
|
getEntityState: partialEngine.entityContainer.getEntityState,
|
|
15534
15291
|
addTransport: crdtSystem.addTransport,
|
|
15535
|
-
getCrdtState: crdtSystem.getCrdt,
|
|
15536
15292
|
entityContainer: partialEngine.entityContainer
|
|
15537
15293
|
};
|
|
15538
15294
|
}
|
|
@@ -42525,14 +42281,17 @@
|
|
|
42525
42281
|
exports.components = index;
|
|
42526
42282
|
exports.createComponentDefinitionFromSchema = createComponentDefinitionFromSchema;
|
|
42527
42283
|
exports.createEthereumProvider = createEthereumProvider;
|
|
42284
|
+
exports.createGetCrdtMessages = createGetCrdtMessages;
|
|
42528
42285
|
exports.createInputSystem = createInputSystem;
|
|
42529
42286
|
exports.createPointerEventSystem = createPointerEventSystem;
|
|
42530
42287
|
exports.createReactBasedUiSystem = createReactBasedUiSystem;
|
|
42531
42288
|
exports.createTaskSystem = createTaskSystem;
|
|
42289
|
+
exports.createUpdateFromCrdt = createUpdateFromCrdt;
|
|
42532
42290
|
exports.cyclicParentingChecker = cyclicParentingChecker;
|
|
42533
42291
|
exports.deepReadonly = deepReadonly;
|
|
42534
42292
|
exports.engine = engine;
|
|
42535
42293
|
exports.executeTask = executeTask;
|
|
42294
|
+
exports.incrementTimestamp = incrementTimestamp;
|
|
42536
42295
|
exports.inputSystem = inputSystem;
|
|
42537
42296
|
exports.isListener = isListener;
|
|
42538
42297
|
exports.onCommsMessage = onCommsMessage;
|