mirror of
https://github.com/MirrorNetworking/Mirror.git
synced 2024-11-18 11:00:32 +00:00
fix: kcp2k V1.13
- uncorks max message size from 144 KB to as much as we want based on receive window size. fixes https://github.com/vis2k/kcp2k/issues/22 fixes https://github.com/skywind3000/kcp/pull/291 - feature: OnData now includes channel it was received on - fixes #2989
This commit is contained in:
parent
52111d1cb8
commit
0a1bfade25
@ -50,6 +50,13 @@ public class KcpTransport : Transport
|
||||
// log statistics for headless servers that can't show them in GUI
|
||||
public bool statisticsLog;
|
||||
|
||||
// translate Kcp <-> Mirror channels
|
||||
static int KcpToMirrorChannel(KcpChannel channel) =>
|
||||
channel == KcpChannel.Reliable ? Channels.Reliable : Channels.Unreliable;
|
||||
|
||||
static KcpChannel MirrorToKcpChannel(int channel) =>
|
||||
channel == Channels.Reliable ? KcpChannel.Reliable : KcpChannel.Unreliable;
|
||||
|
||||
void Awake()
|
||||
{
|
||||
// logging
|
||||
@ -66,18 +73,18 @@ void Awake()
|
||||
client = NonAlloc
|
||||
? new KcpClientNonAlloc(
|
||||
() => OnClientConnected.Invoke(),
|
||||
(message) => OnClientDataReceived.Invoke(message, Channels.Reliable),
|
||||
(message, channel) => OnClientDataReceived.Invoke(message, KcpToMirrorChannel(channel)),
|
||||
() => OnClientDisconnected.Invoke())
|
||||
: new KcpClient(
|
||||
() => OnClientConnected.Invoke(),
|
||||
(message) => OnClientDataReceived.Invoke(message, Channels.Reliable),
|
||||
(message, channel) => OnClientDataReceived.Invoke(message, KcpToMirrorChannel(channel)),
|
||||
() => OnClientDisconnected.Invoke());
|
||||
|
||||
// server
|
||||
server = NonAlloc
|
||||
? new KcpServerNonAlloc(
|
||||
(connectionId) => OnServerConnected.Invoke(connectionId),
|
||||
(connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.Reliable),
|
||||
(connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, KcpToMirrorChannel(channel)),
|
||||
(connectionId) => OnServerDisconnected.Invoke(connectionId),
|
||||
DualMode,
|
||||
NoDelay,
|
||||
@ -89,7 +96,7 @@ void Awake()
|
||||
Timeout)
|
||||
: new KcpServer(
|
||||
(connectionId) => OnServerConnected.Invoke(connectionId),
|
||||
(connectionId, message) => OnServerDataReceived.Invoke(connectionId, message, Channels.Reliable),
|
||||
(connectionId, message, channel) => OnServerDataReceived.Invoke(connectionId, message, KcpToMirrorChannel(channel)),
|
||||
(connectionId) => OnServerDisconnected.Invoke(connectionId),
|
||||
DualMode,
|
||||
NoDelay,
|
||||
@ -126,18 +133,7 @@ public override void ClientConnect(Uri uri)
|
||||
}
|
||||
public override void ClientSend(ArraySegment<byte> segment, int channelId)
|
||||
{
|
||||
// switch to kcp channel.
|
||||
// unreliable or reliable.
|
||||
// default to reliable just to be sure.
|
||||
switch (channelId)
|
||||
{
|
||||
case Channels.Unreliable:
|
||||
client.Send(segment, KcpChannel.Unreliable);
|
||||
break;
|
||||
default:
|
||||
client.Send(segment, KcpChannel.Reliable);
|
||||
break;
|
||||
}
|
||||
client.Send(segment, MirrorToKcpChannel(channelId));
|
||||
}
|
||||
public override void ClientDisconnect() => client.Disconnect();
|
||||
// process incoming in early update
|
||||
@ -185,18 +181,7 @@ public override Uri ServerUri()
|
||||
public override void ServerStart() => server.Start(Port);
|
||||
public override void ServerSend(int connectionId, ArraySegment<byte> segment, int channelId)
|
||||
{
|
||||
// switch to kcp channel.
|
||||
// unreliable or reliable.
|
||||
// default to reliable just to be sure.
|
||||
switch (channelId)
|
||||
{
|
||||
case Channels.Unreliable:
|
||||
server.Send(connectionId, segment, KcpChannel.Unreliable);
|
||||
break;
|
||||
default:
|
||||
server.Send(connectionId, segment, KcpChannel.Reliable);
|
||||
break;
|
||||
}
|
||||
server.Send(connectionId, segment, MirrorToKcpChannel(channelId));
|
||||
}
|
||||
public override void ServerDisconnect(int connectionId) => server.Disconnect(connectionId);
|
||||
public override string ServerGetClientAddress(int connectionId) => server.GetClientAddress(connectionId);
|
||||
@ -227,7 +212,7 @@ public override int GetMaxPacketSize(int channelId = Channels.Reliable)
|
||||
case Channels.Unreliable:
|
||||
return KcpConnection.UnreliableMaxMessageSize;
|
||||
default:
|
||||
return KcpConnection.ReliableMaxMessageSize;
|
||||
return KcpConnection.ReliableMaxMessageSize(ReceiveWindowSize);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,19 @@
|
||||
V1.13 [2021-11-28]
|
||||
- fix: perf: uncork max message size from 144 KB to as much as we want based on
|
||||
receive window size.
|
||||
fixes https://github.com/vis2k/kcp2k/issues/22
|
||||
fixes https://github.com/skywind3000/kcp/pull/291
|
||||
- feature: OnData now includes channel it was received on
|
||||
|
||||
V1.12 [2021-07-16]
|
||||
- where-allocation removed. will be optional in the future.
|
||||
- Tests: don't depend on Unity anymore
|
||||
- fix: #26 - Kcp now catches exception if host couldn't be resolved, and calls
|
||||
OnDisconnected to let the user now.
|
||||
- fix: KcpServer.DualMode is now configurable in the constructor instead of
|
||||
using #if UNITY_SWITCH. makes it run on all other non dual mode platforms too.
|
||||
- fix: where-allocation made optional via virtuals and inheriting
|
||||
KcpServer/Client/Connection NonAlloc classes. fixes a bug where some platforms
|
||||
might not support where-allocation.
|
||||
|
||||
V1.11 rollback [2021-06-01]
|
||||
- perf: Segment MemoryStream initial capacity set to MTU to avoid early runtime
|
||||
|
@ -8,14 +8,14 @@ public class KcpClient
|
||||
{
|
||||
// events
|
||||
public Action OnConnected;
|
||||
public Action<ArraySegment<byte>> OnData;
|
||||
public Action<ArraySegment<byte>, KcpChannel> OnData;
|
||||
public Action OnDisconnected;
|
||||
|
||||
// state
|
||||
public KcpClientConnection connection;
|
||||
public bool connected;
|
||||
|
||||
public KcpClient(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
|
||||
public KcpClient(Action OnConnected, Action<ArraySegment<byte>, KcpChannel> OnData, Action OnDisconnected)
|
||||
{
|
||||
this.OnConnected = OnConnected;
|
||||
this.OnData = OnData;
|
||||
@ -45,10 +45,10 @@ public void Connect(string address, ushort port, bool noDelay, uint interval, in
|
||||
connected = true;
|
||||
OnConnected.Invoke();
|
||||
};
|
||||
connection.OnData = (message) =>
|
||||
connection.OnData = (message, channel) =>
|
||||
{
|
||||
//Log.Debug($"KCP: OnClientData({BitConverter.ToString(message.Array, message.Offset, message.Count)})");
|
||||
OnData.Invoke(message);
|
||||
OnData.Invoke(message, channel);
|
||||
};
|
||||
connection.OnDisconnected = () =>
|
||||
{
|
||||
|
@ -17,7 +17,7 @@ public abstract class KcpConnection
|
||||
KcpState state = KcpState.Disconnected;
|
||||
|
||||
public Action OnAuthenticated;
|
||||
public Action<ArraySegment<byte>> OnData;
|
||||
public Action<ArraySegment<byte>, KcpChannel> OnData;
|
||||
public Action OnDisconnected;
|
||||
|
||||
// Mirror needs a way to stop the kcp message processing while loop
|
||||
@ -43,19 +43,20 @@ public abstract class KcpConnection
|
||||
const int CHANNEL_HEADER_SIZE = 1;
|
||||
|
||||
// reliable channel (= kcp) MaxMessageSize so the outside knows largest
|
||||
// allowed message to send the calculation in Send() is not obvious at
|
||||
// allowed message to send. the calculation in Send() is not obvious at
|
||||
// all, so let's provide the helper here.
|
||||
//
|
||||
// kcp does fragmentation, so max message is way larger than MTU.
|
||||
//
|
||||
// -> runtime MTU changes are disabled: mss is always MTU_DEF-OVERHEAD
|
||||
// -> Send() checks if fragment count < WND_RCV, so we use WND_RCV - 1.
|
||||
// note that Send() checks WND_RCV instead of wnd_rcv which may or
|
||||
// may not be a bug in original kcp. but since it uses the define, we
|
||||
// can use that here too.
|
||||
// -> Send() checks if fragment count < rcv_wnd, so we use rcv_wnd - 1.
|
||||
// NOTE that original kcp has a bug where WND_RCV default is used
|
||||
// instead of configured rcv_wnd, limiting max message size to 144 KB
|
||||
// https://github.com/skywind3000/kcp/pull/291
|
||||
// we fixed this in kcp2k.
|
||||
// -> we add 1 byte KcpHeader enum to each message, so -1
|
||||
//
|
||||
// IMPORTANT: max message is MTU * WND_RCV, in other words it completely
|
||||
// IMPORTANT: max message is MTU * rcv_wnd, in other words it completely
|
||||
// fills the receive window! due to head of line blocking,
|
||||
// all other messages have to wait while a maxed size message
|
||||
// is being delivered.
|
||||
@ -63,7 +64,7 @@ public abstract class KcpConnection
|
||||
// for batching.
|
||||
// => sending UNRELIABLE max message size most of the time is
|
||||
// best for performance (use that one for batching!)
|
||||
public const int ReliableMaxMessageSize = (Kcp.MTU_DEF - Kcp.OVERHEAD - CHANNEL_HEADER_SIZE) * (Kcp.WND_RCV - 1) - 1;
|
||||
public static int ReliableMaxMessageSize(uint rcv_wnd) => (Kcp.MTU_DEF - Kcp.OVERHEAD - CHANNEL_HEADER_SIZE) * ((int)rcv_wnd - 1) - 1;
|
||||
|
||||
// unreliable max message size is simply MTU - channel header size
|
||||
public const int UnreliableMaxMessageSize = Kcp.MTU_DEF - CHANNEL_HEADER_SIZE;
|
||||
@ -71,13 +72,13 @@ public abstract class KcpConnection
|
||||
// buffer to receive kcp's processed messages (avoids allocations).
|
||||
// IMPORTANT: this is for KCP messages. so it needs to be of size:
|
||||
// 1 byte header + MaxMessageSize content
|
||||
byte[] kcpMessageBuffer = new byte[1 + ReliableMaxMessageSize];
|
||||
byte[] kcpMessageBuffer;// = new byte[1 + ReliableMaxMessageSize];
|
||||
|
||||
// send buffer for handing user messages to kcp for processing.
|
||||
// (avoids allocations).
|
||||
// IMPORTANT: needs to be of size:
|
||||
// 1 byte header + MaxMessageSize content
|
||||
byte[] kcpSendBuffer = new byte[1 + ReliableMaxMessageSize];
|
||||
byte[] kcpSendBuffer;// = new byte[1 + ReliableMaxMessageSize];
|
||||
|
||||
// raw send buffer is exactly MTU.
|
||||
byte[] rawSendBuffer = new byte[Kcp.MTU_DEF];
|
||||
@ -143,6 +144,11 @@ protected void SetupKcp(bool noDelay, uint interval = Kcp.INTERVAL, int fastRese
|
||||
// message afterwards.
|
||||
kcp.SetMtu(Kcp.MTU_DEF - CHANNEL_HEADER_SIZE);
|
||||
|
||||
// create message buffers AFTER window size is set
|
||||
// see comments on buffer definition for the "+1" part
|
||||
kcpMessageBuffer = new byte[1 + ReliableMaxMessageSize(receiveWindowSize)];
|
||||
kcpSendBuffer = new byte[1 + ReliableMaxMessageSize(receiveWindowSize)];
|
||||
|
||||
this.timeout = timeout;
|
||||
state = KcpState.Connected;
|
||||
|
||||
@ -328,7 +334,7 @@ void TickIncoming_Authenticated(uint time)
|
||||
if (message.Count > 0)
|
||||
{
|
||||
//Log.Warning($"Kcp recv msg: {BitConverter.ToString(message.Array, message.Offset, message.Count)}");
|
||||
OnData?.Invoke(message);
|
||||
OnData?.Invoke(message, KcpChannel.Reliable);
|
||||
}
|
||||
// empty data = attacker, or something went wrong
|
||||
else
|
||||
@ -489,7 +495,7 @@ public void RawInput(byte[] buffer, int msgLength)
|
||||
if (!paused)
|
||||
{
|
||||
ArraySegment<byte> message = new ArraySegment<byte>(buffer, 1, msgLength - 1);
|
||||
OnData?.Invoke(message);
|
||||
OnData?.Invoke(message, KcpChannel.Unreliable);
|
||||
}
|
||||
|
||||
// set last receive time to avoid timeout.
|
||||
@ -551,7 +557,7 @@ void SendReliable(KcpHeader header, ArraySegment<byte> content)
|
||||
}
|
||||
}
|
||||
// otherwise content is larger than MaxMessageSize. let user know!
|
||||
else Log.Error($"Failed to send reliable message of size {content.Count} because it's larger than ReliableMaxMessageSize={ReliableMaxMessageSize}");
|
||||
else Log.Error($"Failed to send reliable message of size {content.Count} because it's larger than ReliableMaxMessageSize={ReliableMaxMessageSize(kcp.rcv_wnd)}");
|
||||
}
|
||||
|
||||
void SendUnreliable(ArraySegment<byte> message)
|
||||
|
@ -11,7 +11,7 @@ public class KcpServer
|
||||
{
|
||||
// events
|
||||
public Action<int> OnConnected;
|
||||
public Action<int, ArraySegment<byte>> OnData;
|
||||
public Action<int, ArraySegment<byte>, KcpChannel> OnData;
|
||||
public Action<int> OnDisconnected;
|
||||
|
||||
// configuration
|
||||
@ -57,7 +57,7 @@ public class KcpServer
|
||||
public Dictionary<int, KcpServerConnection> connections = new Dictionary<int, KcpServerConnection>();
|
||||
|
||||
public KcpServer(Action<int> OnConnected,
|
||||
Action<int, ArraySegment<byte>> OnData,
|
||||
Action<int, ArraySegment<byte>, KcpChannel> OnData,
|
||||
Action<int> OnDisconnected,
|
||||
bool DualMode,
|
||||
bool NoDelay,
|
||||
@ -226,11 +226,11 @@ public void TickIncoming()
|
||||
// internet.
|
||||
|
||||
// setup data event
|
||||
connection.OnData = (message) =>
|
||||
connection.OnData = (message, channel) =>
|
||||
{
|
||||
// call mirror event
|
||||
//Log.Info($"KCP: OnServerDataReceived({connectionId}, {BitConverter.ToString(message.Array, message.Offset, message.Count)})");
|
||||
OnData.Invoke(connectionId, message);
|
||||
OnData.Invoke(connectionId, message, channel);
|
||||
};
|
||||
|
||||
// setup disconnected event
|
||||
|
@ -6,7 +6,7 @@ namespace kcp2k
|
||||
{
|
||||
public class KcpClientNonAlloc : KcpClient
|
||||
{
|
||||
public KcpClientNonAlloc(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
|
||||
public KcpClientNonAlloc(Action OnConnected, Action<ArraySegment<byte>, KcpChannel> OnData, Action OnDisconnected)
|
||||
: base(OnConnected, OnData, OnDisconnected)
|
||||
{
|
||||
}
|
||||
|
@ -11,7 +11,7 @@ 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)
|
||||
public KcpServerNonAlloc(Action<int> OnConnected, Action<int, ArraySegment<byte>, KcpChannel> 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
|
||||
|
@ -262,10 +262,12 @@ public int Send(byte[] buffer, int offset, int len)
|
||||
if (len <= mss) count = 1;
|
||||
else count = (int)((len + mss - 1) / mss);
|
||||
|
||||
// original kcp uses WND_RCV const even though rcv_wnd is the
|
||||
// runtime variable. may or may not be correct, see also:
|
||||
// see also: https://github.com/skywind3000/kcp/pull/291/files
|
||||
if (count >= WND_RCV) return -2;
|
||||
// original kcp uses WND_RCV const instead of rcv_wnd runtime:
|
||||
// https://github.com/skywind3000/kcp/pull/291/files
|
||||
// which always limits max message size to 144 KB:
|
||||
//if (count >= WND_RCV) return -2;
|
||||
// using configured rcv_wnd uncorks max message size to 'any':
|
||||
if (count >= rcv_wnd) return -2;
|
||||
|
||||
if (count == 0) count = 1;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user