diff --git a/FunGame.Server/Controllers/DataRequestController.cs b/FunGame.Server/Controllers/DataRequestController.cs index 51ef0ee..533d75a 100644 --- a/FunGame.Server/Controllers/DataRequestController.cs +++ b/FunGame.Server/Controllers/DataRequestController.cs @@ -239,8 +239,8 @@ namespace Milimoe.FunGame.Server.Controller } else eventArgs.Success = false; - FunGameSystem.ServerPluginLoader?.OnBeforeLogoutEvent(this, eventArgs); - FunGameSystem.WebAPIPluginLoader?.OnBeforeLogoutEvent(this, eventArgs); + FunGameSystem.ServerPluginLoader?.OnAfterLogoutEvent(this, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterLogoutEvent(this, eventArgs); } resultData.Add("msg", msg); resultData.Add("key", key); diff --git a/FunGame.Server/Main.cs b/FunGame.Server/Main.cs index 4cdb7cd..f106fd0 100644 --- a/FunGame.Server/Main.cs +++ b/FunGame.Server/Main.cs @@ -1,4 +1,5 @@ using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Library.Common.Event; using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Server.Controller; @@ -168,21 +169,34 @@ void StartServerListening() bool isDebugMode = false; // 开始处理客户端连接请求 + ConnectEventArgs eventArgs = new(clientip, Config.ServerPort); + FunGameSystem.ServerPluginLoader?.OnBeforeConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnBeforeConnectEvent(socket, eventArgs); SocketObject[] objs = socket.Receive(); (isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs); + eventArgs.Success = isConnected; if (isConnected) { + eventArgs.ConnectResult = ConnectResult.Success; + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); ServerModel ClientModel = new(listener, socket, isDebugMode); ClientModel.SetClientName(clientip); Task t = Task.Run(ClientModel.Start); } else { + eventArgs.ConnectResult = ConnectResult.ConnectFailed; + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core); } Config.ConnectingPlayerCount--; }).OnError(e => { + ConnectEventArgs eventArgs = new(clientip, Config.ServerPort, ConnectResult.CanNotConnect); + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core); ServerHelper.Error(e); @@ -232,26 +246,39 @@ void StartServerListening() bool isDebugMode = false; // 开始处理客户端连接请求 + ConnectEventArgs eventArgs = new(clientip, Config.ServerPort); + FunGameSystem.ServerPluginLoader?.OnBeforeConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnBeforeConnectEvent(socket, eventArgs); IEnumerable objs = []; while (!objs.Any(o => o.SocketType == SocketMessageType.Connect)) { objs = await socket.ReceiveAsync(); } (isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs.Where(o => o.SocketType == SocketMessageType.Connect)); + eventArgs.Success = isConnected; if (isConnected) { + eventArgs.ConnectResult = ConnectResult.Success; + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); ServerModel ClientModel = new(listener, socket, isDebugMode); ClientModel.SetClientName(clientip); Task t = Task.Run(ClientModel.Start); } else { + eventArgs.ConnectResult = ConnectResult.ConnectFailed; + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core); await socket.CloseAsync(); } Config.ConnectingPlayerCount--; }).OnError(e => { + ConnectEventArgs eventArgs = new(clientip, Config.ServerPort, ConnectResult.CanNotConnect); + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core); ServerHelper.Error(e); diff --git a/FunGame.Server/Models/ConsoleModel.cs b/FunGame.Server/Models/ConsoleModel.cs index 1adeb35..175b485 100644 --- a/FunGame.Server/Models/ConsoleModel.cs +++ b/FunGame.Server/Models/ConsoleModel.cs @@ -51,6 +51,31 @@ namespace Milimoe.FunGame.Server.Model case OrderDictionary.ShowUsers2: ShowUsers(server); break; + case OrderDictionary.ReloadAddons: + FunGameSystem.HotReloadServerPlugins(); + FunGameSystem.HotReloadWebAPIPlugins(); + FunGameSystem.HotReloadGameModuleList(); + break; + case OrderDictionary.ReloadPlugins1: + FunGameSystem.HotReloadServerPlugins(); + FunGameSystem.HotReloadWebAPIPlugins(); + break; + case OrderDictionary.ReloadPlugins2: + FunGameSystem.HotReloadServerPlugins(); + FunGameSystem.HotReloadWebAPIPlugins(); + break; + case OrderDictionary.ReloadPlugins3: + FunGameSystem.HotReloadServerPlugins(); + break; + case OrderDictionary.ReloadPlugins4: + FunGameSystem.HotReloadWebAPIPlugins(); + break; + case OrderDictionary.ReloadModules1: + FunGameSystem.HotReloadGameModuleList(); + break; + case OrderDictionary.ReloadModules2: + FunGameSystem.HotReloadGameModuleList(); + break; default: break; } diff --git a/FunGame.Server/Models/ServerModel.cs b/FunGame.Server/Models/ServerModel.cs index 28d5703..59db27e 100644 --- a/FunGame.Server/Models/ServerModel.cs +++ b/FunGame.Server/Models/ServerModel.cs @@ -664,6 +664,9 @@ namespace Milimoe.FunGame.Server.Model { try { + GeneralEventArgs eventArgs = new(); + FunGameSystem.ServerPluginLoader?.OnAfterDisconnectEvent(this, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterDisconnectEvent(this, eventArgs); await Socket.CloseAsync(); _running = false; Listener.ClientList.Remove(ClientName); diff --git a/FunGame.Server/Others/Config.cs b/FunGame.Server/Others/Config.cs index 8b5ed8a..f6ce468 100644 --- a/FunGame.Server/Others/Config.cs +++ b/FunGame.Server/Others/Config.cs @@ -1,4 +1,5 @@ using System.Text; +using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Library.Constant; namespace Milimoe.FunGame.Server.Others @@ -10,6 +11,19 @@ namespace Milimoe.FunGame.Server.Others /// public static bool AspNetCore { get; set; } = false; + /// + /// 使用可热更新的加载项模式 + /// + public static bool UseHotLoadAddons + { + get + { + _useHotLoadAddon ??= Convert.ToBoolean(INIHelper.ReadINI("Console", "UseHotLoadAddons")); + return _useHotLoadAddon.Value; + } + } + private static bool? _useHotLoadAddon = null; + /// /// 日志级别 /// diff --git a/FunGame.Server/Others/OrderDictionary.cs b/FunGame.Server/Others/OrderDictionary.cs index 2761b1a..60c4477 100644 --- a/FunGame.Server/Others/OrderDictionary.cs +++ b/FunGame.Server/Others/OrderDictionary.cs @@ -7,16 +7,23 @@ public const string Exit = "exit"; public const string Close = "close"; public const string Restart = "restart"; - public const string AddBanned1 = "ban -add"; + public const string AddBanned1 = "ban add"; public const string AddBanned2 = "ban -a"; - public const string RemoveBanned1 = "ban -remove"; + public const string RemoveBanned1 = "ban remove"; public const string RemoveBanned2 = "ban -r"; public const string Kick = "kick"; public const string Logout = "logout"; public const string ShowList = "showlist"; - public const string ShowClients1 = "showclients"; + public const string ShowClients1 = "show clients"; public const string ShowClients2 = "clients"; - public const string ShowUsers1 = "showusers"; + public const string ShowUsers1 = "show users"; public const string ShowUsers2 = "users"; + public const string ReloadAddons = "reload addons"; + public const string ReloadPlugins1 = "reload plugins"; + public const string ReloadPlugins2 = "reload -p"; + public const string ReloadPlugins3 = "reload -sp"; + public const string ReloadPlugins4 = "reload -wp"; + public const string ReloadModules1 = "reload modules"; + public const string ReloadModules2 = "reload -m"; } } diff --git a/FunGame.Server/Services/FunGameSystem.cs b/FunGame.Server/Services/FunGameSystem.cs index 9cdf474..be6fe9a 100644 --- a/FunGame.Server/Services/FunGameSystem.cs +++ b/FunGame.Server/Services/FunGameSystem.cs @@ -159,7 +159,34 @@ namespace Milimoe.FunGame.Server.Services // 读取modules目录下的模组 try { - GameModuleLoader = GameModuleLoader.LoadGameModules(Config.FunGameType, delegates); + if (Config.UseHotLoadAddons && GameModuleLoader != null) + { + GameModuleLoader.HotReload(Config.FunGameType, delegates); + } + else + { + GameModuleLoader = Config.UseHotLoadAddons ? GameModuleLoader.LoadGameModulesByHotLoadMode(Config.FunGameType, delegates) : GameModuleLoader.LoadGameModules(Config.FunGameType, delegates); + } + foreach (GameMap map in GameModuleLoader.Maps.Values) + { + supported.Add(map.Name); + ServerHelper.WriteLine("GameMap Loaded -> " + map.Name, InvokeMessageType.Core); + } + foreach (CharacterModule module in GameModuleLoader.Characters.Values) + { + supported.Add(module.Name); + ServerHelper.WriteLine("CharacterModule Loaded -> " + module.Name, InvokeMessageType.Core); + } + foreach (SkillModule module in GameModuleLoader.Skills.Values) + { + supported.Add(module.Name); + ServerHelper.WriteLine("SkillModule Loaded -> " + module.Name, InvokeMessageType.Core); + } + foreach (ItemModule module in GameModuleLoader.Items.Values) + { + supported.Add(module.Name); + ServerHelper.WriteLine("ItemModule Loaded -> " + module.Name, InvokeMessageType.Core); + } foreach (GameModuleServer module in GameModuleLoader.ModuleServers.Values) { try @@ -173,7 +200,7 @@ namespace Milimoe.FunGame.Server.Services } if (check) { - if (!module.IsAnonymous) supported.Add(module.Name); + supported.Add(module.Name); ServerHelper.WriteLine("GameModule Loaded -> " + module.Name, InvokeMessageType.Core); } } @@ -193,6 +220,31 @@ namespace Milimoe.FunGame.Server.Services return Config.GameModuleSupported.Length > 0; } + /// + /// 热更新游戏模组 + /// + /// + public static bool HotReloadGameModuleList() + { + if (!Config.UseHotLoadAddons) + { + return false; + } + + if (GameModuleLoader != null) + { + GameModuleLoader.Modules.Clear(); + GameModuleLoader.ModuleServers.Clear(); + GameModuleLoader.Maps.Clear(); + GameModuleLoader.Characters.Clear(); + GameModuleLoader.Skills.Clear(); + GameModuleLoader.Items.Clear(); + GameModuleLoader = null; + } + + return GetGameModuleList(); + } + /// /// 加载服务器插件 /// @@ -204,7 +256,14 @@ namespace Milimoe.FunGame.Server.Services try { // 读取plugins目录下的插件 - ServerPluginLoader = ServerPluginLoader.LoadPlugins(delegates); + if (Config.UseHotLoadAddons && ServerPluginLoader != null) + { + ServerPluginLoader.HotReload(delegates); + } + else + { + ServerPluginLoader = Config.UseHotLoadAddons ? ServerPluginLoader.LoadPluginsByHotLoadMode(delegates) : ServerPluginLoader.LoadPlugins(delegates); + } foreach (ServerPlugin plugin in ServerPluginLoader.Plugins.Values) { ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core); @@ -227,7 +286,14 @@ namespace Milimoe.FunGame.Server.Services try { // 读取plugins目录下的插件 - WebAPIPluginLoader = WebAPIPluginLoader.LoadPlugins(delegates, otherobjs); + if (Config.UseHotLoadAddons && WebAPIPluginLoader != null) + { + WebAPIPluginLoader.HotReload(delegates, otherobjs); + } + else + { + WebAPIPluginLoader = Config.UseHotLoadAddons ? WebAPIPluginLoader.LoadPluginsByHotLoadMode(delegates, otherobjs) : WebAPIPluginLoader.LoadPlugins(delegates, otherobjs); + } foreach (WebAPIPlugin plugin in WebAPIPluginLoader.Plugins.Values) { ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core); @@ -239,6 +305,36 @@ namespace Milimoe.FunGame.Server.Services } } + /// + /// 热更新服务器插件 + /// + /// + public static bool HotReloadServerPlugins() + { + if (!Config.UseHotLoadAddons) + { + return false; + } + + GetServerPlugins(); + return (ServerPluginLoader?.Plugins.Count ?? 0) > 0; + } + + /// + /// 热更新 Web API 插件 + /// + /// + public static bool HotReloadWebAPIPlugins() + { + if (!Config.UseHotLoadAddons) + { + return false; + } + + GetWebAPIPlugins(); + return (WebAPIPluginLoader?.Plugins.Count ?? 0) > 0; + } + /// /// Web API 启动完成回调 /// diff --git a/FunGame.Server/Services/General.cs b/FunGame.Server/Services/General.cs index 1e913b2..bee0ece 100644 --- a/FunGame.Server/Services/General.cs +++ b/FunGame.Server/Services/General.cs @@ -4,6 +4,7 @@ using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Server.Others; +using TaskScheduler = Milimoe.FunGame.Core.Api.Utility.TaskScheduler; namespace Milimoe.FunGame.Server.Services { @@ -71,6 +72,12 @@ namespace Milimoe.FunGame.Server.Services public static void Error(Exception e) { Console.WriteLine("\r" + GetPrefix(InvokeMessageType.Error, LogLevel.Error) + e.Message + "\n" + e.StackTrace); + if (e.InnerException != null) + { + Error(e.InnerException); + TXTHelper.AppendErrorLog(e); + return; + } Type(); TXTHelper.AppendErrorLog(e); } @@ -227,6 +234,11 @@ namespace Milimoe.FunGame.Server.Services if (MaxConnectFailed != null) Config.MaxConnectionFaileds = (int)MaxConnectFailed; } WriteLine($"当前输出的日志级别为:{Config.LogLevelValue}", useLevel: false); + if (Config.UseHotLoadAddons) + { + WriteLine($"已启用可热更新加载项的加载模式", useLevel: false); + TaskScheduler.StartCleanUnusedAddonContexts(Error); + } } catch (Exception e) { diff --git a/FunGame.WebAPI/Program.cs b/FunGame.WebAPI/Program.cs index ed32872..b3edaa0 100644 --- a/FunGame.WebAPI/Program.cs +++ b/FunGame.WebAPI/Program.cs @@ -15,6 +15,7 @@ using Milimoe.FunGame; using Milimoe.FunGame.Core.Api.Transmittal; using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Library.Common.Addon; +using Milimoe.FunGame.Core.Library.Common.Event; using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Server.Controller; @@ -320,28 +321,50 @@ async Task WebSocketConnectionHandler(HttpContext context) Guid token = Guid.NewGuid(); ServerWebSocket socket = new(listener, instance, clientip, clientip, token); Config.ConnectingPlayerCount++; - bool isConnected = false; - bool isDebugMode = false; + try + { + bool isConnected = false; + bool isDebugMode = false; - // 开始处理客户端连接请求 - IEnumerable objs = []; - while (!objs.Any(o => o.SocketType == SocketMessageType.Connect)) - { - objs = await socket.ReceiveAsync(); + // 开始处理客户端连接请求 + ConnectEventArgs eventArgs = new(clientip, Config.ServerPort); + FunGameSystem.ServerPluginLoader?.OnBeforeConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnBeforeConnectEvent(socket, eventArgs); + IEnumerable objs = []; + while (!objs.Any(o => o.SocketType == SocketMessageType.Connect)) + { + objs = await socket.ReceiveAsync(); + } + (isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs.Where(o => o.SocketType == SocketMessageType.Connect)); + eventArgs.Success = isConnected; + if (isConnected) + { + eventArgs.ConnectResult = ConnectResult.Success; + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + ServerModel ClientModel = new(listener, socket, isDebugMode); + ClientModel.SetClientName(clientip); + await ClientModel.Start(); + } + else + { + eventArgs.ConnectResult = ConnectResult.ConnectFailed; + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(socket, eventArgs); + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core); + await socket.CloseAsync(); + } + Config.ConnectingPlayerCount--; } - (isConnected, isDebugMode) = await ConnectController.Connect(listener, socket, token, clientip, objs.Where(o => o.SocketType == SocketMessageType.Connect)); - if (isConnected) + catch (Exception e) { - ServerModel ClientModel = new(listener, socket, isDebugMode); - ClientModel.SetClientName(clientip); - await ClientModel.Start(); + ConnectEventArgs eventArgs = new(clientip, Config.ServerPort, ConnectResult.CanNotConnect); + FunGameSystem.ServerPluginLoader?.OnAfterConnectEvent(context, eventArgs); + FunGameSystem.WebAPIPluginLoader?.OnAfterConnectEvent(context, eventArgs); + if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; + ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core); + ServerHelper.Error(e); } - else - { - ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 连接失败。", InvokeMessageType.Core); - await socket.CloseAsync(); - } - Config.ConnectingPlayerCount--; } else { @@ -350,8 +373,6 @@ async Task WebSocketConnectionHandler(HttpContext context) } catch (Exception e) { - if (--Config.ConnectingPlayerCount < 0) Config.ConnectingPlayerCount = 0; - ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 中断连接!", InvokeMessageType.Core); ServerHelper.Error(e); } } diff --git a/README.md b/README.md index dc9b2be..2fee9c0 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # 项目简介 -FunGameServer 是 [FunGame](https://github.com/project-redbud/FunGame-Core) 的服务器端实现,基于 ASP.NET Core Web API,轻量、高性能、跨平台。 +FunGameServer 是 [FunGame](https://github.com/project-redbud/FunGame-Core) 的服务器端实现,基于 ASP.NET Core Web API,高性能且跨平台。 它支持多种服务模式,使得服务器端的扩展和集成更加灵活: