Real-Time WebSockets and REST

Started by
5 comments, last by hplus0603 8 years, 6 months ago

I want to make a real-time light MMO-like Asteroids game using Unity and WebSockets for the client, and NodeJS, SocketIO and Box2D on the server side. I'll handle scaling by having each instance of my server be a separate "universe" the player can explore.

Players only have a single login for their account, and can jump between available servers, which are considered separate "galaxies". Since the player's account isn't directly tied to any specific server instance, the login mechanism needs to be separate from that, so I've thought about going the REST API route. Once successfully authenticated, my REST API would generate a authentication token to represent their login credentials for their session. Once the player is logged in, they can select a galaxy (server) to visit, provided it hasn't met its player capacity. When the player attempts to connect to that server, the client will pass the player's token, and the server will check if that token is a valid session with the database (this might breach RESTfulness).

If the server finds that the token is valid, a socket connect between the client and server is attempted. Upon success, a player object is created on the server, added to the list of current players. The server will use Box2D, or some type of physics engine to process movement, collision, etc, and report the observable portion of galaxy that the player can see as a message to the client ever few milliseconds (the goal is 30 frames per second). The Unity client will render the observable objects within view to the user. The client will periodically send input signals to the server (again, the goal is 30 fps). Those input signals are interpreted by the server to perform actions, such as steering the ship, shooting projectiles, interacting with the environment, etc. Whenever the client sends a message to the server, it also sends its token that the server uses to ensure that the client connection is coming from something that it originally considered legitimate.

WebSockets is primarily meant to send real-time data in the form of binary data instead of JSON to reduce overhead and serialization/deserialization between the client and server. Unity's .NET implementation appears to support binary serialization/deserialization that could be faster than JsonFX, but I have yet to benchmark it. Packing the database into binary datatypes will be a pain on the NodeJS side, but I plan on going back to a C server once I get things figured out. The server also has access to the database to submit queries such as kills, resources mined, health picked up, score, currency, in-game shop transactions, inventory pick-ups, etc.

The player can also update their player info in-game, send money to users, and other actions that the REST API would handle. As far as database queries go, I'd probably use AWS SQS for a larger-scale design, and have some sort of in-memory database to help with queries happening between what's actually in the database, and what's yet to be in it.

I'd use AWS, and have a server for the REST API, one for the database, and one for each galaxy configuration per instance required. How does this sound as far as architecture?

Advertisement

I always applaud the idea of using well known, highly used authentication and authorization methods. The only other response I have at this time is the group will probably be more help if you begin implementing your system and ask specific questions. The architecture side of things will always be debatable and to be honest nothing you are suggesting is standout wrong per say. That being said I would imagine your troubles here won't be with your architecture so much as the network jitter and delay interpolation etc on a space shmup.

One suggestion when writing apps on AWS:

I have been using redis a lot for a bunch of work projects as a lightweight message bus (as opposed to the bulky more feature rich and complex enterprise ready RabbitMQ) that supports pubsub and message queuing and it has worked out really well for being that general abstraction layer between applications. Instead of applications working with databases or filesystems for data stores for example they work with a very thin request/response API over redis and then I have connectors for the various databases/data stores on the back end for various APIs. It takes a bit of extra work up front but has saved me a bunch on the back side when various design decisions and features suddenly change requirements and scope. It has also really made scaling very easy (multiple instances of single threaded apps).

Evillive2
REST-ful-ness and real-time do not mix well. REST is almost purposefully not a real-time design approach, as it trades overhead for long-term evolution robustness. Games can almost always update client and server in parallel, and thus don't often benefit from this (but still have to pay the cost.)

I think your approach sounds like it can work. Some caveats:
Web sockets are TCP -- there will be occasional lag. However, if you've played agar.io, or Realm of the Mad God, you would know how good/bad this is.
Use HTTPS or TLS for all requests if possible. While HTTP and TCP is easier for debugging, there are so many weaknesses with plaintext these days that HTTPS and TLS is pretty much a must.
Amazon may suffer the "noisy neighbors" syndrome, where suddenly you suffer a lot of performance degradation, jitter, packet loss, etc. Beware, monitor, and be prepared to move to another availability zone if it gets bad.

Feel free to come back and ask for more specific questions if you need to, or just keep us all up to date on how the project goes!
enum Bool { True, False, FileNotFound };
Note that you can WebRTC datachannels to get UDP communication going. WebRTC data channels are to UDP what WebSockets are to TCP; they don't give you raw UDP access, but they give you the ability to communicate over unreliable (UDP) channels in JavaScript while automatically handling the complexities of DTLS (encryption) and providing safety.

WebRTC is not as widely supported as WebSockets, though. A game intended for the mainstream will need to be able to fallback to a TCP WebSocket connection if the WebRTC features aren't available in the user's browser.

For an MMO-like game you'd want to "peer" with the main server rather than implementing a pure peer-to-peer model, which is what WebRTC was originally designed for. There's node-webrtc that should handle your case. It basically just lets the server act as both the P2P broker and as a peer, so clients will talk to only the server.

