From 14ff58f4f4a0c0a2a41be993ec0301ea3001ff26 Mon Sep 17 00:00:00 2001 From: milimoe <110188673+milimoe@users.noreply.github.com> Date: Fri, 4 Oct 2024 12:39:15 +0800 Subject: [PATCH] =?UTF-8?q?=E4=B8=BA=E6=9C=8D=E5=8A=A1=E5=99=A8=E7=BB=9F?= =?UTF-8?q?=E4=B8=80=E6=95=B0=E6=8D=AE=E8=AE=BF=E9=97=AE=E8=BF=9E=E6=8E=A5?= =?UTF-8?q?=20(#37)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 添加 Web API 和 RESTful API 模式; * 添加 SQLite 模式; * 添加 ISocketMessageProcessor 和 ISocketListener<> 接口,用于统一数据访问; * 重做了 ISocketModel; * 完善了 WebSocket 的连接模式。 --- FunGame.Server/Controllers/Authenticator.cs | 6 +- .../Controllers/ConnectController.cs | 134 +++ .../Controllers/DataRequestController.cs | 955 +++++++++++------- FunGame.Server/FunGame.Server.csproj | 23 +- FunGame.Server/Main.cs | 335 +++--- FunGame.Server/Models/ConsoleModel.cs | 28 +- FunGame.Server/Models/ServerModel.cs | 656 +++++------- FunGame.Server/Others/Config.cs | 100 +- FunGame.Server/Utilities/ConnectProperties.cs | 52 + FunGame.Server/Utilities/General.cs | 30 +- .../Utilities/{ => MySQL}/MySQLConnection.cs | 47 +- .../Utilities/{ => MySQL}/MySQLHelper.cs | 7 +- .../Utilities/{ => MySQL}/MySQLManager.cs | 0 .../Utilities/SQLite/SQLiteHelper.cs | 229 +++++ FunGame.Server/app.manifest | 79 ++ FunGame.WebAPI/Architecture/RESTfulAPI.cs | 49 + .../Architecture/RESTfulAPIListener.cs | 21 + FunGame.WebAPI/Architecture/WebAPIListener.cs | 22 + .../Controllers/PostDataController.cs | 58 ++ FunGame.WebAPI/Controllers/UserController.cs | 74 ++ FunGame.WebAPI/FunGame.WebAPI.csproj | 48 + FunGame.WebAPI/FunGame.WebAPI.http | 6 + FunGame.WebAPI/Images/logo.ico | Bin 0 -> 16958 bytes FunGame.WebAPI/Models/LoginModel.cs | 8 + FunGame.WebAPI/Models/RESTfulAPIModel.cs | 72 ++ FunGame.WebAPI/Program.cs | 267 +++++ FunGame.WebAPI/Properties/launchSettings.json | 41 + FunGame.WebAPI/Services/JWTService.cs | 33 + FunGame.WebAPI/appsettings.Development.json | 8 + FunGame.WebAPI/appsettings.json | 19 + FunGameServer.sln | 6 + 31 files changed, 2408 insertions(+), 1005 deletions(-) create mode 100644 FunGame.Server/Controllers/ConnectController.cs create mode 100644 FunGame.Server/Utilities/ConnectProperties.cs rename FunGame.Server/Utilities/{ => MySQL}/MySQLConnection.cs (53%) rename FunGame.Server/Utilities/{ => MySQL}/MySQLHelper.cs (96%) rename FunGame.Server/Utilities/{ => MySQL}/MySQLManager.cs (100%) create mode 100644 FunGame.Server/Utilities/SQLite/SQLiteHelper.cs create mode 100644 FunGame.Server/app.manifest create mode 100644 FunGame.WebAPI/Architecture/RESTfulAPI.cs create mode 100644 FunGame.WebAPI/Architecture/RESTfulAPIListener.cs create mode 100644 FunGame.WebAPI/Architecture/WebAPIListener.cs create mode 100644 FunGame.WebAPI/Controllers/PostDataController.cs create mode 100644 FunGame.WebAPI/Controllers/UserController.cs create mode 100644 FunGame.WebAPI/FunGame.WebAPI.csproj create mode 100644 FunGame.WebAPI/FunGame.WebAPI.http create mode 100644 FunGame.WebAPI/Images/logo.ico create mode 100644 FunGame.WebAPI/Models/LoginModel.cs create mode 100644 FunGame.WebAPI/Models/RESTfulAPIModel.cs create mode 100644 FunGame.WebAPI/Program.cs create mode 100644 FunGame.WebAPI/Properties/launchSettings.json create mode 100644 FunGame.WebAPI/Services/JWTService.cs create mode 100644 FunGame.WebAPI/appsettings.Development.json create mode 100644 FunGame.WebAPI/appsettings.json diff --git a/FunGame.Server/Controllers/Authenticator.cs b/FunGame.Server/Controllers/Authenticator.cs index 0799c0c..1423d01 100644 --- a/FunGame.Server/Controllers/Authenticator.cs +++ b/FunGame.Server/Controllers/Authenticator.cs @@ -1,7 +1,7 @@ using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; -using Milimoe.FunGame.Server.Model; namespace Milimoe.FunGame.Server.Controller { @@ -9,11 +9,11 @@ namespace Milimoe.FunGame.Server.Controller { public TwoFactorAuthenticator Login2FA = new(); - private readonly ServerModel Server; + private readonly IServerModel Server; private readonly SQLHelper SQLHelper; private readonly MailSender? MailSender; - public Authenticator(ServerModel Server, SQLHelper SQLHelper, MailSender? MailSender) : base(SQLHelper) + public Authenticator(IServerModel Server, SQLHelper SQLHelper, MailSender? MailSender) : base(SQLHelper) { this.Server = Server; this.SQLHelper = SQLHelper; diff --git a/FunGame.Server/Controllers/ConnectController.cs b/FunGame.Server/Controllers/ConnectController.cs new file mode 100644 index 0000000..4e3de9c --- /dev/null +++ b/FunGame.Server/Controllers/ConnectController.cs @@ -0,0 +1,134 @@ +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Server.Others; +using Milimoe.FunGame.Server.Utility; + +namespace Milimoe.FunGame.Server.Controller +{ + public class ConnectController + { + /// + /// 因为异步函数无法使用 ref 变量,因此使用元组返回 + /// + /// + /// + /// + /// + /// + /// + /// [0]isConnected;[1]isDebugMode + public static async Task<(bool, bool)> Connect(ISocketListener listener, ISocketMessageProcessor socket, Guid token, string clientip, IEnumerable objs) where T : ISocketMessageProcessor + { + try + { + bool isConnected = false; + bool isDebugMode = false; + foreach (SocketObject obj in objs) + { + if (obj.SocketType == SocketMessageType.Connect) + { + if (Config.ConnectingPlayerCount + Config.OnlinePlayerCount > Config.MaxPlayers) + { + await SendRefuseConnect(socket, "服务器可接受的连接数量已上限!"); + ServerHelper.WriteLine("服务器可接受的连接数量已上限!", InvokeMessageType.Core); + return (isConnected, isDebugMode); + } + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 正在连接服务器 . . .", InvokeMessageType.Core); + if (IsIPBanned(listener, clientip)) + { + await SendRefuseConnect(socket, "服务器已拒绝黑名单用户连接。"); + ServerHelper.WriteLine("检测到 " + ServerHelper.MakeClientName(clientip) + " 为黑名单用户,已禁止其连接!", InvokeMessageType.Core); + return (isConnected, isDebugMode); + } + + ServerHelper.WriteLine("[" + SocketSet.GetTypeString(obj.SocketType) + "] " + ServerHelper.MakeClientName(socket.ClientIP), InvokeMessageType.Core); + + // 读取参数 + // 参数1:客户端的游戏模组列表,没有服务器的需要拒绝 + string[] modes = obj.GetParam(0) ?? []; + // 参数2:客户端是否开启了开发者模式,开启开发者模式部分功能不可用 + isDebugMode = obj.GetParam(1); + if (isDebugMode) ServerHelper.WriteLine("客户端已开启开发者模式"); + + string msg = ""; + List ClientDontHave = []; + string strDontHave = string.Join("\r\n", Config.GameModuleSupported.Where(mode => !modes.Contains(mode))); + if (strDontHave != "") + { + strDontHave = "客户端缺少服务器所需的模组:" + strDontHave; + ServerHelper.WriteLine(strDontHave, InvokeMessageType.Core); + msg += strDontHave; + } + + if (msg == "" && await socket.SendAsync(SocketMessageType.Connect, true, msg, token, Config.ServerName, Config.ServerNotice) == SocketResult.Success) + { + isConnected = true; + ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已确认连接", InvokeMessageType.Core); + return (isConnected, isDebugMode); + } + else if (msg != "" && await socket.SendAsync(SocketMessageType.Connect, false, msg) == SocketResult.Success) + { + ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "拒绝连接", InvokeMessageType.Core); + return (isConnected, isDebugMode); + } + else + { + ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core); + return (isConnected, isDebugMode); + } + } + } + + await SendRefuseConnect(socket, "服务器已拒绝连接。"); + ServerHelper.WriteLine("客户端发送了不符合FunGame规定的字符,拒绝连接。", InvokeMessageType.Core); + return (isConnected, isDebugMode); + } + catch (Exception e) + { + ServerHelper.Error(e); + await SendRefuseConnect(socket, "请勿发送错误的数据,请保持FunGame版本为最新。"); + throw new SocketWrongInfoException(); + } + } + + /// + /// 回复拒绝连接消息 + /// + /// + /// + /// + private static async Task SendRefuseConnect(ISocketMessageProcessor socket, string msg) + { + // 发送消息给客户端 + msg = "连接被拒绝,如有疑问请联系服务器管理员:" + msg; + if (await socket.SendAsync(SocketMessageType.Connect, false, msg) == SocketResult.Success) + { + ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已拒绝连接", InvokeMessageType.Core); + return true; + } + else + { + ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core); + return false; + } + } + + /// + /// 判断是否是黑名单里的IP + /// + /// + /// + /// + /// + private static bool IsIPBanned(ISocketListener server, string ip) where T : ISocketMessageProcessor + { + string[] strs = ip.Split(":"); + if (strs.Length == 2 && server.BannedList.Contains(strs[0])) + { + return true; + } + return false; + } + } +} diff --git a/FunGame.Server/Controllers/DataRequestController.cs b/FunGame.Server/Controllers/DataRequestController.cs index 6a694e0..5fef652 100644 --- a/FunGame.Server/Controllers/DataRequestController.cs +++ b/FunGame.Server/Controllers/DataRequestController.cs @@ -1,8 +1,8 @@ -using System.Collections; -using System.Data; +using System.Data; using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; +using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Entity; @@ -12,29 +12,47 @@ using Milimoe.FunGame.Server.Utility; namespace Milimoe.FunGame.Server.Controller { - public class DataRequestController + /// + /// 继承自 + /// + /// + public class DataRequestController where T : ISocketMessageProcessor { - public ServerModel Server { get; } - public MySQLHelper SQLHelper => Server.SQLHelper ?? throw new MySQLConfigException(); + public ServerModel Server { get; } + public SQLHelper? SQLHelper => Server.SQLHelper; public MailSender? MailSender => Server.MailSender; - public Authenticator Authenticator { get; } - public DataRequestType LastRequest => _LastRequest; + public Authenticator? Authenticator { get; } + public DataRequestType LastRequest => _lastRequest; - private string ForgetVerify = ""; - private string RegVerify = ""; - private DataRequestType _LastRequest = DataRequestType.UnKnown; - private readonly bool[] isReadyCheckCD = [false, false]; + private string _forgetVerify = ""; + private string _regVerify = ""; + private DataRequestType _lastRequest = DataRequestType.UnKnown; + private readonly bool[] _isReadyCheckCD = [false, false]; + protected DataSet _dsUser = new(); + protected string _username = ""; + protected Guid _checkLoginKey = Guid.Empty; + protected bool _isMatching; - public DataRequestController(ServerModel server) + /// + /// 数据请求控制器 + /// + /// + public DataRequestController(ServerModel server) { Server = server; - Authenticator = new(Server, SQLHelper, MailSender); + if (SQLHelper != null) Authenticator = new(Server, SQLHelper, MailSender); } - public Hashtable GetResultData(DataRequestType type, Hashtable data) + /// + /// 处理客户端的数据请求 + /// + /// + /// + /// + public async Task> GetResultData(DataRequestType type, Dictionary data) { - Hashtable result = []; - _LastRequest = type; + Dictionary result = []; + _lastRequest = type; switch (type) { @@ -58,11 +76,11 @@ namespace Milimoe.FunGame.Server.Controller break; case DataRequestType.Main_IntoRoom: - IntoRoom(data, result); + await IntoRoom(data, result); break; case DataRequestType.Main_QuitRoom: - QuitRoom(data, result); + await QuitRoom(data, result); break; case DataRequestType.Main_MatchRoom: @@ -70,7 +88,7 @@ namespace Milimoe.FunGame.Server.Controller break; case DataRequestType.Main_Chat: - Chat(data); + await Chat(data); break; case DataRequestType.Main_Ready: @@ -85,12 +103,12 @@ namespace Milimoe.FunGame.Server.Controller StartGame(data, result); break; - case DataRequestType.Reg_GetRegVerifyCode: + case DataRequestType.Reg_Reg: Reg(data, result); break; case DataRequestType.Login_Login: - Login(data, result); + await Login(data, result); break; case DataRequestType.Login_GetFindPasswordVerifyCode: @@ -123,24 +141,27 @@ namespace Milimoe.FunGame.Server.Controller /// /// 退出登录 /// - /// - /// - private void LogOut(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void LogOut(Dictionary requestData, Dictionary resultData) { string msg = ""; Guid key = Guid.Empty; - if (RequestData.Count >= 1) + if (requestData.Count >= 1) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - key = DataRequest.GetHashtableJsonObject(RequestData, "key"); - if (Server.IsLoginKey(key)) + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + key = DataRequest.GetDictionaryJsonObject(requestData, "key"); + if (IsLoginKey(key)) { - Server.LogOut(); + // 从玩家列表移除 + Server.RemoveUser(); + Server.GetUsersCount(); + _checkLoginKey = Guid.Empty; msg = "你已成功退出登录! "; } } - ResultData.Add("msg", msg); - ResultData.Add("key", key); + resultData.Add("msg", msg); + resultData.Add("key", key); } #endregion @@ -150,242 +171,264 @@ namespace Milimoe.FunGame.Server.Controller /// /// 获取公告 /// - /// - private void GetServerNotice(Hashtable ResultData) + /// + private void GetServerNotice(Dictionary resultData) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - ResultData.Add("notice", Config.ServerNotice); + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + resultData.Add("notice", Config.ServerNotice); } /// /// 创建房间 /// - /// - /// - private void CreateRoom(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void CreateRoom(Dictionary requestData, Dictionary resultData) { Room room = General.HallInstance; - if (RequestData.Count >= 3) + if (requestData.Count >= 3) { - RoomType type = DataRequest.GetHashtableJsonObject(RequestData, "roomtype"); - string GameModule = DataRequest.GetHashtableJsonObject(RequestData, "GameModule") ?? ""; - string gamemap = DataRequest.GetHashtableJsonObject(RequestData, "gamemap") ?? ""; - bool isrank = DataRequest.GetHashtableJsonObject(RequestData, "isrank"); - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest) + " : " + RoomSet.GetTypeString(type) + " (" + string.Join(", ", [GameModule, gamemap]) + ")", InvokeMessageType.DataRequest); - if (GameModule == "" || gamemap == "") + RoomType type = DataRequest.GetDictionaryJsonObject(requestData, "roomtype"); + string gamemodule = DataRequest.GetDictionaryJsonObject(requestData, "gamemoduleserver") ?? ""; + string gamemap = DataRequest.GetDictionaryJsonObject(requestData, "gamemap") ?? ""; + bool isrank = DataRequest.GetDictionaryJsonObject(requestData, "isrank"); + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest) + " : " + RoomSet.GetTypeString(type) + " (" + string.Join(", ", [gamemodule, gamemap]) + ")", InvokeMessageType.DataRequest); + if (gamemodule == "" || gamemap == "" || Config.GameModuleLoader is null || !Config.GameModuleLoader.ModuleServers.ContainsKey(gamemodule) || !Config.GameModuleLoader.Maps.ContainsKey(gamemap)) { ServerHelper.WriteLine("缺少对应的模组或地图,无法创建房间。"); - ResultData.Add("room", room); + resultData.Add("room", room); return; } - User user = DataRequest.GetHashtableJsonObject(RequestData, "master") ?? Factory.GetUser(); - string password = DataRequest.GetHashtableJsonObject(RequestData, "password") ?? ""; + User user = DataRequest.GetDictionaryJsonObject(requestData, "master") ?? Factory.GetUser(); + string password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; + int maxusers = DataRequest.GetDictionaryJsonObject(requestData, "maxusers"); if (user.Id != 0) { - string roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7).ToUpper(); - SQLHelper.Execute(RoomQuery.Insert_CreateRoom(roomid, user.Id, type, GameModule, gamemap, isrank, password ?? "")); - if (SQLHelper.Result == SQLResult.Success) + string roomid; + while (true) { - ServerHelper.WriteLine("[CreateRoom] Master: " + user.Username + " RoomID: " + roomid); - SQLHelper.ExecuteDataSet(RoomQuery.Select_IsExistRoom(roomid)); - if (SQLHelper.Result == SQLResult.Success && SQLHelper.DataSet.Tables[0].Rows.Count > 0) + // 防止重复 + roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7).ToUpper(); + if (Config.RoomList.GetRoom(roomid).Roomid == "-1") { - room = Factory.GetRoom(SQLHelper.DataSet.Tables[0].Rows[0], user); - Config.RoomList.AddRoom(room); + break; + } + } + if (roomid != "-1" && SQLHelper != null) + { + SQLHelper.Execute(RoomQuery.Insert_CreateRoom(roomid, user.Id, type, gamemodule, gamemap, isrank, password, maxusers)); + if (SQLHelper.Result == SQLResult.Success) + { + ServerHelper.WriteLine("[CreateRoom] Master: " + user.Username + " RoomID: " + roomid); + SQLHelper.ExecuteDataSet(RoomQuery.Select_IsExistRoom(roomid)); + if (SQLHelper.Result == SQLResult.Success && SQLHelper.DataSet.Tables[0].Rows.Count > 0) + { + room = Factory.GetRoom(SQLHelper.DataSet.Tables[0].Rows[0], user); + Config.RoomList.AddRoom(room); + } } } } } - ResultData.Add("room", room); + resultData.Add("room", room); } /// /// 更新房间列表 /// - /// - private void UpdateRoom(Hashtable ResultData) + /// + private void UpdateRoom(Dictionary resultData) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - ResultData.Add("rooms", Config.RoomList.ListRoom); // 传RoomList + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + resultData.Add("rooms", Config.RoomList.ListRoom); // 传RoomList } /// /// 退出房间,并更新房主 /// - /// - /// - private void QuitRoom(Hashtable RequestData, Hashtable ResultData) + /// + /// + private async Task QuitRoom(Dictionary requestData, Dictionary resultData) { bool result = false; - if (RequestData.Count >= 2) + if (requestData.Count >= 2) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - string roomid = DataRequest.GetHashtableJsonObject(RequestData, "roomid") ?? "-1"; - bool isMaster = DataRequest.GetHashtableJsonObject(RequestData, "isMaster"); + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + string roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; + bool isMaster = DataRequest.GetDictionaryJsonObject(requestData, "isMaster"); if (roomid != "-1" && Config.RoomList.IsExist(roomid)) { - result = Server.QuitRoom(roomid, isMaster); + result = await Server.QuitRoom(roomid, isMaster); } } - ResultData.Add("result", result); + resultData.Add("result", result); } /// /// 进入房间 /// - /// - /// - private void IntoRoom(Hashtable RequestData, Hashtable ResultData) + /// + /// + private async Task IntoRoom(Dictionary requestData, Dictionary resultData) { bool result = false; - if (RequestData.Count >= 1) + if (requestData.Count >= 1) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - string roomid = DataRequest.GetHashtableJsonObject(RequestData, "roomid") ?? "-1"; + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + string roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; if (roomid != "-1") { - SQLHelper.ExecuteDataSet(RoomQuery.Select_IsExistRoom(roomid)); - if (SQLHelper.Success) + if (SQLHelper != null) { - Config.RoomList.IntoRoom(roomid, Server.User); - Server.IntoRoom(roomid); - result = true; - } - else - { - Config.RoomList.RemoveRoom(roomid); + SQLHelper.ExecuteDataSet(RoomQuery.Select_IsExistRoom(roomid)); + if (SQLHelper.Success) + { + Config.RoomList.IntoRoom(roomid, Server.User); + Server.InRoom = Config.RoomList[roomid]; + await Server.SendClients(Server.Listener.ClientList.Where(c => c != null && roomid == c.InRoom.Roomid && c.User.Id != 0), + SocketMessageType.Chat, Server.User.Username, DateTimeUtility.GetNowShortTime() + " [ " + Server.User.Username + " ] 进入了房间。"); + result = true; + } + else + { + Config.RoomList.RemoveRoom(roomid); + } } } } - ResultData.Add("result", result); + resultData.Add("result", result); } /// /// 匹配房间 /// - /// - /// - private void MatchRoom(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void MatchRoom(Dictionary requestData, Dictionary resultData) { bool result = true; - if (RequestData.Count >= 1) + if (requestData.Count >= 1) { - bool iscancel = DataRequest.GetHashtableJsonObject(RequestData, "iscancel"); + bool iscancel = DataRequest.GetDictionaryJsonObject(requestData, "iscancel"); if (!iscancel) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest) + " : Start", InvokeMessageType.DataRequest); - RoomType type = DataRequest.GetHashtableJsonObject(RequestData, "roomtype"); - User user = DataRequest.GetHashtableJsonObject(RequestData, "matcher") ?? Factory.GetUser(); - Server.StartMatching(type, user); + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest) + " : Start", InvokeMessageType.DataRequest); + RoomType type = DataRequest.GetDictionaryJsonObject(requestData, "roomtype"); + User user = DataRequest.GetDictionaryJsonObject(requestData, "matcher") ?? Factory.GetUser(); + StartMatching(type, user); } else { // 取消匹配 - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest) + " : Cancel", InvokeMessageType.DataRequest); - Server.StopMatching(); + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest) + " : Cancel", InvokeMessageType.DataRequest); + StopMatching(); } } - ResultData.Add("result", result); + resultData.Add("result", result); } /// /// 设置已准备状态 /// - /// - /// - private void SetReady(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void SetReady(Dictionary requestData, Dictionary resultData) { bool result = false; string roomid = "-1"; - if (RequestData.Count >= 1) + if (requestData.Count >= 1) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - roomid = DataRequest.GetHashtableJsonObject(RequestData, "roomid") ?? "-1"; + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; User user = Server.User; - if (roomid != "-1" && user.Id != 0 && !Config.RoomList.GetReadyPlayerList(roomid).Contains(user)) + if (roomid != "-1" && user.Id != 0 && user.Id != Config.RoomList.GetRoomMaster(roomid).Id && !Config.RoomList.GetReadyUserList(roomid).Contains(user)) { Config.RoomList.SetReady(roomid, user); result = true; } } - ResultData.Add("result", result); - ResultData.Add("ready", Config.RoomList.GetReadyPlayerList(roomid)); - ResultData.Add("notready", Config.RoomList.GetNotReadyPlayerList(roomid)); + resultData.Add("result", result); + resultData.Add("ready", Config.RoomList.GetReadyUserList(roomid)); + resultData.Add("notready", Config.RoomList.GetNotReadyUserList(roomid)); } /// /// 取消已准备状态 /// - /// - /// - private void CancelReady(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void CancelReady(Dictionary requestData, Dictionary resultData) { bool result = false; string roomid = "-1"; - if (RequestData.Count >= 1) + if (requestData.Count >= 1) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - roomid = DataRequest.GetHashtableJsonObject(RequestData, "roomid") ?? "-1"; + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; User user = Server.User; - if (roomid != "-1" && user.Id != 0 && Config.RoomList.GetReadyPlayerList(roomid).Contains(user)) + if (roomid != "-1" && user.Id != 0 && user.Id != Config.RoomList.GetRoomMaster(roomid).Id && Config.RoomList.GetReadyUserList(roomid).Contains(user)) { Config.RoomList.CancelReady(roomid, user); result = true; } } - ResultData.Add("result", result); - ResultData.Add("ready", Config.RoomList.GetReadyPlayerList(roomid)); - ResultData.Add("notready", Config.RoomList.GetNotReadyPlayerList(roomid)); + resultData.Add("result", result); + resultData.Add("ready", Config.RoomList.GetReadyUserList(roomid)); + resultData.Add("notready", Config.RoomList.GetNotReadyUserList(roomid)); } /// /// 发送聊天消息 /// - /// - private void Chat(Hashtable RequestData) + /// + private async Task Chat(Dictionary requestData) { - if (RequestData.Count >= 1) + if (requestData.Count >= 1) { - string msg = DataRequest.GetHashtableJsonObject(RequestData, "msg") ?? ""; - if (msg.Trim() != "") Server.Chat(msg); + string msg = DataRequest.GetDictionaryJsonObject(requestData, "msg") ?? ""; + if (msg.Trim() != "") + { + await Server.SendClients(Server.Listener.ClientList.Where(c => c != null && Server.InRoom.Roomid == c.InRoom.Roomid && c.User.Id != 0), + SocketMessageType.Chat, Server.User.Username, msg); + } } } /// /// 开始游戏 /// - /// - /// - private void StartGame(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void StartGame(Dictionary requestData, Dictionary resultData) { bool result = false; - if (RequestData.Count >= 2) + if (requestData.Count >= 2) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - string roomid = DataRequest.GetHashtableJsonObject(RequestData, "roomid") ?? "-1"; - bool isMaster = DataRequest.GetHashtableJsonObject(RequestData, "isMaster"); + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + string roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; + bool isMaster = DataRequest.GetDictionaryJsonObject(requestData, "isMaster"); if (roomid != "-1") { if (isMaster) { - string[] usernames = Config.RoomList.GetNotReadyPlayerList(roomid).Select(user => user.Username).ToArray(); + string[] usernames = Config.RoomList.GetNotReadyUserList(roomid).Select(user => user.Username).ToArray(); if (usernames.Length > 0) { - if (isReadyCheckCD[0] == false) + if (_isReadyCheckCD[0] == false) { // 提醒玩家准备 Server.SendSystemMessage(ShowMessageType.None, "还有玩家尚未准备,无法开始游戏。", "", 0, Server.User.Username); Server.SendSystemMessage(ShowMessageType.Tip, "房主即将开始游戏,请准备!", "请准备就绪", 10, usernames); - isReadyCheckCD[0] = true; + _isReadyCheckCD[0] = true; TaskUtility.RunTimer(() => { - isReadyCheckCD[0] = false; + _isReadyCheckCD[0] = false; }, 15000); } else @@ -395,7 +438,7 @@ namespace Milimoe.FunGame.Server.Controller } else { - List users = Config.RoomList.GetPlayerList(roomid); + List users = Config.RoomList.GetUsers(roomid); if (users.Count < 2) { Server.SendSystemMessage(ShowMessageType.None, "玩家数量不足,无法开始游戏。", "", 0, Server.User.Username); @@ -404,20 +447,20 @@ namespace Milimoe.FunGame.Server.Controller { usernames = users.Select(user => user.Username).ToArray(); Server.SendSystemMessage(ShowMessageType.None, "所有玩家均已准备,游戏将在10秒后开始。", "", 0, usernames); - Server.StartGame(roomid, users, usernames); + StartGame(roomid, users, usernames); result = true; } } } - else if (isReadyCheckCD[1] == false) + else if (_isReadyCheckCD[1] == false) { // 提醒房主开始游戏 Server.SendSystemMessage(ShowMessageType.None, "已提醒房主立即开始游戏。", "", 0, Server.User.Username); Server.SendSystemMessage(ShowMessageType.Tip, "房间中的玩家已请求你立即开始游戏。", "请求开始", 10, Config.RoomList[roomid].RoomMaster.Username); - isReadyCheckCD[1] = true; + _isReadyCheckCD[1] = true; TaskUtility.RunTimer(() => { - isReadyCheckCD[1] = false; + _isReadyCheckCD[1] = false; }, 15000); } else @@ -426,7 +469,42 @@ namespace Milimoe.FunGame.Server.Controller } } } - ResultData.Add("result", result); + resultData.Add("result", result); + } + + private void StartGame(string roomid, List users, params string[] usernames) + { + Room room = General.HallInstance; + if (roomid != "-1") + { + room = Config.RoomList[roomid]; + } + if (room.Roomid == "-1") return; + // 启动服务器 + TaskUtility.NewTask(() => + { + if (Config.GameModuleLoader != null && Config.GameModuleLoader.ModuleServers.ContainsKey(room.GameModule)) + { + Server.NowGamingServer = Config.GameModuleLoader.GetServerMode(room.GameModule); + Dictionary all = Server.Listener.UserList.Cast().ToDictionary(k => k.User.Username, v => v); + // 给其他玩家赋值模组服务器 + foreach (IServerModel model in all.Values.Where(s => s.User.Username != Server.User.Username)) + { + model.NowGamingServer = Server.NowGamingServer; + } + if (Server.NowGamingServer.StartServer(room.GameModule, room, users, Server, all)) + { + foreach (IServerModel serverTask in Server.Listener.UserList.Where(model => usernames.Contains(model.User.Username))) + { + if (serverTask != null && serverTask.Socket != null) + { + Config.RoomList.CancelReady(roomid, serverTask.User); + serverTask.Send(SocketMessageType.StartGame, room, users); + } + } + } + } + }); } #endregion @@ -436,139 +514,142 @@ namespace Milimoe.FunGame.Server.Controller /// /// 接收并验证注册验证码 /// - /// - /// - private void Reg(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void Reg(Dictionary requestData, Dictionary resultData) { string msg = ""; RegInvokeType returnType = RegInvokeType.None; bool success = false; - if (RequestData.Count >= 4) + if (requestData.Count >= 4) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - string username = DataRequest.GetHashtableJsonObject(RequestData, "username") ?? ""; - string password = DataRequest.GetHashtableJsonObject(RequestData, "password") ?? ""; - string email = DataRequest.GetHashtableJsonObject(RequestData, "email") ?? ""; - string verifycode = DataRequest.GetHashtableJsonObject(RequestData, "verifycode") ?? ""; + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + string username = DataRequest.GetDictionaryJsonObject(requestData, "username") ?? ""; + string password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; + string email = DataRequest.GetDictionaryJsonObject(requestData, "email") ?? ""; + string verifycode = DataRequest.GetDictionaryJsonObject(requestData, "verifycode") ?? ""; - // 如果没发验证码,就生成验证码 - if (verifycode.Trim() == "") + if (SQLHelper != null) { - // 先检查账号是否重复 - SQLHelper.ExecuteDataSet(UserQuery.Select_IsExistUsername(username)); - if (SQLHelper.Result == SQLResult.Success) + // 如果没发验证码,就生成验证码 + if (verifycode.Trim() == "") { - ServerHelper.WriteLine(Server.GetClientName() + " 账号已被注册"); - msg = "此账号名已被使用!"; - returnType = RegInvokeType.DuplicateUserName; - } - else - { - // 检查邮箱是否重复 - SQLHelper.ExecuteDataSet(UserQuery.Select_IsExistEmail(email)); + // 先检查账号是否重复 + SQLHelper.ExecuteDataSet(UserQuery.Select_IsExistUsername(username)); if (SQLHelper.Result == SQLResult.Success) { - ServerHelper.WriteLine(Server.GetClientName() + " 邮箱已被注册"); - msg = "此邮箱已被注册!"; - returnType = RegInvokeType.DuplicateEmail; + ServerHelper.WriteLine(Server.GetClientName() + " 账号已被注册"); + msg = "此账号名已被使用!"; + returnType = RegInvokeType.DuplicateUserName; } else { - // 检查验证码是否发送过 - SQLHelper.ExecuteDataSet(RegVerifyCodes.Select_HasSentRegVerifyCode(username, email)); + // 检查邮箱是否重复 + SQLHelper.ExecuteDataSet(UserQuery.Select_IsExistEmail(email)); if (SQLHelper.Result == SQLResult.Success) { - DateTime RegTime = (DateTime)SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime]; - string RegVerifyCode = (string)SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegVerifyCode]; - if ((DateTime.Now - RegTime).TotalMinutes < 10) - { - ServerHelper.WriteLine(Server.GetClientName() + $" 十分钟内已向{email}发送过验证码:{RegVerifyCode}"); - } - returnType = RegInvokeType.InputVerifyCode; + ServerHelper.WriteLine(Server.GetClientName() + " 邮箱已被注册"); + msg = "此邮箱已被注册!"; + returnType = RegInvokeType.DuplicateEmail; } else { - // 发送验证码,需要先删除之前过期的验证码 - SQLHelper.NewTransaction(); - SQLHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(username, email)); - RegVerify = Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 6); - SQLHelper.Execute(RegVerifyCodes.Insert_RegVerifyCode(username, email, RegVerify)); + // 检查验证码是否发送过 + SQLHelper.ExecuteDataSet(RegVerifyCodes.Select_HasSentRegVerifyCode(username, email)); if (SQLHelper.Result == SQLResult.Success) { - SQLHelper.Commit(); - if (MailSender != null) + DateTime RegTime = (DateTime)SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime]; + string RegVerifyCode = (string)SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegVerifyCode]; + if ((DateTime.Now - RegTime).TotalMinutes < 10) { - // 发送验证码 - string ServerName = Config.ServerName; - string Subject = $"[{ServerName}] FunGame 注册验证码"; - string Body = $"亲爱的 {username},
感谢您注册[{ServerName}],您的验证码是 {RegVerify} ,10分钟内有效,请及时输入!

{ServerName}
{DateTimeUtility.GetDateTimeToString(TimeType.LongDateOnly)}"; - string[] To = [email]; - if (MailSender.Send(MailSender.CreateMail(Subject, Body, System.Net.Mail.MailPriority.Normal, true, To)) == MailSendResult.Success) - { - ServerHelper.WriteLine(Server.GetClientName() + $" 已向{email}发送验证码:{RegVerify}"); - } - else - { - ServerHelper.WriteLine(Server.GetClientName() + " 无法发送验证码", InvokeMessageType.Error); - ServerHelper.WriteLine(MailSender.ErrorMsg, InvokeMessageType.Error); - } - } - else // 不使用MailSender的情况 - { - ServerHelper.WriteLine(Server.GetClientName() + $" 验证码为:{RegVerify},请服务器管理员告知此用户"); + ServerHelper.WriteLine(Server.GetClientName() + $" 十分钟内已向{email}发送过验证码:{RegVerifyCode}"); } returnType = RegInvokeType.InputVerifyCode; } - else SQLHelper.Rollback(); - } - } - } - } - else - { - // 先检查验证码 - SQLHelper.ExecuteDataSet(RegVerifyCodes.Select_RegVerifyCode(username, email, verifycode)); - if (SQLHelper.Result == SQLResult.Success) - { - // 检查验证码是否过期 - DateTime RegTime = (DateTime)SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime]; - if ((DateTime.Now - RegTime).TotalMinutes >= 10) - { - ServerHelper.WriteLine(Server.GetClientName() + " 验证码已过期"); - msg = "此验证码已过期,请重新注册。"; - SQLHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(username, email)); - } - else - { - // 注册 - if (RegVerify.Equals(SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegVerifyCode])) - { - SQLHelper.NewTransaction(); - ServerHelper.WriteLine("[Reg] UserName: " + username + " Email: " + email); - SQLHelper.Execute(UserQuery.Insert_Register(username, password, email, Server.Socket?.ClientIP ?? "")); - if (SQLHelper.Result == SQLResult.Success) - { - success = true; - msg = "注册成功!请牢记您的账号与密码!"; - SQLHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(username, email)); - SQLHelper.Commit(); - } else { - SQLHelper.Rollback(); - msg = "服务器无法处理您的注册,注册失败!"; + // 发送验证码,需要先删除之前过期的验证码 + SQLHelper.NewTransaction(); + SQLHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(username, email)); + _regVerify = Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 6); + SQLHelper.Execute(RegVerifyCodes.Insert_RegVerifyCode(username, email, _regVerify)); + if (SQLHelper.Result == SQLResult.Success) + { + SQLHelper.Commit(); + if (MailSender != null) + { + // 发送验证码 + string ServerName = Config.ServerName; + string Subject = $"[{ServerName}] FunGame 注册验证码"; + string Body = $"亲爱的 {username},
感谢您注册[{ServerName}],您的验证码是 {_regVerify} ,10分钟内有效,请及时输入!

{ServerName}
{DateTimeUtility.GetDateTimeToString(TimeType.LongDateOnly)}"; + string[] To = [email]; + if (MailSender.Send(MailSender.CreateMail(Subject, Body, System.Net.Mail.MailPriority.Normal, true, To)) == MailSendResult.Success) + { + ServerHelper.WriteLine(Server.GetClientName() + $" 已向{email}发送验证码:{_regVerify}"); + } + else + { + ServerHelper.WriteLine(Server.GetClientName() + " 无法发送验证码", InvokeMessageType.Error); + ServerHelper.WriteLine(MailSender.ErrorMsg, InvokeMessageType.Error); + } + } + else // 不使用MailSender的情况 + { + ServerHelper.WriteLine(Server.GetClientName() + $" 验证码为:{_regVerify},请服务器管理员告知此用户"); + } + returnType = RegInvokeType.InputVerifyCode; + } + else SQLHelper.Rollback(); } } - else msg = "验证码不正确,请重新输入!"; } } - else if (SQLHelper.Result == SQLResult.NotFound) msg = "验证码不正确,请重新输入!"; - else msg = "服务器无法处理您的注册,注册失败!"; + else + { + // 先检查验证码 + SQLHelper.ExecuteDataSet(RegVerifyCodes.Select_RegVerifyCode(username, email, verifycode)); + if (SQLHelper.Result == SQLResult.Success) + { + // 检查验证码是否过期 + DateTime RegTime = (DateTime)SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime]; + if ((DateTime.Now - RegTime).TotalMinutes >= 10) + { + ServerHelper.WriteLine(Server.GetClientName() + " 验证码已过期"); + msg = "此验证码已过期,请重新注册。"; + SQLHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(username, email)); + } + else + { + // 注册 + if (_regVerify.Equals(SQLHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegVerifyCode])) + { + SQLHelper.NewTransaction(); + ServerHelper.WriteLine("[Reg] Username: " + username + " Email: " + email); + SQLHelper.Execute(UserQuery.Insert_Register(username, password, email, Server.Socket?.ClientIP ?? "")); + if (SQLHelper.Result == SQLResult.Success) + { + success = true; + msg = "注册成功!请牢记您的账号与密码!"; + SQLHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(username, email)); + SQLHelper.Commit(); + } + else + { + SQLHelper.Rollback(); + msg = "服务器无法处理您的注册,注册失败!"; + } + } + else msg = "验证码不正确,请重新输入!"; + } + } + else if (SQLHelper.Result == SQLResult.NotFound) msg = "验证码不正确,请重新输入!"; + else msg = "服务器无法处理您的注册,注册失败!"; + } } } - ResultData.Add("msg", msg); - ResultData.Add("type", returnType); - ResultData.Add("success", success); + resultData.Add("msg", msg); + resultData.Add("type", returnType); + resultData.Add("success", success); } #endregion @@ -578,26 +659,26 @@ namespace Milimoe.FunGame.Server.Controller /// /// 登录 /// - /// - /// - private void Login(Hashtable RequestData, Hashtable ResultData) + /// + /// + private async Task Login(Dictionary requestData, Dictionary resultData) { string msg = ""; User user = Factory.GetUser(); - if (RequestData.Count >= 4) + if (requestData.Count >= 4) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - string username = DataRequest.GetHashtableJsonObject(RequestData, "username") ?? ""; - string password = DataRequest.GetHashtableJsonObject(RequestData, "password") ?? ""; - string autokey = DataRequest.GetHashtableJsonObject(RequestData, "autokey") ?? ""; - Guid key = DataRequest.GetHashtableJsonObject(RequestData, "key"); + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + string username = DataRequest.GetDictionaryJsonObject(requestData, "username") ?? ""; + string password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; + string autokey = DataRequest.GetDictionaryJsonObject(requestData, "autokey") ?? ""; + Guid key = DataRequest.GetDictionaryJsonObject(requestData, "key"); // CheckLogin的情况 if (key != Guid.Empty) { - if (Server.IsLoginKey(key)) + if (IsLoginKey(key)) { - Server.CheckLogin(); + await CheckLogin(); user = Server.User; } else ServerHelper.WriteLine("客户端发送了错误的秘钥,不允许本次登录。"); @@ -607,166 +688,212 @@ namespace Milimoe.FunGame.Server.Controller // 验证登录 if (username != null && password != null) { - ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] UserName: " + username); - SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(username, password)); - if (SQLHelper.Result == SQLResult.Success) + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] Username: " + username); + if (SQLHelper != null) { - DataSet DsUser = SQLHelper.DataSet; - if (autokey.Trim() != "") + SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(username, password)); + if (SQLHelper.Result == SQLResult.Success) { - SQLHelper.ExecuteDataSet(UserQuery.Select_CheckAutoKey(username, autokey)); - if (SQLHelper.Result == SQLResult.Success) + DataSet dsUser = SQLHelper.DataSet; + if (autokey.Trim() != "") { - ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] AutoKey: 已确认"); - } - else - { - msg = "AutoKey不正确,拒绝自动登录!"; - ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] " + msg); + SQLHelper.ExecuteDataSet(UserQuery.Select_CheckAutoKey(username, autokey)); + if (SQLHelper.Result == SQLResult.Success) + { + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] AutoKey: 已确认"); + } + else + { + msg = "AutoKey不正确,拒绝自动登录!"; + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] " + msg); + } } + key = Guid.NewGuid(); + PreLogin(dsUser, username, key); + resultData.Add("key", key); + } + else + { + msg = "用户名或密码不正确。"; + ServerHelper.WriteLine(msg); } - key = Guid.NewGuid(); - Server.PreLogin(DsUser, username, key); - ResultData.Add("key", key); - } - else - { - msg = "用户名或密码不正确。"; - ServerHelper.WriteLine(msg); } } } } - ResultData.Add("msg", msg); - ResultData.Add("user", user); + resultData.Add("msg", msg); + resultData.Add("user", user); + } + + /// + /// 预登录 + /// + /// + /// + /// + private void PreLogin(DataSet dsuser, string username, Guid checkloginkey) + { + _dsUser = dsuser; + _username = username; + _checkLoginKey = checkloginkey; + } + + /// + /// 确认登录 + /// + private async Task CheckLogin() + { + // 创建User对象 + Server.User = Factory.GetUser(_dsUser); + // 检查有没有重复登录的情况 + await Server.ForceLogOutDuplicateLogonUser(); + // 添加至玩家列表 + Server.AddUser(); + Server.GetUsersCount(); + } + + /// + /// 检查LoginKey + /// + /// + /// + private bool IsLoginKey(Guid key) + { + return key == _checkLoginKey; } /// /// 接收并验证找回密码时的验证码 /// - /// - /// - private void ForgetPassword(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void ForgetPassword(Dictionary requestData, Dictionary resultData) { string msg = "无法找回您的密码,请稍后再试。"; // 返回的验证信息 - if (RequestData.Count >= 3) + if (requestData.Count >= 3) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - string username = DataRequest.GetHashtableJsonObject(RequestData, ForgetVerifyCodes.Column_Username) ?? ""; - string email = DataRequest.GetHashtableJsonObject(RequestData, ForgetVerifyCodes.Column_Email) ?? ""; - string verifycode = DataRequest.GetHashtableJsonObject(RequestData, ForgetVerifyCodes.Column_ForgetVerifyCode) ?? ""; + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + string username = DataRequest.GetDictionaryJsonObject(requestData, ForgetVerifyCodes.Column_Username) ?? ""; + string email = DataRequest.GetDictionaryJsonObject(requestData, ForgetVerifyCodes.Column_Email) ?? ""; + string verifycode = DataRequest.GetDictionaryJsonObject(requestData, ForgetVerifyCodes.Column_ForgetVerifyCode) ?? ""; // 客户端发来了验证码就进行验证,没有发就生成 if (verifycode.Trim() != "") { // 先检查验证码 - SQLHelper.ExecuteDataSet(ForgetVerifyCodes.Select_ForgetVerifyCode(username, email, verifycode)); - if (SQLHelper.Result == SQLResult.Success) + if (SQLHelper != null) { - // 检查验证码是否过期 - DateTime SendTime = (DateTime)SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_SendTime]; - if ((DateTime.Now - SendTime).TotalMinutes >= 10) + SQLHelper.ExecuteDataSet(ForgetVerifyCodes.Select_ForgetVerifyCode(username, email, verifycode)); + if (SQLHelper.Result == SQLResult.Success) { - ServerHelper.WriteLine(Server.GetClientName() + " 验证码已过期"); - msg = "此验证码已过期,请重新找回密码。"; - SQLHelper.Execute(ForgetVerifyCodes.Delete_ForgetVerifyCode(username, email)); - } - else - { - // 检查验证码是否正确 - if (ForgetVerify.Equals(SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_ForgetVerifyCode])) + // 检查验证码是否过期 + DateTime SendTime = (DateTime)SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_SendTime]; + if ((DateTime.Now - SendTime).TotalMinutes >= 10) { - ServerHelper.WriteLine("[ForgerPassword] UserName: " + username + " Email: " + email); + ServerHelper.WriteLine(Server.GetClientName() + " 验证码已过期"); + msg = "此验证码已过期,请重新找回密码。"; SQLHelper.Execute(ForgetVerifyCodes.Delete_ForgetVerifyCode(username, email)); - msg = ""; } - else msg = "验证码不正确,请重新输入!"; + else + { + // 检查验证码是否正确 + if (_forgetVerify.Equals(SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_ForgetVerifyCode])) + { + ServerHelper.WriteLine("[ForgerPassword] Username: " + username + " Email: " + email); + SQLHelper.Execute(ForgetVerifyCodes.Delete_ForgetVerifyCode(username, email)); + msg = ""; + } + else msg = "验证码不正确,请重新输入!"; + } } + else msg = "验证码不正确,请重新输入!"; } - else msg = "验证码不正确,请重新输入!"; } else { // 检查账号和邮箱是否匹配 - SQLHelper.ExecuteDataSet(UserQuery.Select_CheckEmailWithUsername(username, email)); - if (SQLHelper.Result != SQLResult.Success) + if (SQLHelper != null) { - msg = "此邮箱未绑定此账号,请重试!"; - } - else - { - // 检查验证码是否发送过和是否过期 - SQLHelper.ExecuteDataSet(ForgetVerifyCodes.Select_HasSentForgetVerifyCode(username, email)); - if (SQLHelper.Result != SQLResult.Success || (DateTime.Now - ((DateTime)SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_SendTime])).TotalMinutes >= 10) + SQLHelper.ExecuteDataSet(UserQuery.Select_CheckEmailWithUsername(username, email)); + if (SQLHelper.Result != SQLResult.Success) { - // 发送验证码,需要先删除之前过期的验证码 - SQLHelper.Execute(ForgetVerifyCodes.Delete_ForgetVerifyCode(username, email)); - ForgetVerify = Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 6); - SQLHelper.Execute(ForgetVerifyCodes.Insert_ForgetVerifyCode(username, email, ForgetVerify)); - if (SQLHelper.Result == SQLResult.Success) - { - if (MailSender != null) - { - // 发送验证码 - string ServerName = Config.ServerName; - string Subject = $"[{ServerName}] FunGame 找回密码验证码"; - string Body = $"亲爱的 {username},
您正在找回[{ServerName}]账号的密码,您的验证码是 {ForgetVerify} ,10分钟内有效,请及时输入!

