Initial commit: Client Doc docs Server Tools

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
ud18010
2026-07-10 10:24:29 +08:00
co-authored by Cursor
commit 7e35d8da31
3374 changed files with 680813 additions and 0 deletions
+86
View File
@@ -0,0 +1,86 @@
using System;
using System.IO;
using System.Threading;
using System.Threading.Tasks;
using XWorld.Server.Gateway;
namespace XWorld.Server.Gateway.Runner
{
public static class Program
{
public static async Task Main(string[] args)
{
string gamesRoot = GetArg(args, "--gamesRoot") ??
Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "Server.Host.Tests", "TestGames"));
int port = int.TryParse(GetArg(args, "--port"), out int p) ? p : 5005;
int tickMs = int.TryParse(GetArg(args, "--tickMs"), out int t) ? t : 100;
long matchTimeoutTicks = long.TryParse(GetArg(args, "--matchTimeoutTicks"), out long mt) ? mt : 150;
string devToken = GetArg(args, "--devToken");
string advertiseWs = GetArg(args, "--advertiseWs");
string advertiseCdn = GetArg(args, "--advertiseCdn");
bool lan = Array.Exists(args, a => string.Equals(a, "--lan", StringComparison.OrdinalIgnoreCase));
int cdnPort = int.TryParse(GetArg(args, "--cdnPort"), out int cp) ? cp : 15081;
// --lan 下若未显式给 CDN 地址,用本机 LAN IP + 约定端口兜底广而告之 CDN 根
// (客户端从根派生两套:minigame/<id>/<ver>/ 与 XWorld/<platform>/advertiseWs 缺省由 GameServerHost 内部用 LanIp 兜底)
if (lan && string.IsNullOrEmpty(advertiseCdn))
advertiseCdn = $"http://{XWorld.Server.Gateway.LanIp.Resolve()}:{cdnPort}/";
// 鉴权 opt-in:给了 --authDbPath 才启用账号鉴权(/register、/login、/ws 验 token)。
// 密钥优先级:--authSecret(base64) > DB 同目录 auth.key 文件(不存在则生成并持久化)。
string authDbPath = GetArg(args, "--authDbPath");
string authSecretB64 = GetArg(args, "--authSecret");
byte[] authSecret = null;
if (!string.IsNullOrEmpty(authDbPath))
{
if (!string.IsNullOrEmpty(authSecretB64))
authSecret = Convert.FromBase64String(authSecretB64);
else
{
string keyPath = Path.Combine(
Path.GetDirectoryName(Path.GetFullPath(authDbPath)) ?? ".", "auth.key");
if (File.Exists(keyPath))
authSecret = Convert.FromBase64String(File.ReadAllText(keyPath).Trim());
else
{
authSecret = System.Security.Cryptography.RandomNumberGenerator.GetBytes(32);
File.WriteAllText(keyPath, Convert.ToBase64String(authSecret));
}
}
}
var host = new GameServerHost();
await host.StartAsync(gamesRoot, tickMs, matchTimeoutTicks: matchTimeoutTicks, listenPort: port,
bindAllInterfaces: lan,
devDiscoveryToken: devToken, advertiseGatewayUrl: advertiseWs, advertiseResourceBaseUrl: advertiseCdn,
authSecret: authSecret, authDbPath: authDbPath);
Console.WriteLine("XWorld Gateway smoke server started.");
Console.WriteLine(" gamesRoot: " + gamesRoot);
Console.WriteLine(" tickMs: " + tickMs + ", matchTimeoutTicks: " + matchTimeoutTicks);
Console.WriteLine(" ws: " + host.WsBaseUrl + "/ws?pid=1");
if (!string.IsNullOrEmpty(devToken))
Console.WriteLine(" dev-discovery: ON (UDP " + host.DevDiscoveryPort + "), advertise ws=" +
(string.IsNullOrEmpty(advertiseWs) ? host.WsBaseUrl + "/ws" : advertiseWs) + " cdn=" + advertiseCdn);
Console.WriteLine(" auth: " + (authSecret != null ? ("ON (db=" + authDbPath + ", /register /login /ws?token=)") : "OFF (dev ?pid=)"));
Console.WriteLine("Press Ctrl+C to stop.");
using var stop = new CancellationTokenSource();
Console.CancelKeyPress += (s, e) =>
{
e.Cancel = true;
stop.Cancel();
};
try { await Task.Delay(Timeout.InfiniteTimeSpan, stop.Token); }
catch (OperationCanceledException) { }
await host.StopAsync();
}
private static string GetArg(string[] args, string name)
{
for (int i = 0; i < args.Length - 1; i++)
if (string.Equals(args[i], name, StringComparison.OrdinalIgnoreCase))
return args[i + 1];
return null;
}
}
}