Social

Friends

Friend lists, requests, and online presence.

Server
services.AddCrossplayFriends();
Unity package
com.crossplay.friends
Depends on
core

Providers you can swap 1

Infrastructure seams. Crossplay ships a working implementation of each — replacing one changes where data lives or how it moves, never a game rule.

IFriendStore provider

Friendship persistence SPI. Friendships are unordered account-id pairs; pending requests and blocks are directed (requester/blocker -> target/blocked). The GAME supplies a durable store; the base ships an in-memory default (InMemoryFriendStore) for dev and tests only.

  • void AddFriendship(long a, long b)

    Records a mutual friendship between a and b.

  • void AddRequest(long requester, long target)

    Records a pending directed request from requester to target.

  • bool AreFriends(long a, long b)

    True if a and b are friends.

  • void Block(long blocker, long blocked)

    Records a directed block by blocker against blocked. Implementations MUST also drop any existing friendship and pending requests between the two (blocking implies unfriend). Idempotent — blocking an already-blocked player is a no-op.

  • IReadOnlyList<long> BlockedBy(long accountId)

    All account ids that accountId has blocked.

  • int FriendCount(long accountId)

    Number of friends accountId currently has.

  • IReadOnlyList<long> FriendsOf(long accountId)

    All account ids that are friends of accountId.

  • bool HasRequest(long requester, long target)

    True if a pending request from requester to target exists.

  • bool IsBlocked(long blocker, long blocked)

    True if blocker has blocked blocked.

  • void RemoveFriendship(long a, long b)

    Removes any friendship between a and b.

  • void RemoveRequest(long requester, long target)

    Removes a pending request from requester to target.

  • void Unblock(long blocker, long blocked)

    Lifts a directed block by blocker against blocked.

Services you call 1

Crossplay implements these. Resolve them from DI and call them from your own systems.

IFriendsService

Server-side friends presence entry point — the API the game calls to publish who is online and what they are doing. The implementation is also wired as a Core ISessionLifecycleListener, so raw connect/disconnect drive presence automatically; a game only calls these directly to fan out an out-of-band change (e.g. a status update, or re-announcing after a custom login step). Meaning of the status blob is entirely the game's — the framework stores and relays opaque bytes.

  • void NotifyPresence(ISession session, bool online)

    Best-effort: notifies each online friend of session's account that the player is now online, by pushing a FriendPresenceChanged. With a cluster IAccountDirectory registered, the fan-out reaches friends on any node.

  • void SetStatus(ISession session, byte[] status)

    Sets the caller's rich-presence blob (GAME vocabulary; empty clears; a blob over MaxStatusBytes is dropped) and pushes the change to online friends — the push IS the confirmation. Ephemeral: the status clears when the session disconnects.

  • byte[] StatusOf(long accountId)

    The account's current rich-presence blob, or an empty array when unset/offline.

Configuration 1

Every tunable lives in an options object — there are no magic numbers to hunt for.

FriendsOptions

Configurable friends rules, passed to AddCrossplayFriends. Defaults live here, never inline in handlers (the no-hardcoded-values rule).

  • int MaxFriends { get; set; }

    Maximum number of friends a single account may have. A request past this cap is rejected with LimitReached.

  • int MaxStatusBytes { get; set; }

    Largest rich-presence blob a client may set; an oversize blob is dropped silently (a well-behaved client never sends one).

Wire messages 15

The protocol this piece speaks. Ids are allocated per piece so they can never collide.

1301 FriendRequestSend

Client -> server: request friendship with a player by display name.

1302 FriendRequestResponse

Server -> client: result of a FriendRequestSend.

1303 FriendRequestNotification

Server -> client (to the target): another player requested friendship.

1304 FriendAcceptRequest

Client -> server: accept a pending friend request.

1305 FriendAcceptResponse

Server -> client: result of a FriendAcceptRequest.

1306 FriendRemoveRequest

Client -> server: remove an existing friend.

1307 FriendRemoveResponse

Server -> client: result of a FriendRemoveRequest.

1308 FriendListRequest

Client -> server: request the caller's friend list.

1309 FriendList

Server -> client: the caller's current friend list. Sent both as the reply to a FriendListRequest AND, unsolicited, as a live update pushed to each affected online player after an accept / remove / block / unblock — the same message, no extra wire type, so drop-in panels refresh without polling.

1310 FriendPresenceChanged

Server -> client: a friend came online, went offline, or changed their rich status.

1311 FriendStatusSet

Client → server: set my rich-presence blob. GAME vocabulary — one game sends a UTF-8 activity line, another a packed struct of zone/party/score; the framework only stores and relays it. Empty clears. Fire-and-forget (the confirmation IS the presence push friends receive).

1312 FriendBlockRequest

Client -> server: block a player by account id. Blocking removes any existing friendship and pending requests between the two, and prevents future friend requests.

1313 FriendBlockResponse

Server -> client: result of a FriendBlockRequest.

1314 FriendUnblockRequest

Client -> server: lift a block on a player by account id.

1315 FriendUnblockResponse

Server -> client: result of a FriendUnblockRequest.

Unity services you inject 1

Crossplay binds these in the client context. Inject and call them from your own MonoBehaviours and presenters.

IFriendsClient

Data-layer friends service (no UI): async request/accept/remove/list RPCs plus events for inbound friend requests and presence changes. Self-registers under the Crossplay client context.

  • event Action<FriendRequestNotification> RequestReceived

    Raised when another player sends this client a friend request.

  • event Action<FriendPresenceChanged> PresenceChanged

    Raised when a friend comes online or goes offline.

  • event Action<FriendList> ListChanged

    Raised whenever a FriendList arrives — both as the reply to ListAsync and as the server's unsolicited push after a friends mutation (accept/remove/block/unblock) touched this player's list. UI can re-render from it directly.

  • UniTask<FriendRequestResponse> RequestAsync(string targetName, CancellationToken ct = default)

    Requests friendship with a player by display name.

  • UniTask<FriendAcceptResponse> AcceptAsync(long requesterAccountId, CancellationToken ct = default)

    Accepts a pending friend request from the given requester.

  • UniTask<FriendRemoveResponse> RemoveAsync(long friendAccountId, CancellationToken ct = default)

    Removes an existing friend.

  • UniTask<FriendBlockResponse> BlockAsync(long targetAccountId, CancellationToken ct = default)

    Blocks a player by account id. Blocking also removes any existing friendship and pending requests between the two, and prevents future friend requests either way.

  • UniTask<FriendUnblockResponse> UnblockAsync(long targetAccountId, CancellationToken ct = default)

    Lifts a block on a player by account id.

  • UniTask<FriendList> ListAsync(CancellationToken ct = default)

    Requests the caller's current friend list.

  • void SetStatus(byte[] status)

    Sets my rich-presence blob (GAME vocabulary; empty clears). Fire-and-forget — friends receive it as a PresenceChanged push.

UI views you can replace 1

Each ships a working uGUI panel you can drag into a scene. Substitute your own view to keep the logic and change the look — the presenter never knows.

ICrossplayFriendsView

Narrow contract the presenter drives. UI-only: render calls in, user intents out — no data or services.