调试协议
远程调试协议是一个轻量级的 JSON-RPC 2.0 服务器,嵌入在每一个链接了 debug 模块的 Turian 游戏构建中。它通过 TCP 暴露运行时自省层,使 CLI、MCP 服务器和自定义脚本等外部工具可以查询并(可选地)修改实时的引擎状态。
线路格式
每行一个 JSON 对象(以 \n 终止)。无 Content-Length 头。
请求:
{"jsonrpc":"2.0","id":1,"method":"entity.inspect","params":{"name":"Player"}}
成功响应:
{"jsonrpc":"2.0","id":1,"result":{...}}
错误响应:
{"jsonrpc":"2.0","id":1,"error":{"code":-32000,"message":"Entity not found"}}
方法
场景
| 方法 | 参数 | 描述 |
|---|---|---|
scene.list |
— | 所有已加载的场景 |
scene.inspect |
name? |
场景中的实体(省略时默认为活动场景) |
实体
| 方法 | 参数 | 描述 |
|---|---|---|
entity.find |
name?、component? |
按名称或组件类型筛选实体 |
entity.inspect |
name 或 index |
Transform + 组件详情 |
组件
| 方法 | 参数 | 描述 |
|---|---|---|
component.get |
entity、component |
组件字段值 |
资源
| 方法 | 参数 | 描述 |
|---|---|---|
asset.list |
— | 所有项目资源(guid、path、type) |
asset.inspect |
guid |
单个资源的 guid / path / type |
诊断
| 方法 | 参数 | 描述 |
|---|---|---|
snapshot |
— | 完整场景快照(所有实体 + 指标) |
schema |
— | 内置组件类型目录 |
metrics |
— | 运行时性能计数器 |
profiler.capture |
— | 最新分析器帧(计数器 + 区域) |
memory |
— | memory_bytes + allocation_count |
errors |
— | 最近的 std.log 警告/错误条目(最新在前) |
ping |
— | 健康检查;返回 "pong" |
auth |
token |
认证(当服务器设置了令牌时需要) |
修改(仅读写模式)
修改方法返回 { "ok": bool, "message": string }。它们要求服务器以读写模式启动(allow_write / CLI --rw);否则返回 READONLY。在 Studio 中,每个修改都通过编辑器的撤销栈路由,因此 AI/CLI 的编辑是可撤销的,并且与 UI 一致。
| 方法 | 参数 | 描述 |
|---|---|---|
component.set |
entity、component、field、value |
设置组件字段 |
transform.set |
entity、channel、value([x,y,z]) |
设置位置/旋转/缩放 |
entity.spawn |
name |
创建新的空实体 |
entity.destroy |
entity |
移除实体 |
asset.reload |
guid |
热重载资源 |
会话与事件
| 方法 | 参数 | 描述 |
|---|---|---|
session.readonly |
— | 放弃此连接的写入权限(无法重新授予) |
subscribe |
event(或 "*") |
订阅运行时事件 |
unsubscribe |
event(或 "*") |
停止接收事件 |
已订阅的客户端在事件触发时会收到 JSON-RPC 通知(无 id):
| 事件 | 触发时机 |
|---|---|
entity.created |
实体被创建时 |
entity.destroyed |
实体被移除时 |
scene.loaded |
场景变为活动/加载完成时 |
scene.unloaded |
场景被卸载时 |
resource.reloaded |
资源被热重载时 |
fps.changed |
整数 FPS 区间变化时 |
在 Turian Studio 中,scene.loaded / scene.unloaded 在打开、切换或关闭场景标签时触发,fps.changed 由编辑器自身的帧时序驱动——因此即使在编辑模式下也会触发,而不仅限于播放模式。
错误码
| 码 | 名称 | 含义 |
|---|---|---|
-32700 |
PARSE_ERROR |
无效 JSON |
-32600 |
INVALID_REQUEST |
缺少必填字段 |
-32601 |
METHOD_NOT_FOUND |
未知方法 |
-32602 |
INVALID_PARAMS |
参数缺失或错误 |
-32603 |
INTERNAL_ERROR |
序列化或内部错误 |
-32000 |
NOT_FOUND |
未找到实体/场景/组件 |
-32001 |
READONLY |
尝试在只读会话上执行修改 |
-32002 |
RATE_LIMITED |
超出每个连接速率限制 |
启动调试服务器
const debug = @import("debug");
var srv = debug.Server.init(allocator, .{
.port = 7777,
.localhost_only = true,
.auth_token = "", // 空 = 无需认证
.allow_write = false, // true 以允许修改
.max_clients = 8,
.rate_limit_per_sec = 0, // 0 = 无限制
.max_outbound_queue = 2048, // 每个客户端的事件积压上限
.max_outbound_bytes = 16 << 20,
.max_inbound_queue = 4096, // 所有客户端待处理请求
});
defer srv.deinit(io);
try srv.start(io); // 仅启动接收循环
// 每帧在主线程上调用:
srv.pump(world, applier); // 针对实时状态执行排队的读取/写入
接收循环在后台线程上运行,但每个请求在主线程的 pump 内部执行——因此读取无竞态,写入可以无需锁操作实时引擎状态。支持多个客户端同时连接。修改通过宿主提供的 MutationApplier 应用;事件通过 srv.emit(...) 发出。调用 srv.stop(io) 以干净地关闭。
大响应与背压
大场景的完整 snapshot 序列化后远超 64 KiB。消息以换行符分隔,但不限制在 64 KiB——客户端和服务器都会在堆上增长读取缓冲区,上限为 16 MiB,因此大响应可以完整到达而不会失败。
每个连接的出站事件队列有上限(max_outbound_queue / max_outbound_bytes)。停止读取的订阅者的最旧事件会被丢弃(事件本质上是允许丢失的),而不是无限制地增长服务器内存;落后太多无法接收待处理响应的客户端会被断开连接。共享的入站队列由 max_inbound_queue 限制;一旦满,新请求将被拒绝,并返回明确的"服务器繁忙"错误。
网络暴露。 设置
localhost_only = false会绑定0.0.0.0,并且没有 TLS——auth_token(及所有流量)以明文发送,并且是唯一的防护措施。仅在受信任的局域网内,在防火墙/VPN 后方使用;切勿将调试服务器暴露在公共互联网上。
从 turian-cli 使用
turian-cli debug connect
turian-cli debug scenes
turian-cli debug inspect Player
turian-cli debug snapshot
turian-cli debug schema
turian-cli debug metrics
turian-cli debug assets
turian-cli debug errors
# 修改(服务器必须为读写模式):
turian-cli debug set Player Light intensity 2.5
turian-cli debug spawn Crate
turian-cli debug destroy Crate
# 事件与会话:
turian-cli debug watch entity.created fps.changed
turian-cli debug record session.jsonl
turian-cli debug replay session.jsonl
查看 turian-cli debug --help 获取完整的子命令列表。
默认端口
7777。在服务器和客户端两侧均可通过 --port 覆盖。