feat: gateserver & dispatch server isolation

This commit is contained in:
StopWuyu
2025-08-17 15:54:30 +08:00
parent b5dac65fec
commit dad9efe7a9
8 changed files with 205 additions and 64 deletions

View File

@@ -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<ServerRegion> 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

View File

@@ -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);
}
}
}

View File

@@ -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<AccountData>(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<AccountData>(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");
}
}
}

View File

@@ -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)
{

View File

@@ -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());
}

View File

@@ -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<string> QueryGateway([FromQuery] string version)
public async ValueTask<ContentResult> 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"
};
}
}

View File

@@ -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<ContentResult> 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<AccountData>(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"
};
}
}

View File

@@ -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,