From 7bc244ff49bd012034c6426a12792d334ba7d7de Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Sat, 11 May 2024 13:14:08 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0HTTP=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=EF=BC=88WebSocket=EF=BC=89=20(#74)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加HTTP相关(WebSocket) * 添加WebDataRequest --- Api/Transmittal/DataRequest.cs | 30 +-- Api/Transmittal/WebDataRequest.cs | 136 +++++++++++ Api/Utility/PluginConfig.cs | 2 +- Controller/AddonController.cs | 4 +- Controller/RunTimeController.cs | 2 +- Controller/SocketHandlerController.cs | 2 +- Docs/FunGame.Core.xml | 64 ++++- Interface/Base/{ => Addons}/IAddon.cs | 4 +- .../Base/{ => Addons}/IAddonController.cs | 2 +- Interface/Base/{ => Addons}/IGameMap.cs | 2 +- Interface/Base/{ => Addons}/IGameMode.cs | 4 +- .../Base/{ => Addons}/IGameModeServer.cs | 2 +- Interface/Base/{ => Addons}/IPlugin.cs | 4 +- Interface/Base/HTTP/IHTTPClient.cs | 12 + Interface/Base/HTTP/IHTTPListener.cs | 12 + Interface/Base/{ISocket.cs => IBaseSocket.cs} | 4 +- Interface/Base/{ => Sockets}/IClientSocket.cs | 2 +- Interface/Base/Sockets/ISocket.cs | 10 + .../Base/{ => Sockets}/ISocketHandler.cs | 2 +- .../Base/{ => Sockets}/ISocketHeartBeat.cs | 2 +- Library/Common/Addon/CharacterMode.cs | 2 +- Library/Common/Addon/GameMap.cs | 2 +- Library/Common/Addon/GameMode.cs | 1 + Library/Common/Addon/GameModeServer.cs | 2 +- Library/Common/Addon/ItemMode.cs | 2 +- Library/Common/Addon/Plugin.cs | 5 +- Library/Common/Addon/SkillMode.cs | 2 +- Library/Common/Architecture/HeartBeat.cs | 91 +++++++ Library/Common/Network/ClientSocket.cs | 23 +- Library/Common/Network/HTTPClient.cs | 97 ++++++++ Library/Common/Network/HTTPListener.cs | 61 +++++ Library/Common/Network/ServerSocket.cs | 3 +- Library/Common/Network/Socket.cs | 2 +- Library/Common/Network/SocketObject.cs | 2 +- Library/Constant/General.cs | 4 +- Library/Constant/TypeEnum.cs | 6 + Service/HTTPManager.cs | 224 ++++++++++++++++++ 37 files changed, 757 insertions(+), 74 deletions(-) create mode 100644 Api/Transmittal/WebDataRequest.cs rename Interface/Base/{ => Addons}/IAddon.cs (74%) rename Interface/Base/{ => Addons}/IAddonController.cs (76%) rename Interface/Base/{ => Addons}/IGameMap.cs (52%) rename Interface/Base/{ => Addons}/IGameMode.cs (89%) rename Interface/Base/{ => Addons}/IGameModeServer.cs (91%) rename Interface/Base/{ => Addons}/IPlugin.cs (87%) create mode 100644 Interface/Base/HTTP/IHTTPClient.cs create mode 100644 Interface/Base/HTTP/IHTTPListener.cs rename Interface/Base/{ISocket.cs => IBaseSocket.cs} (71%) rename Interface/Base/{ => Sockets}/IClientSocket.cs (89%) create mode 100644 Interface/Base/Sockets/ISocket.cs rename Interface/Base/{ => Sockets}/ISocketHandler.cs (77%) rename Interface/Base/{ => Sockets}/ISocketHeartBeat.cs (73%) create mode 100644 Library/Common/Architecture/HeartBeat.cs create mode 100644 Library/Common/Network/HTTPClient.cs create mode 100644 Library/Common/Network/HTTPListener.cs create mode 100644 Service/HTTPManager.cs diff --git a/Api/Transmittal/DataRequest.cs b/Api/Transmittal/DataRequest.cs index 2244a75..74215ee 100644 --- a/Api/Transmittal/DataRequest.cs +++ b/Api/Transmittal/DataRequest.cs @@ -41,11 +41,11 @@ namespace Milimoe.FunGame.Core.Api.Transmittal /// /// 私有的实现类 /// - private readonly Request Worker; + private readonly SocketRequest Worker; /// - /// 基于本地已连接的Socket创建新的数据请求 - /// 使用RunTimeModel中的NewDataRequest创建一个新的请求 + /// 基于本地已连接的 创建新的数据请求 + /// 使用 中的 创建一个新的请求 /// /// /// @@ -54,7 +54,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal { Worker = new(Socket, RequestType, IsLongRunning); } - + /// /// 添加数据 /// @@ -105,18 +105,17 @@ namespace Milimoe.FunGame.Core.Api.Transmittal return GetHashtableJsonObject(Worker.ResultData, key); } - private class Request : SocketHandlerController + private class SocketRequest(Socket? Socket, DataRequestType RequestType, bool IsLongRunning = false) : SocketHandlerController(Socket) { - public Hashtable RequestData { get; } = new(); + public Hashtable RequestData { get; } = []; public Hashtable ResultData => _ResultData; public RequestResult Result => _Result; public string Error => _Error; - private readonly Socket? Socket; - private readonly DataRequestType RequestType; - private readonly bool _IsLongRunning; - - private Hashtable _ResultData = new(); + private readonly Socket? Socket = Socket; + private readonly DataRequestType RequestType = RequestType; + private readonly bool _IsLongRunning = IsLongRunning; + private Hashtable _ResultData = []; private RequestResult _Result = RequestResult.Missing; private string _Error = ""; @@ -158,13 +157,6 @@ namespace Milimoe.FunGame.Core.Api.Transmittal } } - public Request(Socket? Socket, DataRequestType RequestType, bool IsLongRunning = false) : base(Socket) - { - this.Socket = Socket; - this.RequestType = RequestType; - _IsLongRunning = IsLongRunning; - } - public override void SocketHandler(SocketObject SocketObject) { try @@ -177,7 +169,7 @@ namespace Milimoe.FunGame.Core.Api.Transmittal if (type == RequestType) { if (!_IsLongRunning) Dispose(); - _ResultData = SocketObject.GetParam(1) ?? new(); + _ResultData = SocketObject.GetParam(1) ?? []; _Result = RequestResult.Success; } } diff --git a/Api/Transmittal/WebDataRequest.cs b/Api/Transmittal/WebDataRequest.cs new file mode 100644 index 0000000..7d428ca --- /dev/null +++ b/Api/Transmittal/WebDataRequest.cs @@ -0,0 +1,136 @@ +using System.Collections; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Library.Exception; + +namespace Milimoe.FunGame.Core.Api.Transmittal +{ + public class WebDataRequest + { + /// + /// 数据请求结果 + /// + public RequestResult Result => Worker.Result; + + /// + /// 详细错误信息 + /// + public string Error => Worker.Error; + + // 获取ResultData中key值对应的Json字符串 + // -- 此索引器仅返回Json字符串,对象类型请使用反序列化方法GetResult() -- + // -- 当然也可以自己反序列化 -- + // -- 基本类型可能有效,但仍建议使用反序列化方法 -- + public object? this[string key] + { + get + { + return Worker.ResultData[key]; + } + set + { + AddRequestData(key, value); + } + } + + /// + /// 私有的实现类 + /// + private readonly WebSocketRequest Worker; + + /// + /// 基于本地已连接的 创建新的数据请求 + /// + /// + /// + internal WebDataRequest(HTTPClient Socket, DataRequestType RequestType) + { + Worker = new(Socket, RequestType); + } + + /// + /// 添加数据 + /// + /// + /// + public void AddRequestData(string key, object? value) + { + if (Worker.RequestData.ContainsKey(key)) Worker.RequestData[key] = value; + else Worker.RequestData.Add(key, value); + } + + /// + /// 向服务器发送数据请求 + /// + /// + public async Task SendRequest() + { + await Worker.SendRequestAsync(); + return Result; + } + + /// + /// 异步向服务器发送数据请求 + /// + /// + public async Task SendRequestAsync() + { + await Worker.SendRequestAsync(); + return Result; + } + + /// + /// 获取指定key对应的反序列化对象 + /// + /// + /// + /// + public T? GetResult(string key) + { + return GetHashtableJsonObject(Worker.ResultData, key); + } + + private class WebSocketRequest(HTTPClient? Socket, DataRequestType RequestType) + { + public Hashtable RequestData { get; } = []; + public Hashtable ResultData => _ResultData; + public RequestResult Result => _Result; + public string Error => _Error; + + private readonly HTTPClient? Socket = Socket; + private readonly DataRequestType RequestType = RequestType; + private readonly Hashtable _ResultData = []; + private RequestResult _Result = RequestResult.Missing; + private string _Error = ""; + + public async Task SendRequestAsync() + { + try + { + if (Socket != null) + { + await Socket.Send(SocketMessageType.DataRequest, RequestType, RequestData); + } + else throw new ConnectFailedException(); + } + catch (Exception e) + { + _Result = RequestResult.Fail; + _Error = e.GetErrorInfo(); + } + } + } + + /// + /// 反序列化Hashtable中的Json对象 + /// + /// + /// + /// + /// + public static T? GetHashtableJsonObject(Hashtable hashtable, string key) + { + return Service.JsonManager.GetObject(hashtable, key); + } + } +} diff --git a/Api/Utility/PluginConfig.cs b/Api/Utility/PluginConfig.cs index 386b564..b2f1980 100644 --- a/Api/Utility/PluginConfig.cs +++ b/Api/Utility/PluginConfig.cs @@ -85,7 +85,7 @@ namespace Milimoe.FunGame.Core.Api.Utility } return default; } - + /// /// 添加一个配置,如果已存在key会覆盖 /// diff --git a/Controller/AddonController.cs b/Controller/AddonController.cs index 052d03a..63e1d13 100644 --- a/Controller/AddonController.cs +++ b/Controller/AddonController.cs @@ -1,6 +1,6 @@ using System.Collections; using Milimoe.FunGame.Core.Api.Transmittal; -using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Controller @@ -78,7 +78,7 @@ namespace Milimoe.FunGame.Core.Controller } private void DefaultPrint(string msg) => Console.Write("\r" + msg + "\n\r> "); - + private void DefaultPrint(Exception e) => DefaultPrint(e.ToString()); private DataRequest DefaultNewDataRequest(DataRequestType type) diff --git a/Controller/RunTimeController.cs b/Controller/RunTimeController.cs index 4ce979a..8a0c7e1 100644 --- a/Controller/RunTimeController.cs +++ b/Controller/RunTimeController.cs @@ -57,7 +57,7 @@ namespace Milimoe.FunGame.Core.Controller return result; } - + /// /// 发送结束游戏反馈 /// diff --git a/Controller/SocketHandlerController.cs b/Controller/SocketHandlerController.cs index 50c6077..fce84bc 100644 --- a/Controller/SocketHandlerController.cs +++ b/Controller/SocketHandlerController.cs @@ -1,4 +1,4 @@ -using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Interface.Sockets; using Milimoe.FunGame.Core.Library.Common.Architecture; using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Service; diff --git a/Docs/FunGame.Core.xml b/Docs/FunGame.Core.xml index e1d0e17..ba76000 100644 --- a/Docs/FunGame.Core.xml +++ b/Docs/FunGame.Core.xml @@ -27,8 +27,8 @@ - 基于本地已连接的Socket创建新的数据请求 - 使用RunTimeModel中的NewDataRequest创建一个新的请求 + 基于本地已连接的 创建新的数据请求 + 使用 中的 创建一个新的请求 @@ -204,6 +204,64 @@ 回滚事务 + + + 数据请求结果 + + + + + 详细错误信息 + + + + + 私有的实现类 + + + + + 基于本地已连接的 创建新的数据请求 + + + + + + + 添加数据 + + + + + + + 向服务器发送数据请求 + + + + + + 异步向服务器发送数据请求 + + + + + + 获取指定key对应的反序列化对象 + + + + + + + + 反序列化Hashtable中的Json对象 + + + + + + 获取角色实例 @@ -1138,7 +1196,7 @@ - + 新建一个AddonController diff --git a/Interface/Base/IAddon.cs b/Interface/Base/Addons/IAddon.cs similarity index 74% rename from Interface/Base/IAddon.cs rename to Interface/Base/Addons/IAddon.cs index c612d24..0107881 100644 --- a/Interface/Base/IAddon.cs +++ b/Interface/Base/Addons/IAddon.cs @@ -1,6 +1,4 @@ -using Milimoe.FunGame.Core.Controller; - -namespace Milimoe.FunGame.Core.Interface +namespace Milimoe.FunGame.Core.Interface.Addons { public interface IAddon { diff --git a/Interface/Base/IAddonController.cs b/Interface/Base/Addons/IAddonController.cs similarity index 76% rename from Interface/Base/IAddonController.cs rename to Interface/Base/Addons/IAddonController.cs index 1ac82bb..594888d 100644 --- a/Interface/Base/IAddonController.cs +++ b/Interface/Base/Addons/IAddonController.cs @@ -1,6 +1,6 @@ using Milimoe.FunGame.Core.Controller; -namespace Milimoe.FunGame.Core.Interface.Base +namespace Milimoe.FunGame.Core.Interface.Addons { public interface IAddonController { diff --git a/Interface/Base/IGameMap.cs b/Interface/Base/Addons/IGameMap.cs similarity index 52% rename from Interface/Base/IGameMap.cs rename to Interface/Base/Addons/IGameMap.cs index 0110057..93e5800 100644 --- a/Interface/Base/IGameMap.cs +++ b/Interface/Base/Addons/IGameMap.cs @@ -1,4 +1,4 @@ -namespace Milimoe.FunGame.Core.Interface +namespace Milimoe.FunGame.Core.Interface.Addons { public interface IGameMap : IAddon { diff --git a/Interface/Base/IGameMode.cs b/Interface/Base/Addons/IGameMode.cs similarity index 89% rename from Interface/Base/IGameMode.cs rename to Interface/Base/Addons/IGameMode.cs index a6d0830..be9ba22 100644 --- a/Interface/Base/IGameMode.cs +++ b/Interface/Base/Addons/IGameMode.cs @@ -1,6 +1,4 @@ -using Milimoe.FunGame.Core.Interface.Base; - -namespace Milimoe.FunGame.Core.Interface +namespace Milimoe.FunGame.Core.Interface.Addons { public interface IGameMode : IAddon, IAddonController, IGamingConnectEventHandler, IGamingDisconnectEventHandler, IGamingReconnectEventHandler, IGamingBanCharacterEventHandler, IGamingPickCharacterEventHandler, IGamingRandomEventHandler, IGamingRoundEventHandler, IGamingLevelUpEventHandler, IGamingMoveEventHandler, IGamingAttackEventHandler, IGamingSkillEventHandler, IGamingItemEventHandler, IGamingMagicEventHandler, diff --git a/Interface/Base/IGameModeServer.cs b/Interface/Base/Addons/IGameModeServer.cs similarity index 91% rename from Interface/Base/IGameModeServer.cs rename to Interface/Base/Addons/IGameModeServer.cs index a2b6ca7..5451758 100644 --- a/Interface/Base/IGameModeServer.cs +++ b/Interface/Base/Addons/IGameModeServer.cs @@ -3,7 +3,7 @@ using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; -namespace Milimoe.FunGame.Core.Interface +namespace Milimoe.FunGame.Core.Interface.Addons { public interface IGameModeServer : IAddon, IAddonController { diff --git a/Interface/Base/IPlugin.cs b/Interface/Base/Addons/IPlugin.cs similarity index 87% rename from Interface/Base/IPlugin.cs rename to Interface/Base/Addons/IPlugin.cs index 658b657..f321c60 100644 --- a/Interface/Base/IPlugin.cs +++ b/Interface/Base/Addons/IPlugin.cs @@ -1,6 +1,4 @@ -using Milimoe.FunGame.Core.Interface.Base; - -namespace Milimoe.FunGame.Core.Interface +namespace Milimoe.FunGame.Core.Interface.Addons { public interface IPlugin : IAddon, IAddonController, IConnectEventHandler, IDisconnectEventHandler, ILoginEventHandler, ILogoutEventHandler, IRegEventHandler, IIntoRoomEventHandler, ISendTalkEventHandler, ICreateRoomEventHandler, IQuitRoomEventHandler, IChangeRoomSettingEventHandler, IStartMatchEventHandler, IStartGameEventHandler, IChangeProfileEventHandler, IChangeAccountSettingEventHandler, diff --git a/Interface/Base/HTTP/IHTTPClient.cs b/Interface/Base/HTTP/IHTTPClient.cs new file mode 100644 index 0000000..d86dccc --- /dev/null +++ b/Interface/Base/HTTP/IHTTPClient.cs @@ -0,0 +1,12 @@ +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Interface.HTTP +{ + public interface IHTTPClient : IBaseSocket + { + public Task Send(SocketMessageType type, params object[] objs); + public SocketObject SocketObject_Handler(SocketObject objs); + } +} diff --git a/Interface/Base/HTTP/IHTTPListener.cs b/Interface/Base/HTTP/IHTTPListener.cs new file mode 100644 index 0000000..5f13086 --- /dev/null +++ b/Interface/Base/HTTP/IHTTPListener.cs @@ -0,0 +1,12 @@ +using System.Net; +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Network; + +namespace Milimoe.FunGame.Core.Interface.HTTP +{ + public interface IHTTPListener : IBaseSocket + { + public HttpListener Instance { get; } + public SocketObject SocketObject_Handler(SocketObject objs); + } +} diff --git a/Interface/Base/ISocket.cs b/Interface/Base/IBaseSocket.cs similarity index 71% rename from Interface/Base/ISocket.cs rename to Interface/Base/IBaseSocket.cs index 691f43c..8b93c4f 100644 --- a/Interface/Base/ISocket.cs +++ b/Interface/Base/IBaseSocket.cs @@ -2,16 +2,14 @@ namespace Milimoe.FunGame.Core.Interface.Base { - public interface ISocket + public interface IBaseSocket { - public System.Net.Sockets.Socket Instance { get; } public SocketRuntimeType Runtime { get; } public Guid Token { get; } public string ServerAddress { get; } public int ServerPort { get; } public string ServerName { get; } public string ServerNotice { get; } - public bool Connected => Instance != null && Instance.Connected; public void Close(); } } diff --git a/Interface/Base/IClientSocket.cs b/Interface/Base/Sockets/IClientSocket.cs similarity index 89% rename from Interface/Base/IClientSocket.cs rename to Interface/Base/Sockets/IClientSocket.cs index 55fe5fc..46c526b 100644 --- a/Interface/Base/IClientSocket.cs +++ b/Interface/Base/Sockets/IClientSocket.cs @@ -1,6 +1,6 @@ using Milimoe.FunGame.Core.Library.Constant; -namespace Milimoe.FunGame.Core.Interface.Base +namespace Milimoe.FunGame.Core.Interface.Sockets { public interface IClientSocket : ISocket { diff --git a/Interface/Base/Sockets/ISocket.cs b/Interface/Base/Sockets/ISocket.cs new file mode 100644 index 0000000..39c5ede --- /dev/null +++ b/Interface/Base/Sockets/ISocket.cs @@ -0,0 +1,10 @@ +using Milimoe.FunGame.Core.Interface.Base; + +namespace Milimoe.FunGame.Core.Interface.Sockets +{ + public interface ISocket : IBaseSocket + { + public System.Net.Sockets.Socket Instance { get; } + public bool Connected => Instance != null && Instance.Connected; + } +} diff --git a/Interface/Base/ISocketHandler.cs b/Interface/Base/Sockets/ISocketHandler.cs similarity index 77% rename from Interface/Base/ISocketHandler.cs rename to Interface/Base/Sockets/ISocketHandler.cs index 501e314..3ee6e5b 100644 --- a/Interface/Base/ISocketHandler.cs +++ b/Interface/Base/Sockets/ISocketHandler.cs @@ -1,6 +1,6 @@ using Milimoe.FunGame.Core.Library.Common.Network; -namespace Milimoe.FunGame.Core.Interface.Base +namespace Milimoe.FunGame.Core.Interface.Sockets { public interface ISocketHandler { diff --git a/Interface/Base/ISocketHeartBeat.cs b/Interface/Base/Sockets/ISocketHeartBeat.cs similarity index 73% rename from Interface/Base/ISocketHeartBeat.cs rename to Interface/Base/Sockets/ISocketHeartBeat.cs index 6930692..f8b206e 100644 --- a/Interface/Base/ISocketHeartBeat.cs +++ b/Interface/Base/Sockets/ISocketHeartBeat.cs @@ -1,4 +1,4 @@ -namespace Milimoe.FunGame.Core.Interface.Base +namespace Milimoe.FunGame.Core.Interface.Sockets { public interface ISocketHeartBeat { diff --git a/Library/Common/Addon/CharacterMode.cs b/Library/Common/Addon/CharacterMode.cs index 35a5170..388b5d1 100644 --- a/Library/Common/Addon/CharacterMode.cs +++ b/Library/Common/Addon/CharacterMode.cs @@ -1,5 +1,5 @@ using Milimoe.FunGame.Core.Entity; -using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; namespace Milimoe.FunGame.Core.Library.Common.Addon { diff --git a/Library/Common/Addon/GameMap.cs b/Library/Common/Addon/GameMap.cs index 01d703a..0900f04 100644 --- a/Library/Common/Addon/GameMap.cs +++ b/Library/Common/Addon/GameMap.cs @@ -1,4 +1,4 @@ -using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; namespace Milimoe.FunGame.Core.Library.Common.Addon { diff --git a/Library/Common/Addon/GameMode.cs b/Library/Common/Addon/GameMode.cs index 30cb67f..422de38 100644 --- a/Library/Common/Addon/GameMode.cs +++ b/Library/Common/Addon/GameMode.cs @@ -1,6 +1,7 @@ using System.Collections; using Milimoe.FunGame.Core.Controller; using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Library.Common.Event; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Model; diff --git a/Library/Common/Addon/GameModeServer.cs b/Library/Common/Addon/GameModeServer.cs index f7b57e1..1fde040 100644 --- a/Library/Common/Addon/GameModeServer.cs +++ b/Library/Common/Addon/GameModeServer.cs @@ -1,7 +1,7 @@ using System.Collections; using Milimoe.FunGame.Core.Controller; using Milimoe.FunGame.Core.Entity; -using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; diff --git a/Library/Common/Addon/ItemMode.cs b/Library/Common/Addon/ItemMode.cs index df89b01..a88816b 100644 --- a/Library/Common/Addon/ItemMode.cs +++ b/Library/Common/Addon/ItemMode.cs @@ -1,5 +1,5 @@ using Milimoe.FunGame.Core.Entity; -using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; namespace Milimoe.FunGame.Core.Library.Common.Addon { diff --git a/Library/Common/Addon/Plugin.cs b/Library/Common/Addon/Plugin.cs index 1829e87..71ed2a7 100644 --- a/Library/Common/Addon/Plugin.cs +++ b/Library/Common/Addon/Plugin.cs @@ -1,8 +1,7 @@ -using Milimoe.FunGame.Core.Api.Transmittal; -using Milimoe.FunGame.Core.Controller; +using Milimoe.FunGame.Core.Controller; using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; using Milimoe.FunGame.Core.Library.Common.Event; -using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Model; namespace Milimoe.FunGame.Core.Library.Common.Addon diff --git a/Library/Common/Addon/SkillMode.cs b/Library/Common/Addon/SkillMode.cs index 18409cf..8962f98 100644 --- a/Library/Common/Addon/SkillMode.cs +++ b/Library/Common/Addon/SkillMode.cs @@ -1,5 +1,5 @@ using Milimoe.FunGame.Core.Entity; -using Milimoe.FunGame.Core.Interface; +using Milimoe.FunGame.Core.Interface.Addons; namespace Milimoe.FunGame.Core.Library.Common.Addon { diff --git a/Library/Common/Architecture/HeartBeat.cs b/Library/Common/Architecture/HeartBeat.cs new file mode 100644 index 0000000..9ea2284 --- /dev/null +++ b/Library/Common/Architecture/HeartBeat.cs @@ -0,0 +1,91 @@ +using Milimoe.FunGame.Core.Interface.Sockets; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Library.Common.Architecture +{ + public class HeartBeat : ISocketHeartBeat + { + public TransmittalType TransmittalType { get; } = TransmittalType.Socket; + public bool SendingHeartBeat => _SendingHeartBeat; + public int HeartBeatFaileds => _HeartBeatFaileds; + + private Task? SendingHeartBeatTask; + private bool _SendingHeartBeat = false; + private int _HeartBeatFaileds = 0; + + private readonly Socket? _Socket = null; + private readonly HTTPClient? _HTTPClient = null; + + public HeartBeat(Socket socket) + { + _Socket = socket; + this.TransmittalType = TransmittalType.Socket; + } + + public HeartBeat(HTTPClient client) + { + _HTTPClient = client; + this.TransmittalType = TransmittalType.WebSocket; + } + + public void StartSendingHeartBeat() + { + if (!FunGameInfo.FunGame_DebugMode) + { + _SendingHeartBeat = true; + SendingHeartBeatTask = Task.Factory.StartNew(SendHeartBeat); + } + } + + public void StopSendingHeartBeat() + { + _SendingHeartBeat = false; + SendingHeartBeatTask?.Wait(1); + SendingHeartBeatTask = null; + } + + private async Task SendHeartBeat() + { + await Task.Delay(100); + if (_Socket != null) + { + while (_Socket.Connected) + { + if (!SendingHeartBeat) _SendingHeartBeat = true; + // 发送心跳包 + if (_Socket.Send(SocketMessageType.HeartBeat) == SocketResult.Success) + { + await Task.Delay(4 * 000); + AddHeartBeatFaileds(); + } + else AddHeartBeatFaileds(); + await Task.Delay(20 * 000); + } + } + else if (_HTTPClient != null) + { + while (_HTTPClient.Instance?.State == System.Net.WebSockets.WebSocketState.Open) + { + if (!SendingHeartBeat) _SendingHeartBeat = true; + // 发送心跳包 + if (await _HTTPClient.Send(SocketMessageType.HeartBeat) == SocketResult.Success) + { + await Task.Delay(4 * 000); + AddHeartBeatFaileds(); + } + else AddHeartBeatFaileds(); + await Task.Delay(20 * 000); + } + } + _SendingHeartBeat = false; + } + + private void AddHeartBeatFaileds() + { + // 超过三次没回应心跳,服务器连接失败。 + if (_HeartBeatFaileds++ >= 3) + throw new LostConnectException(); + } + } +} diff --git a/Library/Common/Network/ClientSocket.cs b/Library/Common/Network/ClientSocket.cs index a4fdc55..fef4424 100644 --- a/Library/Common/Network/ClientSocket.cs +++ b/Library/Common/Network/ClientSocket.cs @@ -1,19 +1,19 @@ -using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Interface.Sockets; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Service; namespace Milimoe.FunGame.Core.Library.Common.Network { - public class ClientSocket : IClientSocket + public class ClientSocket(System.Net.Sockets.Socket Instance, int ServerPort, string ClientIP, string ClientName, Guid Token) : IClientSocket { - public System.Net.Sockets.Socket Instance { get; } + public System.Net.Sockets.Socket Instance { get; } = Instance; public SocketRuntimeType Runtime => SocketRuntimeType.Server; - public Guid Token { get; } = Guid.Empty; + public Guid Token { get; } = Token; public string ServerAddress { get; } = ""; - public int ServerPort { get; } = 0; + public int ServerPort { get; } = ServerPort; public string ServerName { get; } = ""; public string ServerNotice { get; } = ""; - public string ClientIP { get; } = ""; + public string ClientIP { get; } = ClientIP; public string ClientName => _ClientName; public bool Connected => Instance != null && Instance.Connected; public bool Receiving => _Receiving; @@ -21,16 +21,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network private Task? ReceivingTask; private bool _Receiving; - private string _ClientName; - - public ClientSocket(System.Net.Sockets.Socket Instance, int ServerPort, string ClientIP, string ClientName, Guid Token) - { - this.Instance = Instance; - this.ServerPort = ServerPort; - this.ClientIP = ClientIP; - this._ClientName = ClientName; - this.Token = Token; - } + private readonly string _ClientName = ClientName; public void Close() { diff --git a/Library/Common/Network/HTTPClient.cs b/Library/Common/Network/HTTPClient.cs new file mode 100644 index 0000000..130893c --- /dev/null +++ b/Library/Common/Network/HTTPClient.cs @@ -0,0 +1,97 @@ +using System.Net.WebSockets; +using Milimoe.FunGame.Core.Interface.HTTP; +using Milimoe.FunGame.Core.Library.Common.Architecture; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Library.Exception; +using Milimoe.FunGame.Core.Service; + +namespace Milimoe.FunGame.Core.Library.Common.Network +{ + public class HTTPClient : IHTTPClient + { + public ClientWebSocket? Instance { get; } = null; + public HeartBeat HeartBeat { get; } + public SocketRuntimeType Runtime => SocketRuntimeType.Client; + public Guid Token { get; } = Guid.Empty; + public string ServerAddress { get; } = ""; + public int ServerPort { get; } = 0; + public string ServerName { get; } = ""; + public string ServerNotice { get; } = ""; + + private bool _Listening = false; + + private HTTPClient(ClientWebSocket Instance, string ServerAddress, int ServerPort, params object[] args) + { + this.Instance = Instance; + this.ServerAddress = ServerAddress; + this.ServerPort = ServerPort; + HeartBeat = new(this); + HeartBeat.StartSendingHeartBeat(); + Task.Factory.StartNew(async () => await StartListening(args)); + } + + public static async Task Connect(string ServerAddress, int ServerPort, bool SSL, string SubDirectory = "", params object[] args) + { + string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(ServerAddress); + Uri uri = new((SSL ? "wss://" : "ws://") + ServerIP + ":" + ServerPort + "/" + SubDirectory); + ClientWebSocket? socket = await HTTPManager.Connect(uri); + if (socket != null && socket.State == WebSocketState.Open) + { + HTTPClient client = new(socket, ServerAddress, ServerPort, args); + return client; + } + throw new CanNotConnectException(); + } + + private async Task StartListening(params object[] args) + { + if (Instance != null && Instance.State == WebSocketState.Open) + { + if (await HTTPManager.Send(Instance, new(SocketMessageType.Connect, Guid.Empty, args)) == SocketResult.Success && await HTTPManager.ReceiveMessage(this)) + { + _Listening = true; + await Receive(); + } + } + } + + public async Task Receive() + { + while (_Listening) + { + try + { + await HTTPManager.ReceiveMessage(this); + } + catch (System.Exception e) + { + Close(); + Api.Utility.TXTHelper.AppendErrorLog(e.GetErrorInfo()); + throw new SocketWrongInfoException(); + } + } + } + + public async Task Send(SocketMessageType type, params object[] objs) + { + if (Instance != null) + { + return await HTTPManager.Send(Instance, new(type, Token, objs)); + } + return SocketResult.NotSent; + } + + public virtual SocketObject SocketObject_Handler(SocketObject objs) + { + return new(SocketMessageType.Unknown, Guid.Empty); + } + + public void Close() + { + _Listening = false; + HeartBeat.StopSendingHeartBeat(); + Instance?.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); + Instance?.Dispose(); + } + } +} diff --git a/Library/Common/Network/HTTPListener.cs b/Library/Common/Network/HTTPListener.cs new file mode 100644 index 0000000..b911480 --- /dev/null +++ b/Library/Common/Network/HTTPListener.cs @@ -0,0 +1,61 @@ +using System.Net; +using System.Net.WebSockets; +using Milimoe.FunGame.Core.Interface.HTTP; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Service; + +namespace Milimoe.FunGame.Core.Library.Common.Network +{ + public class HTTPListener : IHTTPListener + { + public HttpListener Instance { get; } + public SocketRuntimeType Runtime => SocketRuntimeType.Server; + public Guid Token { get; } = Guid.Empty; + public string ServerAddress { get; } = ""; + public int ServerPort { get; } = 0; + public string ServerName { get; } = ""; + public string ServerNotice { get; } = ""; + public Dictionary ClientSockets { get; } = []; + + private HTTPListener(HttpListener Instance, int ServerPort) + { + this.Instance = Instance; + this.ServerPort = ServerPort; + } + + public static HTTPListener StartListening(int Port, bool SSL = false) + { + HttpListener? socket = HTTPManager.StartListening(Port, SSL); + if (socket != null) + { + HTTPListener instance = new(socket, Port); + Task t = Task.Run(async () => await HTTPManager.Receiving(instance)); + return instance; + } + else throw new SocketCreateListenException(); + } + + public async Task SendMessage(Guid token, SocketObject obj) + { + if (ClientSockets.TryGetValue(token, out WebSocket? socket) && socket != null) + { + await HTTPManager.Send(socket, obj); + } + } + + public virtual bool CheckClientConnection(SocketObject objs) + { + return true; + } + + public virtual SocketObject SocketObject_Handler(SocketObject objs) + { + return new(SocketMessageType.Unknown, Guid.Empty); + } + + public void Close() + { + Instance?.Close(); + } + } +} diff --git a/Library/Common/Network/ServerSocket.cs b/Library/Common/Network/ServerSocket.cs index 66c1ac7..ffe5319 100644 --- a/Library/Common/Network/ServerSocket.cs +++ b/Library/Common/Network/ServerSocket.cs @@ -1,4 +1,5 @@ using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Interface.Sockets; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Service; @@ -16,7 +17,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network public bool Connected => Instance != null && Instance.Connected; public List ClientList => OnlineClients.GetList(); public List UserList => OnlineUsers.GetList(); - public List BannedList { get; } = new(); + public List BannedList { get; } = []; public int ClientCount => OnlineClients.Count; public int UserCount => OnlineUsers.Count; public int BannedCount => BannedList.Count; diff --git a/Library/Common/Network/Socket.cs b/Library/Common/Network/Socket.cs index b70ccf3..0539b4c 100644 --- a/Library/Common/Network/Socket.cs +++ b/Library/Common/Network/Socket.cs @@ -1,4 +1,4 @@ -using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Interface.Sockets; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Exception; using Milimoe.FunGame.Core.Service; diff --git a/Library/Common/Network/SocketObject.cs b/Library/Common/Network/SocketObject.cs index 8251740..933394c 100644 --- a/Library/Common/Network/SocketObject.cs +++ b/Library/Common/Network/SocketObject.cs @@ -9,7 +9,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network { public SocketMessageType SocketType { get; } = SocketMessageType.Unknown; public Guid Token { get; } = Guid.Empty; - public object[] Parameters { get; } = Array.Empty(); + public object[] Parameters { get; } = []; public int Length => Parameters.Length; // 从参数列表中获取指定索引的参数的Json字符串 diff --git a/Library/Constant/General.cs b/Library/Constant/General.cs index ba456d0..d7163b1 100644 --- a/Library/Constant/General.cs +++ b/Library/Constant/General.cs @@ -19,12 +19,12 @@ namespace Milimoe.FunGame.Core.Library.Constant /// 默认的未知用户 /// public static User UnknownUserInstance => new(); - + /// /// 游客用户 /// public static User GuestUserInstance => new(UserType.Guest); - + /// /// 本地用户 /// diff --git a/Library/Constant/TypeEnum.cs b/Library/Constant/TypeEnum.cs index adb883a..076277a 100644 --- a/Library/Constant/TypeEnum.cs +++ b/Library/Constant/TypeEnum.cs @@ -134,6 +134,12 @@ namespace Milimoe.FunGame.Core.Library.Constant Punish } + public enum TransmittalType + { + Socket, + WebSocket + } + public enum SocketRuntimeType { Client, diff --git a/Service/HTTPManager.cs b/Service/HTTPManager.cs new file mode 100644 index 0000000..3ec263a --- /dev/null +++ b/Service/HTTPManager.cs @@ -0,0 +1,224 @@ +using System.Net; +using System.Net.WebSockets; +using System.Text; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Library.Exception; + +namespace Milimoe.FunGame.Core.Service +{ + internal class HTTPManager + { + internal static HttpListener? ServerSocket => _ServerSocket; + private static HttpListener? _ServerSocket = null; + + internal static HttpListener StartListening(int Port = 22227, bool SSL = false) + { + HttpListener listener = new(); + listener.Prefixes.Add((SSL ? "https://" : "http://") + "localhost:" + Port + "/"); + listener.Start(); + return listener; + } + + internal static async Task Connect(Uri uri) + { + ClientWebSocket socket = new(); + await socket.ConnectAsync(uri, CancellationToken.None); + if (socket.State == WebSocketState.Open) + { + return socket; + } + return null; + } + + internal static async Task Send(ClientWebSocket socket, SocketObject obj) + { + if (socket != null) + { + try + { + await socket.SendAsync(new ArraySegment(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))), WebSocketMessageType.Text, true, CancellationToken.None); + return SocketResult.Success; + } + catch (Exception e) + { + TXTHelper.AppendErrorLog(e.GetErrorInfo()); + return SocketResult.Fail; + } + } + return SocketResult.NotSent; + } + + internal static async Task Send(WebSocket socket, SocketObject obj) + { + if (socket != null) + { + try + { + await socket.SendAsync(new ArraySegment(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))), WebSocketMessageType.Text, true, CancellationToken.None); + return SocketResult.Success; + } + catch (Exception e) + { + TXTHelper.AppendErrorLog(e.GetErrorInfo()); + return SocketResult.Fail; + } + } + return SocketResult.NotSent; + } + + internal static async Task Receiving(HTTPListener listener) + { + if (ServerSocket != null) + { + try + { + while (true) + { + HttpListenerContext context = await ServerSocket.GetContextAsync(); + if (context.Request.IsWebSocketRequest) + { + await AddClientWebSocket(listener, context); + } + else + { + context.Response.StatusCode = 400; + context.Response.Close(); + } + } + } + catch + { + _ServerSocket = null; + } + } + } + + internal static async Task ReceiveMessage(HTTPClient client) + { + if (client.Instance is null) return false; + + byte[] buffer = new byte[General.SocketByteSize]; + WebSocketReceiveResult result = await client.Instance.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + string msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim(); + SocketObject[] objs = await GetSocketObjects(client.Instance, result, msg); + + foreach (SocketObject obj in objs) + { + SocketObject sendobject = client.SocketObject_Handler(obj); + if (obj.SocketType == SocketMessageType.Connect) + { + return true; + } + else if (obj.SocketType == SocketMessageType.Disconnect) + { + await client.Instance.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None); + return true; + } + await Send(client.Instance, sendobject); + } + + return true; + } + + private static async Task AddClientWebSocket(HTTPListener listener, HttpListenerContext context) + { + HttpListenerWebSocketContext socketContext = await context.AcceptWebSocketAsync(null); + WebSocket socket = socketContext.WebSocket; + + byte[] buffer = new byte[General.SocketByteSize]; + WebSocketReceiveResult result = await socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + string msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim(); + + SocketObject sendobject = new(SocketMessageType.Unknown, Guid.Empty); + SocketObject[] objs = await GetSocketObjects(socket, result, msg); + bool isConnect = false; + + foreach (SocketObject obj in objs) + { + if (obj.SocketType == SocketMessageType.Connect) + { + isConnect = listener.CheckClientConnection(obj); + } + else if (listener.ClientSockets.ContainsKey(obj.Token)) + { + sendobject = listener.SocketObject_Handler(obj); + isConnect = true; + } + } + + if (isConnect) + { + Guid token = Guid.NewGuid(); + listener.ClientSockets.TryAdd(token, socket); + await Send(socket, sendobject); + + while (!result.CloseStatus.HasValue) + { + try + { + buffer = new byte[General.SocketByteSize]; + result = await socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + msg = Encoding.UTF8.GetString(buffer).Replace("\0", "").Trim(); + objs = await GetSocketObjects(socket, result, msg); + foreach (SocketObject obj in objs) + { + sendobject = listener.SocketObject_Handler(obj); + if (obj.SocketType == SocketMessageType.Disconnect) + { + await socket.CloseAsync(result.CloseStatus ?? WebSocketCloseStatus.NormalClosure, result.CloseStatusDescription, CancellationToken.None); + return; + } + await Send(socket, sendobject); + } + } + catch (Exception e) + { + TXTHelper.AppendErrorLog(e.GetErrorInfo()); + await socket.CloseAsync(WebSocketCloseStatus.InternalServerError, result.CloseStatusDescription, CancellationToken.None); + } + } + } + } + + private static async Task GetSocketObjects(WebSocket socket, WebSocketReceiveResult result, string msg) + { + List objs = []; + + if (JsonManager.IsCompleteJson(msg)) + { + foreach (SocketObject obj in JsonManager.GetObjects(msg)) + { + objs.Add(obj); + } + return [.. objs]; + } + else + { + await Task.Delay(20); + while (true) + { + if (!result.CloseStatus.HasValue) + { + byte[] buffer = new byte[General.SocketByteSize]; + result = await socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + msg += General.DefaultEncoding.GetString(buffer).Replace("\0", "").Trim(); + if (JsonManager.IsCompleteJson(msg)) + { + break; + } + await Task.Delay(20); + } + else break; + } + foreach (SocketObject obj in JsonManager.GetObjects(msg)) + { + objs.Add(obj); + } + } + + return [.. objs]; + } + } +}