Network Clients (clientes de red) y Servers (servidores)
Utilizando el API del Transport Layer

Host Migration (Migración de anfitrión)

En un juego de red multi-jugador sin un servidor dedicado, uno de los compañeros en el juego actúa como el centro de autoridad del juego. Este compañero se llama “host” (anfitrión). Este corre un servidor y un “local client” (cliente local), mientras que otros compañeros cada uno corre un “remote client” (cliente remoto).

Si el host (anfitrión) de un juego se pierde, entonces el juego no puede continuar. El anfitrión podría haberse perdido debido a que el jugador se fue, el proceso del anfitrión fallo o murió, la maquina del anfitrión se apago, o porque la conexión a la red del anfitrión se perdió.

La característica de “Host Migration” le permite a uno de los clientes remotos volverse el nuevo anfitrión, para que el juego multi-jugador continúe.

Cómo funciona

Durante un juego multi-jugador, con host migration (migración de anfitrión) habilitada, las direcciones de los compañeros serán distribuidas a los compañeros en el juego. Cuando el anfitrión se haya perdido, un compañero se puede volver el nuevo anfitrión. Los otros compañeros luego se conectan al nuevo anfitrión y el juego continúa.

El nuevo componente NetworkMigrationManager se puede soltar en un juego multi-jugador que utilice el HLAPI del Unity Networking, y le permite al juego continuar con un nuevo anfitrión después de que el anfitrión original se haya perdido. Abajo hay una captura de pantalla del NetworkMigrationManager en el inspector del editor. Este muestra el estado actual de migración.

El componente Network Migration Manager
El componente Network Migration Manager

Hay una interfaz de usuario simple proporcionada por el NetworkMigrationManager, similar al NetworkManagerHUD. Esta interfaz de usuario está intencionada para pruebas y prototipado; un juego real implementaría una interfaz de usuario personalizada para la migración de anfitrión, y probablemente una lógica personalizada - posiblemente escogiendo el nuevo anfitrión de manera automática sin requerir un input por parte del usuario.

El HUD de prototipado del Network Migration Manager
El HUD de prototipado del Network Migration Manager

Incluso si la migración ocurrió porque el antiguo anfitrión perdió conexión o se salió del juego, es posible que el viejo anfitrión se una nuevamente al juego como un cliente en el nuevo anfitrión.

El estado de SyncVars y SyncList en todos los objetos en red en la escena son mantenidos durante una migración de anfitrión. Esto también aplica a datos personalizados serializados para objetos.

Todos los objetos jugador en el juego son desactivados cuando el anfitrión se haya perdido. Luego, cuando otros clientes se re-unan al nuevo juego en el nuevo anfitrión, los jugadores correspondientes para esos clientes son re-habilitados en el anfitrión y re-generados en otros clientes. Esto se hace para que ningún dato del estado del jugador se pierda durante una migración de anfitrión.

TENGA EN CUENTA: Solamente los datos que están disponibles a los clientes se mantendrán durante una migración de anfitrión. Si hay datos que solamente están en el servidor, estos no estarán disponibles al cliente que se vuelve el nuevo anfitrión. Entonces cualquier dato en el anfitrión que no esté almacenado en SyncVars o SyncList no estarán disponibles después de una migración de anfitrión.

La función callback OnStartServer es invocada para todos los objetos en red cuando el cliente se vuelve un nuevo anfitrión.

En el nuevo anfitrión, el NetworkMigrationManager utiliza la función BecomeNewHost para construir una escena de servidor en red del estado de la ClientScene (escena de cliente) actual.

Los compañeros en un juego con host migration habilitado son identificados por su connectionId en el servidor. Cuando un cliente se re-conecta al nuevo anfitrión de un juego, esta connectionID se pasa al nuevo anfitrión para que pueda coincidir con este cliente con el cliente que fue conectado al anfitrión viejo. Este ID es configurado en la ClientScene como “reconnectId”.

Objetos no-jugador

Los objetos no jugador con autoridad de cliente también son manejados por la migración de anfitrión. Los objetos dueños de cada cliente son desactivados y re-habilitados de la misma manera que los objetos jugador lo son.

Identificando compañeros

Antes de que el anfitrión se pierda, todos los compañeros son conectados al anfitrión. Estos cada uno tienen un connectionId único en el anfitrión - esto se llama el “oldConnectionId” en el contexto de migración de anfitrión.

Cuando un nuevo anfitrión es escogido y los compañeros se re-conectan a este, estos proporcionan su “oldConnectionId” para identificar qué compañero es. Esto permite que el nuevo anfitrión coincida este cliente re-conectándose con el objeto jugador correspondiente.

El antiguo anfitrión utiliza un oldConnectionId especial de cero para re-conectarse - ya que este no tenía una conexión al viejo anfitrión, ERA el antiguo anfitrión. Hay una constante ClientScene.ReconnectIdHost para esto.

Cuando utilice la interfaz de usuario integrada, el oldConnectionId se configura automáticamente. Esta puede ser configurada manualmente utilizando NetworkMigrationManager.Reset o ClientScene.SetReconnectId.

Flujo de Host Migration (Migración de anfitrión)

  1. MaquinaA aloja un juego con host-migration habilitado

  2. MaquinaB empieza un cliente y se une al juego con host-migration habilitado
    • MaquinaB se le dice acerca de sus compañeros (MaquinaA–0, y sí misma (MaquinaB)–1)
  3. MaquinaC empieza un cliente y se une al juego con host-migration habilitado
    • MaquinaC se le dice acerca de sus compañeros (MaquinaA–0, MaquinaB–1, y sí misma (MaquinaC)–2)
  4. MaquinaA se pierde, entonces el anfitrión se pierde

  5. MaquinaB se desconecta del anfitrión
    1. La función callback de la MaquinaB es invocada en MigrationManager en el cliente
    2. Los objetos jugador de la MaquinaB para todos los jugadores son desactivados
    3. MaquinaB se mantiene en la escena en linea
  6. MaquinaB utiliza la función de utilizada para escoger el nuevo anfitrión, escoge así misma.
    1. MaquinaB llama BecomeNewHost()
    2. MaquinaB empieza a escuchar
    3. Los objetos jugador para la MaquinaB en sí se re-activan
    4. MaquinaB El jugador para MaquinaB está ahora devuelta en el juego con todos sus estados viejos
  7. MaquinaC se desconecta del anfitrión
    1. La función callback de la MaquinaC es invocada en MigrationManager en el cliente
    2. Los objetos jugador de la MaquinaC para todos los jugadores se desactivan
    3. MaquinaC se mantiene en la escena en linea
  8. La MaquinaC utiliza la función de utilizada para escoger un nuevo anfitrión, escoge MaquinaB
    • MaquinaC se re-conecta al nuevo anfitrión
  9. MaquinaB recibe la conexión de la MaquinaC
    1. MaquinaC envía un mensaje de re-conexión con oldConnectionId (en vez del mensaje AddPlayer)
    2. La función callback es invocada sobre MigrationManager en el servidor
    3. La MaquinaB utiliza oldConnectionId para encontrar el objeto jugador desactivado para el jugador y lo re-agrega con ReconnectPlayerForConnection()
    4. El objeto jugador es re-generado para MaquinaC
    5. El jugador para MaquinaC está de vuelta al juego con todos sus estados antiguos
  10. MaquinaA recupera (al antiguo anfitrión)
    1. MaquinaA utiliza la función de utilidad para escoger el nuevo anfitrión, escoge MaquinaB
    2. La MaquinaA se “reconecta” a la MaquinaB
  11. La MaquinaB recibe la conexión de la MaquinaA

  12. La MaquinaA envía un mensaje de re-conexión con oldConnectionId de cero
    1. La función callback se invoca en MigrationManager en el servidor (MaquinaB)
    2. MaquinaB utiliza el oldConnectionId para encontrar el objeto jugador desactivado para el jugador y lo re-agrega con ReconnectPlayerForConnection()
    3. El objeto jugador es re-generado para la MaquinaA
    4. El jugador para la MaquinaA está de vuelta en el juego con todos sus estados viejos

Funciones callback

Las funciones callback en el NetworkHostMigrationManager

// called on client after the connection to host is lost. controls whether to switch scenes
protected virtual void OnClientDisconnectedFromHost(
    NetworkConnection conn, 
    out SceneChangeOption sceneChange)

// called on host after the host is lost. host MUST change scenes
protected virtual void OnServerHostShutdown()

// called on new host (server) when a client from the old host re-connects a player
protected virtual void OnServerReconnectPlayer(
    NetworkConnection newConnection, 
    GameObject oldPlayer, 
    int oldConnectionId, 
    short playerControllerId)

// called on new host (server) when a client from the old host re-connects a player
protected virtual void OnServerReconnectPlayer(
    NetworkConnection newConnection, 
    GameObject oldPlayer, 
    int oldConnectionId, 
    short playerControllerId, 
    NetworkReader extraMessageReader)

// called on new host (server) when a client from the old host re-connects a non-player object
protected virtual void OnServerReconnectObject(
    NetworkConnection newConnection, 
    GameObject oldObject, 
    int oldConnectionId)

// called on both host and client when the set of peers is updated
protected virtual void OnPeersUpdated(
    PeerListMessage peers)

// utility function called by the default UI on client after connection to host was lost, to pick a new host.
public virtual bool FindNewHost(
    out NetworkSystem.PeerInfoMessage newHostInfo, 
    out bool youAreNewHost)

// called when the authority of a non-player object changes
protected virtual void OnAuthorityUpdated(
    GameObject go,
    int connectionId,
    bool authorityState)

Restricciones

Los datos que solamente están presentes en el servidor (el anfitrión) se perderán cuando el anfitrión se desconecte. Para que los juegos sean capaces de realizar migraciones de anfitrión correctas, los datos importantes deben estar distribuidos a los clientes, no mantenidos en secreto en el servidor.

Esto funciona para conexiones directas con juegos. Un trabajo adicional se requiere para que esto funcione con el matchmaker y relay server.

Network Clients (clientes de red) y Servers (servidores)
Utilizando el API del Transport Layer