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: 449
  • GamePlayServicesLibraryFiles.zip
    56.3 KB · Views: 533
  • EclipseSourceFiles.zip
    87.6 KB · Views: 371
Last edited:

Informatix

Expert
Licensed User
Longtime User
Here's the second alpha version. I fixed two minor bugs and I added most of the classes and functions for Real-Time multiplayer (RealTimeMessage and RealTimeSocket are still empty wrappers however). Let me know if the existing code works well and if the Initialize functions of RoomConfig cover all situations (I didn't test at all the Real-Time and I don't have time to do so).
In this version, I also removed the GooglePlus scope as I do not intend to support it in the future (its interest for a game is too limited).

EDIT: this zip does not include the TurnBasedMatch example, so download also the first zip if you want it.

[File deleted]
 
Last edited:

Computersmith64

Well-Known Member
Licensed User
Longtime User
Here's the second alpha version. I fixed two minor bugs and I added most of the classes and functions for Real-Time multiplayer (RealTimeMessage and RealTimeSocket are still empty wrappers however). Let me know if the existing code works well and if the Initialize functions of RoomConfig cover all situations (I didn't test at all the Real-Time and I don't have time to do so).
In this version, I also removed the GooglePlus scope as I do not intend to support it in the future (its interest for a game is too limited).

EDIT: this zip does not include the TurnBasedMatch example, so download also the first zip if you want it.

Hi Informatix. Have you implemented Leaderboards & Achievements in this alpha?

- Colin.
 

peacemaker

Expert
Licensed User
Longtime User
Interesting. Is it only for gamers who registered in Google+ ?
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Yes and no. Since the first alpha, all functions are there but they may be unusable because of missing classes or unwrapped types. For now, only Turn-based matches, intents, and (for the most part) Real-Time are functional. But all this needs testing.

So I've been converting the code in my existing test app over to suit the changes you've made, but I can't find the OnRoomCreated listener anywhere. When I look in your RoomConfigWrapper.java file, I see an empty stub for it & I can't see anywhere else that I would get the room object to use in a real-time multiplayer game. Or have you implemented it differently?

Thanks - Colin.
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
So I've been converting the code in my existing test app over to suit the changes you've made, but I can't find the OnRoomCreated listener anywhere. When I look in your RoomConfigWrapper.java file, I see an empty stub for it & I can't see anywhere else that I would get the room object to use in a real-time multiplayer game. Or have you implemented it differently?

Thanks - Colin.

Arrrgh! Forget it - I was looking at the Alpha 1 code. DOH!
 

Informatix

Expert
Licensed User
Longtime User
Arrrgh! Forget it - I was looking at the Alpha 1 code. DOH!
I think that I'll move all events from RoomConfig to GamesClient so this class will be the general dispatcher.

After many tests of my Connection class, here's the code that should cover almost all situations for the Sign-in button:
B4X:
If GPC.IsSignedIn Then
     If GPC.CheckConnections(True) Then
           GPC_onSignInSucceeded
     End If
Else
     GPC.Connect(True)
End If
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
I think that I'll move all events from RoomConfig to GamesClient so this class will be the general dispatcher.

After many tests of my Connection class, here's the code that should cover almost all situations for the Sign-in button:
B4X:
If GPC.IsSignedIn Then
     If GPC.CheckConnections(True) Then
           GPC_onSignInSucceeded
     End If
Else
     GPC.Connect(True)
End If


Hmmm - so I'm getting a "java.lang.NoSuchMethodError: com.google.android.gms.games.GamesClient$Builder.setShowConnectingPopup" error in my GPC.Initialize call...
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
Probably because your google-play-services.jar is not the latest version.

Yep - that was it... Btw - might be useful for people to know that the version # required in the ids.xml for v14 of google-play-services.jar is 4132500. You'll get an exception that tells you this if you have the wrong version # in there, but knowing the correct one in advance saves you a wasted compile & run... :)
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
OK - so far I have been able to successfully implement & test the login/logout & the leaderboard functionality (good job, btw), but I'm stumped when it comes to achievements. The signature for the LoadAchievements method is:

B4X:
LoadAchievements(OnAchievementsLoadedListener listener, boolean forceReload)

& I don't know what to provide for the listener parameter. Previously, it would just raise the _onachievementsloaded listener, so I only had to pass the forceReload parameter. Any hints as to what I should provide for the listener parameter?

Thanks - Colin.
 

Informatix

Expert
Licensed User
Longtime User
Yep - that was it... Btw - might be useful for people to know that the version # required in the ids.xml for v14 of google-play-services.jar is 4132500. You'll get an exception that tells you this if you have the wrong version # in there, but knowing the correct one in advance saves you a wasted compile & run... :)
???
1) You don't have to put this value in ids.xml.
2) ids.xml (or games-ids.xml as it should be named properly) is automatically generated by a link in the dev console and it does not include the version of the jar.
3) You are probably using what's called a "trick" in the StackOverflow forum because many people there have no idea of what they are doing, and their "trick" is in fact full of side effects (look at your unfiltered log and you'll probably see a lot of errors; try to add AppState to scopes and your app will probably crash). You have to set up your application as described in this post.
 

Informatix

Expert
Licensed User
Longtime User
OK - so far I have been able to successfully implement & test the login/logout & the leaderboard functionality (good job, btw), but I'm stumped when it comes to achievements. The signature for the LoadAchievements method is:

B4X:
LoadAchievements(OnAchievementsLoadedListener listener, boolean forceReload)

& I don't know what to provide for the listener parameter. Previously, it would just raise the _onachievementsloaded listener, so I only had to pass the forceReload parameter. Any hints as to what I should provide for the listener parameter?

Thanks - Colin.
As I said: "all functions are there but they may be unusable because of missing classes or unwrapped types. For now, only Turn-based matches, intents, and (for the most part) Real-Time are functional."
 

Computersmith64

Well-Known Member
Licensed User
Longtime User
???
1) You don't have to put this value in ids.xml.
2) ids.xml (or games-ids.xml as it should be named properly) is automatically generated by a link in the dev console and it does not include the version of the jar.
3) You are probably using what's called a "trick" in the StackOverflow forum because many people there have no idea of what they are doing, and their "trick" is in fact full of side effects (look at your unfiltered log and you'll probably see a lot of errors; try to add AppState to scopes and your app will probably crash). You have to set up your application as described in this post.

So the only difference between what you're doing & what I'm doing is that you are placing the version # in the "version.xml" & I'm placing it in the "ids.xml". I have been running the version # in ids.xml since it became a requirement with google-play-services.jar v13 & haven't had an issue with it. Looking at the unfiltered logs, I'm not seeing any errors as a result & to be honest, I don't see why having it in "ids.xml" as opposed to "version.xml" would make a difference. It still ends up being referenced by the manifest.

I have changed it now to the way you suggest - net result being that it works exactly the same.

Thanks - Colin.
 

Informatix

Expert
Licensed User
Longtime User
So the only difference between what you're doing & what I'm doing is that you are placing the version # in the "version.xml" & I'm placing it in the "ids.xml". I have been running the version # in ids.xml since it became a requirement with google-play-services.jar v13 & haven't had an issue with it. Looking at the unfiltered logs, I'm not seeing any errors as a result & to be honest, I don't see why having it in "ids.xml" as opposed to "version.xml" would make a difference. It still ends up being referenced by the manifest.

I have changed it now to the way you suggest - net result being that it works exactly the same.

Thanks - Colin.
When I started rewriting the library, I did exactly what you did. Ok, it seemed to work fine. BUT when I added the AppState scope -> crash. I tried to understand why and discovered that I need to add many resources to my application. The library alone is not enough. By looking at the full unfiltered log with the monitor tool of the SDK, I realized that I had a lot of errors (resource not found) despite it was working pretty well with only the Game scope. So NO it's not the same doing one way or the other.

Edit: With my suggested method, you don't (and won't) have to edit any XML file or change a value in the manifest.
 
Last edited:

Computersmith64

Well-Known Member
Licensed User
Longtime User
When I started rewriting the library, I did exactly what you did. Ok, it seemed to work fine. BUT when I added the AppState scope -> crash. I tried to understand why and discovered that I need to add many resources to my application. The library alone is not enough. By looking at the full unfiltered log with the monitor tool of the SDK, I realized that I had a lot of errors (resource not found) despite it was working pretty well with only the Game scope. So NO it's not the same doing one way or the other.

Edit: With my suggested method, you don't (and won't) have to edit any XML file or change a value in the manifest.

OK - I'll take your word for it. :) On another note, I'm trying to set up a random realtime multiplayer game but I'm a little confused about the process using your library. The way I did it with the old library was to initialize a RoomConfig object, set the random play match criteria using setMatchCriteria, & then call CreateRoom. I'm trying to do the same thing, however I'm getting a null pointer exception when I pass my RoomConfig object to CreateRoom. Here's the code:

B4X:
GRC.Initialize("", False)
GRC.CreateAutoMatchCriteria(1, 1, 0)
GGS.CreateRoom(GRC)

I suspect the issue is that I'm passing an empty string to the Initialize in the InvitationId parameter, but at this point I don't have an InvitationId because I'm trying to create a random match room. What am I missing here?

Thanks - Colin.
 

Informatix

Expert
Licensed User
Longtime User
The way I did it with the old library was to initialize a RoomConfig object, set the random play match criteria using setMatchCriteria, & then call CreateRoom.

I didn't reproduce the previous way of initializing the objects because the configuration class was not clearly separated from the functions using its result (e.g. what does CreateRoom do in RoomConfig?). I also simplified the configuration by creating four Initialize functions that are supposed to cover all cases.
So the process for an auto-match is (for example):
B4X:
Dim Config As GPlayRoomConfig
Dim Criteria As Object = Config.CreateAutoMatchCriteria(1, 1, 0)
Config.InitializeWithAutoMatch(Null, Criteria, False)
GC.CreateRoom(Config)
With this approach, you can have different configurations, but only one call to CreateRoom whatever configuration you chose.

Before continuing on Real-Time classes, I'd like to know whether my Initialize functions cover all cases. I have no interest in Real Time, so I won't probably test this part.
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
Here's the Alpha 3. I added new classes for a partial support of Achievements and Leaderboards but I cannot guarantee these additions work as they are completely untested. Connection + TurnBasedMatch classes and functions, on the contrary, have been fully tested and works very well.
As this alpha version includes all what I need for my current project, I won't improve and publish any new version before extended tests are done by other users.

[File deleted]
 
Last edited:

Computersmith64

Well-Known Member
Licensed User
Longtime User
I didn't reproduce the previous way of initializing the objects because the configuration class was not clearly separated from the functions using its result (e.g. what does CreateRoom do in RoomConfig?). I also simplified the configuration by creating four Initialize functions that are supposed to cover all cases.
So the process for an auto-match is (for example):
B4X:
Dim Config As GPlayRoomConfig
Dim Criteria As Object = Config.CreateAutoMatchCriteria(1, 1, 0)
Config.InitializeWithAutoMatch(Null, Criteria, False)
GC.CreateRoom(Config)
With this approach, you can have different configurations, but only one call to CreateRoom whatever configuration you chose.

Before continuing on Real-Time classes, I'd like to know whether my Initialize functions cover all cases. I have no interest in Real Time, so I won't probably test this part.

Hi Informatix,

So following your steps, I'm getting the following error on the GC.CreateRoom(Config) line (I've included the debug messages from the library leading up to the error):

RoomCfg_InitializeWithAutoMatch: Bundle[{max_automatch_players=1, min_automatch_players=1, exclusive_bit_mask=0}]
CreateRoom: flm.b4a.googleplay.RoomConfigWrapper@428d2a30
java.lang.NullPointerException
at flm.b4a.googleplay.GamesClientWrapper.CreateRoom(GamesClientWrapper.java:299)
at com.airlinemates.yahtzeetest.main._gplaymulti_random_game(main.java:3333)
at com.airlinemates.yahtzeetest.main._btnstart_click(main.java:1519)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:173)
at anywheresoftware.b4a.BA.raiseEvent2(BA.java:161)
at anywheresoftware.b4a.BA.raiseEvent(BA.java:157)
at anywheresoftware.b4a.objects.ViewWrapper$1.onClick(ViewWrapper.java:66)
at android.view.View.performClick(View.java:4438)
at android.view.View$PerformClick.run(View.java:18422)
at android.os.Handler.handleCallback(Handler.java:733)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)

Maybe it doesn't like the Null being passed in the InitializeWithAutoMatch?

- Colin.
 
Top