decentraland / sdk

PM repository for SDK
Apache License 2.0
4 stars 4 forks source link

Discussion: Foreign entities #441

Closed leanmendoza closed 1 year ago

leanmendoza commented 2 years ago

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

enum EntityAccessMode {
    ACCESS_READ_ONLY = 0;
    ACCESS_WRITE = 1;
}

message RequestEntitiesUpdatesRequest {
    int32 scene_id = 1;
    int32 entity_from = 2;
    int32 entity_to = 3;
    repeated int32 component_ids = 4; // default [] means all
    EntityAccessMode mode = 5;
}

message RequestEntitiesUpdatesResponse {
    int32 scene_id = 1;
    int32 entity_from = 2;
    int32 entity_to = 3;
    repeated int32 component_ids = 4; // default [] means all
    EntityAccessMode mode = 5;
}

service SceneComunicationService {
    rpc requestEntitiesUpdate(RequestEntitiesUpdatesRequest) return RequestEntitiesUpdatesResponse {}
}

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:

Entity 1000:

Entity 1001:

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.

nearnshaw commented 1 year ago

Already addressed in Shape Up https://www.notion.so/decentraland/Positions-data-of-all-players-in-scene-9900fc0b293147e38fe025d6edbf3c69