From dad9efe7a9cd4d6d48d43c8caee2d225e1cc92e3 Mon Sep 17 00:00:00 2001 From: StopWuyu Date: Sun, 17 Aug 2025 15:54:30 +0800 Subject: [PATCH] feat: gateserver & dispatch server isolation --- Common/Configuration/ConfigContainer.cs | 17 +++ Common/Util/HttpNetwork.cs | 19 ++++ .../Recv/Player/HandlerPlayerGetTokenCsReq.cs | 38 +++++-- Program/Program/EntryPoint.cs | 100 ++++++++++-------- WebServer/Controllers/DispatchRoutes.cs | 31 ++++-- WebServer/Controllers/GateServerRoutes.cs | 18 +++- WebServer/Controllers/ServerExchangeRoutes.cs | 44 ++++++++ WebServer/Handler/QueryGatewayHandler.cs | 2 +- 8 files changed, 205 insertions(+), 64 deletions(-) create mode 100644 Common/Util/HttpNetwork.cs create mode 100644 WebServer/Controllers/ServerExchangeRoutes.cs diff --git a/Common/Configuration/ConfigContainer.cs b/Common/Configuration/ConfigContainer.cs index c0432a47..89de91b7 100644 --- a/Common/Configuration/ConfigContainer.cs +++ b/Common/Configuration/ConfigContainer.cs @@ -84,6 +84,7 @@ public class ServerOption public ServerProfile ServerProfile { get; set; } = new(); public bool AutoCreateUser { get; set; } = true; public LogOption LogOption { get; set; } = new(); + public ServerConfig ServerConfig { get; set; } = new(); public int FarmingDropRate { get; set; } = 1; public bool UseCache { get; set; } = false; // didnt recommend @@ -93,6 +94,22 @@ public class ServerOption } } +public class ServerConfig +{ + public bool RunDispatch { get; set; } = true; + public string FromDispatchBaseUrl { get; set; } = ""; + public bool RunGateway { get; set; } = true; // if run gateway, also run game server + public List Regions { get; set; } = []; +} + +public class ServerRegion +{ + public string GateWayAddress { get; set; } = ""; + public string GameServerName { get; set; } = ""; + public string GameServerId { get; set; } = ""; + public int EnvType { get; set; } = 21; +} + public class LogOption { #if DEBUG diff --git a/Common/Util/HttpNetwork.cs b/Common/Util/HttpNetwork.cs new file mode 100644 index 00000000..c859e3b0 --- /dev/null +++ b/Common/Util/HttpNetwork.cs @@ -0,0 +1,19 @@ +namespace EggLink.DanhengServer.Util; + +public static class HttpNetwork +{ + public static async ValueTask<(int, string?)> SendGetRequest(string url) + { + try + { + using var client = new HttpClient(); + var response = await client.GetAsync(url); + var content = await response.Content.ReadAsStringAsync(); + return ((int)response.StatusCode, content); + } + catch (Exception ex) + { + return (500, ex.Message); + } + } +} \ No newline at end of file diff --git a/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs b/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs index d670759f..322425d6 100644 --- a/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs +++ b/GameServer/Server/Packet/Recv/Player/HandlerPlayerGetTokenCsReq.cs @@ -13,24 +13,48 @@ namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Player; [Opcode(CmdIds.PlayerGetTokenCsReq)] public class HandlerPlayerGetTokenCsReq : Handler { - private readonly Logger logger = new("GameServer"); + private readonly Logger _logger = new("GameServer"); public override async Task OnHandle(Connection connection, byte[] header, byte[] data) { var req = PlayerGetTokenCsReq.Parser.ParseFrom(data); - var account = DatabaseHelper.Instance?.GetInstance(int.Parse(req.AccountUid)); - if (account == null) + // call dispatch /get_account_info api to get account info + int uid; + + if (ConfigManager.Config.ServerOption.ServerConfig.RunDispatch || string.IsNullOrEmpty(ConfigManager.Config.ServerOption.ServerConfig.FromDispatchBaseUrl)) { - await connection.SendPacket(new PacketPlayerGetTokenScRsp(0, Retcode.RetNotInWhiteList)); - return; + // dispatch running, use local db + var account = DatabaseHelper.Instance?.GetInstance(int.Parse(req.AccountUid)); + if (account == null) + { + await connection.SendPacket(new PacketPlayerGetTokenScRsp(0, Retcode.RetNotInWhiteList)); + return; + } + + uid = account.Uid; } + else + { + // dispatch not running, use dispatch api + var dispatchUrl = ConfigManager.Config.ServerOption.ServerConfig.FromDispatchBaseUrl; + var targetUrl = $"{dispatchUrl}/get_account_info?accountUid={req.AccountUid}"; + var res = await HttpNetwork.SendGetRequest(targetUrl); + if (res.Item1 != 200 || res.Item2 == null) + { + await connection.SendPacket(new PacketPlayerGetTokenScRsp(0, Retcode.RetNotInWhiteList)); + return; + } + + uid = int.Parse(res.Item2); + } + if (!ResourceManager.IsLoaded) // resource manager not loaded, return return; - var prev = Listener.GetActiveConnection(account.Uid); + var prev = Listener.GetActiveConnection(uid); if (prev != null) { await prev.SendPacket(new PacketPlayerKickOutScNotify()); @@ -52,7 +76,7 @@ public class HandlerPlayerGetTokenCsReq : Handler if (ConfigManager.Config.GameServer.UsePacketEncryption) { connection.XorKey = Crypto.GenerateXorKey(connection.ClientSecretKeySeed); - logger.Info($"{connection.RemoteEndPoint} key exchange successful"); + _logger.Info($"{connection.RemoteEndPoint} key exchange successful"); } } } \ No newline at end of file diff --git a/Program/Program/EntryPoint.cs b/Program/Program/EntryPoint.cs index c40b9f70..ad522a99 100644 --- a/Program/Program/EntryPoint.cs +++ b/Program/Program/EntryPoint.cs @@ -167,64 +167,71 @@ public class EntryPoint Logger.Info(I18NManager.Translate("Server.ServerInfo.ServerRunning", I18NManager.Translate("Word.Dispatch"), GetConfig().HttpServer.GetDisplayAddress())); - var handler = new DanhengListener.ConnectionCreatedHandler((conversation, remote) => new Connection(conversation, remote)); - DanhengListener.CreateConnection = handler; - DanhengListener.StartListener(); + if (ConfigManager.Config.ServerOption.ServerConfig.RunGateway) + { + var handler = new DanhengListener.ConnectionCreatedHandler((conversation, remote) => new Connection(conversation, remote)); + DanhengListener.CreateConnection = handler; + DanhengListener.StartListener(); + } GenerateLogMap(); // Load the game data - try + if (ConfigManager.Config.ServerOption.ServerConfig.RunGateway) { - var isCache = false; - if (File.Exists(ResourceCache.CachePath)) - if (ConfigManager.Config.ServerOption.UseCache) + try + { + var isCache = false; + if (File.Exists(ResourceCache.CachePath)) + if (ConfigManager.Config.ServerOption.UseCache) + { + Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", + I18NManager.Translate("Word.Cache"))); + isCache = ResourceCache.LoadCache(); + + // Clear all game data if cache loading fails + if (!isCache) + { + ResourceCache.ClearGameData(); + Logger.Warn(I18NManager.Translate("Server.ServerInfo.CacheLoadFailed")); + } + } + else + { + File.Delete(ResourceCache.CachePath); + Logger.Warn(I18NManager.Translate("Server.ServerInfo.CacheLoadSkip")); + } + + if (!isCache) { Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", - I18NManager.Translate("Word.Cache"))); - isCache = ResourceCache.LoadCache(); + I18NManager.Translate("Word.GameData"))); + ResourceManager.LoadGameData(); - // Clear all game data if cache loading fails - if (!isCache) + // Async process cache saving + if (ConfigManager.Config.ServerOption.UseCache && ResourceCache.IsComplete) { - ResourceCache.ClearGameData(); - Logger.Warn(I18NManager.Translate("Server.ServerInfo.CacheLoadFailed")); + Logger.Warn(I18NManager.Translate("Server.ServerInfo.WaitingItem", + I18NManager.Translate("Word.Cache"))); + _ = ResourceCache.SaveCache(); } } - else - { - File.Delete(ResourceCache.CachePath); - Logger.Warn(I18NManager.Translate("Server.ServerInfo.CacheLoadSkip")); - } - - if (!isCache) - { - Logger.Info(I18NManager.Translate("Server.ServerInfo.LoadingItem", - I18NManager.Translate("Word.GameData"))); - ResourceManager.LoadGameData(); - - // Async process cache saving - if (ConfigManager.Config.ServerOption.UseCache && ResourceCache.IsComplete) - { - Logger.Warn(I18NManager.Translate("Server.ServerInfo.WaitingItem", - I18NManager.Translate("Word.Cache"))); - _ = ResourceCache.SaveCache(); - } } - } - catch (Exception e) - { - Logger.Error( - I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.GameData")), e); - Console.ReadLine(); - return; - } + catch (Exception e) + { + Logger.Error( + I18NManager.Translate("Server.ServerInfo.FailedToLoadItem", I18NManager.Translate("Word.GameData")), + e); + Console.ReadLine(); + return; + } - // check option - if (args.Contains("-generate-tourn")) - { - TournRoomGenerator.GenerateFile("RogueTournRoom.json"); - return; + // check option + if (args.Contains("-generate-tourn")) + { + TournRoomGenerator.GenerateFile("RogueTournRoom.json"); + return; + } } // Register the command handlers @@ -313,7 +320,8 @@ public class EntryPoint }; // generate the handbook - new Task(HandbookGenerator.GenerateAll).Start(); + if (ConfigManager.Config.ServerOption.ServerConfig.RunGateway) + new Task(HandbookGenerator.GenerateAll).Start(); if (!DatabaseHelper.LoadAllData) { diff --git a/WebServer/Controllers/DispatchRoutes.cs b/WebServer/Controllers/DispatchRoutes.cs index c73e0b5e..63c1d9e6 100644 --- a/WebServer/Controllers/DispatchRoutes.cs +++ b/WebServer/Controllers/DispatchRoutes.cs @@ -14,20 +14,37 @@ namespace EggLink.DanhengServer.WebServer.Controllers; [Route("/")] public class DispatchRoutes { - public static ConfigContainer config = ConfigManager.Config; + public static ConfigContainer Config = ConfigManager.Config; public static Logger Logger = new("DispatchServer"); [HttpGet("query_dispatch")] public string QueryDispatch() { + if (!Config.ServerOption.ServerConfig.RunDispatch) + return ""; + var data = new Dispatch(); - data.RegionList.Add(new RegionInfo + + if (Config.ServerOption.ServerConfig.RunGateway) + data.RegionList.Add(new RegionInfo + { + Name = Config.GameServer.GameServerId, + DispatchUrl = $"{Config.HttpServer.GetDisplayAddress()}/query_gateway", + EnvType = "21", + DisplayName = Config.GameServer.GameServerName + }); + + foreach (var region in Config.ServerOption.ServerConfig.Regions) { - Name = config.GameServer.GameServerId, - DispatchUrl = $"{config.HttpServer.GetDisplayAddress()}/query_gateway", - EnvType = "21", - DisplayName = config.GameServer.GameServerName - }); + data.RegionList.Add(new RegionInfo + { + Name = region.GameServerId, + DisplayName = region.GameServerName, + EnvType = region.EnvType.ToString(), + DispatchUrl = region.GateWayAddress, + }); + } + Logger.Info("Client request: query_dispatch"); return Convert.ToBase64String(data.ToByteArray()); } diff --git a/WebServer/Controllers/GateServerRoutes.cs b/WebServer/Controllers/GateServerRoutes.cs index c31c4b67..0dba6fa7 100644 --- a/WebServer/Controllers/GateServerRoutes.cs +++ b/WebServer/Controllers/GateServerRoutes.cs @@ -1,4 +1,5 @@ -using EggLink.DanhengServer.WebServer.Handler; +using EggLink.DanhengServer.Util; +using EggLink.DanhengServer.WebServer.Handler; using Microsoft.AspNetCore.Mvc; namespace EggLink.DanhengServer.WebServer.Controllers; @@ -8,9 +9,20 @@ namespace EggLink.DanhengServer.WebServer.Controllers; public class GateServerRoutes { [HttpGet("/query_gateway")] - public async ValueTask QueryGateway([FromQuery] string version) + public async ValueTask QueryGateway([FromQuery] string version) { + if (!ConfigManager.Config.ServerOption.ServerConfig.RunGateway) + return new ContentResult + { + StatusCode = 404 + }; + await ValueTask.CompletedTask; - return new QueryGatewayHandler(version).Data; + return new ContentResult + { + Content = new QueryGatewayHandler(version).Data, + StatusCode = 200, + ContentType = "plain/text; charset=utf-8" + }; } } \ No newline at end of file diff --git a/WebServer/Controllers/ServerExchangeRoutes.cs b/WebServer/Controllers/ServerExchangeRoutes.cs new file mode 100644 index 00000000..9ad5c7a5 --- /dev/null +++ b/WebServer/Controllers/ServerExchangeRoutes.cs @@ -0,0 +1,44 @@ +using EggLink.DanhengServer.Database; +using EggLink.DanhengServer.Database.Account; +using EggLink.DanhengServer.Util; +using Microsoft.AspNetCore.Mvc; + +namespace EggLink.DanhengServer.WebServer.Controllers; + +[ApiController] +[Route("/")] +public class ServerExchangeRoutes +{ + [HttpGet("/get_account_info")] + public async ValueTask GetAccountInfo([FromQuery] string accountUid) + { + if (!ConfigManager.Config.ServerOption.ServerConfig.RunDispatch) + return new ContentResult + { + StatusCode = 404 + }; + + if (string.IsNullOrEmpty(accountUid) || !int.TryParse(accountUid, out var uid)) + return new ContentResult + { + StatusCode = 400 + }; + + var account = DatabaseHelper.Instance?.GetInstance(uid); + if (account == null) + return new ContentResult + { + StatusCode = 404, + Content = "Account not found" + }; + + await ValueTask.CompletedTask; + + return new ContentResult + { + Content = account.Uid.ToString(), + StatusCode = 200, + ContentType = "plain/text; charset=utf-8" + }; + } +} \ No newline at end of file diff --git a/WebServer/Handler/QueryGatewayHandler.cs b/WebServer/Handler/QueryGatewayHandler.cs index e11da1ee..27b75b5d 100644 --- a/WebServer/Handler/QueryGatewayHandler.cs +++ b/WebServer/Handler/QueryGatewayHandler.cs @@ -24,7 +24,7 @@ internal partial class QueryGatewayHandler RegionName = config.GameServer.GameServerId, Ip = config.GameServer.PublicAddress, Port = config.GameServer.Port, - LoginWhiteMsg = I18NManager.Translate("Server.Web.Maintain"), + Msg = I18NManager.Translate("Server.Web.Maintain"), EnableVersionUpdate = true, EnableUploadBattleLog = true, EnableDesignDataVersionUpdate = true,