Real-time Multiplayer Support in Android Games

This guide shows you how to implement a real-time multiplayer game using the Google Play games services in an Android application. The APIs can be found in the com.google.android.gms.games.multiplayer, com.google.android.gms.games.multiplayer.realtime, and com.google.android.gms.games packages.

Before you begin

If you haven't already done so, you might find it helpful to review the real-time multiplayer game concepts.

Before you start to code your real-time multiplayer game:

Getting the real-time multiplayer client

To start using the real-time multiplayer API, your game must first obtain a RealTimeMultiplayerClient object. You can do this by calling the Games.getRealTimeMultiplayerClient() method and passing in the activity and the GoogleSignInAccount for the current player. To learn how to retrieve the player account information, see Sign-in in Android Games.

Starting a real-time multiplayer game

Your main screen is the player's primary entry point to start a real-time multiplayer game, invite other players, or accept a pending invitation. We recommend that at minimum you implement these UI components on the main screen of your game:

  • Quick Game button - Lets the player play against randomly selected opponents (via auto-matching).
  • Invite Players button - Lets the player invite friends to join a game session or specify some number of random opponents for auto-matching.
  • Show Invitations button - Lets the player see any pending invitations sent by another player. Selecting this option should launch the invitation inbox, as described in Handling invitations.

Quick Game option

When the player selects the Quick Game option, your game should create a virtual room object to join players, auto-match the player to randomly selected opponents without displaying the player picker UI, and immediately start the game.

If your game has multiple player roles (such as farmer, archer, and wizard) and you want to restrict auto-matched games to one player of each role, add an exclusive bitmask to your room configuration. When auto-matching with this option, players will only be considered for a match when the logical AND of their exclusive bit masks is equal to 0.

The following example shows how you can implement the quick game option and use the bit mask to perform auto matching with three exclusive player character roles.

private static final long ROLE_ANY = 0x0; // can play in any match.
private static final long ROLE_FARMER = 0x1; // 001 in binary
private static final long ROLE_ARCHER = 0x2; // 010 in binary
private static final long ROLE_WIZARD = 0x4; // 100 in binary

private void startQuickGame(long role) {
    // auto-match criteria to invite one random automatch opponent.
    // You can also specify more opponents (up to 3).
    Bundle autoMatchCriteria = RoomConfig.createAutoMatchCriteria(1, 1, role);

    // build the room config:
    RoomConfig roomConfig =
            RoomConfig.builder(mRoomUpdateCallback)
                    .setOnMessageReceivedListener(mMessageReceivedHandler)
                    .setRoomStatusUpdateCallback(mRoomStatusCallbackHandler)
                    .setAutoMatchCriteria(autoMatchCriteria)
                    .build();

    // prevent screen from sleeping during handshake
    getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

    // Save the roomConfig so we can use it if we call leave().
    mJoinedRoomConfig = roomConfig;

    // create room:
    Games.getRealTimeMultiplayerClient(this, GoogleSignIn.getLastSignedInAccount(this))
            .create(roomConfig);
}

Invite Players option

When the Invite Players option is selected, your game should launch a player picker UI that prompts the initiating player to select friends to invite to a real-time game session or select a number of random players for auto-matching. Your game should create a virtual room object using the player's criteria, then start the game session, once players are connected to the room.

To obtain the user's selection, your game can display the built-in player picker UI provided by Google Play games services or a custom player picker UI. To launch the default player picker UI, call the getSelectOpponentsIntent() method and use the Intent it returns to start a Activity.

private static final int RC_SELECT_PLAYERS = 9006;

private void invitePlayers() {
    // launch the player selection screen
    // minimum: 1 other player; maximum: 3 other players
    Games.getRealTimeMultiplayerClient(this, GoogleSignIn.getLastSignedInAccount(this))
            .getSelectOpponentsIntent(1, 3, true)
            .addOnSuccessListener(new OnSuccessListener<Intent>() {
                @Override
                public void onSuccess(Intent intent) {
                    startActivityForResult(intent, RC_SELECT_PLAYERS);
                }
            });
}

An example of the default player picker UI is shown below.

