B4A Library Google Play Game Services

This is a wrapper for the Google Play Services related to games. This library was tested successfully with the API 37 of Google Play Services.

List of features:
  • Sign-in: supported;
  • Achievements: supported;
  • Leaderboards: supported;
  • Real-time multiplayer: supported (NEW!);
  • Turn-based multiplayer: supported;
  • Level and XP: supported;
  • Gifts and requests: supported;
  • Events and quests: not supported;
  • Saved games: supported;
  • Notifications: supported;
  • Player stats: supported;
  • Nearby connections: supported.
To compile, you need B4A v6+, JDK v7+ and android.jar v21+ (cf. Tools/Configure paths).

The library is provided with its Java source code and a few B4A modules:
  • ClsConnection: manages the sign-in, sign-out and connection events;
  • CodConverter: converts your data map to and from the byte array needed for the Saved Games service (this module requires three extra libraries: JSON, ByteConverter and RandomAccessFile) in case you don't own the DataCollection library;
  • CodTurnBasedMatch: helper module for turn-based matches.
Before using this library, you need to:
  • download the Google Play services SDK and Android Support Repository with the Android SDK manager;
  • copy the GooglePlayGameServices library (Jar+Xml) to your additional B4A libraries folder.
When you create a project requiring this library, you need to:
  • add this line in the project attributes of your Main module:
    B4X:
    #AdditionalJar: com.google.android.gms:play-services-games
  • add also this line if you want to access the Nearby Connections API:
    B4X:
    #AdditionalJar: com.google.android.gms:play-services-nearby
  • add these lines to your manifest:
    B4X:
    AddApplicationText(
    <meta-data android:name="com.google.android.gms.games.APP_ID"
        android:value="@string/app_id" />
    <meta-data android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />)
  • add also these lines to the manifest if you want to access the Nearby Connections API:
    B4X:
    AddApplicationText(
    <meta-data android:name="com.google.android.gms.nearby.connection.SERVICE_ID"
        android:value="$PACKAGE$" />)
  • copy the games-ids.xml file generated by your Google Play developer console in the Objects\res\values folder.
To generate the games-ids.xml file, click on the link at the bottom of some screens (e.g. achievements) in your developer console (you need of course a game project properly configured to see it):
ressources.png


This file should look like this:
B4X:
<?xml version="1.0" encoding="utf-8"?>
<!--
Google Play game services IDs.
Save this file as res/values/games-ids.xml in your project.
-->
<resources>
  <string name="app_id">01234567890</string>
  <string name="achievement_win_on_a_very_large_map">CgkIuqeG8-kOEAIQAQ</string>
  <string name="achievement_win_on_a_large_map">CgkIuqeG8-kOEAIQAg</string>
  <string name="achievement_win_on_a_medium_map">CgkIuqeG8-kOEAIQAw</string>
  <string name="achievement_win_on_a_small_map">CgkIuqeG8-kOEAIQBA</string>
  <string name="achievement_win_on_a_very_small_map">CgkIuqeG8-kOEAIQBQ</string>
  <string name="leaderboard_main_score">CgkIuqeG8-kOEAIQBw</string>
</resources>

To set up a game in the Google Play developer console, please read this, then this.

To get details about a returned status code, please read this.
 

Attachments

  • Demos_37.zip
    71 KB · Views: 571
  • GooglePlayGameServices_372.zip
    241.3 KB · Views: 568
Last edited:

andymc

Well-Known Member
Licensed User
Longtime User
Hi @Informatix Sorry for asking on this old thread. But I'm trying to add online high scores and achievements to my games but I'm getting the following error trying to compile your example:
B4A version: 6.80
Parsing code. (0.01s)
Compiling code. (0.04s)
Compiling layouts code. (0.00s)
Organizing libraries. (0.00s)
Generating R file. Error
AndroidManifest.xml:21: error: Error: No resource found that matches the given name (at 'value' with value '@string/app_id').

I did save the games-ids.xml file into the values folder in objects/res (had to create the values folder first), but it deletes it when it builds the game.
 

andymc

Well-Known Member
Licensed User
Longtime User
Thanks for the answer, it get's a little further but now shows
Compiling generated Java code. Error
javac 1.8.0_45
src\your\package\name\clsconnection.java:1: error: <identifier> expected
package your.package.name;
^

I may be being stupid here but I'm sure I followed the instructions properly and I'm just trying to compile and run the leaderboards/achievement demo code. I've got a games app that I've added some achievements and high tables to but it's refusing to work!!! Could I have things installed incorrectly on my PC?

EDIT; Nevermind, giving it a proper package name has allowed it to fully compile and install.
Getting sign in failed error but will work on that.
 
Last edited:

andymc

Well-Known Member
Licensed User
Longtime User
Okay, lasty question on this, I hope.

When I try moving the sample code into my main game project, I get the following errors:

Generating R file. Error
c:\users\andy.mcadam\appdata\local\android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values\base_attrs.xml:11: error: Attribute "buttonSize" already defined with incompatible format.
C:\Users\andy.mcadam\Dropbox\basic4android projects\invaders 2017\Objects\bin\extra\res1\res\values\values.xml:47: Original attribute defined here.
c:\users\andy.mcadam\appdata\local\android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values\base_attrs.xml:16: error: Attribute "colorScheme" already defined with incompatible format.
C:\Users\andy.mcadam\Dropbox\basic4android projects\invaders 2017\Objects\bin\extra\res1\res\values\values.xml:55: Original attribute defined here.
c:\users\andy.mcadam\appdata\local\android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values\common_attrs.xml:13: error: Attribute "imageAspectRatioAdjust" already defined with incompatible format.
C:\Users\andy.mcadam\Dropbox\basic4android projects\invaders 2017\Objects\bin\extra\res1\res\values\values.xml:34: Original attribute defined here.


I am already using admob and have the following lines at the top of my source code:
#AdditionalRes: C:\Users\andy.mcadam\AppData\Local\Android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res
#AdditionalJar: com.google.android.gms:play-services-games


My manifest file looks like this:
'AdMob
AddApplicationText(
<meta-data android:name="com.google.android.gms.version"
android:value="8487000"/>
<activity android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>

)
AddApplicationText(
<meta-data android:name="com.google.android.gms.games.APP_ID"
android:value="@string/app_id" />
<meta-data android:name="com.google.android.gms.version"
android:value="@Integer/google_play_services_version" />)
'End of AdMob


'AdMob
AddApplicationText(

<activity android:name="com.google.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
)
 

Informatix

Expert
Licensed User
Longtime User
Okay, lasty question on this, I hope.

When I try moving the sample code into my main game project, I get the following errors:

Generating R file. Error
c:\users\andy.mcadam\appdata\local\android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values\base_attrs.xml:11: error: Attribute "buttonSize" already defined with incompatible format.
C:\Users\andy.mcadam\Dropbox\basic4android projects\invaders 2017\Objects\bin\extra\res1\res\values\values.xml:47: Original attribute defined here.
c:\users\andy.mcadam\appdata\local\android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values\base_attrs.xml:16: error: Attribute "colorScheme" already defined with incompatible format.
C:\Users\andy.mcadam\Dropbox\basic4android projects\invaders 2017\Objects\bin\extra\res1\res\values\values.xml:55: Original attribute defined here.
c:\users\andy.mcadam\appdata\local\android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res\values\common_attrs.xml:13: error: Attribute "imageAspectRatioAdjust" already defined with incompatible format.
C:\Users\andy.mcadam\Dropbox\basic4android projects\invaders 2017\Objects\bin\extra\res1\res\values\values.xml:34: Original attribute defined here.


I am already using admob and have the following lines at the top of my source code:
#AdditionalRes: C:\Users\andy.mcadam\AppData\Local\Android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res
#AdditionalJar: com.google.android.gms:play-services-games


My manifest file looks like this:
'AdMob
AddApplicationText(
<meta-data android:name="com.google.android.gms.version"
android:value="8487000"/>
<activity android:name="com.google.android.gms.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>

)
AddApplicationText(
<meta-data android:name="com.google.android.gms.games.APP_ID"
android:value="@string/app_id" />
<meta-data android:name="com.google.android.gms.version"
android:value="@Integer/google_play_services_version" />)
'End of AdMob


'AdMob
AddApplicationText(

<activity android:name="com.google.ads.AdActivity"
android:configChanges="keyboard|keyboardHidden|orientation|screenLayout|uiMode|screenSize|smallestScreenSize"/>
)
The cause of errors is probably:
C:\Users\andy.mcadam\AppData\Local\Android\sdk\extras\google\google_play_services\libproject\google-play-services_lib\res
It was the former way to embed the resources into apps. My lib uses #AdditionalJar instead, which is simpler. You should try without this line and also without the following line (it is useless if you pay attention to what's in your manifest and, as a general rule of thumb, never use fixed values for the version):
<meta-data android:name="com.google.android.gms.version" android:value="8487000"/>
If that still fails, you should look at FirebaseAdMob.
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Fyi @Informatix - there seems to be a bug in this lib. I'm finding that GPC.IsConnected always returns false, regardless of the actual connection state. I've also found that GPC.ConnectionState will always return GPC.STATE_DISCONNECTED.

The only exception to this is immediately after GPC_onClientConnected returns. At that point GPC.IsConnected is true, but somewhere after that it gets changed to false. When I've got a few minutes I'll look at the lib code & see if I can figure out what's going on...

- Colin.
 

Informatix

Expert
Licensed User
Longtime User
Fyi @Informatix - there seems to be a bug in this lib. I'm finding that GPC.IsConnected always returns false, regardless of the actual connection state. I've also found that GPC.ConnectionState will always return GPC.STATE_DISCONNECTED.
Not possible. No one would be able to use the library or run the demo if that was true. The problem is probably in your code.
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Not possible. No one would be able to use the library or run the demo if that was true. The problem is probably in your code.
Why? Just because it returns false doesn't mean it's not connected. I'm pretty sure even I can't screw up a call to the IsConnected method in your Connection class. I use this library in several of my apps & I don't have issues with it - except that I get random null pointer exceptions reported.
Exception java.lang.NullPointerException: Attempt to invoke interface method 'void com.google.android.gms.games.achievement.Achievements.unlock(com.google.android.gms.common.api.GoogleApiClient, java.lang.String)' on a null object reference
That's what I was looking in to when I discovered the issue with GPC.IsConnected.

Before I try to check achievements or submit to a leaderboard I call a function to check that I am actually connected to Play Services:
B4X:
Private Sub playServicesActive() As Boolean
    Log($"Connection IsInitialized: ${Connection.IsInitialized}"$)
    Log($"Connection IsConnected: ${Connection.IsConnected}"$)
    Log($"Connection IsSignedIn: ${Connection.IsSignedIn}"$)
    If Connection.IsInitialized And Connection.IsSignedIn Then Return True Else Return False
End Sub

I put the log statements in there today because I changed the If statement to:
B4X:
    If Connection.IsInitialized And Connection.IsConnected And Connection.IsSignedIn Then Return True Else Return False
& it would always return false. The output of the log statements is:
Connection IsInitialized: true
Connection IsConnected: false
Connection IsSignedIn: true
Yet if I go ahead & submit a score or unlock an achievement it works fine - so my conclusion is that IsConnected is returning a false False.

- Colin.
 

Informatix

Expert
Licensed User
Longtime User
Why? Just because it returns false doesn't mean it's not connected. I'm pretty sure even I can't screw up a call to the IsConnected method in your Connection class. I use this library in several of my apps & I don't have issues with it - except that I get random null pointer exceptions reported.

That's what I was looking in to when I discovered the issue with GPC.IsConnected.

Before I try to check achievements or submit to a leaderboard I call a function to check that I am actually connected to Play Services:
B4X:
Private Sub playServicesActive() As Boolean
    Log($"Connection IsInitialized: ${Connection.IsInitialized}"$)
    Log($"Connection IsConnected: ${Connection.IsConnected}"$)
    Log($"Connection IsSignedIn: ${Connection.IsSignedIn}"$)
    If Connection.IsInitialized And Connection.IsSignedIn Then Return True Else Return False
End Sub

I put the log statements in there today because I changed the If statement to:
B4X:
    If Connection.IsInitialized And Connection.IsConnected And Connection.IsSignedIn Then Return True Else Return False
& it would always return false. The output of the log statements is:

Yet if I go ahead & submit a score or unlock an achievement it works fine - so my conclusion is that IsConnected is returning a false False.

- Colin.
Ah OK, I misunderstood.
I'm going to check what's wrong.
 

Informatix

Expert
Licensed User
Longtime User
Why? Just because it returns false doesn't mean it's not connected. I'm pretty sure even I can't screw up a call to the IsConnected method in your Connection class. I use this library in several of my apps & I don't have issues with it - except that I get random null pointer exceptions reported.

That's what I was looking in to when I discovered the issue with GPC.IsConnected.

Before I try to check achievements or submit to a leaderboard I call a function to check that I am actually connected to Play Services:
B4X:
Private Sub playServicesActive() As Boolean
    Log($"Connection IsInitialized: ${Connection.IsInitialized}"$)
    Log($"Connection IsConnected: ${Connection.IsConnected}"$)
    Log($"Connection IsSignedIn: ${Connection.IsSignedIn}"$)
    If Connection.IsInitialized And Connection.IsSignedIn Then Return True Else Return False
End Sub

I put the log statements in there today because I changed the If statement to:
B4X:
    If Connection.IsInitialized And Connection.IsConnected And Connection.IsSignedIn Then Return True Else Return False
& it would always return false. The output of the log statements is:

Yet if I go ahead & submit a score or unlock an achievement it works fine - so my conclusion is that IsConnected is returning a false False.

- Colin.
Bug found. The code of IsConnected should be:
return _State == STATE_CONNECTED || _State == STATE_SIGNED_IN;
Currently, when you're signed in, IsConnected returns false because the current state is one step further.
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Since Connection.IsSignedIn implies there's an active connection, the call to IsConnected is useless.
Yes - assuming that IsSignedIn is set to False if the connection is False - however if for some reason IsSignedIn stays True regardless of the connection state, then that could explain why I'm getting those null pointer exceptions. Not saying that's definitely the case, but trying to find it by a process of elimination...

- Colin.
 

Informatix

Expert
Licensed User
Longtime User
Yes - assuming that IsSignedIn is set to False if the connection is False - however if for some reason IsSignedIn stays True regardless of the connection state, then that could explain why I'm getting those null pointer exceptions. Not saying that's definitely the case, but trying to find it by a process of elimination...

- Colin.
There's a single variable for the state. If you're disconnected, IsSignedIn cannot stay to True since it returns this variable.
And note that Connected/Disconnected does not refer to the state of the network but to the connection to the GooglePlay services.
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
There's a single variable for the state. If you're disconnected, IsSignedIn cannot stay to True since it returns this variable.
And note that Connected/Disconnected does not refer to the state of the network but to the connection to the GooglePlay services.
Ok cool - that eliminates that possibility then. Yep - I'm aware that IsConnected is referring to the GoogleApiClient connection state.

- Colin.
 

Jack Cole

Well-Known Member
Licensed User
Longtime User
The code examples for this library work ok for a single activity app. Most apps are probably multiple activity apps. I was able to figure out how to make it work with multiple activities and connect automatically rather than requiring the user to tap a button.

To do this, I moved the following line from Activity_Create to Activity_Resume:

B4X:
Connection.Initialize(True, False, False, Me, "Connection_Success", "Connection_Failure", True, Debug)

Additionally, I found the existing code in Activity_Resume did not work correctly when returning from another activity. Here is my code in Activity_Resume:

B4X:
    Connection.Initialize(True, False, False, Me, "Connection_Success", "Connection_Failure", True, Debug)
btnSignIn_Click

I also had to change the code in Activity_Pause. If not changed, the choose user dialog pops up every time you return to the activity.

B4X:
If UserClosed then Connection.GPC.Disconnect(False)

This will get it working correctly. I also found it helpful to move the functions in the example app Main activity off to a separate class. It is a lot more convenient than having all that code for the events and so forth in every activity.

One more helpful tidbit. It is best to have the game-ids.xml generated automatically. You can add code in the manifest editor to do this.

B4X:
CreateResource(values, game-ids.xml,
<?xml version="1.0" encoding="utf-8"?>
<!--
Google Play game services IDs.
Save this file as res/values/games-ids.xml in your project.
-->
The rest of your file contents goes here.
)
 
Top