mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 02:50:32 +00:00
fix: kcp2k V1.12 (updated)
-> where-allocation now optional to handle platforms that don't support it
This commit is contained in:
parent
d66d228079
commit
6d021c0875
@ -34,8 +34,10 @@ public class KcpTransport : Transport
|
||||
public uint SendWindowSize = 4096; //Kcp.WND_SND; 32 by default. Mirror sends a lot, so we need a lot more.
|
||||
[Tooltip("KCP window size can be modified to support higher loads.")]
|
||||
public uint ReceiveWindowSize = 4096; //Kcp.WND_RCV; 128 by default. Mirror sends a lot, so we need a lot more.
|
||||
[Tooltip("Enable to use where-allocation NonAlloc KcpServer/Client/Connection versions. Highly recommended on all Unity platforms.")]
|
||||
public bool NonAlloc = true;
|
||||
|
||||
// server & client
|
||||
// server & client (where-allocation NonAlloc versions)
|
||||
KcpServer server;
|
||||
KcpClient client;
|
||||
|
||||
@ -60,26 +62,42 @@ void Awake()
|
||||
Log.Error = Debug.LogError;
|
||||
|
||||
// client
|
||||
client = new KcpClient(
|
||||
() => OnClientConnected.Invoke(),
|
||||
(message) => OnClientDataReceived.Invoke(message, Channels.Reliable),
|
||||
() => OnClientDisconnected.Invoke()
|
||||
);
|
||||
client = NonAlloc
|
||||
? new KcpClientNonAlloc(
|
||||
() => OnClientConnected.Invoke(),
|
||||
(message) => OnClientDataReceived.Invoke(message, Channels.Reliable),
|
||||
() => OnClientDisconnected.Invoke())
|
||||
: new KcpClient(
|
||||
() => OnClientConnected.Invoke(),
|
||||
(message) => OnClientDataReceived.Invoke(message, Channels.Reliable),
|
||||
() => OnClientDisconnected.Invoke());
|
||||
|
||||
// server
|
||||
server = new KcpServer(
|
||||
(connectionId) => OnServerConnected.Invoke(connectionId),
|
||||
(connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.Reliable),
|
||||
(connectionId) => OnServerDisconnected.Invoke(connectionId),
|
||||
DualMode,
|
||||
NoDelay,
|
||||
Interval,
|
||||
FastResend,
|
||||
CongestionWindow,
|
||||
SendWindowSize,
|
||||
ReceiveWindowSize,
|
||||
Timeout
|
||||
);
|
||||
server = NonAlloc
|
||||
? new KcpServerNonAlloc(
|
||||
(connectionId) => OnServerConnected.Invoke(connectionId),
|
||||
(connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.Reliable),
|
||||
(connectionId) => OnServerDisconnected.Invoke(connectionId),
|
||||
DualMode,
|
||||
NoDelay,
|
||||
Interval,
|
||||
FastResend,
|
||||
CongestionWindow,
|
||||
SendWindowSize,
|
||||
ReceiveWindowSize,
|
||||
Timeout)
|
||||
: new KcpServer(
|
||||
(connectionId) => OnServerConnected.Invoke(connectionId),
|
||||
(connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.Reliable),
|
||||
(connectionId) => OnServerDisconnected.Invoke(connectionId),
|
||||
DualMode,
|
||||
NoDelay,
|
||||
Interval,
|
||||
FastResend,
|
||||
CongestionWindow,
|
||||
SendWindowSize,
|
||||
ReceiveWindowSize,
|
||||
Timeout);
|
||||
|
||||
if (statisticsLog)
|
||||
InvokeRepeating(nameof(OnLogStatistics), 1, 1);
|
||||
|
@ -22,6 +22,11 @@ public KcpClient(Action OnConnected, Action<ArraySegment<byte>> OnData, Action O
|
||||
this.OnDisconnected = OnDisconnected;
|
||||
}
|
||||
|
||||
// CreateConnection can be overwritten for where-allocation:
|
||||
// https://github.com/vis2k/where-allocation
|
||||
protected virtual KcpClientConnection CreateConnection() =>
|
||||
new KcpClientConnection();
|
||||
|
||||
public void Connect(string address, ushort port, bool noDelay, uint interval, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = KcpConnection.DEFAULT_TIMEOUT)
|
||||
{
|
||||
if (connected)
|
||||
@ -30,7 +35,8 @@ public void Connect(string address, ushort port, bool noDelay, uint interval, in
|
||||
return;
|
||||
}
|
||||
|
||||
connection = new KcpClientConnection();
|
||||
// create connection
|
||||
connection = CreateConnection();
|
||||
|
||||
// setup events
|
||||
connection.OnAuthenticated = () =>
|
||||
|
@ -28,6 +28,15 @@ public static bool ResolveHostname(string hostname, out IPAddress[] addresses)
|
||||
}
|
||||
}
|
||||
|
||||
// EndPoint & Receive functions can be overwritten for where-allocation:
|
||||
// https://github.com/vis2k/where-allocation
|
||||
// NOTE: Client's SendTo doesn't allocate, don't need a virtual.
|
||||
protected virtual void CreateRemoteEndPoint(IPAddress[] addresses, ushort port) =>
|
||||
remoteEndPoint = new IPEndPoint(addresses[0], port);
|
||||
|
||||
protected virtual int ReceiveFrom(byte[] buffer) =>
|
||||
socket.ReceiveFrom(buffer, ref remoteEndPoint);
|
||||
|
||||
public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT)
|
||||
{
|
||||
Log.Info($"KcpClient: connect to {host}:{port}");
|
||||
@ -35,9 +44,14 @@ public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp.
|
||||
// try resolve host name
|
||||
if (ResolveHostname(host, out IPAddress[] addresses))
|
||||
{
|
||||
remoteEndpoint = new IPEndPoint(addresses[0], port);
|
||||
socket = new Socket(remoteEndpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||
socket.Connect(remoteEndpoint);
|
||||
// create remote endpoint
|
||||
CreateRemoteEndPoint(addresses, port);
|
||||
|
||||
// create socket
|
||||
socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
|
||||
socket.Connect(remoteEndPoint);
|
||||
|
||||
// set up kcp
|
||||
SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
|
||||
|
||||
// client should send handshake to server as very first message
|
||||
@ -49,6 +63,7 @@ public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp.
|
||||
else OnDisconnected();
|
||||
}
|
||||
|
||||
|
||||
// call from transport update
|
||||
public void RawReceive()
|
||||
{
|
||||
@ -58,7 +73,7 @@ public void RawReceive()
|
||||
{
|
||||
while (socket.Poll(0, SelectMode.SelectRead))
|
||||
{
|
||||
int msgLength = socket.ReceiveFrom(rawReceiveBuffer, ref remoteEndpoint);
|
||||
int msgLength = ReceiveFrom(rawReceiveBuffer);
|
||||
// IMPORTANT: detect if buffer was too small for the
|
||||
// received msgLength. otherwise the excess
|
||||
// data would be silently lost.
|
||||
|
@ -10,7 +10,7 @@ enum KcpState { Connected, Authenticated, Disconnected }
|
||||
public abstract class KcpConnection
|
||||
{
|
||||
protected Socket socket;
|
||||
protected EndPoint remoteEndpoint;
|
||||
protected EndPoint remoteEndPoint;
|
||||
internal Kcp kcp;
|
||||
|
||||
// kcp can have several different states, let's use a state machine
|
||||
@ -650,7 +650,7 @@ public void Disconnect()
|
||||
}
|
||||
|
||||
// get remote endpoint
|
||||
public EndPoint GetRemoteEndPoint() => remoteEndpoint;
|
||||
public EndPoint GetRemoteEndPoint() => remoteEndPoint;
|
||||
|
||||
// pause/unpause to safely support mirror scene handling and to
|
||||
// immediately pause the receive while loop if needed.
|
||||
|
@ -43,7 +43,7 @@ public class KcpServer
|
||||
public int Timeout;
|
||||
|
||||
// state
|
||||
Socket socket;
|
||||
protected Socket socket;
|
||||
EndPoint newClientEP;
|
||||
|
||||
// IMPORTANT: raw receive buffer always needs to be of 'MTU' size, even
|
||||
@ -137,6 +137,33 @@ public string GetClientAddress(int connectionId)
|
||||
return "";
|
||||
}
|
||||
|
||||
// EndPoint & Receive functions can be overwritten for where-allocation:
|
||||
// https://github.com/vis2k/where-allocation
|
||||
protected virtual int ReceiveFrom(byte[] buffer, out int connectionHash)
|
||||
{
|
||||
// NOTE: ReceiveFrom allocates.
|
||||
// we pass our IPEndPoint to ReceiveFrom.
|
||||
// receive from calls newClientEP.Create(socketAddr).
|
||||
// IPEndPoint.Create always returns a new IPEndPoint.
|
||||
// https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761
|
||||
int read = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP);
|
||||
|
||||
// calculate connectionHash from endpoint
|
||||
// NOTE: IPEndPoint.GetHashCode() allocates.
|
||||
// it calls m_Address.GetHashCode().
|
||||
// m_Address is an IPAddress.
|
||||
// GetHashCode() allocates for IPv6:
|
||||
// https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699
|
||||
//
|
||||
// => using only newClientEP.Port wouldn't work, because
|
||||
// different connections can have the same port.
|
||||
connectionHash = newClientEP.GetHashCode();
|
||||
return read;
|
||||
}
|
||||
|
||||
protected virtual KcpServerConnection CreateConnection() =>
|
||||
new KcpServerConnection(socket, newClientEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);
|
||||
|
||||
// process incoming messages. should be called before updating the world.
|
||||
HashSet<int> connectionsToRemove = new HashSet<int>();
|
||||
public void TickIncoming()
|
||||
@ -145,25 +172,10 @@ public void TickIncoming()
|
||||
{
|
||||
try
|
||||
{
|
||||
// NOTE: ReceiveFrom allocates.
|
||||
// we pass our IPEndPoint to ReceiveFrom.
|
||||
// receive from calls newClientEP.Create(socketAddr).
|
||||
// IPEndPoint.Create always returns a new IPEndPoint.
|
||||
// https://github.com/mono/mono/blob/f74eed4b09790a0929889ad7fc2cf96c9b6e3757/mcs/class/System/System.Net.Sockets/Socket.cs#L1761
|
||||
int msgLength = socket.ReceiveFrom(rawReceiveBuffer, 0, rawReceiveBuffer.Length, SocketFlags.None, ref newClientEP);
|
||||
// receive
|
||||
int msgLength = ReceiveFrom(rawReceiveBuffer, out int connectionId);
|
||||
//Log.Info($"KCP: server raw recv {msgLength} bytes = {BitConverter.ToString(buffer, 0, msgLength)}");
|
||||
|
||||
// calculate connectionId from endpoint
|
||||
// NOTE: IPEndPoint.GetHashCode() allocates.
|
||||
// it calls m_Address.GetHashCode().
|
||||
// m_Address is an IPAddress.
|
||||
// GetHashCode() allocates for IPv6:
|
||||
// https://github.com/mono/mono/blob/bdd772531d379b4e78593587d15113c37edd4a64/mcs/class/referencesource/System/net/System/Net/IPAddress.cs#L699
|
||||
//
|
||||
// => using only newClientEP.Port wouldn't work, because
|
||||
// different connections can have the same port.
|
||||
int connectionId = newClientEP.GetHashCode();
|
||||
|
||||
// IMPORTANT: detect if buffer was too small for the received
|
||||
// msgLength. otherwise the excess data would be
|
||||
// silently lost.
|
||||
@ -173,8 +185,9 @@ public void TickIncoming()
|
||||
// is this a new connection?
|
||||
if (!connections.TryGetValue(connectionId, out KcpServerConnection connection))
|
||||
{
|
||||
// create a new KcpConnection
|
||||
connection = new KcpServerConnection(socket, newClientEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);
|
||||
// create a new KcpConnection based on last received
|
||||
// EndPoint. can be overwritten for where-allocation.
|
||||
connection = CreateConnection();
|
||||
|
||||
// DO NOT add to connections yet. only if the first message
|
||||
// is actually the kcp handshake. otherwise it's either:
|
||||
@ -205,7 +218,7 @@ public void TickIncoming()
|
||||
|
||||
// add to connections dict after being authenticated.
|
||||
connections.Add(connectionId, connection);
|
||||
Log.Info($"KCP: server added connection({connectionId}): {newClientEP}");
|
||||
Log.Info($"KCP: server added connection({connectionId})");
|
||||
|
||||
// setup Data + Disconnected events only AFTER the
|
||||
// handshake. we don't want to fire OnServerDisconnected
|
||||
|
@ -5,16 +5,18 @@ namespace kcp2k
|
||||
{
|
||||
public class KcpServerConnection : KcpConnection
|
||||
{
|
||||
public KcpServerConnection(Socket socket, EndPoint remoteEndpoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT)
|
||||
// Constructor & Send functions can be overwritten for where-allocation:
|
||||
// https://github.com/vis2k/where-allocation
|
||||
public KcpServerConnection(Socket socket, EndPoint remoteEndPoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT)
|
||||
{
|
||||
this.socket = socket;
|
||||
this.remoteEndpoint = remoteEndpoint;
|
||||
this.remoteEndPoint = remoteEndPoint;
|
||||
SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
|
||||
}
|
||||
|
||||
protected override void RawSend(byte[] data, int length)
|
||||
{
|
||||
socket.SendTo(data, 0, length, SocketFlags.None, remoteEndpoint);
|
||||
socket.SendTo(data, 0, length, SocketFlags.None, remoteEndPoint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b320ff06046474eae7bce7240ea478c
|
||||
timeCreated: 1626430641
|
@ -0,0 +1,24 @@
|
||||
// where-allocation version of KcpClientConnection.
|
||||
// may not be wanted on all platforms, so it's an extra optional class.
|
||||
using System.Net;
|
||||
using WhereAllocation;
|
||||
|
||||
namespace kcp2k
|
||||
{
|
||||
public class KcpClientConnectionNonAlloc : KcpClientConnection
|
||||
{
|
||||
IPEndPointNonAlloc reusableEP;
|
||||
|
||||
protected override void CreateRemoteEndPoint(IPAddress[] addresses, ushort port)
|
||||
{
|
||||
// create reusableEP with same address family as remoteEndPoint.
|
||||
// otherwise ReceiveFrom_NonAlloc couldn't use it.
|
||||
reusableEP = new IPEndPointNonAlloc(addresses[0], port);
|
||||
base.CreateRemoteEndPoint(addresses, port);
|
||||
}
|
||||
|
||||
// where-allocation nonalloc recv
|
||||
protected override int ReceiveFrom(byte[] buffer) =>
|
||||
socket.ReceiveFrom_NonAlloc(buffer, reusableEP);
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4c1b235bbe054706bef6d092f361006e
|
||||
timeCreated: 1626430539
|
@ -0,0 +1,17 @@
|
||||
// where-allocation version of KcpClientConnectionNonAlloc.
|
||||
// may not be wanted on all platforms, so it's an extra optional class.
|
||||
using System;
|
||||
|
||||
namespace kcp2k
|
||||
{
|
||||
public class KcpClientNonAlloc : KcpClient
|
||||
{
|
||||
public KcpClientNonAlloc(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
|
||||
: base(OnConnected, OnData, OnDisconnected)
|
||||
{
|
||||
}
|
||||
|
||||
protected override KcpClientConnection CreateConnection() =>
|
||||
new KcpClientConnectionNonAlloc();
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 2cf0ccf7d551480bb5af08fcbe169f84
|
||||
timeCreated: 1626435264
|
@ -0,0 +1,25 @@
|
||||
// where-allocation version of KcpServerConnection.
|
||||
// may not be wanted on all platforms, so it's an extra optional class.
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using WhereAllocation;
|
||||
|
||||
namespace kcp2k
|
||||
{
|
||||
public class KcpServerConnectionNonAlloc : KcpServerConnection
|
||||
{
|
||||
IPEndPointNonAlloc reusableSendEndPoint;
|
||||
|
||||
public KcpServerConnectionNonAlloc(Socket socket, EndPoint remoteEndpoint, IPEndPointNonAlloc reusableSendEndPoint, bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = Kcp.WND_SND, uint receiveWindowSize = Kcp.WND_RCV, int timeout = DEFAULT_TIMEOUT)
|
||||
: base(socket, remoteEndpoint, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout)
|
||||
{
|
||||
this.reusableSendEndPoint = reusableSendEndPoint;
|
||||
}
|
||||
|
||||
protected override void RawSend(byte[] data, int length)
|
||||
{
|
||||
// where-allocation nonalloc send
|
||||
socket.SendTo_NonAlloc(data, 0, length, SocketFlags.None, reusableSendEndPoint);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4e1b74cc224b4c83a0f6c8d8da9090ab
|
||||
timeCreated: 1626430608
|
@ -0,0 +1,51 @@
|
||||
// where-allocation version of KcpServer.
|
||||
// may not be wanted on all platforms, so it's an extra optional class.
|
||||
using System;
|
||||
using System.Net;
|
||||
using System.Net.Sockets;
|
||||
using WhereAllocation;
|
||||
|
||||
namespace kcp2k
|
||||
{
|
||||
public class KcpServerNonAlloc : KcpServer
|
||||
{
|
||||
IPEndPointNonAlloc reusableClientEP;
|
||||
|
||||
public KcpServerNonAlloc(Action<int> OnConnected, Action<int, ArraySegment<byte>> OnData, Action<int> OnDisconnected, bool DualMode, bool NoDelay, uint Interval, int FastResend = 0, bool CongestionWindow = true, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV, int Timeout = KcpConnection.DEFAULT_TIMEOUT)
|
||||
: base(OnConnected, OnData, OnDisconnected, DualMode, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout)
|
||||
{
|
||||
// create reusableClientEP either IPv4 or IPv6
|
||||
reusableClientEP = DualMode
|
||||
? new IPEndPointNonAlloc(IPAddress.IPv6Any, 0)
|
||||
: new IPEndPointNonAlloc(IPAddress.Any, 0);
|
||||
}
|
||||
|
||||
protected override int ReceiveFrom(byte[] buffer, out int connectionHash)
|
||||
{
|
||||
// where-allocation nonalloc ReceiveFrom.
|
||||
int read = socket.ReceiveFrom_NonAlloc(buffer, 0, buffer.Length, SocketFlags.None, reusableClientEP);
|
||||
SocketAddress remoteAddress = reusableClientEP.temp;
|
||||
|
||||
// where-allocation nonalloc GetHashCode
|
||||
connectionHash = remoteAddress.GetHashCode();
|
||||
return read;
|
||||
}
|
||||
|
||||
protected override KcpServerConnection CreateConnection()
|
||||
{
|
||||
// IPEndPointNonAlloc is reused all the time.
|
||||
// we can't store that as the connection's endpoint.
|
||||
// we need a new copy!
|
||||
IPEndPoint newClientEP = reusableClientEP.DeepCopyIPEndPoint();
|
||||
|
||||
// for allocation free sending, we also need another
|
||||
// IPEndPointNonAlloc...
|
||||
IPEndPointNonAlloc reusableSendEP = new IPEndPointNonAlloc(newClientEP.Address, newClientEP.Port);
|
||||
|
||||
// create a new KcpConnection NonAlloc version
|
||||
// -> where-allocation IPEndPointNonAlloc is reused.
|
||||
// need to create a new one from the temp address.
|
||||
return new KcpServerConnectionNonAlloc(socket, newClientEP, reusableSendEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 54b8398dcd544c8a93bcad846214cc40
|
||||
timeCreated: 1626432191
|
Loading…
Reference in New Issue
Block a user