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

Friends & Presence: a real feature, not a roadmap item

Groups: bcchat implements both together, and registers presence per-group here rather than per-friend; Global Chat for the RTT dispatch pattern this reuses.

brainCloud's Friend service works alongside the RTT Presence extension to let a player see who else in their network is online, live. Two reference apps exercise it, scoped two different ways: the bcchat React example (repositories/js-examples/bcchat) fetches a friend list, then scopes presence per group; the BombersRTT Unity example (repositories/unity-examples/BombersRTT) scopes presence directly to the friend list itself, no group involved. An earlier draft of this series described this feature as a roadmap item, SDK surface that "exists today but isn't yet exercised by any reference app." That was incorrect. This article corrects that framing with what's actually there.

How it works

  1. Fetch friends once, right after connecting to RTT. listFriends(platform, includeSummaryData) returns the roster in a single call. No separate "check who's online" call is needed to render the list.
  2. The two reference apps pick two different presence scopes, and both are real. bcchat registers per group the player belongs to (registerListenersForGroup); BombersRTT registers across the whole friend list directly (registerListenersForFriends + getPresenceOfFriends). Presence also supports an arbitrary-profile-list scope (registerListenersForProfiles) for cases outside both. Which one to use follows from what the UI needs to show.
  3. Live presence changes arrive through the same RTT callback as chat. They're tagged service: "presence", operation: "INCOMING".
  4. Adding or removing a friend is fire-and-forget here. This reference app skips any request/accept handshake. The UI updates optimistically, and addFriends/removeFriends confirms the change server-side.

Code

Enable RTT presence alongside chat, right after loginApp.js:296-297
this.bcWrapper.rttService.registerRTTChatCallback(this.onRttMessage.bind(this));
this.bcWrapper.rttService.registerRTTPresenceCallback(this.onRttMessage.bind(this));
Fetch the friend rosterApp.js:337-344
fetchFriendList()
{
  this.bcWrapper.friend.listFriends(this.bcWrapper.friend.friendPlatform.All, false, result =>
  {
    if (result.status === 200)
    {
      let state = this.state;
      state.chatData.friends = result.data.friends;
      // ...
    }
  });
}
bcchat: presence scoped to a group, with initial state in the responseApp.js:433-449
this.bcWrapper.presence.registerListenersForGroup(group.groupId, true, result =>
{
  if (result.status === 200)
  {
    result.data.presence.forEach(presence =>
    {
      var member = group.members.find(member => presence.user.id === member.id);
      if (member)
      {
        member.pic = presence.user.pic || member.pic;
        member.online = presence.online;
      }
    });
  }
});
Presence pushes arrive tagged service: "presence"App.js:816-846
onRttMessage(message)
{
  if (message.service === "chat" && message.operation === "INCOMING") { /* ... */ }
  else if (message.service === "presence" && message.operation === "INCOMING")
  {
    var from = message.data.from;
    let state = this.state;
    state.chatData.groups.forEach(group =>
    {
      group.members?.forEach(member =>
      {
        if (member.id === from.id) member.online = message.data.online;
      });
    });
    this.setState(state);
  }
}
Add / remove a friend (App.js:613, App.js:970)
this.bcWrapper.friend.addFriends([friend.id], result => { /* ... */ });
this.bcWrapper.friend.removeFriends([friend.id], result => { /* ... */ });
BombersRTT (Unity): friend-scoped presence, no group involved (MainMenuState.cs:165-176, GFriendsManager.cs:67-69)
private void OnEnableRTTSuccess()
{
    GCore.Wrapper.RTTService.RegisterRTTPresenceCallback(OnPresenceCallback);
    GCore.Wrapper.Client.PresenceService.RegisterListenersForFriends(platform, true, presenceSuccess);
    OnUpdateStats();
}

private void presenceSuccess(string in_data, object obj)
{
    GFriendsManager.Instance.GetPresenceOfFriends(OnGetPresenceOfFriendsSuccess);
    GCore.Wrapper.Client.PresenceService.SetVisibility(true);
    GPlayerMgr.Instance.UpdateActivity(GPlayerMgr.LOCATION_MAIN_MENU, GPlayerMgr.STATUS_IDLE, "", "", true);
}

// GFriendsManager.cs
public void GetPresenceOfFriends(SuccessCallback in_success = null, FailureCallback in_failure = null)
{
    GCore.Wrapper.Client.PresenceService.GetPresenceOfFriends(m_platform, m_includeOffline, OnGetPresenceOfFriendsSuccess + in_success, OnGetPresenceOfFriendsFailed + in_failure);
}

Gotchas worth knowing up front

  • RegisterListenersForFriends is real and live, just not in bcchat. bcchat only registers per group; BombersRTT is the reference app that scopes presence to the whole friend list instead (RegisterListenersForFriends + GetPresenceOfFriends, plus SetVisibility(true) so the player shows up as online to their friends at all).
  • Chat and presence pushes arrive on the same kind of RTT callback, distinguished only by service. A handler checking operation alone, without also checking service, will misread one for the other.
  • addFriends here is unilateral. There's no pending-invite state in this reference app: anyone can add anyone. Mutual consent, where needed, has to be built as a separate layer; the call itself doesn't enforce it.