Web API for a turn based game?

Started by
15 comments, last by graveyard filla 6 years, 10 months ago

Hi,

Wow. I haven't been here in a long time, and it's crazy to see this forum still thriving and people like hplus still around helping people.

I am working on a prototype for a multiplayer mobile card game using Unity. I'm trying to finalize some architecture decisions. I was thinking of building the server as a Web API (using ASP.NET MVC) and having the client basically just GET and POST when needing to communicate with the server.

I was wondering if people are using WebAPIs in commercial games and how sane of an approach this seemed. Since the MVP will not even support interacting with other players in real-time, it seems like this is going to be the simplest solution rather than creating a state based game server.

Later, when we started adding features like 2 players being able to play against each other, I was thinking we could add a stateful game server at that time. Then, when players wanted to play against each other, the web server would do a hand off to the stateful game server 'players with ID x,y and session Id Z want to begin a match'. The game client would then connect to the stateful game server which would now be warmed up and ready to manage that interaction. This would accomplish 2 goals, 1) allow me to build only the bare minimum needed for the given requirements at a time 2) organically grow the server side as requirements are added 3) reduce the load and complexity on the servers so that they have a minimal amount of responsibilities. The service that does game logic doesn't need to manage friends lists and players news feeds.

Any feedback on this approach?

Thanks for the input!

FTA, my 2D futuristic action MMORPG
Advertisement
Yes, there are games that use web APIs.

Games where this works really well are games like Farmville or Backyard Monsters or such, where the player really is only doing their own thing, and the only need for the back-end is to validate what the player has done, record the state of started work/timers, and such.
If you are doing interaction with other users, POST/GET is a little more iffy. You CAN build a reasonably real-time system based on long polling ("comet" style with AJAX) if you have the ability to suspend a current request on the server, and unblock/return it once there's data, or once enough time has passed (10 seconds or whatever.)
However, this is, shall we say, decidedly "ghetto" networking, and the crossbar to figure out "what is there for this user" and then getting that information to the appropriate server where the user's request is waiting to be unblocked is fragile, and full of race conditions.
If your game is small enough to only ever need one server, then the "A did a thing, so B needs to know about it" isn't so bad, but when you scale across multiple machines, HTTP gets in the way something fierce.
In fact, you're probably better off to just chuck the game state and outgoing messages into a database, and have the user poll every 5 seconds, and polling will return whatever was sent in the last 30 seconds, and purge anything older than that, from the database.
This is highly inefficient, and delays all incoming messages by 5 seconds, but at least it can be made robust.

If you have to run in a web browser, I would suggest perhaps web sockets (something like socket.io) because there at least you have a persistent connection to the server, and thus the mapping from "user id" to "server that currently knows how to send data to that user" can be somewhat less ephemeral.
You'd have some registry of where the connection for each user is, and some ability for processes on different machines (or just different processes on the same machine) to find each other.
Erlang is great for this, btw, but if you have to do it in C#, then that can be done.

If you're building a native client (PC game, console, or mobile,) then you're probably slightly better off just using TCP, or, if timing is super important, using UDP with some method to deal with unreliable messages.
The web stack makes it possible to build highly scalable infrastructure if your goals are de-centralized document editing/access, large document transfers, and high latency updates.
Games have different goals.
enum Bool { True, False, FileNotFound };

hi hplus! I don't know if you remember me or not.. I was a big time user of this forum a decade ago and learned A LOT from you personally here in the multiplayer forum. I have since been a developer in the business space and am now finally returning to games. I just wanted to say thank you for all the help you gave me all those years ago. It truly made an impact in my life.

"Games where this works really well are games like Farmville or Backyard Monsters or such, where the player really is only doing their own thing, and the only need for the back-end is to validate what the player has done, record the state of started work/timers, and such."

This sounds very much like the requirements of the MVP of my game. The client just needs to have an authoritative server to tell it which cards it owns, which decks it has, send it the resources for those decks, social aspects (friends lists, news feed, eventually it would have messaging), etc.

Once the MVP is proven, we would add a 'VS' mode where 2 players could interact with each other in a turn-based way.

Back in the day when I was learning about the architecture for MMOs here, I remember what you had told me about how it worked at the game you were building at that time, I think it was Second Life? You had told me the best architecture is to have distributed services. I have found that to be true in the business world as well.

I was thinking that a good approach to tackling this problem is to start off with a simple web server, and then later, when 2 player VS mode was needed, expand into a TCP connection / stateful game server using some sort of methodology like RPCs or such. UDP wouldn't be needed here as the game is nowhere near real-time and there are no physics interactions in the game outside of animations and such which can be run client side as they don't impact the state of the game and so don't require server authorization.

This approach seems like it would allow me to organically grow the application as needed as well as distribute the work to separate services which would hand over sessions to each other behind the scenes. The Web API could tell the stateful game server a session was coming his way and the clients could then connect with their session ID's to a warmed up game server.

Do you think this approach would lead me to paint my self in a corner, or does it seem sane? I don't want to build a stateful game server from the beginning as it may be overkill. The MVP may not even take off, or it may take off in a different direction altogether and it seems easy enough to add stateful ness later. But, I've only been back in game development for 6 months now, and haven't done serious networking in a game yet since 10 years ago...

Thanks again man!

Yes, there are games that use web APIs.

Games where this works really well are games like Farmville or Backyard Monsters or such, where the player really is only doing their own thing, and the only need for the back-end is to validate what the player has done, record the state of started work/timers, and such.

If you are doing interaction with other users, POST/GET is a little more iffy. You CAN build a reasonably real-time system based on long polling ("comet" style with AJAX) if you have the ability to suspend a current request on the server, and unblock/return it once there's data, or once enough time has passed (10 seconds or whatever.)
However, this is, shall we say, decidedly "ghetto" networking, and the crossbar to figure out "what is there for this user" and then getting that information to the appropriate server where the user's request is waiting to be unblocked is fragile, and full of race conditions.
If your game is small enough to only ever need one server, then the "A did a thing, so B needs to know about it" isn't so bad, but when you scale across multiple machines, HTTP gets in the way something fierce.
In fact, you're probably better off to just chuck the game state and outgoing messages into a database, and have the user poll every 5 seconds, and polling will return whatever was sent in the last 30 seconds, and purge anything older than that, from the database.
This is highly inefficient, and delays all incoming messages by 5 seconds, but at least it can be made robust.

If you have to run in a web browser, I would suggest perhaps web sockets (something like socket.io) because there at least you have a persistent connection to the server, and thus the mapping from "user id" to "server that currently knows how to send data to that user" can be somewhat less ephemeral.
You'd have some registry of where the connection for each user is, and some ability for processes on different machines (or just different processes on the same machine) to find each other.
Erlang is great for this, btw, but if you have to do it in C#, then that can be done.

If you're building a native client (PC game, console, or mobile,) then you're probably slightly better off just using TCP, or, if timing is super important, using UDP with some method to deal with unreliable messages.
The web stack makes it possible to build highly scalable infrastructure if your goals are de-centralized document editing/access, large document transfers, and high latency updates.
Games have different goals.

FTA, my 2D futuristic action MMORPG
Welcome back. Yes, I remember. Time flies :-)

If all you're doing is friends lists and deck management and such, then a web API seems totally fine.
Once you get to versus mode, the ability to notify other people quickly becomes more important, and traditional web APIs fall down. Websockets are still OK for this for many use cases.
If your game is popular, then the web API would presumably even let your players build their own helpful clients/tools. (You might want to use a well-tested oauth API plugin to enable this.)
Welcome back. Yes, I remember. Time flies :-)

If all you're doing is friends lists and deck management and such, then a web API seems totally fine.
Once you get to versus mode, the ability to notify other people quickly becomes more important, and traditional web APIs fall down. Websockets are still OK for this for many use cases.
If your game is popular, then the web API would presumably even let your players build their own helpful clients/tools. (You might want to use a well-tested oauth API plugin to enable this.)
Welcome back. Yes, I remember. Time flies :-)

If all you're doing is friends lists and deck management and such, then a web API seems totally fine.
Once you get to versus mode, the ability to notify other people quickly becomes more important, and traditional web APIs fall down. Websockets are still OK for this for many use cases.
If your game is popular, then the web API would presumably even let your players build their own helpful clients/tools. (You might want to use a well-tested oauth API plugin to enable this.)

enum Bool { True, False, FileNotFound };

thanks for the response! BTW, MVP target is iPhone and eventually Android + PC and potentially consoles way down the road if it sticks. We won't be targeting Web in Unity most likely.


Do you see any holes in my idea of doing a 'hand off' from the web app to the stateful game server? Is there something obvious that I might be missing or does that transition seem viable?

FTA, my 2D futuristic action MMORPG

Do you see any holes in my idea of doing a 'hand off' from the web app to the stateful game server?


Well, there are many ways you could implement it wrong. Handing off with an authentication token that is hard-to-spoof and using TLS for the non-web connection is important, for example.
In general, all systems do "hand off" at some point, because the match-making side of the system isn't the same as the game-playing side.
Separately, if you don't use the "central server" mechanism, you would have to worry about NAT introduction, at which point hand-off becomes much harder, but it doesn't sound like that's something that you'll be doing, so don't worry about that.

One the user has established credentials through the web login (over HTTPS, I hope,) you should then be able to re-use this token for the duration of a session, and keep sufficient state on the back-end for what the player is supposed to be doing at a time (such as what match they are matched up into right now, and such.)
Thus, when the client connects (with TLS) to the non-HTTP server, and provides the player token, the game server ought to be able to pick apart who the player is, and do the right thing.
enum Bool { True, False, FileNotFound };

thanks. I have to think about that hand-off more. I was thinking that the Web Server would generate a session ID (GUID) and encrypt that session ID. Then it could send it to the game server via a normal TCP message, and back to the client in as part of the response to a GET.

FTA, my 2D futuristic action MMORPG
The problem with sending unencrypted tokens is that anyone on an open WiFi (such as Starbucks) is then vulnerable to session hijacking.
Check out the "firesheep" extension from a number of years ago, that finally shamed Facebook and such to move everything to HTTPS.
A randomly generated GUID is probably fine as a token, with the caveat that it has to be random, which then means it has less than 128 bits of entropy (because other parts of the GUID space are reserved for less-random unique identifiers.)
You might be better off with a long fully random number -- 128 bits is probably fine, 256 bits is plenty.
Another option is to use a predictable session id or game id or whatever, but issue a "ticket" that consists of (userid:gameid:issuetime:hash(userid+gameid+issuetime+secret_key))
"hash" might mean "sha256" these days -- sha1 is at this point broken-ish! The benefit of this kind of ticket is that you can detect whether the ticket has expired without a database lookup, assuming your servers are in sync on time (ntp is required.)
You can then go look up the appropriate player and game data based on the identifiers provided, rather than having to indirect through a GUID.
Something to think about.
enum Bool { True, False, FileNotFound };

The problem with sending unencrypted tokens is that anyone on an open WiFi (such as Starbucks) is then vulnerable to session hijacking.
Check out the "firesheep" extension from a number of years ago, that finally shamed Facebook and such to move everything to HTTPS.
A randomly generated GUID is probably fine as a token, with the caveat that it has to be random, which then means it has less than 128 bits of entropy (because other parts of the GUID space are reserved for less-random unique identifiers.)
You might be better off with a long fully random number -- 128 bits is probably fine, 256 bits is plenty.
Another option is to use a predictable session id or game id or whatever, but issue a "ticket" that consists of (userid:gameid:issuetime:hash(userid+gameid+issuetime+secret_key))
"hash" might mean "sha256" these days -- sha1 is at this point broken-ish! The benefit of this kind of ticket is that you can detect whether the ticket has expired without a database lookup, assuming your servers are in sync on time (ntp is required.)
You can then go look up the appropriate player and game data based on the identifiers provided, rather than having to indirect through a GUID.
Something to think about.

What about generating an encryption key on account creation be rather effective method?
For example when a player signs up for the game, the server generates an ecryption key for further communication.
If the client loses the key, resinstalling the client or whatever, the player would have to linked their account to an email address to verify and generate a new encryption key.

This would mean that keys has to be spoofed at the rare occasions of account creation or key regeneration.

The security of that is approximately slightly less than the security of a password.
Without the key, you can't communicate, which is the same as a password.
If you have a hack and lose the player/key table, then other people can impostor the player -- this is worse than strongly hashed password tables.
If you use the key as a "second factor" authentication, and still use a password, then this is a slightly improvement in security, but really no different from just storing a unique token on the client.
The actual encryption set-up should not depend on a fixed shared key -- that has many attack points.
Instead, you might want to ship a public key with the client, and keep the private key on the server, and for each new session, generate a per-session random symmetric key, and send it encrypted with your private key and the password as key material.
Note that you temporarily have the cleartext password on the server when the user logs in, because that's what the user provides for you to hash and check against your table.

It turns out, it's easier to use well-defined well-tested protocol implementations; TLS for TCP and DTLS for UDP.
And for two-factor, you really want a YubiKey or a Google Authenticator or perhaps the european "Bank ID" approach.
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement