XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFE.SpaceEngineers.AgentBridge

【SpaceEngineer】AI调试插件

公开
关注 0 Fork 0 Star 0
UTF-8

Agent Bridge API(协议 1)

本文描述当前 1.0.0 实现,供本机 AI Agent 和其他客户端直接调用。方法、参数和返回字段均按 AgentPlugin.csGameDebugApi.csBlockOperations.csWorldQueries.csCLI 整理。返回字段随方块类型、世界状态和运行实例而变化;不要把缺少的字段当成 false0

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

端点字段为 versionprotocolVersionpipeNamepidstartTimeUtcexecutablePathcreatedUtc。CLI discover 会核对 PID、进程启动时间及可执行文件完整路径,忽略失效、格式错误或不可访问的记录;最多检查 1024 个文件,每文件最多 64 KiB。端点文件存在本身不证明游戏仍存活。

2. 世界权限与通用数据结构

agent.*world.status 无需已加载世界。其他读取方法要求世界 ready:true 且未卸载。写方法要求 world.status.canMutate:true:世界已就绪、未卸载、本机为服务器、onlineModeOFFLINE、世界路径被明确允许。world.load 有独立的允许路径检查。

允许路径来自插件启动时读取的配置:

%LOCALAPPDATA%\XFE\SpaceEngineersAgent\config.json
{"allowedWorldPaths":["D:\\SE-TestCopies\\AgentSandbox"]}

配置最大 64 KiB。条目须为绝对路径;按完整路径去除末尾目录分隔符后、不区分大小写地精确匹配,不是目录前缀授权。未配置时允许列表为空。配置不会自动创建世界副本,也不会在运行中重新加载;修改后须重新加载插件/重启游戏。应填测试副本的存档目录,该目录包含 Sandbox.sbc

ID 与坐标

  • 输入 entityIdgridIdentityIds 中每项都应使用非零 Int64 十进制字符串,支持负数。不要经过 JavaScript Number;其精度不足以保存所有实体 ID。
  • 常规结果中的 entityIdgridIdownerIdcameraIdotherConnectorId 是字符串或按字段约定为 null。无命中摄像头可返回 entityId:"0",无所有者可返回 ownerId:"0";不能用这些零值继续查询实体。库存 itemId 是物品堆标识数字,不是实体 ID。
  • Vec3{x,y,z}pose{position,forward,up,right},来自世界矩阵。bounds 为世界轴对齐包围盒 {min:Vec3,max:Vec3},不是精确碰撞网格;positionInGrid 是网格内整数坐标。
  • 本文 GridBlock 是下述返回形状的简称,不是额外 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 BlockcustomData, detailedInfo, customInfo。后两项各保留前 32768 字符,超出追加 customData 不截断,仍受响应总大小限制。

3. Agent 与世界生命周期

方法 参数 result 核心字段与行为
agent.ping {} version, protocolVersion, ticks, utc, uptimeSecondsticks 是插件 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, pausedByBridgeloaded: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.loadaccepted 只表示请求已发起。轮询 world.status,核对 ready:truepath 是目标世界,写操作前再核对 canMutate:true。加载观察超过 10 分钟会清除 loadingPath 并设置 loadError;这不等于取消了游戏自身的加载流程。读取/保存调用也可能因游戏加载界面或繁忙而超时。

事件同时追加至 %LOCALAPPDATA%\XFE\SpaceEngineersAgent\agent.log,当前日志超过 4 MiB 后在下次写入时轮换为 agent.log.1agent.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 下述选择参数;offsetlimitgrids.list {blocks:[Block],total,offset,truncated}。只列终端方块,不包含普通装甲块。
blocks.get entityId:必填终端方块 ID 详细 Block
grid.stop entityId:必填网格 ID;disableProgrammableBlocks:可选布尔,默认 false;写权限 GridclearedThrusters, clearedGyros, disabledProgrammableBlocks, note。清零该网格推进器覆盖,清零陀螺仪 yaw/pitch/roll 并关闭覆盖,清除该网格速度;选项为 true 时禁用该网格 PB。

blocks.listtelemetry.snapshot 共用以下选择方式:

选择方式 语义
entityIds:["ID",...] 明确指定最多 256 个终端方块,去重并保留首次出现顺序;优先于 gridIdnametype,这些过滤字段此时不再生效。任一实体无效会使整个查询报错。
gridId:"ID" 仅指定网格,不自动扩展到连接器或机械连接的其他网格。省略时遍历当前世界所有未关闭网格。
name:"子串" 可选,匹配方块 CustomName,不区分大小写。
type:"子串" 可选,匹配方块定义的 TypeIdStringSubtypeName,不区分大小写。

entityIds 模式按方块实体 ID 排序。列表分页是调用时的实时结果,没有固定快照游标;total 是过滤后数量,truncated 表示后面仍有项目。

grid.stop 是一次性操作,不会持续冻结网格,不会禁用所有推进器,也不会停止其他相连网格。仍在运行的控制器/PB 可以再次施力;调试时按需显式设置 disableProgrammableBlocks:true 并查询相关网格。

完整度与船体损伤

单个 Block.integrity 直接读取游戏方块的完整度与损伤,数值保留游戏精度,不是百分比:

字段 含义
current, build, maximum 当前完整度、已经建造的组件完整度、完整建造时的上限。
currentDamage build - current,已生效的损伤;不会把尚未建造的组件算作损伤。
accumulatedDamage 已累积、尚待游戏结算的伤害。
hasDeformation 是否存在骨骼变形。

grids.gettelemetry.snapshot.result.grids[] 返回网格 integrity。它遍历该单个网格的所有方块类型,包含装甲,字段如下:

