mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
perf(cdclient): read ComponentsRegistry, ItemComponent and Objects from the fdb
The world and master servers pass the client's res/cdclient.fdb to CDClientManager. When it opens, these three tables, all looked up by their first column, find rows through the fdb's buckets instead of each process caching the whole table: ComponentsRegistry keeps nothing, ItemComponent and Objects keep only the entries asked for (their API returns references). Ids whose rows CDServer.sqlite changes are loaded from SQLite at startup and win. Without an fdb (or with one whose columns don't match) the tables load from CDServer.sqlite as before. ItemComponent and Objects now fill entries from one template for both sources instead of copies of the same field list. Tests cover the SQLite changes on top of the fdb, the no-fdb and unmapped paths, and, when DLU_CLIENT_RES points at a client, every id of the three tables through the fdb against CDServer.sqlite. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,4 +1,6 @@
|
||||
#include "CDClientManager.h"
|
||||
#include "CDFdb.h"
|
||||
#include "Logger.h"
|
||||
#include "CDActivityRewardsTable.h"
|
||||
#include "CDAnimationsTable.h"
|
||||
#include "CDBehaviorParameterTable.h"
|
||||
@@ -111,11 +113,20 @@ DEFINE_TABLE_STORAGE(CDSkillBehaviorTable);
|
||||
DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable);
|
||||
DEFINE_TABLE_STORAGE(CDVendorComponentTable);
|
||||
|
||||
void CDClientManager::LoadValuesFromDatabase() {
|
||||
void CDClientManager::LoadValuesFromDatabase(const std::filesystem::path& fdbPath) {
|
||||
if (!CDClientDatabase::isConnected) {
|
||||
throw std::runtime_error{ "CDClientDatabase is not connected!" };
|
||||
}
|
||||
|
||||
if (!fdbPath.empty()) {
|
||||
if (CDFdb::Open(fdbPath)) {
|
||||
LOG("Reading CDClient rows by id from %s (%s)", fdbPath.string().c_str(),
|
||||
CDFdb::Get()->IsMapped() ? "mapped, shared between processes" : "could not map it, read into memory");
|
||||
} else {
|
||||
LOG("Could not open %s, reading CDClient from CDServer.sqlite only", fdbPath.string().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
CDActivityRewardsTable::Instance().LoadValuesFromDatabase();
|
||||
CDActivitiesTable::Instance().LoadValuesFromDatabase();
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDAnimationsTable::Instance().LoadValuesFromDatabase());
|
||||
@@ -123,10 +134,11 @@ void CDClientManager::LoadValuesFromDatabase() {
|
||||
CDBehaviorTemplateTable::Instance().LoadValuesFromDatabase();
|
||||
CDBrickIDTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDCollectibleComponentTable::Instance().LoadValuesFromDatabase();
|
||||
// Always in memory: every entity and every inventory item asks for its components and item data, and looking up an
|
||||
// ComponentsRegistry, ItemComponent and Objects read rows from the fdb when it is open. Without it, the first two are
|
||||
// always in memory: every entity and every inventory item asks for its components and item data, and looking up an
|
||||
// id not seen yet in the unindexed CDClient scans the whole table (a character holding 3000 different items took a
|
||||
// minute to load)
|
||||
CDComponentsRegistryTable::Instance().LoadValuesFromDatabase();
|
||||
if (!CDComponentsRegistryTable::Instance().LoadFromFdb()) CDComponentsRegistryTable::Instance().LoadValuesFromDatabase();
|
||||
CDCurrencyTableTable::Instance().LoadValuesFromDatabase();
|
||||
CDDeletionRestrictionsTable::Instance().LoadValuesFromDatabase();
|
||||
CDDestructibleComponentTable::Instance().LoadValuesFromDatabase();
|
||||
@@ -134,7 +146,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
||||
CDEventGatingTable::Instance().LoadValuesFromDatabase();
|
||||
CDFeatureGatingTable::Instance().LoadValuesFromDatabase();
|
||||
CDInventoryComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDItemComponentTable::Instance().LoadValuesFromDatabase();
|
||||
if (!CDItemComponentTable::Instance().LoadFromFdb()) CDItemComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDItemSetSkillsTable::Instance().LoadValuesFromDatabase();
|
||||
CDItemSetsTable::Instance().LoadValuesFromDatabase();
|
||||
CDLevelProgressionLookupTable::Instance().LoadValuesFromDatabase();
|
||||
@@ -146,7 +158,7 @@ void CDClientManager::LoadValuesFromDatabase() {
|
||||
CDMissionsTable::Instance().LoadValuesFromDatabase();
|
||||
CDMovementAIComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDObjectSkillsTable::Instance().LoadValuesFromDatabase();
|
||||
CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase());
|
||||
if (!CDObjectsTable::Instance().LoadFromFdb()) CDCLIENT_DONT_CACHE_TABLE(CDObjectsTable::Instance().LoadValuesFromDatabase());
|
||||
CDPhysicsComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDPackageComponentTable::Instance().LoadValuesFromDatabase();
|
||||
CDPetComponentTable::Instance().LoadValuesFromDatabase();
|
||||
|
||||
@@ -3,11 +3,18 @@
|
||||
|
||||
#define UNUSED_TABLE(v)
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
/**
|
||||
* Initialize the CDClient tables so they are all loaded into memory.
|
||||
*/
|
||||
namespace CDClientManager {
|
||||
void LoadValuesFromDatabase();
|
||||
/**
|
||||
* @param fdbPath The client's cdclient.fdb. When it opens, the tables looked up by their first
|
||||
* column read their rows from it (shared between processes) instead of caching them. Empty, or
|
||||
* a file that doesn't open, keeps every table on CDServer.sqlite.
|
||||
*/
|
||||
void LoadValuesFromDatabase(const std::filesystem::path& fdbPath = {});
|
||||
void LoadValuesFromDefaults();
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "CDComponentsRegistryTable.h"
|
||||
#include "CDFdb.h"
|
||||
#include "Logger.h"
|
||||
#include "eReplicaComponentType.h"
|
||||
|
||||
void CDComponentsRegistryTable::LoadValuesFromDatabase() {
|
||||
@@ -20,15 +22,30 @@ void CDComponentsRegistryTable::LoadValuesFromDatabase() {
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto exists = entries.find(id);
|
||||
if (exists != entries.end()) {
|
||||
auto iter = entries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
|
||||
return iter == entries.end() ? defaultValue : iter->second;
|
||||
}
|
||||
bool CDComponentsRegistryTable::LoadFromFdb() {
|
||||
m_FdbTable = nullptr;
|
||||
const auto* table = CDFdb::GetTable("ComponentsRegistry");
|
||||
if (!table) return false;
|
||||
|
||||
// Now get the data. Get all components of this entity so we dont do a query for each component
|
||||
m_TypeColumn = table->GetColumnIndex("component_type");
|
||||
m_ComponentIdColumn = table->GetColumnIndex("component_id");
|
||||
if (m_TypeColumn < 0 || m_ComponentIdColumn < 0) return false;
|
||||
|
||||
const auto changed = CDFdb::FindChangedKeys(*table);
|
||||
if (!changed) return false;
|
||||
|
||||
// Ids whose rows CDServer.sqlite changes are read from it once and kept; everything else comes from the fdb
|
||||
for (const auto id : *changed) LoadFromSqlite(static_cast<uint32_t>(id));
|
||||
LOG("ComponentsRegistry: reading from the fdb, %zu ids differ in CDServer.sqlite and are kept in memory", changed->size());
|
||||
|
||||
m_FdbTable = table;
|
||||
return true;
|
||||
}
|
||||
|
||||
void CDComponentsRegistryTable::LoadFromSqlite(uint32_t id) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
|
||||
// Get all components of this entity so we dont do a query for each component
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ComponentsRegistry WHERE id = ?;");
|
||||
query.bind(1, static_cast<int32_t>(id));
|
||||
|
||||
@@ -46,6 +63,29 @@ int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponent
|
||||
}
|
||||
|
||||
entries.insert_or_assign(id, 0);
|
||||
}
|
||||
|
||||
int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto exists = entries.find(id);
|
||||
if (exists != entries.end()) {
|
||||
auto iter = entries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
|
||||
return iter == entries.end() ? defaultValue : iter->second;
|
||||
}
|
||||
|
||||
if (m_FdbTable) {
|
||||
// The last matching row wins, as when the rows are loaded into the map in file order
|
||||
int32_t result = defaultValue;
|
||||
const auto type = static_cast<int32_t>(componentType);
|
||||
m_FdbTable->ForEachRowWithKey(static_cast<int32_t>(id), [&](const FdbReader::Row& row) {
|
||||
if (row.GetInt(static_cast<uint32_t>(m_TypeColumn), 0) == type) {
|
||||
result = row.GetInt(static_cast<uint32_t>(m_ComponentIdColumn), -1);
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
|
||||
LoadFromSqlite(id);
|
||||
|
||||
auto iter = entries.find(static_cast<uint64_t>(componentType) << 32 | static_cast<uint64_t>(id));
|
||||
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "FdbReader.h"
|
||||
|
||||
enum class eReplicaComponentType : uint32_t;
|
||||
struct CDComponentsRegistry {
|
||||
uint32_t id; //!< The LOT is used as the ID
|
||||
@@ -16,5 +18,17 @@ struct CDComponentsRegistry {
|
||||
class CDComponentsRegistryTable : public CDTable<CDComponentsRegistryTable, std::unordered_map<uint64_t, uint32_t>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
// Reads rows from the client's fdb from now on, if it is open; false keeps the table on CDServer.sqlite
|
||||
bool LoadFromFdb();
|
||||
|
||||
int32_t GetByIDAndType(uint32_t id, eReplicaComponentType componentType, int32_t defaultValue = 0);
|
||||
|
||||
private:
|
||||
// Caches every row of one id from CDServer.sqlite
|
||||
void LoadFromSqlite(uint32_t id);
|
||||
|
||||
const FdbReader::Table* m_FdbTable = nullptr;
|
||||
int32_t m_TypeColumn = -1;
|
||||
int32_t m_ComponentIdColumn = -1;
|
||||
};
|
||||
|
||||
@@ -1,68 +1,67 @@
|
||||
#include "CDItemComponentTable.h"
|
||||
#include "GeneralUtils.h"
|
||||
#include "CDFdb.h"
|
||||
#include "Logger.h"
|
||||
|
||||
CDItemComponent CDItemComponentTable::Default = {};
|
||||
|
||||
void CDItemComponentTable::LoadValuesFromDatabase() {
|
||||
// First, get the size of the table
|
||||
uint32_t size = 0;
|
||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ItemComponent");
|
||||
while (!tableSize.eof()) {
|
||||
size = tableSize.getIntField(0, 0);
|
||||
|
||||
tableSize.nextRow();
|
||||
namespace {
|
||||
// Fills an entry from a CDServer.sqlite row or an fdb row (CDFdb::RowFields), which read alike
|
||||
template<typename Row>
|
||||
CDItemComponent ReadEntry(Row& row) {
|
||||
CDItemComponent entry;
|
||||
entry.id = row.getIntField("id", -1);
|
||||
entry.equipLocation = row.getStringField("equipLocation", "");
|
||||
entry.baseValue = row.getIntField("baseValue", -1);
|
||||
entry.isKitPiece = row.getIntField("isKitPiece", -1) == 1 ? true : false;
|
||||
entry.rarity = row.getIntField("rarity", 0);
|
||||
entry.itemType = row.getIntField("itemType", -1);
|
||||
entry.itemInfo = row.getInt64Field("itemInfo", -1);
|
||||
entry.inLootTable = row.getIntField("inLootTable", -1) == 1 ? true : false;
|
||||
entry.inVendor = row.getIntField("inVendor", -1) == 1 ? true : false;
|
||||
entry.isUnique = row.getIntField("isUnique", -1) == 1 ? true : false;
|
||||
entry.isBOP = row.getIntField("isBOP", -1) == 1 ? true : false;
|
||||
entry.isBOE = row.getIntField("isBOE", -1) == 1 ? true : false;
|
||||
entry.reqFlagID = row.getIntField("reqFlagID", -1);
|
||||
entry.reqSpecialtyID = row.getIntField("reqSpecialtyID", -1);
|
||||
entry.reqSpecRank = row.getIntField("reqSpecRank", -1);
|
||||
entry.reqAchievementID = row.getIntField("reqAchievementID", -1);
|
||||
entry.stackSize = row.getIntField("stackSize", -1);
|
||||
entry.color1 = row.getIntField("color1", -1);
|
||||
entry.decal = row.getIntField("decal", -1);
|
||||
entry.offsetGroupID = row.getIntField("offsetGroupID", -1);
|
||||
entry.buildTypes = row.getIntField("buildTypes", -1);
|
||||
entry.reqPrecondition = row.getStringField("reqPrecondition", "");
|
||||
entry.animationFlag = row.getIntField("animationFlag", 0);
|
||||
entry.equipEffects = row.getIntField("equipEffects", -1);
|
||||
entry.readyForQA = row.getIntField("readyForQA", -1) == 1 ? true : false;
|
||||
entry.itemRating = row.getIntField("itemRating", -1);
|
||||
entry.isTwoHanded = row.getIntField("isTwoHanded", -1) == 1 ? true : false;
|
||||
entry.minNumRequired = row.getIntField("minNumRequired", -1);
|
||||
entry.delResIndex = row.getIntField("delResIndex", -1);
|
||||
entry.currencyLOT = row.getIntField("currencyLOT", -1);
|
||||
entry.altCurrencyCost = row.getIntField("altCurrencyCost", -1);
|
||||
entry.subItems = row.getStringField("subItems", "");
|
||||
UNUSED_COLUMN(entry.audioEventUse = row.getStringField("audioEventUse", ""));
|
||||
entry.noEquipAnimation = row.getIntField("noEquipAnimation", -1) == 1 ? true : false;
|
||||
entry.commendationLOT = row.getIntField("commendationLOT", -1);
|
||||
entry.commendationCost = row.getIntField("commendationCost", -1);
|
||||
UNUSED_COLUMN(entry.audioEquipMetaEventSet = row.getStringField("audioEquipMetaEventSet", ""));
|
||||
entry.currencyCosts = row.getStringField("currencyCosts", "");
|
||||
UNUSED_COLUMN(entry.ingredientInfo = row.getStringField("ingredientInfo", ""));
|
||||
entry.locStatus = row.getIntField("locStatus", -1);
|
||||
entry.forgeType = row.getIntField("forgeType", -1);
|
||||
entry.SellMultiplier = row.getFloatField("SellMultiplier", -1.0f);
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
|
||||
tableSize.finalize();
|
||||
|
||||
void CDItemComponentTable::LoadValuesFromDatabase() {
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ItemComponent");
|
||||
auto& entries = GetEntriesMutable();
|
||||
while (!tableData.eof()) {
|
||||
CDItemComponent entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.equipLocation = tableData.getStringField("equipLocation", "");
|
||||
entry.baseValue = tableData.getIntField("baseValue", -1);
|
||||
entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false;
|
||||
entry.rarity = tableData.getIntField("rarity", 0);
|
||||
entry.itemType = tableData.getIntField("itemType", -1);
|
||||
entry.itemInfo = tableData.getInt64Field("itemInfo", -1);
|
||||
entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false;
|
||||
entry.inVendor = tableData.getIntField("inVendor", -1) == 1 ? true : false;
|
||||
entry.isUnique = tableData.getIntField("isUnique", -1) == 1 ? true : false;
|
||||
entry.isBOP = tableData.getIntField("isBOP", -1) == 1 ? true : false;
|
||||
entry.isBOE = tableData.getIntField("isBOE", -1) == 1 ? true : false;
|
||||
entry.reqFlagID = tableData.getIntField("reqFlagID", -1);
|
||||
entry.reqSpecialtyID = tableData.getIntField("reqSpecialtyID", -1);
|
||||
entry.reqSpecRank = tableData.getIntField("reqSpecRank", -1);
|
||||
entry.reqAchievementID = tableData.getIntField("reqAchievementID", -1);
|
||||
entry.stackSize = tableData.getIntField("stackSize", -1);
|
||||
entry.color1 = tableData.getIntField("color1", -1);
|
||||
entry.decal = tableData.getIntField("decal", -1);
|
||||
entry.offsetGroupID = tableData.getIntField("offsetGroupID", -1);
|
||||
entry.buildTypes = tableData.getIntField("buildTypes", -1);
|
||||
entry.reqPrecondition = tableData.getStringField("reqPrecondition", "");
|
||||
entry.animationFlag = tableData.getIntField("animationFlag", 0);
|
||||
entry.equipEffects = tableData.getIntField("equipEffects", -1);
|
||||
entry.readyForQA = tableData.getIntField("readyForQA", -1) == 1 ? true : false;
|
||||
entry.itemRating = tableData.getIntField("itemRating", -1);
|
||||
entry.isTwoHanded = tableData.getIntField("isTwoHanded", -1) == 1 ? true : false;
|
||||
entry.minNumRequired = tableData.getIntField("minNumRequired", -1);
|
||||
entry.delResIndex = tableData.getIntField("delResIndex", -1);
|
||||
entry.currencyLOT = tableData.getIntField("currencyLOT", -1);
|
||||
entry.altCurrencyCost = tableData.getIntField("altCurrencyCost", -1);
|
||||
entry.subItems = tableData.getStringField("subItems", "");
|
||||
UNUSED_COLUMN(entry.audioEventUse = tableData.getStringField("audioEventUse", ""));
|
||||
entry.noEquipAnimation = tableData.getIntField("noEquipAnimation", -1) == 1 ? true : false;
|
||||
entry.commendationLOT = tableData.getIntField("commendationLOT", -1);
|
||||
entry.commendationCost = tableData.getIntField("commendationCost", -1);
|
||||
UNUSED_COLUMN(entry.audioEquipMetaEventSet = tableData.getStringField("audioEquipMetaEventSet", ""));
|
||||
entry.currencyCosts = tableData.getStringField("currencyCosts", "");
|
||||
UNUSED_COLUMN(entry.ingredientInfo = tableData.getStringField("ingredientInfo", ""));
|
||||
entry.locStatus = tableData.getIntField("locStatus", -1);
|
||||
entry.forgeType = tableData.getIntField("forgeType", -1);
|
||||
entry.SellMultiplier = tableData.getFloatField("SellMultiplier", -1.0f);
|
||||
|
||||
CDItemComponent entry = ReadEntry(tableData);
|
||||
entries.insert(std::make_pair(entry.id, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
@@ -70,6 +69,43 @@ void CDItemComponentTable::LoadValuesFromDatabase() {
|
||||
tableData.finalize();
|
||||
}
|
||||
|
||||
bool CDItemComponentTable::LoadFromFdb() {
|
||||
m_FdbTable = nullptr;
|
||||
const auto* table = CDFdb::GetTable("ItemComponent");
|
||||
if (!table) return false;
|
||||
|
||||
const auto changed = CDFdb::FindChangedKeys(*table);
|
||||
if (!changed) return false;
|
||||
|
||||
// Ids whose rows CDServer.sqlite changes are read from it once and kept; everything else comes from the fdb
|
||||
for (const auto id : *changed) LoadFromSqlite(static_cast<uint32_t>(id));
|
||||
LOG("ItemComponent: reading from the fdb, %zu ids differ in CDServer.sqlite and are kept in memory", changed->size());
|
||||
|
||||
m_FdbTable = table;
|
||||
return true;
|
||||
}
|
||||
|
||||
const CDItemComponent& CDItemComponentTable::LoadFromSqlite(uint32_t id) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ItemComponent WHERE id = ?;");
|
||||
query.bind(1, static_cast<int32_t>(id));
|
||||
|
||||
auto tableData = query.execQuery();
|
||||
if (tableData.eof()) {
|
||||
entries.insert(std::make_pair(id, Default));
|
||||
return Default;
|
||||
}
|
||||
|
||||
while (!tableData.eof()) {
|
||||
CDItemComponent entry = ReadEntry(tableData);
|
||||
entries.insert(std::make_pair(entry.id, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
const auto& it = entries.find(id);
|
||||
return it != entries.end() ? it->second : Default;
|
||||
}
|
||||
|
||||
const CDItemComponent& CDItemComponentTable::GetItemComponentByID(uint32_t skillID) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
const auto& it = entries.find(skillID);
|
||||
@@ -77,70 +113,17 @@ const CDItemComponent& CDItemComponentTable::GetItemComponentByID(uint32_t skill
|
||||
return it->second;
|
||||
}
|
||||
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM ItemComponent WHERE id = ?;");
|
||||
query.bind(1, static_cast<int32_t>(skillID));
|
||||
if (!m_FdbTable) return LoadFromSqlite(skillID);
|
||||
|
||||
auto tableData = query.execQuery();
|
||||
if (tableData.eof()) {
|
||||
// Only the items asked for are kept in memory; the first row of an id wins, as in the SQLite path
|
||||
const auto row = m_FdbTable->FindFirst(static_cast<int32_t>(skillID));
|
||||
if (!row) {
|
||||
entries.insert(std::make_pair(skillID, Default));
|
||||
return Default;
|
||||
}
|
||||
|
||||
while (!tableData.eof()) {
|
||||
CDItemComponent entry;
|
||||
entry.id = tableData.getIntField("id", -1);
|
||||
entry.equipLocation = tableData.getStringField("equipLocation", "");
|
||||
entry.baseValue = tableData.getIntField("baseValue", -1);
|
||||
entry.isKitPiece = tableData.getIntField("isKitPiece", -1) == 1 ? true : false;
|
||||
entry.rarity = tableData.getIntField("rarity", 0);
|
||||
entry.itemType = tableData.getIntField("itemType", -1);
|
||||
entry.itemInfo = tableData.getInt64Field("itemInfo", -1);
|
||||
entry.inLootTable = tableData.getIntField("inLootTable", -1) == 1 ? true : false;
|
||||
entry.inVendor = tableData.getIntField("inVendor", -1) == 1 ? true : false;
|
||||
entry.isUnique = tableData.getIntField("isUnique", -1) == 1 ? true : false;
|
||||
entry.isBOP = tableData.getIntField("isBOP", -1) == 1 ? true : false;
|
||||
entry.isBOE = tableData.getIntField("isBOE", -1) == 1 ? true : false;
|
||||
entry.reqFlagID = tableData.getIntField("reqFlagID", -1);
|
||||
entry.reqSpecialtyID = tableData.getIntField("reqSpecialtyID", -1);
|
||||
entry.reqSpecRank = tableData.getIntField("reqSpecRank", -1);
|
||||
entry.reqAchievementID = tableData.getIntField("reqAchievementID", -1);
|
||||
entry.stackSize = tableData.getIntField("stackSize", -1);
|
||||
entry.color1 = tableData.getIntField("color1", -1);
|
||||
entry.decal = tableData.getIntField("decal", -1);
|
||||
entry.offsetGroupID = tableData.getIntField("offsetGroupID", -1);
|
||||
entry.buildTypes = tableData.getIntField("buildTypes", -1);
|
||||
entry.reqPrecondition = tableData.getStringField("reqPrecondition", "");
|
||||
entry.animationFlag = tableData.getIntField("animationFlag", 0);
|
||||
entry.equipEffects = tableData.getIntField("equipEffects", -1);
|
||||
entry.readyForQA = tableData.getIntField("readyForQA", -1) == 1 ? true : false;
|
||||
entry.itemRating = tableData.getIntField("itemRating", -1);
|
||||
entry.isTwoHanded = tableData.getIntField("isTwoHanded", -1) == 1 ? true : false;
|
||||
entry.minNumRequired = tableData.getIntField("minNumRequired", -1);
|
||||
entry.delResIndex = tableData.getIntField("delResIndex", -1);
|
||||
entry.currencyLOT = tableData.getIntField("currencyLOT", -1);
|
||||
entry.altCurrencyCost = tableData.getIntField("altCurrencyCost", -1);
|
||||
entry.subItems = tableData.getStringField("subItems", "");
|
||||
UNUSED(entry.audioEventUse = tableData.getStringField("audioEventUse", ""));
|
||||
entry.noEquipAnimation = tableData.getIntField("noEquipAnimation", -1) == 1 ? true : false;
|
||||
entry.commendationLOT = tableData.getIntField("commendationLOT", -1);
|
||||
entry.commendationCost = tableData.getIntField("commendationCost", -1);
|
||||
UNUSED(entry.audioEquipMetaEventSet = tableData.getStringField("audioEquipMetaEventSet", ""));
|
||||
entry.currencyCosts = tableData.getStringField("currencyCosts", "");
|
||||
UNUSED(entry.ingredientInfo = tableData.getStringField("ingredientInfo", ""));
|
||||
entry.locStatus = tableData.getIntField("locStatus", -1);
|
||||
entry.forgeType = tableData.getIntField("forgeType", -1);
|
||||
entry.SellMultiplier = tableData.getFloatField("SellMultiplier", -1.0f);
|
||||
|
||||
entries.insert(std::make_pair(entry.id, entry));
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
const auto& it2 = entries.find(skillID);
|
||||
if (it2 != entries.end()) {
|
||||
return it2->second;
|
||||
}
|
||||
|
||||
return Default;
|
||||
const CDFdb::RowFields fields(*m_FdbTable, *row);
|
||||
return entries.insert(std::make_pair(skillID, ReadEntry(fields))).first->second;
|
||||
}
|
||||
|
||||
std::map<LOT, uint32_t> CDItemComponentTable::ParseCraftingCurrencies(const CDItemComponent& itemComponent) {
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
// Custom Classes
|
||||
#include "CDTable.h"
|
||||
#include "dCommonVars.h"
|
||||
#include "FdbReader.h"
|
||||
|
||||
struct CDItemComponent {
|
||||
uint32_t id; //!< The Component ID
|
||||
@@ -52,10 +53,20 @@ struct CDItemComponent {
|
||||
class CDItemComponentTable : public CDTable<CDItemComponentTable, std::map<uint32_t, CDItemComponent>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
// Reads rows from the client's fdb from now on, if it is open; false keeps the table on CDServer.sqlite
|
||||
bool LoadFromFdb();
|
||||
|
||||
static std::map<LOT, uint32_t> ParseCraftingCurrencies(const CDItemComponent& itemComponent);
|
||||
|
||||
// Gets an entry by ID
|
||||
const CDItemComponent& GetItemComponentByID(uint32_t skillID);
|
||||
|
||||
static CDItemComponent Default;
|
||||
|
||||
private:
|
||||
// Caches the entry of one id from CDServer.sqlite (Default when there is none) and returns it
|
||||
const CDItemComponent& LoadFromSqlite(uint32_t id);
|
||||
|
||||
const FdbReader::Table* m_FdbTable = nullptr;
|
||||
};
|
||||
|
||||
@@ -1,21 +1,30 @@
|
||||
#include "CDObjectsTable.h"
|
||||
#include "CDFdb.h"
|
||||
#include "Logger.h"
|
||||
|
||||
namespace {
|
||||
CDObjects ObjDefault;
|
||||
|
||||
// Fills an entry from a CDServer.sqlite row or an fdb row (CDFdb::RowFields), which read alike
|
||||
template<typename Row>
|
||||
void ReadEntry(Row& row, CDObjects& entry) {
|
||||
entry.name = row.getStringField("name", "");
|
||||
UNUSED_COLUMN(entry.placeable = row.getIntField("placeable", -1);)
|
||||
entry.type = row.getStringField("type", "");
|
||||
UNUSED_COLUMN(entry.description = row.getStringField("description", "");)
|
||||
UNUSED_COLUMN(entry.localize = row.getIntField("localize", -1);)
|
||||
UNUSED_COLUMN(entry.npcTemplateID = row.getIntField("npcTemplateID", -1);)
|
||||
UNUSED_COLUMN(entry.displayName = row.getStringField("displayName", "");)
|
||||
entry.interactionDistance = row.getFloatField("interactionDistance", -1.0f);
|
||||
UNUSED_COLUMN(entry.nametag = row.getIntField("nametag", -1);)
|
||||
UNUSED_COLUMN(entry._internalNotes = row.getStringField("_internalNotes", "");)
|
||||
UNUSED_COLUMN(entry.locStatus = row.getIntField("locStatus", -1);)
|
||||
UNUSED_COLUMN(entry.gate_version = row.getStringField("gate_version", "");)
|
||||
UNUSED_COLUMN(entry.HQ_valid = row.getIntField("HQ_valid", -1);)
|
||||
}
|
||||
};
|
||||
|
||||
void CDObjectsTable::LoadValuesFromDatabase() {
|
||||
// First, get the size of the table
|
||||
uint32_t size = 0;
|
||||
auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM Objects");
|
||||
while (!tableSize.eof()) {
|
||||
size = tableSize.getIntField(0, 0);
|
||||
|
||||
tableSize.nextRow();
|
||||
}
|
||||
|
||||
tableSize.finalize();
|
||||
|
||||
// Now get the data
|
||||
auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM Objects");
|
||||
auto& entries = GetEntriesMutable();
|
||||
@@ -24,19 +33,7 @@ void CDObjectsTable::LoadValuesFromDatabase() {
|
||||
|
||||
auto& entry = entries[lot];
|
||||
entry.id = lot;
|
||||
entry.name = tableData.getStringField("name", "");
|
||||
UNUSED_COLUMN(entry.placeable = tableData.getIntField("placeable", -1);)
|
||||
entry.type = tableData.getStringField("type", "");
|
||||
UNUSED_COLUMN(entry.description = tableData.getStringField("description", "");)
|
||||
UNUSED_COLUMN(entry.localize = tableData.getIntField("localize", -1);)
|
||||
UNUSED_COLUMN(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1);)
|
||||
UNUSED_COLUMN(entry.displayName = tableData.getStringField("displayName", "");)
|
||||
entry.interactionDistance = tableData.getFloatField("interactionDistance", -1.0f);
|
||||
UNUSED_COLUMN(entry.nametag = tableData.getIntField("nametag", -1);)
|
||||
UNUSED_COLUMN(entry._internalNotes = tableData.getStringField("_internalNotes", "");)
|
||||
UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1);)
|
||||
UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "");)
|
||||
UNUSED_COLUMN(entry.HQ_valid = tableData.getIntField("HQ_valid", -1);)
|
||||
ReadEntry(tableData, entry);
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
@@ -44,13 +41,24 @@ void CDObjectsTable::LoadValuesFromDatabase() {
|
||||
ObjDefault.id = 0;
|
||||
}
|
||||
|
||||
const CDObjects& CDObjectsTable::GetByID(const uint32_t lot) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
const auto& it = entries.find(lot);
|
||||
if (it != entries.end()) {
|
||||
return it->second;
|
||||
}
|
||||
bool CDObjectsTable::LoadFromFdb() {
|
||||
m_FdbTable = nullptr;
|
||||
const auto* table = CDFdb::GetTable("Objects");
|
||||
if (!table) return false;
|
||||
|
||||
const auto changed = CDFdb::FindChangedKeys(*table);
|
||||
if (!changed) return false;
|
||||
|
||||
// Ids whose rows CDServer.sqlite changes are read from it once and kept; everything else comes from the fdb
|
||||
for (const auto id : *changed) LoadFromSqlite(static_cast<uint32_t>(id));
|
||||
LOG("Objects: reading from the fdb, %zu ids differ in CDServer.sqlite and are kept in memory", changed->size());
|
||||
|
||||
m_FdbTable = table;
|
||||
return true;
|
||||
}
|
||||
|
||||
const CDObjects& CDObjectsTable::LoadFromSqlite(const uint32_t lot) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
auto query = CDClientDatabase::CreatePreppedStmt("SELECT * FROM Objects WHERE id = ?;");
|
||||
query.bind(1, static_cast<int32_t>(lot));
|
||||
|
||||
@@ -62,33 +70,42 @@ const CDObjects& CDObjectsTable::GetByID(const uint32_t lot) {
|
||||
|
||||
// Now get the data
|
||||
while (!tableData.eof()) {
|
||||
const uint32_t lot = tableData.getIntField("id", 0);
|
||||
const uint32_t rowLot = tableData.getIntField("id", 0);
|
||||
|
||||
auto& entry = entries[lot];
|
||||
entry.id = lot;
|
||||
entry.name = tableData.getStringField("name", "");
|
||||
UNUSED(entry.placeable = tableData.getIntField("placeable", -1));
|
||||
entry.type = tableData.getStringField("type", "");
|
||||
UNUSED(entry.description = tableData.getStringField(4, ""));
|
||||
UNUSED(entry.localize = tableData.getIntField("localize", -1));
|
||||
UNUSED(entry.npcTemplateID = tableData.getIntField("npcTemplateID", -1));
|
||||
UNUSED(entry.displayName = tableData.getStringField("displayName", ""));
|
||||
entry.interactionDistance = tableData.getFloatField("interactionDistance", -1.0f);
|
||||
UNUSED(entry.nametag = tableData.getIntField("nametag", -1));
|
||||
UNUSED(entry._internalNotes = tableData.getStringField("_internalNotes", ""));
|
||||
UNUSED(entry.locStatus = tableData.getIntField("locStatus", -1));
|
||||
UNUSED(entry.gate_version = tableData.getStringField("gate_version", ""));
|
||||
UNUSED(entry.HQ_valid = tableData.getIntField("HQ_valid", -1));
|
||||
auto& entry = entries[rowLot];
|
||||
entry.id = rowLot;
|
||||
ReadEntry(tableData, entry);
|
||||
|
||||
tableData.nextRow();
|
||||
}
|
||||
|
||||
tableData.finalize();
|
||||
|
||||
const auto& it2 = entries.find(lot);
|
||||
if (it2 != entries.end()) {
|
||||
return it2->second;
|
||||
const auto& it = entries.find(lot);
|
||||
return it != entries.end() ? it->second : ObjDefault;
|
||||
}
|
||||
|
||||
const CDObjects& CDObjectsTable::GetByID(const uint32_t lot) {
|
||||
auto& entries = GetEntriesMutable();
|
||||
const auto& it = entries.find(lot);
|
||||
if (it != entries.end()) {
|
||||
return it->second;
|
||||
}
|
||||
|
||||
return ObjDefault;
|
||||
if (!m_FdbTable) return LoadFromSqlite(lot);
|
||||
|
||||
// Only the objects asked for are kept in memory; the last row of an id wins, as in the SQLite path
|
||||
std::optional<CDObjects> found;
|
||||
m_FdbTable->ForEachRowWithKey(static_cast<int32_t>(lot), [&](const FdbReader::Row& row) {
|
||||
if (!found) found.emplace();
|
||||
found->id = lot;
|
||||
const CDFdb::RowFields fields(*m_FdbTable, row);
|
||||
ReadEntry(fields, *found);
|
||||
});
|
||||
if (!found) {
|
||||
entries.emplace(lot, ObjDefault);
|
||||
return ObjDefault;
|
||||
}
|
||||
|
||||
return entries.emplace(lot, std::move(*found)).first->second;
|
||||
}
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "FdbReader.h"
|
||||
|
||||
struct CDObjects {
|
||||
uint32_t id; //!< The LOT of the object
|
||||
std::string name; //!< The internal name of the object
|
||||
@@ -25,7 +27,17 @@ struct CDObjects {
|
||||
class CDObjectsTable : public CDTable<CDObjectsTable, std::map<uint32_t, CDObjects>> {
|
||||
public:
|
||||
void LoadValuesFromDatabase();
|
||||
|
||||
// Reads rows from the client's fdb from now on, if it is open; false keeps the table on CDServer.sqlite
|
||||
bool LoadFromFdb();
|
||||
|
||||
// Gets an entry by ID
|
||||
const CDObjects& GetByID(const uint32_t lot);
|
||||
|
||||
private:
|
||||
// Caches the entry of one id from CDServer.sqlite (the default entry when there is none) and returns it
|
||||
const CDObjects& LoadFromSqlite(const uint32_t lot);
|
||||
|
||||
const FdbReader::Table* m_FdbTable = nullptr;
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user