Skip to content

Commit

Permalink
Improved servers and server browser code
Browse files Browse the repository at this point in the history
Updated API spec to include private_key requirements

Modularised the Rust server and compliance to new spec
Updated the PHP server to comply with new spec, additional config to allow flatfile and MySQL databases. Added ReadMe.

ServerBrowser major refactor. Now loads data from the lobby server
  • Loading branch information
AMacro committed Jun 27, 2024
1 parent 492938e commit 94f344f
Show file tree
Hide file tree
Showing 30 changed files with 1,397 additions and 545 deletions.
10 changes: 10 additions & 0 deletions Lobby Servers/PHP Server/DatabaseInterface.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?php

interface DatabaseInterface {
public function addGameServer($data);
public function updateGameServer($data);
public function removeGameServer($data);
public function listGameServers();
}

?>
96 changes: 96 additions & 0 deletions Lobby Servers/PHP Server/FlatfileDatabase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
<?php

class FlatfileDatabase implements DatabaseInterface {
private $filePath;

public function __construct($dbConfig) {
$this->filePath = $dbConfig['flatfile_path'];
}

private function readData() {
if (!file_exists($this->filePath)) {
return [];
}
return json_decode(file_get_contents($this->filePath), true) ?? [];
}

private function writeData($data) {
file_put_contents($this->filePath, json_encode($data, JSON_PRETTY_PRINT));
}

public function addGameServer($data) {
$data['last_update'] = time(); // Set current time as last_update

$servers = $this->readData();
$servers[] = $data;
$this->writeData($servers);

return json_encode(["game_server_id" => $data['game_server_id']]);
}

public function updateGameServer($data) {
$servers = $this->readData();
$updated = false;

foreach ($servers as &$server) {
if ($server['game_server_id'] === $data['game_server_id']) {
$server['current_players'] = $data['current_players'];
$server['time_passed'] = $data['time_passed'];
$server['last_update'] = time(); // Update with current time
$updated = true;
break;
}
}

if ($updated) {
$this->writeData($servers);
return json_encode(["message" => "Server updated"]);
} else {
return json_encode(["error" => "Failed to update server"]);
}
}

public function removeGameServer($data) {
$servers = $this->readData();
$servers = array_filter($servers, function($server) use ($data) {
return $server['game_server_id'] !== $data['game_server_id'];
});
$this->writeData(array_values($servers));
return json_encode(["message" => "Server removed"]);
}

public function listGameServers() {
$servers = $this->readData();
$current_time = time();
$active_servers = [];
$changed = false;

foreach ($servers as $key => $server) {
if ($current_time - $server['last_update'] <= TIMEOUT) {
$active_servers[] = $server;
} else {
$changed = true; // Indicates there's a change if any server is removed
}
}

if ($changed) {
$this->writeData($active_servers); // Write back only if there are changes
}

return json_encode($active_servers);
}



public function getGameServer($game_server_id) {
$servers = $this->readData();
foreach ($servers as $server) {
if ($server['game_server_id'] === $game_server_id) {
return json_encode($server);
}
}
return json_encode(null);
}
}

?>
74 changes: 74 additions & 0 deletions Lobby Servers/PHP Server/MySQLDatabase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

