# Serverless Multiplayer

Decentraland runs scenes locally in a player's instance of the explorer. By default, players are able to see each other and interact directly, but each player interacts with the environment independently. Changes in the environment aren't shared between players by default.

Seeing the same content in the same state is extremely important for players to interact in more meaningful ways.

There are three ways to sync the scene state, so that all players see the same:

* **Mark an entity as synced**: The easiest option. See [Marked an entity as synced](#mark-an-entity-as-synced)
* **Send Explicit MessageBus Messages**: Manually send and listen for specific messages. See [Send explicit MessageBus messages](#send-explicit-messagebus-messages)
* **Use an Authoritative Server**: See [Authoritative Servers](/creator/scenes-sdk7/networking/authoritative-servers.md). The server validates all state changes and is the single source of truth. More setup required, but strongly recommended when players have incentives to exploit your scene.

The first two options are covered in this document. They are simpler, as they require no server. The downside is that you rely more on player's connection speeds, and the scene state is not persisted when all players leave the scene.

## Mark an Entity as Synced

In the [Creator Hub](/creator/scene-editor/get-started/about-editor.md), mark an entity as synced by adding a **Multiplayer component** to it. It includes a checkbox for each of the other components on the entity, allowing you to select which ones to update.

![](/files/uek6snDi0NXFbBPLhu4m)

To mark an entity as synced via code, use the `syncEntity` function:

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

const doorEntity = engine.addEntity();

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

{% hint style="warning" %}
**📔 Note**: In serverless multiplayer, every client calls `syncEntity` on its own. If you upgrade to an [Authoritative Server](/creator/scenes-sdk7/networking/authoritative-servers.md), the pattern changes: only the server should call `syncEntity`, guarded by `isServer()`. Clients should avoid declaring the syncing of shared entities, unless they are created by the client.
{% endhint %}

The `syncEntity` function takes the following inputs:

* **entityId**: A reference to the entity to sync
* **componentIds**: A list of the components that need to be synced from that entity. This is an array that may contain as many entities as needed. All values should be `componentId` properties.
* **entityEnumId**: (optional) A unique id that is used consistently by all players, see [About enum id](#about-the-enum-id).

Not all entities or components need to be synced. Static elements like a tree that remains in the same spot don't require syncing. On entities you do sync, only the components that change over time should be synchronized. For example, if a cube changes color when clicked, you should only sync the Material component, not the MeshRenderer or the Transform, as those will never change.

{% hint style="info" %}
**💡 Tip**: If the data you want to share doesn't exist as a component, define a [custom component](/creator/scenes-sdk7/architecture/custom-components.md) that holds that data.
{% endhint %}

### About the enum id

The **entityEnumId** of an entity must be unique. It's not related to the local entityId assigned on `engine.addEntity()`, that is automatically generated and may vary between players running the same scene. The entityEnumId of an entity must be explicitly defined in the code and be unique.

Explicitly setting this ID is important to avoid inconsistencies if a race condition makes one part of the scene load before another. Maybe for player A the door in the scene is the entity *512*, but for player B that same door is entity *513*. In that case, if player A opens the door, player B instead sees the whole building move.

{% hint style="info" %}
**💡 Tip**: Create an enum in your scene, to keep clear references to each syncable id in your 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
);
```

Here the EntityEnumId enum is used to tag entities with a unique identifier, ensuring that every client recognizes the modified entity, regardless of creation order.
{% endhint %}

{% hint style="warning" %}
**📔 Note**: Avoid using numbers that are higher than **8001** if your scene also includes Smart Items. Items that are created by the [Creator Hub](/creator/scene-editor/get-started/about-editor.md) with a Multiplayer component will use automatically assigned IDs from 8001 up. Any ID lower than 8001 is safe to assign to your synced entities.
{% endhint %}

**Entities created by a player**

If an entity is created as a result of a player interaction, and this entity should be synced with other players, the entity doesn't need an entityEnumId. You can use `syncEntity()` passing only the entity and the list of components. A unique value for entityEnumId is assigned automatically behind the curtains.

All entities instanced on scene initiation need to have a manually-assigned ID. That's to ensure that all players use the same ID on each. When a single player is in charge of instancing an entity, explicit IDs are not needed. Other players get updates about this new entity with an ID already assigned, so there is no risk of ID mismatches.

For example, in a snowball fight scene, every time a player throws a snowball, they're instancing a new entity that gets synced with other players. The snowball doesn't need a unique entityEnumId.

```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]);
}
```

**Parented entities**

The parent of an entity is normally defined via `parent` property in the `Transform` component. This property however points to the local entity id of the parent, which could vary, see [About enum id](#about-the-enum-id). To parent entities that need to be synced, or that have children that need to be synced, use the `parentEntity()` function instead of the `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 that both the parent and the child are synced with `syncEntity`, so all players have a common understanding of what ids are used by both entities. This is necessary even if the parent's components may never need to change. In this example, the `syncEntity` includes an empty array of components, to avoid syncing any unnecessary components.

