aboutsummaryrefslogtreecommitdiff
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
parent1cad8468cbaf61309a64194fca547a67a700a6da (diff)
downloadquerymanager-f827fcc84815c3727aae652d2a60a801b2432768.tar.gz
querymanager-f827fcc84815c3727aae652d2a60a801b2432768.zip
reorganize query functions and impl SET_NOTATION
-rw-r--r--sql/schema.sql2
-rw-r--r--src/connections.cc83
-rw-r--r--src/database.cc401
-rw-r--r--src/querymanager.cc25
-rw-r--r--src/querymanager.hh36
5 files changed, 373 insertions, 174 deletions
diff --git a/sql/schema.sql b/sql/schema.sql
index 61430ed..f027e35 100644
--- a/sql/schema.sql
+++ b/sql/schema.sql
@@ -161,6 +161,8 @@ CREATE TABLE IF NOT EXISTS Namelocks (
GamemasterID INTEGER NOT NULL,
Reason TEXT NOT NULL,
Comment TEXT NOT NULL,
+ Attempts INTEGER NOT NULL DEFAULT 0,
+ Approved INTEGER NOT NULL DEFAULT 0,
PRIMARY KEY (CharacterID)
);
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);