using System; using System.Threading; using System.Threading.Tasks; using ENet; using BITKit; using BITKit.Net; using Cysharp.Threading.Tasks; using Microsoft.Extensions.Logging; using Net.BITKit.Teleport; public class ENetClient : IDisposable, INetClient,INetProvider { private readonly NetProviderService _netProviderService; private readonly IServiceProvider _serviceProvider; private readonly ILogger _logger; // ========== 事件 ========== public event Action? OnStartConnect; public event Action? OnConnected; public event Action? OnDisconnected; public event Action? OnConnectedFailed; public event Action? OnData; // ========== 属性 ========== public bool IsConnected => _isConnected.Allow && _peer is { IsSet: true }; public bool IsConnection => _isConnection.Allow; public bool ManualTick { get; set; } public int Id { get; private set; } public int Ping { get; private set; } // ========== 字段 ========== private Host _client; private Peer _peer; private readonly ValidHandle _manualConnect = new(); private readonly ValidHandle _isConnected = new(); private readonly ValidHandle _manualConnected = new(); private readonly ValidHandle _isConnection = new(); // ========== 构造 ========== public ENetClient(ILogger logger, IServiceProvider serviceProvider, NetProviderService netProviderService) { _logger = logger; _serviceProvider = serviceProvider; _netProviderService = netProviderService; ENet.Library.Initialize(); _isConnected.AddListener(connected => { if (connected) OnConnected?.Invoke(); else OnConnectedFailed?.Invoke(); }); } // ========== 主动连接 ========== public async UniTask Connect(string address = "127.0.0.1", ushort port = 27014) { if (IsConnected) return true; await _isConnection; using var connecting = _isConnection.GetHandle(); _manualConnect.AddElement(0); _client = new Host(); var eNetAddress = new Address(); eNetAddress.SetHost(address); eNetAddress.Port = port; _client.Create(); _peer = _client.Connect(eNetAddress); await Task.Delay(100); for (var i = 0; i < 3; i++) { if (!_manualConnect.Allow) return false; if (_isConnected) break; _client.Flush(); await Task.Delay(300); } if (!_isConnected) return false; _manualConnected.AddElement(0); return true; } // ========== 主动断开 ========== public void Disconnect() { _manualConnect.RemoveElement(0); } // ========== 主动轮询网络事件 ========== public int RpcCount { get; set; } public uint TickRate { get; set; } public void Tick() { _netProviderService.Tick(); if(_client is null )return; _client.Flush(); { var packet = (Packet)default; packet.Create(NetProviderService.Heartbeat, PacketFlags.Reliable); _peer.Send(0, ref packet); packet.Dispose(); } while (_client.CheckEvents(out var netEvent) > 0 || _client.Service(0, out netEvent) > 0) { Id = (int)(netEvent.Peer.ID - ENetServer.Offset); switch (netEvent.Type) { case EventType.None: break; case EventType.Connect: // 假设你已经连接成功并拿到 peer netEvent.Peer.Timeout(3, 1000, 2000); _isConnected.AddElement(0); _logger.LogInformation("✅ Client connected to server."); break; case EventType.Disconnect: _isConnected.RemoveElement(0); _logger.LogInformation("❌ Client disconnected from server."); OnDisconnected?.Invoke(); break; case EventType.Timeout: _isConnected.RemoveElement(0); _logger.LogInformation("⏰ Client connection timeout."); OnDisconnected?.Invoke(); break; case EventType.Receive: _isConnected.AddElement(0); var buffer = new byte[netEvent.Packet.Length]; netEvent.Packet.CopyTo(buffer); OnData?.Invoke(buffer, netEvent.ChannelID); try { _netProviderService.OnData(Id, buffer); } catch (Exception e) { _logger.LogCritical(e, e.Message); } break; } netEvent.Packet.Dispose(); } } public void HandShake() { throw new NotImplementedException(); } public T GetRemoteInterface() => _netProviderService.GetRemoteInterface(); public void Invoke(int id, byte[] bytes) { if (IsConnected is false) { throw new NetOfflineException(); } var packet = default(Packet); packet.Create(bytes); _peer.Send(0,ref packet); packet.Dispose(); } public async UniTask InvokeAsync(int id, byte[] bytes) { var rpcCount = RpcCount; Invoke(id,bytes); var task = _netProviderService.TaskCompletionSources[rpcCount] = new UniTaskCompletionSource(); using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(3)); try { bytes = await task.Task.AttachExternalCancellation(timeout.Token); return bytes; } finally { #if UNITY_5_3_OR_NEWER await UniTask.SwitchToMainThread(); #endif } } // ========== 占位方法 ========== public void SendServerMessage(string message) { throw new NotImplementedException(); } // ========== 释放 ========== public void Dispose() { try { _client?.Flush(); _client?.Dispose(); } catch (InvalidOperationException) { } } }