# 蓝图节点开发接口文档

> 本文档聚焦 **蓝图“逻辑与图表（EventGraph）”** 开发能力：变量、节点、连线，以及用于闭环自修复的编译诊断。
>
> **核心原则（强制）**：
> 1. **不要猜引脚名**：连线必须使用 `blueprint.add_node` 或 `blueprint.get_graph` 返回的 `pins[].name`（真实引脚名），禁止使用 UI 显示名/label。
> 2. **node_id 是唯一引用**：后续连线/修复必须基于 `node_id`（GUID），不要依赖“节点标题文本”。

---

## 接口总览（节点/图表）

| Method | 名称 | 说明 |
| --- | --- | --- |
| **blueprint.create_function** | 创建函数 | 创建自定义函数图表（Functions），可定义输入/输出参数 |
| **blueprint.add_variable** | 定义变量 | 添加蓝图成员变量（可选数组/对象引用/软引用） |
| **blueprint.get_graph** | 上帝视角 | 获取图表全貌（节点 GUID + 真实引脚名） |
| **blueprint.add_node** | 添加节点 | 添加 Event/Function/VariableGet/VariableSet，并返回 pins 说明书 |
| **blueprint.add_timeline** | 添加 Timeline | 添加 Timeline（创建 TimelineTemplate + Timeline 节点），并返回 pins 说明书 |
| **blueprint.connect_pins** | 逻辑连线 | 基于 `node_id + pin.name` 连接执行流/数据流 |
| **blueprint.compile** | 编译与诊断 | 编译并返回 `diagnostics`（用于自修复闭环） |

---

## 0. 创建函数图表 `blueprint.create_function`

用于创建一个新的函数图表（Functions），并可选定义输入/输出参数。创建成功后，返回该函数图表名与入口/返回节点 GUID，供后续在该函数图表中编写逻辑（`graph_name`）。

### 请求（JSON-RPC）

```json
{"ver":"1.0","type":"req","id":"bp_create_fn","method":"blueprint.create_function","params":{
  "blueprint_path":"/Game/BP_MyHero",
  "function_name":"CalculateHealth",
  "inputs":[
    {"name":"BaseValue","type":"Float"},
    {"name":"Multiplier","type":"Float"}
  ],
  "outputs":[
    {"name":"FinalResult","type":"Float"}
  ],
  "pure":false
}}
```

### 响应

```json
{"ver":"1.0","type":"res","id":"bp_create_fn","code":200,"result":{
  "ok":true,
  "graph_name":"CalculateHealth",
  "entry_node_id":"GUID-Entry...",
  "result_node_id":"GUID-Result..."
}}
```

### 说明
- `graph_name` 就是函数图表名；后续 `blueprint.get_graph / add_node / connect_pins` 的 `graph_name` 传这个值即可在函数内部编写逻辑。
- `inputs/outputs` 的 `type` 支持与 `blueprint.add_variable` 相同；若传入不支持类型会被忽略（并在日志中提示）。
- `pure:true` 为 best-effort（不同 UE 版本内部字段不同）；若发现仍有 Exec 引脚，可用 `compile.diagnostics` 与图表检查确认。

---

## 1. 添加蓝图变量 `blueprint.add_variable`

添加蓝图成员变量（Blueprint Member Variable）。

### 请求（JSON-RPC）

```json
{"ver":"1.0","type":"req","id":"bp_add_var","method":"blueprint.add_variable","params":{
  "blueprint_path":"/Game/Blueprints/BP_Hero",
  "name":"Health",
  "type":"float",
  "is_array":false,
  "default_value":"100.0"
}}
```

#### 对象/类引用示例

```json
{"ver":"1.0","type":"req","id":"bp_add_var2","method":"blueprint.add_variable","params":{
  "blueprint_path":"/Game/Blueprints/BP_Hero",
  "name":"TargetActor",
  "type":"object",
  "object_class":"/Script/Engine.Actor"
}}
```

### 响应

```json
{"ver":"1.0","type":"res","id":"bp_add_var","code":200,"result":{
  "ok":true,
  "blueprint_path":"/Game/Blueprints/BP_Hero.BP_Hero",
  "variable":{
    "name":"Health",
    "type":"float",
    "is_array":false,
    "default_value":"100.0"
  }
}}
```

### 参数说明
- `blueprint_path`：蓝图路径或短名（会自动在 AssetRegistry 中查找）。
- `name`：变量名。
- `type`：支持：
  - 基础：`bool/int/int64/float/double/string/name/text`
  - 结构体：`vector/rotator/color/linearcolor`
  - 引用：`object/class/soft_object/soft_class`
