mirror of
https://github.com/EggLinks/DanhengServer-OpenSource.git
synced 2026-01-02 20:26:03 +08:00
feat: gateserver & dispatch server isolation
This commit is contained in:
@@ -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
|
||||
|
||||
19
Common/Util/HttpNetwork.cs
Normal file
19
Common/Util/HttpNetwork.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
};
|
||||
}
|
||||
}
|
||||
44
WebServer/Controllers/ServerExchangeRoutes.cs
Normal file
44
WebServer/Controllers/ServerExchangeRoutes.cs
Normal 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"
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user