The communication between scenes can be very useful to share entities and their respective component data to create interactive content. In this sense, kernel is the channel to achieve it. An example that is not so direct, but can be thought of as two scenes with identically replicated data, is the scene itself and its copy in the renderer for graphical representation. In this case, the renderer side only accepts the components that have a rendering intent.
The communication diagram with the renderer is well established and can be simplified to:
Renderer accumulates component updates
The scene at each tick does a pull of all these updates
All messages are processed and systems run
New updates are sent to the renderer.
For a scene, this can be similar, with the addition of an initial pairing. In the renderer case, the entities are mapped 1-1 because the state is stored in a CRDT instantiated for each scene.
Initial pairing: communication between scenes by RPC
The initial flow would be, with the request of the entities to the owner one;
Requester->Kernel: requestEntitiesUpdate({ sceneId })
Kernel->Kernel: scenes[sceneId].pushEvent(requestEntitiesUpdate)
Note right of Owner: Tick event
Owner->Kernel: pullEvents
Kernel->Owner: sendEventArray
Owner->Owner: handleRequestEntitiesUpdate
Owner->Kernel: Response requestEntitiesUpdate
Note left of Requester: Tick event
Requester->Kernel: pullEvents
Kernel->Requester: sendEventArray
Requester->Requester: Map Foreign entities
Synchronization
The sync part only has to create a custom transport that filters the entity IDs selected by the requester and track the right path to the destination.
Replicating the state
When we talk about the sync, it's really about replicating the same state of some entities and components from the target scene. Replicating this data means having the same information in memory in each scene.
Approach: mapping entities
Some of this is explained here. With this approach, the entities from another scene would be mapped to entities from the current scene.
For example all entities from 1000 to 1500 from sceneId: 'dcl-avatar-scenes' are mapped to my entities from 30000 to 30500.
Pro
The state is shared and the foreign entities are part of the local scene state, so it can be replicated for the behavior of our scene.
Cons
The same feature in Pro, we probably don't want to iterate in our system the foreign entities and we have to add conditional everywhere. Other cons: if we need to send some updates, the remote scene needs to know what entity we're referencing.
Approach: instance a ECS Engine
The goal of this approach is to instance a new Engine for each scene that we want to retrieve a portion of its state.
Pro
The entities are map 1-1 like the renderer, we can add the specific systems to this engine, and the update would be simpler.
A lightweight version of the Engine instance should be possible, maybe selecting what component id should be defined, less transport defined, and fewer features.
Use case: avatar scenes
Scenario
The avatar scene has the player entities starting at entity id 1000, each entity has the Avatar component, the Transform, etc. For example:
When a player appears in the world (island), the avatar scene creates the entity and adds these components.
For every update that the comms module emits, the internal state of the entities is updated. If some player disconnects, the components are removed, which means the entity is now empty or 'deleted'.
It maybe happens that an entity is recycled by a change of userId from the AvatarComponent as one player has just disconnected and another player has just connected in the same instant.
If we want to know about players from another scene, we need to have access to this data.
Kernel and scene states
The communication between scenes can be very useful to share entities and their respective component data to create interactive content. In this sense, kernel is the channel to achieve it. An example that is not so direct, but can be thought of as two scenes with identically replicated data, is the scene itself and its copy in the renderer for graphical representation. In this case, the renderer side only accepts the components that have a rendering intent.
The communication diagram with the renderer is well established and can be simplified to:
For a scene, this can be similar, with the addition of an initial pairing. In the renderer case, the entities are mapped 1-1 because the state is stored in a CRDT instantiated for each scene.
Initial pairing: communication between scenes by RPC
The initial flow would be, with the request of the entities to the owner one;
Synchronization
The sync part only has to create a custom transport that filters the entity IDs selected by the requester and track the right path to the destination.
Replicating the state
When we talk about the sync, it's really about replicating the same state of some entities and components from the target scene. Replicating this data means having the same information in memory in each scene.
Approach: mapping entities
Some of this is explained here. With this approach, the entities from another scene would be mapped to entities from the current scene. For example all entities from 1000 to 1500 from
sceneId: 'dcl-avatar-scenes'
are mapped to my entities from 30000 to 30500.Pro
The state is shared and the foreign entities are part of the local scene state, so it can be replicated for the behavior of our scene.
Cons
The same feature in Pro, we probably don't want to iterate in our system the foreign entities and we have to add conditional everywhere. Other cons: if we need to send some updates, the remote scene needs to know what entity we're referencing.
Approach: instance a ECS Engine
The goal of this approach is to instance a new
Engine
for each scene that we want to retrieve a portion of its state.Pro
The entities are map 1-1 like the renderer, we can add the specific systems to this engine, and the update would be simpler.
A lightweight version of the Engine instance should be possible, maybe selecting what component id should be defined, less transport defined, and fewer features.
Use case: avatar scenes
Scenario
The avatar scene has the player entities starting at entity id 1000, each entity has the Avatar component, the Transform, etc. For example:
Entity 1000:
AvatarComponent { userId: '0xf43004f43f034bc34f...' }
Transform { position: { x: 212.2, y: 322....}, scale, rotation }
Entity 1001:
AvatarComponent { userId: '0xce91x192x3933234ca...' }
Transform { position: { x: 112.2, y: 64.0, ...}, scale, rotation }
When a player appears in the world (island), the avatar scene creates the entity and adds these components. For every update that the comms module emits, the internal state of the entities is updated. If some player disconnects, the components are removed, which means the entity is now empty or 'deleted'. It maybe happens that an entity is recycled by a change of
userId
from theAvatarComponent
as one player has just disconnected and another player has just connected in the same instant.If we want to know about players from another scene, we need to have access to this data.