Social

Party

Ephemeral parties: leader, ready checks, kicks.

Server
services.AddCrossplayParty();
Unity package
com.crossplay.party
Depends on
core

Seams you implement 1

Crossplay calls these; your game supplies them. Each ships an inert or permissive default, so register yours before services.AddCrossplayParty(); and it wins.

IPartyReadySink game SPI

The post-ready-check seam the Party piece OWNS — the analog of Matchmaking's IMatchFormedSink and Lobbies' ILobbyStartSink: fired once when a party's ready check completes with EVERY member ready, so a composition layer can start a match / instance for exactly that group of that size — without Party ever referencing Matches or the Director. The shipped default (NullPartyReadySink) does nothing; a game or a Hosting bridge registers its own BEFORE AddCrossplayParty (TryAdd) to win the seam. Removable: with no sink a passed ready check just broadcasts PartyReadyState exactly as before.

  • void PartyReady(long partyId, IReadOnlyList<PartyReadyMember> members)

    A party's ready check passed with all members ready. members carries each member's account/name and their LocalSession when local.

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.

IPartyStore provider

Persistence SPI for live parties and pending invites. One party per account (a member belongs to at most one). Abstracted so a game can swap in a durable implementation; the framework ships an in-memory default (InMemoryPartyStore) for dev and single-node servers.

  • void AddInvite(long accountId, long partyId)

    Records a pending invite of accountId to partyId.

  • bool AddMember(long partyId, long accountId)

    Adds a member to a party. Returns false if the party is missing or already a member.

  • PartyRecord Create(long leaderAccountId)

    Creates a new party led by (and containing) leaderAccountId.

  • PartyRecord GetByAccount(long accountId)

    Finds the party containing accountId, or null.

  • PartyRecord GetById(long partyId)

    Looks up a party by id, or null.

  • bool HasInvite(long accountId, long partyId)

    True if there is a pending invite of accountId to partyId.

  • IReadOnlyList<long> Members(long partyId)

    The current member account ids of a party (empty if missing).

  • bool Remove(long partyId)

    Removes (disbands) a party. Returns false if it did not exist.

  • void RemoveInvite(long accountId, long partyId)

    Clears a pending invite of accountId to partyId.

  • bool RemoveMember(long partyId, long accountId)

    Removes a member from a party. Returns false if the party is missing or not a member.

Services you call 1

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

IPartyService

The party service the game calls to drive group membership. The same instance also runs the disconnect-cleanup lifecycle (it is registered as a Core ISessionLifecycleListener), so a leaving player is pulled from their party whether they used the leave message or simply dropped their connection. Every Try* operation returns a PartyResult and, on failure, sends the caller a coded notification (a PartyNotificationCodes value); any membership change broadcasts an updated PartyRoster to the party room.

  • void BroadcastRoster(long partyId)

    Broadcasts the current roster of partyId to its room.

  • PartyResult TryAccept(ISession session, long partyId)

    Accepts a pending invite to partyId and joins that party.

  • PartyResult TryCreate(ISession session)

    Creates a party led by (and containing) the session, if it is not already in one.

  • bool TryGetPartyOf(long accountId, out IReadOnlyList<long> members)

    The members of accountId's party (donor-gap E2: the membership READ seam reward-sharing composition builds on — XP splits, party-aware loot, assist credit). False when the account is not in a party; members includes the account itself.

  • PartyResult TryInvite(ISession inviter, string targetName)

    Invites the online player named targetName to the inviter's party, sending them a PartyInviteNotification.

  • PartyResult TryKick(ISession session, long targetAccountId)

    Kicks a member from the caller's party (leader only). The target — online or offline — is removed; an online target is notified with Kicked.

  • PartyResult TryLeave(ISession session)

    Removes the session from its current party (the explicit leave).

  • PartyResult TryReady(ISession session, bool ready)

    Records the caller's ready answer for the active round and broadcasts the updated PartyReadyState (which is the client's feedback).

  • PartyResult TryStartReadyCheck(ISession session)

    Starts a ready check across the party (leader only; a new round replaces any active one). Broadcasts a PartyReadyCheck plus the initial PartyReadyState.

Configuration 1

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

PartyOptions

Configurable party rules, passed to AddCrossplayParty. Defaults live here, never inline in handlers/services (the no-hardcoded-values rule).

  • int MaxMembers { get; set; }

    Maximum members in a party, leader included. Invites past this cap are rejected with PartyFull.

Wire messages 17

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

1201 PartyCreateRequest

Client -> server: create a new party led by the caller.

1202 PartyCreateResponse

Server -> client: result of a PartyCreateRequest.

1203 PartyInviteRequest

Client -> server: invite a player (by display name) to the caller's party.

1204 PartyInviteResponse

Server -> client: result of a PartyInviteRequest.

1205 PartyInviteNotification

Server -> client: the invited player is told about a pending invite.

1206 PartyAcceptRequest

Client -> server: accept a pending invite to a party.

1207 PartyAcceptResponse

Server -> client: result of a PartyAcceptRequest.

1208 PartyLeaveRequest

Client -> server: leave the caller's current party.

1209 PartyLeaveResponse

Server -> client: result of a PartyLeaveRequest.

1210 PartyRoster

Server -> party members: the current roster. Sent to the party room on every membership change and as the reply to a successful create/accept.

1211 PartyKickRequest

Client → server: kick a member (leader only).

1212 PartyKickResponse

Server → client: kick result.

1213 PartyReadyCheckStartRequest

Client → server: start a ready check (leader only; replaces any active round).

1214 PartyReadyCheckStartResponse

Server → client: ready-check start result.

1215 PartyReadyCheck

Server → client: a ready check began (broadcast to the party).

1216 PartyReadyResponse

Client → server: my ready answer.

1217 PartyReadyState

Server → client: the round state after every answer (broadcast to the party).

Unity services you inject 1

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

IPartyClient

Data-layer party service (no UI): async create/invite/accept/leave RPCs plus push events for inbound invites and roster changes. Self-registers under the Crossplay client context.

  • event Action<PartyInviteNotification> InviteReceived

    Raised when this client receives a pending party invite.

  • event Action<PartyRoster> RosterChanged

    Raised whenever the party roster changes (server push on every membership change).

  • UniTask<PartyCreateResponse> CreateAsync(CancellationToken ct = default)

    Creates a new party led by the caller.

  • UniTask<PartyInviteResponse> InviteAsync(string targetName, CancellationToken ct = default)

    Invites a player (by display name) to the caller's party.

  • UniTask<PartyAcceptResponse> AcceptAsync(long partyId, CancellationToken ct = default)

    Accepts a pending invite to the given party.

  • UniTask<PartyLeaveResponse> LeaveAsync(CancellationToken ct = default)

    Leaves the caller's current party.

  • UniTask<PartyKickResponse> KickAsync(long targetAccountId, CancellationToken ct = default)

    Kicks a member (leader only).

  • UniTask<PartyReadyCheckStartResponse> StartReadyCheckAsync(CancellationToken ct = default)

    Starts a ready check (leader only).

  • void Ready(bool ready)

    Answers the active ready check. Fire-and-forget — the broadcast state is the feedback.

  • event Action<PartyReadyCheck> ReadyCheckStarted

    Raised when a ready check begins.

  • event Action<PartyReadyState> ReadyStateChanged

    Raised as members answer (Complete/AllReady flags close the round).

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.

ICrossplayPartyView

Narrow contract the presenter drives. UI-only: render calls in, user intents out.