diff options
| author | fusion32 <marcopuzziello@gmail.com> | 2025-07-13 10:04:53 -0300 |
|---|---|---|
| committer | fusion32 <marcopuzziello@gmail.com> | 2025-07-13 10:04:53 -0300 |
| commit | f827fcc84815c3727aae652d2a60a801b2432768 (patch) | |
| tree | ec0d514cf052ac65585ed02fc12f8630432e9d7d /src | |
| parent | 1cad8468cbaf61309a64194fca547a67a700a6da (diff) | |
| download | querymanager-f827fcc84815c3727aae652d2a60a801b2432768.tar.gz querymanager-f827fcc84815c3727aae652d2a60a801b2432768.zip | |
reorganize query functions and impl SET_NOTATION
Diffstat (limited to 'src')
| -rw-r--r-- | src/connections.cc | 83 | ||||
| -rw-r--r-- | src/database.cc | 401 | ||||
| -rw-r--r-- | src/querymanager.cc | 25 | ||||
| -rw-r--r-- | src/querymanager.hh | 36 |
4 files changed, 371 insertions, 174 deletions
diff --git a/src/connections.cc b/src/connections.cc index d775b0b..52c4fea 100644 --- a/src/connections.cc +++ b/src/connections.cc @@ -481,10 +481,11 @@ void ProcessLoginQuery(TConnection *Connection, TReadBuffer *Buffer){ return; } - int WorldID = -1; + int WorldID = 0; if(ApplicationType == APPLICATION_TYPE_GAME){ - if(!LoadWorldID(LoginData, &WorldID)){ - LOG_WARN("Invalid world name \"%s\"", LoginData); + WorldID = GetWorldID(LoginData); + if(WorldID == 0){ + LOG_WARN("Unknown world name \"%s\"", LoginData); SendQueryStatusFailed(Connection); return; } @@ -518,7 +519,61 @@ void ProcessLogoutGameQuery(TConnection *Connection, TReadBuffer *Buffer){ } void ProcessSetNamelockQuery(TConnection *Connection, TReadBuffer *Buffer){ - SendQueryStatusFailed(Connection); + if(Connection->ApplicationType != APPLICATION_TYPE_GAME){ + SendQueryStatusFailed(Connection); + return; + } + + char CharacterName[30]; + char IPString[16]; + char Reason[200]; + char Comment[200]; + int GamemasterID = (int)Buffer->Read32(); + Buffer->ReadString(CharacterName, sizeof(CharacterName)); + Buffer->ReadString(IPString, sizeof(IPString)); + Buffer->ReadString(Reason, sizeof(Reason)); + Buffer->ReadString(Comment, sizeof(Comment)); + + int IPAddress; + if(!ParseIPAddress(IPString, &IPAddress)){ + SendQueryStatusFailed(Connection); + return; + } + + TransactionScope Tx("SetNamelock"); + if(!Tx.Begin()){ + SendQueryStatusFailed(Connection); + return; + } + + int CharacterID = GetCharacterID(Connection->WorldID, CharacterName); + if(CharacterID == 0){ + SendQueryStatusError(Connection, 1); + return; + } + + if(GetCharacterRight(CharacterID, "NAMELOCK")){ + SendQueryStatusError(Connection, 2); + return; + } + + bool Approved; + if(GetNamelockStatus(CharacterID, &Approved)){ + SendQueryStatusError(Connection, (Approved ? 4 : 3)); + return; + } + + if(!InsertNamelock(CharacterID, IPAddress, GamemasterID, Reason, Comment)){ + SendQueryStatusFailed(Connection); + return; + } + + if(!Tx.Commit()){ + SendQueryStatusFailed(Connection); + return; + } + + SendQueryStatusOk(Connection); } void ProcessBanishAccountQuery(TConnection *Connection, TReadBuffer *Buffer){ @@ -619,7 +674,7 @@ void ProcessEvictFreeAccountsQuery(TConnection *Connection, TReadBuffer *Buffer) } DynamicArray<THouseEviction> Evictions; - if(!LoadFreeAccountEvictions(Connection->WorldID, &Evictions)){ + if(!GetFreeAccountEvictions(Connection->WorldID, &Evictions)){ SendQueryStatusFailed(Connection); return; } @@ -641,7 +696,7 @@ void ProcessEvictDeletedCharactersQuery(TConnection *Connection, TReadBuffer *Bu } DynamicArray<THouseEviction> Evictions; - if(!LoadDeletedCharacterEvictions(Connection->WorldID, &Evictions)){ + if(!GetDeletedCharacterEvictions(Connection->WorldID, &Evictions)){ SendQueryStatusFailed(Connection); return; } @@ -669,15 +724,9 @@ void ProcessEvictExGuildleadersQuery(TConnection *Connection, TReadBuffer *Buffe DynamicArray<int> Evictions; int NumGuildHouses = Buffer->Read16(); for(int i = 0; i < NumGuildHouses; i += 1){ - bool IsGuildLeader = false; int HouseID = Buffer->Read16(); int OwnerID = (int)Buffer->Read32(); - if(!CheckGuildLeaderStatus(Connection->WorldID, OwnerID, &IsGuildLeader)){ - SendQueryStatusFailed(Connection); - return; - } - - if(!IsGuildLeader){ + if(!GetGuildLeaderStatus(Connection->WorldID, OwnerID)){ Evictions.Push(HouseID); } } @@ -747,7 +796,7 @@ void ProcessGetHouseOwnersQuery(TConnection *Connection, TReadBuffer *Buffer){ } DynamicArray<THouseOwner> Owners; - if(!LoadHouseOwners(Connection->WorldID, &Owners)){ + if(!GetHouseOwners(Connection->WorldID, &Owners)){ SendQueryStatusFailed(Connection); return; } @@ -771,7 +820,7 @@ void ProcessGetAuctionsQuery(TConnection *Connection, TReadBuffer *Buffer){ } DynamicArray<int> Auctions; - if(!LoadHouseAuctions(Connection->WorldID, &Auctions)){ + if(!GetHouseAuctions(Connection->WorldID, &Auctions)){ SendQueryStatusFailed(Connection); return; } @@ -927,7 +976,7 @@ void ProcessLoadPlayersQuery(TConnection *Connection, TReadBuffer *Buffer){ int NumEntries; TCharacterIndexEntry Entries[10000]; int MinimumCharacterID = (int)Buffer->Read32(); - if(!LoadCharacterIndex(Connection->WorldID, + if(!GetCharacterIndexEntries(Connection->WorldID, MinimumCharacterID, NARRAY(Entries), &NumEntries, Entries)){ SendQueryStatusFailed(Connection); return; @@ -1002,7 +1051,7 @@ void ProcessLoadWorldConfigQuery(TConnection *Connection, TReadBuffer *Buffer){ } TWorldConfig WorldConfig = {}; - if(!LoadWorldConfig(Connection->WorldID, &WorldConfig)){ + if(!GetWorldConfig(Connection->WorldID, &WorldConfig)){ SendQueryStatusFailed(Connection); return; } diff --git a/src/database.cc b/src/database.cc index 4311fa2..55bec11 100644 --- a/src/database.cc +++ b/src/database.cc @@ -141,10 +141,10 @@ bool TransactionScope::Commit(void){ return true; } -// Queries +// Primary tables //============================================================================== -bool LoadWorldID(const char *WorldName, int *WorldID){ - ASSERT(WorldName != NULL && WorldID != NULL); +int GetWorldID(const char *WorldName){ + ASSERT(WorldName != NULL); sqlite3_stmt *Stmt = PrepareQuery( "SELECT WorldID FROM Worlds WHERE Name = ?1"); if(Stmt == NULL){ @@ -157,15 +157,130 @@ bool LoadWorldID(const char *WorldName, int *WorldID){ return false; } + int ErrorCode = sqlite3_step(Stmt); + if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return 0; + } + + return (ErrorCode == SQLITE_ROW ? sqlite3_column_int(Stmt, 0) : 0); +} + +bool GetWorldConfig(int WorldID, TWorldConfig *WorldConfig){ + ASSERT(WorldConfig != NULL); + sqlite3_stmt *Stmt = PrepareQuery( + "SELECT Type, RebootTime, Address, Port, MaxPlayers," + " PremiumPlayerBuffer, MaxNewbies, PremiumNewbieBuffer" + " FROM Worlds WHERE WorldID = ?1"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return false; + } + + if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK){ + LOG_ERR("Failed to bind WorldID: %s", sqlite3_errmsg(g_Database)); + return false; + } + if(sqlite3_step(Stmt) != SQLITE_ROW){ LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); return false; } - *WorldID = sqlite3_column_int(Stmt, 0); + WorldConfig->Type = sqlite3_column_int(Stmt, 0); + WorldConfig->RebootTime = sqlite3_column_int(Stmt, 1); + WorldConfig->Address = sqlite3_column_int(Stmt, 2); + WorldConfig->Port = sqlite3_column_int(Stmt, 3); + WorldConfig->MaxPlayers = sqlite3_column_int(Stmt, 4); + WorldConfig->PremiumPlayerBuffer = sqlite3_column_int(Stmt, 5); + WorldConfig->MaxNewbies = sqlite3_column_int(Stmt, 6); + WorldConfig->PremiumNewbieBuffer = sqlite3_column_int(Stmt, 7); return true; } +int GetCharacterID(int WorldID, const char *CharacterName){ + ASSERT(CharacterName != NULL); + sqlite3_stmt *Stmt = PrepareQuery( + "SELECT CharacterID FROM Characters" + " WHERE WorldID = ?1 AND Name = ?2"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return 0; + } + + if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK + || sqlite3_bind_text(Stmt, 2, CharacterName, -1, NULL) != SQLITE_OK){ + LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); + return 0; + } + + int ErrorCode = sqlite3_step(Stmt); + if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return 0; + } + + return (ErrorCode == SQLITE_ROW ? sqlite3_column_int(Stmt, 0) : 0); +} + +bool GetCharacterRight(int CharacterID, const char *Right){ + ASSERT(Right != NULL); + sqlite3_stmt *Stmt = PrepareQuery( + "SELECT 1 FROM CharacterRights" + " WHERE CharacterID = ?1 AND Right = ?2"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return false; + } + + if(sqlite3_bind_int(Stmt, 1, CharacterID) != SQLITE_OK + || sqlite3_bind_text(Stmt, 2, Right, -1, NULL) != SQLITE_OK){ + LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); + return false; + } + + int ErrorCode = sqlite3_step(Stmt); + if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return false; + } + + return (ErrorCode == SQLITE_ROW); +} + +bool GetGuildLeaderStatus(int WorldID, int CharacterID){ + // NOTE(fusion): Same as `DecrementIsOnline`. + sqlite3_stmt *Stmt = PrepareQuery( + "SELECT Guild, Rank FROM Characters" + " WHERE WorldID = ?1 AND CharacterID = ?2"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return false; + } + + if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK + || sqlite3_bind_int(Stmt, 2, CharacterID) != SQLITE_OK){ + LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); + return false; + } + + int ErrorCode = sqlite3_step(Stmt); + if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return false; + } + + bool Result = false; + if(ErrorCode == SQLITE_ROW){ + const char *Guild = (const char*)sqlite3_column_text(Stmt, 0); + const char *Rank = (const char*)sqlite3_column_text(Stmt, 1); + if(Guild != NULL && Guild[0] != 0 && Rank != NULL && StringEqCI(Rank, "Leader")){ + Result = true; + } + } + return Result; +} + bool DecrementIsOnline(int WorldID, int CharacterID){ // NOTE(fusion): A character is uniquely identified by its id. The world id // check is purely to avoid a world from modifying a character from another @@ -192,6 +307,69 @@ bool DecrementIsOnline(int WorldID, int CharacterID){ return sqlite3_changes(g_Database) > 0; } +bool ClearIsOnline(int WorldID, int *NumAffectedCharacters){ + sqlite3_stmt *Stmt = PrepareQuery( + "UPDATE Characters SET IsOnline = 0" + " WHERE WorldID = ?1 AND IsOnline != 0"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return false; + } + + if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK){ + LOG_ERR("Failed to bind WorldID: %s", sqlite3_errmsg(g_Database)); + return false; + } + + if(sqlite3_step(Stmt) != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return false; + } + + *NumAffectedCharacters = sqlite3_changes(g_Database); + return true; +} + +bool GetCharacterIndexEntries(int WorldID, int MinimumCharacterID, + int MaxEntries, int *NumEntries, TCharacterIndexEntry *Entries){ + ASSERT(MaxEntries > 0 && NumEntries != NULL && Entries != NULL); + sqlite3_stmt *Stmt = PrepareQuery( + "SELECT CharacterID, Name FROM Characters" + " WHERE WorldID = ?1 AND CharacterID >= ?2" + " ORDER BY CharacterID ASC LIMIT ?3"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return false; + } + + if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK + || sqlite3_bind_int(Stmt, 2, MinimumCharacterID) != SQLITE_OK + || sqlite3_bind_int(Stmt, 3, MaxEntries) != SQLITE_OK){ + LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); + return false; + } + + // NOTE(fusion): We shouldn't get more than `MaxEntries` rows but it's + // always better to be safe. + int EntryIndex = 0; + while(sqlite3_step(Stmt) == SQLITE_ROW && EntryIndex < MaxEntries){ + Entries[EntryIndex].CharacterID = sqlite3_column_int(Stmt, 0); + StringCopy(Entries[EntryIndex].Name, + sizeof(Entries[EntryIndex].Name), + (const char *)sqlite3_column_text(Stmt, 1)); + } + + if(sqlite3_errcode(g_Database) != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return false; + } + + *NumEntries = EntryIndex; + return true; +} + +// House tables +//============================================================================== bool FinishHouseAuctions(int WorldID, DynamicArray<THouseAuction> *Auctions){ ASSERT(Auctions != NULL); // TODO(fusion): If the application crashes while processing finished auctions, @@ -267,7 +445,7 @@ bool FinishHouseTransfers(int WorldID, DynamicArray<THouseTransfer> *Transfers){ return true; } -bool LoadFreeAccountEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions){ +bool GetFreeAccountEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions){ ASSERT(Evictions != NULL); sqlite3_stmt *Stmt = PrepareQuery( "SELECT O.HouseID, O.OwnerID" @@ -300,7 +478,7 @@ bool LoadFreeAccountEvictions(int WorldID, DynamicArray<THouseEviction> *Evictio return true; } -bool LoadDeletedCharacterEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions){ +bool GetDeletedCharacterEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions){ ASSERT(Evictions != NULL); sqlite3_stmt *Stmt = PrepareQuery( "SELECT O.HouseID, O.OwnerID" @@ -333,41 +511,6 @@ bool LoadDeletedCharacterEvictions(int WorldID, DynamicArray<THouseEviction> *Ev return true; } -bool CheckGuildLeaderStatus(int WorldID, int CharacterID, bool *IsGuildLeader){ - // NOTE(fusion): Same as `DecrementIsOnline`. - ASSERT(IsGuildLeader != NULL); - sqlite3_stmt *Stmt = PrepareQuery( - "SELECT Guild, Rank FROM Characters" - " WHERE WorldID = ?1 AND CharacterID = ?2"); - if(Stmt == NULL){ - LOG_ERR("Failed to prepare query"); - return false; - } - - if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK - || sqlite3_bind_int(Stmt, 2, CharacterID) != SQLITE_OK){ - LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); - return false; - } - - int ErrorCode = sqlite3_step(Stmt); - if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){ - LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); - return false; - } - - *IsGuildLeader = false; - if(ErrorCode == SQLITE_ROW){ - const char *Guild = (const char*)sqlite3_column_text(Stmt, 0); - const char *Rank = (const char*)sqlite3_column_text(Stmt, 1); - if(Guild != NULL && Rank != NULL && Guild[0] != 0 && StringEqCI(Rank, "Leader")){ - *IsGuildLeader = true; - } - } - - return true; -} - bool InsertHouseOwner(int WorldID, int HouseID, int OwnerID, int PaidUntil){ sqlite3_stmt *Stmt = PrepareQuery( "INSERT INTO HouseOwners (WorldID, HouseID, OwnerID, PaidUntil)" @@ -441,7 +584,7 @@ bool DeleteHouseOwner(int WorldID, int HouseID){ return sqlite3_changes(g_Database) > 0; } -bool LoadHouseOwners(int WorldID, DynamicArray<THouseOwner> *Owners){ +bool GetHouseOwners(int WorldID, DynamicArray<THouseOwner> *Owners){ ASSERT(Owners != NULL); sqlite3_stmt *Stmt = PrepareQuery( "SELECT O.HouseID, O.OwnerID, C.Name, O.PaidUntil" @@ -476,7 +619,7 @@ bool LoadHouseOwners(int WorldID, DynamicArray<THouseOwner> *Owners){ return true; } -bool LoadHouseAuctions(int WorldID, DynamicArray<int> *Auctions){ +bool GetHouseAuctions(int WorldID, DynamicArray<int> *Auctions){ ASSERT(Auctions != NULL); sqlite3_stmt *Stmt = PrepareQuery( "SELECT HouseID FROM HouseAuctions WHERE WorldID = ?1"); @@ -588,17 +731,79 @@ bool InsertHouses(int WorldID, int NumHouses, THouse *Houses){ return true; } -bool ClearIsOnline(int WorldID, int *NumAffectedCharacters){ +bool ExcludeFromAuctions(int WorldID, int CharacterID, int Duration, int BanishmentID){ + // NOTE(fusion): Same as `DecrementIsOnline`. sqlite3_stmt *Stmt = PrepareQuery( - "UPDATE Characters SET IsOnline = 0" - " WHERE WorldID = ?1 AND IsOnline != 0"); + "INSERT INTO HouseAuctionExclusions (CharacterID, Until, BanishmentID)" + " SELECT CharacterID, (UNIXEPOCH() + ?3), ?4 FROM Characters" + " WHERE WorldID = ?1 AND CharacterID = ?2"); if(Stmt == NULL){ LOG_ERR("Failed to prepare query"); return false; } - if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK){ - LOG_ERR("Failed to bind WorldID: %s", sqlite3_errmsg(g_Database)); + if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK + || sqlite3_bind_int(Stmt, 2, CharacterID) != SQLITE_OK + || sqlite3_bind_int(Stmt, 3, Duration) != SQLITE_OK + || sqlite3_bind_int(Stmt, 3, BanishmentID) != SQLITE_OK){ + LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); + return false; + } + + if(sqlite3_step(Stmt) != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return false; + } + + return sqlite3_changes(g_Database) > 0; +} + +// Banishment tables +//============================================================================== +bool GetNamelockStatus(int CharacterID, bool *Approved){ + ASSERT(Approved); + sqlite3_stmt *Stmt = PrepareQuery( + "SELECT Approved FROM Namelocks WHERE CharacterID = ?1"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return false; + } + + if(sqlite3_bind_int(Stmt, 1, CharacterID) != SQLITE_OK){ + LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); + return false; + } + + int ErrorCode = sqlite3_step(Stmt); + if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){ + LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); + return false; + } + + bool Result = (ErrorCode == SQLITE_ROW); + if(Result){ + *Approved = (sqlite3_column_int(Stmt, 0) != 0); + } + return Result; +} + +bool InsertNamelock(int CharacterID, int IPAddress, + int GamemasterID, const char *Reason, const char *Comment){ + ASSERT(Reason != NULL && Comment != NULL); + sqlite3_stmt *Stmt = PrepareQuery( + "INSERT INTO Namelocks (CharacterID, IPAddress, GamemasterID, Reason, Comment)" + " VALUES (?1, ?2, ?3, ?4, ?5)"); + if(Stmt == NULL){ + LOG_ERR("Failed to prepare query"); + return false; + } + + if(sqlite3_bind_int(Stmt, 1, CharacterID) != SQLITE_OK + || sqlite3_bind_int(Stmt, 2, IPAddress) != SQLITE_OK + || sqlite3_bind_int(Stmt, 3, GamemasterID) != SQLITE_OK + || sqlite3_bind_text(Stmt, 4, Reason, -1, NULL) != SQLITE_OK + || sqlite3_bind_text(Stmt, 5, Comment, -1, NULL) != SQLITE_OK){ + LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); return false; } @@ -607,10 +812,11 @@ bool ClearIsOnline(int WorldID, int *NumAffectedCharacters){ return false; } - *NumAffectedCharacters = sqlite3_changes(g_Database); return true; } +// Info tables +//============================================================================== bool DeleteOnlineCharacters(int WorldID){ sqlite3_stmt *Stmt = PrepareQuery( "DELETE FROM OnlineCharacters WHERE WorldID = ?1"); @@ -692,103 +898,6 @@ bool CheckOnlineRecord(int WorldID, int NumCharacters, bool *NewRecord){ return true; } -bool LoadCharacterIndex(int WorldID, int MinimumCharacterID, - int MaxEntries, int *NumEntries, TCharacterIndexEntry *Entries){ - ASSERT(MaxEntries > 0 && NumEntries != NULL && Entries != NULL); - sqlite3_stmt *Stmt = PrepareQuery( - "SELECT CharacterID, Name FROM Characters" - " WHERE WorldID = ?1 AND CharacterID >= ?2" - " ORDER BY CharacterID ASC LIMIT ?3"); - if(Stmt == NULL){ - LOG_ERR("Failed to prepare query"); - return false; - } - - if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK - || sqlite3_bind_int(Stmt, 2, MinimumCharacterID) != SQLITE_OK - || sqlite3_bind_int(Stmt, 3, MaxEntries) != SQLITE_OK){ - LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); - return false; - } - - // NOTE(fusion): We shouldn't get more than `MaxEntries` rows but it's - // always better to be safe. - int EntryIndex = 0; - while(sqlite3_step(Stmt) == SQLITE_ROW && EntryIndex < MaxEntries){ - Entries[EntryIndex].CharacterID = sqlite3_column_int(Stmt, 0); - StringCopy(Entries[EntryIndex].Name, - sizeof(Entries[EntryIndex].Name), - (const char *)sqlite3_column_text(Stmt, 1)); - } - - if(sqlite3_errcode(g_Database) != SQLITE_DONE){ - LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); - return false; - } - - *NumEntries = EntryIndex; - return true; -} - -bool ExcludeFromAuctions(int WorldID, int CharacterID, int Duration, int BanishmentID){ - // NOTE(fusion): Same as `DecrementIsOnline`. - sqlite3_stmt *Stmt = PrepareQuery( - "INSERT INTO HouseAuctionExclusions (CharacterID, Until, BanishmentID)" - " SELECT CharacterID, (UNIXEPOCH() + ?3), ?4 FROM Characters" - " WHERE WorldID = ?1 AND CharacterID = ?2"); - if(Stmt == NULL){ - LOG_ERR("Failed to prepare query"); - return false; - } - - if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK - || sqlite3_bind_int(Stmt, 2, CharacterID) != SQLITE_OK - || sqlite3_bind_int(Stmt, 3, Duration) != SQLITE_OK - || sqlite3_bind_int(Stmt, 3, BanishmentID) != SQLITE_OK){ - LOG_ERR("Failed to bind parameters: %s", sqlite3_errmsg(g_Database)); - return false; - } - - if(sqlite3_step(Stmt) != SQLITE_DONE){ - LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); - return false; - } - - return sqlite3_changes(g_Database) > 0; -} - -bool LoadWorldConfig(int WorldID, TWorldConfig *WorldConfig){ - ASSERT(WorldConfig != NULL); - sqlite3_stmt *Stmt = PrepareQuery( - "SELECT Type, RebootTime, Address, Port, MaxPlayers," - " PremiumPlayerBuffer, MaxNewbies, PremiumNewbieBuffer" - " FROM Worlds WHERE WorldID = ?1"); - if(Stmt == NULL){ - LOG_ERR("Failed to prepare query"); - return false; - } - - if(sqlite3_bind_int(Stmt, 1, WorldID) != SQLITE_OK){ - LOG_ERR("Failed to bind WorldID: %s", sqlite3_errmsg(g_Database)); - return false; - } - - if(sqlite3_step(Stmt) != SQLITE_ROW){ - LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(g_Database)); - return false; - } - - WorldConfig->Type = sqlite3_column_int(Stmt, 0); - WorldConfig->RebootTime = sqlite3_column_int(Stmt, 1); - WorldConfig->Address = sqlite3_column_int(Stmt, 2); - WorldConfig->Port = sqlite3_column_int(Stmt, 3); - WorldConfig->MaxPlayers = sqlite3_column_int(Stmt, 4); - WorldConfig->PremiumPlayerBuffer = sqlite3_column_int(Stmt, 5); - WorldConfig->MaxNewbies = sqlite3_column_int(Stmt, 6); - WorldConfig->PremiumNewbieBuffer = sqlite3_column_int(Stmt, 7); - return true; -} - // Database Initialization //============================================================================== // NOTE(fusion): From `https://www.sqlite.org/pragma.html`: diff --git a/src/querymanager.cc b/src/querymanager.cc index 7d6c830..dddb86f 100644 --- a/src/querymanager.cc +++ b/src/querymanager.cc @@ -129,6 +129,31 @@ bool StringCopy(char *Dest, int DestCapacity, const char *Src){ return StringCopyN(Dest, DestCapacity, Src, SrcLength); } +bool ParseIPAddress(const char *String, int *OutAddr){ + int Addr[4]; + if(sscanf(String, "%d.%d.%d.%d", &Addr[0], &Addr[1], &Addr[2], &Addr[3]) != 4){ + LOG_ERR("Invalid IP Address format \"%s\"", String); + return false; + } + + if(Addr[0] < 0 || Addr[0] > 0xFF + || Addr[1] < 0 || Addr[1] > 0xFF + || Addr[2] < 0 || Addr[2] > 0xFF + || Addr[3] < 0 || Addr[3] > 0xFF){ + LOG_ERR("Invalid IP Address \"%s\"", String); + return false; + } + + if(OutAddr){ + *OutAddr = ((int)Addr[0] << 24) + | ((int)Addr[1] << 16) + | ((int)Addr[2] << 8) + | ((int)Addr[3] << 0); + } + + return true; +} + bool ReadBooleanConfig(bool *Dest, const char *Val){ ASSERT(Dest && Val); *Dest = StringEqCI(Val, "true"); diff --git a/src/querymanager.hh b/src/querymanager.hh index 8fdd0a8..40066af 100644 --- a/src/querymanager.hh +++ b/src/querymanager.hh @@ -102,6 +102,7 @@ bool StringEq(const char *A, const char *B); bool StringEqCI(const char *A, const char *B); bool StringCopyN(char *Dest, int DestCapacity, const char *Src, int SrcLength); bool StringCopy(char *Dest, int DestCapacity, const char *Src); +bool ParseIPAddress(const char *String, int *OutAddr); bool ReadBooleanConfig(bool *Dest, const char *Val); bool ReadIntegerConfig(int *Dest, const char *Val); @@ -720,30 +721,43 @@ public: bool Commit(void); }; -bool LoadWorldID(const char *WorldName, int *WorldID); +// NOTE(fusion): Primary tables. +int GetWorldID(const char *WorldName); +bool GetWorldConfig(int WorldID, TWorldConfig *WorldConfig); +int GetCharacterID(int WorldID, const char *CharacterName); +bool GetCharacterRight(int CharacterID, const char *Right); +bool GetGuildLeaderStatus(int WorldID, int CharacterID); bool DecrementIsOnline(int WorldID, int CharacterID); +bool ClearIsOnline(int WorldID, int *NumAffectedCharacters); +bool GetCharacterIndexEntries(int WorldID, int MinimumCharacterID, + int MaxEntries, int *NumEntries, TCharacterIndexEntry *Entries); + +// NOTE(fusion): House tables. bool FinishHouseAuctions(int WorldID, DynamicArray<THouseAuction> *Auctions); bool FinishHouseTransfers(int WorldID, DynamicArray<THouseTransfer> *Transfers); -bool LoadFreeAccountEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions); -bool LoadDeletedCharacterEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions); -bool CheckGuildLeaderStatus(int WorldID, int CharacterID, bool *IsGuildLeader); +bool GetFreeAccountEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions); +bool GetDeletedCharacterEvictions(int WorldID, DynamicArray<THouseEviction> *Evictions); bool InsertHouseOwner(int WorldID, int HouseID, int OwnerID, int PaidUntil); bool UpdateHouseOwner(int WorldID, int HouseID, int OwnerID, int PaidUntil); bool DeleteHouseOwner(int WorldID, int HouseID); -bool LoadHouseOwners(int WorldID, DynamicArray<THouseOwner> *Owners); -bool LoadHouseAuctions(int WorldID, DynamicArray<int> *Auctions); +bool GetHouseOwners(int WorldID, DynamicArray<THouseOwner> *Owners); +bool GetHouseAuctions(int WorldID, DynamicArray<int> *Auctions); bool StartHouseAuction(int WorldID, int HouseID); bool DeleteHouses(int WorldID); bool InsertHouses(int WorldID, int NumHouses, THouse *Houses); -bool ClearIsOnline(int WorldID, int *NumAffectedCharacters); +bool ExcludeFromAuctions(int WorldID, int CharacterID, int Duration, int BanishmentID); + +// NOTE(fusion): Banishment tables. +bool GetNamelockStatus(int CharacterID, bool *Approved); +bool InsertNamelock(int CharacterID, int IPAddress, + int GamemasterID, const char *Reason, const char *Comment); + +// NOTE(fusion): Info tables. bool DeleteOnlineCharacters(int WorldID); bool InsertOnlineCharacters(int WorldID, int NumCharacters, TOnlineCharacter *Characters); bool CheckOnlineRecord(int WorldID, int NumCharacters, bool *NewRecord); -bool LoadCharacterIndex(int WorldID, int MinimumCharacterID, - int MaxEntries, int *NumEntries, TCharacterIndexEntry *Entries); -bool ExcludeFromAuctions(int WorldID, int CharacterID, int Duration, int BanishmentID); -bool LoadWorldConfig(int WorldID, TWorldConfig *WorldConfig); +// NOTE(fusion): Internal database utility and initialization. bool FileExists(const char *FileName); bool ExecFile(const char *FileName); bool ExecInternal(const char *Format, ...) ATTR_PRINTF(1, 2); |
