This commit is contained in:
CortexCore 2025-01-24 18:11:03 +08:00
parent 4b72602bfa
commit 41715e4413
12 changed files with 545 additions and 1391 deletions

View File

@ -20,10 +20,10 @@ namespace BITKit
OnNextLine = null;
}
#endif
public static event Action<string> OnLog;
public static event Action<Exception> OnException;
public static event Action<string> OnWarning;
public static event Action OnNextLine;
public static event Action<string> OnLog = Console.WriteLine;
public static event Action<Exception> OnException = Console.WriteLine;
public static event Action<string> OnWarning = Console.WriteLine;
public static event Action OnNextLine=Console.WriteLine;
#if UNITY_5_3_OR_NEWER
[HideInCallstack]
#endif

View File

@ -89,6 +89,11 @@ namespace BITKit
object ReadInternel(BinaryReader reader)
{
if (reader.ReadBoolean() is false)
{
return null;
}
var typeName = reader.ReadString();
if (netReaders.TryGetValue(typeName, out var netReader))
@ -165,6 +170,11 @@ namespace BITKit
return;
void WriteInterel(BinaryWriter writer, object value)
{
if (value is null)
{
writer.Write(false);
return;
}
var typeName = value.GetType().FullName;
writer.Write(typeName!);
if (netReaders.TryGetValue(typeName, out var netReader))

View File

@ -37,20 +37,7 @@ namespace BITKit.Net
);
_timer.Elapsed += Tick;
_logger.LogInformation("已创建KCP客户端");
AddCommandListener<NetClientAllocateIdCommand>(x =>
{
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;
@ -84,7 +71,6 @@ namespace BITKit.Net
private bool _userConnected;
private int _index = int.MinValue;
private DateTime _lastHeartbeat = DateTime.Now;
private DateTime _now = DateTime.Now;
private TimeSpan _interval = TimeSpan.FromMilliseconds(100);
private string _connectedAddress = "127.0.0.1";
@ -168,7 +154,8 @@ namespace BITKit.Net
await UniTask.SwitchToThreadPool();
try
{
_lastHeartbeat = DateTime.Now;
_common.LastHeartbeat.GetOrCreate(0);
_common.LastHeartbeat[0] = DateTime.Now;
_client.Connect(address, port);
@ -193,6 +180,8 @@ namespace BITKit.Net
_connectedAddress = address;
_connectedPort = port;
return _client.connected;
}
@ -220,271 +209,33 @@ namespace BITKit.Net
try
{
Traffic+=new float2(0,bytes.Count);
OnDataInternel(bytes, channel);
OnDataInternal(bytes, channel);
}
catch (Exception e)
{
_logger.LogCritical(e,e.Message);
}
}
private async void OnDataInternel(ArraySegment<byte> bytes, KcpChannel channel)
private void OnDataInternal(ArraySegment<byte> bytes, KcpChannel channel)
{
using var ms = new MemoryStream(bytes.ToArray());
using var reader = new BinaryReader(ms);
var type = (NetCommandType)ms.ReadByte();
switch (type)
{
case NetCommandType.Message:
_logger.LogInformation($"已收到消息:{reader.ReadString()}");
break;
case NetCommandType.AllClientCommand:
case NetCommandType.Command:
var command = BITBinary.Read(reader);
if (command is object[] { Length: 1 } objs) command = objs[0];
//BIT4Log.Log<KcpClient>($"已收到指令:{command},值:\n{JsonConvert.SerializeObject(command, Formatting.Indented)}");
try
{
if (BITApp.SynchronizationContext is not null)
await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext,
BITApp.CancellationToken);
_common.Events.Invoke(command.GetType().FullName, command);
}
catch (OperationCanceledException)
{
_common.OnDataInternal(0,bytes,channel);
}
catch (Exception e)
{
_logger.LogCritical(e,e.Message);
}
break;
case NetCommandType.Heartbeat:
if (Id is not -1)
{
_isConnected.AddElement(this);
}
_lastHeartbeat = DateTime.Now;
break;
case NetCommandType.Ping:
Ping = (int)(DateTime.Now - _lastPingTime).TotalMilliseconds;
break;
case NetCommandType.ReturnToClient:
var id = reader.ReadInt32();
try
{
if (_common.P2P.TryRemove(id, out var source))
{
if (reader.ReadBoolean())
{
var value = BITBinary.Read(reader);
source.TrySetResult(value);
}
else
{
var message = reader.ReadString();
source.TrySetException(new Exception(message));
}
}
else
{
_logger.LogWarning($"ID为{id}的请求未注册回调");
}
}
catch (Exception e)
{
_logger.LogWarning($"请求返回失败:{id}");
_logger.LogCritical(e,e.Message);
}
break;
case NetCommandType.GetFromClient:
try
{
var requestId = reader.ReadInt32();
using var returnMS = new MemoryStream();
await using var returnWriter = new BinaryWriter(returnMS);
returnWriter.Write((byte)NetCommandType.ReturnToServer);
returnWriter.Write(requestId);
try
{
object value = null;
if (reader.ReadBoolean())
{
var path = reader.ReadString();
var pars = BITBinary.Read(reader).As<object[]>();
if (_common.RpcMethods.TryGetValue(path, out var methodInfo))
{
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
var handle = _common.RpcHandles[path];
try
{
if (isAwaitable)
{
dynamic result = methodInfo.Invoke(handle, pars)!;
value = await result;
}
else
{
value = methodInfo.Invoke(handle, pars);
}
}
catch (Exception)
{
_logger.LogWarning(path);
throw;
}
returnWriter.Write(true);
BITBinary.Write(returnWriter, value);
}
else if (_common.RpcEvents.TryGetValue(path, out var eventInfo))
{
var handle = _common.RpcHandles[path];
var fieldInfo = handle.GetType().GetField(eventInfo.Name,ReflectionHelper.Flags)!;
var eventDelegate = fieldInfo.GetValue(handle) as MulticastDelegate;
foreach (var del in eventDelegate!.GetInvocationList())
{
del.Method.Invoke(del.Target, pars);
}
}
else
{
returnWriter.Write(false);
returnWriter.Write($"未找到对应的Rpc方法:{path}");
}
}
else
{
var commandObj = BITBinary.Read(reader)
.As<object[]>()[0];
var func = _common.Rpc[commandObj.GetType()!.FullName!];
value = await func.As<Func<object, UniTask<object>>>().Invoke(commandObj);
}
returnWriter.Write(true);
BITBinary.Write(returnWriter, value);
}
catch (Exception e)
{
_logger.LogCritical(e,e.Message);
returnWriter.Write(false);
returnWriter.Write(e.Message);
}
var _bytes = returnMS.ToArray();
_commandQueue.Enqueue(_bytes);
}
catch(Exception e)
{
_logger.LogCritical(e,e.Message);
}
break;
case NetCommandType.AllRpc:
case NetCommandType.TargetRpc:
{
var rpcName = reader.ReadString();
var pars = BITBinary.Read(reader).As<object[]>();
if (_common.RpcMethods.TryGetValue(rpcName, out var methodInfo))
{
try
{
methodInfo.Invoke(_common.RpcHandles[rpcName], pars);
}
catch (TargetException targetException)
{
var reportBuilder = new StringBuilder();
reportBuilder.AppendLine("正在检查参数类型");
var parameterInfos = methodInfo.GetParameters();
if (parameterInfos.Length != pars.Length)
{
reportBuilder.AppendLine("参数数量不匹配,期望:" + parameterInfos.Length + ",实际:" + pars.Length);
}
for (var i = 0; i < pars.Length; i++)
{
var parameterInfo = parameterInfos[i];
var parameter = pars[i];
if (parameterInfo.ParameterType != parameter.GetType())
{
reportBuilder.AppendLine($"参数{i}类型不匹配,期望:{parameterInfo.ParameterType},实际:{parameter.GetType()}");
}
else
{
reportBuilder.AppendLine($"参数{parameter.GetType()}类型匹配:{parameterInfo.ParameterType}");
}
}
_logger.LogWarning(reportBuilder.ToString());
throw;
}
catch (Exception e)
{
_logger.LogWarning(rpcName);
throw;
}
}
else if (_common.RpcEvents.TryGetValue(rpcName, out var eventInfo))
{
var handle = _common.RpcHandles[rpcName];
var fieldInfo = handle.GetType().GetField(eventInfo.Name,ReflectionHelper.Flags)!;
var eventDelegate = fieldInfo.GetValue(handle) as MulticastDelegate;
if (eventDelegate is null)
{
//BIT4Log.Warning<KcpNetClient>($"未找到对应的事件:{rpcName}");
}
else
{
foreach (var del in eventDelegate.GetInvocationList())
{
del.Method.Invoke(del.Target, pars);
}
}
}
else
{
_logger.LogWarning($"未找到对应的Rpc方法:{rpcName}");
}
}
break;
default:
_logger.LogWarning($"未知消息类型:{type},字节:{(byte)type}");
if (bytes.Array != null)
_logger.LogInformation(
$"已收到:({Id}, {BitConverter.ToString(bytes.Array, bytes.Offset, bytes.Count)} @ {channel})");
break;
}
}
private async void OnConnectedInternal()
{
// if (BITApp.SynchronizationContext is not null)
// await UniTask.SwitchToSynchronizationContext(BITApp.SynchronizationContext);
// OnConnected?.Invoke();
// _logger.LogInformation("已连接");
}
private async void OnDisconnectInternal()
{
// _logger.LogInformation("连接被断开");
DisconnectInternal();
}
private void OnError(ErrorCode errorCode, string message)
@ -492,157 +243,6 @@ namespace BITKit.Net
_logger.LogInformation($"{_client.remoteEndPoint}异常:{errorCode},{message}");
}
public void ServerCommand<T>(T command = default)
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.Command);
BITBinary.Write(writer,command);
var bytes = ms.ToArray();
_commandQueue.Enqueue(bytes);
}
public void AllClientCommand<T>(T command = default)
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.AllClientCommand);
BITBinary.Write(writer,command);
var bytes = ms.ToArray();
}
public void ClientCommand<T>(int id, T command)
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.Command);
BITBinary.Write(writer,command);
var bytes = ms.ToArray();
_commandQueue.Enqueue(bytes);
}
public async UniTask<T> GetFromServer<T>(string path = default, params object[] pars)
{
if (IsConnected is false)
{
throw new NetOfflineException();
}
var id = _index++;
var ms = new MemoryStream();
var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.GetFromServer);
writer.Write(id);
if (string.IsNullOrEmpty(path))
{
writer.Write(false);
}
else
{
writer.Write(true);
writer.Write(path);
}
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 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)
{
_common.AddRpcHandle(rpcHandle);
}
public void AddCommandListener<T>(Action<T> handle)
{
_common. Events.AddListener<T>(handle);
}
public void AddCommandListener<T>(Func<T,UniTask<T>> func)
{
_common. Rpc.TryAdd(typeof(T).FullName, F);
return;
async UniTask<object> F(object o)
{
return await func.Invoke((T)o);
}
}
public void RemoveCommandListener<T>(Func<T,UniTask<T>> func)
{
_common. Rpc.TryRemove(typeof(T).FullName, out _);
}
public void RemoveCommandListener<T>(Action<T> handle)
{
_common. Events.RemoveListener<T>(handle);
}
public void SendRT(string rpcName, params object[] pars)
{
if (IsConnected is false)
{
throw new NetOfflineException();
}
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.GetFromServer);
writer.Write(++_index);
writer.Write(true);
writer.Write(rpcName);
BITBinary.Write(writer,pars);
var bytes = ms.ToArray();
_commandQueue.Enqueue(bytes);
}
public void SendTargetRT(int id, string rpcName, params object[] pars)
{
throw new NotImplementedException();
}
public void SendAllRT(string rpcName, params object[] pars)
{
throw new NotImplementedException();
}
public void SendServerMessage(string message)
{
@ -655,11 +255,6 @@ namespace BITKit.Net
_client.Send(bytes, KcpChannel.Reliable);
}
#if UNITY_EDITOR
private readonly IntervalUpdate _pingInterval = new(1);
#endif
public void Tick()
{
_now = DateTime.Now;
@ -668,7 +263,7 @@ namespace BITKit.Net
{
if (_client.connected)
{
if (DateTime.Now - _lastHeartbeat > TimeSpan.FromSeconds(5))
if (DateTime.Now - _common.LastHeartbeat.GetOrCreate(0) > TimeSpan.FromSeconds(5))
{
_logger.LogWarning("心跳超时,自动断开");
DisconnectInternal();
@ -713,5 +308,25 @@ namespace BITKit.Net
Traffic+=new float2(2,0);
_client.Send(new byte[]{0x01, 0x02}, KcpChannel.Reliable);
}
public T GetRemoteInterface<T>() => _common.GetRemoteInterface<T>();
public void Invoke(Memory<byte> bytes)
{
throw new NotImplementedException();
}
public UniTask<Memory<byte>> InvokeAsync(Memory<byte> bytes)
{
throw new NotImplementedException();
}
public void Invoke(IReadOnlyList<byte> bytes)
{
throw new NotImplementedException();
}
public UniTask<IReadOnlyList<byte>> InvokeAsync(IReadOnlyList<byte> bytes)
{
throw new NotImplementedException();
}
}
}

View File

@ -42,8 +42,6 @@ namespace BITKit.Net
private DateTime _now=DateTime.UtcNow;
[NetRpc]
public event Action<int, float, bool> OnNetRpcTest;
public KCPNetServer(ILogger<KCPNetServer> logger)
{
_logger = logger;
@ -55,23 +53,6 @@ namespace BITKit.Net
KCPNet.Config
);
_timer.Elapsed += Tick;
//BIT4Log.Log<KCPNetServer>("已创建KCP服务器");
AddCommandListener<SimplePing>(F);
AddRpcHandle(this);
OnNetRpcTest += (_int, _float, _bool) =>
{
_logger.LogInformation($"已收到Rpc测试:{_int},{_float},{_bool}");
};
return;
UniTask<SimplePing> F(SimplePing p)
{
p.EndTime = DateTime.Now;
return UniTask.FromResult(p);
}
}
private void Tick(object sender, ElapsedEventArgs e)
@ -212,15 +193,30 @@ namespace BITKit.Net
}
}
public T GetRemoteInterface<T>() => _common.GetRemoteInterface<T>();
public void Invoke(Memory<byte> bytes)
{
throw new NotImplementedException();
}
public UniTask<Memory<byte>> InvokeAsync(Memory<byte> bytes)
{
throw new NotImplementedException();
}
public void Invoke(IReadOnlyList<byte> bytes)
{
throw new NotImplementedException();
}
public UniTask<IReadOnlyList<byte>> InvokeAsync(IReadOnlyList<byte> bytes)
{
throw new NotImplementedException();
}
private void OnConnectedInternel(int id)
{
//server.connections[id].peer.kcp.SetInterval(TickRate);
OnClientConnected?.Invoke(id);
ClientCommand(id,new NetClientAllocateIdCommand
{
Id = id,
Ip = server.connections[id].remoteEndPoint.ToString()
});
_logger.LogInformation($"{id}已连接到:{Name}");
SendMessageToClient(id, $"成功连接到服务器:{Name}");
}
@ -237,421 +233,22 @@ namespace BITKit.Net
{
try
{
OnDataInternel(Id, bytes, channel);
_common.OnDataInternal(Id, bytes, channel);
}
catch (Exception e)
{
BIT4Log.LogException(e);
_logger.LogCritical(e, e.Message);
}
}
private async void OnDataInternel(int Id, ArraySegment<byte> bytes, KcpChannel channel)
{
using var ms = new MemoryStream(bytes.ToArray());
using var reader = new BinaryReader(ms);
try
{
var type = (NetCommandType)ms.ReadByte();
switch (type)
{
case NetCommandType.Message:
_logger.LogInformation($"已收到消息,{Id}:{reader.ReadString()}");
break;
case NetCommandType.Command:
var command = BITBinary.Read(reader);
if (command is object[] { Length: 1 } objs) command = objs[0];
//BIT4Log.Log<KCPNetServer>($"已收到指令:{command},值:\n{JsonConvert.SerializeObject(command, Formatting.Indented)}");
_common.Events.Invoke(command.GetType().FullName, command);
(int Id, object Command) tuple = (Id, command);
_common.Events.InvokeDirect(command.GetType().FullName, tuple);
break;
case NetCommandType.Heartbeat:
if (Connections.ContainsKey(Id))
{
if (_common.LastHeartbeat.ContainsKey(Id))
{
_common.LastHeartbeat[Id] = _now;
}
else
{
_common.LastHeartbeat.TryAdd(Id, _now);
OnConnectedInternel(Id);
}
}
break;
case NetCommandType.AllClientCommand:
foreach (var id in server.connections.Keys.ToArray())
{
_common.SendQueue.Enqueue((id,bytes.ToArray()));
}
break;
case NetCommandType.TargetCommand:
var targetId = reader.ReadInt32();
_common.SendQueue.Enqueue((targetId,bytes.ToArray()));
break;
case NetCommandType.Ping:
_common.SendQueue.Enqueue((Id,new byte[] { (byte)NetCommandType.Ping }));
break;
case NetCommandType.GetFromServer:
{
var requestId = reader.ReadInt32();
using var returnMS = new MemoryStream();
await using var returnWriter = new BinaryWriter(returnMS);
returnWriter.Write((byte)NetCommandType.ReturnToClient);
returnWriter.Write(requestId);
if (reader.ReadBoolean())
{
var path = reader.ReadString();
var pars = BITBinary.Read(reader).As<object[]>();
object value = null;
if (_common.RpcMethods.TryGetValue(path, out var methodInfo))
{
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
var handle = _common.RpcHandles[path];
if (methodInfo.GetParameters().Length is 0)
{
pars = new object[] { };
}
try
{
if (isAwaitable)
{
dynamic result = methodInfo.Invoke(handle, pars)!;
if (methodInfo.ReturnType == typeof(void)
||
methodInfo.ReturnType == typeof(UniTask)
||
methodInfo.ReturnType == typeof(UniTask<>)
)
{
await result;
value = -1;
}
else
{
value = await result;
}
}
else
{
value = methodInfo.Invoke(handle, pars);
}
}
catch (Exception e)
{
if(e is TargetInvocationException tie)
e = tie.InnerException;
returnWriter.Write(false);
returnWriter.Write(e.Message);
var _bytes = returnMS.ToArray();
_common.SendQueue.Enqueue((Id,_bytes));
if (e is InGameException inGameException)
{
BIT4Log.Warning<KCPNetServer>(inGameException.Message);
return;
}
BIT4Log.LogException(e);
return;
}
returnWriter.Write(true);
}else if (_common.RpcEvents.TryGetValue(path, out var eventInfo))
{
var handle = _common.RpcHandles[path];
var fieldInfo = handle.GetType().GetField(eventInfo.Name,ReflectionHelper.Flags)!;
var eventDelegate = fieldInfo.GetValue(handle) as MulticastDelegate;
foreach (var del in eventDelegate!.GetInvocationList())
{
del.Method.Invoke(del.Target, pars);
}
}
else
{
throw new Exception($"未找到对应的Rpc方法:{path}");
}
if (value is not null)
{
BITBinary.Write(returnWriter, value);
}
}
else
{
var commandObjs = BITBinary.Read(reader);
var commandObj = commandObjs.As<object[]>()[0];
var funcName = commandObj.GetType()!.FullName!;
if (_common.Rpc.TryGetValue(funcName, out var func))
{
var value = await func.As<Func<object, UniTask<object>>>().Invoke(commandObj);
returnWriter.Write(true);
BITBinary.Write(returnWriter, value);
}
else
{
throw new Exception($"未找到对应的Rpc方法:{funcName}");
}
}
{
_common.SendQueue.Enqueue((Id,returnMS.ToArray()));
}
}
break;
case NetCommandType.ReturnToServer:
{
var id = reader.ReadInt32();
if (_common.P2P.TryRemove(id, out var source))
{
if (reader.ReadBoolean())
{
var value = BITBinary.Read(reader);
source.TrySetResult(value);
}
else
{
var message = reader.ReadString();
source.TrySetException(new Exception(message));
}
}
else
{
_logger.LogWarning($"ID为{id}的请求未注册回调");
}
}
break;
default:
_logger.LogInformation($"未知消息类型:{type},字节:{(byte)type}");
_logger.LogInformation(
$"已收到:({Id}, {BitConverter.ToString(bytes.Array, bytes.Offset, bytes.Count)} @ {channel})");
break;
}
}
catch (Exception e)
{
ms.Close();
reader.Close();
BIT4Log.LogException(e);
}
}
private void OnError(int Id, ErrorCode errorCode, string message)
{
_logger.LogInformation($"异常:{errorCode},{message}");
}
public void ServerCommand<T>(T command = default)
{
_common. Events.Invoke<T>(command);
}
public void AllClientCommand<T>(T command = default)
{
foreach (var id in server.connections.Keys.ToArray())
{
ClientCommand(id, command);
}
}
public void ClientCommand<T>(int id, T command)
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.Command);
BITBinary.Write(writer,command);
_common.SendQueue.Enqueue((id,ms.ToArray()));
}
public UniTask<T> GetFromServer<T>(string path=default,params object[] pars)
{
throw new NotImplementedException();
}
public async UniTask<T> GetFromClient<T>(int id,string path=default, params object[] pars)
{
await UniTask.SwitchToThreadPool();
var index = _index++;
var ms = new MemoryStream();
var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.GetFromClient);
writer.Write(index);
if (string.IsNullOrEmpty(path))
{
writer.Write(false);
}
else
{
writer.Write(true);
writer.Write(path);
}
BITBinary.Write(writer,pars);
var bytes = ms.ToArray();
await ms.DisposeAsync();
await writer.DisposeAsync();
_common.SendQueue.Enqueue((id,bytes));
var source = new UniTaskCompletionSource<object>();
_common.P2P.TryAdd(index, source);
var timeoutCts = new CancellationTokenSource();
timeoutCts.CancelAfter(5000); // 设置超时时间
var value =await source.Task.AttachExternalCancellation(timeoutCts.Token);
//await BITApp.SwitchToMainThread();
if (value is Exception e)
{
throw e;
}
if (UniTask.CompletedTask is T t)
{
return t;
}
if (typeof(T) == typeof(UniTaskVoid))
{
return default;
}
return (T)value;
}
public void AddRpcHandle(object rpcHandle)
{
foreach (var methodInfo in rpcHandle.GetType().GetMethods())
{
var att = methodInfo.GetCustomAttribute<NetRpcAttribute>(true);
if(att is null)continue;
_common.RpcMethods.TryAdd(methodInfo.Name, methodInfo);
_common.RpcHandles.TryAdd(methodInfo.Name, rpcHandle);
}
foreach (var eventInfo in rpcHandle.GetType().GetEvents())
{
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>(true);
if(att is null)continue;
_common. RpcEvents.TryAdd(eventInfo.Name, eventInfo);
_common. RpcHandles.TryAdd(eventInfo.Name, rpcHandle);
}
}
[NetRpc]
public string MyRpcTest(string hello)
{
return "Hello World";
}
[NetRpc]
public async UniTask<string> MyRpcTestAsync(string hello)
{
await Task.Delay(1000);
return $"{hello} World";
}
public void AddCommandListener<T>(Action<T> handle)
{
_common.Events.AddListener<T>(handle);
}
public void AddCommandListener<T>(Func<T,UniTask<T>> func)
{
_common.Rpc.TryAdd(typeof(T).FullName, F);
return;
async UniTask<object> F(object o)
{
return await func.Invoke((T)o);
}
}
public void RemoveCommandListener<T>(Func<T,UniTask<T>> func)
{
_common. Rpc.TryRemove(typeof(T).FullName, out _);
}
public void AddCommandListenerWithId<T>(Action<int, T> handle)
{
_common.Events.AddListenerDirect(typeof(T).FullName, Callback);
return;
void Callback(object value)
{
if (value is ValueTuple<int, object> tuple && tuple.Item2 is T)
{
handle.Invoke(tuple.Item1, (T)tuple.Item2);
}
else
{
Console.WriteLine(value);
}
}
}
public void KickClient(int id)
{
server.Disconnect(id);
}
public void RemoveCommandListener<T>(Action<T> handle)
{
_common. Events.RemoveListener<T>(handle);
}
public void RemoveCommandListener<T>(Func<string, T> func)
{
throw new NotImplementedException();
}
public void SendRT(string rpcName, params object[] pars)
{
throw new NotImplementedException("服务端不支持此方法");
}
public void SendTargetRT(int id, string rpcName, params object[] pars)
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.TargetRpc);
writer.Write(rpcName);
BITBinary.Write(writer,pars);
var bytes = ms.ToArray();
_common.SendQueue.Enqueue((id,bytes));
}
public void SendAllRT(string rpcName, params object[] pars)
{
using var ms = new MemoryStream();
using var writer = new BinaryWriter(ms);
writer.Write((byte)NetCommandType.AllRpc);
writer.Write(rpcName);
BITBinary.Write(writer,pars);
var bytes = ms.ToArray();
foreach (var id in server.connections.Keys)
{
_common.SendQueue.Enqueue((id,bytes));
}
}
}
}

View File

@ -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()
{
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");
throw new NotImplementedException();
}
foreach (var eventInfo in rpcHandle.GetType().GetEvents())
public async void OnDataInternal(int id,ArraySegment<byte> bytes, KcpChannel channel)
{
var att = eventInfo.GetCustomAttribute<NetRpcAttribute>();
if (att is null) continue;
using var ms = new MemoryStream(bytes.ToArray());
using var reader = new BinaryReader(ms);
RpcEvents.TryAdd(eventInfo.Name, eventInfo);
RpcHandles.TryAdd(eventInfo.Name, rpcHandle);
var type = (NetCommandType)ms.ReadByte();
reportBuilder.AppendLine($"Add [{eventInfo.Name}] as EventInfo");
}
var now = DateTime.Now;
switch (type)
{
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)!;
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;
}
var code = CodeGenerator.Generate(typeof(T));
var options = ScriptOptions.Default;
var assemblies = BITSharp.GetReferencedAssemblies(typeof(T));
foreach (var referencedAssembly in assemblies)
{
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);
}
}
}

View File

@ -2,31 +2,216 @@ using System;
using System.CodeDom.Compiler;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Numerics;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using BITKit.Mod;
using Microsoft.CodeAnalysis.CSharp.Scripting;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.CodeAnalysis.Scripting;
#if UNITY_5_3_OR_NEWER
using System.Collections;
using UnityEngine;
using static BITKit.Mod.DotNetSdkRoslynService;
#endif
#if UNITY_EDITOR
using UnityEditor;
#endif
namespace BITKit
{
public class BITSharp
public static class BITSharp
{
public static ICodeGenerator SharedCodeGenerator = new CodeGenerator();
public interface ICodeGenerator
{
public string Generate(Type type,string className=null);
IReadOnlyList<string> GenerateNamespaces(Type type);
string BeforeGenerate(Type type);
string AfterGenerate(Type type);
string GenerateMethod(MethodInfo methodInfo);
string GenerateMethodContext(MethodInfo methodInfo);
string GenerateField(FieldInfo fieldInfo);
string GenerateProperty(PropertyInfo propertyInfo);
string GenerateEvent(EventInfo eventInfo);
}
public class CodeGenerator:ICodeGenerator
{
public virtual string Generate(Type type, string className = null)
{
if (type.IsInterface is false) throw new InvalidDataException("The type have to be interface");
var codeBuilder = new StringBuilder();
var nameSpaces = new HashSet<string>();
foreach (var propertyInfo in type.GetProperties())
{
nameSpaces.Add(propertyInfo.PropertyType.Namespace);
}
foreach (var fieldInfo in type.GetFields())
{
nameSpaces.Add(fieldInfo.FieldType.Namespace);
}
foreach (var method in type.GetMethods())
{
if (method.DeclaringType is not null)
{
nameSpaces.Add(method.DeclaringType.Namespace);
}
nameSpaces.Add(method.ReturnType.Namespace);
foreach (var argument in method.GetGenericArguments())
{
nameSpaces.Add(argument.Namespace);
}
foreach (var parameterInfo in method.GetParameters())
{
nameSpaces.Add(parameterInfo.ParameterType.Namespace);
}
}
nameSpaces.Add("System");
nameSpaces.Add("System.Collections.Concurrent");
nameSpaces.Add("System.Collections.Generic");
nameSpaces.Add(type.Namespace);
foreach (var name in GenerateNamespaces(type))
{
nameSpaces.Add(name);
}
codeBuilder.AppendLine(string.Join('\n', nameSpaces.Where(x=>string.IsNullOrEmpty(x) is false).Select(x=>$"using {x};")));
//codeBuilder.AppendLine("namespace BITSharpGen");
//codeBuilder.AppendLine("{");
codeBuilder.AppendLine($"public class @{type.Name}Gen : {type.Name}");
codeBuilder.AppendLine("{");
codeBuilder.AppendLine(BeforeGenerate(type));
foreach (var fieldInfo in type.GetFields())
{
codeBuilder.AppendLine(GenerateField(fieldInfo));
}
foreach (var propertyInfo in type.GetProperties())
{
codeBuilder.AppendLine(GenerateProperty(propertyInfo));
}
// 遍历接口的所有成员,生成方法的默认实现
foreach (var method in type.GetMethods())
{
codeBuilder.AppendLine(GenerateMethod(method));
}
codeBuilder.AppendLine(AfterGenerate(type));
codeBuilder.AppendLine("}");
return codeBuilder.ToString();
}
public virtual IReadOnlyList<string> GenerateNamespaces(Type type)
{
return ArraySegment<string>.Empty;
}
public virtual string BeforeGenerate(Type type)
{
return string.Empty;
}
public virtual string AfterGenerate(Type type)
{
return string.Empty;
}
public virtual string GenerateMethod(MethodInfo methodInfo)
{
var codeBuilder = new StringBuilder();
HashSet<MethodInfo> propertyMethods = new();
foreach (var propertyInfo in methodInfo.DeclaringType!.GetProperties())
{
if (propertyInfo.GetMethod is not null)
{
propertyMethods.Add(propertyInfo.GetMethod);
}
if (propertyInfo.SetMethod is not null)
{
propertyMethods.Add(propertyInfo.SetMethod);
}
}
if(propertyMethods.Contains(methodInfo))return string.Empty;
string methodName = methodInfo.Name;
string parameters = string.Join(", ", methodInfo.GetParameters()
.Select(p => $"{CSharpName(p.ParameterType)} {p.Name}"));
var isAwaitable = methodInfo.ReturnType.GetMethod(nameof(Task.GetAwaiter)) != null;
// 检查是否为泛型方法
if (methodInfo.IsGenericMethod)
{
// 处理泛型方法
var genericArgs = string.Join(", ", methodInfo.GetGenericArguments()
.Select(CSharpName));
if (methodInfo.ReturnType == typeof(void))
{
codeBuilder.AppendLine($" public void {methodName}<{genericArgs}>({parameters}) {{ \n{GenerateMethodContext(methodInfo)}\n }}");
}
else
{
codeBuilder.AppendLine($" public {(isAwaitable?"async":string.Empty)} {CSharpName(methodInfo.ReturnType)} {methodName}<{genericArgs}>({parameters}) {{ {GenerateMethodContext(methodInfo)} }}");
}
}
else
{
if (methodInfo.ReturnType == typeof(void))
{
codeBuilder.AppendLine($" public void {methodName}({parameters}) {{ {GenerateMethodContext(methodInfo)}}}"); // Void 方法默认空实现
}
else
{
// 返回默认值
codeBuilder.AppendLine($" public {(isAwaitable?"async":string.Empty)} {CSharpName(methodInfo.ReturnType)} {methodName}({parameters}) {{ {GenerateMethodContext(methodInfo)} }}");
}
}
return codeBuilder.ToString();
}
public virtual string GenerateMethodContext(MethodInfo methodInfo)
{
return methodInfo.ReturnType == typeof(void) ? string.Empty : $"return default;";
}
public virtual string GenerateField(FieldInfo fieldInfo)
{
return $"public {CSharpName(fieldInfo.FieldType)} {fieldInfo.Name};";
}
public virtual string GenerateProperty(PropertyInfo propertyInfo)
{
return $"public {CSharpName(propertyInfo.PropertyType)} {propertyInfo.Name}" + "{ get;set; }";
}
public virtual string GenerateEvent(EventInfo eventInfo)
{
throw new NotImplementedException();
}
}
#if UNITY_5_3_OR_NEWER
[RuntimeInitializeOnLoadMethod]
private static void Reload()
@ -65,6 +250,9 @@ namespace BITKit
public static IReadOnlyCollection<Assembly> GetReferencedAssemblies(Type type)
{
var result = new HashSet<Assembly>();
result.Add(type.Assembly);
foreach (var fieldInfo in type.GetFields())
{
result.Add(fieldInfo.FieldType.Assembly);
@ -84,114 +272,7 @@ namespace BITKit
}
return result;
}
public static string GenerateCodeFromInterface(Type type,string namespaceName = null)
{
if (type.IsInterface is false) throw new InvalidDataException("The type have to be interface");
var codeBuilder = new StringBuilder();
var nameSpaces = new HashSet<string>();
HashSet<Type> allTypes = new();
HashSet<MethodInfo> propertyMethods = new();
foreach (var propertyInfo in type.GetProperties())
{
if (propertyInfo.GetMethod is not null)
{
propertyMethods.Add(propertyInfo.GetMethod);
}
if (propertyInfo.SetMethod is not null)
{
propertyMethods.Add(propertyInfo.SetMethod);
}
}
foreach (var method in type.GetMethods())
{
if (method.DeclaringType is not null)
{
nameSpaces.Add(method.DeclaringType.Namespace);
}
nameSpaces.Add(method.ReturnType.Namespace);
foreach (var argument in method.GetGenericArguments())
{
nameSpaces.Add(argument.Namespace);
}
}
nameSpaces.Add("System");
nameSpaces.Add("System.Collections.Concurrent");
nameSpaces.Add("System.Collections.Generic");
codeBuilder.AppendLine(string.Join('\n', nameSpaces.Select(x=>$"using {x};")));
//codeBuilder.AppendLine("namespace BITSharpGen");
//codeBuilder.AppendLine("{");
codeBuilder.AppendLine($"public class @{type.Name}Gen : {type.Name}");
codeBuilder.AppendLine("{");
foreach (var propertyInfo in type.GetProperties())
{
codeBuilder.AppendLine($"public {CSharpName(propertyInfo.PropertyType)} {propertyInfo.Name}" + "{ get;set; }");
}
// 遍历接口的所有成员,生成方法的默认实现
foreach (var method in type.GetMethods())
{
if(propertyMethods.Contains(method))continue;
string methodName = method.Name;
string parameters = string.Join(", ", method.GetParameters()
.Select(p => $"{CSharpName(p.ParameterType)} {p.Name}"));
// 检查是否为泛型方法
if (method.IsGenericMethod)
{
// 处理泛型方法
var genericArgs = string.Join(", ", method.GetGenericArguments()
.Select(arg => CSharpName(arg)));
if (method.ReturnType == typeof(void))
{
codeBuilder.AppendLine($" public void {methodName}<{genericArgs}>({parameters}) {{ }}");
}
else
{
codeBuilder.AppendLine($" public {CSharpName(method.ReturnType)} {methodName}<{genericArgs}>({parameters}) {{ return default({CSharpName(method.ReturnType)}); }}");
}
}
else
{
if (method.ReturnType == typeof(void))
{
codeBuilder.AppendLine($" public void {methodName}({parameters}) {{ }}"); // Void 方法默认空实现
}
else
{
// 返回默认值
codeBuilder.AppendLine($" public {CSharpName(method.ReturnType)} {methodName}({parameters}) {{ return default({CSharpName(method.ReturnType)}); }}");
}
}
}
//codeBuilder.AppendLine("\t}");
codeBuilder.AppendLine("}");
return codeBuilder.ToString();
}
public static string CSharpName(Type type)
public static string CSharpName(this Type type)
{
var sb = new StringBuilder();
var name = type.Name;
@ -213,10 +294,13 @@ namespace BITKit
using var ms = new MemoryStream();
var result = script.GetCompilation().Emit(ms);
if (!result.Success) throw new Exception(string.Join("\n",result.Diagnostics));
ms.Seek(0, SeekOrigin.Begin);
var assembly = Assembly.Load(ms.ToArray()); // 加载程序集
return assembly;
}
}
}

View File

@ -43,8 +43,6 @@ namespace kcp2k
{
Log.Warning($"Kcp: failed to set Socket RecvBufSize = {recvBufferSize} SendBufSize = {sendBufferSize}");
}
Log.Info($"Kcp: RecvBuf = {initialReceive}=>{socket.ReceiveBufferSize} ({socket.ReceiveBufferSize/initialReceive}x) SendBuf = {initialSend}=>{socket.SendBufferSize} ({socket.SendBufferSize/initialSend}x)");
}

View File

@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: bf1c155248e26c446b9f5bd892f84eef
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,137 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Text;
using Cysharp.Threading.Tasks;
using Newtonsoft.Json;
using Unity.SharpZipLib.Utils;
using UnityEngine;
using UnityEngine.Experimental.Audio;
using UnityEngine.Networking;
namespace BITKit.Mod
{
public class DotNetSdkRoslynService
{
private const string _InstalledFilesKey = "DotNetSdkRoslyn_InstalledFiles";
public static string DownloadUrl =>
"http://server.bitfall.icu:3000/root/BITKit.DotNetSdkRoslyn.Unity/archive/main.zip";
public static bool Installed=>File.Exists(CSCPath);
public static string CSCPath =>
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(BITKit),"DotnetSdkRoslyn","csc.dll");
public static string FolderPath =>
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), nameof(BITKit),"DotnetSdkRoslyn");
public static event Action OnInstall;
public static event Action OnInstalled;
public static event Action OnUnInstalled;
public static event Action<Exception> OnInstallFailed;
public static event Action<float> OnDownloadProgress;
private static string[] _InstalledFiles
{
get
{
if (!PlayerPrefs.HasKey(_InstalledFilesKey)) return Array.Empty<string>();
var json = PlayerPrefs.GetString(_InstalledFilesKey);
try
{
return JsonConvert.DeserializeObject<string[]>(json);
}
catch (Exception e)
{
BIT4Log.LogException(e);
}
return Array.Empty<string>();
}
set
{
PlayerPrefs.SetString(_InstalledFilesKey, JsonConvert.SerializeObject(value));
PlayerPrefs.Save();
}
}
public static async UniTask Install()
{
await UniTask.SwitchToMainThread();
OnInstall?.Invoke();
var request = UnityWebRequest.Get(DownloadUrl);
try
{
await request.SendWebRequest();
while (request.isDone is false)
{
await UniTask.SwitchToMainThread();
OnDownloadProgress?.Invoke(request.downloadProgress);
await UniTask.NextFrame();
}
OnDownloadProgress?.Invoke(1);
using var ms = new MemoryStream(request.downloadHandler.data);
request.Dispose();
await UniTask.SwitchToTaskPool();
var zipArchive = new ZipArchive(ms);
PathHelper.EnsureDirectoryCreated(FolderPath);
List<string> installedFiles = new();
var reportBuilder =new StringBuilder();
reportBuilder.AppendLine($"正在安装文件到:{FolderPath}");
var entryFolder = zipArchive.Entries[0].FullName;
foreach (var zipArchiveEntry in zipArchive.Entries)
{
var entryPath = Path.Combine(FolderPath, zipArchiveEntry.FullName.Replace(entryFolder,string.Empty));
if (zipArchiveEntry.Name is "")
{
reportBuilder.AppendLine($"正在创建目录:{entryPath}");
PathHelper.EnsureDirectoryCreated(entryPath);
}
else
{
reportBuilder.AppendLine($"正在解压文件:{entryPath}");
await using var entryStream = zipArchiveEntry.Open();
await using var fs = System.IO.File.Create(entryPath);
await entryStream.CopyToAsync(fs);
installedFiles.Add(entryPath);
}
}
await UniTask.SwitchToMainThread();
_InstalledFiles = installedFiles.ToArray();
installedFiles.Clear();
}
catch (Exception e)
{
await UniTask.SwitchToMainThread();
request.Dispose();
OnInstallFailed?.Invoke(e);
return;
}
OnInstalled?.Invoke();
}
public static async UniTask UnInstall()
{
if (Installed is false)
{
BIT4Log.Warning<DotNetSdkRoslynService>("未安装,无法卸载");
return;
}
await UniTask.SwitchToMainThread();
var files = _InstalledFiles;
await UniTask.SwitchToTaskPool();
foreach (var path in files)
{
File.Delete(path);
}
await UniTask.SwitchToMainThread();
PlayerPrefs.DeleteKey(_InstalledFilesKey);
PlayerPrefs.Save();
OnUnInstalled?.Invoke();
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 56389bdbeb2065541a8e62a4ae5bd746
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:

View File

@ -1,98 +0,0 @@
using System;
using System.Collections;
using System.Collections.Generic;
using BITKit.Mod;
using Cysharp.Threading.Tasks;
using UnityEngine;
using UnityEngine.UIElements;
using static BITKit.Mod.DotNetSdkRoslynService;
namespace BITKit.UX
{
public class UXDotNetSdkRoslynService : MonoBehaviour
{
[UXBindPath("install-roslyn-button")]
private Button _installButton;
[UXBindPath("uninstall-roslyn-button")]
private Button _uninstallButton;
[UXBindPath("install-roslyn-fill")]
private VisualElement _installFill;
private void OnEnable()
{
OnInstall+=_OnInstall;
OnInstalled+=_OnInstalled;
OnUnInstalled+=_OnUnInstalled;
OnDownloadProgress+=_OnDownloadProgress;
OnInstallFailed += _OnInstallFailed;
}
private void OnDisable()
{
OnInstall-=_OnInstall;
OnInstalled-=_OnInstalled;
OnUnInstalled-=_OnUnInstalled;
OnDownloadProgress-=_OnDownloadProgress;
OnInstallFailed -= _OnInstallFailed;
}
private void Start()
{
UXUtils.Inject(this);
if (Installed)
{
_OnInstalled();
}
else
{
_OnUnInstalled();
}
_installButton.clicked +=()=>Install().Forget();
_uninstallButton.clicked +=()=>
{
_uninstallButton.SetEnabled(false);
UnInstall().Forget();
};
}
private void _OnInstall()
{
_installButton.text = "准备中...";
_installButton.SetEnabled(false);
_uninstallButton.SetEnabled(false);
}
private void _OnUnInstalled()
{
_installButton.text = "安装";
_uninstallButton.SetEnabled(false);
_installButton.SetEnabled(true);
_installFill.style.width = new StyleLength(Length.Percent(0));
}
private void _OnInstalled()
{
_installButton.text = "重新安装";
_installButton.SetEnabled(true);
_uninstallButton.SetEnabled(true);
_installFill.style.width = new StyleLength(Length.Percent(0));
}
private void _OnDownloadProgress(float progress)
{
if (progress >= 1)
{
_installButton.text = "安装中...";
}
else
{
_installButton.text = $"下载中...{progress:P}";
}
_installFill.style.width = new StyleLength(Length.Percent((int)(progress * 100)));
}
private void _OnInstallFailed(Exception e)
{
_installButton.tooltip = e.Message;
}
}
}

View File

@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 8c8c71e08d0a2544aae8e62cd2b86af2
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant: