> 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/creator/content-creator-pt/scenes-sdk7/networking/serverless-multiplayer.md).

# Multiplayer serverless

Decentraland executa scenes localmente na instância do explorer de um jogador. Por predefinição, os jogadores conseguem ver-se uns aos outros e interagir diretamente, mas cada jogador interage com o ambiente de forma independente. As alterações no ambiente não são partilhadas entre jogadores por predefinição.

Ver o mesmo conteúdo no mesmo estado é extremamente importante para os jogadores interagirem de formas mais significativas.

Há três formas de sincronizar o estado da scene, para que todos os jogadores vejam o mesmo:

* **Marcar uma Entity como sincronizada**: A opção mais fácil. Consulte [Marcado uma Entity como sincronizada](#mark-an-entity-as-synced)
* **Enviar mensagens explícitas de MessageBus**: Envie e escute manualmente mensagens específicas. Consulte [Enviar mensagens explícitas de MessageBus](#send-explicit-messagebus-messages)
* **Usar um Authoritative Server**: Consulte [Authoritative Servers](/creator/content-creator-pt/scenes-sdk7/networking/authoritative-servers.md). O server valida todas as alterações de estado e é a única fonte de verdade. Requer mais configuração, mas é fortemente recomendado quando os jogadores têm incentivos para explorar a sua scene.

As duas primeiras opções são abordadas neste documento. São mais simples, pois não requerem um server. A desvantagem é que depende mais da velocidade de conexão do jogador, e o estado da scene não é persistido quando todos os jogadores saem da scene.

## Marcar uma Entity como Sincronizada

No campo [Creator Hub](/creator/content-creator-pt/scene-editor/comecar/about-editor.md), marque uma Entity como sincronizada adicionando-lhe um **componente Multiplayer** Ele inclui uma caixa de seleção para cada um dos outros components na Entity, permitindo-lhe selecionar quais deseja atualizar.

![](/files/95cf911895c4a6f132851a90d0d0c92acb4064e6)

Para marcar uma Entity como sincronizada por código, use a `syncEntity` função:

```ts
import { syncEntity } from "@dcl/sdk/network";

const doorEntity = engine.addEntity();

syncEntity(doorEntity, [Transform.componentId, Animator.componentId], 1);
```

{% hint style="warning" %}
**📔 Nota**: No multiplayer serverless, cada client chama `syncEntity` por conta própria. Se fizer upgrade para um [Authoritative Server](/creator/content-creator-pt/scenes-sdk7/networking/authoritative-servers.md), o padrão muda: apenas o server deve chamar `syncEntity`, protegido por `isServer()`. Os clients devem evitar declarar a sincronização de entidades partilhadas, a menos que sejam criadas pelo client.
{% endhint %}

O `syncEntity` a função recebe as seguintes entradas:

* **entityId**: Uma referência à Entity a sincronizar
* **componentIds**: Uma lista dos components que precisam ser sincronizados dessa Entity. Trata-se de um array que pode conter quantas entities forem necessárias. Todos os valores devem ser `componentId` .
* **entityEnumId**: (opcional) Um id único que é usado de forma consistente por todos os jogadores, veja [Sobre enum id](#about-the-enum-id).

Nem todas as entities ou components precisam de ser sincronizados. Elementos estáticos, como uma árvore que permanece no mesmo lugar, não requerem sincronização. Nas entities que sincroniza, apenas os components que mudam ao longo do tempo devem ser sincronizados. Por exemplo, se um cubo muda de cor quando é clicado, deve sincronizar apenas o componente Material, não o MeshRenderer nem o Transform, pois estes nunca mudam.

{% hint style="info" %}
**💡 Dica**: Se os dados que quer partilhar não existirem como um component, defina um [componente personalizado](/creator/content-creator-pt/scenes-sdk7/arquitetura/custom-components.md) que contenha esses dados.
{% endhint %}

### Sobre o enum id

O **entityEnumId** de uma Entity deve ser único. Não está relacionado com o entityId local atribuído por `engine.addEntity()`, que é gerado automaticamente e pode variar entre jogadores que executam a mesma scene. O entityEnumId de uma Entity deve ser explicitamente definido no código e ser único.

Definir explicitamente este ID é importante para evitar inconsistências se uma condition de corrida fizer com que uma parte da scene carregue antes de outra. Talvez, para o jogador A, a porta na scene seja a Entity *512*, mas para o jogador B essa mesma porta seja a Entity *513*. Nesse caso, se o jogador A abrir a porta, o jogador B vê em vez disso o edifício inteiro mover-se.

{% hint style="info" %}
**💡 Dica**: Crie um enum na sua scene para manter referências claras a cada id sincronizável na sua scene.

```ts
import { syncEntity } from "@dcl/sdk/network";

enum EntityEnumId {
  DOOR = 1,
  DRAW_BRIDGE = 2,
  ELEVATOR = 3,
}

syncEntity(
  doorEntity,
  [Transform.componentId, Animator.componentId],
  EntityEnumId.DOOR
);
```

Aqui o enum EntityEnumId é usado para etiquetar entities com um identificador único, garantindo que cada client reconhece a Entity modificada, independentemente da ordem de criação.
{% endhint %}

{% hint style="warning" %}
**📔 Nota**: Evite usar números superiores a **8001** se a sua scene também incluir Smart Items. Os items que são criados pelo [Creator Hub](/creator/content-creator-pt/scene-editor/comecar/about-editor.md) com um componente Multiplayer usarão IDs atribuídos automaticamente a partir de 8001. Qualquer ID inferior a 8001 é seguro para atribuir às suas entities sincronizadas.
{% endhint %}

**Entities criadas por um jogador**

Se uma Entity for criada como resultado da interação de um jogador e esta Entity deva ser sincronizada com outros jogadores, a Entity não precisa de um entityEnumId. Pode usar `syncEntity()` passando apenas a Entity e a lista de components. Um valor único para entityEnumId é atribuído automaticamente nos bastidores.

Todas as entities instanciadas na iniciação da scene precisam de ter um ID atribuído manualmente. Isso garante que todos os jogadores usem o mesmo ID em cada uma. Quando um único jogador é responsável por instanciar uma Entity, não são necessários IDs explícitos. Os outros jogadores recebem atualizações sobre esta nova Entity com um ID já atribuído, por isso não há risco de incompatibilidades de ID.

Por exemplo, numa scene de luta de bolas de neve, sempre que um jogador atira uma bola de neve, está a instanciar uma nova Entity que é sincronizada com outros jogadores. A bola de neve não precisa de um entityEnumId único.

```ts
import { syncEntity } from "@dcl/sdk/network";

function onThrow() {
  const ball = engine.addEntity();
  Transform.create(ball, {});
  GLTFContainer.create(ball, { src: "assets/snowBall.glb" });
  syncEntity(ball, [Transform.componentId, GLTFContainer.componentId]);
}
```

**Entities parented**

O parent de uma Entity é normalmente definido através do `parent` propriedade em `Transform` component. No entanto, esta propriedade aponta para o local entity id do parent, que pode variar, veja [Sobre enum id](#about-the-enum-id). Para parentar entities que precisam de ser sincronizadas, ou que têm children que precisam de ser sincronizados, use a `parentEntity()` função em vez de `Transform`.

```ts
import { syncEntity, parentEntity } from "@dcl/sdk/network";

const parent = engine.addEntity();
Transform.create(parent, { position: somePosition });
syncEntity(parent, []);

const child: Entity = engine.addEntity();
syncEntity(child, [Transform.componentId]);

parentEntity(child, parent);
```

Note que tanto o parent como o child são sincronizados com `syncEntity`, por isso todos os jogadores têm uma compreensão comum de que ids são usados por ambas as entities. Isto é necessário mesmo que os components do parent possam nunca precisar de mudar. Neste exemplo, o `syncEntity` inclui um array vazio de components, para evitar sincronizar quaisquer components desnecessários.

{% hint style="warning" %}
**📔 Nota**: Se uma Entity for parented tanto pelo `parentEntity()` como também pelo `parent` propriedade em `Transform` component, a propriedade no `Transform` component é ignorada.
{% endhint %}

Quando entities são parented através da `parentEntity()` função, também pode utilizar as seguintes funções auxiliares:

* **removeParent()**: Reverte os efeitos de `parentEntity()`. Requer que passe apenas a child Entity. O novo parent da Entity passa a ser a root entity da scene. O parent original da Entity não é removido da scene.
* **getParent()**: Devolve a parent Entity de uma Entity que passou.
* **getChildren()**: Devolve a lista de children da Entity que passou, como um iterável.
* **getFirstChild()**: Devolve o primeiro child da lista da Entity que passou.

```ts
import { syncEntity, parentEntity } from "@dcl/sdk/network";

const parent = engine.addEntity();
Transform.create(parent, { position: somePosition });
syncEntity(parent, []);

const child: Entity = engine.addEntity();
syncEntity(child, [Transform.componentId]);

// define o parent como parent
parentEntity(child, parent);

// getParent
const getParentResult = getParent(child);
// devolve parent

// getFirstChild
const getFirstChildResult = getFirstChild(parent);
// devolve child

// getChildren
const getChildrenResult = Array.from(getChildren(parent));
// devolve [child]

// remove parent do child
removeParent(child);
```

## Verificar o estado de sincronização

Quando um jogador acaba de carregar numa scene, é possível que ainda não esteja sincronizado com os outros jogadores à sua volta. Se o jogador começar a alterar o estado do jogo antes de estar sincronizado, isso pode causar problemas no seu jogo. Recomendamos verificar sempre se um jogador está sincronizado antes de lhe permitir editar qualquer coisa na scene.

Se um jogador sair dos parcels da scene, também ficará fora de sincronização com a scene enquanto estiver fora. Por isso, também é importante que os systems da scene tratem esse cenário, pois a scene continua a correr enquanto o jogador estiver por perto. Assim que o jogador regressa, é atualizado automaticamente com quaisquer alterações do estado da scene.

Pode verificar se o estado da scene está atualmente sincronizado para um jogador através da `isStateSyncronized()` função. Esta função devolve um boolean, que é true se o jogador já estiver sincronizado com a scene.

```ts
import { isStateSyncronized } from "@dcl/sdk/network";

const isConnected = isStateSyncronized();
```

Pode, por exemplo, incluir esta verificação num system e bloquear qualquer interação se esta função devolver false.

```ts
import { isStateSyncronized } from "@dcl/sdk/network";

engine.addSystem(() => {
  if (isStateSyncronized() && !button.enabled) {
    console.log("Enable Start Game");
    button.enable();
  }

  if (!isStateSyncronized() && button.enabled) {
    console.log(`Disable Start Game.`);
    button.disable();
  }
});
```

## Enviar mensagens explícitas de MessageBus

**Iniciar um message bus**

Crie um objeto message bus para lidar com os métodos necessários para enviar e receber mensagens entre jogadores.

```ts
import { MessageBus } from "@dcl/sdk/message-bus";

const sceneMessageBus = new MessageBus();
```

**Enviar mensagens**

Use a `.emit` comando do message bus para enviar uma mensagem a todos os outros jogadores na scene.

```ts
import { MessageBus } from "@dcl/sdk/message-bus";

const sceneMessageBus = new MessageBus();

const myEntity = engine.addEntity();
MeshRenderer.setBox(myEntity);
MeshCollider.setBox(myEntity);

pointerEventsSystem.onPointerDown(
  {
    entity: myEntity,
    opts: { button: InputAction.IA_PRIMARY, hoverText: "Click" },
  },
  function () {
    sceneMessageBus.emit("box1Clicked", {});
  }
);
```

Cada mensagem pode conter um payload como segundo argumento. O payload é do tipo `Object`, e pode conter quaisquer dados relevantes que queira enviar.

```ts
import { MessageBus } from "@dcl/sdk/message-bus";

const sceneMessageBus = new MessageBus();

sceneMessageBus.emit("spawn", { position: { x: 10, y: 2, z: 10 } });
```

{% hint style="info" %}
**💡 Dica**: Se precisar que uma única mensagem inclua dados de mais do que uma variável, crie um tipo personalizado para guardar todos esses dados num único object.
{% endhint %}

**Receber mensagens**

Para tratar mensagens de todos os outros jogadores nessa scene, use `.on`. Ao usar esta função, fornece uma string de mensagem e define uma função a executar. Sempre que chega uma mensagem com uma string correspondente, a função dada é executada uma vez.

```ts
import { MessageBus } from "@dcl/sdk/message-bus";

const sceneMessageBus = new MessageBus();

type NewBoxPosition = {
  position: { x: number; y: number; z: number };
};

sceneMessageBus.on("spawn", (info: NewBoxPosition) => {
  const myEntity = engine.addEntity();
  Transform.create(myEntity, {
    position: { x: info.position.x, y: info.position.y, z: info.position.z },
  });
  MeshRenderer.setBox(myEntity);
  MeshCollider.setBox(myEntity);
});
```

{% hint style="warning" %}
**📔 Nota**: As mensagens enviadas por um jogador também são recebidas por esse mesmo jogador. O `.on` método não consegue distinguir entre uma mensagem emitida por esse mesmo jogador e uma mensagem emitida por outros jogadores.
{% endhint %}

**Exemplo completo de MessageBus**

Este exemplo usa um message bus para enviar uma nova mensagem sempre que o cubo principal é clicado, gerando um novo cubo numa posição aleatória. A mensagem inclui a posição do novo cubo, para que todos os jogadores vejam estes novos cubos nas mesmas posições.

```ts
import { MessageBus } from "@dcl/sdk/message-bus";

/// --- Criar message bus ---
const sceneMessageBus = new MessageBus();

// Fábrica de cubos
function createCube(x: number, y: number, z: number): Entity {
  const meshEntity = engine.addEntity();
  Transform.create(meshEntity, { position: { x, y, z } });
  MeshRenderer.setBox(meshEntity);
  MeshCollider.setBox(meshEntity);

  // Quando um cubo é clicado, enviar mensagem para gerar outro
  pointerEventsSystem.onPointerDown(
    {
      entity: myEntity,
      opts: { button: InputAction.IA_PRIMARY, hoverText: "Press E to spawn" },
    },
    function () {
      sceneMessageBus.emit("spawn", {
        position: {
          x: 1 + Math.random() * 8,
          y: Math.random() * 8,
          z: 1 + Math.random() * 8,
        },
      });
    }
  );

  return meshEntity;
}

// Iniciar
createCube(8, 1, 8);

// definir tipo de dados
type NewBoxPosition = {
  position: { x: number; y: number; z: number };
};

// na mensagem spawn, criar novo cubo
sceneMessageBus.on("spawn", (info: NewBoxPosition) => {
  createCube(info.position.x, info.position.y, info.position.z);
});
```

## Testar uma scene multiplayer localmente

Se iniciar um preview de uma scene e o abrir em duas (ou mais) janelas Explorer diferentes, cada janela aberta será interpretada como um jogador separado, e um mock communications server manterá estes jogadores sincronizados.

Interaja com a scene numa janela e depois mude para a outra para ver que os efeitos dessa interação também são visíveis lá.

Usando o Creator Hub, clique no botão Preview uma segunda vez, e isso abre uma segunda janela do explorer do Decentraland. Você deve se conectar em ambas as janelas com endereços diferentes. As mesmas sessões permanecerão abertas à medida que a scene recarrega.

![](/files/465409d90d75e560b57bd495eeb6cde4213f8cf1)

Como alternativa, pode abrir uma segunda janela do Decentraland Explorer escrevendo o seguinte num URL do browser:

> `decentraland://realm=http://127.0.0.1:8000&local-scene=true&debug=true&multi-instance=true`

## Scenes de jogador único

Se a sua scene for implantada num [Decentraland World](/creator/content-creator-pt/worlds/about.md), pode torná-la uma scene de jogador único. Os jogadores não se verão uns aos outros, não poderão conversar nem ver os efeitos das ações uns dos outros.

Para fazer isso, configure o `scene.json` file da scene para definir o **fixedAdapter** a `offline:offline`. A scene não terá qualquer Communication Service e cada utilizador que entrar nesse world estará sempre sozinho.

**Exemplo:**

```json
{
  "worldConfiguration": {
    "name": "my-name.dcl.eth",
    "fixedAdapter": "offline:offline"
  }
}
```


---

# 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 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/creator/content-creator-pt/scenes-sdk7/networking/serverless-multiplayer.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.
