Database updates

Load Account (for chat handles, etc)
Cleanup Character Data
Cleanup Sessions
This commit is contained in:
HikikoMarmy
2025-07-01 13:54:10 +01:00
parent 494dea65ff
commit c7a3b3baab
2 changed files with 220 additions and 151 deletions

View File

@@ -12,6 +12,8 @@ Database::Database()
throw std::runtime_error( "Failed to open DB: " + std::string( sqlite3_errmsg( m_db ) ) ); throw std::runtime_error( "Failed to open DB: " + std::string( sqlite3_errmsg( m_db ) ) );
} }
m_lastMaintenance = std::chrono::steady_clock::now();
CreateTables(); CreateTables();
PrepareStatements(); PrepareStatements();
} }
@@ -23,6 +25,19 @@ Database::~Database()
sqlite3_close( m_db ); sqlite3_close( m_db );
} }
void Database::Process()
{
auto now = std::chrono::steady_clock::now();
if( now - m_lastMaintenance < std::chrono::hours( 1 ) )
return;
m_lastMaintenance = now;
Log::Info( "Performing database maintenance..." );
DeleteOldSessions();
}
void Database::CreateTables() void Database::CreateTables()
{ {
Execute( "CREATE TABLE IF NOT EXISTS RealmUsers (" Execute( "CREATE TABLE IF NOT EXISTS RealmUsers ("
@@ -37,51 +52,54 @@ void Database::CreateTables()
"account_id INTEGER NOT NULL," "account_id INTEGER NOT NULL,"
"character_id INTEGER PRIMARY KEY AUTOINCREMENT," "character_id INTEGER PRIMARY KEY AUTOINCREMENT,"
"meta_data BLOB NOT NULL," "meta_data BLOB NOT NULL,"
"character_data BLOB NOT NULL," "character_data BLOB NOT NULL)" );
"inventory_data BLOB)" );
Execute( "CREATE TABLE IF NOT EXISTS RealmSession (" Execute( "CREATE TABLE IF NOT EXISTS RealmSession ("
"account_id INTEGER NOT NULL," "account_id INTEGER NOT NULL,"
"session_id TEXT NOT NULL UNIQUE," "session_id TEXT NOT NULL UNIQUE,"
"ip_address TEXT NOT NULL," "character_id INTEGER NOT NULL,"
"expire_time INTEGER NOT NULL," "ip_address TEXT NOT NULL,"
"PRIMARY KEY(session_id))"); "expire_time INTEGER NOT NULL,"
"PRIMARY KEY(session_id))" );
} }
void Database::PrepareStatements() void Database::PrepareStatements()
{ {
const std::vector<std::pair<QueryID, const char *>> queries = { const std::vector<std::pair<QueryID, const char *>> queries = {
{ QueryID::CreateAccount, { QueryID::CreateAccount,
"INSERT INTO RealmUsers (username, password, email_address, date_of_birth, chat_handle ) VALUES (?, ?, ?, ?, ?);" }, "INSERT INTO RealmUsers ( username, password, email_address, date_of_birth, chat_handle ) VALUES ( ?, ?, ?, ?, ? );" },
{ QueryID::VerifyAccount, { QueryID::VerifyAccount,
"SELECT account_id, username, password FROM RealmUsers WHERE username = ?;" }, "SELECT account_id, username, password FROM RealmUsers WHERE username = ?;" },
{ QueryID::LoadAccount,
"SELECT chat_handle FROM RealmUsers WHERE account_id = ?;" },
{ QueryID::CreateSession, { QueryID::CreateSession,
"INSERT INTO RealmSession (account_id, session_id, ip_address, expire_time) VALUES (?, ?, ?, ?);" }, "INSERT INTO RealmSession ( account_id, session_id, character_id, ip_address, expire_time ) VALUES ( ?, ?, ?, ?, ? );" },
{ QueryID::UpdateSession, { QueryID::UpdateSession,
"UPDATE RealmSession SET expire_time = ? WHERE session_id = ?;" }, "UPDATE RealmSession SET expire_time = ?, character_id = ? WHERE session_id = ?;" },
{ QueryID::DeleteSession, { QueryID::DeleteSession,
"DELETE FROM RealmSession WHERE session_id = ?;" }, "DELETE FROM RealmSession WHERE session_id = ?;" },
{ QueryID::GetSession, { QueryID::GetSession,
"SELECT account_id, expire_time FROM RealmSession WHERE session_id = ? AND ip_address = ?;" }, "SELECT account_id, character_id, expire_time FROM RealmSession WHERE session_id = ? AND ip_address = ?;" },
{ QueryID::DeleteOldSessions,
"DELETE FROM RealmSession WHERE expire_time < ?" },
{ QueryID::CreateNewCharacter, { QueryID::CreateNewCharacter,
"INSERT INTO RealmCharacters (account_id, meta_data, character_data) VALUES (?, ?, ?);" }, "INSERT INTO RealmCharacters ( account_id, meta_data, character_data ) VALUES ( ?, ?, ? );" },
{ QueryID::SaveCharacter, { QueryID::SaveCharacter,
"UPDATE RealmCharacters SET meta_data = ?, character_data = ? WHERE account_id = ? AND character_id = ?;" }, "UPDATE RealmCharacters SET meta_data = ?, character_data = ? WHERE account_id = ? AND character_id = ?;" },
{ QueryID::SaveInventory,
"UPDATE RealmCharacters SET inventory_data = ? WHERE account_id = ? AND character_id = ?;" },
{ QueryID::LoadCharacter, { QueryID::LoadCharacter,
"SELECT character_id, meta_data, character_data, inventory_data FROM RealmCharacters WHERE account_id = ? AND character_id = ?;" }, "SELECT character_id, meta_data, character_data FROM RealmCharacters WHERE account_id = ? AND character_id = ?;" },
{ QueryID::LoadAllCharacterMetaData, { QueryID::LoadCharacterSlots,
"SELECT character_id, meta_data FROM RealmCharacters WHERE account_id = ? ORDER BY character_id;" } "SELECT character_id, meta_data FROM RealmCharacters WHERE account_id = ? ORDER BY character_id;" }
}; };
@@ -100,7 +118,7 @@ void Database::PrepareStatements()
catch( const std::exception &e ) catch( const std::exception &e )
{ {
FinalizeStatements(); FinalizeStatements();
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
} }
@@ -130,7 +148,32 @@ void Database::Execute( const char *sql )
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
}
}
void Database::DeleteOldSessions()
{
try
{
auto stmt = m_statements[ QueryID::DeleteOldSessions ];
sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt );
int64_t currentTime = time( nullptr );
sqlite3_bind_int64( stmt, 1, currentTime );
if( sqlite3_step( stmt ) != SQLITE_DONE )
{
throw std::runtime_error( "Delete old sessions failed: " + std::string( sqlite3_errmsg( m_db ) ) );
}
Log::Info( "Old sessions deleted successfully." );
}
catch( const std::exception &e )
{
Log::Error( "Database error: {}", std::string( e.what() ) );
} }
} }
@@ -169,16 +212,18 @@ int64_t Database::CreateNewAccount( const std::string &username,
return 0; return 0;
} }
int64_t Database::VerifyAccount( const std::string &username, const std::string &password ) int64_t Database::VerifyAccount( const std::wstring &username, const std::wstring &password )
{ {
try try
{ {
auto stmt = m_statements[ QueryID::VerifyAccount ]; auto stmt = m_statements[ QueryID::VerifyAccount ];
auto username_utf8 = Util::WideToUTF8( username );
auto password_utf8 = Util::WideToUTF8( password );
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
sqlite3_bind_text( stmt, 1, username.c_str(), -1, SQLITE_TRANSIENT ); sqlite3_bind_text( stmt, 1, username_utf8.c_str(), -1, SQLITE_TRANSIENT );
// Execute the statement // Execute the statement
if( sqlite3_step( stmt ) == SQLITE_ROW ) if( sqlite3_step( stmt ) == SQLITE_ROW )
@@ -187,14 +232,14 @@ int64_t Database::VerifyAccount( const std::string &username, const std::string
const char *dbUsername = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 1 ) ); const char *dbUsername = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 1 ) );
const char *dbPassword = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 2 ) ); const char *dbPassword = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 2 ) );
if( username == dbUsername && VerifyPassword( password, dbPassword ) ) if( username_utf8 == dbUsername && VerifyPassword( password_utf8, dbPassword ) )
{ {
Log::Debug( "Account verified: %s (ID: %lld)", username.c_str(), accountId ); Log::Debug( "Account verified: {} (ID: {})", username, accountId );
return accountId; return accountId;
} }
else else
{ {
Log::Error( "Invalid credentials for account ID: %lld", accountId ); Log::Error( "Invalid credentials for account ID: {}", accountId );
return -1; return -1;
} }
} }
@@ -209,14 +254,58 @@ int64_t Database::VerifyAccount( const std::string &username, const std::string
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return -1; return -1;
} }
bool Database::CreateSession(const int64_t account_id, const std::wstring& session_id, const std::string& ip_address) std::tuple< bool, std::wstring > Database::LoadAccount( const int64_t account_id )
{
try
{
auto stmt = m_statements[ QueryID::LoadAccount ];
sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt );
sqlite3_bind_int64( stmt, 1, account_id );
if( sqlite3_step( stmt ) == SQLITE_ROW )
{
const char *chatHandle = reinterpret_cast< const char * >( sqlite3_column_text( stmt, 0 ) );
if( chatHandle )
{
Log::Debug( "Account loaded: ID: {}, Chat Handle: {}", account_id, chatHandle );
return std::make_tuple( true, Util::UTF8ToWide( chatHandle ) );
}
else
{
Log::Error( "Chat handle not found for account ID: {}", account_id );
return std::make_tuple( false, L"" );
}
}
else if( sqlite3_step( stmt ) == SQLITE_DONE )
{
Log::Error( "No account found for ID: {}", account_id );
return std::make_tuple( false, L"" );
}
else
{
throw std::runtime_error( "Query failed: " + std::string( sqlite3_errmsg( m_db ) ) );
}
}
catch( const std::exception & )
{
}
Log::Error( "Database error while loading account ID: {}", account_id );
return std::make_tuple( false, L"" );
}
bool Database::CreateSession( const int64_t account_id, const std::wstring &session_id, const std::string &ip_address )
{ {
try try
{ {
@@ -228,12 +317,13 @@ bool Database::CreateSession(const int64_t account_id, const std::wstring& sessi
sqlite3_bind_int64( stmt, 1, account_id ); sqlite3_bind_int64( stmt, 1, account_id );
sqlite3_bind_text( stmt, 2, sessionId.c_str(), -1, SQLITE_TRANSIENT ); sqlite3_bind_text( stmt, 2, sessionId.c_str(), -1, SQLITE_TRANSIENT );
sqlite3_bind_text( stmt, 3, ip_address.c_str(), -1, SQLITE_TRANSIENT ); sqlite3_bind_int( stmt, 3, 0 );
sqlite3_bind_int64( stmt, 4, time( nullptr ) + 300 ); // Session expires in 5 minutes sqlite3_bind_text( stmt, 4, ip_address.c_str(), -1, SQLITE_TRANSIENT );
sqlite3_bind_int64( stmt, 5, time( nullptr ) + 300 ); // Session expires in 5 minutes
if( sqlite3_step( stmt ) != SQLITE_DONE ) if( sqlite3_step( stmt ) != SQLITE_DONE )
{ {
Log::Error( "SQLite insert failed: %s", sqlite3_errmsg( m_db ) ); Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) );
return false; return false;
} }
@@ -241,13 +331,13 @@ bool Database::CreateSession(const int64_t account_id, const std::wstring& sessi
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return false; return false;
} }
bool Database::UpdateSession(const std::wstring& session_id) bool Database::UpdateSession( const std::wstring &session_id, const uint32_t character_id )
{ {
try try
{ {
@@ -257,27 +347,28 @@ bool Database::UpdateSession(const std::wstring& session_id)
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
sqlite3_bind_int64(stmt, 1, time(nullptr) + 300); // Extend session by 5 minutes sqlite3_bind_int64( stmt, 1, time( nullptr ) + 300 ); // Extend session by 5 minutes
sqlite3_bind_text(stmt, 2, sessionId.c_str(), -1, SQLITE_TRANSIENT); sqlite3_bind_int( stmt, 2, character_id );
sqlite3_bind_text( stmt, 3, sessionId.c_str(), -1, SQLITE_TRANSIENT );
if (sqlite3_step(stmt) != SQLITE_DONE) if( sqlite3_step( stmt ) != SQLITE_DONE )
{ {
Log::Error("SQLite update failed: %s", sqlite3_errmsg(m_db)); Log::Error( "SQLite update failed: {}", sqlite3_errmsg( m_db ) );
return false; return false;
} }
Log::Debug("Session updated: %s", session_id.c_str()); Log::Debug( "Session updated: {}", session_id );
return true; return true;
} }
catch (const std::exception& e) catch( const std::exception &e )
{ {
Log::Error("Database error: " + std::string(e.what())); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return false; return false;
} }
bool Database::DeleteSession(const std::wstring& session_id) bool Database::DeleteSession( const std::wstring &session_id )
{ {
try try
{ {
@@ -291,21 +382,21 @@ bool Database::DeleteSession(const std::wstring& session_id)
if( sqlite3_step( stmt ) != SQLITE_DONE ) if( sqlite3_step( stmt ) != SQLITE_DONE )
{ {
Log::Error( "SQLite delete failed: %s", sqlite3_errmsg( m_db ) ); Log::Error( "SQLite delete failed: {}", sqlite3_errmsg( m_db ) );
return false; return false;
} }
Log::Debug( "Session deleted: %s", session_id.c_str() ); Log::Debug( "Session deleted: {}", session_id );
return true; return true;
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return false; return false;
} }
int64_t Database::GetSession(std::wstring& session_id, std::string& ip_address) std::tuple< int64_t, uint32_t > Database::GetSession( const std::wstring &session_id, const std::string &ip_address )
{ {
try try
{ {
@@ -320,84 +411,90 @@ int64_t Database::GetSession(std::wstring& session_id, std::string& ip_address)
if( sqlite3_step( stmt ) == SQLITE_ROW ) if( sqlite3_step( stmt ) == SQLITE_ROW )
{ {
int64_t account_id = sqlite3_column_int64( stmt, 0 ); uint64_t account_id = sqlite3_column_int64( stmt, 0 );
int64_t expire_time = sqlite3_column_int64( stmt, 1 ); uint32_t character_id = sqlite3_column_int( stmt, 1 );
uint64_t expire_time = sqlite3_column_int64( stmt, 2 );
if( expire_time > time( nullptr ) ) if( expire_time > time( nullptr ) )
{ {
Log::Debug( "Session found: %s for account ID: %lld", session_id.c_str(), account_id ); Log::Debug( "Session found: {} for account ID: {}", session_id, account_id );
return account_id; return std::make_tuple( account_id, character_id );
}
else
{
Log::Error( "Session expired: %s", session_id.c_str() );
return -1; // Session expired
} }
} }
else if( sqlite3_step( stmt ) == SQLITE_DONE ) else if( sqlite3_step( stmt ) == SQLITE_DONE )
{ {
Log::Error( "No session found for ID: %s", session_id.c_str() ); Log::Error( "No session found for ID: {}", session_id );
return -1; // No session found
} }
else else
{ {
throw std::runtime_error("Query failed: " + std::string(sqlite3_errmsg(m_db))); throw std::runtime_error( "Query failed: " + std::string( sqlite3_errmsg( m_db ) ) );
} }
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return -1; return std::make_tuple( -1, -1 );
} }
int32_t Database::CreateNewCharacter( const int64_t account_id, uint32_t Database::CreateNewCharacter( const int64_t account_id, const CharacterSlotData meta, const std::vector< uint8_t > &blob )
const std::vector< uint8_t > &meta_data,
const std::vector< uint8_t > &character_blob )
{ {
if( account_id <= 0 || meta.empty() || blob.empty() )
{
Log::Error( "Invalid parameters for CreateNewCharacter" );
return 0;
}
try try
{ {
auto stmt = m_statements[ QueryID::CreateNewCharacter ]; auto stmt = m_statements[ QueryID::CreateNewCharacter ];
const auto meta_data = meta.Serialize();
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
sqlite3_bind_int64( stmt, 1, account_id ); sqlite3_bind_int64( stmt, 1, account_id );
sqlite3_bind_blob( stmt, 2, meta_data.data(), static_cast< int >( meta_data.size() ), SQLITE_STATIC ); sqlite3_bind_blob( stmt, 2, meta_data.data(), static_cast< int >( meta_data.size() ), SQLITE_STATIC );
sqlite3_bind_blob( stmt, 3, character_blob.data(), static_cast< int >( character_blob.size() ), SQLITE_STATIC ); sqlite3_bind_blob( stmt, 3, blob.data(), static_cast< int >( blob.size() ), SQLITE_STATIC );
int rc = sqlite3_step( stmt ); int rc = sqlite3_step( stmt );
if( rc != SQLITE_DONE ) if( rc != SQLITE_DONE )
{ {
Log::Error( "SQLite insert failed: %s", sqlite3_errmsg( m_db ) ); Log::Error( "SQLite insert failed: {}", sqlite3_errmsg( m_db ) );
return -1; return 0;
} }
auto character_id = sqlite3_last_insert_rowid( m_db ); uint32_t character_id = static_cast< uint32_t >( sqlite3_last_insert_rowid( m_db ) );
return static_cast< int32_t >( character_id );
return character_id;
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return -1; return 0;
} }
bool Database::SaveCharacter( const int64_t account_id, bool Database::SaveCharacter( const int64_t account_id, const int32_t character_id, const CharacterSlotData meta, const std::vector< uint8_t > &blob )
const int32_t character_id,
const std::vector< uint8_t > &meta_data,
const std::vector< uint8_t > &character_blob )
{ {
if( account_id <= 0 || character_id <= 0 || meta.empty() || blob.empty() )
{
Log::Error( "Invalid parameters for SaveCharacter" );
return false;
}
try try
{ {
auto stmt = m_statements[ QueryID::SaveCharacter ]; auto stmt = m_statements[ QueryID::SaveCharacter ];
const auto meta_data = meta.Serialize();
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
sqlite3_bind_blob(stmt, 1, meta_data.data(), static_cast<int>(meta_data.size()), SQLITE_STATIC); sqlite3_bind_blob( stmt, 1, meta_data.data(), static_cast< int >( meta_data.size() ), SQLITE_STATIC );
sqlite3_bind_blob( stmt, 2, character_blob.data(), static_cast< int >( character_blob.size() ), SQLITE_STATIC ); sqlite3_bind_blob( stmt, 2, blob.data(), static_cast< int >( blob.size() ), SQLITE_STATIC );
sqlite3_bind_int64( stmt, 3, account_id ); sqlite3_bind_int64( stmt, 3, account_id );
sqlite3_bind_int( stmt, 4, character_id ); sqlite3_bind_int( stmt, 4, character_id );
@@ -410,49 +507,19 @@ bool Database::SaveCharacter( const int64_t account_id,
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return false; return false;
} }
bool Database::SaveInventory( const int64_t account_id, std::map< uint32_t, CharacterSlotData > Database::LoadCharacterSlots( const int64_t account_id )
const int32_t character_id,
const std::vector< uint8_t > &inventory_blob )
{ {
try std::map< uint32_t, CharacterSlotData > result;
{
auto stmt = m_statements[ QueryID::SaveInventory ];
sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt );
sqlite3_bind_blob( stmt, 1, inventory_blob.data(), static_cast< int >( inventory_blob.size() ), SQLITE_STATIC );
sqlite3_bind_int64( stmt, 2, account_id );
sqlite3_bind_int( stmt, 3, character_id );
if( sqlite3_step( stmt ) != SQLITE_DONE )
{
throw std::runtime_error( "Update failed: " + std::string( sqlite3_errmsg( m_db ) ) );
}
return true;
}
catch( const std::exception &e )
{
Log::Error( "Database error: " + std::string( e.what() ) );
}
return false;
}
std::map< int32_t, RealmCharacterMetaData > Database::LoadCharacterList( const int64_t account_id )
{
// Character ID, Meta Data
std::map< int32_t, RealmCharacterMetaData > result;
try try
{ {
auto stmt = m_statements[ QueryID::LoadAllCharacterMetaData ]; auto stmt = m_statements[ QueryID::LoadCharacterSlots ];
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -461,23 +528,24 @@ std::map< int32_t, RealmCharacterMetaData > Database::LoadCharacterList( const i
while( sqlite3_step( stmt ) == SQLITE_ROW ) while( sqlite3_step( stmt ) == SQLITE_ROW )
{ {
const auto character_id = sqlite3_column_int( stmt, 0 ); const auto character_id = sqlite3_column_int( stmt, 0 );
const auto *meta_data_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 1 ) );
const auto meta_data_size = sqlite3_column_bytes( stmt, 1 );
if( meta_data_blob == nullptr || meta_data_size == 0 ) const auto *slot_data_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 1 ) );
const auto slot_data_size = sqlite3_column_bytes( stmt, 1 );
if( slot_data_blob == nullptr || slot_data_size == 0 )
{ {
Log::Error( "Character ID %d has no metadata", character_id ); Log::Error( "Character ID {} has no metadata", character_id );
continue; continue;
} }
RealmCharacterMetaData meta_data( std::vector< uint8_t >( meta_data_blob, meta_data_blob + meta_data_size ) ); CharacterSlotData slot_data( std::vector< uint8_t >( slot_data_blob, slot_data_blob + slot_data_size ) );
result[ character_id ] = meta_data; result.insert( { character_id, slot_data } );
} }
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
result.clear(); result.clear();
} }
@@ -490,6 +558,7 @@ sptr_realm_character Database::LoadCharacterData( const int64_t account_id, cons
{ {
auto data = std::make_shared< RealmCharacter >(); auto data = std::make_shared< RealmCharacter >();
auto stmt = m_statements[ QueryID::LoadCharacter ]; auto stmt = m_statements[ QueryID::LoadCharacter ];
sqlite3_reset( stmt ); sqlite3_reset( stmt );
sqlite3_clear_bindings( stmt ); sqlite3_clear_bindings( stmt );
@@ -498,12 +567,14 @@ sptr_realm_character Database::LoadCharacterData( const int64_t account_id, cons
if( sqlite3_step( stmt ) == SQLITE_ROW ) if( sqlite3_step( stmt ) == SQLITE_ROW )
{ {
const auto character_id = sqlite3_column_int( stmt, 0 );
const auto *meta_data_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 1 ) ); const auto *meta_data_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 1 ) );
const auto meta_data_size = sqlite3_column_bytes( stmt, 1 ); const auto meta_data_size = sqlite3_column_bytes( stmt, 1 );
if( meta_data_blob && meta_data_size > 0 ) if( meta_data_blob && meta_data_size > 0 )
{ {
data->SetMetaData( std::vector< uint8_t >( meta_data_blob, meta_data_blob + meta_data_size ) ); data->Deserialize( std::vector< uint8_t >( meta_data_blob, meta_data_blob + meta_data_size ) );
} }
const auto *character_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 2 ) ); const auto *character_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 2 ) );
@@ -511,28 +582,21 @@ sptr_realm_character Database::LoadCharacterData( const int64_t account_id, cons
if( character_blob && character_size > 0 ) if( character_blob && character_size > 0 )
{ {
data->SetCharacterData( std::vector< uint8_t >( character_blob, character_blob + character_size ) ); data->m_characterId = character_id;
data->m_data = std::vector< uint8_t >( character_blob, character_blob + character_size );
} }
const auto *inventory_blob = static_cast< const uint8_t * >( sqlite3_column_blob( stmt, 3 ) ); Log::Debug( "Character data loaded for account ID: {} and character ID: {}", account_id, character_id );
const auto inventory_size = sqlite3_column_bytes( stmt, 3 );
if( inventory_blob && inventory_size > 0 )
{
data->SetInventoryData( std::vector< uint8_t >( inventory_blob, inventory_blob + inventory_size ) );
}
Log::Debug( "Character data loaded for account ID: %lld and character ID: %d", account_id, character_id );
return data; return data;
} }
else else
{ {
Log::Error( "No character data found for account ID: %lld and character ID: %d", account_id, character_id ); Log::Error( "No character data found for account ID: {} and character ID: {}", account_id, character_id );
} }
} }
catch( const std::exception &e ) catch( const std::exception &e )
{ {
Log::Error( "Database error: " + std::string( e.what() ) ); Log::Error( "Database error: {}", std::string( e.what() ) );
} }
return nullptr; return nullptr;

View File

@@ -3,36 +3,39 @@
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <string> #include <string>
#include <vector> #include <tuple>
#include <chrono>
#include <map> #include <map>
#include <sqlite3.h>
#include <sqlite3.h>
#include "../../Game/RealmCharacter.h" #include "../../Game/RealmCharacter.h"
#include "../../Game/RealmCharacterMetaKV.h" #include "../../Game/RealmCharacterMetaKV.h"
enum class QueryID { enum class QueryID {
CreateAccount, CreateAccount,
VerifyAccount, VerifyAccount,
LoadAccount,
CreateSession, CreateSession,
UpdateSession, UpdateSession,
DeleteSession, DeleteSession,
GetSession, GetSession,
DeleteOldSessions,
LoadCharacterSlots,
CreateNewCharacter, CreateNewCharacter,
SaveCharacter, SaveCharacter,
SaveInventory,
LoadCharacter, LoadCharacter,
LoadAllCharacterMetaData,
}; };
class Database { class Database {
private: private:
static inline std::unique_ptr<Database> m_instance; static inline std::unique_ptr<Database> m_instance;
static inline std::mutex m_mutex; static inline std::mutex m_mutex;
static inline std::chrono::time_point< std::chrono::steady_clock > m_lastMaintenance;
sqlite3* m_db = nullptr; sqlite3 *m_db = nullptr;
std::unordered_map< QueryID, sqlite3_stmt * > m_statements; std::unordered_map< QueryID, sqlite3_stmt * > m_statements;
public: public:
@@ -50,34 +53,34 @@ public:
Database(); Database();
~Database(); ~Database();
void Process();
public: public:
int64_t CreateNewAccount( const std::string &username, int64_t CreateNewAccount( const std::string &username,
const std::string &password, const std::string &password,
const std::string &email_address, const std::string &email_address,
const std::string &date_of_birth, const std::string &date_of_birth,
const std::string &chat_handle ); const std::string &chat_handle );
int64_t VerifyAccount( const std::string &username, const std::string &password ); int64_t VerifyAccount( const std::wstring &username, const std::wstring &password );
std::tuple< bool, std::wstring > LoadAccount( const int64_t account_id );
bool CreateSession( const int64_t account_id, const std::wstring &session_id, const std::string &ip_address ); bool CreateSession( const int64_t account_id, const std::wstring &session_id, const std::string &ip_address );
bool UpdateSession( const std::wstring &session_id ); bool UpdateSession( const std::wstring &session_id, const uint32_t character_id );
bool DeleteSession( const std::wstring &session_id ); bool DeleteSession( const std::wstring &session_id );
int64_t GetSession(std::wstring& session_id, std::string& ip_address);
int32_t CreateNewCharacter( const int64_t account_id, std::tuple< int64_t, uint32_t > GetSession( const std::wstring &session_id, const std::string &ip_address );
const std::vector< uint8_t > &meta_data,
const std::vector< uint8_t > &character_blob ); uint32_t CreateNewCharacter( const int64_t account_id,
const CharacterSlotData meta,
const std::vector< uint8_t > &blob );
bool SaveCharacter( const int64_t account_id, bool SaveCharacter( const int64_t account_id,
const int32_t character_id, const int32_t character_id,
const std::vector< uint8_t > &meta_data, const CharacterSlotData meta,
const std::vector< uint8_t > &character_blob ); const std::vector< uint8_t > &blob );
bool SaveInventory( const int64_t account_id, std::map< uint32_t, CharacterSlotData > LoadCharacterSlots( const int64_t account_id );
const int32_t character_id,
const std::vector< uint8_t > &inventory_data );
std::map< int32_t, RealmCharacterMetaData > LoadCharacterList( const int64_t account_id );
sptr_realm_character LoadCharacterData( const int64_t account_id, const int32_t character_id ); sptr_realm_character LoadCharacterData( const int64_t account_id, const int32_t character_id );
@@ -86,4 +89,6 @@ private:
void PrepareStatements(); void PrepareStatements();
void FinalizeStatements(); void FinalizeStatements();
void Execute( const char *sql ); void Execute( const char *sql );
void DeleteOldSessions();
}; };