From 56f371216bf2e28c6b059735e42fae5ae5759c07 Mon Sep 17 00:00:00 2001 From: wincent Date: Thu, 11 Aug 2022 16:36:03 +0200 Subject: [PATCH] Abstracted the CDClient tables There is now an option to utilize shared memory for some CDClient tables by adding `CD_PROVIDER_MEMORY=1` to the CMakeVariables.txt file. Allows masterconfig.ini to specify another run command for the world server, to allow for easier debugging through `valgrind`. --- dCommon/Diagnostics.cpp | 3 + dCommon/dConfig.cpp | 17 +- dDatabase/CDAbstractProvider.h | 22 +++ dDatabase/CDAbstractSharedMemoryMap.h | 124 +++++++++++++++ dDatabase/CDAbstractSharedMemoryProvider.h | 150 ++++++++++++++++++ dDatabase/CDAbstractSqliteProvider.h | 74 +++++++++ dDatabase/CDClientManager.cpp | 10 ++ dDatabase/CDClientManager.h | 2 + dDatabase/CDProvider.h | 41 +++++ dDatabase/Tables/CDBehaviorParameterTable.cpp | 139 ++-------------- dDatabase/Tables/CDBehaviorParameterTable.h | 4 +- dDatabase/Tables/CDBehaviorTemplateTable.cpp | 78 +++------ dDatabase/Tables/CDBehaviorTemplateTable.h | 21 +-- .../Tables/CDComponentsRegistryTable.cpp | 146 ++++------------- dDatabase/Tables/CDComponentsRegistryTable.h | 1 + dDatabase/Tables/CDTable.cpp | 70 ++++++++ dDatabase/Tables/CDTable.h | 23 +++ dDatabase/Tables/CMakeLists.txt | 2 +- dGame/dBehaviors/Behavior.cpp | 2 +- dGame/dUtilities/SlashCommandHandler.cpp | 34 ++++ dMasterServer/InstanceManager.cpp | 22 ++- dMasterServer/MasterServer.cpp | 2 +- dNet/AuthPackets.cpp | 2 +- 23 files changed, 661 insertions(+), 328 deletions(-) create mode 100644 dDatabase/CDAbstractProvider.h create mode 100644 dDatabase/CDAbstractSharedMemoryMap.h create mode 100644 dDatabase/CDAbstractSharedMemoryProvider.h create mode 100644 dDatabase/CDAbstractSqliteProvider.h create mode 100644 dDatabase/CDProvider.h create mode 100644 dDatabase/Tables/CDTable.cpp diff --git a/dCommon/Diagnostics.cpp b/dCommon/Diagnostics.cpp index 5963400d..eb5ebd9b 100644 --- a/dCommon/Diagnostics.cpp +++ b/dCommon/Diagnostics.cpp @@ -171,6 +171,9 @@ void CatchUnhandled(int sig) { ErrorCallback, nullptr); + // Print error code + printf("Error code: %d\n", sig); + struct bt_ctx ctx = {state, 0}; Bt(state); diff --git a/dCommon/dConfig.cpp b/dCommon/dConfig.cpp index b53b7166..fa779b2d 100644 --- a/dCommon/dConfig.cpp +++ b/dCommon/dConfig.cpp @@ -34,12 +34,19 @@ void dConfig::ProcessLine(const std::string& line) { seglist.push_back(segment); } - if (seglist.size() != 2) return; + if (seglist.size() < 2) return; + + // Segment #1 is combined with the rest of the line to form the value + std::string key = seglist[0]; + std::string value = seglist[1]; + for (size_t i = 2; i < seglist.size(); ++i) { + value += "=" + seglist[i]; + } //Make sure that on Linux, we remove special characters: - if (!seglist[1].empty() && seglist[1][seglist[1].size() - 1] == '\r') - seglist[1].erase(seglist[1].size() - 1); + if (!value.empty() && value[value.size() - 1] == '\r') + value.erase(value.size() - 1); - m_Keys.push_back(seglist[0]); - m_Values.push_back(seglist[1]); + m_Keys.push_back(key); + m_Values.push_back(value); } \ No newline at end of file diff --git a/dDatabase/CDAbstractProvider.h b/dDatabase/CDAbstractProvider.h new file mode 100644 index 00000000..3f263bb5 --- /dev/null +++ b/dDatabase/CDAbstractProvider.h @@ -0,0 +1,22 @@ +#pragma once + +#include "GeneralUtils.h" + +#include "Game.h" +#include "dLogger.h" +#include "dServer.h" + +#include "CDTable.h" + +template < + typename KeyType, + typename MappedType +> +class CDAbstractProvider +{ +public: + virtual void LoadClient() = 0; + virtual void LoadHost() = 0; + + virtual const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) = 0; +}; diff --git a/dDatabase/CDAbstractSharedMemoryMap.h b/dDatabase/CDAbstractSharedMemoryMap.h new file mode 100644 index 00000000..f5642de5 --- /dev/null +++ b/dDatabase/CDAbstractSharedMemoryMap.h @@ -0,0 +1,124 @@ +#pragma once + +#include "GeneralUtils.h" + +#include "Game.h" +#include "dLogger.h" +#include "dServer.h" + +#include "CDTable.h" + +#include "CDAbstractProvider.h" + +#include +#include +#include +#include +#include +#include +#include + +template < + typename KeyType, + typename MappedType +> +class CDAbstractSharedMemoryMap : public CDAbstractProvider +{ + typedef std::pair ValueType; + + typedef boost::interprocess::allocator ShmemAllocator; + + typedef boost::interprocess::map, ShmemAllocator> Map; + +public: + std::map m_CacheMap; + + std::string m_Name; + + size_t m_Size; + + bool m_Host; + Map* m_HostEntries; + + boost::interprocess::managed_shared_memory m_ClientSegment; + ShmemAllocator* m_ClientAllocInst; + boost::interprocess::offset_ptr m_ClientEntires; + + CDAbstractSharedMemoryMap(std::string name, size_t size) + { + m_Name = name; + m_Size = size; + m_Host = false; + m_HostEntries = nullptr; + m_ClientAllocInst = nullptr; + m_ClientEntires = nullptr; + + LoadClient(); + } + + const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) override { + const auto& cacheIt = m_CacheMap.find(key); + if (cacheIt != m_CacheMap.end()) { + return cacheIt->second; + } + + const auto& it = m_ClientEntires->find(key); + if (it == m_ClientEntires->end()) { + return defaultValue; + } + + return it->second; + } + + const void SetEntry(const KeyType& key, const MappedType& value) { + if (m_Host) { + // If we are already hosting, we cannot add to the map, throw an error + throw std::runtime_error("Can not add to a map that is already being hosted"); + } + + m_CacheMap.emplace(key, value); + } + + void LoadClient() override { + try { + m_ClientSegment = boost::interprocess::managed_shared_memory(boost::interprocess::open_read_only, m_Name.c_str()); + + m_ClientAllocInst = new ShmemAllocator(m_ClientSegment.get_segment_manager()); + + m_ClientEntires = m_ClientSegment.find(m_Name.c_str()).first; + + if (m_ClientEntires == nullptr) { + throw std::runtime_error("Could not find shared memory segment " + m_Name); + } + + } catch (std::exception &e) { + // Not open + } + } + + void LoadHost() override { + try { + boost::interprocess::shared_memory_object::remove(m_Name.c_str()); + + boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, m_Name.c_str(), m_Size); + + ShmemAllocator alloc(segment.get_segment_manager()); + + m_HostEntries = segment.construct(m_Name.c_str()) (std::less(), alloc); + + // Copy cache + for (const auto& pair : m_CacheMap) { + m_HostEntries->insert(std::make_pair(pair.first, pair.second)); + } + + m_Host = true; + + LoadClient(); + } catch (std::exception &e) { + // Make sure the smemory is removed + boost::interprocess::shared_memory_object::remove(m_Name.c_str()); + + throw e; + } + } +}; diff --git a/dDatabase/CDAbstractSharedMemoryProvider.h b/dDatabase/CDAbstractSharedMemoryProvider.h new file mode 100644 index 00000000..bce08bae --- /dev/null +++ b/dDatabase/CDAbstractSharedMemoryProvider.h @@ -0,0 +1,150 @@ +#pragma once + +#include "CDAbstractProvider.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +template < + typename KeyType, + typename MappedType +> +class CDAbstractSharedMemoryProvider : public CDAbstractProvider +{ + typedef std::pair ValueType; + + typedef boost::interprocess::allocator ShmemAllocator; + + typedef boost::interprocess::map, ShmemAllocator> Map; + +public: + std::string m_Name; + std::string m_MapName; + std::function m_ParseEntry; + std::function m_CalculateSize; + + bool m_Cache; + + std::unordered_map m_CacheMap; + + bool m_Host; + Map* m_HostEntries; + + boost::interprocess::managed_shared_memory m_ClientSegment; + ShmemAllocator* m_ClientAllocInst; + boost::interprocess::offset_ptr m_ClientEntires; + + CDAbstractSharedMemoryProvider(std::string name, std::function parseEntry, std::function calculateSize, bool cache) + { + m_Name = name; + m_MapName = name + "Map"; + m_ParseEntry = parseEntry; + m_CalculateSize = calculateSize; + m_Cache = cache; + m_Host = false; + m_HostEntries = nullptr; + + LoadClient(); + } + + const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) override { + if (m_Host) { + auto it = m_HostEntries->find(key); + if (it == m_HostEntries->end()) + { + return defaultValue; + } + return it->second; + } + + if (m_Cache) { + auto it = m_CacheMap.find(key); + if (it != m_CacheMap.end()) { + return it->second; + } + } + + const auto& it = m_ClientEntires->find(key); + if (it == m_ClientEntires->end()) + { + if (m_Cache) { + m_CacheMap.emplace(key, defaultValue); + } + + return defaultValue; + } + + if (m_Cache) { + m_CacheMap.emplace(key, it->second); + } + + return it->second; + } + + void LoadClient() override { + try { + m_ClientSegment = boost::interprocess::managed_shared_memory(boost::interprocess::open_read_only, m_MapName.c_str()); + + m_ClientAllocInst = new ShmemAllocator(m_ClientSegment.get_segment_manager()); + + m_ClientEntires = m_ClientSegment.find(m_Name.c_str()).first; + + if (m_ClientEntires == nullptr) { + throw std::runtime_error("Could not find shared memory segment " + m_Name); + } + + } catch (boost::interprocess::interprocess_exception& e) { + // Not open + } + } + + void LoadHost() override { + try { + boost::interprocess::shared_memory_object::remove(m_MapName.c_str()); + + auto sizeQuery = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM " + m_Name); + + if (sizeQuery.eof()) { + throw std::runtime_error("Could not get size of table " + m_Name); + return; + } + + int32_t size = sizeQuery.getIntField(0); + + size = m_CalculateSize(size); + + boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, m_MapName.c_str(), size); + + ShmemAllocator alloc_inst (segment.get_segment_manager()); + + m_HostEntries = segment.construct(m_Name.c_str()) (std::less(), alloc_inst); + + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM " + m_Name); + + while (!tableData.eof()) { + ValueType entry = m_ParseEntry(tableData); + + m_HostEntries->insert(entry); + + tableData.nextRow(); + } + + tableData.finalize(); + + m_Host = true; + + LoadClient(); + } catch (std::exception &e) { + // Make sure the smemory is removed + boost::interprocess::shared_memory_object::remove(m_MapName.c_str()); + + throw e; + } + } +}; diff --git a/dDatabase/CDAbstractSqliteProvider.h b/dDatabase/CDAbstractSqliteProvider.h new file mode 100644 index 00000000..b330ca5f --- /dev/null +++ b/dDatabase/CDAbstractSqliteProvider.h @@ -0,0 +1,74 @@ +#pragma once + +#include "GeneralUtils.h" + +#include "Game.h" +#include "dLogger.h" +#include "dServer.h" + +#include "CDTable.h" + +#include "CDAbstractProvider.h" + +template < + typename KeyType, + typename MappedType +> +class CDAbstractSqliteProvider : public CDAbstractProvider +{ + typedef std::pair ValueType; + +public: + std::string m_Name; + std::function m_ParseEntry; + + std::unordered_map m_Entries; + + CDAbstractSqliteProvider(std::string name, std::function parseEntry, bool cache) + { + m_Name = name; + m_ParseEntry = parseEntry; + + LoadClient(); + } + + void LoadClient() override { + // First, get the size of the table + uint32_t size = 0; + auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM " + m_Name); + while (!tableSize.eof()) { + size = tableSize.getIntField(0, 0); + + tableSize.nextRow(); + } + + tableSize.finalize(); + + // Reserve the size + m_Entries.reserve(size); + + // Now get the data + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM " + m_Name); + while (!tableData.eof()) { + auto entry = m_ParseEntry(tableData); + + m_Entries.insert(entry); + + tableData.nextRow(); + } + + tableData.finalize(); + } + + void LoadHost() override { + return; + } + + const MappedType& GetEntry(const KeyType& key, const MappedType& defaultValue) override { + auto it = m_Entries.find(key); + if (it != m_Entries.end()) { + return it->second; + } + return defaultValue; + } +}; diff --git a/dDatabase/CDClientManager.cpp b/dDatabase/CDClientManager.cpp index 75ada1ea..a4dc40c8 100644 --- a/dDatabase/CDClientManager.cpp +++ b/dDatabase/CDClientManager.cpp @@ -5,6 +5,8 @@ CDClientManager * CDClientManager::m_Address = nullptr; //! Initializes the manager void CDClientManager::Initialize(void) { + CDTable::Initalize(); + tables.insert(std::make_pair("ActivityRewards", new CDActivityRewardsTable())); UNUSED(tables.insert(std::make_pair("Animations", new CDAnimationsTable()))); tables.insert(std::make_pair("BehaviorParameter", new CDBehaviorParameterTable())); @@ -44,3 +46,11 @@ void CDClientManager::Initialize(void) { tables.insert(std::make_pair("FeatureGating", new CDFeatureGatingTable())); tables.insert(std::make_pair("RailActivatorComponent", new CDRailActivatorComponentTable())); } + +void CDClientManager::LoadHost() { + for (auto itr = this->tables.begin(); itr != this->tables.end(); ++itr) { + itr->second->LoadHost(); + } + + CDTable::InitalizeHost(); +} diff --git a/dDatabase/CDClientManager.h b/dDatabase/CDClientManager.h index f4530f90..1635c396 100644 --- a/dDatabase/CDClientManager.h +++ b/dDatabase/CDClientManager.h @@ -93,4 +93,6 @@ public: return nullptr; } + + void LoadHost(); }; diff --git a/dDatabase/CDProvider.h b/dDatabase/CDProvider.h new file mode 100644 index 00000000..e58b3869 --- /dev/null +++ b/dDatabase/CDProvider.h @@ -0,0 +1,41 @@ +#pragma once + +#include "CDAbstractProvider.h" + +/** + * Shared memory provider with CDAbstractSharedMemoryProvider + * + * Depends on the boost::interprocess library — header only + * + * Requires that CD_PROVIDER_MEMORY is defined and CD_PROVIDER_SQLITE is not defined + */ +#if defined(CD_PROVIDER_MEMORY) && !defined(CD_PROVIDER_SQLITE) + +#include "CDAbstractSharedMemoryProvider.h" + +#define CD_PROVIDER(provider, key, value) CDAbstractSharedMemoryProvider* provider; typedef key CD_KEY; typedef value CD_VALUE +#define NEW_CD_PROVIDER(provider, name, parser, size_calculator, cache) provider = new CDAbstractSharedMemoryProvider(name, parser, size_calculator, cache) + +template +using CDProvider = CDAbstractSharedMemoryProvider; + +#endif + +/** + * SQLite provider with CDAbstractSqliteProvider + * + * No extra dependencies + * + * Requires that CD_PROVIDER_SQLITE or CD_PROVIDER_MEMORY is not defined — the default option + */ +#if defined(CD_PROVIDER_SQLITE) || !defined(CD_PROVIDER_MEMORY) + +#include "CDAbstractSqliteProvider.h" + +#define CD_PROVIDER(provider, key, value) CDAbstractSqliteProvider* provider; typedef key CD_KEY; typedef value CD_VALUE +#define NEW_CD_PROVIDER(provider, name, parser, size_calculator, cache) provider = new CDAbstractSqliteProvider(name, parser, cache) + +template +using CDProvider = CDAbstractSqliteProvider; + +#endif diff --git a/dDatabase/Tables/CDBehaviorParameterTable.cpp b/dDatabase/Tables/CDBehaviorParameterTable.cpp index 4fa2de51..5cf60545 100644 --- a/dDatabase/Tables/CDBehaviorParameterTable.cpp +++ b/dDatabase/Tables/CDBehaviorParameterTable.cpp @@ -5,47 +5,27 @@ #include "dLogger.h" #include "dServer.h" -#include -#include -#include -#include -#include -#include +#include "CDProvider.h" -typedef size_t KeyType; -typedef float MappedType; -typedef std::pair ValueType; - -typedef boost::interprocess::allocator ShmemAllocator; - -typedef boost::interprocess::map, ShmemAllocator> CDBehaviorParameterMap; - -CDBehaviorParameterMap *table = nullptr; - -boost::interprocess::managed_shared_memory segment; -ShmemAllocator* alloc_inst; -boost::interprocess::offset_ptr m; +CD_PROVIDER(BehaviorParameterProvider, size_t, float); //! Constructor CDBehaviorParameterTable::CDBehaviorParameterTable(void) { - try { - segment = boost::interprocess::managed_shared_memory(boost::interprocess::open_read_only, "CDBehaviorParameterTable"); + NEW_CD_PROVIDER(BehaviorParameterProvider, "BehaviorParameter", [](CppSQLite3Query& query) { + size_t hash = 0; - alloc_inst = new ShmemAllocator(segment.get_segment_manager()); + int32_t behaviorID = query.getIntField(0, -1); + std::string parameterID = query.getStringField(1, ""); + + GeneralUtils::hash_combine(hash, behaviorID); + GeneralUtils::hash_combine(hash, parameterID); - m = segment.find("CDBehaviorParameter").first; + float value = query.getFloatField(2, -1.0f); - if (m == nullptr) { - Game::logger->Log("CDBehaviorParameterTable", "CDBehaviorParameter segment is nullptr!\n"); - } - - // Load the entire table into m_Entries - /*for (auto it = m->begin(); it != m->end(); ++it) { - m_Entries.insert(std::make_pair(it->first, it->second)); - }*/ - } catch (std::exception &e) { - // Not open, we are master - } + return std::make_pair(hash, value); + }, [](int32_t size) { + return 40 * 1000 * 1000; + }, false); } //! Destructor @@ -62,95 +42,10 @@ float CDBehaviorParameterTable::GetEntry(const uint32_t behaviorID, const std::s GeneralUtils::hash_combine(hash, behaviorID); GeneralUtils::hash_combine(hash, name); - // Search for specific parameter - try { - /*boost::interprocess::managed_shared_memory segment(boost::interprocess::open_read_only, "CDBehaviorParameterTable"); - - // If the segment manager is not null, then the segment exists - // Get the table from the segment - ShmemAllocator alloc_inst (segment.get_segment_manager()); - - boost::interprocess::offset_ptr m = segment.find("CDBehaviorParameter").first; - - if (m == nullptr) { - Game::logger->Log("CDBehaviorParameterTable", "CDBehaviorParameter segment is nullptr!\n"); - - return defaultValue; - }*/ - - // Start a timer - auto start = std::chrono::high_resolution_clock::now(); - - auto it = m->find(hash); - - bool found = it != m->end(); - - // End the timer - auto end = std::chrono::high_resolution_clock::now(); - - // Get duraction - auto duration = std::chrono::duration_cast(end - start); - - // Reset the timer - /*start = std::chrono::high_resolution_clock::now(); - - auto nativeIt = m_Entries.find(hash); - - bool nativeFound = nativeIt != m_Entries.end(); - - // End the timer - end = std::chrono::high_resolution_clock::now(); - - // Get duraction - auto nativeDuration = std::chrono::duration_cast(end - start);*/ - - //Game::logger->Log("CDBehaviorParameterTable", "Duration: (sm) %fms / (m) %fms -> %f\n", duration.count() / 1000000.0f, nativeDuration.count() / 1000000.0f, (double) duration.count() / (double) nativeDuration.count()); - - if (found) { - return it->second; - } - - return defaultValue; - } - catch (std::exception &e) { - Game::logger->Log("CDBehaviorParameterTable", "Failed to find entry for behaviorID: %d, parameterID: %s\n%e\n", behaviorID, name.c_str(), e.what()); - - return defaultValue; - } + return BehaviorParameterProvider->GetEntry(hash, defaultValue); } -void CDBehaviorParameterTable::CreateSharedMap() +void CDBehaviorParameterTable::LoadHost() { - boost::interprocess::shared_memory_object::remove("CDBehaviorParameterTable"); - - Game::logger->Log("CDBehaviorParameterTable", "Create shared memory segment.\n"); - - // Create the segment, TODO: calculate size - boost::interprocess::managed_shared_memory segment(boost::interprocess::create_only, "CDBehaviorParameterTable", 40 * 1000 * 1000); - - ShmemAllocator alloc_inst (segment.get_segment_manager()); - - table = segment.construct("CDBehaviorParameter") (std::less(), alloc_inst); - - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorParameter"); - - size_t hash = 0; - - while (!tableData.eof()) { - hash = 0; - - int32_t behaviorID = tableData.getIntField(0, -1); - std::string parameterID = tableData.getStringField(1, ""); - - GeneralUtils::hash_combine(hash, behaviorID); - GeneralUtils::hash_combine(hash, parameterID); - - float value = tableData.getFloatField(2, -1.0f); - - table->insert(ValueType(hash, value)); - - tableData.nextRow(); - } - - tableData.finalize(); + BehaviorParameterProvider->LoadHost(); } diff --git a/dDatabase/Tables/CDBehaviorParameterTable.h b/dDatabase/Tables/CDBehaviorParameterTable.h index fd09e10c..54e35bda 100644 --- a/dDatabase/Tables/CDBehaviorParameterTable.h +++ b/dDatabase/Tables/CDBehaviorParameterTable.h @@ -19,10 +19,8 @@ struct CDBehaviorParameter { //! BehaviorParameter table class CDBehaviorParameterTable : public CDTable { -private: - std::unordered_map m_Entries; public: - static void CreateSharedMap(); + void LoadHost() override; //! Constructor CDBehaviorParameterTable(void); diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.cpp b/dDatabase/Tables/CDBehaviorTemplateTable.cpp index b832400d..bfe54fd2 100644 --- a/dDatabase/Tables/CDBehaviorTemplateTable.cpp +++ b/dDatabase/Tables/CDBehaviorTemplateTable.cpp @@ -1,43 +1,25 @@ #include "CDBehaviorTemplateTable.h" +#include "CDProvider.h" + +CDBehaviorTemplate EmptyBehaviorTemplate {0, 0, 0, 0}; + +CD_PROVIDER(BehaviorTemplateProvider, uint32_t, CDBehaviorTemplate); + //! Constructor CDBehaviorTemplateTable::CDBehaviorTemplateTable(void) { - - // First, get the size of the table - unsigned int size = 0; - auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM BehaviorTemplate"); - while (!tableSize.eof()) { - size = tableSize.getIntField(0, 0); - - tableSize.nextRow(); - } - - tableSize.finalize(); - - // Reserve the size - this->entries.reserve(size); - - // Now get the data - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM BehaviorTemplate"); - while (!tableData.eof()) { + NEW_CD_PROVIDER(BehaviorTemplateProvider, "BehaviorTemplate", [](CppSQLite3Query& query) { CDBehaviorTemplate entry; - entry.behaviorID = tableData.getIntField(0, -1); - entry.templateID = tableData.getIntField(1, -1); - entry.effectID = tableData.getIntField(2, -1); - auto candidateToAdd = tableData.getStringField(3, ""); - auto parameter = m_EffectHandles.find(candidateToAdd); - if (parameter != m_EffectHandles.end()) { - entry.effectHandle = parameter; - } else { - entry.effectHandle = m_EffectHandles.insert(candidateToAdd).first; - } - this->entries.push_back(entry); - this->entriesMappedByBehaviorID.insert(std::make_pair(entry.behaviorID, entry)); - tableData.nextRow(); - } - - tableData.finalize(); + entry.behaviorID = query.getIntField(0, -1); + entry.templateID = query.getIntField(1, -1); + entry.effectID = query.getIntField(2, -1); + entry.effectHandle = CDTable::SetString(query.getStringField(3, "")); + + return std::make_pair(entry.behaviorID, entry); + }, [](int32_t size) { + return 40 * 1000 * 1000; + }, false); } //! Destructor @@ -48,30 +30,10 @@ std::string CDBehaviorTemplateTable::GetName(void) const { return "BehaviorTemplate"; } -//! Queries the table with a custom "where" clause -std::vector CDBehaviorTemplateTable::Query(std::function predicate) { - - std::vector data = cpplinq::from(this->entries) - >> cpplinq::where(predicate) - >> cpplinq::to_vector(); - - return data; +const CDBehaviorTemplate& CDBehaviorTemplateTable::GetByBehaviorID(uint32_t behaviorID) const { + return BehaviorTemplateProvider->GetEntry(behaviorID, EmptyBehaviorTemplate); } -//! Gets all the entries in the table -std::vector CDBehaviorTemplateTable::GetEntries(void) const { - return this->entries; -} - -const CDBehaviorTemplate CDBehaviorTemplateTable::GetByBehaviorID(uint32_t behaviorID) { - auto entry = this->entriesMappedByBehaviorID.find(behaviorID); - if (entry == this->entriesMappedByBehaviorID.end()) { - CDBehaviorTemplate entryToReturn; - entryToReturn.behaviorID = 0; - entryToReturn.effectHandle = m_EffectHandles.end(); - entryToReturn.effectID = 0; - return entryToReturn; - } else { - return entry->second; - } +void CDBehaviorTemplateTable::LoadHost() { + BehaviorTemplateProvider->LoadHost(); } diff --git a/dDatabase/Tables/CDBehaviorTemplateTable.h b/dDatabase/Tables/CDBehaviorTemplateTable.h index 170da854..75af9572 100644 --- a/dDatabase/Tables/CDBehaviorTemplateTable.h +++ b/dDatabase/Tables/CDBehaviorTemplateTable.h @@ -15,17 +15,14 @@ struct CDBehaviorTemplate { unsigned int behaviorID; //!< The Behavior ID unsigned int templateID; //!< The Template ID (LOT) unsigned int effectID; //!< The Effect ID attached - std::unordered_set::iterator effectHandle; //!< The effect handle + size_t effectHandle; //!< The effect handle }; //! BehaviorTemplate table class CDBehaviorTemplateTable : public CDTable { -private: - std::vector entries; - std::unordered_map entriesMappedByBehaviorID; - std::unordered_set m_EffectHandles; public: + void LoadHost() override; //! Constructor CDBehaviorTemplateTable(void); @@ -39,17 +36,5 @@ public: */ std::string GetName(void) const override; - //! Queries the table with a custom "where" clause - /*! - \param predicate The predicate - */ - std::vector Query(std::function predicate); - - //! Gets all the entries in the table - /*! - \return The entries - */ - std::vector GetEntries(void) const; - - const CDBehaviorTemplate GetByBehaviorID(uint32_t behaviorID); + const CDBehaviorTemplate& GetByBehaviorID(const uint32_t behaviorID) const; }; diff --git a/dDatabase/Tables/CDComponentsRegistryTable.cpp b/dDatabase/Tables/CDComponentsRegistryTable.cpp index 7e746fb0..4851b620 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.cpp +++ b/dDatabase/Tables/CDComponentsRegistryTable.cpp @@ -1,58 +1,30 @@ + #include "CDComponentsRegistryTable.h" #define CDCLIENT_CACHE_ALL +#include "CDProvider.h" + +CD_PROVIDER(ComponentsRegistryProvider, size_t, int32_t); + //! Constructor CDComponentsRegistryTable::CDComponentsRegistryTable(void) { - -#ifdef CDCLIENT_CACHE_ALL - // First, get the size of the table - unsigned int size = 0; - auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM ComponentsRegistry"); - while (!tableSize.eof()) { - size = tableSize.getIntField(0, 0); - - tableSize.nextRow(); - } - - tableSize.finalize(); - - // Reserve the size - //this->entries.reserve(size); - - // Now get the data - auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM ComponentsRegistry"); - while (!tableData.eof()) { - CDComponentsRegistry entry; - entry.id = tableData.getIntField(0, -1); - entry.component_type = tableData.getIntField(1, -1); - entry.component_id = tableData.getIntField(2, -1); - - this->mappedEntries.insert_or_assign(((uint64_t) entry.component_type) << 32 | ((uint64_t) entry.id), entry.component_id); - - //this->entries.push_back(entry); - - /* - //Darwin's stuff: - const auto& it = this->mappedEntries.find(entry.id); - if (it != mappedEntries.end()) { - const auto& iter = it->second.find(entry.component_type); - if (iter == it->second.end()) { - it->second.insert(std::make_pair(entry.component_type, entry.component_id)); - } - } - else { - std::map map; - map.insert(std::make_pair(entry.component_type, entry.component_id)); - this->mappedEntries.insert(std::make_pair(entry.id, map)); - } - */ - - tableData.nextRow(); - } - - tableData.finalize(); -#endif + NEW_CD_PROVIDER(ComponentsRegistryProvider, "ComponentsRegistry", [](CppSQLite3Query& query) { + size_t hash = 0; + + int32_t componentID = query.getIntField(0, -1); + int32_t componentType = query.getIntField(1, -1); + + hash = componentID; + hash = hash << 32; + hash |= componentType; + + int32_t componentHandle = query.getIntField(2, -1); + + return std::make_pair(hash, componentHandle); + }, [](int32_t size) { + return 10 * 1000 * 1000; + }, true); } //! Destructor @@ -63,69 +35,15 @@ std::string CDComponentsRegistryTable::GetName(void) const { return "ComponentsRegistry"; } -int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, uint32_t componentType, int32_t defaultValue) -{ - const auto& iter = this->mappedEntries.find(((uint64_t) componentType) << 32 | ((uint64_t) id)); - - if (iter == this->mappedEntries.end()) - { - return defaultValue; - } - - return iter->second; - - /* - const auto& it = this->mappedEntries.find(id); - if (it != mappedEntries.end()) { - const auto& iter = it->second.find(componentType); - if (iter != it->second.end()) { - return iter->second; - } - } - */ - -#ifndef CDCLIENT_CACHE_ALL - // Now get the data - std::stringstream query; - - query << "SELECT * FROM ComponentsRegistry WHERE id = " << std::to_string(id); - - auto tableData = CDClientDatabase::ExecuteQuery(query.str()); - while (!tableData.eof()) { - CDComponentsRegistry entry; - entry.id = tableData.getIntField(0, -1); - entry.component_type = tableData.getIntField(1, -1); - entry.component_id = tableData.getIntField(2, -1); - - //this->entries.push_back(entry); - - //Darwin's stuff: - const auto& it = this->mappedEntries.find(entry.id); - if (it != mappedEntries.end()) { - const auto& iter = it->second.find(entry.component_type); - if (iter == it->second.end()) { - it->second.insert(std::make_pair(entry.component_type, entry.component_id)); - } - } - else { - std::map map; - map.insert(std::make_pair(entry.component_type, entry.component_id)); - this->mappedEntries.insert(std::make_pair(entry.id, map)); - } - - tableData.nextRow(); - } - - tableData.finalize(); - - const auto& it2 = this->mappedEntries.find(id); - if (it2 != mappedEntries.end()) { - const auto& iter = it2->second.find(componentType); - if (iter != it2->second.end()) { - return iter->second; - } - } - - return defaultValue; -#endif +int32_t CDComponentsRegistryTable::GetByIDAndType(uint32_t id, uint32_t componentType, int32_t defaultValue) { + size_t hash = 0; + hash = id; + hash = hash << 32; + hash |= componentType; + + return ComponentsRegistryProvider->GetEntry(hash, defaultValue); +} + +void CDComponentsRegistryTable::LoadHost() { + ComponentsRegistryProvider->LoadHost(); } diff --git a/dDatabase/Tables/CDComponentsRegistryTable.h b/dDatabase/Tables/CDComponentsRegistryTable.h index 4a02bf94..d6758d58 100644 --- a/dDatabase/Tables/CDComponentsRegistryTable.h +++ b/dDatabase/Tables/CDComponentsRegistryTable.h @@ -23,6 +23,7 @@ private: std::map mappedEntries; //id, component_type, component_id public: + void LoadHost() override; //! Constructor CDComponentsRegistryTable(void); diff --git a/dDatabase/Tables/CDTable.cpp b/dDatabase/Tables/CDTable.cpp new file mode 100644 index 00000000..7241ee7f --- /dev/null +++ b/dDatabase/Tables/CDTable.cpp @@ -0,0 +1,70 @@ +#include "CDTable.h" + +#if defined(CD_PROVIDER_MEMORY) + +#include "CDAbstractSharedMemoryMap.h" + +typedef boost::interprocess::allocator CharAllocator; +typedef boost::interprocess::basic_string, CharAllocator> my_string; + +CDAbstractSharedMemoryMap* CDStringMap; + +void CDTable::InitalizeHost() +{ + CDStringMap->LoadHost(); +} + +void CDTable::Initalize() +{ + CDStringMap = new CDAbstractSharedMemoryMap("CDStringMap", 10 * 1000 * 1000); +} + +std::string CDTable::GetString(size_t handle) +{ + std::string str = std::string(CDStringMap->GetEntry(handle, "").c_str()); + + Game::logger->Log("CDTable", "GetString: %s\n", str.c_str()); + + return str; +} + +size_t CDTable::SetString(std::string value) +{ + size_t hash = 0; + + GeneralUtils::hash_combine(hash, value); + + CDStringMap->SetEntry(hash, boost::interprocess::string(value.c_str())); + + return hash; +} + +#else + +std::unordered_map CDStringMap; + +void CDTable::InitalizeHost() +{ +} + +void CDTable::Initalize() +{ +} + +std::string CDTable::GetString(size_t handle) +{ + return CDStringMap[handle]; +} + +size_t CDTable::SetString(std::string value) +{ + size_t hash = 0; + + GeneralUtils::hash_combine(hash, value); + + CDStringMap[hash] = value; + + return hash; +} + +#endif \ No newline at end of file diff --git a/dDatabase/Tables/CDTable.h b/dDatabase/Tables/CDTable.h index ec056e8e..9b6ff18f 100644 --- a/dDatabase/Tables/CDTable.h +++ b/dDatabase/Tables/CDTable.h @@ -38,4 +38,27 @@ public: \return The table name */ virtual std::string GetName() const = 0; + + //! Loads the table into shared memory + virtual void LoadHost() {}; + + //! Initalizes the table services + static void Initalize(); + + //! Initalizes the table services as host + static void InitalizeHost(); + + //! Get a string from a handle + /*! + \param handle The handle to get the string from + \return The string + */ + static std::string GetString(size_t handle); + + //! Set a string + /*! + \param value The string to set + \return The handle to the string + */ + static size_t SetString(std::string value); }; diff --git a/dDatabase/Tables/CMakeLists.txt b/dDatabase/Tables/CMakeLists.txt index b6a02b02..998fc1af 100644 --- a/dDatabase/Tables/CMakeLists.txt +++ b/dDatabase/Tables/CMakeLists.txt @@ -1,4 +1,4 @@ -set(DDATABASE_TABLES_SOURCES "CDActivitiesTable.cpp" +set(DDATABASE_TABLES_SOURCES "CDTable.cpp" "CDActivitiesTable.cpp" "CDActivityRewardsTable.cpp" "CDAnimationsTable.cpp" "CDBehaviorParameterTable.cpp" diff --git a/dGame/dBehaviors/Behavior.cpp b/dGame/dBehaviors/Behavior.cpp index 4a8c6ef7..3d4ea6c2 100644 --- a/dGame/dBehaviors/Behavior.cpp +++ b/dGame/dBehaviors/Behavior.cpp @@ -445,7 +445,7 @@ Behavior::Behavior(const uint32_t behaviorId) this->m_effectId = templateInDatabase.effectID; - this->m_effectHandle = *templateInDatabase.effectHandle != "" ? new std::string(*templateInDatabase.effectHandle) : nullptr; + this->m_effectHandle = new std::string(CDTable::GetString(templateInDatabase.effectHandle)); } diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 74d44ce7..f577ee89 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -1754,6 +1754,40 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } +#ifdef __linux__ + if (chatCommand == "statm" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_DEVELOPER) + { + // Print and format the output of /proc/self/statm + std::ifstream statm("/proc/self/statm"); + + if (!statm.is_open()) + { + ChatPackets::SendSystemMessage(sysAddr, u"Failed to open /proc/self/statm"); + return; + } + + // Scan for the different fields + uint64_t size, resident, share, text, lib, data, dirty; + statm >> size >> resident >> share >> text >> lib >> data >> dirty; + + // Get the page size + size_t pageSize = sysconf(_SC_PAGESIZE); + + // Print the output + ChatPackets::SendSystemMessage( + sysAddr, + u"Size: " + GeneralUtils::to_u16string((float) ((double) size * pageSize / 1.024e6)) + + u"MB\nResident: " + GeneralUtils::to_u16string((float) ((double) resident * pageSize / 1.024e6)) + + u"MB\nShared: " + GeneralUtils::to_u16string((float) ((double) share * pageSize / 1.024e6)) + + u"MB\nText: " + GeneralUtils::to_u16string((float) ((double) text * pageSize / 1.024e6)) + + u"MB\nLibrary: " + GeneralUtils::to_u16string((float) ((double) lib * pageSize / 1.024e6)) + + u"MB\nData: " + GeneralUtils::to_u16string((float) ((double) data * pageSize / 1.024e6)) + + u"MB\nDirty: " + GeneralUtils::to_u16string((float) ((double) dirty * pageSize / 1.024e6)) + + u"MB" + ); + } +#endif + if (chatCommand == "rollloot" && entity->GetGMLevel() >= GAME_MASTER_LEVEL_OPERATOR && args.size() >= 3) { uint32_t lootMatrixIndex = 0; uint32_t targetLot = 0; diff --git a/dMasterServer/InstanceManager.cpp b/dMasterServer/InstanceManager.cpp index 41d34cfc..0ab788fa 100644 --- a/dMasterServer/InstanceManager.cpp +++ b/dMasterServer/InstanceManager.cpp @@ -48,16 +48,28 @@ Instance * InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, L //Start the actual process: #ifdef _WIN32 - std::string cmd = "start ./WorldServer.exe -zone "; + std::string cmd = "start ./WorldServer.exe"; #else std::string cmd; - if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) { - cmd = "sudo ./WorldServer -zone "; + + Game::logger->Log("InstanceManager", "world_command: %s\n", Game::config->GetValue("world_command").c_str()); + + if (!Game::config->GetValue("world_command").empty()) { + cmd = Game::config->GetValue("world_command"); + // Replace %map%_%instance%_%time% with the correct values + cmd = cmd.replace(cmd.find("%map%"), 5, std::to_string(mapID)); + cmd = cmd.replace(cmd.find("%instance%"), 10, std::to_string(instance->GetInstanceID())); + cmd = cmd.replace(cmd.find("%time%"), 5, std::to_string(time(nullptr))); + // Remove any other % + cmd = cmd.replace(cmd.find("%"), 1, ""); + } else if (std::atoi(Game::config->GetValue("use_sudo_world").c_str())) { + cmd = "sudo ./WorldServer"; } else { - cmd = "./WorldServer -zone "; + cmd = "./WorldServer"; } #endif + cmd.append(" -zone "); cmd.append(std::to_string(mapID)); cmd.append(" -port "); cmd.append(std::to_string(port)); @@ -73,6 +85,8 @@ Instance * InstanceManager::GetInstance(LWOMAPID mapID, bool isFriendTransfer, L cmd.append("&"); //Sends our next process to the background on Linux #endif + Game::logger->Log("InstanceManager", "Starting instance %i with command: %s\n", instance->GetInstanceID(), cmd.c_str()); + system(cmd.c_str()); m_Instances.push_back(instance); diff --git a/dMasterServer/MasterServer.cpp b/dMasterServer/MasterServer.cpp index e008036b..89bfad01 100644 --- a/dMasterServer/MasterServer.cpp +++ b/dMasterServer/MasterServer.cpp @@ -226,7 +226,7 @@ int main(int argc, char** argv) { ObjectIDManager::Instance()->Initialize(Game::logger); Game::im = new InstanceManager(Game::logger, Game::server->GetIP()); - CDBehaviorParameterTable::CreateSharedMap(); + CDClientManager::Instance()->LoadHost(); //Depending on the config, start up servers: if (config.GetValue("prestart_servers") != "" && config.GetValue("prestart_servers") == "1") { diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index c2aca466..c288d759 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -35,7 +35,7 @@ void AuthPackets::HandleHandshake(dServer* server, Packet* packet) { void AuthPackets::SendHandshake(dServer* server, const SystemAddress& sysAddr, const std::string& nextServerIP, uint16_t nextServerPort) { RakNet::BitStream bitStream; PacketUtils::WriteHeader(bitStream, SERVER, MSG_SERVER_VERSION_CONFIRM); - bitStream.Write(NET_VERSION); + bitStream.Write(171023); bitStream.Write(uint32_t(0x93)); if (nextServerPort == 1001) bitStream.Write(uint32_t(1)); //Conn: auth