- `object_class`：当 `type` 为 `object/class/soft_object/soft_class` 时可指定类（如 `/Script/Engine.Actor`），缺省为 `/Script/Engine.Object`。
- `is_array`：是否数组（默认 `false`）。
- `default_value`：默认值（字符串形式；UE 会按蓝图变量默认值规则解析）。

### 错误码
- `400`：参数缺失/类型不支持
- `404`：蓝图或类找不到
- `409`：变量已存在

---

## 2. 获取图表全貌 `blueprint.get_graph`

获取指定图表的节点列表与引脚元数据（真实引脚名）。

### 请求（JSON-RPC）

```json
{"ver":"1.0","type":"req","id":"bp_get_graph","method":"blueprint.get_graph","params":{
  "blueprint_path":"/Game/Blueprints/BP_Greeter",
  "graph_name":"EventGraph"
}}
```

> `graph_name` 可省略，默认 `EventGraph`。若要读取函数图表（例如 `CalculateHealth`），请将 `graph_name` 设置为函数名。

### 响应（节选）

```json
{"ver":"1.0","type":"res","id":"bp_get_graph","code":200,"result":{
  "ok":true,
  "blueprint_path":"/Game/Blueprints/BP_Greeter.BP_Greeter",
  "graph_name":"EventGraph",
  "nodes":[
    {
      "node_id":"GUID-...",
      "class":"K2Node_Event",
      "title":"Event BeginPlay",
      "pos_x":0,
      "pos_y":0,
      "pins":[
        {"name":"Then","dir":"Output","category":"exec","sub_category":"","is_array":false,"is_reference":false,"is_const":false}
      ]
    }
  ]
}}
```

### 字段说明
- `nodes[].node_id`：节点 GUID（后续连线/定位必须用它）。
- `nodes[].pins[]`：
  - `name`：**真实 pin 名**（连线必须用它）
  - `dir`：`Input`/`Output`
  - `category/sub_category/sub_category_object`：Pin 类型元数据
  - `friendly_name`：UI 友好名（**仅供展示，不要用来连线**）

---

## 3. 添加节点 `blueprint.add_node`

在图表中添加节点，并返回该节点的 pins 说明书（真实引脚名）。

### 请求（JSON-RPC）

```json
{"ver":"1.0","type":"req","id":"bp_add_node","method":"blueprint.add_node","params":{
  "blueprint_path":"/Game/Blueprints/BP_Greeter",
  "graph_name":"EventGraph",
  "node_type":"Event",
  "node_name":"BeginPlay",
  "node_position":{"x":0,"y":0}
}}
```

#### 添加函数节点示例（PrintString）

```json
{"ver":"1.0","type":"req","id":"bp_add_node2","method":"blueprint.add_node","params":{
  "blueprint_path":"/Game/Blueprints/BP_Greeter",
  "graph_name":"EventGraph",
  "node_type":"Function",
  "node_name":"KismetSystemLibrary.PrintString",
  "node_position":{"x":300,"y":0}
}}
```

### 响应（节选）

```json
{"ver":"1.0","type":"res","id":"bp_add_node2","code":200,"result":{
  "ok":true,
  "blueprint_path":"/Game/Blueprints/BP_Greeter.BP_Greeter",
  "graph_name":"EventGraph",
  "node_id":"GUID-...",
  "node_class":"K2Node_CallFunction",
  "pins":[
    {"name":"Exec","dir":"Input","category":"exec","sub_category":"","is_array":false,"is_reference":false,"is_const":false},
    {"name":"Then","dir":"Output","category":"exec","sub_category":"","is_array":false,"is_reference":false,"is_const":false},
    {"name":"InString","dir":"Input","category":"string","sub_category":"","is_array":false,"is_reference":false,"is_const":false}
  ]
}}
```

### 参数说明
- `node_type` 当前支持：
  - `Event`：事件（常用 `BeginPlay` 会自动映射为 `ReceiveBeginPlay`）
  - `Function`：函数调用（建议使用 `ClassName.FunctionName` 形式，如 `KismetSystemLibrary.PrintString`）
  - `VariableGet` / `VariableSet`：变量 Get/Set（`node_name` 填变量名）
- `node_position`：可选 `{x,y}`。

### 错误码
- `400`：参数缺失/不支持的 node_type
- `404`：蓝图/图表/函数/变量找不到

---

## 3.1 添加 Timeline `blueprint.add_timeline`