{ServerName}
{DateTimeUtility.GetDateTimeToString(TimeType.LongDateOnly)}"; - string[] To = [email]; - if (MailSender.Send(MailSender.CreateMail(Subject, Body, System.Net.Mail.MailPriority.Normal, true, To)) == MailSendResult.Success) - { - ServerHelper.WriteLine(Server.GetClientName() + $" 已向{email}发送验证码:{ForgetVerify}"); - msg = ""; - } - else - { - ServerHelper.WriteLine(Server.GetClientName() + " 无法发送验证码"); - ServerHelper.WriteLine(MailSender.ErrorMsg); - } - } - else // 不使用MailSender的情况 - { - ServerHelper.WriteLine(Server.GetClientName() + $" 验证码为:{ForgetVerify},请服务器管理员告知此用户"); - msg = ""; - } - } + msg = "此邮箱未绑定此账号,请重试!"; } else { - // 发送过验证码且验证码没有过期 - string ForgetVerifyCode = (string)SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_ForgetVerifyCode]; - ServerHelper.WriteLine(Server.GetClientName() + $" 十分钟内已向{email}发送过验证码:{ForgetVerifyCode}"); - msg = ""; + // 检查验证码是否发送过和是否过期 + SQLHelper.ExecuteDataSet(ForgetVerifyCodes.Select_HasSentForgetVerifyCode(username, email)); + if (SQLHelper.Result != SQLResult.Success || (DateTime.Now - ((DateTime)SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_SendTime])).TotalMinutes >= 10) + { + // 发送验证码,需要先删除之前过期的验证码 + SQLHelper.Execute(ForgetVerifyCodes.Delete_ForgetVerifyCode(username, email)); + _forgetVerify = Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 6); + SQLHelper.Execute(ForgetVerifyCodes.Insert_ForgetVerifyCode(username, email, _forgetVerify)); + if (SQLHelper.Result == SQLResult.Success) + { + if (MailSender != null) + { + // 发送验证码 + string ServerName = Config.ServerName; + string Subject = $"[{ServerName}] FunGame 找回密码验证码"; + string Body = $"亲爱的 {username},
您正在找回[{ServerName}]账号的密码,您的验证码是 {_forgetVerify} ,10分钟内有效,请及时输入!

{ServerName}
{DateTimeUtility.GetDateTimeToString(TimeType.LongDateOnly)}"; + string[] To = [email]; + if (MailSender.Send(MailSender.CreateMail(Subject, Body, System.Net.Mail.MailPriority.Normal, true, To)) == MailSendResult.Success) + { + ServerHelper.WriteLine(Server.GetClientName() + $" 已向{email}发送验证码:{_forgetVerify}"); + msg = ""; + } + else + { + ServerHelper.WriteLine(Server.GetClientName() + " 无法发送验证码"); + ServerHelper.WriteLine(MailSender.ErrorMsg); + } + } + else // 不使用MailSender的情况 + { + ServerHelper.WriteLine(Server.GetClientName() + $" 验证码为:{_forgetVerify},请服务器管理员告知此用户"); + msg = ""; + } + } + } + else + { + // 发送过验证码且验证码没有过期 + string ForgetVerifyCode = (string)SQLHelper.DataSet.Tables[0].Rows[0][ForgetVerifyCodes.Column_ForgetVerifyCode]; + ServerHelper.WriteLine(Server.GetClientName() + $" 十分钟内已向{email}发送过验证码:{ForgetVerifyCode}"); + msg = ""; + } } } } } - ResultData.Add("msg", msg); + resultData.Add("msg", msg); } /// /// 更新用户的密码 /// - /// - /// - private void UpdatePassword(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void UpdatePassword(Dictionary requestData, Dictionary resultData) { string msg = "无法更新您的密码,请稍后再试。"; - if (RequestData.Count >= 2) + if (requestData.Count >= 2) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - string username = DataRequest.GetHashtableJsonObject(RequestData, UserQuery.Column_Username) ?? ""; - string password = DataRequest.GetHashtableJsonObject(RequestData, UserQuery.Column_Password) ?? ""; + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + string username = DataRequest.GetDictionaryJsonObject(requestData, UserQuery.Column_Username) ?? ""; + string password = DataRequest.GetDictionaryJsonObject(requestData, UserQuery.Column_Password) ?? ""; if (username.Trim() != "" && password.Trim() != "") { Server.SQLHelper?.Execute(UserQuery.Update_Password(username, password)); - if (SQLHelper.Success) + if (SQLHelper?.Success ?? false) { // 更新成功返回空值 msg = ""; } } } - ResultData.Add("msg", msg); + resultData.Add("msg", msg); } #endregion @@ -776,17 +903,125 @@ namespace Milimoe.FunGame.Server.Controller /// /// 获取房间内玩家数量 /// - /// - /// - private void GetRoomPlayerCount(Hashtable RequestData, Hashtable ResultData) + /// + /// + private void GetRoomPlayerCount(Dictionary requestData, Dictionary resultData) { string roomid = "-1"; - if (RequestData.Count >= 1) + if (requestData.Count >= 1) { - ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_LastRequest), InvokeMessageType.DataRequest); - roomid = DataRequest.GetHashtableJsonObject(RequestData, "roomid") ?? "-1"; + ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); + roomid = DataRequest.GetDictionaryJsonObject(requestData, "roomid") ?? "-1"; } - ResultData.Add("count", Config.RoomList.GetPlayerCount(roomid)); + resultData.Add("count", Config.RoomList.GetUserCount(roomid)); + } + + /// + /// 开始匹配 + /// + /// + /// + private void StartMatching(RoomType type, User user) + { + _isMatching = true; + ServerHelper.WriteLine(Server.GetClientName() + " 开始匹配。类型:" + RoomSet.GetTypeString(type)); + TaskUtility.NewTask(async () => + { + if (_isMatching) + { + Room room = await MatchingRoom(type, user); + if (_isMatching && Server.Socket != null) + { + await Server.Send(SocketMessageType.MatchRoom, room); + } + _isMatching = false; + } + }).OnError(e => + { + ServerHelper.Error(e); + _isMatching = false; + }); + } + + /// + /// 终止匹配 + /// + private void StopMatching() + { + if (_isMatching) + { + ServerHelper.WriteLine(Server.GetClientName() + " 取消了匹配。"); + _isMatching = false; + } + } + + /// + /// 匹配线程 + /// + /// + /// + /// + private async Task MatchingRoom(RoomType roomtype, User user) + { + int i = 1; // Elo扩大系数 + double time = 0; // 已经匹配的时间 + double expandInterval = 10; // 扩大匹配范围的间隔时间 + double maxTime = 50; // 最大匹配时间 + + while (_isMatching) + { + // 匹配房间类型(如果是All,则匹配所有房间) + List targets; + if (roomtype == RoomType.All) + { + targets = [.. Config.RoomList.ListRoom.Where(r => r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching)]; + } + else + { + targets = [.. Config.RoomList.ListRoom.Where(r => (r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching) && r.RoomType == roomtype)]; + } + + // 如果匹配停止,则退出 + if (!_isMatching) break; + + foreach (Room room in targets) + { + // 获取当前房间的玩家列表 + List players = Config.RoomList.GetUsers(room.Roomid); + if (players.Count > 0) + { + // 计算房间平均Elo + double avgElo = players.Sum(u => u.Statistics.EloStats.TryGetValue(0, out double value) ? value : 0) / players.Count; + double userElo = user.Statistics.EloStats.TryGetValue(0, out double userValue) ? userValue : 0; + + // 匹配Elo范围,随着时间增加,范围逐渐扩大 + if (userElo >= avgElo - (300 * i) && userElo <= avgElo + (300 * i)) + { + // 找到匹配的房间,立即返回 + return room; + } + } + } + + // 如果匹配停止,则退出 + if (!_isMatching) break; + + // 检查是否已经过了10秒,扩大匹配范围 + if (time >= expandInterval * i) + { + i++; + } + // 达到最大匹配时间后不再匹配Elo,直接返回第一个房间 + if (time >= maxTime) + { + return targets.FirstOrDefault() ?? General.HallInstance; + } + + await Task.Delay(100); + time += 0.1; + } + + return General.HallInstance; } #endregion diff --git a/FunGame.Server/FunGame.Server.csproj b/FunGame.Server/FunGame.Server.csproj index c37dfb7..0fa6df9 100644 --- a/FunGame.Server/FunGame.Server.csproj +++ b/FunGame.Server/FunGame.Server.csproj @@ -16,20 +16,34 @@ 1.0 FunGameServer Milimoe.$(MSBuildProjectName.Replace(" ", "_")) + app.manifest embedded + 1701;1702;IDE0130 embedded + 1701;1702;IDE0130 + + + + + + + + + + + @@ -38,15 +52,6 @@ ..\..\FunGame.Core\bin\Debug\net8.0\FunGame.Core.dll - - ..\bin\Debug\net7.0\MySql.Data.dll - - - ..\bin\Debug\net7.0\System.Configuration.ConfigurationManager.dll - - - ..\bin\Debug\net7.0\System.Security.Permissions.dll - diff --git a/FunGame.Server/Main.cs b/FunGame.Server/Main.cs index c1c79de..45147f5 100644 --- a/FunGame.Server/Main.cs +++ b/FunGame.Server/Main.cs @@ -1,10 +1,8 @@ -using System.Collections; -using System.Collections.Generic; -using Milimoe.FunGame; +using Milimoe.FunGame; using Milimoe.FunGame.Core.Api.Utility; -using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Server.Controller; using Milimoe.FunGame.Server.Model; using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Utility; @@ -13,7 +11,8 @@ Console.Title = Config.ServerName; Console.WriteLine(FunGameInfo.GetInfo(Config.FunGameType)); bool Running = true; -ServerSocket? ListeningSocket = null; +SocketListener? SocketListener = null; +HTTPListener? WebSocketListener = null; StartServer(); @@ -32,7 +31,7 @@ while (Running) Running = false; break; case OrderDictionary.Restart: - if (ListeningSocket == null) + if (SocketListener is null || WebSocketListener is null) { ServerHelper.WriteLine("重启服务器"); StartServer(); @@ -40,7 +39,14 @@ while (Running) else ServerHelper.WriteLine("服务器正在运行,拒绝重启!"); break; default: - ConsoleModel.Order(ListeningSocket, order); + if (SocketListener != null) + { + await ConsoleModel.Order(SocketListener, order); + } + else + { + await ConsoleModel.Order(WebSocketListener, order); + } break; } } @@ -51,7 +57,7 @@ Console.ReadKey(); void StartServer() { - Task t = Task.Factory.StartNew(() => + TaskUtility.NewTask(async () => { try { @@ -60,7 +66,7 @@ void StartServer() ServerHelper.InitOrderList(); // 读取游戏模组 - if (!GetGameModuleList()) + if (!Config.GetGameModuleList()) { ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。"); } @@ -83,51 +89,135 @@ void StartServer() // 创建全局SQLHelper Config.InitSQLHelper(); - // 创建监听 - ListeningSocket = ServerSocket.StartListening(); + // 使用Socket还是WebSocket + bool useWebSocket = Config.UseWebSocket; - // 开始监听连接 - AddBannedList(ListeningSocket); - ServerHelper.WriteLine("Listen -> " + Config.ServerPort); - ServerHelper.WriteLine("服务器启动成功,开始监听 . . ."); - - if (Config.ServerNotice != "") - ServerHelper.WriteLine("\n\n********** 服务器公告 **********\n\n" + Config.ServerNotice + "\n"); - else - ServerHelper.WriteLine("无法读取服务器公告"); - - while (Running) + if (!useWebSocket) { - ClientSocket socket; - string clientip = ""; - try + // 创建监听 + SocketListener listener = SocketListener.StartListening(Config.ServerPort, Config.MaxPlayers); + SocketListener = listener; + + // 开始监听连接 + listener.BannedList.AddRange(Config.ServerBannedList); + ServerHelper.WriteLine("Listen -> " + Config.ServerPort); + ServerHelper.WriteLine("服务器启动成功,开始监听 . . ."); + + if (Config.ServerNotice != "") + ServerHelper.WriteLine("\n\n********** 服务器公告 **********\n\n" + Config.ServerNotice + "\n"); + else + ServerHelper.WriteLine("无法读取服务器公告"); + + while (Running) { - Guid token = Guid.NewGuid(); - socket = ListeningSocket.Accept(token); - clientip = socket.ClientIP; - Config.ConnectingPlayerCount++; - // 开始处理客户端连接请求 - bool isDebugMode = false; - if (Connect(socket, token, clientip, ref isDebugMode)) + ServerSocket socket; + string clientip = ""; + try { - ServerModel ClientModel = new(ListeningSocket, socket, Running, isDebugMode); - Task t = Task.Factory.StartNew(() => + Guid token = Guid.NewGuid(); + socket = listener.Accept(token); + + TaskUtility.NewTask(async () => { - ClientModel.Start(); + clientip = socket.ClientIP; + Config.ConnectingPlayerCount++; + bool isConnected = false; + bool isDebugMode = false; + + // 开始处理客户端连接请求 + SocketObject[] objs = socket.Receive(); + (isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs); + if (isConnected) + { + ServerModel ClientModel = new(listener, socket, isDebugMode); + ClientModel.SetClientName(clientip); + Task t = Task.Run(ClientModel.Start); + } + else + { + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core); + } + Config.ConnectingPlayerCount--; + }).OnError(e => + { + if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core); + ServerHelper.Error(e); }); - ClientModel.SetClientName(clientip); } - else + catch (Exception e) { - ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core); + ServerHelper.Error(e); } - Config.ConnectingPlayerCount--; } - catch (Exception e) + } + else + { + if (Config.WebSocketAddress == "*") { - if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; - ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core); - ServerHelper.Error(e); + ServerHelper.WriteLine("WebSocket 监听 * 地址要求权限提升,如果提示拒绝访问请以管理员身份运行服务器。", InvokeMessageType.Warning); + } + + // 创建监听 + HTTPListener listener = HTTPListener.StartListening(Config.WebSocketAddress, Config.WebSocketPort, Config.WebSocketSubUrl, Config.WebSocketSSL); + WebSocketListener = listener; + + // 开始监听连接 + listener.BannedList.AddRange(Config.ServerBannedList); + ServerHelper.WriteLine("Listen -> " + listener.Instance.Prefixes.First()); + ServerHelper.WriteLine("服务器启动成功,开始监听 . . ."); + + if (Config.ServerNotice != "") + ServerHelper.WriteLine("\n\n********** 服务器公告 **********\n\n" + Config.ServerNotice + "\n"); + else + ServerHelper.WriteLine("无法读取服务器公告"); + + while (Running) + { + ServerWebSocket socket; + string clientip = ""; + try + { + Guid token = Guid.NewGuid(); + socket = await listener.Accept(token); + + TaskUtility.NewTask(async () => + { + clientip = socket.ClientIP; + Config.ConnectingPlayerCount++; + bool isConnected = false; + bool isDebugMode = false; + + // 开始处理客户端连接请求 + IEnumerable objs = []; + while (!objs.Any(o => o.SocketType == SocketMessageType.Connect)) + { + objs = objs.Union(await socket.ReceiveAsync()); + } + (isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs.Where(o => o.SocketType == SocketMessageType.Connect)); + if (isConnected) + { + ServerModel ClientModel = new(listener, socket, isDebugMode); + ClientModel.SetClientName(clientip); + Task t = Task.Run(ClientModel.Start); + } + else + { + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core); + await socket.CloseAsync(); + } + Config.ConnectingPlayerCount--; + }).OnError(e => + { + if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core); + ServerHelper.Error(e); + }); + } + catch (Exception e) + { + ServerHelper.Error(e); + } } } } @@ -135,158 +225,31 @@ void StartServer() { if (e.Message.Equals(new ServerErrorException().Message)) { - if (ListeningSocket != null) + if (SocketListener != null) { - ListeningSocket.Close(); - ListeningSocket = null; + SocketListener.Close(); + SocketListener = null; + } + if (WebSocketListener != null) + { + WebSocketListener.Close(); + WebSocketListener = null; } } ServerHelper.Error(e); } finally { - if (ListeningSocket != null) + if (SocketListener != null) { - ListeningSocket.Close(); - ListeningSocket = null; + SocketListener.Close(); + SocketListener = null; + } + if (WebSocketListener != null) + { + WebSocketListener.Close(); + WebSocketListener = null; } } }); } - -bool GetGameModuleList() -{ - List supported = []; - // 构建AddonController - Hashtable delegates = []; - delegates.Add("WriteLine", new Action(msg => ServerHelper.WriteLine(msg, InvokeMessageType.GameModule))); - delegates.Add("Error", new Action(ServerHelper.Error)); - // 读取modules目录下的模组 - Config.GameModuleLoader = GameModuleLoader.LoadGameModules(Config.FunGameType, delegates); - foreach (GameModule module in Config.GameModuleLoader.AssociatedServers.Keys) - { - bool check = true; - // 检查模组是否存在对应的模组服务器 - if (Config.GameModuleLoader.AssociatedServers[module] is null) - { - ServerHelper.WriteLine("[GameModule] Load Failed: " + module.Name + " 缺少模组服务器"); - check = false; - } - // 检查模组是否有相对应的地图 - if (!Config.GameModuleLoader.Maps.ContainsKey(module.DefaultMap)) - { - ServerHelper.WriteLine("[GameModule] Load Failed: " + module + " 没有找到相对应的地图,加载失败"); - check = false; - } - if (check) - { - supported.Add(module.Name); - } - } - // 设置全局 - Config.GameModuleSupported = supported.Distinct().ToArray(); - foreach (string modename in Config.GameModuleSupported) - { - ServerHelper.WriteLine("[GameModule] Loaded: " + modename); - } - - return Config.GameModuleSupported.Length > 0; -} - -bool Connect(ClientSocket socket, Guid token, string clientip, ref bool isDebugMode) -{ - // 接收客户端消息 - foreach (SocketObject read in socket.Receive()) - { - if (read.SocketType == SocketMessageType.Connect) - { - if (Config.ConnectingPlayerCount + Config.OnlinePlayerCount > Config.MaxPlayers) - { - SendRefuseConnect(socket, "服务器可接受的连接数量已上限!"); - ServerHelper.WriteLine("服务器可接受的连接数量已上限!", InvokeMessageType.Core); - return false; - } - ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 正在连接服务器 . . .", InvokeMessageType.Core); - if (IsIPBanned(ListeningSocket, clientip)) - { - SendRefuseConnect(socket, "服务器已拒绝黑名单用户连接。"); - ServerHelper.WriteLine("检测到 " + ServerHelper.MakeClientName(clientip) + " 为黑名单用户,已禁止其连接!", InvokeMessageType.Core); - return false; - } - - ServerHelper.WriteLine("[" + SocketSet.GetTypeString(read.SocketType) + "] " + ServerHelper.MakeClientName(socket.ClientIP), InvokeMessageType.Core); - - // 读取参数 - // 参数1:客户端的游戏模组列表,没有服务器的需要拒绝 - string[] modes = read.GetParam(0) ?? []; - // 参数2:客户端是否开启了开发者模式,开启开发者模式部分功能不可用 - isDebugMode = read.GetParam(1); - if (isDebugMode) ServerHelper.WriteLine("客户端已开启开发者模式"); - - string msg = ""; - List ClientDontHave = []; - string strDontHave = string.Join("\r\n", Config.GameModuleSupported.Where(mode => !modes.Contains(mode))); - if (strDontHave != "") - { - strDontHave = "客户端缺少服务器所需的模组:" + strDontHave; - ServerHelper.WriteLine(strDontHave, InvokeMessageType.Core); - msg += strDontHave; - } - - if (msg == "" && socket.Send(SocketMessageType.Connect, true, msg, token, Config.ServerName, Config.ServerNotice) == SocketResult.Success) - { - ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已确认连接", InvokeMessageType.Core); - return true; - } - else if (msg != "" && socket.Send(SocketMessageType.Connect, false, msg) == SocketResult.Success) - { - ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "拒绝连接", InvokeMessageType.Core); - return false; - } - else - { - ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core); - return false; - } - } - } - - SendRefuseConnect(socket, "服务器已拒绝连接。"); - ServerHelper.WriteLine("客户端发送了不符合FunGame规定的字符,拒绝连接。", InvokeMessageType.Core); - return false; -} - -bool SendRefuseConnect(ClientSocket socket, string msg) -{ - // 发送消息给客户端 - msg = "连接被拒绝,如有疑问请联系服务器管理员:" + msg; - if (socket.Send(SocketMessageType.Connect, false, msg) == SocketResult.Success) - { - ServerHelper.WriteLine(ServerHelper.MakeClientName(socket.ClientIP) + " <- " + "已拒绝连接", InvokeMessageType.Core); - return true; - } - else - { - ServerHelper.WriteLine("无法传输数据,与客户端的连接可能丢失。", InvokeMessageType.Core); - return false; - } -} - -void AddBannedList(ServerSocket server) -{ - string[] bans = Config.ServerBannedList.Split(','); - foreach (string banned in bans) - { - server.BannedList.Add(banned.Trim()); - } -} - -bool IsIPBanned(ServerSocket server, string ip) -{ - string[] strs = ip.Split(":"); - if (strs.Length == 2 && server.BannedList.Contains(strs[0])) - { - return true; - } - return false; -} \ No newline at end of file diff --git a/FunGame.Server/Models/ConsoleModel.cs b/FunGame.Server/Models/ConsoleModel.cs index ada59b8..344f11c 100644 --- a/FunGame.Server/Models/ConsoleModel.cs +++ b/FunGame.Server/Models/ConsoleModel.cs @@ -1,5 +1,4 @@ using Milimoe.FunGame.Core.Interface.Base; -using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Utility; @@ -7,7 +6,7 @@ namespace Milimoe.FunGame.Server.Model { public class ConsoleModel { - public static void Order(ServerSocket? server, string order) + public static async Task Order(ISocketListener? server, string order) where T : ISocketMessageProcessor { try { @@ -19,7 +18,7 @@ namespace Milimoe.FunGame.Server.Model string client = Console.ReadLine() ?? ""; if (client != "" && server != null) { - ((ServerModel)server.GetClient(client))?.Kick("您已被服务器管理员踢出此服务器。"); + await Kick((ServerModel)server.ClientList[client]); } break; } @@ -29,7 +28,7 @@ namespace Milimoe.FunGame.Server.Model string user = Console.ReadLine() ?? ""; if (user != "" && server != null) { - ((ServerModel)server.GetUser(user))?.ForceLogOut("您已被服务器管理员强制下线。"); + await ForceLogOut((ServerModel)server.UserList[user]); } break; } @@ -46,7 +45,7 @@ namespace Milimoe.FunGame.Server.Model ShowUsers(server); break; case OrderDictionary.Help: - ServerHelper.WriteLine("Milimoe -> 帮助"); + ShowHelp(); break; } } @@ -56,7 +55,17 @@ namespace Milimoe.FunGame.Server.Model } } - private static void ShowClients(ServerSocket? server) + public static async Task Kick(ServerModel clientModel) where T : ISocketMessageProcessor + { + await clientModel.Kick("您已被服务器管理员踢出此服务器。"); + } + + public static async Task ForceLogOut(ServerModel clientModel) where T : ISocketMessageProcessor + { + await clientModel.ForceLogOut("您已被服务器管理员强制下线。"); + } + + public static void ShowClients(ISocketListener? server) where T : ISocketMessageProcessor { if (server != null) { @@ -69,7 +78,7 @@ namespace Milimoe.FunGame.Server.Model } } - private static void ShowUsers(ServerSocket? server) + public static void ShowUsers(ISocketListener? server) where T : ISocketMessageProcessor { if (server != null) { @@ -81,5 +90,10 @@ namespace Milimoe.FunGame.Server.Model } } } + + public static void ShowHelp() + { + ServerHelper.WriteLine("Milimoe -> 帮助"); + } } } diff --git a/FunGame.Server/Models/ServerModel.cs b/FunGame.Server/Models/ServerModel.cs index 6c96c04..7567de2 100644 --- a/FunGame.Server/Models/ServerModel.cs +++ b/FunGame.Server/Models/ServerModel.cs @@ -1,5 +1,4 @@ -using System.Collections; -using System.Data; +using System.Data; using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; @@ -14,191 +13,225 @@ using Milimoe.FunGame.Server.Utility; namespace Milimoe.FunGame.Server.Model { - public class ServerModel : IServerModel + public class ServerModel : IServerModel where T : ISocketMessageProcessor { /** * Public */ - public bool Running => _Running; - public ClientSocket? Socket => _Socket; - public string ClientName => _ClientName; - public User User => _User; - public Room Room - { - get => _Room; - set => _Room = value; - } - public MySQLHelper? SQLHelper => _SQLHelper; - public MailSender? MailSender => _MailSender; + public bool Running => _running; + public ISocketMessageProcessor Socket { get; } + public ISocketListener Listener { get; } + public DataRequestController DataRequestController { get; } + public Guid Token => Socket?.Token ?? Guid.Empty; + public string ClientName => _clientName; + public User User { get; set; } = General.UnknownUserInstance; + public Room InRoom { get; set; } = General.HallInstance; + public SQLHelper? SQLHelper => _sqlHelper; + public MailSender? MailSender => _mailer; public bool IsDebugMode { get; } + public GameModuleServer? NowGamingServer { get; set; } = null; /** - * Private + * protected */ - private GameModuleServer? NowGamingServer = null; + protected bool _running = true; + protected int _failedTimes = 0; // 超过一定次数断开连接 + protected string _clientName = ""; + protected SQLHelper? _sqlHelper = null; + protected MailSender? _mailer = null; + protected string _username = ""; + protected long _loginTime = 0; + protected long _logoutTime = 0; - private ClientSocket? _Socket = null; - private bool _Running = false; - private User _User = General.UnknownUserInstance; - private Room _Room = General.HallInstance; - private string _ClientName = ""; - public MySQLHelper? _SQLHelper = null; - public MailSender? _MailSender = null; - - private Guid CheckLoginKey = Guid.Empty; - private int FailedTimes = 0; // 超过一定次数断开连接 - private string UserName = ""; - private DataSet DsUser = new(); - private readonly Guid Token; - private readonly ServerSocket Server; - private readonly DataRequestController DataRequestController; - private long LoginTime; - private long LogoutTime; - private bool IsMatching; - - public ServerModel(ServerSocket server, ClientSocket socket, bool running, bool isDebugMode) + public ServerModel(ISocketListener server, ISocketMessageProcessor socket, bool isDebugMode) { - Server = server; - _Socket = socket; - _Running = running; - Token = socket.Token; - this.IsDebugMode = isDebugMode; - if (Config.SQLMode) _SQLHelper = new(this); - _MailSender = SmtpHelper.GetMailSender(); + Listener = server; + Socket = socket; DataRequestController = new(this); + IsDebugMode = isDebugMode; + if (Config.SQLMode == SQLMode.MySQL) _sqlHelper = new MySQLHelper(this); + else if (Config.SQLMode == SQLMode.SQLite) _sqlHelper = Config.SQLHelper; + else ServerHelper.WriteLine("SQL 服务处于关闭状态", InvokeMessageType.Warning); + _mailer = SmtpHelper.GetMailSender(); } - public bool Read(ClientSocket socket) + public virtual async Task SocketMessageHandler(ISocketMessageProcessor socket, SocketObject obj) { - // 接收客户端消息 - try + // 读取收到的消息 + SocketMessageType type = obj.SocketType; + Guid token = obj.Token; + string msg = ""; + + // 验证Token + if (type != SocketMessageType.HeartBeat && token != socket.Token) { - // 禁止GameModuleServer调用 - if ((IServerModel)this is GameModuleServer) throw new NotSupportedException("请勿在GameModuleServer类中调用此方法"); - - SocketObject[] SocketObjects = socket.Receive(); - if (SocketObjects.Length == 0) - { - ServerHelper.WriteLine(GetClientName() + " 发送了空信息。"); - return false; - } - SocketObject SocketObject = SocketObjects[0]; - - SocketMessageType type = SocketObject.SocketType; - Guid token = SocketObject.Token; - object[] args = SocketObject.Parameters; - string msg = ""; - - // 验证Token - if (type != SocketMessageType.HeartBeat && token != Token) - { - ServerHelper.WriteLine(GetClientName() + " 使用了非法方式传输消息,服务器拒绝回应 -> [" + SocketSet.GetTypeString(type) + "]"); - return false; - } - - if (type == SocketMessageType.EndGame) - { - NowGamingServer = null; - return true; - } - - if (type == SocketMessageType.DataRequest) - { - return DataRequestHandler(socket, SocketObject); - } - - if (type == SocketMessageType.Gaming) - { - return GamingMessageHandler(socket, SocketObject); - } - - if (type == SocketMessageType.HeartBeat) - { - return HeartBeat(socket); - } - - switch (type) - { - case SocketMessageType.Disconnect: - ServerHelper.WriteLine("[" + SocketSet.GetTypeString(SocketMessageType.DataRequest) + "] " + GetClientName() + " -> Disconnect", InvokeMessageType.Core); - msg = "你已成功断开与服务器的连接: " + Config.ServerName + "。 "; - break; - } - return Send(socket, type, msg); - } - catch (Exception e) - { - ServerHelper.WriteLine(GetClientName() + " 没有回应。"); - ServerHelper.Error(e); + ServerHelper.WriteLine(GetClientName() + " 使用了非法方式传输消息,服务器拒绝回应 -> [" + SocketSet.GetTypeString(type) + "]"); return false; } + + if (type == SocketMessageType.HeartBeat) + { + return await HeartBeat(); + } + + if (type == SocketMessageType.EndGame) + { + NowGamingServer = null; + return true; + } + + if (type == SocketMessageType.DataRequest) + { + return await DataRequestHandler(obj); + } + + if (type == SocketMessageType.GamingRequest) + { + return await GamingRequestHandler(obj); + } + + if (type == SocketMessageType.Gaming) + { + return await GamingMessageHandler(obj); + } + + switch (type) + { + case SocketMessageType.Disconnect: + ServerHelper.WriteLine("[" + SocketSet.GetTypeString(SocketMessageType.DataRequest) + "] " + GetClientName() + " -> Disconnect", InvokeMessageType.Core); + msg = "你已成功断开与服务器的连接: " + Config.ServerName + "。 "; + break; + } + + return await Send(type, msg); } - public bool DataRequestHandler(ClientSocket socket, SocketObject SocketObject) + public async Task HeartBeat() + { + bool result = await Send(SocketMessageType.HeartBeat); + if (!result) + { + ServerHelper.WriteLine("[ " + _username + " ] " + nameof(HeartBeat) + ": " + result, InvokeMessageType.Error); + } + return result; + } + + protected async Task DataRequestHandler(SocketObject obj) { if (SQLHelper != null) { - Hashtable result = []; + Dictionary result = []; + Guid requestID = Guid.Empty; DataRequestType type = DataRequestType.UnKnown; - if (SocketObject.Parameters.Length > 0) + if (obj.Parameters.Length > 0) { try { - type = SocketObject.GetParam(0); - Hashtable data = SocketObject.GetParam(1) ?? []; + type = obj.GetParam(0); + requestID = obj.GetParam(1); + Dictionary data = obj.GetParam>(2) ?? []; - result = DataRequestController.GetResultData(type, data); + result = await DataRequestController.GetResultData(type, data); } catch (Exception e) { ServerHelper.Error(e); SQLHelper.Rollback(); - return Send(socket, SocketMessageType.DataRequest, type, result); + return await Send(SocketMessageType.DataRequest, type, requestID, result); } } - return Send(socket, SocketMessageType.DataRequest, type, result); + bool sendResult = await Send(SocketMessageType.DataRequest, type, requestID, result); + if (!sendResult) + { + ServerHelper.WriteLine("[ " + _username + " ] " + nameof(DataRequestHandler) + ": " + sendResult, InvokeMessageType.Error); + } + return sendResult; } + ServerHelper.WriteLine("[ " + _username + " ] " + nameof(DataRequestHandler) + ": " + false, InvokeMessageType.Error); return false; } - public bool GamingMessageHandler(ClientSocket socket, SocketObject SocketObject) + protected async Task GamingRequestHandler(SocketObject obj) { if (NowGamingServer != null) { - Hashtable result = []; + Dictionary result = []; + Guid requestID = Guid.Empty; GamingType type = GamingType.None; - if (SocketObject.Parameters.Length > 0) + if (obj.Parameters.Length > 0) { try { - type = SocketObject.GetParam(0); - Hashtable data = SocketObject.GetParam(1) ?? []; + type = obj.GetParam(0); + requestID = obj.GetParam(1); + Dictionary data = obj.GetParam>(2) ?? []; - result = NowGamingServer.GamingMessageHandler(UserName, type, data); + result = await NowGamingServer.GamingMessageHandler(_username, type, data); } catch (Exception e) { ServerHelper.Error(e); - return Send(socket, SocketMessageType.Gaming, type, result); + return await Send(SocketMessageType.GamingRequest, type, requestID, result); } } - return Send(socket, SocketMessageType.Gaming, type, result); + bool sendResult = await Send(SocketMessageType.GamingRequest, type, requestID, result); + if (!sendResult) + { + ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingRequestHandler) + ": " + sendResult, InvokeMessageType.Error); + } + return sendResult; } + ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingRequestHandler) + ": " + false, InvokeMessageType.Error); return false; } - public bool Send(ClientSocket socket, SocketMessageType type, params object[] objs) + protected async Task GamingMessageHandler(SocketObject obj) + { + if (NowGamingServer != null) + { + Dictionary result = []; + GamingType type = GamingType.None; + + if (obj.Parameters.Length > 0) + { + try + { + type = obj.GetParam(0); + Dictionary data = obj.GetParam>(1) ?? []; + + result = await NowGamingServer.GamingMessageHandler(_username, type, data); + } + catch (Exception e) + { + ServerHelper.Error(e); + return await Send(SocketMessageType.Gaming, type, result); + } + } + + bool sendResult = await Send(SocketMessageType.Gaming, type, result); + if (!sendResult) + { + ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingMessageHandler) + ": " + sendResult, InvokeMessageType.Error); + } + return sendResult; + } + + ServerHelper.WriteLine("[ " + _username + " ] " + nameof(GamingMessageHandler) + ": " + false, InvokeMessageType.Error); + return false; + } + + public virtual async Task Send(SocketMessageType type, params object[] objs) { // 发送消息给客户端 try { - if (socket.Send(type, objs) == SocketResult.Success) + if (await Socket.SendAsync(type, objs) == SocketResult.Success) { switch (type) { @@ -207,14 +240,15 @@ namespace Milimoe.FunGame.Server.Model break; case SocketMessageType.Disconnect: RemoveUser(); - Close(); + await Close(); break; case SocketMessageType.Chat: return true; } - object obj = objs[0]; - if (obj.GetType() == typeof(string) && (string)obj != "") - ServerHelper.WriteLine("[" + SocketSet.GetTypeString(type) + "] " + GetClientName() + " <- " + obj, InvokeMessageType.Core); + if (objs.Length > 0 && objs[0] is string str && str != "") + { + ServerHelper.WriteLine("[" + SocketSet.GetTypeString(type) + "] " + GetClientName() + " <- " + str, InvokeMessageType.Core); + } return true; } throw new CanNotSendToClientException(); @@ -227,18 +261,36 @@ namespace Milimoe.FunGame.Server.Model } } - public void Start() - { - if ((IServerModel)this is GameModuleServer) throw new NotSupportedException("请勿在GameModuleServer类中调用此方法"); // 禁止GameModuleServer调用 - Task StreamReader = Task.Factory.StartNew(CreateStreamReader); - Task PeriodicalQuerier = Task.Factory.StartNew(CreatePeriodicalQuerier); + public async Task SendClients(IEnumerable clients, SocketMessageType type, params object[] objs) + { + // 发送消息给多个客户端 + try + { + foreach (IServerModel client in clients) + { + if (client.Socket != null) + { + await client.Socket.SendAsync(type, objs); + } + } + } + catch (Exception e) + { + ServerHelper.Error(e); + } + } + + public async Task Start() + { + TaskUtility.NewTask(CreatePeriodicalQuerier); + await CreateStreamReader(); } public void SetClientName(string ClientName) { - _ClientName = ClientName; + _clientName = ClientName; // 添加客户端到列表中 - Server.AddClient(_ClientName, this); + Listener.ClientList.Add(_clientName, this); Config.OnlinePlayerCount++; GetUsersCount(); } @@ -248,55 +300,18 @@ namespace Milimoe.FunGame.Server.Model return ServerHelper.MakeClientName(ClientName, User); } - public void PreLogin(DataSet dsuser, string username, Guid checkloginkey) + public void SendSystemMessage(ShowMessageType showtype, string msg, string title, int autoclose, params string[] usernames) { - DsUser = dsuser; - UserName = username; - CheckLoginKey = checkloginkey; - } - - public void CheckLogin() - { - // 创建User对象 - _User = Factory.GetUser(DsUser); - // 检查有没有重复登录的情况 - KickUser(); - // 添加至玩家列表 - AddUser(); - GetUsersCount(); - // CheckLogin - LoginTime = DateTime.Now.Ticks; - SQLHelper?.Execute(UserQuery.Update_CheckLogin(UserName, _Socket?.ClientIP.Split(':')[0] ?? "127.0.0.1")); - } - - public bool IsLoginKey(Guid key) - { - return key == CheckLoginKey; - } - - public void LogOut() - { - // 从玩家列表移除 - RemoveUser(); - GetUsersCount(); - CheckLoginKey = Guid.Empty; - } - - public void ForceLogOut(string msg, string username = "") - { - ServerModel serverTask = (ServerModel)Server.GetUser(username == "" ? UserName : username); - if (serverTask.Socket != null) + foreach (IServerModel serverTask in Listener.UserList.Where(model => usernames.Length > 0 && usernames.Contains(model.User.Username))) { - serverTask.Room = General.HallInstance; - foreach (Room room in Config.RoomList.Cast()) + if (serverTask != null && serverTask.Socket != null) { - QuitRoom(room.Roomid, room.RoomMaster.Id == User.Id); + serverTask.Send(SocketMessageType.System, showtype, msg, title, autoclose); } - serverTask.Send(serverTask.Socket, SocketMessageType.ForceLogout, msg); } } - public bool QuitRoom(string roomid, bool isMaster) + public async Task QuitRoom(string roomid, bool isMaster) { bool result; @@ -306,21 +321,21 @@ namespace Milimoe.FunGame.Server.Model // 是否是房主 if (isMaster) { - List users = Config.RoomList.GetPlayerList(roomid); + List users = [.. Config.RoomList[roomid].UserAndIsReady.Keys]; if (users.Count > 0) // 如果此时房间还有人,更新房主 { User NewMaster = users[0]; Room.RoomMaster = NewMaster; SQLHelper?.Execute(RoomQuery.Update_QuitRoom(roomid, User.Id, NewMaster.Id)); - this.Room = General.HallInstance; - UpdateRoomMaster(Room, true); + this.InRoom = General.HallInstance; + await UpdateRoomMaster(Room, true); result = true; } else // 没人了就解散房间 { Config.RoomList.RemoveRoom(roomid); SQLHelper?.Execute(RoomQuery.Delete_QuitRoom(roomid, User.Id)); - this.Room = General.HallInstance; + this.InRoom = General.HallInstance; ServerHelper.WriteLine("[ " + GetClientName() + " ] 解散了房间 " + roomid); result = true; } @@ -328,236 +343,86 @@ namespace Milimoe.FunGame.Server.Model // 不是房主直接退出房间 else { - this.Room = General.HallInstance; - UpdateRoomMaster(Room); + this.InRoom = General.HallInstance; + await UpdateRoomMaster(Room); result = true; } return result; } - public void Kick(string msg, string clientname = "") + public async Task UpdateRoomMaster(Room room, bool isUpdateRoomMaster = false) { - // 将客户端踢出服务器 - ServerModel serverTask = (ServerModel)Server.GetClient(clientname == "" ? ClientName : clientname); - if (serverTask.Socket != null) + foreach (IServerModel Client in Listener.ClientList.Where(c => c != null && c.User.Id != 0 && room.Roomid == c.InRoom?.Roomid)) { - serverTask.Room = General.HallInstance; - foreach (Room room in Config.RoomList.Cast()) + await Client.Send(SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。"); + if (isUpdateRoomMaster && room.RoomMaster?.Id != 0 && room.Roomid != "-1") { - QuitRoom(room.Roomid, room.RoomMaster.Id == User.Id); - } - RemoveUser(); - serverTask.Send(serverTask.Socket, SocketMessageType.Disconnect, msg); - } - Close(); - } - - public void Chat(string msg) - { - ServerHelper.WriteLine(msg); - foreach (ServerModel Client in Server.ClientList.Cast()) - { - if (Room.Roomid == Client.Room.Roomid) - { - if (Client != null && User.Id != 0) - { - Client.Send(Client.Socket!, SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + msg); - } + await Client.Send(SocketMessageType.UpdateRoomMaster, room); } } } - public void SendSystemMessage(ShowMessageType showtype, string msg, string title, int autoclose, params string[] usernames) + public async Task Kick(string msg) { - foreach (ServerModel serverTask in Server.UserList.Cast().Where(model => usernames.Length > 0 && usernames.Contains(model.UserName))) - { - if (serverTask != null && serverTask.Socket != null) - { - serverTask.Send(serverTask.Socket, SocketMessageType.System, showtype, msg, title, autoclose); - } - } + await QuitRoom(InRoom.Roomid, InRoom.RoomMaster.Id == User.Id); + RemoveUser(); + InRoom = General.HallInstance; + await Send(SocketMessageType.Disconnect, msg); + await Close(); } - public void StartGame(string roomid, List users, params string[] usernames) + public async Task ForceLogOut(string msg) { - Room room = General.HallInstance; - if (roomid != "-1") - { - room = Config.RoomList[roomid]; - } - if (room.Roomid == "-1") return; - // 启动服务器 - TaskUtility.NewTask(() => - { - if (Config.GameModuleLoader != null && Config.GameModuleLoader.ServerModules.ContainsKey(room.GameModule)) - { - NowGamingServer = Config.GameModuleLoader.GetServerMode(room.GameModule); - Dictionary others = Server.UserList.Cast().Where(model => usernames.Contains(model.User.Username) && model.User.Username != UserName).ToDictionary(k => k.User.Username, v => v); - if (NowGamingServer.StartServer(room.GameModule, room, users, this, others)) - { - foreach (ServerModel serverTask in Server.UserList.Cast().Where(model => usernames.Contains(model.User.Username))) - { - if (serverTask != null && serverTask.Socket != null) - { - Config.RoomList.CancelReady(roomid, serverTask.User); - serverTask.Send(serverTask.Socket, SocketMessageType.StartGame, room, users); - } - } - } - } - }); + await QuitRoom(InRoom.Roomid, InRoom.RoomMaster.Id == User.Id); + InRoom = General.HallInstance; + await Send(SocketMessageType.ForceLogout, msg); } - public void IntoRoom(string roomid) - { - Room = Config.RoomList[roomid]; - foreach (ServerModel Client in Server.ClientList.Cast().Where(c => c != null && c.Socket != null && roomid == c.Room.Roomid)) - { - if (User.Id != 0) - { - Client.Send(Client.Socket!, SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 进入了房间。"); - } - } - } - - public void UpdateRoomMaster(Room Room, bool bolIsUpdateRoomMaster = false) - { - foreach (ServerModel Client in Server.ClientList.Cast().Where(c => c != null && c.Socket != null && Room.Roomid == c.Room.Roomid)) - { - if (User.Id != 0) - { - Client.Send(Client.Socket!, SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。"); - if (bolIsUpdateRoomMaster && Room.RoomMaster?.Id != 0 && Room.Roomid != "-1") - { - Client.Send(Client.Socket!, SocketMessageType.UpdateRoomMaster, Room); - } - } - } - } - - public bool HeartBeat(ClientSocket socket) - { - return Send(socket, SocketMessageType.HeartBeat, ""); - } - - public void StartMatching(RoomType type, User user) - { - IsMatching = true; - ServerHelper.WriteLine(GetClientName() + " 开始匹配。类型:" + RoomSet.GetTypeString(type)); - TaskUtility.NewTask(async () => - { - if (IsMatching) - { - Room room = await MatchingRoom(type, user); - if (IsMatching && Socket != null) - { - Send(Socket, SocketMessageType.MatchRoom, room); - } - IsMatching = false; - } - }).OnError(e => - { - ServerHelper.Error(e); - IsMatching = false; - }); - } - - public void StopMatching() - { - if (IsMatching) - { - ServerHelper.WriteLine(GetClientName() + " 取消了匹配。"); - IsMatching = false; - } - } - - private async Task MatchingRoom(RoomType roomtype, User user) - { - int i = 1; - int time = 0; - while (IsMatching) - { - // 先列出符合条件的房间 - List targets = Config.RoomList.ListRoom.Where(r => r.RoomType == roomtype).ToList(); - - // 匹配Elo - foreach (Room room in targets) - { - // 计算房间平均Elo - List players = Config.RoomList.GetPlayerList(room.Roomid); - if (players.Count > 0) - { - decimal avgelo = players.Sum(u => u.Statistics.EloStats.ContainsKey(0) ? u.Statistics.EloStats[0] : 0M) / players.Count; - decimal userelo = user.Statistics.EloStats.ContainsKey(0) ? user.Statistics.EloStats[0] : 0M; - if (userelo >= avgelo - (300 * i) && userelo <= avgelo + (300 * i)) - { - return room; - } - } - } - - if (!IsMatching) break; - - // 等待10秒 - await Task.Delay(10 * 1000); - time += 10 * 1000; - if (time >= 50 * 1000) - { - // 50秒后不再匹配Elo,直接返回第一个房间 - if (targets.Count > 0) - { - return targets[0]; - } - break; - } - i++; - } - - return General.HallInstance; - } - - private void KickUser() + public async Task ForceLogOutDuplicateLogonUser() { if (User.Id != 0) { string user = User.Username; - if (Server.ContainsUser(user)) + if (Listener.UserList.ContainsKey(user)) { ServerHelper.WriteLine("OnlinePlayers: 玩家 " + user + " 重复登录!"); - ForceLogOut("您的账号在别处登录,已强制下线。"); + await ForceLogOut("您的账号在别处登录,已强制下线。"); } } } - private bool AddUser() + public bool AddUser() { if (User.Id != 0 && this != null) { - Server.AddUser(User.Username, this); - UserName = User.Username; + Listener.UserList.Add(User.Username, this); + _username = User.Username; ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已添加"); + // 更新最后登录时间、IP地址 + _loginTime = DateTime.Now.Ticks; + SQLHelper?.Execute(UserQuery.Update_CheckLogin(_username, Socket?.ClientIP.Split(':')[0] ?? "127.0.0.1")); return true; } return false; } - private bool RemoveUser() + public bool RemoveUser() { if (User.Id != 0 && this != null) { - LogoutTime = DateTime.Now.Ticks; - int TotalMinutes = Convert.ToInt32((new DateTime(LogoutTime) - new DateTime(LoginTime)).TotalMinutes); + _logoutTime = DateTime.Now.Ticks; + int TotalMinutes = Convert.ToInt32((new DateTime(_logoutTime) - new DateTime(_loginTime)).TotalMinutes); SQLHelper?.Execute(UserQuery.Update_GameTime(User.Username, TotalMinutes)); - if (SQLHelper?.Result == SQLResult.Success) + if (SQLHelper != null && SQLHelper.Result == SQLResult.Success) { ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 本次已游玩" + TotalMinutes + "分钟"); } else ServerHelper.WriteLine("OnlinePlayers: 无法更新玩家 " + User.Username + " 的游戏时长"); - if (Server.RemoveUser(User.Username)) + if (Listener.UserList.Remove(User.Username)) { ServerHelper.WriteLine("OnlinePlayers: 玩家 " + User.Username + " 已移除"); - _User = General.UnknownUserInstance; + User = General.UnknownUserInstance; return true; } else ServerHelper.WriteLine("OnlinePlayers: 移除玩家 " + User.Username + " 失败"); @@ -565,37 +430,65 @@ namespace Milimoe.FunGame.Server.Model return false; } - private void GetUsersCount() + public void GetUsersCount() { - ServerHelper.WriteLine($"目前在线客户端数量: {Server.ClientCount}(已登录的玩家数量:{Server.UserCount})"); + ServerHelper.WriteLine($"{Listener.Name} 的目前在线客户端数量: {Listener.ClientList.Count}(已登录的玩家数量:{Listener.UserList.Count})"); } - private void CreateStreamReader() + protected virtual async Task Read(ISocketMessageProcessor socket) { - Thread.Sleep(20); + // 接收客户端消息 + try + { + SocketObject[] objs = await socket.ReceiveAsync(); + + if (objs.Length == 0) + { + ServerHelper.WriteLine(GetClientName() + " 发送了空信息。"); + return false; + } + + foreach (SocketObject obj in objs) + { + await SocketMessageHandler(socket, obj); + } + + return true; + } + catch (Exception e) + { + ServerHelper.WriteLine(GetClientName() + " 没有回应。"); + ServerHelper.Error(e); + return false; + } + } + + protected async Task CreateStreamReader() + { + await Task.Delay(20); ServerHelper.WriteLine("Creating: StreamReader -> " + GetClientName() + " ...OK"); while (Running) { if (Socket != null) { - if (!Read(Socket)) + if (!await Read(Socket)) { - FailedTimes++; - if (FailedTimes >= Config.MaxConnectionFaileds) + _failedTimes++; + if (_failedTimes >= Config.MaxConnectionFaileds) { RemoveUser(); - Close(); + await Close(); ServerHelper.WriteLine(GetClientName() + " Error -> Too Many Faileds."); ServerHelper.WriteLine(GetClientName() + " Close -> StreamReader is Closed."); break; } } - else if (FailedTimes - 1 >= 0) FailedTimes--; + else if (_failedTimes - 1 >= 0) _failedTimes--; } else { RemoveUser(); - Close(); + await Close(); ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed."); ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed."); break; @@ -603,41 +496,40 @@ namespace Milimoe.FunGame.Server.Model } } - private void CreatePeriodicalQuerier() + protected async Task CreatePeriodicalQuerier() { - Thread.Sleep(20); + await Task.Delay(20); ServerHelper.WriteLine("Creating: PeriodicalQuerier -> " + GetClientName() + " ...OK"); while (Running) { // 每两小时触发一次SQL服务器的心跳查询,防止SQL服务器掉线 try { - Thread.Sleep(2 * 1000 * 3600); - SQLHelper?.ExecuteDataSet(UserQuery.Select_IsExistUsername(UserName)); + await Task.Delay(2 * 1000 * 3600); + SQLHelper?.ExecuteDataSet(UserQuery.Select_IsExistUsername(_username)); } catch (Exception e) { ServerHelper.Error(e); RemoveUser(); - Close(); + await Close(); ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed."); ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed."); } } } - private void Close() + protected async Task Close() { try { SQLHelper?.Close(); - _SQLHelper = null; + _sqlHelper = null; MailSender?.Dispose(); - _MailSender = null; - Socket?.Close(); - _Socket = null; - _Running = false; - Server.RemoveClient(ClientName); + _mailer = null; + await Socket.CloseAsync(); + _running = false; + Listener.ClientList.Remove(ClientName); Config.OnlinePlayerCount--; GetUsersCount(); } diff --git a/FunGame.Server/Others/Config.cs b/FunGame.Server/Others/Config.cs index c2f2de0..8ca94e5 100644 --- a/FunGame.Server/Others/Config.cs +++ b/FunGame.Server/Others/Config.cs @@ -2,11 +2,13 @@ using System.Text; using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Entity; using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Server.Utility; +using Milimoe.FunGame.Server.Utility.DataUtility; namespace Milimoe.FunGame.Server.Others { @@ -18,10 +20,35 @@ namespace Milimoe.FunGame.Server.Others public static string ServerName { get; set; } = "FunGame Server"; /// - /// 默认端口 + /// Socket 端口 /// public static int ServerPort { get; set; } = 22222; + /// + /// 使用 WebSocket + /// + public static bool UseWebSocket { get; set; } = false; + + /// + /// WebSocket 监听地址 + /// + public static string WebSocketAddress { get; set; } = "localhost"; + + /// + /// WebSocket 端口 + /// + public static int WebSocketPort { get; set; } = 22222; + + /// + /// WebSocket 监听子路径 + /// + public static string WebSocketSubUrl { get; set; } = "ws"; + + /// + /// WebSocket 开启 SSL + /// + public static bool WebSocketSSL { get; set; } = false; + /// /// 默认状态:1可连接 0不可连接 -1不可用 /// @@ -50,7 +77,7 @@ namespace Milimoe.FunGame.Server.Others /// /// 禁止连接的黑名单 /// - public static string ServerBannedList { get; set; } = ""; + public static List ServerBannedList { get; set; } = []; /// /// 最多接受连接的玩家数量 @@ -95,7 +122,7 @@ namespace Milimoe.FunGame.Server.Others /// /// 是否运行数据库模式 /// - public static bool SQLMode { get; set; } = false; + public static SQLMode SQLMode { get; set; } = SQLMode.None; /// /// Server实际安装的模组 @@ -114,7 +141,7 @@ namespace Milimoe.FunGame.Server.Others { get { - if (_SQLHelper is null) throw new MySQLConfigException(); + if (_SQLHelper is null) throw new SQLServiceException(); return _SQLHelper; } } @@ -128,12 +155,30 @@ namespace Milimoe.FunGame.Server.Others { try { - _SQLHelper = new MySQLHelper("", false); - if (((MySQLHelper)_SQLHelper).Connection != null) + if (INIHelper.ExistINIFile()) { - SQLMode = true; - ServerLogin(); - ClearRoomList(); + if (INIHelper.ReadINI("MySQL", "UseMySQL").Trim() == "true") + { + _SQLHelper = new MySQLHelper("", false); + if (((MySQLHelper)_SQLHelper).Connection != null) + { + SQLMode = _SQLHelper.Mode; + ServerLogin(); + ClearRoomList(); + } + } + else if (INIHelper.ReadINI("SQLite", "UseSQLite").Trim() == "true") + { + _SQLHelper = new SQLiteHelper(); + SQLMode = _SQLHelper.Mode; + ServerLogin(); + ClearRoomList(); + } + else + { + SQLMode = SQLMode.None; + ServerHelper.WriteLine("未开启 SQL 服务,某些请求将无法处理。", InvokeMessageType.Warning); + } } } catch (Exception e) @@ -142,12 +187,45 @@ namespace Milimoe.FunGame.Server.Others } } + public static bool GetGameModuleList() + { + List supported = []; + // 构建AddonController + Hashtable delegates = []; + delegates.Add("WriteLine", new Action(msg => ServerHelper.WriteLine(msg, InvokeMessageType.GameModule))); + delegates.Add("Error", new Action(ServerHelper.Error)); + // 读取modules目录下的模组 + GameModuleLoader = GameModuleLoader.LoadGameModules(FunGameType, delegates); + foreach (GameModuleServer module in GameModuleLoader.ModuleServers.Values) + { + bool check = true; + // 检查模组是否有相对应的地图 + if (!GameModuleLoader.Maps.ContainsKey(module.DefaultMap)) + { + ServerHelper.WriteLine("GameModule Load Failed: " + module + " 没有找到相对应的地图,加载失败", InvokeMessageType.Error); + check = false; + } + if (check) + { + supported.Add(module.Name); + } + } + // 设置全局 + GameModuleSupported = supported.Distinct().ToArray(); + foreach (string modename in GameModuleSupported) + { + ServerHelper.WriteLine("Loaded: " + modename, InvokeMessageType.GameModule); + } + + return GameModuleSupported.Length > 0; + } + /// /// 服务器启动登记 /// public static void ServerLogin() { - if (SQLMode) + if (SQLMode != SQLMode.None) { SQLHelper.Execute(ServerLoginLogs.Insert_ServerLoginLogs(ServerName, ServerKey)); } @@ -158,7 +236,7 @@ namespace Milimoe.FunGame.Server.Others /// public static void ClearRoomList() { - if (SQLMode) + if (SQLMode != SQLMode.None) { SQLHelper.Execute(RoomQuery.Delete_Rooms()); } diff --git a/FunGame.Server/Utilities/ConnectProperties.cs b/FunGame.Server/Utilities/ConnectProperties.cs new file mode 100644 index 0000000..0cd08ee --- /dev/null +++ b/FunGame.Server/Utilities/ConnectProperties.cs @@ -0,0 +1,52 @@ +using Milimoe.FunGame.Core.Api.Utility; + +namespace Milimoe.FunGame.Server.Utility +{ + public class ConnectProperties + { + public static string Name { get; set; } = ""; + public static string DataSource { get; set; } = ""; + public static string Port { get; set; } = ""; + public static string DataBase { get; set; } = ""; + public static string User { get; set; } = ""; + public static string Password { get; set; } = ""; + + /// + /// 读取MySQL服务器配置文件 + /// + /// + public static string GetConnectPropertiesForMySQL() + { + if (Name == "" && DataSource == "" && Port == "" && DataBase == "" && User == "" && Password == "") + { + if (INIHelper.ExistINIFile()) + { + DataSource = INIHelper.ReadINI("MySQL", "DBServer"); + Port = INIHelper.ReadINI("MySQL", "DBPort"); + DataBase = INIHelper.ReadINI("MySQL", "DBName"); + User = INIHelper.ReadINI("MySQL", "DBUser"); + Password = INIHelper.ReadINI("MySQL", "DBPassword"); + } + else ServerHelper.Error(new MySQLConfigException()); + } + return "data source = " + DataSource + "; port = " + Port + "; database = " + DataBase + "; user = " + User + "; password = " + Password + "; charset = utf8mb4;"; + } + + /// + /// 读取SQLite服务器配置文件 + /// + /// + public static string GetConnectPropertiesForSQLite() + { + if (DataSource == "") + { + if (INIHelper.ExistINIFile()) + { + DataSource = INIHelper.ReadINI("SQLite", "DataSource"); + } + else ServerHelper.Error(new SQLServiceException()); + } + return "data source=" + DataSource; + } + } +} diff --git a/FunGame.Server/Utilities/General.cs b/FunGame.Server/Utilities/General.cs index 4d79b0e..cad0202 100644 --- a/FunGame.Server/Utilities/General.cs +++ b/FunGame.Server/Utilities/General.cs @@ -26,6 +26,10 @@ namespace Milimoe.FunGame.Server.Utility Console.ForegroundColor = ConsoleColor.Yellow; prefix = "[Api] "; break; + case InvokeMessageType.Warning: + Console.ForegroundColor = ConsoleColor.Yellow; + prefix = "[Warning] "; + break; case InvokeMessageType.Interface: Console.ForegroundColor = ConsoleColor.Magenta; prefix = "[Interface] "; @@ -62,15 +66,18 @@ namespace Milimoe.FunGame.Server.Utility public static void Write(string msg, InvokeMessageType type = InvokeMessageType.System) { if (msg.Trim() != "") Console.Write("\r" + GetPrefix(type) + msg + "> "); + Console.ResetColor(); } public static void WriteLine(string msg, InvokeMessageType type = InvokeMessageType.System) { if (msg.Trim() != "") Console.Write("\r" + GetPrefix(type) + msg + "\n\r> "); + Console.ResetColor(); } public static void Type() { + Console.ResetColor(); Console.Write("\r> "); } @@ -86,7 +93,7 @@ namespace Milimoe.FunGame.Server.Utility private static Hashtable GetServerSettingHashtable() { - Hashtable settings = new(); + Hashtable settings = []; if (INIHelper.ExistINIFile()) { settings.Add("Name", INIHelper.ReadINI("Server", "Name")); @@ -99,6 +106,11 @@ namespace Milimoe.FunGame.Server.Utility settings.Add("OfficialMail", INIHelper.ReadINI("ServerMail", "OfficialMail")); settings.Add("SupportMail", INIHelper.ReadINI("ServerMail", "SupportMail")); settings.Add("Port", Convert.ToInt32(INIHelper.ReadINI("Socket", "Port"))); + settings.Add("UseWebSocket", Convert.ToBoolean(INIHelper.ReadINI("Socket", "UseWebSocket"))); + settings.Add("WebSocketAddress", Convert.ToString(INIHelper.ReadINI("Socket", "WebSocketAddress"))); + settings.Add("WebSocketPort", Convert.ToInt32(INIHelper.ReadINI("Socket", "WebSocketPort"))); + settings.Add("WebSocketSubUrl", Convert.ToString(INIHelper.ReadINI("Socket", "WebSocketSubUrl"))); + settings.Add("WebSocketSSL", Convert.ToBoolean(INIHelper.ReadINI("Socket", "WebSocketSSL"))); settings.Add("MaxPlayer", Convert.ToInt32(INIHelper.ReadINI("Socket", "MaxPlayer"))); settings.Add("MaxConnectFailed", Convert.ToInt32(INIHelper.ReadINI("Socket", "MaxConnectFailed"))); } @@ -124,7 +136,7 @@ namespace Milimoe.FunGame.Server.Utility if (Describe != null) Config.ServerDescription = Describe; if (Notice != null) Config.ServerNotice = Notice; if (Key != null) Config.ServerKey = Key; - if (BannedList != null) Config.ServerBannedList = BannedList; + if (BannedList != null) Config.ServerBannedList = BannedList.Split(',').Select(s => s.Trim()).ToList(); string? OfficialMail = (string?)settings["OfficialMail"]; string? SupportMail = (string?)settings["SupportMail"]; @@ -134,18 +146,28 @@ namespace Milimoe.FunGame.Server.Utility int? Status = (int?)settings["Status"]; int? Port = (int?)settings["Port"]; + bool? UseWebSocket = (bool?)settings["UseWebSocket"]; + string? WebSocketAddress = (string?)settings["WebSocketAddress"]; + int? WebSocketPort = (int?)settings["WebSocketPort"]; + string? WebSocketSubUrl = (string?)settings["WebSocketSubUrl"]; + bool? WebSocketSSL = (bool?)settings["WebSocketSSL"]; int? MaxPlayer = (int?)settings["MaxPlayer"]; int? MaxConnectFailed = (int?)settings["MaxConnectFailed"]; if (Status != null) Config.ServerStatus = (int)Status; if (Port != null) Config.ServerPort = (int)Port; + if (UseWebSocket != null) Config.UseWebSocket = (bool)UseWebSocket; + if (WebSocketAddress != null) Config.WebSocketAddress = WebSocketAddress; + if (WebSocketPort != null) Config.WebSocketPort = (int)WebSocketPort; + if (WebSocketSubUrl != null) Config.WebSocketSubUrl = WebSocketSubUrl; + if (WebSocketSSL != null) Config.WebSocketSSL = (bool)WebSocketSSL; if (MaxPlayer != null) Config.MaxPlayers = (int)MaxPlayer; if (MaxConnectFailed != null) Config.MaxConnectionFaileds = (int)MaxConnectFailed; } } catch (Exception e) { - ServerHelper.WriteLine(e.StackTrace ?? ""); + Error(e); } } @@ -192,7 +214,7 @@ namespace Milimoe.FunGame.Server.Utility if (SmtpPort > 0) return new MailSender(SenderMailAddress, SenderName, SenderPassword, SmtpHost, SmtpPort, OpenSSL); } } - ServerHelper.WriteLine("Smtp服务处于关闭状态"); + ServerHelper.WriteLine("SMTP 服务处于关闭状态", InvokeMessageType.Warning); return null; } throw new SmtpHelperException(); diff --git a/FunGame.Server/Utilities/MySQLConnection.cs b/FunGame.Server/Utilities/MySQL/MySQLConnection.cs similarity index 53% rename from FunGame.Server/Utilities/MySQLConnection.cs rename to FunGame.Server/Utilities/MySQL/MySQLConnection.cs index 6ca0cec..79fe54c 100644 --- a/FunGame.Server/Utilities/MySQLConnection.cs +++ b/FunGame.Server/Utilities/MySQL/MySQLConnection.cs @@ -1,41 +1,8 @@ -using Milimoe.FunGame.Core.Api.Utility; -using Milimoe.FunGame.Core.Model; +using Milimoe.FunGame.Core.Model; using MySql.Data.MySqlClient; namespace Milimoe.FunGame.Server.Utility.DataUtility { - public class ConnectProperties - { - public static string Name { get; set; } = ""; - public static string DataSource { get; set; } = ""; - public static string Port { get; set; } = ""; - public static string DataBase { get; set; } = ""; - public static string User { get; set; } = ""; - public static string Password { get; set; } = ""; - - /// - /// 读取MySQL服务器配置文件 - /// - /// - public static string GetConnectProperties() - { - if (Name == "" && DataSource == "" && Port == "" && DataBase == "" && User == "" && Password == "") - { - if (INIHelper.ExistINIFile()) - { - DataSource = INIHelper.ReadINI("MySQL", "DBServer"); - Port = INIHelper.ReadINI("MySQL", "DBPort"); - DataBase = INIHelper.ReadINI("MySQL", "DBName"); - User = INIHelper.ReadINI("MySQL", "DBUser"); - Password = INIHelper.ReadINI("MySQL", "DBPassword"); - return "data source = " + DataSource + "; port = " + Port + "; database = " + DataBase + "; user = " + User + "; password = " + Password + "; charset = utf8mb4;"; - } - else ServerHelper.Error(new MySQLConfigException()); - } - return "data source = " + DataSource + "; port = " + Port + "; database = " + DataBase + "; user = " + User + "; password = " + Password + "; charset = utf8mb4;"; - } - } - public class MySQLConnection { public MySqlConnection? Connection @@ -87,15 +54,15 @@ namespace Milimoe.FunGame.Server.Utility.DataUtility { try { - string _GetConnection = ConnectProperties.GetConnectProperties(); - if (_GetConnection != null) + string connectionString = ConnectProperties.GetConnectPropertiesForMySQL(); + if (connectionString != null) { - string[] DataSetting = _GetConnection.Split(";"); - if (DataSetting.Length > 1 && DataSetting[0].Length > 14 && DataSetting[1].Length > 8) + string[] strings = connectionString.Split(";"); + if (strings.Length > 1 && strings[0].Length > 14 && strings[1].Length > 8) { - ServerHelper.WriteLine("Connect -> MySQL://" + DataSetting[0][14..] + ":" + DataSetting[1][8..]); + ServerHelper.WriteLine("Connect -> MySQL://" + strings[0][14..] + ":" + strings[1][8..]); } - _Connection = new MySqlConnection(_GetConnection); + _Connection = new MySqlConnection(connectionString); _Connection.Open(); if (_Connection.State == System.Data.ConnectionState.Open) { diff --git a/FunGame.Server/Utilities/MySQLHelper.cs b/FunGame.Server/Utilities/MySQL/MySQLHelper.cs similarity index 96% rename from FunGame.Server/Utilities/MySQLHelper.cs rename to FunGame.Server/Utilities/MySQL/MySQLHelper.cs index ee1f6a2..7db2d5f 100644 --- a/FunGame.Server/Utilities/MySQLHelper.cs +++ b/FunGame.Server/Utilities/MySQL/MySQLHelper.cs @@ -1,8 +1,8 @@ using System.Data; using Milimoe.FunGame.Core.Api.Transmittal; +using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Model; -using Milimoe.FunGame.Server.Model; using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Utility.DataUtility; using MySql.Data.MySqlClient; @@ -12,6 +12,7 @@ namespace Milimoe.FunGame.Server.Utility public class MySQLHelper : SQLHelper { public override FunGameInfo.FunGame FunGameType => Config.FunGameType; + public override SQLMode Mode => SQLMode.MySQL; public override string Script { get; set; } = ""; public override CommandType CommandType { get; set; } = CommandType.Text; public override SQLResult Result => _Result; @@ -27,7 +28,7 @@ namespace Milimoe.FunGame.Server.Utility private DataSet _DataSet = new(); private MySQLConnection? _Connection; private MySqlTransaction? _Transaction; - private readonly ServerModel? ServerModel; + private readonly IServerModel? ServerModel; private readonly bool _IsOneTime = false; /// @@ -123,7 +124,7 @@ namespace Milimoe.FunGame.Server.Utility /// 创建为SocketModel服务的SQLHelper /// /// SocketModel - public MySQLHelper(ServerModel ServerModel) + public MySQLHelper(IServerModel ServerModel) { this.ServerModel = ServerModel; Script = ""; diff --git a/FunGame.Server/Utilities/MySQLManager.cs b/FunGame.Server/Utilities/MySQL/MySQLManager.cs similarity index 100% rename from FunGame.Server/Utilities/MySQLManager.cs rename to FunGame.Server/Utilities/MySQL/MySQLManager.cs diff --git a/FunGame.Server/Utilities/SQLite/SQLiteHelper.cs b/FunGame.Server/Utilities/SQLite/SQLiteHelper.cs new file mode 100644 index 0000000..9b0ca92 --- /dev/null +++ b/FunGame.Server/Utilities/SQLite/SQLiteHelper.cs @@ -0,0 +1,229 @@ +using System.Data; +using Microsoft.Data.Sqlite; +using Milimoe.FunGame.Core.Api.Transmittal; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Core.Model; + +namespace Milimoe.FunGame.Server.Utility.DataUtility +{ + public class SQLiteHelper : SQLHelper + { + public override FunGameInfo.FunGame FunGameType { get; } = FunGameInfo.FunGame.FunGame_Server; + public override SQLMode Mode { get; } = SQLMode.SQLite; + public override string Script { get; set; } = ""; + public override CommandType CommandType { get; set; } = CommandType.Text; + public override SQLResult Result => _result; + public override SQLServerInfo ServerInfo => _serverInfo ?? SQLServerInfo.Create(); + public override int UpdateRows => _updateRows; + public override DataSet DataSet => _dataSet; + + private readonly SqliteConnection _connection; + private SqliteTransaction? _transaction; + private DataSet _dataSet = new(); + private SQLResult _result = SQLResult.NotFound; + private readonly SQLServerInfo? _serverInfo; + private int _updateRows = 0; + private readonly string _connectionString = ""; + + public SQLiteHelper(string script = "", CommandType type = CommandType.Text) + { + Script = script; + CommandType = type; + _connectionString = ConnectProperties.GetConnectPropertiesForSQLite(); + string[] strings = _connectionString.Split("="); + if (strings.Length > 1) + { + ServerHelper.WriteLine("Connect -> SQLite://" + strings[1]); + _serverInfo = SQLServerInfo.Create(database: strings[1]); + } + _connection = new SqliteConnection(_connectionString); + } + + /// + /// 打开数据库连接 + /// + private void OpenConnection() + { + if (_connection.State != ConnectionState.Open) + { + _connection.Open(); + } + } + + /// + /// 关闭数据库连接 + /// + public override void Close() + { + _transaction?.Dispose(); + if (_connection.State != ConnectionState.Closed) + { + _connection.Close(); + } + } + + /// + /// 执行一个命令 + /// + /// + public override int Execute() + { + return Execute(Script); + } + + /// + /// 执行一个指定的命令 + /// + /// + /// + /// + public override int Execute(string script) + { + try + { + OpenConnection(); + ServerHelper.WriteLine("SQLQuery -> " + script, InvokeMessageType.Api); + using SqliteCommand command = new(script, _connection); + command.CommandType = CommandType; + if (_transaction != null) + { + command.Transaction = _transaction; + } + + _updateRows = command.ExecuteNonQuery(); + _result = SQLResult.Success; + Close(); + return UpdateRows; + } + catch (Exception ex) + { + _result = SQLResult.Fail; + throw new Exception($"SQL execution failed: {ex.Message}", ex); + } + } + + /// + /// 查询DataSet + /// + /// + public override DataSet ExecuteDataSet() + { + return ExecuteDataSet(Script); + } + + /// + /// 执行指定的命令查询DataSet + /// + /// + /// + /// + public override DataSet ExecuteDataSet(string script) + { + try + { + OpenConnection(); + ServerHelper.WriteLine("SQLQuery -> " + script, InvokeMessageType.Api); + using SqliteCommand command = new(script, _connection) + { + CommandType = CommandType + }; + using SqliteDataReader reader = command.ExecuteReader(); + _dataSet = new(); + DataTable table = new(); + table.Load(reader); + _dataSet.Tables.Add(table); + Close(); + return _dataSet; + } + catch (Exception ex) + { + _result = SQLResult.Fail; + throw new Exception($"SQL execution failed: {ex.Message}", ex); + } + } + + /// + /// 执行指定的命令查询DataRow + /// + /// + public override DataRow? ExecuteDataRow() + { + return ExecuteDataRow(Script); + } + + /// + /// 执行指定的命令查询DataRow + /// + /// + /// + public override DataRow? ExecuteDataRow(string script) + { + OpenConnection(); + ServerHelper.WriteLine("SQLQuery -> " + script, InvokeMessageType.Api); + DataSet dataSet = ExecuteDataSet(script); + if (dataSet.Tables.Count > 0 && dataSet.Tables[0].Rows.Count > 0) + { + Close(); + return dataSet.Tables[0].Rows[0]; + } + Close(); + return null; + } + + /// + /// 创建一个SQL事务 + /// + public override void NewTransaction() + { + OpenConnection(); + _transaction = _connection.BeginTransaction(); + } + + /// + /// 提交事务 + /// + /// + public override void Commit() + { + try + { + _transaction?.Commit(); + Close(); + _result = SQLResult.Success; + } + catch (Exception ex) + { + _result = SQLResult.Fail; + throw new Exception($"Transaction commit failed: {ex.Message}", ex); + } + } + + /// + /// 回滚事务 + /// + /// + public override void Rollback() + { + try + { + _transaction?.Rollback(); + Close(); + _result = SQLResult.Success; + } + catch (Exception ex) + { + _result = SQLResult.Fail; + throw new Exception($"Transaction rollback failed: {ex.Message}", ex); + } + } + + /// + /// 资源清理 + /// + public void Dispose() + { + _transaction?.Dispose(); + _connection.Dispose(); + } + } +} diff --git a/FunGame.Server/app.manifest b/FunGame.Server/app.manifest new file mode 100644 index 0000000..04ee0ee --- /dev/null +++ b/FunGame.Server/app.manifest @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FunGame.WebAPI/Architecture/RESTfulAPI.cs b/FunGame.WebAPI/Architecture/RESTfulAPI.cs new file mode 100644 index 0000000..1a919e5 --- /dev/null +++ b/FunGame.WebAPI/Architecture/RESTfulAPI.cs @@ -0,0 +1,49 @@ +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.WebAPI.Architecture +{ + public class RESTfulAPI(Guid token, string clientip, string clientname) : ISocketMessageProcessor + { + public Type InstanceType => typeof(RESTfulAPI); + + public Guid Token { get; init; } = token; + + public string ClientIP { get; init; } = clientip; + + public string ClientName { get; init; } = clientname; + + public void Close() + { + + } + + public async Task CloseAsync() + { + await Task.Delay(100); + } + + public SocketObject[] Receive() + { + return []; + } + + public async Task ReceiveAsync() + { + await Task.Delay(100); + return []; + } + + public SocketResult Send(SocketMessageType type, params object[] objs) + { + return SocketResult.Success; + } + + public async Task SendAsync(SocketMessageType type, params object[] objs) + { + await Task.Delay(100); + return SocketResult.Success; + } + } +} diff --git a/FunGame.WebAPI/Architecture/RESTfulAPIListener.cs b/FunGame.WebAPI/Architecture/RESTfulAPIListener.cs new file mode 100644 index 0000000..5b16b99 --- /dev/null +++ b/FunGame.WebAPI/Architecture/RESTfulAPIListener.cs @@ -0,0 +1,21 @@ +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Base; + +namespace Milimoe.FunGame.WebAPI.Architecture +{ + public class RESTfulAPIListener : ISocketListener + { + public string Name => "RESTfulAPIListener"; + + public ConcurrentModelList ClientList { get; } = []; + + public ConcurrentModelList UserList { get; } = []; + + public List BannedList { get; } = []; + + public void Close() + { + + } + } +} diff --git a/FunGame.WebAPI/Architecture/WebAPIListener.cs b/FunGame.WebAPI/Architecture/WebAPIListener.cs new file mode 100644 index 0000000..de01a78 --- /dev/null +++ b/FunGame.WebAPI/Architecture/WebAPIListener.cs @@ -0,0 +1,22 @@ +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Network; + +namespace Milimoe.FunGame.WebAPI.Architecture +{ + public class WebAPIListener : ISocketListener + { + public string Name => "WebAPIListener"; + + public ConcurrentModelList ClientList { get; } = []; + + public ConcurrentModelList UserList { get; } = []; + + public List BannedList { get; } = []; + + public void Close() + { + + } + } +} diff --git a/FunGame.WebAPI/Controllers/PostDataController.cs b/FunGame.WebAPI/Controllers/PostDataController.cs new file mode 100644 index 0000000..2821c57 --- /dev/null +++ b/FunGame.WebAPI/Controllers/PostDataController.cs @@ -0,0 +1,58 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.WebAPI.Architecture; +using Milimoe.FunGame.WebAPI.Models; + +namespace Milimoe.FunGame.WebAPI.Controllers +{ + [ApiController] + [Route("[controller]")] + [Authorize] + public class PostDataController(ILogger logger) : ControllerBase + { + public static Dictionary ResultDatas { get; } = []; + + private readonly ILogger _logger = logger; + + [HttpPost("{username}", Name = "username")] + public async Task Post(string username, [FromBody] SocketObject obj) + { + try + { + RESTfulAPIListener? apiListener = Singleton.Get(); + if (apiListener != null && apiListener.UserList.ContainsKey(username)) + { + RESTfulAPIModel model = (RESTfulAPIModel)apiListener.UserList[username]; + if (model.LastRequestID == Guid.Empty) + { + Guid uid = Guid.NewGuid(); + model.LastRequestID = uid; + await model.SocketMessageHandler(model.Socket, obj); + model.LastRequestID = Guid.Empty; + if (ResultDatas.TryGetValue(uid, out SocketObject list)) + { + return Ok(list); + } + else + { + return NotFound(); + } + } + else + { + Ok(new SocketObject(SocketMessageType.System, model.Token, "δִϣȴ")); + } + } + return NotFound(); + } + catch (Exception e) + { + _logger.LogError(e, "Error during post data"); + return StatusCode(500, "ڲ"); + } + } + } +} diff --git a/FunGame.WebAPI/Controllers/UserController.cs b/FunGame.WebAPI/Controllers/UserController.cs new file mode 100644 index 0000000..7f5b63f --- /dev/null +++ b/FunGame.WebAPI/Controllers/UserController.cs @@ -0,0 +1,74 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.SQLScript.Entity; +using Milimoe.FunGame.Server.Others; +using Milimoe.FunGame.Server.Utility; +using Milimoe.FunGame.WebAPI.Architecture; +using Milimoe.FunGame.WebAPI.Models; +using Milimoe.FunGame.WebAPI.Services; + +namespace Milimoe.FunGame.WebAPI.Controllers +{ + [ApiController] + [Route("[controller]")] + public class UserController(JWTService jwtTokenService) : ControllerBase + { + [HttpPost("login")] + public async Task Login([FromBody] LoginModel loginModel) + { + string msg = "用户名或密码不正确。"; + string clientip = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort; + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 通过 RESTful API 连接至服务器,正在登录 . . .", Core.Library.Constant.InvokeMessageType.Core); + string username = loginModel.Username; + string password = loginModel.Password; + RESTfulAPIListener? apiListener = Singleton.Get(); + if (apiListener != null) + { + // 移除旧模型 + if (apiListener.UserList.ContainsKey(username)) + { + await apiListener.UserList[username].Send(Core.Library.Constant.SocketMessageType.Disconnect); + } + // 创建新模型 + if (!apiListener.UserList.ContainsKey(username)) + { + Config.ConnectingPlayerCount++; + RESTfulAPIModel model = new(apiListener, clientip); + model.SetClientName(clientip); + // 创建User对象 + if (model.SQLHelper != null) + { + model.SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(username, password)); + Core.Entity.User user = Factory.GetUser(model.SQLHelper?.DataSet ?? new()); + if (user.Id != 0) + { + model.User = user; + // 检查有没有重复登录的情况 + await model.ForceLogOutDuplicateLogonUser(); + // 添加至玩家列表 + model.AddUser(); + model.GetUsersCount(); + string token = jwtTokenService.GenerateToken(username); + Config.ConnectingPlayerCount--; + return Ok(new { BearerToken = token, InternalToken = model.Token }); + } + } + else msg = "服务器暂时无法处理登录请求。"; + await model.Send(Core.Library.Constant.SocketMessageType.Disconnect); + } + } + + Config.ConnectingPlayerCount--; + ServerHelper.WriteLine(msg, Core.Library.Constant.InvokeMessageType.Core); + return Unauthorized(msg); + } + + [HttpPost("refresh")] + [Authorize] + public IActionResult Refresh([FromBody] LoginModel request) + { + return Ok(jwtTokenService.GenerateToken(request.Username)); + } + } +} diff --git a/FunGame.WebAPI/FunGame.WebAPI.csproj b/FunGame.WebAPI/FunGame.WebAPI.csproj new file mode 100644 index 0000000..545abd7 --- /dev/null +++ b/FunGame.WebAPI/FunGame.WebAPI.csproj @@ -0,0 +1,48 @@ + + + + net8.0 + enable + enable + Milimoe.$(MSBuildProjectName.Replace(" ", "_")) + ..\bin\ + FunGameWebAPI + Images\logo.ico + Milimoe + + + + embedded + + + + embedded + + + + + + + + + + + + + + + + + + ..\..\FunGame.Core\bin\Debug\net8.0\FunGame.Core.dll + + + + + + \ + True + + + + diff --git a/FunGame.WebAPI/FunGame.WebAPI.http b/FunGame.WebAPI/FunGame.WebAPI.http new file mode 100644 index 0000000..adae99e --- /dev/null +++ b/FunGame.WebAPI/FunGame.WebAPI.http @@ -0,0 +1,6 @@ +@FunGame.WebAPI_HostAddress = http://localhost:5117 + +GET {{FunGame.WebAPI_HostAddress}}/weatherforecast/ +Accept: application/json + +### diff --git a/FunGame.WebAPI/Images/logo.ico b/FunGame.WebAPI/Images/logo.ico new file mode 100644 index 0000000000000000000000000000000000000000..4082874accbeb80cd336bcb14ff9004ad35920ff GIT binary patch literal 16958 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 literal 0 HcmV?d00001 diff --git a/FunGame.WebAPI/Models/LoginModel.cs b/FunGame.WebAPI/Models/LoginModel.cs new file mode 100644 index 0000000..0c7db3f --- /dev/null +++ b/FunGame.WebAPI/Models/LoginModel.cs @@ -0,0 +1,8 @@ +namespace Milimoe.FunGame.WebAPI.Models +{ + public class LoginModel(string username, string password) + { + public string Username { get; set; } = username; + public string Password { get; set; } = password; + } +} diff --git a/FunGame.WebAPI/Models/RESTfulAPIModel.cs b/FunGame.WebAPI/Models/RESTfulAPIModel.cs new file mode 100644 index 0000000..6e098ca --- /dev/null +++ b/FunGame.WebAPI/Models/RESTfulAPIModel.cs @@ -0,0 +1,72 @@ +using Milimoe.FunGame.Core.Interface.Base; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Server.Model; +using Milimoe.FunGame.Server.Utility; +using Milimoe.FunGame.WebAPI.Architecture; +using Milimoe.FunGame.WebAPI.Controllers; + +namespace Milimoe.FunGame.WebAPI.Models +{ + public class RESTfulAPIModel(ISocketListener server, string clientip) : ServerModel(server, new RESTfulAPI(Guid.NewGuid(), clientip, clientip), false) + { + public Guid LastRequestID { get; set; } = Guid.Empty; + public List ToBeSent { get; set; } = []; + + public override async Task Send(SocketMessageType type, params object[] objs) + { + if (type == SocketMessageType.Disconnect || type == SocketMessageType.ForceLogout) + { + RemoveUser(); + await Close(); + return true; + } + if (type != SocketMessageType.HeartBeat) + { + SocketObject obj = new(type, Token, objs); + if (LastRequestID != Guid.Empty) + { + return PostDataController.ResultDatas.TryAdd(LastRequestID, obj); + } + else + { + ToBeSent.Add(obj); + return true; + } + } + return false; + } + + public override async Task SocketMessageHandler(ISocketMessageProcessor socket, SocketObject obj) + { + // 读取收到的消息 + SocketMessageType type = obj.SocketType; + Guid token = obj.Token; + string msg = ""; + + // 验证Token + if (type != SocketMessageType.HeartBeat && token != socket.Token) + { + ServerHelper.WriteLine(GetClientName() + " 使用了非法方式传输消息,服务器拒绝回应 -> [" + SocketSet.GetTypeString(type) + "]"); + return false; + } + + if (type == SocketMessageType.DataRequest) + { + return await DataRequestHandler(obj); + } + + if (type == SocketMessageType.GamingRequest) + { + return await GamingRequestHandler(obj); + } + + if (type == SocketMessageType.Gaming) + { + return await GamingMessageHandler(obj); + } + + return await Send(type, msg); + } + } +} diff --git a/FunGame.WebAPI/Program.cs b/FunGame.WebAPI/Program.cs new file mode 100644 index 0000000..1b743ca --- /dev/null +++ b/FunGame.WebAPI/Program.cs @@ -0,0 +1,267 @@ +using System.Net.WebSockets; +using System.Text; +using System.Text.Encodings.Web; +using System.Text.Json.Serialization; +using System.Text.Unicode; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Diagnostics; +using Microsoft.IdentityModel.Tokens; +using Microsoft.OpenApi.Models; +using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.Common.JsonConverter; +using Milimoe.FunGame.Core.Library.Common.Network; +using Milimoe.FunGame.Core.Library.Constant; +using Milimoe.FunGame.Server.Controller; +using Milimoe.FunGame.Server.Model; +using Milimoe.FunGame.Server.Others; +using Milimoe.FunGame.Server.Utility; +using Milimoe.FunGame.WebAPI.Architecture; +using Milimoe.FunGame.WebAPI.Services; + +WebAPIListener listener = new(); + +try +{ + Console.Title = Config.ServerName; + Console.WriteLine(FunGameInfo.GetInfo(Config.FunGameType)); + + ServerHelper.WriteLine("ڶȡļʼ . . ."); + // ʼ˵ + ServerHelper.InitOrderList(); + + // ȡϷģ + if (!Config.GetGameModuleList()) + { + ServerHelper.WriteLine("ƺδװκϷģ飬Ƿȷװǡ"); + } + + // Ƿļ + if (!INIHelper.ExistINIFile()) + { + ServerHelper.WriteLine("δ⵽ļԶļ . . ."); + INIHelper.Init(Config.FunGameType); + ServerHelper.WriteLine("ļFunGame.iniɹ޸ĸļȻ"); + Console.ReadKey(); + return; + } + else + { + ServerHelper.GetServerSettings(); + } + + // + RESTfulAPIListener apiListener = new(); + Singleton.Add(apiListener); + + ServerHelper.WriteLine(" help ȡ quit رշ"); + + // ȫSQLHelper + Config.InitSQLHelper(); + + ServerHelper.WriteLine(" Web API . . ."); + + WebApplicationBuilder builder = WebApplication.CreateBuilder(args); + + // Add services to the container. + builder.Services.AddControllers().AddJsonOptions(options => + { + options.JsonSerializerOptions.WriteIndented = true; + options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Create(UnicodeRanges.All); + options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles; + options.JsonSerializerOptions.Converters.Add(new DateTimeConverter()); + options.JsonSerializerOptions.Converters.Add(new DataTableConverter()); + options.JsonSerializerOptions.Converters.Add(new DataSetConverter()); + options.JsonSerializerOptions.Converters.Add(new UserConverter()); + options.JsonSerializerOptions.Converters.Add(new RoomConverter()); + options.JsonSerializerOptions.Converters.Add(new CharacterConverter()); + options.JsonSerializerOptions.Converters.Add(new MagicResistanceConverter()); + options.JsonSerializerOptions.Converters.Add(new EquipSlotConverter()); + options.JsonSerializerOptions.Converters.Add(new SkillConverter()); + options.JsonSerializerOptions.Converters.Add(new EffectConverter()); + options.JsonSerializerOptions.Converters.Add(new ItemConverter()); + }); + // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle + builder.Services.AddEndpointsApiExplorer(); + builder.Services.AddSwaggerGen(options => + { + options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme + { + Name = "Authorization", + Type = SecuritySchemeType.Http, + Scheme = "Bearer", + BearerFormat = "JWT", + In = ParameterLocation.Header, + Description = " Auth ص BearerToken", + }); + options.AddSecurityRequirement(new OpenApiSecurityRequirement + { + { + new OpenApiSecurityScheme + { + Reference = new OpenApiReference + { + Type = ReferenceType.SecurityScheme, + Id = "Bearer" + } + }, + Array.Empty() + } + }); + }); + // CORS + builder.Services.AddCors(options => + { + options.AddPolicy("AllowSpecificOrigin", policy => + { + policy.AllowAnyOrigin() + .AllowAnyHeader() + .AllowAnyMethod(); + }); + }); + // JWT ֤ + builder.Services.AddScoped(); + builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => + { + options.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuer = true, + ValidateAudience = true, + ValidateLifetime = true, + ValidateIssuerSigningKey = true, + ValidIssuer = builder.Configuration["Jwt:Issuer"], + ValidAudience = builder.Configuration["Jwt:Audience"], + IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? "undefined")) + }; + }); + + WebApplication app = builder.Build(); + + // CORS + app.UseCors("AllowSpecificOrigin"); + + // Configure the HTTP request pipeline. + if (app.Environment.IsDevelopment()) + { + app.UseSwagger(); + app.UseSwaggerUI(); + } + + app.UseHttpsRedirection(); + + app.UseAuthorization(); + + app.MapControllers(); + + app.UseExceptionHandler(errorApp => + { + errorApp.Run(async context => + { + context.Response.StatusCode = 500; + context.Response.ContentType = "application/json"; + IExceptionHandlerFeature? contextFeature = context.Features.Get(); + if (contextFeature != null) + { + await context.Response.WriteAsync(new + { + context.Response.StatusCode, + Message = "Internal Server Error.", + Detailed = contextFeature.Error.Message + }.ToString() ?? ""); + } + }); + }); + + // WebSockets м + WebSocketOptions webSocketOptions = new() + { + KeepAliveInterval = TimeSpan.FromMinutes(2) // WebSocket ı + }; + app.UseWebSockets(webSocketOptions); + + // ·ɵ WebSocket + app.Map("/ws", WebSocketConnectionHandler); + + // ʼ + listener.BannedList.AddRange(Config.ServerBannedList); + + if (Config.ServerNotice != "") + Console.WriteLine("\n\n********** **********\n\n" + Config.ServerNotice + "\n"); + else + Console.WriteLine("޷ȡ"); + + Task order = Task.Factory.StartNew(GetConsoleOrder); + + app.Run(); +} +catch (Exception e) +{ + ServerHelper.Error(e); +} + +async Task GetConsoleOrder() +{ + while (true) + { + string order = Console.ReadLine() ?? ""; + ServerHelper.Type(); + if (order != "") + { + order = order.ToLower(); + switch (order) + { + default: + await ConsoleModel.Order(listener, order); + break; + } + } + } +} + +async Task WebSocketConnectionHandler(HttpContext context) +{ + string clientip = ""; + try + { + if (context.WebSockets.IsWebSocketRequest) + { + WebSocket instance = await context.WebSockets.AcceptWebSocketAsync(); + clientip = context.Connection.RemoteIpAddress?.ToString() + ":" + context.Connection.RemotePort; + + Guid token = Guid.NewGuid(); + ServerWebSocket socket = new(listener, instance, clientip, clientip, token); + Config.ConnectingPlayerCount++; + bool isConnected = false; + bool isDebugMode = false; + + // ʼͻ + IEnumerable objs = []; + while (!objs.Any(o => o.SocketType == SocketMessageType.Connect)) + { + objs = objs.Union(await socket.ReceiveAsync()); + } + (isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs.Where(o => o.SocketType == SocketMessageType.Connect)); + if (isConnected) + { + ServerModel ClientModel = new(listener, socket, isDebugMode); + ClientModel.SetClientName(clientip); + await ClientModel.Start(); + } + else + { + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " ʧܡ", InvokeMessageType.Core); + await socket.CloseAsync(); + } + Config.ConnectingPlayerCount--; + } + else + { + context.Response.StatusCode = 400; + } + } + catch (Exception e) + { + if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " жӣ", InvokeMessageType.Core); + ServerHelper.Error(e); + } +} diff --git a/FunGame.WebAPI/Properties/launchSettings.json b/FunGame.WebAPI/Properties/launchSettings.json new file mode 100644 index 0000000..5c47465 --- /dev/null +++ b/FunGame.WebAPI/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:45590", + "sslPort": 44356 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "http://localhost:5117", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "swagger", + "applicationUrl": "https://localhost:7162;http://localhost:5117", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "swagger", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/FunGame.WebAPI/Services/JWTService.cs b/FunGame.WebAPI/Services/JWTService.cs new file mode 100644 index 0000000..0068985 --- /dev/null +++ b/FunGame.WebAPI/Services/JWTService.cs @@ -0,0 +1,33 @@ +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Microsoft.IdentityModel.Tokens; +using Milimoe.FunGame.Core.Library.Constant; + +namespace Milimoe.FunGame.WebAPI.Services +{ + public class JWTService(IConfiguration configuration) + { + public string GenerateToken(string username) + { + // 创建一个包含用户信息的声明 + Claim[] claims = [ + new Claim(JwtRegisteredClaimNames.Sub, username), + new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) + ]; + + // 获取密钥和发行者 + SymmetricSecurityKey key = new(General.DefaultEncoding.GetBytes(configuration["Jwt:Key"] ?? "undefined")); + SigningCredentials creds = new(key, SecurityAlgorithms.HmacSha256); + + JwtSecurityToken token = new( + issuer: configuration["Jwt:Issuer"], + audience: configuration["Jwt:Audience"], + claims: claims, + expires: DateTime.Now.AddMinutes(30), // 设置过期时间 + signingCredentials: creds + ); + + return new JwtSecurityTokenHandler().WriteToken(token); + } + } +} diff --git a/FunGame.WebAPI/appsettings.Development.json b/FunGame.WebAPI/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/FunGame.WebAPI/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/FunGame.WebAPI/appsettings.json b/FunGame.WebAPI/appsettings.json new file mode 100644 index 0000000..3701d01 --- /dev/null +++ b/FunGame.WebAPI/appsettings.json @@ -0,0 +1,19 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "Kestrel": { + "EndpointDefaults": { + "Protocols": "Http1AndHttp2AndHttp3" + } + }, + "Jwt": { + "Key": "166afec8ff6e0c3a647c7230294ea10be39d5d217f37aa5195f795017403da730ce6313790335b4975d7387c14aaa06c52d1cd90b5ef47d1831b6d7d524a12bf", + "Issuer": "FunGame", + "Audience": "FunGame Web API" + } +} diff --git a/FunGameServer.sln b/FunGameServer.sln index 7015657..9d1875c 100644 --- a/FunGameServer.sln +++ b/FunGameServer.sln @@ -7,6 +7,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunGame.Server", "FunGame.S EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunGame.Implement", "FunGame.Implement\FunGame.Implement.csproj", "{F5BACA36-3DE2-450A-8518-E5DC29991875}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "FunGame.WebAPI", "FunGame.WebAPI\FunGame.WebAPI.csproj", "{F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -21,6 +23,10 @@ Global {F5BACA36-3DE2-450A-8518-E5DC29991875}.Debug|Any CPU.Build.0 = Debug|Any CPU {F5BACA36-3DE2-450A-8518-E5DC29991875}.Release|Any CPU.ActiveCfg = Release|Any CPU {F5BACA36-3DE2-450A-8518-E5DC29991875}.Release|Any CPU.Build.0 = Release|Any CPU + {F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F4C6F3E8-84EA-49B4-99B7-A886C56C44F2}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE