En el HLAPI (API de Alto Nivel) del sistema en red los jugadores son un tipo especial de objetos. Estos representan el jugador en el servidor y tienen la habilidad de correr comandos (los cuales son llamadas procedure cliente-servidor remotas seguras) del cliente del jugador. En este sistema autoritario de servidor, otros objetos del lado del servidor no jugador no tienen la capacidad de recibir comandos directamente de objeto en los clientes. Esto es tanto por seguridad y para reducir la complejidad de trabajar en un ambiente distribuido. Enrutar todos los comandos entrantes de los usuarios a través del objeto jugador asegura que estos mensajes vinieron del lugar correcto, el cliente correcto, y pueden manejarse a través de una ubicación central.
Cuando utilice el NetworkManager, un jugador es agregado por defecto cuando un cliente se conecta al servidor. Aunque en algunas situaciones, agregar los jugadores se deberían deferir hasta que algún evento input suceda, por lo que este comportamiento se puede apagar con la casilla de verificación AutoCreatePlayer en el NetworkManager. Cuando un jugador es agregado, el NetworkManager va a instanciar un objeto del PlayerPrefab y lo va a asociar con la conexión. Esto en realidad es hecho por el NetworkManager al llamar NetworkServer.AddPlayerForConnection. Este comportamiento se puede modificar al anular el NetworkManager.OnServerAddPlayer. La implementación por defecto de OnServerAddPlayer instancia una nueva instancia del jugador del PlayerPrefab y llama el NetworkServer.AddPlayerForConnection para generar una nueva instancia jugador. Una implementación personalizada de OnServerAddPlayer también debe llamar NetworkServer.AddPlayerForConnection, pero es libre de realizar cualquier otra inicialización que requiera. El ejemplo de abajo personaliza el color de un jugador:
class Player : NetworkBehaviour
{
[SyncVar]
public Color color;
}
class MyManager : NetworkManager
{
public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
{
GameObject player = (GameObject)Instantiate(playerPrefab, Vector3.zero, Quaternion.identity);
player.GetComponent<Player>().color = Color.red;
NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);
}
}
La función NetworkServer.AddPlayerForConnection no se necesita llamar de OnServerAddPlayer. Con tal de que el objeto de conexión correcto y playerControllerId sean pasados, se puede llamar después de que OnServerAddPlayer haya devuelto. Esto permite que pasos asincrónicos sucedan entre ellos, tal como cargar datos jugador de una fuente de datos remota.
El HLAPI (API de alto nivel) trata a los jugadores y clientes como objetos separados. En la mayoría de casos, hay un solo jugador para cada cliente. Pero, en algunas situaciones - tal como cuándo hay varios controles conectados a un sistema de consola, pueden haber varios objetos jugador para una sola conexión. Cuando hay varios jugadores para una conexión la propiedad playerControllerId es utilizada para diferenciarlos. Esto es un identificador que se relaciona a la conexión - para que literalmente asigne el id del control asociado con el jugador en ese cliente.
El objeto jugador pasado a NetworkServer.AddPlayerForConnection en el servidor es automáticamente generado por el sistema, por lo que no hay necesidad de llamar NetworkServer.Spawn para el jugador. Una vez el jugador esté listo, los objetos NetworkIdentity activos en la escena serán generados en el cliente del jugador. Por lo que todos los objetos en red en el juego serán creados en ese cliente con su último estado, por lo que están sincronizados con otros participantes en el juego.
El playerPrefab en el NetworkManager no tiene que ser utilizado para crear objetos jugador. Usted puede utilizar diferentes métodos de crear diferentes jugadores.
La función AddPlayerForConnection no tiene que ser llamada dentro de OnServerAddPlayer. También puede ser llamada de manera asincrónica, tal como cuando una solicitud de otro servicio como una base de datos devuelve información acerca de qué tipo de jugador crear.
Adicionalmente a los jugadores, las conexiones cliente también tienen un estado “ready” (listo). Un cliente que está listo es enviado objetos generados (spawned) y actualizaciones de estado sincronizadas; un cliente que no esté listo, no se le envía estas actualizaciones. Cuando un cliente inicialmente se conecta a un servidor, no está listo. Mientras se está en este estado, el cliente es capaz de hacer cosas que no requieran interacciones en tiempo real con la simulación del servidor, tal como cargar escenas, escoger avatars o llenas casillas de login. Una vez un cliente tenga todo su trabajo pre-juego hecho, y todos sus assets han sido cargados, pueden llamar ClientScene.Ready para ingresar el estado ready (listo). El ejemplo simple de arriba también funciona, como al agregar un jugador con NetworkServer.AddPlayerForConnection también coloca el cliente en el estado ready (listo) si ya no está en ese estado.
Los clientes pueden enviar y recibir mensajes en red sin estar ready (listos), lo cual también significa sin tener un jugador activo. Por lo que un cliente en un menú o en una pantalla de selección puede conectarse al juego e interactuar con él, incluso si no tiene un objeto jugador. Hay una sección después en este documento acerca de enviar mensajes sin utilizar comandos y llamadas RPC.
El objeto jugador para una conexión se puede remplazar con NetworkServer.ReplacePlayerForConnection. Esto puede ser útil para restringir los comandos que son emitidos por jugadores en cierto tiempo, tal como en una pantalla lobby antes del juego. Esta funciona toma los mismo argumentos que AddPlayerForConnection, pero permite que ya esté un jugador para esa conexión. El objeto jugador viejo no tiene que ser destruido. El NetworkLobbyManager utiliza esta técnica para cambiar de LobbyPlayer a un jugador dentro del juego cuando todos los jugadores en el lobby estén listos.
Esto también se puede utilizar par re-generar (respawn) un jugador después de que su objeto sea destruido. En la mayoría de casos, es mejor simplemente desactivar un objeto y re-iniciar los atributos de juego en la re-generación, pero para en realidad remplazarlo con un nuevo objeto usted podría utilizar código así:
class GameManager
{
public void PlayerWasKilled(Player player)
{
var conn = oldPlayer.connectionToClient;
var newPlayer = Instantiate<GameObject>(playerPrefab);
Destroy(oldPlayer.gameObject);
NetworkServer.ReplacePlayerForConnection(conn, newPlayer, 0);
}
}
Si el objeto jugador para una conexión es destruido, entonces ese cliente será incapaz de ejecutar comandos. Estos sin embargo todavía envían mensajes en red.
Para utilizar ReplacePlayerForConnection usted debe tener el objeto NetworkConnection para el cliente del jugador para establecer la relación entre el objeto y el cliente. Esto en realidad usualmente es la propiedad connectionToClient en la clase NetworkBehaviour, pero si el jugador viejo ya ha sido destruido, entonces eso podría no estar fácilmente disponible.
Para encontrar la conexión, hay algunas listas disponibles. Si utilizando el NetworkLobbyManager, entonces los jugadores lobby están disponibles en lobbySlots. También, el NetworkServer tiene listas de conexiones y conexioneslocales.