Agent Bridge API(协议 1)
本文描述当前 1.0.0 实现,供本机 AI Agent 和其他客户端直接调用。方法、参数和返回字段均按 AgentPlugin.cs、GameDebugApi.cs、BlockOperations.cs、WorldQueries.cs 及 CLI 整理。返回字段随方块类型、世界状态和运行实例而变化;不要把缺少的字段当成 false 或 0。
1. 连接、协议与执行边界
插件使用 Windows 本机命名管道 XFE.SE.Agent.<游戏进程 PID>。管道 ACL 只允许当前 Windows 用户,显式拒绝 Network SID;没有 HTTP/TCP 接口。每个连接发送一行 UTF-8 JSON,接收一行 JSON,然后关闭连接。请求以 LF 结束,也接受 CRLF;不要在同一连接发送第二个请求或额外字节。
{"id":"request-1","method":"world.status","params":{},"timeoutMs":10000}
成功与失败响应分别为:
{"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或缩小查询确认状态。
发现活动端点
插件写入:
%LOCALAPPDATA%\XFE\SpaceEngineersAgent\endpoints\<PID>.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 有独立的允许路径检查。
允许路径来自插件启动时读取的配置:
%LOCALAPPDATA%\XFE\SpaceEngineersAgent\config.json
{"allowedWorldPaths":["D:\\SE-TestCopies\\AgentSandbox"]}
配置最大 64 KiB。条目须为绝对路径;按完整路径去除末尾目录分隔符后、不区分大小写地精确匹配,不是目录前缀授权。未配置时允许列表为空。配置不会自动创建世界副本,也不会在运行中重新加载;修改后须重新加载插件/重启游戏。应填测试副本的存档目录,该目录包含 Sandbox.sbc。
ID 与坐标
- 输入
entityId、gridId、entityIds中每项都应使用非零 Int64 十进制字符串,支持负数。不要经过 JavaScriptNumber;其精度不足以保存所有实体 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 请求限制。
执行顺序:
- 用当前 PB 源码哈希核对
expectedSha256,忽略十六进制字母大小写。不匹配时报execution_error,要求重新读取。核对范围是源码,不是 CustomData/Storage。 - 在当前世界的
Storage\XFE.AgentBridge\Backups下创建独立 JSON 备份并完成落盘。 - 设置
ProgramData,由游戏同步重编译并运行脚本构造函数。 - 返回
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 字符,超出追加
…。可能出现<budget exhausted>、<depth limit: ...>、<reference>、<truncated>、<unavailable: ...>、<类型全名>或对象的$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,单位度,相对于摄像头;两者绝对值均不得超过该摄像头锥角限制。 |
充能不足返回正常结果:
{"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。命令与选项区分大小写,重复/未知选项报错。
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,不会自动查询哈希或自动部署第二次。
可重复的调用流程
discover选择游戏实例,调用agent.capabilities和world.status。- 若需要测试世界且在主菜单,调用
world.load;轮询到指定路径就绪。写入前核对canMutate。 - 用
grids.list/blocks.list查找精确网格和 PB;后续保存实体 ID 字符串,避免靠可能重复的名字定位。 pb.read保存当前sha256,检查原脚本/编译状态;将该哈希传给deploy。- 检查部署结果的
compiled和错误列表;再按脚本定义选择是否pb.run。用telemetry.snapshot/watch和pb.inspect验证实际状态。 - 需要持久化时
world.save;需要退出游戏时明确发送world.exit的{"save":true}。
例如准备一个只读取指定 PB 的参数文件,避免不同 shell 对内联 JSON 引号的处理差异:
# 用实际查询得到的 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 '<pb.read.result.sha256>' --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、预算标记与响应错误,再判断数据是否完整。