For the complete documentation index, see llms.txt. This page is also available as Markdown.

Multiplayer sin servidor

Sincroniza el estado de la escena entre jugadores.

Decentraland ejecuta las Scenes localmente en la instancia de un jugador del explorer. De forma predeterminada, los jugadores pueden verse entre sí e interactuar directamente, pero cada jugador interactúa con el entorno de manera independiente. Los cambios en el entorno no se comparten entre jugadores por defecto.

Ver el mismo contenido en el mismo estado es extremadamente importante para que los jugadores interactúen de maneras más significativas.

Hay tres formas de sincronizar el estado de la Scene, para que todos los jugadores vean lo mismo:

  • Marcar una entity como sincronizada: La opción más fácil. Ver Marcar una entity como sincronizada

  • Enviar mensajes explícitos de MessageBus: Enviar y escuchar manualmente mensajes específicos. Ver Enviar mensajes explícitos de MessageBus

  • Usar un Authoritative Server: Ver Authoritative Servers. El server valida todos los cambios de estado y es la única fuente de verdad. Requiere más configuración, pero se recomienda encarecidamente cuando los jugadores tienen incentivos para explotar tu Scene.

Las dos primeras opciones se cubren en este documento. Son más simples, ya que no requieren server. La desventaja es que dependes más de las velocidades de conexión del jugador, y el estado de la Scene no se mantiene cuando todos los jugadores salen de la Scene.

Marcar una Entity como sincronizada

En el Creator Hub, marca una entity como sincronizada añadiéndole un componente Multiplayer . Incluye una casilla para cada uno de los demás componentes de la entity, lo que te permite seleccionar cuáles actualizar.



Para marcar una entity como sincronizada mediante código, usa la syncEntity function:

El syncEntity function recibe las siguientes entradas:

  • entityId: Una referencia a la entity que se va a sincronizar

  • componentIds: Una lista de los componentes que necesitan sincronizarse de esa entity. Es un array que puede contener tantas entities como sea necesario. Todos los valores deben ser propiedades de componentId .

  • entityEnumId: (opcional) Un id único que se usa de forma consistente por todos los jugadores, ver About enum id.

No todas las entities o componentes necesitan sincronizarse. Los elementos estáticos, como un árbol que permanece en el mismo lugar, no requieren sincronización. En las entities que sí sincronizas, solo deben sincronizarse los componentes que cambian con el tiempo. Por ejemplo, si un cubo cambia de color al hacer click, solo deberías sincronizar el componente Material, no el MeshRenderer ni el Transform, ya que esos nunca cambiarán.

💡 Consejo: Si los datos que quieres compartir no existen como componente, define un componente personalizado que contenga esos datos.

Acerca del enum id

El entityEnumId de una entity debe ser único. No está relacionado con el entityId local asignado por engine.addEntity(), que se genera automáticamente y puede variar entre los jugadores que ejecutan la misma Scene. El entityEnumId de una entity debe definirse explícitamente en el código y ser único.

Establecer explícitamente este ID es importante para evitar inconsistencias si una condición de carrera hace que una parte de la Scene cargue antes que otra. Quizá para el jugador A la puerta de la Scene sea la entity 512, pero para el jugador B esa misma puerta es la entity 513. En ese caso, si el jugador A abre la puerta, el jugador B ve en su lugar que todo el edificio se mueve.

💡 Consejo: Crea un enum en tu Scene, para mantener referencias claras a cada id sincronizable en tu Scene.

Aquí el enum EntityEnumId se usa para etiquetar entities con un identificador único, asegurando que cada client reconozca la entity modificada, independientemente del orden de creación.

Entities creadas por un jugador

Si una entity se crea como resultado de la interacción de un jugador, y esta entity debe sincronizarse con otros jugadores, la entity no necesita un entityEnumId. Puedes usar syncEntity() pasando solo la entity y la lista de componentes. Un valor único para entityEnumId se asigna automáticamente entre bastidores.

Todas las entities instanciadas al iniciar la Scene necesitan tener un ID asignado manualmente. Eso es para garantizar que todos los jugadores usen el mismo ID en cada una. Cuando un solo jugador está a cargo de instanciar una entity, no se necesitan IDs explícitos. Los demás jugadores reciben actualizaciones sobre esta nueva entity con un ID ya asignado, por lo que no hay riesgo de desajustes de IDs.

Por ejemplo, en una Scene de pelea de bolas de nieve, cada vez que un jugador lanza una bola de nieve, está instanciando una nueva entity que se sincroniza con otros jugadores. La bola de nieve no necesita un entityEnumId único.

Entities parentadas

El padre de una entity normalmente se define mediante parent propiedad en el Transform component. Sin embargo, esta propiedad apunta al entity id local del padre, que podría variar, ver About enum id. Para parentar entities que necesitan sincronizarse, o que tienen hijos que necesitan sincronizarse, usa la parentEntity() function en lugar de la Transform.

Ten en cuenta que tanto el padre como el hijo se sincronizan con syncEntity, así que todos los jugadores tienen una comprensión común de qué ids usan ambas entities. Esto es necesario incluso si los componentes del padre quizá nunca necesiten cambiar. En este ejemplo, la syncEntity incluye un array vacío de componentes, para evitar sincronizar componentes innecesarios.

Cuando las entities se parentan mediante la parentEntity() function, también puedes hacer uso de las siguientes funciones auxiliares:

  • removeParent(): Deshace los efectos de parentEntity(). Requiere que pases solo la entity hija. El nuevo padre de la entity pasa a ser la root entity de la Scene. La entity padre original no se elimina de la Scene.

  • getParent(): Devuelve la entity padre de una entity que hayas pasado.

  • getChildren(): Devuelve la lista de hijos de la entity que hayas pasado, como un iterable.

  • getFirstChild(): Devuelve el primer hijo de la lista de la entity que hayas pasado.

Comprueba el estado de sincronización

Cuando un jugador acaba de cargar en una Scene, es posible que todavía no esté sincronizado con los otros jugadores que lo rodean. Si el jugador empieza a alterar el estado del juego antes de estar sincronizado, esto podría causar problemas en tu juego. Recomendamos comprobar siempre que un jugador esté sincronizado antes de permitirle editar cualquier cosa de la Scene.

Si un jugador sale de los parcels de la Scene, también quedará fuera de sincronización con la Scene mientras permanezca fuera. Por eso también es importante que los systems de la Scene manejen ese escenario, ya que la Scene sigue ejecutándose mientras el jugador esté cerca. Cuando el jugador vuelva a entrar, se le actualizará automáticamente con cualquier cambio del estado de la Scene.

Puedes comprobar si el estado de la Scene está sincronizado actualmente para un jugador mediante la isStateSyncronized() function. Esta function devuelve un boolean, que es true si el jugador ya está sincronizado con la Scene.

Por ejemplo, podrías incluir esta comprobación en un system y bloquear cualquier interacción si esta function devuelve false.

Enviar mensajes explícitos de MessageBus

Iniciar un message bus

Crea un objeto message bus para gestionar los métodos necesarios para enviar y recibir mensajes entre jugadores.

Enviar mensajes

Usa la .emit command del message bus para enviar un mensaje a todos los demás jugadores de la Scene.

Cada mensaje puede contener un payload como segundo argumento. El payload es de tipo Object, y puede contener cualquier dato relevante que quieras enviar.

💡 Consejo: Si necesitas que un solo mensaje incluya datos de más de una variable, crea un tipo personalizado para guardar todos esos datos en un solo objeto.

Recibir mensajes

Para manejar mensajes de todos los demás jugadores en esa Scene, usa .on. Al usar esta function, proporcionas una cadena de mensaje y defines una function que se ejecutará. Cada vez que llega un mensaje con una cadena coincidente, la function dada se ejecuta una vez.

Ejemplo completo de MessageBus

Este ejemplo usa un message bus para enviar un nuevo mensaje cada vez que se hace click en el cubo principal, generando un nuevo cubo en una posición aleatoria. El mensaje incluye la posición del nuevo cubo, de modo que todos los jugadores vean estos nuevos cubos en las mismas posiciones.

Probar localmente una escena multijugador

Si lanzas una vista previa de la escena y la abres en dos (o más) ventanas de Explorer diferentes, cada ventana abierta se interpretará como un jugador distinto, y un servidor de comunicaciones simulado mantendrá a estos jugadores sincronizados.

Interactúa con la escena en una ventana y luego cambia a la otra para ver que los efectos de esa interacción también son visibles allí.

Usando Creator Hub, haz clic en el botón Preview una segunda vez, y eso abrirá una segunda ventana del explorer de Decentraland. Debes conectarte en ambas ventanas con direcciones diferentes. Las mismas sesiones permanecerán abiertas mientras la escena se recarga.



Como alternativa, puedes abrir una segunda ventana de Explorer de Decentraland escribiendo lo siguiente en una URL del navegador:

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

Escenas de un solo jugador

Si tu Scene se despliega en un Decentraland World, puedes convertirla en una Scene de un solo jugador. Los jugadores no se verán entre sí, no podrán chatear ni ver los efectos de las acciones de los demás.

Para hacer esto, configura el scene.json archivo de la Scene para establecer el fixedAdapter a offline:offline. La Scene no tendrá ningún Communication Service en absoluto y cada usuario que entre en ese World siempre estará solo.

Ejemplo:

Última actualización