From db474673fb1d2f7cecb058a35fe25cc9d1f10f37 Mon Sep 17 00:00:00 2001 From: milimoe Date: Sun, 20 Apr 2025 16:03:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E9=9B=85=E7=9A=84=E5=85=B3=E9=97=AD?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=EF=BC=9B=E8=A1=A5=E5=85=A8=E4=BA=86?= =?UTF-8?q?=E6=89=80=E6=9C=89=E6=95=B0=E6=8D=AE=E8=AF=B7=E6=B1=82=E5=AE=9E?= =?UTF-8?q?=E7=8E=B0=EF=BC=9BAPI=20Token=20=E5=8A=A0=E5=AF=86=E6=96=B9?= =?UTF-8?q?=E5=BC=8F=E4=BF=AE=E6=94=B9=EF=BC=9B=E6=B7=BB=E5=8A=A0=E4=BA=86?= =?UTF-8?q?=E6=9C=8D=E5=8A=A1=E5=99=A8=E5=88=9D=E5=A7=8B=E5=8C=96=E6=97=B6?= =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=AE=A1=E7=90=86=E5=91=98=E8=B4=A6=E5=8F=B7?= =?UTF-8?q?=E7=9A=84=E5=BF=85=E8=A6=81=E6=AD=A5=E9=AA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Controllers/DataRequestController.cs | 500 ++++++++++++++++-- FunGame.Server/Main.cs | 62 +-- FunGame.Server/Models/ConsoleModel.cs | 96 +++- FunGame.Server/Models/ServerModel.cs | 2 +- FunGame.Server/Services/DataRequestService.cs | 377 +++++++------ FunGame.Server/Services/FunGameSystem.cs | 45 +- FunGame.Server/Services/General.cs | 10 +- FunGame.WebAPI/Program.cs | 22 + .../Services/APIBearerTokenHandler.cs | 2 +- 9 files changed, 857 insertions(+), 259 deletions(-) diff --git a/FunGame.Server/Controllers/DataRequestController.cs b/FunGame.Server/Controllers/DataRequestController.cs index c6805d2..b760d61 100644 --- a/FunGame.Server/Controllers/DataRequestController.cs +++ b/FunGame.Server/Controllers/DataRequestController.cs @@ -174,6 +174,10 @@ namespace Milimoe.FunGame.Server.Controller MarketSell(data, result); break; + case DataRequestType.Inventory_MarketDelist: + MarketDelist(data, result); + break; + case DataRequestType.Inventory_UpdateMarketPrice: UpdateMarketPrice(data, result); break; @@ -250,12 +254,12 @@ namespace Milimoe.FunGame.Server.Controller Room room = General.HallInstance; if (requestData.Count >= 3) { - 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("[CreateRoom] " + RoomSet.GetTypeString(type) + " (" + string.Join(", ", [gamemodule, gamemap]) + ")", InvokeMessageType.DataRequest); - if (gamemodule == "" || gamemap == "" || FunGameSystem.GameModuleLoader is null || !FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(gamemodule) || !FunGameSystem.GameModuleLoader.Maps.ContainsKey(gamemap)) + RoomType type = DataRequest.GetDictionaryJsonObject(requestData, "roomType"); + string gameModule = DataRequest.GetDictionaryJsonObject(requestData, "moduleServer") ?? ""; + string gameMap = DataRequest.GetDictionaryJsonObject(requestData, "map") ?? ""; + bool isRank = DataRequest.GetDictionaryJsonObject(requestData, "isRank"); + ServerHelper.WriteLine("[CreateRoom] " + RoomSet.GetTypeString(type) + " (" + string.Join(", ", [gameModule, gameMap]) + ")", InvokeMessageType.DataRequest); + if (gameModule == "" || gameMap == "" || FunGameSystem.GameModuleLoader is null || !FunGameSystem.GameModuleLoader.ModuleServers.ContainsKey(gameModule) || !FunGameSystem.GameModuleLoader.Maps.ContainsKey(gameMap)) { ServerHelper.WriteLine("缺少对应的模组或地图,无法创建房间。"); resultData.Add("room", room); @@ -263,7 +267,7 @@ namespace Milimoe.FunGame.Server.Controller } User user = DataRequest.GetDictionaryJsonObject(requestData, "master") ?? Factory.GetUser(); string password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; - int maxusers = DataRequest.GetDictionaryJsonObject(requestData, "maxusers"); + int maxusers = DataRequest.GetDictionaryJsonObject(requestData, "maxUsers"); if (user.Id != 0) { @@ -279,7 +283,7 @@ namespace Milimoe.FunGame.Server.Controller } if (roomid != "-1" && SQLHelper != null) { - SQLHelper.Execute(RoomQuery.Insert_CreateRoom(SQLHelper, roomid, user.Id, type, gamemodule, gamemap, isrank, password, maxusers)); + SQLHelper.Execute(RoomQuery.Insert_CreateRoom(SQLHelper, roomid, user.Id, type, gameModule, gameMap, isRank, password, maxusers)); if (SQLHelper.Result == SQLResult.Success) { ServerHelper.WriteLine("[CreateRoom] Master: " + user.Username + " RoomID: " + roomid); @@ -372,11 +376,11 @@ namespace Milimoe.FunGame.Server.Controller bool result = true; if (requestData.Count >= 1) { - bool iscancel = DataRequest.GetDictionaryJsonObject(requestData, "iscancel"); + bool iscancel = DataRequest.GetDictionaryJsonObject(requestData, "isCancel"); if (!iscancel) { ServerHelper.WriteLine("[MatchRoom] Start", InvokeMessageType.DataRequest); - RoomType type = DataRequest.GetDictionaryJsonObject(requestData, "roomtype"); + RoomType type = DataRequest.GetDictionaryJsonObject(requestData, "roomType"); User user = DataRequest.GetDictionaryJsonObject(requestData, "matcher") ?? Factory.GetUser(); StartMatching(type, user); } @@ -669,9 +673,9 @@ namespace Milimoe.FunGame.Server.Controller string msg = "无法找回您的密码,请稍后再试。"; // 返回的验证信息 if (requestData.Count >= 3) { - string username = DataRequest.GetDictionaryJsonObject(requestData, ForgetVerifyCodes.Column_Username) ?? ""; - string email = DataRequest.GetDictionaryJsonObject(requestData, ForgetVerifyCodes.Column_Email) ?? ""; - string verifycode = DataRequest.GetDictionaryJsonObject(requestData, ForgetVerifyCodes.Column_ForgetVerifyCode) ?? ""; + string username = DataRequest.GetDictionaryJsonObject(requestData, "username") ?? ""; + string email = DataRequest.GetDictionaryJsonObject(requestData, "email") ?? ""; + string verifycode = DataRequest.GetDictionaryJsonObject(requestData, "forgetVerifyCode") ?? ""; // 客户端发来了验证码就进行验证,没有发就生成 if (verifycode.Trim() != "") @@ -1013,7 +1017,17 @@ namespace Milimoe.FunGame.Server.Controller /// private void UpdateUser(Dictionary requestData, Dictionary resultData) { - // TODO + string msg = "无法更新用户数据,请稍后再试。"; + if (SQLHelper != null && requestData.Count > 0) + { + User user = DataRequest.GetDictionaryJsonObject(requestData, "user") ?? Factory.GetUser(); + SQLHelper.UpdateUser(user); + if (SQLHelper.Success) + { + msg = ""; + } + } + resultData.Add("msg", msg); } /// @@ -1026,8 +1040,8 @@ namespace Milimoe.FunGame.Server.Controller string msg = "无法更新您的密码,请稍后再试。"; if (requestData.Count >= 2) { - string username = DataRequest.GetDictionaryJsonObject(requestData, UserQuery.Column_Username) ?? ""; - string password = DataRequest.GetDictionaryJsonObject(requestData, UserQuery.Column_Password) ?? ""; + string username = DataRequest.GetDictionaryJsonObject(requestData, "username") ?? ""; + string password = DataRequest.GetDictionaryJsonObject(requestData, "password") ?? ""; if (username.Trim() != "" && password.Trim() != "") { FunGameSystem.UpdateUserKey(username); @@ -1094,7 +1108,16 @@ namespace Milimoe.FunGame.Server.Controller /// private void GetStore(Dictionary requestData, Dictionary resultData) { - // TODO + List stores = []; + + if (requestData.Count > 0) + { + long[] ids = DataRequest.GetDictionaryJsonObject(requestData, "ids") ?? []; + stores = SQLHelper?.GetStoresWithGoods(ids) ?? []; + } + + resultData.Add("result", stores.Count > 0); + resultData.Add("stores", stores); } /// @@ -1104,7 +1127,34 @@ namespace Milimoe.FunGame.Server.Controller /// private void GetMarket(Dictionary requestData, Dictionary resultData) { - // TODO + List markets = []; + + if (requestData.Count > 0) + { + long[] users = DataRequest.GetDictionaryJsonObject(requestData, "users") ?? []; + MarketItemState state = DataRequest.GetDictionaryJsonObject(requestData, "state"); + + if (users.Length > 0) + { + foreach (long userid in users) + { + markets.AddRange(SQLHelper?.GetAllMarketsItem(userid, state) ?? []); + } + } + else + { + markets = SQLHelper?.GetAllMarketsItem(0, state) ?? []; + } + + long[] items = DataRequest.GetDictionaryJsonObject(requestData, "items") ?? []; + if (items.Length > 0) + { + markets = [.. markets.Where(i => items.Contains(i.Id))]; + } + } + + resultData.Add("result", markets.Count > 0); + resultData.Add("markets", markets); } /// @@ -1114,7 +1164,122 @@ namespace Milimoe.FunGame.Server.Controller /// private void StoreBuy(Dictionary requestData, Dictionary resultData) { - // TODO + bool result = true; + double totalCost = 0; + List buyResult = []; + if (SQLHelper != null && requestData.Count > 0) + { + long storeid = DataRequest.GetDictionaryJsonObject(requestData, "storeid"); + long userid = DataRequest.GetDictionaryJsonObject(requestData, "userid"); + string currency = DataRequest.GetDictionaryJsonObject(requestData, "currency") ?? ""; + Dictionary counts = DataRequest.GetDictionaryJsonObject>(requestData, "counts") ?? []; + bool ignore = DataRequest.GetDictionaryJsonObject(requestData, "ignore"); + + Store? store = SQLHelper.GetStore(storeid); + User? user = SQLHelper.GetUserById(userid, true); + if (store != null && user != null) + { + try + { + SQLHelper.NewTransaction(); + + foreach (long goodsId in counts.Keys) + { + Goods goods = store.Goods[goodsId]; + int count = counts[goodsId]; + if (count > goods.Stock) + { + result = false; + buyResult.Add($"购买失败,原因:库存不足,当前库存为:{goods.Stock},购买数量:{count}。"); + continue; + } + if (goods.GetPrice(currency, out double price)) + { + bool subResult = true; + bool useCredits = true; + double totalPrice = price * count; + if (currency == General.GameplayEquilibriumConstant.InGameCurrency && user.Inventory.Credits >= totalPrice) + { + user.Inventory.Credits -= totalPrice; + } + else + { + subResult = false; + buyResult.Add($"购买失败,原因:需要花费 {totalPrice} {General.GameplayEquilibriumConstant.InGameCurrency},但是您只有 {user.Inventory.Credits} {General.GameplayEquilibriumConstant.InGameCurrency}。"); + } + if (currency == General.GameplayEquilibriumConstant.InGameMaterial && user.Inventory.Materials >= totalPrice) + { + user.Inventory.Materials -= totalPrice; + useCredits = false; + } + else + { + subResult = false; + buyResult.Add($"购买失败,原因:需要花费 {totalPrice} {General.GameplayEquilibriumConstant.InGameMaterial},但是您只有 {user.Inventory.Credits} {General.GameplayEquilibriumConstant.InGameMaterial}。"); + } + if (subResult) + { + goods.Stock -= count; + totalCost += totalPrice; + ProcessStoreBuy(goods, useCredits, price, count, user); + buyResult.Add($"成功消费:{totalPrice} {currency},购买了 {count} 个 {goods.Name}。"); + } + else + { + result = false; + } + } + } + + if (result || (!result && ignore)) + { + SQLHelper.UpdateInventory(user.Inventory); + } + + if (SQLHelper.Success) + { + SQLHelper.Commit(); + } + else + { + SQLHelper.Rollback(); + } + } + catch (Exception e) + { + SQLHelper.Rollback(); + ServerHelper.Error(e); + buyResult.Add("暂时无法处理此购买,请稍后再试。"); + } + } + } + resultData.Add("result", result); + resultData.Add("msg", string.Join("\r\n", buyResult)); + } + + /// + /// 处理商店购买 + /// + /// + /// + /// + /// + /// + private static void ProcessStoreBuy(Goods goods, bool useCredits, double price, int count, User user) + { + foreach (Item item in goods.Items) + { + for (int i = 0; i < count; i++) + { + Item newItem = item.Copy(); + newItem.IsTradable = false; + newItem.NextTradableTime = DateTimeUtility.GetTradableTime(); + newItem.Price = useCredits ? Calculation.Round2Digits(price * 0.35) : Calculation.Round2Digits(price * 7); + newItem.User = user; + newItem.EntityState = EntityState.Added; + user.Inventory.Items.Add(newItem); + } + } } /// @@ -1124,7 +1289,86 @@ namespace Milimoe.FunGame.Server.Controller /// private void MarketBuy(Dictionary requestData, Dictionary resultData) { - // TODO + bool result = false; + string msg = "无法购买此物品,请稍后再试。"; + if (SQLHelper != null && requestData.Count > 0) + { + Guid itemGuid = DataRequest.GetDictionaryJsonObject(requestData, "itemGuid"); + + MarketItem? marketItem = SQLHelper.GetMarketItem(itemGuid); + if (marketItem != null) + { + long userid = DataRequest.GetDictionaryJsonObject(requestData, "userid"); + double price = DataRequest.GetDictionaryJsonObject(requestData, "price"); + + try + { + User? buyer = SQLHelper.GetUserById(userid, true); + User? itemUser = SQLHelper.GetUserById(marketItem.User.Id, true); + if (itemUser != null && buyer != null && itemUser.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) + { + if (buyer.Inventory.Credits >= price) + { + buyer.Inventory.Credits -= price; + double fee = Calculation.Round2Digits(price * 0.15); + itemUser.Inventory.Credits += price - fee; + result = true; + } + else + { + msg = $"购买失败,原因:需要花费 {price} {General.GameplayEquilibriumConstant.InGameCurrency},但是您只有 {buyer.Inventory.Credits} {General.GameplayEquilibriumConstant.InGameCurrency}。"; + } + + if (result) + { + SQLHelper.NewTransaction(); + + try + { + item.EntityState = EntityState.Deleted; + SQLHelper.DeleteMarketItem(itemGuid); + + Item newItem = item.Copy(); + newItem.IsTradable = false; + newItem.NextTradableTime = DateTimeUtility.GetTradableTime(); + newItem.User = buyer; + newItem.EntityState = EntityState.Added; + buyer.Inventory.Items.Add(newItem); + + SQLHelper.UpdateInventory(itemUser.Inventory); + SQLHelper.UpdateInventory(buyer.Inventory); + } + catch + { + result = false; + } + + if (result) + { + msg = $"成功消费:{price} {General.GameplayEquilibriumConstant.InGameCurrency},购买了 {itemUser.Username} 出售的 {item.Name}。"; + SQLHelper.Commit(); + } + else + { + SQLHelper.Rollback(); + } + } + } + } + catch (Exception e) + { + SQLHelper.Rollback(); + ServerHelper.Error(e); + msg = "暂时无法处理此购买,请稍后再试。"; + } + } + else + { + msg = "目标物品不存在。"; + } + } + resultData.Add("result", result); + resultData.Add("msg", msg); } /// @@ -1134,7 +1378,18 @@ namespace Milimoe.FunGame.Server.Controller /// private void UpdateInventory(Dictionary requestData, Dictionary resultData) { - // TODO + string msg = "无法更新库存数据,请稍后再试。"; + if (SQLHelper != null && requestData.Count > 0) + { + Inventory inventory = DataRequest.GetDictionaryJsonObject(requestData, "inventory") ?? Factory.GetInventory(); + bool fullUpdate = DataRequest.GetDictionaryJsonObject(requestData, "fullUpdate"); + SQLHelper.UpdateInventory(inventory, fullUpdate); + if (SQLHelper.Success) + { + msg = ""; + } + } + resultData.Add("msg", msg); } /// @@ -1144,7 +1399,36 @@ namespace Milimoe.FunGame.Server.Controller /// private void Use(Dictionary requestData, Dictionary resultData) { - // TODO + bool result = false; + string msg = "无法使用此物品,请稍后再试。"; + if (SQLHelper != null && requestData.Count > 0) + { + Guid itemGuid = DataRequest.GetDictionaryJsonObject(requestData, "itemGuid"); + long userid = DataRequest.GetDictionaryJsonObject(requestData, "userid"); + Character[] targets = DataRequest.GetDictionaryJsonObject(requestData, "targets") ?? []; + long useCount = DataRequest.GetDictionaryJsonObject(requestData, "useCount"); + User? user = SQLHelper.GetUserById(userid, true); + if (user != null && user.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) + { + // 暂定标准实现是传这两个参数,作用目标和使用数量 + Dictionary args = new() + { + { "targets", targets }, + { "useCount", useCount } + }; + bool used = item.UseItem(args); + if (used) + { + SQLHelper.UpdateInventory(user.Inventory); + } + } + if (result) + { + msg = ""; + } + } + resultData.Add("result", result); + resultData.Add("msg", msg); } /// @@ -1154,7 +1438,33 @@ namespace Milimoe.FunGame.Server.Controller /// private void StoreSell(Dictionary requestData, Dictionary resultData) { - // TODO + bool result = false; + string msg = "无法出售此物品,请稍后再试。"; + if (SQLHelper != null && requestData.Count > 0) + { + Guid itemGuid = DataRequest.GetDictionaryJsonObject(requestData, "itemGuid"); + long userid = DataRequest.GetDictionaryJsonObject(requestData, "userid"); + User? user = SQLHelper.GetUserById(userid, true); + if (user != null && user.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) + { + if (!item.IsSellable) + { + msg = $"此物品无法出售{(item.NextSellableTime != DateTime.MinValue ? $",此物品将在 {item.NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售" : "")}。"; + } + else + { + double reward = item.Price; + user.Inventory.Credits += reward; + item.EntityState = EntityState.Deleted; + SQLHelper.UpdateInventory(user.Inventory); + } + } + if (result) + { + msg = ""; + } + } + resultData.Add("msg", msg); } /// @@ -1164,7 +1474,58 @@ namespace Milimoe.FunGame.Server.Controller /// private void MarketSell(Dictionary requestData, Dictionary resultData) { - // TODO + string msg = "无法上架此物品,请稍后再试。"; + if (requestData.Count > 0) + { + Guid itemGuid = DataRequest.GetDictionaryJsonObject(requestData, "itemGuid"); + long userid = DataRequest.GetDictionaryJsonObject(requestData, "userid"); + double price = DataRequest.GetDictionaryJsonObject(requestData, "price"); + User? user = SQLHelper?.GetUserById(userid, true); + if (user != null && user.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) + { + if (!item.IsSellable) + { + msg = $"此物品无法出售{(item.NextSellableTime != DateTime.MinValue ? $",此物品将在 {item.NextSellableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可出售" : "")}。"; + } + else + { + SQLHelper?.AddMarketItem(itemGuid, userid, price); + if (SQLHelper?.Success ?? false) + { + msg = ""; + } + } + } + } + resultData.Add("msg", msg); + } + + /// + /// 下架市场物品 + /// + /// + /// + private void MarketDelist(Dictionary requestData, Dictionary resultData) + { + string msg = "无法下架市场物品,请稍后再试。"; + if (requestData.Count > 0) + { + long userid = DataRequest.GetDictionaryJsonObject(requestData, "userid"); + if (userid != 0) + { + SQLHelper?.DeleteMarketItemByUserId(userid); + } + else + { + Guid itemGuid = DataRequest.GetDictionaryJsonObject(requestData, "itemGuid"); + if (itemGuid != Guid.Empty) SQLHelper?.DeleteMarketItem(itemGuid); + } + if (SQLHelper?.Success ?? false) + { + msg = ""; + } + } + resultData.Add("msg", msg); } /// @@ -1174,7 +1535,18 @@ namespace Milimoe.FunGame.Server.Controller /// private void UpdateMarketPrice(Dictionary requestData, Dictionary resultData) { - // TODO + string msg = "无法更新市场价格,请稍后再试。"; + if (requestData.Count > 0) + { + Guid itemGuid = DataRequest.GetDictionaryJsonObject(requestData, "itemGuid"); + double price = DataRequest.GetDictionaryJsonObject(requestData, "price"); + SQLHelper?.UpdateMarketItemPrice(itemGuid, price); + if (SQLHelper?.Success ?? false) + { + msg = ""; + } + } + resultData.Add("msg", msg); } /// @@ -1187,7 +1559,7 @@ namespace Milimoe.FunGame.Server.Controller string msg = "无法获取报价,请稍后再试。"; if (SQLHelper != null && requestData.Count >= 1) { - long offerId = DataRequest.GetDictionaryJsonObject(requestData, OffersQuery.Column_Id); + long offerId = DataRequest.GetDictionaryJsonObject(requestData, "id"); bool apiQuery = DataRequest.GetDictionaryJsonObject(requestData, "apiQuery"); Offer? offer = SQLHelper.GetOffer(offerId); if (offer != null) @@ -1222,7 +1594,7 @@ namespace Milimoe.FunGame.Server.Controller string msg = "无法创建报价,请稍后再试。"; if (SQLHelper != null && requestData.Count >= 1) { - long offeree = DataRequest.GetDictionaryJsonObject(requestData, OffersQuery.Column_Offeree); + long offeree = DataRequest.GetDictionaryJsonObject(requestData, "offeree"); long offeror = Server.User.Id; if (offeror != 0 && offeree != 0 && offeror != offeree) { @@ -1256,7 +1628,7 @@ namespace Milimoe.FunGame.Server.Controller string msg = "无法修改报价,请稍后再试。"; if (SQLHelper != null && requestData.Count >= 3) { - long offerId = DataRequest.GetDictionaryJsonObject(requestData, OffersQuery.Column_Id); + long offerId = DataRequest.GetDictionaryJsonObject(requestData, "id"); OfferActionType action = DataRequest.GetDictionaryJsonObject(requestData, "action"); List offerorItems = DataRequest.GetDictionaryJsonObject>(requestData, "offerorItems") ?? []; List offereeItems = DataRequest.GetDictionaryJsonObject>(requestData, "offereeItems") ?? []; @@ -1367,23 +1739,53 @@ namespace Milimoe.FunGame.Server.Controller if (canProceed) { - // 更新物品 - SQLHelper.DeleteOfferItemsByOfferId(offerId); - foreach (Guid itemGuid in offerorItems) + // 更新物品,同时对物品进行检查 + User? offeree = SQLHelper.GetUserById(offer.Offeree, true); + User? offeror = SQLHelper.GetUserById(offer.Offeror, true); + if (offeree != null && offeror != null) { - SQLHelper.AddOfferItem(offerId, offer.Offeror, itemGuid); - } - foreach (Guid itemGuid in offereeItems) - { - SQLHelper.AddOfferItem(offerId, offer.Offeree, itemGuid ); - } + SQLHelper.DeleteOfferItemsByOfferId(offerId); + foreach (Guid itemGuid in offerorItems) + { + if (offeror.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) + { + if (!item.IsTradable) + { + msg = $"此物品无法交易{(item.NextTradableTime != DateTime.MinValue ? $",此物品将在 {item.NextTradableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可交易" : "")}。"; + break; + } + else + { + SQLHelper.AddOfferItem(offerId, offer.Offeror, item.Guid); + } + } + } + foreach (Guid itemGuid in offereeItems) + { + if (offeree.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) + { + if (!item.IsTradable) + { + msg = $"此物品无法交易{(item.NextTradableTime != DateTime.MinValue ? $",此物品将在 {item.NextTradableTime.ToString(General.GeneralDateTimeFormatChinese)} 后可交易" : "")}。"; + break; + } + else + { + SQLHelper.AddOfferItem(offerId, offer.Offeree, item.Guid); + } + } + } - offer = SQLHelper.GetOffer(offerId); - if (offer != null) - { - SQLHelper.Commit(); - resultData.Add("offer", offer); - msg = ""; + if (msg != "") + { + offer = SQLHelper.GetOffer(offerId); + if (offer != null) + { + SQLHelper.Commit(); + resultData.Add("offer", offer); + msg = ""; + } + } } } @@ -1417,7 +1819,7 @@ namespace Milimoe.FunGame.Server.Controller string msg = "无法回应报价,请稍后再试。"; if (SQLHelper != null && requestData.Count >= 2) // 只需要 offerId 和 action { - long offerId = DataRequest.GetDictionaryJsonObject(requestData, OffersQuery.Column_Id); + long offerId = DataRequest.GetDictionaryJsonObject(requestData, "id"); OfferActionType action = DataRequest.GetDictionaryJsonObject(requestData, "action"); long userId = Server.User.Id; @@ -1470,15 +1872,14 @@ namespace Milimoe.FunGame.Server.Controller { if (offer.Status == OfferState.Completed) { - User offeree = Server.User; + User? offeree = SQLHelper.GetUserById(offer.Offeree, true); User? offeror = SQLHelper.GetUserById(offer.Offeror, true); - if (offeror != null) + if (offeree != null && offeror != null) { foreach (Guid itemGuid in offer.OffereeItems) { if (offeree.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) { - offeree.Inventory.Items.Remove(item); item.EntityState = EntityState.Deleted; Item newItem = item.Copy(); @@ -1487,15 +1888,14 @@ namespace Milimoe.FunGame.Server.Controller newItem.IsTradable = false; newItem.NextSellableTime = DateTimeUtility.GetTradableTime(); newItem.NextTradableTime = DateTimeUtility.GetTradableTime(); - offeror.Inventory.Items.Add(newItem); newItem.EntityState = EntityState.Added; + offeror.Inventory.Items.Add(newItem); } } foreach (Guid itemGuid in offer.OfferorItems) { if (offeror.Inventory.Items.FirstOrDefault(i => i.Guid == itemGuid) is Item item) { - offeror.Inventory.Items.Remove(item); item.EntityState = EntityState.Deleted; Item newItem = item.Copy(); @@ -1504,8 +1904,8 @@ namespace Milimoe.FunGame.Server.Controller newItem.IsTradable = false; newItem.NextSellableTime = DateTimeUtility.GetTradableTime(); newItem.NextTradableTime = DateTimeUtility.GetTradableTime(); - offeree.Inventory.Items.Add(newItem); newItem.EntityState = EntityState.Added; + offeree.Inventory.Items.Add(newItem); } } SQLHelper.UpdateInventory(offeror.Inventory); diff --git a/FunGame.Server/Main.cs b/FunGame.Server/Main.cs index b609e54..662dfbb 100644 --- a/FunGame.Server/Main.cs +++ b/FunGame.Server/Main.cs @@ -1,5 +1,4 @@ -using Milimoe.FunGame; -using Milimoe.FunGame.Core.Api.Utility; +using Milimoe.FunGame.Core.Api.Utility; using Milimoe.FunGame.Core.Library.Common.Network; using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Server.Controller; @@ -23,6 +22,28 @@ Console.CancelKeyPress += (sender, e) => Environment.Exit(0); // 退出程序 }; +FunGameSystem.CloseListener += async () => +{ + if (SocketListener != null) + { + foreach (ServerModel model in SocketListener.ClientList.Cast>()) + { + await model.Kick("服务器正在关闭。"); + } + SocketListener.Close(); + SocketListener = null; + } + if (WebSocketListener != null) + { + foreach (ServerModel model in WebSocketListener.ClientList.Cast>()) + { + await model.Kick("服务器正在关闭。"); + } + WebSocketListener.Close(); + WebSocketListener = null; + } +}; + while (Running) { string order = Console.ReadLine() ?? ""; @@ -30,21 +51,23 @@ while (Running) if (order != "" && Running) { order = order.ToLower(); + if (FunGameSystem.OrderList.TryGetValue(order, out Action? action) && action != null) + { + action(order); + } switch (order) { case OrderDictionary.Quit: case OrderDictionary.Exit: case OrderDictionary.Close: - Running = false; CloseServer(); break; case OrderDictionary.Restart: if (SocketListener is null || WebSocketListener is null) { - ServerHelper.WriteLine("重启服务器"); StartServer(); } - else ServerHelper.WriteLine("服务器正在运行,拒绝重启!"); + else ServerHelper.WriteLine("服务器正在运行,请手动结束服务器进程再启动!"); break; default: if (SocketListener != null) @@ -60,9 +83,6 @@ while (Running) } } -ServerHelper.WriteLine("服务器已关闭,按任意键退出程序。"); -Console.ReadKey(); - void StartServer() { TaskUtility.NewTask(async () => @@ -246,38 +266,18 @@ void StartServer() } catch (Exception e) { - if (e.Message.Equals(new ServerErrorException().Message)) - { - if (SocketListener != null) - { - SocketListener.Close(); - SocketListener = null; - } - if (WebSocketListener != null) - { - WebSocketListener.Close(); - WebSocketListener = null; - } - } ServerHelper.Error(e); + CloseServer(); } finally { - if (SocketListener != null) - { - SocketListener.Close(); - SocketListener = null; - } - if (WebSocketListener != null) - { - WebSocketListener.Close(); - WebSocketListener = null; - } + CloseServer(); } }); } void CloseServer() { + Running = false; FunGameSystem.CloseServer(); } diff --git a/FunGame.Server/Models/ConsoleModel.cs b/FunGame.Server/Models/ConsoleModel.cs index 411510f..ab19d95 100644 --- a/FunGame.Server/Models/ConsoleModel.cs +++ b/FunGame.Server/Models/ConsoleModel.cs @@ -1,7 +1,13 @@ -using Milimoe.FunGame.Core.Interface.Base; +using System.Text; +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.Common.Addon; +using Milimoe.FunGame.Core.Library.Constant; using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Services; +using ProjectRedbud.FunGame.SQLQueryExtension; namespace Milimoe.FunGame.Server.Model { @@ -45,9 +51,6 @@ namespace Milimoe.FunGame.Server.Model case OrderDictionary.ShowUsers2: ShowUsers(server); break; - case OrderDictionary.Help: - ShowHelp(); - break; default: break; } @@ -109,9 +112,90 @@ namespace Milimoe.FunGame.Server.Model } } - public static void ShowHelp() + public static void FirstRunRegAdmin() { - ServerHelper.WriteLine("Milimoe -> 帮助"); + using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper() ?? throw new SQLServiceException(); + ServerHelper.WriteLine("首次启动需要注册管理员账号,请按提示输入信息!", InvokeMessageType.Core); + string username, password, email; + ServerHelper.Write("请输入管理员用户名:", InvokeMessageType.Core); + while (true) + { + username = Console.ReadLine() ?? ""; + int usernameLength = NetworkUtility.GetUserNameLength(username); + if (usernameLength < 3 || usernameLength > 12) + { + ServerHelper.WriteLine("账号名长度不符合要求:3~12个字符数(一个中文2个字符)", InvokeMessageType.Error); + } + else + { + break; + } + } + ServerHelper.Write("请输入管理员邮箱:", InvokeMessageType.Core); + while (true) + { + email = Console.ReadLine() ?? ""; + if (!NetworkUtility.IsEmail(email)) + { + ServerHelper.WriteLine("这不是一个邮箱地址!", InvokeMessageType.Error); + } + else + { + break; + } + } + ServerHelper.Write("请输入管理员密码:", InvokeMessageType.Core); + while (true) + { + StringBuilder passwordBuilder = new(); + ConsoleKeyInfo key; + + do + { + key = Console.ReadKey(true); + + if (key.Key == ConsoleKey.Enter) + { + Console.WriteLine(); + break; + } + else if (key.Key == ConsoleKey.Backspace) + { + if (passwordBuilder.Length > 0) + { + passwordBuilder.Remove(passwordBuilder.Length - 1, 1); + Console.Write("\b \b"); + } + } + else if (!char.IsControl(key.KeyChar)) + { + passwordBuilder.Append(key.KeyChar); + Console.Write("*"); + } + } while (true); + + password = passwordBuilder.ToString(); + + if (password.Length < 6 || password.Length > 15) + { + ServerHelper.WriteLine("密码长度不符合要求:6~15个字符数", InvokeMessageType.Error); + } + else + { + break; + } + } + (string msg, RegInvokeType type, bool success) = DataRequestService.RegisterUser(sql, username, password, email, "localhost"); + ServerHelper.WriteLine(msg, InvokeMessageType.Core); + if (success) + { + User? user = sql.GetUserByUsernameAndEmail(username, email); + if (user != null) + { + user.IsAdmin = true; + sql.UpdateUser(user); + } + } } } } diff --git a/FunGame.Server/Models/ServerModel.cs b/FunGame.Server/Models/ServerModel.cs index 87f09b9..8237150 100644 --- a/FunGame.Server/Models/ServerModel.cs +++ b/FunGame.Server/Models/ServerModel.cs @@ -392,7 +392,7 @@ namespace Milimoe.FunGame.Server.Model Room Room = FunGameSystem.RoomList[roomid] ?? General.HallInstance; User.OnlineState = OnlineState.Online; // 是否是房主 - if (isMaster) + if (isMaster && Room.Roomid != "-1") { List users = [.. FunGameSystem.RoomList[roomid].UserAndIsReady.Keys]; User? newRoomMaster = null; diff --git a/FunGame.Server/Services/DataRequestService.cs b/FunGame.Server/Services/DataRequestService.cs index 0cb900b..2c9d2ad 100644 --- a/FunGame.Server/Services/DataRequestService.cs +++ b/FunGame.Server/Services/DataRequestService.cs @@ -12,9 +12,19 @@ namespace Milimoe.FunGame.Server.Services { public class DataRequestService { + /// + /// 获取插件取消请求的原因 + /// + /// + /// + /// + private static string GetPluginCancelString(DataRequestType type, GeneralEventArgs e) => $"{DataRequestSet.GetTypeString(type)} 请求已取消。{(e.EventMsg != "" ? $"原因:{e.EventMsg}" : "")}"; + + #region Register + 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; bool success = false; string clientName = ServerHelper.MakeClientName(clientIP); @@ -31,124 +41,14 @@ namespace Milimoe.FunGame.Server.Services using SQLHelper? sqlHelper = Factory.OpenFactory.GetSQLHelper(); using MailSender? mailSender = Factory.OpenFactory.GetMailSender(); + if (sqlHelper != null) { - // 如果没发验证码,就生成验证码 - if (verifyCode.Trim() == "") - { - // 先检查账号是否重复 - sqlHelper.ExecuteDataSet(UserQuery.Select_IsExistUsername(sqlHelper, username)); - if (sqlHelper.Result == SQLResult.Success) - { - ServerHelper.WriteLine(clientName + " 账号已被注册"); - msg = "此账号名已被使用!"; - type = RegInvokeType.DuplicateUserName; - } - else - { - // 检查邮箱是否重复 - sqlHelper.ExecuteDataSet(UserQuery.Select_IsExistEmail(sqlHelper, email)); - if (sqlHelper.Result == SQLResult.Success) - { - ServerHelper.WriteLine(clientName + " 邮箱已被注册"); - msg = "此邮箱已被注册!"; - type = RegInvokeType.DuplicateEmail; - } - else - { - // 检查验证码是否发送过 - sqlHelper.ExecuteDataSet(RegVerifyCodes.Select_HasSentRegVerifyCode(sqlHelper, username, email)); - if (sqlHelper.Result == SQLResult.Success && DateTime.TryParse(sqlHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime].ToString(), out DateTime RegTime) && (DateTime.Now - RegTime).TotalMinutes < 10) - { - string RegVerifyCode = sqlHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegVerifyCode].ToString() ?? ""; - ServerHelper.WriteLine(clientName + $" 十分钟内已向{email}发送过验证码:{RegVerifyCode}"); - type = RegInvokeType.InputVerifyCode; - } - else - { - // 发送验证码,需要先删除之前过期的验证码 - sqlHelper.NewTransaction(); - sqlHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(sqlHelper, username, email)); - string regVerify = Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 6); - sqlHelper.Execute(RegVerifyCodes.Insert_RegVerifyCode(sqlHelper, username, email, regVerify)); - if (sqlHelper.Result == SQLResult.Success) - { - sqlHelper.Commit(); - if (mailSender != null) - { - // 发送验证码 - string ServerName = Config.ServerName; - string Subject = $"[{ServerName}] 注册验证码"; - 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(clientName + $" 已向{email}发送验证码:{regVerify}"); - } - else - { - ServerHelper.WriteLine(clientName + " 无法发送验证码", InvokeMessageType.Error); - ServerHelper.WriteLine(mailSender.ErrorMsg, InvokeMessageType.Error); - } - } - else // 不使用MailSender的情况 - { - ServerHelper.WriteLine(clientName + $" 验证码为:{regVerify},请服务器管理员告知此用户"); - } - type = RegInvokeType.InputVerifyCode; - } - else sqlHelper.Rollback(); - } - } - } - } - else - { - // 先检查验证码 - sqlHelper.ExecuteDataSet(RegVerifyCodes.Select_RegVerifyCode(sqlHelper, username, email, verifyCode)); - if (sqlHelper.Result == SQLResult.Success) - { - if (!DateTime.TryParse(sqlHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime].ToString(), out DateTime RegTime)) - { - RegTime = General.DefaultTime; - } - // 检查验证码是否过期 - if ((DateTime.Now - RegTime).TotalMinutes >= 10) - { - ServerHelper.WriteLine(clientName + " 验证码已过期"); - msg = "此验证码已过期,请重新注册。"; - sqlHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(sqlHelper, username, email)); - type = RegInvokeType.None; - } - else - { - // 注册 - if (verifyCode.Equals(sqlHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegVerifyCode])) - { - sqlHelper.NewTransaction(); - ServerHelper.WriteLine("[Reg] Username: " + username + " Email: " + email); - FunGameSystem.UpdateUserKey(username); - password = password.Encrypt(FunGameSystem.GetUserKey(username)); - sqlHelper.RegisterUser(username, password, email, clientIP); - if (sqlHelper.Result == SQLResult.Success) - { - success = true; - msg = "注册成功!请牢记您的账号与密码!"; - sqlHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(sqlHelper, username, email)); - sqlHelper.Commit(); - } - else - { - sqlHelper.Rollback(); - msg = "服务器无法处理您的注册,注册失败!"; - } - } - else msg = "验证码不正确,请重新输入!"; - } - } - else if (sqlHelper.Result == SQLResult.NotFound) msg = "验证码不正确,请重新输入!"; - else msg = "服务器无法处理您的注册,注册失败!"; - } + (msg, type, success) = ProcessRegistration(sqlHelper, mailSender, username, password, email, verifyCode, clientIP, clientName); + } + else + { + msg = "服务器无法处理您的注册,注册失败!"; } eventArgs.Success = success; @@ -158,11 +58,152 @@ namespace Milimoe.FunGame.Server.Services return (msg, type, success); } + internal static (string Msg, RegInvokeType Type, bool Success) ProcessRegistration(SQLHelper sqlHelper, MailSender? mailSender, string username, string password, string email, string verifyCode, string clientIP, string clientName) + { + if (string.IsNullOrWhiteSpace(verifyCode)) + { + return HandleNoVerifyCode(sqlHelper, mailSender, username, email, clientName); + } + else + { + return HandleWithVerifyCode(sqlHelper, username, password, email, verifyCode, clientIP, clientName); + } + } + + internal static (string Msg, RegInvokeType Type, bool Success) HandleNoVerifyCode(SQLHelper sqlHelper, MailSender? mailSender, string username, string email, string clientName) + { + // 先检查账号是否重复 + sqlHelper.ExecuteDataSet(UserQuery.Select_IsExistUsername(sqlHelper, username)); + if (sqlHelper.Result == SQLResult.Success) + { + ServerHelper.WriteLine(clientName + " 账号已被注册"); + return ("此账号名已被使用!", RegInvokeType.DuplicateUserName, false); + } + + // 检查邮箱是否重复 + sqlHelper.ExecuteDataSet(UserQuery.Select_IsExistEmail(sqlHelper, email)); + if (sqlHelper.Result == SQLResult.Success) + { + ServerHelper.WriteLine(clientName + " 邮箱已被注册"); + return ("此邮箱已被注册!", RegInvokeType.DuplicateEmail, false); + } + + // 检查验证码是否发送过 + sqlHelper.ExecuteDataSet(RegVerifyCodes.Select_HasSentRegVerifyCode(sqlHelper, username, email)); + if (sqlHelper.Result == SQLResult.Success && DateTime.TryParse(sqlHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime].ToString(), out DateTime RegTime) && (DateTime.Now - RegTime).TotalMinutes < 10) + { + string RegVerifyCode = sqlHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegVerifyCode].ToString() ?? ""; + ServerHelper.WriteLine(clientName + $" 十分钟内已向{email}发送过验证码:{RegVerifyCode}"); + return ("", RegInvokeType.InputVerifyCode, false); + } + + // 发送验证码 + return SendVerificationCode(sqlHelper, mailSender, username, email, clientName); + } + + internal static (string Msg, RegInvokeType Type, bool Success) SendVerificationCode(SQLHelper sqlHelper, MailSender? mailSender, string username, string email, string clientName) + { + sqlHelper.NewTransaction(); + sqlHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(sqlHelper, username, email)); + string regVerify = Verification.CreateVerifyCode(VerifyCodeType.NumberVerifyCode, 6); + sqlHelper.Execute(RegVerifyCodes.Insert_RegVerifyCode(sqlHelper, username, email, regVerify)); + + if (sqlHelper.Result == SQLResult.Success) + { + sqlHelper.Commit(); + + if (mailSender != null) + { + // 发送验证码 + string ServerName = Config.ServerName; + string Subject = $"[{ServerName}] 注册验证码"; + 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(clientName + $" 已向{email}发送验证码:{regVerify}"); + } + else + { + ServerHelper.WriteLine(clientName + " 无法发送验证码", InvokeMessageType.Error); + ServerHelper.WriteLine(mailSender.ErrorMsg, InvokeMessageType.Error); + } + } + else // 不使用MailSender的情况 + { + ServerHelper.WriteLine(clientName + $" 验证码为:{regVerify},请服务器管理员告知此用户"); + } + return ("", RegInvokeType.InputVerifyCode, false); + } + else + { + sqlHelper.Rollback(); + return ("发送验证码失败!", RegInvokeType.None, false); + } + } + + internal static (string Msg, RegInvokeType Type, bool Success) HandleWithVerifyCode(SQLHelper sqlHelper, string username, string password, string email, string verifyCode, string clientIP, string clientName) + { + // 先检查验证码 + sqlHelper.ExecuteDataSet(RegVerifyCodes.Select_RegVerifyCode(sqlHelper, username, email, verifyCode)); + if (sqlHelper.Result == SQLResult.Success) + { + if (!DateTime.TryParse(sqlHelper.DataSet.Tables[0].Rows[0][RegVerifyCodes.Column_RegTime].ToString(), out DateTime RegTime)) + { + RegTime = General.DefaultTime; + } + + // 检查验证码是否过期 + if ((DateTime.Now - RegTime).TotalMinutes >= 10) + { + ServerHelper.WriteLine(clientName + " 验证码已过期"); + sqlHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(sqlHelper, username, email)); + return ("此验证码已过期,请重新注册。", RegInvokeType.None, false); + } + + // 注册 + return RegisterUser(sqlHelper, username, password, email, clientIP); + } + else if (sqlHelper.Result == SQLResult.NotFound) + { + return ("验证码不正确,请重新输入!", RegInvokeType.None, false); + } + else + { + return ("服务器无法处理您的注册,注册失败!", RegInvokeType.None, false); + } + } + + internal static (string Msg, RegInvokeType Type, bool Success) RegisterUser(SQLHelper sqlHelper, string username, string password, string email, string clientIP) + { + sqlHelper.NewTransaction(); + ServerHelper.WriteLine("[Reg] Username: " + username + " Email: " + email); + FunGameSystem.UpdateUserKey(username); + password = password.Encrypt(FunGameSystem.GetUserKey(username)); + sqlHelper.RegisterUser(username, password, email, clientIP); + + if (sqlHelper.Result == SQLResult.Success) + { + sqlHelper.Execute(RegVerifyCodes.Delete_RegVerifyCode(sqlHelper, username, email)); + sqlHelper.Commit(); + return ("注册成功!请牢记您的账号与密码!", RegInvokeType.None, true); + } + else + { + sqlHelper.Rollback(); + return ("服务器无法处理您的注册,注册失败!", RegInvokeType.None, false); + } + } + + #endregion + + #region Login + 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 = "用户名或密码不正确。"; + string msg; Guid key = Guid.Empty; LoginEventArgs eventArgs = new(username, password, autokey); @@ -175,38 +216,7 @@ namespace Milimoe.FunGame.Server.Services 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); - } - } - } - } - } + (success, dsUser, msg, key) = ProcessLogin(username, password, autokey); eventArgs.Success = success; FunGameSystem.ServerPluginLoader?.OnAfterLoginEvent(sender, eventArgs); @@ -216,6 +226,71 @@ namespace Milimoe.FunGame.Server.Services return (success, dsUser, msg, key); } - private static string GetPluginCancelString(DataRequestType type, GeneralEventArgs e) => $"{DataRequestSet.GetTypeString(type)} 请求已取消。{(e.EventMsg != "" ? $"原因:{e.EventMsg}" : "")}"; + internal static (bool Success, DataSet DataSet, string Msg, Guid Key) ProcessLogin(string username, string password, string autokey) + { + bool success = false; + DataSet dsUser = new(); + string msg = "用户名或密码不正确。"; + Guid key = Guid.Empty; + + if (!string.IsNullOrEmpty(username) && !string.IsNullOrEmpty(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) + { + (success, dsUser, msg, key) = ValidateLogin(sqlHelper, username, password, autokey); + } + else + { + msg = "无法连接到数据库,登录失败!"; + } + } + + return (success, dsUser, msg, key); + } + + internal static (bool Success, DataSet DataSet, string Msg, Guid Key) ValidateLogin(SQLHelper sqlHelper, string username, string password, string autokey) + { + bool success = false; + DataSet dsUser = new(); + string msg = "用户名或密码不正确。"; + Guid key = Guid.NewGuid(); + + sqlHelper.ExecuteDataSet(UserQuery.Select_Users_LoginQuery(sqlHelper, username, password)); + if (sqlHelper.Result == SQLResult.Success) + { + dsUser = sqlHelper.DataSet; + success = true; + msg = ""; + + if (!string.IsNullOrWhiteSpace(autokey)) + { + (success, msg) = CheckAutoKey(sqlHelper, username, autokey); + } + } + + return (success, dsUser, msg, key); + } + + internal static (bool Success, string Msg) CheckAutoKey(SQLHelper sqlHelper, string username, string autokey) + { + sqlHelper.ExecuteDataSet(UserQuery.Select_CheckAutoKey(sqlHelper, username, autokey)); + if (sqlHelper.Result == SQLResult.Success) + { + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] AutoKey: 已确认"); + return (true, ""); + } + else + { + string msg = "AutoKey 不正确,拒绝自动登录!"; + ServerHelper.WriteLine("[" + DataRequestSet.GetTypeString(DataRequestType.Login_Login) + "] " + msg); + return (false, msg); + } + } + + #endregion } } diff --git a/FunGame.Server/Services/FunGameSystem.cs b/FunGame.Server/Services/FunGameSystem.cs index ec2238a..bf9f788 100644 --- a/FunGame.Server/Services/FunGameSystem.cs +++ b/FunGame.Server/Services/FunGameSystem.cs @@ -1,11 +1,11 @@ -using System.Collections; -using Milimoe.FunGame.Core.Api.Transmittal; +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.Model; using Milimoe.FunGame.Server.Others; using Milimoe.FunGame.Server.Services.DataUtility; @@ -13,10 +13,13 @@ namespace Milimoe.FunGame.Server.Services { public class FunGameSystem { + public delegate Task CloseListenerHandler(); + public static event CloseListenerHandler? CloseListener; + /// /// 服务器指令列表 /// - public static Hashtable OrderList { get; } = []; + public static Dictionary> OrderList { get; } = []; /// /// 在线房间列表 @@ -59,9 +62,9 @@ namespace Milimoe.FunGame.Server.Services public const string FunGameWebAPITokenID = "fungame_web_api"; /// - /// API Secret 字段名 + /// API Secret 文件名 /// - public const string APISecretField = "api_secret"; + public const string APISecretFileName = ".apisecret"; /// /// 初始化数据库连接器 @@ -286,12 +289,12 @@ namespace Milimoe.FunGame.Server.Services /// 检查是否存在 API Secret Key /// /// - public static bool IsAPISecretKeyExist(string key) + public static bool APISecretKeyExists(string key) { using SQLHelper? sql = Factory.OpenFactory.GetSQLHelper(); if (sql != null) { - key = Encryption.HmacSha256(key, LocalConfig.Get(APISecretField) ?? ""); + key = Encryption.HmacSha256(key, Encryption.FileSha256(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, APISecretFileName))); sql.ExecuteDataSet(ApiTokens.Select_GetAPISecretKey(sql, key)); if (sql.Result == SQLResult.Success) { @@ -302,18 +305,18 @@ namespace Milimoe.FunGame.Server.Services } /// - /// 设置 API Secret Key + /// 创建 API Secret Key /// /// /// /// - public static string SetAPISecretKey(string token, string reference1 = "", string reference2 = "", SQLHelper? sqlHelper = null) + public static string CreateAPISecretKey(string token, string reference1 = "", string reference2 = "", SQLHelper? sqlHelper = null) { bool useSQLHelper = sqlHelper != null; sqlHelper ??= Factory.OpenFactory.GetSQLHelper(); string key = Encryption.GenerateRandomString(); - string enKey = Encryption.HmacSha256(key, LocalConfig.Get(APISecretField) ?? ""); - if (sqlHelper != null) + string enKey = Encryption.HmacSha256(key, Encryption.FileSha256(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, APISecretFileName))); + if (sqlHelper != null && enKey != "") { sqlHelper.ExecuteDataSet(ApiTokens.Select_GetAPIToken(sqlHelper, token)); if (sqlHelper.Success) @@ -325,6 +328,10 @@ namespace Milimoe.FunGame.Server.Services sqlHelper.Execute(ApiTokens.Insert_APIToken(sqlHelper, token, enKey, reference1, reference2)); } } + else + { + ServerHelper.WriteLine($"API Secret Key '{token}' 创建失败,未连接到数据库或者找不到加密秘钥。", InvokeMessageType.Error); + } if (!useSQLHelper) { sqlHelper?.Dispose(); @@ -333,7 +340,7 @@ namespace Milimoe.FunGame.Server.Services } /// - /// 创建 SQL 服务后需要做的事 + /// 创建 SQL 服务后需要做的事,包括数据库初始化,API 初始化,首次建立管理员账户等等 /// /// public static void AfterCreateSQLService(SQLHelper sqlHelper) @@ -351,8 +358,12 @@ namespace Milimoe.FunGame.Server.Services { mysqlHelper.ExecuteSqlFile(AppDomain.CurrentDomain.BaseDirectory + "fungame.sql"); } - LocalConfig.Add(APISecretField, Encryption.GenerateRandomString()); - SetAPISecretKey(FunGameWebAPITokenID, sqlHelper: sqlHelper); + ConsoleModel.FirstRunRegAdmin(); + using StreamWriter sw = File.CreateText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, APISecretFileName)); + sw.WriteLine(Encryption.GenerateRandomString()); + sw.Flush(); + sw.Close(); + ServerHelper.WriteLine($"已生成一个默认的 API Token,Token ID: {FunGameWebAPITokenID}, Secret Key: 【{CreateAPISecretKey(FunGameWebAPITokenID, sqlHelper: sqlHelper)}】,请妥善保管方括号内的内容,仅显示一次。如遗忘需要使用管理员账号重置。"); sqlHelper.Execute(Configs.Insert_Config(sqlHelper, "Initialization", FunGameInfo.FunGame_Version, "SQL Service Installed.")); SQLConfig.Clear(); SQLConfig.Add("Initialized", true); @@ -365,6 +376,10 @@ namespace Milimoe.FunGame.Server.Services sqlHelper.Dispose(); } + /// + /// 数据库是否存在 + /// + /// public static bool DatabaseExists() { SQLConfig.LoadConfig(); @@ -401,6 +416,8 @@ namespace Milimoe.FunGame.Server.Services plugin.Controller.Close(); } } + // 停止所有正在运行的监听 + CloseListener?.Invoke(); } } } diff --git a/FunGame.Server/Services/General.cs b/FunGame.Server/Services/General.cs index a3201bc..efe0dcc 100644 --- a/FunGame.Server/Services/General.cs +++ b/FunGame.Server/Services/General.cs @@ -234,11 +234,11 @@ namespace Milimoe.FunGame.Server.Services public static void InitOrderList() { FunGameSystem.OrderList.Clear(); - FunGameSystem.OrderList.Add(OrderDictionary.Help, "Milimoe -> 帮助"); - FunGameSystem.OrderList.Add(OrderDictionary.Quit, "关闭服务器"); - FunGameSystem.OrderList.Add(OrderDictionary.Exit, "关闭服务器"); - FunGameSystem.OrderList.Add(OrderDictionary.Close, "关闭服务器"); - FunGameSystem.OrderList.Add(OrderDictionary.Restart, "重启服务器"); + FunGameSystem.OrderList.Add(OrderDictionary.Help, s => WriteLine("Milimoe -> 帮助")); + FunGameSystem.OrderList.Add(OrderDictionary.Quit, s => WriteLine("关闭服务器")); + FunGameSystem.OrderList.Add(OrderDictionary.Exit, s => WriteLine("关闭服务器")); + FunGameSystem.OrderList.Add(OrderDictionary.Close, s => WriteLine("关闭服务器")); + FunGameSystem.OrderList.Add(OrderDictionary.Restart, s => WriteLine("重启服务器")); } } diff --git a/FunGame.WebAPI/Program.cs b/FunGame.WebAPI/Program.cs index e7a000a..e7bf6f5 100644 --- a/FunGame.WebAPI/Program.cs +++ b/FunGame.WebAPI/Program.cs @@ -247,6 +247,16 @@ try // 开始监听连接 listener.BannedList.AddRange(Config.ServerBannedList); + FunGameSystem.CloseListener += async () => + { + foreach (ServerModel model in listener.ClientList.Cast>()) + { + await model.Kick("服务器正在关闭。"); + } + listener.Close(); + await app.StopAsync(); + }; + Task order = Task.Factory.StartNew(GetConsoleOrder); app.Run(); @@ -265,8 +275,20 @@ async Task GetConsoleOrder() if (order != "") { order = order.ToLower(); + if (FunGameSystem.OrderList.TryGetValue(order, out Action? action) && action != null) + { + action(order); + } switch (order) { + case OrderDictionary.Quit: + case OrderDictionary.Exit: + case OrderDictionary.Close: + CloseServer(); + break; + case OrderDictionary.Restart: + ServerHelper.WriteLine("服务器正在运行,请手动结束服务器进程再启动!"); + break; default: await ConsoleModel.Order(listener, order); break; diff --git a/FunGame.WebAPI/Services/APIBearerTokenHandler.cs b/FunGame.WebAPI/Services/APIBearerTokenHandler.cs index d12bc51..92d93b3 100644 --- a/FunGame.WebAPI/Services/APIBearerTokenHandler.cs +++ b/FunGame.WebAPI/Services/APIBearerTokenHandler.cs @@ -25,7 +25,7 @@ namespace Milimoe.FunGame.WebAPI.Services string key = authorizationHeader["Bearer ".Length..].Trim(); // 验证 API Bearer Token - if (key == "" || !FunGameSystem.IsAPISecretKeyExist(key)) + if (key == "" || !FunGameSystem.APISecretKeyExists(key)) { await Task.CompletedTask; return AuthenticateResult.Fail("Invalid Token.");