字段 含义
inspectedBlockCount, truncated 本次实际检查数量,以及是否存在未检查方块。每网格最多检查 20000 块truncated:true 时以下总量只代表已检查部分。完整方块数仍见 Grid.blockCount
current, build, maximum, currentDamage, accumulatedDamage 已检查方块对应数值的累加。
damagedBlockCount currentDamage > 0accumulatedDamage > 0 的方块数量。
deformedBlockCount, incompleteBlockCount 发生变形的数量,以及 build < maximum 的数量;同一块可能同时计入多个类别。
damageLocations 最多 32 个受损或变形方块;每项为单块 integrity 字段加 positionInGrid。以网格坐标定位,不依赖返回顺序。
damageLocationsTruncated 已检查部分中,是否还有未列出的受损/变形位置;网格扫描是否完整另查 truncated

普通 grids.listgrid.stopGrid 不进行这项汇总。遥测只汇总本次实际返回方块所属的网格,不自动包含连接器或机械连接的其他网格。高频观察时用精确 entityIdsgridId 限定范围。

核对飞行损伤时,先保存相同网格 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

必填参数:entityIdsource 字符串、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, compiledcompiled 仅在没有编译错误且已有实例时为 true。

备份含 formatVersion, entityId, utc, programData, sha256, customData, storage, liveStorage。没有自动恢复 RPC。编译失败不会自动回滚源码;必须检查 compiledhasCompileErrorscompilerErrors,需要恢复时读取备份、重新读取当前 PB 哈希,然后再次部署旧源码。备份其他字段不会由 pb.deploy 自动恢复。

不要传 recompilerunpreserveStorage 等未实现参数。部署本身会触发游戏编译/构造函数,不是单纯把文件放入磁盘;脚本后续是否运行取决于其逻辑、方块状态和更新频率。

pb.run

参数:entityId 必填,argument 可选字符串,默认 "",最多 16384 个字符;需要写权限。调用 PB TryRun(argument),返回 pb.read 字段加 ranran:false 是正常返回值,不能因 RPC 成功就判定脚本执行成功。此接口不提供单步执行或持续运行控制器。

pb.inspect

参数:entityId 必填;depth 可选整数,默认 2,夹在 0–3;fields 可选根脚本字段名字符串数组,最多 32 项。

  • 无脚本实例:{entityId,hasInstance:false}
  • 有脚本实例:{entityId,hasInstance:true,fields:<反射快照>,budgetRemaining}
  • 不传 fields 时查看脚本根字段;传入后只展开这些根字段,不接受点路径。不存在的名字报错;空数组得到空字段对象。
  • 只遍历脚本程序集内的实例字段,包含公有/非公有字段;不读取任意属性 getter,不沿游戏实体、委托或框架对象无限遍历。
  • 支持数组,以及准确运行类型为 ListDictionaryHashSetQueueStack 及对应 MemorySafe* 的集合。集合最多 64 项,每个对象最多考察 128 个字段,全局值访问预算 600。集合包装层不消耗对象字段深度,但元素仍受数量和全局预算限制。
  • 字典输出 [{key,value},...],不是以字符串键展开的 JSON 对象。枚举输出名称字符串;向量输出坐标对象;MatrixD 输出 pose 形状;TimeSpan 输出秒;普通原始数值字段仍为 JSON 数字,包括脚本自己的 long 字段。
  • 文本/StringBuilder 最多 4096 字符,超出追加 。可能出现 <budget exhausted><depth limit: ...><reference><truncated><unavailable: ...><类型全名> 或对象的 $truncated:true;这些标记不是脚本原始值。

**AMS 压缩脚本字段注意事项:**字段名是实际已编译脚本中的名字,压缩构建可能是 aA 等短名,同一对象可能同时包含仅大小写不同的字段。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 可为 nulltimestamp 原样来自游戏探测信息;先判断 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, forceDirectionforceDirection 为该推进器世界矩阵的 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.readhasCompileErrors 字段名。
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 分别表示 statusConnectedConnectable
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]

calldeploywatch 都接受 --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.deployentityIdsourceexpectedSha256,不会自动查询哈希或自动部署第二次。

可重复的调用流程

  1. discover 选择游戏实例,调用 agent.capabilitiesworld.status
  2. 若需要测试世界且在主菜单,调用 world.load;轮询到指定路径就绪。写入前核对 canMutate
  3. grids.list / blocks.list 查找精确网格和 PB;后续保存实体 ID 字符串,避免靠可能重复的名字定位。
  4. pb.read 保存当前 sha256,检查原脚本/编译状态;将该哈希传给 deploy
  5. 检查部署结果的 compiled 和错误列表;再按脚本定义选择是否 pb.run。用 telemetry.snapshot / watchpb.inspect 验证实际状态。
  6. 需要持久化时 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_foundconflictunauthorized 等细分码。

服务端错误码 含义与处理
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_mismatchtimeoutrequest_too_largeresponse_too_large 也可由客户端检测。

CLI 退出码 含义
0 命令成功;仍需检查方法自己的 savedcompiledranscanned 等业务结果。
1 本地文件、访问或一般参数转换错误。
2 命令用法/输入 JSON 错误。
3 客户端/管道/响应协议错误。
4 收到服务端 RPC 错误。
130 客户端等待被取消,例如 Ctrl+C;不保证已经开始的游戏操作被取消。

查询上限通常通过截断/夹取实现:列表 limit、检查深度、集合数量、Echo/屏幕文本等;请求尺寸、明确 ID 数量、PB 源码长度及不合法扫描参数会拒绝调用。先检查 totaltruncated$truncated、预算标记与响应错误,再判断数据是否完整。