服务器结构调整;添加 APIBearer;修复强制下线的目标客户端错误的问题 (#46)

* 服务器结构调整;添加 APIBearer

* Server.exe 需要初始化用户密钥列表,UeerKeys 均需要小写用户名保存和读取

* 修复强制下线的目标客户端错误的问题
This commit is contained in:
milimoe 2025-03-17 00:49:58 +08:00 committed by GitHub
parent 5f8d8e5214
commit d35f9aa81a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
13 changed files with 542 additions and 272 deletions

View File

@ -4,6 +4,7 @@ using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Entity; using Milimoe.FunGame.Core.Entity;
using Milimoe.FunGame.Core.Interface.Base; using Milimoe.FunGame.Core.Interface.Base;
using Milimoe.FunGame.Core.Library.Common.Addon; using Milimoe.FunGame.Core.Library.Common.Addon;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Common;
using Milimoe.FunGame.Core.Library.SQLScript.Entity; using Milimoe.FunGame.Core.Library.SQLScript.Entity;
@ -27,9 +28,7 @@ namespace Milimoe.FunGame.Server.Controller
private DataRequestType _lastRequest = DataRequestType.UnKnown; private DataRequestType _lastRequest = DataRequestType.UnKnown;
private readonly bool[] _isReadyCheckCD = [false, false]; private readonly bool[] _isReadyCheckCD = [false, false];
protected DataSet _dsUser = new();
protected string _username = ""; protected string _username = "";
protected Guid _checkLoginKey = Guid.Empty;
protected bool _isMatching; protected bool _isMatching;
/// <summary> /// <summary>
@ -150,12 +149,11 @@ namespace Milimoe.FunGame.Server.Controller
{ {
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest);
key = DataRequest.GetDictionaryJsonObject<Guid>(requestData, "key"); key = DataRequest.GetDictionaryJsonObject<Guid>(requestData, "key");
if (IsLoginKey(key)) if (Server.IsLoginKey(key))
{ {
// 从玩家列表移除 // 从玩家列表移除
Server.RemoveUser(); Server.RemoveUser();
Server.GetUsersCount(); Server.GetUsersCount();
_checkLoginKey = Guid.Empty;
msg = "你已成功退出登录! "; msg = "你已成功退出登录! ";
} }
} }
@ -192,7 +190,7 @@ namespace Milimoe.FunGame.Server.Controller
string gamemap = DataRequest.GetDictionaryJsonObject<string>(requestData, "gamemap") ?? ""; string gamemap = DataRequest.GetDictionaryJsonObject<string>(requestData, "gamemap") ?? "";
bool isrank = DataRequest.GetDictionaryJsonObject<bool>(requestData, "isrank"); bool isrank = DataRequest.GetDictionaryJsonObject<bool>(requestData, "isrank");
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest) + " : " + RoomSet.GetTypeString(type) + " (" + string.Join(", ", [gamemodule, gamemap]) + ")", InvokeMessageType.DataRequest); 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)) if (gamemodule == "" || gamemap == "" || FunGameSystem.GameModuleLoader is null || !FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(gamemodule) || !FunGameSystem.GameModuleLoader.Maps.ContainsKey(gamemap))
{ {
ServerHelper.WriteLine("缺少对应的模组或地图,无法创建房间。"); ServerHelper.WriteLine("缺少对应的模组或地图,无法创建房间。");
resultData.Add("room", room); resultData.Add("room", room);
@ -209,7 +207,7 @@ namespace Milimoe.FunGame.Server.Controller
{ {
// 防止重复 // 防止重复
roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7).ToUpper(); roomid = Verification.CreateVerifyCode(VerifyCodeType.MixVerifyCode, 7).ToUpper();
if (Config.RoomList.GetRoom(roomid).Roomid == "-1") if (FunGameSystem.RoomList.GetRoom(roomid).Roomid == "-1")
{ {
break; break;
} }
@ -224,7 +222,7 @@ namespace Milimoe.FunGame.Server.Controller
if (SQLHelper.Result == SQLResult.Success && SQLHelper.DataSet.Tables[0].Rows.Count > 0) if (SQLHelper.Result == SQLResult.Success && SQLHelper.DataSet.Tables[0].Rows.Count > 0)
{ {
room = Factory.GetRoom(SQLHelper.DataSet.Tables[0].Rows[0], user); room = Factory.GetRoom(SQLHelper.DataSet.Tables[0].Rows[0], user);
Config.RoomList.AddRoom(room); FunGameSystem.RoomList.AddRoom(room);
} }
} }
} }
@ -240,7 +238,7 @@ namespace Milimoe.FunGame.Server.Controller
private void UpdateRoom(Dictionary<string, object> resultData) private void UpdateRoom(Dictionary<string, object> resultData)
{ {
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest);
resultData.Add("rooms", Config.RoomList.ListRoom); // 传RoomList resultData.Add("rooms", FunGameSystem.RoomList.ListRoom); // 传RoomList
} }
/// <summary> /// <summary>
@ -257,7 +255,7 @@ namespace Milimoe.FunGame.Server.Controller
string roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1"; string roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1";
bool isMaster = DataRequest.GetDictionaryJsonObject<bool>(requestData, "isMaster"); bool isMaster = DataRequest.GetDictionaryJsonObject<bool>(requestData, "isMaster");
if (roomid != "-1" && Config.RoomList.IsExist(roomid)) if (roomid != "-1" && FunGameSystem.RoomList.IsExist(roomid))
{ {
result = await Server.QuitRoom(roomid, isMaster); result = await Server.QuitRoom(roomid, isMaster);
} }
@ -285,15 +283,15 @@ namespace Milimoe.FunGame.Server.Controller
SQLHelper.ExecuteDataSet(RoomQuery.Select_IsExistRoom(SQLHelper, roomid)); SQLHelper.ExecuteDataSet(RoomQuery.Select_IsExistRoom(SQLHelper, roomid));
if (SQLHelper.Success) if (SQLHelper.Success)
{ {
Config.RoomList.IntoRoom(roomid, Server.User); FunGameSystem.RoomList.IntoRoom(roomid, Server.User);
Server.InRoom = Config.RoomList[roomid]; Server.InRoom = FunGameSystem.RoomList[roomid];
await Server.SendClients(Server.Listener.ClientList.Where(c => c != null && roomid == c.InRoom.Roomid && c.User.Id != 0), 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 + " ] 进入了房间。"); SocketMessageType.Chat, Server.User.Username, DateTimeUtility.GetNowShortTime() + " [ " + Server.User.Username + " ] 进入了房间。");
result = true; result = true;
} }
else else
{ {
Config.RoomList.RemoveRoom(roomid); FunGameSystem.RoomList.RemoveRoom(roomid);
} }
} }
} }
@ -344,15 +342,15 @@ namespace Milimoe.FunGame.Server.Controller
roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1"; roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1";
User user = Server.User; User user = Server.User;
if (roomid != "-1" && user.Id != 0 && user.Id != Config.RoomList.GetRoomMaster(roomid).Id && !Config.RoomList.GetReadyUserList(roomid).Contains(user)) if (roomid != "-1" && user.Id != 0 && user.Id != FunGameSystem.RoomList.GetRoomMaster(roomid).Id && !FunGameSystem.RoomList.GetReadyUserList(roomid).Contains(user))
{ {
Config.RoomList.SetReady(roomid, user); FunGameSystem.RoomList.SetReady(roomid, user);
result = true; result = true;
} }
} }
resultData.Add("result", result); resultData.Add("result", result);
resultData.Add("ready", Config.RoomList.GetReadyUserList(roomid)); resultData.Add("ready", FunGameSystem.RoomList.GetReadyUserList(roomid));
resultData.Add("notready", Config.RoomList.GetNotReadyUserList(roomid)); resultData.Add("notready", FunGameSystem.RoomList.GetNotReadyUserList(roomid));
} }
/// <summary> /// <summary>
@ -370,15 +368,15 @@ namespace Milimoe.FunGame.Server.Controller
roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1"; roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1";
User user = Server.User; User user = Server.User;
if (roomid != "-1" && user.Id != 0 && user.Id != Config.RoomList.GetRoomMaster(roomid).Id && Config.RoomList.GetReadyUserList(roomid).Contains(user)) if (roomid != "-1" && user.Id != 0 && user.Id != FunGameSystem.RoomList.GetRoomMaster(roomid).Id && FunGameSystem.RoomList.GetReadyUserList(roomid).Contains(user))
{ {
Config.RoomList.CancelReady(roomid, user); FunGameSystem.RoomList.CancelReady(roomid, user);
result = true; result = true;
} }
} }
resultData.Add("result", result); resultData.Add("result", result);
resultData.Add("ready", Config.RoomList.GetReadyUserList(roomid)); resultData.Add("ready", FunGameSystem.RoomList.GetReadyUserList(roomid));
resultData.Add("notready", Config.RoomList.GetNotReadyUserList(roomid)); resultData.Add("notready", FunGameSystem.RoomList.GetNotReadyUserList(roomid));
} }
/// <summary> /// <summary>
@ -416,7 +414,7 @@ namespace Milimoe.FunGame.Server.Controller
{ {
if (isMaster) if (isMaster)
{ {
string[] usernames = [.. Config.RoomList.GetNotReadyUserList(roomid).Select(user => user.Username)]; string[] usernames = [.. FunGameSystem.RoomList.GetNotReadyUserList(roomid).Select(user => user.Username)];
if (usernames.Length > 0) if (usernames.Length > 0)
{ {
if (_isReadyCheckCD[0] == false) if (_isReadyCheckCD[0] == false)
@ -437,7 +435,7 @@ namespace Milimoe.FunGame.Server.Controller
} }
else else
{ {
List<User> users = Config.RoomList.GetUsers(roomid); List<User> users = FunGameSystem.RoomList.GetUsers(roomid);
if (users.Count < 2) if (users.Count < 2)
{ {
Server.SendSystemMessage(ShowMessageType.None, "玩家数量不足,无法开始游戏。", "", 0, Server.User.Username); Server.SendSystemMessage(ShowMessageType.None, "玩家数量不足,无法开始游戏。", "", 0, Server.User.Username);
@ -455,7 +453,7 @@ namespace Milimoe.FunGame.Server.Controller
{ {
// 提醒房主开始游戏 // 提醒房主开始游戏
Server.SendSystemMessage(ShowMessageType.None, "已提醒房主立即开始游戏。", "", 0, Server.User.Username); Server.SendSystemMessage(ShowMessageType.None, "已提醒房主立即开始游戏。", "", 0, Server.User.Username);
Server.SendSystemMessage(ShowMessageType.Tip, "房间中的玩家已请求你立即开始游戏。", "请求开始", 10, Config.RoomList[roomid].RoomMaster.Username); Server.SendSystemMessage(ShowMessageType.Tip, "房间中的玩家已请求你立即开始游戏。", "请求开始", 10, FunGameSystem.RoomList[roomid].RoomMaster.Username);
_isReadyCheckCD[1] = true; _isReadyCheckCD[1] = true;
TaskUtility.RunTimer(() => TaskUtility.RunTimer(() =>
{ {
@ -476,15 +474,15 @@ namespace Milimoe.FunGame.Server.Controller
Room room = General.HallInstance; Room room = General.HallInstance;
if (roomid != "-1") if (roomid != "-1")
{ {
room = Config.RoomList[roomid]; room = FunGameSystem.RoomList[roomid];
} }
if (room.Roomid == "-1") return; if (room.Roomid == "-1") return;
// 启动服务器 // 启动服务器
TaskUtility.NewTask(() => TaskUtility.NewTask(() =>
{ {
if (Config.GameModuleLoader != null && Config.GameModuleLoader.ModuleServers.ContainsKey(room.GameModule)) if (FunGameSystem.GameModuleLoader != null && FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(room.GameModule))
{ {
Server.NowGamingServer = Config.GameModuleLoader.GetServerMode(room.GameModule); Server.NowGamingServer = FunGameSystem.GameModuleLoader.GetServerMode(room.GameModule);
Dictionary<string, IServerModel> all = Server.Listener.UserList.Cast<IServerModel>().ToDictionary(k => k.User.Username, v => v); Dictionary<string, IServerModel> all = Server.Listener.UserList.Cast<IServerModel>().ToDictionary(k => k.User.Username, v => v);
// 给其他玩家赋值模组服务器 // 给其他玩家赋值模组服务器
foreach (IServerModel model in all.Values.Where(s => s.User.Username != Server.User.Username)) foreach (IServerModel model in all.Values.Where(s => s.User.Username != Server.User.Username))
@ -499,7 +497,7 @@ namespace Milimoe.FunGame.Server.Controller
{ {
if (serverTask != null && serverTask.Socket != null) if (serverTask != null && serverTask.Socket != null)
{ {
Config.RoomList.CancelReady(roomid, serverTask.User); FunGameSystem.RoomList.CancelReady(roomid, serverTask.User);
serverTask.Send(SocketMessageType.StartGame, room, users); serverTask.Send(SocketMessageType.StartGame, room, users);
} }
} }
@ -529,7 +527,11 @@ namespace Milimoe.FunGame.Server.Controller
string password = DataRequest.GetDictionaryJsonObject<string>(requestData, "password") ?? ""; string password = DataRequest.GetDictionaryJsonObject<string>(requestData, "password") ?? "";
string email = DataRequest.GetDictionaryJsonObject<string>(requestData, "email") ?? ""; string email = DataRequest.GetDictionaryJsonObject<string>(requestData, "email") ?? "";
string verifycode = DataRequest.GetDictionaryJsonObject<string>(requestData, "verifycode") ?? ""; string verifycode = DataRequest.GetDictionaryJsonObject<string>(requestData, "verifycode") ?? "";
(msg, returnType, success) = DataRequestService.Reg(username, password, email, verifycode, Server.Socket?.ClientIP ?? "", SQLHelper, MailSender); (msg, returnType, success) = DataRequestService.Reg(Server, username, password, email, verifycode, Server.Socket?.ClientIP ?? "");
}
else
{
ServerHelper.WriteLine("客户端提供的参数不足。", InvokeMessageType.DataRequest, LogLevel.Warning);
} }
resultData.Add("msg", msg); resultData.Add("msg", msg);
resultData.Add("type", returnType); resultData.Add("type", returnType);
@ -547,106 +549,69 @@ namespace Milimoe.FunGame.Server.Controller
/// <param name="resultData"></param> /// <param name="resultData"></param>
private async Task Login(Dictionary<string, object> requestData, Dictionary<string, object> resultData) private async Task Login(Dictionary<string, object> requestData, Dictionary<string, object> resultData)
{ {
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest);
string msg = ""; string msg = "";
User user = Factory.GetUser(); User user = Factory.GetUser();
string username = "";
string password = "";
string autokey = "";
Guid key = Guid.Empty;
if (requestData.Count >= 4) if (requestData.Count >= 4)
{ {
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); username = DataRequest.GetDictionaryJsonObject<string>(requestData, "username") ?? "";
string username = DataRequest.GetDictionaryJsonObject<string>(requestData, "username") ?? ""; password = DataRequest.GetDictionaryJsonObject<string>(requestData, "password") ?? "";
string password = DataRequest.GetDictionaryJsonObject<string>(requestData, "password") ?? ""; autokey = DataRequest.GetDictionaryJsonObject<string>(requestData, "autokey") ?? "";
string autokey = DataRequest.GetDictionaryJsonObject<string>(requestData, "autokey") ?? ""; key = DataRequest.GetDictionaryJsonObject<Guid>(requestData, "key");
Guid key = DataRequest.GetDictionaryJsonObject<Guid>(requestData, "key"); }
else
{
ServerHelper.WriteLine("客户端提供的参数不足。", InvokeMessageType.DataRequest, LogLevel.Warning);
}
// CheckLogin的情况 LoginEventArgs eventArgs = new(username, password, autokey);
if (key != Guid.Empty) FunGameSystem.ServerPluginLoader?.OnBeforeLoginEvent(this, eventArgs);
FunGameSystem.WebAPIPluginLoader?.OnBeforeLoginEvent(this, eventArgs);
if (eventArgs.Cancel)
{
msg = $"{DataRequestSet.GetTypeString(_lastRequest)} 请求已取消。{(eventArgs.EventMsg != "" ? $"{eventArgs.EventMsg}" : "")}";
ServerHelper.WriteLine(msg);
resultData.Add("msg", msg);
resultData.Add("user", user);
return;
}
// CheckLogin的情况
if (key != Guid.Empty)
{
if (Server.IsLoginKey(key))
{ {
if (IsLoginKey(key)) await Server.CheckLogin();
{ user = Server.User;
await CheckLogin();
user = Server.User;
}
else ServerHelper.WriteLine("客户端发送了错误的秘钥,不允许本次登录。");
} }
else else
{ {
// 验证登录 msg = "客户端发送了错误的秘钥,不允许本次登录。";
if (username != null && password != null) ServerHelper.WriteLine(msg, InvokeMessageType.DataRequest, LogLevel.Warning);
{
password = password.Encrypt(username);
ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] Username: " + username);
if (SQLHelper != null)
{
SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(SQLHelper, username, password));
if (SQLHelper.Result == SQLResult.Success)
{
DataSet dsUser = SQLHelper.DataSet;
if (autokey.Trim() != "")
{
SQLHelper.ExecuteDataSet(UserQuery.Select_CheckAutoKey(SQLHelper, 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);
}
}
}
} }
} }
else
{
// 进行预登录
(bool success, DataSet dsUser, msg, key) = DataRequestService.PreLogin(this, username, password, autokey);
if (success)
{
Server.PreLogin(dsUser, key);
resultData.Add("key", key);
}
}
FunGameSystem.ServerPluginLoader?.OnAfterLoginEvent(this, eventArgs);
FunGameSystem.WebAPIPluginLoader?.OnAfterLoginEvent(this, eventArgs);
resultData.Add("msg", msg); resultData.Add("msg", msg);
resultData.Add("user", user); resultData.Add("user", user);
} }
/// <summary>
/// 预登录
/// </summary>
/// <param name="dsuser"></param>
/// <param name="username"></param>
/// <param name="checkloginkey"></param>
private void PreLogin(DataSet dsuser, string username, Guid checkloginkey)
{
_dsUser = dsuser;
_username = username;
_checkLoginKey = checkloginkey;
}
/// <summary>
/// 确认登录
/// </summary>
private async Task CheckLogin()
{
// 创建User对象
Server.User = Factory.GetUser(_dsUser);
// 检查有没有重复登录的情况
await Server.ForceLogOutDuplicateLogonUser();
// 添加至玩家列表
Server.AddUser();
Server.GetUsersCount();
}
/// <summary>
/// 检查LoginKey
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
private bool IsLoginKey(Guid key)
{
return key == _checkLoginKey;
}
/// <summary> /// <summary>
/// 接收并验证找回密码时的验证码 /// 接收并验证找回密码时的验证码
/// </summary> /// </summary>
@ -773,7 +738,8 @@ namespace Milimoe.FunGame.Server.Controller
string password = DataRequest.GetDictionaryJsonObject<string>(requestData, UserQuery.Column_Password) ?? ""; string password = DataRequest.GetDictionaryJsonObject<string>(requestData, UserQuery.Column_Password) ?? "";
if (username.Trim() != "" && password.Trim() != "") if (username.Trim() != "" && password.Trim() != "")
{ {
password = password.Encrypt(username); FunGameSystem.UpdateUserKey(username);
password = password.Encrypt(FunGameSystem.GetUserKey(username));
SQLHelper?.Execute(UserQuery.Update_Password(SQLHelper, username, password)); SQLHelper?.Execute(UserQuery.Update_Password(SQLHelper, username, password));
if (SQLHelper?.Success ?? false) if (SQLHelper?.Success ?? false)
{ {
@ -802,7 +768,7 @@ namespace Milimoe.FunGame.Server.Controller
ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest); ServerHelper.WriteLine(Server.GetClientName() + " -> " + DataRequestSet.GetTypeString(_lastRequest), InvokeMessageType.DataRequest);
roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1"; roomid = DataRequest.GetDictionaryJsonObject<string>(requestData, "roomid") ?? "-1";
} }
resultData.Add("count", Config.RoomList.GetUserCount(roomid)); resultData.Add("count", FunGameSystem.RoomList.GetUserCount(roomid));
} }
/// <summary> /// <summary>
@ -863,11 +829,11 @@ namespace Milimoe.FunGame.Server.Controller
List<Room> targets; List<Room> targets;
if (roomtype == RoomType.All) if (roomtype == RoomType.All)
{ {
targets = [.. Config.RoomList.ListRoom.Where(r => r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching)]; targets = [.. FunGameSystem.RoomList.ListRoom.Where(r => r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching)];
} }
else else
{ {
targets = [.. Config.RoomList.ListRoom.Where(r => (r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching) && r.RoomType == roomtype)]; targets = [.. FunGameSystem.RoomList.ListRoom.Where(r => (r.RoomState == RoomState.Created || r.RoomState == RoomState.Matching) && r.RoomType == roomtype)];
} }
// 如果匹配停止,则退出 // 如果匹配停止,则退出
@ -876,7 +842,7 @@ namespace Milimoe.FunGame.Server.Controller
foreach (Room room in targets) foreach (Room room in targets)
{ {
// 获取当前房间的玩家列表 // 获取当前房间的玩家列表
List<User> players = Config.RoomList.GetUsers(room.Roomid); List<User> players = FunGameSystem.RoomList.GetUsers(room.Roomid);
if (players.Count > 0) if (players.Count > 0)
{ {
// 计算房间平均Elo // 计算房间平均Elo

View File

@ -105,6 +105,9 @@ void StartServer()
ServerHelper.WriteLine("请输入 help 来获取帮助,按下 Ctrl+C 关闭服务器。"); ServerHelper.WriteLine("请输入 help 来获取帮助,按下 Ctrl+C 关闭服务器。");
// 初始化用户密钥列表
FunGameSystem.InitUserKeys();
ServerHelper.PrintFunGameTitle(); ServerHelper.PrintFunGameTitle();
// 使用Socket还是WebSocket // 使用Socket还是WebSocket

View File

@ -52,16 +52,16 @@ namespace Milimoe.FunGame.Server.Model
break; break;
} }
// 广播到插件 // 广播到插件
if (Config.ServerPluginLoader != null) if (FunGameSystem.ServerPluginLoader != null)
{ {
foreach (ServerPlugin plugin in Config.ServerPluginLoader.Plugins.Values) foreach (ServerPlugin plugin in FunGameSystem.ServerPluginLoader.Plugins.Values)
{ {
plugin.ProcessInput(order); plugin.ProcessInput(order);
} }
} }
if (Config.WebAPIPluginLoader != null) if (FunGameSystem.WebAPIPluginLoader != null)
{ {
foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) foreach (WebAPIPlugin plugin in FunGameSystem.WebAPIPluginLoader.Plugins.Values)
{ {
plugin.ProcessInput(order); plugin.ProcessInput(order);
} }

View File

@ -43,6 +43,8 @@ namespace Milimoe.FunGame.Server.Model
protected string _username = ""; protected string _username = "";
protected long _loginTime = 0; protected long _loginTime = 0;
protected long _logoutTime = 0; protected long _logoutTime = 0;
protected DataSet _dsUser = new();
protected Guid _checkLoginKey = Guid.Empty;
public ServerModel(ISocketListener<T> server, ISocketMessageProcessor socket, bool isDebugMode) public ServerModel(ISocketListener<T> server, ISocketMessageProcessor socket, bool isDebugMode)
{ {
@ -259,9 +261,9 @@ namespace Milimoe.FunGame.Server.Model
else else
{ {
// 建立连接 // 建立连接
if (Config.GameModuleLoader != null && Config.GameModuleLoader.ModuleServers.ContainsKey(serverName)) if (FunGameSystem.GameModuleLoader != null && FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(serverName))
{ {
GameModuleServer mod = Config.GameModuleLoader.GetServerMode(serverName); GameModuleServer mod = FunGameSystem.GameModuleLoader.GetServerMode(serverName);
if (mod.StartAnonymousServer(this, data)) if (mod.StartAnonymousServer(this, data))
{ {
NowGamingServer = mod; NowGamingServer = mod;
@ -381,13 +383,13 @@ namespace Milimoe.FunGame.Server.Model
{ {
bool result; bool result;
Config.RoomList.CancelReady(roomid, User); FunGameSystem.RoomList.CancelReady(roomid, User);
Config.RoomList.QuitRoom(roomid, User); FunGameSystem.RoomList.QuitRoom(roomid, User);
Room Room = Config.RoomList[roomid] ?? General.HallInstance; Room Room = FunGameSystem.RoomList[roomid] ?? General.HallInstance;
// 是否是房主 // 是否是房主
if (isMaster) if (isMaster)
{ {
List<User> users = [.. Config.RoomList[roomid].UserAndIsReady.Keys]; List<User> users = [.. FunGameSystem.RoomList[roomid].UserAndIsReady.Keys];
if (users.Count > 0) // 如果此时房间还有人,更新房主 if (users.Count > 0) // 如果此时房间还有人,更新房主
{ {
User NewMaster = users[0]; User NewMaster = users[0];
@ -399,7 +401,7 @@ namespace Milimoe.FunGame.Server.Model
} }
else // 没人了就解散房间 else // 没人了就解散房间
{ {
Config.RoomList.RemoveRoom(roomid); FunGameSystem.RoomList.RemoveRoom(roomid);
SQLHelper?.Execute(RoomQuery.Delete_QuitRoom(SQLHelper, roomid, User.Id)); SQLHelper?.Execute(RoomQuery.Delete_QuitRoom(SQLHelper, roomid, User.Id));
this.InRoom = General.HallInstance; this.InRoom = General.HallInstance;
ServerHelper.WriteLine("[ " + GetClientName() + " ] 解散了房间 " + roomid); ServerHelper.WriteLine("[ " + GetClientName() + " ] 解散了房间 " + roomid);
@ -421,10 +423,13 @@ namespace Milimoe.FunGame.Server.Model
{ {
foreach (IServerModel Client in Listener.ClientList.Where(c => c != null && c.User.Id != 0 && room.Roomid == c.InRoom?.Roomid)) foreach (IServerModel Client in Listener.ClientList.Where(c => c != null && c.User.Id != 0 && room.Roomid == c.InRoom?.Roomid))
{ {
await Client.Send(SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。"); if (room.Roomid != "-1")
if (isUpdateRoomMaster && room.RoomMaster?.Id != 0 && room.Roomid != "-1")
{ {
await Client.Send(SocketMessageType.UpdateRoomMaster, room); await Client.Send(SocketMessageType.Chat, User.Username, DateTimeUtility.GetNowShortTime() + " [ " + User.Username + " ] 离开了房间。");
if (isUpdateRoomMaster && room.RoomMaster?.Id != 0)
{
await Client.Send(SocketMessageType.UpdateRoomMaster, room);
}
} }
} }
} }
@ -453,11 +458,28 @@ namespace Milimoe.FunGame.Server.Model
if (Listener.UserList.ContainsKey(user)) if (Listener.UserList.ContainsKey(user))
{ {
ServerHelper.WriteLine("OnlinePlayers: 玩家 " + user + " 重复登录!"); ServerHelper.WriteLine("OnlinePlayers: 玩家 " + user + " 重复登录!");
await ForceLogOut("您的账号在别处登录,已强制下线。"); await ((ServerModel<T>)Listener.UserList[user]).ForceLogOut("您的账号在别处登录,已强制下线。");
} }
} }
} }
public void PreLogin(DataSet dsuser, Guid checkloginkey)
{
_dsUser = dsuser;
_checkLoginKey = checkloginkey;
}
public async Task CheckLogin()
{
// 创建User对象
User = Factory.GetUser(_dsUser);
// 检查有没有重复登录的情况
await ForceLogOutDuplicateLogonUser();
// 添加至玩家列表
AddUser();
GetUsersCount();
}
public bool AddUser() public bool AddUser()
{ {
if (User.Id != 0 && this != null) if (User.Id != 0 && this != null)
@ -477,6 +499,7 @@ namespace Milimoe.FunGame.Server.Model
{ {
if (User.Id != 0 && this != null) if (User.Id != 0 && this != null)
{ {
_checkLoginKey = Guid.Empty;
_logoutTime = DateTime.Now.Ticks; _logoutTime = DateTime.Now.Ticks;
int TotalMinutes = Convert.ToInt32((new DateTime(_logoutTime) - new DateTime(_loginTime)).TotalMinutes); int TotalMinutes = Convert.ToInt32((new DateTime(_logoutTime) - new DateTime(_loginTime)).TotalMinutes);
SQLHelper?.Execute(UserQuery.Update_GameTime(SQLHelper, User.Username, TotalMinutes)); SQLHelper?.Execute(UserQuery.Update_GameTime(SQLHelper, User.Username, TotalMinutes));
@ -501,6 +524,11 @@ namespace Milimoe.FunGame.Server.Model
ServerHelper.WriteLine($"{Listener.Name} 的目前在线客户端数量: {Listener.ClientList.Count}(已登录的玩家数量:{Listener.UserList.Count}"); ServerHelper.WriteLine($"{Listener.Name} 的目前在线客户端数量: {Listener.ClientList.Count}(已登录的玩家数量:{Listener.UserList.Count}");
} }
public bool IsLoginKey(Guid key)
{
return key == _checkLoginKey;
}
protected virtual async Task<bool> Read(ISocketMessageProcessor socket) protected virtual async Task<bool> Read(ISocketMessageProcessor socket)
{ {
// 接收客户端消息 // 接收客户端消息

View File

@ -139,35 +139,10 @@ namespace Milimoe.FunGame.Server.Others
public static FunGameInfo.FunGame FunGameType => FunGameInfo.FunGame.FunGame_Server; public static FunGameInfo.FunGame FunGameType => FunGameInfo.FunGame.FunGame_Server;
/// <summary> /// <summary>
/// 服务器指令列表 /// 运行的数据库模式
/// </summary>
public static Hashtable OrderList { get; } = [];
/// <summary>
/// 在线房间列表
/// </summary>
public static RoomList RoomList { get; } = new();
/// <summary>
/// 是否运行数据库模式
/// </summary> /// </summary>
public static SQLMode SQLMode { get; set; } = SQLMode.None; public static SQLMode SQLMode { get; set; } = SQLMode.None;
/// <summary>
/// Server实际安装的模组
/// </summary>
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>

View File

@ -1,5 +1,7 @@
using Milimoe.FunGame.Core.Api.Transmittal; using System.Data;
using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Api.Utility;
using Milimoe.FunGame.Core.Library.Common.Event;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Common;
using Milimoe.FunGame.Core.Library.SQLScript.Entity; using Milimoe.FunGame.Core.Library.SQLScript.Entity;
@ -9,15 +11,25 @@ namespace Milimoe.FunGame.Server.Services
{ {
public class DataRequestService public class DataRequestService
{ {
public static (string Msg, RegInvokeType RegInvokeType, bool Success) Reg(string username, string password, string email, string verifyCode, string clientIP = "", SQLHelper? sqlHelper = null, MailSender? mailSender = null) public static (string Msg, RegInvokeType RegInvokeType, bool Success) Reg(object sender, string username, string password, string email, string verifyCode, string clientIP = "")
{ {
string msg = ""; string msg = "";
RegInvokeType type = RegInvokeType.None; RegInvokeType type = RegInvokeType.None;
bool success = false; bool success = false;
string clientName = ServerHelper.MakeClientName(clientIP); string clientName = ServerHelper.MakeClientName(clientIP);
sqlHelper ??= Factory.OpenFactory.GetSQLHelper(); RegisterEventArgs eventArgs = new(username, password, email);
mailSender ??= Factory.OpenFactory.GetMailSender(); FunGameSystem.ServerPluginLoader?.OnBeforeRegEvent(sender, eventArgs);
FunGameSystem.WebAPIPluginLoader?.OnBeforeRegEvent(sender, eventArgs);
if (eventArgs.Cancel)
{
msg = $"{DataRequestSet.GetTypeString(DataRequestType.Reg_Reg)} 请求已取消。{(eventArgs.EventMsg != "" ? $"{eventArgs.EventMsg}" : "")}";
ServerHelper.WriteLine(msg, InvokeMessageType.DataRequest, LogLevel.Warning);
return (eventArgs.EventMsg, RegInvokeType.None, false);
}
using SQLHelper? sqlHelper = Factory.OpenFactory.GetSQLHelper();
using MailSender? mailSender = Factory.OpenFactory.GetMailSender();
if (sqlHelper != null) if (sqlHelper != null)
{ {
// 如果没发验证码,就生成验证码 // 如果没发验证码,就生成验证码
@ -114,6 +126,8 @@ namespace Milimoe.FunGame.Server.Services
{ {
sqlHelper.NewTransaction(); sqlHelper.NewTransaction();
ServerHelper.WriteLine("[Reg] Username: " + username + " Email: " + email); ServerHelper.WriteLine("[Reg] Username: " + username + " Email: " + email);
FunGameSystem.UpdateUserKey(username);
password = password.Encrypt(FunGameSystem.GetUserKey(username));
sqlHelper.Execute(UserQuery.Insert_Register(sqlHelper, username, password, email, clientIP)); sqlHelper.Execute(UserQuery.Insert_Register(sqlHelper, username, password, email, clientIP));
if (sqlHelper.Result == SQLResult.Success) if (sqlHelper.Result == SQLResult.Success)
{ {
@ -136,7 +150,69 @@ namespace Milimoe.FunGame.Server.Services
} }
} }
eventArgs.Success = success;
FunGameSystem.ServerPluginLoader?.OnAfterRegEvent(sender, eventArgs);
FunGameSystem.WebAPIPluginLoader?.OnAfterRegEvent(sender, eventArgs);
return (msg, type, success); return (msg, type, success);
} }
public static (bool Success, DataSet DataSet, string Msg, Guid Key) PreLogin(object sender, string username, string password, string autokey = "")
{
bool success = false;
DataSet dsUser = new();
string msg = "用户名或密码不正确。";
Guid key = Guid.Empty;
LoginEventArgs eventArgs = new(username, password, autokey);
FunGameSystem.ServerPluginLoader?.OnBeforeLoginEvent(sender, eventArgs);
FunGameSystem.WebAPIPluginLoader?.OnBeforeLoginEvent(sender, eventArgs);
if (eventArgs.Cancel)
{
msg = $"{DataRequestSet.GetTypeString(DataRequestType.Login_Login)} 请求已取消。{(eventArgs.EventMsg != "" ? $"{eventArgs.EventMsg}" : "")}";
ServerHelper.WriteLine(msg, InvokeMessageType.DataRequest, LogLevel.Warning);
return (success, dsUser, eventArgs.EventMsg, key);
}
// 验证登录
if (username != "" && password != "")
{
password = password.Encrypt(FunGameSystem.GetUserKey(username));
ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] Username: " + username);
using SQLHelper? sqlHelper = Factory.OpenFactory.GetSQLHelper();
if (sqlHelper != null)
{
sqlHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(sqlHelper, username, password));
if (sqlHelper.Result == SQLResult.Success)
{
dsUser = sqlHelper.DataSet;
key = Guid.NewGuid();
success = true;
msg = "";
if (autokey.Trim() != "")
{
sqlHelper.ExecuteDataSet(UserQuery.Select_CheckAutoKey(sqlHelper, username, autokey));
if (sqlHelper.Result == SQLResult.Success)
{
ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] AutoKey: 已确认");
}
else
{
success = false;
msg = "AutoKey 不正确,拒绝自动登录!";
ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] " + msg);
}
}
}
}
}
eventArgs.Success = success;
FunGameSystem.ServerPluginLoader?.OnAfterLoginEvent(sender, eventArgs);
FunGameSystem.WebAPIPluginLoader?.OnAfterLoginEvent(sender, eventArgs);
ServerHelper.WriteLine(msg, InvokeMessageType.Core);
return (success, dsUser, msg, key);
}
} }
} }

View File

@ -3,10 +3,9 @@ using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Core.Model;
using Milimoe.FunGame.Server.Models; using Milimoe.FunGame.Server.Models;
using Milimoe.FunGame.Server.Services;
using MySql.Data.MySqlClient; using MySql.Data.MySqlClient;
namespace Milimoe.FunGame.Server.DataUtility namespace Milimoe.FunGame.Server.Services.DataUtility
{ {
public class MySQLHelper : SQLHelper public class MySQLHelper : SQLHelper
{ {
@ -187,6 +186,29 @@ namespace Milimoe.FunGame.Server.DataUtility
return _dataSet; return _dataSet;
} }
/// <summary>
/// 检查数据库是否存在
/// </summary>
/// <returns></returns>
public override bool DatabaseExists()
{
try
{
OpenConnection();
return true;
}
catch (Exception e)
{
ServerHelper.Error(e);
_result = SQLResult.Fail;
return false;
}
finally
{
Close();
}
}
/// <summary> /// <summary>
/// 创建一个SQL事务 /// 创建一个SQL事务
/// </summary> /// </summary>

View File

@ -4,9 +4,8 @@ using Milimoe.FunGame.Core.Api.Transmittal;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Model; using Milimoe.FunGame.Core.Model;
using Milimoe.FunGame.Server.Models; using Milimoe.FunGame.Server.Models;
using Milimoe.FunGame.Server.Services;
namespace Milimoe.FunGame.Server.DataUtility namespace Milimoe.FunGame.Server.Services.DataUtility
{ {
public class SQLiteHelper : SQLHelper public class SQLiteHelper : SQLHelper
{ {
@ -244,35 +243,11 @@ namespace Milimoe.FunGame.Server.DataUtility
/// 检查数据库是否存在 /// 检查数据库是否存在
/// </summary> /// </summary>
/// <returns></returns> /// <returns></returns>
public bool DatabaseExists() public override bool DatabaseExists()
{ {
return File.Exists(ServerInfo.SQLServerDataBase); return File.Exists(ServerInfo.SQLServerDataBase);
} }
/// <summary>
/// 执行SQL文件中的所有SQL语句来初始化数据库
/// </summary>
/// <param name="sqlFilePath">SQL文件路径</param>
public void ExecuteSqlFile(string sqlFilePath)
{
if (!File.Exists(sqlFilePath))
{
throw new FileNotFoundException("SQL文件不存在", sqlFilePath);
}
string sqlContent = File.ReadAllText(sqlFilePath);
string[] sqlCommands = sqlContent.Split([";"], StringSplitOptions.RemoveEmptyEntries);
foreach (string commandText in sqlCommands)
{
string sql = commandText.Trim();
if (!string.IsNullOrEmpty(sql))
{
Execute(sql);
}
}
}
private bool _isDisposed = false; private bool _isDisposed = false;
/// <summary> /// <summary>

View File

@ -1,16 +1,53 @@
using Milimoe.FunGame.Core.Api.Transmittal; using System.Collections;
using Milimoe.FunGame.Core.Api.Transmittal;
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.Addon;
using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.SQLScript.Common; using Milimoe.FunGame.Core.Library.SQLScript.Common;
using Milimoe.FunGame.Core.Library.SQLScript.Entity; using Milimoe.FunGame.Core.Library.SQLScript.Entity;
using Milimoe.FunGame.Server.DataUtility; using Milimoe.FunGame.Core.Model;
using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Others;
using Milimoe.FunGame.Server.Services.DataUtility;
namespace Milimoe.FunGame.Server.Services namespace Milimoe.FunGame.Server.Services
{ {
public class FunGameSystem public class FunGameSystem
{ {
/// <summary>
/// 服务器指令列表
/// </summary>
public static Hashtable OrderList { get; } = [];
/// <summary>
/// 在线房间列表
/// </summary>
public static RoomList RoomList { get; } = new();
/// <summary>
/// Server实际安装的模组
/// </summary>
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>
public static PluginConfig UserKeys { get; set; } = new("system", "user_keys");
/// <summary>
/// FunGame Web API Token ID
/// </summary>
public const string FunGameWebAPITokenID = "fungame_web_api";
/// <summary> /// <summary>
/// 初始化数据库连接器 /// 初始化数据库连接器
/// </summary> /// </summary>
@ -98,14 +135,14 @@ namespace Milimoe.FunGame.Server.Services
// 读取modules目录下的模组 // 读取modules目录下的模组
try try
{ {
Config.GameModuleLoader = GameModuleLoader.LoadGameModules(Config.FunGameType, delegates); GameModuleLoader = GameModuleLoader.LoadGameModules(Config.FunGameType, delegates);
foreach (GameModuleServer module in Config.GameModuleLoader.ModuleServers.Values) foreach (GameModuleServer module in GameModuleLoader.ModuleServers.Values)
{ {
try try
{ {
bool check = true; bool check = true;
// 检查模组是否有相对应的地图 // 检查模组是否有相对应的地图
if (!Config.GameModuleLoader.Maps.ContainsKey(module.DefaultMap)) if (!GameModuleLoader.Maps.ContainsKey(module.DefaultMap))
{ {
ServerHelper.WriteLine("GameModule Load Failed: " + module.Name + " 没有找到相对应的地图,加载失败", InvokeMessageType.Error); ServerHelper.WriteLine("GameModule Load Failed: " + module.Name + " 没有找到相对应的地图,加载失败", InvokeMessageType.Error);
check = false; check = false;
@ -127,7 +164,7 @@ namespace Milimoe.FunGame.Server.Services
ServerHelper.Error(e2); ServerHelper.Error(e2);
} }
// 设置全局 // 设置全局
Config.GameModuleSupported = supported.Distinct().ToArray(); Config.GameModuleSupported = [.. supported.Distinct()];
return Config.GameModuleSupported.Length > 0; return Config.GameModuleSupported.Length > 0;
} }
@ -143,8 +180,8 @@ namespace Milimoe.FunGame.Server.Services
try try
{ {
// 读取plugins目录下的插件 // 读取plugins目录下的插件
Config.ServerPluginLoader = ServerPluginLoader.LoadPlugins(delegates); ServerPluginLoader = ServerPluginLoader.LoadPlugins(delegates);
foreach (ServerPlugin plugin in Config.ServerPluginLoader.Plugins.Values) foreach (ServerPlugin plugin in ServerPluginLoader.Plugins.Values)
{ {
ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core); ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core);
} }
@ -166,8 +203,8 @@ namespace Milimoe.FunGame.Server.Services
try try
{ {
// 读取plugins目录下的插件 // 读取plugins目录下的插件
Config.WebAPIPluginLoader = WebAPIPluginLoader.LoadPlugins(delegates, otherobjs); WebAPIPluginLoader = WebAPIPluginLoader.LoadPlugins(delegates, otherobjs);
foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) foreach (WebAPIPlugin plugin in WebAPIPluginLoader.Plugins.Values)
{ {
ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core); ServerHelper.WriteLine("Plugin Loaded -> " + plugin.Name, InvokeMessageType.Core);
} }
@ -194,6 +231,87 @@ namespace Milimoe.FunGame.Server.Services
sqlHelper.Execute(RoomQuery.Delete_Rooms(sqlHelper)); sqlHelper.Execute(RoomQuery.Delete_Rooms(sqlHelper));
} }
/// <summary>
/// 初始化用户密钥列表
/// </summary>
public static void InitUserKeys()
{
UserKeys.LoadConfig();
UserKeys.SaveConfig();
}
/// <summary>
/// 获取指定用户的密钥
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public static string GetUserKey(string username)
{
if (UserKeys.TryGetValue(username.ToLower(), out object? value) && value is string key)
{
return key;
}
return username;
}
/// <summary>
/// 更新指定用户的密钥
/// </summary>
/// <param name="username"></param>
/// <returns></returns>
public static void UpdateUserKey(string username)
{
UserKeys.Add(username.ToLower(), Encryption.GenerateRandomString());
UserKeys.SaveConfig();
}
/// <summary>
/// 获取 API Secret Key
/// </summary>
/// <param name="token"></param>
public static string GetAPISecretKey(string token)
{
using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper();
if (sql != null)
{
sql.ExecuteDataSet(ApiTokens.Select_GetAPIToken(sql, token));
if (sql.Result == SQLResult.Success)
{
return sql.DataSet.Tables[0].Rows[0][ApiTokens.Column_SecretKey].ToString() ?? "";
}
}
return "";
}
/// <summary>
/// 设置 API Secret Key
/// </summary>
/// <param name="token"></param>
/// <param name="reference1"></param>
/// <param name="reference2"></param>
public static void SetAPISecretKey(string token, string reference1 = "", string reference2 = "", SQLHelper? sqlHelper = null)
{
bool useSQLHelper = sqlHelper != null;
sqlHelper ??= Factory.OpenFactory.GetSQLHelper();
string key = Encryption.GenerateRandomString();
if (sqlHelper != null)
{
sqlHelper.ExecuteDataSet(ApiTokens.Select_GetAPIToken(sqlHelper, token));
if (sqlHelper.Success)
{
sqlHelper.Execute(ApiTokens.Update_GetAPIToken(sqlHelper, token, key, reference1, reference2));
}
else
{
sqlHelper.Execute(ApiTokens.Insert_APITokens(sqlHelper, token, key, reference1, reference2));
}
}
if (!useSQLHelper)
{
sqlHelper?.Dispose();
}
}
/// <summary> /// <summary>
/// 创建 SQL 服务后需要做的事 /// 创建 SQL 服务后需要做的事
/// </summary> /// </summary>
@ -201,10 +319,18 @@ namespace Milimoe.FunGame.Server.Services
public static void AfterCreateSQLService(SQLHelper sqlHelper) public static void AfterCreateSQLService(SQLHelper sqlHelper)
{ {
Config.SQLMode = sqlHelper.Mode; Config.SQLMode = sqlHelper.Mode;
if (sqlHelper is SQLiteHelper sqliteHelper && !sqliteHelper.DatabaseExists()) if (!sqlHelper.DatabaseExists())
{ {
ServerHelper.WriteLine("正在初始化数据库 . . .", InvokeMessageType.Core); ServerHelper.WriteLine("正在初始化数据库 . . .", InvokeMessageType.Core);
sqliteHelper.ExecuteSqlFile(AppDomain.CurrentDomain.BaseDirectory + "fungame_sqlite.sql"); if (sqlHelper is SQLiteHelper sqliteHelper)
{
sqliteHelper.ExecuteSqlFile(AppDomain.CurrentDomain.BaseDirectory + "fungame_sqlite.sql");
}
else if (sqlHelper is MySQLHelper mysqlHelper)
{
mysqlHelper.ExecuteSqlFile(AppDomain.CurrentDomain.BaseDirectory + "fungame.sql");
}
SetAPISecretKey(FunGameWebAPITokenID, sqlHelper: sqlHelper);
} }
ServerLogin(sqlHelper); ServerLogin(sqlHelper);
ClearRoomList(sqlHelper); ClearRoomList(sqlHelper);
@ -216,23 +342,23 @@ namespace Milimoe.FunGame.Server.Services
/// </summary> /// </summary>
public static void CloseServer() public static void CloseServer()
{ {
if (Config.GameModuleLoader != null) if (GameModuleLoader != null)
{ {
foreach (GameModuleServer server in Config.GameModuleLoader.ModuleServers.Values) foreach (GameModuleServer server in GameModuleLoader.ModuleServers.Values)
{ {
server.Controller.Close(); server.Controller.Close();
} }
} }
if (Config.ServerPluginLoader != null) if (ServerPluginLoader != null)
{ {
foreach (ServerPlugin plugin in Config.ServerPluginLoader.Plugins.Values) foreach (ServerPlugin plugin in ServerPluginLoader.Plugins.Values)
{ {
plugin.Controller.Close(); plugin.Controller.Close();
} }
} }
if (Config.WebAPIPluginLoader != null) if (WebAPIPluginLoader != null)
{ {
foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) foreach (WebAPIPlugin plugin in WebAPIPluginLoader.Plugins.Values)
{ {
plugin.Controller.Close(); plugin.Controller.Close();
} }

View File

@ -233,12 +233,12 @@ namespace Milimoe.FunGame.Server.Services
public static void InitOrderList() public static void InitOrderList()
{ {
Config.OrderList.Clear(); FunGameSystem.OrderList.Clear();
Config.OrderList.Add(OrderDictionary.Help, "Milimoe -> 帮助"); FunGameSystem.OrderList.Add(OrderDictionary.Help, "Milimoe -> 帮助");
Config.OrderList.Add(OrderDictionary.Quit, "关闭服务器"); FunGameSystem.OrderList.Add(OrderDictionary.Quit, "关闭服务器");
Config.OrderList.Add(OrderDictionary.Exit, "关闭服务器"); FunGameSystem.OrderList.Add(OrderDictionary.Exit, "关闭服务器");
Config.OrderList.Add(OrderDictionary.Close, "关闭服务器"); FunGameSystem.OrderList.Add(OrderDictionary.Close, "关闭服务器");
Config.OrderList.Add(OrderDictionary.Restart, "重启服务器"); FunGameSystem.OrderList.Add(OrderDictionary.Restart, "重启服务器");
} }
} }

View File

@ -1,9 +1,8 @@
using System.Security.Claims; using System.Data;
using System.Security.Claims;
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.Constant; using Milimoe.FunGame.Core.Library.Constant;
using Milimoe.FunGame.Core.Library.SQLScript.Entity;
using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Others;
using Milimoe.FunGame.Server.Services; using Milimoe.FunGame.Server.Services;
using Milimoe.FunGame.WebAPI.Architecture; using Milimoe.FunGame.WebAPI.Architecture;
@ -17,19 +16,21 @@ namespace Milimoe.FunGame.WebAPI.Controllers
public class UserController(RESTfulAPIListener apiListener, JWTService jwtTokenService, ILogger<AdapterController> logger) : ControllerBase public class UserController(RESTfulAPIListener apiListener, JWTService jwtTokenService, ILogger<AdapterController> logger) : ControllerBase
{ {
[HttpPost("reg")] [HttpPost("reg")]
[Authorize(AuthenticationSchemes = "APIBearer")]
public IActionResult Reg([FromBody] RegDTO dto) public IActionResult Reg([FromBody] RegDTO dto)
{ {
// 因为注册 API 不需要先登录,所以需要进行 API Bearer Token 验证,防止 API 滥用
// API Bearer Token 保存在数据库 apitokens 表中,由服务器管理员配置
try try
{ {
string clientIP = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort; string clientIP = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort;
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientIP) + " 通过 RESTful API 注册账号 . . .", InvokeMessageType.Core); ServerHelper.WriteLine(ServerHelper.MakeClientName(clientIP) + " 通过 RESTful API 注册账号 . . .", InvokeMessageType.Core);
string username = dto.Username; string username = dto.Username;
string password = dto.Password; string password = dto.Password;
string email = dto.Email; string email = dto.Email;
string verifycode = dto.VerifyCode; string verifycode = dto.VerifyCode;
(string msg, RegInvokeType type, bool success) = DataRequestService.Reg(username, password, email, verifycode, clientIP); (string msg, RegInvokeType type, bool success) = DataRequestService.Reg(apiListener, username, password, email, verifycode, clientIP);
return Ok(new PayloadModel<DataRequestType>() return Ok(new PayloadModel<DataRequestType>()
{ {
@ -53,69 +54,85 @@ namespace Milimoe.FunGame.WebAPI.Controllers
[HttpPost("login")] [HttpPost("login")]
public async Task<IActionResult> Login([FromBody] LoginDTO dto) public async Task<IActionResult> Login([FromBody] LoginDTO dto)
{ {
Config.ConnectingPlayerCount++;
try try
{ {
PayloadModel<DataRequestType> response = new() PayloadModel<DataRequestType> response = new()
{ {
RequestType = DataRequestType.Login_Login RequestType = DataRequestType.Login_Login
}; };
string msg = "用户名或密码不正确。"; string msg = "服务器暂时无法处理登录请求。";
string clientIP = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort; string clientIP = HttpContext.Connection.RemoteIpAddress?.ToString() + ":" + HttpContext.Connection.RemotePort;
ServerHelper.WriteLine(ServerHelper.MakeClientName(clientIP) + " 通过 RESTful API 连接至服务器,正在登录 . . .", InvokeMessageType.Core); ServerHelper.WriteLine(ServerHelper.MakeClientName(clientIP) + " 通过 RESTful API 连接至服务器,正在登录 . . .", InvokeMessageType.Core);
string username = dto.Username; string username = dto.Username;
string password = dto.Password; string password = dto.Password;
if (apiListener != null) RESTfulAPIModel? model = await CheckConnection(username, clientIP);
if (model != null)
{ {
// 移除旧模型 // 预登录
if (apiListener.UserList.ContainsKey(username)) (bool success, DataSet dsUser, msg, Guid key) = DataRequestService.PreLogin(this, username, password);
if (success)
{ {
await apiListener.UserList[username].Send(SocketMessageType.Disconnect); model.PreLogin(dsUser, key);
} // 确认登录
// 创建新模型 await model.CheckLogin();
if (!apiListener.UserList.ContainsKey(username)) string token = jwtTokenService.GenerateToken(username);
{ Config.ConnectingPlayerCount--;
Config.ConnectingPlayerCount++; response.StatusCode = 200;
RESTfulAPIModel model = new(apiListener, clientIP); response.Message = "登录成功!";
model.SetClientName(clientIP); response.Data = new()
// 创建User对象
if (model.SQLHelper != null)
{ {
model.SQLHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(model.SQLHelper, username, password)); { "bearerToken", token },
Core.Entity.User user = Factory.GetUser(model.SQLHelper?.DataSet ?? new()); { "openToken", model.Token }
if (user.Id != 0) };
{ return Ok(response);
model.User = user;
// 检查有没有重复登录的情况
await model.ForceLogOutDuplicateLogonUser();
// 添加至玩家列表
model.AddUser();
model.GetUsersCount();
string token = jwtTokenService.GenerateToken(username);
Config.ConnectingPlayerCount--;
response.StatusCode = 200;
response.Message = "登录成功!";
response.Data = new()
{
{ "bearerToken", token },
{ "openToken", model.Token }
};
return Ok(response);
}
}
else msg = "服务器暂时无法处理登录请求。";
await model.Send(SocketMessageType.Disconnect);
} }
await model.Send(SocketMessageType.Disconnect);
} }
Config.ConnectingPlayerCount--; Config.ConnectingPlayerCount--;
ServerHelper.WriteLine(msg, InvokeMessageType.Core);
response.Message = msg; response.Message = msg;
response.StatusCode = 401; response.StatusCode = 401;
return Unauthorized(response); return Unauthorized(response);
} }
catch (Exception e) catch (Exception e)
{
Config.ConnectingPlayerCount--;
logger.LogError("Error: {e}", e);
}
return BadRequest();
}
[HttpPost("logout")]
[Authorize]
public async Task<IActionResult> LogOut()
{
try
{
PayloadModel<DataRequestType> response = new()
{
RequestType = DataRequestType.RunTime_Logout
};
string username = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "";
if (apiListener.UserList.ContainsKey(username))
{
RESTfulAPIModel model = (RESTfulAPIModel)apiListener.UserList[username];
await model.Send(SocketMessageType.Disconnect);
RevokeToken();
model.GetUsersCount();
response.Message = "你已成功退出登录! ";
response.StatusCode = 200;
return Ok(response);
}
response.Message = "退出登录失败!";
response.StatusCode = 400;
return BadRequest(response);
}
catch (Exception e)
{ {
logger.LogError("Error: {e}", e); logger.LogError("Error: {e}", e);
} }
@ -128,16 +145,12 @@ namespace Milimoe.FunGame.WebAPI.Controllers
{ {
try try
{ {
string oldToken = HttpContext.Request.Headers.Authorization.ToString().Replace("Bearer ", ""); RevokeToken();
// 吊销
jwtTokenService.RevokeToken(oldToken);
// 生成
string username = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? ""; string username = User.FindFirstValue(ClaimTypes.NameIdentifier) ?? "";
string newToken = jwtTokenService.GenerateToken(username); if (username.Trim() != "")
{
return Ok(newToken); return Ok(jwtTokenService.GenerateToken(username));
}
} }
catch (Exception e) catch (Exception e)
{ {
@ -145,5 +158,33 @@ namespace Milimoe.FunGame.WebAPI.Controllers
} }
return BadRequest(); return BadRequest();
} }
private async Task<RESTfulAPIModel?> CheckConnection(string username, string clientIP)
{
if (apiListener != null)
{
// 移除旧模型
if (apiListener.UserList.ContainsKey(username))
{
await apiListener.UserList[username].Send(SocketMessageType.Disconnect);
RevokeToken();
}
// 创建新模型
if (!apiListener.UserList.ContainsKey(username))
{
RESTfulAPIModel model = new(apiListener, clientIP);
model.SetClientName(clientIP);
return model;
}
}
return null;
}
private void RevokeToken()
{
// 吊销令牌
string oldToken = HttpContext.Request.Headers.Authorization.ToString().Replace("Bearer ", "");
if (oldToken.Trim() != "") jwtTokenService.RevokeToken(oldToken);
}
} }
} }

View File

@ -62,6 +62,9 @@ try
// 读取 Server 插件 // 读取 Server 插件
FunGameSystem.GetServerPlugins(); FunGameSystem.GetServerPlugins();
// 初始化用户密钥列表
FunGameSystem.InitUserKeys();
// Add services to the container. // Add services to the container.
WebApplicationBuilder builder = WebApplication.CreateBuilder(args); WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
@ -92,9 +95,9 @@ try
Console.WriteLine("\r "); Console.WriteLine("\r ");
// 读取扩展控制器 // 读取扩展控制器
if (Config.WebAPIPluginLoader != null) if (FunGameSystem.WebAPIPluginLoader != null)
{ {
foreach (WebAPIPlugin plugin in Config.WebAPIPluginLoader.Plugins.Values) foreach (WebAPIPlugin plugin in FunGameSystem.WebAPIPluginLoader.Plugins.Values)
{ {
Assembly? pluginAssembly = Assembly.GetAssembly(plugin.GetType()); Assembly? pluginAssembly = Assembly.GetAssembly(plugin.GetType());
@ -150,7 +153,8 @@ try
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? "undefined")), IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"] ?? "undefined")),
NameClaimType = ClaimTypes.NameIdentifier NameClaimType = ClaimTypes.NameIdentifier
}; };
}).AddScheme<AuthenticationSchemeOptions, CustomBearerAuthenticationHandler>("CustomBearer", options => { }); }).AddScheme<AuthenticationSchemeOptions, APIBearerAuthenticationHandler>("APIBearer", options => { }).
AddScheme<AuthenticationSchemeOptions, CustomBearerAuthenticationHandler>("CustomBearer", options => { });
builder.Logging.AddConsole(options => builder.Logging.AddConsole(options =>
{ {
options.FormatterName = "CustomFormatter"; options.FormatterName = "CustomFormatter";

View File

@ -0,0 +1,54 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Options;
using Milimoe.FunGame.Server.Services;
namespace Milimoe.FunGame.WebAPI.Services
{
public class APIBearerAuthenticationHandler(IMemoryCache memoryCache, IOptionsMonitor<AuthenticationSchemeOptions> options, ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
// 检查是否有 Authorization Header
if (!Request.Headers.TryGetValue("Authorization", out Microsoft.Extensions.Primitives.StringValues value))
{
return AuthenticateResult.Fail("Authorization header is missing.");
}
string authorizationHeader = value.ToString();
if (!authorizationHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
return AuthenticateResult.Fail("Invalid Authorization header format.");
}
string token = authorizationHeader["Bearer ".Length..].Trim();
// 验证 API Bearer Token
string key;
if (memoryCache.TryGetValue(FunGameSystem.FunGameWebAPITokenID, out object? cacheValue) && cacheValue is string str)
{
key = str;
}
else
{
key = FunGameSystem.GetAPISecretKey(FunGameSystem.FunGameWebAPITokenID);
memoryCache.Set(FunGameSystem.FunGameWebAPITokenID, key, TimeSpan.FromMinutes(5));
}
if (key == "" || token != key)
{
await Task.CompletedTask;
return AuthenticateResult.Fail("Invalid Token.");
}
// 如果验证通过,创建 ClaimsIdentity
Claim[] claims = [new Claim(ClaimTypes.Name, "FunGame Web API Claim")];
ClaimsIdentity identity = new(claims, Scheme.Name);
ClaimsPrincipal principal = new(identity);
AuthenticationTicket ticket = new(principal, Scheme.Name);
return AuthenticateResult.Success(ticket);
}
}
}