using System; using System.Net; using System.IO; using dotNetty_kcp; using DotNetty.Buffers; using UnityEngine; using Guru; using Dof; namespace DofLibrary { public class GameKcpClient : KcpListener { public Action OnConnected; public Action OnDisConnected; private KcpClient _kcpClient; public IMessageSender sender { get; private set; } = new MessageSender(); private IMessageReceiver _receiver; private bool _running; private int? _timeoutMillis; public GameKcpClient(DotNetty.Unity.Level level = DotNetty.Unity.Level.ALL) { // 指定DotNetty日志的显示级别 DotNetty.Unity.UnityLoggerFactory.Default.Level = level; } /// /// 当前 Client 是否处于连接状态 /// public bool Running => _running; /// /// 创建一个到 Dof GameServer 的连接 /// /// Dof GameServer 的地址,形如 xxx.xxx.xxx.xxx /// 房间ID /// 事件回调接口 public async void Connect(string serverAddress, int port, IMessageReceiver receiver) { _receiver = receiver; _receiver.MessageSender = sender; _running = true; var channelConfig = new ChannelConfig(); channelConfig.initNodelay(true, 40, 2, true); channelConfig.Sndwnd = 512; channelConfig.Rcvwnd = 512; channelConfig.Mtu = 512; channelConfig.FecDataShardCount = 10; channelConfig.FecParityShardCount = 3; channelConfig.AckNoDelay = true; channelConfig.Crc32Check = false; channelConfig.UseConvChannel = true; //channelConfig.Conv = UnityEngine.Random.Range(1, int.MaxValue); if(_timeoutMillis.HasValue) channelConfig.TimeoutMillis = _timeoutMillis.Value; _kcpClient = new KcpClient(); _kcpClient.init(channelConfig); var remoteAddress = new IPEndPoint(IPAddress.Parse(serverAddress), port); var task = _kcpClient.BindLocal(); await task; var channel = task.Result; _kcpClient.connect(channel, remoteAddress, channelConfig, this); } private void ProcessMessage(ServerMessage serverMessage) { //Debug.Log("[ProcessMessage]"); if (serverMessage.PlayerEntered != null) { //Debug.Log("[ProcessMessage]PlayerEntered"); var cid = serverMessage.PlayerEntered.Cid; sender.Cid = cid; _receiver.OnPlayerEntered(cid); } else if (serverMessage.GameStart != null) { //Debug.Log("[ProcessMessage]GameStart"); _receiver.OnGameStart(serverMessage.GameStart); } else if (serverMessage.LevelStart != null) { //Debug.Log("[ProcessMessage]LevelStart"); _receiver.OnLevelStart(serverMessage.LevelStart); } else if (serverMessage.PointFound != null) { //Debug.Log("[ProcessMessage]PointFound"); _receiver.OnPointFound(serverMessage.PointFound); } else if (serverMessage.GameFinish != null) { //Debug.Log("[ProcessMessage]GameFinish"); _receiver.OnGameFinish(serverMessage.GameFinish); } else if (serverMessage.Heartbeat != null) { //Debug.Log("[ProcessMessage]Heartbeat"); _receiver.OnHeartbeat(serverMessage.Heartbeat); } } /// /// kcp 连接后的回调函数 /// /// kcp连接 public void onConnected(Ukcp ukcp) { Debug.Log($"[GameKcpClient]onConnected: conv = {ukcp.getConv()}"); sender.Client = ukcp; if (OnConnected != null) { LoomUtil.QueueOnMainThread(() => { OnConnected.Invoke(); }); } } /// /// kcp 接收到消息的回调函数 /// /// 接收到的消息buffer /// kcp连接 public void handleReceive(IByteBuffer byteBuf, Ukcp ukcp) { Debug.Log("[GameKcpClient]handleReceive"); var ms = new MemoryStream(1024 * 1024 * 1); var data_len = byteBuf.ReadableBytes; byteBuf.ReadBytes(ms, data_len); var msg = ProtobufHelper.FromBytes(typeof(ServerMessage), ms.GetBuffer(), 0, data_len); if (msg is ServerMessage server_msg) { LoomUtil.QueueOnMainThread(() => { ProcessMessage(server_msg); }); } else { Debug.LogError("[GameKcpClient]handleReceive: Fail to Deserialize ServerMessage"); } } /// /// kcp 连接或者接收消息出错的回调函数 /// /// 异常 /// kcp连接 public void handleException(Exception ex, Ukcp ukcp) { Debug.LogError($"[GameKcpClient]handleException: {ex}"); } /// /// kcp 连接关闭时的回调函数 /// /// kcp连接 public void handleClose(Ukcp ukcp) { Debug.Log("[GameKcpClient]: Connection closed"); if (OnDisConnected != null) { LoomUtil.QueueOnMainThread(() => { OnDisConnected.Invoke(); }); } } public void setTimeoutMillis(int timeoutMillis) { _timeoutMillis = timeoutMillis; } /// /// 关闭 Client,应该在游戏结束时调用 /// public void Close() { _kcpClient?.stop(); _running = false; } } }