> For the complete documentation index, see [llms.txt](https://docs.convai.com/api-docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/multi-character-sessions/readiness-and-partial-dispatch.md).

# 房间就绪状态

了解共享 Unity 房间何时准备好接受玩家输入、为什么一个慢角色不会阻塞其他角色，以及如何报告启动失败。

多角色房间中的角色会独立启动，因此房间不会一次性变得可用。就绪状态按成员分别跟踪，而整个会话只报告一个成员的就绪状态。理解这种分离，就能解释为什么房间可以已经就绪，而一半角色仍在启动；也能解释为什么某个角色一直未就绪属于可恢复状态，而不是连接失败。

***

### 成员可以处于的三种状态

`CharacterRoomStatus` 恰好有三个值，且 `CharacterRoomMembership.Status` 始终持有其中之一。

| 值          | 含义                                                 |
| ---------- | -------------------------------------------------- |
| `Starting` | 该成员已存在于名册中，但 Convai 仍未对其作出任何报告。这是每个新成员开始时的状态。      |
| `Ready`    | 该角色已表明可以接收输入。从此刻起，将玩家输入路由给它是安全的。                   |
| `Failed`   | 该角色未能启动。 `FailureCode` 携带 Convai 报告的原因，而该成员仍留在名册中。 |

状态后面有两个字段。 `ProvisioningStatus` 是 Convai 为该条目返回的原始配置字符串，而 `FailureCode` 是附加在失败上的原因字符串。其 `ProvisioningStatus` 是 `dispatch_failed` 会被标记为 `Failed` 在创建的那一刻立即如此，无需等待单独的生命周期消息。

SDK 会触发 `CharacterStatusChanged` 在每次切换到 `Ready` 或 `Failed`，以及插入新成员时触发。需要显示逐角色可用性的场景，应当根据该事件驱动其界面，而不是轮询 `状态`.

***

### 为什么会话就绪状态只跟随初始角色

`MultiCharacterRoomSession.IsReady` 当初始角色的状态为 `Ready`，并且不受其他任何成员影响。次要角色已就绪不会使会话就绪，而次要角色失败也不会使其变为未就绪。

之所以这样定义，是因为房间的路由模型。房间打开时，初始角色就是玩家要面向的成员，因此在对话开始前，房间只需要它。次要角色可以在之后按需寻址，而会话无法知道某个具体场景实际需要其中哪一个。如果把它们中的任何一个都视为阻塞项，就会延迟一个本已可用的房间。

`InitialCharacter` 是 Convai 标记为初始的成员。当没有任何条目标有该标记时，SDK 会回退到名册中的第一个成员，因此该属性绝不会 `null` 对于任何至少有一个成员的房间来说，都是如此。

***

### 等待房间变得可用

`WaitUntilReadyAsync(CancellationToken)` 一旦初始角色达到 `Ready`就会完成，并且在会话已经就绪时会立即返回。它会以一个 `InvalidOperationException` 当初始角色达到 `Failed` 异常失败，并携带消息 `初始角色启动失败（<code>）。` 其中 `<code>` 是报告的失败代码，或 `unknown` 当 Convai 未提供任何失败代码时则为 null。

该失败可能在第一次 `await`出现。如果连接响应已将初始角色标记为失败，那么等待会在首次调用时失败，而不是一直挂起。请在每个调用点处理该异常：

```csharp
try
{
    await session.WaitUntilReadyAsync(cancellationToken);
    BeginConversation();
}
catch (InvalidOperationException error)
{
    // 初始角色启动失败；其余名册仍可能可用。
    ShowInitialCharacterUnavailable(error.Message);
}
```

等待某个特定的次要角色是另一项工作。没有按成员分别等待的方法，因此请订阅 `CharacterStatusChanged` 并检查 `状态` 你关心的那个成员上的相应事件。

***

### 部分派发

`PartialDispatch` 由连接响应设置，用于报告 Convai 是否完整接受了名册。当它为 `true`时，至少有一个请求的角色未被派发，受影响的成员会带有失败的配置状态和失败代码。该值在会话生命周期内是固定的——它描述的是连接时发生了什么，而不是房间当前的健康状况。

部分派发的房间仍然是可工作的房间。已启动的成员处于就绪或正常启动中，而 `角色` 使用 `状态` 设置为 `Failed`。应检查名册，而不要把该标志当作连接失败：

```csharp
if (session.PartialDispatch)
{
    foreach (CharacterRoomMembership membership in session.Characters)
    {
        if (membership.Status != CharacterRoomStatus.Failed) continue;
        Debug.LogWarning(
            $"[MultiCharacter] {membership.CharacterId} 未启动: " +
            $"{membership.FailureCode ?? \"未报告失败代码\"} ({membership.ProvisioningStatus}).");
    }
}
```

{% hint style="warning" %}
不要将玩家输入路由给一个尚未 `Ready`。处于 `Starting` 后面还没有实时对话，而处于 `Failed` 状态的成员将永远不会有。
{% endhint %}

***

### 就绪状态如何到达客户端

直接路径是 `character-status` 生命周期消息。SDK 将消息解析为某个成员，合并其携带的名册纪元，并将该成员标记为 `Ready` 或 `Failed` 为报告的状态。对于客户端尚未见过的成员，其状态消息会先将该成员插入名册，因此房间可以在同一条消息中获知某个角色及其就绪状态。

一个成员即使仍然 `Starting` 也绝不会被标记为 `Ready` 仅凭未经验证的证据而被标记为。可归因于某个成员的语音——文本、口型同步帧、语音开始——会立即使其恢复，因为未就绪的东西不会产生语音。较弱的证据，例如已订阅的音轨，只会启动一个短暂等待，等待真正的 `character-status` 消息，而不会当场完成就绪；如果那条消息始终未到，等待时间一到，成员就会恢复。这正是防止角色卡在 `Starting` 状态中，即使状态消息丢失，也不会在服务实际上尚未开始路由给它之前就宣布某个角色。

会话自身的连接状态跟随初始角色。房间会报告 `已连接` 当初始成员的就绪信号到达时，而其他成员的就绪信号不会使其变化。

***

### 不均衡就绪状态的设计影响

把名册视为一组可独立可用的角色，而不是一个要么整体可用要么整体不可用的单元。由此会带来三个习惯。

按成员而不是按房间来控制交互。检查 `状态` 你即将要面向的成员，并让场景其余部分在它仍处于启动中时继续推进。

订阅 `CharacterStatusChanged` 任何需要响应的内容——启用名牌、解锁对话提示，或记录一个不可用的角色。轮询会错过这些事件提供的顺序保证。

预先决定次要角色失败对你的场景意味着什么。带有必需评审员的训练模拟应将失败上报给主持人；带有可选旁观者的场景则应在没有它的情况下继续进行。无论哪种情况，SDK 都会报告失败并将该成员保留在名册中，因此这个决定留给应用程序。

***

### 下一步

{% content-ref url="/pages/1de47e20d64086de6ea2148c4871a665eec92182" %}
[多角色会话工作原理](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/multi-character-sessions/how-multi-character-sessions-work.md)
{% endcontent-ref %}

{% content-ref url="/pages/6481618ebe16f2ac14d3eecace53ed97c7f369d1" %}
[角色身份](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/multi-character-sessions/character-identity.md)
{% endcontent-ref %}

{% content-ref url="/pages/a6e9c177aa5c598a856889f7e4baf09e8b838a4d" %}
[构建你的第一个多角色会话](/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/multi-character-sessions/quick-start.md)
{% endcontent-ref %}


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.convai.com/api-docs/zh/cha-jian-yu-ji-cheng/convai-unity-sdk/features/multi-character-sessions/readiness-and-partial-dispatch.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
