# Engine API

O `EngineApi` o módulo fornece acesso à estrutura Entity-Component-System compartilhada entre o World Explorer (que executa o loop do jogo) e cenas individuais (que gerenciam suas próprias entidades), além de utilitários relacionados.

```ts
const engine = require("~system/EngineApi");
```

Este módulo é o mais complexo e rico em funcionalidades, e o que provavelmente receberá atualizações e extensões. É onde reside a maior parte do poder do Decentraland, já que determina o tipo de experiências que as pessoas podem criar.

Este módulo contém os seguintes métodos:

* [`function crdtSendToRenderer`](#crdtSendToRenderer)
* [`function crdtGetState`](#crdtGetState)

### Introdução

O `EngineApi` o módulo foi projetado para sincronizar o estado do mundo entre o Explorer e a cena trocando atualizações, eventos e comandos. Implementa um protocolo de mensagens extensível que permite às cenas, entre outras coisas:

* Criar e destruir entidades.
* Anexar, atualizar e remover componentes.
* Lançar raios no ambiente 3D e detectar colisões.
* Receber eventos como entrada do jogador.

```
.------------------------------------------------------.
| World Explorer                                       |
|                                                      |
|                   [Engine API]                       |
|                        |                             |
|  .--------.            |              .------------. |
|  |        |<-----------+<-------------+  Runtime   | |
|  |  Game  |            |   Commands   |  .------.  | |
|  |        |   Events   |              |  | Scene | | |
|  | Engine +----------->+------------->|  |       | | |
|  |        |            |              |  '-------' | |
|  '--------'            |              '------------' |
|                                                      |
'------------------------------------------------------'

```

{% hint style="info" %}
O `EngineApi` o módulo está passando por reformas. Se você for às definições de origem, encontrará métodos legados que não são mais usados ou que podem ser implementados como no-ops na versão mais recente (mais sobre isso abaixo).
{% endhint %}

### Framework ECS

Com o `EngineApi` módulo vem uma implementação genérica e extensível de um framework ECS compartilhado, que permite tanto às cenas quanto ao próprio motor do jogo criar entidades, anexar componentes e atualizar seus estados.

As mudanças feitas de qualquer lado são refletidas no outro por meio da troca de mensagens. O mecanismo usado ([veja abaixo](#synchronization)) garante que ambas as partes concordem sobre a ordem de quaisquer atualizações e alcancem consistência eventual sobre o estado do mundo.

O World Explorer implementa um conjunto bem conhecido de componentes básicos (posições, formas, texturas, mídias e mais), e sabe desserializar e aplicar alterações ao seu estado quando recebe uma atualização. Cenas usando o [SDK](https://docs.decentraland.org/creator) também têm utilitários para criar componentes customizados, mas esses são totalmente gerenciados pela cena (não sincronizados) e, portanto, estão fora do escopo do protocolo.

{% hint style="info" %}
A maioria das cenas usa o Decentraland SDK, que encapsula o `EngineApi` módulo e oferece uma interface de nível mais alto e muito melhor para desenvolvedores de conteúdo. Cenas que acessam diretamente o protocolo de mensagens neste módulo são extremamente raras.
{% endhint %}

#### Identificando Entidades <a href="#identifying" id="identifying"></a>

IDs de entidade são inteiros simples começando com `0`.

Por protocolo, IDs abaixo de `512` são reservados para o World Explorer, e portanto inválidos para entidades criadas pela cena. Números `0`, `1` e `2` estão de fato em uso (veja [entidades básicas](https://github.com/decentraland/docs/blob/main/contributor/entities/README.md)), com o restante do intervalo disponível para futuras extensões.

Cada cena tem sua própria faixa privada de IDs, que o Explorer pode mapear de forma transparente para um identificador global entre todas as entidades no motor do jogo.

Com o tempo, à medida que entidades são criadas e excluídas, é perfeitamente válido reutilizar IDs que foram previamente liberados — de fato, isso pode ser necessário por razões de desempenho ([veja abaixo](https://github.com/decentraland/docs/blob/main/contributor/runtime-modules/crdtStateDeleteEntities/README.md)).

#### Sincronização <a href="#synchronization" id="synchronization"></a>

Tanto o World Explorer quanto a cena devem gerenciar um conjunto de entidades e componentes compartilhados, onde atualizações vêm de ambos os lados de forma assíncrona. Sem medidas adicionais, suas versões do estado do mundo podem (e vão) acabar diferentes.

Para prevenir isso, um CRDT ([tipo de dado replicado sem conflitos](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)) é usado para chegar a um acordo entre ambas as partes trocando mensagens com atualizações. Isso oferece várias vantagens:

* Ambas as partes podem atualizar o estado de forma independente e concorrente.
* Conflitos são gerenciados por uma estratégia de resolução compartilhada aplicada localmente por ambas as partes.
* Ambas as partes têm garantia de convergir para um estado compartilhado e idêntico.
* Além de um pequeno overhead no tamanho das mensagens, nenhuma coordenação adicional ou ida-e-volta de mensagens é necessária.

{% hint style="info" %}
Esta página explica o uso do CRDT para sincronizar estado entre uma cena executando localmente e o motor do jogo, mas o verdadeiro potencial deste mecanismo está em cenários multiplayer. Usando este protocolo, jogadores podem coordenar seu estado de jogo e compartilhar a mesma experiência.
{% endhint %}

Para que isso funcione, todas as mensagens devem ser comutativas e idempotentes. Mensagens fora de ordem podem ser aplicadas mesmo se uma suposta atualização intermediária ainda não tiver sido recebida, e mensagens iguais podem ser reprocessadas sem quebrar a consistência.

Vamos revisitar o diagrama arquitetônico acima, agora focando no CRDT:

```
.-------------------------------------------------------.
| World Explorer                                        |
|                                                       |
| .-------------.                       .------------.  |
| | Game Engine |                       |   Scene     | |
| |  .-------.  |                       |  .-------.  | |
| |  |       +--+-----------------------+->|       |  | |
| |  | CRDT  |  |     Sync Protocol     |  | CRDT  |  | |
| |  |       |<-+-----------------------+--+       |  | |
| |  '-------'  |                       |  '-------'  | |
| |             |                       |             | |
| '-------------'                       '-------------' |
'-------------------------------------------------------'

```

A implementação do mecanismo de sincronização CRDT consiste em três partes:

1. Uma representação interna do estado de todas as entidades e componentes.
2. Uma estratégia de resolução de conflitos para escolher entre estados concorrentes.
3. Um protocolo de mensagens para comunicar e processar atualizações ao estado.

**Estado do CRDT**

O CRDT mantém o estado de todos os componentes para todas as suas entidades anexadas, e pode ser armazenado em um mapeamento de componentes para estados com carimbo de tempo (timestamp) para cada entidade. Em pseudocódigo:

```ts
// O estado (completamente genérico) de um componente para uma entidade específica, em um ponto no tempo:
type EntityComponentState = { timestamp: number; state: byte[] };

// O estado de um componente para todas as entidades anexadas:
type ComponentState = Map<EntityId, EntityComponentState>;

// O estado completo do CRDT:
type CRDTState = Map<ComponentId, ComponentState>;
```

Timestamps na verdade não são timestamps no estilo Unix. Eles são um tipo de contador incremental para sequenciar atualizações, sem relação com o tempo do relógio. Mais sobre isso [abaixo](#timestamps).

Como a camada CRDT não conhece (nem precisa conhecer) todos os componentes disponíveis, o estado inicial é um mapa vazio. Entradas são criadas sob demanda quando operações envolvem um `ComponentId` ou `EntityId` que ainda não estava mapeado. Quando uma entidade é removida, o estado associado é excluído de todos os componentes.

Vamos ilustrar isso com mais pseudocódigo.

```ts
const crdtState = new Map(); // vazio, automaticamente abriremos espaço para novos componentes e entidades
```

```ts
function putEntityComponentState(componentId, entityId, entityComponentState) {
  // Se este componente ainda não é conhecido, adicioná-lo:
  if (!crdtState[componentId]) {
    crdtState[componentId] = new Map();
  }

  // Se esta entidade não tem estado para este componente, adicioná-lo e pronto:
  if (!crdtState[componentId][entityId] && shouldCreate(entityId)) {
    crdtState[componentId][entityId] = entityComponentState;
    return;
  }

  // Neste ponto, temos dois estados concorrentes. O que acabamos de receber não é necessariamente o
  // que deve ser mantido. Precisamos decidir.
  const existingEntityComponentState = crdtState[componentId][entityId];

  // Somente se as regras de resolução de conflitos o indicarem (mais sobre isso depois), substituir o estado:
  if (shouldReplace(entityComponentState, existingEntityComponentState)) {
    crdtState[componentId][entityId] = entityComponentState;
  }
}
```

O pseudocódigo acima (se traduzido para código real) é obviamente incompleto e subótimo. Em particular, falta a definição de `shouldCreate` e `shouldReplace` , que encapsulam a estratégia de resolução de conflitos. Estes têm requisitos:

* `shouldCreate` deve adicionar novas entidades, mas recusar recriar entidades deletadas.
* `shouldReplace` deve preferir manter estados que têm timestamps maiores (com alguns casos limite).

Para suportar o requisito de `shouldCreate` , você vai querer rastrear IDs de entidades deletadas, para ignorar quaisquer futuras atualizações de estado para elas. Vamos ver isto em mais pseudocódigo:

```ts
const crdtDeletedEntities = new Set();

function shouldCreate(entityId) {
  return !crdtDeletedEntities.contains(entityId);
}

function deleteEntity(entityId) {
  // Marcar esta entidade como deletada:
  crdtDeletedEntities.add(entityId);

  // Excluir o estado desta entidade em cada componente:
  for (componentState of crdtState) {
    delete componentState[entityId];
  }
}
```

{% hint style="info" %}
Note que, na implementação ingênua acima, o conjunto de entidades deletadas só crescerá. Em cenas que reciclam entidades rapidamente, isso pode levar a uma explosão no uso de memória.

Para evitar isso, o explorer da Foundation emprega um índice generacional para reutilizar IDs em um espaço numérico finito, limitando a quantidade de memória necessária para acompanhar tanto entidades ativas quanto deletadas.
{% endhint %}

Quanto ao requisito de `shouldReplace` , veja [resolução de conflitos](#crdtConflicts) abaixo.

**Timestamps**

Como mencionado acima, ao falar sobre timestamps do CRDT não estamos nos referindo a timestamps Unix reais para um ponto no tempo. Em vez disso, um [Lamport timestamp](https://en.wikipedia.org/wiki/Lamport_timestamp) (um tipo especial de contador incremental compartilhado) é usado para acompanhar a sequência de eventos por ambas as partes.

O valor deste contador é atualizado de acordo com estas regras:

1. Inicializar o contador local em `0`.
2. Antes de enviar uma mensagem para partes remotas, incrementar o contador local.
3. Após receber uma mensagem de uma parte remota, definir o contador local para o máximo entre seu valor e o valor recebido, mais `1`.

Em pseudocódigo:

```ts
const localTimestamp = 0;

function sendState(state) {
  send({ state, timestamp: ++localTimestamp });
}

function receiveState() {
  const { state, timestamp } = receive();
  localTimestamp = max(localTimestamp, remoteTimestamp) + 1;
}
```

**Resolução de Conflitos**

Quando o CRDT encontra dois estados concorrentes, ele precisa de uma estratégia de resolução compartilhada que todas as partes apliquem de forma idêntica, garantindo que a mesma decisão seja alcançada por todos. O protocolo Decentraland usa regras muito simples:

1. Se a entidade foi deletada (e o ID nunca reutilizado), ignorar novo estado.
2. Se não havia estado prévio, manter o novo estado.
3. Se o novo estado tem um timestamp maior, manter o novo estado.
4. Se o novo estado tem um timestamp menor, manter o estado antigo.
5. Se ambos os timestamps são iguais, comparar os estados byte-a-byte e manter o menor valor.

A maioria dos casos será resolvida pelas regras `1`, `2` e `3`.

**Sincronização Inicial**

Antes de uma cena começar a executar seu próprio código, o runtime popula o CRDT com o estado de todos [entidades básicas](https://github.com/decentraland/docs/blob/main/contributor/entities/README.md) e seus componentes.

Durante essa sincronização inicial, somente o runtime pode definir o estado compartilhado. A cena não tem permissão para fazer modificações até que o processo esteja completo.

**Protocolo de Mensagens**

Mensagens entre o World Explorer e o runtime da cena são estruturas em uma representação binária serializada. Cada mensagem carrega um cabeçalho indicando o tipo e comprimento, seguido por um payload particular para cada tipo de mensagem.

```
.----------------.--------------.---------------------------------.
| length: uint32 | type: uint32 |     payload: byte[length]       |
'----------------'--------------'---------------------------------'
╵         campos comuns         ╵         dependente do tipo          ╵

```

Existem três tipos de mensagens no protocolo CRDT:

* [`PutComponent`](#PutComponent)
* [`DeleteComponent`](#DeleteComponent)
* [`DeleteEntity`](#DeleteEntity)

Observe que não existem `CreateComponent` ou `CreateEntity` mensagens. As regras do CRDT auto-criam entidades e componentes que não eram previamente conhecidos, como [explicado acima](#crdtStateAutoCreate).

**`PutComponentMessage`**

Atualizar o `estado` de um `componente` para um determinado `entity` , criando componentes e entidades desconhecidos no CRDT se necessário. [Resolver conflitos](#crdtConflicts) de acordo com `timestamp`.

```
.----------------.-------------------.-------------------------------------------.
| entity: uint32 | component: uint32 | timestamp: uint32 |     state: byte[]     |
'----------------'-------------------'-------------------------------------------'
╵                       campos comuns                    ╵  dependente do componente  ╵

```

O `estado` field é uma serialização binária definida pelo componente. A maioria dos componentes usa [protocol buffers](https://protobuf.dev) para codificar mensagens conforme especificado no `.proto` arquivos do [pacote de protocolo Decentraland](https://github.com/decentraland/protocol). A exceção notável a esta regra é o `Transform` componente, que (sendo de longe o mais comum) tem um formato de serialização otimizado.

Esta camada adicional de serialização tem uma vantagem importante: o protocolo CRDT é agnóstico a quaisquer implementações de componentes presentes ou futuras.

Se a atualização contida nesta mensagem for aplicada ao CRDT, o `estado` campo é copiado tal como está para dentro da estrutura.

**`DeleteComponentMessage`**

Remover o estado de `componente` para um `entity`. [Resolver conflitos](#crdtConflicts) de acordo com `timestamp`.

```
.----------------.-------------------.-------------------.
| entity: uint32 | component: uint32 | timestamp: uint32 |
'----------------'-------------------'-------------------'

```

**`DeleteEntityMessage`**

Deletar `entity` (i.e. todo o estado de componente associado), esperando que o identificador nunca seja reutilizado para uma entidade diferente.

```
.----------------.
| entity: uint32 |
'----------------'

```

Observe que não há campo `timestamp` . Como o tempo de vida de `entity` acabou, a estratégia de resolução de conflitos para quaisquer atualizações fora de ordem é simplesmente ignorá-las.

### Métodos

O conjunto de métodos em `EngineApi` fornece a interface para trocar mensagens e reconstruir o estado do mundo do zero.

**`crdtSendToRenderer`**

Enviar uma mensagem serializada da cena para o renderer, retornar um array de mensagens serializadas que o renderer tem para a cena.

```ts
interface Request {
  // A mensagem serializada para esta requisição:
  data: byte[];
}

interface Response {
  // Um array de mensagens serializadas do renderer (se houver):
  data: byte[][];
}

function crdtSendToRenderer(Request): Promise<Response>;
```

**`crdtGetState`**

```ts
interface Request {}

interface Response {
  // Se o estado tem entidades criadas pela cena:
  hasEntities: boolean;

  // Um array de mensagens que podem reconstruir o estado do CRDT:
  data: byte[][];
}

function crdtGetState(Request): Promise<Response>;
```

### Cenas Legadas

Cenas ao estilo antigo (i.e. aquelas construídas usando o SDK versão 6 ou anterior) não usam o mecanismo CRDT moderno, em vez disso chamando métodos em `EngineApi` que agora estão obsoletos.

O suporte a essas cenas não é um requisito para uma aplicação Decentraland compatível com o protocolo.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.decentraland.org/contributor/contributor-pt/scene-runtime/runtime-modules/engine-api.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