class MySQLDatabase implements DatabaseInterface {
private $pdo;

public function __construct($dbConfig) {
$this->pdo = new PDO("mysql:host={$dbConfig['host']};dbname={$dbConfig['dbname']}", $dbConfig['username'], $dbConfig['password']);
$this->pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}

public function addGameServer($data) {
$stmt = $this->pdo->prepare("INSERT INTO game_servers (game_server_id, private_key, ip, port, server_name, password_protected, game_mode, difficulty, time_passed, current_players, max_players, required_mods, game_version, multiplayer_version, server_info, last_update)
VALUES (:game_server_id, :private_key, :ip, :port, :server_name, :password_protected, :game_mode, :difficulty, :time_passed, :current_players, :max_players, :required_mods, :game_version, :multiplayer_version, :server_info, :last_update)");
$stmt->execute([
':game_server_id' => $data['game_server_id'],
':private_key' => $data['private_key'],
':ip' => $data['ip'],
':port' => $data['port'],
':server_name' => $data['server_name'],
':password_protected' => $data['password_protected'],
':game_mode' => $data['game_mode'],
':difficulty' => $data['difficulty'],
':time_passed' => $data['time_passed'],
':current_players' => $data['current_players'],
':max_players' => $data['max_players'],
':required_mods' => $data['required_mods'],
':game_version' => $data['game_version'],
':multiplayer_version' => $data['multiplayer_version'],
':server_info' => $data['server_info'],
':last_update' => time() //use current time
]);
return json_encode(["game_server_id" => $data['game_server_id']]);
}

public function updateGameServer($data) {
$stmt = $this->pdo->prepare("UPDATE game_servers
SET current_players = :current_players, time_passed = :time_passed, last_update = :last_update
WHERE game_server_id = :game_server_id");
$stmt->execute([
':current_players' => $data['current_players'],
':time_passed' => $data['time_passed'],
':last_update' => time(), // Update with current time
':game_server_id' => $data['game_server_id']
]);

return $stmt->rowCount() > 0 ? json_encode(["message" => "Server updated"]) : json_encode(["error" => "Failed to update server"]);
}

public function removeGameServer($data) {
$stmt = $this->pdo->prepare("DELETE FROM game_servers WHERE game_server_id = :game_server_id");
$stmt->execute([':game_server_id' => $data['game_server_id']]);
return $stmt->rowCount() > 0 ? json_encode(["message" => "Server removed"]) : json_encode(["error" => "Failed to remove server"]);
}

public function listGameServers() {
// Remove servers that exceed TIMEOUT directly in the SQL query
$stmt = $this->pdo->prepare("DELETE FROM game_servers WHERE last_update < :timeout");
$stmt->execute([':timeout' => time() - TIMEOUT]);

// Fetch remaining servers
$stmt = $this->pdo->query("SELECT * FROM game_servers");
$servers = $stmt->fetchAll(PDO::FETCH_ASSOC);

return json_encode($servers);
}

public function getGameServer($game_server_id) {
$stmt = $this->pdo->prepare("SELECT * FROM game_servers WHERE game_server_id = :game_server_id");
$stmt->execute([':game_server_id' => $game_server_id]);
return json_encode($stmt->fetch(PDO::FETCH_ASSOC));
}
}

?>
146 changes: 146 additions & 0 deletions Lobby Servers/PHP Server/Read Me.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
# Lobby Server - PHP

This is a PHP implementation of the Derail Valley Lobby Server REST API service. It is designed to run on any standard web hosting and does not rely on long-running/persistent behaviour.
HTTPS support depends on the configuration of the hosting environment.

As this implementation is not persistent in memory, a database is used to store server information. Two options are available for the database engine - a JSON based flatfile or a MySQL database.

## Installing

1. Copy the following files to your public html folder (consult your web server/web host's documentation)
```
index.php
DatabaseInterface.php
FlatfileDatabase.php
MySQLDatabase.php
```
2. Copy `config.php` to a secure location outside of your public html directory
3. Edit `index.php` and update the path to the config file on line 2:
```php
<?php
include '/path/to/config.php';
...
```

### Setup the database

Choose between a MySQL database and a flatfile database.

#### MySQL

1. Edit `config.php` and set the `type` parameter to `mysql`
2. Update the `host`, `dbname`, `username` and `password` parameters for your MySQL server
3. Copy the `install.php` file to a location you can execute it
4. Edit `install.php` and update the path to the config file on line 2:
```php
<?php
include '/path/to/config.php';
...
```
5. Run `install.php`
6. If `install.php` is in a publically visible location, remove it


#### Flatfile
1. In a secure location (outside of your public html directory), create a directory to store the database
2. Edit `config.php` and set the `type` parameter to `flatfile`
3. Update `flatfile_path` to the directory created in step 1


## Configuration Parameters
The server can be configured using the `config.php` file.

Below are the available parameters along with their defaults:
- `TIMEOUT` (u64): The time-out period in seconds for server removal.
-- Default: `120`
-- `dbConfig`.`type` (string): Type of database for persisting data.
-- Options: `mysql`, `flatfile`
-- Default: `mysql`
- `dbConfig`.`host` (string): hostname of MySQLServer.
-- Required when: `dbConfig`.`type` is `mysql`
-- Default: `localhost`
- `dbConfig`.`dbname` (string): Database name.
- Required when: `dbConfig`.`type` is `mysql`
-- Default: N/A
- `dbConfig`.`username` (string): Username for connecting to the MySQLServer.
-- Required when: `dbConfig`.`type` is `mysql`
-- Default: N/A
- `dbConfig`.`password` (string): Password for connecting to the MySQLServer.
-- Required when: `dbConfig`.`type` is `mysql`
-- Default: N/A
- `dbConfig`.`flatfile_path` (string): Path to secure directory.
-- Required when: `dbConfig`.`type` is `flatfile`
-- Default: N/A

Example `config.php` using MySQL:
```php
<?php

// Timeout value (in seconds)
define('TIMEOUT', 120);

// Database configuration
$dbConfig = [
'type' => 'mysql',
'host' => 'localhost',
'dbname' => 'dv_lobby',
'username' => 'dv_lobby_server',
'password' => 'n16O5+LMpeqI`{E',
'flatfile_path' => '' // Path to store the flatfile database
];
?>
```

Example `config.php` using Flatfile:
```php
<?php

// Timeout value (in seconds)
define('TIMEOUT', 120);

// Database configuration
$dbConfig = [
'type' => 'flatfile',
'host' => '',
'dbname' => '',
'username' => '',
'password' => '',
'flatfile_path' => '/dv_lobby/flatfile.db' // Path to store the flatfile database
];
?>
```

## Security Considerations
This is a non-comprehensive overview of security considerations. You should always use up-to-date best practices and seek professional advice where required.

### Environment variables
Consider using environment variables to store sensitive database credentials (e.g. `dbConfig`.`host`, `dbConfig`.`dbname`, `dbConfig`.`username`, `dbConfig`.`password`) instead of hardcoding them in config.php.
Your `config.php` can be updated to reference the environment variables.

Example:
```php
$dbConfig = [
'type' => 'mysql',
'host' => getenv('DB_HOST'),
'dbname' => getenv('DB_NAME'),
'username' => getenv('DB_USER'),
'password' => getenv('DB_PASSWORD'),
'flatfile_path' => '/path/to/flatfile.db'
];
```


### File Permissions
Ensure that `config.php` and any other sensitive files outside the web root are only readable by the web server user (chmod 600).
For directories containing flatfile databases, restrict permissions (chmod 700 or 750) to prevent unauthorised access.

### HTTPS (SSL)
Configure your server to use https. Many web hosts provide free SSL certificates via Let's Encrypt.
Consider forcing https via server config/`.httaccess`.

Example:
```apacheconf
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
```
4 changes: 3 additions & 1 deletion Lobby Servers/PHP Server/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@

// Database configuration
$dbConfig = [
'type' => 'mysql', // Change to 'flatfile' to use flatfile database
'host' => 'localhost',
'dbname' => 'your_database',
'username' => 'your_username',
'password' => 'your_password'
'password' => 'your_password',
'flatfile_path' => '/path/to/flatfile.db' // Path to store the flatfile database
];

?>
Loading

0 comments on commit 94f344f

Please sign in to comment.