MMO Journey: Weeks 7-9 (logging in)

I needed to get more than one player connected and identified.  For some reason I decided to approach this by creating an entire Registration / Login / World Select / Character Select system.  I'm sure there are around a million better ways to prototype this, but you know what they say about hindsight.

I got re-familiar with Unity's UI elements and in about 15 minutes created a basic login screen that asks for your email and password.  The problem then became what to do with that information.  Database!  I spun up a PostgreSQL database and added tables for Account, Reality (this project's name for a "Realm") and Character.  I have some experience building web apps, so I also spun up a basic website using Ruby and Sinatra for account creation that creates rows in the Account table when someone submits their deets.

Now I had to decide how the login screen was going to authenticate.  I had no desire to try to figure out HTTPS and authentication in C++ and also realized that if I'm going to have more than one game server the authentication process should probably take place somewhere more central.  Since I already had a web app, I just added some API endpoints for logging in, listing game servers, and listing/creating characters.  Nice!

At this point I could log in, which provided the game client with a token (UUID string) that could be used for future communication.  The client then showed a list of game servers, and when one is chosen a character screen is shown, listing characters the user has on the selected server.  I could add and delete characters at will, with a good amount of error handling and checking.  Wow!  What a feeling of accomplishment!

Screenshots:
Login
Server list
Character screen

But wait, this is cool but I still hadn't solved my original problem: Getting more than one client logged in to the game server at the same time.  This is where I hit a fairly major stumbling block.  The login, server select, and character screens are only communicating with an HTTP API on the web server.  Now when I press "Enter" I want the client to connect to the selected game server with the selected character.  But the game server doesn't use HTTPS or know anything about the client's authentication status.

After some thinking, I came up with this solution:
  1. Upon successful login (via HTTPS API), client receives a token that can be used for future API calls.
  2. This token is stored in the database, so it can be accessed by both the web server and the game server.
  3. When the client tries to enter the world, a TCP connection (no encryption) is opened with the game server they selected, and an AuthenticationMessage is sent.  This message can't contain the actual token, because the token is extremely sensitive information and the game server isn't using any sort of encryption.  So instead, the client irreversibly hashes the token and sends that hash, along with the user's account_id, as authentication.
  4. When the server receives the AuthenticationMessage, it uses the account_id to pull the account's token from the database, hashes it with the same algorithm the client used, and compares the two hashes.  If they match, let the client know they have been authenticated, otherwise disconnect the session.
  5. After the client is authenticated, it sends an EnterRequest to the server, and the server replies with all the data the client needs to render the world.
It turns out that those five steps represent a crapton of work.  I used libpqxx for the C++ PostgreSQL connection, then ended up completely rewriting the game message classes on both the server and client to simplify adding new message types in the future.  Unfortunately I haven't found a good way to share code between C++ and C#, so any work on the messages passed between the client and server has to be done twice. On the upside, I learned a ton about class inheritance, polymorphism, virtual methods, pointers, and all sorts of technical goodies in the process for both C++ and C#.

At this point in time I'm about to start step 5 of the process above.  After writing all this down, I'm thinking I should give this project a name, or at least a working title.  "Project Discord" it is.

Current code stats (counted using CLOC):

component       blank     comment     code
--------------------------------------------

client (C#)     200       86          877
server (C++)    106       39          435
web (Ruby/CSS)  122       55          521
--------------------------------------------
Total           428       180         1833 

Previous entry
Next entry
 

Comments