> For the complete documentation index, see [llms.txt](https://docs.decentraland.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.decentraland.org/contributor/contributor-pt/runtime-da-scene/modulos-de-runtime/engine-api.md).

# API do Engine

O `EngineApi` O módulo fornece acesso ao framework Entity-Component-System partilhado entre o World Explorer (que executa o ciclo de jogo) e as scenes individuais (que gerem as suas próprias entities), além de utilitários relacionados.

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

Este módulo é o mais complexo e rico em funcionalidades, e o mais provável de receber atualizações e extensões. É aqui que 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 concebido para sincronizar o estado do mundo entre o Explorer e a scene através da troca de atualizações, eventos e comandos. Implementa um protocolo de mensagens extensível que permite às scenes, entre outras coisas:

* Criar e destruir entities.
* Associar, atualizar e remover components.
* Lançar rays no ambiente 3D e detetar impactos.
* Receber events como input do player.

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

```

{% hint style="info" %}
O `EngineApi` O módulo está a passar por renovação. Se fores às definições de origem, vais encontrar métodos legacy que já não são usados ou que podem ser implementados como no-ops na versão mais recente (mais sobre isto abaixo).
{% endhint %}

### Framework ECS

Com o `EngineApi` módulo vem uma implementação genérica e extensível de um framework ECS partilhado, que permite tanto às scenes como ao próprio game engine criar entities, associar components e atualizar os seus estados.

As alterações feitas de qualquer dos lados refletem-se no outro através da troca de mensagens. O mecanismo utilizado ([veja abaixo](#synchronization)) garante que ambas as partes concordam com a ordem de quaisquer atualizações e alcançam uma consistência eventual quanto ao estado do mundo.

O World Explorer implementa um conjunto conhecido de components básicos (posições, formas, texturas, media e mais) e sabe desserializar e aplicar alterações ao seu estado quando recebe uma atualização. Scenes que usam o [SDK](https://docs.decentraland.org/creator) também dispõem de utilitários para criar components personalizados, mas esses são inteiramente geridos pela scene (não sincronizados) e, portanto, estão fora do âmbito do protocolo.

{% hint style="info" %}
A maioria das scenes usa o Decentraland SDK, que encapsula o `EngineApi` módulo e oferece uma interface muito mais agradável e de nível mais alto para os criadores de conteúdo. Scenes que acedem diretamente ao protocolo de mensagens neste módulo são extremamente raras.
{% endhint %}

#### Identificação de Entities <a href="#identifying" id="identifying"></a>

Os IDs das entities são inteiros simples que começam com `0`.

Por protocolo, os IDs abaixo de `512` estão reservados ao World Explorer e, por isso, são inválidos para entities criadas pela scene. Os números `0`, `1` e `2` estão realmente em uso (ver [basic entities](https://github.com/decentraland/docs/tree/main/contributor/entities/README.md)), com o restante do intervalo disponível para extensões futuras.

Cada scene tem o seu próprio intervalo privado de IDs, que o Explorer pode mapear de forma transparente para um identificador global entre todas as entities no game engine.

Com o tempo, à medida que as entities são criadas e eliminadas, é perfeitamente válido reutilizar IDs que tenham sido anteriormente libertados — na verdade, isso pode ser necessário por motivos de desempenho ([veja abaixo](https://github.com/decentraland/docs/tree/main/contributor/runtime-modules/crdtStateDeleteEntities/README.md)).

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

Tanto o World Explorer como a scene têm de gerir um conjunto de entities e components partilhados, em que as atualizações vêm de ambos os lados de forma assíncrona. Sem medidas adicionais, as suas versões do estado do mundo podem (e vão) acabar por ser diferentes.

Para evitar isso, é utilizado um CRDT ([tipo de dados replicado sem conflitos](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type)) para chegar a um acordo entre ambas as partes através da troca de mensagens com atualizações. Isto oferece várias vantagens:

* Ambas as partes podem atualizar o estado de forma independente e concorrente.
* Os conflitos são geridos por uma estratégia de resolução partilhada aplicada localmente por ambas as partes.
* Ambas as partes têm garantia de convergir para um estado partilhado e idêntico.
* Além de um pequeno acréscimo no tamanho das mensagens, não são necessárias coordenação adicional nem rondas extra de mensagens.

{% hint style="info" %}
Esta página explica a utilização do CRDT para sincronizar o estado entre uma scene em execução local e o game engine, mas o verdadeiro potencial deste mecanismo reside em cenários multiplayer. Usando este protocolo, os players podem coordenar o seu estado de jogo e partilhar a mesma experiência.
{% endhint %}

Para que isto funcione, todas as mensagens têm de ser comutativas e idempotentes. Mensagens fora de ordem podem ser aplicadas mesmo que uma atualização intermédia alegada ainda não tenha sido recebida, e mensagens iguais podem ser reprocessadas sem quebrar a consistência.

Vamos revisitar o diagrama arquitetónico acima, agora com foco 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 entities e components.
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 contém o estado de todos os components para todas as entities associadas, e pode ser armazenado num mapeamento de components para estados com timestamp de cada entity. Em pseudocódigo:

```ts
// O estado (completamente genérico) de um component para uma entity específica, num dado momento:
type EntityComponentState = { timestamp: number; state: byte[] };

// O estado de um component para todas as entities associadas:
type ComponentState = Map<EntityId, EntityComponentState>;

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

Os timestamps não são, na verdade, timestamps ao estilo Unix. São um tipo de contador incremental para sequenciar atualizações, sem relação com a hora do relógio. Mais sobre isto [abaixo](#timestamps).

Como a camada CRDT não conhece (nem precisa de conhecer) todos os components disponíveis, o estado inicial é um mapa vazio. As entradas são criadas dinamicamente quando as operações envolvem um `ComponentId` ou `EntityId` que ainda não estava mapeado. Quando uma entity é removida, o estado associado é eliminado de todos os components.

Vamos ilustrar isto com mais pseudocódigo.

```ts
const crdtState = new Map(); // vazio, vamos criar espaço automaticamente para novos components e entities
```

```ts
function putEntityComponentState(componentId, entityId, entityComponentState) {
  // Se este component ainda não for conhecido, adiciona-o:
  if (!crdtState[componentId]) {
    crdtState[componentId] = new Map();
  }

  // Se esta entity não tiver estado para este component, adiciona-o e termina:
  if (!crdtState[componentId][entityId] && shouldCreate(entityId)) {
    crdtState[componentId][entityId] = entityComponentState;
    return;
  }

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

  // Apenas se as regras de resolução de conflitos assim o disserem (mais sobre isto a seguir), substitui o estado:
  if (shouldReplace(entityComponentState, existingEntityComponentState)) {
    crdtState[componentId][entityId] = entityComponentState;
  }
}
```

O pseudocódigo acima (se fosse traduzido para código real) está obviamente incompleto e abaixo do ideal. Em particular, falta a definição de `shouldCreate` e `shouldReplace`, que encapsulam a estratégia de resolução de conflitos. Têm estes requisitos:

* `shouldCreate` deve adicionar novas entities, mas recusar recriar entities eliminadas.
* `shouldReplace` deve dar preferência a manter estados com timestamps maiores (com alguns casos-limite).

Para dar suporte ao `shouldCreate` requisito, quererás manter o registo dos IDs das entities eliminadas, para ignorar quaisquer futuras atualizações de estado para essas entities. Vejamos isto em mais pseudocódigo:

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

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

function deleteEntity(entityId) {
  // Marca esta entity como eliminada:
  crdtDeletedEntities.add(entityId);

  // Elimina o estado desta entity em todos os components:
  for (componentState of crdtState) {
    delete componentState[entityId];
  }
}
```

{% hint style="info" %}
Note-se que, na implementação ingénua acima, o conjunto de entities eliminadas só irá crescer. Em scenes que reciclem entities rapidamente, isto pode levar a uma explosão no uso de memória.

Para evitar isto, o Explorer da Foundation usa um índice geracional para reutilizar IDs num espaço numérico finito, limitando a quantidade de memória necessária para acompanhar tanto as entities ativas como as eliminadas.
{% endhint %}

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

**Timestamps**

Como referido acima, quando falamos de timestamps do CRDT não nos estamos a referir a timestamps Unix reais para um momento no tempo. Em vez disso, é utilizado um [timestamp de Lamport](https://en.wikipedia.org/wiki/Lamport_timestamp) (um tipo especial de contador incremental partilhado) para acompanhar a sequência de events por ambas as partes.

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

1. Inicializa o contador local para `0`.
2. Antes de enviar uma mensagem para partes remotas, incrementa o contador local.
3. Depois de receber uma mensagem de uma parte remota, define o contador local como o máximo entre o 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, precisa de uma estratégia de resolução partilhada que todas as partes apliquem de forma idêntica, garantindo que todos chegam à mesma decisão. O protocolo do Decentraland usa regras muito simples:

1. Se a entity foi eliminada (e o ID nunca foi reutilizado), ignora o novo estado.
2. Se não havia estado anterior, mantém o novo estado.
3. Se o novo estado tiver um timestamp maior, mantém o novo estado.
4. Se o novo estado tiver um timestamp menor, mantém o estado antigo.
5. Se ambos os timestamps forem iguais, compara os estados byte a byte e mantém o valor mais baixo.

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

**Sincronização inicial**

Antes de uma scene começar a executar o seu próprio código, o runtime preenche o CRDT com o estado de todas as [basic entities](https://github.com/decentraland/docs/tree/main/contributor/entities/README.md) e dos seus components.

Durante esta sincronização inicial, apenas o runtime pode definir o estado partilhado. A scene não tem permissão para fazer modificações até que o processo esteja concluído.

**Protocolo de mensagens**

As mensagens entre o World Explorer e o runtime da scene são estruturas numa representação binária serializada. Cada mensagem transporta um cabeçalho que indica o tipo e o comprimento, seguido de uma carga útil específica para cada tipo de mensagem.

```
.----------------.--------------.---------------------------------.
| length: uint32 | type: uint32 |     payload: byte[length]       |
'----------------'--------------'---------------------------------'
╵         common fields         ╵         type-dependent          ╵

```

Há três tipos de mensagem no protocolo CRDT:

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

Note-se que não existem `CreateComponent` ou `CreateEntity` mensagens. As regras do CRDT criam automaticamente entities e components que anteriormente não eram conhecidos, como [explicado acima](#crdtStateAutoCreate).

**`PutComponentMessage`**

Atualiza o `estado` de um `component` para uma determinada `Entity`, criando components e entities desconhecidos no CRDT se necessário. [Resolve conflitos](#crdtConflicts) de acordo com `timestamp`.

```
.----------------.-------------------.-------------------------------------------.
| entity: uint32 | component: uint32 | timestamp: uint32 |     state: byte[]     |
'----------------'-------------------'-------------------------------------------'
╵                       common fields                    ╵  component-dependent  ╵

```

O `estado` campo é uma serialização binária definida pelo component. A maioria dos components usa [protocol buffers](https://protobuf.dev) para codificar mensagens conforme especificado nos `.proto` ficheiros do [pacote de protocolo do Decentraland](https://github.com/decentraland/protocol). A exceção notável a esta regra é o `Transform` component, 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 component presentes ou futuras.

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

**`DeleteComponentMessage`**

Remove o estado de `component` para um `Entity`. [Resolve conflitos](#crdtConflicts) de acordo com `timestamp`.

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

```

**`DeleteEntityMessage`**

Elimina `Entity` (isto é, todo o estado dos components associado), esperando que o identificador nunca seja reutilizado para uma entity diferente.

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

```

Note-se que não existe `timestamp` campo. Como o ciclo de vida de `Entity` terminou, 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`**

Envia uma mensagem serializada da scene para o renderer; devolve uma array de mensagens serializadas que o renderer tem para a scene.

```ts
interface Request {
  // A mensagem serializada para este pedido:
  data: byte[];
}

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

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

**`crdtGetState`**

```ts
interface Request {}

interface Response {
  // Indica se o estado tem entities criadas pela scene:
  hasEntities: boolean;

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

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

### Scenes legacy

Scenes antigas (isto é, as construídas usando a versão 6 do SDK ou anterior) não usam o mecanismo CRDT moderno, chamando em vez disso métodos em `EngineApi` que agora estão obsoletos.

O suporte a estas scenes não é um requisito para uma aplicação Decentraland em conformidade com o protocolo.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

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

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.decentraland.org/contributor/contributor-pt/runtime-da-scene/modulos-de-runtime/engine-api.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

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.
