253 lines
6.6 KiB
C#
253 lines
6.6 KiB
C#
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<ENetClient> _logger;
|
|
|
|
// ========== 事件 ==========
|
|
public event Action? OnStartConnect;
|
|
public event Action? OnConnected;
|
|
public event Action? OnDisconnected;
|
|
public event Action? OnConnectedFailed;
|
|
public event Action<byte[], int>? 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<ENetClient> 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<bool> 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<T>() => _netProviderService.GetRemoteInterface<T>();
|
|
|
|
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<byte[]> InvokeAsync(int id, byte[] bytes)
|
|
{
|
|
var rpcCount = RpcCount;
|
|
|
|
Invoke(id,bytes);
|
|
|
|
var task = _netProviderService.TaskCompletionSources[rpcCount] = new UniTaskCompletionSource<byte[]>();
|
|
|
|
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)
|
|
{
|
|
|
|
}
|
|
|
|
}
|
|
}
|