Your game receives the initiating player's criteria on the onActivityResult() callback. It should then create the room and set up listeners to receive notifications of room status changes or incoming messages.

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == RC_SELECT_PLAYERS) {
        if (resultCode != Activity.RESULT_OK) {
            // Canceled or some other error.
            return;
        }

        // Get the invitee list.
        final ArrayList<String> invitees = data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);

        // Get Automatch criteria.
        int minAutoPlayers = data.getIntExtra(Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
        int maxAutoPlayers = data.getIntExtra(Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);

        // Create the room configuration.
        RoomConfig.Builder roomBuilder = RoomConfig.builder(mRoomUpdateCallback)
                .setOnMessageReceivedListener(mMessageReceivedHandler)
                .setRoomStatusUpdateCallback(mRoomStatusCallbackHandler)
                .addPlayersToInvite(invitees);
        if (minAutoPlayers > 0) {
            roomBuilder.setAutoMatchCriteria(
                    RoomConfig.createAutoMatchCriteria(minAutoPlayers, maxAutoPlayers, 0));
        }

        // Save the roomConfig so we can use it if we call leave().
        mJoinedRoomConfig = roomBuilder.build();
        Games.getRealTimeMultiplayerClient(this, GoogleSignIn.getLastSignedInAccount(this))
                .create(mJoinedRoomConfig);
    }
}

Handling room creation errors

To be notified of errors during room creation, your game can use the RoomUpdateCallback class. If a room creation error occurred, your game should display a message to notify players and return to the main screen.

private RoomUpdateCallback mRoomUpdateCallback = new RoomUpdateCallback() {
    @Override
    public void onRoomCreated(int code, @Nullable Room room) {
        // Update UI and internal state based on room updates.
        if (code == GamesCallbackStatusCodes.OK && room != null) {
            Log.d(TAG, "Room " + room.getRoomId() + " created.");
        } else {
            Log.w(TAG, "Error creating room: " + code);
            // let screen go to sleep
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        }
    }

    @Override
    public void onJoinedRoom(int code, @Nullable Room room) {
        // Update UI and internal state based on room updates.
        if (code == GamesCallbackStatusCodes.OK && room != null) {
            Log.d(TAG, "Room " + room.getRoomId() + " joined.");
        } else {
            Log.w(TAG, "Error joining room: " + code);
            // let screen go to sleep
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        }
    }

    @Override
    public void onLeftRoom(int code, @NonNull String roomId) {
        Log.d(TAG, "Left room" + roomId);
    }

    @Override
    public void onRoomConnected(int code, @Nullable Room room) {
        if (code == GamesCallbackStatusCodes.OK && room != null) {
            Log.d(TAG, "Room " + room.getRoomId() + " connected.");
        } else {
            Log.w(TAG, "Error connecting to room: " + code);
            // let screen go to sleep
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

        }
    }
};

Connecting players

To be notified when all players are connected, your game can use the RoomUpdateCallback.onRoomConnected() method.

Your game can also use the RoomStatusUpdateCallback class to monitor the connection status of the participants. Based on the participant connection status, your game can decide whether to start or cancel the gaming session.

For example:

// are we already playing?
boolean mPlaying = false;

// at least 2 players required for our game
final static int MIN_PLAYERS = 2;

// returns whether there are enough players to start the game
boolean shouldStartGame(Room room) {
    int connectedPlayers = 0;
    for (Participant p : room.getParticipants()) {
        if (p.isConnectedToRoom()) {
            ++connectedPlayers;
        }
    }
    return connectedPlayers >= MIN_PLAYERS;
}

// Returns whether the room is in a state where the game should be canceled.
boolean shouldCancelGame(Room room) {
    // TODO: Your game-specific cancellation logic here. For example, you might decide to
    // cancel the game if enough people have declined the invitation or left the room.
    // You can check a participant's status with Participant.getStatus().
    // (Also, your UI should have a Cancel button that cancels the game too)
    return false;
}

private Activity thisActivity = this;
private Room mRoom;
private RoomStatusUpdateCallback mRoomStatusCallbackHandler = new RoomStatusUpdateCallback() {
    @Override
    public void onRoomConnecting(@Nullable Room room) {
        // Update the UI status since we are in the process of connecting to a specific room.
    }

    @Override
    public void onRoomAutoMatching(@Nullable Room room) {
        // Update the UI status since we are in the process of matching other players.
    }

    @Override
    public void onPeerInvitedToRoom(@Nullable Room room, @NonNull List<String> list) {
        // Update the UI status since we are in the process of matching other players.
    }

    @Override
    public void onPeerDeclined(@Nullable Room room, @NonNull List<String> list) {
        // Peer declined invitation, see if game should be canceled
        if (!mPlaying && shouldCancelGame(room)) {
            Games.getRealTimeMultiplayerClient(thisActivity,
                    GoogleSignIn.getLastSignedInAccount(thisActivity))
                    .leave(mJoinedRoomConfig, room.getRoomId());
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    }

    @Override
    public void onPeerJoined(@Nullable Room room, @NonNull List<String> list) {
        // Update UI status indicating new players have joined!
    }

    @Override
    public void onPeerLeft(@Nullable Room room, @NonNull List<String> list) {
        // Peer left, see if game should be canceled.
        if (!mPlaying && shouldCancelGame(room)) {
            Games.getRealTimeMultiplayerClient(thisActivity,
                    GoogleSignIn.getLastSignedInAccount(thisActivity))
                    .leave(mJoinedRoomConfig, room.getRoomId());
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    }

    @Override
    public void onConnectedToRoom(@Nullable Room room) {
        // Connected to room, record the room Id.
        mRoom = room;
        Games.getPlayersClient(thisActivity, GoogleSignIn.getLastSignedInAccount(thisActivity))
                .getCurrentPlayerId().addOnSuccessListener(new OnSuccessListener<String>() {
            @Override
            public void onSuccess(String playerId) {
                mMyParticipantId = mRoom.getParticipantId(playerId);
            }
        });
    }

    @Override
    public void onDisconnectedFromRoom(@Nullable Room room) {
        // This usually happens due to a network error, leave the game.
        Games.getRealTimeMultiplayerClient(thisActivity, GoogleSignIn.getLastSignedInAccount(thisActivity))
                .leave(mJoinedRoomConfig, room.getRoomId());
        getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        // show error message and return to main screen
        mRoom = null;
        mJoinedRoomConfig = null;
    }

    @Override
    public void onPeersConnected(@Nullable Room room, @NonNull List<String> list) {
        if (mPlaying) {
            // add new player to an ongoing game
        } else if (shouldStartGame(room)) {
            // start game!
        }
    }

    @Override
    public void onPeersDisconnected(@Nullable Room room, @NonNull List<String> list) {
        if (mPlaying) {
            // do game-specific handling of this -- remove player's avatar
            // from the screen, etc. If not enough players are left for
            // the game to go on, end the game and leave the room.
        } else if (shouldCancelGame(room)) {
            // cancel the game
            Games.getRealTimeMultiplayerClient(thisActivity,
                    GoogleSignIn.getLastSignedInAccount(thisActivity))
                    .leave(mJoinedRoomConfig, room.getRoomId());
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    }

    @Override
    public void onP2PConnected(@NonNull String participantId) {
        // Update status due to new peer to peer connection.
    }

    @Override
    public void onP2PDisconnected(@NonNull String participantId) {
        // Update status due to  peer to peer connection being disconnected.
    }
};

Optional: Adding a waiting room UI

We recommend that your game use a "waiting room" UI so that players can see the current status of the room as participants join and get connected. Your game can display the default waiting room UI (shown in the figure below) or a custom UI.

To launch the default waiting room UI, call the RealTimeMultiplayerClient.getWaitingRoomIntent() method and use the intent it returns to start an activity.

private static final int RC_WAITING_ROOM = 9007;

private void showWaitingRoom(Room room, int maxPlayersToStartGame) {
    Games.getRealTimeMultiplayerClient(this, GoogleSignIn.getLastSignedInAccount(this))
            .getWaitingRoomIntent(room, maxPlayersToStartGame)
            .addOnSuccessListener(new OnSuccessListener<Intent>() {
                @Override
                public void onSuccess(Intent intent) {
                    startActivityForResult(intent, RC_WAITING_ROOM);
                }
            });
}

Your game can launch the waiting room UI from the RoomUpdateCallback.onRoomConnected() and the RoomUpdateCallback.onJoinedRoom() methods.

When the waiting room UI is dismissed, your game receives the result through your activity's onActivityResult() callback. The reason for the dismissal is indicated in the responseCode callback parameter and can be one of the following:

Next, your game should handle the waiting room results in its onActivityResult() callback:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == RC_WAITING_ROOM) {

        // Look for finishing the waiting room from code, for example if a
        // "start game" message is received.  In this case, ignore the result.
        if (mWaitingRoomFinishedFromCode) {
            return;
        }

        if (resultCode == Activity.RESULT_OK) {
            // Start the game!
        } else if (resultCode == Activity.RESULT_CANCELED) {
            // Waiting room was dismissed with the back button. The meaning of this
            // action is up to the game. You may choose to leave the room and cancel the
            // match, or do something else like minimize the waiting room and
            // continue to connect in the background.

            // in this example, we take the simple approach and just leave the room:
            Games.getRealTimeMultiplayerClient(thisActivity,
                    GoogleSignIn.getLastSignedInAccount(this))
                    .leave(mJoinedRoomConfig, mRoom.getRoomId());
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        } else if (resultCode == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
            // player wants to leave the room.
            Games.getRealTimeMultiplayerClient(thisActivity,
                    GoogleSignIn.getLastSignedInAccount(this))
                    .leave(mJoinedRoomConfig, mRoom.getRoomId());
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    }
}

You can implement a different response depending on whether the user explicitly canceled the game (GamesActivityResultCodes.RESULT_LEFT_ROOM) or whether the user quit the waiting room UI (Activity.RESULT_CANCELED). For example, if the player dismisses the UI with the Back button, you can minimize the app and still continue the handshake process in the background. However, if the player chose the Leave Room button from the waiting room UI, you can cancel the handshake.

If you use the waiting room UI, you do not need to implement additional logic to decide when the game should be started or canceled. When you obtain an Activity.RESULT_OK result, you can start right away since the required number of participants have been connected. Likewise, when you get an error result from the waiting room UI, you can simply leave the room.

Starting the game before all players are connected

When creating the waiting room, your game can specify the minimum number of players required to start the game session. If the number of connected participants is more than or equal to the specified minimum to start the game, the system enables the Start Playing option in the waiting room UI. When the user clicks this option, the system dismisses the waiting room UI and delivers the Activity.RESULT_OK result code.

When a player clicks the Start Playing option, the waiting room UI is not automatically dismissed for the other players in the game. To dismiss the waiting room for the other players, your game should send a reliable real-time message to the other players to indicate that the game is starting early. When your game receives the message, it should dismiss the waiting room UI.

For example:

boolean mWaitingRoomFinishedFromCode = false;

private void onStartGameMessageReceived() {
    mWaitingRoomFinishedFromCode = true;
    finishActivity(RC_WAITING_ROOM);
}

The mWaitingRoomFinishedFromCode flag is necessary because dismissing the waiting room as shown above causes a result code of Activity.RESULT_CANCELED to be returned. You must differentiate this case from the case where the player has dismissed the waiting room using the back button:

Querying a participant's status

The Participant.getStatus() method returns the current status of the participant.

  • STATUS_INVITED: The participant has been invited but has not yet acted on the invitation.
  • STATUS_DECLINED: The participant has declined the invitation.
  • STATUS_JOINED: The participant has joined the room.
  • STATUS_LEFT: The participants has left the room.

Your game can also detect if a participant is connected by calling Participant.isConnectedToRoom().

Make sure to construct your game logic carefully to take each participant's status and connectedness into account. For example, in a racing game, to determine if all racers have crossed the finish line, your game should only consider the participants who are connected. Your game should not wait for all players in the room cross the finish line, because not all participants might be connected (for example, one or more players might have left the room or declined the invitation).

For example:

Set<String> mFinishedRacers;

boolean haveAllRacersFinished(Room room) {
    for (Participant p : room.getParticipants()) {
        String pid = p.getParticipantId();
        if (p.isConnectedToRoom() && !mFinishedRacers.contains(pid)) {
            // at least one racer is connected but hasn't finished
            return false;
        }
    }
    // all racers who are connected have finished the race
    return true;
}

Detecting when a player is disconnected

Your player might be disconnected from the room due to network connectivity or server issues. To be notified when the player is disconnected from the room, use the RoomStatusUpdateCallback.onDisconnectedFromRoom() method.

Handling invitations

Once the player has signed in, your game may be notified of invitations to join a room created by another player. Your game should handle invitations for these scenarios.

At player sign-in

If the signed-in player accepts an invitation from the notification area on the Android status bar, your game should accept the invitation and go directly to the game screen (skipping the main menu).

First, check if an invitation is available after the player signs in successfully. Use the GamesClient.getActivationHint() method to determine if there is an invitation to accept.

private void checkForInvitation() {
    Games.getGamesClient(this, GoogleSignIn.getLastSignedInAccount(this))
            .getActivationHint()
            .addOnSuccessListener(
                    new OnSuccessListener<Bundle>() {
                        @Override
                        public void onSuccess(Bundle bundle) {
                            Invitation invitation = bundle.getParcelable(Multiplayer.EXTRA_INVITATION);
                            if (invitation != null) {
                                RoomConfig.Builder builder = RoomConfig.builder(mRoomUpdateCallback)
                                        .setInvitationIdToAccept(invitation.getInvitationId());
                                mJoinedRoomConfig = builder.build();
                                Games.getRealTimeMultiplayerClient(thisActivity,
                                        GoogleSignIn.getLastSignedInAccount(thisActivity))
                                        .join(mJoinedRoomConfig);
                                // prevent screen from sleeping during handshake
                                getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
                            }
                        }
                    }
            );

}

If the device goes to sleep during handshake or gameplay, the player will be disconnected from the room. To prevent the device from sleeping during multiplayer handshake or gameplay, we recommend that you enable the FLAG_KEEP_SCREEN_ON flag in your activity's onCreate() method. Don't forget to clear this flag at the end of gameplay or when the game is canceled.

During gameplay

To be notified of incoming invitations, your game use the InvitationCallback class. Incoming invitations will not generate a status bar notification. Instead, the callback receives an Invitation object via the onInvitationReceived() method, and your game can then display an in-game popup dialog or notification to inform the user. If the user accepts, your game should process the invitation and launch the game screen.

private InvitationCallback mInvitationCallbackHandler = new InvitationCallback() {
    @Override
    public void onInvitationReceived(@NonNull Invitation invitation) {
        RoomConfig.Builder builder = RoomConfig.builder(mRoomUpdateCallback)
                .setInvitationIdToAccept(invitation.getInvitationId());
        mJoinedRoomConfig = builder.build();
        Games.getRealTimeMultiplayerClient(thisActivity,
                GoogleSignIn.getLastSignedInAccount(thisActivity))
                .join(mJoinedRoomConfig);
        // prevent screen from sleeping during handshake
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
    }

    @Override
    public void onInvitationRemoved(@NonNull String invitationId) {
        // Invitation removed.
    }
};

From the Invitation Inbox

The Invitation Inbox is an optional UI component that your game can display by using the Intent from InvitationsClient.getInvitationInboxIntent(). The Inbox displays all the available invitations that a player received. If the player selects a pending invitation from the Inbox, your game should accept the invitation and launch the game screen.

You can add a button to launch the Invitation Inbox from the main screen of your game.

To launch the inbox:

private static final int RC_INVITATION_INBOX = 9008;

private void showInvitationInbox() {
    Games.getInvitationsClient(this, GoogleSignIn.getLastSignedInAccount(this))
            .getInvitationInboxIntent()
            .addOnSuccessListener(new OnSuccessListener<Intent>() {
                @Override
                public void onSuccess(Intent intent) {
                    startActivityForResult(intent, RC_INVITATION_INBOX);
                }
            });
}

When the player selects an invitation from the Inbox, your game is notified via onActivityResult(). Your game can then process the invitation.

For example:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (requestCode == RC_INVITATION_INBOX) {
        if (resultCode != Activity.RESULT_OK) {
            // Canceled or some error.
            return;
        }
        Invitation invitation = data.getExtras().getParcelable(Multiplayer.EXTRA_INVITATION);
        if (invitation != null) {
            RoomConfig.Builder builder = RoomConfig.builder(mRoomUpdateCallback)
                    .setInvitationIdToAccept(invitation.getInvitationId());
            mJoinedRoomConfig = builder.build();
            Games.getRealTimeMultiplayerClient(thisActivity,
                    GoogleSignIn.getLastSignedInAccount(this))
                    .join(mJoinedRoomConfig);
            // prevent screen from sleeping during handshake
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
    }
}

Exchanging game data between clients

Please review Sending game data to familiarize yourself with the concepts behind data messaging using the real-time multiplayer API.

Sending messages

To send a message, your game can use either the RealTimeMultiplayerClient.sendReliableMessage() or RealTimeMultiplayerClient.sendUnreliableMessage() method, depending on the kind of message to send. Alternatively, your game can use the RealTimeMultiplayerClient.sendUnreliableMessageToOthers() method to send a broadcast message.

For example, to send a reliable message to all other participants:

void sendToAllReliably(byte[] message) {
    for (String participantId : mRoom.getParticipantIds()) {
        if (!participantId.equals(mMyParticipantId)) {
            Task<Integer> task = Games.
                    getRealTimeMultiplayerClient(this, GoogleSignIn.getLastSignedInAccount(this))
                    .sendReliableMessage(message, mRoom.getRoomId(), participantId,
                            handleMessageSentCallback).addOnCompleteListener(new OnCompleteListener<Integer>() {
                        @Override
                        public void onComplete(@NonNull Task<Integer> task) {
                            // Keep track of which messages are sent, if desired.
                            recordMessageToken(task.getResult());
                        }
                    });
        }
    }
}

When using sendReliableMessage(),you need to specify a RealTimeMultiplayerClient.ReliableMessageSentCallback as a parameter. If the sendReliableMessage() call is successful, the message is placed in an internal queue and a Task object is returned. The Task object provides a message token ID for the pending message. When the system actually sends the message, your game is notified through the RealTimeMultiplayerClient.ReliableMessageSentCallback.

For example:

HashSet<Integer> pendingMessageSet = new HashSet<>();

synchronized void recordMessageToken(int tokenId) {
    pendingMessageSet.add(tokenId);
}

private RealTimeMultiplayerClient.ReliableMessageSentCallback handleMessageSentCallback =
        new RealTimeMultiplayerClient.ReliableMessageSentCallback() {
            @Override
            public void onRealTimeMessageSent(int statusCode, int tokenId, String recipientId) {
                // handle the message being sent.
                synchronized (this) {
                    pendingMessageSet.remove(tokenId);
                }
            }
        };

Receiving messages

When your game receives a message, it is notified by the OnRealTimeMessageReceivedListener.onRealTimeMessageReceived() method. This method will be called whether it's a reliable or unreliable message. Be sure to register this listener when setting up your room configuration. This method will be called whether it's a reliable or unreliable message.

For example:

private OnRealTimeMessageReceivedListener mMessageReceivedHandler =
        new OnRealTimeMessageReceivedListener() {
            @Override
            public void onRealTimeMessageReceived(@NonNull RealTimeMessage realTimeMessage) {
                // Handle messages received here.
                byte[] message = realTimeMessage.getMessageData();
                // process message contents...
            }
        };

Leaving the room

Your game should leave the active room when one of these scenarios occurs:

  • Gameplay is over - If you don't leave the room, Google Play games services will continue to send notifications for that room to your room listeners.
  • onStop() is called - If the onStop() method is called, this might indicate that your activity is being destroyed and you should leave the room.
  • The user cancels the game in the waiting room UI - Your game should also leave the room if the response code returned in the onActivityResult() callback is GamesActivityResultCodes.RESULT_LEFT_ROOM.

To leave the room, call RealTimeMultiplayerClient.leave().

For example:

Games.getRealTimeMultiplayerClient(thisActivity, GoogleSignIn.getLastSignedInAccount(this))
        .leave(mJoinedRoomConfig, mRoom.getRoomId());
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);

After you leave the room, wait until you receive a call to the RoomUpdateCallback.onLeftRoom() method before attempting to start or join another room.

Send feedback about...

Play Games Services for Android
Play Games Services for Android