> ## Documentation Index
> Fetch the complete documentation index at: https://devdocs.xbox.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 使用玩家发布者数据奖励多游戏游玩

> 使用 PlayFab 玩家发布者数据和 CloudScript 跟踪跨游戏登录，并向游玩多个游戏的玩家发放虚拟货币奖励。

奖励通常会涉及玩家数据*以外*的其他系统，为简化起见，本示例演示奖励虚拟货币。

## 要求

* 一个 [PlayFab 开发者账户](https://developer.playfab.com)。
* 玩家必须使用相同的凭据登录到*两个*游戏。一种方法是使用**可恢复凭据**，如我们的[登录基础和最佳实践](/services/playfab/identity/player-identity/login/login-basics-best-practices)教程中所述。若要向匿名账户添加**可恢复**登录，请参阅我们的[账户关联](/services/playfab/identity/player-identity/login/quickstart)教程。
* 本示例需要 [CloudScript](/services/playfab/live-service-management/service-gateway/automation/cloudscript/writing-custom-cloudscript) 的实用知识：
  * 我们的示例演示了基本的数据安全，以避免玩家作弊。同样地，如果游戏使用自定义游戏服务器，也可以在其中使用服务器 API。
* 通过 PlayFab 触发的奖励需要使用相应的 PlayFab 功能。PlayFab 奖励可以是[**虚拟货币**](/services/playfab/economy-monetization/economy-v2/tutorials/currencies)、[**库存物品**](/services/playfab/player-progression/player-data/player-inventory)、[**自定义玩家数据**](/services/playfab/player-progression/player-data/quickstart)、[**统计数据**](/services/playfab/community/leaderboards/tournaments-leaderboards/using-player-statistics)等形式。在 PlayFab 系统之外分发奖励是一个高级主题，不在本教程范围内。

我们还建议开发者对从 CloudScript 发出的所有服务器 API 调用进行良好的错误处理。

## 每个游戏向发布者数据报告登录

每个游戏都需要报告发生了登录。为简化起见，我们的示例仅针对游玩的每个其他发布者游戏为每个游戏提供*一次*奖励。

您可以自由地扩展此想法，通过计数器、时间戳或其他机制来提供渐进式或顺序式奖励。

以下 CloudScript 需要存在于工作室的*每个*游戏中。

```javascript theme={null}
// CloudScript/Javascript

var PUBLISHER_USED_TITLES_KEY = "playedTitleIds";
handlers.TrackTitleUsage = function () {
    // Get the User Publisher Data for this player, and convert it into our expected format
    var getRequest = { Keys: [PUBLISHER_USED_TITLES_KEY], PlayFabId: currentPlayerId };
    var getResult = server.GetUserPublisherInternalData(getRequest);
    var playedTitlesList = JSON.parse(getResult.Data[PUBLISHER_USED_TITLES_KEY].Value); // format is arbitrary, but this example assumes Array<string>
    if (!playedTitlesList)
        playedTitlesList = [];
    // Check if we've played this title already
    var alreadyPlayed = false;
    for (var i = 0; i < playedTitlesList.length; i++)
        alreadyPlayed = alreadyPlayed || playedTitlesList[i] === script.titleId;
    if (alreadyPlayed)
        return; // Nothing to do
    // Update the list of played titles, and re-save
    playedTitlesList.push(script.titleId);
    var setRequest = { PlayFabId: currentPlayerId, Data: { PUBLISHER_USED_TITLES_KEY: JSON.stringify(playedTitlesList) } };
    server.UpdateUserPublisherInternalData(setRequest);
}
```

特别是，本示例演示了如何使用：

* server.[GetUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.getuserpublisherinternaldata)
* server.[UpdateUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.updateuserpublisherinternaldata)。

## 每个游戏检查可兑换的奖励

在您跟踪了玩过哪些游戏之后，需要跟踪并发放奖励。此 CloudScript 函数将根据玩过其他游戏的情况检查并发放可用奖励。

```javascript theme={null}
// CloudScript/Javascript

var PUBLISHER_REDEEMED_TITLES_KEY = "redeemedTitleIds";
var MY_CROSS_TITLE_REWARDS = { "AU": 10 };
handlers.CheckCrossTitleRewards = function () {
    // Get the publisher data concerning cross-title rewards for this player
    var getRequest = { Keys: [PUBLISHER_USED_TITLES_KEY, PUBLISHER_REDEEMED_TITLES_KEY], PlayFabId: currentPlayerId };
    var getResult = server.GetUserPublisherInternalData(getRequest);
    var redeemedTitleRewards = JSON.parse(getResult.Data[PUBLISHER_REDEEMED_TITLES_KEY].Value); // format is arbitrary, but this example assumes { [key: string]: Array<string> }
    if (!redeemedTitleRewards)
        redeemedTitleRewards = {};
    var playedTitlesList = JSON.parse(getResult.Data[PUBLISHER_USED_TITLES_KEY].Value); // format is arbitrary, but this example assumes Array<string>
    if (!playedTitlesList)
        playedTitlesList = [];

    // Determine which titles are un-redeemed
    var unredeemedTitleIds = [];
    for (var i = 0; i < playedTitlesList.length; i++) {
        if (!redeemedTitleRewards.hasOwnProperty(playedTitlesList[i]) && playedTitlesList[i] !== script.titleId)
            unredeemedTitleIds.push(playedTitlesList[i]);
    }
    if (unredeemedTitleIds.length === 0)
        return null; // Nothing to redeem

    // Give the cross title rewards
    var multiplier = unredeemedTitleIds.length;
    var actualRewards = {}; // MY_CROSS_TITLE_REWARDS is a global constant, so don't modify it or you'll mess up future calls
    // Please note that the number of API calls that may be made from CloudScript, as well as the total available processing time is limited,
    // and so the number of rewards should be as small as possible (only one VC, in this case)
    for (var eachKey in MY_CROSS_TITLE_REWARDS) {
        actualRewards[eachKey] = MY_CROSS_TITLE_REWARDS[eachKey] * multiplier;
        server.AddUserVirtualCurrency({ PlayFabId: currentPlayerId, VirtualCurrency: eachKey, Amount: MY_CROSS_TITLE_REWARDS[eachKey] }); // Can only add 1 VC at a time
    }

    // Save the Publisher data indicating rewards are claimed
    redeemedTitleRewards[script.titleId] = playedTitlesList;
    var setRequest = { PlayFabId: currentPlayerId, Data: { PUBLISHER_REDEEMED_TITLES_KEY: JSON.stringify(redeemedTitleRewards) } };
    server.UpdateUserPublisherInternalData(setRequest);

    // Tell the client the reward
    return actualRewards;
};
```

<Note>
  此示例演示了如何使用 server.[GetUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.getuserpublisherinternaldata)（请求多个键）、server.[UpdateUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.updateuserpublisherinternaldata) 和 server.[AddUserVirtualCurrency](xref:titleid.playfabapi.com.server.playeritemmanagement.adduservirtualcurrency)。
</Note>

代码块表示以下步骤：

* 从 PlayFab 获取数据，并将其解析为本地数据结构（调用 [GetUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.getuserpublisherinternaldata)）。
* 检查数据并搜索未奖励的游戏 ID。
* 发放奖励（调用 [AddUserVirtualCurrency](xref:titleid.playfabapi.com.server.playeritemmanagement.adduservirtualcurrency)）。
* 更新已领取的奖励（调用 [UpdateUserPublisherInternalData](xref:titleid.playfabapi.com.server.playerdatamanagement.updateuserpublisherinternaldata)）。
* 将奖励金额提供给客户端（通过 CloudScript 的 return 语句）。

## 结论

玩家发布者数据和玩家数据在结构上是相同的。

玩家数据应该是*特定于游戏*的，而玩家发布者数据应*仅*包含与您所有游戏相关的信息。

## 另请参阅

[游戏内简单交叉推广](https://github.com/PlayFab/PlayFab-Samples/tree/master/Recipes/SimpleCrossPromotion)：奖励参与您多款游戏的玩家。


## Related topics

- [玩家数据](/zh-CN/services/playfab/player-progression/player-data/index.md)
- [游戏数据](/zh-CN/services/playfab/live-service-management/game-configuration/titledata/index.md)
- [玩家数据快速入门](/zh-CN/services/playfab/player-progression/player-data/quickstart.md)
- [使用玩家详情](/zh-CN/services/playfab/player-progression/player-data/player-details.md)
- [使用玩家统计信息](/zh-CN/services/playfab/community/leaderboards/tournaments-leaderboards/using-player-statistics.md)
