KcpClient.cs 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120
  1. // kcp client logic abstracted into a class.
  2. // for use in Mirror, DOTSNET, testing, etc.
  3. using System;
  4. namespace kcp2k
  5. {
  6. public class KcpClient
  7. {
  8. // events
  9. public Action OnConnected;
  10. public Action<ArraySegment<byte>> OnData;
  11. public Action OnDisconnected;
  12. // state
  13. public KcpClientConnection connection;
  14. public bool connected;
  15. public KcpClient(Action OnConnected, Action<ArraySegment<byte>> OnData, Action OnDisconnected)
  16. {
  17. this.OnConnected = OnConnected;
  18. this.OnData = OnData;
  19. this.OnDisconnected = OnDisconnected;
  20. }
  21. // CreateConnection can be overwritten for where-allocation:
  22. // https://github.com/vis2k/where-allocation
  23. protected virtual KcpClientConnection CreateConnection() =>
  24. new KcpClientConnection();
  25. 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)
  26. {
  27. if (connected)
  28. {
  29. Log.Warning("KCP: client already connected!");
  30. return;
  31. }
  32. // create connection
  33. connection = CreateConnection();
  34. // setup events
  35. connection.OnAuthenticated = () =>
  36. {
  37. Log.Info($"KCP: OnClientConnected");
  38. connected = true;
  39. OnConnected.Invoke();
  40. };
  41. connection.OnData = (message) =>
  42. {
  43. //Log.Debug($"KCP: OnClientData({BitConverter.ToString(message.Array, message.Offset, message.Count)})");
  44. OnData.Invoke(message);
  45. };
  46. connection.OnDisconnected = () =>
  47. {
  48. Log.Info($"KCP: OnClientDisconnected");
  49. connected = false;
  50. connection = null;
  51. OnDisconnected.Invoke();
  52. };
  53. // connect
  54. connection.Connect(address, port, noDelay, interval, fastResend, congestionWindow, sendWindowSize, receiveWindowSize, timeout);
  55. }
  56. public void Send(ArraySegment<byte> segment, KcpChannel channel)
  57. {
  58. if (connected)
  59. {
  60. connection.SendData(segment, channel);
  61. }
  62. else Log.Warning("KCP: can't send because client not connected!");
  63. }
  64. public void Disconnect()
  65. {
  66. // only if connected
  67. // otherwise we end up in a deadlock because of an open Mirror bug:
  68. // https://github.com/vis2k/Mirror/issues/2353
  69. if (connected)
  70. {
  71. // call Disconnect and let the connection handle it.
  72. // DO NOT set it to null yet. it needs to be updated a few more
  73. // times first. let the connection handle it!
  74. connection?.Disconnect();
  75. }
  76. }
  77. // process incoming messages. should be called before updating the world.
  78. public void TickIncoming()
  79. {
  80. // recv on socket first, then process incoming
  81. // (even if we didn't receive anything. need to tick ping etc.)
  82. // (connection is null if not active)
  83. connection?.RawReceive();
  84. connection?.TickIncoming();
  85. }
  86. // process outgoing messages. should be called after updating the world.
  87. public void TickOutgoing()
  88. {
  89. // process outgoing
  90. // (connection is null if not active)
  91. connection?.TickOutgoing();
  92. }
  93. // process incoming and outgoing for convenience
  94. // => ideally call ProcessIncoming() before updating the world and
  95. // ProcessOutgoing() after updating the world for minimum latency
  96. public void Tick()
  97. {
  98. TickIncoming();
  99. TickOutgoing();
  100. }
  101. // pause/unpause to safely support mirror scene handling and to
  102. // immediately pause the receive while loop if needed.
  103. public void Pause() => connection?.Pause();
  104. public void Unpause() => connection?.Unpause();
  105. }
  106. }