@@ -0,0 +1,116 @@
# AGC Godot 编辑器插件接入
> 文档状态:`current`
> 规范关系:承接 AGC 通用插件宿主与编辑器适配主规范
更新时间:`2026-09-20`
## 目标与边界
将 Godot 编辑器操控接入现有 AGC PluginHost、EditorAdapter、Runner、内置插件开关、权限审计和 Agent 工具链。Windows x64 的 Godot 4.7 及以上标准编辑器是首个实现目标,实机验收使用 4.7.2;其他平台和 .NET 编辑器不得从该结果推断支持。
初版工程路径支持 Windows 本地盘符目录;UNC/网络共享路径在准备描述文件前明确拒绝。链接/reparse point 继续按同一文件边界失败关闭。
DLL 原件随 AGC 安装包放在插件资源目录中;Godot Windows 加载器会在被加载文件旁生成 `~DLL` ,因此宿主在 AGC 私有配置目录的运行缓存中按编辑器实例和构建身份准备临时加载副本。AGC 向项目新增一个可扫描的受管 `.gdextension` 描述文件,通过绝对路径引用该实例的加载副本;Godot 可自动生成其同名 `.uid` 伴生文件。DLL 不复制进工程。重新聚焦 Godot 后,由官方文件扫描完成首次加载;不需要用户打开或运行脚本,不创建 EditorPlugin addon,不修改 project.godot 或业务场景文件。编译工具链只属于开发与打包环境,不要求终端用户安装编译器。
本次包含连接、状态、GDScript 执行和真实回执、断开及资源清理,并验证通过代码读取和修改独立测试场景。专用截图/输入/场景工具目录、云服务、额外 MCP 服务、公开后端 API 和发布上传不在本次范围;通用执行可以调用 EditorInterface,不能把文件生成冒充编辑器内执行。
## 入口与归属
- 插件 id 为 `agc-godot-editor` ,适配器为 `godot-editor` ;命令 `godot.editor.execute` 、连接能力 `godot.editor.connection` , DirectProject 工具为 `agc_godot_execute` 。复用已有扩展列表和启用开关,不建立平行插件管理页面。
- 项目发现沿用现有 Godot 工作区合同:工作区根保持用户选定目录;实际 Godot 根由普通 project.godot 在根或唯一一层子目录中确定。准备描述文件和读取 Godot 缓存只作用于实际 Godot 根,通用文件工具/Runtime 的工作区根不改变。
- 平台、内置开关、项目及目标身份必须在执行入口重新检查。插件只能处理宿主传入的当前受控项目,模型不能覆盖项目路径、DLL 路径、端口、令牌或目标实例。
- 只连接已打开且唯一匹配真实工程路径的 Godot Editor;校验 PID、进程启动身份、Godot 版本、握手中的工程路径与会话代次。多个候选、非编辑器、路径不符或已退出的进程均拒绝,不启动或关闭用户编辑器。
- GUI、DirectProject 和 Agent Runtime 的原生操作统一由长寿命 Runner 持有。项目切换使连接失效,迟到回执不能改变新项目状态。
## 分发与描述文件
- 安装资源布局为 `plugins/agc-godot-editor/native/gdextension/bin/win-x64/agc_godot_editor.dll` ,邻接元数据记录协议、构建身份和 DLL SHA256。开发模式允许宿主提供仓库插件目录中的同结构产物;RPC 不接受自定义 DLL 候选。
- 缓存根仅由宿主提供,为其私有配置目录下的 `godot-editor-runtime` ;按 `PID + startedFileTime + buildId` 隔离,所有路径分量受控且拒绝链接/reparse point。复制前验证安装原件及元数据,缓存已有文件必须匹配来源、归属及 SHA,不能加载被替换的同名文件。受管描述同时保留原件与加载副本身份,重启恢复不得把工程给出的任意 DLL 路径当作受信任来源。
- 实际模块核验同时覆盖加载副本及 Godot 在同目录生成的精确 `~agc_godot_editor.dll` ,要求规范化路径和 DLL 字节身份一致。确认原生卸载后,只清理本实例、本构建、内容未变的缓存文件;不能跨编辑器删除或复用影子副本。安装位置更新和实例 PID 重用都必须重新验证。
- 原生扩展只加载自身受信任包内的实现;GDScript 桥源码编译进 DLL,目标工程不能替换引导脚本。保留 Godot 官方 ABI 来源及 MIT 许可;生成的 DLL、缓存和机器路径不提交。
- 描述文件固定为实际 Godot 根下的 `agc-editor-bridge.gdextension` ,带 AGC 所有权标记和构建身份。首次使用可创建,内容一致时不重写;安装路径或构建身份变化时只更新本插件拥有的文件。已有同名非受管文件、链接/reparse point 或未知内容必须拒绝覆盖。
- `.agent` 、点号目录和 `.gdignore` 路径不会被 Godot 自动扫描,因此描述文件不能放在那里。会话发现文件仅放在 `.godot/agc/` 缓存内,不进入 manifest、对话或日志;校验缓存各级目录没有链接跳转。
- 描述文件及引擎自动生成的 `.uid` 按同一归属管理:创建描述文件前已有孤立同名 UID 时拒绝接管;扫描生成后在 `.godot/agc/` 记录描述内容及 UID 内容指纹。重连、升级和清理核对此记录,仅删除原内容未变的本插件伴生文件;记录丢失、用户改动或未知同名文件时保留并报告,不把“格式合法”当作删除授权。
- connect/execute 可以准备受管描述文件,并尝试把经过验证的目标编辑器窗口置前触发扫描;若系统不允许聚焦或握手未就绪,返回明确的未派发错误,提示重新聚焦后连接。连接重试不重放业务代码。
- 升级、断开和禁用时先通过有效旧连接停用内存桥/卸载原生扩展,再清理与本会话匹配的受管描述文件和会话缓存。用户改动过的文件不删除;无可信握手时不能把删除文件当作已卸载。编辑器已退出时允许清理已确认归属的本地痕迹。
- 安装目录可能只读;插件不得向 DLL 所在目录写令牌、状态或日志。
- 安装位置/构建身份升级必须先确认旧扩展已卸载,再原子更新受管描述文件。旧 DLL 仍被目标进程加载、shutdown 结果不明或旧会话无法核实时,保留痕迹并返回可诊断错误,不用替换引用冒充完成升级。
- 正式描述文件禁用自动 DLL 热重载;首次聚焦发现加载不受此设置影响。版本/路径更新走上述受控卸载和新加载,防止引擎自动热重载越过正在执行或待核对的任务。
## 执行协议与结果
引擎端协议固定为 `agc.godot.editor.v1` 。本机回环 TCP 的 JSONL 每个请求包含 `protocol` 、正整数 `id` 、`generation` 、`token` 、`method` 和 `params` ;方法为 `status` 、`execute` 、`shutdown` 。会话缓存字段为 `protocol` 、`buildId` 、`pid` 、`startedFileTime` (字符串)、`generation` 、`projectPath` 、`version` 、`port` 、`token` 。文件名为 `editor-bridge-<pid>.json` ,单文件最多 64 KiB。每次加载产生随机代次和令牌,端口只监听 127.0.0.1。
每条响应回显 `protocol/id/generation/pid/projectPath/buildId` ,并包含 `result` ;执行的 result 沿用 AGC 结构:`ok` 、`status` 、`dispatched` 、`retryAllowed:false` 、成功 `result` 或失败 `error:{code,message}` ,可附有界日志。状态为 `completed` 、`failed` 、`needs-reconciliation` 。连接状态沿用 `EditorConnectionInfo` ,可增加代次、就绪与版本诊断字段。
`execute.params` 固定为 `{code:string,timeoutMs:integer}` ; `timeoutMs` 为 1..60000 的剩余预算。`status.params` 和 `shutdown.params` 均为空对象。status 的 result 为 `{connected:true,pid,projectPath,version,generation,buildId,executing:boolean}` 。shutdown 在没有在途执行时返回 `{accepted:true,status:"shutting-down"}` ,仅表示停机请求已受理;桥在发送该回执后移除自己的会话缓存、关闭监听并卸载扩展。宿主必须继续校验同一目标进程的原生模块已卸载、原代次会话消失,才允许删除/替换描述文件或投影为断开完成。拒绝停机返回 `{accepted:false,error:{code,message}}` ,不能把 accepted 当作卸载完成回执。
- GDScript 是可含 return/await 的函数体,非空、不含 NUL,最多 128 KiB;请求和回执最多 2 MiB,日志有界。执行运行于编辑器主线程,临时桥的 owner=null,不加入用户场景。
- 只有真实执行完成且没有捕获到脚本运行错误才返回 completed。编译失败和确定运行失败返回可修复诊断,不以 nil 返回值伪装成功;空值返回本身仍是合法结果。执行日志不暴露令牌或宿主凭据。
- 使用同一总期限覆盖发现、准备、连接、发送和读取;并发写执行立即拒绝,不积压截止后可能被派发的代码。原生服务不自动重发 execute。
- 发送前的参数/路径/权限/平台/未连接错误为 `failed, dispatched:false` ;发送后的超时、断线、损坏或身份不符回执为 `needs-reconciliation, dispatched:true, retryAllowed:false` 。
- 主线程无限循环不能承诺硬中止。async 未返回或运行状态不明时必须保留不确定阻断,不能因重连、插件启停、切换工程或 Runner 自动重启消除。
- await 全程保持单执行占用;未完成或结果不确定期间,shutdown/禁用/切换只能禁止新增执行,不能卸载正在使用的桥或清除 fence。待状态已知且无在途执行后再完成资源清理;不确定时返回明确待核对状态。
- 复用现有执行回执确认机制:Runner 派发前持久化请求身份;调用者确认完整匹配的终态回执后才能清理 pending。丢失最后一跳回执保持阻断,只有核对后退出全部 AGC/Runner 并重新打开才能恢复。
- fence 的持久范围是同一完整宿主会话:自动重启 Runner、新窗口、JS 插件重载均不能清除。只有用户已核对编辑器状态、全部旧 AGC/Runner 退出,且新 GUI 同时独占现有 GUI 参与锁与 Runner 实例锁时,才能沿用现有恢复入口开始新的宿主会话;该规则与 Unity 一致,不建立另一套自动 reconcile。
## 契约与兼容
不修改服务端 API、DTO 结构、SpacetimeDB schema 或游戏持久业务数据。已有项目命令目录增加 `godot.editor.execute` ,默认权限为 confirm, Rust 与 TypeScript 镜像保持一致;实际执行继续服从当前运行档及项目权限策略。通用 EditorAdapter 文档允许编辑器适配器按自身已授权合同维护受管引导文件;Cocos/Unity 的不写工程连接行为保持原约定。内置开关和审计继续使用已有存储。现有项目没有描述文件时按首次连接创建,不引入历史兼容路径。
## 验收
| 条款 | 必须取得的证据 |
| --- | --- |
| 包与来源 | 原生 DLL 构建、官方 ABI/许可、源码内无机器路径;staging 校验 DLL 及元数据只进入 Windows x64 资源 |
| 受管文件 | 首次生成、幂等、安装路径更新、非受管文件冲突、路径穿越/链接拒绝、正常卸载及异常保留 |
| 目标身份 | 根/一层 Godot 项目,PID/启动身份/工程/代次/构建身份校验;错误目标拒绝 |
| 宿主接入 | manifest、启停、开关、权限、Runner RPC、DirectProject/Runtime 工具和项目切换定向测试 |
| 执行 | 42、场景读取与独立场景修改/撤销、nil、编译错、运行错、async、有界日志和拒绝并发 |
| 不确定结果 | 发送前后失败分类、超时/断线/损坏回执、ACK 归属、插件及 Runner 重启不解除阻断 |
| 实机 | 已打开的独立 Godot 4.7.2 工程中无需脚本 UI 操作的首次加载、执行、卸载、重新连接;DLL 保持安装资源布局且原有工程文件哈希不变 |
| 仓库门禁 | 相关 Rust/JS/前端定向测试、类型检查、文档索引、编码和 git diff --check;真实编辑器、打包资源与安装包 UI 的结果分别说明 |
## 已确定的产品选择
用户明确选择 DLL 原件留在 AGC 安装目录,并确认每个编辑器的临时加载副本放 AGC 缓存,工程内不复制 DLL。正式实现已按下列证据重新验收,前置 PoC 不作为交付依据。
## 本地验收结果(2026-09-20)
Windows x64、Godot 4.7.2 标准编辑器的本地实现验收通过。证据保存在 gitignored 的 `.app/diagnostics/` ,不随源码提交;复验入口保留在插件源码和现有测试中。下列测试集合存在交叉,不相加为总用例数。
| 验收面 | 已取得的证据 |
| --- | --- |
| 原生服务与文件边界 | `godot-cache-tests-final.log` :30 项通过;覆盖受管文件及 UID、目标身份、缓存来源/哈希、安装更新、实例隔离、链接拒绝、异常保留和不确定状态 |
| 真实引擎原生执行 | `godot-native-final-tests.log` :12 项通过、0 跳过;真实 headless Godot 验证同步/async、编译/运行错、超时占用、有界输出、代次拒绝及卸载重连 |
| 插件与宿主 | `godot-plugin-js-final.log` : Cocos/Unity/Godot JS 共 31 项通过;`godot-shell-final-tests.log` : Godot 过滤 29 项通过;`godot-host-final-tests.log` : PluginHost 16 项通过,含切项目撤销旧上下文、派发前取消及派发后回执丢失 |
| 共享权限与前端 | `godot-web-final-tests.log` :28 项通过;Rust 命令权限契约通过,AGC typecheck 通过;Godot 命令继续使用默认 confirm |
| 现有宿主回归 | EditorAdapter、Unity、Runner 重启 fence 和缺失 endpoint 清理的定向测试通过;构建脚本、workspace、CI 路由和 rustfmt hook 定向测试通过 |
| 原生 GUI | `godot-plugin-verified-20260920/evidence/native-gui-smoke.log` :同一编辑器内返回 42/null、读取场景、增加节点后撤销、await、编译/运行错误、卸载、重连及再次卸载成功 |
| 多实例与只读安装 | 同目录 `parallel-live-modules.json` 、`parallel-verified-summary.json` :两个编辑器同时使用不同缓存路径的官方 `~DLL` ,均返回 42、确认卸载,安装原件只读且未变 |
| 安装位置更新 | 同目录 `install-location-smoke.log` :旧连接卸载、新安装来源生成新代次和引用、返回 42、清理两代缓存;两个安装来源及原有工程文件均未变 |
| 真实 Runner | 同目录 `runner-smoke-psapi.log` : standalone Runner 经正式执行/ACK 路径取得真实 Godot 回执,拒绝错误 ACK,接受正确 ACK,完成场景修改/撤销及断开;`runner-restart-before.log` 、`runner-restart-after.log` :新 Runner 无需重新连接即可恢复原归属并清理旧桥 |
| 资源与收尾 | 完整 Windows feature debug 构建通过;当前 `src-tauri/resources/plugins/agc-godot-editor` 仅含 manifest、入口、DLL、元数据、许可和来源六个文件,其 DLL 与实机测试一致;同目录 `final-cleanup.json` 确认工程及安装来源哈希未变、自建 GUI 正常退出 |
正式 Runner smoke 使用安装资源布局下的 debug 可执行文件,不等同于 NSIS 安装包 UI 验证。本次未运行安装包 UI smoke、真实 Provider 生成或远端 CI,未制作发布包或上传发布;其他 Godot 版本、.NET 编辑器与其他平台仍须各自验收。AGC 全量聚合门禁不在上述定向结果中。
### 复验入口
从仓库根运行,原生构建要求 Windows x64 C 编译器。先把 `AGC_GODOT_TEST_EXECUTABLE` 设置为待验证的标准 Godot 编辑器绝对路径;未设置时 headless 测试会跳过,不能视为实机通过。
``` powershell
powershell -NoProfile -File plugins / agc-godot -editor / native / gdextension / build . ps1
node - -test plugins / agc-godot -editor / native / gdextension / tests / native-smoke . test . mjs
cargo test - -locked - -manifest -path plugins / agc-godot -editor / native / godot-editor -bridge / Cargo . toml
npm run agc : plugins : test
cargo test - -locked - -manifest -path apps / ai-game -creator -shell / src-tauri / Cargo . toml - -features cocos-editor -execute , unity-editor -execute , godot-editor -execute godot
cargo test - -locked - -manifest -path apps / ai-game -creator -shell / src-tauri / Cargo . toml - -features cocos-editor -execute , unity-editor -execute , godot-editor -execute plugin_host
npx vitest run apps / ai-game -creator -shell / tests / pluginHost . test . ts packages / shared / src / contracts / gameCreationApp . test . ts - -threads = false
npm run typecheck - -workspace @genarrative / ai-game -creator -shell
npm run check : doc-index
npm run check : encoding
git diff - -check
```
真实 GUI 使用 `native/godot-editor-bridge/examples/live_smoke.rs` ;安装位置变更使用同目录的 `install_location_smoke.rs` 。二者要求显式传入自有可丢弃工程、已打开编辑器 PID、可信安装 DLL、工程外私有缓存及 `--allow-fixture-mutations` ,具体参数见源码用法。多实例验证分别传入两个工程和 PID,通过 `live_smoke` 的 `--hold-ms` 让加载时间重叠,同时核验原生模块路径。Runner 复验须经正式长度前缀 RPC、完整 ACK 和私有配置恢复路径,不能用原生示例替代 Runner 证据。