Screwing multiplayer to the mobile game "Make a word from a word" on iOS and Android, written in C ++
Earlier, I already wrote about my experience in developing a mobile word game on Android and iOS, which enjoys a certain popularity, and I decided to tie the multiplayer mode to it, when two players compete with each other, making up words one by one as the final round of the TV show Sergei Suponev Star. hour".

It took me a month and a half to study and implement multiplayer, in the article I will try to describe the concept without source code examples, making a squeeze from the amount of work done.
The application was written in C ++ using the Marmalade SDK. Since then, the vendor has stopped supporting this platform, selling sorts to the Japanese, and the future of this development environment has become very vague.

The question arose of what to port the current projects for their further support.

Cocos2d-x is one of the most common engines for developing cross-platform mobile games in C ++. Apparently, due to its free and open source. The engine is poorly documented. The description covers a scanty part of the engine and most of the material is long outdated.
According to the results of a certain period, I still managed to create a prototype of my application. But the impressions were very bad: the feeling that cocos2d-x is assembled on the knee. The levels of abstraction Scene, Sprite, Application Delegate seemed to me very uncomfortable, and the need to look for answers to questions on the coconut forum more often lead you to think that you are doing something wrong. Probably my hands are not growing from that place.

SDL , as well as Marmalade SDL, is not an engine, it is a platform. It provides a low-level API, from which I later build convenient levels of abstraction for me. All this is written in C, the source code is open.
In a nutshell, SDL is a free cross-platform library for working with graphics, sound, and processing messages from the operating system. It is very convenient to do a win32 build and debug the logic of the game on Windows, leaving only OS debugging for mobile emulators and physical devices.
Fortunately or unfortunately, the SDL does not provide tools for such a narrow task as developing multiplayer for iOS and Android, so I had to integrate with the corresponding services myself.
The application logic and all the work with graphics is implemented in the main thread, which is the message processing cycle and starts in the main function. Let's call this thread SDL Thread. In turn, other threads throw events (SDL_PushEvent) to it for processing into a queue, and that one reads them from it using SDL_WaitEvent and SDL_PollEvent. These are either system events thrown by the system and whose support is already implemented in the SDL, or calls to Callbacks and Listeners that we implement above SDL functionality.

The whole logic of the game is written in C ++. The project directory contains a set of * .cpp files that can be divided into three groups:
Accordingly, there are three separate directories for the project of each platform:
Now we need to paste a separate layer that will be responsible for such functionality as:
Both iOS and Android platforms support Real-time Multiplayer (RTMP). In the case of Android, we integrate with Google Play Services (GPS), in the case of iOS - Game Center. Previously, Google supported and integration with iOS, but this year decided to abandon it.
In this article I will not describe the actions that need to be performed in the Google Play Console and AppStoreConnect to configure multiplayer, I will not describe the specification of classes and integration methods - all this is described on vendor sites.
Next, I briefly describe what changes need to be made in the project for each of the platforms.
How? I have not said this yet? To compile C ++ code, Android NDK is used . Although, if you are an Android developer, then you already know.
General instructions for integrating Google Play Services into an Android project are described on the website for Android developers. In my project I use the following dependencies:
Initially, the idea was to use C ++ api , which comes in the form of compiled static libraries without source codes. Due to the fact that there is no build for the x86_64 platform in the library list, I decided that the guys from Google didn’t really follow the relevance of this SDK and decided toinvent my bike to write this layer in Java, wrapping it with JNI wrappers. And then, why do I need an extra dependency in the form of a lib without source code, which all the same jerk Java inside of me? In addition to the relevance of Java classes, you will also need to monitor the relevance of these libs.
As a guide used a good example from Google Samples . Thank you google for this. Apple, take a cue from Google!
To integrate with the Game Center you need to connect the GameKit framework. We describe the whole layer of integration with the Game Center in one * .m-file and provide the interface to it through a separate * .h file. Since C ++ is a subset of the language objective-C, then with the assembly of * .cpp and * .m files in one project there will be no problems.
In addition to the official documentation, I guided this project: GameCenterManager . True, some things from the example are already outdated, XCode 10 will indicate this to you and you will replace the outdated functionality with a new one.
Having studied the features of working with multiplayer on both platforms, I created a single C ++ asbestos for my application and, at the time of compilation, the corresponding implementation is “attached” to it depending on the specific platform. That is, my application does not know about any Google Play Services, Game Center and their features. It knows only the C ++ api provided to it, where, for example, there are such methods as:
A player can invite a friend from his list of contacts, or start a game with a random opponent. The player who received the invitation can accept it, or reject it. For all of these scenarios, I use the standard interface of the service used. It should be noted that googled faces look much nicer than iOS-ovsky. Maybe someday my hands will get there and I will write my interface with dominoes and young ladies.
When two players have connected to the virtual game room, they get the corresponding callback. Now you need to choose who will be the host.
Among the players you need to choose a host, so that he determines the initial state of the game.
Consider the possible ways of routing messages between players in the general case. Please note that in the second version the host also has the role of the router.

