diff --git a/Api/Factory/RoomFactory.cs b/Api/Factory/RoomFactory.cs index c9a96ad..f59f328 100644 --- a/Api/Factory/RoomFactory.cs +++ b/Api/Factory/RoomFactory.cs @@ -13,9 +13,9 @@ namespace Milimoe.FunGame.Core.Api.Factory return RoomFactory.Create(); } - public static Room Create(long Id = 0, string Roomid = "-1", DateTime? CreateTime = null, User? RoomMaster = null, RoomType RoomType = RoomType.All, string GameModule = "", string GameMap = "", RoomState RoomState = RoomState.Created, bool IsRank = false, string Password = "") + public static Room Create(long id = 0, string roomid = "-1", DateTime? createTime = null, User? roomMaster = null, RoomType roomType = RoomType.All, string gameModule = "", string gameMap = "", RoomState roomState = RoomState.Created, bool isRank = false, string password = "", int maxUsers = 4) { - return new Room(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password); + return new Room(id, roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password, maxUsers); } } } diff --git a/Service/ModelManager.cs b/Api/Utility/ConcurrentModelList.cs similarity index 84% rename from Service/ModelManager.cs rename to Api/Utility/ConcurrentModelList.cs index eab3a35..741b207 100644 --- a/Service/ModelManager.cs +++ b/Api/Utility/ConcurrentModelList.cs @@ -1,14 +1,14 @@ using System.Collections; using System.Collections.Concurrent; -namespace Milimoe.FunGame.Core.Service +namespace Milimoe.FunGame.Core.Api.Utility { - internal class ModelManager : IEnumerable + public class ConcurrentModelList : IEnumerable { /// /// 目前的Model数量 /// - internal int Count => Models.Count; + public int Count => Models.Count; /// /// 最大接受的Model数量 @@ -24,7 +24,7 @@ namespace Milimoe.FunGame.Core.Service /// Init ModelManager /// /// MaxModel - internal ModelManager(int MaxModel = 0) + public ConcurrentModelList(int MaxModel = 0) { if (MaxModel <= 0) this.MaxModel = Library.Constant.General.MaxTask_2C2G; @@ -39,7 +39,7 @@ namespace Milimoe.FunGame.Core.Service /// /// Model的Key /// Model对象 - internal T this[string name] => Models[name]; + public T this[string name] => Models[name]; /// /// 向Model管理器中添加Model @@ -47,7 +47,7 @@ namespace Milimoe.FunGame.Core.Service /// Model的Key /// Model对象 /// True:操作成功 - internal bool Add(string name, T t) + public bool Add(string name, T t) { if (Models.Count + 1 > MaxModel) return false; return Models.TryAdd(name, t); @@ -58,7 +58,7 @@ namespace Milimoe.FunGame.Core.Service /// /// Model的Key /// True:操作成功 - internal bool Remove(string name) + public bool Remove(string name) { return Models.TryRemove(name, out _); } @@ -69,7 +69,7 @@ namespace Milimoe.FunGame.Core.Service /// Model的Key /// Model对象 /// 被移除的Model - internal bool Remove(string name, ref T? t) + public bool Remove(string name, ref T? t) { return Models.TryRemove(name, out t); } @@ -79,13 +79,13 @@ namespace Milimoe.FunGame.Core.Service /// /// Model的Key /// 被移除的Model - internal T? RemoveAndGet(string name) + public T? RemoveAndGet(string name) { Models.TryRemove(name, out T? result); return result; } - internal bool ContainsKey(string name) + public bool ContainsKey(string name) { return Models.ContainsKey(name); } @@ -93,7 +93,7 @@ namespace Milimoe.FunGame.Core.Service /// /// 清空Model管理器 /// - internal void Clear() + public void Clear() { Models.Clear(); } @@ -102,7 +102,7 @@ namespace Milimoe.FunGame.Core.Service /// /// 获取Model对象的列表 /// - internal List GetList() + public List GetList() { return [.. Models.Values]; } diff --git a/Api/Utility/Factory.cs b/Api/Utility/Factory.cs index e5894ba..4acdcf7 100644 --- a/Api/Utility/Factory.cs +++ b/Api/Utility/Factory.cs @@ -64,44 +64,46 @@ namespace Milimoe.FunGame.Core.Api.Utility /// /// 获取房间实例 /// - /// 房间内部序列号 - /// 房间号 - /// 创建时间 - /// 房主 - /// 房间类型 - /// 游戏模组 - /// - /// 房间状态 - /// - /// 房间密码 + /// 房间内部序列号 + /// 房间号 + /// 创建时间 + /// 房主 + /// 房间类型 + /// 游戏模组 + /// + /// 房间状态 + /// + /// 房间密码 + /// 人数上限 /// - public static Room GetRoom(long Id = 0, string Roomid = "-1", DateTime? CreateTime = null, User? RoomMaster = null, RoomType RoomType = RoomType.All, string GameModule = "", string GameMap = "", RoomState RoomState = RoomState.Created, bool IsRank = false, string Password = "") + public static Room GetRoom(long id = 0, string roomid = "-1", DateTime? createTime = null, User? roomMaster = null, RoomType roomType = RoomType.All, string gameModule = "", string gameMap = "", RoomState roomState = RoomState.Created, bool isRank = false, string password = "", int maxUsers = 4) { - return RoomFactory.Create(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password); + return RoomFactory.Create(id, roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password, maxUsers); } /// /// 通过DataSet获取房间实例 /// - /// - /// + /// + /// /// - public static Room GetRoom(DataRow DrRoom, User User) + public static Room GetRoom(DataRow drRoom, User user) { Room room = General.HallInstance; - if (DrRoom != null) + if (drRoom != null) { - long Id = (long)DrRoom[RoomQuery.Column_ID]; - string Roomid = (string)DrRoom[RoomQuery.Column_RoomID]; - DateTime CreateTime = (DateTime)DrRoom[RoomQuery.Column_CreateTime]; - User RoomMaster = User; - RoomType RoomType = (RoomType)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomType]); - string GameModule = (string)DrRoom[RoomQuery.Column_GameModule]; - string GameMap = (string)DrRoom[RoomQuery.Column_GameMap]; - RoomState RoomState = (RoomState)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomState]); - bool IsRank = Convert.ToInt32(DrRoom[RoomQuery.Column_IsRank]) == 1; - string Password = (string)DrRoom[RoomQuery.Column_Password]; - room = GetRoom(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password); + long id = (long)drRoom[RoomQuery.Column_ID]; + string roomid = (string)drRoom[RoomQuery.Column_RoomID]; + DateTime createTime = (DateTime)drRoom[RoomQuery.Column_CreateTime]; + User roomMaster = user; + RoomType roomType = (RoomType)Convert.ToInt32(drRoom[RoomQuery.Column_RoomType]); + string gameModule = (string)drRoom[RoomQuery.Column_GameModule]; + string gameMap = (string)drRoom[RoomQuery.Column_GameMap]; + RoomState roomState = (RoomState)Convert.ToInt32(drRoom[RoomQuery.Column_RoomState]); + bool isRank = Convert.ToInt32(drRoom[RoomQuery.Column_IsRank]) == 1; + string password = (string)drRoom[RoomQuery.Column_Password]; + int maxUsers = (int)drRoom[RoomQuery.Column_MaxUsers]; + room = GetRoom(id, roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password, maxUsers); } return room; } @@ -109,38 +111,38 @@ namespace Milimoe.FunGame.Core.Api.Utility /// /// 通过DataSet获取房间列表 /// - /// - /// + /// + /// /// - public static List GetRooms(DataSet DsRoom, DataSet DsUser) + public static List GetRooms(DataSet dsRoom, DataSet dsUser) { List list = [ General.HallInstance ]; - if (DsRoom != null && DsRoom.Tables[0].Rows.Count > 0) + if (dsRoom != null && dsRoom.Tables[0].Rows.Count > 0) { - foreach (DataRow DrRoom in DsRoom.Tables[0].Rows) + foreach (DataRow drRoom in dsRoom.Tables[0].Rows) { - long Id = (long)DrRoom[RoomQuery.Column_ID]; - string Roomid = (string)DrRoom[RoomQuery.Column_RoomID]; - DateTime CreateTime = (DateTime)DrRoom[RoomQuery.Column_CreateTime]; - User RoomMaster = General.UnknownUserInstance; - if (DsUser != null && DsUser.Tables.Count > 0) + long Id = (long)drRoom[RoomQuery.Column_ID]; + string Roomid = (string)drRoom[RoomQuery.Column_RoomID]; + DateTime createTime = (DateTime)drRoom[RoomQuery.Column_CreateTime]; + User roomMaster = General.UnknownUserInstance; + if (dsUser != null && dsUser.Tables.Count > 0) { - DataRow[] rows = DsUser.Tables[0].Select($"{UserQuery.Column_UID} = {(long)DrRoom[RoomQuery.Column_RoomMaster]}"); + DataRow[] rows = dsUser.Tables[0].Select($"{UserQuery.Column_UID} = {(long)drRoom[RoomQuery.Column_RoomMaster]}"); if (rows.Length > 0) { - RoomMaster = GetUser(rows[0]); + roomMaster = GetUser(rows[0]); } } - RoomType RoomType = (RoomType)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomType]); - string GameModule = (string)DrRoom[RoomQuery.Column_GameModule]; - string GameMap = (string)DrRoom[RoomQuery.Column_GameMap]; - RoomState RoomState = (RoomState)Convert.ToInt32(DrRoom[RoomQuery.Column_RoomState]); - bool IsRank = Convert.ToInt32(DrRoom[RoomQuery.Column_IsRank]) == 1; - string Password = (string)DrRoom[RoomQuery.Column_Password]; - list.Add(GetRoom(Id, Roomid, CreateTime, RoomMaster, RoomType, GameModule, GameMap, RoomState, IsRank, Password)); + RoomType roomType = (RoomType)Convert.ToInt32(drRoom[RoomQuery.Column_RoomType]); + string gameModule = (string)drRoom[RoomQuery.Column_GameModule]; + string gameMap = (string)drRoom[RoomQuery.Column_GameMap]; + RoomState roomState = (RoomState)Convert.ToInt32(drRoom[RoomQuery.Column_RoomState]); + bool isRank = Convert.ToInt32(drRoom[RoomQuery.Column_IsRank]) == 1; + string password = (string)drRoom[RoomQuery.Column_Password]; + list.Add(GetRoom(Id, Roomid, createTime, roomMaster, roomType, gameModule, gameMap, roomState, isRank, password)); } } return list; diff --git a/Api/Utility/General.cs b/Api/Utility/General.cs index 935d3c1..652cd8e 100644 --- a/Api/Utility/General.cs +++ b/Api/Utility/General.cs @@ -2,7 +2,6 @@ using System.Net; using System.Net.NetworkInformation; using System.Security.Cryptography; -using System.Text; using System.Text.Json; using System.Text.RegularExpressions; using Milimoe.FunGame.Core.Library.Common.Architecture; @@ -282,7 +281,7 @@ namespace Milimoe.FunGame.Core.Api.Utility /// public static async Task HttpPost(string url, string json) { - HttpContent content = new StringContent(json, Encoding.UTF8, "application/json"); + HttpContent content = new StringContent(json, General.DefaultEncoding, "application/json"); HttpResponseMessage response = await client.PostAsync(url, content); response.EnsureSuccessStatusCode(); @@ -431,7 +430,7 @@ namespace Milimoe.FunGame.Core.Api.Utility /// public static string RSAEncrypt(string plain_text, string plublic_key) { - byte[] plain = Encoding.UTF8.GetBytes(plain_text); + byte[] plain = General.DefaultEncoding.GetBytes(plain_text); using RSACryptoServiceProvider rsa = new(); rsa.FromXmlString(plublic_key); byte[] encrypted = rsa.Encrypt(plain, false); @@ -450,7 +449,7 @@ namespace Milimoe.FunGame.Core.Api.Utility using RSACryptoServiceProvider rsa = new(); rsa.FromXmlString(private_key); byte[] decrypted = rsa.Decrypt(secret, false); - return Encoding.UTF8.GetString(decrypted); + return General.DefaultEncoding.GetString(decrypted); } } diff --git a/Api/Utility/TextReader.cs b/Api/Utility/TextReader.cs index e7d8f88..ef1ff27 100644 --- a/Api/Utility/TextReader.cs +++ b/Api/Utility/TextReader.cs @@ -57,15 +57,12 @@ namespace Milimoe.FunGame.Core.Api.Utility /// public static void Init(FunGameInfo.FunGame FunGameType) { - StreamWriter writer = new(DefaultFileName, false, General.DefaultEncoding); switch (FunGameType) { case FunGameInfo.FunGame.FunGame_Core: case FunGameInfo.FunGame.FunGame_Core_Api: case FunGameInfo.FunGame.FunGame_Console: case FunGameInfo.FunGame.FunGame_Desktop: - writer.Write("[Config]"); - writer.Close(); /** * Config */ @@ -79,8 +76,6 @@ namespace Milimoe.FunGame.Core.Api.Utility WriteINI("Account", "AutoKey", ""); break; case FunGameInfo.FunGame.FunGame_Server: - writer.Write("[Server]"); - writer.Close(); /** * Server */ @@ -100,6 +95,11 @@ namespace Milimoe.FunGame.Core.Api.Utility * Socket */ WriteINI("Socket", "Port", "22222"); + WriteINI("Socket", "UseWebSocket", "false"); + WriteINI("Socket", "WebSocketAddress", "*"); + WriteINI("Socket", "WebSocketPort", "22223"); + WriteINI("Socket", "WebSocketSubUrl", "ws"); + WriteINI("Socket", "WebSocketSSL", "false"); WriteINI("Socket", "MaxPlayer", "20"); WriteINI("Socket", "MaxConnectFailed", "0"); /** diff --git a/Controller/SocketHandlerController.cs b/Controller/SocketHandlerController.cs index f83ac96..4407a3e 100644 --- a/Controller/SocketHandlerController.cs +++ b/Controller/SocketHandlerController.cs @@ -1,7 +1,6 @@ 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; namespace Milimoe.FunGame.Core.Controller { @@ -37,7 +36,7 @@ namespace Milimoe.FunGame.Core.Controller if (socket != null) { _Socket = socket; - socket.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler)); + socket.AddSocketObjectHandler(SocketHandler); } else throw new SocketCreateReceivingException(); } @@ -51,7 +50,7 @@ namespace Milimoe.FunGame.Core.Controller if (websocket != null) { _WebSocket = websocket; - websocket.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler)); + websocket.AddSocketObjectHandler(SocketHandler); } else throw new SocketCreateReceivingException(); } @@ -90,8 +89,8 @@ namespace Milimoe.FunGame.Core.Controller { if (Disposing) { - _Socket?.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler), true); - _WebSocket?.BindEvent(new SocketManager.SocketReceiveHandler(SocketHandler), true); + _Socket?.RemoveSocketObjectHandler(SocketHandler); + _WebSocket?.RemoveSocketObjectHandler(SocketHandler); } } IsDisposed = true; diff --git a/Entity/Statistics/UserStatistics.cs b/Entity/Statistics/UserStatistics.cs index 041951d..c10f97b 100644 --- a/Entity/Statistics/UserStatistics.cs +++ b/Entity/Statistics/UserStatistics.cs @@ -8,15 +8,15 @@ namespace Milimoe.FunGame.Core.Entity { public long Id => User.Id; public User User { get; } - public Dictionary DamageStats { get; } = new(); - public Dictionary PhysicalDamageStats { get; } = new(); - public Dictionary MagicDamageStats { get; } = new(); - public Dictionary RealDamageStats { get; } = new(); + public Dictionary DamageStats { get; } = []; + public Dictionary PhysicalDamageStats { get; } = []; + public Dictionary MagicDamageStats { get; } = []; + public Dictionary RealDamageStats { get; } = []; public Dictionary AvgDamageStats { get { - Dictionary avgdamage = new(); + Dictionary avgdamage = []; foreach (long key in Plays.Keys) { long plays = Plays[key]; @@ -34,7 +34,7 @@ namespace Milimoe.FunGame.Core.Entity { get { - Dictionary avgdamage = new(); + Dictionary avgdamage = []; foreach (long key in Plays.Keys) { long plays = Plays[key]; @@ -52,7 +52,7 @@ namespace Milimoe.FunGame.Core.Entity { get { - Dictionary avgdamage = new(); + Dictionary avgdamage = []; foreach (long key in Plays.Keys) { long plays = Plays[key]; @@ -70,7 +70,7 @@ namespace Milimoe.FunGame.Core.Entity { get { - Dictionary avgdamage = new(); + Dictionary avgdamage = []; foreach (long key in Plays.Keys) { long plays = Plays[key]; @@ -84,17 +84,17 @@ namespace Milimoe.FunGame.Core.Entity return avgdamage; } } - public Dictionary Kills { get; } = new(); - public Dictionary Deaths { get; } = new(); - public Dictionary Assists { get; } = new(); - public Dictionary Plays { get; } = new(); - public Dictionary Wins { get; } = new(); - public Dictionary Loses { get; } = new(); + public Dictionary Kills { get; } = []; + public Dictionary Deaths { get; } = []; + public Dictionary Assists { get; } = []; + public Dictionary Plays { get; } = []; + public Dictionary Wins { get; } = []; + public Dictionary Loses { get; } = []; public Dictionary Winrates { get { - Dictionary winrates = new(); + Dictionary winrates = []; foreach (long key in Plays.Keys) { long plays = Plays[key]; @@ -108,15 +108,15 @@ namespace Milimoe.FunGame.Core.Entity return winrates; } } - public Dictionary RatingStats { get; } = new(); - public Dictionary EloStats { get; } = new(); - public Dictionary RankStats { get; } = new(); + public Dictionary RatingStats { get; } = []; + public Dictionary EloStats { get; } = []; + public Dictionary RankStats { get; } = []; public string GetWinrate(long season) { - if (Winrates.ContainsKey(season)) + if (Winrates.TryGetValue(season, out double value)) { - return Winrates[season].ToString("0.##%"); + return value.ToString("0.##%"); } return "0%"; } diff --git a/Entity/System/Room.cs b/Entity/System/Room.cs index 8e9b141..504e60f 100644 --- a/Entity/System/Room.cs +++ b/Entity/System/Room.cs @@ -17,6 +17,8 @@ namespace Milimoe.FunGame.Core.Entity public bool IsRank { get; set; } = false; public bool HasPass => Password.Trim() != ""; public string Password { get; set; } = ""; + public int MaxUsers { get; set; } = 0; + public Dictionary UserAndIsReady { get; } = []; public GameStatistics Statistics { get; set; } internal Room() @@ -24,18 +26,19 @@ namespace Milimoe.FunGame.Core.Entity Statistics = new(this); } - internal Room(long Id = 0, string Roomid = "-1", DateTime? CreateTime = null, User? RoomMaster = null, RoomType RoomType = RoomType.All, string GameModule = "", string GameMap = "", RoomState RoomState = RoomState.Created, bool IsRank = false, string Password = "") + internal Room(long id = 0, string roomid = "-1", DateTime? createTime = null, User? roomMaster = null, RoomType roomType = RoomType.All, string gameModule = "", string gameMap = "", RoomState roomState = RoomState.Created, bool isRank = false, string password = "", int maxUsers = 4) { - this.Id = Id; - this.Roomid = Roomid; - this.CreateTime = CreateTime ?? General.DefaultTime; - this.RoomMaster = RoomMaster ?? General.UnknownUserInstance; - this.RoomType = RoomType; - this.GameModule = GameModule; - this.GameMap = GameMap; - this.RoomState = RoomState; - this.IsRank = IsRank; - this.Password = Password; + Id = id; + Roomid = roomid; + CreateTime = createTime ?? General.DefaultTime; + RoomMaster = roomMaster ?? General.UnknownUserInstance; + RoomType = roomType; + GameModule = gameModule; + GameMap = gameMap; + RoomState = roomState; + IsRank = isRank; + Password = password; + MaxUsers = maxUsers; Statistics = new(this); } diff --git a/Entity/System/Season.cs b/Entity/System/Season.cs new file mode 100644 index 0000000..bfeda9f --- /dev/null +++ b/Entity/System/Season.cs @@ -0,0 +1,9 @@ +namespace Milimoe.FunGame.Core.Entity.System +{ + public class Season(long id, string name, string description) + { + public long Id { get; } = id; + public string Name { get; } = name; + public string Description { get; } = description; + } +} diff --git a/Interface/Base/Addons/IGameModuleServer.cs b/Interface/Base/Addons/IGameModuleServer.cs index 1c4ad41..0ee4fca 100644 --- a/Interface/Base/Addons/IGameModuleServer.cs +++ b/Interface/Base/Addons/IGameModuleServer.cs @@ -8,6 +8,6 @@ namespace Milimoe.FunGame.Core.Interface.Addons { public bool StartServer(string GameModule, Room Room, List Users, IServerModel RoomMasterServerModel, Dictionary ServerModels, params object[] args); - public Dictionary GamingMessageHandler(string username, GamingType type, Dictionary data); + public Task> GamingMessageHandler(string username, GamingType type, Dictionary data); } } diff --git a/Interface/Base/HTTP/IHTTPClient.cs b/Interface/Base/HTTP/IHTTPClient.cs index 8101dd1..7d3bcef 100644 --- a/Interface/Base/HTTP/IHTTPClient.cs +++ b/Interface/Base/HTTP/IHTTPClient.cs @@ -6,8 +6,10 @@ namespace Milimoe.FunGame.Core.Interface.HTTP { public interface IHTTPClient : IBaseSocket { + public bool Receiving { get; } + public Task Receive(); public Task Send(SocketMessageType type, params object[] objs); - public SocketObject SocketObject_Handler(SocketObject objs); - public void BindEvent(Delegate method, bool remove = false); + public void AddSocketObjectHandler(Action method); + public void RemoveSocketObjectHandler(Action method); } } diff --git a/Interface/Base/IServerModel.cs b/Interface/Base/IServerModel.cs index 8ac4c31..639ecb8 100644 --- a/Interface/Base/IServerModel.cs +++ b/Interface/Base/IServerModel.cs @@ -1,6 +1,6 @@ -using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Api.Transmittal; +using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Library.Common.Addon; -using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Interface.Base @@ -10,28 +10,43 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// 服务器实例是否在运行 /// - public abstract bool Running { get; } + public bool Running { get; } /// /// 客户端的套接字实例 /// - public abstract ISocketMessageProcessor? Socket { get; } + public ISocketMessageProcessor? Socket { get; } + + /// + /// 客户端的数据库连接实例 + /// + public SQLHelper? SQLHelper { get; } + + /// + /// 客户端的邮件服务实例 + /// + public MailSender? MailSender { get; } /// /// 客户端的用户实例,在用户登录后有效 /// - public abstract User User { get; } + public User User { get; } /// /// 客户端的名称,默认是客户端的IP地址 /// - public abstract string ClientName { get; } + public string ClientName { get; } /// /// 客户端是否启动了开发者模式 /// public bool IsDebugMode { get; } + /// + /// 客户端所在的房间 + /// + public Room InRoom { get; set; } + /// /// 客户端的游戏模组服务器 /// @@ -40,11 +55,10 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// 向客户端发送消息 /// - /// /// /// /// - public bool Send(ISocketMessageProcessor socket, SocketMessageType type, params object[] objs); + public Task Send(SocketMessageType type, params object[] objs); /// /// 向客户端发送系统消息 @@ -61,19 +75,5 @@ namespace Milimoe.FunGame.Core.Interface.Base /// /// public string GetClientName(); - - /// - /// 开始接收客户端消息 - /// 请勿在 中调用此方法 - /// - /// - /// - public bool Read(ISocketMessageProcessor socket); - - /// - /// 启动对客户端的监听 - /// 请勿在 中调用此方法 - /// - public void Start(); } } diff --git a/Interface/Base/ISocketListener.cs b/Interface/Base/ISocketListener.cs new file mode 100644 index 0000000..13db755 --- /dev/null +++ b/Interface/Base/ISocketListener.cs @@ -0,0 +1,32 @@ +using Milimoe.FunGame.Core.Api.Utility; + +namespace Milimoe.FunGame.Core.Interface.Base +{ + public interface ISocketListener where T : ISocketMessageProcessor + { + /// + /// 监听器的名称 + /// + public string Name { get; } + + /// + /// 已连接的客户端列表 + /// + public ConcurrentModelList ClientList { get; } + + /// + /// 已登录的用户列表 + /// + public ConcurrentModelList UserList { get; } + + /// + /// 黑名单IP地址列表 + /// + public List BannedList { get; } + + /// + /// 关闭监听 + /// + public void Close(); + } +} diff --git a/Interface/Base/ISocketMessageProcessor.cs b/Interface/Base/ISocketMessageProcessor.cs index d9e212f..253cc55 100644 --- a/Interface/Base/ISocketMessageProcessor.cs +++ b/Interface/Base/ISocketMessageProcessor.cs @@ -1,15 +1,20 @@ -using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Interface.Base { public interface ISocketMessageProcessor { public Type InstanceType { get; } + public Guid Token { get; } public string ClientIP { get; } public string ClientName { get; } + public SocketObject[] Receive(); + public Task ReceiveAsync(); public SocketResult Send(SocketMessageType type, params object[] objs); public Task SendAsync(SocketMessageType type, params object[] objs); public void Close(); + public Task CloseAsync(); } } diff --git a/Interface/Base/Sockets/IClientSocket.cs b/Interface/Base/Sockets/IClientSocket.cs index 7b621a4..3880150 100644 --- a/Interface/Base/Sockets/IClientSocket.cs +++ b/Interface/Base/Sockets/IClientSocket.cs @@ -1,4 +1,5 @@ -using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Core.Interface.Sockets { @@ -6,8 +7,10 @@ namespace Milimoe.FunGame.Core.Interface.Sockets { public bool Receiving { get; } public void StartReceiving(Task t); + public void StopReceiving(); public SocketResult Send(SocketMessageType type, params object[] objs); - public Library.Common.Network.SocketObject[] Receive(); - public void BindEvent(Delegate method, bool remove = false); + public SocketObject[] Receive(); + public void AddSocketObjectHandler(Action method); + public void RemoveSocketObjectHandler(Action method); } } diff --git a/Interface/Base/Sockets/IClientWebSocket.cs b/Interface/Base/Sockets/IClientWebSocket.cs new file mode 100644 index 0000000..262c24d --- /dev/null +++ b/Interface/Base/Sockets/IClientWebSocket.cs @@ -0,0 +1,16 @@ +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.Core.Interface.Sockets +{ + public interface IClientWebSocket : IBaseSocket + { + public System.Net.WebSockets.WebSocket Instance { get; } + public bool Connected => Instance != null && Instance.State == System.Net.WebSockets.WebSocketState.Open; + public bool Receiving { get; } + public void StartReceiving(Task t); + public void StopReceiving(); + public Task ReceiveAsync(); + public Task SendAsync(SocketMessageType type, params object[] objs); + } +} diff --git a/Interface/Base/Sockets/IWebSocket.cs b/Interface/Base/Sockets/IWebSocket.cs deleted file mode 100644 index 07ab29b..0000000 --- a/Interface/Base/Sockets/IWebSocket.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Milimoe.FunGame.Core.Interface.Base; - -namespace Milimoe.FunGame.Core.Interface.Sockets -{ - public interface IWebSocket : IBaseSocket - { - public System.Net.WebSockets.WebSocket Instance { get; } - public bool Connected => Instance != null && Instance.State == System.Net.WebSockets.WebSocketState.Open; - } -} diff --git a/Library/Common/Addon/Example/ExampleGameModule.cs b/Library/Common/Addon/Example/ExampleGameModule.cs index 986dfd8..7b78f45 100644 --- a/Library/Common/Addon/Example/ExampleGameModule.cs +++ b/Library/Common/Addon/Example/ExampleGameModule.cs @@ -48,6 +48,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example public override RoomType RoomType => RoomType.Mix; + public override int MaxUsers => 8; + public override bool HideMain => false; public ExampleGameModule() @@ -175,7 +177,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example UserData[username].Add("connect_token", token); } } - SendGamingMessage(All.Values, GamingType.UpdateInfo, data); + await SendGamingMessage(All.Values, GamingType.UpdateInfo, data); // 新建一个线程等待所有玩家确认 while (true) @@ -187,7 +189,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example Controller.WriteLine("所有玩家都已经连接。"); } - public override Dictionary GamingMessageHandler(string username, GamingType type, Dictionary data) + public override async Task> GamingMessageHandler(string username, GamingType type, Dictionary data) { Dictionary result = []; @@ -205,6 +207,9 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon.Example } else Controller.WriteLine(username + " 确认连接失败!"); break; + default: + await Task.Delay(1); + break; } return result; diff --git a/Library/Common/Addon/GameModule.cs b/Library/Common/Addon/GameModule.cs index 64fb782..b5ef04f 100644 --- a/Library/Common/Addon/GameModule.cs +++ b/Library/Common/Addon/GameModule.cs @@ -44,6 +44,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// public abstract RoomType RoomType { get; } + /// + /// 模组的容纳人数 + /// + public abstract int MaxUsers { get; } + /// /// 是否隐藏主界面 /// diff --git a/Library/Common/Addon/GameModuleServer.cs b/Library/Common/Addon/GameModuleServer.cs index 97aa741..f269398 100644 --- a/Library/Common/Addon/GameModuleServer.cs +++ b/Library/Common/Addon/GameModuleServer.cs @@ -72,7 +72,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// 消息类型 /// 消息参数 /// 底层会将字典中的数据发送给客户端 - public abstract Dictionary GamingMessageHandler(string username, GamingType type, Dictionary data); + public abstract Task> GamingMessageHandler(string username, GamingType type, Dictionary data); /// /// 加载标记 @@ -120,15 +120,12 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// /// /// - protected virtual void SendGamingMessage(IEnumerable clients, GamingType type, Dictionary data) + protected virtual async Task SendGamingMessage(IEnumerable clients, GamingType type, Dictionary data) { // 发送局内消息 foreach (IServerModel s in clients) { - if (s != null && s.Socket != null) - { - s.Send(s.Socket, SocketMessageType.Gaming, type, data); - } + await s.Send(SocketMessageType.Gaming, type, data); } } @@ -138,15 +135,12 @@ namespace Milimoe.FunGame.Core.Library.Common.Addon /// /// /// - protected virtual void Send(IEnumerable clients, SocketMessageType type, params object[] args) + protected virtual async Task Send(IEnumerable clients, SocketMessageType type, params object[] args) { // 发送消息 foreach (IServerModel s in clients) { - if (s != null && s.Socket != null) - { - s.Send(s.Socket, type, args); - } + await s.Send(type, args); } } } diff --git a/Library/Common/Architecture/HeartBeat.cs b/Library/Common/Architecture/HeartBeat.cs index 9ea2284..e3f452c 100644 --- a/Library/Common/Architecture/HeartBeat.cs +++ b/Library/Common/Architecture/HeartBeat.cs @@ -12,6 +12,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture private Task? SendingHeartBeatTask; private bool _SendingHeartBeat = false; + private bool _LastHeartbeatReceived = false; private int _HeartBeatFaileds = 0; private readonly Socket? _Socket = null; @@ -20,13 +21,13 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture public HeartBeat(Socket socket) { _Socket = socket; - this.TransmittalType = TransmittalType.Socket; + TransmittalType = TransmittalType.Socket; } public HeartBeat(HTTPClient client) { _HTTPClient = client; - this.TransmittalType = TransmittalType.WebSocket; + TransmittalType = TransmittalType.WebSocket; } public void StartSendingHeartBeat() @@ -34,6 +35,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture if (!FunGameInfo.FunGame_DebugMode) { _SendingHeartBeat = true; + _Socket?.AddSocketObjectHandler(SocketObject_Handler); + _HTTPClient?.AddSocketObjectHandler(SocketObject_Handler); SendingHeartBeatTask = Task.Factory.StartNew(SendHeartBeat); } } @@ -43,6 +46,8 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture _SendingHeartBeat = false; SendingHeartBeatTask?.Wait(1); SendingHeartBeatTask = null; + _Socket?.RemoveSocketObjectHandler(SocketObject_Handler); + _HTTPClient?.RemoveSocketObjectHandler(SocketObject_Handler); } private async Task SendHeartBeat() @@ -54,13 +59,14 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture { if (!SendingHeartBeat) _SendingHeartBeat = true; // 发送心跳包 + _LastHeartbeatReceived = false; if (_Socket.Send(SocketMessageType.HeartBeat) == SocketResult.Success) { - await Task.Delay(4 * 000); - AddHeartBeatFaileds(); + await Task.Delay(4 * 1000); + if (!_LastHeartbeatReceived) AddHeartBeatFaileds(); } else AddHeartBeatFaileds(); - await Task.Delay(20 * 000); + await Task.Delay(20 * 1000); } } else if (_HTTPClient != null) @@ -71,11 +77,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture // 发送心跳包 if (await _HTTPClient.Send(SocketMessageType.HeartBeat) == SocketResult.Success) { - await Task.Delay(4 * 000); + await Task.Delay(4 * 1000); AddHeartBeatFaileds(); } else AddHeartBeatFaileds(); - await Task.Delay(20 * 000); + await Task.Delay(20 * 1000); } } _SendingHeartBeat = false; @@ -85,7 +91,20 @@ namespace Milimoe.FunGame.Core.Library.Common.Architecture { // 超过三次没回应心跳,服务器连接失败。 if (_HeartBeatFaileds++ >= 3) + { + _Socket?.Close(); + _HTTPClient?.Close(); throw new LostConnectException(); + } + } + + private void SocketObject_Handler(SocketObject obj) + { + if (obj.SocketType == SocketMessageType.HeartBeat) + { + _LastHeartbeatReceived = true; + _HeartBeatFaileds = 0; + } } } } diff --git a/Library/Common/Event/RoomEventArgs.cs b/Library/Common/Event/RoomEventArgs.cs index b4bbbe4..b05ecbd 100644 --- a/Library/Common/Event/RoomEventArgs.cs +++ b/Library/Common/Event/RoomEventArgs.cs @@ -22,7 +22,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Event RoomType = type; RoomTypeString = RoomSet.GetTypeString(type); Password = password; - Room = Factory.GetRoom(RoomType: RoomType, Password: Password); + Room = Factory.GetRoom(roomType: RoomType, password: Password); } public RoomEventArgs(Room room) diff --git a/Library/Common/Network/ClientSocket.cs b/Library/Common/Network/ClientSocket.cs deleted file mode 100644 index 6be9172..0000000 --- a/Library/Common/Network/ClientSocket.cs +++ /dev/null @@ -1,93 +0,0 @@ -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(System.Net.Sockets.Socket instance, string clientIP, string clientName, Guid token) : IClientSocket, ISocketMessageProcessor - { - public System.Net.Sockets.Socket Instance { get; } = instance; - public SocketRuntimeType Runtime => SocketRuntimeType.Server; - public Guid Token { get; } = token; - public string ClientIP { get; } = clientIP; - public string ClientName => _ClientName; - public bool Connected => Instance != null && Instance.Connected; - public bool Receiving => _Receiving; - public Type InstanceType => typeof(ClientSocket); - - private Task? ReceivingTask; - - private bool _Receiving; - private readonly string _ClientName = clientName; - - public void Close() - { - StopReceiving(); - Instance?.Close(); - } - - public SocketObject[] Receive() - { - try - { - return SocketManager.Receive(Instance); - } - catch - { - throw new SocketWrongInfoException(); - } - } - - public SocketResult Send(SocketMessageType type, params object[] objs) - { - if (Instance != null) - { - if (SocketManager.Send(Instance, new(type, Token, objs)) == SocketResult.Success) - { - return SocketResult.Success; - } - else return SocketResult.Fail; - } - return SocketResult.NotSent; - } - - public async Task SendAsync(SocketMessageType type, params object[] objs) - { - if (Instance != null) - { - if (await SocketManager.SendAsync(Instance, new(type, Token, objs)) == SocketResult.Success) - { - return SocketResult.Success; - } - else return SocketResult.Fail; - } - return SocketResult.NotSent; - } - - public void BindEvent(Delegate method, bool remove = false) - { - if (!remove) - { - SocketManager.SocketReceive += (SocketManager.SocketReceiveHandler)method; - } - else - { - SocketManager.SocketReceive -= (SocketManager.SocketReceiveHandler)method; - } - } - - public void StartReceiving(Task t) - { - _Receiving = true; - ReceivingTask = t; - } - - public void StopReceiving() - { - _Receiving = false; - ReceivingTask?.Wait(1); - ReceivingTask = null; - } - } -} diff --git a/Library/Common/Network/ClientWebSocket.cs b/Library/Common/Network/ClientWebSocket.cs deleted file mode 100644 index c72736c..0000000 --- a/Library/Common/Network/ClientWebSocket.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.Net.WebSockets; -using Milimoe.FunGame.Core.Api.Utility; -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 ClientWebSocket(HTTPListener listener, WebSocket instance, string clientIP, string clientName, Guid token) : IWebSocket, ISocketMessageProcessor - { - public HTTPListener Listener => listener; - public WebSocket Instance => instance; - public SocketRuntimeType Runtime => SocketRuntimeType.Server; - public Guid Token => token; - public string ClientIP => clientIP; - public string ClientName => clientName; - public Type InstanceType => typeof(ClientWebSocket); - - public void Close() - { - TaskUtility.NewTask(async () => - { - if (Instance.State == WebSocketState.Open) - { - // 安全关闭 WebSocket 连接 - await Instance.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None); - Listener.ClientSockets.Remove(Token); - } - }); - } - - public SocketResult Send(SocketMessageType type, params object[] objs) - { - throw new AsyncSendException(); - } - - public async Task SendAsync(SocketMessageType type, params object[] objs) - { - return await HTTPManager.Send(Instance, new(type, token, objs)); - } - } -} diff --git a/Library/Common/Network/HTTPClient.cs b/Library/Common/Network/HTTPClient.cs index 5ed0b49..d847be0 100644 --- a/Library/Common/Network/HTTPClient.cs +++ b/Library/Common/Network/HTTPClient.cs @@ -11,53 +11,44 @@ namespace Milimoe.FunGame.Core.Library.Common.Network { public System.Net.WebSockets.ClientWebSocket? Instance { get; } = null; public SocketRuntimeType Runtime => SocketRuntimeType.Client; - public Guid Token { get; } = Guid.Empty; + public Guid Token { get; set; } = Guid.Empty; public string ServerAddress { get; } = ""; public int ServerPort { get; } = 0; public string ServerName { get; } = ""; public string ServerNotice { get; } = ""; + public bool Connected => Instance != null && Instance.State == WebSocketState.Open; + public bool Receiving => _receiving; + private HeartBeat HeartBeat { get; } - private bool _Listening = false; - private readonly HeartBeat HeartBeat; + private bool _receiving = false; + private readonly HashSet> _boundEvents = []; - private HTTPClient(System.Net.WebSockets.ClientWebSocket Instance, string ServerAddress, int ServerPort, params object[] args) + private HTTPClient(System.Net.WebSockets.ClientWebSocket instance, string serverAddress, int serverPort, params object[] args) { - this.Instance = Instance; - this.ServerAddress = ServerAddress; - this.ServerPort = ServerPort; + Instance = instance; + ServerAddress = serverAddress; + 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) + public static async Task Connect(string serverAddress, int serverPort, bool ssl, string subUrl = "", params object[] args) { - string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(ServerAddress); - Uri uri = new((SSL ? "wss://" : "ws://") + ServerIP + ":" + ServerPort + "/" + SubDirectory); + string ServerIP = Api.Utility.NetworkUtility.GetIPAddress(serverAddress); + Uri uri = new((ssl ? "wss://" : "ws://") + ServerIP + ":" + serverPort + "/" + subUrl.Trim('/') + "/"); System.Net.WebSockets.ClientWebSocket? socket = await HTTPManager.Connect(uri); if (socket != null && socket.State == WebSocketState.Open) { - HTTPClient client = new(socket, ServerAddress, ServerPort, args); + 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) + while (_receiving) { try { @@ -81,29 +72,42 @@ namespace Milimoe.FunGame.Core.Library.Common.Network return SocketResult.NotSent; } - public virtual SocketObject SocketObject_Handler(SocketObject objs) + public void AddSocketObjectHandler(Action method) { - return new(SocketMessageType.Unknown, Guid.Empty); + if (_boundEvents.Add(method)) + { + SocketManager.SocketReceive += new SocketManager.SocketReceiveHandler(method); + } } - public void BindEvent(Delegate method, bool remove = false) + public void RemoveSocketObjectHandler(Action method) { - if (!remove) - { - SocketManager.SocketReceive += (SocketManager.SocketReceiveHandler)method; - } - else - { - SocketManager.SocketReceive -= (SocketManager.SocketReceiveHandler)method; - } + _boundEvents.Remove(method); + SocketManager.SocketReceive -= new SocketManager.SocketReceiveHandler(method); } public void Close() { - _Listening = false; + _receiving = false; HeartBeat.StopSendingHeartBeat(); Instance?.CloseOutputAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None); Instance?.Dispose(); + foreach (Action method in _boundEvents.ToList()) + { + RemoveSocketObjectHandler(method); + } + } + + 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)) + { + _receiving = true; + await Receive(); + } + } } } } diff --git a/Library/Common/Network/HTTPListener.cs b/Library/Common/Network/HTTPListener.cs index d9d8fb4..607688a 100644 --- a/Library/Common/Network/HTTPListener.cs +++ b/Library/Common/Network/HTTPListener.cs @@ -1,18 +1,22 @@ using System.Net; using System.Net.WebSockets; using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Base; 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 class HTTPListener : IHTTPListener, ISocketListener { public HttpListener Instance { get; } + public string Name => "HTTPListener"; public SocketRuntimeType Runtime => SocketRuntimeType.Server; public Guid Token { get; } = Guid.Empty; - public Dictionary ClientSockets { get; } = []; + public ConcurrentModelList ClientList { get; } = []; + public ConcurrentModelList UserList { get; } = []; + public List BannedList { get; } = []; private HTTPListener(HttpListener instance) { @@ -20,24 +24,27 @@ namespace Milimoe.FunGame.Core.Library.Common.Network Token = Guid.NewGuid(); } - public static HTTPListener StartListening(int port, bool ssl = false) + public static HTTPListener StartListening(string address = "*", int port = 22223, string subUrl = "ws", bool ssl = false) { - HttpListener? socket = HTTPManager.StartListening(port, ssl); + HttpListener? socket = HTTPManager.StartListening(address, port, subUrl, ssl); if (socket != null) { HTTPListener instance = new(socket); - Task t = Task.Run(async () => await HTTPManager.Receiving(instance)); return instance; } else throw new SocketCreateListenException(); } - public async Task SendMessage(Guid token, SocketObject obj) + public async Task Accept(Guid token) { - if (ClientSockets.TryGetValue(token, out WebSocket? socket) && socket != null) + object[] result = await HTTPManager.Accept(); + if (result != null && result.Length == 2) { - await HTTPManager.Send(socket, obj); + string clientIP = (string)result[0]; + WebSocket client = (WebSocket)result[1]; + return new ServerWebSocket(this, client, clientIP, clientIP, token); } + throw new SocketGetClientException(); } public virtual bool CheckClientConnection(SocketObject objs) @@ -52,27 +59,7 @@ namespace Milimoe.FunGame.Core.Library.Common.Network public void Close() { - bool closing = true; - TaskUtility.NewTask(async () => - { - // 关闭所有 WebSocket 连接 - foreach (WebSocket socket in ClientSockets.Values) - { - if (socket.State == WebSocketState.Open) - { - // 安全关闭 WebSocket 连接 - await socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None); - } - } - ClientSockets.Clear(); - Instance?.Close(); - closing = true; - }); - while (closing) - { - if (!closing) break; - Thread.Sleep(100); - } + Instance?.Close(); } } } diff --git a/Library/Common/Network/ServerSocket.cs b/Library/Common/Network/ServerSocket.cs index d5e86ae..d1d2c0a 100644 --- a/Library/Common/Network/ServerSocket.cs +++ b/Library/Common/Network/ServerSocket.cs @@ -5,109 +5,109 @@ using Milimoe.FunGame.Core.Service; namespace Milimoe.FunGame.Core.Library.Common.Network { - public class ServerSocket : ISocket + public class ServerSocket(SocketListener listener, System.Net.Sockets.Socket instance, string clientIP, string clientName, Guid token) : IClientSocket, ISocketMessageProcessor { - public System.Net.Sockets.Socket Instance { get; } + public SocketListener Listener { get; } = listener; + 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 ClientIP { get; } = clientIP; + public string ClientName => clientName; public bool Connected => Instance != null && Instance.Connected; - public List ClientList => OnlineClients.GetList(); - public List UserList => OnlineUsers.GetList(); - public List BannedList { get; } = []; - public int ClientCount => OnlineClients.Count; - public int UserCount => OnlineUsers.Count; - public int BannedCount => BannedList.Count; + public bool Receiving => _receiving; + public Type InstanceType => typeof(ServerSocket); - private readonly ModelManager OnlineClients; - private readonly ModelManager OnlineUsers; - - private ServerSocket(System.Net.Sockets.Socket instance, int maxConnection = 0) - { - Token = Guid.NewGuid(); - Instance = instance; - if (maxConnection <= 0) - { - OnlineClients = []; - OnlineUsers = []; - } - else - { - OnlineClients = new(maxConnection); - OnlineUsers = new(maxConnection); - } - } - - public static ServerSocket StartListening(int port = 22222, int maxConnection = 0) - { - if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G; - System.Net.Sockets.Socket? socket = SocketManager.StartListening(port, maxConnection); - if (socket != null) return new ServerSocket(socket, port); - else throw new SocketCreateListenException(); - } - - public static ClientSocket Accept(Guid token) - { - object[] result = SocketManager.Accept(); - if (result != null && result.Length == 2) - { - string clientIP = (string)result[0]; - System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)result[1]; - return new ClientSocket(client, clientIP, clientIP, token); - } - throw new SocketGetClientException(); - } - - public bool AddClient(string name, IServerModel t) - { - name = name.ToLower(); - return OnlineClients.Add(name, t); - } - - public bool RemoveClient(string name) - { - name = name.ToLower(); - return OnlineClients.Remove(name); - } - - public bool ContainsClient(string name) - { - name = name.ToLower(); - return OnlineClients.ContainsKey(name); - } - - public IServerModel GetClient(string name) - { - name = name.ToLower(); - return OnlineClients[name]; - } - - public bool AddUser(string name, IServerModel t) - { - name = name.ToLower(); - return OnlineUsers.Add(name, t); - } - - public bool RemoveUser(string name) - { - name = name.ToLower(); - return OnlineUsers.Remove(name); - } - - public bool ContainsUser(string name) - { - name = name.ToLower(); - return OnlineUsers.ContainsKey(name); - } - - public IServerModel GetUser(string name) - { - name = name.ToLower(); - return OnlineUsers[name]; - } + private Task? _receivingTask; + private bool _receiving; + private readonly HashSet> _boundEvents = []; public void Close() { - Instance?.Close(); + StopReceiving(); + Instance.Close(); + } + + public async Task CloseAsync() + { + StopReceiving(); + await Task.Run(() => Instance?.Close()); + } + + public SocketObject[] Receive() + { + try + { + return SocketManager.Receive(Instance); + } + catch + { + throw new SocketWrongInfoException(); + } + } + + public async Task ReceiveAsync() + { + try + { + return await SocketManager.ReceiveAsync(Instance); + } + catch + { + throw new SocketWrongInfoException(); + } + } + + public SocketResult Send(SocketMessageType type, params object[] objs) + { + if (Instance != null) + { + if (SocketManager.Send(Instance, new(type, Token, objs)) == SocketResult.Success) + { + return SocketResult.Success; + } + else return SocketResult.Fail; + } + return SocketResult.NotSent; + } + + public async Task SendAsync(SocketMessageType type, params object[] objs) + { + if (Instance != null) + { + if (await SocketManager.SendAsync(Instance, new(type, Token, objs)) == SocketResult.Success) + { + return SocketResult.Success; + } + else return SocketResult.Fail; + } + return SocketResult.NotSent; + } + + public void AddSocketObjectHandler(Action method) + { + if (_boundEvents.Add(method)) + { + SocketManager.SocketReceive += new SocketManager.SocketReceiveHandler(method); + } + } + + public void RemoveSocketObjectHandler(Action method) + { + _boundEvents.Remove(method); + SocketManager.SocketReceive -= new SocketManager.SocketReceiveHandler(method); + } + + public void StartReceiving(Task t) + { + _receiving = true; + _receivingTask = t; + } + + public void StopReceiving() + { + _receiving = false; + _receivingTask?.Wait(1); + _receivingTask = null; } } } diff --git a/Library/Common/Network/ServerWebSocket.cs b/Library/Common/Network/ServerWebSocket.cs new file mode 100644 index 0000000..4130e49 --- /dev/null +++ b/Library/Common/Network/ServerWebSocket.cs @@ -0,0 +1,77 @@ +using System.Net.WebSockets; +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 ServerWebSocket(ISocketListener listener, WebSocket instance, string clientIP, string clientName, Guid token) : IClientWebSocket, ISocketMessageProcessor + { + public ISocketListener Listener => listener; + public WebSocket Instance => instance; + public SocketRuntimeType Runtime => SocketRuntimeType.Server; + public Guid Token => token; + public string ClientIP => clientIP; + public string ClientName => clientName; + public Type InstanceType => typeof(ServerWebSocket); + public bool Receiving => _receiving; + + private Task? _receivingTask; + private bool _receiving = false; + + public void Close() + { + throw new AsyncSendException(); + } + + public async Task CloseAsync() + { + if (Instance.State == WebSocketState.Open) + { + // 安全关闭 WebSocket 连接 + await Instance.CloseAsync(WebSocketCloseStatus.NormalClosure, "服务器正在关闭,断开连接!", CancellationToken.None); + } + } + + public SocketObject[] Receive() + { + throw new AsyncReadException(); + } + + public async Task ReceiveAsync() + { + try + { + return await HTTPManager.Receive(Instance); + } + catch + { + throw new SocketWrongInfoException(); + } + } + + public SocketResult Send(SocketMessageType type, params object[] objs) + { + throw new AsyncSendException(); + } + + public async Task SendAsync(SocketMessageType type, params object[] objs) + { + return await HTTPManager.Send(Instance, new(type, token, objs)); + } + + public void StartReceiving(Task t) + { + _receiving = true; + _receivingTask = t; + } + + public void StopReceiving() + { + _receiving = false; + _receivingTask?.Wait(1); + _receivingTask = null; + } + } +} diff --git a/Library/Common/Network/Socket.cs b/Library/Common/Network/Socket.cs index e769e5e..6bf1a50 100644 --- a/Library/Common/Network/Socket.cs +++ b/Library/Common/Network/Socket.cs @@ -16,11 +16,12 @@ namespace Milimoe.FunGame.Core.Library.Common.Network public string ServerName { get; } = ""; public string ServerNotice { get; } = ""; public bool Connected => Instance != null && Instance.Connected; - public bool Receiving => _Receiving; + public bool Receiving => _receiving; + private HeartBeat HeartBeat { get; } - private Task? ReceivingTask; - private readonly HeartBeat HeartBeat; - private bool _Receiving = false; + private Task? _receivingTask; + private bool _receiving = false; + private readonly HashSet> _boundEvents = []; private Socket(System.Net.Sockets.Socket instance, string serverAddress, int serverPort) { @@ -65,36 +66,42 @@ namespace Milimoe.FunGame.Core.Library.Common.Network } } - public void BindEvent(Delegate method, bool remove = false) + public void AddSocketObjectHandler(Action method) { - if (!remove) + if (_boundEvents.Add(method)) { - SocketManager.SocketReceive += (SocketManager.SocketReceiveHandler)method; - } - else - { - SocketManager.SocketReceive -= (SocketManager.SocketReceiveHandler)method; + SocketManager.SocketReceive += new SocketManager.SocketReceiveHandler(method); } } + public void RemoveSocketObjectHandler(Action method) + { + _boundEvents.Remove(method); + SocketManager.SocketReceive -= new SocketManager.SocketReceiveHandler(method); + } + public void Close() { HeartBeat.StopSendingHeartBeat(); StopReceiving(); Instance?.Close(); + foreach (Action method in _boundEvents.ToList()) + { + RemoveSocketObjectHandler(method); + } } public void StartReceiving(Task t) { - _Receiving = true; - ReceivingTask = t; + _receiving = true; + _receivingTask = t; } - private void StopReceiving() + public void StopReceiving() { - _Receiving = false; - ReceivingTask?.Wait(1); - ReceivingTask = null; + _receiving = false; + _receivingTask?.Wait(1); + _receivingTask = null; } } } diff --git a/Library/Common/Network/SocketListener.cs b/Library/Common/Network/SocketListener.cs new file mode 100644 index 0000000..d5bc88d --- /dev/null +++ b/Library/Common/Network/SocketListener.cs @@ -0,0 +1,51 @@ +using Milimoe.FunGame.Core.Api.Utility; +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 SocketListener : ISocket, ISocketListener + { + public System.Net.Sockets.Socket Instance { get; } + public string Name => "SocketListener"; + public SocketRuntimeType Runtime => SocketRuntimeType.Server; + public Guid Token { get; } = Guid.Empty; + public bool Connected => Instance != null && Instance.Connected; + public ConcurrentModelList ClientList { get; } = []; + public ConcurrentModelList UserList { get; } = []; + public List BannedList { get; } = []; + + private SocketListener(System.Net.Sockets.Socket instance) + { + Token = Guid.NewGuid(); + Instance = instance; + } + + public static SocketListener StartListening(int port = 22222, int maxConnection = 0) + { + if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G; + System.Net.Sockets.Socket? socket = SocketManager.StartListening(port, maxConnection); + if (socket != null) return new SocketListener(socket); + else throw new SocketCreateListenException(); + } + + public ServerSocket Accept(Guid token) + { + object[] result = SocketManager.Accept(); + if (result != null && result.Length == 2) + { + string clientIP = (string)result[0]; + System.Net.Sockets.Socket client = (System.Net.Sockets.Socket)result[1]; + return new ServerSocket(this, client, clientIP, clientIP, token); + } + throw new SocketGetClientException(); + } + + public void Close() + { + Instance?.Close(); + } + } +} diff --git a/Library/Common/Network/SocketObject.cs b/Library/Common/Network/SocketObject.cs index 933394c..aef6476 100644 --- a/Library/Common/Network/SocketObject.cs +++ b/Library/Common/Network/SocketObject.cs @@ -27,11 +27,11 @@ namespace Milimoe.FunGame.Core.Library.Common.Network } [JsonConstructor] - public SocketObject(SocketMessageType SocketType, Guid Token, params object[] Parameters) + public SocketObject(SocketMessageType type, Guid token, params object[] args) { - this.SocketType = SocketType; - this.Token = Token; - if (Parameters != null && Parameters.Length > 0) this.Parameters = Parameters; + SocketType = type; + Token = token; + if (args != null && args.Length > 0) Parameters = args; } /// diff --git a/Library/Constant/ConstantSet.cs b/Library/Constant/ConstantSet.cs index 9519dee..54ba832 100644 --- a/Library/Constant/ConstantSet.cs +++ b/Library/Constant/ConstantSet.cs @@ -102,7 +102,7 @@ namespace Milimoe.FunGame.Core.Library.Constant /** * Register */ - public const string Reg_GetRegVerifyCode = "Reg::GetRegVerifyCode"; + public const string Reg_Reg = "Reg::Reg"; /** * Login */ @@ -140,7 +140,7 @@ namespace Milimoe.FunGame.Core.Library.Constant DataRequestType.Main_Ready => Main_Ready, DataRequestType.Main_CancelReady => Main_CancelReady, DataRequestType.Main_StartGame => Main_StartGame, - DataRequestType.Reg_GetRegVerifyCode => Reg_GetRegVerifyCode, + DataRequestType.Reg_Reg => Reg_Reg, DataRequestType.Login_Login => Login_Login, DataRequestType.Login_GetFindPasswordVerifyCode => Login_GetFindPasswordVerifyCode, DataRequestType.Login_UpdatePassword => Login_UpdatePassword, diff --git a/Library/Constant/General.cs b/Library/Constant/General.cs index 6821e29..d59e8aa 100644 --- a/Library/Constant/General.cs +++ b/Library/Constant/General.cs @@ -38,7 +38,7 @@ namespace Milimoe.FunGame.Core.Library.Constant /// /// 默认的字符编码 /// - public static Encoding DefaultEncoding => Encoding.Unicode; + public static Encoding DefaultEncoding => Encoding.UTF8; /// /// 默认的时间格式 yyyy-MM-dd HH:mm:ss.fff diff --git a/Library/Constant/TypeEnum.cs b/Library/Constant/TypeEnum.cs index 4090910..a158c20 100644 --- a/Library/Constant/TypeEnum.cs +++ b/Library/Constant/TypeEnum.cs @@ -97,7 +97,7 @@ namespace Milimoe.FunGame.Core.Library.Constant Main_Ready, Main_CancelReady, Main_StartGame, - Reg_GetRegVerifyCode, + Reg_Reg, Login_Login, Login_GetFindPasswordVerifyCode, Login_UpdatePassword, @@ -843,7 +843,8 @@ namespace Milimoe.FunGame.Core.Library.Constant Interface, DataRequest, Plugin, - GameModule + GameModule, + Warning } public enum SQLMode diff --git a/Library/Exception/Exception.cs b/Library/Exception/Exception.cs index 1c7d0ba..ab1ac06 100644 --- a/Library/Exception/Exception.cs +++ b/Library/Exception/Exception.cs @@ -165,13 +165,18 @@ public override string Message => "试图在不支持的类中创建数据请求 (#10033)"; } - public class AsyncSendException : Exception - { - public override string Message => "必须以异步方式发送数据 (#10034)"; - } - public class SQLServiceException : Exception { - public override string Message => "SQL服务遇到错误,请检查SQL配置 (#10035)"; + public override string Message => "SQL服务遇到错误,请检查SQL配置 (#10034)"; + } + + public class AsyncSendException : Exception + { + public override string Message => "必须以异步方式发送数据 (#10035)"; + } + + public class AsyncReadException : Exception + { + public override string Message => "必须以异步方式读取数据 (#10036)"; } } diff --git a/Library/SQLScript/Entity/RoomQuery.cs b/Library/SQLScript/Entity/RoomQuery.cs index cea243e..543238e 100644 --- a/Library/SQLScript/Entity/RoomQuery.cs +++ b/Library/SQLScript/Entity/RoomQuery.cs @@ -15,16 +15,17 @@ public const string Column_IsRank = "IsRank"; public const string Column_HasPass = "HasPass"; public const string Column_Password = "Password"; + public const string Column_MaxUsers = "MaxUsers"; public const string Select_Rooms = $"{Command_Select} {TableName}.{Command_All}, {UserQuery.TableName}.{UserQuery.Column_Username} {Command_As} {Column_RoomMasterName} " + $"{Command_From} {TableName} {Command_LeftJoin} {UserQuery.TableName} {Command_On} {UserQuery.TableName}.{UserQuery.Column_UID} = {TableName}.{Column_RoomMaster}"; - public static string Insert_CreateRoom(string RoomID, long RoomMaster, Library.Constant.RoomType RoomType, string GameModule, string GameMap, bool IsRank, string Password) + public static string Insert_CreateRoom(string roomid, long roomMaster, Library.Constant.RoomType roomType, string gameModule, string gameMap, bool isRank, string password, int maxUsers) { Library.Constant.RoomState RoomState = Library.Constant.RoomState.Created; DateTime NowTime = DateTime.Now; - bool HasPass = Password.Trim() != ""; - return $"{Command_Insert} {Command_Into} {TableName} ({Column_RoomID}, {Column_CreateTime}, {Column_RoomMaster}, {Column_RoomType}, {Column_GameModule}, {Column_GameMap}, {Column_RoomState}, {Column_IsRank}, {Column_HasPass}, {Column_Password})" + - $" {Command_Values} ('{RoomID}', '{NowTime}', {RoomMaster}, {(int)RoomType}, '{GameModule}', '{GameMap}', {(int)RoomState}, {(IsRank ? 1 : 0)}, {(HasPass ? 1 : 0)}, '{Password}')"; + bool HasPass = password.Trim() != ""; + return $"{Command_Insert} {Command_Into} {TableName} ({Column_RoomID}, {Column_CreateTime}, {Column_RoomMaster}, {Column_RoomType}, {Column_GameModule}, {Column_GameMap}, {Column_RoomState}, {Column_IsRank}, {Column_HasPass}, {Column_Password}, {Column_MaxUsers})" + + $" {Command_Values} ('{roomid}', '{NowTime}', {roomMaster}, {(int)roomType}, '{gameModule}', '{gameMap}', {(int)RoomState}, {(isRank ? 1 : 0)}, {(HasPass ? 1 : 0)}, '{password}', {maxUsers})"; } public static string Delete_Rooms(params string[] roomids) @@ -37,19 +38,19 @@ return $"{Command_Delete} {Command_From} {TableName}"; } - public static string Delete_QuitRoom(string RoomID, long RoomMaster) + public static string Delete_QuitRoom(string roomID, long roomMaster) { - return $"{Command_Delete} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{RoomID}' {Command_And} {Column_RoomMaster} = {RoomMaster}"; + return $"{Command_Delete} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{roomID}' {Command_And} {Column_RoomMaster} = {roomMaster}"; } - public static string Update_QuitRoom(string RoomID, long OldRoomMaster, long NewRoomMaster) + public static string Update_QuitRoom(string roomid, long oldRoomMaster, long newRoomMaster) { - return $"{Command_Update} {TableName} {Command_Set} {Column_RoomMaster} = {NewRoomMaster} {Command_Where} {Column_RoomID} = '{RoomID}' {Command_And} {Column_RoomMaster} = {OldRoomMaster}"; + return $"{Command_Update} {TableName} {Command_Set} {Column_RoomMaster} = {newRoomMaster} {Command_Where} {Column_RoomID} = '{roomid}' {Command_And} {Column_RoomMaster} = {oldRoomMaster}"; } - public static string Select_IsExistRoom(string RoomID) + public static string Select_IsExistRoom(string roomid) { - return $"{Command_Select} {Command_All} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{RoomID}'"; + return $"{Command_Select} {Command_All} {Command_From} {TableName} {Command_Where} {Column_RoomID} = '{roomid}'"; } } } diff --git a/Library/SQLScript/Entity/UserQuery.cs b/Library/SQLScript/Entity/UserQuery.cs index 1df5742..f83af9e 100644 --- a/Library/SQLScript/Entity/UserQuery.cs +++ b/Library/SQLScript/Entity/UserQuery.cs @@ -42,7 +42,7 @@ public static string Select_Users_Where(string Where) { - return $"{Select_Users} {Command_Where} {Where}'"; + return $"{Select_Users} {Command_Where} {Where}"; } public static string Select_CheckAutoKey(string Username, string AutoKey) diff --git a/Model/RoomList.cs b/Model/RoomList.cs index 591e5ef..0570963 100644 --- a/Model/RoomList.cs +++ b/Model/RoomList.cs @@ -7,39 +7,41 @@ namespace Milimoe.FunGame.Core.Model public class RoomList : IEnumerable { private readonly Dictionary _List = []; - private readonly Dictionary> _PlayerList = []; - private readonly Dictionary> _ReadyPlayerList = []; + private readonly Dictionary> _UserList = []; + private readonly Dictionary> _ReadyUserList = []; public Room this[string roomid] => GetRoom(roomid); public int Count => _List.Count; - public int GetPlayerCount(string roomid) => GetPlayerList(roomid).Count; + public int GetUserCount(string roomid) => this[roomid].UserAndIsReady.Count; - public int GetReadyPlayerCount(string roomid) => GetReadyPlayerList(roomid).Count; + public int GetReadyUserCount(string roomid) => GetReadyUserList(roomid).Count; public List ListRoom => [.. _List.Values]; public List ListRoomID => [.. _List.Keys]; - public List GetPlayerList(string roomid) => _PlayerList.TryGetValue(roomid, out List? user) ? user : []; + public User GetRoomMaster(string roomid) => this[roomid].RoomMaster; - public List GetReadyPlayerList(string roomid) => _ReadyPlayerList.TryGetValue(roomid, out List? user) ? user : []; + public List GetUsers(string roomid) => [.. this[roomid].UserAndIsReady.Keys]; - public List GetNotReadyPlayerList(string roomid) => _PlayerList.TryGetValue(roomid, out List? user) ? user.Except(GetReadyPlayerList(roomid)).Except([this[roomid].RoomMaster]).ToList() : []; + public List GetReadyUserList(string roomid) => this[roomid].UserAndIsReady.Where(kv => kv.Value && kv.Key.Id != GetRoomMaster(roomid).Id).Select(kv => kv.Key).ToList(); + + public List GetNotReadyUserList(string roomid) => this[roomid].UserAndIsReady.Where(kv => !kv.Value && kv.Key.Id != GetRoomMaster(roomid).Id).Select(kv => kv.Key).ToList(); public void Clear() { _List.Clear(); - _PlayerList.Clear(); - _ReadyPlayerList.Clear(); + _UserList.Clear(); + _ReadyUserList.Clear(); } public void AddRoom(Room room) { _List.Add(room.Roomid, room); - _PlayerList.Add(room.Roomid, []); - _ReadyPlayerList.Add(room.Roomid, []); + _UserList.Add(room.Roomid, []); + _ReadyUserList.Add(room.Roomid, []); } public void AddRooms(List rooms) @@ -53,8 +55,8 @@ namespace Milimoe.FunGame.Core.Model public void RemoveRoom(string roomid) { _List.Remove(roomid); - _PlayerList.Remove(roomid); - _ReadyPlayerList.Remove(roomid); + _UserList.Remove(roomid); + _ReadyUserList.Remove(roomid); } public void RemoveRoom(Room room) => RemoveRoom(room.Roomid); @@ -63,7 +65,10 @@ namespace Milimoe.FunGame.Core.Model { if (roomid != "-1" && user.Id != 0) { - GetPlayerList(roomid).Add(user); + if (!this[roomid].UserAndIsReady.TryAdd(user, false)) + { + this[roomid].UserAndIsReady[user] = false; + } } } @@ -71,23 +76,23 @@ namespace Milimoe.FunGame.Core.Model { if (roomid != "-1" && user.Id != 0) { - GetPlayerList(roomid).Remove(user); + this[roomid].UserAndIsReady.Remove(user); } } public void SetReady(string roomid, User user) { - if (roomid != "-1" && user.Id != 0) + if (roomid != "-1" && user.Id != 0 && this[roomid].UserAndIsReady.ContainsKey(user)) { - GetReadyPlayerList(roomid).Add(user); + this[roomid].UserAndIsReady[user] = true; } } public void CancelReady(string roomid, User user) { - if (roomid != "-1" && user.Id != 0) + if (roomid != "-1" && user.Id != 0 && this[roomid].UserAndIsReady.ContainsKey(user)) { - GetReadyPlayerList(roomid).Remove(user); + this[roomid].UserAndIsReady[user] = false; } } @@ -95,15 +100,6 @@ namespace Milimoe.FunGame.Core.Model public bool IsExist(string roomid) => _List.ContainsKey(roomid); - public User GetRoomMaster(string roomid) - { - foreach (Room room in ListRoom.Where(r => r.Roomid == roomid && r.RoomMaster != null)) - { - return room.RoomMaster; - } - return General.UnknownUserInstance; - } - public void SetRoomMaster(string roomid, User user) { if (roomid != "-1" && user.Id != 0) diff --git a/Service/HTTPManager.cs b/Service/HTTPManager.cs index 7e692a6..02bb428 100644 --- a/Service/HTTPManager.cs +++ b/Service/HTTPManager.cs @@ -10,17 +10,34 @@ namespace Milimoe.FunGame.Core.Service { internal class HTTPManager { - internal static HttpListener? ServerSocket => _ServerSocket; - private static HttpListener? _ServerSocket = null; + /// + /// 实际的 监听实例 [ 单例 ] + /// + internal static HttpListener? HttpListener => _HttpListener; + private static HttpListener? _HttpListener = null; - internal static HttpListener StartListening(int port = 22227, bool ssl = false) + /// + /// 开始监听 + /// 当 = "*" 时,需要管理员权限 + /// + /// + /// + /// + /// + /// + internal static HttpListener StartListening(string address = "*", int port = 22223, string subUrl = "ws", bool ssl = false) { - HttpListener listener = new(); - listener.Prefixes.Add((ssl ? "https://" : "http://") + "localhost:" + port + "/ws"); - listener.Start(); - return listener; + _HttpListener = new(); + _HttpListener.Prefixes.Add((ssl ? "https://" : "http://") + address + ":" + port + "/" + subUrl.Trim('/') + "/"); + _HttpListener.Start(); + return _HttpListener; } + /// + /// 客户端连接远程 WebSocket 服务器 + /// + /// + /// internal static async Task Connect(Uri uri) { System.Net.WebSockets.ClientWebSocket socket = new(); @@ -32,6 +49,12 @@ namespace Milimoe.FunGame.Core.Service return null; } + /// + /// 客户端向服务器发送消息 + /// + /// + /// + /// internal static async Task Send(System.Net.WebSockets.ClientWebSocket socket, SocketObject obj) { if (socket != null) @@ -50,6 +73,12 @@ namespace Milimoe.FunGame.Core.Service return SocketResult.NotSent; } + /// + /// 服务器向客户端发送消息 + /// + /// + /// + /// internal static async Task Send(WebSocket socket, SocketObject obj) { if (socket != null) @@ -68,45 +97,95 @@ namespace Milimoe.FunGame.Core.Service return SocketResult.NotSent; } - internal static async Task Receiving(HTTPListener listener) + /// + /// 服务器接受一个 HTTP 的 WebSocket 升级请求 + /// + /// [0]客户端IP地址;[1]客户端的WebSocket实例 + internal static async Task Accept() { - if (ServerSocket != null) + if (HttpListener is null) return []; + try { - try + HttpListenerContext context = await HttpListener.GetContextAsync(); + if (context.Request.IsWebSocketRequest) { - while (true) + HttpListenerWebSocketContext socketContext = await context.AcceptWebSocketAsync(null); + WebSocket socket = socketContext.WebSocket; + string ip = context.Request.RemoteEndPoint.ToString(); + return [ip, socket]; + } + else + { + context.Response.StatusCode = 400; + context.Response.Close(); + } + } + catch + { + HttpListener?.Close(); + } + return []; + } + + /// + /// 服务器接收客户端消息 + /// + /// + /// + internal static async Task Receive(WebSocket socket) + { + try + { + List objs = []; + if (socket != null) + { + byte[] buffer = new byte[General.SocketByteSize]; + WebSocketReceiveResult result; + StringBuilder builder = new(); + + do { - HttpListenerContext context = await ServerSocket.GetContextAsync(); - if (context.Request.IsWebSocketRequest) + result = await socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); + builder.Append(General.DefaultEncoding.GetString(buffer, 0, result.Count).Replace("\0", "").Trim()); + } + while (!result.EndOfMessage); + + string msg = builder.ToString(); + + if (JsonManager.IsCompleteJson(msg)) + { + foreach (SocketObject obj in JsonManager.GetObjects(msg)) { - TaskUtility.NewTask(async () => await AddClientWebSocket(listener, context)); - } - else - { - context.Response.StatusCode = 400; - context.Response.Close(); + objs.Add(obj); } } } - catch - { - _ServerSocket = null; - } + return [.. objs]; + } + catch (Exception e) + { + TXTHelper.AppendErrorLog(e.GetErrorInfo()); + return []; } } + /// + /// 客户端接收服务器消息 + /// + /// + /// 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(); + string msg = General.DefaultEncoding.GetString(buffer).Replace("\0", "").Trim(); SocketObject[] objs = await GetSocketObjects(client.Instance, result, msg); foreach (SocketObject obj in objs) { - SocketObject sendobject = client.SocketObject_Handler(obj); + SocketManager.OnSocketReceive(obj); if (obj.SocketType == SocketMessageType.Connect) { return true; @@ -116,81 +195,18 @@ namespace Milimoe.FunGame.Core.Service 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 (socket.State == WebSocketState.Open) - { - try - { - buffer = new byte[General.SocketByteSize]; - result = await socket.ReceiveAsync(new ArraySegment(buffer), CancellationToken.None); - - if (result.CloseStatus.HasValue) - { - await socket.CloseAsync(result.CloseStatus.Value, result.CloseStatusDescription, CancellationToken.None); - return; - } - - 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(WebSocketCloseStatus.NormalClosure, "Disconnect received", CancellationToken.None); - return; - } - await Send(socket, sendobject); - } - } - catch (Exception e) - { - // 处理其他异常 - TXTHelper.AppendErrorLog(e.GetErrorInfo()); - await socket.CloseAsync(WebSocketCloseStatus.InternalServerError, "Server Error", CancellationToken.None); - return; - } - } - } - } - + /// + /// 将收到的消息反序列为 + /// + /// + /// + /// + /// private static async Task GetSocketObjects(WebSocket socket, WebSocketReceiveResult result, string msg) { List objs = []; diff --git a/Service/MailManager.cs b/Service/MailManager.cs index 370b73d..a1824d9 100644 --- a/Service/MailManager.cs +++ b/Service/MailManager.cs @@ -67,10 +67,10 @@ namespace Milimoe.FunGame.Core.Service MailMessage Msg = new() { Subject = Mail.Subject, - SubjectEncoding = System.Text.Encoding.UTF8, + SubjectEncoding = General.DefaultEncoding, Body = Mail.Body, - BodyEncoding = System.Text.Encoding.UTF8, - From = new MailAddress(Mail.Sender, Mail.SenderName, System.Text.Encoding.UTF8), + BodyEncoding = General.DefaultEncoding, + From = new MailAddress(Mail.Sender, Mail.SenderName, General.DefaultEncoding), IsBodyHtml = Mail.HTML, Priority = Mail.Priority }; diff --git a/Service/SocketManager.cs b/Service/SocketManager.cs index 4bee11d..9382cd0 100644 --- a/Service/SocketManager.cs +++ b/Service/SocketManager.cs @@ -30,18 +30,18 @@ namespace Milimoe.FunGame.Core.Service /// /// 创建服务器监听Socket /// - /// 监听端口号 - /// 最大连接数量 + /// 监听端口号 + /// 最大连接数量 /// 服务器端专用Socket - internal static Socket? StartListening(int Port = 22222, int MaxConnection = 0) + internal static Socket? StartListening(int port = 22222, int maxConnection = 0) { - if (MaxConnection <= 0) MaxConnection = SocketSet.MaxConnection_2C2G; + if (maxConnection <= 0) maxConnection = SocketSet.MaxConnection_2C2G; try { _ServerSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); - IPEndPoint ServerEndPoint = new(IPAddress.Any, Port); + IPEndPoint ServerEndPoint = new(IPAddress.Any, port); _ServerSocket.Bind(ServerEndPoint); - _ServerSocket.Listen(MaxConnection); + _ServerSocket.Listen(maxConnection); _ServerSocket.NoDelay = true; return _ServerSocket; } @@ -65,8 +65,7 @@ namespace Milimoe.FunGame.Core.Service { Client = ServerSocket.Accept(); Client.NoDelay = true; - IPEndPoint? ClientIPEndPoint = (IPEndPoint?)Client.RemoteEndPoint; - ClientIP = (ClientIPEndPoint != null) ? ClientIPEndPoint.ToString() : "Unknown"; + ClientIP = Client.RemoteEndPoint?.ToString() ?? "Unknown"; return [ClientIP, Client]; } catch @@ -79,17 +78,17 @@ namespace Milimoe.FunGame.Core.Service /// /// 创建客户端Socket /// - /// 服务器IP地址 - /// 服务器监听端口 + /// 服务器IP地址 + /// 服务器监听端口 /// 客户端专用Socket - internal static Socket? Connect(string Address, int Port = 22222) + internal static Socket? Connect(string address, int port = 22222) { Socket? ClientSocket; EndPoint ServerEndPoint; try { - string IP = Api.Utility.NetworkUtility.GetIPAddress(Address); - ServerEndPoint = new IPEndPoint(IPAddress.Parse(IP), Port); + string IP = NetworkUtility.GetIPAddress(address); + ServerEndPoint = new IPEndPoint(IPAddress.Parse(IP), port); if (ServerEndPoint != null) { ClientSocket = new(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); @@ -131,14 +130,14 @@ namespace Milimoe.FunGame.Core.Service /// /// 用于服务器端向客户端Socket发送信息 /// - /// 客户端Socket - /// Socket信息容器 + /// 客户端Socket + /// Socket信息容器 /// 通信结果 - internal static SocketResult Send(Socket ClientSocket, Library.Common.Network.SocketObject SocketObject) + internal static SocketResult Send(Socket clientSocket, Library.Common.Network.SocketObject obj) { - if (ClientSocket != null) + if (clientSocket != null) { - if (ClientSocket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) + if (clientSocket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0) { return SocketResult.Success; } @@ -150,14 +149,14 @@ namespace Milimoe.FunGame.Core.Service /// /// 用于服务器端向客户端Socket发送信息 [ 异步版 ] /// - /// 客户端Socket - /// Socket信息容器 + /// 客户端Socket + /// Socket信息容器 /// 通信结果 - internal static async Task SendAsync(Socket ClientSocket, Library.Common.Network.SocketObject SocketObject) + internal static async Task SendAsync(Socket clientSocket, Library.Common.Network.SocketObject obj) { - if (ClientSocket != null) + if (clientSocket != null) { - if (await ClientSocket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) + if (await clientSocket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0) { return SocketResult.Success; } @@ -169,13 +168,13 @@ namespace Milimoe.FunGame.Core.Service /// /// 用于客户端向服务器Socket发送信息 /// - /// Socket信息容器 + /// Socket信息容器 /// 通信结果 - internal static SocketResult Send(Library.Common.Network.SocketObject SocketObject) + internal static SocketResult Send(Library.Common.Network.SocketObject obj) { if (Socket != null) { - if (Socket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) + if (Socket.Send(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0) { return SocketResult.Success; } @@ -187,13 +186,13 @@ namespace Milimoe.FunGame.Core.Service /// /// 用于客户端向服务器Socket发送信息 [ 异步版 ] /// - /// Socket信息容器 + /// Socket信息容器 /// 通信结果 - internal static async Task SendAsync(Library.Common.Network.SocketObject SocketObject) + internal static async Task SendAsync(Library.Common.Network.SocketObject obj) { if (Socket != null) { - if (await Socket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(SocketObject))) > 0) + if (await Socket.SendAsync(General.DefaultEncoding.GetBytes(JsonManager.GetString(obj))) > 0) { return SocketResult.Success; } @@ -204,16 +203,16 @@ namespace Milimoe.FunGame.Core.Service /// /// 接收数据流中的信息 - /// 如果是服务器接收信息需要传入客户端Socket + /// 如果是服务器接收信息需要传入客户端Socket /// - /// 如果是服务器接收信息需要传入客户端Socket + /// 如果是服务器接收信息需要传入客户端Socket /// SocketObjects - internal static Library.Common.Network.SocketObject[] Receive(Socket? ClientSocket = null) + internal static Library.Common.Network.SocketObject[] Receive(Socket? clientSocket = null) { try { List result = []; - Socket? tempSocket = ClientSocket is null ? Socket : ClientSocket; + Socket? tempSocket = clientSocket is null ? Socket : clientSocket; if (tempSocket != null) { // 从服务器接收消息 @@ -229,7 +228,7 @@ namespace Milimoe.FunGame.Core.Service { result.Add(obj); // 客户端接收消息,广播ScoketObject到每个UIModel - if (ClientSocket is null) OnSocketReceive(obj); + if (clientSocket is null) OnSocketReceive(obj); } return [.. result]; } @@ -256,7 +255,73 @@ namespace Milimoe.FunGame.Core.Service { result.Add(obj); // 客户端接收消息,广播ScoketObject到每个UIModel - if (ClientSocket is null) OnSocketReceive(obj); + if (clientSocket is null) OnSocketReceive(obj); + } + } + return [.. result]; + } + catch (Exception e) + { + TXTHelper.AppendErrorLog(e.GetErrorInfo()); + return []; + } + } + + /// + /// 接收数据流中的信息 [ 异步版 ] + /// 如果是服务器接收信息需要传入客户端Socket + /// + /// 如果是服务器接收信息需要传入客户端Socket + /// SocketObjects + internal static async Task ReceiveAsync(Socket? clientSocket = null) + { + try + { + List result = []; + Socket? tempSocket = clientSocket is null ? Socket : clientSocket; + if (tempSocket != null) + { + // 从服务器接收消息 + byte[] buffer = new byte[General.SocketByteSize]; + int length = await tempSocket.ReceiveAsync(new ArraySegment(buffer), SocketFlags.None); + string msg = ""; + if (length > 0) + { + msg = General.DefaultEncoding.GetString(buffer, 0, length); + if (JsonManager.IsCompleteJson(msg)) + { + foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects(msg)) + { + result.Add(obj); + // 客户端接收消息,广播ScoketObject到每个UIModel + if (clientSocket is null) OnSocketReceive(obj); + } + return [.. result]; + } + else + { + Thread.Sleep(20); + while (true) + { + if (tempSocket.Available > 0) + { + length = tempSocket.Receive(buffer, buffer.Length, SocketFlags.None); + msg += General.DefaultEncoding.GetString(buffer, 0, length); + if (JsonManager.IsCompleteJson(msg)) + { + break; + } + Thread.Sleep(20); + } + else break; + } + } + } + foreach (Library.Common.Network.SocketObject obj in JsonManager.GetObjects(msg)) + { + result.Add(obj); + // 客户端接收消息,广播ScoketObject到每个UIModel + if (clientSocket is null) OnSocketReceive(obj); } } return [.. result]; @@ -275,8 +340,8 @@ namespace Milimoe.FunGame.Core.Service /// /// 监听事件的委托 /// - /// SocketObject - internal delegate void SocketReceiveHandler(Library.Common.Network.SocketObject SocketObject); + /// SocketObject + internal delegate void SocketReceiveHandler(Library.Common.Network.SocketObject obj); /// /// 监听事件 @@ -286,10 +351,10 @@ namespace Milimoe.FunGame.Core.Service /// /// 触发异步监听事件 /// - /// SocketObject - internal static void OnSocketReceive(Library.Common.Network.SocketObject SocketObject) + /// SocketObject + internal static void OnSocketReceive(Library.Common.Network.SocketObject obj) { - SocketReceive?.Invoke(SocketObject); + SocketReceive?.Invoke(obj); } #endregion diff --git a/Service/TaskManager.cs b/Service/TaskManager.cs index 3bc3096..96d7572 100644 --- a/Service/TaskManager.cs +++ b/Service/TaskManager.cs @@ -34,9 +34,9 @@ namespace Milimoe.FunGame.Core.Service private bool _IsCompleted = false; private Exception _Exception = new(); - internal TaskAwaiter(Action action) => _ = Worker(action); + internal TaskAwaiter(Action action) => Worker(action); - internal TaskAwaiter(Func function) => _ = Worker(function); + internal TaskAwaiter(Func function) => Worker(function); /// /// 返回ITaskAwaiter可以进一步调用方法 @@ -63,34 +63,40 @@ namespace Milimoe.FunGame.Core.Service return this; } - private async Task Worker(Action action) + private void Worker(Action action) { - try + Task.Run(async () => { - await Task.Run(action); - _IsCompleted = true; - Completed?.Invoke(); - } - catch (Exception e) - { - _Exception = e; - Error?.Invoke(e); - } + try + { + await Task.Run(action); + _IsCompleted = true; + Completed?.Invoke(); + } + catch (Exception e) + { + _Exception = e; + Error?.Invoke(e); + } + }); } - private async Task Worker(Func function) + private void Worker(Func function) { - try + Task.Run(async () => { - await function(); - _IsCompleted = true; - Completed?.Invoke(); - } - catch (Exception e) - { - _Exception = e; - Error?.Invoke(e); - } + try + { + await function(); + _IsCompleted = true; + Completed?.Invoke(); + } + catch (Exception e) + { + _Exception = e; + Error?.Invoke(e); + } + }); } } }