Timeline（时间轴）是蓝图里的**特殊资源**：
- 不能用 `blueprint.add_variable` 直接创建
- 也不适合走 `blueprint.add_node` 的普通节点创建流程

本接口会：
1. 在 Blueprint 上创建（或复用）同名 `TimelineTemplate`
2. 在指定图表中放置 `Timeline` 节点并绑定该模板
3. 返回该节点的 pins（真实 pin 名）用于后续连线

### 请求（JSON-RPC）

```json
{"ver":"1.0","type":"req","id":"bp_add_timeline","method":"blueprint.add_timeline","params":{
  "blueprint_path":"/Game/Blueprints/BP_Greeter",
  "graph_name":"EventGraph",
  "timeline_name":"TL_Move",
  "node_position":{"x":600,"y":0},
  "reuse_existing":true
}}
```

### 响应（节选）

```json
{"ver":"1.0","type":"res","id":"bp_add_timeline","code":200,"result":{
  "ok":true,
  "blueprint_path":"/Game/Blueprints/BP_Greeter.BP_Greeter",
  "graph_name":"EventGraph",
  "timeline_name":"TL_Move",
  "node_id":"GUID-...",
  "node_class":"K2Node_Timeline",
  "template_created":true,
  "pins":[
    {"name":"Play","dir":"Input","category":"exec","sub_category":"","is_array":false,"is_reference":false,"is_const":false}
  ]
}}
```

### 参数说明
- `timeline_name`：Timeline 名称（建议 `TL_` 前缀，避免与变量/函数重名）。
- `reuse_existing`：默认 `true`，若图表中已存在同名 Timeline 节点则直接复用返回（幂等）。
- `node_position/force_position`：与 `blueprint.add_node` 同语义；若不传位置，会自动放置在图表右侧。

---

## 4. 连接引脚 `blueprint.connect_pins`

基于 `node_id + pin.name` 连接执行流/数据流。

### 请求（JSON-RPC）

```json
{"ver":"1.0","type":"req","id":"bp_connect","method":"blueprint.connect_pins","params":{
  "blueprint_path":"/Game/Blueprints/BP_Greeter",
  "graph_name":"EventGraph",
  "source_node_id":"GUID-A",
  "source_pin":"Then",
  "target_node_id":"GUID-B",
  "target_pin":"Exec"
}}
```

### 响应

```json
{"ver":"1.0","type":"res","id":"bp_connect","code":200,"result":{
  "ok":true,
  "message":"Connection created"
}}
```

### 说明
- 连接前请确保 pin 名来自 `get_graph/add_node` 的 `pins[].name`。
- 当连接被 schema 判定为不允许时，会返回 `code:400`，并在错误 message 中包含原因。

---

## 5. 编译与诊断 `blueprint.compile`

编译蓝图，并返回 `diagnostics` 用于自修复闭环。

### 请求（JSON-RPC）

```json
{"ver":"1.0","type":"req","id":"bp_compile","method":"blueprint.compile","params":{
  "blueprint_path":"/Game/Blueprints/BP_Greeter",
  "save":true
}}
```

### 响应（含 diagnostics）

```json
{"ver":"1.0","type":"res","id":"bp_compile","code":200,"result":{
  "ok":false,
  "status":"Error",
  "saved":false,
  "path":"/Game/Blueprints/BP_Greeter.BP_Greeter",
  "diagnostics":[
    {"type":"Error","message":"Pin 'InString' is missing a connection ...","node_id":"GUID-...","pin":"InString"}
  ]
}}
```

### 说明
- `ok` 仅表示编译结果是否为 `UpToDate`；即使 `ok:false` 也会返回 `code:200`，请以 `status` + `diagnostics` 做闭环修复。
- `diagnostics[].node_id/pin` 为 best-effort（在 UE5.0 可能无法总是绑定到对象 token）。

---

## SOP 示例：BeginPlay 打印 Hello（闭环）

1. `blueprint.add_node(Event, BeginPlay)` → 得到 `NODE_A` 与 pins（拿到真实 `Then`）
2. `blueprint.add_node(Function, KismetSystemLibrary.PrintString)` → 得到 `NODE_B` 与 pins（拿到真实 `Exec/InString`）
3. `blueprint.connect_pins(NODE_A.Then -> NODE_B.Exec)`
4. （可选）补一个字符串常量节点并把输出连到 `NODE_B.InString`，或用默认值
5. `blueprint.compile()`
   - 若 `ok:true`：完成
   - 若 `ok:false`：读取 `diagnostics`，修正节点/引脚/连线后再次 compile
