Have you just built your board game for Android? Want to get it online? You are in the right place — let’s build it together!
We are going to implement match-making with the Firebase Realtime Database in this article. For that, you need Firebase setup in your Android project — see how to do that here.
- Firebase users — here, we are assuming you were able to login your user into Firebase using any method. This is necessary to identify whom to communicate with after the match-making is finished.
- Gradle Dependencies — Add these to the
dependenciesblock in your app module's grade file.
implementation 'com.google.firebase:firebase-core:16.0.6' implementation 'com.google.firebase:firebase-auth:16.1.0' implementation 'com.google.firebase:firebase-database:16.0.6'
- You aren’t done after implementing match-making. That’s because you’ve only found two players that will play — but haven’t implemented how their moves will propagate over the network.
We will use a node in the Firebase database called the game room which will store all the active challenges that users have pushed. Each user will search for existing challenges within the game room and will accept the first one found. Otherwise, the user will upload their own challenge and wait until someone accepts the challenge.
You will be able to add more features in your match-making implementation like matching based on similar performance ratings, friends, regional-bias, etc.
How the heck are we gonna implement that?
I’ve divided our problem into three objects:
- Matcher: Finds any existing matches in the game room.
- SelfChallengeManager: Manages the challenge a user uploads if Matcher fails to find any one.
- SelfChallengeCanceller: Cancels the match-making process if this user doesn’t wanna play anymore.
In addition, we will require a “Challenge” object that has two properties — communication node reference and the challenger’s user ID (get why we need to login to Firebase now?). This object will be uploaded into the game-room by
Writing our components first
Before writing our three components, we need to understand what a Firebase transaction is. We don’t want two users accepting the same challenge at the same time — which would corrupt our database and make our precious users angry. Transactions come to the rescue by preventing concurrent operations on a node in the database (which will be the game room).
How does that relate to our components? — Our components will be modeled as transactions as inner classes in our ‘FirebasePlayerMatchMaker’ class.
We use two callback interfaces —
OnMatchMadeCallback and internally
OnFailCallback. The factory method takes a
OnMatchMadeCallback which is called whenever a match is made. The
OnFailCallback is invoked whenever MatchMaker fails to find a match.
findMatch runs on a separate thread and creates an
Matcher doesn't find a match. In that case, we have to create a
SelfChallengeManager and run it as a transaction.
doTransaction() method iterates over all the children of the game room node and searches for a
Challenge that is compatible for our user. By default,
true, but you can change that by adding additional constraints like ratings. The first compatible challenge is then stored and the challenge node is deleted (by setting the value to
null) in the database. Note that by deleting the node, the other player will be notified of the acceptance.
doTransaction method adds a child node to the
GAME_RECORD node in our database. This is where the game moves will be communicated after the match-making. It then uploads a
Challenge to the game room and adds itself as a
ValueEventListener. Whenever another user accepts the request, they will delete this node and this user will get notified as we are listening.
Our code just deletes the node created in by
SelfChallengeManager by iterating over all the game room and finding our challenge. BUT IT’S INCOMPLETE!! You (optionally) should add a feature where the this user automatically resigns from the game if the match was accepted already.
OnMatchMadeCallback you provide to
FirebasePlayerMatchMaker.newInstance, you must initialize the game communication to be done in the node (by path)
Yo, you’ve done it. Thanks for reading!
Originally published at hashnode.com.