@dcl/ecs 7.3.31 → 7.3.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/index.d.ts +10 -0
- package/dist/components/index.js +12 -0
- package/dist/components/manual/NetworkEntity.d.ts +12 -0
- package/dist/components/manual/NetworkEntity.js +9 -0
- package/dist/components/manual/NetworkParent.d.ts +12 -0
- package/dist/components/manual/NetworkParent.js +9 -0
- package/dist/components/types.d.ts +2 -0
- package/dist/engine/index.js +8 -21
- package/dist/engine/lww-element-set-component-definition.js +5 -2
- package/dist/engine/types.d.ts +0 -12
- package/dist/index.d.ts +11 -1
- package/dist/index.js +10 -0
- package/dist/runtime/helpers/tree.d.ts +1 -1
- package/dist/runtime/helpers/tree.js +25 -2
- package/dist/serialization/crdt/index.d.ts +3 -0
- package/dist/serialization/crdt/index.js +3 -0
- package/dist/serialization/crdt/message.js +12 -0
- package/dist/serialization/crdt/network/deleteComponentNetwork.d.ts +14 -0
- package/dist/serialization/crdt/network/deleteComponentNetwork.js +43 -0
- package/dist/serialization/crdt/network/deleteEntityNetwork.d.ts +11 -0
- package/dist/serialization/crdt/network/deleteEntityNetwork.js +33 -0
- package/dist/serialization/crdt/network/putComponentNetwork.d.ts +15 -0
- package/dist/serialization/crdt/network/putComponentNetwork.js +49 -0
- package/dist/serialization/crdt/network/utils.d.ts +9 -0
- package/dist/serialization/crdt/network/utils.js +62 -0
- package/dist/serialization/crdt/types.d.ts +48 -3
- package/dist/serialization/crdt/types.js +5 -1
- package/dist/systems/crdt/index.js +111 -69
- package/dist/systems/crdt/types.d.ts +9 -1
- package/dist-cjs/components/index.d.ts +10 -0
- package/dist-cjs/components/index.js +15 -1
- package/dist-cjs/components/manual/NetworkEntity.d.ts +12 -0
- package/dist-cjs/components/manual/NetworkEntity.js +11 -0
- package/dist-cjs/components/manual/NetworkParent.d.ts +12 -0
- package/dist-cjs/components/manual/NetworkParent.js +11 -0
- package/dist-cjs/components/types.d.ts +2 -0
- package/dist-cjs/engine/index.js +8 -21
- package/dist-cjs/engine/lww-element-set-component-definition.js +5 -2
- package/dist-cjs/engine/types.d.ts +0 -12
- package/dist-cjs/index.d.ts +11 -1
- package/dist-cjs/index.js +11 -1
- package/dist-cjs/runtime/helpers/tree.d.ts +1 -1
- package/dist-cjs/runtime/helpers/tree.js +25 -2
- package/dist-cjs/serialization/crdt/index.d.ts +3 -0
- package/dist-cjs/serialization/crdt/index.js +3 -0
- package/dist-cjs/serialization/crdt/message.js +12 -0
- package/dist-cjs/serialization/crdt/network/deleteComponentNetwork.d.ts +14 -0
- package/dist-cjs/serialization/crdt/network/deleteComponentNetwork.js +46 -0
- package/dist-cjs/serialization/crdt/network/deleteEntityNetwork.d.ts +11 -0
- package/dist-cjs/serialization/crdt/network/deleteEntityNetwork.js +36 -0
- package/dist-cjs/serialization/crdt/network/putComponentNetwork.d.ts +15 -0
- package/dist-cjs/serialization/crdt/network/putComponentNetwork.js +52 -0
- package/dist-cjs/serialization/crdt/network/utils.d.ts +9 -0
- package/dist-cjs/serialization/crdt/network/utils.js +69 -0
- package/dist-cjs/serialization/crdt/types.d.ts +48 -3
- package/dist-cjs/serialization/crdt/types.js +5 -1
- package/dist-cjs/systems/crdt/index.js +133 -68
- package/dist-cjs/systems/crdt/types.d.ts +9 -1
- package/package.json +2 -2
|
@@ -8,7 +8,10 @@ export declare enum CrdtMessageType {
|
|
|
8
8
|
DELETE_COMPONENT = 2,
|
|
9
9
|
DELETE_ENTITY = 3,
|
|
10
10
|
APPEND_VALUE = 4,
|
|
11
|
-
|
|
11
|
+
PUT_COMPONENT_NETWORK = 5,
|
|
12
|
+
DELETE_COMPONENT_NETWORK = 6,
|
|
13
|
+
DELETE_ENTITY_NETWORK = 7,
|
|
14
|
+
MAX_MESSAGE_TYPE = 8
|
|
12
15
|
}
|
|
13
16
|
/**
|
|
14
17
|
* Min length = 8 bytes
|
|
@@ -41,6 +44,10 @@ export type PutComponentMessageBody = {
|
|
|
41
44
|
timestamp: number;
|
|
42
45
|
data: Uint8Array;
|
|
43
46
|
};
|
|
47
|
+
export type PutNetworkComponentMessageBody = Omit<PutComponentMessageBody, 'type'> & {
|
|
48
|
+
type: CrdtMessageType.PUT_COMPONENT_NETWORK;
|
|
49
|
+
networkId: number;
|
|
50
|
+
};
|
|
44
51
|
/**
|
|
45
52
|
* Min. length = header (8 bytes) + 16 bytes = 24 bytes
|
|
46
53
|
*
|
|
@@ -69,6 +76,20 @@ export type DeleteComponentMessageBody = {
|
|
|
69
76
|
componentId: number;
|
|
70
77
|
timestamp: number;
|
|
71
78
|
};
|
|
79
|
+
/**
|
|
80
|
+
* @param entity - Uint32 number of the entity
|
|
81
|
+
* @param componentId - Uint32 number of id
|
|
82
|
+
* @param timestamp - Uint32 Lamport timestamp
|
|
83
|
+
* @param networkId - Uint32 user network id
|
|
84
|
+
* @public
|
|
85
|
+
*/
|
|
86
|
+
export type DeleteComponentNetworkMessageBody = {
|
|
87
|
+
type: CrdtMessageType.DELETE_COMPONENT_NETWORK;
|
|
88
|
+
entityId: Entity;
|
|
89
|
+
componentId: number;
|
|
90
|
+
timestamp: number;
|
|
91
|
+
networkId: number;
|
|
92
|
+
};
|
|
72
93
|
/**
|
|
73
94
|
* @param entity - uint32 number of the entity
|
|
74
95
|
* @public
|
|
@@ -77,6 +98,14 @@ export type DeleteEntityMessageBody = {
|
|
|
77
98
|
type: CrdtMessageType.DELETE_ENTITY;
|
|
78
99
|
entityId: Entity;
|
|
79
100
|
};
|
|
101
|
+
/**
|
|
102
|
+
* @public
|
|
103
|
+
*/
|
|
104
|
+
export type DeleteEntityNetworkMessageBody = {
|
|
105
|
+
type: CrdtMessageType.DELETE_ENTITY_NETWORK;
|
|
106
|
+
entityId: Entity;
|
|
107
|
+
networkId: number;
|
|
108
|
+
};
|
|
80
109
|
/**
|
|
81
110
|
* @public
|
|
82
111
|
*/
|
|
@@ -85,10 +114,18 @@ export type AppendValueMessage = CrdtMessageHeader & AppendValueMessageBody;
|
|
|
85
114
|
* @public
|
|
86
115
|
*/
|
|
87
116
|
export type PutComponentMessage = CrdtMessageHeader & PutComponentMessageBody;
|
|
117
|
+
/**
|
|
118
|
+
* @public
|
|
119
|
+
*/
|
|
120
|
+
export type PutNetworkComponentMessage = CrdtMessageHeader & PutNetworkComponentMessageBody;
|
|
88
121
|
/**
|
|
89
122
|
* @public
|
|
90
123
|
*/
|
|
91
124
|
export type DeleteComponentMessage = CrdtMessageHeader & DeleteComponentMessageBody;
|
|
125
|
+
/**
|
|
126
|
+
* @public
|
|
127
|
+
*/
|
|
128
|
+
export type DeleteComponentNetworkMessage = CrdtMessageHeader & DeleteComponentNetworkMessageBody;
|
|
92
129
|
/**
|
|
93
130
|
* @public
|
|
94
131
|
*/
|
|
@@ -96,11 +133,19 @@ export type DeleteEntityMessage = CrdtMessageHeader & DeleteEntityMessageBody;
|
|
|
96
133
|
/**
|
|
97
134
|
* @public
|
|
98
135
|
*/
|
|
99
|
-
export type
|
|
136
|
+
export type DeleteEntityNetworkMessage = CrdtMessageHeader & DeleteEntityNetworkMessageBody;
|
|
137
|
+
/**
|
|
138
|
+
* @public
|
|
139
|
+
*/
|
|
140
|
+
export type CrdtMessage = PutComponentMessage | DeleteComponentMessage | AppendValueMessage | DeleteEntityMessage | PutNetworkComponentMessage | DeleteComponentNetworkMessage | DeleteEntityNetworkMessage;
|
|
141
|
+
/**
|
|
142
|
+
* @public
|
|
143
|
+
*/
|
|
144
|
+
export type CrdtNetworkMessageBody = PutNetworkComponentMessageBody | DeleteComponentNetworkMessageBody | DeleteEntityNetworkMessageBody;
|
|
100
145
|
/**
|
|
101
146
|
* @public
|
|
102
147
|
*/
|
|
103
|
-
export type CrdtMessageBody = PutComponentMessageBody | DeleteComponentMessageBody | DeleteEntityMessageBody | AppendValueMessageBody;
|
|
148
|
+
export type CrdtMessageBody = PutComponentMessageBody | DeleteComponentMessageBody | DeleteEntityMessageBody | AppendValueMessageBody | CrdtNetworkMessageBody;
|
|
104
149
|
export declare enum ProcessMessageResultType {
|
|
105
150
|
/**
|
|
106
151
|
* Typical message and new state set.
|
|
@@ -9,7 +9,11 @@ export var CrdtMessageType;
|
|
|
9
9
|
CrdtMessageType[CrdtMessageType["DELETE_COMPONENT"] = 2] = "DELETE_COMPONENT";
|
|
10
10
|
CrdtMessageType[CrdtMessageType["DELETE_ENTITY"] = 3] = "DELETE_ENTITY";
|
|
11
11
|
CrdtMessageType[CrdtMessageType["APPEND_VALUE"] = 4] = "APPEND_VALUE";
|
|
12
|
-
|
|
12
|
+
// Network operations
|
|
13
|
+
CrdtMessageType[CrdtMessageType["PUT_COMPONENT_NETWORK"] = 5] = "PUT_COMPONENT_NETWORK";
|
|
14
|
+
CrdtMessageType[CrdtMessageType["DELETE_COMPONENT_NETWORK"] = 6] = "DELETE_COMPONENT_NETWORK";
|
|
15
|
+
CrdtMessageType[CrdtMessageType["DELETE_ENTITY_NETWORK"] = 7] = "DELETE_ENTITY_NETWORK";
|
|
16
|
+
CrdtMessageType[CrdtMessageType["MAX_MESSAGE_TYPE"] = 8] = "MAX_MESSAGE_TYPE";
|
|
13
17
|
})(CrdtMessageType || (CrdtMessageType = {}));
|
|
14
18
|
/**
|
|
15
19
|
* @public
|
|
@@ -1,21 +1,26 @@
|
|
|
1
1
|
import { EntityState } from '../../engine/entity';
|
|
2
2
|
import { ReadWriteByteBuffer } from '../../serialization/ByteBuffer';
|
|
3
|
-
import { AppendValueOperation, CrdtMessageProtocol } from '../../serialization/crdt';
|
|
3
|
+
import { AppendValueOperation, CrdtMessageProtocol, DeleteComponentNetwork, DeleteEntityNetwork } from '../../serialization/crdt';
|
|
4
4
|
import { DeleteComponent } from '../../serialization/crdt/deleteComponent';
|
|
5
5
|
import { DeleteEntity } from '../../serialization/crdt/deleteEntity';
|
|
6
6
|
import { PutComponentOperation } from '../../serialization/crdt/putComponent';
|
|
7
7
|
import { CrdtMessageType } from '../../serialization/crdt/types';
|
|
8
|
+
import { PutNetworkComponentOperation } from '../../serialization/crdt/network/putComponentNetwork';
|
|
9
|
+
import { NetworkEntity as defineNetworkEntity, NetworkParent as defineNetworkParent, Transform as defineTransform } from '../../components';
|
|
10
|
+
import * as networkUtils from '../../serialization/crdt/network/utils';
|
|
8
11
|
/**
|
|
9
12
|
* @internal
|
|
10
13
|
*/
|
|
11
14
|
export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
12
15
|
const transports = [];
|
|
16
|
+
// Components that we used on this system
|
|
17
|
+
const NetworkEntity = defineNetworkEntity(engine);
|
|
18
|
+
const NetworkParent = defineNetworkParent(engine);
|
|
19
|
+
const Transform = defineTransform(engine);
|
|
13
20
|
// Messages that we received at transport.onMessage waiting to be processed
|
|
14
21
|
const receivedMessages = [];
|
|
15
22
|
// Messages already processed by the engine but that we need to broadcast to other transports.
|
|
16
23
|
const broadcastMessages = [];
|
|
17
|
-
// Messages receieved by a transport that were outdated. We need to correct them
|
|
18
|
-
const outdatedMessages = [];
|
|
19
24
|
/**
|
|
20
25
|
*
|
|
21
26
|
* @param transportId tranport id to identiy messages
|
|
@@ -32,45 +37,41 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
32
37
|
let header;
|
|
33
38
|
while ((header = CrdtMessageProtocol.getHeader(buffer))) {
|
|
34
39
|
const offset = buffer.currentReadOffset();
|
|
40
|
+
let message = undefined;
|
|
35
41
|
if (header.type === CrdtMessageType.DELETE_COMPONENT) {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
41
|
-
});
|
|
42
|
+
message = DeleteComponent.read(buffer);
|
|
43
|
+
}
|
|
44
|
+
else if (header.type === CrdtMessageType.DELETE_COMPONENT_NETWORK) {
|
|
45
|
+
message = DeleteComponentNetwork.read(buffer);
|
|
42
46
|
}
|
|
43
47
|
else if (header.type === CrdtMessageType.PUT_COMPONENT) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
49
|
-
});
|
|
48
|
+
message = PutComponentOperation.read(buffer);
|
|
49
|
+
}
|
|
50
|
+
else if (header.type === CrdtMessageType.PUT_COMPONENT_NETWORK) {
|
|
51
|
+
message = PutNetworkComponentOperation.read(buffer);
|
|
50
52
|
}
|
|
51
53
|
else if (header.type === CrdtMessageType.DELETE_ENTITY) {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
57
|
-
});
|
|
54
|
+
message = DeleteEntity.read(buffer);
|
|
55
|
+
}
|
|
56
|
+
else if (header.type === CrdtMessageType.DELETE_ENTITY_NETWORK) {
|
|
57
|
+
message = DeleteEntityNetwork.read(buffer);
|
|
58
58
|
}
|
|
59
59
|
else if (header.type === CrdtMessageType.APPEND_VALUE) {
|
|
60
|
-
|
|
61
|
-
receivedMessages.push({
|
|
62
|
-
...message,
|
|
63
|
-
transportId,
|
|
64
|
-
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
65
|
-
});
|
|
60
|
+
message = AppendValueOperation.read(buffer);
|
|
66
61
|
// Unknown message, we skip it
|
|
67
62
|
}
|
|
68
63
|
else {
|
|
69
64
|
// consume the message
|
|
70
65
|
buffer.incrementReadOffset(header.length);
|
|
71
66
|
}
|
|
67
|
+
if (message) {
|
|
68
|
+
receivedMessages.push({
|
|
69
|
+
...message,
|
|
70
|
+
transportId,
|
|
71
|
+
messageBuffer: buffer.buffer().subarray(offset, buffer.currentReadOffset())
|
|
72
|
+
});
|
|
73
|
+
}
|
|
72
74
|
}
|
|
73
|
-
// TODO: do something if buffler.len>0
|
|
74
75
|
};
|
|
75
76
|
}
|
|
76
77
|
/**
|
|
@@ -81,46 +82,61 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
81
82
|
const messagesToProcess = value.splice(0, value.length);
|
|
82
83
|
return messagesToProcess;
|
|
83
84
|
}
|
|
85
|
+
/**
|
|
86
|
+
* Find the local entityId associated to the network component message.
|
|
87
|
+
* It's a mapping Network -> to Local
|
|
88
|
+
* If it's not a network message, return the entityId received by the message
|
|
89
|
+
*/
|
|
90
|
+
function findNetworkId(msg) {
|
|
91
|
+
const hasNetworkId = 'networkId' in msg;
|
|
92
|
+
if (hasNetworkId) {
|
|
93
|
+
for (const [entityId, network] of engine.getEntitiesWith(NetworkEntity)) {
|
|
94
|
+
if (network.networkId === msg.networkId && network.entityId === msg.entityId) {
|
|
95
|
+
return { entityId, network };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
return { entityId: msg.entityId };
|
|
100
|
+
}
|
|
84
101
|
/**
|
|
85
102
|
* This fn will be called on every tick.
|
|
86
103
|
* Process all the messages queue received by the transport
|
|
87
104
|
*/
|
|
88
105
|
async function receiveMessages() {
|
|
89
106
|
const messagesToProcess = getMessages(receivedMessages);
|
|
90
|
-
const bufferForOutdated = new ReadWriteByteBuffer();
|
|
91
107
|
const entitiesShouldBeCleaned = [];
|
|
92
108
|
for (const msg of messagesToProcess) {
|
|
93
|
-
|
|
94
|
-
|
|
109
|
+
let { entityId, network } = findNetworkId(msg);
|
|
110
|
+
// We receive a new Entity. Create the localEntity and map it to the NetworkEntity component
|
|
111
|
+
if (networkUtils.isNetworkMessage(msg) && !network) {
|
|
112
|
+
entityId = engine.addEntity();
|
|
113
|
+
network = { entityId: msg.entityId, networkId: msg.networkId };
|
|
114
|
+
NetworkEntity.createOrReplace(entityId, network);
|
|
115
|
+
}
|
|
116
|
+
if (msg.type === CrdtMessageType.DELETE_ENTITY || msg.type === CrdtMessageType.DELETE_ENTITY_NETWORK) {
|
|
117
|
+
entitiesShouldBeCleaned.push(entityId);
|
|
95
118
|
broadcastMessages.push(msg);
|
|
96
119
|
}
|
|
97
120
|
else {
|
|
98
|
-
const entityState = engine.entityContainer.getEntityState(
|
|
121
|
+
const entityState = engine.entityContainer.getEntityState(entityId);
|
|
99
122
|
// Skip updates from removed entityes
|
|
100
123
|
if (entityState === EntityState.Removed)
|
|
101
124
|
continue;
|
|
102
125
|
// Entities with unknown entities should update its entity state
|
|
103
126
|
if (entityState === EntityState.Unknown) {
|
|
104
|
-
engine.entityContainer.updateUsedEntity(
|
|
127
|
+
engine.entityContainer.updateUsedEntity(entityId);
|
|
105
128
|
}
|
|
106
129
|
const component = engine.getComponentOrNull(msg.componentId);
|
|
107
130
|
/* istanbul ignore else */
|
|
108
131
|
if (component) {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
else if (conflictMessage.type === CrdtMessageType.DELETE_COMPONENT) {
|
|
116
|
-
DeleteComponent.write(msg.entityId, component.componentId, conflictMessage.timestamp, bufferForOutdated);
|
|
117
|
-
}
|
|
118
|
-
outdatedMessages.push({
|
|
119
|
-
...msg,
|
|
120
|
-
messageBuffer: bufferForOutdated.buffer().subarray(offset, bufferForOutdated.currentWriteOffset())
|
|
121
|
-
});
|
|
132
|
+
if (msg.type === CrdtMessageType.PUT_COMPONENT &&
|
|
133
|
+
component.componentId === Transform.componentId &&
|
|
134
|
+
NetworkEntity.has(entityId) &&
|
|
135
|
+
NetworkParent.has(entityId)) {
|
|
136
|
+
msg.data = networkUtils.fixTransformParent(msg);
|
|
122
137
|
}
|
|
123
|
-
|
|
138
|
+
const [conflictMessage, value] = component.updateFromCrdt({ ...msg, entityId });
|
|
139
|
+
if (!conflictMessage) {
|
|
124
140
|
// Add message to transport queue to be processed by others transports
|
|
125
141
|
broadcastMessages.push(msg);
|
|
126
142
|
onProcessEntityComponentChange && onProcessEntityComponentChange(msg.entityId, msg.type, component, value);
|
|
@@ -134,12 +150,6 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
134
150
|
}
|
|
135
151
|
// the last stage of the syncrhonization is to delete the entities
|
|
136
152
|
for (const entity of entitiesShouldBeCleaned) {
|
|
137
|
-
// If we tried to resend outdated message and the entity was deleted before, we avoid sending them.
|
|
138
|
-
for (let i = outdatedMessages.length - 1; i >= 0; i--) {
|
|
139
|
-
if (outdatedMessages[i].entityId === entity && outdatedMessages[i].type !== CrdtMessageType.DELETE_ENTITY) {
|
|
140
|
-
outdatedMessages.splice(i, 1);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
153
|
for (const definition of engine.componentsIter()) {
|
|
144
154
|
// TODO: check this with pato/pravus
|
|
145
155
|
definition.entityDeleted(entity, true);
|
|
@@ -154,7 +164,6 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
154
164
|
async function sendMessages(entitiesDeletedThisTick) {
|
|
155
165
|
// CRDT Messages will be the merge between the recieved transport messages and the new crdt messages
|
|
156
166
|
const crdtMessages = getMessages(broadcastMessages);
|
|
157
|
-
const outdatedMessagesBkp = getMessages(outdatedMessages);
|
|
158
167
|
const buffer = new ReadWriteByteBuffer();
|
|
159
168
|
for (const component of engine.componentsIter()) {
|
|
160
169
|
for (const message of component.getCrdtUpdates()) {
|
|
@@ -199,25 +208,58 @@ export function crdtSceneSystem(engine, onProcessEntityComponentChange) {
|
|
|
199
208
|
for (const index in transports) {
|
|
200
209
|
const transportIndex = Number(index);
|
|
201
210
|
const transport = transports[transportIndex];
|
|
211
|
+
const isRendererTransport = transport.type === 'renderer';
|
|
212
|
+
const isNetworkTransport = transport.type === 'network';
|
|
202
213
|
transportBuffer.resetBuffer();
|
|
203
|
-
|
|
204
|
-
// So we can fix their crdt state
|
|
205
|
-
for (const message of outdatedMessagesBkp) {
|
|
206
|
-
if (message.transportId === transportIndex &&
|
|
207
|
-
// TODO: This is an optimization, the state should converge anyway, whatever the message is sent.
|
|
208
|
-
// Avoid sending multiple messages for the same entity-componentId
|
|
209
|
-
!crdtMessages.find((m) => m.entityId === message.entityId &&
|
|
210
|
-
// TODO: as any, with multiple type of messages, it should have many checks before the check for similar messages
|
|
211
|
-
m.componentId &&
|
|
212
|
-
m.componentId === message.componentId)) {
|
|
213
|
-
transportBuffer.writeBuffer(message.messageBuffer, false);
|
|
214
|
-
}
|
|
215
|
-
}
|
|
214
|
+
const buffer = new ReadWriteByteBuffer();
|
|
216
215
|
// Then we send all the new crdtMessages that the transport needs to process
|
|
217
216
|
for (const message of crdtMessages) {
|
|
218
|
-
|
|
219
|
-
|
|
217
|
+
// Avoid echo messages
|
|
218
|
+
if (message.transportId === transportIndex)
|
|
219
|
+
continue;
|
|
220
|
+
// Redundant message for the transport
|
|
221
|
+
if (!transport.filter(message))
|
|
222
|
+
continue;
|
|
223
|
+
const { entityId } = findNetworkId(message);
|
|
224
|
+
const transformNeedsFix = 'componentId' in message &&
|
|
225
|
+
message.componentId === Transform.componentId &&
|
|
226
|
+
Transform.has(entityId) &&
|
|
227
|
+
NetworkParent.has(entityId) &&
|
|
228
|
+
NetworkEntity.has(entityId);
|
|
229
|
+
// If there was a LOCAL change in the transform. Add the parent to that transform
|
|
230
|
+
if (isRendererTransport && message.type === CrdtMessageType.PUT_COMPONENT && transformNeedsFix) {
|
|
231
|
+
const parent = findNetworkId(NetworkParent.get(entityId));
|
|
232
|
+
const transformData = networkUtils.fixTransformParent(message, Transform.get(entityId), parent.entityId);
|
|
233
|
+
const offset = buffer.currentWriteOffset();
|
|
234
|
+
PutComponentOperation.write(entityId, message.timestamp, message.componentId, transformData, buffer);
|
|
235
|
+
transportBuffer.writeBuffer(buffer.buffer().subarray(offset, buffer.currentWriteOffset()), false);
|
|
236
|
+
continue;
|
|
237
|
+
}
|
|
238
|
+
if (isRendererTransport && networkUtils.isNetworkMessage(message)) {
|
|
239
|
+
// If it's the renderer transport and its a NetworkMessage, we need to fix the entityId field and convert it to a known Message.
|
|
240
|
+
// PUT_NETWORK_COMPONENT -> PUT_COMPONENT
|
|
241
|
+
let transformData = 'data' in message ? message.data : new Uint8Array();
|
|
242
|
+
if (transformNeedsFix) {
|
|
243
|
+
const parent = findNetworkId(NetworkParent.get(entityId));
|
|
244
|
+
transformData = networkUtils.fixTransformParent(message, Transform.get(entityId), parent.entityId);
|
|
245
|
+
}
|
|
246
|
+
networkUtils.networkMessageToLocal({ ...message, data: transformData }, entityId, buffer, transportBuffer);
|
|
247
|
+
// Iterate the next message
|
|
248
|
+
continue;
|
|
249
|
+
}
|
|
250
|
+
// If its a network transport and its a PUT_COMPONENT that has a NetworkEntity component, we need to send this message
|
|
251
|
+
// through comms with the EntityID and NetworkID from ther NetworkEntity so everyone can recieve this message and map to their custom entityID.
|
|
252
|
+
if (isNetworkTransport && !networkUtils.isNetworkMessage(message)) {
|
|
253
|
+
const networkData = NetworkEntity.getOrNull(message.entityId);
|
|
254
|
+
// If it has networkData convert the message to PUT_NETWORK_COMPONENT.
|
|
255
|
+
if (networkData) {
|
|
256
|
+
networkUtils.localMessageToNetwork(message, networkData, buffer, transportBuffer);
|
|
257
|
+
// Iterate the next message
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
220
260
|
}
|
|
261
|
+
// Common message
|
|
262
|
+
transportBuffer.writeBuffer(message.messageBuffer, false);
|
|
221
263
|
}
|
|
222
264
|
const message = transportBuffer.currentWriteOffset() ? transportBuffer.toBinary() : new Uint8Array([]);
|
|
223
265
|
await transport.send(message);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CrdtMessageBody } from '../../serialization/crdt/types';
|
|
1
|
+
import { CrdtMessageBody, CrdtNetworkMessageBody } from '../../serialization/crdt/types';
|
|
2
2
|
/**
|
|
3
3
|
* @public
|
|
4
4
|
*/
|
|
@@ -6,6 +6,13 @@ export type ReceiveMessage = CrdtMessageBody & {
|
|
|
6
6
|
transportId?: number;
|
|
7
7
|
messageBuffer: Uint8Array;
|
|
8
8
|
};
|
|
9
|
+
/**
|
|
10
|
+
* @public
|
|
11
|
+
*/
|
|
12
|
+
export type ReceiveNetworkMessage = CrdtNetworkMessageBody & {
|
|
13
|
+
transportId?: number;
|
|
14
|
+
messageBuffer: Uint8Array;
|
|
15
|
+
};
|
|
9
16
|
/**
|
|
10
17
|
* @public
|
|
11
18
|
*/
|
|
@@ -17,4 +24,5 @@ export type Transport = {
|
|
|
17
24
|
send(message: Uint8Array): Promise<void>;
|
|
18
25
|
onmessage?(message: Uint8Array): void;
|
|
19
26
|
filter(message: Omit<TransportMessage, 'messageBuffer'>): boolean;
|
|
27
|
+
type?: string;
|
|
20
28
|
};
|
|
@@ -8,6 +8,8 @@ import { TweenComponentDefinitionExtended } from './extended/Tween';
|
|
|
8
8
|
import { LwwComponentGetter, GSetComponentGetter } from './generated/index.gen';
|
|
9
9
|
import { NameType } from './manual/Name';
|
|
10
10
|
import { ISyncComponentsType } from './manual/SyncComponents';
|
|
11
|
+
import { INetowrkEntityType } from './manual/NetworkEntity';
|
|
12
|
+
import { INetowrkParentType } from './manual/NetworkParent';
|
|
11
13
|
import { TransformComponentExtended } from './manual/Transform';
|
|
12
14
|
export * from './generated/index.gen';
|
|
13
15
|
export type { GrowOnlyValueSetComponentDefinition, LastWriteWinElementSetComponentDefinition, LwwComponentGetter, GSetComponentGetter };
|
|
@@ -25,3 +27,11 @@ export declare const Name: (engine: Pick<IEngine, 'defineComponent'>) => LastWri
|
|
|
25
27
|
* @alpha
|
|
26
28
|
*/
|
|
27
29
|
export declare const SyncComponents: (engine: Pick<IEngine, 'defineComponent'>) => LastWriteWinElementSetComponentDefinition<ISyncComponentsType>;
|
|
30
|
+
/**
|
|
31
|
+
* @alpha
|
|
32
|
+
*/
|
|
33
|
+
export declare const NetworkEntity: (engine: Pick<IEngine, 'defineComponent'>) => LastWriteWinElementSetComponentDefinition<INetowrkEntityType>;
|
|
34
|
+
/**
|
|
35
|
+
* @alpha
|
|
36
|
+
*/
|
|
37
|
+
export declare const NetworkParent: (engine: Pick<IEngine, 'defineComponent'>) => LastWriteWinElementSetComponentDefinition<INetowrkParentType>;
|
|
@@ -17,7 +17,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.SyncComponents = exports.Name = exports.Tween = exports.MeshCollider = exports.MeshRenderer = exports.Animator = exports.Material = exports.Transform = void 0;
|
|
20
|
+
exports.NetworkParent = exports.NetworkEntity = exports.SyncComponents = exports.Name = exports.Tween = exports.MeshCollider = exports.MeshRenderer = exports.Animator = exports.Material = exports.Transform = void 0;
|
|
21
21
|
const Animator_1 = require("./extended/Animator");
|
|
22
22
|
const Material_1 = require("./extended/Material");
|
|
23
23
|
const MeshCollider_1 = require("./extended/MeshCollider");
|
|
@@ -25,6 +25,8 @@ const MeshRenderer_1 = require("./extended/MeshRenderer");
|
|
|
25
25
|
const Tween_1 = require("./extended/Tween");
|
|
26
26
|
const Name_1 = __importDefault(require("./manual/Name"));
|
|
27
27
|
const SyncComponents_1 = __importDefault(require("./manual/SyncComponents"));
|
|
28
|
+
const NetworkEntity_1 = __importDefault(require("./manual/NetworkEntity"));
|
|
29
|
+
const NetworkParent_1 = __importDefault(require("./manual/NetworkParent"));
|
|
28
30
|
const Transform_1 = require("./manual/Transform");
|
|
29
31
|
__exportStar(require("./generated/index.gen"), exports);
|
|
30
32
|
/* @__PURE__ */
|
|
@@ -57,3 +59,15 @@ exports.Name = Name;
|
|
|
57
59
|
/* @__PURE__ */
|
|
58
60
|
const SyncComponents = (engine) => (0, SyncComponents_1.default)(engine);
|
|
59
61
|
exports.SyncComponents = SyncComponents;
|
|
62
|
+
/**
|
|
63
|
+
* @alpha
|
|
64
|
+
*/
|
|
65
|
+
/* @__PURE__ */
|
|
66
|
+
const NetworkEntity = (engine) => (0, NetworkEntity_1.default)(engine);
|
|
67
|
+
exports.NetworkEntity = NetworkEntity;
|
|
68
|
+
/**
|
|
69
|
+
* @alpha
|
|
70
|
+
*/
|
|
71
|
+
/* @__PURE__ */
|
|
72
|
+
const NetworkParent = (engine) => (0, NetworkParent_1.default)(engine);
|
|
73
|
+
exports.NetworkParent = NetworkParent;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Entity } from '../../engine';
|
|
2
|
+
import { IEngine, LastWriteWinElementSetComponentDefinition } from '../../engine/types';
|
|
3
|
+
export interface INetowrkEntityType {
|
|
4
|
+
networkId: number;
|
|
5
|
+
entityId: Entity;
|
|
6
|
+
}
|
|
7
|
+
export type INetowrkEntity = LastWriteWinElementSetComponentDefinition<INetowrkEntityType>;
|
|
8
|
+
declare function defineNetworkEntityComponent(engine: Pick<IEngine, 'defineComponent'>): import("../../engine").MapComponentDefinition<import("../..").MapResult<{
|
|
9
|
+
networkId: import("../../schemas").ISchema<number>;
|
|
10
|
+
entityId: import("../../schemas").ISchema<Entity>;
|
|
11
|
+
}>>;
|
|
12
|
+
export default defineNetworkEntityComponent;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const schemas_1 = require("../../schemas");
|
|
4
|
+
function defineNetworkEntityComponent(engine) {
|
|
5
|
+
const EntityNetwork = engine.defineComponent('core-schema::Network-Entity', {
|
|
6
|
+
networkId: schemas_1.Schemas.Int64,
|
|
7
|
+
entityId: schemas_1.Schemas.Entity
|
|
8
|
+
});
|
|
9
|
+
return EntityNetwork;
|
|
10
|
+
}
|
|
11
|
+
exports.default = defineNetworkEntityComponent;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { Entity } from '../../engine';
|
|
2
|
+
import { IEngine, LastWriteWinElementSetComponentDefinition } from '../../engine/types';
|
|
3
|
+
export interface INetowrkParentType {
|
|
4
|
+
networkId: number;
|
|
5
|
+
entityId: Entity;
|
|
6
|
+
}
|
|
7
|
+
export type INetowrkParent = LastWriteWinElementSetComponentDefinition<INetowrkParentType>;
|
|
8
|
+
declare function defineNetworkParentComponent(engine: Pick<IEngine, 'defineComponent'>): import("../../engine").MapComponentDefinition<import("../..").MapResult<{
|
|
9
|
+
networkId: import("../../schemas").ISchema<number>;
|
|
10
|
+
entityId: import("../../schemas").ISchema<Entity>;
|
|
11
|
+
}>>;
|
|
12
|
+
export default defineNetworkParentComponent;
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const schemas_1 = require("../../schemas");
|
|
4
|
+
function defineNetworkParentComponent(engine) {
|
|
5
|
+
const EntityNetwork = engine.defineComponent('core-schema::Network-Parent', {
|
|
6
|
+
networkId: schemas_1.Schemas.Int64,
|
|
7
|
+
entityId: schemas_1.Schemas.Entity
|
|
8
|
+
});
|
|
9
|
+
return EntityNetwork;
|
|
10
|
+
}
|
|
11
|
+
exports.default = defineNetworkParentComponent;
|
|
@@ -6,3 +6,5 @@ export type { TweenHelper, TweenComponentDefinitionExtended } from './extended/T
|
|
|
6
6
|
export type { TransformComponentExtended, TransformTypeWithOptionals } from './manual/Transform';
|
|
7
7
|
export type { NameComponent, NameType } from './manual/Name';
|
|
8
8
|
export type { ISyncComponents, ISyncComponentsType } from './manual/SyncComponents';
|
|
9
|
+
export type { INetowrkEntity, INetowrkEntityType } from './manual/NetworkEntity';
|
|
10
|
+
export type { INetowrkParent, INetowrkParentType } from './manual/NetworkParent';
|
package/dist-cjs/engine/index.js
CHANGED
|
@@ -44,7 +44,6 @@ function preEngine() {
|
|
|
44
44
|
const entityContainer = (0, entity_1.EntityContainer)();
|
|
45
45
|
const componentsDefinition = new Map();
|
|
46
46
|
const systems = (0, systems_1.SystemContainer)();
|
|
47
|
-
let networkManager;
|
|
48
47
|
let sealed = false;
|
|
49
48
|
function addSystem(fn, priority = systems_1.SYSTEMS_REGULAR_PRIORITY, name) {
|
|
50
49
|
systems.add(fn, priority, name);
|
|
@@ -52,30 +51,22 @@ function preEngine() {
|
|
|
52
51
|
function removeSystem(selector) {
|
|
53
52
|
return systems.remove(selector);
|
|
54
53
|
}
|
|
55
|
-
function getNetworkManager() {
|
|
56
|
-
if (!networkManager)
|
|
57
|
-
throw new Error('Network manager not initialized. Start CRDT Server');
|
|
58
|
-
return networkManager;
|
|
59
|
-
}
|
|
60
|
-
function addNetworkManager(reservedLocalEntities, range) {
|
|
61
|
-
entityContainer.setNetworkEntitiesRange(reservedLocalEntities, range);
|
|
62
|
-
networkManager = {
|
|
63
|
-
addEntity: () => entityContainer.generateEntity(true)
|
|
64
|
-
};
|
|
65
|
-
return networkManager;
|
|
66
|
-
}
|
|
67
54
|
function addEntity() {
|
|
68
55
|
const entity = entityContainer.generateEntity();
|
|
69
56
|
return entity;
|
|
70
57
|
}
|
|
71
58
|
function removeEntity(entity) {
|
|
72
59
|
for (const [, component] of componentsDefinition) {
|
|
60
|
+
// TODO: hack for the moment. It should be enough to delete the entity, but the renderer is not cleaning the components.
|
|
61
|
+
// So we still need the NetworkEntity to forward this message to the SyncTransport.
|
|
62
|
+
if (component.componentName === 'core-schema::Network-Entity')
|
|
63
|
+
continue;
|
|
73
64
|
component.entityDeleted(entity, true);
|
|
74
65
|
}
|
|
75
66
|
return entityContainer.removeEntity(entity);
|
|
76
67
|
}
|
|
77
68
|
function removeEntityWithChildren(entity) {
|
|
78
|
-
return (0, tree_1.removeEntityWithChildren)({ removeEntity, defineComponentFromSchema, getEntitiesWith }, entity);
|
|
69
|
+
return (0, tree_1.removeEntityWithChildren)({ removeEntity, defineComponentFromSchema, getEntitiesWith, defineComponent }, entity);
|
|
79
70
|
}
|
|
80
71
|
function registerComponentDefinition(componentName, component) {
|
|
81
72
|
/* istanbul ignore next */
|
|
@@ -152,7 +143,7 @@ function preEngine() {
|
|
|
152
143
|
const componentId = typeof componentIdOrName === 'number' ? componentIdOrName : (0, component_number_1.componentNumberFromName)(componentIdOrName);
|
|
153
144
|
const component = componentsDefinition.get(componentId);
|
|
154
145
|
if (!component) {
|
|
155
|
-
throw new Error(`Component ${
|
|
146
|
+
throw new Error(`Component ${componentIdOrName} not found. You need to declare the components at the beginnig of the engine declaration`);
|
|
156
147
|
}
|
|
157
148
|
return component;
|
|
158
149
|
}
|
|
@@ -226,9 +217,7 @@ function preEngine() {
|
|
|
226
217
|
registerComponentDefinition,
|
|
227
218
|
entityContainer,
|
|
228
219
|
componentsIter,
|
|
229
|
-
seal
|
|
230
|
-
addNetworkManager,
|
|
231
|
-
getNetworkManager
|
|
220
|
+
seal
|
|
232
221
|
};
|
|
233
222
|
}
|
|
234
223
|
/**
|
|
@@ -272,9 +261,7 @@ function Engine(options) {
|
|
|
272
261
|
CameraEntity: 2,
|
|
273
262
|
getEntityState: partialEngine.entityContainer.getEntityState,
|
|
274
263
|
addTransport: crdtSystem.addTransport,
|
|
275
|
-
entityContainer: partialEngine.entityContainer
|
|
276
|
-
addNetworkManager: partialEngine.addNetworkManager,
|
|
277
|
-
getNetworkManager: partialEngine.getNetworkManager
|
|
264
|
+
entityContainer: partialEngine.entityContainer
|
|
278
265
|
};
|
|
279
266
|
}
|
|
280
267
|
exports.Engine = Engine;
|
|
@@ -86,7 +86,10 @@ function createUpdateLwwFromCrdt(componentId, timestamps, schema, data) {
|
|
|
86
86
|
}
|
|
87
87
|
return (msg) => {
|
|
88
88
|
/* istanbul ignore next */
|
|
89
|
-
if (msg.type !== crdt_1.CrdtMessageType.PUT_COMPONENT &&
|
|
89
|
+
if (msg.type !== crdt_1.CrdtMessageType.PUT_COMPONENT &&
|
|
90
|
+
msg.type !== crdt_1.CrdtMessageType.PUT_COMPONENT_NETWORK &&
|
|
91
|
+
msg.type !== crdt_1.CrdtMessageType.DELETE_COMPONENT &&
|
|
92
|
+
msg.type !== crdt_1.CrdtMessageType.DELETE_COMPONENT_NETWORK)
|
|
90
93
|
/* istanbul ignore next */
|
|
91
94
|
return [null, data.get(msg.entityId)];
|
|
92
95
|
const action = crdtRuleForCurrentState(msg);
|
|
@@ -95,7 +98,7 @@ function createUpdateLwwFromCrdt(componentId, timestamps, schema, data) {
|
|
|
95
98
|
case crdt_1.ProcessMessageResultType.StateUpdatedData:
|
|
96
99
|
case crdt_1.ProcessMessageResultType.StateUpdatedTimestamp: {
|
|
97
100
|
timestamps.set(entity, msg.timestamp);
|
|
98
|
-
if (msg.type === crdt_1.CrdtMessageType.PUT_COMPONENT) {
|
|
101
|
+
if (msg.type === crdt_1.CrdtMessageType.PUT_COMPONENT || msg.type === crdt_1.CrdtMessageType.PUT_COMPONENT_NETWORK) {
|
|
99
102
|
const buf = new ByteBuffer_1.ReadWriteByteBuffer(msg.data);
|
|
100
103
|
data.set(entity, schema.deserialize(buf));
|
|
101
104
|
}
|
|
@@ -227,16 +227,4 @@ export interface IEngine {
|
|
|
227
227
|
* components that will be available to this engine and to run optimizations.
|
|
228
228
|
*/
|
|
229
229
|
seal(): void;
|
|
230
|
-
/**
|
|
231
|
-
* @alpha
|
|
232
|
-
* Initialize network manager
|
|
233
|
-
*/
|
|
234
|
-
addNetworkManager(reservedLocalEntities: number, range: [number, number]): {
|
|
235
|
-
addEntity: IEngine['addEntity'];
|
|
236
|
-
};
|
|
237
|
-
/**
|
|
238
|
-
* @alpha
|
|
239
|
-
* Get netowrk manager to create entities.
|
|
240
|
-
*/
|
|
241
|
-
getNetworkManager(): ReturnType<IEngine['addNetworkManager']>;
|
|
242
230
|
}
|