# Get Nodes（查询 API）

对应右键 Select 菜单的常规 7 项（不含 `[Debug]`）的**查询专用 API**。动词为 `get`，主要目的是查询句柄/名称。仅在传入 `select=1` 时才会附带执行场景选择（默认 `select=0` = 仅查询，不改变场景选择）。

---

## 方法列表

| 方法 | 右键菜单 | 数据来源 |
| --- | --- | --- |
| `get_all_objects(slot, select=0)` | All Objects | 引用全部节点句柄 |
| `get_skin_meshes(slot, select=0)` | Skin Meshes | 蒙皮网格句柄 |
| `get_skin_bones(slot, select=0)` | Skin Bones | 蒙皮骨骼句柄 |
| `get_all_skin_objects(slot, select=0)` | All Skin Objects | 网格 + 骨骼（去重，网格优先） |
| `get_duplicate_nodes(slot, select=0)` | Duplicate Nodes | 仅名称重复的节点 |
| `get_biped_com(slot, select=0)` | Biped COM | Biped COM 节点句柄 |
| `get_biped_nodes(slot, select=0)` | Biped Nodes | Biped 全部节点句柄 |

---

## 公共参数

| 参数 | 类型 | 必填 | 说明 |
| --- | --- | --- | --- |
| `slot` | `int` | 是 | 槽位号（0–9）。**bool/str/float 立即抛出 `TypeError`** |
| `select` | `int` (0/1) 或 `bool` | 否（默认 `0`） | `0`=仅查询，`1`=查询后在场景中选择。`False`→`0`，`True`→`1`。其他值立即抛出 `ValueError` |

```text
select=0（默认） → 仅查询。不改变场景选择。
select=1         → 查询后用返回的 handles 在 Max 场景中选择。
```

---

## 公共返回约定

```python
{
    "success": True,
    "slot": 0,
    "category": "skin_meshes",
    "count": 12,
    "handles": [101, 102, ...],
    "names": ["Mesh_A", ...],
    "selected": 0,
    "selected_count": 0,
    "message": "skin_meshes: 12개",
}
```

| 方法 | `category` | 额外字段 |
| --- | --- | --- |
| `get_all_objects` | `"all_objects"` | `reference_name` |
| `get_skin_meshes` | `"skin_meshes"` | — |
| `get_skin_bones` | `"skin_bones"` | — |
| `get_all_skin_objects` | `"all_skin_objects"` | — |
| `get_duplicate_nodes` | `"duplicate_nodes"` | `duplicate_names` (`list[str]`) |
| `get_biped_com` | `"biped_com"` | — |
| `get_biped_nodes` | `"biped_nodes"` | — |

---

## 空结果与有效句柄重收集

- 无已存句柄，或全部无效（已删除）→ `success=True`、`count=0`、`handles=[]`、`names=[]`（**不是错误**）。
- 无重复时，`get_duplicate_nodes` 返回 `duplicate_names=[]`、`count=0`。
- 即使 `select=1`，无可选对象时 → `selected_count=0`（不是错误）。
- 返回的 `handles`/`names`/`count` 仅基于**当前场景中存在的有效节点**重新收集。

---

## 异常处理（FAIL FAST）

| 情况 | 异常 |
| --- | --- |
| `slot` 不是 `int`（含 bool） | `TypeError` |
| `slot` 越界 / 空槽位（未加载引用） | `ValueError` |
| `select` 不是 0/1/bool | `ValueError` |

非法的 `slot`/`select` 会立即以**异常**暴露，而不是返回 `{"success": False}`。

---

## 已删除的 select_* → get_* 替换

旧 `select_*` API 已**完全删除**（无 shim）。若出现 `AttributeError`，请按下表替换。

| 已删除 | 替换为 |
| --- | --- |
| `select_reference_nodes` | `get_all_objects(select=1)` |
| `select_duplicate_nodes` | `get_duplicate_nodes(select=1)` |
| `select_skin_mesh` | `get_skin_meshes(select=1)` |
| `select_skin_bones` | `get_skin_bones(select=1)` |

---

## 示例

```python
from os_fast_ref.api import FastRefAPI

api = FastRefAPI()

# 仅查询（不选择场景）
r = api.get_all_objects(slot=0)
print(r["reference_name"], r["names"])

# 查询 + 场景选择（与右键菜单相同）
api.get_skin_meshes(slot=0, select=1)

# 重复名校验（管线用 — 建议 select=0）
dup = api.get_duplicate_nodes(slot=0)
if dup["duplicate_names"]:
    print("duplicates:", dup["duplicate_names"])
```

外部自动化请始终使用 `FastRefAPI`。semver 保证范围仅覆盖 `FastRefAPI`。