> 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` 始终持有其中一个。

| 值     | 含义                                                   |
| ----- | ---------------------------------------------------- |
| `启动中` | 该成员资格存在于名册中，Convai 尚未以任何方式报告它。这是每个新成员资格开始时的状态。       |
| `就绪`  | 角色已发出信号，表示它可以接收输入。从现在起，把玩家输入路由给它是安全的。                |
| `失败`  | 该角色未启动。 `FailureCode` 携带 Convai 报告的原因，而该成员资格仍保留在名册中。 |

状态背后有两个字段。 `ProvisioningStatus` 是 Convai 为该条目返回的原始 provisioning 字符串，而 `FailureCode` 是附加在失败上的原因字符串。某个成员资格如果其 `ProvisioningStatus` 为 `dispatch_failed` 会被标记为 `失败` 在它创建的瞬间就会如此，无需等待单独的生命周期消息。

SDK 会触发 `CharacterStatusChanged` 在每次切换到 `就绪` 或 `失败`时，以及在新增成员资格时。显示按角色可用性的场景应从该事件驱动其 UI，而不是轮询 `状态`.

***

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

`MultiCharacterRoomSession.IsReady` 在初始角色的状态为 `就绪`时，并且不受任何其他成员资格影响。已就绪的次要角色不会让会话就绪，而失败的次要角色也不会让它变回未就绪。

之所以这样定义，是因为房间的路由模型。初始角色是房间打开时玩家所面对的成员资格，因此它是房间在对话开始前唯一需要的角色。次要角色稍后可以按需寻址，而会话无法知道给定场景实际上需要的是哪一个。把任何一个次要角色都视为阻塞项，都会延迟一个其实已经可用的房间。

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

***

### 等待房间变为可用

`WaitUntilReadyAsync(CancellationToken)` 一旦初始角色到达 `就绪`，并在会话已经就绪时立即返回。它会以一个 `InvalidOperationException` 当初始角色到达 `失败` 时则抛出，并携带消息 `Initial character failed to start (<code>).` 其中 `<code>` 是报告的失败代码，或者 `未知` 当 Convai 未提供任何代码时。

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

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

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

***

### 部分派发

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

部分派发的房间仍然是一个可工作的房间。已启动的成员资格会正常就绪或正常启动，而未启动的则会出现在 `角色` 与 `状态` 设置为 `失败`。请检查名册，而不要把该标志当作连接失败：

```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" %}
不要将玩家输入路由到一个并非 `就绪`。处于 `启动中` 后面还没有实时对话，而处于 `失败` 则永远不会。
{% endhint %}

***

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

直接路径是 `character-status` 生命周期消息。SDK 会将该消息解析到某个成员资格，合并其携带的名册 epoch，并将该成员资格标记为 `就绪` 或 `失败` ，其状态来自报告的状态。尚未被客户端见过的成员资格状态消息会先把该成员资格插入名册，因此房间可以在同一条消息里同时了解某个角色及其就绪状态。

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

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

***

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

把名册看作一组彼此独立可用的角色，而不是一个非上即下的整体。从这一点会引出三个习惯。

按成员资格而不是按房间来门控交互。检查 `状态` 你即将对其发出地址的那个成员资格，并让场景其余部分在它仍处于启动中时继续进行。

订阅 `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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
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=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

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.
