# Agent Bridge API(协议 1) 本文描述当前 `1.0.0` 实现,供本机 AI Agent 和其他客户端直接调用。方法、参数和返回字段均按 [AgentPlugin.cs](../src/XFE.SeAgent.Plugin/AgentPlugin.cs)、[GameDebugApi.cs](../src/XFE.SeAgent.Plugin/Game/GameDebugApi.cs)、[BlockOperations.cs](../src/XFE.SeAgent.Plugin/Game/BlockOperations.cs)、[WorldQueries.cs](../src/XFE.SeAgent.Plugin/Game/WorldQueries.cs) 及 [CLI](../src/XFE.SeAgent.Cli/CliApplication.cs) 整理。返回字段随方块类型、世界状态和运行实例而变化;不要把缺少的字段当成 `false` 或 `0`。 ## 1. 连接、协议与执行边界 插件使用 Windows 本机命名管道 `XFE.SE.Agent.<游戏进程 PID>`。管道 ACL 只允许当前 Windows 用户,显式拒绝 Network SID;没有 HTTP/TCP 接口。每个连接发送一行 UTF-8 JSON,接收一行 JSON,然后关闭连接。请求以 LF 结束,也接受 CRLF;不要在同一连接发送第二个请求或额外字节。 ```json {"id":"request-1","method":"world.status","params":{},"timeoutMs":10000} ``` 成功与失败响应分别为: ```json {"id":"request-1","result":{"loaded":false,"loadingPath":null,"loadError":null}} {"id":"request-1","error":{"code":"execution_error","message":"No ready game world is loaded."}} ``` | 请求字段 | 约定 | | --- | --- | | `id` | 必填字符串或整数,最多 128 个字符;建议使用唯一字符串。无法解析或识别请求 ID 时,错误响应的 `id` 可以为 `null`。ID 仅用于关联响应,没有重复请求去重功能。 | | `method` | 必填,1–128 个 ASCII 字母、数字、`.`、`-`、`_`;实际方法名区分大小写。 | | `params` | 对象;省略或 `null` 等同 `{}`。本文参数表中的“必填”指该方法要求的字段。 | | `timeoutMs` | 整数,1–30000 毫秒,省略时服务端默认 10000。CLI 默认 30000。 | JSON 字段名区分大小写。请求最大深度 32,不接受重复属性名。请求行在 LF 前最多 1 MiB;CLI 更严格地把结束 LF 也计入 1 MiB。响应包含 LF 最多 4 MiB。JSON 转义、字段名和协议外层都会占用字节额度,不能只按源文件大小判断。 服务端最多 4 个并发连接、128 个队列项。所有方法,包括查询和 `agent.ping`,都经队列在游戏 `Update` 线程执行;当前插件每次 `Update` 最多执行 2 个请求。连接线程不会访问游戏对象。游戏长时间不更新时,管道存在也不代表 RPC 能及时完成。 ### 超时、取消与重试 `timeoutMs` 的服务端期限从完整请求解析后开始,覆盖排队和执行等待;接收首行另有 10 秒上限,发送响应另有 5 秒上限。CLI 的期限覆盖连接、发送和接收,因此可能先于服务端返回错误。 - **尚未开始执行**:服务端在派发前检查截止时间、取消、连接状态和服务器停止状态。已超时、已断开或已取消的队列项不会以后再执行。 - **已经开始执行**:不能安全中断游戏线程中的操作,也不提供自动回滚。服务端执行期超时的错误消息为 `Request timed out after execution began; the operation may have completed. Check state before retrying.`;排队过期的消息为 `Request expired before execution.`。 - **客户端超时、断开或没有收到完整响应**:客户端通常无法知道执行到了哪一步。先查询相应状态,再决定是否重试写操作。相同 `id` 不会阻止再次执行。 - `response_too_large` 也可能发生在写操作已经完成之后。例如部署成功但完整 PB 状态太大,仍可能只能收到尺寸错误;应重新调用 `pb.read` 或缩小查询确认状态。 ### 发现活动端点 插件写入: ```text %LOCALAPPDATA%\XFE\SpaceEngineersAgent\endpoints\.json ``` 端点字段为 `version`、`protocolVersion`、`pipeName`、`pid`、`startTimeUtc`、`executablePath`、`createdUtc`。CLI `discover` 会核对 PID、进程启动时间及可执行文件完整路径,忽略失效、格式错误或不可访问的记录;最多检查 1024 个文件,每文件最多 64 KiB。端点文件存在本身不证明游戏仍存活。 ## 2. 世界权限与通用数据结构 `agent.*` 和 `world.status` 无需已加载世界。其他读取方法要求世界 `ready:true` 且未卸载。写方法要求 `world.status.canMutate:true`:世界已就绪、未卸载、本机为服务器、`onlineMode` 为 `OFFLINE`、世界路径被明确允许。`world.load` 有独立的允许路径检查。 允许路径来自插件启动时读取的配置: ```text %LOCALAPPDATA%\XFE\SpaceEngineersAgent\config.json ``` ```json {"allowedWorldPaths":["D:\\SE-TestCopies\\AgentSandbox"]} ``` 配置最大 64 KiB。条目须为绝对路径;按完整路径去除末尾目录分隔符后、不区分大小写地精确匹配,不是目录前缀授权。未配置时允许列表为空。配置不会自动创建世界副本,也不会在运行中重新加载;修改后须重新加载插件/重启游戏。应填测试副本的存档目录,该目录包含 `Sandbox.sbc`。 ### ID 与坐标 - 输入 `entityId`、`gridId`、`entityIds` 中每项都应使用**非零 Int64 十进制字符串**,支持负数。不要经过 JavaScript `Number`;其精度不足以保存所有实体 ID。 - 常规结果中的 `entityId`、`gridId`、`ownerId`、`cameraId`、`otherConnectorId` 是字符串或按字段约定为 `null`。无命中摄像头可返回 `entityId:"0"`,无所有者可返回 `ownerId:"0"`;不能用这些零值继续查询实体。库存 `itemId` 是物品堆标识数字,不是实体 ID。 - `Vec3` 为 `{x,y,z}`;`pose` 为 `{position,forward,up,right}`,来自世界矩阵。`bounds` 为世界轴对齐包围盒 `{min:Vec3,max:Vec3}`,不是精确碰撞网格;`positionInGrid` 是网格内整数坐标。 - 本文 `Grid` 和 `Block` 是下述返回形状的简称,不是额外 JSON 外层。 | 结构 | 字段 | | --- | --- | | `Grid` | `entityId`, `name`, `isStatic`, `gridSize`, `blockCount`, `pose`, `bounds`;存在物理对象时还有 `linearVelocity`, `angularVelocity`, `speed`。 | | `Block` | `entityId`, `gridId`, `name`, `type`, `subtype`, `functional`, `working`, `ownerId`, `positionInGrid`, `pose`, `bounds`, `inventoryCount`;功能方块另有 `enabled`,可取得底层方块时另有下述 `integrity`。 | | 详细 `Block` | `Block` 加 `customData`, `detailedInfo`, `customInfo`。后两项各保留前 32768 字符,超出追加 `…`;`customData` 不截断,仍受响应总大小限制。 | ## 3. Agent 与世界生命周期 | 方法 | 参数 | `result` 核心字段与行为 | | --- | --- | --- | | `agent.ping` | `{}` | `version`, `protocolVersion`, `ticks`, `utc`, `uptimeSeconds`。`ticks` 是插件 Update 次数。 | | `agent.capabilities` | `{}` | `protocolVersion`, `transport`, `maxRequestBytes`, `maxResponseBytes`, `maxTimeoutMs`, `entityIds`, `mutationPolicy`, `methods`。以此核对目标实例支持的方法。 | | `agent.events` | `after`:可选整数,默认 0 | `events:[{sequence,utc,message}]`, `lastSequence`;仅返回 `sequence > after` 的事件。内存只保留最近 256 条,没有分页或补回已丢弃事件的接口。下次用 `lastSequence` 继续取。 | | `world.status` | `{}` | 总有 `loaded`, `loadingPath`, `loadError`。有会话时另有 `name`, `path`, `ready`, `isUnloading`, `isServer`, `onlineMode`, `authorizedWorld`, `canMutate`, `scriptsEnabled`, `saveInProgress`, `frame`, `elapsedSeconds`, `simulationSpeed`, `paused`, `pausedByBridge`。`loaded:true` 不等于 `ready:true`。 | | `world.load` | `path`:必填字符串 | 从主菜单加载允许路径的离线单人世界。通常返回 `{accepted:true,path,state:"loading",pollMethod:"world.status"}`。若同一路径世界已经就绪,直接返回 `world.status` 的形状。其他世界仍加载时拒绝,不会代为卸载或丢弃当前世界。 | | `world.save` | `{}`;写权限 | 返回 `saved`, `path`, `saveInProgress`。已有保存正在进行时拒绝;须检查 `saved`,RPC 成功不等于保存结果必为 `true`。 | | `world.pause` | `paused`:必填布尔;写权限 | 返回 `paused`, `pausedByBridge`, `note`。仅增减本桥持有的暂停;`paused:false` 不会解除菜单等其他来源的暂停,这时 `note` 说明仍有其他暂停。插件 Dispose 会释放自身暂停。 | | `world.exit` | **`save:true` 必填**;写权限 | 保存成功且无保存进行时,请求**正常退出整个游戏进程**,返回 `{saved:true,path,exitRequested:true}`。不支持无保存退出,也不是返回主菜单。保存失败不会退出。退出可能使管道先关闭,需结合进程/端点状态判断,不能因丢失响应盲目重试。 | | `debug.screenshot` | `{}`;写权限 | 返回 `{accepted:true,path,note}`。PNG 位于 `%LOCALAPPDATA%\XFE\SpaceEngineersAgent\Screenshots`;渲染线程异步写文件,返回时文件不一定已生成。 | `world.load` 的 `accepted` 只表示请求已发起。轮询 `world.status`,核对 `ready:true`、`path` 是目标世界,写操作前再核对 `canMutate:true`。加载观察超过 10 分钟会清除 `loadingPath` 并设置 `loadError`;这不等于取消了游戏自身的加载流程。读取/保存调用也可能因游戏加载界面或繁忙而超时。 事件同时追加至 `%LOCALAPPDATA%\XFE\SpaceEngineersAgent\agent.log`,当前日志超过 4 MiB 后在下次写入时轮换为 `agent.log.1`。`agent.events` 是有限日志记录,不是完整方块状态变更流。 ## 4. 网格与方块查询 | 方法 | 参数 | `result` | | --- | --- | --- | | `grids.list` | `name`:可选名称子串;`offset`:默认 0、负数按 0;`limit`:默认 256、夹在 1–2048 | `{grids:[Grid],total,offset,truncated}`。过滤后的网格按实体 ID 排序;名称不区分大小写。 | | `grids.get` | `entityId`:必填网格 ID | `Grid` 加下述网格 `integrity` 汇总,包含普通装甲块。 | | `blocks.list` | 下述选择参数;`offset`、`limit` 同 `grids.list` | `{blocks:[Block],total,offset,truncated}`。只列终端方块,不包含普通装甲块。 | | `blocks.get` | `entityId`:必填终端方块 ID | 详细 `Block`。 | | `grid.stop` | `entityId`:必填网格 ID;`disableProgrammableBlocks`:可选布尔,默认 false;写权限 | `Grid` 加 `clearedThrusters`, `clearedGyros`, `disabledProgrammableBlocks`, `note`。清零该网格推进器覆盖,清零陀螺仪 yaw/pitch/roll 并关闭覆盖,清除该网格速度;选项为 true 时禁用该网格 PB。 | `blocks.list` 与 `telemetry.snapshot` 共用以下选择方式: | 选择方式 | 语义 | | --- | --- | | `entityIds:["ID",...]` | 明确指定最多 256 个终端方块,去重并保留首次出现顺序;优先于 `gridId`、`name`、`type`,这些过滤字段此时不再生效。任一实体无效会使整个查询报错。 | | `gridId:"ID"` | 仅指定网格,不自动扩展到连接器或机械连接的其他网格。省略时遍历当前世界所有未关闭网格。 | | `name:"子串"` | 可选,匹配方块 `CustomName`,不区分大小写。 | | `type:"子串"` | 可选,匹配方块定义的 `TypeIdString` 或 `SubtypeName`,不区分大小写。 | 非 `entityIds` 模式按方块实体 ID 排序。列表分页是调用时的实时结果,没有固定快照游标;`total` 是过滤后数量,`truncated` 表示后面仍有项目。 `grid.stop` 是一次性操作,不会持续冻结网格,不会禁用所有推进器,也不会停止其他相连网格。仍在运行的控制器/PB 可以再次施力;调试时按需显式设置 `disableProgrammableBlocks:true` 并查询相关网格。 ### 完整度与船体损伤 单个 `Block.integrity` 直接读取游戏方块的完整度与损伤,数值保留游戏精度,不是百分比: | 字段 | 含义 | | --- | --- | | `current`, `build`, `maximum` | 当前完整度、已经建造的组件完整度、完整建造时的上限。 | | `currentDamage` | `build - current`,已生效的损伤;不会把尚未建造的组件算作损伤。 | | `accumulatedDamage` | 已累积、尚待游戏结算的伤害。 | | `hasDeformation` | 是否存在骨骼变形。 | `grids.get` 和 `telemetry.snapshot.result.grids[]` 返回网格 `integrity`。它遍历该**单个网格**的所有方块类型,包含装甲,字段如下: | 字段 | 含义 | | --- | --- | | `inspectedBlockCount`, `truncated` | 本次实际检查数量,以及是否存在未检查方块。每网格最多检查 **20000 块**;`truncated:true` 时以下总量只代表已检查部分。完整方块数仍见 `Grid.blockCount`。 | | `current`, `build`, `maximum`, `currentDamage`, `accumulatedDamage` | 已检查方块对应数值的累加。 | | `damagedBlockCount` | `currentDamage > 0` 或 `accumulatedDamage > 0` 的方块数量。 | | `deformedBlockCount`, `incompleteBlockCount` | 发生变形的数量,以及 `build < maximum` 的数量;同一块可能同时计入多个类别。 | | `damageLocations` | 最多 32 个受损或变形方块;每项为单块 `integrity` 字段加 `positionInGrid`。以网格坐标定位,不依赖返回顺序。 | | `damageLocationsTruncated` | 已检查部分中,是否还有未列出的受损/变形位置;网格扫描是否完整另查 `truncated`。 | 普通 `grids.list` 和 `grid.stop` 的 `Grid` 不进行这项汇总。遥测只汇总本次实际返回方块所属的网格,不自动包含连接器或机械连接的其他网格。高频观察时用精确 `entityIds` 或 `gridId` 限定范围。 核对飞行损伤时,先保存相同网格 ID 的基线,再比较 `blockCount`、完整度、伤害和变形数量;被摧毁并移除的方块不会继续出现在当前损伤列表。完整度没有变化只能说明这些指标未记录到损伤,不能单独证明没有发生接触。 ## 5. 终端动作和属性 | 方法 | 参数 | `result` | | --- | --- | --- | | `blocks.actions` | `entityId` 必填;`limit` 默认 256、1–2048 | `{entityId,actions:[{id,name,enabled,value}],total}`。`value` 是当前显示文本,超过 4096 字符截断并追加 `…`。 | | `blocks.action` | `entityId`, `actionId` 必填;写权限 | `{applied:true,entityId,actionId}`。动作必须存在且启用;使用查询返回的动作 `id`,不是本地化显示名称。 | | `blocks.properties` | `entityId` 必填;`limit` 默认 256、1–2048 | `{entityId,properties:[{id,type,value,supported}],total}`。某项取值失败时该项含 `error`,可能没有 `value`/`supported`。不支持的类型返回 `value:null,supported:false`。 | | `blocks.setProperty` | `entityId`, `propertyId`, `value` 必填;写权限 | `{entityId,propertyId,value}`,`value` 是写入后重新读取的值。 | 动作/属性列表没有 `offset` 参数。设置前先枚举真实方块提供的 ID,不要假定其他方块/版本拥有同名属性。`supported:true` 表示桥支持该属性的类型,不保证方块一定允许此刻设置成功。 | 属性类型 | JSON `value` 与限制 | | --- | --- | | `Boolean` | 布尔,不能用 `"true"` 字符串替代。 | | `Single` / `Double` | 有限 JSON 数字;按属性 `GetMinimum`/`GetMaximum` 检查上下限。列表当前不输出上下限。 | | `Int32` / `Int64` | 使用可精确表示的整数;按属性上下限检查。Int64 读出为十进制字符串,写入也可使用十进制字符串以免客户端丢精度。 | | `String` / `StringBuilder` | JSON 字符串;读出超过 4096 字符截断并追加 `…`,设置时未另设字符上限但受请求总大小限制。 | | `Color` | `{r,g,b,a}`,各通道 0–255;`a` 可省略,默认 255。 | 上述表是 CLR 属性类型概念;`properties[].type` 保留游戏提供的原始类型名称。其他类型不能通过此接口写入。没有提供任意 CLR 方法调用、表达式求值或调用方指定的反射路径。 ## 6. 可编程方块(PB) ### `pb.read` 参数:`entityId`,必填 PB 方块 ID。返回详细 `Block` 加以下字段: | 字段 | 含义 | | --- | --- | | `source`, `programData` | 当前 PB 源码,两个字段内容相同,不截断。 | | `sha256` | 对当前源码按 UTF-8 计算的 SHA-256,小写 64 位十六进制;用于部署时并发核对。 | | `customData` | PB CustomData,不截断。 | | `storage` | PB 后备持久化存储字段当前值。 | | `liveStorage` | 仅有运行实例时返回实例的 `Storage`;可能与 `storage` 不同。 | | `hasCompileErrors`, `compilerErrors` | 编译错误标志与最多 256 条错误。 | | `echo` | 当前 Echo 输出,最多 32768 字符,超出追加 `…`。 | | `terminationReason`, `isRunning`, `hasInstance` | 当前终止原因、执行标志、是否有已编译脚本实例。 | | `runtime` | 仅有实例时返回下述运行信息。 | `runtime` 字段:`lastRunTimeMs`, `timeSinceLastRunSeconds`, `updateFrequency`, `lifetimeTicks`,以及可读取时的 `maxInstructionCount`, `maxCallChainDepth`, `currentInstructionCount`, `currentCallChainDepth`。计数器访问失败时返回 `counterUnavailable`。读取时的计数值不是逐语句追踪结果。 ### `pb.deploy` 必填参数:`entityId`、`source` 字符串、`expectedSha256` 字符串;需要写权限且世界启用游戏内脚本。`source` 最多 **100000 个 .NET 字符(UTF-16 代码单元)**,仍受 1 MiB 请求限制。 执行顺序: 1. 用当前 PB 源码哈希核对 `expectedSha256`,忽略十六进制字母大小写。不匹配时报 `execution_error`,要求重新读取。核对范围是源码,不是 CustomData/Storage。 2. 在当前世界的 `Storage\XFE.AgentBridge\Backups` 下创建独立 JSON 备份并完成落盘。 3. 设置 `ProgramData`,由游戏同步重编译并运行脚本构造函数。 4. 返回 `pb.read` 的字段,加 `backupPath`, `deployed:true`, `compiled`。`compiled` 仅在没有编译错误且已有实例时为 true。 备份含 `formatVersion`, `entityId`, `utc`, `programData`, `sha256`, `customData`, `storage`, `liveStorage`。没有自动恢复 RPC。编译失败不会自动回滚源码;必须检查 `compiled`、`hasCompileErrors` 和 `compilerErrors`,需要恢复时读取备份、重新读取当前 PB 哈希,然后再次部署旧源码。备份其他字段不会由 `pb.deploy` 自动恢复。 不要传 `recompile`、`run` 或 `preserveStorage` 等未实现参数。部署本身会触发游戏编译/构造函数,不是单纯把文件放入磁盘;脚本后续是否运行取决于其逻辑、方块状态和更新频率。 ### `pb.run` 参数:`entityId` 必填,`argument` 可选字符串,默认 `""`,最多 16384 个字符;需要写权限。调用 PB `TryRun(argument)`,返回 `pb.read` 字段加 `ran`。`ran:false` 是正常返回值,不能因 RPC 成功就判定脚本执行成功。此接口不提供单步执行或持续运行控制器。 ### `pb.inspect` 参数:`entityId` 必填;`depth` 可选整数,默认 2,夹在 0–3;`fields` 可选根脚本字段名字符串数组,最多 32 项。 - 无脚本实例:`{entityId,hasInstance:false}`。 - 有脚本实例:`{entityId,hasInstance:true,fields:<反射快照>,budgetRemaining}`。 - 不传 `fields` 时查看脚本根字段;传入后只展开这些根字段,不接受点路径。不存在的名字报错;空数组得到空字段对象。 - 只遍历脚本程序集内的实例字段,包含公有/非公有字段;不读取任意属性 getter,不沿游戏实体、委托或框架对象无限遍历。 - 支持数组,以及准确运行类型为 `List`、`Dictionary`、`HashSet`、`Queue`、`Stack` 及对应 `MemorySafe*` 的集合。集合最多 64 项,每个对象最多考察 128 个字段,全局值访问预算 600。集合包装层不消耗对象字段深度,但元素仍受数量和全局预算限制。 - 字典输出 `[{key,value},...]`,不是以字符串键展开的 JSON 对象。枚举输出名称字符串;向量输出坐标对象;`MatrixD` 输出 `pose` 形状;`TimeSpan` 输出秒;普通原始数值字段仍为 JSON 数字,包括脚本自己的 `long` 字段。 - 文本/StringBuilder 最多 4096 字符,超出追加 `…`。可能出现 ``、``、``、``、``、`<类型全名>` 或对象的 `$truncated:true`;这些标记不是脚本原始值。 **AMS 压缩脚本字段注意事项:**字段名是实际已编译脚本中的名字,压缩构建可能是 `a`、`A` 等短名,同一对象可能同时包含仅大小写不同的字段。`fields:["a"]` 与 `fields:["A"]` 不等价;不同构建也可能改变映射。先读取当前 `pb.inspect`/当前部署源码,再据此选择真实字段,不要推测它们对应未压缩源码中的哪个成员。 客户端须用保留大小写的 JSON 解析器/字典。PowerShell 默认 `ConvertFrom-Json` 转为属性对象时不能可靠表示 `a`/`A` 这样的键组合;可使用 PowerShell 7 的 `ConvertFrom-Json -AsHashtable`,或 `System.Text.Json.JsonDocument` 逐个读取精确属性名。不要先用不区分大小写的对象反序列化,再尝试恢复字段。处理脚本反射出的 Int64 数字时也要保留整数精度。 ## 7. 摄像头扫描 `cameras.scan` 需要写权限,因为扫描会消耗充能,且可改变 `EnableRaycast`。 | 参数 | 约定 | | --- | --- | | `entityId` | 必填摄像头方块 ID。 | | `enableRaycast` | 可选布尔;显式为 true 时先开启 Raycast。省略/false 不会关闭它;若原本未开启则报错。 | | `distance` | 可选有限数,默认 100 米;范围 `(0,1000000]`,且不得超过摄像头非负的 `RaycastDistanceLimit`。 | | `pitch`, `yaw` | 可选有限数,默认 0,单位度,相对于摄像头;两者绝对值均不得超过该摄像头锥角限制。 | 充能不足返回正常结果: ```json {"scanned":false,"reason":"insufficientCharge","availableScanRange":75.0,"timeUntilScanMs":500} ``` 完成扫描返回 `scanned:true`, `cameraId`, `availableScanRange`, `empty`, `entityId`, `name`, `type`, `position`, `hitPosition`, `velocity`, `bounds`, `relationship`, `timestamp`。其中 `hitPosition` 可为 `null`,`timestamp` 原样来自游戏探测信息;先判断 `empty` 再解释目标字段。扫描后 `availableScanRange` 是剩余可用扫描距离。 此方法每次仅对指定摄像头发出一次角度射线,不自动选择六方向摄像头、不旋转飞船、不规划避障路线。`enableRaycast:true` 在其他参数校验前设置,即使随后扫描参数无效,摄像头也可能已经开启。 ## 8. 遥测快照 `telemetry.snapshot` 使用第 4 节的方块选择参数;另有: | 参数 | 默认与限制 | | --- | --- | | `limit` | 256,夹在 1–1024;显式 `entityIds` 仍最多 256 项。 | | `includeInventoryItems` | true;false 时保留库存汇总但不展开物品。 | | `includeScreens` | true;false 时不读屏幕。 | 返回 `{utc,frame,grids:[Grid],blocks:[...],total,truncated}`。`grids` 仅包含本次实际输出方块所在的去重网格;`total` 是选择命中的方块总数。此方法**没有 `offset` 分页**;大量方块请先 `blocks.list` 分页获取 ID,再分批指定 `entityIds`。单个方块补充遥测失败时,该项带 `telemetryError`,其他项可继续返回。 每项先包含基本 `Block`,按方块支持的接口追加: | 对象 | 字段 | | --- | --- | | `battery` | `storedMWh`, `maxStoredMWh`, `inputMW`, `outputMW`, `chargeMode`, `charging`。 | | `thrust` | `currentN`, `maximumN`, `maxEffectiveN`, `overrideN`, `overrideRatio`, `gridDirection`, `forceDirection`。`forceDirection` 为该推进器世界矩阵的 Backward。 | | `gyro` | `override`, `power`, `yaw`, `pitch`, `roll`。 | | `connector` | `status`, `connected`, `connectable`, `otherConnectorId`(无对端为 null), `throwOut`, `collectAll`, `pullStrength`;实际游戏连接器另有下述约束点、吸附和交易字段。 | | `camera` | `enabledRaycast`, `availableScanRange`, `coneLimitDegrees`, `distanceLimit`。 | | `gasTank` | `capacity`, `filledRatio`, `stockpile`。 | | `flight` | `linearVelocity`, `angularVelocity`, `speed`, `naturalGravity`, `artificialGravity`, `totalMassKg`, `physicalMassKg`, `baseMassKg`, `centerOfMass`, `dampeners`, `underControl`, `controlThrusters`, `moveIndicator`, `rotationIndicator:{x,y}`, `rollIndicator`。仅船舶控制器提供。 | | `programmableBlock` | `sha256`, `compileErrors`(布尔), `hasInstance`, `runtime`(无实例为 null), `echo`(最多 8192 字符后加 `…`), `terminationReason`。此处不是 `pb.read` 的 `hasCompileErrors` 字段名。 | | `inventories` | 有库存时输出数组,每方块最多 16 个库存;每项含 `index`, `massKg`, `volumeM3`, `maxVolumeM3`, `itemCount`。展开物品时还有 `items:[{itemId,type,subtype,amount}]`(最多 128 项)和 `truncated`。 | | `cargoInventory` | 有库存方块的分类标记;仅货箱、钻头、连接器为 true。反应堆、氢氧制造机等仍可返回库存,但此标记为 false;统计矿机待卸货库存时可据此排除燃料/生产库存。 | | `screens` | 每方块最多 16 个屏幕,每项 `index`, `name`, `displayName`, `contentType`, `text`, `script`, `surfaceSize:{x,y}`;`text` 最多 8192 字符后加 `…`。没有屏幕时可缺省整个字段。 | ### 连接器精确遥测 | 字段 | 含义 | | --- | --- | | `connected`, `connectable` | 分别表示 `status` 为 `Connected`、`Connectable`。 | | `constraintPosition` | `MyShipConnector.ConstraintPositionWorld()` 返回的世界坐标,单位米;用于比较连接约束点,不能用 `pose.position` 的方块中心替代。 | | `inConstraint` | 游戏的 `InConstraint` 标志。 | | `magnetized` | 本桥计算的 `inConstraint && !connected`,表示已进入约束但还未锁定;不表示存在磁力范围内的所有候选接口。 | | `constraintOtherConnectorId` | 游戏实际 `Other` 的实体 ID,无对端时为 null;吸附阶段也可能已有值。原有 `otherConnectorId` 来自脚本接口的 `OtherConnector`,不应用它代替本字段判断吸附对端。 | | `otherConstraintPosition`, `constraintDistance` | 仅实际 `Other` 非空时返回,对端约束点及两个约束点的欧氏距离(米)。还未吸附时要在同一次查询中明确选择两端连接器,比较各自 `constraintPosition`。 | | `isSmallConnector` | 游戏的小型接口类别标志,不等同于“安装在小网格上”。 | | `tradingEnabled`, `protectedFromLockingByTrading` | 实际交易开关,以及游戏当前是否因交易保护而阻止锁定。 | 这些字段通过固定的游戏 API 只读取得,不主动寻找、吸附或连接接口。约束点距离只是捕获条件之一;还须结合状态、功能/供电、所有者、接口类型、方向与交易保护判断。捕获阈值属于当前游戏实现,插件不将某个固定距离当成通用的“可以连接”结论。 `screens[].text` 是文本接口读到的字符串,不是 Sprite/图形画面的 OCR。遥测是一次游戏线程采样;需要趋势时用 CLI `watch`。库存物品、屏幕文本或大量详细对象可能触发 4 MiB 响应上限,先减少数量或关闭可选展开项。 ## 9. CLI 调用 以下 `xfe-se.exe` 指已构建/发布的 CLI 路径;可用完整路径替换。`stdout` 为 JSON,诊断写 `stderr`。命令与选项区分大小写,重复/未知选项报错。 ```text xfe-se.exe discover [--pid PID] [--endpoint-directory PATH] xfe-se.exe call METHOD [--params JSON|@FILE] [--pipe NAME|--pid PID] [--timeout 30] xfe-se.exe deploy --block ID --file SCRIPT.CS --expected-hash SHA256 [--pipe NAME|--pid PID] xfe-se.exe watch --block ID,ID --seconds 30 --interval 1 --out telemetry.jsonl [--pipe NAME|--pid PID] ``` `call`、`deploy`、`watch` 都接受 `--timeout` 和 `--endpoint-directory`。`--timeout` 单位是**秒**,默认 30,可在 0.001–30 之间;协议 `timeoutMs` 单位是毫秒。`--pipe` 与 `--pid` 不能同时出现。发现多实例时必须明确选一个;显式 `--pipe` 传管道名字 `XFE.SE.Agent.1234`,不要传 `\\.\pipe\...` 路径。 `call --params @FILE` 从 UTF-8 JSON 文件读取对象;文件最多 1 MiB,整个请求还须符合协议限制。CLI `deploy` 读取源码文件,将 `--block`、文件内容和 `--expected-hash` 映射到 `pb.deploy` 的 `entityId`、`source`、`expectedSha256`,不会自动查询哈希或自动部署第二次。 ### 可重复的调用流程 1. `discover` 选择游戏实例,调用 `agent.capabilities` 和 `world.status`。 2. 若需要测试世界且在主菜单,调用 `world.load`;轮询到指定路径就绪。写入前核对 `canMutate`。 3. 用 `grids.list` / `blocks.list` 查找精确网格和 PB;后续保存实体 ID 字符串,避免靠可能重复的名字定位。 4. `pb.read` 保存当前 `sha256`,检查原脚本/编译状态;将该哈希传给 `deploy`。 5. 检查部署结果的 `compiled` 和错误列表;再按脚本定义选择是否 `pb.run`。用 `telemetry.snapshot` / `watch` 和 `pb.inspect` 验证实际状态。 6. 需要持久化时 `world.save`;需要退出游戏时明确发送 `world.exit` 的 `{"save":true}`。 例如准备一个只读取指定 PB 的参数文件,避免不同 shell 对内联 JSON 引号的处理差异: ```powershell # 用实际查询得到的 ID 替换该示例字符串。 '{"entityId":"1234567890123456789"}' | Set-Content -LiteralPath .\pb-read.json -Encoding utf8 & .\xfe-se.exe call pb.read --params '@pb-read.json' --pid 1234 & .\xfe-se.exe deploy --block '1234567890123456789' --file .\script.cs --expected-hash '' --pid 1234 ``` 上例 PID、实体 ID 和哈希都必须替换为实际值;不应复制示例 ID 尝试操作真实方块。源码中的引号/换行由 `deploy` 命令序列化,不需手工转义。 `watch` 的 `--block` 是逗号分隔的 ID,`--out` 必填,**会覆盖同名文件**。`--seconds` 默认 30,范围 0.05–86400;`--interval` 默认 1,范围 0.05–60。每次查询 `telemetry.snapshot`,向 JSONL 写入 `{capturedUtc,response:<完整 RPC 响应>}`,每样本刷新文件。间隔是在上次响应后等待,不是精确固定频率;进行中的调用可使总时长超过指定秒数。RPC 错误样本也会写入,然后停止。成功时 stdout 返回 `{samples,elapsedSeconds,outputPath}`。 ## 10. 错误处理与限制速查 服务端 `error` 是 `{code,message}`,最长分别 64 / 2048 字符。下面是当前代码实际使用的错误码;不要依赖不存在的 `not_found`、`conflict`、`unauthorized` 等细分码。 | 服务端错误码 | 含义与处理 | | --- | --- | | `parse_error` | UTF-8/JSON 无效、深度超过 32、重复属性或不是单个对象;修正请求。 | | `invalid_request` | 协议字段格式不符,或同一连接有多条/额外请求数据。 | | `invalid_params` | 协议外层 `params` 不是对象。具体方法参数错误通常是下方 `execution_error`。 | | `request_too_large` | 请求超过 1 MiB。 | | `response_too_large` | 响应超过 4 MiB;减少对象数量、字段展开或文本规模。写操作可能已发生。 | | `server_busy` | 请求队列达到上限;减少并发后重试。 | | `timeout` | 排队或执行等待期限耗尽;按第 1 节区分未开始与已开始,先核对状态。 | | `server_stopped` | 插件/服务已停止;未开始的队列项被取消,连接也可能直接关闭。 | | `disconnected` | 客户端已断开;客户端通常无法再收到该错误。 | | `execution_error` | 方法未知、世界未就绪、写入未授权、实体不存在/类型不符、参数不合法、源码哈希冲突、游戏操作异常等。结合 `message` 和当前状态修正,不要自动重试所有情况。 | CLI 本地错误没有 RPC `id`,返回 `{error:{code,message}}`。另可能出现 `invalid_arguments`, `invalid_json`, `local_error`, `cancelled`, `invalid_pipe`, `invalid_timeout`, `pipe_error`, `incomplete_response`, `invalid_response`, `response_id_mismatch`;`timeout`、`request_too_large`、`response_too_large` 也可由客户端检测。 | CLI 退出码 | 含义 | | --- | --- | | 0 | 命令成功;仍需检查方法自己的 `saved`、`compiled`、`ran`、`scanned` 等业务结果。 | | 1 | 本地文件、访问或一般参数转换错误。 | | 2 | 命令用法/输入 JSON 错误。 | | 3 | 客户端/管道/响应协议错误。 | | 4 | 收到服务端 RPC 错误。 | | 130 | 客户端等待被取消,例如 Ctrl+C;不保证已经开始的游戏操作被取消。 | 查询上限通常通过**截断/夹取**实现:列表 `limit`、检查深度、集合数量、Echo/屏幕文本等;请求尺寸、明确 ID 数量、PB 源码长度及不合法扫描参数会拒绝调用。先检查 `total`、`truncated`、`$truncated`、预算标记与响应错误,再判断数据是否完整。