Since I always have only two players in the game, it turns out that I have a special case of peer-to-peer connections. And therefore, only the definition of the initial state drops to the role of the host, namely, the choice of the word from which the words will be composed.
So, after the players connected to the game room, each of the players knows the list of identifiers of the participants in the game that has begun. We will call his list of participantID. A participantID is a kind of unique string identifier for a participant in a game, which is assigned by the service. You need to choose which of them will be the host, bring it to the host itself and inform the other that its opponent has been selected as the host. How to do it?
In the google dock, I did not find tips on choosing a host. Silent, partisans. But kind people at stackoverflow.com threw a link to the video , which explains in detail the following principle:
For iOS, there is a method chooseBestHostPlayerWithCompletionHandler , which greatly simplifies the scenario of choosing a host compared to what I described for Android. But, judging by the noticeable delays during the call of this method, it evaluates the parameters of the network response, measures the ping and, based on this statistics, decides who to be the host. This is most likely suitable for the client-server architecture above, where the host acts as a router. In my version of a private peer-to-peer connection, this does not make sense and in order to save time, I use a principle similar to what I did for Android.
What is a message? The message is an array of bytes.
There are 2 types of sending messages:
Unreliable is usually delivered faster than Reliable. More information can be found on the vendors website:
How will we use this array? Very simple:
So, we define enum with the types of messages that players will exchange with each other during the game:
When an application receives an incoming message from an opponent, the corresponding Callback is called, which in turn sends it to the main SDL Thread for processing.
Gaming services (like Google and Apple) have listener functions that, in one form or another, are designed to notify us about breaking the connection with an opponent. But, I noticed that if one of the players is disconnected from the Internet, the second one does not immediately recognize that the first one has disconnected and there is no one to play with. Callbacks are not called in such cases, or they are called after a sufficiently long time. So that in this case the second player does not have to wait for the cancer on the mountain to whistle, I had to do my own monitoring of the connection, which works according to the principle:
As a result of the work done, I got a game that I play with my friends and family. I play both on iOS, and on Android.
True, there is a nuance on iOS - for some reason, points are not fixed in Leaderboards, which I am currently in correspondence with Apple support.
I hope this article will be useful as the members of my team, and those who are interested in the development of mobile applications. Thanks for attention.

It took me a month and a half to study and implement multiplayer, in the article I will try to describe the concept without source code examples, making a squeeze from the amount of work done.
A bit of history
The application was written in C ++ using the Marmalade SDK. Since then, the vendor has stopped supporting this platform, selling sorts to the Japanese, and the future of this development environment has become very vague.

The question arose of what to port the current projects for their further support.
Why not cocos2d-x

Cocos2d-x is one of the most common engines for developing cross-platform mobile games in C ++. Apparently, due to its free and open source. The engine is poorly documented. The description covers a scanty part of the engine and most of the material is long outdated.
According to the results of a certain period, I still managed to create a prototype of my application. But the impressions were very bad: the feeling that cocos2d-x is assembled on the knee. The levels of abstraction Scene, Sprite, Application Delegate seemed to me very uncomfortable, and the need to look for answers to questions on the coconut forum more often lead you to think that you are doing something wrong. Probably my hands are not growing from that place.
My choice fell on SDL

SDL , as well as Marmalade SDL, is not an engine, it is a platform. It provides a low-level API, from which I later build convenient levels of abstraction for me. All this is written in C, the source code is open.
In a nutshell, SDL is a free cross-platform library for working with graphics, sound, and processing messages from the operating system. It is very convenient to do a win32 build and debug the logic of the game on Windows, leaving only OS debugging for mobile emulators and physical devices.
Fortunately or unfortunately, the SDL does not provide tools for such a narrow task as developing multiplayer for iOS and Android, so I had to integrate with the corresponding services myself.
Multithreaded application architecture
The application logic and all the work with graphics is implemented in the main thread, which is the message processing cycle and starts in the main function. Let's call this thread SDL Thread. In turn, other threads throw events (SDL_PushEvent) to it for processing into a queue, and that one reads them from it using SDL_WaitEvent and SDL_PollEvent. These are either system events thrown by the system and whose support is already implemented in the SDL, or calls to Callbacks and Listeners that we implement above SDL functionality.

The whole logic of the game is written in C ++. The project directory contains a set of * .cpp files that can be divided into three groups:
- cross-platform - those files that are included in the assembly of all platforms (the logic of the game);
- monoplatform, i.e. included in the application of a single platform for the implementation of its features.
Accordingly, there are three separate directories for the project of each platform:
- proj.win32 - project VS2017 Community Edition;
- proj.android - Android project using Gradle;
- proj.ios - Xcode project for iOS.
Integration with multiplayer services
Now we need to paste a separate layer that will be responsible for such functionality as:
- search for an opponent, connect to the game;
- messaging between rivals;
- exit from the game room;
- fixing player points in Leaderboards.
Both iOS and Android platforms support Real-time Multiplayer (RTMP). In the case of Android, we integrate with Google Play Services (GPS), in the case of iOS - Game Center. Previously, Google supported and integration with iOS, but this year decided to abandon it.
In this article I will not describe the actions that need to be performed in the Google Play Console and AppStoreConnect to configure multiplayer, I will not describe the specification of classes and integration methods - all this is described on vendor sites.
Next, I briefly describe what changes need to be made in the project for each of the platforms.
Android
How? I have not said this yet? To compile C ++ code, Android NDK is used . Although, if you are an Android developer, then you already know.
General instructions for integrating Google Play Services into an Android project are described on the website for Android developers. In my project I use the following dependencies:
implementation 'com.google.android.gms:play-services-games:16.0.0'
implementation 'com.google.android.gms:play-services-nearby:16.0.0'
implementation 'com.google.android.gms:play-services-auth:16.0.1'
Initially, the idea was to use C ++ api , which comes in the form of compiled static libraries without source codes. Due to the fact that there is no build for the x86_64 platform in the library list, I decided that the guys from Google didn’t really follow the relevance of this SDK and decided to
As a guide used a good example from Google Samples . Thank you google for this. Apple, take a cue from Google!
iOS
To integrate with the Game Center you need to connect the GameKit framework. We describe the whole layer of integration with the Game Center in one * .m-file and provide the interface to it through a separate * .h file. Since C ++ is a subset of the language objective-C, then with the assembly of * .cpp and * .m files in one project there will be no problems.
In addition to the official documentation, I guided this project: GameCenterManager . True, some things from the example are already outdated, XCode 10 will indicate this to you and you will replace the outdated functionality with a new one.
The principle of working with a multiplayer layer
Single entry point
Having studied the features of working with multiplayer on both platforms, I created a single C ++ asbestos for my application and, at the time of compilation, the corresponding implementation is “attached” to it depending on the specific platform. That is, my application does not know about any Google Play Services, Game Center and their features. It knows only the C ++ api provided to it, where, for example, there are such methods as:
SignIn() // войти в игровой сервис
SignOut() // выйти из игрового сервиса
LeaveRoom() // покинуть комнату игры
SendMessage(...) // отправить сообщение оппоненту
ShowLeaderboards() // показать доску лидеров
SubmitScore(...) // зафиксировать набранные очки
...
Search for an opponent
A player can invite a friend from his list of contacts, or start a game with a random opponent. The player who received the invitation can accept it, or reject it. For all of these scenarios, I use the standard interface of the service used. It should be noted that googled faces look much nicer than iOS-ovsky. Maybe someday my hands will get there and I will write my interface with dominoes and young ladies.
Connect to the game room
When two players have connected to the virtual game room, they get the corresponding callback. Now you need to choose who will be the host.
Host selection
Among the players you need to choose a host, so that he determines the initial state of the game.
Consider the possible ways of routing messages between players in the general case. Please note that in the second version the host also has the role of the router.

Since I always have only two players in the game, it turns out that I have a special case of peer-to-peer connections. And therefore, only the definition of the initial state drops to the role of the host, namely, the choice of the word from which the words will be composed.
So, after the players connected to the game room, each of the players knows the list of identifiers of the participants in the game that has begun. We will call his list of participantID. A participantID is a kind of unique string identifier for a participant in a game, which is assigned by the service. You need to choose which of them will be the host, bring it to the host itself and inform the other that its opponent has been selected as the host. How to do it?
Choosing a host on Android
In the google dock, I did not find tips on choosing a host. Silent, partisans. But kind people at stackoverflow.com threw a link to the video , which explains in detail the following principle:
- each of the participants sorts the participantID list (ascending or descending - it does not matter, as long as everything is done in the same order);
- each participant compares his participantID with the first participantID from the list;
- if they match, then the current player is given the right to choose who will be the host. He
throws a coinjerks random (), thereby selecting a host from existing members, and informs everyone who is a host.
IOS Host Selection
For iOS, there is a method chooseBestHostPlayerWithCompletionHandler , which greatly simplifies the scenario of choosing a host compared to what I described for Android. But, judging by the noticeable delays during the call of this method, it evaluates the parameters of the network response, measures the ping and, based on this statistics, decides who to be the host. This is most likely suitable for the client-server architecture above, where the host acts as a router. In my version of a private peer-to-peer connection, this does not make sense and in order to save time, I use a principle similar to what I did for Android.
Messaging between players
What is a message? The message is an array of bytes.
- In java, this is the type:
byte[]
- in objective-C, this is:
NSData *
- in C ++, I drop everything above
std::vector<Uint8>
There are 2 types of sending messages:
- Reliable - guaranteed delivery through the queue. Used to deliver critical messages.
- Unreliable - unwarranted delivery. Used messages whose delivery success can be neglected.
Unreliable is usually delivered faster than Reliable. More information can be found on the vendors website:
How will we use this array? Very simple:
- in the first byte we will write the message type.
- if the message has any parameters, we will put them in the next bytes. For each type of message that has an additional. parameters, we realize our function of serialization and deserialization.
- We’ll put a checksum at the end of the message to check the integrity.
So, we define enum with the types of messages that players will exchange with each other during the game:
- I am selected as a host. I pass the initial state. Now my turn (parameters: the version number of the messaging protocol, the original word);
- You are the selected host. I am waiting for you to start;
- I open (call) the word. Now it's your turn (parameter: named word);
- I give up. You won;
- I could not make a word during the course. You won;
- I agree to a rematch;
- I am leaving the game;
- Error parsing message. Disconnect;
- Your version of the messaging protocol is outdated. Check the application update. Disconnect;
- My version of the messaging protocol is outdated. Need to check the update. Disconnect;
- Ping (system message);
When an application receives an incoming message from an opponent, the corresponding Callback is called, which in turn sends it to the main SDL Thread for processing.
Connection monitoring
Gaming services (like Google and Apple) have listener functions that, in one form or another, are designed to notify us about breaking the connection with an opponent. But, I noticed that if one of the players is disconnected from the Internet, the second one does not immediately recognize that the first one has disconnected and there is no one to play with. Callbacks are not called in such cases, or they are called after a sufficiently long time. So that in this case the second player does not have to wait for the cancer on the mountain to whistle, I had to do my own monitoring of the connection, which works according to the principle:
- Each player sends a ping message to the opponent every second;
- Each player checks: if there was no message from the opponent for more than 5 seconds, then the connection is lost, we exit the game.
Result
As a result of the work done, I got a game that I play with my friends and family. I play both on iOS, and on Android.
True, there is a nuance on iOS - for some reason, points are not fixed in Leaderboards, which I am currently in correspondence with Apple support.
I hope this article will be useful as the members of my team, and those who are interested in the development of mobile applications. Thanks for attention.