---
metadata:
  - name: generator
    content: Diplodoc Platform v5.52.0
alternate:
  - https://yandex.com/dev/games/doc/en/sdk/sdk-multiplayer-sessions.md
  - https://yandex.com/dev/games/doc/hi/sdk/sdk-multiplayer-sessions.md
  - https://yandex.com/dev/games/doc/ko/sdk/sdk-multiplayer-sessions.md
  - https://yandex.com/dev/games/doc/ru/sdk/sdk-multiplayer-sessions.md
  - https://yandex.com/dev/games/doc/tr/sdk/sdk-multiplayer-sessions.md
  - https://yandex.com/dev/games/doc/vi/sdk/sdk-multiplayer-sessions.md
  - https://yandex.com/dev/games/doc/zh/sdk/sdk-multiplayer-sessions.md
  - href: zh/sdk/sdk-multiplayer-sessions.md
    type: text/markdown
    title: Markdown version
  - href: ../llms.txt
    type: text/markdown
    title: llms.txt
---
> **Documentation Index:** Fetch the complete configuration index at https://yandex.com/dev/games/doc/zh/llms.txt

# 异步多人游戏

<!-- source: zh/_includes/script-common.md -->
<!-- source: zh/_includes/script/index-js.md -->

<!-- endsource: zh/_includes/script/index-js.md -->

<!-- source: zh/_includes/script/requirements-js.md -->

<!-- endsource: zh/_includes/script/requirements-js.md -->

<!-- source: zh/_includes/script/image-modal-js.md -->

<!-- endsource: zh/_includes/script/image-modal-js.md -->
<!-- endsource: zh/_includes/script-common.md -->

通过 `ysdk.multiplayer` 模块，您可以创建类似在线多人游戏的竞技模式。这样您无需：

- 编写和维护自己的服务器解决方案；
- 拥有快速寻找对手所需的临界玩家数量。

可以通过 SDK 实现核心机制的游戏类型示例：

- **益智游戏**： 异步会话可以在相邻的游戏板上播放，可以增加按时间或分数的竞争。适合的游戏示例：[空当接龙在线！](https://yandex.com/games/app/211144){.external} (来自 KosmosGames)，[Match Arena - 消消乐！](https://yandex.com/games/app/96787){.external} (来自 PecPoc Piggy)。
- **跑酷和赛车游戏**： 对手的会话可以显示为“阴影”，与玩家一起在关卡中移动 (鬼魂驾驶机制)。适合的游戏示例：[疯狂赛车 2](https://yandex.com/games/app/374739){.external} (来自 JL studio) 中的“极速竞赛”模式，[疯狂摩托](https://yandex.com/games/app/233231){.external} (来自 haoda games)。
- **策略和自动战斗游戏**： 可以实现与其他玩家战术对抗的战斗。适合的游戏示例：[Ludus](https://yandex.com/games/app/376147){.external} (来自 Positron Dynamics)，[Like a King](https://yandex.com/games/app/140656){.external} (来自 Vladimir Saponenko)，[TOYS: Crash Arena](https://yandex.com/games/app/291515){.external} (来自 Mad Pixel)。



## 概念 {#concept}

1. 用户 A 游戏时，SDK 记录关键事件（按键、状态变化）及其时间。
2. 这些事件作为时间线（事务列表）保存在服务器上。
3. 用户 B 启动游戏时，已保存的时间线中的对手将加载到他的会话中。
4. SDK 实时重现对手的动作，营造同时游戏的感觉。



## 初始化和加载会话 {#init}

要开始使用异步多人游戏，请调用方法 `ysdk.multiplayer.sessions.init()`。该方法进行初始初始化和加载对手的游戏会话。

该方法返回[已加载会话的数组](#response-format)。

接受以下参数：

#|
|| **参数** | **类型** | **描述** ||
|| `count` | `number` | 确定要加载的会话数量。响应中的最大会话数为 10 个。 ||
|| `isEventBased` | `boolean` | 通过事件[初始化工作](#use)的标志。 ||
|| `maxOpponentTurnTime` | `number` | 限制对手回合时间，并以毫秒为单位设置 `multiplayer-sessions-transaction` 事件发送之间的最大间隔。如果指定了该参数，则每个持续时间超过 `maxOpponentTurnTime` 的对手回合将被缩短到指定值。默认情况下，对手的回合时间是无限制的。

{% cut "`maxOpponentTurnTime` 工作示例" %}

在已记录的会话中，对手以 2−10 秒的间隔进行操作，`maxOpponentTurnTime` 参数值为 5000 毫秒。在这种情况下，无论该对手的回合原始持续时间如何，所有操作都将在不晚于 5 秒内发生。

{% endcut %} ||
|| `meta` | `object` | 用于筛选的自定义参数 `meta1`、`meta2`、`meta3`。它们的形式为对象 `{ min: %number%, max: %number% }`，并在[保存会话](#push)时设置。例如，如果 `meta1` 中存储了游戏分数，`meta2` 中存储了玩家等级，您可以加载这些参数接近于当前用户的已保存会话。 ||
|#

{% note warning %}

加载会话时，必须至少设置三个 `meta` 参数中的一个，并且 `count` 参数的值要大于零。否则，多人游戏将仅初始化为记录。

{% endnote %}

{% list tabs %}

- 请求示例

  ```javascript showLineNumbers
  ysdk.multiplayer.sessions.init({
    count: 2, // 要加载的对手会话数量（最多为10）。
    isEventBased: true, // 通过事件初始化工作的标志。
    maxOpponentTurnTime: 200, // 限制对手的回合时间（毫秒）。
    [meta](*meta): {
      meta1: {
        min: 0,
        max: 6000,
      },
      meta2: {
        min: 2,
        max: 10,
      },
    },
  }).then(opponents => console.log(opponents));
  ```

- ES2017 异步/等待的请求示例

  ```javascript showLineNumbers
  const work = async () => {
    const opponents = await ysdk.multiplayer.sessions.init({
      count: 2, // 要加载的对手会话数量（最多为10）。
      isEventBased: true, // 通过事件初始化工作的标志。
      maxOpponentTurnTime: 200, // 限制对手的回合时间（毫秒）。
      [meta](*meta): {
        meta1: {
          min: 0,
          max: 6000,
        },
        meta2: {
          min: 2,
          max: 10,
        },
      },
    });

    console.log(opponents);
  }

  work();
  ```

{% endlist %}

#### 回答格式 {#response-format}

```javascript showLineNumbers
[
  {
    [id](*id): string;
    [meta](*meta): {
      meta1: number;
      meta2: number;
      meta3: number;
    };
    [player](*player): {
      avatar: string;
      name: string;
    };
    [timeline](*timeline): [
      {
        [id](*timeline_id): string;
        [payload](*payload): object | string | undefined;
        [time](*time): number;
      },
      ...
    ];
  },
  ...
]
```

#|
|| **参数** | **类型** | **描述** ||
|| `id` | string | 会话标识符。 ||
|| `meta` | object | 自定义参数 `meta1`、`meta2`、`meta3`。例如，游戏中的分数或玩家等级。 ||
|| `player` | object | 对手玩家信息:
- `avatar: string` —— 用户头像的URL；
- `name: string` —— 玩家的姓名。
||
|| `timeline` | array | 描述游戏会话的事件时间线数组：
- `id: string` —— 事件的唯一标识；
- `payload` —— 事件数据：反映游戏世界中变化原因的信息（例如，角色的新坐标或鼠标点击）；
- `time: number` —— 游戏开始后的时间，考虑暂停（毫秒）。
||
|#



## 游戏会话记录 {#record}

{% note alert %}

最大的单个会话记录大小为200 KB。

{% endnote %}

使用 SDK 记录用户的游戏会话（带有时间戳的事件序列）。事件可以是拼图游戏中棋子在棋盘上的移动，也可以是跑酷游戏中键盘或鼠标按键的动作。

在用户输入连续的游戏中，例如跑酷游戏，可以在特定时间间隔内人工生成事件，记录角色状态的指标——坐标、能量水平等。这也可以是游戏中的随机事件——奖励、地震。

游戏中发生的事件作为事务保存。每个事务都有：

#|
|| **参数** | **类型** | **描述** ||
|| `id` | `number` | 事件的唯一标识符。 ||
|| `payload` | `object` | 事件数据：反映游戏世界本质变化原因的信息（例如角色的新坐标或鼠标按键动作）。包含键值对的对象。 ||
|| `time` | `number` | 游戏开始的时间，包含暂停的修正。 ||
|#

在游戏过程中，通过方法 [commit()](#commit) 保存 `payload`。这样会形成一个事务列表——游戏会话的时间线 (`timeline`)。

在游戏结束时，通过方法 [push()](#push) 将会话保存到服务器。保存后，会话可以在下一个游戏中加载并回放。


### ysdk.multiplayer.sessions.commit() {#commit}

方法 `commit()` 固定当前游戏会话的事务。事件数据（[payload](*payload)）传递给它。

{% note warning %}

交易的其他参数——标识符 (`id`) 和游戏开始时间 (`time`)——是在 SDK 中计算的，因此及时发送 `payload` 非常重要。

{% endnote %}

#### 示例 {#commit-ex}

```javascript showLineNumbers
// 第一次交易。
ysdk.multiplayer.sessions.commit({ x: 1, y: 2, z: 3, health: 67 });

// .......

// 下一次交易。
ysdk.multiplayer.sessions.commit({ x: 4, y: -2, z: 19, health: 15 });

// .......
```


### ysdk.multiplayer.sessions.push() {#push}

方法 `push()` 用于将时间线保存到远程服务器。游戏结束时调用。

保存会话时设置 `meta1`、`meta2`、`meta3` 的值。至少必须定义一个 [meta 参数](*meta)。

#### 示例 {#push-ex}

```javascript
ysdk.multiplayer.sessions.push({ meta1: 12, meta2: -2 });
```



## 使用会话 {#use}

要选择如何处理加载的对手会话，请在方法 [init()](#init) 中传递所需的 `isEventBased` 标志值：

#|
|| **值** | **描述** ||
|| `isEventBased: true` | 通过事件处理。 ||
|| `isEventBased: false` | 自行处理。 ||
|#

{% list tabs %}

- 通过事件

  客户端通过 `ysdk.on()` 订阅事件 `multiplayer-sessions-transaction` 和 `multiplayer-sessions-finish`，而 SDK 自行加载共有会话并根据其中指定的时间戳发出事件：

  - 在游戏过程中，对手的交易会在从会话开始记录时间的时刻到达 `multiplayer-sessions-transaction` 处理器，但会考虑对手回合时间限制（`maxOpponentTurnTime`，方法 [init()](#init) 的参数）。

  - 当会话结束时，将调用 `multiplayer-sessions-finish` 处理器，并提供游戏结束的对手的标识符。

  ```javascript showLineNumbers
  ysdk.multiplayer.sessions.init({
    count: 2, // 要加载的对手会话数量（最多为10）。
    isEventBased: true, // 通过事件初始化工作的标志。
    maxOpponentTurnTime: 200, // 限制对手的回合时间（毫秒）。
    [meta](*meta): {
      meta1: {
        min: 0,
        max: 6000,
      },
      meta2: {
        min: 2,
        max: 10,
      },
    },
  });

  // 在此处我们接收当前需要执行的事务数组：
  // 当前事务以及由于游戏可能出现卡顿而延迟的事务。
  ysdk.on('multiplayer-sessions-transaction', ({ opponentId, transactions }) = > {
    console.log(opponentId, transactions);
    // 在游戏场景中应用 transaction.payload 的数据。
  });

  ysdk.on('multiplayer-sessions-finish', (opponentId) => console.log(opponentId));
  ```

  多人游戏的启动和暂停由[游戏玩法标记](sdk-game-events.md/#gameplay)方法控制：

  ```javascript showLineNumbers
  // 启动多人模式。
  ysdk.features.GameplayAPI.start();

  // 暂停多人模式。
  ysdk.features.GameplayAPI.stop();
  ```

- 自己的解决方案

  多人游戏初始化方法 [init()](#init) 返回加载的对手会话数组，包含带有事务的时间线。

  自行获取和处理数据：

  ```javascript showLineNumbers
  const start = (opponent) => {
    console.log('player', opponent.player);
    console.log('timeline', opponent.timeline);

    // 实现使用时间轴和播放器数据的机制。
  }

  const work = async () => {
    const opponents = await ysdk.multiplayer.sessions.init({
      count: 2, // 要加载的对手会话数量（最多为10）。
      isEventBased: false, // 通过事件初始化工作的标志。
      maxOpponentTurnTime: 200, // 限制对手的回合时间（毫秒）。
      [meta](*meta): {
        meta1: {
          min: 0,
          max: 6000,
        },
        meta2: {
          min: 2,
          max: 10,
        },
      },
    });

    console.log('opponents', opponents);

    for (let i = 0; i < opponents.length; i++) {
      start(opponents[i]);
    }
  }

  work();
  ```

{% endlist %}


---

<!-- source: zh/_includes/sdk-support.md -->
{% note info %}

技术支持团队将协助您将已完成的游戏发布到 Yandex 游戏平台。关于开发和测试方面的具体问题，其他开发人员将在[Discord 频道](https://discord.com/invite/wU4p3whr4T){.external}中进行回答。

{% endnote %}

如果您遇到 Yandex Games SDK 方面的问题或有其他问题想要咨询，请联系支持部门：

<!-- source: zh/_includes/button-chat.md -->
<a href="https://yandex.com/chat/#/user/a4fa5c06-75db-9b38-6eea-b1673785f7d5">
  <span class="button">写入聊天信息</span>
</a>
<!-- endsource: zh/_includes/button-chat.md -->
<!-- endsource: zh/_includes/sdk-support.md -->

[*id]: 会话标识符。

[*meta]: 自定义参数 `meta1`、`meta2`、`meta3`，它们在保存会话时设置。例如，游戏分数或玩家等级。

[*player]: 对手玩家的信息：
- `avatar: string` —— 用户头像的URL；
- `name: string` —— 玩家姓名。

[*timeline]: 描述游戏会话的事件时间轴数组：
- `id: string` —— 事件的唯一标识符；
- `payload` —— 事件数据：反映游戏世界变化本质、原因的信息（例如，角色的新坐标或鼠标点击）；
- `time: number` —— 从游戏开始到事件发生的时间（毫秒），考虑了暂停的影响。

[*timeline_id]: 事件的唯一标识符。

[*payload]: 事件数据：反映游戏世界变化本质、原因的信息（例如，角色的新坐标或鼠标点击）。

[*time]: 从游戏开始到事件发生的时间，考虑了暂停的影响。