Merge pull request #650 from vis2k/networkclient_static_2

BREAKING CHANGE: Make NetworkClient Great Again!
This commit is contained in:
vis2k 2019-03-26 12:17:38 +01:00 committed by GitHub
commit fac05428cc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 174 additions and 228 deletions

View File

@ -314,7 +314,7 @@ public void SetTrigger(int hash)
{
if (hasAuthority && localPlayerAuthority)
{
if (NetworkClient.singleton != null && ClientScene.readyConnection != null)
if (ClientScene.readyConnection != null)
{
CmdOnAnimationTriggerServerMessage(hash);
}

View File

@ -328,7 +328,7 @@ public override void OnStopHost()
#region client handlers
public override void OnStartClient(NetworkClient lobbyClient)
public override void OnStartClient()
{
if (lobbyPlayerPrefab == null || lobbyPlayerPrefab.gameObject == null)
Debug.LogError("NetworkLobbyManager no LobbyPlayer prefab is registered. Please add a LobbyPlayer prefab.");
@ -340,7 +340,7 @@ public override void OnStartClient(NetworkClient lobbyClient)
else
ClientScene.RegisterPrefab(playerPrefab);
OnLobbyStartClient(lobbyClient);
OnLobbyStartClient();
}
public override void OnClientConnect(NetworkConnection conn)
@ -373,9 +373,9 @@ public override void OnClientChangeScene(string newSceneName)
{
if (LogFilter.Debug) Debug.LogFormat("OnClientChangeScene from {0} to {1}", SceneManager.GetActiveScene().name, newSceneName);
if (SceneManager.GetActiveScene().name == LobbyScene && newSceneName == GameplayScene && dontDestroyOnLoad && IsClientConnected() && client != null)
if (SceneManager.GetActiveScene().name == LobbyScene && newSceneName == GameplayScene && dontDestroyOnLoad && IsClientConnected())
{
GameObject lobbyPlayer = client?.connection?.playerController?.gameObject;
GameObject lobbyPlayer = NetworkClient.connection?.playerController?.gameObject;
if (lobbyPlayer != null)
{
lobbyPlayer.transform.SetParent(null);
@ -385,14 +385,14 @@ public override void OnClientChangeScene(string newSceneName)
Debug.LogWarningFormat("OnClientChangeScene: lobbyPlayer is null");
}
else
if (LogFilter.Debug) Debug.LogFormat("OnClientChangeScene {0} {1} {2}", dontDestroyOnLoad, IsClientConnected(), client != null);
if (LogFilter.Debug) Debug.LogFormat("OnClientChangeScene {0} {1}", dontDestroyOnLoad, IsClientConnected());
}
public override void OnClientSceneChanged(NetworkConnection conn)
{
if (SceneManager.GetActiveScene().name == LobbyScene)
{
if (client.isConnected)
if (NetworkClient.isConnected)
CallOnClientEnterLobby();
}
else
@ -452,7 +452,7 @@ public virtual void OnLobbyClientConnect(NetworkConnection conn) {}
public virtual void OnLobbyClientDisconnect(NetworkConnection conn) {}
public virtual void OnLobbyStartClient(NetworkClient lobbyClient) {}
public virtual void OnLobbyStartClient() {}
public virtual void OnLobbyStopClient() {}

View File

@ -138,14 +138,6 @@ public static bool Ready(NetworkConnection conn)
return false;
}
public static NetworkClient ConnectLocalServer()
{
LocalClient newClient = new LocalClient();
NetworkServer.ActivateLocalClientScene();
newClient.InternalConnectLocalServer();
return newClient;
}
internal static void HandleClientDisconnect(NetworkConnection conn)
{
if (readyConnection == conn && ready)

View File

@ -1,68 +0,0 @@
using System.Collections.Generic;
using UnityEngine;
namespace Mirror
{
sealed class LocalClient : NetworkClient
{
// local client in host mode might call Cmds/Rpcs during Update, but we
// want to apply them in LateUpdate like all other Transport messages
// to avoid race conditions. keep packets in Queue until LateUpdate.
internal Queue<byte[]> packetQueue = new Queue<byte[]>();
internal void InternalConnectLocalServer()
{
// create local connection to server
connection = new ULocalConnectionToServer();
SetHandlers(connection);
// create server connection to local client
ULocalConnectionToClient connectionToClient = new ULocalConnectionToClient(this);
NetworkServer.SetLocalConnection(connectionToClient);
connectState = ConnectState.Connected;
active = true;
RegisterSystemHandlers(true);
packetQueue.Enqueue(MessagePacker.Pack(new ConnectMessage()));
}
public override void Disconnect()
{
connectState = ConnectState.Disconnected;
ClientScene.HandleClientDisconnect(connection);
if (isConnected)
{
packetQueue.Enqueue(MessagePacker.Pack(new DisconnectMessage()));
}
NetworkServer.RemoveLocalConnection();
}
internal override void Update()
{
// process internal messages so they are applied at the correct time
while (packetQueue.Count > 0)
{
byte[] packet = packetQueue.Dequeue();
OnDataReceived(packet);
}
}
// Called by the server to set the LocalClient's LocalPlayer object during NetworkServer.AddPlayer()
internal void AddLocalPlayer(NetworkIdentity localPlayer)
{
if (LogFilter.Debug) Debug.Log("Local client AddLocalPlayer " + localPlayer.gameObject.name + " conn=" + connection.connectionId);
connection.isReady = true;
connection.SetPlayerController(localPlayer);
if (localPlayer != null)
{
localPlayer.isClient = true;
NetworkIdentity.spawned[localPlayer.netId] = localPlayer;
localPlayer.connectionToServer = connection;
}
// there is no SystemOwnerMessage for local client. add to ClientScene here instead
ClientScene.InternalAddPlayer(localPlayer);
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 5c4d04450e91c438385de7300abef1b6
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -6,19 +6,15 @@ namespace Mirror
// sending messages on this connection causes the client's handler function to be invoked directly
class ULocalConnectionToClient : NetworkConnection
{
public LocalClient localClient;
public ULocalConnectionToClient(LocalClient localClient) : base ("localClient")
public ULocalConnectionToClient() : base ("localClient")
{
this.localClient = localClient;
// local player always has connectionId == 0
connectionId = 0;
}
internal override bool SendBytes(byte[] bytes, int channelId = Channels.DefaultReliable)
{
localClient.packetQueue.Enqueue(bytes);
NetworkClient.localClientPacketQueue.Enqueue(bytes);
return true;
}
}

View File

@ -4,53 +4,52 @@
namespace Mirror
{
public enum ConnectState
{
None,
Connecting,
Connected,
Disconnected
}
// TODO make fully static after removing obsoleted singleton!
public class NetworkClient
{
// the client (can be a regular NetworkClient or a LocalClient)
public static NetworkClient singleton;
[Obsolete("Use NetworkClient directly. Singleton isn't needed anymore, all functions are static now. For example: NetworkClient.Send(message) instead of NetworkClient.singleton.Send(message).")]
public static NetworkClient singleton = new NetworkClient();
[Obsolete("Use NetworkClient.singleton instead. There is always exactly one client.")]
public static List<NetworkClient> allClients => new List<NetworkClient>{singleton};
public readonly Dictionary<int, NetworkMessageDelegate> handlers = new Dictionary<int, NetworkMessageDelegate>();
public static readonly Dictionary<int, NetworkMessageDelegate> handlers = new Dictionary<int, NetworkMessageDelegate>();
public NetworkConnection connection { get; protected set; }
public static NetworkConnection connection { get; internal set; }
protected enum ConnectState
{
None,
Connecting,
Connected,
Disconnected
}
protected ConnectState connectState = ConnectState.None;
internal static ConnectState connectState = ConnectState.None;
public string serverIp { get; private set; } = "";
public static string serverIp { get; internal set; } = "";
// active is true while a client is connecting/connected
// (= while the network is active)
public static bool active { get; protected set; }
public static bool active { get; internal set; }
public bool isConnected => connectState == ConnectState.Connected;
public static bool isConnected => connectState == ConnectState.Connected;
public NetworkClient()
{
if (LogFilter.Debug) Debug.Log("Client created version " + Version.Current);
// NetworkClient can connect to local server in host mode too
public static bool isLocalClient => connection is ULocalConnectionToServer;
if (singleton != null)
{
Debug.LogError("NetworkClient: can only create one!");
return;
}
singleton = this;
}
// local client in host mode might call Cmds/Rpcs during Update, but we
// want to apply them in LateUpdate like all other Transport messages
// to avoid race conditions. keep packets in Queue until LateUpdate.
internal static Queue<byte[]> localClientPacketQueue = new Queue<byte[]>();
internal void SetHandlers(NetworkConnection conn)
internal static void SetHandlers(NetworkConnection conn)
{
conn.SetHandlers(handlers);
}
public void Connect(string ip)
// connect remote
public static void Connect(string ip)
{
if (LogFilter.Debug) Debug.Log("Client Connect: " + ip);
@ -69,7 +68,43 @@ public void Connect(string ip)
connection.SetHandlers(handlers);
}
void InitializeTransportHandlers()
// connect host mode
internal static void ConnectLocalServer()
{
if (LogFilter.Debug) Debug.Log("Client Connect Local Server");
active = true;
RegisterSystemHandlers(true);
connectState = ConnectState.Connected;
// create local connection to server
connection = new ULocalConnectionToServer();
SetHandlers(connection);
// create server connection to local client
ULocalConnectionToClient connectionToClient = new ULocalConnectionToClient();
NetworkServer.SetLocalConnection(connectionToClient);
localClientPacketQueue.Enqueue(MessagePacker.Pack(new ConnectMessage()));
}
// Called by the server to set the LocalClient's LocalPlayer object during NetworkServer.AddPlayer()
internal static void AddLocalPlayer(NetworkIdentity localPlayer)
{
if (LogFilter.Debug) Debug.Log("Local client AddLocalPlayer " + localPlayer.gameObject.name + " conn=" + connection.connectionId);
connection.isReady = true;
connection.SetPlayerController(localPlayer);
if (localPlayer != null)
{
localPlayer.isClient = true;
NetworkIdentity.spawned[localPlayer.netId] = localPlayer;
localPlayer.connectionToServer = connection;
}
// there is no SystemOwnerMessage for local client. add to ClientScene here instead
ClientScene.InternalAddPlayer(localPlayer);
}
static void InitializeTransportHandlers()
{
Transport.activeTransport.OnClientConnected.AddListener(OnConnected);
Transport.activeTransport.OnClientDataReceived.AddListener(OnDataReceived);
@ -77,12 +112,12 @@ void InitializeTransportHandlers()
Transport.activeTransport.OnClientError.AddListener(OnError);
}
void OnError(Exception exception)
static void OnError(Exception exception)
{
Debug.LogException(exception);
}
void OnDisconnected()
static void OnDisconnected()
{
connectState = ConnectState.Disconnected;
@ -91,7 +126,7 @@ void OnDisconnected()
connection?.InvokeHandler(new DisconnectMessage());
}
protected void OnDataReceived(byte[] data)
internal static void OnDataReceived(byte[] data)
{
if (connection != null)
{
@ -100,7 +135,7 @@ protected void OnDataReceived(byte[] data)
else Debug.LogError("Skipped Data message handling because m_Connection is null.");
}
void OnConnected()
static void OnConnected()
{
if (connection != null)
{
@ -110,29 +145,42 @@ void OnConnected()
// the handler may want to send messages to the client
// thus we should set the connected state before calling the handler
connectState = ConnectState.Connected;
NetworkTime.UpdateClient(this);
NetworkTime.UpdateClient();
connection.InvokeHandler(new ConnectMessage());
}
else Debug.LogError("Skipped Connect message handling because m_Connection is null.");
}
public virtual void Disconnect()
public static void Disconnect()
{
connectState = ConnectState.Disconnected;
ClientScene.HandleClientDisconnect(connection);
if (connection != null)
{
connection.Disconnect();
connection.Dispose();
connection = null;
RemoveTransportHandlers();
}
// the client's network is not active anymore.
active = false;
// local or remote connection?
if (isLocalClient)
{
if (isConnected)
{
localClientPacketQueue.Enqueue(MessagePacker.Pack(new DisconnectMessage()));
}
NetworkServer.RemoveLocalConnection();
}
else
{
if (connection != null)
{
connection.Disconnect();
connection.Dispose();
connection = null;
RemoveTransportHandlers();
}
// the client's network is not active anymore.
active = false;
}
}
void RemoveTransportHandlers()
static void RemoveTransportHandlers()
{
// so that we don't register them more than once
Transport.activeTransport.OnClientConnected.RemoveListener(OnConnected);
@ -142,7 +190,7 @@ void RemoveTransportHandlers()
}
[Obsolete("Use SendMessage<T> instead with no message id instead")]
public bool Send(short msgType, MessageBase msg)
public static bool Send(short msgType, MessageBase msg)
{
if (connection != null)
{
@ -157,7 +205,7 @@ public bool Send(short msgType, MessageBase msg)
return false;
}
public bool Send<T>(T message) where T : IMessageBase
public static bool Send<T>(T message) where T : IMessageBase
{
if (connection != null)
{
@ -172,12 +220,25 @@ public bool Send<T>(T message) where T : IMessageBase
return false;
}
internal virtual void Update()
internal static void Update()
{
// only update things while connected
if (active && connectState == ConnectState.Connected)
// local or remote connection?
if (isLocalClient)
{
NetworkTime.UpdateClient(this);
// process internal messages so they are applied at the correct time
while (localClientPacketQueue.Count > 0)
{
byte[] packet = localClientPacketQueue.Dequeue();
OnDataReceived(packet);
}
}
else
{
// only update things while connected
if (active && connectState == ConnectState.Connected)
{
NetworkTime.UpdateClient();
}
}
}
@ -228,12 +289,12 @@ void GenerateError(byte error)
*/
[Obsolete("Use NetworkTime.rtt instead")]
public float GetRTT()
public static float GetRTT()
{
return (float)NetworkTime.rtt;
}
internal void RegisterSystemHandlers(bool localClient)
internal static void RegisterSystemHandlers(bool localClient)
{
// local client / regular client react to some messages differently.
// but we still need to add handlers for all of them to avoid
@ -268,7 +329,7 @@ internal void RegisterSystemHandlers(bool localClient)
}
[Obsolete("Use RegisterHandler<T> instead")]
public void RegisterHandler(int msgType, NetworkMessageDelegate handler)
public static void RegisterHandler(int msgType, NetworkMessageDelegate handler)
{
if (handlers.ContainsKey(msgType))
{
@ -278,12 +339,12 @@ public void RegisterHandler(int msgType, NetworkMessageDelegate handler)
}
[Obsolete("Use RegisterHandler<T> instead")]
public void RegisterHandler(MsgType msgType, NetworkMessageDelegate handler)
public static void RegisterHandler(MsgType msgType, NetworkMessageDelegate handler)
{
RegisterHandler((int)msgType, handler);
}
public void RegisterHandler<T>(Action<NetworkConnection, T> handler) where T : IMessageBase, new()
public static void RegisterHandler<T>(Action<NetworkConnection, T> handler) where T : IMessageBase, new()
{
int msgType = MessagePacker.GetId<T>();
if (handlers.ContainsKey(msgType))
@ -297,33 +358,27 @@ public void RegisterHandler(MsgType msgType, NetworkMessageDelegate handler)
}
[Obsolete("Use UnregisterHandler<T> instead")]
public void UnregisterHandler(int msgType)
public static void UnregisterHandler(int msgType)
{
handlers.Remove(msgType);
}
[Obsolete("Use UnregisterHandler<T> instead")]
public void UnregisterHandler(MsgType msgType)
public static void UnregisterHandler(MsgType msgType)
{
UnregisterHandler((int)msgType);
}
public void UnregisterHandler<T>() where T : IMessageBase
public static void UnregisterHandler<T>() where T : IMessageBase
{
// use int to minimize collisions
int msgType = MessagePacker.GetId<T>();
handlers.Remove(msgType);
}
internal static void UpdateClient()
{
singleton?.Update();
}
public static void Shutdown()
{
if (LogFilter.Debug) Debug.Log("Shutting down client.");
singleton = null;
active = false;
}

View File

@ -61,7 +61,8 @@ public class NetworkManager : MonoBehaviour
public static string networkSceneName = "";
[NonSerialized]
public bool isNetworkActive;
public NetworkClient client;
[Obsolete("Use NetworkClient directly, it will be made static soon. For example, use NetworkClient.Send(message) instead of NetworkManager.client.Send(message)")]
public NetworkClient client => NetworkClient.singleton;
static int s_StartPositionIndex;
public static NetworkManager singleton;
@ -187,7 +188,7 @@ public virtual void LateUpdate()
// -> we don't only call while Client/Server.Connected, because then we would stop if disconnected and the
// NetworkClient wouldn't receive the last Disconnect event, result in all kinds of issues
NetworkServer.Update();
NetworkClient.UpdateClient();
NetworkClient.Update();
UpdateScene();
}
@ -298,13 +299,13 @@ public bool StartServer()
return true;
}
internal void RegisterClientMessages(NetworkClient client)
internal void RegisterClientMessages()
{
client.RegisterHandler<ConnectMessage>(OnClientConnectInternal);
client.RegisterHandler<DisconnectMessage>(OnClientDisconnectInternal);
client.RegisterHandler<NotReadyMessage>(OnClientNotReadyMessageInternal);
client.RegisterHandler<ErrorMessage>(OnClientErrorInternal);
client.RegisterHandler<SceneMessage>(OnClientSceneInternal);
NetworkClient.RegisterHandler<ConnectMessage>(OnClientConnectInternal);
NetworkClient.RegisterHandler<DisconnectMessage>(OnClientDisconnectInternal);
NetworkClient.RegisterHandler<NotReadyMessage>(OnClientNotReadyMessageInternal);
NetworkClient.RegisterHandler<ErrorMessage>(OnClientErrorInternal);
NetworkClient.RegisterHandler<SceneMessage>(OnClientSceneInternal);
if (playerPrefab != null)
{
@ -320,7 +321,7 @@ internal void RegisterClientMessages(NetworkClient client)
}
}
public NetworkClient StartClient()
public void StartClient()
{
InitializeSingleton();
@ -329,43 +330,38 @@ public NetworkClient StartClient()
isNetworkActive = true;
client = new NetworkClient();
RegisterClientMessages(client);
RegisterClientMessages();
if (string.IsNullOrEmpty(networkAddress))
{
Debug.LogError("Must set the Network Address field in the manager");
return null;
return;
}
if (LogFilter.Debug) Debug.Log("NetworkManager StartClient address:" + networkAddress);
client.Connect(networkAddress);
NetworkClient.Connect(networkAddress);
OnStartClient(client);
OnStartClient();
s_Address = networkAddress;
return client;
}
public virtual NetworkClient StartHost()
public virtual void StartHost()
{
OnStartHost();
if (StartServer())
{
NetworkClient localClient = ConnectLocalClient();
OnStartClient(localClient);
return localClient;
ConnectLocalClient();
OnStartClient();
}
return null;
}
NetworkClient ConnectLocalClient()
void ConnectLocalClient()
{
if (LogFilter.Debug) Debug.Log("NetworkManager StartHost");
networkAddress = "localhost";
client = ClientScene.ConnectLocalServer();
RegisterClientMessages(client);
return client;
NetworkServer.ActivateLocalClientScene();
NetworkClient.ConnectLocalServer();
RegisterClientMessages();
}
public void StopHost()
@ -399,13 +395,10 @@ public void StopClient()
if (LogFilter.Debug) Debug.Log("NetworkManager StopClient");
isNetworkActive = false;
if (client != null)
{
// only shutdown this client, not ALL clients.
client.Disconnect();
NetworkClient.Shutdown();
client = null;
}
// shutdown client
NetworkClient.Disconnect();
NetworkClient.Shutdown();
ClientScene.DestroyAllClientObjects();
if (!string.IsNullOrEmpty(offlineScene))
@ -467,11 +460,8 @@ internal void ClientChangeScene(string newSceneName, bool forceReload)
// vis2k: pause message handling while loading scene. otherwise we will process messages and then lose all
// the state as soon as the load is finishing, causing all kinds of bugs because of missing state.
// (client may be null after StopClient etc.)
if (client != null)
{
if (LogFilter.Debug) Debug.Log("ClientChangeScene: pausing handlers while scene is loading to avoid data loss after scene was loaded.");
Transport.activeTransport.enabled = false;
}
if (LogFilter.Debug) Debug.Log("ClientChangeScene: pausing handlers while scene is loading to avoid data loss after scene was loaded.");
Transport.activeTransport.enabled = false;
// Let client prepare for scene change
OnClientChangeScene(newSceneName);
@ -484,22 +474,15 @@ void FinishLoadScene()
{
// NOTE: this cannot use NetworkClient.allClients[0] - that client may be for a completely different purpose.
if (client != null)
{
// process queued messages that we received while loading the scene
if (LogFilter.Debug) Debug.Log("FinishLoadScene: resuming handlers after scene was loading.");
Transport.activeTransport.enabled = true;
// process queued messages that we received while loading the scene
if (LogFilter.Debug) Debug.Log("FinishLoadScene: resuming handlers after scene was loading.");
Transport.activeTransport.enabled = true;
if (s_ClientReadyConnection != null)
{
clientLoadedScene = true;
OnClientConnect(s_ClientReadyConnection);
s_ClientReadyConnection = null;
}
}
else
if (s_ClientReadyConnection != null)
{
if (LogFilter.Debug) Debug.Log("FinishLoadScene client is null");
clientLoadedScene = true;
OnClientConnect(s_ClientReadyConnection);
s_ClientReadyConnection = null;
}
if (NetworkServer.active)
@ -508,10 +491,10 @@ void FinishLoadScene()
OnServerSceneChanged(networkSceneName);
}
if (IsClientConnected() && client != null)
if (IsClientConnected())
{
RegisterClientMessages(client);
OnClientSceneChanged(client.connection);
RegisterClientMessages();
OnClientSceneChanged(NetworkClient.connection);
}
}
@ -546,7 +529,7 @@ public static void UnRegisterStartPosition(Transform start)
public bool IsClientConnected()
{
return client != null && client.isConnected;
return NetworkClient.isConnected;
}
// this is the only way to clear the singleton, so another instance can be created.
@ -810,7 +793,9 @@ public virtual void OnClientSceneChanged(NetworkConnection conn)
public virtual void OnStartHost() {}
public virtual void OnStartServer() {}
[Obsolete("Use OnStartClient() instead of OnStartClient(NetworkClient client). All NetworkClient functions are static now, so you can use NetworkClient.Send(message) instead of client.Send(message) directly now.")]
public virtual void OnStartClient(NetworkClient client) {}
public virtual void OnStartClient() {}
public virtual void OnStopServer() {}
public virtual void OnStopClient() {}
public virtual void OnStopHost() {}

View File

@ -26,13 +26,10 @@ void OnGUI()
if (!showGUI)
return;
bool noConnection = (manager.client == null || manager.client.connection == null ||
manager.client.connection.connectionId == -1);
GUILayout.BeginArea(new Rect(10 + offsetX, 40 + offsetY, 215, 9999));
if (!manager.IsClientConnected() && !NetworkServer.active)
{
if (noConnection)
if (!NetworkClient.active)
{
// LAN Host
if (Application.platform != RuntimePlatform.WebGLPlayer)
@ -91,7 +88,7 @@ void OnGUI()
{
if (GUILayout.Button("Client Ready"))
{
ClientScene.Ready(manager.client.connection);
ClientScene.Ready(NetworkClient.connection);
if (ClientScene.localPlayer == null)
{

View File

@ -721,7 +721,7 @@ static bool SetupLocalPlayerForConnection(NetworkConnection conn, NetworkIdentit
SendSpawnMessage(identity, null);
// Set up local player instance on the client instance and update local object map
localConnection.localClient.AddLocalPlayer(identity);
NetworkClient.AddLocalPlayer(identity);
identity.SetClientOwner(conn);
// Trigger OnAuthority

View File

@ -50,12 +50,12 @@ internal static NetworkPingMessage GetPing()
return new NetworkPingMessage(LocalTime());
}
internal static void UpdateClient(NetworkClient networkClient)
internal static void UpdateClient()
{
if (Time.time - lastPingTime >= PingFrequency)
{
NetworkPingMessage pingMessage = GetPing();
networkClient.Send(pingMessage);
NetworkClient.Send(pingMessage);
lastPingTime = Time.time;
}
}