Turn-Based Multiplayer

In a turn-based multiplayer game, a single shared state is passed between multiple players, and only one player has permission to modify the shared state at a time. Players take turns asynchronously according to an order of play determined by the game. Your game can use the turn-based multiplayer API provided by Play Game services to manage the following tasks:

  • Invite players to join a turn-based multiplayer match, look for random players to be automatically matched to your game, or a combination of both. Play Game services allows you to host up to eight participants in a match.
  • Store participant and match state information on Google's servers and share updated match data asynchronously with all participants over the lifecycle of the turn-based match.
  • Send match invitation and turn notifications to players. Notifications appear on all devices on which the player is logged in (unless disabled).

Turn-based match basics

A turn-based match is a gaming session with multiple participants who take consecutive turns to update the game data during the match. Matches must be initiated by a player who is signed-in to Google+. Your game can use the turn-based multiplayer API to join up to eight Google+ players together in a match, including the initiating player and any auto-matched players. Matches take place asynchronously and participants do not need to be simultaneously connected to Play Game services to play.

A turn-based multiplayer match contains these key properties:

  • Participants. A user can become a participant in a turn-based match by initiating a match, joining a match by accepting an invitation, or being auto-matched into a game. Your game can retrieve the participant IDs for all players in a match.
  • Game data. Game-specific data for this match. As a match progresses, the current player can modify and store the game data on Google's servers. The other participants can then retrieve and update this data on their turn. Your game must store the game data in an appropriate format for the device platform. For example, on Android, you must store this data in a byte array and the size of the data must not exceed 128 KB.
  • Match state. A match can fall into one of the following states: ACTIVEAUTO_MATCHINGCOMPLETECANCELED, and EXPIRED, depending on participant and game actions during the match. The state of a match is managed by Play Game services. Your game can check the match state to determine whether a match can proceed, whether players can join by auto-match, and if the match is over (and if it completed normally or ended because of some user action).

Participants

If your game runs on a mobile device, the turn-based multiplayer API provides a default player selection UI. The UI allows players to invite friends in their Google+ circles or select a number of auto-match opponents. This simplifies your UI coding but you can also choose to implement your player selection UI.

The participants in a turn-based match can fall into one of these categories:

  • Initiating player. An initiating player who starts a turn-based game can send invitations to other Google+ users to join a match, or request to be auto-matched to random Google+ users. The initiating player can also request a mix of the two (for example, one specific invitee from their Google+ circles, and two auto-matched players).
  • Auto-matched players. Auto-matched players do not receive notifications to join a match; from their perspective, it appears that they are the ones initiating the match. Auto-match players do not need to be in each other's Google+ circles.
  • Invited players. Users who are invited to a match will receive invitation notifications on their device, as long their notification settings permit it. To learn more about how users can modify their notification settings by using the Google Play Games app, see Gaming with Google Play Games. Once the user accepts a match invitation, the user is joined to the match as a participant.

Note: In general, when a participant leaves a match, another player cannot take that participant’s place. If a player joins by auto-matching but leaves without taking a turn immediately, Play Game services considers that player to have abandoned the match and frees the auto-match slot for another player to join.

Turn-taking

The basic flow for turn-taking is as follows:

  1. If the player initiated a match, check to see if the game data is null. If so, the client should initialize this data as needed by your game. For example, you might need to initialize the starting positions for players in a strategy game or initialize the first hand for players in a card game.

  2. Let the current player perform their normal turn.

  3. Update the match. On Android, call takeTurn(). On iOS, the equivalent method is takeTurnWithNextParticipantId:. In your method call, pass in the following information:

    • The state of the match, after the current player performs the turn.
    • The player to take the next turn. To learn more about specifying the next player, see Specifying the order of play.
  4. Repeat steps 2-3 until the match is completed, or until the game ends some other way.

Specifying the order of play

The order of play is determined by the design of your game. The turn-based multiplayer API provided by the Play Game services allows your game to flexibly specify the order of play during a match.

When updating the match, your game can assign one of these players to take the next turn:

  • The current player who just took a turn.
  • Another player who has been invited to join the match, or who has already joined the match.
  • An auto-match player who has not joined the match (if there are vacant auto-match slots open). In this case, the match is suspended until a player joins.

For example, in a 2-player turn-based match, your game might randomly pick one player to take the first turn, then subsequently switch to the other player for the next turn, and so on.

Notifications

If you are using the turn-based multiplayer APIs on mobile devices, Play Game services sends these types of notifications:

  • Match invitation. Players receive this notification when they have been invited to join a match and are assigned to play their first turn by the game.
  • Turn notification. Players receive this notification when they have already joined the match and the game assigns them to be the next player.
  • Match event notification. Game clients that are joined to the same match receive these notifications whenever a match event occurs (for example, when a match is initiated, updated, or, canceled).

On Android and iOS devices, you can bring up the match list user interface provided by the Play Game services SDK to allow players to view the list of all matches that they are participating in.

Match states

The following table lists the possible states of a turn-based match:

Match State Description
ACTIVE Indicates that a new match is created and your game can let match participants take their turns.
AUTO_MATCHING This state indicates that there are still empty auto-match slots available and your game is waiting for an auto-matched player to join. Your game cannot let other match participants take turns while the match is in this state.
COMPLETE Indicates that a match has been played to completion (for example, a player has won the match). Once a player signals that the game is finished, Play Game services sends a notification to all match participants to inform them that the match is over. Players have a chance to save their final game data but may not modify the game state further.
CANCELED Indicates that a match ended before its normal completion. This might occur, for example, if a user who was invited to the match declined the invitation or if a participant explicitly cancels the match. Play Game services allows participants to cancel the match at any point after joining a match (if you game interface supports this action).
EXPIRED Indicates that a match has expired. A match expires when a user does not respond to an invitation or turn notification for two weeks. A match also expires in one day if there is an empty auto-match slot available but Play Game services cannot find a Google+ user to auto-match.

The callouts in Figure 1 describe how transitions occur between match states:

  • A: After the first player takes a turn, your game can specify a pending participant to take the next turn. If the pending participant is not null, Play Game services sends a turn notification to the pending participant, and the match stays in ACTIVE state.
  • B: The match is played to completion. Each participant can retrieve the match data but may not modify the game state further.
  • C: This transition occurs when a player declines the match invitation, a participant cancels the match, or a participant leaves the match with only one other participant remaining.
  • D: A player does not respond to a match or turn notification for two weeks. For example, this might occur when a player dismisses the match from the match user interface.
  • E: The match transitions from AUTO_MATCHING to ACTIVE state whenever a player joins by auto-match. Conversely, the match transitions from ACTIVE to AUTO_MATCHING when you specify null as the pending participant. You might do this when you want to suspend the match until a new player joins by auto-match. There must be at least one empty auto-match slot available. A match in AUTO-MATCHING state stays in the same state if a match participant leaves the match but there are still other participants remaining.
  • F: This transition occurs when Play Game services cannot find a player to auto-match after a day.
  • G: This transition occurs when the game is waiting for auto-match players to join and a match participant cancels the match.

Implementation

Before you start to code your turn-based multiplayer game:

Once the player is signed in and connected to play service, your game can start using the turn-based multiplayer API.

GooglePlayConnection.State == GPConnectionState.STATE_CONNECTED

 

Starting a match

To begin a turn-based match, your game can prompt players to select how they want to be matched to other participants or build the match using some default criteria. Play Game services uses the match configuration data from your game to return a  GP_TBM_Match object to your game; this object is updated and shared asynchronously with all participants over the lifecycle of the turn-based match.

Selecting players with the default user interface

Play Game services provides a default player selection UI that lets players select their friends to invite to a match or select to be auto-matched with random players. To start a match from the default player selection UI call StartSelectOpponentsView method. The game will display a default player selection UI that prompts the user to select match invitees  for auto-matching.  Play Game services uses this configuration data to determine whether to build a new match or to auto-match the user to an existing game.

Use GooglePlayTBM class to manage your TBM implementation.

int minPlayers = 2;
int maxPlayers = 2; 
bool allowAutomatch = true;

GooglePlayTBM.Instance.StartSelectOpponentsView (minPlayers, maxPlayers, allowAutomatch);

Important: minPlayers and maxPlayers parameters are amounts of players, that will be inveted to match excluding you. For example, if you want to create match for you and two of your friends, than you have to set min and maxPlayers equals 2. So, overall match will contains 3 players - you and 2 of your friends/opponents.

Next, add a callbacks for ActionMatchCreationCanceled and ActionMatchInitiated actions.  Follwing code snipped wll show how to subscribe for this actions. To see how to implemented the ActionMatchInitiated  action , see Taking the first turn

public void findMatch() {
	GooglePlayTBM.ActionMatchCreationCanceled += ActionMatchCreationCanceled;
	GooglePlayTBM.ActionMatchInitiated += ActionMatchInitiated;

	int minPlayers = 2;
	int maxPlayers = 2; 
	bool allowAutomatch = true;

	GooglePlayTBM.Instance.StartSelectOpponentsView (minPlayers, maxPlayers, allowAutomatch);
}

void ActionMatchCreationCanceled (AndroidActivityResult result) {
	// Match Creation was cnaceled by user
}

void ActionMatchInitiated (GP_TBM_MatchResult result) {
	// Match Initilized
}

 

Optionally, you might want to ensure that only players who are interested in a specific type of game variant are auto-matched together. If there are different versions of your app, your game can also use variants to ensure that only players who are on compatible versions are auto-matched. To specify a variant when creating a turn-based match, use the SetVariant method.

int variant = 1;
GooglePlayTBM.Instance.SetVariant (variant);

 

Your game can also use the exclusiveBitMask parameter to pair auto-matched players who are interested in playing specific exclusive roles in a game.

int ROLE_WIZARD = 0x4; // 100 in binary
GooglePlayTBM.Instance.SetExclusiveBitMask (ROLE_WIZARD);

 

Taking the first turn

if the GP_TBM_MatchResult result is successful, Play Game services returns a GP_TBM_Match object inside the  GP_TBM_MatchResult result  to signal that the player can take a turn. Before proceeding, remember to initialize the game data as needed by your game. For example, you might need to initialize the starting positions for players in a strategy game or initialize the first hand for players in a card game.

To implement the first turn, follow these steps:

  1. Retrieve the  GP_TBM_Match object from the GP_TBM_MatchResult object, then call Data getter

    •  If the call returns a null value, this indicates that the game data is uninitialized because no player has taken a turn yet (that is, the current player is the first player in this match). Your game can initialize the game data and store it in a byte array. The size of the game data must be less than128 KB.
    • If the call returns a non-null value, this indicates that the game has already started and the game data is already initialized, so make sure your game does not reinitialize the data.
  2. Optionally, call GooglePlayTBM.Instance.TakeTrun(...) now. If your initial state is based on a random value (for example, a card game where the player starts with a random hand) and your game does not call GooglePlayTBM.Instance.TakeTrun(...)   to persist the data to Google's servers, the player could keep restarting the first turn to try to get a better starting position.
  3. Let the player perform game actions, and if appropriate, update the byte array containing the game data.
  4. Save the game data to Google’s servers by calling  GooglePlayTBM.Instance.TakeTrun(...). Your game can specify the next player's participant ID, or specify null to let Play Game services find a player for auto-matching. Your game can let match participants take turns even when not all invited players have joined or when there are auto-match slots still available. To get players into gameplay faster, your game should let players take their turn as soon as they join. When you call  GooglePlayTBM.Instance.TakeTrun(...)., Play Game services sends a notification to the pending participant and updates the turn data on all participants' devices asynchronously.
  5. When the next player takes a turn, make sure to load the initialized data by calling Data getter  before you let the player perform game actions.

The following snippet shows how you might initialize a match and let the first player take a turn.

void ActionMatchInitiated (GP_TBM_MatchResult result) {
	if(!result.isSuccess) {
		AndroidMessage.Create("Match Initi Failed", "Status code: " + result.response);
		return;
	}

	GP_TBM_Match  Match = result.Match;
	// If this player is not the first player in this match, continue.
	if (Match.Data != null) {
		showTurnUI(match); 
		return;
	}

	// Otherwise, this is the first player. Initialize the game state.
	initGame(match);
	
	// Let the player take the first turn
	showTurnUI(match);
}

 

Handling invitations

The invitation system is almost the same for the real-time and turn-based matches, and described in this article.

Taking a turn

After the first player takes a turn, your game can let the same participant or another participant take the next turn. Generally, turn-taking involves loading the most recent match data from Play Game services, letting the participant interact with your game, and calling GooglePlayTBM.Instance.TakeTrun(...) to update the match data and pass the turn to another participant.

To implement turn-taking, follow these steps:

  1. Optionally, if you want be notified whenever any participant in the match takes a turn, register Match Update Listener using  GooglePlayTBM.Instance.RegisterMatchUpdateListener()  method. Whenever the match is updated following a player's turn, your will be notifayed via ActionMatchReceived and ActionMatchRemoved actions. 
  2. In order for a participant to take a turn, the match must be in active state and it must be that participant's turn. Your game can use the GP_TBM_Match object to verify this.
    • Check the match state with Status getter. A result of MATCH_STATUS_ACTIVE indicates that the match is active.
    • Check the match turn status with TurnStatus  getter. A result of MATCH_TURN_STATUS_MY_TURN indicates that it is now the user's turn.
  3. Load the most recent match data from Play Game services using Data getter and render your game accordingly.
  4. Let the user perform game actions and, if appropriate, persist the updated game data to a byte array.
  5. Determine the pending participant who should take the next turn. This is usually dependent on the order of play specified by your game design. The pending participant can be another player in the match or the current participant whose turn it is. Your game can also pass the turn to the next player joining by auto-match, by setting the pending participant to null.
  6. Cal lGooglePlayTBM.Instance.TakeTrun(...) to update the Play Game services with the latest game data and pass the turn to the pending participant. Play Game services returns the status of your match update operation in an GP_TBM_MatchResult  object. If the update is successful, Play Game services sends a notification to the pending participant to inform them that it's their turn.

To take a turn, use this method:

Method Description
TakeTrun(...) Your game should only invoke this method when it is the user's turn in an active turn-based match. After invoking this method, the user sees this match under the Their Turn list in the match list UI.
  • If a participant ID is specified in the method call, that participant becomes the current player and can play a turn. The first time this happens to an invited player, the player receives an invitation notification. Subsequently, the player receives a turn notification.
  • If no pending participant ID is specified, the match state changes to MATCH_STATUS_AUTO_MATCHING. Your game can only leave the participant ID unspecified if there are player slots that are still vacant for auto-matching.
  • Your game can call GooglePlayTBM.Instance.TakeTrun(...)  with the current player as the pending participant. In this case, Play Game services uploads the the game data but no notification is sent.

 

The following snippet shows how you might implement the current player's turn

private GP_TBM_Match mMatch;

...

public void Init() {
	GooglePlayTBM.ActionMatchUpdated += ActionMatchUpdated;
}

// Call this method when a player has completed his turn and wants to
// go onto the next player, which may be himself.
public void playTurn() {
	string nextParticipantId;

	// Get the next participant in the game-defined way, possibly round-robin.
	nextParticipantId = getNextParticipantId();


	// Get the updated state. In this example, we simply use a
	// a pre-defined string. In your game, there may be more complicated state.
	string mTurnData = "My turn data sample";

	// At this point, you might want to show a waiting dialog so that
	// the current player does not try to submit turn actions twice.
	AndroidNativeUtility.ShowPreloader();

	// Invoke the next turn. We are converting our data to a byte array.

	System.Text.UTF8Encoding  encoding = new System.Text.UTF8Encoding();
	byte[] byteArray = encoding.GetBytes(mTurnData);

	GooglePlayTBM.Instance.TakeTrun(mMatch.Id, byteArray, nextParticipantId);
}

void ActionMatchUpdated (GP_TBM_MatchResult result) {
	// Match Date updated
}

Saving game state

You can use the turn-based multiplayer API to manage your game state if your game state data fits within the allowed size 128 KB.

To save game state with the turn-based multiplayer API:

  1. Call GooglePlayTBM.Instance.TakeTrun(...)   and pass in your game state data as the matchData parameter.
  2. If the call is successful, Play Game services notifies other participants in the match about the update and makes the match data available on all participant devices.
  3. Your game can then use Data getter to retrieve the updated game state.

Warning: Using the Saved Games API together with the turn-based multiplayer API could cause version conflicts because the save game and match data are not updated synchronously. To avoid these conflicts in your game, pick and use just one API to save your game state.

Your game should try to save game data for a partially-finished turn whenever a player's turn is interrupted and the player has to temporarily leave the game (for example, because of an incoming phone call). To do this, use OnApplicationPause() callback  to call GooglePlayTBM.Instance.TakeTrun(...). Make sure to specify the current player as the pending participant by using the same participant ID as in the last call to GooglePlayTBM.Instance.TakeTrun(...). If successful, the call stores the game data in Google’s servers but does not generate a new turn notification.

Completing a match

When the match has been played to completion (for example, a user has won the game), your game should call GooglePlayTBM.Instance.FinishMatch(...) to upload the user’s game data and signal to the other participants that the match is over. When your game invokes this method for the first time during a match, it must be during the user's turn. The match then appears under the Completed Matches category in the user's match list UI.

Once a player calls GooglePlayTBM.Instance.FinishMatch(...)  for the first time in the match, your game cannot call GooglePlayTBM.Instance.TakeTrun(...) again in this match. Play Game services sends a notification to all other match participants to inform them that the match is over. These participants see this match under Your Turncategory in their respective match list UIs. At this point, your game can call GooglePlayTBM.Instance.FinishMatch(...) for these participants to save their final game data. Invoking this method also moves the match to the Completed Matches category in the participant’s match list UI.

To complete a match, use this method:

Method Description
FinishMatch(...)  Calling this method changes the match status changes to MATCH_STATUS_COMPLETE. After FinishMatch(...)  is called for the first time in the match, Play Game services sends a notification to the other match participants to inform them that the match is over.

You can obtain the result by subscribing to the ActionMatchUpdated  action. 

public static Action<GP_TBM_MatchResult> ActionMatchUpdated =  delegate{};

 

Leaving a match

Participants can choose to leave at any time during the match, while allowing the match to continue. To signal that a participant is leaving, your game should call either GooglePlayTBM.Instance.LeaveMatch(...)  or GooglePlayTBM.Instance.LeaveMatchDuringTurn(...) . When a participant leaves a match, the match can still continue with other participants as long as these conditions are met:

  • There are two or more other remaining participants, or
  • There is one remaining participant and at least one empty auto-match slot available.

Otherwise, the match is canceled.

In general, when a participant leaves a match, another player cannot join and take that participant’s place. One exception is when a player who joined by auto-match and who has not taken a turn calls GooglePlayTBM.Instance.LeaveMatchDuringTurn(...). In this case, the match reverts to the MATCH_STATUS_AUTO_MATCHING state and another player can take over the place of the participant who left.

To leave a match, your game can call these methods:

Method Description
LeaveMatch(...)  Removes the match from all GoogleApiClient objects registered to this user. You should only invoke this method when it is not the user's turn yet in an active match. Calling this method changes the user's participant status to STATUS_LEFT. If only one more participant remains, the match status changes to MATCH_STATUS_CANCELED.
LeaveMatchDuringTurn(...) Removes the match from all GoogleApiClient objects registered to the user. You should only invoke this method on the user's turn in an active match. Calling this method changes the user's participant status to STATUS_LEFT.
  • If only one more participant remains, the match status changes to MATCH_STATUS_CANCELED.
  • If a participant ID is specified in the method call, that participant becomes the current player and can play a turn. The first time this happens to an invited player, the player receives an invitation notification. Subsequently, the player receives a turn notification.
  • If no pending participant ID is specified, the match state changes to MATCH_STATUS_AUTO_MATCHING. Your game can only leave the participant ID unspecified if there are empty auto-matching slots that are still available.

You can obtain the result by subscribing to the ActionMatchLeaved action. 

public static Action<GP_TBM_MatchResult> ActionMatchLeaved =  delegate{};

Canceling a match

Your game can end a match for all participants before the match is played to normal completion, by calling GooglePlayTBM.Instance.CancelMatch(...) . After calling this method, your game cannot call GooglePlayTBM.Instance.TakeTrun(...) again in this match; the match now appears under the Completed Matches category in the match list UI.

To cancel a match, use this method:

Method Description
CancelMatch(...) Removes the match from all GoogleApiClient objects registered to the user. You should only invoke this method on the user's turn in an active match. Calling this method changes the user's participant status to STATUS_LEFT and the match status changes to MATCH_STATUS_CANCELED.

You can obtain the result by subscribing to the ActionMatchCanceled  action which uses GP_TBM_CancelMatchResult object. 

public static Action<GP_TBM_CancelMatchResult> ActionMatchCanceled =  delegate{};

Dismissing a match

Your game can let users dismiss a turn or invitation so that they do not have to see the match again. This hides the match from the dismisser's match list UI and causes the match to eventually expire. Other match participants can continue to play until the dismissed match expires after two weeks, or until the match is played to completion or canceled (whichever happens first). To other participants, the dismisser still appears as a participant in the match. Another player cannot take the dismisser's place.

To dismiss a match, use this method:

Method Description
DismissMatch(...) Removes the match from all GoogleApiClient objects registered to the user. The user's participant status is not changed.

Tracking match expiration

A match can expire if a player does not respond to a match or turn notification for two weeks (for example, after a player whose turn it is selects Dismiss in the default match list UI). You can use the following approach on the game client to identify which player caused a game to expire:

  1. Load a list of the currently signed-in player’s list of games by calling GooglePlayTBM.Instance.LoadMatchesInfo(...) or GooglePlayTBM.Instance.LoadAllMatchesInfo(...) .
  2. Next, get matches data from GP_TBM_LoadMatchesResult object, which will be delivered wiht the ActionMatchesResultLoaded action.  to get a list of matches in the MATCH_TURN_STATUS_COMPLETE state.
  3. Then, filter that list for matches in the MATCH_STATUS_EXPIRED state.
  4. If a match participant has STATUS_UNRESPONSIVE state, it indicates that this player has let the game expire.

 Implementing a rematch

When a match is over, a participant may want to play a rematch with the same set of invited and auto-match opponents. Your game can initiate a rematch by invoking GooglePlayTBM.Instance.Rematch(...) . Your game can only invoke this method when the match state is MATCH_STATUS_COMPLETE and no other participants have requested a rematch. If the call is successful, Play Game services sends invitation notifications to all rematched opponents.

To implement a rematch, use this method:

Method Description
Rematch(...) Creates a new turn-based match object with the same participants as the previous match. The user must then take a first turn. If another participant already requested a rematch, calling this method returns a STATUS_MATCH_ERROR_ALREADY_REMATCHEDerror.

 

If your game uses the default match inbox UI, players can also initiate rematches from that UI. When a player initiates a rematch from the match inbox UI, your game will  receive an GP_TBM_MatchResult object.