B4A Library Google Play Game Services

First off, my apologies on my Wrapping Skill, this is no where near as polished as from the big boys of wrapping here. Also, many thanks to Erel for helping me get through this.

The story: I need this library, and it is working for me. If you know what it is, and have been waiting for it, hopefully my tutorial will help you get going with it. If you don't know what this library does, or if you need it, check out here

Right now, this library can handle Leaderboards, Achievements, Room Creation/Communication (have not done RealTimeSocket yet), One-Click signIn, and Anti-Piracy Checks. Did not implement Cloud Save.

Still interested? Read on.

First, please review Google's branding guidelines, as there is no way I can keep that up to date on this thread. Here

Next, I recommend just studying the flow of events here particularly the Developer's Guide section.

Still awake? Good.

Before any of this works, you need to register your app within your Developer Account, to get the OAuth necessary. The Google/Java speak instructions are in that developer's guide.

For B4A speak. Must do the following:

1. Follow these directions for adding your app to the console here.

** EDIT - See Erel's post below on how to grab SHA1 fingerprint straight from B4A... **

When you get to the part about adding your SHA1 fingerprint, it's time to break out your command prompt. (If you already know how to get your SHA1 fingerprint, then continue on) First, find out where (or make a new) password file for B4A is kept by looking at your B4A->Tools->Private Sign Key.

Then in your command prompt, change to the directory with keytool in it (for me that is "c:\Program Files\Java\jre6\bin") and type
B4X:
keytool -exportcert -keystore <path-to-debug-or-production-keystore> -list -v
which for me would be
B4X:
keytool -exportcert -keystore C:\Programming\b4a.keystore -list -v
A prompt asking for your password should then pop up. Type your password in that you used to create it(shown on your B4A->Private Sign Key window)

Then, enter that SHA1 fingerprint (type it, copy paste it... I copy the whole command prompt to notepad, and then copy/paste it from there) into the final OAUTH Step and you should get back that your app is linked.
Note the code back will look something like this -> 211205627476-74off6bsgue1qbcka2878p3lurctabft.apps.googleusercontent.com ONLY THE FIRST PART BEFORE THE HASH IS YOUR APP_ID FOR USE IN THE XML FILE

2. Create your new B4A project and include this in your manifest:
AddApplicationText(
<meta-data android:name="com.google.android.gms.games.APP_ID"
android:value="@string/app_id" />
)

3. Create an XML file in your B4A (using your favorite text editor) project called ids.xml in your projectdirectory-> Objects -> res -> values folder, and put something like this in it
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2013 Google Inc.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<!-- TODO: Replace this by your app's app ID! -->
<string name="app_id">ReplaceME</string>
</resources>

Put your AppID obtained from before in the ReplaceMe section and then make sure to set the file to read only (just edit the file included in the sample app)

4. Now it is time for the achievements/leaderboard ID's Need to do that here and here

In the sample app created I use two leaderboards (easy and hard) and four achievements (put in five if you need to continue in your Developer account, but not required to use them all) the four achievements are:
B4X:
Dim easyLeaderboard As String = "ReplaceMe"
Dim hardLeaderboard As String = "ReplaceMe"
Dim EasyAchievement As String = "ReplaceMe" 'for playing a game of easy level
Dim HardAchievement As String = "ReplaceMe" 'for playing a game of hard leve
Dim IncrementAchievement As String = "ReplaceMe" 'for playing 10 games (either hard or easy)
Dim HiddenAchievement As String = "ReplaceMe" 'not visibile until hard and easy games played

Follow these directions, but use these sample Achievements and Leaderboards for the sample application and fill in with your own pithy comments, and Replace the ReplaceMe's for each one in the sample app **Note the IncrementAchivement is of type increment and I used 10 as the amount. The HiddenAchivement is of type Hidden, and you can see in the code how I went about unlocking it**

5. Now time for a choice, either download the attached xml/jar files that go in your library, or (advanced mode) download and compile the attached project source files. Either way, must have gameplayservices.jar, gameplayservices.xml (which are the library wrappers) installed in your Libraries folder and then have gameplayservices selected in B4A. Also, I am a big fan of having a bit of something in my demos, so you will also need Informatix' most excellent AnimationPlus library installed (do search for latest link) and Agraham's superbly efficient ByteConverter library (again, do search).

6. Not sure why, but in order to get the required dependency file "googleplayservices.jar" You must follow the directions here to download the SDK. Then, grab that file and put it in your library. (the file itself is a bit above the limit for me to include here, and I'm not sure about the legal ramifications of including it by itself after reading here) If anybody can tell me otherwise, I'll post it someplace else for easier access to download.

7. Three modules are included in the sample app. The main module is just the basics of logging in (once you get past that, then it's time for the other two) and displaying the SignIn button. The other two modules are my own take on the Type A Number and Button Clicker sample apps (no typing of numbers in my version, but I do add displaying player icons, more robust message passing)

8. Logic/documentation. As I said, trying to figure out how to wrap all of this was a very good challenge/learning process for me. As some of these wrappers included nested classes, and I didn't know exactly how to approach, I made the nested classes separate classes for exposing to B4A. What does that mean? Well, some of the things that happen "behind the scenes" in the original library, I had to make a choice on where they happen in my wrapper. I finally decided that almost anything to do with Room Stuff, happens in the gRoomConfig wrapper (As opposed to RoomConfig, Room and GamesClient in original flow). My samples show a lot of what was wrapped, but if you have a question about a function, and you go to look for more detail on Google's developer site, Where that function is exposed may be different in this library, and sometimes not at all. (If not exposed, it is generally something I didn't see how to write a wrapper for that functionality, at least not yet, but again... it's working for me with a lot of what I wanted to accomplish) I am also including all of the source files, which may help out for those looking to try and learn how to wrap things in the future. Please forgive my poor coding logic in advance. Also, some things are exposed that don't do anything (at least not for me) and are there to show where I think they should be exposed moving forward (most notably the RealTimeSocket stuff), but they are annotated with the hint system as not being ready for prime time!

Of note, many, many listeners in this, and I did include code in the Java to explain many of them, but I don't see how to bring up those hints when using the "Sub "space-Tab" auto-fill" feature.

9. Going forward - I am sure I will be updating some things as I go along, but I don't know at what pace, and who knows, maybe RTS's are really, really important to someone, or the ability to do cloud saving. So, I also am including the source files that I used to wrap. While a learning tool in and of themselves, I do ask, that if you add any functionality, that you pass those additions back to the community. I think we all benefit from using these libraries that others create, and let's face it, sometimes having that 1 library wrapped makes all the difference.. and until Erel figures out how to auto-wrap any library out there... some of us come to a halt going down certain paths unless someone out there can help boot strap our project. (which is why I started this in the first place :) ) Also, please note you'll probably see some ("why is THAT there") in the source code, and it's because I learned a LOT doing this, and changed how some implementations worked as I kept building wrapper classes. I would do it all from scratch to make it cleaner... but I need to get going on the projects that I built this for!
 

Attachments

  • GoogleGameServiceSampleApp.zip
    20.5 KB · Views: 450
  • GamePlayServicesLibraryFiles.zip
    56.3 KB · Views: 536
  • EclipseSourceFiles.zip
    87.6 KB · Views: 373
Last edited:

Informatix

Expert
Licensed User
Longtime User
At this point I am interested in sending messages (other than notifications).

Not possible with turn-based matches. A workaround is to take a turn to send the message (in the match data) to the other player, and either wait for the confirmation of the reception (the other player takes automatically his turn when the message is received so the order of play is not changed) or set the next participant Id to the sender of the message, so the order of play does not change (but you'll get no confirmation of reception).
 

Bas Hamstra

Member
Licensed User
Longtime User
To you last question: No. So I guess point 3 just works as designed, no real issue.

Point 2: while both emulators have the testapp running, the emulators receive nothing in the notification bar. Info about accepted invites, taken turns, ended match, declined match etc. are only visible after the "Status" button is clicked.

Then on the phone sleeping in my pocket (using the same G+ account as 1 of the emulators) I get notifications in the notification bar. I consider this part kind of normal: you only get real notifications in the notification bar if the app is NOT open.

I will do some more testing and see what I can put in the turndata.

Kind regards,

Bas
 

Informatix

Expert
Licensed User
Longtime User
Point 2: while both emulators have the testapp running, the emulators receive nothing in the notification bar. Info about accepted invites, taken turns, ended match, declined match etc. are only visible after the "Status" button is clicked.

It's normal since I redirect all these notifications to the app with GC.RegisterInvitationListener and GC.RegisterMatchUpdateListener.

Then on the phone sleeping in my pocket (using the same G+ account as 1 of the emulators) I get notifications in the notification bar. I consider this part kind of normal: you only get real notifications in the notification bar if the app is NOT open.

Normal again. When signed out, all notifications are received by the Google Play service in the background.

Look at the log. Do you see the incoming onTurnBasedMatchReceived event when the other player takes his turn?
 

Bas Hamstra

Member
Licensed User
Longtime User
Hi Frederick,

Your testapp is a wonder of simplicity, very clean and easy to understand. I appreciate a lot that you kept the code short, so I don't get lost. Also this TBM functionality is exactly what I am after.

I DO think there is an issue though. I have been stress-testing the testapp and I am convinced messages get lost sometimes. I think I understand the testapp enough to know that when P1 has taken a turn then P2 fires a handler and the screen/UI is updated. But after a random number of correct turns on both devices suddenly nothing happens. Both players say "its their turn".

Say I wait 2 minutes. Still nothing. Then when I click the "status" button of the player who's turn it is supposed to be, there is a message saying "my turn started 2 minutes ago". When I click that message, then and only THEN the "Take Turn" button is enabled. It's as if the handler does not fire: the log does not show the handler fires (which it DOES do as long as it works correctly).

<disclaimer: still using GenyMotion, since there's no other way I can test>

Could you try to reproduce? Just take 30 turns or so, and see it keeps working...

Bas Hamstra
 

Informatix

Expert
Licensed User
Longtime User
Could you try to reproduce? Just take 30 turns or so, and see it keeps working...

To simulate a match between two devices, I appended the following lines to GPC_onTurnBasedMatchReceived:
B4X:
If Match.TurnStatus = Match.MATCH_TURN_STATUS_MY_TURN AND btnTakeTurn.Enabled Then
    btnTakeTurn_Click
End If
To start the match, I had to click on Sign in and Quick match, of course, to initiate the match and on Take Turn (only once) for the first player to play.
The data are sent and received via a mobile network with a fluctuant signal switching between 3G and 3G+. After 133 exchanges (Match.Version = 133), the game stopped on an error 5 on one device (STATUS_NETWORK_ERROR_OPERATION_DEFERRED), then restarted automatically. Currently, Match.Version = 203.
 

Informatix

Expert
Licensed User
Longtime User
But after a random number of correct turns on both devices suddenly nothing happens. Both players say "its their turn".
This happens constantly for the following reason:
- it's my turn, so I send data
- the status is refreshed when the updated event is raised
- my screen displays "it's their turn"
- the other player while waiting the data is still in Not-My-Turn state, so its screen displays also "it's their turn"
- when data are received, the status is refreshed and the two devices show different status
- then the other player sends his data and while data are transfered to my device, both devices show the same status again
 

Bas Hamstra

Member
Licensed User
Longtime User
Thank you for investigating this so fundamentally. The app is simply brilliant for sorting issues out now, in stead of being part of a game.

(Note that I did it not via 3G, but on a completely wired network, via 2 GM emulators, to be precise)

Kind regards,

Bas
 

Bas Hamstra

Member
Licensed User
Longtime User
I noticed one of my emulators had 2Gig and the other 1 Gig, my comp has 4Gig. That's maybe a bit hard on resources, with a browser and B4a open. Set both emus to 1 Gig and can't find anything wrong now. Everything works perfect!

Wow, it seems I no longer have an excuse to NOT continue developing my board game :)

Kind regards and it's SO cool you made this available.

Bas
 

Informatix

Expert
Licensed User
Longtime User
Here's the last alpha version (0.7). I think that my work is finished with Leaderboards but they are completely untested. I stop working on this library because Google just published a new version (v15) which deprecates all my work (they replaced the current classes and functions with a new API). I won't rewrite everything once more.

[File deleted]
 
Last edited:

Bas Hamstra

Member
Licensed User
Longtime User
Hi Frederick,

That's terrible, because TBM functionality fits a big gap for me. I wonder what this means for the TBM functionality? Does this mean it can't be trusted to work on all devices anymore? Maybe if we can specify the GPS version parameter = 14 in some XML we can "freeze" the functionality and the lib would comletely keep it's value? If I am not mistaken I remember we can specify the GPS version parameter, but I forgot where.

I can't imagine the existing games in the market with GPS will suddenly stop working...surely there must be some way of backward compability? If so, why don't we just ignore V15 for the moment? It wouldn't bother me for a second if there's a new version, as long as the old one works...

Kind regards,

Bas Hamstra
 

Informatix

Expert
Licensed User
Longtime User
Hi Frederick,

That's terrible, because TBM functionality fits a big gap for me. I wonder what this means for the TBM functionality? Does this mean it can't be trusted to work on all devices anymore? Maybe if we can specify the GPS version parameter = 14 in some XML we can "freeze" the functionality and the lib would comletely keep it's value? If I am not mistaken I remember we can specify the GPS version parameter, but I forgot where.

I can't imagine the existing games in the market with GPS will suddenly stop working...surely there must be some way of backward compability? If so, why don't we just ignore V15 for the moment? It wouldn't bother me for a second if there's a new version, as long as the old one works...

Kind regards,

Bas Hamstra
The old library will work as long as Google decide it can work. For now, it is usable and as you said, there are many games already using it so it wouldn't be a good idea for Google to make it unusable or unreliable. But that means for us that we have to keep a copy of the folder containing the version 14 because if we update the SDK files, we will replace them by the new files, which do not work with my version (I tried and there are some changes that produce errors).
 
Last edited:

Bas Hamstra

Member
Licensed User
Longtime User
I made a copy of this directory:

C:\Program Files (x86)\Android\android-sdk\extras\google\google_play_services\libproject

To my D-drive, just in case. It's under 2Mb, so could theoretically be distributed together with B4A lib and testapp. Integrating multiplayer functionality into an existing app has NEVER been easier than today, thanks to you. I am halfway, but I have to rethink the (rather basic) UI of my boardgame a bit. Up till now I have spent most of my time in improving the computer player (have written a GM level chess program in the past, so that part I know).

Bas
 

Informatix

Expert
Licensed User
Longtime User
To my D-drive, just in case. It's under 2Mb, so could theoretically be distributed together with B4A lib and testapp.

No, it's too big. We have to send the file to Erel if we want to make it available as a downloadable file on the forum. Since my own copy does not include all languages (I removed the ones I don't plan to use in my projects), it's better to send another copy, more complete.
 

walterf25

Expert
Licensed User
Longtime User
Hi Frederick, i'm trying to use your library in a libgdx game i'm working, i can sign in succesfully without a problem but when i try to submit a score my application crashes, i'm not too familiar with how to use every function in your library so maybe i'm doing something wrong, can you maybe provide a small example on how to award the achievements and how to implement the leaderboard function, i would really appreciate it.

thanks,
Walter
 

Informatix

Expert
Licensed User
Longtime User
can you maybe provide a small example on how to award the achievements and how to implement the leaderboard function, i would really appreciate it.

thanks,
Walter
I'm afraid that I have no time to do this. That's why I asked for testers for this library. The method to use is the same as for Java users, so the Google documentation should help you (if it is not updated yet for the new API).

PS: my first name is Frédéric (without k).
 

iwan.deejay

Member
Licensed User
Longtime User
Hii NFOBoy,

It is a great and hard work. Thank!

I followed your steps , and compiled a APK after many try errors :))

but , it cause a error (ANR) after click login google in my mobile .

I can not catch some useful information from the log . the error looks like google service JAR internal.

LogCat connected to: 0123456789ABCDEF
--------- beginning of /dev/log/main

--------- beginning of /dev/log/system

** Activity (main) Create, isFirst = true **

** Activity (main) Resume **

** Activity (main) Pause, UserClosed = false **

** Activity (main) Resume **

** Activity (main) Pause, UserClosed = false **

** Activity (main) Resume **

java.lang.IllegalStateException: A fatal developer error has occurred. Check the logs for further information.

at com.google.android.gms.internal.de$f.a(Unknown Source)
at com.google.android.gms.internal.de$f.a(Unknown Source)
at com.google.android.gms.internal.de$b.be(Unknown Source)
at com.google.android.gms.internal.de$a.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4624)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:965)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:732)
at dalvik.system.NativeStart.main(Native Method)


Could you get me any advice? Thanks!

Have you managed to overcome the error?
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Hi Frederick, i'm trying to use your library in a libgdx game i'm working, i can sign in succesfully without a problem but when i try to submit a score my application crashes, i'm not too familiar with how to use every function in your library so maybe i'm doing something wrong, can you maybe provide a small example on how to award the achievements and how to implement the leaderboard function, i would really appreciate it.

thanks,
Walter

Hi Walter,

Here is an example of how I check achievements in my Yahtzee! game. This game uses a modified version of the original wrapper that NFOBoy wrote, but it is functionally very similar to what Informatix did - so you should be able to figure it out. Here's what I do for checking achievements that can be awarded at the end of a game (like number of games completed):

B4X:
Sub Check_Games_Achievements
    Dim ach As gAchievement

   
    Try
        For Each ach As gAchievement In gAL 'gAL is a globally declared list that holds the achievements downloaded from Play Services
            If ach.AchievementId = g10GameAchievement Then
                If ach.STATE <> ach.STATE_UNLOCKED Then
                    If cStats.mOverallStats.mGamesCompleted >= 10  Then
                        GGS.GamesClient.unlockAchievement(g10GameAchievement)
                        GGS.GamesClient.revealAchievment(g25GameAchievement)
                        bUnlockedAchievement = True
                    End If
                End If
            Else If ach.AchievementId = g25GameAchievement Then
                If ach.STATE <> ach.STATE_UNLOCKED Then
                    If cStats.mOverallStats.mGamesCompleted >= 25 Then
                        GGS.GamesClient.unlockAchievement(g25GameAchievement)
                        GGS.GamesClient.revealAchievment(g50GameAchievement)
                        bUnlockedAchievement = True
                    End If
                End If
            Else If ach.AchievementId = g50GameAchievement Then
                If ach.STATE <> ach.STATE_UNLOCKED Then
                    If cStats.mOverallStats.mGamesCompleted >= 50 Then
                        GGS.GamesClient.unlockAchievement(g50GameAchievement)
                        GGS.GamesClient.revealAchievment(g100GameAchievement)
                        bUnlockedAchievement = True
                    End If
                End If
            Else If ach.AchievementId = g100GameAchievement Then
                If ach.STATE <> ach.STATE_UNLOCKED Then
                    If cStats.mOverallStats.mGamesCompleted >= 100 Then
                        GGS.GamesClient.unlockAchievement(g100GameAchievement)
                        bUnlockedAchievement = True
                    End If
                End If   
            End If
        Next
    Catch
        Send_Error_Report("Check_Games_Achievement", LastException.Message)
    End Try
   

End Sub

Basically, I loop through the list of achievements & for those that are not yet unlocked, I see if my test criteria (cStats.mOverallStats.mGamesCompleted) allows that achievement to be unlocked. If it does, then I unlock the achievement (& unhide the next one if appropriate). Unlocking the achievement causes the Play Services API to create a notification that slides down from the top of the screen to tell the player they have unlocked the achievement. I use the bUnlockedAchievement flag to tell me to show the Play Services Achievements screen that displays all the player's achievements. This is done by a simple call to GGS.showAchievements.

For achievements you want to award during the game (eg: when the player gets their first Yahtzee), it's basically the same process, but in a different sub that you would call at the appropriate time during the game - note that I call the above (Check_Games_Achievements) sub only once at the end of the game, whereas I call the Check_Score_Achievements every time I process the results of a roll, & every time the player moves to the next turn:

B4X:
Sub Check_Score_Achievements
    Dim ach As gAchievement
   
    Try
        For Each ach As gAchievement In gAL
            If ach.AchievementId = gOneYahtAchievement Then
                If ach.STATE <> ach.STATE_UNLOCKED Then
                    If lblYahtzeeScore.TextColor = Colors.Red AND CInt(lblYahtzeeScore.Text) = 50 Then
                        GGS.GamesClient.unlockAchievement(gOneYahtAchievement)
                        GGS.GamesClient.revealAchievment(gDoubleYahtAchievement)
                        bUnlockedAchievement = True
                    End If
                End If
            Else If ach.AchievementId = g300ClubAchievement Then
                If ach.STATE <> ach.STATE_UNLOCKED Then
                    If CInt(lblGrandTotal.Text) >= 300 Then
                        GGS.GamesClient.unlockAchievement(g300ClubAchievement)
                        GGS.GamesClient.revealAchievment(g400ClubAchievement)
                        bUnlockedAchievement = True
                    End If
                End If
            End If
        Next
    Catch
        Send_Error_Report("Check_Score_Achievements", LastException.Message)
    End Try
   
End Sub

Submitting to the leaderboard is very easy - you simply call GGS.GamesClient.submitScore & provide the leaderboard ID (a string value) as the first parameter & the score as the second parameter. Note that the score must be a long. You can easily convert an int to a long using:

B4X:
Sub cLng(o as Object) as long
    Return floor(o)
End Sub

Hope this helps. It's not too difficult to figure out once you get started - hell, I did it & I'm a dummy!

- Colin.
 

lucad

Member
Licensed User
Longtime User
Informatix library didn't work for me. I was not able to initialize a GameClient , maybe because of a bad GPgS connection setup.
Only when I tried the original NFOBoy library, GooglePlay finally welcomed me with a message ( Game and user were recognized correctly ).

Now I hope you can help me, as right after, I was stuck again when managing a Leaderboard.
Is the leaderboard linked to a mandatory achievement set-up ? I didn't configure the achievements because I was only trying record a score value.
Could you post a draft/rough example about how we could manage a leaderboard ?

Regarding to Informatix changes,
I do believe I made something wrong, can you post some details about your latest lib ?

thank you all for sharing,
Luca
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Informatix library didn't work for me. I was not able to initialize a GameClient , maybe because of a bad GPgS connection setup.
Only when I tried the original NFOBoy library, GooglePlay finally welcomed me with a message ( Game and user were recognized correctly ).

Now I hope you can help me, as right after, I was stuck again when managing a Leaderboard.
Is the leaderboard linked to a mandatory achievement set-up ? I didn't configure the achievements because I was only trying record a score value.
Could you post a draft/rough example about how we could manage a leaderboard ?

Hi Luca. Yes, you do need to create achievements in order to be able to use a leaderboard. Play Services won't let you have a leaderboard without achievements. I think from memory (& I could be wrong) that you need to have at least 3 achievements for each leaderboard. Are you looking for code to manage the leaderboard? If so, it's pretty easy. Once your player is signed in to Play Services, to submit a score all you need to do is:

B4X:
Sub Globals
    Dim leaderBoardID as String = "your leaderboard ID here"
End Sub

Sub submitScore
    GGS.GamesClient.submitscore(leaderBoardID, score)
End Sub

Remember that the score has to be a long data type - so if you are using an int (or other data type variable) to keep your score in, you'll need to convert it to a long. You can use the simple sub I put in post #98 above to do this.

Hope this helps.

- Colin.
 
Top