实体组
PlayFab 公会解决方案
假设您需要公会、部落、企业、公司、宗族或您的游戏中对此的任何其他称呼 - PlayFab 有此功能。
PlayFab 使用新的实体编程模型(或者更具体地说是组的实体类型)构建公会。 实体组是一个比公会更广泛的概念,但从根本上说,实体组是成熟的公会解决方案。
实体组
实体组是受部落/公会需求启发而诞生的根本概念。
实体组的核心是可用于任何目的的任何实体逻辑组。 实体组可在游戏中同时提供多种用途。
示例
部落/公会 - 这是起点,也是主要的驱动需求。 实体组可用于描述一组经常一起玩游戏的玩家 - 不管是何种社会凝聚力让他们长期聚集在一起。
群 - 实体组可用于创建短期组,允许个人玩家实现直接目标,并可轻松解散。
聊天频道 - 短期或长期聊天频道可以定义为实体组。
游戏内信息订阅 - 您的游戏中是否有单实例传奇物品? 玩家是否希望持续获取有关此物品的最新信息? 可以创建一个看重此物品的实体组,并让所有对此物品感兴趣的玩家实体成为其成员。
简而言之,实体组可以是任何 实体集合(不管是 NPC 还是玩家控制角色、真实还是抽象 - 只要他们需要与此组进行持久状态的绑定)。
好友列表是一个组。 三人私聊也是一个组。 任意发挥吧!
注意
如果您在尝试我们可能没想到的东西,请在论坛中发帖讨论,我们万分感激...
此外,由于实体组本身也是实体,因此它们将包含实体的所有初始特征:
- 对象数据
- 文件数据
- 档案
实体组有资格获得新的实体特征(如果这些特征与组有关)。
注意
组的默认限制为每个组 1000 个成员
使用实体组
目前,实体组可以包含玩家和/或角色。 创建组时,添加到组中的第一个实体将获得管理员角色(为简单起见,本指南将此实体称为所有者)。 然后,所有者将能够邀请新成员、创建具有各种可自定义权限的新角色、修改成员角色、将成员踢出组等。
此外,实体的相同实体函数 也适用于组,因此你将能够将 JSON 对象和文件直接保存到组以保存特定于游戏的任意数据。
下面提供的代码示例可让您了解基本的公会交互。
它允许您创建组、添加和删除成员以及删除组。 这只是一个简单示例,不涉及任何角色或权限内容。
using PlayFab;
using PlayFab.GroupsModels;
using System;
using System.Collections.Generic;
using UnityEngine;
namespace TestGuildController
{
/// <summary>
/// Assumptions for this controller:
/// + Entities can be in multiple groups
/// - This is game specific, many games would only allow 1 group, meaning you'd have to perform some additional checks to validate this.
/// </summary>
[Serializable]
public class GuildTestController
{
// A local cache of some bits of PlayFab data
// This cache pretty much only serves this example , and assumes that entities are uniquely identifiable by EntityId alone, which isn't technically true. Your data cache will have to be better.
public readonly HashSet<KeyValuePair<string, string>> EntityGroupPairs = new HashSet<KeyValuePair<string, string>>();
public readonly Dictionary<string, string> GroupNameById = new Dictionary<string, string>();
public static EntityKey EntityKeyMaker(string entityId)
{
return new EntityKey { Id = entityId };
}
private void OnSharedError(PlayFab.PlayFabError error)
{
Debug.LogError(error.GenerateErrorReport());
}
public void ListGroups(EntityKey entityKey)
{
var request = new ListMembershipRequest { Entity = entityKey };
PlayFabGroupsAPI.ListMembership(request, OnListGroups, OnSharedError);
}
private void OnListGroups(ListMembershipResponse response)
{
var prevRequest = (ListMembershipRequest)response.Request;
foreach (var pair in response.Groups)
{
GroupNameById[pair.Group.Id] = pair.GroupName;
EntityGroupPairs.Add(new KeyValuePair<string, string>(prevRequest.Entity.Id, pair.Group.Id));
}
}
public void CreateGroup(string groupName, EntityKey entityKey)
{
// A player-controlled entity creates a new group
var request = new CreateGroupRequest { GroupName = groupName, Entity = entityKey };
PlayFabGroupsAPI.CreateGroup(request, OnCreateGroup, OnSharedError);
}
private void OnCreateGroup(CreateGroupResponse response)
{
Debug.Log("Group Created: " + response.GroupName + " - " + response.Group.Id);
var prevRequest = (CreateGroupRequest)response.Request;
EntityGroupPairs.Add(new KeyValuePair<string, string>(prevRequest.Entity.Id, response.Group.Id));
GroupNameById[response.Group.Id] = response.GroupName;
}
public void DeleteGroup(string groupId)
{
// A title, or player-controlled entity with authority to do so, decides to destroy an existing group
var request = new DeleteGroupRequest { Group = EntityKeyMaker(groupId) };
PlayFabGroupsAPI.DeleteGroup(request, OnDeleteGroup, OnSharedError);
}
private void OnDeleteGroup(EmptyResponse response)
{
var prevRequest = (DeleteGroupRequest)response.Request;
Debug.Log("Group Deleted: " + prevRequest.Group.Id);
var temp = new HashSet<KeyValuePair<string, string>>();
foreach (var each in EntityGroupPairs)
if (each.Value != prevRequest.Group.Id)
temp.Add(each);
EntityGroupPairs.IntersectWith(temp);
GroupNameById.Remove(prevRequest.Group.Id);
}
public void InviteToGroup(string groupId, EntityKey entityKey)
{
// A player-controlled entity invites another player-controlled entity to an existing group
var request = new InviteToGroupRequest { Group = EntityKeyMaker(groupId), Entity = entityKey };
PlayFabGroupsAPI.InviteToGroup(request, OnInvite, OnSharedError);
}
public void OnInvite(InviteToGroupResponse response)
{
var prevRequest = (InviteToGroupRequest)response.Request;
// Presumably, this would be part of a separate process where the recipient reviews and accepts the request
var request = new AcceptGroupInvitationRequest { Group = EntityKeyMaker(prevRequest.Group.Id), Entity = prevRequest.Entity };
PlayFabGroupsAPI.AcceptGroupInvitation(request, OnAcceptInvite, OnSharedError);
}
public void OnAcceptInvite(EmptyResponse response)
{
var prevRequest = (AcceptGroupInvitationRequest)response.Request;
Debug.Log("Entity Added to Group: " + prevRequest.Entity.Id + " to " + prevRequest.Group.Id);
EntityGroupPairs.Add(new KeyValuePair<string, string>(prevRequest.Entity.Id, prevRequest.Group.Id));
}
public void ApplyToGroup(string groupId, EntityKey entityKey)
{
// A player-controlled entity applies to join an existing group (of which they are not already a member)
var request = new ApplyToGroupRequest { Group = EntityKeyMaker(groupId), Entity = entityKey };
PlayFabGroupsAPI.ApplyToGroup(request, OnApply, OnSharedError);
}
public void OnApply(ApplyToGroupResponse response)
{
var prevRequest = (ApplyToGroupRequest)response.Request;
// Presumably, this would be part of a separate process where the recipient reviews and accepts the request
var request = new AcceptGroupApplicationRequest { Group = prevRequest.Group, Entity = prevRequest.Entity };
PlayFabGroupsAPI.AcceptGroupApplication(request, OnAcceptApplication, OnSharedError);
}
public void OnAcceptApplication(EmptyResponse response)
{
var prevRequest = (AcceptGroupApplicationRequest)response.Request;
Debug.Log("Entity Added to Group: " + prevRequest.Entity.Id + " to " + prevRequest.Group.Id);
}
public void KickMember(string groupId, EntityKey entityKey)
{
var request = new RemoveMembersRequest { Group = EntityKeyMaker(groupId), Members = new List<EntityKey> { entityKey } };
PlayFabGroupsAPI.RemoveMembers(request, OnKickMembers, OnSharedError);
}
private void OnKickMembers(EmptyResponse response)
{
var prevRequest= (RemoveMembersRequest)response.Request;
Debug.Log("Entity kicked from Group: " + prevRequest.Members[0].Id + " to " + prevRequest.Group.Id);
EntityGroupPairs.Remove(new KeyValuePair<string, string>(prevRequest.Members[0].Id, prevRequest.Group.Id));
}
}
}
示例解析
此示例构建为控制器,它将最少的数据保存到本地缓存(使用 PlayFab 作为权威数据层),并提供对组执行 CRUD 操作的方法。
下面,我们来看看所提供的示例中的一些函数:
OnSharedError
- 这是具有 PlayFab 示例的典型模式。 处理错误的最简单方法是报告错误。 您的游戏客户端可能会有更复杂的错误处理逻辑。ListMembership
- 这将调用ListMembership
来确定给定实体所属的所有组。 玩家希望了解他们已加入的组。CreateGroup
/DeleteGroup
- 大多数是自述的。 此示例演示了在成功执行这些调用时更新本地组信息缓存。InviteToGroup
/ApplyToGroup
- 加入组是一个两步过程,可以从两个方向激活:- 玩家可以请求加入组。
- 组也可以邀请玩家。
AcceptGroupInvitation
/AcceptGroupApplication
- 联接过程的第二步。 响应实体接受邀请,完成使玩家成为此组成员的过程。RemoveMembers
- 有权执行此操作的成员(由其角色权限定义)将能够从组中启动成员。
服务器与客户端
与所有新的实体 API 方法一样,服务器 API 和客户端 API 之间没有区别。
操作由调用方根据进程的身份验证方式执行。 将以此方式识别客户端,并作为作品玩家实体调用这些方法,每个调用都将评估玩家在组中的角色和权限,确保他们有权执行此操作。
服务器使用相同的 developerSecretKey
进行身份验证,后者将该进程标识为标题实体。 作品绕过角色检查,并且作品执行的 API 调用仅在无法执行操作时才会失败,例如:如果某实体并非成员,则无法删除此实体。