One small note, I realized it was insanely stupid to mark the titles by parts. This makes the post ambiguous to the reader, so instead, I’ll be highlighting the topic in the title from now on.
What is a Player?
A player in a game in a nutshell:
- Can be authenticated -> holds credentials
- Persists longterm data -> Achievements, stats, … etc.
- Engage in multiplayer activities -> no direct implications
I’ll honestly confess, initially I was completely confused how to approach this. With the functionality highlighted above, I still need a player model that will hold the game data itself. Like, what is the hitpoints of the player, what is his next move, etc. Thankfully, we haz teh Internetz.
I got a brilliant answer on gamedev.stackexchange. They are completely different, and with that enlightenment, everything fell into place.
So here is the first lesson:
Make a clear distinction between the in-game player and the human player.
With that distinction made, I now have to different player classes in the code base, each under its respective namespace.
This is actually covered in the previous post… Sorry about this disorganization from my part.
If you get right down to it, a Leaderboard is just a sorted list of players. With the
rating property being part of the
player model, we simply sort the players based on that key, and we are all set.
However, there are a few small gotchas that you might want take note of:
You could potentially be serving 1000s of CCU, so it be best to minimize the database calls and queries. To do that on AppEngine is a very simple task.
Enter the Cron Jobs
Cron jobs are the key here.
Using a cron job, you can basically trigger a scheduled task that will run ever X amount of time. In our case, we can run the leaderboard query, and store the result on memcache, and voila. You got yourself a super efficient cache.
You may also be thinking about computing the leaderboards lazily, meaning whenever you get a leaderboard request, check if you already computed the leaderboards and it didn’t expire. If it is not present or is expired, compute it. This may work very well for single threaded servers, but forget it for GAE and large scale platforms, since you have to deal with transactions, collisions and other crap that you can easily avoid.
My game’s leaderboard does an extra neat thing. It shows an indicator next to the player’s name on whether they are rising up the leaderboard, falling, or there is no change. I honestly had no idea how I am gonna implement that, but while typing this post, I figured it out.
It’s very simple. Simply storing 2 versions of the leaderboard is all that is needed to implement this feature. As you generate the leaderboard, loop over the players, and check their position on the previous leaderboard. The pseudo code is something like:
for current_rank, player in enumerate(current_leaderboard): try: previous_rank = previous_leaderboard.index(player) change = previous_rank - current_rank catch ValueError as e: change = 1
Finally, check if
change is positive, that means the player has gone up. If the change is negative, he has fallen in the ranks. Else, there is no change.
Nice .. We have completed the player concept in this post, and the leaderboard as well. We will hopefully start looking at the Match model next!