diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPlayerFlagsTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPlayerFlagsTable.cpp new file mode 100644 index 00000000..9e26ad48 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPlayerFlagsTable.cpp @@ -0,0 +1,38 @@ +#include "CDPlayerFlagsTable.h" + +#include "CDClientDatabase.h" + +namespace CDPlayerFlagsTable { + Table entries; + + void ReadEntry(CppSQLite3Query& table) { + Entry entry; + entry.sessionOnly = table.getIntField("SessionOnly") == 1; + entry.onlySetByServer = table.getIntField("OnlySetByServer") == 1; + entry.sessionZoneOnly = table.getIntField("SessionZoneOnly") == 1; + entries[table.getIntField("id")] = entry; + } + + void LoadValuesFromDatabase() { + auto table = CDClientDatabase::ExecuteQuery("SELECT * FROM PlayerFlags;"); + + if (!table.eof()) { + do { + ReadEntry(table); + } while (!table.nextRow()); + } + } + + const std::optional GetEntry(const FlagId flagId) { + if (!entries.contains(flagId)) { + auto table = CDClientDatabase::CreatePreppedStmt("SELECT * FROM PlayerFlags WHERE id = ?;"); + table.bind(1, static_cast(flagId)); + auto result = table.execQuery(); + if (!result.eof()) { + ReadEntry(result); + } + } + + return entries[flagId]; + } +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPlayerFlagsTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDPlayerFlagsTable.h new file mode 100644 index 00000000..a5a370d0 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPlayerFlagsTable.h @@ -0,0 +1,21 @@ +#ifndef CDPLAYERFLAGSTABLE_H +#define CDPLAYERFLAGSTABLE_H + +#include +#include + +namespace CDPlayerFlagsTable { + struct Entry { + bool sessionOnly{}; + bool onlySetByServer{}; + bool sessionZoneOnly{}; + }; + + using FlagId = uint32_t; + using Table = std::map>; + + void LoadValuesFromDatabase(); + const std::optional GetEntry(const FlagId flagId); +}; + +#endif //!CDPLAYERFLAGSTABLE_H diff --git a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt index f4551646..57731ad9 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt +++ b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt @@ -25,6 +25,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp" "CDObjectsTable.cpp" "CDPetComponentTable.cpp" "CDPackageComponentTable.cpp" + "CDPlayerFlagsTable.cpp" "CDPhysicsComponentTable.cpp" "CDPropertyEntranceComponentTable.cpp" "CDPropertyTemplateTable.cpp" diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 0e838ba8..a1538e4d 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -21,6 +21,7 @@ #include "eObjectBits.h" #include "eGameMasterLevel.h" #include "ePlayerFlag.h" +#include "CDPlayerFlagsTable.h" Character::Character(uint32_t id, User* parentUser) { //First load the name, etc: @@ -231,6 +232,12 @@ void Character::SetBuildMode(bool buildMode) { } void Character::SaveXMLToDatabase() { + // Check that we can actually _save_ before saving + if (!m_OurEntity) { + LOG("%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str()); + return; + } + //For metrics, we'll record the time it took to save: auto start = std::chrono::system_clock::now(); @@ -277,39 +284,19 @@ void Character::SaveXMLToDatabase() { } flags->DeleteChildren(); //Clear it if we have anything, so that we can fill it up again without dupes - for (std::pair flag : m_PlayerFlags) { - auto* f = m_Doc.NewElement("f"); - f->SetAttribute("id", flag.first); - - //Because of the joy that is tinyxml2, it doesn't offer a function to set a uint64 as an attribute. - //Only signed 64-bits ints would work. - std::string v = std::to_string(flag.second); - f->SetAttribute("v", v.c_str()); - - flags->LinkEndChild(f); + for (const auto& [index, flagBucket] : m_PlayerFlags) { + auto* f = flags->InsertNewChildElement("f"); + f->SetAttribute("id", index); + f->SetAttribute("v", flagBucket); } - // Prevents the news feed from showing up on world transfers - if (GetPlayerFlag(ePlayerFlag::IS_NEWS_SCREEN_VISIBLE)) { - auto* s = m_Doc.NewElement("s"); - s->SetAttribute("si", ePlayerFlag::IS_NEWS_SCREEN_VISIBLE); - flags->LinkEndChild(s); - } - - if (GetPlayerFlag(ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR)) { - auto* s = m_Doc.NewElement("s"); - s->SetAttribute("si", ePlayerFlag::EQUPPED_TRIAL_FACTION_GEAR); - flags->LinkEndChild(s); + for (const auto& sessionFlag : m_SessionFlags) { + auto* s = flags->InsertNewChildElement("s"); + s->SetAttribute("si", sessionFlag); } SaveXmlRespawnCheckpoints(); - //Call upon the entity to update our xmlDoc: - if (!m_OurEntity) { - LOG("%i:%s didn't have an entity set while saving! CHARACTER WILL NOT BE SAVED!", this->GetID(), this->GetName().c_str()); - return; - } - m_OurEntity->UpdateXMLDoc(m_Doc); WriteToDatabase(); @@ -329,8 +316,8 @@ void Character::SetIsNewLogin() { while (currentChild) { auto* nextChild = currentChild->NextSiblingElement(); if (currentChild->Attribute("si")) { + LOG("Removed session flag (%s) from character %i:%s, saving character to database", currentChild->Attribute("si"), GetID(), GetName().c_str()); flags->DeleteChild(currentChild); - LOG("Removed isLoggedIn flag from character %i:%s, saving character to database", GetID(), GetName().c_str()); WriteToDatabase(); } currentChild = nextChild; @@ -363,7 +350,9 @@ void Character::SetPlayerFlag(const uint32_t flagId, const bool value) { } } - if (flagId == EQUPPED_TRIAL_FACTION_GEAR || flagId == IS_NEWS_SCREEN_VISIBLE) { + const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId); + + if (flagEntry && flagEntry->sessionOnly) { if (value) m_SessionFlags.insert(flagId); else m_SessionFlags.erase(flagId); } else { @@ -402,8 +391,8 @@ bool Character::GetPlayerFlag(const uint32_t flagId) const { bool toReturn = false; //by def, return false. - // TODO make actual session flag checker using flags table in database. - if (flagId == EQUPPED_TRIAL_FACTION_GEAR || flagId == IS_NEWS_SCREEN_VISIBLE) { + const auto flagEntry = CDPlayerFlagsTable::GetEntry(flagId); + if (flagEntry && flagEntry->sessionOnly) { toReturn = m_SessionFlags.contains(flagId); } else { // Calculate the index first diff --git a/thirdparty/SQLite/CppSQLite3.cpp b/thirdparty/SQLite/CppSQLite3.cpp index 21e2811e..1dfd9961 100644 --- a/thirdparty/SQLite/CppSQLite3.cpp +++ b/thirdparty/SQLite/CppSQLite3.cpp @@ -573,16 +573,18 @@ bool CppSQLite3Query::eof() } -void CppSQLite3Query::nextRow() +bool CppSQLite3Query::nextRow() { checkVM(); int nRet = sqlite3_step(mpVM); + bool bRet = true; if (nRet == SQLITE_DONE) { // no rows mbEof = true; + bRet = false; } else if (nRet == SQLITE_ROW) { @@ -590,6 +592,7 @@ void CppSQLite3Query::nextRow() } else { + bRet = false; nRet = sqlite3_finalize(mpVM); mpVM = 0; const char* szError = sqlite3_errmsg(mpDB); @@ -597,6 +600,7 @@ void CppSQLite3Query::nextRow() (char*)szError, DONT_DELETE_MSG); } + return bRet; } diff --git a/thirdparty/SQLite/CppSQLite3.h b/thirdparty/SQLite/CppSQLite3.h index a98277b1..dfed83e6 100644 --- a/thirdparty/SQLite/CppSQLite3.h +++ b/thirdparty/SQLite/CppSQLite3.h @@ -165,7 +165,8 @@ public: bool eof(); - void nextRow(); + // Returns true if there is another row to read, false otherwise. + bool nextRow(); void finalize(); @@ -207,6 +208,9 @@ public: int getIntField(int nField, int nNullValue=0); int getIntField(const char* szField, int nNullValue=0); + sqlite_int64 getInt64Field(int nField, sqlite_int64 nNullValue=0); + sqlite_int64 getInt64Field(const char* szField, sqlite_int64 nNullValue=0); + double getFloatField(int nField, double fNullValue=0.0); double getFloatField(const char* szField, double fNullValue=0.0); @@ -218,6 +222,9 @@ public: void setRow(int nRow); + // Returns true if there is another row to read, false otherwise. + bool nextRow(); + void finalize(); private: @@ -226,6 +233,7 @@ private: int mnCols; int mnRows; + bool mbEof; int mnCurrentRow; char** mpaszResults; };