Sean Middleditch – Game Systems Engineer – Join my team!

In regards to WebSockets I personally wouldn't bother with socket.io. You can just use the ws module for games. Falling back isn't necessary anymore for any browser or device. (Nor for a game is it really worth it).

I always applaud the idea of using well known, highly used authentication and authorization methods. The only other response I have at this time is the group will probably be more help if you begin implementing your system and ask specific questions. The architecture side of things will always be debatable and to be honest nothing you are suggesting is standout wrong per say. That being said I would imagine your troubles here won't be with your architecture so much as the network jitter and delay interpolation etc on a space shmup.

I debated about this: start now, and ask questions later, or sit back and contemplate. I chose to ask about architecture first, but ended up diving into the project. I initially wanted to ask about jitter, latency and how to combat that with dead reckoning/path interpolation. I've read some papers online, but wanted to go over the specifics of it once I actually had something implemented. I ran into the issue of writing my Asteroids MMO using TCP sockets a few years back. It was a nightmare attempting to convert it to UDP, and I never finished UDP support. The game was plagued by jittering when 2 players weren't playing off of the same access point.

I started writing a REST API for user account management. This allows users to create new accounts, verify them via email, change emails, rename usernames, etc. I'm thinking about using it for login purposes too, but my NodeJS server could handle this as well since it'll have database access as well. At my old job, we would pass an API-KEY key-value pair in each of our REST API's requests. The request would return an error if the call didn't include it with the correct value. I've seen the use of API keys mentioned online, but I don't see the point of using them. Anyway monitoring the API calls via proxy can figure what the key is, so why bother using one? Is this to help stave some type of automated attacks not specifically targeted at my application?

REST-ful-ness and real-time do not mix well. REST is almost purposefully not a real-time design approach, as it trades overhead for long-term evolution robustness. Games can almost always update client and server in parallel, and thus don't often benefit from this (but still have to pay the cost.)

I think your approach sounds like it can work. Some caveats:
Web sockets are TCP -- there will be occasional lag. However, if you've played agar.io, or Realm of the Mad God, you would know how good/bad this is.
Use HTTPS or TLS for all requests if possible. While HTTP and TCP is easier for debugging, there are so many weaknesses with plaintext these days that HTTPS and TLS is pretty much a must.
Amazon may suffer the "noisy neighbors" syndrome, where suddenly you suffer a lot of performance degradation, jitter, packet loss, etc. Beware, monitor, and be prepared to move to another availability zone if it gets bad.

Feel free to come back and ask for more specific questions if you need to, or just keep us all up to date on how the project goes!

I thought agar.io used WebSockets. Player movement didn't appear to have any jittering unless players broke into smaller pieces since you'd suddenly have a bunch of objects moving much faster onscreen. I'll ask more specific questions when I get to that point. Working on the sign-in system.


I have been using redis a lot for a bunch of work projects as a lightweight message bus (as opposed to the bulky more feature rich and complex enterprise ready RabbitMQ)

I've used both in Python projects at work in the past, but not in PHP yet. I'm considering Redis for sessions, possibly


Note that you can WebRTC datachannels to get UDP communication going.

Perfect! I've wondered if there was a UDP counterpart to WebSockets. I've searched online in the past, but found nothing. When I first read about WebRTC, I also wondered how supported it'd be client-side in browsers, but I think it'll work for my purposes. I'm writing stuff that's meant to be more experimental than mass-supported.


In regards to WebSockets I personally wouldn't bother with socket.io. You can just use the ws module for games. Falling back isn't necessary anymore for any browser or device. (Nor for a game is it really worth it).

I've wondered about this myself. It seems like WebSockets have been widely supported since 2012.

I thought agar.io used WebSockets.


Yes! And it is sometimes laggy, and sometimes smooth. Just like you'd expect with a TCP-based system.

Regarding API keys: They are often used to separate different applications talking to the same API in multi-tenant cloud situations.
For example, if you call the "create user account" function on "supergamehost.net" that hosts a lot of games, the API key token tells supergamehost.net which game to create the user for.
Separately, there are often "server secret tokens" that work as passwords, and must not be leaked to the clients. Those tokens allow non-public operations. Thus, the "delete a particular user" call would require both the API key (user for which game?) and the server secret key (to make sure it's an authorized admin request, and not some player trying to grief-delete some other player.)
If you just have one application talking to one server, neither of those are needed.

However, you still need to solve the user authorization and authentication problems somehow. On the web, this is typically done with session cookies. In UDP or similar, that's typically done by including he same kind of information in a payload packet. Often, games will just autehenticate once, and then assume that any future packets from the same remote IP+port endpoint are from the same user, as long as there is no long gap in receiving packets (timing out the "session" constructed at the application layer.)
enum Bool { True, False, FileNotFound };

This topic is closed to new replies.

Advertisement