fix: improve minigame reconnect flow
This commit is contained in:
@@ -22,9 +22,11 @@ public class XWorldUtil
|
||||
private const string LOCAL_SERVER_EXE = "../../Server/build-gateway-local3/Debug/AIProjectServer.exe";
|
||||
private const string LOCAL_SERVER_DLL_DIR = "../../Server/build-local-sqlite/vcpkg_installed/x64-windows/debug/bin";
|
||||
private const string LOCAL_SERVER_LOG_DIR = "../../Server/logs";
|
||||
private const string MINI_GAME_GATEWAY_RUNNER_PROJECT = "../../Server/Gateway.Runner/Gateway.Runner.csproj";
|
||||
private const string MINI_GAME_GATEWAY_RUNNER_DLL = "../../Server/Gateway.Runner/bin/Debug/net10.0/XWorld.Server.Gateway.Runner.dll";
|
||||
private const string MINI_GAME_SERVER_GAMES_ROOT = "../../deploy/minigame";
|
||||
private const string MINI_GAME_GATEWAY_PID_FILE = "pc-minigame-gateway.pid";
|
||||
private const string MINI_GAME_CDN_RUNNER_PROJECT = "../../Server/Cdn.Runner/Cdn.Runner.csproj";
|
||||
private const string MINI_GAME_CDN_RUNNER_DLL = "../../Server/Cdn.Runner/bin/Debug/net10.0/XWorld.Server.Cdn.Runner.dll";
|
||||
private const string MINI_GAME_CDN_ROOT = "../../CDN";
|
||||
private const string MINI_GAME_CDN_PID_FILE = "pc-cdn.pid";
|
||||
@@ -37,6 +39,7 @@ public class XWorldUtil
|
||||
private const int LOCAL_INTERNAL_GAME_PORT = 7003;
|
||||
private const int MINI_GAME_GATEWAY_PORT = 5005;
|
||||
private const int MINI_GAME_TICK_MS = 100;
|
||||
private const int MINI_GAME_RECONNECT_WINDOW_TICKS = 1800;
|
||||
private const int MINI_GAME_MATCH_TIMEOUT_TICKS = 150;
|
||||
|
||||
private static readonly List<Process> LocalServerProcesses = new List<Process>();
|
||||
@@ -323,6 +326,22 @@ public class XWorldUtil
|
||||
{
|
||||
StopExitedLocalServerProcesses();
|
||||
SetLocalServerPrefs();
|
||||
|
||||
if (IsVisibleMiniGameGatewayConsoleRunning())
|
||||
{
|
||||
UnityEngine.Debug.Log("PC MiniGame Gateway console is already running; skipping build.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ShouldBuildMiniGameLocalServer(IsMiniGameGatewayRunnerRunning()))
|
||||
{
|
||||
UnityEngine.Debug.Log("PC MiniGame Gateway is already running; skipping build.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!BuildMiniGameLocalServer())
|
||||
return;
|
||||
|
||||
// 内网 CDN(HTTP 静态文件)独立于网关,先确保起来(端口已开则跳过)
|
||||
StartMiniGameCdnRunnerProcess("XWorld PC CDN");
|
||||
if (IsVisibleMiniGameGatewayConsoleRunning())
|
||||
@@ -390,12 +409,26 @@ public class XWorldUtil
|
||||
"OK");
|
||||
}
|
||||
|
||||
[MenuItem("XWorld/Server/Build Local Server", false, 605)]
|
||||
static public void BuildLocalServer()
|
||||
{
|
||||
if (BuildMiniGameLocalServer())
|
||||
EditorUtility.DisplayDialog("XWorld Server", "Local server build finished.", "OK");
|
||||
}
|
||||
|
||||
[MenuItem("XWorld/Server/Build CDN Runner", false, 606)]
|
||||
static public void BuildCdnRunner()
|
||||
{
|
||||
if (BuildMiniGameRunnerProject("XWorld PC CDN", GetMiniGameCdnRunnerProjectPath(), GetMiniGameCdnRunnerPath()))
|
||||
EditorUtility.DisplayDialog("XWorld Server", "CDN runner build finished.", "OK");
|
||||
}
|
||||
|
||||
static public bool IsPcMiniGameGatewayReady()
|
||||
{
|
||||
return IsMiniGameGatewayRunnerRunning();
|
||||
}
|
||||
|
||||
[MenuItem("XWorld/Server/Open Latest Log", false, 605)]
|
||||
[MenuItem("XWorld/Server/Open Latest Log", false, 607)]
|
||||
static public void OpenLatestLocalServerLog()
|
||||
{
|
||||
string logDir = Path.GetFullPath(Path.Combine(Application.dataPath, LOCAL_SERVER_LOG_DIR));
|
||||
@@ -422,7 +455,7 @@ public class XWorldUtil
|
||||
[MenuItem("XWorld/Server/Open Local Server (Separate)", true)]
|
||||
static public bool CanOpenLocalServer()
|
||||
{
|
||||
return File.Exists(GetMiniGameGatewayRunnerPath());
|
||||
return File.Exists(GetMiniGameGatewayRunnerProjectPath());
|
||||
}
|
||||
|
||||
[MenuItem("XWorld/Server/Stop Local Server", true)]
|
||||
@@ -484,6 +517,11 @@ public class XWorldUtil
|
||||
return Path.GetFullPath(Path.Combine(Application.dataPath, MINI_GAME_GATEWAY_RUNNER_DLL));
|
||||
}
|
||||
|
||||
static private string GetMiniGameGatewayRunnerProjectPath()
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(Application.dataPath, MINI_GAME_GATEWAY_RUNNER_PROJECT));
|
||||
}
|
||||
|
||||
static private string GetMiniGameServerGamesRoot()
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(Application.dataPath, MINI_GAME_SERVER_GAMES_ROOT));
|
||||
@@ -501,6 +539,11 @@ public class XWorldUtil
|
||||
return Path.GetFullPath(Path.Combine(Application.dataPath, MINI_GAME_CDN_RUNNER_DLL));
|
||||
}
|
||||
|
||||
static private string GetMiniGameCdnRunnerProjectPath()
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(Application.dataPath, MINI_GAME_CDN_RUNNER_PROJECT));
|
||||
}
|
||||
|
||||
static private string GetMiniGameCdnRoot()
|
||||
{
|
||||
return Path.GetFullPath(Path.Combine(Application.dataPath, MINI_GAME_CDN_ROOT));
|
||||
@@ -523,6 +566,101 @@ public class XWorldUtil
|
||||
return Path.Combine(logDir, safeTitle + "-" + DateTime.Now.ToString("yyyyMMdd-HHmmss") + ".log");
|
||||
}
|
||||
|
||||
static private bool BuildMiniGameLocalServer()
|
||||
{
|
||||
if (!BuildMiniGameRunnerProject("XWorld PC minigame gateway", GetMiniGameGatewayRunnerProjectPath(), GetMiniGameGatewayRunnerPath()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static private bool ShouldBuildMiniGameLocalServer(bool isGatewayRunning)
|
||||
{
|
||||
return !isGatewayRunning;
|
||||
}
|
||||
|
||||
static private bool BuildMiniGameRunnerProject(string title, string projectPath, string runnerPath)
|
||||
{
|
||||
if (!File.Exists(projectPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("XWorld Server", "Server project not found:\n" + projectPath, "OK");
|
||||
return false;
|
||||
}
|
||||
|
||||
string logPath = GetLocalServerLogPath(title + " build");
|
||||
UnityEngine.Debug.Log("Building " + title + " from " + projectPath + "\nLog: " + logPath);
|
||||
EditorUtility.DisplayProgressBar("XWorld Server", "Building " + title, 0.5f);
|
||||
|
||||
try
|
||||
{
|
||||
StringBuilder output = new StringBuilder();
|
||||
ProcessStartInfo startInfo = new ProcessStartInfo();
|
||||
startInfo.FileName = "dotnet";
|
||||
startInfo.Arguments = "build " + QuoteProcessArg(projectPath) + " -c Debug --no-incremental -m:1";
|
||||
startInfo.WorkingDirectory = GetProjectRoot();
|
||||
startInfo.UseShellExecute = false;
|
||||
startInfo.RedirectStandardOutput = true;
|
||||
startInfo.RedirectStandardError = true;
|
||||
startInfo.CreateNoWindow = true;
|
||||
|
||||
using (Process process = new Process())
|
||||
{
|
||||
process.StartInfo = startInfo;
|
||||
process.OutputDataReceived += (sender, args) =>
|
||||
{
|
||||
if (args.Data != null)
|
||||
output.AppendLine(args.Data);
|
||||
};
|
||||
process.ErrorDataReceived += (sender, args) =>
|
||||
{
|
||||
if (args.Data != null)
|
||||
output.AppendLine(args.Data);
|
||||
};
|
||||
|
||||
process.Start();
|
||||
process.BeginOutputReadLine();
|
||||
process.BeginErrorReadLine();
|
||||
process.WaitForExit();
|
||||
process.WaitForExit(1000);
|
||||
|
||||
File.WriteAllText(logPath, output.ToString(), new UTF8Encoding(false));
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
EditorUtility.DisplayDialog("XWorld Server",
|
||||
title + " build failed. Old DLL will not be started.\n\nLog:\n" + logPath,
|
||||
"OK");
|
||||
UnityEngine.Debug.LogError(title + " build failed with exit code " + process.ExitCode + ". Log: " + logPath);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
File.WriteAllText(logPath, ex.ToString(), new UTF8Encoding(false));
|
||||
EditorUtility.DisplayDialog("XWorld Server",
|
||||
title + " build failed before launch. Old DLL will not be started.\n\n" + ex.Message + "\n\nLog:\n" + logPath,
|
||||
"OK");
|
||||
UnityEngine.Debug.LogError(title + " build failed: " + ex);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorUtility.ClearProgressBar();
|
||||
}
|
||||
|
||||
if (!IsDotnetRunnerLaunchable(runnerPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("XWorld Server",
|
||||
title + " build finished, but runner output is incomplete:\n" + runnerPath +
|
||||
"\n" + GetDotnetRunnerRuntimeConfigPath(runnerPath),
|
||||
"OK");
|
||||
return false;
|
||||
}
|
||||
|
||||
UnityEngine.Debug.Log(title + " build finished. Runner: " + runnerPath);
|
||||
return true;
|
||||
}
|
||||
|
||||
static private void StartServerProcess(string arguments, string title)
|
||||
{
|
||||
string exePath = GetLocalServerExePath();
|
||||
@@ -573,10 +711,12 @@ public class XWorldUtil
|
||||
static private void StartMiniGameGatewayRunnerProcess(string title)
|
||||
{
|
||||
string runnerPath = GetMiniGameGatewayRunnerPath();
|
||||
if (!File.Exists(runnerPath))
|
||||
if (!IsDotnetRunnerLaunchable(runnerPath))
|
||||
{
|
||||
EditorUtility.DisplayDialog("XWorld Server",
|
||||
"Gateway runner not found:\n" + runnerPath + "\n\nBuild Server/Gateway.Runner first.", "OK");
|
||||
"Gateway runner output is incomplete:\n" + runnerPath +
|
||||
"\n" + GetDotnetRunnerRuntimeConfigPath(runnerPath) +
|
||||
"\n\nBuild Server/Gateway.Runner first.", "OK");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -612,6 +752,7 @@ public class XWorldUtil
|
||||
"Write-Host ''\r\n" +
|
||||
"& dotnet $runner --gamesRoot $gamesRoot --port " + MINI_GAME_GATEWAY_PORT +
|
||||
" --tickMs " + MINI_GAME_TICK_MS +
|
||||
" --reconnectWindowTicks " + MINI_GAME_RECONNECT_WINDOW_TICKS +
|
||||
" --matchTimeoutTicks " + MINI_GAME_MATCH_TIMEOUT_TICKS +
|
||||
" --lan --devToken " + DEV_DISCOVERY_TOKEN + // 开 DevDiscovery(UDP48923) 响应 + 广播 LAN IP;CDN 地址由 --lan 兜底为 http://<lanip>:15081/
|
||||
" *>&1 | Tee-Object -FilePath $logPath\r\n" +
|
||||
@@ -645,9 +786,10 @@ public class XWorldUtil
|
||||
static private void StartMiniGameCdnRunnerProcess(string title)
|
||||
{
|
||||
string runnerPath = GetMiniGameCdnRunnerPath();
|
||||
if (!File.Exists(runnerPath))
|
||||
if (!IsDotnetRunnerLaunchable(runnerPath))
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("[CDN] 未找到 Cdn.Runner:" + runnerPath + "\n请先构建 Server/Cdn.Runner(dotnet build Server/Server.sln)。");
|
||||
UnityEngine.Debug.LogWarning("[CDN] Cdn.Runner output is incomplete; skipping CDN startup.\nDLL: " +
|
||||
runnerPath + "\nRuntimeConfig: " + GetDotnetRunnerRuntimeConfigPath(runnerPath));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -722,6 +864,18 @@ public class XWorldUtil
|
||||
return (text ?? string.Empty).Replace("'", "''");
|
||||
}
|
||||
|
||||
static private string GetDotnetRunnerRuntimeConfigPath(string runnerPath)
|
||||
{
|
||||
string directory = Path.GetDirectoryName(runnerPath);
|
||||
string fileName = Path.GetFileNameWithoutExtension(runnerPath) + ".runtimeconfig.json";
|
||||
return Path.Combine(directory ?? string.Empty, fileName);
|
||||
}
|
||||
|
||||
static private bool IsDotnetRunnerLaunchable(string runnerPath)
|
||||
{
|
||||
return File.Exists(runnerPath) && File.Exists(GetDotnetRunnerRuntimeConfigPath(runnerPath));
|
||||
}
|
||||
|
||||
static private void StopExitedLocalServerProcesses()
|
||||
{
|
||||
LocalServerProcesses.RemoveAll(process =>
|
||||
|
||||
Reference in New Issue
Block a user