Advertisement

Future proof scoreboard server design pattern

Started by March 23, 2015 09:21 PM
12 comments, last by hplus0603 9 years, 7 months ago

I'm implementing an iOS game that has Game Center support, but it also has access to a database server that is controlled by me. That server maintains for instance scores, and the server will also set the skill level for each player so that equal players can be matched against each other in the Game Center matchmaking process. I will thus be able to tune the match making process as the number of players increase exponentially. tongue.png

I want to make the interface between the app and my database as future proof and maintainable as possible. Where can I find information about such good designs? Unless you don't just tell me, that is. cool.png

Some thoughts:

  • I must be able to adjust and add functionality to the PHP scripts and the database.
    • I guess the server shall be set in maintenance mode. How is that usually handled?
    • What's the best way to inform the players of the maintenance break? Does a typical app like Clash of Clans pull for that data once every five minutes, or is that handled by some push mechanism? A pull is simpler...
  • I must be able to change ISP. Can that typically be done in the IP network only for some time, or does that require a modification of the app as well?
  • Please add more things that I shall consider!

Thanks!

So you generally need to write an API for your backend. I am sure you can find many good sources on that, these look good (I just glanced over them, there is PHP section). In short, your game makes normal request to your site like www.yoursite.com/leaderboard to get top players on leaderboard or POST request to www.yoursite.com/matches to register for a match. Doing it this way you provide very light server application which should scale well.

For maintenance break just return HTTP 503 response code when he sends any request. You can also make client sends GET /status request every minute.

Your biggest problem will probably be cheating. How will you know who won a match? If you relay on player reporting his victory it may go badly - someone can send thousands of victory reports in seconds. My only idea right now is to limit reports per hour. If your standard match lasts 15 minutes it is very unlikely that someone will win more than 5 times per hour so you can limit that. You could also always wait for 2 reports - if winner reports that he won match with ID 120, and looser reports that he was defeated in match with ID 120 then you can give him points. If someone has more than 90% win ratio and way to many games you can also mark it as suspicious behavior which you can reports to yourself and check it manually to avoid banning dedicated players. Maybe someone will have better idea.

What do you mean by ISP change? Do you want to host it on your computer or are you going to change hosts frequently (and why)? As long as you purchase domain and all the requests are made to this domain (which should point to your application) it will be fine. Client always sends GET request to www.your-awesome-site.com/status. If you change your hosting you just change IP to which your domain points to.

Advertisement
I'd recommend including a version identifier in client requests, preferably in the URL.

So you generally need to write an API for your backend. I am sure you can find many good sources on that, these look good (I just glanced over them, there is PHP section). In short, your game makes normal request to your site like www.yoursite.com/leaderboard to get top players on leaderboard or POST request to www.yoursite.com/matches to register for a match. Doing it this way you provide very light server application which should scale well.

This makes sense! I'm using POSTs for everything now, but I'll change that!

For maintenance break just return HTTP 503 response code when he sends any request. You can also make client sends GET /status request every minute.

Will do! According to http://aws.amazon.com/s3/pricing/ the price for a GET is "$0.004 per 10,000 requests", so I guess thats ok.

Your biggest problem will probably be cheating. How will you know who won a match? If you relay on player reporting his victory it may go badly - someone can send thousands of victory reports in seconds. My only idea right now is to limit reports per hour. If your standard match lasts 15 minutes it is very unlikely that someone will win more than 5 times per hour so you can limit that. You could also always wait for 2 reports - if winner reports that he won match with ID 120, and looser reports that he was defeated in match with ID 120 then you can give him points. If someone has more than 90% win ratio and way to many games you can also mark it as suspicious behavior which you can reports to yourself and check it manually to avoid banning dedicated players. Maybe someone will have better idea.

I plan to use SSL and POST for those matchReports. To keep it simple in the beginning I'm planning to include a secret key in that report and also the match ID. The server will only allow one report for a specific match ID, so a packet can not only be copied and resent multiple times. Because of the secret key it is not that easy to make a matchReport, because the key is not globally known. If my app is successful I plan to include some more advanced authentication scheme. What do you think about that? I'm not a security expert in any way so feedback is most welcome.

I've also thought about that mutual reports from both players, but I won't start with that unless you really recommend so

What do you mean by ISP change? Do you want to host it on your computer or are you going to change hosts frequently (and why)? As long as you purchase domain and all the requests are made to this domain (which should point to your application) it will be fine. Client always sends GET request to www.your-awesome-site.com/status. If you change your hosting you just change IP to which your domain points to.

I plan to use a my current ISP for a start, but that's mainly a WordPress account with a mySql database that I interface via PHP. Maybe in the future I need to move to Amazon Web Services or something similar. But maybe this reveals some of my weak spots. (I'm an embedded system engineer).

I've got a domain. Can that domain move between ISPs? So I can:

  1. set up the same setup on Amazon,
  2. take down the system in maintenance,
  3. copy the database to Amazon,
  4. change the domain IP, and finally
  5. stop maintenance mode.

If so, that sounds great!

I'd recommend including a version identifier in client requests, preferably in the URL.

Yep. What do you mean by "preferably in the URL"? Do you mean that my PHP scripts shall have a version ID in their name?

This will allow multiple versions of my app to access the same database. It will for instance be possible to rename a variable. Is that the reason for this version ID?


This makes sense! I'm using POSTs for everything now, but I'll change that!

Use POST and GET, whatever fits best. GET is most often used for retrieval when POST is for create/update. Both are fine.


Will do! According to http://aws.amazon.com/s3/pricing/ the price for a GET is "$0.004 per 10,000 requests", so I guess that is ok.

I am not sure if Amazon is best place to start, there are cheaper options. Although I heard that they have first year for free (if you do not go above cheapest solution).


I plan to use SSL and POST for those matchReports. To keep it simple in the beginning I'm planning to include a secret key in that report and also the match ID. The server will only allow one report for a specific match ID, so a packet can not only be copied and resent multiple times. Because of the secret key it is not that easy to make a matchReport, because the key is not globally known. If my app is successful I plan to include some more advanced authentication scheme. What do you think about that? I'm not a security expert in any way so feedback is most welcome.

How application will know this secret? If client application knows secret then so can a cheater. What if same client reports hundreds of wins with different match ID?


I've also thought about that mutual reports from both players, but I won't start with that unless you really recommend so

That would kill your game.


I've got a domain. Can that domain move between ISPs? So I can:

I do not know how it looks on amazon but on different web hosting providers you can add domain so it should be possible. I do not have much experience here so I may be wrong.

Advertisement


How application will know this secret? If client application knows secret then so can a cheater. What if same client reports hundreds of wins with different match ID?

Absolutely, you can find out the secret by looking at the binary code of my app. But that takes some skills.

Maybe I shall try some other solution. But I guess that the only secure method is that the server authenticates the app in every message. Ok, I guess I need to learn more about security considerations. It would be nice to know this is handled in some state of the art games.

I'd recommend including a version identifier in client requests, preferably in the URL.

Yep. What do you mean by "preferably in the URL"? Do you mean that my PHP scripts shall have a version ID in their name?

This will allow multiple versions of my app to access the same database. It will for instance be possible to rename a variable. Is that the reason for this version ID?

Something like yourgame.yoursite.com/v1/scoreboard

If you need to create a new version of the API, just point them at /v2/whatever instead. This way, it is very easy to distinguish requests using the older version. In particular, it is each to configure front-facing servers to match on URLs and forward requests to different back-end servers.

This may not be a concern right now, but this gives flexibility in future.

Another thing to consider is to specify a couple of response types now that all client versions can understand. For example, a particular HTTP status and/or payload might indicate that the client is out of date, so the client could display a "please update" message to the user.
For versioning, I prefer to put that in HTTP headers, rather than the URL. Accept: is one such header, for example.
You can treat a missing version header as "serve the latest."
Separately, for telling which client is making the request, you can look at the User-Agent: header -- you can arrange for your game to send a unique user agent which might be gamename/buildnumber, for example.

Also, there are a few things that you want to nail down ahead of time, and make them ALWAYS TRUE.
For example, if you use JSON, you may want the response to always contain the following fields:

status: "success" or "failure"
error: integer (if failure)
message: human-readable message (if failure)
data: actual-payload-data
Then wrap all the HTTP clients you use in a wrapper that can tell success from failure, and deliver the decoded payload to the application. A central library that does things right, and whose behavior you can easily reason about, goes a long way once the time comes to make changes.

You probably also want to make sure that your HTTP client always recognizes 300-level redirects and the Location: header, as well as a well-defined code (like 410) for what happens when you want to tell a client you're no longer prepared to serve it. Good user errors for 503 (temporarily unavailable) would be good, too.

Separately, you should build the client library to not blow up if the JSON fields come in a different order at different times. You should also not blow up if there are extra fields that you don't recognize -- just ignore them. Finally, if you expect a field, and it isn't there, you shouldn't blow up, but instead substitute some empty/null value in the decoded value; this may cause the game to detect an error, but should not throw an exception or cause a crash.

Using a version specifier is somewhat useful if your API evolves slowly. An alternative is to just tack the version number onto each service. https://api.yoursite.com/user3/ for users-version-3, and https://api.yoursite.com/purchase8/ for purchases-version-8. (This is similar to how we have interface names like IDirect3D12 -- it's a well-tested method that works.)

Oh, and always use HTTPS if at all possible.
enum Bool { True, False, FileNotFound };

Wow, thank you guys. Excellent comments!


Oh, and always use HTTPS if at all possible.

Yea, I will.

hplus0603: I understand if you're not that keen on describe your security related protocols. But if you don't mind to commenting about the need for authentication procedures etc, it would be great. But again, only if you feel confident with it.

And finally, this one was just super! wub.png


enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement