change how guild data is stored
This commit is contained in:
parent
0ff6217227
commit
27a0df43b3
@ -2,6 +2,8 @@
|
||||
-- the database in some partial state. Also, notice we don't use `IF NOT EXISTS`
|
||||
-- because it could also leave the database in a bad state if for example, some
|
||||
-- old version of a table already existed.
|
||||
--==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- IMPORTANT(fusion): PosgreSQL doesn't have a defined `NOCASE` collation like
|
||||
@ -78,9 +80,6 @@ CREATE TABLE Characters (
|
||||
AccountID INTEGER NOT NULL,
|
||||
Name TEXT NOT NULL COLLATE NOCASE,
|
||||
Sex SMALLINT NOT NULL,
|
||||
Guild TEXT NOT NULL COLLATE NOCASE DEFAULT '',
|
||||
Rank TEXT NOT NULL COLLATE NOCASE DEFAULT '',
|
||||
Title TEXT NOT NULL DEFAULT '',
|
||||
Level SMALLINT NOT NULL DEFAULT 0,
|
||||
Profession TEXT NOT NULL DEFAULT '',
|
||||
Residence TEXT NOT NULL DEFAULT '',
|
||||
@ -93,7 +92,6 @@ CREATE TABLE Characters (
|
||||
);
|
||||
CREATE INDEX CharactersWorldIndex ON Characters(WorldID, IsOnline);
|
||||
CREATE INDEX CharactersAccountIndex ON Characters(AccountID, IsOnline);
|
||||
CREATE INDEX CharactersGuildIndex ON Characters(Guild, Rank);
|
||||
|
||||
-- NOTE(fusion): It seems `RIGHT` is a reserved keyword and trying to use
|
||||
-- it as a column name will generate an error.
|
||||
@ -136,6 +134,46 @@ CREATE TABLE LoginAttempts (
|
||||
CREATE INDEX LoginAttemptsAccountIndex ON LoginAttempts(AccountID, Timestamp);
|
||||
CREATE INDEX LoginAttemptsAddressIndex ON LoginAttempts(IPAddress, Timestamp);
|
||||
|
||||
-- Guild Tables
|
||||
--==============================================================================
|
||||
CREATE TABLE Guilds (
|
||||
WorldID INTEGER NOT NULL,
|
||||
GuildID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY,
|
||||
Name TEXT NOT NULL COLLATE NOCASE,
|
||||
LeaderID INTEGER NOT NULL,
|
||||
Created TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (GuildID),
|
||||
UNIQUE (Name),
|
||||
UNIQUE (LeaderID)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildRanks (
|
||||
GuildID INTEGER NOT NULL,
|
||||
Rank SMALLINT NOT NULL,
|
||||
Name TEXT NOT NULL,
|
||||
PRIMARY KEY (GuildID, Rank)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildMembers (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
Rank SMALLINT NOT NULL,
|
||||
Title TEXT NOT NULL,
|
||||
Joined TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildMembersGuildIndex ON GuildMembers(GuildID, Rank);
|
||||
|
||||
CREATE TABLE GuildInvites (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
RecruiterID INTEGER NOT NULL,
|
||||
Timestamp TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (GuildID, CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildInvitesCharacterIndex ON GuildInvites(CharacterID);
|
||||
CREATE INDEX GuildInvitesRecruiterIndex ON GuildInvites(RecruiterID);
|
||||
|
||||
-- House Tables
|
||||
--==============================================================================
|
||||
CREATE TABLE Houses (
|
||||
|
||||
54
postgres/z-002-migrate-v02-to-v03.sql
Normal file
54
postgres/z-002-migrate-v02-to-v03.sql
Normal file
@ -0,0 +1,54 @@
|
||||
-- NOTE(fusion): This file contains the migration script from v0.2 to v0.3. It's
|
||||
-- put inside a transaction to avoid errors from leaving the database in some
|
||||
-- partial state.
|
||||
-- These changes are already present in the latest `schema.sql`, so trying to
|
||||
-- apply them on a newly created database will result in errors.
|
||||
--==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
DROP INDEX CharactersGuildIndex;
|
||||
ALTER TABLE Characters DROP COLUMN Guild;
|
||||
ALTER TABLE Characters DROP COLUMN Rank;
|
||||
ALTER TABLE Characters DROP COLUMN Title;
|
||||
|
||||
CREATE TABLE Guilds (
|
||||
WorldID INTEGER NOT NULL,
|
||||
GuildID INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY,
|
||||
Name TEXT NOT NULL COLLATE NOCASE,
|
||||
LeaderID INTEGER NOT NULL,
|
||||
Created TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (GuildID),
|
||||
UNIQUE (Name),
|
||||
UNIQUE (LeaderID)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildRanks (
|
||||
GuildID INTEGER NOT NULL,
|
||||
Rank SMALLINT NOT NULL,
|
||||
Name TEXT NOT NULL,
|
||||
PRIMARY KEY (GuildID, Rank)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildMembers (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
Rank SMALLINT NOT NULL,
|
||||
Title TEXT NOT NULL,
|
||||
Joined TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildMembersGuildIndex ON GuildMembers(GuildID, Rank);
|
||||
|
||||
CREATE TABLE GuildInvites (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
RecruiterID INTEGER NOT NULL,
|
||||
Timestamp TIMESTAMPTZ NOT NULL,
|
||||
PRIMARY KEY (GuildID, CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildInvitesCharacterIndex ON GuildInvites(CharacterID);
|
||||
CREATE INDEX GuildInvitesRecruiterIndex ON GuildInvites(RecruiterID);
|
||||
|
||||
COMMIT;
|
||||
|
||||
@ -1,5 +1,7 @@
|
||||
-- NOTE(fusion): This file contains sample initial data. It's wrapped into a
|
||||
-- NOTE(fusion): This file contains sample initial data. It's put inside a
|
||||
-- transaction to avoid errors from leaving the database in some partial state.
|
||||
--==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
-- 111111/tibia
|
||||
@ -57,9 +57,6 @@ CREATE TABLE Characters (
|
||||
AccountID INTEGER NOT NULL,
|
||||
Name TEXT NOT NULL COLLATE NOCASE,
|
||||
Sex INTEGER NOT NULL,
|
||||
Guild TEXT NOT NULL COLLATE NOCASE DEFAULT '',
|
||||
Rank TEXT NOT NULL COLLATE NOCASE DEFAULT '',
|
||||
Title TEXT NOT NULL DEFAULT '',
|
||||
Level INTEGER NOT NULL DEFAULT 0,
|
||||
Profession TEXT NOT NULL DEFAULT '',
|
||||
Residence TEXT NOT NULL DEFAULT '',
|
||||
@ -72,7 +69,6 @@ CREATE TABLE Characters (
|
||||
);
|
||||
CREATE INDEX CharactersWorldIndex ON Characters(WorldID, IsOnline);
|
||||
CREATE INDEX CharactersAccountIndex ON Characters(AccountID, IsOnline);
|
||||
CREATE INDEX CharactersGuildIndex ON Characters(Guild, Rank);
|
||||
|
||||
/*
|
||||
-- TODO(fusion): Have group rights instead of adding individual rights to characters?
|
||||
@ -123,6 +119,46 @@ CREATE TABLE LoginAttempts (
|
||||
CREATE INDEX LoginAttemptsAccountIndex ON LoginAttempts(AccountID, Timestamp);
|
||||
CREATE INDEX LoginAttemptsAddressIndex ON LoginAttempts(IPAddress, Timestamp);
|
||||
|
||||
-- Guild Tables
|
||||
--==============================================================================
|
||||
CREATE TABLE Guilds (
|
||||
WorldID INTEGER NOT NULL,
|
||||
GuildID INTEGER NOT NULL,
|
||||
Name TEXT NOT NULL COLLATE NOCASE,
|
||||
LeaderID INTEGER NOT NULL,
|
||||
Created INTEGER NOT NULL,
|
||||
PRIMARY KEY (GuildID),
|
||||
UNIQUE (Name),
|
||||
UNIQUE (LeaderID)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildRanks (
|
||||
GuildID INTEGER NOT NULL,
|
||||
Rank INTEGER NOT NULL,
|
||||
Name TEXT NOT NULL,
|
||||
PRIMARY KEY (GuildID, Rank)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildMembers (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
Rank INTEGER NOT NULL,
|
||||
Title TEXT NOT NULL,
|
||||
Joined INTEGER NOT NULL,
|
||||
PRIMARY KEY (CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildMembersGuildIndex ON GuildMembers(GuildID, Rank);
|
||||
|
||||
CREATE TABLE GuildInvites (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
RecruiterID INTEGER NOT NULL,
|
||||
Timestamp INTEGER NOT NULL,
|
||||
PRIMARY KEY (GuildID, CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildInvitesCharacterIndex ON GuildInvites(CharacterID);
|
||||
CREATE INDEX GuildInvitesRecruiterIndex ON GuildInvites(RecruiterID);
|
||||
|
||||
-- House Tables
|
||||
--==============================================================================
|
||||
CREATE TABLE Houses (
|
||||
|
||||
@ -2,10 +2,10 @@
|
||||
-- must be manually executed as `sqlite3 -bail -echo tibia.db < migration.sql`
|
||||
-- because the original schema didn't have a `Patches` table which is necessary
|
||||
-- with the new automatic patching system. Future migration scripts can be placed
|
||||
-- in `patches/` for automatic execution but not this one, unfortunately.
|
||||
-- in `patches/` for automatic execution but not this one, unfortunately. For
|
||||
-- more details see `sqlite/README.txt`.
|
||||
-- These changes are already present in the latest `schema.sql`, so trying to
|
||||
-- apply it to a newly created database will result in errors. For more details
|
||||
-- see `sqlite/README.txt`.
|
||||
-- apply them to a newly created database will result in errors.
|
||||
--==============================================================================
|
||||
|
||||
BEGIN;
|
||||
|
||||
50
sqlite/z-002-migrate-v02-to-v03.sql
Normal file
50
sqlite/z-002-migrate-v02-to-v03.sql
Normal file
@ -0,0 +1,50 @@
|
||||
-- NOTE(fusion): This file contains the migration script from v0.2 to v0.3. It
|
||||
-- can be executed automatically as a patch if placed at `sqlite/patches`. For
|
||||
-- more details see `sqlite/README.txt`.
|
||||
-- These changes are already present in the latest `schema.sql`, so trying to
|
||||
-- apply them to a newly created database will result in errors.
|
||||
--==============================================================================
|
||||
|
||||
DROP INDEX CharactersGuildIndex;
|
||||
ALTER TABLE Characters DROP COLUMN Guild;
|
||||
ALTER TABLE Characters DROP COLUMN Rank;
|
||||
ALTER TABLE Characters DROP COLUMN Title;
|
||||
|
||||
CREATE TABLE Guilds (
|
||||
WorldID INTEGER NOT NULL,
|
||||
GuildID INTEGER NOT NULL,
|
||||
Name TEXT NOT NULL COLLATE NOCASE,
|
||||
LeaderID INTEGER NOT NULL,
|
||||
Created INTEGER NOT NULL,
|
||||
PRIMARY KEY (GuildID),
|
||||
UNIQUE (Name),
|
||||
UNIQUE (LeaderID)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildRanks (
|
||||
GuildID INTEGER NOT NULL,
|
||||
Rank INTEGER NOT NULL,
|
||||
Name TEXT NOT NULL,
|
||||
PRIMARY KEY (GuildID, Rank)
|
||||
);
|
||||
|
||||
CREATE TABLE GuildMembers (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
Rank INTEGER NOT NULL,
|
||||
Title TEXT NOT NULL,
|
||||
Joined INTEGER NOT NULL,
|
||||
PRIMARY KEY (CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildMembersGuildIndex ON GuildMembers(GuildID, Rank);
|
||||
|
||||
CREATE TABLE GuildInvites (
|
||||
GuildID INTEGER NOT NULL,
|
||||
CharacterID INTEGER NOT NULL,
|
||||
RecruiterID INTEGER NOT NULL,
|
||||
Timestamp INTEGER NOT NULL,
|
||||
PRIMARY KEY (GuildID, CharacterID)
|
||||
);
|
||||
CREATE INDEX GuildInvitesCharacterIndex ON GuildInvites(CharacterID);
|
||||
CREATE INDEX GuildInvitesRecruiterIndex ON GuildInvites(RecruiterID);
|
||||
|
||||
@ -1771,8 +1771,7 @@ bool GetCharacterID(TDatabase *Database, int WorldID, const char *CharacterName,
|
||||
bool GetCharacterLoginData(TDatabase *Database, const char *CharacterName, TCharacterLoginData *Character){
|
||||
ASSERT(Database != NULL && CharacterName != NULL && Character != NULL);
|
||||
const char *Stmt = PrepareQuery(Database,
|
||||
"SELECT WorldID, CharacterID, AccountID, Name,"
|
||||
" Sex, Guild, Rank, Title, Deleted"
|
||||
"SELECT WorldID, CharacterID, AccountID, Name, Sex, Deleted"
|
||||
" FROM Characters WHERE Name = $1::TEXT");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
@ -1797,10 +1796,7 @@ bool GetCharacterLoginData(TDatabase *Database, const char *CharacterName, TChar
|
||||
Character->AccountID = GetResultInt(Result, 0, 2);
|
||||
StringBufCopy(Character->Name, GetResultText(Result, 0, 3));
|
||||
Character->Sex = GetResultInt(Result, 0, 4);
|
||||
StringBufCopy(Character->Guild, GetResultText(Result, 0, 5));
|
||||
StringBufCopy(Character->Rank, GetResultText(Result, 0, 6));
|
||||
StringBufCopy(Character->Title, GetResultText(Result, 0, 7));
|
||||
Character->Deleted = GetResultBool(Result, 0, 8);
|
||||
Character->Deleted = GetResultBool(Result, 0, 5);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1809,7 +1805,7 @@ bool GetCharacterLoginData(TDatabase *Database, const char *CharacterName, TChar
|
||||
bool GetCharacterProfile(TDatabase *Database, const char *CharacterName, TCharacterProfile *Character){
|
||||
ASSERT(Database != NULL && CharacterName != NULL && Character != NULL);
|
||||
const char *Stmt = PrepareQuery(Database,
|
||||
"SELECT C.Name, W.Name, C.Sex, C.Guild, C.Rank, C.Title, C.Level,"
|
||||
"SELECT C.CharacterID, C.Name, W.Name, C.Sex, C.Level,"
|
||||
" C.Profession, C.Residence, C.LastLoginTime, C.IsOnline,"
|
||||
" C.Deleted, GREATEST(A.PremiumEnd - CURRENT_TIMESTAMP, '0')"
|
||||
" FROM Characters AS C"
|
||||
@ -1817,7 +1813,7 @@ bool GetCharacterProfile(TDatabase *Database, const char *CharacterName, TCharac
|
||||
" LEFT JOIN Accounts AS A ON A.AccountID = C.AccountID"
|
||||
" LEFT JOIN CharacterRights AS R"
|
||||
" ON R.CharacterID = C.CharacterID"
|
||||
" AND R.Name = 'NO_STATISTICS'"
|
||||
" AND R.Name = 'NO_STATISTICS'"
|
||||
" WHERE C.Name = $1::TEXT AND R.Name IS NULL");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
@ -1837,19 +1833,17 @@ bool GetCharacterProfile(TDatabase *Database, const char *CharacterName, TCharac
|
||||
|
||||
memset(Character, 0, sizeof(TCharacterProfile));
|
||||
if(PQntuples(Result) > 0){
|
||||
StringBufCopy(Character->Name, GetResultText(Result, 0, 0));
|
||||
StringBufCopy(Character->World, GetResultText(Result, 0, 1));
|
||||
Character->Sex = GetResultInt(Result, 0, 2);
|
||||
StringBufCopy(Character->Guild, GetResultText(Result, 0, 3));
|
||||
StringBufCopy(Character->Rank, GetResultText(Result, 0, 4));
|
||||
StringBufCopy(Character->Title, GetResultText(Result, 0, 5));
|
||||
Character->Level = GetResultInt(Result, 0, 6);
|
||||
StringBufCopy(Character->Profession, GetResultText(Result, 0, 7));
|
||||
StringBufCopy(Character->Residence, GetResultText(Result, 0, 8));
|
||||
Character->LastLogin = GetResultTimestamp(Result, 0, 9);
|
||||
Character->Online = GetResultBool(Result, 0, 10);
|
||||
Character->Deleted = GetResultBool(Result, 0, 11);
|
||||
Character->PremiumDays = RoundSecondsToDays(GetResultInterval(Result, 0, 12));
|
||||
Character->CharacterID = GetResultInt(Result, 0, 0);
|
||||
StringBufCopy(Character->Name, GetResultText(Result, 0, 1));
|
||||
StringBufCopy(Character->World, GetResultText(Result, 0, 2));
|
||||
Character->Sex = GetResultInt(Result, 0, 3);
|
||||
Character->Level = GetResultInt(Result, 0, 4);
|
||||
StringBufCopy(Character->Profession, GetResultText(Result, 0, 5));
|
||||
StringBufCopy(Character->Residence, GetResultText(Result, 0, 6));
|
||||
Character->LastLogin = GetResultTimestamp(Result, 0, 7);
|
||||
Character->Online = GetResultBool(Result, 0, 8);
|
||||
Character->Deleted = GetResultBool(Result, 0, 9);
|
||||
Character->PremiumDays = RoundSecondsToDays(GetResultInterval(Result, 0, 10));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1915,8 +1909,8 @@ bool GetGuildLeaderStatus(TDatabase *Database, int WorldID, int CharacterID, boo
|
||||
ASSERT(Database != NULL && GuildLeader != NULL);
|
||||
// NOTE(fusion): Same as `DecrementIsOnline`.
|
||||
const char *Stmt = PrepareQuery(Database,
|
||||
"SELECT Guild, Rank FROM Characters"
|
||||
" WHERE WorldID = $1::INTEGER AND CharacterID = $2::INTEGER");
|
||||
"SELECT 1 FROM Guilds"
|
||||
" WHERE WorldID = $1::INTEGER AND LeaderID = $2::INTEGER");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
return false;
|
||||
@ -1934,16 +1928,8 @@ bool GetGuildLeaderStatus(TDatabase *Database, int WorldID, int CharacterID, boo
|
||||
return false;
|
||||
}
|
||||
|
||||
*GuildLeader = false;
|
||||
if(PQntuples(Result) > 0){
|
||||
const char *Guild = GetResultText(Result, 0, 0);
|
||||
const char *Rank = GetResultText(Result, 0, 1);
|
||||
if(Guild != NULL && !StringEmpty(Guild) && Rank != NULL && StringEqCI(Rank, "Leader")){
|
||||
*GuildLeader = true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
*GuildLeader = (PQntuples(Result) > 0);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool IncrementIsOnline(TDatabase *Database, int WorldID, int CharacterID){
|
||||
@ -2341,6 +2327,46 @@ bool GetIPAddressFailedLoginAttempts(TDatabase *Database, int IPAddress, int Tim
|
||||
return true;
|
||||
}
|
||||
|
||||
// Guild Tables
|
||||
//==============================================================================
|
||||
bool GetCharacterGuildData(TDatabase *Database, int CharacterID, TCharacterGuildData *GuildData){
|
||||
ASSERT(Database != NULL && GuildData != NULL);
|
||||
const char *Stmt = PrepareQuery(Database,
|
||||
"SELECT G.GuildID, R.Rank, G.Name, R.Name, M.Title"
|
||||
" FROM Characters AS C"
|
||||
" LEFT JOIN GuildMembers AS M ON M.CharacterID = C.CharacterID"
|
||||
" LEFT JOIN Guilds AS G ON G.GuildID = M.GuildID"
|
||||
" LEFT JOIN GuildRanks AS R"
|
||||
" ON R.GuildID = M.GuildID AND R.Rank = M.Rank"
|
||||
" WHERE C.CharacterID = $1::INTEGER");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
return false;
|
||||
}
|
||||
|
||||
ParamBuffer Params = {};
|
||||
ParamBegin(&Params, 1, 1);
|
||||
ParamInt(&Params, CharacterID);
|
||||
PGresult *Result = PQexecPrepared(Database->Handle, Stmt, Params.NumParams,
|
||||
Params.Values, Params.Lengths, Params.Formats, 1);
|
||||
AutoResultClear ResultGuard(Result);
|
||||
if(PQresultStatus(Result) != PGRES_TUPLES_OK){
|
||||
LOG_ERR("Failed to execute query: %s", PQerrorMessage(Database->Handle));
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(GuildData, 0, sizeof(TCharacterGuildData));
|
||||
if(PQntuples(Result) > 0){
|
||||
GuildData->GuildID = GetResultInt(Result, 0, 0);
|
||||
GuildData->Rank = GetResultInt(Result, 0, 1);
|
||||
StringBufCopy(GuildData->GuildName, GetResultText(Result, 0, 2));
|
||||
StringBufCopy(GuildData->RankName, GetResultText(Result, 0, 3));
|
||||
StringBufCopy(GuildData->Title, GetResultText(Result, 0, 4));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// House Tables
|
||||
//==============================================================================
|
||||
bool FinishHouseAuctions(TDatabase *Database, int WorldID, DynamicArray<THouseAuction> *Auctions){
|
||||
|
||||
@ -1022,8 +1022,7 @@ bool GetCharacterID(TDatabase *Database, int WorldID, const char *CharacterName,
|
||||
bool GetCharacterLoginData(TDatabase *Database, const char *CharacterName, TCharacterLoginData *Character){
|
||||
ASSERT(Database != NULL && CharacterName != NULL && Character != NULL);
|
||||
sqlite3_stmt *Stmt = PrepareQuery(Database,
|
||||
"SELECT WorldID, CharacterID, AccountID, Name,"
|
||||
" Sex, Guild, Rank, Title, Deleted"
|
||||
"SELECT WorldID, CharacterID, AccountID, Name, Sex, Deleted"
|
||||
" FROM Characters WHERE Name = ?1");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
@ -1049,10 +1048,7 @@ bool GetCharacterLoginData(TDatabase *Database, const char *CharacterName, TChar
|
||||
Character->AccountID = sqlite3_column_int(Stmt, 2);
|
||||
StringBufCopy(Character->Name, (const char*)sqlite3_column_text(Stmt, 3));
|
||||
Character->Sex = sqlite3_column_int(Stmt, 4);
|
||||
StringBufCopy(Character->Guild, (const char*)sqlite3_column_text(Stmt, 5));
|
||||
StringBufCopy(Character->Rank, (const char*)sqlite3_column_text(Stmt, 6));
|
||||
StringBufCopy(Character->Title, (const char*)sqlite3_column_text(Stmt, 7));
|
||||
Character->Deleted = (sqlite3_column_int(Stmt, 8) != 0);
|
||||
Character->Deleted = (sqlite3_column_int(Stmt, 5) != 0);
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1061,7 +1057,7 @@ bool GetCharacterLoginData(TDatabase *Database, const char *CharacterName, TChar
|
||||
bool GetCharacterProfile(TDatabase *Database, const char *CharacterName, TCharacterProfile *Character){
|
||||
ASSERT(Database != NULL && CharacterName != NULL && Character != NULL);
|
||||
sqlite3_stmt *Stmt = PrepareQuery(Database,
|
||||
"SELECT C.Name, W.Name, C.Sex, C.Guild, C.Rank, C.Title, C.Level,"
|
||||
"SELECT C.CharacterID, C.Name, W.Name, C.Sex, C.Level,"
|
||||
" C.Profession, C.Residence, C.LastLoginTime, C.IsOnline,"
|
||||
" C.Deleted, MAX(A.PremiumEnd - UNIXEPOCH(), 0)"
|
||||
" FROM Characters AS C"
|
||||
@ -1069,7 +1065,7 @@ bool GetCharacterProfile(TDatabase *Database, const char *CharacterName, TCharac
|
||||
" LEFT JOIN Accounts AS A ON A.AccountID = C.AccountID"
|
||||
" LEFT JOIN CharacterRights AS R"
|
||||
" ON R.CharacterID = C.CharacterID"
|
||||
" AND R.Name = 'NO_STATISTICS'"
|
||||
" AND R.Name = 'NO_STATISTICS'"
|
||||
" WHERE C.Name = ?1 AND R.Name IS NULL");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
@ -1090,19 +1086,17 @@ bool GetCharacterProfile(TDatabase *Database, const char *CharacterName, TCharac
|
||||
|
||||
memset(Character, 0, sizeof(TCharacterProfile));
|
||||
if(ErrorCode == SQLITE_ROW){
|
||||
StringBufCopy(Character->Name, (const char*)sqlite3_column_text(Stmt, 0));
|
||||
StringBufCopy(Character->World, (const char*)sqlite3_column_text(Stmt, 1));
|
||||
Character->Sex = sqlite3_column_int(Stmt, 2);
|
||||
StringBufCopy(Character->Guild, (const char*)sqlite3_column_text(Stmt, 3));
|
||||
StringBufCopy(Character->Rank, (const char*)sqlite3_column_text(Stmt, 4));
|
||||
StringBufCopy(Character->Title, (const char*)sqlite3_column_text(Stmt, 5));
|
||||
Character->Level = sqlite3_column_int(Stmt, 6);
|
||||
StringBufCopy(Character->Profession, (const char*)sqlite3_column_text(Stmt, 7));
|
||||
StringBufCopy(Character->Residence, (const char*)sqlite3_column_text(Stmt, 8));
|
||||
Character->LastLogin = sqlite3_column_int(Stmt, 9);
|
||||
Character->Online = (sqlite3_column_int(Stmt, 10) != 0);
|
||||
Character->Deleted = (sqlite3_column_int(Stmt, 11) != 0);
|
||||
Character->PremiumDays = RoundSecondsToDays(sqlite3_column_int(Stmt, 12));
|
||||
Character->CharacterID = sqlite3_column_int(Stmt, 0);
|
||||
StringBufCopy(Character->Name, (const char*)sqlite3_column_text(Stmt, 1));
|
||||
StringBufCopy(Character->World, (const char*)sqlite3_column_text(Stmt, 2));
|
||||
Character->Sex = sqlite3_column_int(Stmt, 3);
|
||||
Character->Level = sqlite3_column_int(Stmt, 4);
|
||||
StringBufCopy(Character->Profession, (const char*)sqlite3_column_text(Stmt, 5));
|
||||
StringBufCopy(Character->Residence, (const char*)sqlite3_column_text(Stmt, 6));
|
||||
Character->LastLogin = sqlite3_column_int(Stmt, 7);
|
||||
Character->Online = (sqlite3_column_int(Stmt, 8) != 0);
|
||||
Character->Deleted = (sqlite3_column_int(Stmt, 9) != 0);
|
||||
Character->PremiumDays = RoundSecondsToDays(sqlite3_column_int(Stmt, 10));
|
||||
}
|
||||
|
||||
return true;
|
||||
@ -1168,8 +1162,8 @@ bool GetGuildLeaderStatus(TDatabase *Database, int WorldID, int CharacterID, boo
|
||||
ASSERT(Database != NULL && GuildLeader != NULL);
|
||||
// NOTE(fusion): Same as `DecrementIsOnline`.
|
||||
sqlite3_stmt *Stmt = PrepareQuery(Database,
|
||||
"SELECT Guild, Rank FROM Characters"
|
||||
" WHERE WorldID = ?1 AND CharacterID = ?2");
|
||||
"SELECT 1 FROM Guilds"
|
||||
" WHERE WorldID = ?1 AND LeaderID = ?2");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
return false;
|
||||
@ -1188,15 +1182,7 @@ bool GetGuildLeaderStatus(TDatabase *Database, int WorldID, int CharacterID, boo
|
||||
return false;
|
||||
}
|
||||
|
||||
*GuildLeader = 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 && !StringEmpty(Guild) && Rank != NULL && StringEqCI(Rank, "Leader")){
|
||||
*GuildLeader = true;
|
||||
}
|
||||
}
|
||||
|
||||
*GuildLeader = (ErrorCode == SQLITE_ROW);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1592,6 +1578,47 @@ bool GetIPAddressFailedLoginAttempts(TDatabase *Database, int IPAddress, int Tim
|
||||
return true;
|
||||
}
|
||||
|
||||
// Guild Tables
|
||||
//==============================================================================
|
||||
bool GetCharacterGuildData(TDatabase *Database, int CharacterID, TCharacterGuildData *GuildData){
|
||||
ASSERT(Database != NULL && GuildData != NULL);
|
||||
sqlite3_stmt *Stmt = PrepareQuery(Database,
|
||||
"SELECT G.GuildID, R.Rank, G.Name, R.Name, M.Title"
|
||||
" FROM Characters AS C"
|
||||
" LEFT JOIN GuildMembers AS M ON M.CharacterID = C.CharacterID"
|
||||
" LEFT JOIN Guilds AS G ON G.GuildID = M.GuildID"
|
||||
" LEFT JOIN GuildRanks AS R"
|
||||
" ON R.GuildID = M.GuildID AND R.Rank = M.Rank"
|
||||
" WHERE C.CharacterID = ?1");
|
||||
if(Stmt == NULL){
|
||||
LOG_ERR("Failed to prepare query");
|
||||
return false;
|
||||
}
|
||||
|
||||
AutoStmtReset StmtReset(Stmt);
|
||||
if(sqlite3_bind_int(Stmt, 1, CharacterID) != SQLITE_OK){
|
||||
LOG_ERR("Failed to bind CharacterID: %s", sqlite3_errmsg(Database->Handle));
|
||||
return false;
|
||||
}
|
||||
|
||||
int ErrorCode = sqlite3_step(Stmt);
|
||||
if(ErrorCode != SQLITE_ROW && ErrorCode != SQLITE_DONE){
|
||||
LOG_ERR("Failed to execute query: %s", sqlite3_errmsg(Database->Handle));
|
||||
return false;
|
||||
}
|
||||
|
||||
memset(GuildData, 0, sizeof(TCharacterGuildData));
|
||||
if(ErrorCode == SQLITE_ROW){
|
||||
GuildData->GuildID = sqlite3_column_int(Stmt, 0);
|
||||
GuildData->Rank = sqlite3_column_int(Stmt, 1);
|
||||
StringBufCopy(GuildData->GuildName, (const char*)sqlite3_column_text(Stmt, 2));
|
||||
StringBufCopy(GuildData->RankName, (const char*)sqlite3_column_text(Stmt, 3));
|
||||
StringBufCopy(GuildData->Title, (const char*)sqlite3_column_text(Stmt, 4));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// House Tables
|
||||
//==============================================================================
|
||||
bool FinishHouseAuctions(TDatabase *Database, int WorldID, DynamicArray<THouseAuction> *Auctions){
|
||||
|
||||
20
src/query.cc
20
src/query.cc
@ -679,6 +679,9 @@ static void LoginGameTx(TDatabase *Database, TQuery *Query,
|
||||
QUERY_ERROR_IF(!GamemasterOutfit, 14);
|
||||
}
|
||||
|
||||
TCharacterGuildData GuildData;
|
||||
QUERY_STOP_IF(!GetCharacterGuildData(Database, Character.CharacterID, &GuildData));
|
||||
|
||||
DynamicArray<TAccountBuddy> Buddies;
|
||||
QUERY_STOP_IF(!GetBuddies(Database, Query->WorldID, Account.AccountID, &Buddies));
|
||||
|
||||
@ -704,9 +707,9 @@ static void LoginGameTx(TDatabase *Database, TQuery *Query,
|
||||
Response->Write32((uint32)Character.CharacterID);
|
||||
Response->WriteString(Character.Name);
|
||||
Response->Write8((uint8)Character.Sex);
|
||||
Response->WriteString(Character.Guild);
|
||||
Response->WriteString(Character.Rank);
|
||||
Response->WriteString(Character.Title);
|
||||
Response->WriteString(GuildData.GuildName);
|
||||
Response->WriteString(GuildData.RankName);
|
||||
Response->WriteString(GuildData.Title);
|
||||
|
||||
int NumBuddies = std::min<int>(Buddies.Length(), UINT8_MAX);
|
||||
Response->Write8((uint8)NumBuddies);
|
||||
@ -1478,15 +1481,18 @@ void ProcessGetCharacterProfile(TDatabase *Database, TQuery *Query){
|
||||
|
||||
TCharacterProfile Character;
|
||||
QUERY_STOP_IF(!GetCharacterProfile(Database, CharacterName, &Character));
|
||||
QUERY_ERROR_IF(!StringEqCI(Character.Name, CharacterName), 1);
|
||||
QUERY_ERROR_IF(Character.CharacterID == 0, 1);
|
||||
|
||||
TCharacterGuildData GuildData;
|
||||
QUERY_STOP_IF(!GetCharacterGuildData(Database, Character.CharacterID, &GuildData));
|
||||
|
||||
TWriteBuffer *Response = QueryBeginResponse(Query, QUERY_STATUS_OK);
|
||||
Response->WriteString(Character.Name);
|
||||
Response->WriteString(Character.World);
|
||||
Response->Write8((uint8)Character.Sex);
|
||||
Response->WriteString(Character.Guild);
|
||||
Response->WriteString(Character.Rank);
|
||||
Response->WriteString(Character.Title);
|
||||
Response->WriteString(GuildData.GuildName);
|
||||
Response->WriteString(GuildData.RankName);
|
||||
Response->WriteString(GuildData.Title);
|
||||
Response->Write16((uint16)Character.Level);
|
||||
Response->WriteString(Character.Profession);
|
||||
Response->WriteString(Character.Residence);
|
||||
|
||||
@ -809,7 +809,7 @@ int main(int argc, const char **argv){
|
||||
g_Config.MaxConnections = 25;
|
||||
g_Config.MaxConnectionIdleTime = 60 * 5; // seconds
|
||||
|
||||
LOG("Tibia Query Manager v0.2 (%s)", DATABASE_SYSTEM_NAME);
|
||||
LOG("Tibia Query Manager v0.3 (%s)", DATABASE_SYSTEM_NAME);
|
||||
if(!ReadConfig("config.cfg", &g_Config)){
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
@ -884,19 +884,22 @@ struct TCharacterLoginData{
|
||||
int AccountID;
|
||||
char Name[30];
|
||||
int Sex;
|
||||
char Guild[30];
|
||||
char Rank[30];
|
||||
char Title[30];
|
||||
bool Deleted;
|
||||
};
|
||||
|
||||
struct TCharacterGuildData{
|
||||
int GuildID;
|
||||
int Rank;
|
||||
char GuildName[30];
|
||||
char RankName[30];
|
||||
char Title[30];
|
||||
};
|
||||
|
||||
struct TCharacterProfile{
|
||||
int CharacterID;
|
||||
char Name[30];
|
||||
char World[30];
|
||||
int Sex;
|
||||
char Guild[30];
|
||||
char Rank[30];
|
||||
char Title[30];
|
||||
int Level;
|
||||
char Profession[30];
|
||||
char Residence[30];
|
||||
@ -1048,6 +1051,9 @@ bool InsertLoginAttempt(TDatabase *Database, int AccountID, int IPAddress, bool
|
||||
bool GetAccountFailedLoginAttempts(TDatabase *Database, int AccountID, int TimeWindow, int *FailedAttempts);
|
||||
bool GetIPAddressFailedLoginAttempts(TDatabase *Database, int IPAddress, int TimeWindow, int *FailedAttempts);
|
||||
|
||||
// NOTE(fusion): Guild Tables
|
||||
bool GetCharacterGuildData(TDatabase *Database, int CharacterID, TCharacterGuildData *GuildData);
|
||||
|
||||
// NOTE(fusion): House Tables
|
||||
bool FinishHouseAuctions(TDatabase *Database, int WorldID, DynamicArray<THouseAuction> *Auctions);
|
||||
bool FinishHouseTransfers(TDatabase *Database, int WorldID, DynamicArray<THouseTransfer> *Transfers);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user