# 发送推送消息

> Push messages to clients over a WebSocket connection from Cloud Code modules.

Cloud Code C# 模块允许您通过 WebSocket 将消息推送到客户端。本页将概述此功能，并介绍如何在 Cloud Code 模块和 Cloud Code SDK 中使用此功能。

> **Note:**
>
> **注意**：推送消息和[推送通知](/push-notifications.md)在游戏中有不同的用途。推送消息定向投放到游戏中参与度较高的活跃玩家，而推送通知是当游戏处于非活动状态或在后台运行时推送到设备。

要发送推送消息，您需要调用模块终端。您可以从 Unity 编辑器中订阅推送消息。

## 用例和优点##use-cases-and-benefits

推送消息可以有多种用途，例如：

| 用途                                                 | 示例                                                                 | 优点       |
| :------------------------------------------------- | :----------------------------------------------------------------- | :------- |
| 将消息从服务器实时推送到客户端，并增强游戏的互动性。                         | 您可以告知玩家有关游戏活动、更新或异步多人游戏（例如，竞技卡牌游戏）的信息。                             | 实时交互     |
| 通过直接向客户端推送更新和通知，让用户及时了解游戏中的最新活动、变更或行动。             | 您可以在有新数据可用时立即通知用户，这样您就无需手动刷新或检查更新。这种功能对于需要频繁更新的游戏（例如实时战略游戏）特别有用。   | 用户体验     |
| 使用 WebSocket 推送消息，比传统的轮询技术效率更高。                    | 您的客户端可以等待服务器在有新数据可用时发送这些数据，而不是不断要求服务器提供新数据。这样可以减少不必要的网络流量，并可以降低延迟。 | 提高网络使用效率 |
| 利用推送模型的可扩展性来支持拥有大量用户的游戏。                           | 您可以在 Cloud Code 中使用推送消息一次性向所有已连接的客户端发送更新，从而降低拥有大量玩家的多人游戏的运营成本和请求量。 | 可扩展性和成本  |
| 使用事件驱动模型来自定义游戏对不同类型的消息的响应。                         | 您可以根据收到的消息的 MessageType 属性触发不同的游戏操作。                               | 可自定义     |
| 作为 Unity 生态系统的一部分，Cloud Code 可以与其他 Unity 服务无缝结合使用。 | 您可以使用 Unity 的 Authentication 服务处理玩家身份验证，并在 Unity 游戏引擎中处理消息。        | 无缝集成     |

## 创建模块##create-a-module

以下代码示例演示了包含两个方法的模块的基本结构：

* `SendPlayerMessage`：向已连接的特定玩家发送一条消息。
* `SendProjectMessage`：向项目中所有已连接的玩家广播一条消息。

> **Warning:**
>
> **重要：**`SendProjectMessage` 方法向项目中所有已连接的玩家发送一条消息。这可能会造成滥用的机会，例如向玩家滥发消息。为了防止这种情况，您可以创建[访问控制](./access-control.md)策略来限制玩家对终端的访问。

> **Note:**
>
> **注意**：您可以使用[触发器](/triggers.md)向整个项目广播消息。请参阅[使用示例：祝玩家新年快乐](/triggers/tutorials/use-cases/happy-new-year.md)。

`Unity.Services.CloudCode.Apis` 包提供了 `PushClient` 实例作为用于发送推送消息的接口。

```csharp
using Microsoft.Extensions.DependencyInjection;
using Unity.Services.CloudCode.Core;
using Unity.Services.CloudCode.Apis;

namespace PushExample
{
    public class PushExample
    {
        [CloudCodeFunction("SendPlayerMessage")]
        public async Task<string> SendPlayerMessage(IExecutionContext context, PushClient pushClient, string message, string messageType, string playerId)
        {
            var response = await pushClient.SendPlayerMessageAsync(context, message, messageType, playerId);
            return "Player message sent";
        }

        // Recommended to use access control to limit access to this endpoint
        [CloudCodeFunction("SendProjectMessage")]
        public async Task<string> SendProjectMessage(IExecutionContext context, PushClient pushClient, string message, string messageType)
        {
            var response = await pushClient.SendProjectMessageAsync(context, message, messageType);
            return "Project message sent";
        }
    }

    public class ModuleConfig : ICloudCodeSetup
    {
        public void Setup(ICloudCodeConfig config)
        {
            config.Dependencies.AddSingleton(PushClient.Create());
        }
    }
}
```

要了解如何部署模块，请参阅[部署 Hello World](../getting-started) 页面。

## 设置 Unity 编辑器##set-up-the-unity-editor

Cloud Code SDK 提供了一个用于订阅消息的接口。您可以订阅特定玩家或整个项目的消息。

要在 Unity 编辑器中使用 Cloud Code，必须先安装 Cloud Code SDK，并将 [Unity Gaming Services（Unity 游戏服务）项目](/cloud/projects.md)关联到 Unity 编辑器。

### 关联项目##link-project

将 [Unity Gaming Services（Unity 游戏服务）项目](/cloud/projects.md)与 Unity 编辑器关联。您可以在 Unity Dashboard 中找到您的 UGS Project ID。

1. 在 Unity 编辑器中，选择 **Edit（编辑）**> **Project Settings（项目设置）**> **Services（服务）**。

2. 关联您的项目。
   如果项目没有 Unity Project ID：

   1. 选择 **Create a Unity Project ID（创建 Unity Project ID）**> **Organizations（组织）**，然后从下拉选单中选择一个组织。
   2. 选择 **Create project ID（创建 Project ID）**。

   如果已有 Unity Project ID：

   1. 选择 **Use an existing Unity project ID（使用现有 Unity Project ID）**。
   2. 从下拉选单中选择组织和项目。
   3. 选择 **Link project ID（关联 Project ID）**。

此时将显示您的 Unity Project ID，并将项目关联到 Unity 服务。此外，还可以使用 `UnityEditor.CloudProjectSettings.projectId` 参数在 Unity 编辑器脚本中访问您的 Project ID。

### SDK 安装##sdk-installation

安装适用于 Unity 编辑器的最新 Cloud Code 包：

1. 在 Unity 编辑器中，打开 **Window（窗口）**> **Package Manager（包管理器）**。
2. 在 Package Manager（包管理器）中，选择 **Unity Registry（Unity 注册表）** 列表视图。
3. 搜索 `com.unity.services.cloudcode` 或在列表中找到 Cloud Code 包。
4. 选择包，然后单击 **Install（安装）**。

> **Note:**
>
> **注意**：您可以使用 Cloud Code SDK 版本 2.4.0+ 订阅消息。

> **Note:**
>
> **注意**：要熟悉 Unity Package Manager（包管理器）界面，请参阅 [Package Manager（包管理器）窗口](https://docs.unity3d.com/Manual/upm-ui.html)文档。

### SDK 设置##sdk-setup

开始使用 Cloud Code SDK：

1. 确保通过 Cloud Code 服务后台页面启用该服务。
2. 确保 Cloud Code SDK 和 Authentication SDK 均已安装。
3. 在 Unity 编辑器中选择 **Edit（编辑）**> **Project Settings（项目设置）**> **Services（服务）** 登录到您的云项目。
4. 在 Unity 编辑器中创建新的 C# Monobehaviour 脚本。请参阅 Unity 手册中的[创建和使用脚本](https://docs.unity3d.com/Manual/CreatingAndUsingScripts.html)。
5. 在脚本中，使用 await `UnityServices.InitializeAsync()` 初始化 Core SDK。
6. 在脚本中，初始化 Authentication SDK。

> **Note:**
>
> **注意**：玩家必须拥有有效的玩家 ID 和访问令牌才能访问 Cloud Code 服务。在使用任何 Cloud Code API 之前，您必须使用 Authentication SDK 对玩家进行身份验证。为此，您可以使用以下代码片段进行匿名身份验证，或查看[身份验证](/authentication.md)文档，了解更多详细信息和其他登录方法。

```cs
await AuthenticationService.Instance.SignInAnonymouslyAsync();
```

## 订阅消息##subscribe-to-messages

您可以订阅特定玩家或整个项目的消息。您可以使用以下代码片段作为 Unity 项目的 `MonoBehaviour` 脚本参考指南。

该脚本以匿名玩家的身份进行身份验证，订阅特定于玩家和特定于项目的消息，并处理各种类型的事件：

```csharp
using System;
using System.Threading.Tasks;
using Newtonsoft.Json;
using Unity.Services.Authentication;
using Unity.Services.CloudCode;
using Unity.Services.CloudCode.Subscriptions;
using Unity.Services.Core;
using UnityEngine;
namespace CloudCode
{
	public class CloudCodePushExample : MonoBehaviour
	{
    	async void Start()
        {
            await UnityServices.InitializeAsync();
            await AuthenticationService.Instance.SignInAnonymouslyAsync();
            Debug.Log(AuthenticationService.Instance.PlayerId);
            await SubscribeToPlayerMessages();
            await SubscribeToProjectMessages();
        }
        // This method creates a subscription to player messages and logs out the messages received,
        // the state changes of the connection, when the player is kicked and when an error occurs.
        Task SubscribeToPlayerMessages()
        {
            // Register callbacks, which are triggered when a player message is received
            var callbacks = new SubscriptionEventCallbacks();
            callbacks.MessageReceived += @event =>
            {
                Debug.Log(DateTime.Now.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK"));
                Debug.Log($"Got player subscription Message: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            callbacks.ConnectionStateChanged += @event =>
            {
                Debug.Log($"Got player subscription ConnectionStateChanged: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            callbacks.Kicked += () =>
            {
                Debug.Log($"Got player subscription Kicked");
            };
            callbacks.Error += @event =>
            {
                Debug.Log($"Got player subscription Error: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            return CloudCodeService.Instance.SubscribeToPlayerMessagesAsync(callbacks);
        }
        // This method creates a subscription to project messages and logs out the messages received,
        // the state changes of the connection, when the player is kicked and when an error occurs.
        Task SubscribeToProjectMessages()
        {
            var callbacks = new SubscriptionEventCallbacks();
            callbacks.MessageReceived += @event =>
            {
                Debug.Log(DateTime.Now.ToString("yyyy-MM-dd'T'HH:mm:ss.fffK"));
                Debug.Log($"Got project subscription Message: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            callbacks.ConnectionStateChanged += @event =>
            {
                Debug.Log($"Got project subscription ConnectionStateChanged: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            callbacks.Kicked += () =>
            {
                Debug.Log($"Got project subscription Kicked");
            };
            callbacks.Error += @event =>
            {
                Debug.Log($"Got project subscription Error: {JsonConvert.SerializeObject(@event, Formatting.Indented)}");
            };
            return CloudCodeService.Instance.SubscribeToProjectMessagesAsync(callbacks);
        }
	}
}
```

## 向玩家发送消息##send-messages-to-players

要向玩家发送消息，请从模块中调用 `SendPlayerMessage` 或 `SendProjectMessage` 函数。

要了解如何运行模块，请参阅[运行模块](./run-modules.md)。

### 从 Unity Runtime 运行##run-from-unity-runtime

您可以通过 `CloudCodeService` 实例的 `CallModuleEndpointAsync` 方法向其他玩家发送消息。您需要使用模块名称和函数名称作为其前两个参数，并使用包含必要参数的字典作为第三个参数。

例如，要发送玩家消息，您可以调用：

```csharp
var sendPlayerMessageResult = await CloudCodeService.Instance.CallModuleEndpointAsync<string>("PushExample", "SendPlayerMessage",
	new Dictionary<string, object> {{"message", "hello with a player message from PushExample!"}, {"messageType", "testType"}, {"playerId", "<other-unity-player-ID>"}});
```

> **Note:**
>
> **注意**：您可以创建[访问控制](./access-control.md)策略，拒绝玩家调用 `SendProjectMessage` 模块函数向游戏中的所有其他玩家发送项目消息。

## 事件##events

特定于玩家的订阅和特定于项目的订阅都有四个事件处理程序：

* `MessageReceived`：当玩家收到新消息时触发。
* `ConnectionStateChanged`：当连接状态改变时触发。
* `Kicked`：当服务器强制取消订阅玩家的消息时触发。这不会关闭 WebSocket 连接，也不会阻止游戏客户端再次订阅消息。
* `Error`：当连接处理发生错误或玩家接收消息发生错误时触发。

## 消息信封##message-envelope

`IMessageReceivedEvent` 作为一个接口，表示玩家从推送服务接收的消息。当 Cloud Code 通过 WebSocket 连接发送消息时，会触发该事件。

请参阅下表以了解 `IMessageReceivedEvent` 接口的有效负载：

| 属性              | 描述                                                         | 示例                                         |
| --------------- | ---------------------------------------------------------- | ------------------------------------------ |
| `SpecVersion`   | 收到消息的信封的规范版本。使用此属性来确保客户端可以正确处理消息。                          | 1.0                                        |
| `Id`            | 消息的唯一标识符。使用此属性来跟踪或记录消息。                                    | `4f88f657-27da-4b1d-a013-9033dbc7b48b`     |
| `Source`        | 消息的信封的来源。此属性指示消息的来源，可用于调试或路由消息。该来源始终是 Cloud Code 域。        | `https://cloud-code.service.api.unity.com` |
| `Type`          | 消息的信封类型。此属性指示消息包含的数据类型，并帮助客户端确定处理方式。该值始终是 Cloud Code 消息类型。 | `com.unity.services.cloud-code.push.v1`    |
| `Time`          | 创建消息的日期和时间。您可以使用此属性来记录消息或计算延迟。                             | `2023-09-19T10:26:40.436878688Z`           |
| `ProjectId`     | 与消息关联的项目的 ID。                                              | `b6670776-9fd3-4d66-8bf4-63fb1112dc95`     |
| `EnvironmentId` | 消息所适用的环境的 ID。您可以使用此属性来区分不同的环境，例如开发、暂存和生产环境。                | `a4bc2571-d1e7-4507-9cc9-80b687d48d8f`     |
| `CorrelationId` | 一个可用于跟踪一系列相关消息的标识符。例如，如果多条消息形成一个对话或工作流程，这些消息可能会共享一个相关性 ID。 | `afbd7c6e-e999-4ca2-86fc-3c908ba83bf2`     |
| `Message`       | 从 Cloud Code 发送到玩家的实际消息内容。                                 | "hello world!"                             |
| `MessageType`   | 玩家收到的消息类型。此属性与 `Type` 类似，但取决于消息内容，而不是信封。                   | "Information"                              |
