615 lines
15 KiB
C#
Raw Normal View History

2020-12-23 13:48:31 +00:00
using QuantumUNET.Logging;
2020-12-07 21:19:16 +00:00
using QuantumUNET.Messages;
using QuantumUNET.Transport;
2020-12-02 18:40:38 +00:00
using System;
2020-12-02 12:42:26 +00:00
using System.Collections.Generic;
using System.Linq;
2020-12-02 12:42:26 +00:00
using System.Net;
using System.Net.Sockets;
using UnityEngine;
using UnityEngine.Networking;
2020-12-04 22:14:53 +00:00
namespace QuantumUNET
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
public class QNetworkClient
2020-12-02 12:42:26 +00:00
{
2021-04-27 20:56:04 +01:00
public QNetworkClient()
{
m_MsgBuffer = new byte[65535];
m_MsgReader = new NetworkReader(m_MsgBuffer);
AddClient(this);
}
public QNetworkClient(QNetworkConnection conn)
{
m_MsgBuffer = new byte[65535];
m_MsgReader = new NetworkReader(m_MsgBuffer);
AddClient(this);
SetActive(true);
m_Connection = conn;
m_AsyncConnect = ConnectState.Connected;
conn.SetHandlers(m_MessageHandlers);
RegisterSystemHandlers(false);
}
public static List<QNetworkClient> allClients { get; private set; } = new List<QNetworkClient>();
2021-04-27 20:57:55 +01:00
2021-04-27 20:56:04 +01:00
public static bool active { get; private set; }
2021-04-27 20:57:55 +01:00
internal void SetHandlers(QNetworkConnection conn) => conn.SetHandlers(m_MessageHandlers);
2021-04-27 20:56:04 +01:00
public string serverIp { get; private set; } = "";
2021-04-27 20:57:55 +01:00
2021-04-27 20:56:04 +01:00
public int serverPort { get; private set; }
2021-04-27 20:57:55 +01:00
2021-04-27 20:56:04 +01:00
public QNetworkConnection connection => m_Connection;
2021-04-27 20:57:55 +01:00
internal int hostId { get; private set; } = -1;
2021-04-27 20:57:55 +01:00
public Dictionary<short, QNetworkMessageDelegate> handlers => m_MessageHandlers.GetHandlers();
2021-04-27 20:57:55 +01:00
public int numChannels => hostTopology.DefaultConfig.ChannelCount;
2021-04-27 20:57:55 +01:00
public HostTopology hostTopology { get; private set; }
private const int k_MaxEventsPerFrame = 500;
private int m_HostPort;
private int m_ClientConnectionId = -1;
private int m_StatResetTime;
private static readonly QCRCMessage s_CRCMessage = new QCRCMessage();
private readonly QNetworkMessageHandlers m_MessageHandlers = new QNetworkMessageHandlers();
protected QNetworkConnection m_Connection;
private readonly byte[] m_MsgBuffer;
private readonly NetworkReader m_MsgReader;
protected ConnectState m_AsyncConnect = ConnectState.None;
private string m_RequestedServerHost = "";
public int hostPort
2020-12-02 12:42:26 +00:00
{
get => m_HostPort;
2020-12-02 12:42:26 +00:00
set
{
if (value < 0)
{
throw new ArgumentException("Port must not be a negative number.");
}
if (value > 65535)
{
throw new ArgumentException("Port must not be greater than 65535.");
}
m_HostPort = value;
2020-12-02 12:42:26 +00:00
}
}
public bool isConnected => m_AsyncConnect == ConnectState.Connected;
2021-04-27 20:57:55 +01:00
public Type networkConnectionClass { get; private set; } = typeof(QNetworkConnection);
2020-12-02 12:42:26 +00:00
2021-04-27 20:57:55 +01:00
public void SetNetworkConnectionClass<T>() where T : QNetworkConnection => networkConnectionClass = typeof(T);
2020-12-02 12:42:26 +00:00
2021-04-26 14:30:21 +01:00
public bool Configure(ConnectionConfig config, int maxConnections)
2020-12-02 12:42:26 +00:00
{
2021-04-26 14:30:21 +01:00
var topology = new HostTopology(config, maxConnections);
return Configure(topology);
2020-12-02 12:42:26 +00:00
}
2021-04-26 14:30:21 +01:00
public bool Configure(HostTopology topology)
2020-12-02 12:42:26 +00:00
{
hostTopology = topology;
2021-04-26 14:30:21 +01:00
return true;
2020-12-02 12:42:26 +00:00
}
private static bool IsValidIpV6(string address) =>
address.All(c => c == ':' || (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
2020-12-02 12:42:26 +00:00
public void Connect(string serverIp, int serverPort)
{
PrepareForConnect();
2021-04-27 20:56:04 +01:00
this.serverPort = serverPort;
2020-12-02 12:42:26 +00:00
if (Application.platform == RuntimePlatform.WebGLPlayer)
{
2021-04-27 20:56:04 +01:00
this.serverIp = serverIp;
m_AsyncConnect = ConnectState.Resolved;
2020-12-02 12:42:26 +00:00
}
else if (serverIp.Equals("127.0.0.1") || serverIp.Equals("localhost"))
{
2021-04-27 20:56:04 +01:00
this.serverIp = "127.0.0.1";
m_AsyncConnect = ConnectState.Resolved;
2020-12-02 12:42:26 +00:00
}
else if (serverIp.IndexOf(":") != -1 && IsValidIpV6(serverIp))
{
2021-04-27 20:56:04 +01:00
this.serverIp = serverIp;
m_AsyncConnect = ConnectState.Resolved;
2020-12-02 12:42:26 +00:00
}
else
{
2020-12-23 13:48:31 +00:00
QLog.Log($"Async DNS START:{serverIp}");
m_RequestedServerHost = serverIp;
m_AsyncConnect = ConnectState.Resolving;
Dns.BeginGetHostAddresses(serverIp, GetHostAddressesCallback, this);
2020-12-02 12:42:26 +00:00
}
}
2020-12-23 13:48:31 +00:00
private void PrepareForConnect()
2020-12-02 12:42:26 +00:00
{
SetActive(true);
RegisterSystemHandlers(false);
if (hostTopology == null)
2020-12-02 12:42:26 +00:00
{
var connectionConfig = new ConnectionConfig();
2020-12-02 12:42:26 +00:00
connectionConfig.AddChannel(QosType.ReliableSequenced);
connectionConfig.AddChannel(QosType.Unreliable);
2020-12-23 13:48:31 +00:00
connectionConfig.UsePlatformSpecificProtocols = false;
hostTopology = new HostTopology(connectionConfig, 8);
2020-12-02 12:42:26 +00:00
}
hostId = NetworkTransport.AddHost(hostTopology, m_HostPort);
2020-12-02 12:42:26 +00:00
}
internal static void GetHostAddressesCallback(IAsyncResult ar)
{
try
{
var array = Dns.EndGetHostAddresses(ar);
2020-12-23 12:58:45 +00:00
var networkClient = (QNetworkClient)ar.AsyncState;
2020-12-02 12:42:26 +00:00
if (array.Length == 0)
{
QLog.Error($"DNS lookup failed for:{networkClient.m_RequestedServerHost}");
networkClient.m_AsyncConnect = ConnectState.Failed;
2020-12-02 12:42:26 +00:00
}
else
{
2021-04-27 20:56:04 +01:00
networkClient.serverIp = array[0].ToString();
networkClient.m_AsyncConnect = ConnectState.Resolved;
2020-12-23 13:48:31 +00:00
QLog.Log(
2021-04-27 20:56:04 +01:00
$"Async DNS Result:{networkClient.serverIp} for {networkClient.m_RequestedServerHost}: {networkClient.serverIp}");
2020-12-02 12:42:26 +00:00
}
}
catch (SocketException ex)
{
2020-12-23 12:58:45 +00:00
var networkClient2 = (QNetworkClient)ar.AsyncState;
QLog.Error($"DNS resolution failed: {ex.GetErrorCode()}");
QLog.Error($"Exception:{ex}");
networkClient2.m_AsyncConnect = ConnectState.Failed;
2020-12-02 12:42:26 +00:00
}
}
internal void ContinueConnect()
{
2021-04-27 20:56:04 +01:00
m_ClientConnectionId = NetworkTransport.Connect(hostId, serverIp, serverPort, 0, out var b);
m_Connection = (QNetworkConnection)Activator.CreateInstance(networkConnectionClass);
m_Connection.SetHandlers(m_MessageHandlers);
2021-04-27 20:56:04 +01:00
m_Connection.Initialize(serverIp, hostId, m_ClientConnectionId, hostTopology);
2020-12-02 12:42:26 +00:00
}
public virtual void Disconnect()
{
m_AsyncConnect = ConnectState.Disconnected;
QClientScene.HandleClientDisconnect(m_Connection);
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
m_Connection.Disconnect();
m_Connection.Dispose();
m_Connection = null;
if (hostId != -1)
2020-12-02 12:42:26 +00:00
{
NetworkTransport.RemoveHost(hostId);
hostId = -1;
2020-12-02 12:42:26 +00:00
}
}
}
2020-12-23 12:58:45 +00:00
public bool Send(short msgType, QMessageBase msg)
2020-12-02 12:42:26 +00:00
{
bool result;
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
if (m_AsyncConnect != ConnectState.Connected)
2020-12-02 12:42:26 +00:00
{
QLog.Error("NetworkClient Send when not connected to a server");
2020-12-02 12:42:26 +00:00
result = false;
}
else
{
result = m_Connection.Send(msgType, msg);
2020-12-02 12:42:26 +00:00
}
}
else
{
QLog.Error("NetworkClient Send with no connection");
2020-12-02 12:42:26 +00:00
result = false;
}
return result;
}
2020-12-23 12:58:45 +00:00
public bool SendWriter(QNetworkWriter writer, int channelId)
2020-12-02 12:42:26 +00:00
{
bool result;
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
if (m_AsyncConnect != ConnectState.Connected)
2020-12-02 12:42:26 +00:00
{
QLog.Error("NetworkClient SendWriter when not connected to a server");
2020-12-02 12:42:26 +00:00
result = false;
}
else
{
result = m_Connection.SendWriter(writer, channelId);
2020-12-02 12:42:26 +00:00
}
}
else
{
QLog.Error("NetworkClient SendWriter with no connection");
2020-12-02 12:42:26 +00:00
result = false;
}
return result;
}
public bool SendBytes(byte[] data, int numBytes, int channelId)
{
bool result;
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
if (m_AsyncConnect != ConnectState.Connected)
2020-12-02 12:42:26 +00:00
{
QLog.Error("NetworkClient SendBytes when not connected to a server");
2020-12-02 12:42:26 +00:00
result = false;
}
else
{
result = m_Connection.SendBytes(data, numBytes, channelId);
2020-12-02 12:42:26 +00:00
}
}
else
{
QLog.Error("NetworkClient SendBytes with no connection");
2020-12-02 12:42:26 +00:00
result = false;
}
return result;
}
2020-12-23 12:58:45 +00:00
public bool SendUnreliable(short msgType, QMessageBase msg)
2020-12-02 12:42:26 +00:00
{
bool result;
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
if (m_AsyncConnect != ConnectState.Connected)
2020-12-02 12:42:26 +00:00
{
QLog.Error("NetworkClient SendUnreliable when not connected to a server");
2020-12-02 12:42:26 +00:00
result = false;
}
else
{
result = m_Connection.SendUnreliable(msgType, msg);
2020-12-02 12:42:26 +00:00
}
}
else
{
QLog.Error("NetworkClient SendUnreliable with no connection");
2020-12-02 12:42:26 +00:00
result = false;
}
return result;
}
2020-12-23 12:58:45 +00:00
public bool SendByChannel(short msgType, QMessageBase msg, int channelId)
2020-12-02 12:42:26 +00:00
{
bool result;
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
if (m_AsyncConnect != ConnectState.Connected)
2020-12-02 12:42:26 +00:00
{
QLog.Error("NetworkClient SendByChannel when not connected to a server");
2020-12-02 12:42:26 +00:00
result = false;
}
else
{
result = m_Connection.SendByChannel(msgType, msg, channelId);
2020-12-02 12:42:26 +00:00
}
}
else
{
QLog.Error("NetworkClient SendByChannel with no connection");
2020-12-02 12:42:26 +00:00
result = false;
}
return result;
}
public void SetMaxDelay(float seconds)
{
if (m_Connection == null)
2020-12-02 12:42:26 +00:00
{
QLog.Warning("SetMaxDelay failed, not connected.");
2020-12-02 12:42:26 +00:00
}
else
{
m_Connection.SetMaxDelay(seconds);
2020-12-02 12:42:26 +00:00
}
}
public void Shutdown()
{
QLog.Log($"Shutting down client {hostId}");
if (hostId != -1)
2020-12-02 12:42:26 +00:00
{
NetworkTransport.RemoveHost(hostId);
hostId = -1;
2020-12-02 12:42:26 +00:00
}
RemoveClient(this);
2021-04-27 20:56:04 +01:00
if (allClients.Count == 0)
2020-12-02 12:42:26 +00:00
{
SetActive(false);
}
}
internal virtual void Update()
{
if (hostId != -1)
2020-12-02 12:42:26 +00:00
{
switch (m_AsyncConnect)
2020-12-02 12:42:26 +00:00
{
case ConnectState.None:
case ConnectState.Resolving:
case ConnectState.Disconnected:
return;
case ConnectState.Resolved:
m_AsyncConnect = ConnectState.Connecting;
2020-12-02 12:42:26 +00:00
ContinueConnect();
return;
case ConnectState.Failed:
GenerateConnectError(11);
m_AsyncConnect = ConnectState.Disconnected;
2020-12-02 12:42:26 +00:00
return;
}
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
if ((int)Time.time != m_StatResetTime)
2020-12-02 12:42:26 +00:00
{
m_Connection.ResetStats();
m_StatResetTime = (int)Time.time;
2020-12-02 12:42:26 +00:00
}
}
var num = 0;
2020-12-02 12:42:26 +00:00
byte b;
for (; ; )
{
var networkEventType = NetworkTransport.ReceiveFromHost(hostId, out var num2, out var channelId, m_MsgBuffer, (ushort)m_MsgBuffer.Length, out var numBytes, out b);
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
m_Connection.LastError = (NetworkError)b;
2020-12-02 12:42:26 +00:00
}
switch (networkEventType)
{
case NetworkEventType.DataEvent:
if (b != 0)
{
goto Block_11;
}
m_MsgReader.SeekZero();
m_Connection.TransportReceive(m_MsgBuffer, numBytes, channelId);
2020-12-02 12:42:26 +00:00
break;
2020-12-03 08:28:05 +00:00
2020-12-02 12:42:26 +00:00
case NetworkEventType.ConnectEvent:
2020-12-23 13:48:31 +00:00
QLog.Log("Client connected");
2020-12-02 12:42:26 +00:00
if (b != 0)
{
goto Block_10;
}
m_AsyncConnect = ConnectState.Connected;
m_Connection.InvokeHandlerNoData(32);
2020-12-02 12:42:26 +00:00
break;
2020-12-03 08:28:05 +00:00
2020-12-02 12:42:26 +00:00
case NetworkEventType.DisconnectEvent:
2020-12-23 13:48:31 +00:00
QLog.Log("Client disconnected");
m_AsyncConnect = ConnectState.Disconnected;
2020-12-02 12:42:26 +00:00
if (b != 0)
{
if (b != 6)
{
GenerateDisconnectError(b);
2020-12-02 12:42:26 +00:00
}
}
QClientScene.HandleClientDisconnect(m_Connection);
m_Connection?.InvokeHandlerNoData(33);
2020-12-02 12:42:26 +00:00
break;
2020-12-03 08:28:05 +00:00
2020-12-02 12:42:26 +00:00
case NetworkEventType.Nothing:
break;
2020-12-03 08:28:05 +00:00
2020-12-02 12:42:26 +00:00
default:
QLog.Error($"Unknown network message type received: {networkEventType}");
2020-12-02 12:42:26 +00:00
break;
}
if (++num >= 500)
2020-12-02 12:42:26 +00:00
{
goto Block_17;
}
if (hostId == -1)
2020-12-02 12:42:26 +00:00
{
goto Block_19;
}
if (networkEventType == NetworkEventType.Nothing)
{
goto IL_2C6;
}
}
2021-01-05 16:04:20 +00:00
Block_10:
GenerateConnectError(b);
2020-12-02 12:42:26 +00:00
return;
2021-01-05 16:04:20 +00:00
Block_11:
GenerateDataError(b);
2020-12-02 12:42:26 +00:00
return;
2021-01-05 16:04:20 +00:00
Block_17:
QLog.Log($"MaxEventsPerFrame hit ({500})");
2021-01-05 16:04:20 +00:00
Block_19:
IL_2C6:
if (m_Connection != null && m_AsyncConnect == ConnectState.Connected)
2020-12-02 12:42:26 +00:00
{
m_Connection.FlushChannels();
2020-12-02 12:42:26 +00:00
}
}
}
private void GenerateConnectError(int error)
{
QLog.Error($"UNet Client Error Connect Error: {error}");
2020-12-02 12:42:26 +00:00
GenerateError(error);
}
private void GenerateDataError(int error)
{
QLog.Error($"UNet Client Data Error: {(NetworkError)error}");
2020-12-02 12:42:26 +00:00
GenerateError(error);
}
private void GenerateDisconnectError(int error)
{
QLog.Error($"UNet Client Disconnect Error: {(NetworkError)error}");
2020-12-02 12:42:26 +00:00
GenerateError(error);
}
private void GenerateError(int error)
{
var handler = m_MessageHandlers.GetHandler(34)
?? m_MessageHandlers.GetHandler(34);
2020-12-02 12:42:26 +00:00
if (handler != null)
{
2020-12-23 12:58:45 +00:00
var errorMessage = new QErrorMessage
{
errorCode = error
};
var buffer = new byte[200];
2020-12-23 12:58:45 +00:00
var writer = new QNetworkWriter(buffer);
2020-12-02 12:42:26 +00:00
errorMessage.Serialize(writer);
2020-12-23 12:58:45 +00:00
var reader = new QNetworkReader(buffer);
handler(new QNetworkMessage
2020-12-02 12:42:26 +00:00
{
2020-12-03 11:56:32 +00:00
MsgType = 34,
Reader = reader,
Connection = m_Connection,
2020-12-03 11:56:32 +00:00
ChannelId = 0
2020-12-02 12:42:26 +00:00
});
}
}
public void GetStatsOut(out int numMsgs, out int numBufferedMsgs, out int numBytes, out int lastBufferedPerSecond)
{
numMsgs = 0;
numBufferedMsgs = 0;
numBytes = 0;
lastBufferedPerSecond = 0;
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
m_Connection.GetStatsOut(out numMsgs, out numBufferedMsgs, out numBytes, out lastBufferedPerSecond);
2020-12-02 12:42:26 +00:00
}
}
public void GetStatsIn(out int numMsgs, out int numBytes)
{
numMsgs = 0;
numBytes = 0;
if (m_Connection != null)
2020-12-02 12:42:26 +00:00
{
m_Connection.GetStatsIn(out numMsgs, out numBytes);
2020-12-02 12:42:26 +00:00
}
}
2020-12-23 12:58:45 +00:00
public Dictionary<short, QNetworkConnection.PacketStat> GetConnectionStats() =>
m_Connection?.PacketStats;
2020-12-02 12:42:26 +00:00
public void ResetConnectionStats() => m_Connection?.ResetStats();
2020-12-02 12:42:26 +00:00
public int GetRTT() =>
hostId == -1 ? 0 : NetworkTransport.GetCurrentRTT(hostId, m_ClientConnectionId, out var b);
2020-12-02 12:42:26 +00:00
internal void RegisterSystemHandlers(bool localClient)
{
2020-12-23 12:58:45 +00:00
QClientScene.RegisterSystemHandlers(this, localClient);
RegisterHandlerSafe(14, OnCRC);
2020-12-02 12:42:26 +00:00
}
2020-12-23 12:58:45 +00:00
private void OnCRC(QNetworkMessage netMsg)
2020-12-02 12:42:26 +00:00
{
netMsg.ReadMessage(s_CRCMessage);
QNetworkCRC.Validate(s_CRCMessage.scripts, numChannels);
2020-12-02 12:42:26 +00:00
}
public void RegisterHandler(short msgType, QNetworkMessageDelegate handler) => m_MessageHandlers.RegisterHandler(msgType, handler);
2020-12-02 12:42:26 +00:00
public void RegisterHandlerSafe(short msgType, QNetworkMessageDelegate handler) => m_MessageHandlers.RegisterHandlerSafe(msgType, handler);
2020-12-02 12:42:26 +00:00
public void UnregisterHandler(short msgType) => m_MessageHandlers.UnregisterHandler(msgType);
2020-12-02 12:42:26 +00:00
2020-12-23 12:58:45 +00:00
public static Dictionary<short, QNetworkConnection.PacketStat> GetTotalConnectionStats()
2020-12-02 12:42:26 +00:00
{
2020-12-23 12:58:45 +00:00
var dictionary = new Dictionary<short, QNetworkConnection.PacketStat>();
2021-04-27 20:56:04 +01:00
foreach (var networkClient in allClients)
2020-12-02 12:42:26 +00:00
{
var connectionStats = networkClient.GetConnectionStats();
foreach (var key in connectionStats.Keys)
2020-12-02 12:42:26 +00:00
{
if (dictionary.ContainsKey(key))
{
var packetStat = dictionary[key];
2020-12-02 12:42:26 +00:00
packetStat.count += connectionStats[key].count;
packetStat.bytes += connectionStats[key].bytes;
dictionary[key] = packetStat;
}
else
{
2020-12-23 12:58:45 +00:00
dictionary[key] = new QNetworkConnection.PacketStat(connectionStats[key]);
2020-12-02 12:42:26 +00:00
}
}
}
return dictionary;
}
2021-04-27 20:56:04 +01:00
internal static void AddClient(QNetworkClient client) => allClients.Add(client);
2020-12-02 12:42:26 +00:00
2021-04-27 20:56:04 +01:00
internal static bool RemoveClient(QNetworkClient client) => allClients.Remove(client);
2020-12-02 12:42:26 +00:00
internal static void UpdateClients()
{
2021-04-27 20:56:04 +01:00
for (var i = 0; i < allClients.Count; i++)
2020-12-02 12:42:26 +00:00
{
2021-04-27 20:56:04 +01:00
if (allClients[i] != null)
2020-12-02 12:42:26 +00:00
{
2021-04-27 20:56:04 +01:00
allClients[i].Update();
2020-12-02 12:42:26 +00:00
}
else
{
2021-04-27 20:56:04 +01:00
allClients.RemoveAt(i);
2020-12-02 12:42:26 +00:00
}
}
}
public static void ShutdownAll()
{
2021-04-27 20:56:04 +01:00
while (allClients.Count != 0)
2020-12-02 12:42:26 +00:00
{
2021-04-27 20:56:04 +01:00
allClients[0].Shutdown();
2020-12-02 12:42:26 +00:00
}
2021-04-27 20:56:04 +01:00
allClients = new List<QNetworkClient>();
active = false;
2020-12-23 12:58:45 +00:00
QClientScene.Shutdown();
2020-12-02 12:42:26 +00:00
}
internal static void SetActive(bool state)
{
2021-04-27 20:56:04 +01:00
if (!active && state)
2020-12-02 12:42:26 +00:00
{
NetworkTransport.Init();
}
2021-04-27 20:56:04 +01:00
active = state;
2020-12-02 12:42:26 +00:00
}
protected enum ConnectState
{
None,
Resolving,
Resolved,
Connecting,
Connected,
Disconnected,
Failed
}
}
2020-12-03 08:28:05 +00:00
}