{% hint style="warning" %}
**📔 Note**: If an entity is parented by both the `parentEntity()` and also the `parent` property in the `Transform` component, the property in the `Transform` component is ignored.
{% endhint %}

When entities are parented via the `parentEntity()` function, you can also make use of the following helper functions:

* **removeParent()**: Undo the effects of `parentEntity()`. It requires that you pass only the child entity. The entity's new parent becomes the scene's root entity. The original parent entity is not removed from the scene.
* **getParent()**: Returns the parent entity of an entity you passed.
* **getChildren()**: Returns the list of children of the entity you passed, as an iterable.
* **getFirstChild()**: Returns the first child on the list for the entity you passed.

```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]);

// sets parent as parent
parentEntity(child, parent);

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

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

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

// removes parent from child
removeParent(child);
```

## Check the sync state

When a player just loads into a scene, they may not yet be synchronized with other players surrounding them. If the player starts altering the state of the game before they are synced, this could cause problems in your game. We recommend always checking for a player to be synchronized before they are allowed to edit anything about the scene.

If a player steps out of the parcels of the scene, they will also be out of sync with the scene while they're standing outside. So it's also important that the scene's systems handle that scenario, as the scene keeps running while the player is nearby. Once the player steps back in, they are updated automatically with any changes from the scene's state.

You can check if the scene state is currently synced for a player via the `isStateSyncronized()` function. This function returns a boolean, that is true if the player is already synchronized with the scene.

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

const isConnected = isStateSyncronized();
```

You could for example include this check in a system, and block any interaction if this function returns 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();
  }
});
```

## Send Explicit MessageBus Messages

**Initiate a message bus**

Create a message bus object to handle the methods that are needed to send and receive messages between players.

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

const sceneMessageBus = new MessageBus();
```

**Send messages**

Use the `.emit` command of the message bus to send a message to all other players in the 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", {});
  }
);
```

Each message can contain a payload as a second argument. The payload is of type `Object`, and can contain any relevant data you wish to send.

```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" %}
**💡 Tip**: If you need a single message to include data from more than one variable, create a custom type to hold all this data in a single object.
{% endhint %}

**Receive messages**

To handle messages from all other players in that scene, use `.on`. When using this function, you provide a message string and define a function to execute. For each time that a message with a matching string arrives, the given function is executed once.

```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" %}
**📔 Note**: Messages that are sent by a player are also picked up by that same player. The `.on` method can't distinguish between a message that was emitted by that same player from a message emitted from other players.
{% endhint %}

**Full MessageBus example**

This example uses a message bus to send a new message every time the main cube is clicked, generating a new cube in a random position. The message includes the position of the new cube, so that all players see these new cubes in the same positions.

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

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

// Cube factory
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);

  // When a cube is clicked, send message to spawn another one
  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;
}

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

// define type of data
type NewBoxPosition = {
  position: { x: number; y: number; z: number };
};

// on spawn message, create new cube
sceneMessageBus.on("spawn", (info: NewBoxPosition) => {
  createCube(info.position.x, info.position.y, info.position.z);
});
```

## Test a multiplayer scene locally

If you launch a scene preview and open it in two (or more) different explorer windows, each open window will be interpreted as a separate player, and a mock communications server will keep these players in sync.

Interact with the scene on one window, then switch to the other to see that the effects of that interaction are also visible there.

Using the Creator Hub, click the Preview button a second time, and that opens a second Decentraland explorer window. You must connect on both windows with different addresses. The same sessions will remain open as the scene reloads.

![](/files/kFXFURDUtsBJrB1sRPNv)

As an alternative, you can open a second Decentraland explorer window by writing the following into a browser URL:

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

## Single player scenes

If your scene is deployed to a [Decentraland World](/creator/worlds/about.md), you can make it a single player scene. Players won't see each other, won't be able to chat or see the effects of each other's actions.

To do this, configure the scene's `scene.json` file to set the **fixedAdapter** to `offline:offline`. The scene will have no Communication Service at all and each user joining that world will always be alone.

**Example:**

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


---

# 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/creator/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.
