using System;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Security.Permissions;
using WhereAllocation;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: InternalsVisibleTo("kcp2k.Tests")]
[assembly: SecurityPermission(SecurityAction.RequestMinimum, SkipVerification = true)]
[assembly: AssemblyVersion("0.0.0.0")]
[module: UnverifiableCode]
[CompilerGenerated]
[EditorBrowsable(EditorBrowsableState.Never)]
[GeneratedCode("Unity.MonoScriptGenerator.MonoScriptInfoGenerator", null)]
internal class UnitySourceGeneratedAssemblyMonoScriptTypes_v1
{
private struct MonoScriptData
{
public byte[] FilePathsData;
public byte[] TypesData;
public int TotalTypes;
public int TotalFiles;
public bool IsEditorOnly;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static MonoScriptData Get()
{
MonoScriptData result = default(MonoScriptData);
result.FilePathsData = new byte[1087]
{
0, 0, 0, 1, 0, 0, 0, 65, 92, 65,
115, 115, 101, 116, 115, 92, 77, 105, 114, 114,
111, 114, 92, 82, 117, 110, 116, 105, 109, 101,
92, 84, 114, 97, 110, 115, 112, 111, 114, 116,
92, 75, 67, 80, 92, 107, 99, 112, 50, 107,
92, 104, 105, 103, 104, 108, 101, 118, 101, 108,
92, 75, 99, 112, 67, 108, 105, 101, 110, 116,
46, 99, 115, 0, 0, 0, 1, 0, 0, 0,
75, 92, 65, 115, 115, 101, 116, 115, 92, 77,
105, 114, 114, 111, 114, 92, 82, 117, 110, 116,
105, 109, 101, 92, 84, 114, 97, 110, 115, 112,
111, 114, 116, 92, 75, 67, 80, 92, 107, 99,
112, 50, 107, 92, 104, 105, 103, 104, 108, 101,
118, 101, 108, 92, 75, 99, 112, 67, 108, 105,
101, 110, 116, 67, 111, 110, 110, 101, 99, 116,
105, 111, 110, 46, 99, 115, 0, 0, 0, 1,
0, 0, 0, 69, 92, 65, 115, 115, 101, 116,
115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
104, 108, 101, 118, 101, 108, 92, 75, 99, 112,
67, 111, 110, 110, 101, 99, 116, 105, 111, 110,
46, 99, 115, 0, 0, 0, 1, 0, 0, 0,
65, 92, 65, 115, 115, 101, 116, 115, 92, 77,
105, 114, 114, 111, 114, 92, 82, 117, 110, 116,
105, 109, 101, 92, 84, 114, 97, 110, 115, 112,
111, 114, 116, 92, 75, 67, 80, 92, 107, 99,
112, 50, 107, 92, 104, 105, 103, 104, 108, 101,
118, 101, 108, 92, 75, 99, 112, 83, 101, 114,
118, 101, 114, 46, 99, 115, 0, 0, 0, 1,
0, 0, 0, 75, 92, 65, 115, 115, 101, 116,
115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
104, 108, 101, 118, 101, 108, 92, 75, 99, 112,
83, 101, 114, 118, 101, 114, 67, 111, 110, 110,
101, 99, 116, 105, 111, 110, 46, 99, 115, 0,
0, 0, 1, 0, 0, 0, 59, 92, 65, 115,
115, 101, 116, 115, 92, 77, 105, 114, 114, 111,
114, 92, 82, 117, 110, 116, 105, 109, 101, 92,
84, 114, 97, 110, 115, 112, 111, 114, 116, 92,
75, 67, 80, 92, 107, 99, 112, 50, 107, 92,
104, 105, 103, 104, 108, 101, 118, 101, 108, 92,
76, 111, 103, 46, 99, 115, 0, 0, 0, 1,
0, 0, 0, 92, 92, 65, 115, 115, 101, 116,
115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
65, 108, 108, 111, 99, 92, 75, 99, 112, 67,
108, 105, 101, 110, 116, 67, 111, 110, 110, 101,
99, 116, 105, 111, 110, 78, 111, 110, 65, 108,
108, 111, 99, 46, 99, 115, 0, 0, 0, 1,
0, 0, 0, 82, 92, 65, 115, 115, 101, 116,
115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
65, 108, 108, 111, 99, 92, 75, 99, 112, 67,
108, 105, 101, 110, 116, 78, 111, 110, 65, 108,
108, 111, 99, 46, 99, 115, 0, 0, 0, 1,
0, 0, 0, 92, 92, 65, 115, 115, 101, 116,
115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
65, 108, 108, 111, 99, 92, 75, 99, 112, 83,
101, 114, 118, 101, 114, 67, 111, 110, 110, 101,
99, 116, 105, 111, 110, 78, 111, 110, 65, 108,
108, 111, 99, 46, 99, 115, 0, 0, 0, 1,
0, 0, 0, 82, 92, 65, 115, 115, 101, 116,
115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
92, 107, 99, 112, 50, 107, 92, 104, 105, 103,
104, 108, 101, 118, 101, 108, 92, 78, 111, 110,
65, 108, 108, 111, 99, 92, 75, 99, 112, 83,
101, 114, 118, 101, 114, 78, 111, 110, 65, 108,
108, 111, 99, 46, 99, 115, 0, 0, 0, 2,
0, 0, 0, 53, 92, 65, 115, 115, 101, 116,
115, 92, 77, 105, 114, 114, 111, 114, 92, 82,
117, 110, 116, 105, 109, 101, 92, 84, 114, 97,
110, 115, 112, 111, 114, 116, 92, 75, 67, 80,
92, 107, 99, 112, 50, 107, 92, 107, 99, 112,
92, 75, 99, 112, 46, 99, 115, 0, 0, 0,
1, 0, 0, 0, 54, 92, 65, 115, 115, 101,
116, 115, 92, 77, 105, 114, 114, 111, 114, 92,
82, 117, 110, 116, 105, 109, 101, 92, 84, 114,
97, 110, 115, 112, 111, 114, 116, 92, 75, 67,
80, 92, 107, 99, 112, 50, 107, 92, 107, 99,
112, 92, 80, 111, 111, 108, 46, 99, 115, 0,
0, 0, 1, 0, 0, 0, 57, 92, 65, 115,
115, 101, 116, 115, 92, 77, 105, 114, 114, 111,
114, 92, 82, 117, 110, 116, 105, 109, 101, 92,
84, 114, 97, 110, 115, 112, 111, 114, 116, 92,
75, 67, 80, 92, 107, 99, 112, 50, 107, 92,
107, 99, 112, 92, 83, 101, 103, 109, 101, 110,
116, 46, 99, 115, 0, 0, 0, 1, 0, 0,
0, 55, 92, 65, 115, 115, 101, 116, 115, 92,
77, 105, 114, 114, 111, 114, 92, 82, 117, 110,
116, 105, 109, 101, 92, 84, 114, 97, 110, 115,
112, 111, 114, 116, 92, 75, 67, 80, 92, 107,
99, 112, 50, 107, 92, 107, 99, 112, 92, 85,
116, 105, 108, 115, 46, 99, 115
};
result.TypesData = new byte[355]
{
0, 0, 0, 0, 15, 107, 99, 112, 50, 107,
124, 75, 99, 112, 67, 108, 105, 101, 110, 116,
0, 0, 0, 0, 25, 107, 99, 112, 50, 107,
124, 75, 99, 112, 67, 108, 105, 101, 110, 116,
67, 111, 110, 110, 101, 99, 116, 105, 111, 110,
0, 0, 0, 0, 19, 107, 99, 112, 50, 107,
124, 75, 99, 112, 67, 111, 110, 110, 101, 99,
116, 105, 111, 110, 0, 0, 0, 0, 15, 107,
99, 112, 50, 107, 124, 75, 99, 112, 83, 101,
114, 118, 101, 114, 0, 0, 0, 0, 25, 107,
99, 112, 50, 107, 124, 75, 99, 112, 83, 101,
114, 118, 101, 114, 67, 111, 110, 110, 101, 99,
116, 105, 111, 110, 0, 0, 0, 0, 9, 107,
99, 112, 50, 107, 124, 76, 111, 103, 0, 0,
0, 0, 33, 107, 99, 112, 50, 107, 124, 75,
99, 112, 67, 108, 105, 101, 110, 116, 67, 111,
110, 110, 101, 99, 116, 105, 111, 110, 78, 111,
110, 65, 108, 108, 111, 99, 0, 0, 0, 0,
23, 107, 99, 112, 50, 107, 124, 75, 99, 112,
67, 108, 105, 101, 110, 116, 78, 111, 110, 65,
108, 108, 111, 99, 0, 0, 0, 0, 33, 107,
99, 112, 50, 107, 124, 75, 99, 112, 83, 101,
114, 118, 101, 114, 67, 111, 110, 110, 101, 99,
116, 105, 111, 110, 78, 111, 110, 65, 108, 108,
111, 99, 0, 0, 0, 0, 23, 107, 99, 112,
50, 107, 124, 75, 99, 112, 83, 101, 114, 118,
101, 114, 78, 111, 110, 65, 108, 108, 111, 99,
0, 0, 0, 0, 9, 107, 99, 112, 50, 107,
124, 75, 99, 112, 0, 0, 0, 0, 17, 107,
99, 112, 50, 107, 46, 75, 99, 112, 124, 65,
99, 107, 73, 116, 101, 109, 0, 0, 0, 0,
10, 107, 99, 112, 50, 107, 124, 80, 111, 111,
108, 0, 0, 0, 0, 13, 107, 99, 112, 50,
107, 124, 83, 101, 103, 109, 101, 110, 116, 0,
0, 0, 0, 11, 107, 99, 112, 50, 107, 124,
85, 116, 105, 108, 115
};
result.TotalFiles = 14;
result.TotalTypes = 15;
result.IsEditorOnly = false;
return result;
}
}
namespace kcp2k;
public enum KcpChannel : byte
{
Reliable = 1,
Unreliable
}
public class KcpClient
{
public Action OnConnected;
public Action<ArraySegment<byte>> OnData;
public Action OnDisconnected;
public KcpClientConnection connection;
public bool connected;
public KcpClient(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
{
this.OnConnected = OnConnected;
this.OnData = OnData;
this.OnDisconnected = OnDisconnected;
}
protected virtual KcpClientConnection CreateConnection()
{
return new KcpClientConnection();
}
public void Connect(string address, ushort port, bool noDelay, uint interval, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
{
if (connected)
{
Log.Warning("KCP: client already connected!");
return;
}
connection = CreateConnection();
connection.OnAuthenticated = delegate
{
Log.Info("KCP: OnClientConnected");
connected = true;
OnConnected();
};
connection.OnData = delegate(ArraySegment<byte> message)
{
OnData(message);
};
connection.OnDisconnected = delegate
{
Log.Info("KCP: OnClientDisconnected");
connected = false;
connection = null;
OnDisconnected();
};
connection.Connect(address, port, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
}
public void Send(ArraySegment<byte> segment, KcpChannel channel)
{
if (connected)
{
connection.SendData(segment, channel);
}
else
{
Log.Warning("KCP: can't send because client not connected!");
}
}
public void Disconnect()
{
if (connected)
{
connection?.Disconnect();
}
}
public void TickIncoming()
{
connection?.RawReceive();
connection?.TickIncoming();
}
public void TickOutgoing()
{
connection?.TickOutgoing();
}
public void Tick()
{
TickIncoming();
TickOutgoing();
}
public void Pause()
{
connection?.Pause();
}
public void Unpause()
{
connection?.Unpause();
}
}
public class KcpClientConnection : KcpConnection
{
private readonly byte[] rawReceiveBuffer = new byte[1200];
public static bool ResolveHostname(string hostname, out IPAddress[] addresses)
{
try
{
addresses = Dns.GetHostAddresses(hostname);
return addresses.Length >= 1;
}
catch (SocketException)
{
Log.Info("Failed to resolve host: " + hostname);
addresses = null;
return false;
}
}
protected virtual void CreateRemoteEndPoint(IPAddress[] addresses, ushort port)
{
remoteEndPoint = new IPEndPoint(addresses[0], port);
}
protected virtual int ReceiveFrom(byte[] buffer)
{
return socket.ReceiveFrom(buffer, ref remoteEndPoint);
}
public void Connect(string host, ushort port, bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
{
Log.Info($"KcpClient: connect to {host}:{port}");
if (ResolveHostname(host, out var addresses))
{
CreateRemoteEndPoint(addresses, port);
socket = new Socket(remoteEndPoint.AddressFamily, SocketType.Dgram, ProtocolType.Udp);
socket.Connect(remoteEndPoint);
SetupKcp(noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
SendHandshake();
RawReceive();
}
else
{
OnDisconnected();
}
}
public void RawReceive()
{
try
{
if (socket == null)
{
return;
}
while (socket.Poll(0, SelectMode.SelectRead))
{
int num = ReceiveFrom(rawReceiveBuffer);
if (num <= rawReceiveBuffer.Length)
{
RawInput(rawReceiveBuffer, num);
continue;
}
Log.Error($"KCP ClientConnection: message of size {num} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting.");
Disconnect();
}
}
catch (SocketException)
{
}
}
protected override void Dispose()
{
socket.Close();
socket = null;
}
protected override void RawSend(byte[] data, int length)
{
socket.Send(data, length, SocketFlags.None);
}
}
internal enum KcpState
{
Connected,
Authenticated,
Disconnected
}
public abstract class KcpConnection
{
protected Socket socket;
protected EndPoint remoteEndPoint;
internal Kcp kcp;
private KcpState state = KcpState.Disconnected;
public Action OnAuthenticated;
public Action<ArraySegment<byte>> OnData;
public Action OnDisconnected;
private bool paused;
public const int DEFAULT_TIMEOUT = 10000;
public int timeout = 10000;
private uint lastReceiveTime;
private readonly Stopwatch refTime = new Stopwatch();
private const int CHANNEL_HEADER_SIZE = 1;
public const int ReliableMaxMessageSize = 149224;
public const int UnreliableMaxMessageSize = 1199;
private byte[] kcpMessageBuffer = new byte[149225];
private byte[] kcpSendBuffer = new byte[149225];
private byte[] rawSendBuffer = new byte[1200];
public const int PING_INTERVAL = 1000;
private uint lastPingTime;
internal const int QueueDisconnectThreshold = 10000;
public int SendQueueCount => kcp.snd_queue.Count;
public int ReceiveQueueCount => kcp.rcv_queue.Count;
public int SendBufferCount => kcp.snd_buf.Count;
public int ReceiveBufferCount => kcp.rcv_buf.Count;
public uint MaxSendRate => kcp.snd_wnd * kcp.mtu * 1000 / kcp.interval;
public uint MaxReceiveRate => kcp.rcv_wnd * kcp.mtu * 1000 / kcp.interval;
protected void SetupKcp(bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
{
kcp = new Kcp(0u, RawSendReliable);
kcp.SetNoDelay(noDelay ? 1u : 0u, interval, fastResend, !congestionWindow);
kcp.SetWindowSize(sendWindowSize, receiveWindowSize);
kcp.SetMtu(1199u);
this.timeout = timeout;
state = KcpState.Connected;
refTime.Start();
}
private void HandleTimeout(uint time)
{
if (time >= lastReceiveTime + timeout)
{
Log.Warning($"KCP: Connection timed out after not receiving any message for {timeout}ms. Disconnecting.");
Disconnect();
}
}
private void HandleDeadLink()
{
if (kcp.state == -1)
{
Log.Warning("KCP Connection dead_link detected. Disconnecting.");
Disconnect();
}
}
private void HandlePing(uint time)
{
if (time >= lastPingTime + 1000)
{
SendPing();
lastPingTime = time;
}
}
private void HandleChoked()
{
int num = kcp.rcv_queue.Count + kcp.snd_queue.Count + kcp.rcv_buf.Count + kcp.snd_buf.Count;
if (num >= 10000)
{
Log.Warning("KCP: disconnecting connection because it can't process data fast enough.\n" + $"Queue total {num}>{10000}. rcv_queue={kcp.rcv_queue.Count} snd_queue={kcp.snd_queue.Count} rcv_buf={kcp.rcv_buf.Count} snd_buf={kcp.snd_buf.Count}\n" + "* Try to Enable NoDelay, decrease INTERVAL, disable Congestion Window (= enable NOCWND!), increase SEND/RECV WINDOW or compress data.\n* Or perhaps the network is simply too slow on our end, or on the other end.\n");
kcp.snd_queue.Clear();
Disconnect();
}
}
private bool ReceiveNextReliable(out KcpHeader header, out ArraySegment<byte> message)
{
int num = kcp.PeekSize();
if (num > 0)
{
if (num <= kcpMessageBuffer.Length)
{
int num2 = kcp.Receive(kcpMessageBuffer, num);
if (num2 >= 0)
{
header = (KcpHeader)kcpMessageBuffer[0];
message = new ArraySegment<byte>(kcpMessageBuffer, 1, num - 1);
lastReceiveTime = (uint)refTime.ElapsedMilliseconds;
return true;
}
Log.Warning($"Receive failed with error={num2}. closing connection.");
Disconnect();
}
else
{
Log.Warning($"KCP: possible allocation attack for msgSize {num} > buffer {kcpMessageBuffer.Length}. Disconnecting the connection.");
Disconnect();
}
}
message = default(ArraySegment<byte>);
header = KcpHeader.Disconnect;
return false;
}
private void TickIncoming_Connected(uint time)
{
HandleTimeout(time);
HandleDeadLink();
HandlePing(time);
HandleChoked();
if (ReceiveNextReliable(out var header, out var _))
{
switch (header)
{
case KcpHeader.Handshake:
Log.Info("KCP: received handshake");
state = KcpState.Authenticated;
OnAuthenticated?.Invoke();
break;
case KcpHeader.Data:
case KcpHeader.Disconnect:
Log.Warning($"KCP: received invalid header {header} while Connected. Disconnecting the connection.");
Disconnect();
break;
case KcpHeader.Ping:
break;
}
}
}
private void TickIncoming_Authenticated(uint time)
{
HandleTimeout(time);
HandleDeadLink();
HandlePing(time);
HandleChoked();
KcpHeader header;
ArraySegment<byte> message;
while (!paused && ReceiveNextReliable(out header, out message))
{
switch (header)
{
case KcpHeader.Handshake:
Log.Warning($"KCP: received invalid header {header} while Authenticated. Disconnecting the connection.");
Disconnect();
break;
case KcpHeader.Data:
if (message.Count > 0)
{
OnData?.Invoke(message);
break;
}
Log.Warning("KCP: received empty Data message while Authenticated. Disconnecting the connection.");
Disconnect();
break;
case KcpHeader.Disconnect:
Log.Info("KCP: received disconnect message");
Disconnect();
break;
}
}
}
public void TickIncoming()
{
uint time = (uint)refTime.ElapsedMilliseconds;
try
{
switch (state)
{
case KcpState.Connected:
TickIncoming_Connected(time);
break;
case KcpState.Authenticated:
TickIncoming_Authenticated(time);
break;
case KcpState.Disconnected:
break;
}
}
catch (SocketException arg)
{
Log.Info($"KCP Connection: Disconnecting because {arg}. This is fine.");
Disconnect();
}
catch (ObjectDisposedException arg2)
{
Log.Info($"KCP Connection: Disconnecting because {arg2}. This is fine.");
Disconnect();
}
catch (Exception ex)
{
Log.Error(ex.ToString());
Disconnect();
}
}
public void TickOutgoing()
{
uint currentTimeMilliSeconds = (uint)refTime.ElapsedMilliseconds;
try
{
switch (state)
{
case KcpState.Connected:
case KcpState.Authenticated:
kcp.Update(currentTimeMilliSeconds);
break;
}
}
catch (SocketException arg)
{
Log.Info($"KCP Connection: Disconnecting because {arg}. This is fine.");
Disconnect();
}
catch (ObjectDisposedException arg2)
{
Log.Info($"KCP Connection: Disconnecting because {arg2}. This is fine.");
Disconnect();
}
catch (Exception ex)
{
Log.Error(ex.ToString());
Disconnect();
}
}
public void RawInput(byte[] buffer, int msgLength)
{
if (msgLength <= 0)
{
return;
}
byte b = buffer[0];
switch (b)
{
case 1:
{
int num = kcp.Input(buffer, 1, msgLength - 1);
if (num != 0)
{
Log.Warning($"Input failed with error={num} for buffer with length={msgLength - 1}");
}
break;
}
case 2:
if (state == KcpState.Authenticated)
{
if (!paused)
{
ArraySegment<byte> obj = new ArraySegment<byte>(buffer, 1, msgLength - 1);
OnData?.Invoke(obj);
}
lastReceiveTime = (uint)refTime.ElapsedMilliseconds;
}
else
{
Log.Warning($"KCP: received unreliable message in state {state}. Disconnecting the connection.");
Disconnect();
}
break;
default:
Log.Info($"Disconnecting connection because of invalid channel header: {b}");
Disconnect();
break;
}
}
protected abstract void RawSend(byte[] data, int length);
private void RawSendReliable(byte[] data, int length)
{
rawSendBuffer[0] = 1;
Buffer.BlockCopy(data, 0, rawSendBuffer, 1, length);
RawSend(rawSendBuffer, length + 1);
}
private void SendReliable(KcpHeader header, ArraySegment<byte> content)
{
if (1 + content.Count <= kcpSendBuffer.Length)
{
kcpSendBuffer[0] = (byte)header;
if (content.Count > 0)
{
Buffer.BlockCopy(content.Array, content.Offset, kcpSendBuffer, 1, content.Count);
}
int num = kcp.Send(kcpSendBuffer, 0, 1 + content.Count);
if (num < 0)
{
Log.Warning($"Send failed with error={num} for content with length={content.Count}");
}
}
else
{
Log.Error($"Failed to send reliable message of size {content.Count} because it's larger than ReliableMaxMessageSize={149224}");
}
}
private void SendUnreliable(ArraySegment<byte> message)
{
if (message.Count <= 1199)
{
rawSendBuffer[0] = 2;
Buffer.BlockCopy(message.Array, 0, rawSendBuffer, 1, message.Count);
RawSend(rawSendBuffer, message.Count + 1);
}
else
{
Log.Error($"Failed to send unreliable message of size {message.Count} because it's larger than UnreliableMaxMessageSize={1199}");
}
}
public void SendHandshake()
{
Log.Info("KcpConnection: sending Handshake to other end!");
SendReliable(KcpHeader.Handshake, default(ArraySegment<byte>));
}
public void SendData(ArraySegment<byte> data, KcpChannel channel)
{
if (data.Count == 0)
{
Log.Warning("KcpConnection: tried sending empty message. This should never happen. Disconnecting.");
Disconnect();
return;
}
switch (channel)
{
case KcpChannel.Reliable:
SendReliable(KcpHeader.Data, data);
break;
case KcpChannel.Unreliable:
SendUnreliable(data);
break;
}
}
private void SendPing()
{
SendReliable(KcpHeader.Ping, default(ArraySegment<byte>));
}
private void SendDisconnect()
{
SendReliable(KcpHeader.Disconnect, default(ArraySegment<byte>));
}
protected virtual void Dispose()
{
}
public void Disconnect()
{
if (state == KcpState.Disconnected)
{
return;
}
if (socket.Connected)
{
try
{
SendDisconnect();
kcp.Flush();
}
catch (SocketException)
{
}
catch (ObjectDisposedException)
{
}
}
Log.Info("KCP Connection: Disconnected.");
state = KcpState.Disconnected;
OnDisconnected?.Invoke();
}
public EndPoint GetRemoteEndPoint()
{
return remoteEndPoint;
}
public void Pause()
{
paused = true;
}
public void Unpause()
{
paused = false;
lastReceiveTime = (uint)refTime.ElapsedMilliseconds;
}
}
public enum KcpHeader : byte
{
Handshake = 1,
Ping,
Data,
Disconnect
}
public class KcpServer
{
public Action<int> OnConnected;
public Action<int, ArraySegment<byte>> OnData;
public Action<int> OnDisconnected;
public bool DualMode;
public bool NoDelay;
public uint Interval;
public int FastResend;
public bool CongestionWindow;
public uint SendWindowSize;
public uint ReceiveWindowSize;
public int Timeout;
protected Socket socket;
private EndPoint newClientEP;
private readonly byte[] rawReceiveBuffer = new byte[1200];
public Dictionary<int, KcpServerConnection> connections = new Dictionary<int, KcpServerConnection>();
private HashSet<int> connectionsToRemove = new HashSet<int>();
public KcpServer(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 = 32u, uint ReceiveWindowSize = 128u, int Timeout = 10000)
{
this.OnConnected = OnConnected;
this.OnData = OnData;
this.OnDisconnected = OnDisconnected;
this.DualMode = DualMode;
this.NoDelay = NoDelay;
this.Interval = Interval;
this.FastResend = FastResend;
this.CongestionWindow = CongestionWindow;
this.SendWindowSize = SendWindowSize;
this.ReceiveWindowSize = ReceiveWindowSize;
this.Timeout = Timeout;
newClientEP = (DualMode ? new IPEndPoint(IPAddress.IPv6Any, 0) : new IPEndPoint(IPAddress.Any, 0));
}
public bool IsActive()
{
return socket != null;
}
public void Start(ushort port)
{
if (socket != null)
{
Log.Warning("KCP: server already started!");
}
if (DualMode)
{
socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Dgram, ProtocolType.Udp);
socket.DualMode = true;
socket.Bind(new IPEndPoint(IPAddress.IPv6Any, port));
}
else
{
socket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(IPAddress.Any, port));
}
}
public void Send(int connectionId, ArraySegment<byte> segment, KcpChannel channel)
{
if (connections.TryGetValue(connectionId, out var value))
{
value.SendData(segment, channel);
}
}
public void Disconnect(int connectionId)
{
if (connections.TryGetValue(connectionId, out var value))
{
value.Disconnect();
}
}
public string GetClientAddress(int connectionId)
{
if (connections.TryGetValue(connectionId, out var value))
{
return (value.GetRemoteEndPoint() as IPEndPoint).Address.ToString();
}
return "";
}
protected virtual int ReceiveFrom(byte[] buffer, out int connectionHash)
{
int result = socket.ReceiveFrom(buffer, 0, buffer.Length, SocketFlags.None, ref newClientEP);
connectionHash = newClientEP.GetHashCode();
return result;
}
protected virtual KcpServerConnection CreateConnection()
{
return new KcpServerConnection(socket, newClientEP, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);
}
public void TickIncoming()
{
while (socket != null && socket.Poll(0, SelectMode.SelectRead))
{
try
{
int connectionId;
int num = ReceiveFrom(rawReceiveBuffer, out connectionId);
if (num <= rawReceiveBuffer.Length)
{
if (!connections.TryGetValue(connectionId, out var connection))
{
connection = CreateConnection();
connection.OnAuthenticated = delegate
{
connection.SendHandshake();
connections.Add(connectionId, connection);
Log.Info($"KCP: server added connection({connectionId})");
connection.OnData = delegate(ArraySegment<byte> message)
{
OnData(connectionId, message);
};
connection.OnDisconnected = delegate
{
connectionsToRemove.Add(connectionId);
Log.Info($"KCP: OnServerDisconnected({connectionId})");
OnDisconnected(connectionId);
};
Log.Info($"KCP: OnServerConnected({connectionId})");
OnConnected(connectionId);
};
connection.RawInput(rawReceiveBuffer, num);
connection.TickIncoming();
}
else
{
connection.RawInput(rawReceiveBuffer, num);
}
}
else
{
Log.Error($"KCP Server: message of size {num} does not fit into buffer of size {rawReceiveBuffer.Length}. The excess was silently dropped. Disconnecting connectionId={connectionId}.");
Disconnect(connectionId);
}
}
catch (SocketException)
{
}
}
foreach (KcpServerConnection value in connections.Values)
{
value.TickIncoming();
}
foreach (int item in connectionsToRemove)
{
connections.Remove(item);
}
connectionsToRemove.Clear();
}
public void TickOutgoing()
{
foreach (KcpServerConnection value in connections.Values)
{
value.TickOutgoing();
}
}
public void Tick()
{
TickIncoming();
TickOutgoing();
}
public void Stop()
{
socket?.Close();
socket = null;
}
public void Pause()
{
foreach (KcpServerConnection value in connections.Values)
{
value.Pause();
}
}
public void Unpause()
{
foreach (KcpServerConnection value in connections.Values)
{
value.Unpause();
}
}
}
public class KcpServerConnection : KcpConnection
{
public KcpServerConnection(Socket socket, EndPoint remoteEndPoint, bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
{
base.socket = socket;
base.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);
}
}
public static class Log
{
public static Action<string> Info = Console.WriteLine;
public static Action<string> Warning = Console.WriteLine;
public static Action<string> Error = Console.Error.WriteLine;
}
public class KcpClientConnectionNonAlloc : KcpClientConnection
{
private IPEndPointNonAlloc reusableEP;
protected override void CreateRemoteEndPoint(IPAddress[] addresses, ushort port)
{
//IL_0005: Unknown result type (might be due to invalid IL or missing references)
//IL_000f: Expected O, but got Unknown
reusableEP = new IPEndPointNonAlloc(addresses[0], (int)port);
base.CreateRemoteEndPoint(addresses, port);
}
protected override int ReceiveFrom(byte[] buffer)
{
return Extensions.ReceiveFrom_NonAlloc(socket, buffer, reusableEP);
}
}
public class KcpClientNonAlloc : KcpClient
{
public KcpClientNonAlloc(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
: base(OnConnected, OnData, OnDisconnected)
{
}
protected override KcpClientConnection CreateConnection()
{
return new KcpClientConnectionNonAlloc();
}
}
public class KcpServerConnectionNonAlloc : KcpServerConnection
{
private IPEndPointNonAlloc reusableSendEndPoint;
public KcpServerConnectionNonAlloc(Socket socket, EndPoint remoteEndpoint, IPEndPointNonAlloc reusableSendEndPoint, bool noDelay, uint interval = 100u, int fastResend = 0, bool congestionWindow = true, uint sendWindowSize = 32u, uint receiveWindowSize = 128u, int timeout = 10000)
: base(socket, remoteEndpoint, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout)
{
this.reusableSendEndPoint = reusableSendEndPoint;
}
protected override void RawSend(byte[] data, int length)
{
Extensions.SendTo_NonAlloc(socket, data, 0, length, SocketFlags.None, reusableSendEndPoint);
}
}
public class KcpServerNonAlloc : KcpServer
{
private 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 = 32u, uint ReceiveWindowSize = 128u, int Timeout = 10000)
: base(OnConnected, OnData, OnDisconnected, DualMode, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout)
{
//IL_0031: Unknown result type (might be due to invalid IL or missing references)
//IL_0024: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
reusableClientEP = (DualMode ? new IPEndPointNonAlloc(IPAddress.IPv6Any, 0) : new IPEndPointNonAlloc(IPAddress.Any, 0));
}
protected override int ReceiveFrom(byte[] buffer, out int connectionHash)
{
int result = Extensions.ReceiveFrom_NonAlloc(socket, buffer, 0, buffer.Length, SocketFlags.None, reusableClientEP);
SocketAddress temp = reusableClientEP.temp;
connectionHash = temp.GetHashCode();
return result;
}
protected override KcpServerConnection CreateConnection()
{
//IL_0018: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
IPEndPoint iPEndPoint = reusableClientEP.DeepCopyIPEndPoint();
IPEndPointNonAlloc reusableSendEndPoint = new IPEndPointNonAlloc(iPEndPoint.Address, iPEndPoint.Port);
return new KcpServerConnectionNonAlloc(socket, iPEndPoint, reusableSendEndPoint, NoDelay, Interval, FastResend, CongestionWindow, SendWindowSize, ReceiveWindowSize, Timeout);
}
}
public class Kcp
{
internal struct AckItem
{
internal uint serialNumber;
internal uint timestamp;
}
public const int RTO_NDL = 30;
public const int RTO_MIN = 100;
public const int RTO_DEF = 200;
public const int RTO_MAX = 60000;
public const int CMD_PUSH = 81;
public const int CMD_ACK = 82;
public const int CMD_WASK = 83;
public const int CMD_WINS = 84;
public const int ASK_SEND = 1;
public const int ASK_TELL = 2;
public const int WND_SND = 32;
public const int WND_RCV = 128;
public const int MTU_DEF = 1200;
public const int ACK_FAST = 3;
public const int INTERVAL = 100;
public const int OVERHEAD = 24;
public const int DEADLINK = 20;
public const int THRESH_INIT = 2;
public const int THRESH_MIN = 2;
public const int PROBE_INIT = 7000;
public const int PROBE_LIMIT = 120000;
public const int FASTACK_LIMIT = 5;
internal int state;
private readonly uint conv;
internal uint mtu;
internal uint mss;
internal uint snd_una;
internal uint snd_nxt;
internal uint rcv_nxt;
internal uint ssthresh;
internal int rx_rttval;
internal int rx_srtt;
internal int rx_rto;
internal int rx_minrto;
internal uint snd_wnd;
internal uint rcv_wnd;
internal uint rmt_wnd;
internal uint cwnd;
internal uint probe;
internal uint interval;
internal uint ts_flush;
internal uint xmit;
internal uint nodelay;
internal bool updated;
internal uint ts_probe;
internal uint probe_wait;
internal uint dead_link;
internal uint incr;
internal uint current;
internal int fastresend;
internal int fastlimit;
internal bool nocwnd;
internal readonly Queue<Segment> snd_queue = new Queue<Segment>(16);
internal readonly Queue<Segment> rcv_queue = new Queue<Segment>(16);
internal readonly List<Segment> snd_buf = new List<Segment>(16);
internal readonly List<Segment> rcv_buf = new List<Segment>(16);
internal readonly List<AckItem> acklist = new List<AckItem>(16);
internal byte[] buffer;
private readonly Action<byte[], int> output;
private readonly Pool<Segment> SegmentPool = new Pool<Segment>(() => new Segment(), delegate(Segment segment)
{
segment.Reset();
}, 32);
public int WaitSnd => snd_buf.Count + snd_queue.Count;
public Kcp(uint conv, Action<byte[], int> output)
{
this.conv = conv;
this.output = output;
snd_wnd = 32u;
rcv_wnd = 128u;
rmt_wnd = 128u;
mtu = 1200u;
mss = mtu - 24;
rx_rto = 200;
rx_minrto = 100;
interval = 100u;
ts_flush = 100u;
ssthresh = 2u;
fastlimit = 5;
dead_link = 20u;
buffer = new byte[(mtu + 24) * 3];
}
private Segment SegmentNew()
{
return SegmentPool.Take();
}
private void SegmentDelete(Segment seg)
{
SegmentPool.Return(seg);
}
public int Receive(byte[] buffer, int len)
{
if (len < 0)
{
throw new NotSupportedException("Receive ispeek for negative len is not supported!");
}
if (rcv_queue.Count == 0)
{
return -1;
}
if (len < 0)
{
len = -len;
}
int num = PeekSize();
if (num < 0)
{
return -2;
}
if (num > len)
{
return -3;
}
bool flag = rcv_queue.Count >= rcv_wnd;
int num2 = 0;
len = 0;
while (rcv_queue.Count > 0)
{
Segment segment = rcv_queue.Dequeue();
Buffer.BlockCopy(segment.data.GetBuffer(), 0, buffer, num2, (int)segment.data.Position);
num2 += (int)segment.data.Position;
len += (int)segment.data.Position;
uint frg = segment.frg;
SegmentDelete(segment);
if (frg == 0)
{
break;
}
}
int num3 = 0;
foreach (Segment item in rcv_buf)
{
if (item.sn == rcv_nxt && rcv_queue.Count < rcv_wnd)
{
num3++;
rcv_queue.Enqueue(item);
rcv_nxt++;
continue;
}
break;
}
rcv_buf.RemoveRange(0, num3);
if (rcv_queue.Count < rcv_wnd && flag)
{
probe |= 2u;
}
return len;
}
public int PeekSize()
{
int num = 0;
if (rcv_queue.Count == 0)
{
return -1;
}
Segment segment = rcv_queue.Peek();
if (segment.frg == 0)
{
return (int)segment.data.Position;
}
if (rcv_queue.Count < segment.frg + 1)
{
return -1;
}
foreach (Segment item in rcv_queue)
{
num += (int)item.data.Position;
if (item.frg == 0)
{
break;
}
}
return num;
}
public int Send(byte[] buffer, int offset, int len)
{
if (len < 0)
{
return -1;
}
int num = (int)((len <= mss) ? 1 : ((len + mss - 1) / mss));
if (num >= 128)
{
return -2;
}
if (num == 0)
{
num = 1;
}
for (int i = 0; i < num; i++)
{
int num2 = ((len > (int)mss) ? ((int)mss) : len);
Segment segment = SegmentNew();
if (len > 0)
{
segment.data.Write(buffer, offset, num2);
}
segment.frg = (byte)(num - i - 1);
snd_queue.Enqueue(segment);
offset += num2;
len -= num2;
}
return 0;
}
private void UpdateAck(int rtt)
{
if (rx_srtt == 0)
{
rx_srtt = rtt;
rx_rttval = rtt / 2;
}
else
{
int num = rtt - rx_srtt;
if (num < 0)
{
num = -num;
}
rx_rttval = (3 * rx_rttval + num) / 4;
rx_srtt = (7 * rx_srtt + rtt) / 8;
if (rx_srtt < 1)
{
rx_srtt = 1;
}
}
int value = rx_srtt + Math.Max((int)interval, 4 * rx_rttval);
rx_rto = Utils.Clamp(value, rx_minrto, 60000);
}
internal void ShrinkBuf()
{
if (snd_buf.Count > 0)
{
Segment segment = snd_buf[0];
snd_una = segment.sn;
}
else
{
snd_una = snd_nxt;
}
}
internal void ParseAck(uint sn)
{
if (Utils.TimeDiff(sn, snd_una) < 0 || Utils.TimeDiff(sn, snd_nxt) >= 0)
{
return;
}
for (int i = 0; i < snd_buf.Count; i++)
{
Segment segment = snd_buf[i];
if (sn == segment.sn)
{
snd_buf.RemoveAt(i);
SegmentDelete(segment);
break;
}
if (Utils.TimeDiff(sn, segment.sn) < 0)
{
break;
}
}
}
private void ParseUna(uint una)
{
int num = 0;
foreach (Segment item in snd_buf)
{
if (Utils.TimeDiff(una, item.sn) > 0)
{
num++;
SegmentDelete(item);
continue;
}
break;
}
snd_buf.RemoveRange(0, num);
}
private void ParseFastack(uint sn, uint ts)
{
if (Utils.TimeDiff(sn, snd_una) < 0 || Utils.TimeDiff(sn, snd_nxt) >= 0)
{
return;
}
foreach (Segment item in snd_buf)
{
if (Utils.TimeDiff(sn, item.sn) < 0)
{
break;
}
if (sn != item.sn)
{
item.fastack++;
}
}
}
private void AckPush(uint sn, uint ts)
{
acklist.Add(new AckItem
{
serialNumber = sn,
timestamp = ts
});
}
private void ParseData(Segment newseg)
{
uint sn = newseg.sn;
if (Utils.TimeDiff(sn, rcv_nxt + rcv_wnd) >= 0 || Utils.TimeDiff(sn, rcv_nxt) < 0)
{
SegmentDelete(newseg);
return;
}
InsertSegmentInReceiveBuffer(newseg);
MoveReceiveBufferDataToReceiveQueue();
}
internal void InsertSegmentInReceiveBuffer(Segment newseg)
{
bool flag = false;
int num;
for (num = rcv_buf.Count - 1; num >= 0; num--)
{
Segment segment = rcv_buf[num];
if (segment.sn == newseg.sn)
{
flag = true;
break;
}
if (Utils.TimeDiff(newseg.sn, segment.sn) > 0)
{
break;
}
}
if (!flag)
{
rcv_buf.Insert(num + 1, newseg);
}
else
{
SegmentDelete(newseg);
}
}
private void MoveReceiveBufferDataToReceiveQueue()
{
int num = 0;
foreach (Segment item in rcv_buf)
{
if (item.sn == rcv_nxt && rcv_queue.Count < rcv_wnd)
{
num++;
rcv_queue.Enqueue(item);
rcv_nxt++;
continue;
}
break;
}
rcv_buf.RemoveRange(0, num);
}
public int Input(byte[] data, int offset, int size)
{
uint earlier = snd_una;
uint num = 0u;
uint ts = 0u;
int num2 = 0;
if (data == null || size < 24)
{
return -1;
}
while (true)
{
uint c = 0u;
uint c2 = 0u;
uint c3 = 0u;
uint c4 = 0u;
uint c5 = 0u;
ushort c6 = 0;
byte c7 = 0;
byte c8 = 0;
if (size < 24)
{
break;
}
offset += Utils.Decode32U(data, offset, ref c5);
if (c5 != conv)
{
return -1;
}
offset += Utils.Decode8u(data, offset, ref c7);
offset += Utils.Decode8u(data, offset, ref c8);
offset += Utils.Decode16U(data, offset, ref c6);
offset += Utils.Decode32U(data, offset, ref c);
offset += Utils.Decode32U(data, offset, ref c2);
offset += Utils.Decode32U(data, offset, ref c4);
offset += Utils.Decode32U(data, offset, ref c3);
size -= 24;
if (size < c3 || c3 < 0)
{
return -2;
}
if (c7 != 81 && c7 != 82 && c7 != 83 && c7 != 84)
{
return -3;
}
rmt_wnd = c6;
ParseUna(c4);
ShrinkBuf();
switch (c7)
{
case 82:
if (Utils.TimeDiff(current, c) >= 0)
{
UpdateAck(Utils.TimeDiff(current, c));
}
ParseAck(c2);
ShrinkBuf();
if (num2 == 0)
{
num2 = 1;
num = c2;
ts = c;
}
else if (Utils.TimeDiff(c2, num) > 0)
{
num = c2;
ts = c;
}
break;
case 81:
if (Utils.TimeDiff(c2, rcv_nxt + rcv_wnd) >= 0)
{
break;
}
AckPush(c2, c);
if (Utils.TimeDiff(c2, rcv_nxt) >= 0)
{
Segment segment = SegmentNew();
segment.conv = c5;
segment.cmd = c7;
segment.frg = c8;
segment.wnd = c6;
segment.ts = c;
segment.sn = c2;
segment.una = c4;
if (c3 != 0)
{
segment.data.Write(data, offset, (int)c3);
}
ParseData(segment);
}
break;
case 83:
probe |= 2u;
break;
default:
return -3;
case 84:
break;
}
offset += (int)c3;
size -= (int)c3;
}
if (num2 != 0)
{
ParseFastack(num, ts);
}
if (Utils.TimeDiff(snd_una, earlier) > 0 && cwnd < rmt_wnd)
{
if (cwnd < ssthresh)
{
cwnd++;
incr += mss;
}
else
{
if (incr < mss)
{
incr = mss;
}
incr += mss * mss / incr + mss / 16;
if ((cwnd + 1) * mss <= incr)
{
cwnd = (incr + mss - 1) / ((mss == 0) ? 1 : mss);
}
}
if (cwnd > rmt_wnd)
{
cwnd = rmt_wnd;
incr = rmt_wnd * mss;
}
}
return 0;
}
private uint WndUnused()
{
if (rcv_queue.Count < rcv_wnd)
{
return rcv_wnd - (uint)rcv_queue.Count;
}
return 0u;
}
public void Flush()
{
int offset = 0;
bool flag = false;
if (!updated)
{
return;
}
Segment segment = SegmentNew();
segment.conv = conv;
segment.cmd = 82u;
segment.wnd = WndUnused();
segment.una = rcv_nxt;
foreach (AckItem item in acklist)
{
MakeSpace(24);
segment.sn = item.serialNumber;
segment.ts = item.timestamp;
offset += segment.Encode(buffer, offset);
}
acklist.Clear();
if (rmt_wnd == 0)
{
if (probe_wait == 0)
{
probe_wait = 7000u;
ts_probe = current + probe_wait;
}
else if (Utils.TimeDiff(current, ts_probe) >= 0)
{
if (probe_wait < 7000)
{
probe_wait = 7000u;
}
probe_wait += probe_wait / 2;
if (probe_wait > 120000)
{
probe_wait = 120000u;
}
ts_probe = current + probe_wait;
probe |= 1u;
}
}
else
{
ts_probe = 0u;
probe_wait = 0u;
}
if ((probe & (true ? 1u : 0u)) != 0)
{
segment.cmd = 83u;
MakeSpace(24);
offset += segment.Encode(buffer, offset);
}
if ((probe & 2u) != 0)
{
segment.cmd = 84u;
MakeSpace(24);
offset += segment.Encode(buffer, offset);
}
probe = 0u;
uint num = Math.Min(snd_wnd, rmt_wnd);
if (!nocwnd)
{
num = Math.Min(cwnd, num);
}
while (Utils.TimeDiff(snd_nxt, snd_una + num) < 0 && snd_queue.Count != 0)
{
Segment segment2 = snd_queue.Dequeue();
segment2.conv = conv;
segment2.cmd = 81u;
segment2.wnd = segment.wnd;
segment2.ts = current;
segment2.sn = snd_nxt++;
segment2.una = rcv_nxt;
segment2.resendts = current;
segment2.rto = rx_rto;
segment2.fastack = 0u;
segment2.xmit = 0u;
snd_buf.Add(segment2);
}
uint num2 = ((fastresend > 0) ? ((uint)fastresend) : uint.MaxValue);
uint num3 = ((nodelay == 0) ? ((uint)rx_rto >> 3) : 0u);
int num4 = 0;
foreach (Segment item2 in snd_buf)
{
bool flag2 = false;
if (item2.xmit == 0)
{
flag2 = true;
item2.xmit++;
item2.rto = rx_rto;
item2.resendts = (uint)((int)current + item2.rto) + num3;
}
else if (Utils.TimeDiff(current, item2.resendts) >= 0)
{
flag2 = true;
item2.xmit++;
xmit++;
if (nodelay == 0)
{
item2.rto += Math.Max(item2.rto, rx_rto);
}
else
{
int num5 = ((nodelay < 2) ? item2.rto : rx_rto);
item2.rto += num5 / 2;
}
item2.resendts = current + (uint)item2.rto;
flag = true;
}
else if (item2.fastack >= num2 && (item2.xmit <= fastlimit || fastlimit <= 0))
{
flag2 = true;
item2.xmit++;
item2.fastack = 0u;
item2.resendts = current + (uint)item2.rto;
num4++;
}
if (flag2)
{
item2.ts = current;
item2.wnd = segment.wnd;
item2.una = rcv_nxt;
int space2 = 24 + (int)item2.data.Position;
MakeSpace(space2);
offset += item2.Encode(buffer, offset);
if (item2.data.Position > 0)
{
Buffer.BlockCopy(item2.data.GetBuffer(), 0, buffer, offset, (int)item2.data.Position);
offset += (int)item2.data.Position;
}
if (item2.xmit >= dead_link)
{
state = -1;
}
}
}
SegmentDelete(segment);
FlushBuffer();
if (num4 > 0)
{
uint num6 = snd_nxt - snd_una;
ssthresh = num6 / 2;
if (ssthresh < 2)
{
ssthresh = 2u;
}
cwnd = ssthresh + num2;
incr = cwnd * mss;
}
if (flag)
{
ssthresh = num / 2;
if (ssthresh < 2)
{
ssthresh = 2u;
}
cwnd = 1u;
incr = mss;
}
if (cwnd < 1)
{
cwnd = 1u;
incr = mss;
}
void FlushBuffer()
{
if (offset > 0)
{
output(buffer, offset);
}
}
void MakeSpace(int space)
{
if (offset + space > mtu)
{
output(buffer, offset);
offset = 0;
}
}
}
public void Update(uint currentTimeMilliSeconds)
{
current = currentTimeMilliSeconds;
if (!updated)
{
updated = true;
ts_flush = current;
}
int num = Utils.TimeDiff(current, ts_flush);
if (num >= 10000 || num < -10000)
{
ts_flush = current;
num = 0;
}
if (num >= 0)
{
ts_flush += interval;
if (Utils.TimeDiff(current, ts_flush) >= 0)
{
ts_flush = current + interval;
}
Flush();
}
}
public uint Check(uint current_)
{
uint num = ts_flush;
int num2 = int.MaxValue;
int num3 = int.MaxValue;
if (!updated)
{
return current_;
}
if (Utils.TimeDiff(current_, num) >= 10000 || Utils.TimeDiff(current_, num) < -10000)
{
num = current_;
}
if (Utils.TimeDiff(current_, num) >= 0)
{
return current_;
}
num2 = Utils.TimeDiff(num, current_);
foreach (Segment item in snd_buf)
{
int num4 = Utils.TimeDiff(item.resendts, current_);
if (num4 <= 0)
{
return current_;
}
if (num4 < num3)
{
num3 = num4;
}
}
uint num5 = (uint)((num3 < num2) ? num3 : num2);
if (num5 >= interval)
{
num5 = interval;
}
return current_ + num5;
}
public void SetMtu(uint mtu)
{
if (mtu < 50 || mtu < 24)
{
throw new ArgumentException("MTU must be higher than 50 and higher than OVERHEAD");
}
buffer = new byte[(mtu + 24) * 3];
this.mtu = mtu;
mss = mtu - 24;
}
public void SetInterval(uint interval)
{
if (interval > 5000)
{
interval = 5000u;
}
else if (interval < 10)
{
interval = 10u;
}
this.interval = interval;
}
public void SetNoDelay(uint nodelay, uint interval = 100u, int resend = 0, bool nocwnd = false)
{
this.nodelay = nodelay;
if (nodelay != 0)
{
rx_minrto = 30;
}
else
{
rx_minrto = 100;
}
if (interval >= 0)
{
if (interval > 5000)
{
interval = 5000u;
}
else if (interval < 10)
{
interval = 10u;
}
this.interval = interval;
}
if (resend >= 0)
{
fastresend = resend;
}
this.nocwnd = nocwnd;
}
public void SetWindowSize(uint sendWindow, uint receiveWindow)
{
if (sendWindow != 0)
{
snd_wnd = sendWindow;
}
if (receiveWindow != 0)
{
rcv_wnd = Math.Max(receiveWindow, 128u);
}
}
}
public class Pool<T>
{
private readonly Stack<T> objects = new Stack<T>();
private readonly Func<T> objectGenerator;
private readonly Action<T> objectResetter;
public int Count => objects.Count;
public Pool(Func<T> objectGenerator, Action<T> objectResetter, int initialCapacity)
{
this.objectGenerator = objectGenerator;
this.objectResetter = objectResetter;
for (int i = 0; i < initialCapacity; i++)
{
objects.Push(objectGenerator());
}
}
public T Take()
{
if (objects.Count <= 0)
{
return objectGenerator();
}
return objects.Pop();
}
public void Return(T item)
{
objectResetter(item);
objects.Push(item);
}
public void Clear()
{
objects.Clear();
}
}
internal class Segment
{
internal uint conv;
internal uint cmd;
internal uint frg;
internal uint wnd;
internal uint ts;
internal uint sn;
internal uint una;
internal uint resendts;
internal int rto;
internal uint fastack;
internal uint xmit;
internal MemoryStream data = new MemoryStream(1200);
internal int Encode(byte[] ptr, int offset)
{
int num = offset;
offset += Utils.Encode32U(ptr, offset, conv);
offset += Utils.Encode8u(ptr, offset, (byte)cmd);
offset += Utils.Encode8u(ptr, offset, (byte)frg);
offset += Utils.Encode16U(ptr, offset, (ushort)wnd);
offset += Utils.Encode32U(ptr, offset, ts);
offset += Utils.Encode32U(ptr, offset, sn);
offset += Utils.Encode32U(ptr, offset, una);
offset += Utils.Encode32U(ptr, offset, (uint)data.Position);
return offset - num;
}
internal void Reset()
{
conv = 0u;
cmd = 0u;
frg = 0u;
wnd = 0u;
ts = 0u;
sn = 0u;
una = 0u;
rto = 0;
xmit = 0u;
resendts = 0u;
fastack = 0u;
data.SetLength(0L);
}
}
public static class Utils
{
public static int Clamp(int value, int min, int max)
{
if (value < min)
{
return min;
}
if (value > max)
{
return max;
}
return value;
}
public static int Encode8u(byte[] p, int offset, byte c)
{
p[offset] = c;
return 1;
}
public static int Decode8u(byte[] p, int offset, ref byte c)
{
c = p[offset];
return 1;
}
public static int Encode16U(byte[] p, int offset, ushort w)
{
p[offset] = (byte)w;
p[1 + offset] = (byte)(w >> 8);
return 2;
}
public static int Decode16U(byte[] p, int offset, ref ushort c)
{
ushort num = 0;
num |= p[offset];
num |= (ushort)(p[1 + offset] << 8);
c = num;
return 2;
}
public static int Encode32U(byte[] p, int offset, uint l)
{
p[offset] = (byte)l;
p[1 + offset] = (byte)(l >> 8);
p[2 + offset] = (byte)(l >> 16);
p[3 + offset] = (byte)(l >> 24);
return 4;
}
public static int Decode32U(byte[] p, int offset, ref uint c)
{
uint num = 0u;
num |= p[offset];
num |= (uint)(p[1 + offset] << 8);
num |= (uint)(p[2 + offset] << 16);
num |= (uint)(p[3 + offset] << 24);
c = num;
return 4;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int TimeDiff(uint later, uint earlier)
{
return (int)(later - earlier);
}
}