# 处理跨玩家数据

> Access and modify data for multiple players simultaneously with Cloud Code scripts.

您可以使用 Cloud Code 脚本同时访问和修改多个玩家的数据。

这种功能解锁了多种用例，例如：

* 在 Cloud Save 中一次性为多个玩家更新玩家数据。
* 一次性更新多个玩家的 Economy 余额。

> **Note:**
>
> **注意：** 为了降低滥用风险，请确保只有特定用户才能调用处理跨玩家数据的脚本，而不是所有玩家都具有这样的能力。要限制对特定脚本的访问，可以使用[访问控制](./access-control.md)。

## 获取玩家 ID##retrieve-player-ids

为了处理多个玩家的数据，您需要先调用 UGS 服务来获取玩家 ID。

为了获取玩家 ID，可以使用 Cloud Code JavaScripts SDK。请参阅 [Cloud Code JavaScript SDK](https://cloud-code-sdk-documentation.cloud.unity3d.com/) 了解更多信息。

此外，也可以直接使用服务的 REST API。可用于获取玩家 ID 的服务包括 [Leaderboards](/leaderboards.md)、[Lobby](/lobby.md) 和 [Friends](/friends.md)。

### 使用 Leaderboards##use-leaderboards

首先，需要[创建一个排行榜](/leaderboards/get-started.md)。

例如，可以从排行榜中获取前五名玩家并保存他们的 ID。以下脚本接受一个排行榜 ID 并返回前五名玩家的分数列表：

#### JavaScript##javascript

```javascript
const { LeaderboardsApi } = require("@unity-services/leaderboards-1.1");

module.exports = async ({ params, context, logger }) => {
  const leaderboardsApi = new LeaderboardsApi(context);
  let result;

  try {
    result = await leaderboardsApi.getLeaderboardScores(context.projectId, params.leaderboardId, 0, 5);
    return result.data.results;
  } catch (err) {
    logger.error("Failed to retrieve players from the leaderboard", {"error.message": err.message}, {"leaderboardId" : params.leaderboardId});
    throw err;
  }
};

// Uncomment the code below to enable the inline parameter definition
// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)
//
// module.exports.params = {
//   leaderboardId: { type: "String", required: true },
// };
```

响应类似如下所示：

```json
{
    "output": [
        {
            "playerId": "wYI5NW5gEVvR3PBmYXEzFS1JvSz3",
            "playerName": "IncredibleGleamingPelican#3",
            "rank": 0,
            "score": 44.0
        },
        {
            "playerId": "ryuAA3ZX23aRHN5ZJClC1Z5BrpVb",
            "playerName": "EffectiveBindingBlackberry#9",
            "rank": 1,
            "score": 1.0
        }
    ]
}
```

### 使用 Lobby##use-lobby

首先，需要[创建一个大厅](/lobby/get-started.md)。

您可以获取当前在大厅中的玩家列表，并使用他们的 ID 来处理他们的数据。

此脚本将大厅 ID 作为参数。

#### JavaScript##javascript

```javascript
const { LobbyApi } = require("@unity-services/lobby-1.2");

module.exports = async ({ params, context, logger }) => {
  const lobbyApi = new LobbyApi(context);

  try {
    // Get and return the lobby.
    const getResponse = await lobbyApi.getLobby(params.lobbyId, "cloud-code");
    return getResponse.data.players;

  } catch (err) {
    logger.error("Failed to retrieve players from the Lobby", {"error.message": err.message}, {"lobbyId" : params.lobbyId});
    throw err;
  }
};

// Uncomment the code below to enable the inline parameter definition
// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)
//
// module.exports.params = {
//   lobbyId: { type: "String", required: true },
// };
```

响应类似如下所示：

```json
{
    "output": [
        {
            "allocationId": null,
            "connectionInfo": null,
            "data": null,
            "id": "Z96pNb4wfgdaMLqMQfWpwXEclaRR",
            "joined": "2023-09-08T11:02:18.13Z",
            "lastUpdated": "2023-09-08T11:02:18.13Z"
        }
    ]
}
```

### 使用 Friends##use-friends

为了使用 Friends，您需要创建一个关系。如需了解更多信息，请参阅 [Friends 文档](/friends.md)。

然后，可以获取玩家的好友列表，并使用他们的 ID 来处理他们的数据。以下示例显示了如何获取玩家已将好友请求发送到的玩家 ID 列表：

#### 可选 - 发送好友请求##optional---send-a-friend-request

您可以使用辅助脚本向玩家发送好友请求。此脚本将玩家 ID 作为参数。

要在 [Unity Dashboard](https://cloud.unity3d.com/) 中测试此示例，请在两个不同的浏览器标签页中以两个不同玩家的身份进行身份验证，然后运行脚本。在一个标签页中使用第一个玩家的 ID 运行脚本，然后在另一个标签页中使用第二个玩家的 ID 运行脚本。

##### JavaScript##javascript

```javascript
const { RelationshipsApi } = require("@unity-services/friends-1.0");


module.exports = async ({ params, context, logger }) => {
  const relationshipsApi = new RelationshipsApi({ accessToken: context.accessToken });

  try {
    const payload = {
      type: "FRIEND_REQUEST",
      members: [{ id: params.playerId }]
    };

    const createRes = await relationshipsApi.createRelationship(true, true, payload);
    logger.info(`New relationship initiated: ${JSON.stringify(createRes.data)}`)
  } catch (err) {
    // The call will fail if the friend request has already been sent
    logger.error("Error while sending a friend request", { "error.message": err.message });
  }

  try {
    const list = await relationshipsApi.getRelationships(10, 0, true, true, ["FRIEND_REQUEST", "FRIEND"]);
    logger.info(`Retrieved relationships: ${JSON.stringify(list.data)}`)
  } catch (err) {
    logger.error("Error while retrieving relationships", { "error.message": err.message });
  }
}

// Uncomment the code below to enable the inline parameter definition
// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)
//
// module.exports.params = {
//   playerId: { type: "String", required: true },
// };
```

#### 获取好友列表##retrieve-a-list-of-friends

以下脚本将获取玩家已将好友请求发送到的玩家 ID 列表：

##### JavaScript##javascript

```javascript
const { RelationshipsApi } = require("@unity-services/friends-1.0");

module.exports = async ({ params, context, logger }) => {
  const relationshipsApi = new RelationshipsApi({ accessToken: context.accessToken });

  try {
    const list = await relationshipsApi.getRelationships(10, 0, true, true, ["FRIEND_REQUEST", "FRIEND"]);
    logger.info(`Retrieved relationships: ${JSON.stringify(list.data)}`)
    return list.data[0].members;
  } catch (err) {
    logger.error("Error while retrieving relationships", { "error.message": err.message });
  }
}
```

响应类似如下所示：

```json
{
    "output": [
        {
            "created": "2023-09-18T14:46:34.74Z",
            "expires": null,
            "id": "5774e898-a078-4f92-9f0a-4c9beeb6d1bb",
            "members": [
                {
                    "id": "0gvQingjjBwpZhkUJfeoKnFUkD4T"
                }
            ],
            "type": "FRIEND_REQUEST"
        }
    ]
}
```

## 处理玩家数据##interact-with-player-data

获得玩家 ID 列表后，您就可以使用它们通过 UGS 服务更新玩家数据。

以下示例显示了如何使用 [Cloud Save](/cloud-save.md) 和 [Economy](/economy.md) 服务来处理玩家数据。

### 以 Cloud Code 身份进行身份验证##authenticate-as-cloud-code

要处理跨玩家数据，您需要以 Cloud Code 身份进行身份验证。此操作可以通过在创建客户端时使用 `serviceToken` 而不是 `accessToken` 来完成：

```csharp
const cloudSaveApi = new DataApi(context);
```

> **Note:**
>
> **注意：** 默认情况下，将 `context` 对象传递给客户端将使用 `serviceToken` 对客户端进行身份验证。

请查看[服务和访问令牌支持](./token-support.md)以获取支持 `ServiceToken` 的服务列表。

### 使用 Cloud Save 更新前 5 名玩家数据##update-top-5-player-data-using-cloud-save

处理玩家数据的一种方法是将玩家 ID 列表传递给 Cloud Save。

示例中的脚本将排行榜 ID 作为参数。

以下示例显示了如何在 Cloud Save 中为从排行榜中获取的前五名玩家设置一个值。为了确保示例正常工作，请按照以下步骤操作：

1. 使用 [Leaderboards 服务](/leaderboards/get-started.md)创建一个排行榜。
2. 在排行榜上准备好一些玩家分数。

创建一个新脚本并添加以下代码：

#### JavaScript##javascript

```javascript
const { LeaderboardsApi } = require("@unity-services/leaderboards-1.1");
const { DataApi } = require("@unity-services/cloud-save-1.4");


module.exports = async ({ params, context, logger }) => {
  const leaderboardsApi = new LeaderboardsApi(context);
  const cloudSaveApi = new DataApi(context);

  let result;

  try {
    result = await leaderboardsApi.getLeaderboardScores(context.projectId, params.leaderboardId, 0, 5);
  } catch (err) {
    logger.error("Failed to retrieve players from the leaderboard", {"error.message": err.message}, {"leaderboardId" : params.leaderboardId});
    throw err;
  }

  if (result.len != 0) {
    // Set Cloud Save data for every player
    const promises = result.data.results.map(async (score) => {
      try {

        await cloudSaveApi.setItem(context.projectId, score.playerId, {
          key: "ReachedTop5",
          value: true,
        });

      } catch (err) {
        logger.error("Failed to update Cloud Save data", {"error.message": err.message}, {"affectedPlayerId" : score.playerId});
        return;
      }
    });

    await Promise.all(promises);
  }
};

// Uncomment the code below to enable the inline parameter definition
// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)
//
// module.exports.params = {
//   leaderboardId: { type: "String", required: true },
// };
```

此脚本为排行榜中的所有玩家设置一个键/值对。要验证这一点，您可以导航到 Unity Dashboard 中的 **Player Management（玩家管理）** 服务，并检查排名靠前的玩家的 Cloud Save 数据。

### 对大厅中所有使用 Economy 的玩家进行奖励##reward-all-players-in-the-lobby-using-economy

要处理玩家余额，您可以将玩家 ID 列表传递给 Economy API。

以下示例显示了如何增加从大厅中获取的玩家列表的货币余额。模块函数将大厅 ID、货币 ID 和余额增加金额作为参数。

为了确保示例正常工作，请按照以下步骤操作：

1. 使用 [Lobby 服务](/lobby/create-a-lobby.md)创建大厅。
2. 在大厅中准备好一名玩家。
3. 在 Economy 服务中[创建](/economy/add-currency.md)和[发布](/economy/publication.md)货币。

创建一个新脚本并添加以下代码：

#### JavaScript##javascript

```javascript
const { LobbyApi } = require("@unity-services/lobby-1.2");
const { CurrenciesApi, ConfigurationApi } = require("@unity-services/economy-2.4");

module.exports = async ({ params, context, logger }) => {
  const lobbyApi = new LobbyApi(context);

  let result;
  try {
    // Get and return the lobby.
    result = await lobbyApi.getLobby(params.lobbyId, "cloud-code");

  } catch (err) {
    logger.error("Failed to retrieve players from the Lobby", {"error.message": err.message}, {"lobbyId" : params.lobbyId});
    throw err;
  }

  const currenciesApi = new CurrenciesApi(context);
  const configApi = new ConfigurationApi(context);

  if (result.len != 0) {
    // Reward currency to every player
    const promises = result.data.players.map(async (player) => {
      try {
        // Retrieve the player config to get the configAssignmentHash.
        // This is needed to ensure the currency is synced
        var config = await configApi.getPlayerConfiguration({
          playerId: player.id,
          projectId: context.projectId,
        });


        await currenciesApi.incrementPlayerCurrencyBalance({
          currencyId: params.currencyId,
          playerId: player.id,
          projectId: context.projectId,
          configAssignmentHash: config.data.metadata.configAssignmentHash,
          currencyModifyBalanceRequest: { amount: params.amount }
        });

      } catch (err) {
        logger.error("Failed to increment currency", {"error.message": err.message}, {"currencyId" : params.currencyId});
        throw err;
      }
    });

    await Promise.all(promises);
  }
};

// Uncomment the code below to enable the inline parameter definition
// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)
//
// module.exports.params = {
//   lobbyId: { type: "String", required: true },
//   currencyId: { type: "String", required: true },
//   amount: { type: "Numeric", required: true },
// };
```

此脚本会增加大厅中所有玩家的货币余额。

要进行验证，您可以导航到 [Unity Dashboard](https://cloud.unity.com) 中的 **Player Management（玩家管理）** 服务，并检查玩家的余额。
