Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: allow SQLite database backend #1663

Merged
merged 48 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from 46 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
a7ca72d
simplify leaderboard code, fully abstract database
EmosewaMC Dec 4, 2024
c42b57b
update exception catching
EmosewaMC Dec 4, 2024
02b2572
update exception catching and sql references, remove ugc from gamemes…
EmosewaMC Dec 4, 2024
bcc3239
remove ugc from gamemessages
EmosewaMC Dec 4, 2024
e77d1d3
Update GameMessages.cpp
EmosewaMC Dec 4, 2024
83caecb
Update Leaderboard.cpp
EmosewaMC Dec 4, 2024
0ce02ca
bug fixes
EmosewaMC Dec 4, 2024
e56d4df
fix racing leaderboard
EmosewaMC Dec 4, 2024
768a9ed
remove extra stuff
EmosewaMC Dec 4, 2024
abd8ac7
update
EmosewaMC Dec 4, 2024
ef03a93
add sqlite
EmosewaMC Dec 4, 2024
c78a0d0
use a default for optimizations
EmosewaMC Dec 5, 2024
65206a2
update sqlite
EmosewaMC Dec 5, 2024
5d21553
Fix limits on update and delete
EmosewaMC Dec 5, 2024
5d4afbe
fix bugs
EmosewaMC Dec 6, 2024
2559a05
Merge branch 'main' into sqlite
EmosewaMC Dec 6, 2024
f1511c2
Merge branch 'main' into sqlite
EmosewaMC Dec 7, 2024
2a89e67
use definition to switch between databases
EmosewaMC Dec 7, 2024
fc17a9a
add switch for different backends
EmosewaMC Dec 7, 2024
64aa326
fix include guard and includes
EmosewaMC Dec 7, 2024
ebc74a6
always build both
EmosewaMC Dec 7, 2024
7f7bee8
add mysql if block
EmosewaMC Dec 7, 2024
56b95a3
Update Database.cpp
EmosewaMC Dec 7, 2024
5b6d878
add new options and add check to prevent overriding mysql
EmosewaMC Dec 7, 2024
1da0feb
correct config names
EmosewaMC Dec 7, 2024
2d9f4aa
Update README.md
EmosewaMC Dec 7, 2024
478e2a7
Update README.md
EmosewaMC Dec 7, 2024
d480fc3
merge to 1 sql file for sqlite database
EmosewaMC Dec 7, 2024
f96fda9
move to sqlite folder
EmosewaMC Dec 8, 2024
79f5b0f
add back mysql migrations
EmosewaMC Dec 8, 2024
af04392
Update README.md
EmosewaMC Dec 8, 2024
b3faff3
add migration to correct the folder name or mysql
EmosewaMC Dec 8, 2024
0826828
yes aron
EmosewaMC Dec 8, 2024
321b21b
Merge branch 'main' into sqlite
EmosewaMC Dec 8, 2024
b1e80c1
updates
EmosewaMC Dec 8, 2024
981fb44
Update CMakeLists.txt
EmosewaMC Dec 8, 2024
1be8074
Merge branch 'main' into sqlite
EmosewaMC Dec 8, 2024
9998277
Merge branch 'main' into sqlite
EmosewaMC Dec 9, 2024
8f1801a
dont use paths at all, add where check to only update if folder name …
EmosewaMC Dec 9, 2024
ffa5f8a
default dont auto create account
EmosewaMC Dec 9, 2024
e0b2a9c
default 0
EmosewaMC Dec 9, 2024
490d548
add times played query
EmosewaMC Dec 10, 2024
b979f25
fix leaderboard not incrementing on a not better score
EmosewaMC Dec 10, 2024
47323a2
add env vars with defaults for docker
EmosewaMC Dec 10, 2024
039ab05
use an "enum"
EmosewaMC Dec 12, 2024
bd22776
Merge branch 'main' into sqlite
EmosewaMC Dec 12, 2024
5fedb48
default to mariadb
EmosewaMC Dec 14, 2024
e3c9c71
Update .env.example
EmosewaMC Dec 17, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ NET_VERSION=171022
ACCOUNT_MANAGER_SECRET=
# Should be the externally facing IP of your server host
EXTERNAL_IP=localhost

# The database type that will be used.
# Acceptable values are `sqlite`, `mysql`, `mariadb`, `maria`.
# Case insensitive.
database_type=sqlite
SQLITE_DATABASE_PATH=resServer/dlu.sqlite

# Database values
# Be careful with special characters here. It is more safe to use normal characters and/or numbers.
MARIADB_USER=darkflame
MARIADB_PASSWORD=
MARIADB_DATABASE=darkflame
SKIP_ACCOUNT_CREATION=1
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ docker/configs
# Third party libraries
thirdparty/mysql/
thirdparty/mysql_linux/
CMakeVariables.txt

