Asynchronous Multiplayer Using MariaDB and WebSocket

About The Game

Play at

This is a golfing “mmo” with invisible floors where previous best paths are visible to everyone. The more who play the more you can see where to go. Paths are reset every 20 minutes.

Technical Details

This game was created using using Unity3D, Apache2/PHP, MariaDB, and Docker.

The client initiates the game by visiting This downloads the game client from our generic web server onto to client device. Once the game client has been downloaded, and started, the game will start making requests to the Game API.

In this case, the game API is created using PHP scripts. These scripts are hosted on the same Apache2 server which hosts the game client. The game API could just as well be located on a separate system, however for this project it was placed in the same Apache2 docker containers to reduce infrastructure complexity.

The game API talks to the MariaDB to store any persistent game data. For example, when the game client requests the path of all the previous players, it send that request to the game API. The game API will then ask for all the paths uploaded within the last 20 minutes, with a limit of a hundred paths or so. The game API then sends back the results from the database in form of a JSON object which the client can decode, and render locally.

Excerpt from get_all_player_ghosts.php

$time = time();

// This creates "time slots" with 20 minutes intervals
$secondsToReset = 1200; // 20 minutes
$remainder = $time % $secondsToReset;
$secondsTillReset = $secondsToReset - $remainder;
$cutoffTime = $time - $remainder;

// Requests are made using JSON objects encoded to GET URLs.
// Here we extract the get parameter object and decode it.
$postBody = $_GET["body"];
$getParams = json_decode($postBody, true);

$Level = $getParams["Level"];          // Currently played level on the game client.
$MaxAmount = $getParams["MaxAmount"];  // Max ammount of paths to return to the client (weaker clients might request fewer)

// Connect to DB
$conn = Database::createConnectionToDB();
if (!$conn) {
    echo "Failed to connect to database!";

// Select all our paths within our current time-slot, sort by latest, and limit result to $MaxAmount
$stmt = "SELECT * FROM ".SQL::GHOSTS_TABLE." WHERE world = ? AND time > ? ORDER BY score DESC LIMIT ?";
$stmt = $conn->prepare($stmt);
if (!$stmt) {
    echo "Ivalid SQL statment: " . $conn->error;
$stmt->bind_param('sii', $Level, $cutoffTime, $MaxAmount);
if (!$stmt->execute()) {
    error_msg("Failed to get ghost info: " . $conn->error);
$result = $stmt->get_result();

// Iterate over result and send back as JSON

Full code can be found here.

Related tags: