主题
纯客户端调试指南
纯客户端调试用于只启动客户端运行时,不启动服务端和 host 连接。它适合 GameGraph 纯客户端游戏,也适合把有服务端的项目切到一个轻量的本地客户端分支,用来快速验证 UI、客户端数编场景、模型表现或本地工具逻辑。
适用场景
- 纯 GameGraph 游戏:没有 Entity、Unit、CloudData 等服务端逻辑,调试启动更快。
- UI 测试:构造本地 view model 或假数据,验证布局、交互、动画、资源路径。
- 客户端数编场景与表现测试:通过
Scene.LoadClientOnly()加载GameDataScene对应的场景资产,再创建ActorModel、ActorParticle、ActorSound或 GameGraph 节点验证表现效果,跳过匹配和服务端流程。 - 低层渲染原型:直接用 GameGraph 加载节点、材质、相机、特效,验证不依赖数编场景的局部渲染逻辑。
- 本地客户端工具:例如局内编辑器、配置预览器、关卡辅助工具。
- 调试期配置导出:结合
user_files写权限,把生成结果输出到项目文件中。
运行时特征
从编辑器进入纯客户端调试时,客户端进程会带上 -client_only 参数。框架会把当前运行模式设置为 GameRunMode.ClientOnly,代码里可用:
csharp
if (Game.IsClientOnly)
{
Game.Logger.LogInformation("当前是纯客户端调试模式");
}纯客户端模式下没有服务端连接,因此这些能力不可用:
- Entity / Unit 等服务端对象
- CloudData
- 服务端触发器、服务端 AI、服务端刷怪逻辑
- 客户端到服务端的消息往返
客户端 GameGraph、UI、日志、资源加载、客户端 user_files 读写仍可用于调试。ActorSound、ActorModel、ActorParticle 等纯表现层 Actor 也应能在纯客户端模式下创建和播放;不能用的是依赖服务端同步的 Entity / Unit / CloudData / 服务端触发器能力。
如果纯客户端预览需要加载一个数编场景资产,可以显式调用客户端专用 API:
csharp
#if CLIENT
if (Game.IsClientOnly)
{
var scene = Scene.GetOrCreate(ScopeData.GameDataScene.new_scene);
scene.OnLoadEnd += () =>
{
Game.Logger.LogInformation("纯客户端场景加载完成: {Scene}", scene);
};
scene.LoadClientOnly();
}
#endif如果只想加载当前游戏模式的默认场景,可以使用:
csharp
#if CLIENT
if (Game.IsClientOnly)
{
Scene.LoadDefaultClientOnly();
}
#endifLoadClientOnly() 会把 GameDataScene.HostedSceneTag.SceneTag 设为客户端当前场景,并用 HostedSceneTag.AssetTemplate 加载实际地图资源。LoadDefaultClientOnly() 会读取当前 GameDataGameMode.DefaultScene,再调用对应场景的 LoadClientOnly()。这两个 API 都只用于纯客户端模式;正常客户端/服务端游戏仍由服务端发起场景切换,不要在普通联机流程里调用它。
如果一个 GameMode 在纯客户端调试中总是需要默认数编场景,也可以在 GameMode 数据上打开自动加载:
csharp
_ = new GameDataGameMode(ScopeData.GameDataGameMode.MapGameMode)
{
SceneList = [ScopeData.GameDataScene.new_scene],
DefaultScene = ScopeData.GameDataScene.new_scene,
AutoLoadDefaultSceneInClientOnly = true,
};该配置默认是 false。保持关闭时,纯客户端调试仍是轻量入口,适合只测 UI、GameGraph 局部原型或本地工具;打开后,框架会在纯客户端 Game.OnGameStart 后自动调用 Scene.LoadDefaultClientOnly()。
推荐代码结构
如果项目本身依赖服务端,可以在客户端启动阶段为纯客户端调试提供一个本地分支:
csharp
#if CLIENT
public async Task StartClientAsync()
{
if (Game.IsClientOnly)
{
await StartLocalPreviewAsync();
return;
}
await StartNormalClientFlowAsync();
}
private async Task StartLocalPreviewAsync()
{
Game.Logger.LogInformation("启动纯客户端预览");
await Game.Delay(TimeSpan.FromMilliseconds(1));
Scene.LoadDefaultClientOnly();
// 构造 UI 假数据、加载模型/场景、创建本地调试工具等。
}
#endif这样同一套项目既能走正常联机流程,也能快速启动客户端局部测试。
输出调试文件
纯客户端调试属于客户端编辑器调试阶段,主项目 user_files 目录可写。适合输出工具生成的临时配置、UI 快照、场景编辑结果等:
csharp
#if CLIENT
if (Game.IsClientOnly)
{
var outputDir = Path.Combine("user_files", "generated");
Directory.CreateDirectory(outputDir);
var outputPath = Path.Combine(outputDir, "local-preview.json");
File.WriteAllText(outputPath, "{ \"mode\": \"client_only\" }");
Game.Logger.LogInformation("已输出纯客户端调试文件: {Path}", outputPath);
}
#endif注意:
- 只写主项目的
user_files/...。 deps/<依赖库名>/user_files/...始终只读。- AppBundle 根目录
.始终只读。 - 生成目录如果不希望提交到版本库,应在项目
.gitignore中额外忽略。
更多文件路径和权限规则见 项目文件读写指南。
不打开编辑器直接启动
纯客户端调试可以直接通过命令行启动,不必先打开编辑器 UI。AI 助手或自动化脚本推荐使用:
powershell
$ProjectRoot = "D:\Maps\MyMap"
$Script = Join-Path $ProjectRoot "ai\tools\Start-SceClientOnlyDebug.ps1"
if (-not (Test-Path -LiteralPath $Script)) {
$Script = Join-Path $ProjectRoot ".cursor\tools\Start-SceClientOnlyDebug.ps1"
}
if (-not (Test-Path -LiteralPath $Script)) {
$Script = "<WasiCoreSDK>\docs\ai\tools\Start-SceClientOnlyDebug.ps1"
}
$PowerShellExe = Get-Command pwsh -ErrorAction SilentlyContinue | Select-Object -First 1 -ExpandProperty Source
if ([string]::IsNullOrWhiteSpace($PowerShellExe)) {
$PowerShellExe = Get-Command powershell -ErrorAction Stop | Select-Object -First 1 -ExpandProperty Source
}
& $PowerShellExe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $Script -ProjectRoot $ProjectRoot -ResolveOnly
& $PowerShellExe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $Script -ProjectRoot $ProjectRoot项目内的 ai/tools/Start-SceClientOnlyDebug.ps1 是编辑器打开项目时生成的,已经包含本机启动器路径和当前项目的客户端调试参数。.cursor/tools/Start-SceClientOnlyDebug.ps1 是兼容旧 AI 客户端的镜像;如果项目内脚本不存在,再使用 SDK 文档目录中的通用脚本。上例中的 <WasiCoreSDK> 需要替换为本机 SDK 路径。
脚本启动的是编辑器记录的 launcher,可执行文件可能再派生真正运行游戏的 sce 客户端进程。新脚本会在 JSON 输出里同时给出 launcherProcessId、clientProcessId 和 clientProcess;判断客户端是否存活、查命令行参数或做截图/MCP 诊断时,应优先看 clientProcessId。旧脚本只有 processId 时,这个 PID 可能只是 launcher,不能据此断定客户端已退出;应通过 Win32_Process 搜索带 -client_only、-map_path=<ProjectRoot> 或 -runtime_mcp_port=18765 的 sce 进程。
SDK 通用脚本会读取项目中记录的纯客户端调试启动配置:
<ProjectRoot>/docs/.client-only-debug.json:当前项目的启动器路径、API 版本和客户端调试参数。
这份配置由编辑器打开项目时生成。通常不需要 AI 助手自己查找安装目录或重新组装参数。
下列文件仅作为兼容记录,供旧脚本或排障时参考:
<ProjectRoot>/docs/.editor-root:当前机器对应的编辑器根目录。<ProjectRoot>/docs/.editor-launcher:当前机器对应的启动器 exe 路径。<ProjectRoot>/docs/.editor-api-version:当前项目/编辑器使用的 API 版本,例如2000。
若项目内脚本或启动配置不存在、路径已失效,请先用编辑器打开一次项目,再重新运行脚本。
脚本只负责启动客户端,不负责编译和部署。编辑器内点击调试时会自动编译并把最新 DLL 复制到运行时 AppBundle;直接用脚本拉起客户端时,如果刚刚执行过 dotnet build,需要先手动完成这一步:
powershell
$ProjectRoot = "D:\Maps\MyMap"
dotnet build (Join-Path $ProjectRoot "src\GameEntry.csproj") -c Server-Debug
dotnet build (Join-Path $ProjectRoot "src\GameEntry.csproj") -c Client-Debug
Copy-Item -LiteralPath (Join-Path $ProjectRoot "src\bin\Server-Debug\net9.0\GameEntry.dll") -Destination (Join-Path $ProjectRoot "AppBundle\managed\GameEntry.dll") -Force
Copy-Item -LiteralPath (Join-Path $ProjectRoot "src\bin\Client-Debug\net9.0\GameEntry.dll") -Destination (Join-Path $ProjectRoot "ui\AppBundle\managed\GameEntry.dll") -Force如果跳过部署,客户端会继续加载 AppBundle 里的旧 DLL。此时即使编译产物已经更新,运行表现、日志和 Runtime MCP 自定义工具也可能仍来自旧代码,容易造成错误归因。
等价的核心参数如下:
text
-env=game
-editor_server_debug
-client_only
-editor_api_version=<docs/.editor-api-version 或项目 api_version,当前 C# 版本通常是 2000>
-game=<ProjectName>
-map_kind=0
-map_path=<ProjectRoot>
-runtime_mcp_port=18765其中 -editor_server_debug 仍然需要保留。它用于进入编辑器调试权限分支,使 Game.IsDebugTestMode 为真,并开放主项目 user_files 的写权限。
-runtime_mcp_port=18765 是编辑器调试默认 Runtime MCP bridge 端口。旧的项目启动配置可能没有显式写这个参数,客户端仍会在 Wasmtime 启动后尝试打开默认端口;新配置应显式写出,方便 AI 助手诊断。
模拟指定窗口尺寸或竖屏手机画面
纯客户端启动参数支持 -width=<pixels> 和 -height=<pixels>。它们设置的是客户端窗口的客户区尺寸;Windows 标题栏和边框会让外框尺寸略大一些,但不影响渲染区域。使用 Start-SceClientOnlyDebug.ps1 时,通过 -ExtraArgs 追加:
powershell
# 横屏窗口,例如 1280x720
& $PowerShellExe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $Script -ProjectRoot $ProjectRoot -ExtraArgs @("-width=1280", "-height=720")
# 竖屏手机比例预览,例如 390x844
& $PowerShellExe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $Script -ProjectRoot $ProjectRoot -ExtraArgs @("-width=390", "-height=844")
# 按默认竖屏设计分辨率预览,例如 1080x1920
& $PowerShellExe -NoLogo -NoProfile -ExecutionPolicy Bypass -File $Script -ProjectRoot $ProjectRoot -ExtraArgs @("-width=1080", "-height=1920")当 AI 助手需要验证响应式 UI、竖屏布局、移动端 Canvas 表现或截图对比时,应优先用这两个参数启动对应比例的纯客户端窗口,再通过 Runtime MCP 的 debug.capture_screenshot、ui.snapshot 等工具检查真实画面。
一次完成预览与截图
项目已经在编辑器中打开且调试工具可用时,直接复用该编辑器。没有合适的编辑器会话、且本次验证只依赖客户端时,可以用更新后的启动助手一次收集截图、UI 状态和日志:
powershell
$projectRoot = 'D:/Maps/MyGame'
dotnet build (Join-Path $projectRoot 'src/GameEntry.csproj') -c Client-Debug
& (Join-Path $projectRoot 'ai/tools/Start-SceClientOnlyDebug.ps1') `
-ProjectRoot $projectRoot -Preview -DeployClientDll `
-ExtraArgs @('-width=1280', '-height=720')如果项目内的助手没有 -Preview 参数,刷新项目 AI 上下文,或使用当前 SDK 的 docs/ai/tools/Start-SceClientOnlyDebug.ps1 并传入 -ProjectRoot。项目仍需已有由编辑器生成的 docs/.client-only-debug.json。
-DeployClientDll 将已编译的客户端 GameEntry.dll 复制到 ui/AppBundle/managed 并校验 SHA256;助手本身不编译、不部署依赖库,也不启动服务端。需要完整游戏验收时,仍要按项目要求完成双端编译和正常服务端调试。
默认报告保存到项目的 artifacts/preview/<本次运行目录>/:preview.json 记录启动、DLL、截图路径、Runtime MCP 客户端版本和进程清理结果;ping.json、capture.json、ui.json 与客户端日志摘录保留验证证据。PNG 使用唯一文件名,等待文件写完整后才结束运行。助手在成功或失败时都会关闭它自己启动的客户端和 launcher;已有编辑器或其他进程不会被关闭。
常用参数:-EvidenceDirectory 指定报告目录,-ReadyTimeoutSec 60 和 -CaptureTimeoutSec 30 限制等待时间,-SettleMs 1000 为场景留出稳定时间。截图默认限制在 1280x720 内,精细检查可传 -MaxWidth 0 -MaxHeight 0 保留原始分辨率。非标准编译目录可配合 -DeployClientDll -ClientDllPath '<完整 DLL 路径>'。
支持启动状态的运行时会在 debug.ping.startup_state 返回 game_started 和 loading_visible。预览助手等待游戏启动且框架加载页退出后,再等待 -SettleMs 并截图,状态保存到 startup.json。旧运行时缺少该字段时,报告 startupEvidence.state=unsupported 并保留警告;自定义加载页和具体玩法状态仍需检查实际截图或项目状态,MCP 可调用、HUD 已创建都不能证明加载页已经退出。
报告中的 success: true 表示证据已收集;请打开截图检查角色、光照、构图与 UI,不能仅凭 ping 或文件存在认定画面达标。
预览助手先发现 Runtime MCP 工具,再按可用性采集 scenegraph.render_snapshot 到 render.json。renderEvidence.state 区分 captured、unsupported、discovery_failed 与 failed;旧运行时缺少工具时仍采集截图和 UI,发现或调用错误会保留诊断警告。渲染快照提供镜头、候选光照区域、雾和灯光配置,实际生效区域与遮罩绑定无法确认时标为未知。
调试日志
纯客户端调试不会产生服务端运行日志。客户端运行时日志通常在编辑器安装目录下:
text
<EditorRoot>/logs
<EditorRoot>/logs/client常见本地路径示例:
text
E:/WasmSCE/logs
E:/WasmSCE/logs/clientlogs/ 根目录还包含 lua、CSharp、Network、updater 等子目录以及根级日志文件;问题不确定属于客户端还是编辑器流程时,先按时间排序日志根目录。
日志中可搜索:
Client-only modeRunMode=ClientOnlyWASI user_files main preopen
如果看到类似 writable[1],表示当前客户端调试进程的主项目 user_files 写权限已开放。
常见问题
不要在纯客户端模式里直接访问 Entity、Unit 或 CloudData。需要服务端数据时,用本地假数据或文件读取替代。
不要把纯客户端分支做成正式游戏存档方案。调试输出适合辅助工具和临时配置,玩家正式持久化数据仍应使用 CloudData。
不要把服务端逻辑悄悄搬到客户端。纯客户端调试可以帮助验证表现层,但涉及权威逻辑、战斗结算、存档和多人同步时,仍需要正常客户端 + 服务端调试。
如果日志看起来像“缺少 Lua 入口”或 UI 代码没有生效,先核对 src/bin/Client-Debug/net9.0/GameEntry.dll 与 ui/AppBundle/managed/GameEntry.dll 的时间和大小。直接启动客户端时,旧 DLL 比缺少 ui/script/main.lua 更常见。