Skip to main content

Database

HELIX provides a built-in database system through the Database module, backed by SQLite. This gives you persistent storage for player data, inventories, leaderboards, world state, and anything else that needs to survive a server restart.

Initializing the Database

The database file lives on your server and is created automatically when you first initialize. Call Database.Initialize once with the file path for your .db file.

// Initialize (or create) a SQLite database
Database::Initialize("Saved/Database/my_world.db");

Creating Tables

Define your schema with standard SQL. Run this on server start so your tables are always ready.

Database::Execute(R"(
CREATE TABLE IF NOT EXISTS players (
id TEXT PRIMARY KEY,
name TEXT,
money INTEGER DEFAULT 0,
inventory TEXT DEFAULT '[]',
last_position TEXT
)
)");

CRUD Operations

Use Database.Execute for writes and Database.Select for reads. Parameterized queries keep your data safe from injection.

// Insert a new player
Database::Execute("INSERT OR IGNORE INTO players (id, name) VALUES (?, ?)",
Player->GetSteamID(), Player->GetName());

// Update player money
Database::Execute("UPDATE players SET money = ? WHERE id = ?",
500, Player->GetSteamID());

// Read player data
TArray<FDatabaseRow> Rows = Database::Select("SELECT * FROM players WHERE id = ?",
Player->GetSteamID());

// Delete a player record
Database::Execute("DELETE FROM players WHERE id = ?", Player->GetSteamID());

Saving Player Data on Events

A common pattern is to save data when players disconnect and load it when they join.

// Save position on disconnect
Events::Subscribe("PlayerDisconnect", [](APlayerController* Player) {
FVector Pos = Player->GetCharacter()->GetLocation();
Database::Execute("UPDATE players SET last_position = ? WHERE id = ?",
FString::Printf(TEXT("%f,%f,%f"), Pos.X, Pos.Y, Pos.Z),
Player->GetSteamID());
});

// Load position on join
Events::Subscribe("PlayerReady", [](APlayerController* Player) {
auto Rows = Database::Select("SELECT last_position FROM players WHERE id = ?",
Player->GetSteamID());
if (Rows.Num() > 0 && !Rows[0]["last_position"].IsEmpty()) {
// Parse and teleport to saved position
}
});

Async Operations

For operations that shouldn't block the game loop, use Database.ExecuteAsync and Database.SelectAsync. These run in the background and call a callback function when complete.

tip

Always use parameterized queries (? placeholders) instead of string concatenation. This prevents SQL injection and handles special characters automatically.