Game Services | Real-Time Multiplayer: Scripting

This section provides a guide to work with the Real-Time Multiplayer scripting API of the Game Services module.

You can access the Real-Time Multiplayer API via the RealTime property of the GameServices class under the EasyMobile namespace. The API is intended to be used after the initialization has completed (the user has logged in to Game Center or Google Play Games). Failure to do so is a programming error.

Implementation Checklist

To build a real-time multiplayer game, write code to handle the following activities:

Match-making

Match-making is the process of creating a new match and fill it with participants. The Real-Time Multiplayer API allows you to either create a quick match with random (auto-matched) opponents, or create a match using the built-in matchmaker UI in which you can invite friends, nearby players, or random players. Both methods of match making start with creating a match request, which is a set of parameters that describe the match to be created.

Match Request

Before creating a new match, create a MatchRequest object with appropriate parameters to describe the match you want.

MatchRequest request = new MatchRequest();

// The minimum number of players that may join the match, including
// the player who is making the match request. 
// Must be at least 2 (default).
request.MinPlayers = 2;

// The maximum number of players that may join the match, including
// the player who is making the match request. 
request.MaxPlayers = 2;

// The match variant. The meaning of this parameter is defined by the game.
// It usually indicates a particular game type or mode (for example "capture the flag", 
// "first to 10 points", etc). It allows the player to match only with players whose 
// match request shares the same variant number. This value must
// be between 0 and 511 (inclusive). Default value is 0.
request.Variant = 0;

// 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 match request. When auto-matching with this option, 
// players will only be considered for a match when the logical AND of their exclusive 
// bitmasks is equal to 0. In other words, this value represents the exclusive role the 
// player making this request wants to play in the created match. If this value is 0 (default) 
// it will be ignored. If you're creating a match with the standard matchmaker UI, this value
// will also be ignored.
request.ExclusiveBitmask = 0;

Create a Quick Match

To programmatically start a quick match with random opponents, use the CreateQuickMatch method. When using this method, the underlying platform will initialize a new real-time multiplayer room and attempt to match the local player with other players who at that time also look for random opponents, provided that their match request is compatible with the local player's match request. No UI will be shown.

Beside the MatchRequest, this method also takes a 'listener' parameter, which must implement the IRealTimeMultiplayerListener interface (see Handling Real-Time Multiplayer Events). During the room setup (waiting for all participants to join), the OnRoomSetupProgress method of the listener will be called to report the progress. Once the setup completes, the OnRoomConnected method of the listener will be called with a bool argument indicating if the setup was successful or not. If the room is successfully connected, all participants have joined and your match is ready to start.

public void CreateQuickMatch()
{
    // First create a match request.
    MatchRequest request = new MatchRequest();
    request.MinPlayers = 2;
    request.MaxPlayers = 2;

    // Create a quick match with a random opponent. No UI will be shown.
    // 'listener' is an object implementing the IRealTimeMultiplayerListener interface.
    // During setup, the 'OnRoomSetupProgress' method of the listener will be called.
    // Once setup completed, the 'OnRoomConnected' method will be called.
    GameServices.RealTime.CreateQuickMatch(request, listener);
}

Create with the Matchmaker UI

Game Center and Google Play Games both provide a matchmaker UI that allows starting a match by inviting friends, nearby players or picking random opponents. This built-in UI offers the user a standardized and intuitive way to perform these tasks and can save you a significant coding effort of building your own matchmaker UI. Below are screenshots of the matchmaker UI provided by Game Center and Google Play Games, respectively.

To create a match with this UI, simply call the CreateWithMatchmakerUI method. This method takes a MatchRequest parameter and a 'listener' parameter, which must implement the IRealTimeMultiplayerListener interface (see Handling Real-Time Multiplayer Events). During the room setup (waiting for all participants to join), the OnRoomSetupProgress method of the listener will be called to report the progress. Once the setup completes, the OnRoomConnected method of the listener will be called with a bool argument indicating if the setup was successful or not. If the room is successfully connected, all participants have joined and your match is ready to start.

public void CreateWithMatchmakerUI()
{
    // First create a match request.
    MatchRequest request = new MatchRequest();
    request.MinPlayers = 2;
    request.MaxPlayers = 2;

    // Create a match using the built-in matchmaker UI.
    // 'listener' is an object implementing the IRealTimeMultiplayerListener interface.
    // During setup, the 'OnRoomSetupProgress' method of the listener will be called.
    // Once setup completed, the 'OnRoomConnected' method will be called.
    GameServices.RealTime.CreateWithMatchmakerUI(request, listener);
}

Handling Invitations

A player being invited to join a real-time match receives an invitation during the match-making process. Real-Time invitations are handled a bit differently between Game Center and Google Play Games platforms.

  • Game Center: invitations are sent as messages in the iMessage app; the user taps a message to accept the associated invitation, which will open the app and invoke the registered InvitationReceivedDelegate with the shouldAutoAccept argument being true.
  • Google Play Games: there are two cases:
    • App is in foreground: if the app is running in foreground and an invitation comes, the InvitationReceivedDelegate will be called if it has been registered before, and the shouldAutoAccept argument will be false.
    • App is in background or not running: the invitation will come as a notification, the user can tap this notification to bring up the default inbox UI of Google Play Games where he can accept or decline the invitation. If the invitation is accepted, the app will be opened and the registered InvitationReceivedDelegate will be called with shouldAutoAccept being true.

To handle real-time invitations you must register an InvitationReceivedDelegate as soon as the user has logged in - that is, inside the handler of the UserLoginSucceeded event. This delegate will be called when an invitation arrives as described above.

You can accept or decline an invitation programmatically using the AcceptInvitation or DeclineInvitation method, respectively. If the invitation is accepted, a new real-time multiplayer room will be initialized, same as when creating a new match. The AcceptInvitation method also takes an IRealTimeMultiplayerListener parameter (see Handling Real-Time Multiplayer Events). During the room setup (waiting for all participants to join), the OnRoomSetupProgress method of the listener will be called to report the progress. Once the setup completes, the OnRoomConnected method of the listener will be called with a bool argument indicating if the setup was successful or not. If the room is successfully connected, all participants have joined and your match is ready to start. You can optionally set the showWaitingRoomUI parameter as true when calling AcceptInvitation to show the standard waiting room UI during the room setup.

void OnEnable()
{
    // Subscribe to this event before initializing EasyMobile runtime.
    // Normally you should add this script to the first scene of your app.
    GameServices.UserLoginSucceeded += GameServices_UserLoginSucceeded;
}

// This handler is called when authentication completes successfully.
void GameServices_UserLoginSucceeded()
{
    // Register our invitation delegate.
    GameServices.RegisterInvitationDelegate(OnInvitationReceived);
}

// This is called when an invitation is received. 'shouldAutoAccept' will be true
// if the user accepts the invitation through the notification, otherwise it will
// be false (e.g. when the invitation comes whilst the app is in foreground).
// This is always called on the main thread.
void OnInvitationReceived(Invitation invitation, bool shouldAutoAccept)
{
    Debug.Log("Received a multiplayer invitation. InvType: " + invitation.InvitationType);  // TurnBased or RealTime

    if (shouldAutoAccept)
    {
        // Invitation should be accepted immediately.
        // This will setup a room, same as when creating a new match.
        // Set 'showWaitingRoomUI' to true to display the default UI 
        // while waiting for all participants to join.
        // 'listener' is an object implementing the IRealTimeMultiplayerListener interface.
        // During setup, the 'OnRoomSetupProgress' method of the listener will be called.
        // Once setup completed, the 'OnRoomConnected' method will be called.
        GameServices.RealTime.AcceptInvitation(invitation, true, listener);
    }
    else
    {
        // The invitation comes when the app is in foreground.
        // The user has not yet indicated that they want to accept this invitation.
        // We should *not* automatically accept it. Rather we should show some popup
        // asking if the user would like to accept or decline it.
    }
}

Handling Real-Time Multiplayer Events

Your game must prepare a listener to handle various events that occur during a real-time multiplayer game session. This listener must implement the IRealTimeMultiplayerListener. You specify a listener with the Real-Time Multiplayer API while setting up a room and during the lifecycle of that room (from initialization to closure), this listener will act as the handler for the events associated to the room. Specifically, when an event occurs, a corresponding method of the listener will be called. You write code to implement the listener's methods to handle various events of the match according to your game design.

// Prepare a listener class that implements the IRealTimeMultiplayerListener.
// You write code for each method of this listener to handle various events
// according to your game design.
// When setting up a room, specify a instance of this class with the
// Real-Time Multiplayer API to act as the listener for that room.
public class MyRealTimeMultiplayerListener : IRealTimeMultiplayerListener
{
    public void OnRoomSetupProgress(float percent)
    {
        // This method is called during the room setup process.
        // You can, for example, write code that shows a progress bar
        // to inform the user the setup progress.
    }

    public void OnRoomConnected(bool success)
    {
        // This method is called once all participants have joined the room.
        // Game is ready to start! Go to game screen and play!
    }

    public void OnLeftRoom()
    {
        // This method is called once the local player has left the room,
        // which may happen because you called the LeaveRoom method.
        // You should react by having the local player stop taking part in the match and
        // possibly showing an error screen (unless leaving the room was the player's
        // request, naturally).
    }

    public void OnParticipantLeft(Participant participant)
    {
        // This method is called during room setup if a player declines an invitation
        // or leaves. The status of the participant can be inspected to determine
        // the reason. If all players have left, the room is closed automatically.
    }

    public void OnPeersConnected(string[] participantIds)
    {
        // This method is called when participants
        // connect to the room.
    }

    public void OnPeersDisconnected(string[] participantIds)
    {
        // This method is called when participants disconnect
        // from the room. The remaining participants are still
        // fully connected to each other. You may react by updating
        // the game UI to inform the other participants that a player has left.
        // When all other participants have left, you should end the match
        // and have the local player leave the room so that it can be closed.
    }

    public void OnRealTimeMessageReceived(string senderId, byte[] data)
    {
        // This method is called when the local player has received a
        // real-time message. The sender can be identified by the 'senderId'.
        // The received data is simply a byte array, your game is responsible
        // for interpreting the meaning of this data and updating the state of
        // the match accordingly.
    }

    public bool ShouldReinviteDisconnectedPlayer(Participant participant)
    {
        // This method is only available on Game Center platform.
        // It is called when a participant in a two-player match was disconnected.
        // Your game should return true if it wants Game Center to attempt to
        // reconnect the disconnected player.
    }
}

// Specify a listener while creating a match programmatically.
public void CreateQuickMatch()
{
    // First create a match request.
    MatchRequest request = new MatchRequest();
    request.MinPlayers = 2;
    request.MaxPlayers = 2;

    // Create a listener for the room being initialized.
    IRealTimeMultiplayerListener listener = new MyRealTimeMultiplayerListener();

    // Create a quick match with a random opponent. No UI will be shown.
    // 'listener' will be the event handler for this match.
    // During setup, the 'OnRoomSetupProgress' method of the listener will be called.
    // Once setup completed, the 'OnRoomConnected' method will be called.
    GameServices.RealTime.CreateQuickMatch(request, listener);
}

Obtaining Participants Information

Once the room is connected, you can list the room's participants by calling the GetConnectedParticipants method. This list is guaranteed to be sorted by participant ID, so all participants in the game will get this list in the same order.

// Get a list of all participants connected to the room, including the local player.
List<Participant> participants = GameServices.RealTime.GetConnectedParticipants();

You can find a participant with a specific ID using the GetParticipant method.

// Get the participant with the ID specified by 'participantID'.
Participant participant = GameServices.RealTime.GetParticipant(participantID);

You can also find the participant representing the local player by calling the GetSelf method.

// Get the participant representing the local player.
Participant self = GameServices.RealTime.GetSelf();

Sending Messages

To send a message to all other participant, use the SendMessageToAll method. The reliable parameter controls whether the message will be sent reliably or unreliably (see Sending Game Data).

Unreliable messages can be considerably faster than reliable messages. Typically a game should send transient updates via unreliable messages (where missing a few updates does not affect gameplay), and send important messages via reliable messages.

// Setting data sending mode: true = reliable, false = unreliable.
bool reliable = true;   

// Broadcast a message to all other participants.
// 'data' is a byte array representing your message.
GameServices.RealTime.SendMessageToAll(reliable, data);

To send a message to a particular participant, call the SendMessage method with the ID of the recipient.

// Setting data sending mode: true = reliable, false = unreliable.
bool reliable = false;   

// Send a message to the participant with the ID specified by 'participantID'.
// 'data' is a byte array representing your message.
GameServices.RealTime.SendMessage(reliable, participantID, data);

A Note on Rematch

The Real-Time multiplayer API doesn't provide a built-in rematch method, because that can be done easily with the versatile message exchange system. Since you can send virtual any kind of data with the message sending methods, you can create your own rematch method that best suits your game by just sending some specific signal from one of the players that represents a rematch invitation, which can be as simple as a predefined string or int value.

Receiving Messages

When you receive a message from another participant, your listener's OnRealTimeMessageReceived method will be called (see Handling Real-Time Multiplayer Events).

public void OnRealTimeMessageReceived(string senderId, byte[] data)
{
    // This method is called when the local player has received a
    // real-time message. The sender can be identified by the 'senderId'.
    // The received data is simply a byte array, your game is responsible
    // for interpreting the meaning of this data and updating the state of
    // the match accordingly.
}

Leaving a Room

When finished with your real-time game, leave the room by calling the LeaveRoom method.

// Leave the current real-time multiplayer room.
GameServices.RealTime.LeaveRoom();

Leaving the room will trigger a call to your listener's OnLeftRoom method (see Handling Real-Time Multiplayer Events).

Known Issue

As of version 0.9.57, the Google Play Games Plugin for Unity (GPGS) doesn't really invoke the registered invitation delegate if the app is launched from a multiplayer notification. This means the InvitationReceivedDelegate in our API will also not be called in such case. This doesn't occur when the app is running, either in background or foreground; and it also doesn't occur on Game Center platform.

This is a longstanding issue of GPGS and we encourage you to upvote the issue so it attracts more attention from Google and hopefully will be fixed sooner: GPGS Issue #1139

results matching ""

    No results matching ""