使用 WebAPIPlugin 接入模组控制器 (#38)

* WebAPI 现在可以添加插件了

* 添加 接收服务器控制台的输入

* 现在可以加载服务器插件

* 添加单例 SQLHelper 和 MailSender

* SQL 服务守护

* 添加 wwwroot 支持静态访问

* 添加使用默认文档,以便可以访问 index.html
This commit is contained in:
milimoe 2024-10-30 10:24:03 +08:00 committed by GitHub
parent 2f71d015b2
commit c060c06d1c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 219 additions and 69 deletions

1
.gitignore vendored
View File

@ -365,3 +365,4 @@ FodyWeavers.xsd
#FunGame.Implement #FunGame.Implement
FunGame.Implement/*.cs FunGame.Implement/*.cs
FunGame.Implement/Implement/*.cs FunGame.Implement/Implement/*.cs
/FunGame.WebAPI/wwwroot/*

View File

@ -65,12 +65,21 @@ void StartServer()
// 初始化命令菜单 // 初始化命令菜单
ServerHelper.InitOrderList(); ServerHelper.InitOrderList();
// 创建全局SQLHelper
Config.InitSQLHelper();
// 创建全局MailSender
Config.InitMailSender();
// 读取游戏模组 // 读取游戏模组
if (!Config.GetGameModuleList()) if (!Config.GetGameModuleList())
{ {
ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。"); ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。");
} }
// 读取Server插件
Config.GetServerPlugins();
// 检查是否存在配置文件 // 检查是否存在配置文件
if (!INIHelper.ExistINIFile()) if (!INIHelper.ExistINIFile())
{ {
@ -86,9 +95,6 @@ void StartServer()
} }
ServerHelper.WriteLine("请输入 help 来获取帮助,输入 quit 关闭服务器。"); ServerHelper.WriteLine("请输入 help 来获取帮助,输入 quit 关闭服务器。");
// 创建全局SQLHelper
Config.InitSQLHelper();
// 使用Socket还是WebSocket // 使用Socket还是WebSocket
bool useWebSocket = Config.UseWebSocket; bool useWebSocket = Config.UseWebSocket;

View File

@ -1,4 +1,5 @@
using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Others;
using Milimoe.FunGame.Server.Utility; using Milimoe.FunGame.Server.Utility;
@ -47,6 +48,23 @@ namespace Milimoe.FunGame.Server.Model
case OrderDictionary.Help: case OrderDictionary.Help:
ShowHelp(); ShowHelp();
break; break;
default:
break;
}
// 广播到插件
if (Config.ServerPluginLoader != null)
{
foreach (ServerPlugin plugin in Config.ServerPluginLoader.Plugins.Values)
{
plugin.ProcessInput(order);
}
}
if (Config.WebAPIPluginLoader != null)
{
foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values)
{
plugin.ProcessInput(order);
}
} }
} }
catch (Exception e) catch (Exception e)

View File

@ -49,10 +49,11 @@ namespace Milimoe.FunGame.Server.Model
Socket = socket; Socket = socket;
DataRequestController = new(this); DataRequestController = new(this);
IsDebugMode = isDebugMode; IsDebugMode = isDebugMode;
if (Config.SQLMode == SQLMode.MySQL) _sqlHelper = new MySQLHelper(this); if (Config.SQLMode != SQLMode.None)
else if (Config.SQLMode == SQLMode.SQLite) _sqlHelper = Config.SQLHelper; {
else ServerHelper.WriteLine("SQL 服务处于关闭状态", InvokeMessageType.Warning); _sqlHelper = Config.SQLHelper;
_mailer = SmtpHelper.GetMailSender(); }
_mailer = Config.MailSender;
} }
public virtual async Task<bool> SocketMessageHandler(ISocketMessageProcessor socket, SocketObject obj) public virtual async Task<bool> SocketMessageHandler(ISocketMessageProcessor socket, SocketObject obj)
@ -282,7 +283,6 @@ namespace Milimoe.FunGame.Server.Model
public async Task Start() public async Task Start()
{ {
TaskUtility.NewTask(CreatePeriodicalQuerier);
await CreateStreamReader(); await CreateStreamReader();
} }
@ -496,37 +496,10 @@ namespace Milimoe.FunGame.Server.Model
} }
} }
protected async Task CreatePeriodicalQuerier()
{
await Task.Delay(20);
ServerHelper.WriteLine("Creating: PeriodicalQuerier -> " + GetClientName() + " ...OK");
while (Running)
{
// 每两小时触发一次SQL服务器的心跳查询防止SQL服务器掉线
try
{
await Task.Delay(2 * 1000 * 3600);
SQLHelper?.ExecuteDataSet(UserQuery.Select_IsExistUsername(_username));
}
catch (Exception e)
{
ServerHelper.Error(e);
RemoveUser();
await Close();
ServerHelper.WriteLine(GetClientName() + " Error -> Socket is Closed.");
ServerHelper.WriteLine(GetClientName() + " Close -> StringStream is Closed.");
}
}
}
protected async Task Close() protected async Task Close()
{ {
try try
{ {
SQLHelper?.Close();
_sqlHelper = null;
MailSender?.Dispose();
_mailer = null;
await Socket.CloseAsync(); await Socket.CloseAsync();
_running = false; _running = false;
Listener.ClientList.Remove(ClientName); Listener.ClientList.Remove(ClientName);

View File

@ -129,6 +129,16 @@ namespace Milimoe.FunGame.Server.Others
/// </summary> /// </summary>
public static GameModuleLoader? GameModuleLoader { get; set; } public static GameModuleLoader? GameModuleLoader { get; set; }
/// <summary>
/// Server插件
/// </summary>
public static ServerPluginLoader? ServerPluginLoader { get; set; }
/// <summary>
/// Web API插件
/// </summary>
public static WebAPIPluginLoader? WebAPIPluginLoader { get; set; }
/// <summary> /// <summary>
/// 未Loadmodules时此属性表示至少需要安装的模组 /// 未Loadmodules时此属性表示至少需要安装的模组
/// </summary> /// </summary>
@ -146,7 +156,13 @@ namespace Milimoe.FunGame.Server.Others
} }
} }
/// <summary>
/// 全局邮件发送器
/// </summary>
public static MailSender? MailSender => _MailSender;
private static SQLHelper? _SQLHelper; private static SQLHelper? _SQLHelper;
private static MailSender? _MailSender;
/// <summary> /// <summary>
/// 初始化数据库连接器 /// 初始化数据库连接器
@ -162,17 +178,13 @@ namespace Milimoe.FunGame.Server.Others
_SQLHelper = new MySQLHelper("", false); _SQLHelper = new MySQLHelper("", false);
if (((MySQLHelper)_SQLHelper).Connection != null) if (((MySQLHelper)_SQLHelper).Connection != null)
{ {
SQLMode = _SQLHelper.Mode; AfterCreateSQLService(_SQLHelper);
ServerLogin();
ClearRoomList();
} }
} }
else if (INIHelper.ReadINI("SQLite", "UseSQLite").Trim() == "true") else if (INIHelper.ReadINI("SQLite", "UseSQLite").Trim() == "true")
{ {
_SQLHelper = new SQLiteHelper(); _SQLHelper = new SQLiteHelper();
SQLMode = _SQLHelper.Mode; AfterCreateSQLService(_SQLHelper);
ServerLogin();
ClearRoomList();
} }
else else
{ {
@ -187,16 +199,39 @@ namespace Milimoe.FunGame.Server.Others
} }
} }
/// <summary>
/// 初始化邮件发送器
/// </summary>
public static void InitMailSender()
{
try
{
_MailSender = SmtpHelper.GetMailSender();
}
catch (Exception e)
{
ServerHelper.Error(e);
}
if (_MailSender != null)
{
Singleton.AddOrUpdate(_MailSender);
}
}
public static bool GetGameModuleList() public static bool GetGameModuleList()
{ {
List<string> supported = []; List<string> supported = [];
// 构建AddonController // 构建AddonController
Hashtable delegates = []; Dictionary<string, object> delegates = [];
delegates.Add("WriteLine", new Action<string>(msg => ServerHelper.WriteLine(msg, InvokeMessageType.GameModule))); delegates.Add("WriteLine", new Action<string>(msg => ServerHelper.WriteLine(msg, InvokeMessageType.GameModule)));
delegates.Add("Error", new Action<Exception>(ServerHelper.Error)); delegates.Add("Error", new Action<Exception>(ServerHelper.Error));
// 读取modules目录下的模组 // 读取modules目录下的模组
try
{
GameModuleLoader = GameModuleLoader.LoadGameModules(FunGameType, delegates); GameModuleLoader = GameModuleLoader.LoadGameModules(FunGameType, delegates);
foreach (GameModuleServer module in GameModuleLoader.ModuleServers.Values) foreach (GameModuleServer module in GameModuleLoader.ModuleServers.Values)
{
try
{ {
bool check = true; bool check = true;
// 检查模组是否有相对应的地图 // 检查模组是否有相对应的地图
@ -210,6 +245,16 @@ namespace Milimoe.FunGame.Server.Others
supported.Add(module.Name); supported.Add(module.Name);
} }
} }
catch (Exception e)
{
ServerHelper.Error(e);
}
}
}
catch (Exception e2)
{
ServerHelper.Error(e2);
}
// 设置全局 // 设置全局
GameModuleSupported = supported.Distinct().ToArray(); GameModuleSupported = supported.Distinct().ToArray();
foreach (string modename in GameModuleSupported) foreach (string modename in GameModuleSupported)
@ -220,6 +265,46 @@ namespace Milimoe.FunGame.Server.Others
return GameModuleSupported.Length > 0; return GameModuleSupported.Length > 0;
} }
public static void GetServerPlugins()
{
Dictionary<string, object> delegates = [];
delegates.Add("WriteLine", new Action<string>(msg => ServerHelper.WriteLine(msg, InvokeMessageType.Plugin)));
delegates.Add("Error", new Action<Exception>(ServerHelper.Error));
try
{
// 读取plugins目录下的插件
ServerPluginLoader = ServerPluginLoader.LoadPlugins(delegates);
foreach (ServerPlugin plugin in ServerPluginLoader.Plugins.Values)
{
ServerHelper.WriteLine("Loaded: " + plugin.Name, InvokeMessageType.Plugin);
}
}
catch (Exception e)
{
ServerHelper.Error(e);
}
}
public static void GetWebAPIPlugins()
{
Dictionary<string, object> delegates = [];
delegates.Add("WriteLine", new Action<string>(msg => ServerHelper.WriteLine(msg, InvokeMessageType.Plugin)));
delegates.Add("Error", new Action<Exception>(ServerHelper.Error));
try
{
// 读取plugins目录下的插件
WebAPIPluginLoader = WebAPIPluginLoader.LoadPlugins(delegates);
foreach (WebAPIPlugin plugin in WebAPIPluginLoader.Plugins.Values)
{
ServerHelper.WriteLine("Loaded: " + plugin.Name, InvokeMessageType.Plugin);
}
}
catch (Exception e)
{
ServerHelper.Error(e);
}
}
/// <summary> /// <summary>
/// 服务器启动登记 /// 服务器启动登记
/// </summary> /// </summary>
@ -241,6 +326,30 @@ namespace Milimoe.FunGame.Server.Others
SQLHelper.Execute(RoomQuery.Delete_Rooms()); SQLHelper.Execute(RoomQuery.Delete_Rooms());
} }
} }
public static void AfterCreateSQLService(SQLHelper sqlHelper)
{
SQLMode = sqlHelper.Mode;
Singleton.AddOrUpdate(sqlHelper, true);
ServerLogin();
ClearRoomList();
Task t = Task.Run(async () =>
{
while (true)
{
// 每两小时触发一次SQL服务器的心跳查询防止SQL服务器掉线
try
{
await Task.Delay(2 * 1000 * 3600);
SQLHelper?.ExecuteDataSet(ServerLoginLogs.Select_GetLastLoginTime());
}
catch (Exception e)
{
ServerHelper.Error(e);
}
}
});
}
} }
/// <summary> /// <summary>

View File

@ -59,8 +59,9 @@ namespace Milimoe.FunGame.Server.Utility
public static void Error(Exception e) public static void Error(Exception e)
{ {
Console.Write("\r" + GetPrefix(InvokeMessageType.Error) + e.Message + "\n" + e.StackTrace + "\n\r> "); Console.WriteLine("\r" + GetPrefix(InvokeMessageType.Error) + e.Message + "\n" + e.StackTrace);
Console.ResetColor(); Console.ResetColor();
Console.Write("\r> ");
} }
public static void Write(string msg, InvokeMessageType type = InvokeMessageType.System) public static void Write(string msg, InvokeMessageType type = InvokeMessageType.System)
@ -71,8 +72,9 @@ namespace Milimoe.FunGame.Server.Utility
public static void WriteLine(string msg, InvokeMessageType type = InvokeMessageType.System) public static void WriteLine(string msg, InvokeMessageType type = InvokeMessageType.System)
{ {
if (msg.Trim() != "") Console.Write("\r" + GetPrefix(type) + msg + "\n\r> "); if (msg.Trim() != "") Console.WriteLine("\r" + GetPrefix(type) + msg);
Console.ResetColor(); Console.ResetColor();
Console.Write("\r> ");
} }
public static void Type() public static void Type()
@ -211,7 +213,11 @@ namespace Milimoe.FunGame.Server.Utility
SmtpPort = Port; SmtpPort = Port;
if (bool.TryParse(INIHelper.ReadINI("Mailer", "OpenSSL").ToLower(), out bool SSL)) if (bool.TryParse(INIHelper.ReadINI("Mailer", "OpenSSL").ToLower(), out bool SSL))
OpenSSL = SSL; OpenSSL = SSL;
if (SmtpPort > 0) return new MailSender(SenderMailAddress, SenderName, SenderPassword, SmtpHost, SmtpPort, OpenSSL); if (SmtpPort > 0)
{
ServerHelper.WriteLine("SMTP 服务已启动!");
return new MailSender(SenderMailAddress, SenderName, SenderPassword, SmtpHost, SmtpPort, OpenSSL);
}
} }
} }
ServerHelper.WriteLine("SMTP 服务处于关闭状态", InvokeMessageType.Warning); ServerHelper.WriteLine("SMTP 服务处于关闭状态", InvokeMessageType.Warning);

View File

@ -5,6 +5,8 @@ namespace Milimoe.FunGame.WebAPI.Architecture
{ {
public class RESTfulAPIListener : ISocketListener<RESTfulAPI> public class RESTfulAPIListener : ISocketListener<RESTfulAPI>
{ {
public static RESTfulAPIListener? Instance { get; set; } = null;
public string Name => "RESTfulAPIListener"; public string Name => "RESTfulAPIListener";
public ConcurrentModelList<IServerModel> ClientList { get; } = []; public ConcurrentModelList<IServerModel> ClientList { get; } = [];

View File

@ -1,6 +1,5 @@
using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.WebAPI.Architecture; using Milimoe.FunGame.WebAPI.Architecture;
@ -22,7 +21,7 @@ namespace Milimoe.FunGame.WebAPI.Controllers
{ {
try try
{ {
RESTfulAPIListener? apiListener = Singleton.Get<RESTfulAPIListener>(); RESTfulAPIListener? apiListener = RESTfulAPIListener.Instance;
if (apiListener != null && apiListener.UserList.ContainsKey(username)) if (apiListener != null && apiListener.UserList.ContainsKey(username))
{ {
RESTfulAPIModel model = (RESTfulAPIModel)apiListener.UserList[username]; RESTfulAPIModel model = (RESTfulAPIModel)apiListener.UserList[username];

View File

@ -22,7 +22,7 @@ namespace Milimoe.FunGame.WebAPI.Controllers
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 通过 RESTful API 连接至服务器,正在登录 . . .", Core.Library.Constant.InvokeMessageType.Core); ServerHelper.WriteLine(ServerHelper.MakeClientName(clientip) + " 通过 RESTful API 连接至服务器,正在登录 . . .", Core.Library.Constant.InvokeMessageType.Core);
string username = loginModel.Username; string username = loginModel.Username;
string password = loginModel.Password; string password = loginModel.Password;
RESTfulAPIListener? apiListener = Singleton.Get<RESTfulAPIListener>(); RESTfulAPIListener? apiListener = RESTfulAPIListener.Instance;
if (apiListener != null) if (apiListener != null)
{ {
// 移除旧模型 // 移除旧模型

View File

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web"> <Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
@ -45,4 +45,8 @@
</None> </None>
</ItemGroup> </ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
</ItemGroup>
</Project> </Project>

View File

@ -1,13 +1,16 @@
using System.Net.WebSockets; using System.Net.WebSockets;
using System.Reflection;
using System.Text; using System.Text;
using System.Text.Encodings.Web; using System.Text.Encodings.Web;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using System.Text.Unicode; using System.Text.Unicode;
using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Diagnostics; using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Mvc.ApplicationParts;
using Microsoft.IdentityModel.Tokens; using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.JsonConverter; using Milimoe.FunGame.Core.Library.Common.JsonConverter;
using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Common.Network;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
@ -29,12 +32,24 @@ try
// 初始化命令菜单 // 初始化命令菜单
ServerHelper.InitOrderList(); ServerHelper.InitOrderList();
// 创建全局SQLHelper
Config.InitSQLHelper();
// 创建全局MailSender
Config.InitMailSender();
// 读取游戏模组 // 读取游戏模组
if (!Config.GetGameModuleList()) if (!Config.GetGameModuleList())
{ {
ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。"); ServerHelper.WriteLine("服务器似乎未安装任何游戏模组,请检查是否正确安装它们。");
} }
// 读取Server插件
Config.GetServerPlugins();
// 读取Web API插件
Config.GetWebAPIPlugins();
// 检查是否存在配置文件 // 检查是否存在配置文件
if (!INIHelper.ExistINIFile()) if (!INIHelper.ExistINIFile())
{ {
@ -51,18 +66,36 @@ try
// 创建单例 // 创建单例
RESTfulAPIListener apiListener = new(); RESTfulAPIListener apiListener = new();
Singleton.Add(apiListener); RESTfulAPIListener.Instance = apiListener;
ServerHelper.WriteLine("请输入 help 来获取帮助,输入 quit 关闭服务器。"); ServerHelper.WriteLine("请输入 help 来获取帮助,输入 quit 关闭服务器。");
// 创建全局SQLHelper if (Config.ServerNotice != "")
Config.InitSQLHelper(); Console.WriteLine("\r \n********** 服务器公告 **********\n\n" + Config.ServerNotice + "\n");
else
Console.WriteLine("无法读取服务器公告");
ServerHelper.WriteLine("正在启动 Web API 监听 . . ."); ServerHelper.WriteLine("正在启动 Web API 监听 . . .");
Console.WriteLine("\r ");
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
// 读取扩展控制器
if (Config.WebAPIPluginLoader != null)
{
foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values)
{
Assembly? pluginAssembly = Assembly.GetAssembly(plugin.GetType());
if (pluginAssembly != null)
{
// 注册所有控制器
builder.Services.AddControllers().PartManager.ApplicationParts.Add(new AssemblyPart(pluginAssembly));
}
}
}
// 添加 JSON 转换器
builder.Services.AddControllers().AddJsonOptions(options => builder.Services.AddControllers().AddJsonOptions(options =>
{ {
options.JsonSerializerOptions.WriteIndented = true; options.JsonSerializerOptions.WriteIndented = true;
@ -146,6 +179,10 @@ try
app.UseSwaggerUI(); app.UseSwaggerUI();
} }
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseAuthorization(); app.UseAuthorization();
@ -184,11 +221,6 @@ try
// 开始监听连接 // 开始监听连接
listener.BannedList.AddRange(Config.ServerBannedList); 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); Task order = Task.Factory.StartNew(GetConsoleOrder);
app.Run(); app.Run();