Implementing a network server (Part 1)
In this article, I will continue the topic of implementing a network server for an MMO game. I discuss how to do this in Python and Godot but using the same type of architecture you might easily translate this to any other language for the network server or engine for the client. Python and Godot provide a free and easy way to prototype this kind of solution but also to scale them up in time and be ready for production use.
Table of Contents
You should already know…
Before you read the article though I should warn you that I will not discuss some technologies in detail and you should have prior knowledge of the following:
- SQL – We will create databases to store user data but I won’t go into details about how SQL works
- Python – I will assume that you good understanding of Python’s basics
Considering those 3 things I will provide you with the necessary information for servers and how they work.
I would also strongly recommend you first read the previous article where I explain the whole network server architecture so that you get familiar with where we stand with the current implementation.
Creating an HTTP server in Python
The first thing we will do is the HTTP server. HTTP is a protocol for how messages are transported over the web. You are using HTTP to read this article.
How it works is that you request data by sending a request message. The message has an address consisting of a host (e.g. thatonegamedev.com) and a path and query (e.g. /python-gdscript/network-game-architecture/). The address usually is prepended with the protocol that you want to use for the message (e.g. http:// or https://). There are also types of requests with the two most popular being GET and POST.
GET requests consist of some data about the requestor and the requested address (commonly known as URL).
POST requests also consist of something called a body where other data can be submitted with the request.
The most important thing in HTTP is that the network server cannot communicate with the client on its own. The client makes a request and the server answers. This is why we use this type of server for more static one-way communication and not real-time applications. In our network setup, this could include the requests for login and registration with the game because HTTP is a bit more secure than your own custom communication channel.
In this article, I will be using Python and Flask to create the HTTP server. The HTTP server will also have data storage in the form of an SQL database. To connect to the database I will use the package PyODBC which can easily connect to any database that implements the ODBC standard.
To install both of these just go with the following commands:
pip install flask
pip install odbc
Database Setup
For the database setup, I have installed for myself MariaDB. I will not go through the installation in this article but you can follow their very easy tutorial on how to install MariaDB. After you do this you can open up your Windows menu and search for “MariaDB” then click on the “MySQL client (MariaDB)” search result. It will ask you for the password you gave during the installation process.
After you provide a password you can start executing SQL. We will start by creating a new database:
CREATE DATABASE Network;
USE Network;
This would create our database and change it to the current execution target. In this database, we want to create a single table for now for storing our user accounts. For my user accounts I would like to store a unique ID, username, password, and token. The token I will discuss later but it can be used to auto-login without storing the real username and password and to also communicate the user with the WebSocket server later. You can put any security rules that you want for this token (e.g. how often it expires, what can you do with the token before asking for password confirmation, etc.)
The table creation script would look something like this:
CREATE TABLE Users
(
Id INT AUTO_INCREMENT PRIMARY KEY,
Username VARCHAR(255) NOT NULL,
Password VARCHAR(255) NOT NULL,
Token VARCHAR(512) NULL
);
I will also execute the following script to add a single test user:
INSERT INTO Users(Username, Password) VALUES ("Test", "Test");
After executing this I need not do anything else in SQL. The rest can be handled by Python.
One more thing will be needed though. To use an ODBC connection you need a driver for your communication. This means that you need to install a driver for your MariaDB server. This can easily be installed from MariaDB’s site.
Barebones Flask setup
Next up is the flask server. Create a new folder for your server application. In mine, I put up 3 files – database.py, webapp.py, and socket_server.py.
For our flask server, we will need to create a URL endpoint for our login menu. To do that we will start by making a single function in database.py for validating a set of credentials against the database. Here is what it looks like:
import pyodbc
def is_user_valid(username, password):
# Database setup - should come from the environment and not be hardcoded
server = "localhost"
port = 3306
db_user = "root"
db_pass = "root"
db_name = "Network"
# Creating a connection to the database
db_connection = pyodbc.connect(
f"""
DRIVER={{MariaDB ODBC 3.1 Driver}};
SERVER={server};
PORT={port};
DATABASE={db_name};
UID={db_user};
PWD={db_pass};
"""
)
# Cursor is able to execute SQL code and read back responses
db_cursor = db_connection.cursor()
db_cursor.execute("SELECT COUNT(*) FROM Users WHERE Username = ? AND Password = ?;", username, password)
# Fetch one row and get the value for the first column
matching_users = db_cursor.fetchone()[0]
# Close the connection
db_connection.close()
return matching_users > 0
It is pretty straightforward. We create a connection with our database credentials. Then we create a cursor and execute a request to find if there is a user with that username and password. You will notice in the SQL that we use ? – these will safely be replaced by pyodbc into the SQL so that the client cannot inject SQL code and hack us.
While reading the code keep in mind that this is for example purposes only and is not the best example of a clean implementation. The example is not safe or scalable. It only represents a starting point on which to build upon.
This covers the database handling in our server. The next part is to actually create our flask endpoint. Flask works with functions – it is really easy. You create a flask app (the server). You use the @app.route("/")
to help it define which functions should be called when – and whatever the functions return is what the user will get.
So our goal is to have an endpoint route for logging in with our account. This will be called in from the engine later. This needs to be a POST method so that the data may be safely transferred to the request’s body part. We will also use a common transfer format called JSON that any programming language can translate from and into.
from flask import Flask, request, jsonify
from database import is_user_valid
app = Flask(__name__, instance_relative_config=True)
@app.route("/")
def home():
return "OK", 200
@app.route("/login", methods=["POST"])
def login():
content = request.get_json()
if not content or "username" not in content or "password" not in content:
return "Input data not valid", 400
valid_user = is_user_valid(content["username"], content["password"])
return jsonify({"status": valid_user})
app.run()
So the above example is enough for the prototype. You will see that we created the app. Then we define a dummy home page just to see that the server is working and then we define a route “/login” for our login process.
To run this just run the webapp.py file and it will start the server locally so you will be able to open it up on your PC.
Deployment is a broad topic and is usually biased. I will not talk here on how to deploy your flask applicaiton to the broad public as you will have to figure that yourself. There are a lot of different hosting options that will run your python scripts but it’s up to you.
Conclusion
I will stop right about here. In the second part of this tutorial I will explore more on how to make this HTTP server into a WebSocket server for chat communication between clients and possibly for matchmaking and other purposes.
Leave a comment