diff --git a/Src/Core/BITLog/BIT4Log.cs b/Src/Core/BITLog/BIT4Log.cs index aa26fe7..d648e27 100644 --- a/Src/Core/BITLog/BIT4Log.cs +++ b/Src/Core/BITLog/BIT4Log.cs @@ -20,10 +20,10 @@ namespace BITKit OnNextLine = null; } #endif - public static event Action OnLog; - public static event Action OnException; - public static event Action OnWarning; - public static event Action OnNextLine; + public static event Action OnLog = Console.WriteLine; + public static event Action OnException = Console.WriteLine; + public static event Action OnWarning = Console.WriteLine; + public static event Action OnNextLine=Console.WriteLine; #if UNITY_5_3_OR_NEWER [HideInCallstack] #endif diff --git a/Src/Core/Binary/BITBinary.cs b/Src/Core/Binary/BITBinary.cs index fec3264..4a82c1b 100644 --- a/Src/Core/Binary/BITBinary.cs +++ b/Src/Core/Binary/BITBinary.cs @@ -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)) diff --git a/Src/Core/Kcp/KcpNetClient.cs b/Src/Core/Kcp/KcpNetClient.cs index bda7362..a3086e5 100644 --- a/Src/Core/Kcp/KcpNetClient.cs +++ b/Src/Core/Kcp/KcpNetClient.cs @@ -37,20 +37,7 @@ namespace BITKit.Net ); _timer.Elapsed += Tick; _logger.LogInformation("已创建KCP客户端"); - AddCommandListener(x => - { - Id = x.Id; - }); _isConnected.AddListener(ConnectionCallback); - - AddCommandListener(F); - - return; - UniTask F(SimplePing p) - { - p.EndTime = DateTime.Now; - return UniTask.FromResult(p); - } } public event Action OnStartConnect; @@ -83,8 +70,7 @@ namespace BITKit.Net private readonly ValidHandle _isConnected = new(); 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,8 +154,9 @@ namespace BITKit.Net await UniTask.SwitchToThreadPool(); try { - _lastHeartbeat = DateTime.Now; - + _common.LastHeartbeat.GetOrCreate(0); + _common.LastHeartbeat[0] = DateTime.Now; + _client.Connect(address, port); _timer.Start(); @@ -192,6 +179,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 bytes, KcpChannel channel) + private void OnDataInternal(ArraySegment bytes, KcpChannel channel) { - using var ms = new MemoryStream(bytes.ToArray()); - using var reader = new BinaryReader(ms); - - var type = (NetCommandType)ms.ReadByte(); - switch (type) + try { - 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($"已收到指令:{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) - { - - } - 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(); - - - 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()[0]; - var func = _common.Rpc[commandObj.GetType()!.FullName!]; - value = await func.As>>().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(); - 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($"未找到对应的事件:{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; + _common.OnDataInternal(0,bytes,channel); } + catch (Exception e) + { + _logger.LogCritical(e,e.Message); + } + } 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 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 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(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 GetFromServer(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() as object }; - } - - BITBinary.Write(writer, pars); - - var bytes = ms.ToArray(); - - await ms.DisposeAsync(); - await writer.DisposeAsync(); - - _commandQueue.Enqueue(bytes); - - var source = new UniTaskCompletionSource(); - - _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(); - } - - - - public UniTask GetFromClient(int id,string path, params object[] pars) - { - throw new NotImplementedException(); - } - - public void AddRpcHandle(object rpcHandle) - { - _common.AddRpcHandle(rpcHandle); - } - - public void AddCommandListener(Action handle) - { - _common. Events.AddListener(handle); - } - - public void AddCommandListener(Func> func) - { - _common. Rpc.TryAdd(typeof(T).FullName, F); - return; - - async UniTask F(object o) - { - return await func.Invoke((T)o); - } - } - - public void RemoveCommandListener(Func> func) - { - _common. Rpc.TryRemove(typeof(T).FullName, out _); - } - - public void RemoveCommandListener(Action handle) - { - _common. Events.RemoveListener(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() => _common.GetRemoteInterface(); + public void Invoke(Memory bytes) + { + throw new NotImplementedException(); + } + + public UniTask> InvokeAsync(Memory bytes) + { + throw new NotImplementedException(); + } + + public void Invoke(IReadOnlyList bytes) + { + throw new NotImplementedException(); + } + + public UniTask> InvokeAsync(IReadOnlyList bytes) + { + throw new NotImplementedException(); + } } } \ No newline at end of file diff --git a/Src/Core/Kcp/KcpNetServer.cs b/Src/Core/Kcp/KcpNetServer.cs index 795af41..aa85d14 100644 --- a/Src/Core/Kcp/KcpNetServer.cs +++ b/Src/Core/Kcp/KcpNetServer.cs @@ -42,8 +42,6 @@ namespace BITKit.Net private DateTime _now=DateTime.UtcNow; - [NetRpc] - public event Action OnNetRpcTest; public KCPNetServer(ILogger logger) { _logger = logger; @@ -55,23 +53,6 @@ namespace BITKit.Net KCPNet.Config ); _timer.Elapsed += Tick; - //BIT4Log.Log("已创建KCP服务器"); - - AddCommandListener(F); - - AddRpcHandle(this); - - OnNetRpcTest += (_int, _float, _bool) => - { - _logger.LogInformation($"已收到Rpc测试:{_int},{_float},{_bool}"); - }; - - return; - UniTask 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() => _common.GetRemoteInterface(); + public void Invoke(Memory bytes) + { + throw new NotImplementedException(); + } + + public UniTask> InvokeAsync(Memory bytes) + { + throw new NotImplementedException(); + } + + public void Invoke(IReadOnlyList bytes) + { + throw new NotImplementedException(); + } + + public UniTask> InvokeAsync(IReadOnlyList 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 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($"已收到指令:{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 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(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()[0]; - var funcName = commandObj.GetType()!.FullName!; - if (_common.Rpc.TryGetValue(funcName, out var func)) - { - var value = await func.As>>().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 command = default) - { - _common. Events.Invoke(command); - } - - public void AllClientCommand(T command = default) - { - foreach (var id in server.connections.Keys.ToArray()) - { - ClientCommand(id, command); - } - } - - public void ClientCommand(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 GetFromServer(string path=default,params object[] pars) - { - throw new NotImplementedException(); - } - - public async UniTask GetFromClient(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(); - - - _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(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(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 MyRpcTestAsync(string hello) - { - await Task.Delay(1000); - return $"{hello} World"; - } - - public void AddCommandListener(Action handle) - { - _common.Events.AddListener(handle); - } - - public void AddCommandListener(Func> func) - { - _common.Rpc.TryAdd(typeof(T).FullName, F); - return; - - async UniTask F(object o) - { - return await func.Invoke((T)o); - } - } - public void RemoveCommandListener(Func> func) - { - _common. Rpc.TryRemove(typeof(T).FullName, out _); - } - public void AddCommandListenerWithId(Action handle) - { - _common.Events.AddListenerDirect(typeof(T).FullName, Callback); - return; - - void Callback(object value) - { - if (value is ValueTuple 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(Action handle) - { - _common. Events.RemoveListener(handle); - } - - public void RemoveCommandListener(Func 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)); - } - } } } \ No newline at end of file diff --git a/Src/Core/Net/NetProvider.cs b/Src/Core/Net/NetProvider.cs index 8c2202a..6048ab6 100644 --- a/Src/Core/Net/NetProvider.cs +++ b/Src/Core/Net/NetProvider.cs @@ -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; - } - } /// /// 帮助类 /// @@ -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, } /// /// 网络提供服务,包括了基础网络服务,e.g @@ -94,96 +79,9 @@ namespace BITKit /// public interface INetProvider { + public uint TickRate { get; set; } - /// - /// 向服务端发送指令 - /// - /// 远程指令类型 - void ServerCommand(T command = default); - - /// - /// 向所有客户端发送指令 - /// - /// 远程指令类型 - void AllClientCommand(T command = default); - /// - /// 向单个客户端发送指令 - /// - /// 客户端ID - /// 指令实例 - /// 远程指令类型 - void ClientCommand(int id, T command); - - /// - /// 从服务端获取数据 - /// - /// - /// - UniTask GetFromServer(string path = null,params object[] pars); - - /// - /// 从客户端获取数据 - /// - /// 客户端ID - /// 可寻址路劲 e.g. Key - /// 远程指令类型 - /// - UniTask GetFromClient(int id,string path = null,params object[] pars); - - /// - /// 添加RPC远程服务,服务类型为需要的方法标记[RPC] - /// - /// RPC服务实例 - void AddRpcHandle(object rpcHandle); - - /// - /// 监听远程指令 - /// - /// 远程指令回调 - /// 远程指令类型 - void AddCommandListener(Action handle); - /// - /// 取消监听远程指令 - /// - /// - /// - void RemoveCommandListener(Action handle); - /// - /// 监听远程func - /// - /// - /// - void AddCommandListener(Func> func); - - /// - /// 取消监听远程指令 - /// - /// 远程指令回调 - /// 远程指令类型 - void RemoveCommandListener(Func> func); - - /// - /// 向服务端发送Rpc - /// - /// RPC名称 - /// RPC参数 - void SendRT(string rpcName, params object[] pars); - - /// - /// 向指定客户端发送Rpc - /// - /// 远程客户端Id - /// RPC名称 - /// RPC参数 - void SendTargetRT(int id, string rpcName, params object[] pars); - - /// - /// 向所有客户端发送Rpc - /// - /// RPC名称 - /// RPC参数 - void SendAllRT(string rpcName, params object[] pars); /// /// 更新Tick /// @@ -192,6 +90,23 @@ namespace BITKit /// 连接协议握手 /// void HandShake(); + /// + /// 获取远程接口 + /// + /// + /// + T GetRemoteInterface(); + /// + /// 远程调用 + /// + void Invoke(Memory bytes); + /// + /// 异步远程调用,有返回结果 + /// + /// + /// + /// + UniTask> InvokeAsync(Memory bytes); } /// @@ -265,12 +180,6 @@ namespace BITKit /// public IDictionary Connections { get; } - /// - /// 添加远程命令监听,包括客户端Id - /// - /// - /// - void AddCommandListenerWithId(Action handle); /// /// 踢出客户端 /// @@ -292,10 +201,6 @@ namespace BITKit /// 通讯接口 /// public INetProvider NetProvider=>this as INetProvider; - /// - /// 源物体,用于通过代理直接访问 - /// - 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 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> P2P = new(); - public readonly ConcurrentDictionary>> Rpc = new(); - public readonly ConcurrentDictionary RpcMethods = new(); - public readonly ConcurrentDictionary RpcEvents = new(); - public readonly ConcurrentDictionary RpcHandles = new(); + public int Timeout=8000; + + public int Index; + + public readonly ConcurrentDictionary>> P2P = new(); public readonly ConcurrentDictionary LastHeartbeat = new(); + public readonly ConcurrentDictionary RemoteInterfaces = new(); + + public readonly ConcurrentDictionary LocalServices = new(); public readonly ConcurrentQueue<(int id,byte[] bytes)> SendQueue = new(); public readonly ConcurrentDictionary 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 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(); - 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(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() + { + 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(); - 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 bytes) + { + throw new NotImplementedException(); + } + + public async UniTask> InvokeAsync(Memory bytes) + { + var index = Index++; + var source = new UniTaskCompletionSource>(); + P2P.TryAdd(index, source); + + var cancelCts = new CancellationTokenSource(); + cancelCts.CancelAfter(Timeout); + + return await source.Task.AttachExternalCancellation(cancelCts.Token); } } } diff --git a/Src/Core/Sharp/BITSharp.cs b/Src/Core/Sharp/BITSharp.cs index 0c5df96..ff1df51 100644 --- a/Src/Core/Sharp/BITSharp.cs +++ b/Src/Core/Sharp/BITSharp.cs @@ -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 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(); + + 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 GenerateNamespaces(Type type) + { + return ArraySegment.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 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 GetReferencedAssemblies(Type type) { var result = new HashSet(); + + 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(); - - HashSet allTypes = new(); - HashSet 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; @@ -204,19 +285,22 @@ namespace BITKit return sb.ToString(); } - public static Assembly Compile(string code,ScriptOptions scriptOptions=null) + public static Assembly Compile(string code, ScriptOptions scriptOptions = null) { scriptOptions ??= ScriptOptions.Default; - - var script = CSharpScript.Create(code,scriptOptions); + + var script = CSharpScript.Create(code, scriptOptions); script.Compile(); 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; + } } } \ No newline at end of file diff --git a/Src/Core/kcp2k/highlevel/Common.cs b/Src/Core/kcp2k/highlevel/Common.cs index d481acf..d770477 100644 --- a/Src/Core/kcp2k/highlevel/Common.cs +++ b/Src/Core/kcp2k/highlevel/Common.cs @@ -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)"); } diff --git a/Src/Unity/Scripts/DotNetSdkRoslynService.meta b/Src/Unity/Scripts/DotNetSdkRoslynService.meta deleted file mode 100644 index 20fe903..0000000 --- a/Src/Unity/Scripts/DotNetSdkRoslynService.meta +++ /dev/null @@ -1,8 +0,0 @@ -fileFormatVersion: 2 -guid: bf1c155248e26c446b9f5bd892f84eef -folderAsset: yes -DefaultImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Src/Unity/Scripts/DotNetSdkRoslynService/DotNetSdkRoslynService.cs b/Src/Unity/Scripts/DotNetSdkRoslynService/DotNetSdkRoslynService.cs deleted file mode 100644 index b325b35..0000000 --- a/Src/Unity/Scripts/DotNetSdkRoslynService/DotNetSdkRoslynService.cs +++ /dev/null @@ -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 OnInstallFailed; - public static event Action OnDownloadProgress; - - private static string[] _InstalledFiles - { - get - { - if (!PlayerPrefs.HasKey(_InstalledFilesKey)) return Array.Empty(); - var json = PlayerPrefs.GetString(_InstalledFilesKey); - try - { - return JsonConvert.DeserializeObject(json); - } - catch (Exception e) - { - BIT4Log.LogException(e); - } - return Array.Empty(); - } - 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 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("未安装,无法卸载"); - 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(); - } - } -} - diff --git a/Src/Unity/Scripts/DotNetSdkRoslynService/DotNetSdkRoslynService.cs.meta b/Src/Unity/Scripts/DotNetSdkRoslynService/DotNetSdkRoslynService.cs.meta deleted file mode 100644 index 674f24d..0000000 --- a/Src/Unity/Scripts/DotNetSdkRoslynService/DotNetSdkRoslynService.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 56389bdbeb2065541a8e62a4ae5bd746 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Src/Unity/Scripts/UX/DotNetSdkRoslyn/UXDotNetSdkRoslynService.cs b/Src/Unity/Scripts/UX/DotNetSdkRoslyn/UXDotNetSdkRoslynService.cs deleted file mode 100644 index b9b15a0..0000000 --- a/Src/Unity/Scripts/UX/DotNetSdkRoslyn/UXDotNetSdkRoslynService.cs +++ /dev/null @@ -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; - } - } - -} diff --git a/Src/Unity/Scripts/UX/DotNetSdkRoslyn/UXDotNetSdkRoslynService.cs.meta b/Src/Unity/Scripts/UX/DotNetSdkRoslyn/UXDotNetSdkRoslynService.cs.meta deleted file mode 100644 index 37a2268..0000000 --- a/Src/Unity/Scripts/UX/DotNetSdkRoslyn/UXDotNetSdkRoslynService.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8c8c71e08d0a2544aae8e62cd2b86af2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: