Ongoing game connection over potentially days

Started by
20 comments, last by flodihn 6 years, 11 months ago

Hey forum!

I would like to offer a service in a game of mine that allows to have a multiplayer turn-based game to go on for potentially days. It is available on Android/iOS, but that should not matter much, I assume?

First idea was an SQL-database, where game-moves are saved as table-entries. Players would send their moves to a web-API, PHP/NodeJS, and these check whether the move is valid and possible, if so, it will be inserted into the table.

Nonetheless, I can't really say how slow that would be and testing is a bit difficult for me at the moment. Establishing a direct connection might be faster? Are there any other possibilities?

I like that with a database, I can check the validity of a move and even test whether either player won. Barely a way to "cheat"/bypass game functions/manipulating packages, as it seems, might be wrong on this part.

Anyway, I'm not that versed on networking and might missed a technology that would suit my problem.

Thanks for taking your time to read my post, would be glad if you can help me out!

Advertisement
Direct connections to databases from the greater internet is always a bad idea, because you will get hacked within minutes.
Using an application server that speaks HTTP/HTTPS, where you POST the game move, and receive back the status (as well as being able to perhaps GET the game state to just look at it) is a very common way of doing what you're describing.
The main question is how a player knows that the other player has made a move. Mobile push notifications often work, but are not 100% reliable.
Thus, you might want to also wake up every so often (every 8 hours? every day?) and poll the current game for status from the client.
HTTP/HTTPS application servers, there are tons of. Node.js if you like JavaScript. golib/Golang. Flask/Python. Cake/PHP. Warp/Haskell. Tomcat/Java. You can find them for all languages, so it mostly depends on what you're comfortable with, and perhaps on what you can and cannot do on your hosting server.
enum Bool { True, False, FileNotFound };

Yes, I thought so. Also considered something like P2P, but it is harder to protect against cheating, I assume?

Great to hear that is is actually common practice to utilise HTTP/HTTPS.

The main question is how a player knows that the other player has made a move. Mobile push notifications often work, but are not 100% reliable

That was actually what I would want to do, but not sure how manage that the game actually progressed and inform someone of their turn. Would a Cron-job be suitable? But since the web-application would not be able to directly connect to the other player, this would not really work. It might be even better if the player continuously requests whether the status changed, as that is the only connection that can be made sure of?

What relational database would be recommended for such? Simply MySQL?

P2P is not really practical - who would own the data? What if everybody turned off their device?

Cron jobs are one way of getting an event-driven technology like PHP to do things on a timer. More sophisticated server software - i.e. pretty much every other way of writing web software other than PHP - can give you a long-running process that can perform tasks whenever it needs to.

Alternatively you can do the 'just-in-time' method you mentioned, where you just run any overdue updates whenever someone connects to the server.

There is nothing in your post to say which database would be better for you. A good all-round choice is PostgreSQL. MySQL is also good (as is MariaDB, based on MySQL). SQLite is also perfectly fine when you're just starting out. Then there are a ton of document-oriented options, like MongoDB, CouchDB, and RethinkDB, and more esoteric DBs like Arango and Neo4J. Choosing a database is a topic all of its own, but 9 times out of 10 a standard SQL DB runing Postgres or MySQL is good enough.

Ah, true! I thought users could serialise contents and... but yes, I totally understand the problem with this now. Okay, I would rather go with NodeJS, got some experience in it. I just thought a bit outdated, I truly can just keep an instance running all day, instead of letting one being run at a wanted time.

The connecting-part will be difficult. How often is healthy for the battery/data volume is probably what I need to figure out. It is hard to figure out if people just passively wait for a move to be made and to other things on their phone during this state or if they just busy in general. Running one connection per minute adds one minute of delay between every move while doing other things on the phone. Well, I will have to see : )

Okay, I will roll with MySQL then.

Also considered something like P2P, but it is harder to protect against cheating, I assume?


P2P is extremely hard to make robust on mobile networks (even more so than on wired internet.)
I would not recommend it.

Regarding things happening on a timer, there are multiple solutions.
Cron jobs are fine. They have the benefit of being robust. If the system stops, once you start it, it will catch up.
The draw-back is that it's harder to shard lots of the same kind of work across multiple cron jobs and still stay robust.
Also, when there's nothing to do, the cron job still wakes up, looks at things, and stops, which uses a little more resources than necessary (not really all that much,) and also cron jobs only run every so often, so you will have some amount of latency between "event happened" and "notification sent."

A long-running process is, indeed, more sophisticated, and can use a priority queue of timer-based events to run-at-time.
These systems have much lower latency, and are optimal from an efficiency point of view.
There is the danger that, when this long-running process dies, it will lose the state, so you need to design with that in mind, and once you handle all the failure cases, you end up with either a less efficient system, or a more complex system.

Another option I quite like is to use a message queue of some sort. Ideally one that supports priority keying.
Then, have some number of workers share the work reading from this message queue, and doing the work.
As long as your message queue is durable, and worker-sharing is supported, this system will be robust and efficient, and lower latency than cron jobs.


If you don't want any of that, you can still make it work.
When a player submits their move, you can send the push notification right then and there.
This is as low latency as you can get!
In addition to handling the push notification, you can also have the local game wake up every now and then and "check in" with the server, and display a local notification if a game turn is ready.
You could even make sure to run that polling each time the game is opened/brought to the front. That way, the impatient player can make sure to quickly catch up after emerging from a cave or whatnot.

If there are things that happen on a timer (all of the "build your base" type games do this, as well as Farmville and such) then you simply enqueue "this task is done at time T" and use the delta between "now" and "T" to figure out how complete it is.
This doesn't need a cron job at all; it's purely based on the state. Decay, in turn, is also based on a timer: "this thing was last maintained at time T" and you check how long ago that was to determine the state of decay.
enum Bool { True, False, FileNotFound };

Thanks for your point of view onto this and especially your bits of information : )

When a player submits their move, you can send the push notification right then and there.

Since a game can go on for days, players are of course not always online or capable to play. So I would not know how to send the push-notification from player 1 to player 2.

I might misunderstand an essential part, do your suggestion require something like a socket-connection? As in, once a player is capable of going online, they will establish a connection for that online time being?

Otherwise, the players would have to play the "cron-job" and keep "spamming" my database to see if an update has been committed.

So I would not know how to send the push-notification from player 1 to player 2.


I assume that by "push notifications" we're talking about the notifications APIs provided by the mobile platform vendors.
Thus, a user signs up, and enables push notifications, and you use the vendor API to queue push notifications to the device.
This can be done even if the user isn't currently connected to your site -- you tell Apple/Google about the device/user to send the notification to, and they do it.
On the device side, when the user activates the notification, your app is opened, with information about the notification.
(But it's usually easiest to just fetch the latest-and-greatest each time the app starts, no matter how.)
enum Bool { True, False, FileNotFound };

Oh, I did not know that! So, all I need is the device ID and then get a library for NodeJS (or whatever) and I can send a push? That is amazing and solves a seemingly hard difficulty!

But I do not really understand the last part of your message. Fetch what latest and greatest?

But I do not really understand the last part of your message. Fetch what latest and greatest?


Have the client (the game code running on the player's device) fetch the current game state from the server (since push notifications are not 100% reliable, the last PN you received might not contain the most recent game state).

PNs are kind of a hassle to set up. The abstract flow is:

- You configure your mobile app with Apple/Google/etc.
- You set up a server that holds a secret key that you use to make a request to Apple/Google/etc to send notifications. You cannot publish this with your game. The secret key is given to you by Apple/Google/etc and can be revoked by you or them if anything gets shady.

- When you start your app on the player's device, your code makes a call that registers with Apple/Google/etc's servers and returns a unique token.
- Your game code uploads that token to your servers which associates that player account with that token.
- When you want to send that player a notification, you make a request to Apple/Google/etc with that player's token and your server's secret authentication token.

The player's token allows the player to revoke permission to send them PNs at any time.

Your secret token prevents anyone else from sending PNs that pretend to be your app.

Another gotcha is that one player might play on different devices, and each one will return a different token. Your database may end up containing multiple tokens per player, perhaps one for iOS, one for Android, one for Kindle, one for Windows Phone (OK, now we're being overly optimistic), etc. You have to decide yourself whether you only send to the most recent registered device, or all of them, or what. You also have to remove expired tokens (they will expire naturally even if the player does not revoke them explicitly).

This topic is closed to new replies.

Advertisement