aboutsummaryrefslogtreecommitdiff
path: root/src/database.cc
diff options
context:
space:
mode:
authorfusion32 <marcopuzziello@gmail.com>2025-07-13 10:04:53 -0300
committerfusion32 <marcopuzziello@gmail.com>2025-07-13 10:04:53 -0300
commitf827fcc84815c3727aae652d2a60a801b2432768 (patch)
treeec0d514cf052ac65585ed02fc12f8630432e9d7d /src/database.cc
parent1cad8468cbaf61309a64194fca547a67a700a6da (diff)
downloadquerymanager-f827fcc84815c3727aae652d2a60a801b2432768.tar.gz
querymanager-f827fcc84815c3727aae652d2a60a801b2432768.zip
reorganize query functions and impl SET_NOTATION
Diffstat (limited to 'src/database.cc')
-rw-r--r--src/database.cc401
1 files changed, 255 insertions, 146 deletions
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`: