使用共享组数据

共享组数据是玩家与受到严格约束的其他玩家列表共享某些信息的简单方法。

注意

共享组数据的最初设计是在大多数情况下由服务器权威驱动,以前的建议是 将玩家直接添加到共享组数据,因为这会给予他们读取/写入权限(允许作弊)。 但是,我们新的 API 访问策略在功能和安全性方面与原始设计存在较大差异。 有关更多信息,请参阅本文档的高级部分。

警告

共享组数据只应由最多包含十几位玩家的组使用。 共享组数据存在一个问题,过多玩家同时尝试读取相同数据会导致读取数据出现延迟(共享组数据不像旨在由许多玩家同时读取的数据(例如作品数据)那样进行 分片缓存)。 此外,应特别注意避免玩家覆盖彼此的数据。 当多位玩家同时尝试写入同一个键时,这些写入中只有一个能最终写入,这将导致其他用户的数据丢失。

示例:基于回合制的多人异步游戏

共享组数据的原始和最佳用例是存储在线棋盘游戏的状态。 玩家按明确的回合顺序轮流通过 CloudScript 修改数据。

玩家可以注销并稍后恢复游戏,其游戏状态存储在云中。

以下 CloudScript 示例是任何常见棋盘游戏的回合制结构。 棋盘游戏本身以伪代码形式表示。

假设

已启动代表游戏的共享组数据,并且已定义其成员资格。

// CloudScript/Javascript
const MY_GAME_GROUP_KEYS: Array<string> = ["gameState", "currentPlayerTurn"];
interface PlayerTurnArgs {
    sharedGroupId: string;
    nextPlayerTurn: string;
    turnData: any;
}
handlers.TakePlayerTurn = function (args: PlayerTurnArgs) {
    var getRequest: PlayFabServerModels.GetSharedGroupDataRequest = { SharedGroupId: args.sharedGroupId, GetMembers: true, Keys: MY_GAME_GROUP_KEYS };
    var gameData: PlayFabServerModels.GetSharedGroupDataResult = server.GetSharedGroupData(getRequest);
    CheckValidPlayer(currentPlayerId, args.sharedGroupId, gameData.Members, gameData.Data["currentPlayerTurn"].Value, args.nextPlayerTurn);
    var newGameStateJson = UpdateGameState(args.turnData, gameData.Data["gameState"].Value);
    var updateRequest: PlayFabServerModels.UpdateSharedGroupDataRequest = {
        SharedGroupId: args.sharedGroupId,
        Data: {
            "gameState": newGameStateJson,
            "currentPlayerTurn": args.nextPlayerTurn
        }
    };
    server.UpdateSharedGroupData(updateRequest);
}
function CheckValidPlayer(playFabId: string, sharedGroupId: string, members: Array<string>, currentPlayerTurn: string, nextPlayerTurn: string): void {
    var validCurPlayer = false;
    var validNextPlayer = false;
    for (var m = 0; m < members.length; m++) {
        if (members[m] === playFabId)
            validCurPlayer = true;
        if (members[m] === nextPlayerTurn)
            validNextPlayer = true;
    }
    if (!validCurPlayer || !validNextPlayer) // Take extreme action against a player trying to cheat
    {
        server.BanUsers({ Bans: [{ PlayFabId: playFabId, Reason: "Trying to play a game you don't belong to: " + sharedGroupId }] });
        throw "You have been banned";
    }

    if (playFabId !== currentPlayerTurn)
        // May wish to additionally implement a spam-counter here and potentially take more extreme action for high-spam count
        throw "Not your turn";
}
function UpdateGameState(turnData: any, currentState: string): string {
    // PSEUDO-CODE-STUB: Update the turn-based game state according to the rules of this game
    return JSON.stringify({});
}

概括地说,只要组大小相对较小(如上所述),就可以使用共享组数据来实现群/团队或其他半永久性的玩家组。

虽然目前没有严格实施限制,但不支持对很多玩家使用共享组数据;在极端情况下,这可能导致对作品功能进行限制,以避免对服务中的其他使用者产生影响。

关键限制

  • 共享组数据
    • 仅包含简单的键/值对数据(字符串)。 要用作任何其他数据类型(例如物品栏物品、统计数据、虚拟货币等),作品必须提供任何必要的转换。
    • 既不分片 也不缓存,因此当多位玩家同时访问时,响应速度会变慢。 在设计使用共享组数据的功能时,应注意将玩家组限制在较小的规模,并且不允许同时写入。

客户端权限问题

组中没有角色/等级系统,这意味着组中的任何成员在组内都具有绝对权威(没有既定领导者)。

坦白地说,这意味着除非使用 API 访问策略禁用客户端共享组数据方法,否则客户端将可以完全控制数据,这可能导致数据漏洞。

最佳实践是将共享组数据用于影响游戏的数据,或禁用客户端 API 中的共享组数据方法。