The key to a successful multiplayer game is having a critical mass of people to play with each other. But how do you amass players without an initial player base in the first place? Remove the requirement of "must-be-online-to-play". Asynchronous matchmaking enables players to engage in multiplayer games, even if they’re not online! Random matchups allow players to meet new people and make new friends, and an invitation system enables them to grow these friendships and create a viral spread of enthusiasm for your game. This article will explain how to use your game server and database to manage the process of creating and joining multiplayer games.
This article will explain:
- How to create an intuitive multiplayer interface
- How to setup an asynchronous multiplayer match with a random player
- How to design an asynchronous game invitation system
- How to design a friends management system so players can invite each other to game even when their friends aren’t online
Requirements |
|
Creating an Intuitive Multiplayer Experience
User experience is the most important part of creating an asynchronous multiplayer game. Regardless of how awesome your game may be, you will lose users if it’s not immediately clear to them how to setup multiplayer games.
In the original, real-time multiplayer implementation for Hero Mages, users would login and immediately enter a chat lobby where they can converse with other players, create games, and access other modules of the system such as the leaderboards and map editor. The system was modeled to mimic the design of popular multiplayer gateways like the original Battle.net for Diablo and Starcraft. Hardcore gamers are generally familiar with this setup, but many casual gamers are completely lost and uncertain about what to do next.
Learn by Example
Defining, in clear terms, how the game interface should function and designing layouts that are effective and intuitive to users is a challenging process. One of the most effective ways to overcome this challenge is to explore how other games have handled the problem and then seek to improve upon existing implementations.
Hero Academy is a very successful asynchronous multiplayer game that has an intuitive user interface. Multiplayer launches directly to the player’s game list where they can start new matches or continue playing games they’ve already started. Creating a new game takes you to a screen with three options for connecting with friends: inviting a player via search, finding a random opponent, and inviting a friend via Facebook. Hero Academy’s social media integration is an important feature for making it easier to invite friends. Finally, Hero Academy’s random match screen allows player to setup their team and find an opponent. Note the well-placed "Buy" button here that helps promote In-App purchasing.
Note: I won’t be discussing how to integrate with Facebook in this article (perhaps in the future) but I will explain the process for designing your own in-game friends list for those players who don’t use Facebook.
The new multiplayer interface for Hero Mages features an intuitive gateway screen that allows players to access all available functions of the application, including the ability to create new games, load and continue playing existing games, view the leaderboard, design custom maps, and so forth. The classic live-chat lobby interface is included as well and can be accessed via the icon in the upper left.
As Hero Mages supports both real-time multiplayer and asynchronous multiplayer, it was pertinent to design a new game creation screen that highlights the different options available to the player. As with Hero Academy, I’ve added the ability to buy expansions directly from the hero selection screen. There’s a subtle difference here, however: "Buy" labels are not placed over each character. Rather, the "Buy" button takes you to a new expansions screen for different products. The first reason for this is that characters are bought in sets. The second is that by having "locked" heroes directly in the list, many existing players express feelings of "not getting the full game" when they purchase the app. This separation helps to better distinguish the fact that new characters are expansions (not part of the core set).
Finally, the invitation system for Hero Mages demands a separate screen since, unlike Hero Academy, players can invite up to seven of their friends to a game. One of the weaknesses of the Hero Academy "Friend Search" is that, if your friend is NOT on Facebook, it’s necessary to type in your friends’ username each time you want to play a game. Hero Mages improves on this system by building an in-game friends list as you invite players to join your games. I’ll explain how this system works later in the article.

To invite friends, you select them from a drop down menu which also provides the option to add players who you aren't yet friends with.
What about joining games?
Notice how neither interface style shown above includes a "Join Game" button. With all this game creation going on, how are people actually connecting with one and other? The answer is that the joining of games is handled for the user behind the scenes by the server. When a player "creates" a new match, they’re actually sending a request to the server to connect with a player. Depending on whether another player is waiting or not, the server will either store their request as a new game record or connect their game preferences with an existing game record waiting on queue. The player shouldn’t be bothered with the task of joining because this diminishes the simplicity of the experience. The exception to this principle is if your game also supports live multiplayer games and you want to allow users to join sessions in progress. This is accomplished in Hero Mages via the main chat lobby. Users can click on live games in the list to the left to join them.
It’s also worthy to note that Hero Mages will also connect asynchronous players to a live game session while they’re taking their turn. If, at the same time a person is playing asynchronously their opponent loads that session from their games list, they’ll be automatically joined together in a live game session.
Summary of an Effective Multiplayer Interface
As demonstrated in the above images, an effective multiplayer game interface includes the following key elements:
- Gateway screen for accessing all of the multiplayer features
- Obvious and intuitive means to create new games
- Effective system for inviting friends
Setting up a Random 1v1 Match
As illustrated above, effective asynchronous multiplayer interfaces eliminate the concept of joining games in favor of matchmaking. The most basic way to create a random 1v1 match between two players is to create a new game record the first time a match is requested. Then, the next time a player requests a match, connect them with the preexisting session.
When a player starts a random opponent match, one of two things happens:
- An existing match is not available, so the server creates a new match and stores it in the database. The user is taken to a "Finding Player" screen. They may continue waiting for an opponent to join, or cancel. Even if they cancel, their request remains on the server. This way, they can be notified when an opponent has joined and they are ready to take their turn.
- An existing match is available, so the server completes a pending match with the user’s game preferences and sends them the game record to load. The server should always configure the "joining" player to get the first turn. This way, they can begin playing immediately once the game is loaded.

By the time the player reaches this screen, a new game record has been created. The player can wait to connect with an opponent or choose to cancel.

The system will store the match request within the database and notify the player when an opponent has been found so they don't have to keep waiting.

A new record appears in the player's game list, representing that the system is searching for opponent.
How it Works
The specific code implementation used to program the random matchmaking for Hero Mages is beyond the scope of this article, but a complete outline of the process is explained below. Using this outline and the techniques for coding client and server side code learned in the last two articles should provide a helpful starting point for programming any asynchronous multiplayer game.
In order to create the seamless matchmaking experience illustrated above, several communications between the client and server take place to appropriately configure the game:
- When the user selects the "Random Match" button, a "sendMatchMakeCommand" is triggered which instructs the game creation class on the client side to build a new game data structure and send a request to the server.
- On server side, check to see if there are any unmatched games waiting in queue
- If no unmatched games available, create a new game record on the server with a status of -1 to indicate that the game is waiting for opponent. Send response back to client indicating that a match was not found.
- If an unmatched game is available, load this game record’s data and send it back to the client in the response
- On client side,
- If no match was found, show "Finding Player" screen. If player cancels, show prompt saying "We’ll notify you when your match is ready."
- If match was found, create a new game data that merges player’s game preferences such as hero selections with the matched player’s existing game setup data and send response back to server
- On server side,
- Update the game results table with new record entry for the matched player
- Update the game record itself with new game setup data
- Return response command to load game (works just like loading the game in article 3 except an additional flag for matched game is included)
- On client side,
- Load the game data and start the game. Matched player may begin playing immediately.
- Send a response to server indicating game has been matched
- On server side, check if original match creator is online. If so, send them in-app notification that their match is ready. If not online, send a push notification or email. If the player is still on the "Finding Player" screen, we can connect them to a live game session with their opponent.
Random asynchronous games are well suited for 1v1 matches, but what if you want to play a game with more than 2 people? While it’s possible to setup a larger random game, this allows the potential for issues that may be disastrous for an asynchronous game. For example, what if you play a random 2v2 game and your teammate decides to quit playing? Also consider that the more players involved in a large game, the longer it will take before each player’s turn cycles back. With random players, there’s no way to contact each other and say "Hey, you, take your turn so we can finish up this game!" This is why it’s beneficial to create a game invitation system that’s restricted to friends. This way, you know the people you’re playing with have had a chance to see the game settings and are committed to finishing the session.
Designing an Asynchronous Game Invitation System
A game invitation system is essential for players to engage their friends in asynchronous multiplayer battles. The first requirement of building an invitation is the game creation screen- a place where the game host can configure options like which map to play on, whether or not there will be teams or computer opponents, and what the game objective will be. The second requirement is a mean to communicate between all invited players so that each player can submit their character choices and invitation acceptance and the game can begin when everyone is ready.
How it Works
- The game organizer launches a new game creation screen and can configure all game options. This includes choosing the map, game objective, organizing teams, and adding computer opponents. The game screen should include cells for each player (human or computer) that can be added.
- The game host can invite players using the player options drop down and selecting "Invite Friend". It’s important to limit invitations to friends because you don’t players being allowed to spam invites to random players who may not want to play a multiplayer game.
- When all options are setup and configured, the game organizer can click the "Send Invitation" button. This sends a message to the server with the game setup data and the ID’s of all the players involved.
- On the server side, a new game record is created using the same functionality discussed in Tutorial 2 with two differences:
- The game status is stored as -2 to indicate the game is pending invitation acceptance
- The hm_gameresults table should be modified to include new parameters that store the players’ invitation acceptance status and custom game preferences.
- Once the game record has been created by the server in the games data, it will appear in the invited players’ game lists just like other games. Instead of launching the game when a player selects it, however, the application will load the game’s current parameters back into the game creation screen. Here the player can select their heroes and choose to accept or decline the invitation.
- If the player declines invitation, the game status is set to -3, or Invitation Declined. The game will be flagged as completed for all players that were invited and will no longer be accessible.
- If the player accepts invitation, a call to the server is made to set the acceptance status for that player to true.
- On the client side, the game record interface should interpret accepted responses and draw appropriate markers on the game record. For example, players who were invited but haven’t yet responded show an envelope. Players who have accepted show a green check mark. Players who decline show a red x.
- When the final player has accepted the game invitation, the server will assemble the game data to incorporate all of the new player preferences. If the player who accepted has the active turn, it will send back a signal to launch the game so they can take their turn. Otherwise, it will send a notification to whichever player has the first turn.
Additions to the Game Results Table
Recall from part 2 of this series the creation of a table called hm_gameresults. This table is responsible for storing player-specific data related to the game, including the player’s win/loss result and the index of the last game command they witnessed (used for viewing game animation replays). For the invitation system, additional values are needed to track invitation response and any parameters for customizing the player’s team or character preferences.
hm_gameresults | ||
Field | Type | Notes |
accept | tinyint(4) | Stores value of -1, 1 or 0 to indicate whether player has not yet responded, accepted, or declined respectively |
ID_MAGE | int(10) | Stores the unique id of the player’s mage they have chosen to bring to battle |
ID_GUARD1 | int(10) | Stores the unique id of the player’s guardian they have chosen to bring to battle |
ID_GUARD2 | int(10) | Stores the unique id of the player’s guardian they have chosen to bring to battle |
Remember, a game invitation is simply creating a new game record that can show up in the player’s game list. The only difference is that until all players have accepted, the status of that game is -2, or pending invitation acceptance from all players. We don’t want to store players’ ready status in the game data itself because two players accepting an invitation at the same time could overwrite the others’ acceptance. By using each player’s own game result record, each player can individually modify their status. Only when the final player has accepted the invitation do we rewrite the game data to include all of the player’s game setup information (similar to merging the matched random player in the section above).
Further Design Considerations
Important questions regarding how to handle unique situations will arise during the creation of the game invitation system. Below are a few challenges that came up during the development of Hero Mages invitation system and how I handled them.
What happens to a game invitation once it’s been sent and what if players don’t respond to it?
Game invitations are designed to act in the same fashion as game records. They appear in the player’s game list with the games they are currently playing or have completed. The difference is invitations are marked with a different status id, in this case, -2, and are denoted on the game list as "Pending invitation".
Note that iconic symbols are used to represent the status of the game invitation. Players who were invited but haven’t yet responded show an envelope. Players who have accepted show a green check mark. Players who decline show a red x.
It’s important to keep the game list as tidy as possible to ensure players have a clear set of "tasks" to complete when they return to the application.
The game list sort algorithm is adjusted from the previous articles to work as follows:
- Games whose invitation the player needs to respond to are the highest priority because a game can’t start until all players have accepts. These will be listed first in the game list.
- Games in progress where it’s the player’s turn to attack are the next highest priority because these games cannot progress until the player has completed their turn. These records are shown next in the list with a secondary sort condition by date.
- Games in progress or waiting for other players to accept invitation are listed next, also sorted by the date/time of the last activity on that record.
- Finally, completed games and declined invitations, also sorted by date, are listed last.
To address the original design concern, invitations that haven’t been responded to remain listed in the player’s game list, pending action from other players (similar to games waiting on opponent’s turn). Players have the option to "drop" opponents who haven’t taken their turn after (3) days. And, at any time before a game starts, any of the players may change their acceptance to a game invitation to "Decline". Doing so will set the status of the game to "Invitation Declined". The game will be considered complete and will not affect player stats.
When a game is ready to play, who takes the first turn?
Since invitation based games can potentially involve up to 8 players, it’s not advisable to simply have the last player who accepts go first. Instead, the game should follow the random turn order pattern originally calculated in the initial game setup. The system should be configured to send a notification to the first player to take their turn once the game has started. Once a game has started, the status changes to "Game in progress", and players will see the game record updated as such on their game list.
What mechanism prevents a player from spamming undesired game invitation requests?
Players may use the leaderboard as a means to look up the highest ranked player and submit challenge request for the chance to earn higher rating points. Because this could potentially overwhelm popular players, Hero Mages requires that players invite friends rather than just any player. This mitigates unsolicited game invitation requests and keeps players’ games lists manageable.
At the same time, it’s important not to limit the development of friendships within the game. For this reason, Hero Mages allows you to request new friends via the game invitation itself. When a new friend is added, players may send them (1) game invitation request before they’ve accepted the friendship. If the player accepts the game invite, they’ll automatically become friends with the game organizer. If they decline the game request, the friendship status will remain pending until they accept it or the inviting player cancels their invitation request. The process for creating an in-game friends system is explained in the next section.
Creating an In-Game Friends System
While social networks like Facebook are a great solution for connecting players with their existing friends, relying solely on these sites can isolate players that don’t use social media or simply prefer not to give your game access to their social media accounts.
Having an in-game friends system is advisable for the following reasons:
- You have full control over how in-game friendships are formed allowing for greater flexibility and viral spread of your game’s internal community
- Friendships are independent of social networks, so your game can build community regardless of whether your users are on Facebook or other social networks
- The option to integrate with social media to enhance your game’s community is still available
This section describes the process of designing an in-game friends system and integrating it with your game’s interface and invitation system to foster the development of your game’s player community.
The Friends Table
The first step in creating a friends system is to create a new database table. Following the standard Hero Mages convention, I used the identifier "hm_friends". The table should include the following properties:
hm_friends | ||
Field | Type | Notes |
ID_LINK | int(10) | The unique ID of the friend relationship |
ID_MEMBER | int(10) | The ID of the member who has a friend |
ID_FRIEND | int(10) | The ID of the member who is the member’s friend |
accepted | tinyint(4) | ( 0, 1, -1) Indicates whether friend invite has been declined, accepted, or not responded to yet respectively |
sentGameInvite | tinyint(4) | (0 or 1) Indicates whether or not game invite has been sent on this friendship |
Client Side Friend List UI
In order to create new friend relationships, the client side will need some additional user interface components setup in order to send a new friend request to the server. The important question to ask is: "How will players setup a friendship within the game?" The more intuitive your friends system is, the greater the likelihood that players will establish friendships with the game, strengthening the game’s player community and long-term viability.
Friendships in Hero Mages can be established in three ways:
- By opening another player’s stats profile and selecting the "Add Friend" button. Player stat profiles can be accessed in the live chat room, the leaderboard, and within games by selecting the player’s name in the player list.
- By using the "Friends Management" screen (accessed via the multiplayer gateway) and searching for the player’s username.
- By selecting "Add Friend" when setting up a new game invitation. Using this action will leverage the Friends Management option #2 and then return to the game invitation screen, allowing the player the ability to quickly invite new friends to their game.
Adding Friends
With the proper UI established, the client side will send the server a friend request that includes the ID of the member who’s requesting the friendship and the ID of the player they want to become friends with.
On the server side, the following happens:
- First check to see if an existing friendship exists
sql = "SELECT * FROM hm_friends WHERE ID_MEMBER = "+params.hmId+" AND ID_FRIEND = "+params.fId; - Depending on whether or not query returns results, create new friendship record or update the status of the existing friendship record
- If query returns no results, relationship does not exist yet, create new record:
sql = "INSERT into hm_friends (ID_MEMBER, ID_FRIEND, accepted) VALUES ("+params.hmId+", "+params.fId+", 1) , ("+params.fId+", "+params.hmId+", -1)"; - Otherwise, relationship exists, update the status of the relationship:
sql = "UPDATE hm_friends set accepted = 1 WHERE ID_MEMBER = "+params.hmId+" and ID_FRIEND = "+params.fId; - Send the client side a response that includes the player’s updated friendship list. This list includes the names, ids, avatars, and relationship status of the player’s friends.
It is important to note that each friendship is defined by two records in the hm_friends table. For each friendship, the player will have a record where their ID_MEMBER stores their own ID and another record where for their friend stores their ID in ID_FRIEND. The two records are necessary so that friendships can be added and removed without the need to create/delete additional records.
It’s advisable to follow this practice for three reasons:
- Keeping track of existing friendship relationships ensures that player’s cannot indefinitely spam players to be their friends. Once a friend request is declined, the "accepted" property is set to 0 so that additional friend requests will not appear from this user. The friendship can only be established by this player adding the original requester as a friend.
- Keeping track of existing friendships allows the program to check the "sentGameInvite" property. This property stores whether or not a game invite was sent on this friendship. The game should not allow players to spam invites to players who have not accepted their friendship if this value has been flagged as true.
- Most importantly, two records are needed to validate friendship acceptance. Think of the system as a two-way handshake. The player who initiates the friendship automatically sets their "acceptance" flag to true. The player who is requested as a friend will have their "acceptance" set to -1 (undecided) until they decide to accept the friendship or decline it.
Removing Friends
Removing a friendship with this setup is easy: simply update the "accepted" status of the player opting to remove the friend to ’0′. The following statement is used:
1 | sql = "UPDATE hm_friends set accepted = 0 WHERE ID_MEMBER = "+params.hmId+" and ID_FRIEND = "+params.fId; |
Getting the Friends List
The friend system comes together via the MySQL statement on the server side that returns the friend list back to the player on the client side. Below is an example of a "getFriendsList" function that should be implemented in the server side code:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | //CODE EXAMPLE FRIEND LIST function getFriendsList(hmId){//PASS THE ID OF THE PLAYER WHOSE FRIEND LIST WE ARE RETURNING //STEP 1: Generate friends list query results by joining tables with pertinent data together sql = "SELECT * FROM (\r"; sql += "SELECT s.memberName as friendName, avatar, m.ID_FRIEND as fId, f.accepted as friendAccept, m.accepted as myAccept, m.sentGameInvite as g FROM hm_friends m\r" sql += "JOIN hm_friends f ON f.ID_MEMBER = m.ID_FRIEND AND m.ID_MEMBER = f.ID_FRIEND\r" sql += "JOIN smf_members s on m.ID_FRIEND = s.ID_MEMBER\r" sql += "WHERE m.ID_MEMBER = "+hmId+" and f.accepted != 0 and m.accepted != 0"; sql += ") as x\r"; sql += "ORDER BY myAccept, friendAccept, friendName"; //STEP 2: Populate the results into an array of objects that can be populated into a list component by client var queryRes = dbase.executeQuery(sql); var friendsList = []; for(var i = 0; i < queryRes.size(); i++){ var tempRow = queryRes.get(i) var row = new Object() row.n = tempRow.getItem("friendName") row.a = tempRow.getItem("avatar"); row.fId = tempRow.getItem("fId"); row.g = tempRow.getItem("g"); //STEP 3: Set the status of the player based on query results if(tempRow.getItem("myAccept") == -1){ row.s = -2; //This player has requested me as a friend } else if(tempRow.getItem("friendAccept") == -1){ row.s = -1; //I've requested this player as a friend and am waiting response } else{ row.s = 1; //We're friends } friendsList.push(row); } return friendsList; } |
Handle Server Response on Client Side
As in previous examples, the last step is handling the server side response on the client. The best practice is to store the friend list data in a universally accessible location. This way, the data can populate multiple style views like a friend list UI in the friend management system or a drop down menu in the game invitation system.
Coming Next
This article explained how to design an effective asynchronous multiplayer interface, how to setup random matches, how to design a game invite system, and how to create a simple friends list. The next article in this series will explain the final element necessary to tie the complete asynchronous multiplayer experience together: notifications.