1
This commit is contained in:
@@ -8,28 +8,21 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Cysharp.Threading.Tasks;
|
||||
#if UNITY
|
||||
using UnityEngine;
|
||||
#endif
|
||||
using kcp2k;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
|
||||
namespace BITKit
|
||||
{
|
||||
[AttributeUsage(AttributeTargets.Method|AttributeTargets.Event)]
|
||||
public sealed class NetRpcAttribute : Attribute
|
||||
{
|
||||
public readonly bool AddTypeNamePrefix;
|
||||
public NetRpcAttribute()
|
||||
{
|
||||
}
|
||||
public NetRpcAttribute(bool addTypeNamePrefix)
|
||||
{
|
||||
AddTypeNamePrefix = addTypeNamePrefix;
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 帮助类
|
||||
/// </summary>
|
||||
@@ -64,21 +57,13 @@ namespace BITKit
|
||||
public enum NetCommandType:byte
|
||||
{
|
||||
Undefined=0,
|
||||
Command=1,
|
||||
TargetCommand=2,
|
||||
File=3,
|
||||
Rpc=4,
|
||||
AllClientCommand=5,
|
||||
Message=6,
|
||||
Heartbeat=7,
|
||||
Ping=8,
|
||||
GetFromServer=9,
|
||||
GetFromClient=10,
|
||||
ReciveFile=11,
|
||||
ReturnToServer=12,
|
||||
ReturnToClient=13,
|
||||
TargetRpc=14,
|
||||
AllRpc=15,
|
||||
Message=1,
|
||||
Heartbeat=2,
|
||||
Rpc,
|
||||
SetPropertyValue,
|
||||
SetFieldValue,
|
||||
WaitTask,
|
||||
ReturnValue,
|
||||
}
|
||||
/// <summary>
|
||||
/// 网络提供服务,包括了基础网络服务,e.g
|
||||
@@ -94,96 +79,9 @@ namespace BITKit
|
||||
/// </summary>
|
||||
public interface INetProvider
|
||||
{
|
||||
|
||||
public uint TickRate { get; set; }
|
||||
/// <summary>
|
||||
/// 向服务端发送指令
|
||||
/// </summary>
|
||||
/// <typeparam name="T">远程指令类型</typeparam>
|
||||
void ServerCommand<T>(T command = default);
|
||||
|
||||
/// <summary>
|
||||
/// 向所有客户端发送指令
|
||||
/// </summary>
|
||||
/// <typeparam name="T">远程指令类型</typeparam>
|
||||
void AllClientCommand<T>(T command = default);
|
||||
/// <summary>
|
||||
/// 向单个客户端发送指令
|
||||
/// </summary>
|
||||
/// <param name="id">客户端ID</param>
|
||||
/// <param name="command">指令实例</param>
|
||||
/// <typeparam name="T">远程指令类型</typeparam>
|
||||
void ClientCommand<T>(int id, T command);
|
||||
|
||||
/// <summary>
|
||||
/// 从服务端获取数据
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
UniTask<T> GetFromServer<T>(string path = null,params object[] pars);
|
||||
|
||||
/// <summary>
|
||||
/// 从客户端获取数据
|
||||
/// </summary>
|
||||
/// <param name="id">客户端ID</param>
|
||||
/// <param name="addressablePath">可寻址路劲 e.g. Key</param>
|
||||
/// <typeparam name="T">远程指令类型</typeparam>
|
||||
/// <returns></returns>
|
||||
UniTask<T> GetFromClient<T>(int id,string path = null,params object[] pars);
|
||||
|
||||
/// <summary>
|
||||
/// 添加RPC远程服务,服务类型为需要的方法标记[RPC]
|
||||
/// </summary>
|
||||
/// <param name="rpcHandle">RPC服务实例</param>
|
||||
void AddRpcHandle(object rpcHandle);
|
||||
|
||||
/// <summary>
|
||||
/// 监听远程指令
|
||||
/// </summary>
|
||||
/// <param name="handle">远程指令回调</param>
|
||||
/// <typeparam name="T">远程指令类型</typeparam>
|
||||
void AddCommandListener<T>(Action<T> handle);
|
||||
/// <summary>
|
||||
/// 取消监听远程指令
|
||||
/// </summary>
|
||||
/// <param name="handle"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
void RemoveCommandListener<T>(Action<T> handle);
|
||||
|
||||
/// <summary>
|
||||
/// 监听远程func
|
||||
/// </summary>
|
||||
/// <param name="func"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
void AddCommandListener<T>(Func<T,UniTask<T>> func);
|
||||
|
||||
/// <summary>
|
||||
/// 取消监听远程指令
|
||||
/// </summary>
|
||||
/// <param name="handle">远程指令回调</param>
|
||||
/// <typeparam name="T">远程指令类型</typeparam>
|
||||
void RemoveCommandListener<T>(Func<T,UniTask<T>> func);
|
||||
|
||||
/// <summary>
|
||||
/// 向服务端发送Rpc
|
||||
/// </summary>
|
||||
/// <param name="rpcName">RPC名称</param>
|
||||
/// <param name="pars">RPC参数</param>
|
||||
void SendRT(string rpcName, params object[] pars);
|
||||
|
||||
/// <summary>
|
||||
/// 向指定客户端发送Rpc
|
||||
/// </summary>
|
||||
/// <param name="id">远程客户端Id</param>
|
||||
/// <param name="rpcName">RPC名称</param>
|
||||
/// <param name="pars">RPC参数</param>
|
||||
void SendTargetRT(int id, string rpcName, params object[] pars);
|
||||
|
||||
/// <summary>
|
||||
/// 向所有客户端发送Rpc
|
||||
/// </summary>
|
||||
/// <param name="rpcName">RPC名称</param>
|
||||
/// <param name="pars">RPC参数</param>
|
||||
void SendAllRT(string rpcName, params object[] pars);
|
||||
/// <summary>
|
||||
/// 更新Tick
|
||||
/// </summary>
|
||||
@@ -192,6 +90,23 @@ namespace BITKit
|
||||
/// 连接协议握手
|
||||
/// </summary>
|
||||
void HandShake();
|
||||
/// <summary>
|
||||
/// 获取远程接口
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
T GetRemoteInterface<T>();
|
||||
/// <summary>
|
||||
/// 远程调用
|
||||
/// </summary>
|
||||
void Invoke(Memory<byte> bytes);
|
||||
/// <summary>
|
||||
/// 异步远程调用,有返回结果
|
||||
/// </summary>
|
||||
/// <param name="bytes"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <returns></returns>
|
||||
UniTask<Memory<byte>> InvokeAsync(Memory<byte> bytes);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -265,12 +180,6 @@ namespace BITKit
|
||||
/// </summary>
|
||||
public IDictionary<int,EndPoint> Connections { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 添加远程命令监听,包括客户端Id
|
||||
/// </summary>
|
||||
/// <param name="handle"></param>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
void AddCommandListenerWithId<T>(Action<int,T> handle);
|
||||
/// <summary>
|
||||
/// 踢出客户端
|
||||
/// </summary>
|
||||
@@ -292,10 +201,6 @@ namespace BITKit
|
||||
/// 通讯接口
|
||||
/// </summary>
|
||||
public INetProvider NetProvider=>this as INetProvider;
|
||||
/// <summary>
|
||||
/// 源物体,用于通过代理直接访问
|
||||
/// </summary>
|
||||
public object Source => this;
|
||||
|
||||
//基本客户端回调
|
||||
public event Action OnStartConnect;
|
||||
@@ -337,47 +242,257 @@ namespace BITKit
|
||||
void SendServerMessage(string message);
|
||||
}
|
||||
|
||||
public class NetProviderCommon
|
||||
public class NetProviderCommon:INetProvider
|
||||
{
|
||||
public static readonly BITSharp.ICodeGenerator CodeGenerator = new RemoteInterfaceGenerator();
|
||||
|
||||
private class RemoteInterfaceGenerator:BITSharp.CodeGenerator
|
||||
{
|
||||
public INetProvider NetProvider = new NetProviderCommon();
|
||||
|
||||
public override string BeforeGenerate(Type type)
|
||||
{
|
||||
return $"public {nameof(INetProvider)} {nameof(NetProvider)};";
|
||||
}
|
||||
|
||||
public override IReadOnlyList<string> GenerateNamespaces(Type type)
|
||||
{
|
||||
return new string[]
|
||||
{
|
||||
"System.IO",
|
||||
typeof(INetProvider).Namespace,
|
||||
};
|
||||
}
|
||||
|
||||
public override string GenerateMethodContext(MethodInfo methodInfo)
|
||||
{
|
||||
var codeBuilder = new StringBuilder();
|
||||
var parameterInfos = methodInfo.GetParameters();
|
||||
|
||||
using var ms = new MemoryStream();
|
||||
using var writer = new BinaryWriter(ms);
|
||||
|
||||
codeBuilder.AppendLine(" using var ms = new MemoryStream();");
|
||||
codeBuilder.AppendLine(" using var writer = new BinaryWriter(ms);");
|
||||
codeBuilder.AppendLine(" writer.Write((byte)NetCommandType.Rpc);");
|
||||
codeBuilder.AppendLine(" writer.Write((byte)NetCommandType.Rpc);");
|
||||
|
||||
codeBuilder.AppendLine($" writer.Write(\"{methodInfo.DeclaringType!.FullName}\");");
|
||||
codeBuilder.AppendLine($" writer.Write(\"{methodInfo.Name}\");");
|
||||
|
||||
codeBuilder.AppendLine($" writer.Write({parameterInfos.Length});");
|
||||
|
||||
foreach (var parameterInfo in parameterInfos)
|
||||
{
|
||||
codeBuilder.AppendLine($"BITBinary.Write(writer,{parameterInfo.Name});");
|
||||
}
|
||||
|
||||
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
|
||||
|
||||
if (isAwaitable)
|
||||
{
|
||||
var generics = methodInfo.ReturnType.CSharpName();
|
||||
|
||||
generics= generics.Replace(nameof(UniTask), string.Empty);
|
||||
generics = generics.Replace(nameof(Task), string.Empty);
|
||||
|
||||
codeBuilder.AppendLine($" return BITBinary.Read{generics}((await NetProvider.InvokeAsync(ms.ToArray())).ToArray());");
|
||||
}
|
||||
else
|
||||
{
|
||||
codeBuilder.AppendLine(base.GenerateMethodContext(methodInfo));
|
||||
}
|
||||
|
||||
|
||||
return codeBuilder.ToString();
|
||||
}
|
||||
|
||||
public override string GenerateProperty(PropertyInfo propertyInfo)
|
||||
{
|
||||
var codeBuilder = new StringBuilder();
|
||||
|
||||
var source = base.GenerateProperty(propertyInfo);
|
||||
|
||||
source = source.Replace("get;", $"\nget=>_{propertyInfo.Name};");
|
||||
source = source.Replace("set;", $"\nset{{\n_{propertyInfo.Name}=value; \n using var ms = new MemoryStream();\n using var writer = new BinaryWriter(ms);\n writer.Write((byte)NetCommandType.Rpc);\n writer.Write((byte)NetCommandType.SetPropertyValue);\n writer.Write(\"{propertyInfo.DeclaringType!.FullName}\");\n writer.Write(\"{propertyInfo.Name}\");\n BITBinary.Write(writer,value);\n NetProvider.Invoke(ms.ToArray());\n }}");
|
||||
|
||||
codeBuilder.AppendLine(source);
|
||||
codeBuilder.AppendLine($"public {propertyInfo.PropertyType.CSharpName()} _{propertyInfo.Name};");
|
||||
|
||||
return codeBuilder.ToString();
|
||||
}
|
||||
}
|
||||
private void SyntaxTest()
|
||||
{
|
||||
/*
|
||||
using var ms = new MemoryStream();
|
||||
using var writer = new BinaryWriter(ms);
|
||||
writer.Write((byte)NetCommandType.Rpc);
|
||||
writer.Write((byte)NetCommandType.SetPropertyValue);
|
||||
writer.Write(nameof(SyntaxTest));
|
||||
BITBinary.Write(writer,1);
|
||||
NetProvider.Invoke(ms.ToArray());
|
||||
*/
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider { get; set; } = new ServiceCollection().BuildServiceProvider();
|
||||
|
||||
public readonly GenericEvent Events = new();
|
||||
|
||||
public readonly ConcurrentDictionary<int, UniTaskCompletionSource<object>> P2P = new();
|
||||
public readonly ConcurrentDictionary<string,Func<object,UniTask<object>>> Rpc = new();
|
||||
public readonly ConcurrentDictionary<string,MethodInfo> RpcMethods = new();
|
||||
public readonly ConcurrentDictionary<string,EventInfo> RpcEvents = new();
|
||||
public readonly ConcurrentDictionary<string,object> RpcHandles = new();
|
||||
public int Timeout=8000;
|
||||
|
||||
public int Index;
|
||||
|
||||
public readonly ConcurrentDictionary<int, UniTaskCompletionSource<Memory<byte>>> P2P = new();
|
||||
public readonly ConcurrentDictionary<int,DateTime> LastHeartbeat = new();
|
||||
public readonly ConcurrentDictionary<string, object> RemoteInterfaces = new();
|
||||
|
||||
public readonly ConcurrentDictionary<Type,object> LocalServices = new();
|
||||
|
||||
public readonly ConcurrentQueue<(int id,byte[] bytes)> SendQueue = new();
|
||||
public readonly ConcurrentDictionary<int,int> DropCount = new();
|
||||
|
||||
public readonly byte[] HeartBeat = new byte[] { (byte)NetCommandType.Heartbeat };
|
||||
|
||||
public void AddRpcHandle(object rpcHandle)
|
||||
public uint TickRate { get; set; }
|
||||
public void Tick()
|
||||
{
|
||||
var reportBuilder = new StringBuilder();
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
reportBuilder.AppendLine($"正在通过反射注册{rpcHandle.GetType().Name}");
|
||||
foreach (var methodInfo in rpcHandle.GetType().GetMethods())
|
||||
public void HandShake()
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
public async void OnDataInternal(int id,ArraySegment<byte> bytes, KcpChannel channel)
|
||||
{
|
||||
using var ms = new MemoryStream(bytes.ToArray());
|
||||
using var reader = new BinaryReader(ms);
|
||||
|
||||
var type = (NetCommandType)ms.ReadByte();
|
||||
|
||||
var now = DateTime.Now;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
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);
|
||||
case NetCommandType.Heartbeat:
|
||||
{
|
||||
LastHeartbeat.GetOrCreate(id);
|
||||
LastHeartbeat[id]=now;
|
||||
}
|
||||
break;
|
||||
case NetCommandType.Message:
|
||||
{
|
||||
var message = reader.ReadString();
|
||||
BIT4Log.Log<INetProvider>(message);
|
||||
}
|
||||
break;
|
||||
case NetCommandType.Rpc:
|
||||
{
|
||||
var command = (NetCommandType)ms.ReadByte();
|
||||
|
||||
var serviceName = reader.ReadString();
|
||||
var serviceType = BITSharp.GetTypeFromFullName(serviceName);
|
||||
var service = ServiceProvider.GetRequiredService(serviceType);
|
||||
|
||||
switch (command)
|
||||
{
|
||||
case NetCommandType.SetPropertyValue:
|
||||
{
|
||||
serviceType.GetProperty(reader.ReadString())!.SetValue(service,BITBinary.Read(reader));
|
||||
}
|
||||
break;
|
||||
case NetCommandType.SetFieldValue:
|
||||
{
|
||||
serviceType.GetField(reader.ReadString())!.SetValue(service,BITBinary.Read(reader));
|
||||
}
|
||||
break;
|
||||
case NetCommandType.Rpc:
|
||||
{
|
||||
var methodInfo = serviceType.GetMethod(reader.ReadString())!;
|
||||
var parameterLength = reader.ReadInt32();
|
||||
var parameters = new object[parameterLength];
|
||||
for (var i = 0; i < parameterLength; i++)
|
||||
{
|
||||
parameters[i] = BITBinary.Read(reader);
|
||||
}
|
||||
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
|
||||
if (isAwaitable)
|
||||
{
|
||||
dynamic result = methodInfo.Invoke(service, parameters)!;
|
||||
|
||||
reportBuilder.AppendLine($"Add [{methodInfo.Name}] as MethodInfo");
|
||||
object resultValue = null;
|
||||
|
||||
if (methodInfo.ReturnType == typeof(void)
|
||||
||
|
||||
methodInfo.ReturnType == typeof(UniTask)
|
||||
||
|
||||
methodInfo.ReturnType == typeof(UniTask<>)
|
||||
)
|
||||
{
|
||||
await result;
|
||||
resultValue = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultValue = await result;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
methodInfo.Invoke(service, parameters);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
public T GetRemoteInterface<T>()
|
||||
{
|
||||
if(RemoteInterfaces.TryGetValue(typeof(T).FullName!,out var obj))
|
||||
{
|
||||
return (T)obj;
|
||||
}
|
||||
|
||||
foreach (var eventInfo in rpcHandle.GetType().GetEvents())
|
||||
var code = CodeGenerator.Generate(typeof(T));
|
||||
|
||||
var options = ScriptOptions.Default;
|
||||
|
||||
var assemblies = BITSharp.GetReferencedAssemblies(typeof(T));
|
||||
|
||||
foreach (var referencedAssembly in assemblies)
|
||||
{
|
||||
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");
|
||||
options= options.AddReferences(referencedAssembly);
|
||||
}
|
||||
|
||||
var assembly = BITSharp.Compile(code, options);
|
||||
|
||||
var type = assembly.GetExportedTypes().First(x => typeof(T).IsAssignableFrom(x));
|
||||
|
||||
var instance = Activator.CreateInstance(type)!;
|
||||
|
||||
instance.GetType().GetField("NetProvider")!.SetValue(instance,this);
|
||||
|
||||
return (T)instance;
|
||||
}
|
||||
|
||||
public void Invoke(Memory<byte> bytes)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public async UniTask<Memory<byte>> InvokeAsync(Memory<byte> bytes)
|
||||
{
|
||||
var index = Index++;
|
||||
var source = new UniTaskCompletionSource<Memory<byte>>();
|
||||
P2P.TryAdd(index, source);
|
||||
|
||||
var cancelCts = new CancellationTokenSource();
|
||||
cancelCts.CancelAfter(Timeout);
|
||||
|
||||
return await source.Task.AttachExternalCancellation(cancelCts.Token);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user