Parties
Parties let friends queue together. They are optional: skip this page if your game only does solo searches. For how parties work on the platform, including team placement, see Parties; this page is the Unity side.
Parties need two things before they work:
- Turn parties on for the game mode in the dashboard (see Game Modes).
- Connect before you search. A party is formed over the connection, so you open it first with
ConnectAsyncinstead of lettingBeginSearchAsyncopen it.
Needs SDK 1.2.0
Parties arrived in version 1.2.0 of the SDK, which implements version 1.1.0 of the API Specs.
The flow
await _matchmaking.ConnectAsync("eu-west-2"); // open the connection, don't search yet
await _matchmaking.CreatePartyAsync(); // you are the leader
// ...share party.JoinCode with your friends...
await _matchmaking.BeginSearchAsync("3v3"); // leader only: queues the whole partyA friend, connected the same way, joins with the code:
await _matchmaking.ConnectAsync("eu-west-2");
await _matchmaking.JoinPartyAsync("K7M2QX");BeginSearchAsync on a connection opened with ConnectAsync searches on that same connection, so
the party stays intact.
Use the same region for everyone
Every member of a party needs a ticket for the same region. Pass the same region to ConnectAsync
for the whole group, rather than leaving it to automatic selection. The region is passed to your
ticket endpoint as preferredRegion.
Party methods
| Method | Who | Effect |
|---|---|---|
ConnectAsync(region) | anyone | Opens the connection without searching. State becomes Connected. |
CreatePartyAsync() | anyone | Creates a party with you as leader. |
JoinPartyAsync(code) | anyone | Joins the party with that join code. |
InviteToPartyAsync(userId) | leader | Invites a connected player. They receive OnPartyInvite. |
AcceptInviteAsync(partyId) | invitee | Joins the inviting party. |
DeclineInviteAsync(partyId) | invitee | Discards the invite. Optional: invites expire after two minutes. |
LeavePartyAsync() | any member | Leaves the party. |
KickMemberAsync(userId) | leader | Removes a member. |
PromoteLeaderAsync(userId) | leader | Hands leadership to another member. |
Each call returns once the request is sent. The result arrives as an event: a PartyState on
OnPartyUpdated, or an error on OnError. The party methods throw InvalidOperationException if
there is no open connection, and ConnectAsync throws if it can't get a ticket or open the
connection.
Showing the party
OnPartyUpdated always carries the full party, so render from it instead of patching your UI:
_matchmaking.OnPartyUpdated.AddListener(party =>
{
codeLabel.text = party.JoinCode;
memberList.text = string.Join("\n", party.Members.Select(m =>
m.UserId == party.LeaderUserId ? m.UserId + " (leader)" : m.UserId));
searchButton.interactable = party.PartyStatus == PartyStatus.Lobby && IsLeader(party);
});
_matchmaking.OnPartyLeft.AddListener(left => ShowMainMenu());_matchmaking.Party holds the latest state, or null when you aren't in a party. Like the events,
read it on Unity's main thread.
PartyState has PartyId, JoinCode, LeaderUserId, Members (each with a UserId),
PartyStatus and, while a ping check is running, GameMode. PartyStatus is:
| Status | Meaning |
|---|---|
Lobby | Forming up. Members can join and leave. |
Starting | A ping-based game mode is measuring each member's latency. Membership is locked. |
Searching | Queued. Membership is locked. |
Knowing whether you are the leader
LeaderUserId and each member's UserId are the userId values your backend chose when it
minted the tickets. The SDK doesn't see them, so to tell whether the local player leads the party,
keep their userId on the client (it's usually their account ID) and compare:
bool IsLeader(PartyState party) => party.LeaderUserId == _myUserId;Inviting a player
The leader invites by userId, and the invitee accepts with the PartyId from the invite:
await _matchmaking.InviteToPartyAsync("bob"); // leader
_matchmaking.OnPartyInvite.AddListener(invite => // bob's client
{
ShowInvitePrompt(invite.FromUserId, accept: () =>
_ = _matchmaking.AcceptInviteAsync(invite.PartyId));
});The invited player must be connected, in your own tenant, and not already in a party. If the invite
can't be delivered you get PARTY_INVITEE_UNAVAILABLE, which deliberately doesn't say whether the
player is offline or just busy. Invites expire after two minutes.
Searching as a party
Only the leader calls BeginSearchAsync. It queues every member at once, and the other members
don't call it at all. When the party is queued, every member's OnSearchStarted fires and their
state becomes Searching; the SDK then sends their heartbeats for them.
_matchmaking.OnSearchStarted.AddListener(started =>
Debug.Log($"Queued in {started.GameMode}, party {started.PartyId}"));In a UI, enable Search and Cancel only for the leader. A member who calls BeginSearchAsync gets
NOT_PARTY_LEADER, and the SDK drops back out of Searching.
CancelSearchAsync()from the leader stops the search for everyone.- If a member leaves, is kicked or disconnects mid-search, the search is cancelled for everyone.
The remaining members receive
OnPartySearchCancelledwith aReasonofLEADER_CANCELLED,MEMBER_LEFT,MEMBER_KICKED,MEMBER_DISCONNECTEDorSTART_FAILED. - While the party is
StartingorSearching, nobody can join and the leader can't invite. - For ping-based game modes there is a short extra step first, with each member's SDK answering the
ping check automatically. If a member doesn't answer within 10 seconds the party quietly returns
to
Lobby, so have the leader search again.
After a match
Each player receives OnMatchFound as usual, and the party returns to Lobby on the same
connection, so the same group can search again. The state returns to Connected. Don't call
CloseAsync() if the party should stay together: closing the connection removes the player from
the party. See After a Match.
When the connection drops
A search started with BeginSearchAsync alone reconnects for you. A connection opened with
ConnectAsync does not: the server removes a disconnected player from their party straight away,
so there is nothing to resume. The state becomes Closed and Party becomes null. Call
ConnectAsync again and rejoin with the join code or a new invite.
An idle connection can also be closed by the platform after about ten minutes. If your party sits in
the lobby for long periods, expect to handle Closed and offer to rebuild it.
Errors
Party errors arrive on OnError, never close the connection, and are not terminal:
| Code | Meaning |
|---|---|
ALREADY_IN_PARTY / NOT_IN_PARTY | You tried to create or join while in a party, or act on one while in none. |
PARTY_NOT_FOUND | The join code or party doesn't exist. |
PARTY_MEMBER_NOT_FOUND | The userId isn't in your party. |
PARTY_FULL | The party is at the game mode's party size limit. |
PARTY_SEARCHING | The party is searching or starting, so it can't change. |
NOT_PARTY_LEADER | Only the leader can do that. |
PARTIES_NOT_ENABLED | Parties are off for this game mode. |
PARTY_TOO_LARGE | The party is bigger than the game mode allows. |
PARTY_MEMBER_ATTRIBUTE_MISSING | A member's ticket lacks an attribute the game mode needs. Details names the member and attribute. |
PARTY_CHANGED | The party changed while the request ran. Retry. |
PARTY_INVITEE_UNAVAILABLE | The invite couldn't be delivered. |
PARTY_INVITE_NOT_FOUND | The invite expired or doesn't exist. |
Each has a matching PairiquiumErrorCode value, such as PairiquiumErrorCode.NotPartyLeader.
A worked example
The Arena Duel sample has a party panel: create a party, join by code, invite a player, and search together. See Samples & Troubleshooting.
Next
See what the SDK does for you without any code: Automatic Behaviour.