您最多选择25个主题
主题必须以中文或者字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
831 行
31 KiB
831 行
31 KiB
using System;
|
|
using System.Net.Sockets;
|
|
using System.Reflection;
|
|
using System.Runtime.InteropServices;
|
|
using Unity.Networking.Transport.LowLevel.Unsafe;
|
|
using Unity.Collections;
|
|
using Unity.Collections.LowLevel.Unsafe;
|
|
using Unity.Networking.Transport.Protocols;
|
|
using Unity.Networking.Transport.Utilities;
|
|
using Unity.Jobs;
|
|
using UnityEngine.Assertions;
|
|
using Random = System.Random;
|
|
|
|
namespace Unity.Networking.Transport
|
|
{
|
|
using UdpNetworkDriver = BasicNetworkDriver<IPv4UDPSocket>;
|
|
using LocalNetworkDriver = BasicNetworkDriver<IPCSocket>;
|
|
|
|
|
|
public struct BasicNetworkDriver<T> : INetworkDriver, INetworkPacketReceiver where T : struct, INetworkInterface
|
|
{
|
|
public Concurrent ToConcurrent()
|
|
{
|
|
Concurrent concurrent;
|
|
concurrent.m_EventQueue = m_EventQueue.ToConcurrent();
|
|
concurrent.m_ConnectionList = m_ConnectionList;
|
|
concurrent.m_DataStream = m_DataStream;
|
|
concurrent.m_NetworkInterface = m_NetworkInterface;
|
|
return concurrent;
|
|
}
|
|
public struct Concurrent
|
|
{
|
|
public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader slice)
|
|
{
|
|
int offset, size;
|
|
slice = default(DataStreamReader);
|
|
if (connectionId.m_NetworkId < 0 || connectionId.m_NetworkId >= m_ConnectionList.Length ||
|
|
m_ConnectionList[connectionId.m_NetworkId].Version != connectionId.m_NetworkVersion)
|
|
return (int) NetworkEvent.Type.Empty;
|
|
var type = m_EventQueue.PopEventForConnection(connectionId.m_NetworkId, out offset, out size);
|
|
if (size > 0)
|
|
slice = new DataStreamReader(m_DataStream, offset, size);
|
|
return type;
|
|
}
|
|
|
|
public unsafe int Send(NetworkConnection id, DataStreamWriter strm)
|
|
{
|
|
if (strm.IsCreated && strm.Length > 0)
|
|
{
|
|
return Send(id, (IntPtr) strm.GetUnsafeReadOnlyPtr(), strm.Length);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
public int Send(NetworkConnection id, IntPtr data, int len)
|
|
{
|
|
if (id.m_NetworkId < 0 || id.m_NetworkId >= m_ConnectionList.Length)
|
|
return 0;
|
|
var connection = m_ConnectionList[id.m_NetworkId];
|
|
if (connection.Version != id.m_NetworkVersion)
|
|
return 0;
|
|
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
if (connection.State == NetworkConnection.State.Connecting)
|
|
throw new InvalidOperationException("Cannot send data while connecting");
|
|
#endif
|
|
|
|
// update last attempt;
|
|
var header = new UdpCHeader
|
|
{
|
|
Type = (int) UdpCProtocol.Data,
|
|
SessionToken = connection.SendToken
|
|
};
|
|
|
|
unsafe
|
|
{
|
|
if (connection.DidReceiveData == 0)
|
|
{
|
|
header.Flags = 1;
|
|
return NativeSend(m_NetworkInterface, ref header, (void*) data, len, &connection.ReceiveToken, 2, connection.Address);
|
|
}
|
|
return NativeSend(m_NetworkInterface, ref header, (void*) data, len, null, 0, connection.Address);
|
|
}
|
|
}
|
|
|
|
internal NetworkEventQueue.Concurrent m_EventQueue;
|
|
[ReadOnly] internal NativeList<Connection> m_ConnectionList;
|
|
[ReadOnly] internal DataStreamWriter m_DataStream;
|
|
internal T m_NetworkInterface;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Connection is the internal representation of a connection.
|
|
/// </summary>
|
|
internal struct Connection
|
|
{
|
|
public NetworkEndPoint Address;
|
|
public long LastAttempt;
|
|
public int Id;
|
|
public int Version;
|
|
public int Attempts;
|
|
public NetworkConnection.State State;
|
|
public ushort ReceiveToken;
|
|
public ushort SendToken;
|
|
public byte DidReceiveData;
|
|
|
|
public static bool operator ==(Connection lhs, Connection rhs)
|
|
{
|
|
return lhs.Id == rhs.Id && lhs.Version == rhs.Version && lhs.Address == rhs.Address;
|
|
}
|
|
|
|
public static bool operator !=(Connection lhs, Connection rhs)
|
|
{
|
|
return lhs.Id != rhs.Id || lhs.Version != rhs.Version || lhs.Address != rhs.Address;
|
|
}
|
|
|
|
public override bool Equals(object compare)
|
|
{
|
|
return this == (Connection) compare;
|
|
}
|
|
|
|
public static Connection Null => new Connection() {Id = 0, Version = 0};
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
return Id;
|
|
}
|
|
|
|
public bool Equals(Connection connection)
|
|
{
|
|
return connection.Id == Id && connection.Version == Version && connection.Address == Address;
|
|
}
|
|
}
|
|
|
|
/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
/// internal variables :::::::::::::::::::::::::::::::::::::::::::::::::
|
|
T m_NetworkInterface;
|
|
|
|
NetworkEventQueue m_EventQueue;
|
|
|
|
NativeQueue<int> m_FreeList;
|
|
NativeQueue<int> m_NetworkAcceptQueue;
|
|
NativeList<Connection> m_ConnectionList;
|
|
NativeArray<int> m_InternalState;
|
|
NativeQueue<int> m_PendingFree;
|
|
NativeArray<ushort> m_SessionIdCounter;
|
|
|
|
#pragma warning disable 649
|
|
struct Parameters
|
|
{
|
|
public NetworkDataStreamParameter dataStream;
|
|
public NetworkConfigParameter config;
|
|
}
|
|
#pragma warning restore 649
|
|
|
|
private Parameters m_NetworkParams;
|
|
private DataStreamWriter m_DataStream;
|
|
|
|
[NativeSetClassTypeToNullOnSchedule]
|
|
private Timer m_timer;
|
|
private long m_updateTime;
|
|
|
|
/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
/// properties :::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
private const int InternalStateListening = 0;
|
|
private const int InternalStateBound = 1;
|
|
public bool Listening
|
|
{
|
|
get { return (m_InternalState[InternalStateListening] != 0); }
|
|
internal set { m_InternalState[InternalStateListening] = value ? 1 : 0; }
|
|
}
|
|
|
|
/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
public BasicNetworkDriver(params INetworkParameter[] param)
|
|
{
|
|
m_timer = new Timer();
|
|
m_updateTime = m_timer.ElapsedMilliseconds;
|
|
m_NetworkParams = default(Parameters);
|
|
object boxedParams = m_NetworkParams;
|
|
foreach (var field in typeof(Parameters).GetFields(
|
|
BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public))
|
|
{
|
|
for (int i = 0; i < param.Length; ++i)
|
|
{
|
|
if (field.FieldType.IsAssignableFrom(param[i].GetType()))
|
|
field.SetValue(boxedParams, param[i]);
|
|
}
|
|
}
|
|
|
|
m_NetworkParams = (Parameters) boxedParams;
|
|
|
|
if (m_NetworkParams.config.maxConnectAttempts == 0)
|
|
m_NetworkParams.config.maxConnectAttempts = NetworkParameterConstants.MaxConnectAttempts;
|
|
|
|
if (m_NetworkParams.config.connectTimeout == 0)
|
|
m_NetworkParams.config.connectTimeout = NetworkParameterConstants.ConnectTimeout;
|
|
|
|
if (m_NetworkParams.config.disconnectTimeout == 0)
|
|
m_NetworkParams.config.disconnectTimeout = NetworkParameterConstants.DisconnectTimeout;
|
|
|
|
int initialStreamSize = m_NetworkParams.dataStream.size;
|
|
if (initialStreamSize == 0)
|
|
initialStreamSize = NetworkParameterConstants.DriverDataStreamSize;
|
|
m_DataStream = new DataStreamWriter(initialStreamSize, Allocator.Persistent);
|
|
|
|
m_NetworkInterface = new T();
|
|
m_NetworkInterface.Initialize();
|
|
|
|
m_NetworkAcceptQueue = new NativeQueue<int>(Allocator.Persistent);
|
|
|
|
m_ConnectionList = new NativeList<Connection>(1, Allocator.Persistent);
|
|
|
|
m_FreeList = new NativeQueue<int>(Allocator.Persistent);
|
|
m_EventQueue = new NetworkEventQueue(NetworkParameterConstants.InitialEventQueueSize);
|
|
|
|
m_InternalState = new NativeArray<int>(2, Allocator.Persistent);
|
|
m_PendingFree = new NativeQueue<int>(Allocator.Persistent);
|
|
|
|
m_ReceiveCount = new NativeArray<int>(1, Allocator.Persistent);
|
|
m_SessionIdCounter = new NativeArray<ushort>(1, Allocator.Persistent);
|
|
m_SessionIdCounter[0] = (ushort)(new Random().Next() & 0xFFFF);
|
|
ReceiveCount = 0;
|
|
Listening = false;
|
|
}
|
|
|
|
/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
/// interface implementation :::::::::::::::::::::::::::::::::::::::::::
|
|
public void Dispose()
|
|
{
|
|
m_NetworkInterface.Dispose();
|
|
m_DataStream.Dispose();
|
|
|
|
m_EventQueue.Dispose();
|
|
|
|
m_NetworkAcceptQueue.Dispose();
|
|
m_ConnectionList.Dispose();
|
|
m_FreeList.Dispose();
|
|
m_InternalState.Dispose();
|
|
m_PendingFree.Dispose();
|
|
m_ReceiveCount.Dispose();
|
|
m_SessionIdCounter.Dispose();
|
|
}
|
|
|
|
struct UpdateJob : IJob
|
|
{
|
|
public BasicNetworkDriver<T> driver;
|
|
|
|
public void Execute()
|
|
{
|
|
driver.InternalUpdate();
|
|
}
|
|
}
|
|
|
|
public JobHandle ScheduleUpdate(JobHandle dep = default(JobHandle))
|
|
{
|
|
m_updateTime = m_timer.ElapsedMilliseconds;
|
|
var job = new UpdateJob {driver = this};
|
|
var handle = job.Schedule(dep);
|
|
return m_NetworkInterface.ScheduleReceive(this, handle);
|
|
}
|
|
|
|
void InternalUpdate()
|
|
{
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
for (int i = 0; i < m_ConnectionList.Length; ++i)
|
|
{
|
|
int conCount = m_EventQueue.GetCountForConnection(i);
|
|
if (conCount != 0 && m_ConnectionList[i].State != NetworkConnection.State.Disconnected)
|
|
{
|
|
UnityEngine.Debug.LogError("Resetting event queue with pending events (Count=" +
|
|
conCount +
|
|
", ConnectionID="+i+") Listening: " + Listening);
|
|
}
|
|
}
|
|
#endif
|
|
int free;
|
|
while (m_PendingFree.TryDequeue(out free))
|
|
{
|
|
int ver = m_ConnectionList[free].Version + 1;
|
|
if (ver == 0)
|
|
ver = 1;
|
|
m_ConnectionList[free] = new Connection {Id = free, Version = ver};
|
|
m_FreeList.Enqueue(free);
|
|
}
|
|
|
|
m_EventQueue.Clear();
|
|
m_DataStream.Clear();
|
|
CheckTimeouts();
|
|
}
|
|
|
|
public int Bind(NetworkEndPoint endpoint)
|
|
{
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
if (!m_InternalState.IsCreated)
|
|
throw new InvalidOperationException(
|
|
"Driver must be constructed with a populated or empty INetworkParameter params list");
|
|
if (m_InternalState[InternalStateBound] != 0)
|
|
throw new InvalidOperationException(
|
|
"Bind can only be called once per NetworkDriver");
|
|
if (m_ConnectionList.Length > 0)
|
|
throw new InvalidOperationException(
|
|
"Bind cannot be called after establishing connections");
|
|
#endif
|
|
var ret = m_NetworkInterface.Bind(endpoint);
|
|
if (ret == 0)
|
|
m_InternalState[InternalStateBound] = 1;
|
|
return ret;
|
|
}
|
|
|
|
public int Listen()
|
|
{
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
if (!m_InternalState.IsCreated)
|
|
throw new InvalidOperationException(
|
|
"Driver must be constructed with a populated or empty INetworkParameter params list");
|
|
if (Listening)
|
|
throw new InvalidOperationException(
|
|
"Listen can only be called once per NetworkDriver");
|
|
if (m_InternalState[InternalStateBound] == 0)
|
|
throw new InvalidOperationException(
|
|
"Listen can only be called after a successful call to Bind");
|
|
#endif
|
|
if (m_InternalState[InternalStateBound] == 0)
|
|
return -1;
|
|
Listening = true;
|
|
return 0;
|
|
}
|
|
|
|
public NetworkConnection Accept()
|
|
{
|
|
if (!Listening)
|
|
return default(NetworkConnection);
|
|
|
|
int id;
|
|
if (!m_NetworkAcceptQueue.TryDequeue(out id))
|
|
return default(NetworkConnection);
|
|
return new NetworkConnection {m_NetworkId = id, m_NetworkVersion = m_ConnectionList[id].Version};
|
|
}
|
|
|
|
public NetworkConnection Connect(NetworkEndPoint endpoint)
|
|
{
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
if (!m_InternalState.IsCreated)
|
|
throw new InvalidOperationException(
|
|
"Driver must be constructed with a populated or empty INetworkParameter params list");
|
|
#endif
|
|
int id;
|
|
if (!m_FreeList.TryDequeue(out id))
|
|
{
|
|
id = m_ConnectionList.Length;
|
|
m_ConnectionList.Add(new Connection{Id = id, Version = 1});
|
|
}
|
|
|
|
int ver = m_ConnectionList[id].Version;
|
|
var c = new Connection
|
|
{
|
|
Id = id,
|
|
Version = ver,
|
|
State = NetworkConnection.State.Connecting,
|
|
Address = endpoint,
|
|
Attempts = 1,
|
|
LastAttempt = m_updateTime,
|
|
SendToken = 0,
|
|
ReceiveToken = m_SessionIdCounter[0]
|
|
};
|
|
m_SessionIdCounter[0] = (ushort)(m_SessionIdCounter[0] + 1);
|
|
|
|
m_ConnectionList[id] = c;
|
|
var netcon = new NetworkConnection {m_NetworkId = id, m_NetworkVersion = ver};
|
|
SendConnectionRequest(c);
|
|
|
|
return netcon;
|
|
}
|
|
|
|
void SendConnectionRequest(Connection c)
|
|
{
|
|
var header = new UdpCHeader
|
|
{
|
|
Type = (int) UdpCProtocol.ConnectionRequest,
|
|
SessionToken = c.ReceiveToken
|
|
};
|
|
|
|
unsafe
|
|
{
|
|
// TODO: Actually use data part instead of header
|
|
if (NativeSend(m_NetworkInterface, ref header, null, 0, null, 0, c.Address) <= 0)
|
|
{
|
|
UnityEngine.Debug.LogError("Failed to send connect message");
|
|
}
|
|
}
|
|
}
|
|
|
|
public int Disconnect(NetworkConnection id)
|
|
{
|
|
Connection connection;
|
|
if ((connection = GetConnection(id)) == Connection.Null)
|
|
return 0;
|
|
|
|
if (connection.State == NetworkConnection.State.Connected)
|
|
{
|
|
SendPacket(UdpCProtocol.Disconnect, id);
|
|
}
|
|
RemoveConnection(connection);
|
|
|
|
return 0;
|
|
}
|
|
|
|
public NetworkConnection.State GetConnectionState(NetworkConnection con)
|
|
{
|
|
Connection connection;
|
|
if ((connection = GetConnection(con)) == Connection.Null)
|
|
return NetworkConnection.State.Disconnected;
|
|
return connection.State;
|
|
}
|
|
|
|
public NetworkEndPoint RemoteEndPoint(NetworkConnection id)
|
|
{
|
|
if (id.m_NetworkId == 0)
|
|
return m_NetworkInterface.RemoteEndPoint;
|
|
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
public NetworkEndPoint LocalEndPoint()
|
|
{
|
|
return m_NetworkInterface.LocalEndPoint;
|
|
}
|
|
|
|
public int Send(NetworkConnection id, DataStreamWriter strm)
|
|
{
|
|
unsafe
|
|
{
|
|
return Send(id, (IntPtr) strm.GetUnsafeReadOnlyPtr(), strm.Length);
|
|
}
|
|
}
|
|
|
|
public int Send(NetworkConnection id, IntPtr data, int len)
|
|
{
|
|
if (id.m_NetworkId < 0 || id.m_NetworkId >= m_ConnectionList.Length)
|
|
return 0;
|
|
var connection = m_ConnectionList[id.m_NetworkId];
|
|
if (connection.Version != id.m_NetworkVersion)
|
|
return 0;
|
|
|
|
#if ENABLE_UNITY_COLLECTIONS_CHECKS
|
|
if (connection.State == NetworkConnection.State.Connecting)
|
|
throw new InvalidOperationException("Cannot send data while connecting");
|
|
#endif
|
|
|
|
// update last attempt;
|
|
var header = new UdpCHeader
|
|
{
|
|
Type = (int) UdpCProtocol.Data,
|
|
SessionToken = connection.SendToken
|
|
};
|
|
|
|
unsafe
|
|
{
|
|
return NativeSend(m_NetworkInterface, ref header, (void*) data, len, null, 0, connection.Address);
|
|
}
|
|
}
|
|
|
|
public NetworkEvent.Type PopEvent(out NetworkConnection con, out DataStreamReader slice)
|
|
{
|
|
int offset, size;
|
|
slice = default(DataStreamReader);
|
|
int id;
|
|
var type = m_EventQueue.PopEvent(out id, out offset, out size);
|
|
if (size > 0)
|
|
slice = new DataStreamReader(m_DataStream, offset, size);
|
|
con = id < 0
|
|
? default(NetworkConnection)
|
|
: new NetworkConnection {m_NetworkId = id, m_NetworkVersion = m_ConnectionList[id].Version};
|
|
return type;
|
|
}
|
|
|
|
public NetworkEvent.Type PopEventForConnection(NetworkConnection connectionId, out DataStreamReader slice)
|
|
{
|
|
int offset, size;
|
|
slice = default(DataStreamReader);
|
|
if (connectionId.m_NetworkId < 0 || connectionId.m_NetworkId >= m_ConnectionList.Length ||
|
|
m_ConnectionList[connectionId.m_NetworkId].Version != connectionId.m_NetworkVersion)
|
|
return (int) NetworkEvent.Type.Empty;
|
|
var type = m_EventQueue.PopEventForConnection(connectionId.m_NetworkId, out offset, out size);
|
|
if (size > 0)
|
|
slice = new DataStreamReader(m_DataStream, offset, size);
|
|
return type;
|
|
}
|
|
|
|
/// ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
|
|
/// internal helper functions ::::::::::::::::::::::::::::::::::::::::::
|
|
void AddConnection(int id)
|
|
{
|
|
m_EventQueue.PushEvent(new NetworkEvent {connectionId = id, type = NetworkEvent.Type.Connect});
|
|
}
|
|
|
|
void AddDisconnection(int id)
|
|
{
|
|
m_EventQueue.PushEvent(new NetworkEvent {connectionId = id, type = NetworkEvent.Type.Disconnect});
|
|
}
|
|
|
|
Connection GetConnection(NetworkConnection id)
|
|
{
|
|
var con = m_ConnectionList[id.m_NetworkId];
|
|
if (con.Version != id.m_NetworkVersion)
|
|
return Connection.Null;
|
|
return con;
|
|
}
|
|
|
|
Connection GetConnection(NetworkEndPoint address, ushort sessionId)
|
|
{
|
|
for (int i = 0; i < m_ConnectionList.Length; i++)
|
|
{
|
|
if (address == m_ConnectionList[i].Address && m_ConnectionList[i].ReceiveToken == sessionId )
|
|
return m_ConnectionList[i];
|
|
}
|
|
|
|
return Connection.Null;
|
|
}
|
|
|
|
Connection GetNewConnection(NetworkEndPoint address, ushort sessionId)
|
|
{
|
|
for (int i = 0; i < m_ConnectionList.Length; i++)
|
|
{
|
|
if (address == m_ConnectionList[i].Address && m_ConnectionList[i].SendToken == sessionId )
|
|
return m_ConnectionList[i];
|
|
}
|
|
|
|
return Connection.Null;
|
|
}
|
|
|
|
void SetConnection(Connection connection)
|
|
{
|
|
m_ConnectionList[connection.Id] = connection;
|
|
}
|
|
|
|
bool RemoveConnection(Connection connection)
|
|
{
|
|
if (connection.State != NetworkConnection.State.Disconnected && connection == m_ConnectionList[connection.Id])
|
|
{
|
|
connection.State = NetworkConnection.State.Disconnected;
|
|
m_ConnectionList[connection.Id] = connection;
|
|
m_PendingFree.Enqueue(connection.Id);
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool UpdateConnection(Connection connection)
|
|
{
|
|
if (connection == m_ConnectionList[connection.Id])
|
|
{
|
|
SetConnection(connection);
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
int SendPacket(UdpCProtocol type, Connection connection)
|
|
{
|
|
var header = new UdpCHeader
|
|
{
|
|
Type = (byte) type,
|
|
SessionToken = connection.SendToken
|
|
};
|
|
|
|
unsafe
|
|
{
|
|
if (connection.DidReceiveData == 0)
|
|
{
|
|
header.Flags = 1;
|
|
return NativeSend(m_NetworkInterface, ref header, &connection.ReceiveToken, 2, null, 0, connection.Address);
|
|
}
|
|
return NativeSend(m_NetworkInterface, ref header, null, 0, null, 0, connection.Address);
|
|
}
|
|
}
|
|
|
|
int SendPacket(UdpCProtocol type, NetworkConnection id)
|
|
{
|
|
Connection connection;
|
|
if ((connection = GetConnection(id)) == Connection.Null)
|
|
return 0;
|
|
|
|
return SendPacket(type, connection);
|
|
}
|
|
|
|
static unsafe int NativeSend(T networkInterface, ref UdpCHeader header, void* payload, int payloadSize,
|
|
void* footer, int footerLen, NetworkEndPoint remote)
|
|
{
|
|
var iov = stackalloc network_iovec[3];
|
|
int result;
|
|
|
|
fixed (byte* ptr = header.Data)
|
|
{
|
|
iov[0].buf = ptr;
|
|
iov[0].len = UdpCHeader.Length;
|
|
|
|
iov[1].buf = payload;
|
|
iov[1].len = payloadSize;
|
|
|
|
iov[2].buf = footer;
|
|
iov[2].len = footerLen;
|
|
|
|
result = networkInterface.SendMessage(iov, 3, ref remote);
|
|
}
|
|
|
|
if (result == -1)
|
|
{
|
|
int error = Marshal.GetLastWin32Error();
|
|
throw new SocketException(error);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void CheckTimeouts()
|
|
{
|
|
for (int i = 0; i < m_ConnectionList.Length; ++i)
|
|
{
|
|
var connection = m_ConnectionList[i];
|
|
if (connection == Connection.Null)
|
|
continue;
|
|
|
|
long now = m_updateTime;
|
|
|
|
var netcon = new NetworkConnection {m_NetworkId = connection.Id, m_NetworkVersion = connection.Version};
|
|
if ((connection.State == NetworkConnection.State.Connecting ||
|
|
connection.State == NetworkConnection.State.AwaitingResponse) &&
|
|
now - connection.LastAttempt > m_NetworkParams.config.connectTimeout)
|
|
{
|
|
if (connection.Attempts >= m_NetworkParams.config.maxConnectAttempts)
|
|
{
|
|
RemoveConnection(connection);
|
|
AddDisconnection(connection.Id);
|
|
continue;
|
|
}
|
|
|
|
connection.Attempts = ++connection.Attempts;
|
|
connection.LastAttempt = now;
|
|
SetConnection(connection);
|
|
|
|
if (connection.State == NetworkConnection.State.Connecting)
|
|
SendConnectionRequest(connection);
|
|
else
|
|
SendPacket(UdpCProtocol.ConnectionAccept, netcon);
|
|
}
|
|
|
|
if (connection.State == NetworkConnection.State.Connected &&
|
|
now - connection.LastAttempt > m_NetworkParams.config.disconnectTimeout)
|
|
{
|
|
Disconnect(netcon);
|
|
AddDisconnection(connection.Id);
|
|
}
|
|
}
|
|
}
|
|
|
|
public DataStreamWriter GetDataStream()
|
|
{
|
|
return m_DataStream;
|
|
}
|
|
|
|
private NativeArray<int> m_ReceiveCount;
|
|
public int ReceiveCount {
|
|
get { return m_ReceiveCount[0]; }
|
|
set { m_ReceiveCount[0] = value; }
|
|
}
|
|
|
|
public bool DynamicDataStreamSize()
|
|
{
|
|
return m_NetworkParams.dataStream.size == 0;
|
|
}
|
|
|
|
public unsafe int AppendPacket(NetworkEndPoint address, UdpCHeader header, int dataLen)
|
|
{
|
|
int count = 0;
|
|
switch ((UdpCProtocol) header.Type)
|
|
{
|
|
case UdpCProtocol.ConnectionRequest:
|
|
{
|
|
if (!Listening)
|
|
return 0;
|
|
|
|
Connection c;
|
|
if ((c = GetNewConnection(address, header.SessionToken)) == Connection.Null || c.State == NetworkConnection.State.Disconnected)
|
|
{
|
|
int id;
|
|
var sessionId = m_SessionIdCounter[0];
|
|
m_SessionIdCounter[0] = (ushort) (m_SessionIdCounter[0] + 1);
|
|
if (!m_FreeList.TryDequeue(out id))
|
|
{
|
|
id = m_ConnectionList.Length;
|
|
m_ConnectionList.Add(new Connection{Id = id, Version = 1});
|
|
}
|
|
|
|
int ver = m_ConnectionList[id].Version;
|
|
c = new Connection
|
|
{
|
|
Id = id,
|
|
Version = ver,
|
|
ReceiveToken = sessionId,
|
|
SendToken = header.SessionToken,
|
|
State = NetworkConnection.State.Connected,
|
|
Address = address,
|
|
Attempts = 1,
|
|
LastAttempt = m_updateTime
|
|
};
|
|
SetConnection(c);
|
|
m_NetworkAcceptQueue.Enqueue(id);
|
|
count++;
|
|
}
|
|
else
|
|
{
|
|
c.Attempts++;
|
|
c.LastAttempt = m_updateTime;
|
|
SetConnection(c);
|
|
}
|
|
|
|
SendPacket(UdpCProtocol.ConnectionAccept,
|
|
new NetworkConnection {m_NetworkId = c.Id, m_NetworkVersion = c.Version});
|
|
}
|
|
break;
|
|
case UdpCProtocol.ConnectionReject:
|
|
{
|
|
// m_EventQ.Enqueue(Id, (int)NetworkEvent.Connect);
|
|
}
|
|
break;
|
|
case UdpCProtocol.ConnectionAccept:
|
|
{
|
|
if (header.Flags != 1)
|
|
{
|
|
UnityEngine.Debug.LogError("Accept message received without flag set");
|
|
return 0;
|
|
}
|
|
|
|
Connection c = GetConnection(address, header.SessionToken);
|
|
if (c != Connection.Null)
|
|
{
|
|
c.DidReceiveData = 1;
|
|
|
|
if (c.State == NetworkConnection.State.Connected)
|
|
{
|
|
//DebugLog("Dropping connect request for an already connected endpoint [" + address + "]");
|
|
return 0;
|
|
}
|
|
|
|
if (c.State == NetworkConnection.State.Connecting)
|
|
{
|
|
var sliceOffset = m_DataStream.Length;
|
|
m_DataStream.WriteBytesWithUnsafePointer(2);
|
|
var dataStreamReader = new DataStreamReader(m_DataStream, sliceOffset, 2);
|
|
var context = default(DataStreamReader.Context);
|
|
c.SendToken = dataStreamReader.ReadUShort(ref context);
|
|
m_DataStream.WriteBytesWithUnsafePointer(-2);
|
|
|
|
c.State = NetworkConnection.State.Connected;
|
|
UpdateConnection(c);
|
|
AddConnection(c.Id);
|
|
count++;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case UdpCProtocol.Disconnect:
|
|
{
|
|
Connection c = GetConnection(address, header.SessionToken);
|
|
if (c != Connection.Null)
|
|
{
|
|
if (RemoveConnection(c))
|
|
AddDisconnection(c.Id);
|
|
count++;
|
|
}
|
|
}
|
|
break;
|
|
case UdpCProtocol.Data:
|
|
{
|
|
Connection c = GetConnection(address, header.SessionToken);
|
|
if (c == Connection.Null)
|
|
return 0;
|
|
|
|
c.DidReceiveData = 1;
|
|
c.LastAttempt = m_updateTime;
|
|
UpdateConnection(c);
|
|
|
|
var length = dataLen - UdpCHeader.Length;
|
|
|
|
if (c.State == NetworkConnection.State.Connecting)
|
|
{
|
|
if (header.Flags != 1)
|
|
{
|
|
UnityEngine.Debug.LogError("Received data without connection (no send token)");
|
|
return 0;
|
|
}
|
|
|
|
var tokenOffset = m_DataStream.Length + length - 2;
|
|
m_DataStream.WriteBytesWithUnsafePointer(length);
|
|
var dataStreamReader = new DataStreamReader(m_DataStream, tokenOffset, 2);
|
|
var context = default(DataStreamReader.Context);
|
|
c.SendToken = dataStreamReader.ReadUShort(ref context);
|
|
m_DataStream.WriteBytesWithUnsafePointer(-length);
|
|
|
|
c.State = NetworkConnection.State.Connected;
|
|
UpdateConnection(c);
|
|
Assert.IsTrue(!Listening);
|
|
AddConnection(c.Id);
|
|
count++;
|
|
}
|
|
|
|
if (header.Flags == 1)
|
|
length -= 2;
|
|
|
|
var sliceOffset = m_DataStream.Length;
|
|
m_DataStream.WriteBytesWithUnsafePointer(length);
|
|
|
|
m_EventQueue.PushEvent(new NetworkEvent
|
|
{
|
|
connectionId = c.Id,
|
|
type = NetworkEvent.Type.Data,
|
|
offset = sliceOffset,
|
|
size = length
|
|
});
|
|
count++;
|
|
} break;
|
|
}
|
|
|
|
return count;
|
|
}
|
|
}
|
|
}
|