From a27aad4d92e391f741fa4755ee2e7732e83591a8 Mon Sep 17 00:00:00 2001 From: vis2k Date: Thu, 29 Oct 2020 10:36:59 +0100 Subject: [PATCH] Configurable FastResend, CongestionWindow --- kcp2k/Assets/kcp2k/MirrorTransport/KcpTransport.cs | 12 +++++++++--- kcp2k/Assets/kcp2k/highlevel/KcpClient.cs | 4 ++-- kcp2k/Assets/kcp2k/highlevel/KcpClientConnection.cs | 4 ++-- kcp2k/Assets/kcp2k/highlevel/KcpConnection.cs | 6 ++++-- kcp2k/Assets/kcp2k/highlevel/KcpServer.cs | 13 ++++++++++++- kcp2k/Assets/kcp2k/highlevel/KcpServerConnection.cs | 4 ++-- 6 files changed, 31 insertions(+), 12 deletions(-) diff --git a/kcp2k/Assets/kcp2k/MirrorTransport/KcpTransport.cs b/kcp2k/Assets/kcp2k/MirrorTransport/KcpTransport.cs index ea0c47e1..70a6b40b 100644 --- a/kcp2k/Assets/kcp2k/MirrorTransport/KcpTransport.cs +++ b/kcp2k/Assets/kcp2k/MirrorTransport/KcpTransport.cs @@ -16,9 +16,13 @@ public class KcpTransport : Transport [Tooltip("KCP internal update interval. 100ms is KCP default, but a lower interval is recommended to minimize latency and to scale to more networked entities.")] public uint Interval = 10; [Header("Advanced")] - [Tooltip("KCP window size can be modified to support higher loads. For example, Mirror Benchmark requires 128 for 4k monsters, 512 for 10k monsters")] + [Tooltip("KCP fastresend parameter. Faster resend for the cost of higher bandwidth.")] + public int FastResend = 0; + [Tooltip("KCP congestion window can be disabled. This is necessary to Mirror 10k Benchmark. Disable this for high scale games if connections get chocked regularly.")] + public bool CongestionWindow = true; // KCP 'NoCongestionWindow' is false by default. here we negate it for ease of use. + [Tooltip("KCP window size can be modified to support higher loads. For example, Mirror Benchmark requires 128 for 4k monsters, 256 for 10k monsters (if CongestionWindow is disabled.)")] public uint SendWindowSize = 128; //Kcp.WND_SND; 32 by default. 128 is better for 4k Benchmark etc. - [Tooltip("KCP window size can be modified to support higher loads. For example, Mirror Benchmark requires 128 for 4k monsters, 512 for 10k monsters")] + [Tooltip("KCP window size can be modified to support higher loads. For example, Mirror Benchmark requires 128 for 4k monsters, 256 for 10k monsters (if CongestionWindow is disabled.)")] public uint ReceiveWindowSize = Kcp.WND_RCV; // server & client @@ -44,6 +48,8 @@ void Awake() (connectionId) => OnServerDisconnected.Invoke(connectionId), NoDelay, Interval, + FastResend, + CongestionWindow, SendWindowSize, ReceiveWindowSize ); @@ -58,7 +64,7 @@ public override bool Available() => public override bool ClientConnected() => client.connected; public override void ClientConnect(string address) { - client.Connect(address, Port, NoDelay, Interval, SendWindowSize, ReceiveWindowSize); + client.Connect(address, Port, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize); } public override bool ClientSend(int channelId, ArraySegment segment) { diff --git a/kcp2k/Assets/kcp2k/highlevel/KcpClient.cs b/kcp2k/Assets/kcp2k/highlevel/KcpClient.cs index 879fc611..b4e73176 100644 --- a/kcp2k/Assets/kcp2k/highlevel/KcpClient.cs +++ b/kcp2k/Assets/kcp2k/highlevel/KcpClient.cs @@ -23,7 +23,7 @@ public KcpClient(Action OnConnected, Action> OnData, Action O this.OnDisconnected = OnDisconnected; } - public void Connect(string address, ushort port, bool noDelay, uint interval, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV) + 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) { if (connected) { @@ -54,7 +54,7 @@ public void Connect(string address, ushort port, bool noDelay, uint interval, ui }; // connect - connection.Connect(address, port, noDelay, interval, SendWindowSize, ReceiveWindowSize); + connection.Connect(address, port, noDelay, interval, fastResend, congestionWindow, SendWindowSize, ReceiveWindowSize); } public void Send(ArraySegment segment) diff --git a/kcp2k/Assets/kcp2k/highlevel/KcpClientConnection.cs b/kcp2k/Assets/kcp2k/highlevel/KcpClientConnection.cs index efd4f980..8ae5ac54 100644 --- a/kcp2k/Assets/kcp2k/highlevel/KcpClientConnection.cs +++ b/kcp2k/Assets/kcp2k/highlevel/KcpClientConnection.cs @@ -8,7 +8,7 @@ public class KcpClientConnection : KcpConnection { readonly byte[] buffer = new byte[1500]; - public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp.INTERVAL, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV) + 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) { Debug.Log($"KcpClient: connect to {host}:{port}"); IPAddress[] ipAddress = Dns.GetHostAddresses(host); @@ -18,7 +18,7 @@ public void Connect(string host, ushort port, bool noDelay, uint interval = Kcp. remoteEndpoint = new IPEndPoint(ipAddress[0], port); socket = new Socket(remoteEndpoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp); socket.Connect(remoteEndpoint); - SetupKcp(noDelay, interval, SendWindowSize, ReceiveWindowSize); + SetupKcp(noDelay, interval, fastResend, congestionWindow, SendWindowSize, ReceiveWindowSize); // client should send handshake to server as very first message SendHandshake(); diff --git a/kcp2k/Assets/kcp2k/highlevel/KcpConnection.cs b/kcp2k/Assets/kcp2k/highlevel/KcpConnection.cs index de2ab0bf..68e6d9ae 100644 --- a/kcp2k/Assets/kcp2k/highlevel/KcpConnection.cs +++ b/kcp2k/Assets/kcp2k/highlevel/KcpConnection.cs @@ -59,10 +59,12 @@ public abstract class KcpConnection // NoDelay, interval, window size are the most important configurations. // let's force require the parameters so we don't forget it anywhere. - protected void SetupKcp(bool noDelay, uint interval = Kcp.INTERVAL, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV) + protected void SetupKcp(bool noDelay, uint interval = Kcp.INTERVAL, int fastResend = 0, bool congestionWindow = true, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV) { kcp = new Kcp(0, RawSend); - kcp.SetNoDelay(noDelay ? 1u : 0u, interval); + // set nodelay. + // note that kcp uses 'nocwnd' internally so we negate the parameter + kcp.SetNoDelay(noDelay ? 1u : 0u, interval, fastResend, !congestionWindow); kcp.SetWindowSize(SendWindowSize, ReceiveWindowSize); refTime.Start(); state = KcpState.Connected; diff --git a/kcp2k/Assets/kcp2k/highlevel/KcpServer.cs b/kcp2k/Assets/kcp2k/highlevel/KcpServer.cs index 7c27b9e2..e5efe293 100644 --- a/kcp2k/Assets/kcp2k/highlevel/KcpServer.cs +++ b/kcp2k/Assets/kcp2k/highlevel/KcpServer.cs @@ -23,6 +23,13 @@ public class KcpServer // interval is recommended to minimize latency and to scale to more // networked entities. public uint Interval; + // KCP fastresend parameter. Faster resend for the cost of higher + // bandwidth. + public int FastResend; + // KCP 'NoCongestionWindow' is false by default. here we negate it for + // ease of use. This can be disabled for high scale games if connections + // choke regularly. + public bool CongestionWindow; // KCP window size can be modified to support higher loads. // for example, Mirror Benchmark requires: // 128, 128 for 4k monsters @@ -44,6 +51,8 @@ public KcpServer(Action OnConnected, Action OnDisconnected, bool NoDelay, uint Interval, + int FastResend = 0, + bool CongestionWindow = true, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV) { @@ -52,6 +61,8 @@ public KcpServer(Action OnConnected, this.OnDisconnected = OnDisconnected; this.NoDelay = NoDelay; this.Interval = Interval; + this.FastResend = FastResend; + this.CongestionWindow = CongestionWindow; this.SendWindowSize = SendWindowSize; this.ReceiveWindowSize = ReceiveWindowSize; } @@ -111,7 +122,7 @@ public void Tick() if (!connections.TryGetValue(connectionId, out KcpServerConnection connection)) { // create a new KcpConnection - connection = new KcpServerConnection(socket, newClientEP, NoDelay, Interval, SendWindowSize, ReceiveWindowSize); + connection = new KcpServerConnection(socket, newClientEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize); // DO NOT add to connections yet. only if the first message // is actually the kcp handshake. otherwise it's either: diff --git a/kcp2k/Assets/kcp2k/highlevel/KcpServerConnection.cs b/kcp2k/Assets/kcp2k/highlevel/KcpServerConnection.cs index 40fd2091..ececd53d 100644 --- a/kcp2k/Assets/kcp2k/highlevel/KcpServerConnection.cs +++ b/kcp2k/Assets/kcp2k/highlevel/KcpServerConnection.cs @@ -5,11 +5,11 @@ namespace kcp2k { public class KcpServerConnection : KcpConnection { - public KcpServerConnection(Socket socket, EndPoint remoteEndpoint, bool noDelay, uint interval = Kcp.INTERVAL, uint SendWindowSize = Kcp.WND_SND, uint ReceiveWindowSize = Kcp.WND_RCV) + 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) { this.socket = socket; this.remoteEndpoint = remoteEndpoint; - SetupKcp(noDelay, interval, SendWindowSize, ReceiveWindowSize); + SetupKcp(noDelay, interval, fastResend, congestionWindow, SendWindowSize, ReceiveWindowSize); } protected override void RawSend(byte[] data, int length)