> 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/rede/serverless-multiplayer.md).

# Multiplayer sem Servidor

Sincronize o estado da scene entre jogadores.

Decentraland executa scenes localmente na instância do Explorer de um player. Por padrão, os players conseguem se ver e interagir diretamente, mas cada player interage com o ambiente de forma independente. As mudanças no ambiente não são compartilhadas entre os players por padrão.

Ver o mesmo conteúdo no mesmo estado é extremamente importante para que os players interajam de maneiras mais significativas.

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

* **Marque uma Entity como sincronizada**: A opção mais fácil. Veja [Uma Entity marcada como sincronizada](#mark-an-entity-as-synced)
* **Enviar mensagens explícitas do MessageBus**: Envie e ouça manualmente mensagens específicas. Veja [Enviar mensagens explícitas do MessageBus](#send-explicit-messagebus-messages)
* **Use um Multiplayer Server**: Veja [Multiplayer Server](/creator/content-creator-pt/scenes-sdk7/rede/authoritative-servers.md). O servidor valida todas as mudanças de estado e é a única fonte de verdade. É necessário mais configuração, mas isso é fortemente recomendado quando os players têm incentivos para explorar sua scene.

As duas primeiras opções são abordadas neste documento. Elas são mais simples, pois não exigem servidor. A desvantagem é que você depende mais da velocidade de conexão do player, e o estado da scene não é persistido quando todos os players saem da scene.

## Marque uma Entity como Sincronizada

No [Creator Hub](/creator/content-creator-pt/scene-editor/primeiros-passos/about-editor.md), marque uma Entity como sincronizada adicionando um **componente Multiplayer** a ela. Ele inclui uma caixa de seleção para cada um dos outros components na Entity, permitindo que você selecione quais atualizar.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-f64a1bf75807de2bc206e2b7faaacbee74d976d7%2Fmultiplayer-component.png?alt=media\&token=c69c63c3-ebfb-4c39-8088-ac431969f56f)

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

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

export function main() {
  const doorEntity = engine.addEntity();

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

{% hint style="warning" %}
**📔 Nota**: Chame sempre `syncEntity()` dentro da `main()` função, ou em uma função que seja executada depois de `main()` (como um callback ou um system). Chamá-la no nível superior de um arquivo gera um erro, porque nesse ponto o perfil do player ainda não foi inicializado.
{% endhint %}

{% hint style="warning" %}
**📔 Nota**: Em multiplayer sem servidor, cada client chama `syncEntity` por conta própria. Se você migrar para um [Multiplayer Server](/creator/content-creator-pt/scenes-sdk7/rede/authoritative-servers.md)Multiplayer Server `syncEntity`, protegido por `isServer()`. Os clients devem evitar declarar a sincronização de Entities compartilhadas, a menos que elas sejam criadas pelo client.
{% endhint %}

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

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

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

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

### Sobre o id do enum

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

Definir esse ID explicitamente é importante para evitar inconsistências caso uma condição de corrida faça uma parte da scene carregar antes de outra. Talvez, para o player A, a porta na scene seja a Entity *512*, mas para o player B essa mesma porta é a Entity *513*. Nesse caso, se o player A abrir a porta, o player B verá o prédio inteiro se mover.

{% 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,
}

export function main() {
  syncEntity(
    doorEntity,
    [Transform.componentId, Animator.componentId],
    EntityEnumId.DOOR
  );
}
```

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

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

**Entities criadas por um player**

Se uma Entity for criada como resultado da interação de um player, e essa Entity deve ser sincronizada com outros players, a Entity não precisa de um entityEnumId. Você 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 inicialização da scene precisam ter um ID atribuído manualmente. Isso garante que todos os players usem o mesmo ID em cada uma. Quando apenas um player fica responsável por instanciar uma Entity, IDs explícitos não são necessários. Os outros players recebem atualizações sobre essa nova Entity com um ID já atribuído, então não há risco de incompatibilidades de ID.

Por exemplo, em uma scene de luta de bolas de neve, toda vez que um player lança uma bola de neve, ele está instanciando uma nova Entity que é sincronizada com outros players. 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 com parent**

O parent de uma Entity normalmente é definido via `parent` propriedade no `Transform` component. No entanto, essa propriedade aponta para o id local da Entity pai, que pode variar, veja [Sobre o enum id](#about-the-enum-id). Para parentear Entities que precisam ser sincronizadas, ou que têm children que precisam ser sincronizados, use a `parentEntity()` função em vez da `Transform`.

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

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

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

  parentEntity(child, parent);
}
```

Observe que tanto o parent quanto o child são sincronizados com `syncEntity`, então todos os players têm um entendimento comum de quais ids são usados pelas duas Entities. Isso é necessário mesmo que os components do parent nunca precisem mudar. Neste exemplo, o `syncEntity` inclui um array vazio de components, para evitar sincronizar components desnecessários.

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

Quando Entities são parentadas via a `parentEntity()` função, você também pode usar as seguintes funções auxiliares:

* **removeParent()**: Desfaça os efeitos de `parentEntity()`.
* **getParent()**: Retorna a Entity pai da Entity que você passou.
* **getChildren()**: Retorna a lista de filhos da Entity que você passou, como um iterável.
* **getFirstChild()**: Retorna o primeiro filho da lista da Entity que você passou.

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

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

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

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

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

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

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

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

## Verifique o estado de sincronização

Quando um player acabou de carregar em uma scene, ele pode ainda não estar sincronizado com os outros players ao seu redor. Se o player começar a alterar o estado do jogo antes de estar sincronizado, isso pode causar problemas no seu jogo. Recomendamos sempre verificar se um player está sincronizado antes de permitir que ele edite qualquer coisa da scene.

Se um player sair dos parcels da scene, ele também ficará fora de sync com a scene enquanto estiver do lado de fora. Por isso também é importante que os systems da scene lidem com esse cenário, já que a scene continua executando enquanto o player estiver por perto. Quando o player voltar, ele é atualizado automaticamente com quaisquer mudanças do estado da scene.

Você pode verificar se o estado da scene está atualmente sincronizado para um player por meio da `isStateSyncronized()` função. Essa função retorna um boolean, que é true se o player já estiver sincronizado com a scene.

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

const isConnected = isStateSyncronized();
```

Você poderia, por exemplo, incluir essa verificação em um system e bloquear qualquer interação se essa função retornar 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 do MessageBus

{% hint style="warning" %}
**📔 Nota**: A `MessageBus` API está marcada como obsoleta no SDK e pode ser removida em uma versão futura. Para a maioria dos casos de uso, prefira [marcar Entities como sincronizadas](#mark-an-entity-as-synced).
{% endhint %}

**Inicie um message bus**

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

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

const sceneMessageBus = new MessageBus();
```

**Envie mensagens**

Use o `.emit` comando do message bus para enviar uma mensagem a todos os outros players 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 você 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 você precisar que uma única mensagem inclua dados de mais de uma variável, crie um tipo personalizado para armazenar todos esses dados em um único objeto.
{% endhint %}

**Receba mensagens**

Para lidar com mensagens de todos os outros players nessa scene, use `.on`. Ao usar essa função, você fornece uma string de mensagem e define uma função para executar. Cada vez que uma mensagem com uma string correspondente chega, a função fornecida é 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**: Mensagens enviadas por um player também são recebidas por esse mesmo player. O `.on` método não consegue distinguir entre uma mensagem emitida por esse mesmo player e uma mensagem emitida por outros players.
{% endhint %}

**Exemplo completo de MessageBus**

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

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

/// --- Crie o 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, envie uma mensagem para gerar outro
  pointerEventsSystem.onPointerDown(
    {
      entity: meshEntity,
      opts: { button: InputAction.IA_PRIMARY, hoverText: "Pressione E para gerar" },
    },
    function () {
      sceneMessageBus.emit("spawn", {
        position: {
          x: 1 + Math.random() * 8,
          y: Math.random() * 8,
          z: 1 + Math.random() * 8,
        },
      });
    }
  );

  return meshEntity;
}

// Inicialização
createCube(8, 1, 8);

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

// ao receber a mensagem de spawn, crie um novo cubo
sceneMessageBus.on("spawn", (info: NewBoxPosition) => {
  createCube(info.position.x, info.position.y, info.position.z);
});
```

## Teste localmente uma cena multijogador

Se iniciar uma pré-visualização de cena e a abrir em duas (ou mais) janelas diferentes do Explorer, cada janela aberta será interpretada como um jogador separado, e um servidor de comunicações simulado manterá estes jogadores sincronizados.

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

No Creator Hub, ative **Pré-visualização com várias instâncias** no **Cliente Desktop** o submenu de **Opções de Play**, depois clique no **Play** botão uma segunda vez para abrir uma segunda janela do Decentraland explorer. Tem de se ligar em ambas as janelas com endereços diferentes. As mesmas sessões permanecerão abertas à medida que a cena recarrega.

![](https://2402076176-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2FoPnXBby9S6MrsW83Y9qZ%2Fuploads%2Fgit-blob-30317b50bc4b4e646a1b28effe1ff76939ab0f28%2Fpreview-button.png?alt=media\&token=fbed0f2f-8063-4283-9b07-2d964c9c0e68)

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

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

## Scenes para um único player

Se sua scene for implantada em um [Decentraland World](/creator/content-creator-pt/scenes-sdk7/publicacao/publishing-options.md#decentraland-worlds), você pode transformá-la em uma scene para um único player. Os players não se verão, não poderão conversar nem ver os efeitos das ações uns dos outros.

Para fazer isso, configure o `scene.json` arquivo da scene para definir o **fixedAdapter** para `offline:offline`. A scene não terá nenhum Communication Service e cada usuário 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 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/creator/content-creator-pt/scenes-sdk7/rede/serverless-multiplayer.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.