# Build folders
build/
Expand Down
33 changes: 11 additions & 22 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -175,16 +175,18 @@ foreach(resource_file ${RESOURCE_FILES})
list(GET line_split 0 variable_name)

if(NOT ${parsed_current_file_contents} MATCHES ${variable_name})
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
set(line_to_add ${line_to_add} ${line})
# For backwards compatibility with older setup versions, dont add this option.
if(NOT ${variable_name} MATCHES "database_type")
message(STATUS "Adding missing config option " ${variable_name} " to " ${resource_file})
set(line_to_add ${line_to_add} ${line})

foreach(line_to_append ${line_to_add})
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n" ${line_to_append})
endforeach()
foreach(line_to_append ${line_to_add})
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n" ${line_to_append})
endforeach()

file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n")
file(APPEND ${DLU_CONFIG_DIR}/${resource_file} "\n")
endif()
endif()

set(line_to_add "")
else()
set(line_to_add ${line_to_add} ${line})
Expand Down Expand Up @@ -214,21 +216,8 @@ foreach(file ${VANITY_FILES})
endforeach()

# Move our migrations for MasterServer to run
file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/dlu/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/dlu/*.sql)

foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME)
configure_file(${CMAKE_SOURCE_DIR}/migrations/dlu/${file} ${PROJECT_BINARY_DIR}/migrations/dlu/${file})
endforeach()

file(MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/migrations/cdserver/)
file(GLOB SQL_FILES ${CMAKE_SOURCE_DIR}/migrations/cdserver/*.sql)

foreach(file ${SQL_FILES})
get_filename_component(file ${file} NAME)
configure_file(${CMAKE_SOURCE_DIR}/migrations/cdserver/${file} ${PROJECT_BINARY_DIR}/migrations/cdserver/${file})
endforeach()
file(REMOVE_RECURSE ${PROJECT_BINARY_DIR}/migrations)
file(COPY ${CMAKE_SOURCE_DIR}/migrations DESTINATION ${CMAKE_BINARY_DIR})

# Add system specfic includes for Apple, Windows and Other Unix OS' (including Linux)
if (APPLE)
Expand Down
36 changes: 27 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,32 @@ Darkflame Universe is licensed under AGPLv3, please read [LICENSE](LICENSE). Som
* You must disclose any changes you make to the code when you distribute it
* Hosting a server for others counts as distribution

## Disclaimers
EmosewaMC marked this conversation as resolved.
Show resolved Hide resolved
### Setup difficulty
Throughout the entire build and setup process a level of familiarity with the command line and preferably a Unix-like development environment is greatly advantageous.

### Hosting a server
We do not recommend hosting public servers. Darkflame Universe is intended for small scale deployment, for example within a group of friends. It has not been tested for large scale deployment which comes with additional security risks.

### Supply of resource files
Darkflame Universe is a server emulator and does not distribute any LEGO® Universe files. A separate game client is required to setup this server emulator and play the game, which we cannot supply. Users are strongly suggested to refer to the safe checksums listed [here](#verifying-your-client-files) to see if a client will work.

## Step by step walkthrough for a single-player server
If you would like a setup for a single player server only on a Windows machine, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.
## Setting up a single player server
* If you don't know what WSL is, skip this warning.
Warning: WSL version 1 does NOT support using sqlite as a database due to how it handles filesystem synchronization.
You must use Version 2 if you must run the server under WSL. Not doing so will result in save data loss.
* Single player installs now no longer require building the server from source or installing development tools.
* Download the [latest release](https://github.com/DarkflameUniverse/DarkflameServer/releases) and extract the files into a folder inside your client.
* You should be able to see the folder with the server executables in the same folder as `legouniverse.exe`.
* Open `sharedconfig.ini` and find the line that says `client_location` and put `..` after it so the line reads `client_location=..`.
* To run the server, double-click `MasterServer.exe`.
* You will be asked to create an account the first time you run the server.
* When shutting down the server, it is highly recommended to click the `MasterServer.exe` window and hold `ctrl` while pressing `c` to stop the server.
* We are working on a way to make it so when you close the game, the server saves automatically alongside when you open the game, the server starts automatically.

<font size="32">**If you are not planning on hosting a server for others, working in the codebase or wanting to use MariaDB for a database, you can stop reading here.**</font>

If you would like to use a MariaDB as a database instead of the default of sqlite, follow the steps [here](#database-setup).

## Steps to setup server
# Steps to setup a development environment
* [Clone this repository](#clone-the-repository)
* [Setting up a development environment](#setting-up-a-development-environment)
* [Install dependencies](#install-dependencies)
* [Database setup](#database-setup)
* [Build the server](#build-the-server)
Expand All @@ -39,6 +50,13 @@ If you would like a setup for a single player server only on a Windows machine,
* [User Guide](#user-guide)
* [Docker](#docker)

## Disclaimers
### Setup difficulty
Throughout the entire build and setup process a level of familiarity with the command line and preferably a Unix-like development environment is greatly advantageous.

## Step by step walkthrough for building a single-player Windows server from source
If you would like a setup for a single player server only on a Windows machine built from source, use the [Native Windows Setup Guide by HailStorm](https://gist.github.com/HailStorm32/169df65a47a104199b5cc57d10fa57de) and skip this README.

## Clone the repository
If you are on Windows, you will need to download and install git from [here](https://git-scm.com/download/win)

Expand Down Expand Up @@ -266,8 +284,8 @@ systemctl stop darkflame.service
journalctl -xeu darkflame.service
```

### First admin user
Run `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users!
### First user or adding more users.
The first time you run `MasterServer`, you will be prompted to create an account. To create more accounts from the command line, `MasterServer -a` to get prompted to create an admin account. This method is only intended for the system administrator as a means to get started, do NOT use this method to create accounts for other users!

### Account management tool (Nexus Dashboard)
**If you are just using this server for yourself, you can skip setting up Nexus Dashboard**
Expand Down
1 change: 0 additions & 1 deletion dCommon/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ target_include_directories(dCommon
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase"
"${PROJECT_SOURCE_DIR}/dDatabase/GameDatabase/ITables"
"${PROJECT_SOURCE_DIR}/dDatabase/CDClientDatabase"
"${PROJECT_SOURCE_DIR}/thirdparty/mariadb-connector-cpp/include"
)

if (UNIX)
Expand Down
13 changes: 10 additions & 3 deletions dDatabase/GameDatabase/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ foreach(file ${DDATABSE_DATABSES_MYSQL_SOURCES})
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "MySQL/${file}")
endforeach()

add_subdirectory(SQLite)

foreach(file ${DDATABSE_DATABSES_SQLITE_SOURCES})
set(DDATABASE_GAMEDATABASE_SOURCES ${DDATABASE_GAMEDATABASE_SOURCES} "SQLite/${file}")
endforeach()

add_subdirectory(TestSQL)

foreach(file ${DDATABSE_DATABSES_TEST_SQL_SOURCES})
Expand All @@ -16,13 +22,14 @@ endforeach()

add_library(dDatabaseGame STATIC ${DDATABASE_GAMEDATABASE_SOURCES})
target_include_directories(dDatabaseGame PUBLIC "."
"ITables" PRIVATE "MySQL" "TestSQL"
"ITables" PRIVATE "MySQL" "SQLite" "TestSQL"
"${PROJECT_SOURCE_DIR}/dCommon"
"${PROJECT_SOURCE_DIR}/dCommon/dEnums"
)

target_link_libraries(dDatabaseGame
PUBLIC MariaDB::ConnCpp
INTERFACE dCommon)
INTERFACE dCommon
PRIVATE sqlite3 MariaDB::ConnCpp)

# Glob together all headers that need to be precompiled
file(
Expand Down
28 changes: 26 additions & 2 deletions dDatabase/GameDatabase/Database.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,46 @@
#include "Game.h"
#include "dConfig.h"
#include "Logger.h"
#include "MySQLDatabase.h"
#include "DluAssert.h"

#include "SQLiteDatabase.h"
#include "MySQLDatabase.h"

#include <ranges>

#pragma warning (disable:4251) //Disables SQL warnings

namespace {
GameDatabase* database = nullptr;
}

std::string Database::GetMigrationFolder() {
const std::set<std::string> validMysqlTypes = { "mysql", "mariadb", "maria" };
auto databaseType = Game::config->GetValue("database_type");
std::ranges::transform(databaseType, databaseType.begin(), ::tolower);
if (databaseType == "sqlite") return "sqlite";
else if (validMysqlTypes.contains(databaseType)) return "mysql";
else {
LOG("No database specified, using MySQL");
return "mysql";
}
}

void Database::Connect() {
if (database) {
LOG("Tried to connect to database when it's already connected!");
return;
}

database = new MySQLDatabase();
const auto databaseType = GetMigrationFolder();

if (databaseType == "sqlite") database = new SQLiteDatabase();
else if (databaseType == "mysql") database = new MySQLDatabase();
else {
LOG("Invalid database type specified in config, using MySQL");
database = new MySQLDatabase();
}

database->Connect();
}

Expand Down
2 changes: 2 additions & 0 deletions dDatabase/GameDatabase/Database.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ namespace Database {
// Used for assigning a test database as the handler for database logic.
// Do not use in production code.
void _setDatabase(GameDatabase* const db);

std::string GetMigrationFolder();
};
8 changes: 1 addition & 7 deletions dDatabase/GameDatabase/GameDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,8 @@
#include "IBehaviors.h"
#include "IUgcModularBuild.h"

namespace sql {
class Statement;
class PreparedStatement;
};

#ifdef _DEBUG
# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (sql::SQLException& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0)
# define DLU_SQL_TRY_CATCH_RETHROW(x) do { try { x; } catch (std::exception& ex) { LOG("SQL Error: %s", ex.what()); throw; } } while(0)
jadebenn marked this conversation as resolved.
Show resolved Hide resolved
#else
# define DLU_SQL_TRY_CATCH_RETHROW(x) x
#endif // _DEBUG
Expand All @@ -50,7 +45,6 @@ class GameDatabase :
virtual void Connect() = 0;
virtual void Destroy(std::string source = "") = 0;
virtual void ExecuteCustomQuery(const std::string_view query) = 0;
virtual sql::PreparedStatement* CreatePreppedStmt(const std::string& query) = 0;
virtual void Commit() = 0;
virtual bool GetAutoCommit() = 0;
virtual void SetAutoCommit(bool value) = 0;
Expand Down
2 changes: 2 additions & 0 deletions dDatabase/GameDatabase/ITables/IAccounts.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ class IAccounts {

// Update the GameMaster level of an account.
virtual void UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) = 0;

virtual uint32_t GetAccountCount() = 0;
};

#endif //!__IACCOUNTS__H__
5 changes: 3 additions & 2 deletions dDatabase/GameDatabase/MySQL/MySQLDatabase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ namespace {
};

void MySQLDatabase::Connect() {
LOG("Using MySQL database");
driver = sql::mariadb::get_driver_instance();

// The mariadb connector is *supposed* to handle unix:// and pipe:// prefixes to hostName, but there are bugs where
Expand Down Expand Up @@ -67,7 +68,7 @@ void MySQLDatabase::ExecuteCustomQuery(const std::string_view query) {

sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& query) {
if (!con) {
Connect();
Database::Get()->Connect();
LOG("Trying to reconnect to MySQL");
}

Expand All @@ -76,7 +77,7 @@ sql::PreparedStatement* MySQLDatabase::CreatePreppedStmt(const std::string& quer

con = nullptr;

Connect();
Database::Get()->Connect();
LOG("Trying to reconnect to MySQL from invalid or closed connection");
}

Expand Down
3 changes: 2 additions & 1 deletion dDatabase/GameDatabase/MySQL/MySQLDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ class MySQLDatabase : public GameDatabase {
void Connect() override;
void Destroy(std::string source = "") override;

sql::PreparedStatement* CreatePreppedStmt(const std::string& query) override;
void Commit() override;
bool GetAutoCommit() override;
void SetAutoCommit(bool value) override;
Expand Down Expand Up @@ -125,6 +124,8 @@ class MySQLDatabase : public GameDatabase {
void IncrementTimesPlayed(const uint32_t playerId, const uint32_t gameId) override;
void InsertUgcBuild(const std::string& modules, const LWOOBJID bigId, const std::optional<uint32_t> characterId) override;
void DeleteUgcBuild(const LWOOBJID bigId) override;
sql::PreparedStatement* CreatePreppedStmt(const std::string& query);
uint32_t GetAccountCount() override;
private:

// Generic query functions that can be used for any query.
Expand Down
5 changes: 5 additions & 0 deletions dDatabase/GameDatabase/MySQL/Tables/Accounts.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,8 @@ void MySQLDatabase::InsertNewAccount(const std::string_view username, const std:
void MySQLDatabase::UpdateAccountGmLevel(const uint32_t accountId, const eGameMasterLevel gmLevel) {
ExecuteUpdate("UPDATE accounts SET gm_level = ? WHERE id = ?;", static_cast<int32_t>(gmLevel), accountId);
}

uint32_t MySQLDatabase::GetAccountCount() {
auto res = ExecuteSelect("SELECT COUNT(*) as count FROM accounts;");
return res->next() ? res->getUInt("count") : 0;
}
11 changes: 11 additions & 0 deletions dDatabase/GameDatabase/SQLite/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
SET(DDATABSE_DATABSES_SQLITE_SOURCES
"SQLiteDatabase.cpp"
)

add_subdirectory(Tables)

foreach(file ${DDATABASES_DATABASES_SQLITE_TABLES_SOURCES})
set(DDATABSE_DATABSES_SQLITE_SOURCES ${DDATABSE_DATABSES_SQLITE_SOURCES} "Tables/${file}")
endforeach()

set(DDATABSE_DATABSES_SQLITE_SOURCES ${DDATABSE_DATABSES_SQLITE_SOURCES} PARENT_SCOPE)
Loading
Loading