⌘K
↑↓ Navigate ↵ Open Esc Close
Tier 3GroupChatPresence14 / 29

Groups: a shared roster with real membership and data infrastructure of its own

Global Chat: a group's channel is the exact same getChannelId/channelConnect flow, just with channel type "gr" instead of "gl"; Friends & Presence for the presence half of this same reference app; Entity Storage for Group Entity, the group-scoped data store this article names but doesn't demonstrate.

brainCloud's Group service is more than a roster with Chat and Presence layered on top. Membership is real infrastructure in its own right: open and closed groups, invitations, join requests with approve/reject, and per-member roles and attributes. A group also carries its own data, separate from anything shared with a single member: Group Data, one JSON blob per group, and Group Entities, a full paginated, queryable collection scoped to the group, both with their own ACLs. This article covers what bcchat, the reference app here, actually drives: create, join, leave, plus Chat and Presence layered on the roster once a groupId exists, which is genuinely where those two services' patterns reuse directly. Group Data, Group Entities, invitations, join requests, and roles are the rest of the service: real and documented, just not part of this particular walkthrough.

How it works

  1. List the groups you're already in, once at login. getMyGroups() returns every group the player belongs to.
  2. Loading a group is a three-step chain, reusing Chat's exact pattern. Resolve the group's channel id via getChannelId("gr", groupId, ...), connect to it the same way as Global Chat's "gl" channel, then layer group-specific membership and presence on top.
  3. Membership and presence are separate calls from the channel connect. readGroupMembers(groupId) gets the roster; registerListenersForGroup(groupId, true) gets live online/offline status for that roster, including each member's initial state in the response.
  4. Creating a group is one call with a full config payload: name, type, visibility, ACL, and default attribute blobs for owner and members. A successful call produces a normal group, loaded the same way as any other.

Code

Fetch every group you're in, at loginApp.js:379-393
fetchGroups()
{
  this.bcWrapper.group.getMyGroups(result =>
  {
    if (result.status === 200)
    {
      result.data.groups.forEach(group => this.loadGroup(group));
    }
  });
}
Loading a group: channel connect, then members, then presence (App.js:398-459, abridged)
loadGroup(group)
{
  this.bcWrapper.chat.getChannelId("gr", group.groupId, result =>
  {
    group.id = result.data.channelId;
    this.connectToChannel(group, () =>
    {
      this.bcWrapper.group.readGroupMembers(group.groupId, result =>
      {
        group.members = Object.keys(result.data).map(profileId => ({
          id: profileId,
          name: result.data[profileId].playerName,
          pic: result.data[profileId].pic,
          online: false,
        }));

        this.bcWrapper.presence.registerListenersForGroup(group.groupId, true, result =>
        {
          // result.data.presence[] carries each member's CURRENT online status:
          // this is the initial snapshot; live changes arrive via the RTT presence callback
          result.data.presence.forEach(presence => { /* merge into group.members */ });
        });
      });
    });
  });
}
Create a groupApp.js:581-598
this.bcWrapper.group.createGroup(
  name,
  "bcchat",   // groupType: an app-defined string, not a fixed enum
  true,       // isOpenGroup
  null,       // acl
  {},         // ownerAttributes
  {},         // defaultMemberAttributes
  {},         // data
  result =>
  {
    if (result.status === 200) this.loadGroup(result.data);
  });
Join / leave (App.js:555-566, App.js:930-942)
this.bcWrapper.group.joinGroup(group.groupId, result =>
{
  if (result.status === 200) this.loadGroup(group);
});

this.bcWrapper.group.leaveGroup(group.groupId, result => { /* ... */ });

Gotchas worth knowing up front

  • Groups have a real member cap, not an unlimited roster. Max Size is a per-group-type portal setting, 2 to 50 members, set under Design → Groups → Group Types. The platform-wide default ceiling is also 50, specifically to limit optimistic-locking contention on group and group entity writes; raising it past 50 is a support request, not a client-side flag.
  • A group's chat channel, membership, and presence are three independent calls. No single call returns all three. Skipping any one of them (channel connect, readGroupMembers, registerListenersForGroup) leaves a group missing chat, a roster, or live status, respectively.
  • readGroupMembers's response is keyed by profile id, which is a feature, not friction. Looking up one specific member's data is a direct result.data[profileId], no scan required. Iterating the whole roster is still one line: Object.keys(result.data).
  • registerListenersForGroup's response carries each member's presence at registration time. That's separate from the live RTT push that follows: it's the initial snapshot, not a live update. The relationship mirrors channelConnect's message history to the live chat push (see the Global Chat article).
  • groupType is an app-defined string ("bcchat" here). The SDK doesn't fix it to an enum. It's typically used server-side to distinguish group kinds (guild vs. party vs. clan) when a game has more than one.