Before Rebuild NetProvider
This commit is contained in:
@@ -13,6 +13,7 @@ using System.Net;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using BITKit.Net.Examples;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Newtonsoft.Json;
|
||||
@@ -22,6 +23,7 @@ namespace BITKit.Net
|
||||
{
|
||||
public class KcpNetClient:INetClient,INetProvider
|
||||
{
|
||||
private readonly NetProviderCommon _common = new();
|
||||
private readonly ILogger<KcpNetClient> _logger;
|
||||
public KcpNetClient(ILogger<KcpNetClient> logger)
|
||||
{
|
||||
@@ -40,15 +42,24 @@ namespace BITKit.Net
|
||||
Id = x.Id;
|
||||
});
|
||||
_isConnected.AddListener(ConnectionCallback);
|
||||
|
||||
AddCommandListener<SimplePing>(F);
|
||||
|
||||
return;
|
||||
UniTask<SimplePing> F(SimplePing p)
|
||||
{
|
||||
p.EndTime = DateTime.Now;
|
||||
return UniTask.FromResult(p);
|
||||
}
|
||||
}
|
||||
|
||||
public event Action OnStartConnect;
|
||||
public event Action OnConnected;
|
||||
public event Action OnDisconnected;
|
||||
public event Action OnConnectedFailed;
|
||||
public uint TickRate { get; set; } = 8;
|
||||
public bool IsConnected => _isConnected;
|
||||
public bool IsConnecting { get; private set; }
|
||||
public double RpcTimeOut { get; set; } = 5;
|
||||
public bool AutoReconnect { get; set; } = true;
|
||||
public float2 Traffic { get; private set; }
|
||||
public bool ManualTick { get; set; }
|
||||
@@ -68,25 +79,17 @@ namespace BITKit.Net
|
||||
AutoReset = true
|
||||
};
|
||||
|
||||
private readonly GenericEvent _events = new();
|
||||
|
||||
private readonly ValidHandle _isConnected = new();
|
||||
private bool _userConnected;
|
||||
private int _index = int.MinValue;
|
||||
private readonly ConcurrentDictionary<int, object> _p2p = new();
|
||||
private readonly ConcurrentDictionary<string,Func<object,UniTask<object>>> _rpc = new();
|
||||
private readonly ConcurrentDictionary<string,MethodInfo> _rpcMethods = new();
|
||||
private readonly ConcurrentDictionary<string,EventInfo> _rpcEvents = new();
|
||||
private readonly ConcurrentDictionary<string,object> _rpcHandles = new();
|
||||
|
||||
private DateTime _lastHeartbeat = DateTime.Now;
|
||||
private DateTime _now = DateTime.Now;
|
||||
private TimeSpan _interval = TimeSpan.FromMilliseconds(100);
|
||||
private string _connectedAddress = "127.0.0.1";
|
||||
private ushort _connectedPort = 27014;
|
||||
|
||||
private readonly byte[] _heartBeat = new byte[] { (byte)NetCommandType.Heartbeat };
|
||||
|
||||
|
||||
private async void ConnectionCallback(bool x)
|
||||
{
|
||||
await BITApp.SwitchToMainThread();
|
||||
@@ -136,6 +139,7 @@ namespace BITKit.Net
|
||||
return false;
|
||||
}
|
||||
_userConnected = true;
|
||||
|
||||
//如果address是域名,解析为Ip
|
||||
if (address.Contains("."))
|
||||
{
|
||||
@@ -170,19 +174,15 @@ namespace BITKit.Net
|
||||
|
||||
_timer.Start();
|
||||
_interval = TimeSpan.FromMilliseconds(_timer.Interval);
|
||||
|
||||
HandShake();
|
||||
|
||||
await BITApp.SwitchToMainThread();
|
||||
|
||||
|
||||
for (var i = 0; i < 5; i++)
|
||||
{
|
||||
_client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable);
|
||||
Traffic += new float2(1, 0);
|
||||
if (_client.connected)
|
||||
{
|
||||
HandShake();
|
||||
_client.Send(new[] { (byte)NetCommandType.Heartbeat }, KcpChannel.Reliable);
|
||||
Traffic += new float2(1, 0);
|
||||
}
|
||||
_client.Tick();
|
||||
await Task.Delay(100);
|
||||
}
|
||||
|
||||
if (_client.connected)
|
||||
{
|
||||
@@ -192,6 +192,7 @@ namespace BITKit.Net
|
||||
|
||||
_connectedAddress = address;
|
||||
_connectedPort = port;
|
||||
|
||||
return _client.connected;
|
||||
}
|
||||
|
||||
@@ -247,7 +248,7 @@ namespace BITKit.Net
|
||||
if (BITApp.SynchronizationContext is not null)
|
||||
await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext,
|
||||
BITApp.CancellationToken);
|
||||
_events.Invoke(command.GetType().FullName, command);
|
||||
_common.Events.Invoke(command.GetType().FullName, command);
|
||||
}
|
||||
catch (OperationCanceledException)
|
||||
{
|
||||
@@ -272,21 +273,24 @@ namespace BITKit.Net
|
||||
case NetCommandType.ReturnToClient:
|
||||
|
||||
var id = reader.ReadInt32();
|
||||
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
if (reader.ReadBoolean())
|
||||
if (_common.P2P.TryRemove(id, out var source))
|
||||
{
|
||||
var value = BITBinary.Read(reader);
|
||||
_p2p.TryAdd(id,value);
|
||||
if (reader.ReadBoolean())
|
||||
{
|
||||
var value = BITBinary.Read(reader);
|
||||
source.TrySetResult(value);
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = reader.ReadString();
|
||||
source.TrySetException(new Exception(message));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
var message = reader.ReadString();
|
||||
|
||||
_p2p.TryAdd(id,new Exception(message));
|
||||
_logger.LogWarning($"ID为{id}的请求未注册回调");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -314,10 +318,10 @@ namespace BITKit.Net
|
||||
var pars = BITBinary.Read(reader).As<object[]>();
|
||||
|
||||
|
||||
if (_rpcMethods.TryGetValue(path, out var methodInfo))
|
||||
if (_common.RpcMethods.TryGetValue(path, out var methodInfo))
|
||||
{
|
||||
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
|
||||
var handle = _rpcHandles[path];
|
||||
var handle = _common.RpcHandles[path];
|
||||
|
||||
try
|
||||
{
|
||||
@@ -342,9 +346,9 @@ namespace BITKit.Net
|
||||
returnWriter.Write(true);
|
||||
BITBinary.Write(returnWriter, value);
|
||||
}
|
||||
else if (_rpcEvents.TryGetValue(path, out var eventInfo))
|
||||
else if (_common.RpcEvents.TryGetValue(path, out var eventInfo))
|
||||
{
|
||||
var handle = _rpcHandles[path];
|
||||
var handle = _common.RpcHandles[path];
|
||||
var fieldInfo = handle.GetType().GetField(eventInfo.Name,ReflectionHelper.Flags)!;
|
||||
|
||||
var eventDelegate = fieldInfo.GetValue(handle) as MulticastDelegate;
|
||||
@@ -364,7 +368,7 @@ namespace BITKit.Net
|
||||
{
|
||||
var commandObj = BITBinary.Read(reader)
|
||||
.As<object[]>()[0];
|
||||
var func = _rpc[commandObj.GetType()!.FullName!];
|
||||
var func = _common.Rpc[commandObj.GetType()!.FullName!];
|
||||
value = await func.As<Func<object, UniTask<object>>>().Invoke(commandObj);
|
||||
}
|
||||
|
||||
@@ -392,11 +396,11 @@ namespace BITKit.Net
|
||||
{
|
||||
var rpcName = reader.ReadString();
|
||||
var pars = BITBinary.Read(reader).As<object[]>();
|
||||
if (_rpcMethods.TryGetValue(rpcName, out var methodInfo))
|
||||
if (_common.RpcMethods.TryGetValue(rpcName, out var methodInfo))
|
||||
{
|
||||
try
|
||||
{
|
||||
methodInfo.Invoke(_rpcHandles[rpcName], pars);
|
||||
methodInfo.Invoke(_common.RpcHandles[rpcName], pars);
|
||||
}
|
||||
catch (TargetException targetException)
|
||||
{
|
||||
@@ -434,9 +438,9 @@ namespace BITKit.Net
|
||||
}
|
||||
|
||||
}
|
||||
else if (_rpcEvents.TryGetValue(rpcName, out var eventInfo))
|
||||
else if (_common.RpcEvents.TryGetValue(rpcName, out var eventInfo))
|
||||
{
|
||||
var handle = _rpcHandles[rpcName];
|
||||
var handle = _common.RpcHandles[rpcName];
|
||||
var fieldInfo = handle.GetType().GetField(eventInfo.Name,ReflectionHelper.Flags)!;
|
||||
|
||||
var eventDelegate = fieldInfo.GetValue(handle) as MulticastDelegate;
|
||||
@@ -517,13 +521,13 @@ namespace BITKit.Net
|
||||
_commandQueue.Enqueue(bytes);
|
||||
}
|
||||
|
||||
public async UniTask<T> GetFromServer<T>(string path = default,params object[] pars)
|
||||
public async UniTask<T> GetFromServer<T>(string path = default, params object[] pars)
|
||||
{
|
||||
if (IsConnected is false)
|
||||
{
|
||||
throw new NetOfflineException();
|
||||
}
|
||||
//await UniTask.SwitchToThreadPool();
|
||||
|
||||
var id = _index++;
|
||||
var ms = new MemoryStream();
|
||||
var writer = new BinaryWriter(ms);
|
||||
@@ -538,95 +542,63 @@ namespace BITKit.Net
|
||||
writer.Write(true);
|
||||
writer.Write(path);
|
||||
}
|
||||
BITBinary.Write(writer,pars);
|
||||
|
||||
|
||||
if (pars.Length is 0)
|
||||
{
|
||||
pars = new[] { System.Activator.CreateInstance<T>() as object };
|
||||
}
|
||||
|
||||
BITBinary.Write(writer, pars);
|
||||
|
||||
var bytes = ms.ToArray();
|
||||
|
||||
|
||||
await ms.DisposeAsync();
|
||||
await writer.DisposeAsync();
|
||||
|
||||
_commandQueue.Enqueue(bytes);
|
||||
var startTime = _now;
|
||||
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (IsConnected is false)
|
||||
{
|
||||
throw new NetOfflineException();
|
||||
}
|
||||
if ((_now - startTime).TotalSeconds > RpcTimeOut)
|
||||
{
|
||||
if (string.IsNullOrEmpty(path))
|
||||
{
|
||||
throw new TimeoutException("请求超时或已断开连接");
|
||||
|
||||
}
|
||||
throw new TimeoutException($"请求超时或已断开连接,请求为{path}");
|
||||
}
|
||||
|
||||
if (_p2p.TryRemove(id, out var value))
|
||||
{
|
||||
await BITApp.SwitchToMainThread();
|
||||
if (value is Exception e)
|
||||
{
|
||||
throw new InGameException(e.Message);
|
||||
}
|
||||
if (UniTask.CompletedTask is T t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
return value.As<T>();
|
||||
}
|
||||
await Task.Delay(_interval);
|
||||
_commandQueue.Enqueue(bytes);
|
||||
|
||||
var source = new UniTaskCompletionSource<object>();
|
||||
|
||||
_common.P2P.TryAdd(id, source);
|
||||
|
||||
var timeoutCts = new CancellationTokenSource();
|
||||
timeoutCts.CancelAfter(5000); // 设置超时时间
|
||||
|
||||
var value = await source.Task.AttachExternalCancellation(timeoutCts.Token);
|
||||
|
||||
if (value is Exception e)
|
||||
{
|
||||
throw new InGameException(e.Message);
|
||||
}
|
||||
|
||||
if (UniTask.CompletedTask is T t)
|
||||
{
|
||||
return t;
|
||||
}
|
||||
|
||||
return value.As<T>();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public UniTask<T> GetFromClient<T>(int id,string path, params object[] pars)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
|
||||
public void AddRpcHandle(object rpcHandle)
|
||||
{
|
||||
var reportBuilder = new StringBuilder();
|
||||
|
||||
reportBuilder.AppendLine($"正在通过反射注册{rpcHandle.GetType().Name}");
|
||||
foreach (var methodInfo in rpcHandle.GetType().GetMethods())
|
||||
{
|
||||
var att = methodInfo.GetCustomAttribute<NetRpcAttribute>();
|
||||
if (att is null) continue;
|
||||
_rpcMethods.AddOrUpdate(methodInfo.Name, methodInfo, (s, info) => methodInfo);
|
||||
_rpcHandles.AddOrUpdate(methodInfo.Name, rpcHandle, (s, o) => rpcHandle);
|
||||
|
||||
reportBuilder.AppendLine($"Add [{methodInfo.Name}] as MethodInfo");
|
||||
}
|
||||
|
||||
foreach (var eventInfo in rpcHandle.GetType().GetEvents())
|
||||
{
|
||||
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>();
|
||||
if (att is null) continue;
|
||||
|
||||
_rpcEvents.TryAdd(eventInfo.Name, eventInfo);
|
||||
_rpcHandles.TryAdd(eventInfo.Name, rpcHandle);
|
||||
|
||||
reportBuilder.AppendLine($"Add [{eventInfo.Name}] as EventInfo");
|
||||
}
|
||||
|
||||
_logger.LogInformation(reportBuilder.ToString());
|
||||
_common.AddRpcHandle(rpcHandle);
|
||||
}
|
||||
|
||||
public void AddCommandListener<T>(Action<T> handle)
|
||||
{
|
||||
_events.AddListener<T>(handle);
|
||||
_common. Events.AddListener<T>(handle);
|
||||
}
|
||||
|
||||
public void AddCommandListener<T>(Func<T,UniTask<T>> func)
|
||||
{
|
||||
_rpc.TryAdd(typeof(T).FullName, F);
|
||||
_common. Rpc.TryAdd(typeof(T).FullName, F);
|
||||
return;
|
||||
|
||||
async UniTask<object> F(object o)
|
||||
@@ -637,12 +609,12 @@ namespace BITKit.Net
|
||||
|
||||
public void RemoveCommandListener<T>(Func<T,UniTask<T>> func)
|
||||
{
|
||||
_rpc.TryRemove(typeof(T).FullName, out _);
|
||||
_common. Rpc.TryRemove(typeof(T).FullName, out _);
|
||||
}
|
||||
|
||||
public void RemoveCommandListener<T>(Action<T> handle)
|
||||
{
|
||||
_events.RemoveListener<T>(handle);
|
||||
_common. Events.RemoveListener<T>(handle);
|
||||
}
|
||||
|
||||
public void SendRT(string rpcName, params object[] pars)
|
||||
@@ -709,7 +681,7 @@ namespace BITKit.Net
|
||||
_client.Send(bytes, KcpChannel.Reliable);
|
||||
}
|
||||
Traffic+=new float2(1,0);
|
||||
_client.Send(_heartBeat, KcpChannel.Unreliable);
|
||||
_client.Send(_common.HeartBeat, KcpChannel.Unreliable);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
Reference in New Issue
Block a user