From 8fdc212cda523c8a1377e245fd0a57c63bb1e96b Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 24 Apr 2024 10:09:04 -0500 Subject: [PATCH 1/3] chore: Convert heap allocation to optional (#1553) * chore: Convert heap allocation to optional * Update dGame/dComponents/PetComponent.h Default-initialize --- dGame/dComponents/PetComponent.cpp | 9 ++++----- dGame/dComponents/PetComponent.h | 6 +++--- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 6b809ec0e..68c29a6d5 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -40,7 +40,7 @@ std::unordered_map PetComponent::activePets{}; * Maps all the pet lots to a flag indicating that the player has caught it. All basic pets have been guessed by ObjID * while the faction ones could be checked using their respective missions. */ -std::map PetComponent::petFlags = { +const std::map PetComponent::petFlags{ { 3050, 801 }, // Elephant { 3054, 803 }, // Cat { 3195, 806 }, // Triceratops @@ -87,7 +87,6 @@ PetComponent::PetComponent(Entity* parentEntity, uint32_t componentId) : Compone m_StartPosition = NiPoint3Constant::ZERO; m_MovementAI = nullptr; m_TresureTime = 0; - m_Preconditions = nullptr; std::string checkPreconditions = GeneralUtils::UTF16ToWTF8(parentEntity->GetVar(u"CheckPrecondition")); @@ -158,7 +157,7 @@ void PetComponent::OnUse(Entity* originator) { return; } - if (m_Preconditions != nullptr && !m_Preconditions->Check(originator, true)) { + if (m_Preconditions.has_value() && !m_Preconditions->Check(originator, true)) { return; } @@ -1086,6 +1085,6 @@ void PetComponent::LoadPetNameFromModeration() { } } -void PetComponent::SetPreconditions(std::string& preconditions) { - m_Preconditions = new PreconditionExpression(preconditions); +void PetComponent::SetPreconditions(const std::string& preconditions) { + m_Preconditions = std::make_optional(preconditions); } diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index f4198caef..1abdf1558 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -165,7 +165,7 @@ class PetComponent final : public Component * Sets preconditions for the pet that need to be met before it can be tamed * @param conditions the preconditions to set */ - void SetPreconditions(std::string& conditions); + void SetPreconditions(const std::string& conditions); /** * Returns the entity that this component belongs to @@ -258,7 +258,7 @@ class PetComponent final : public Component /** * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet */ - static std::map petFlags; + static const std::map petFlags; /** * The ID of the component in the pet component table @@ -349,7 +349,7 @@ class PetComponent final : public Component /** * Preconditions that need to be met before an entity can tame this pet */ - PreconditionExpression* m_Preconditions; + std::optional m_Preconditions{}; /** * Pet information loaded from the CDClientDatabase From 0367c67c85f864ac4b49d68de5ff183d53aa8ae8 Mon Sep 17 00:00:00 2001 From: jadebenn Date: Wed, 24 Apr 2024 10:09:15 -0500 Subject: [PATCH 2/3] chore: move the pet minigame table loading logic out of petcomponent (#1551) * move the pet minigame table loading logic out of petcomponent * misc fixes * actually, using paths is dumb here when they're already char strings. why bother? silly me. * removed unga bunga reference-casting * add back in puzzle not found error message * pre-allocate unordered map and make getter const-correct * Update dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.cpp Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> --------- Co-authored-by: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> --- .../CDClientDatabase/CDClientManager.cpp | 16 ++- .../CDClientTables/CDPetComponentTable.cpp | 2 +- .../CDTamingBuildPuzzleTable.cpp | 35 +++++++ .../CDClientTables/CDTamingBuildPuzzleTable.h | 60 ++++++++++++ .../CDClientTables/CMakeLists.txt | 1 + dGame/dComponents/PetComponent.cpp | 98 +++++-------------- dGame/dComponents/PetComponent.h | 5 - 7 files changed, 127 insertions(+), 90 deletions(-) create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.cpp create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.h diff --git a/dDatabase/CDClientDatabase/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp index 0e05c0b83..6ecfb0ad0 100644 --- a/dDatabase/CDClientDatabase/CDClientManager.cpp +++ b/dDatabase/CDClientDatabase/CDClientManager.cpp @@ -25,6 +25,7 @@ #include "CDScriptComponentTable.h" #include "CDSkillBehaviorTable.h" #include "CDZoneTableTable.h" +#include "CDTamingBuildPuzzleTable.h" #include "CDVendorComponentTable.h" #include "CDActivitiesTable.h" #include "CDPackageComponentTable.h" @@ -41,8 +42,6 @@ #include "CDRewardCodesTable.h" #include "CDPetComponentTable.h" -#include - #ifndef CDCLIENT_CACHE_ALL // Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory. // A vanilla CDClient takes about 46MB of memory + the regular world data. @@ -55,13 +54,6 @@ #define CDCLIENT_DONT_CACHE_TABLE(x) #endif -class CDClientConnectionException : public std::exception { -public: - virtual const char* what() const throw() { - return "CDClientDatabase is not connected!"; - } -}; - // Using a macro to reduce repetitive code and issues from copy and paste. // As a note, ## in a macro is used to concatenate two tokens together. @@ -108,11 +100,14 @@ DEFINE_TABLE_STORAGE(CDRewardCodesTable); DEFINE_TABLE_STORAGE(CDRewardsTable); DEFINE_TABLE_STORAGE(CDScriptComponentTable); DEFINE_TABLE_STORAGE(CDSkillBehaviorTable); +DEFINE_TABLE_STORAGE(CDTamingBuildPuzzleTable); DEFINE_TABLE_STORAGE(CDVendorComponentTable); DEFINE_TABLE_STORAGE(CDZoneTableTable); void CDClientManager::LoadValuesFromDatabase() { - if (!CDClientDatabase::isConnected) throw CDClientConnectionException(); + if (!CDClientDatabase::isConnected) { + throw std::runtime_error{ "CDClientDatabase is not connected!" }; + } CDActivityRewardsTable::Instance().LoadValuesFromDatabase(); CDActivitiesTable::Instance().LoadValuesFromDatabase(); @@ -152,6 +147,7 @@ void CDClientManager::LoadValuesFromDatabase() { CDRewardsTable::Instance().LoadValuesFromDatabase(); CDScriptComponentTable::Instance().LoadValuesFromDatabase(); CDSkillBehaviorTable::Instance().LoadValuesFromDatabase(); + CDTamingBuildPuzzleTable::Instance().LoadValuesFromDatabase(); CDVendorComponentTable::Instance().LoadValuesFromDatabase(); CDZoneTableTable::Instance().LoadValuesFromDatabase(); } diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp index f3371ecb8..80c101122 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp +++ b/dDatabase/CDClientDatabase/CDClientTables/CDPetComponentTable.cpp @@ -50,7 +50,7 @@ void CDPetComponentTable::LoadValuesFromDatabase() { } void CDPetComponentTable::LoadValuesFromDefaults() { - GetEntriesMutable().insert(std::make_pair(defaultEntry.id, defaultEntry)); + GetEntriesMutable().emplace(defaultEntry.id, defaultEntry); } CDPetComponent& CDPetComponentTable::GetByID(const uint32_t componentID) { diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.cpp new file mode 100644 index 000000000..c2301b33a --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.cpp @@ -0,0 +1,35 @@ +#include "CDTamingBuildPuzzleTable.h" + +void CDTamingBuildPuzzleTable::LoadValuesFromDatabase() { + // First, get the size of the table + uint32_t size = 0; + auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM TamingBuildPuzzles"); + while (!tableSize.eof()) { + size = tableSize.getIntField(0, 0); + tableSize.nextRow(); + } + + // Reserve the size + auto& entries = GetEntriesMutable(); + entries.reserve(size); + + // Now get the data + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM TamingBuildPuzzles"); + while (!tableData.eof()) { + const auto lot = static_cast(tableData.getIntField("NPCLot", LOT_NULL)); + entries.emplace(lot, CDTamingBuildPuzzle{ + .puzzleModelLot = lot, + .validPieces{ tableData.getStringField("ValidPiecesLXF") }, + .timeLimit = static_cast(tableData.getFloatField("Timelimit", 30.0f)), + .numValidPieces = tableData.getIntField("NumValidPieces", 6), + .imaginationCost = tableData.getIntField("imagCostPerBuild", 10) + }); + tableData.nextRow(); + } +} + +const CDTamingBuildPuzzle* CDTamingBuildPuzzleTable::GetByLOT(const LOT lot) const { + const auto& entries = GetEntries(); + const auto itr = entries.find(lot); + return itr != entries.cend() ? &itr->second : nullptr; +} diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.h new file mode 100644 index 000000000..acbd65bfa --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDTamingBuildPuzzleTable.h @@ -0,0 +1,60 @@ +#pragma once +#include "CDTable.h" + +/** + * Information for the minigame to be completed + */ +struct CDTamingBuildPuzzle { + UNUSED_COLUMN(uint32_t id = 0;) + + // The LOT of the object that is to be created + LOT puzzleModelLot = LOT_NULL; + + // The LOT of the NPC + UNUSED_COLUMN(LOT npcLot = LOT_NULL;) + + // The .lxfml file that contains the bricks required to build the model + std::string validPieces{}; + + // The .lxfml file that contains the bricks NOT required to build the model + UNUSED_COLUMN(std::string invalidPieces{};) + + // Difficulty value + UNUSED_COLUMN(int32_t difficulty = 1;) + + // The time limit to complete the build + float timeLimit = 30.0f; + + // The number of pieces required to complete the minigame + int32_t numValidPieces = 6; + + // Number of valid pieces + UNUSED_COLUMN(int32_t totalNumPieces = 16;) + + // Model name + UNUSED_COLUMN(std::string modelName{};) + + // The .lxfml file that contains the full model + UNUSED_COLUMN(std::string fullModel{};) + + // The duration of the pet taming minigame + UNUSED_COLUMN(float duration = 45.0f;) + + // The imagination cost for the tamer to start the minigame + int32_t imaginationCost = 10; +}; + +class CDTamingBuildPuzzleTable : public CDTable> { +public: + /** + * Load values from the CD client database + */ + void LoadValuesFromDatabase(); + + /** + * Gets the pet ability table corresponding to the pet LOT + * @returns A pointer to the corresponding table, or nullptr if one cannot be found + */ + [[nodiscard]] + const CDTamingBuildPuzzle* GetByLOT(const LOT lot) const; +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt index af401db29..f45516463 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt +++ b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt @@ -36,5 +36,6 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp" "CDRewardsTable.cpp" "CDScriptComponentTable.cpp" "CDSkillBehaviorTable.cpp" + "CDTamingBuildPuzzleTable.cpp" "CDVendorComponentTable.cpp" "CDZoneTableTable.cpp" PARENT_SCOPE) diff --git a/dGame/dComponents/PetComponent.cpp b/dGame/dComponents/PetComponent.cpp index 68c29a6d5..debe0bd82 100644 --- a/dGame/dComponents/PetComponent.cpp +++ b/dGame/dComponents/PetComponent.cpp @@ -2,6 +2,7 @@ #include "GameMessages.h" #include "BrickDatabase.h" #include "CDClientDatabase.h" +#include "CDTamingBuildPuzzleTable.h" #include "ChatPackets.h" #include "EntityManager.h" #include "Character.h" @@ -32,7 +33,6 @@ #include "eMissionState.h" #include "dNavMesh.h" -std::unordered_map PetComponent::buildCache{}; std::unordered_map PetComponent::currentActivities{}; std::unordered_map PetComponent::activePets{}; @@ -151,8 +151,7 @@ void PetComponent::OnUse(Entity* originator) { m_Tamer = LWOOBJID_EMPTY; } - auto* inventoryComponent = originator->GetComponent(); - + auto* const inventoryComponent = originator->GetComponent(); if (inventoryComponent == nullptr) { return; } @@ -161,86 +160,44 @@ void PetComponent::OnUse(Entity* originator) { return; } - auto* movementAIComponent = m_Parent->GetComponent(); - + auto* const movementAIComponent = m_Parent->GetComponent(); if (movementAIComponent != nullptr) { movementAIComponent->Stop(); } inventoryComponent->DespawnPet(); - const auto& cached = buildCache.find(m_Parent->GetLOT()); - int32_t imaginationCost = 0; - - std::string buildFile; - - if (cached == buildCache.end()) { - auto query = CDClientDatabase::CreatePreppedStmt( - "SELECT ValidPiecesLXF, PuzzleModelLot, Timelimit, NumValidPieces, imagCostPerBuild FROM TamingBuildPuzzles WHERE NPCLot = ?;"); - query.bind(1, static_cast(m_Parent->GetLOT())); - - auto result = query.execQuery(); - - if (result.eof()) { - ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to find the puzzle minigame for this pet."); - - return; - } - - if (result.fieldIsNull("ValidPiecesLXF")) { - result.finalize(); - - return; - } - - buildFile = std::string(result.getStringField("ValidPiecesLXF")); - - PetPuzzleData data; - data.buildFile = buildFile; - data.puzzleModelLot = result.getIntField("PuzzleModelLot"); - data.timeLimit = result.getFloatField("Timelimit"); - data.numValidPieces = result.getIntField("NumValidPieces"); - data.imaginationCost = result.getIntField("imagCostPerBuild"); - if (data.timeLimit <= 0) data.timeLimit = 60; - imaginationCost = data.imaginationCost; - - buildCache[m_Parent->GetLOT()] = data; - - result.finalize(); - } else { - buildFile = cached->second.buildFile; - imaginationCost = cached->second.imaginationCost; + const auto* const entry = CDClientManager::GetTable()->GetByLOT(m_Parent->GetLOT()); + if (!entry) { + ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to find the puzzle minigame for this pet."); + return; } - auto* destroyableComponent = originator->GetComponent(); - + const auto* const destroyableComponent = originator->GetComponent(); if (destroyableComponent == nullptr) { return; } - auto imagination = destroyableComponent->GetImagination(); - - if (imagination < imaginationCost) { + const auto imagination = destroyableComponent->GetImagination(); + if (imagination < entry->imaginationCost) { return; } - const auto& bricks = BrickDatabase::GetBricks(buildFile); - + const auto& bricks = BrickDatabase::GetBricks(entry->validPieces); if (bricks.empty()) { ChatPackets::SendSystemMessage(originator->GetSystemAddress(), u"Failed to load the puzzle minigame for this pet."); - LOG("Couldn't find %s for minigame!", buildFile.c_str()); + LOG("Couldn't find %s for minigame!", entry->validPieces.c_str()); return; } - auto petPosition = m_Parent->GetPosition(); + const auto petPosition = m_Parent->GetPosition(); - auto originatorPosition = originator->GetPosition(); + const auto originatorPosition = originator->GetPosition(); m_Parent->SetRotation(NiQuaternion::LookAt(petPosition, originatorPosition)); float interactionDistance = m_Parent->GetVar(u"interaction_distance"); - if (interactionDistance <= 0) { interactionDistance = 15; } @@ -476,9 +433,8 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { return; } - const auto& cached = buildCache.find(m_Parent->GetLOT()); - - if (cached == buildCache.end()) return; + const auto* const entry = CDClientManager::GetTable()->GetByLOT(m_Parent->GetLOT()); + if (!entry) return; auto* destroyableComponent = tamer->GetComponent(); @@ -486,14 +442,14 @@ void PetComponent::TryBuild(uint32_t numBricks, bool clientFailed) { auto imagination = destroyableComponent->GetImagination(); - imagination -= cached->second.imaginationCost; + imagination -= entry->imaginationCost; destroyableComponent->SetImagination(imagination); Game::entityManager->SerializeEntity(tamer); if (clientFailed) { - if (imagination < cached->second.imaginationCost) { + if (imagination < entry->imaginationCost) { ClientFailTamingMinigame(); } } else { @@ -516,17 +472,14 @@ void PetComponent::NotifyTamingBuildSuccess(NiPoint3 position) { return; } - const auto& cached = buildCache.find(m_Parent->GetLOT()); - - if (cached == buildCache.end()) { - return; - } + const auto* const entry = CDClientManager::GetTable()->GetByLOT(m_Parent->GetLOT()); + if (!entry) return; GameMessages::SendPlayFXEffect(tamer, -1, u"petceleb", "", LWOOBJID_EMPTY, 1, 1, true); RenderComponent::PlayAnimation(tamer, u"rebuild-celebrate"); EntityInfo info{}; - info.lot = cached->second.puzzleModelLot; + info.lot = entry->puzzleModelLot; info.pos = position; info.rot = NiQuaternionConstant::IDENTITY; info.spawnerID = tamer->GetObjectID(); @@ -730,13 +683,10 @@ void PetComponent::ClientExitTamingMinigame(bool voluntaryExit) { } void PetComponent::StartTimer() { - const auto& cached = buildCache.find(m_Parent->GetLOT()); - - if (cached == buildCache.end()) { - return; - } + const auto* const entry = CDClientManager::GetTable()->GetByLOT(m_Parent->GetLOT()); + if (!entry) return; - m_Timer = cached->second.timeLimit; + m_Timer = entry->timeLimit; } void PetComponent::ClientFailTamingMinigame() { diff --git a/dGame/dComponents/PetComponent.h b/dGame/dComponents/PetComponent.h index 1abdf1558..323330d2e 100644 --- a/dGame/dComponents/PetComponent.h +++ b/dGame/dComponents/PetComponent.h @@ -250,11 +250,6 @@ class PetComponent final : public Component */ static std::unordered_map currentActivities; - /** - * Cache of all the minigames and their information from the database - */ - static std::unordered_map buildCache; - /** * Flags that indicate that a player has tamed a pet, indexed by the LOT of the pet */ From 3801a9772262c648600e16c8e08930345841675d Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Wed, 24 Apr 2024 21:35:45 -0500 Subject: [PATCH 3/3] feat: add nlohmann/json lib (#1552) * feat: add nlohmann/json lib * remove build test off --- CMakeLists.txt | 3 ++- cmake/FindJSON.cmake | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 cmake/FindJSON.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index aa5171822..ed1a46f17 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -96,6 +96,7 @@ set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) find_package(MariaDB) +find_package(JSON) # Create a /resServer directory make_directory(${CMAKE_BINARY_DIR}/resServer) @@ -284,7 +285,7 @@ add_subdirectory(dPhysics) add_subdirectory(dServer) # Create a list of common libraries shared between all binaries -set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum") +set(COMMON_LIBRARIES "dCommon" "dDatabase" "dNet" "raknet" "MariaDB::ConnCpp" "magic_enum" "nlohmann_json::nlohmann_json") # Add platform specific common libraries if(UNIX) diff --git a/cmake/FindJSON.cmake b/cmake/FindJSON.cmake new file mode 100644 index 000000000..291ebcd35 --- /dev/null +++ b/cmake/FindJSON.cmake @@ -0,0 +1,20 @@ +include(FetchContent) + +message(STATUS "Fetching json...") + +FetchContent_Declare( + json + GIT_REPOSITORY https://github.com/nlohmann/json + GIT_TAG v3.11.3 +) + +FetchContent_GetProperties(json) +if(NOT json_POPULATED) + FetchContent_Populate(json) + add_subdirectory(${json_SOURCE_DIR} ${json_BINARY_DIR} EXCLUDE_FROM_ALL) +endif() + +FetchContent_MakeAvailable(json) + +message(STATUS "json fetched and is now ready.") +set(JSON_FOUND TRUE)