From 0c4108e730bf54d78fe4c466527177c5de225e0a Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 03:36:29 -0700 Subject: [PATCH] Add loading from database yahoo --- dDatabase/GameDatabase/ITables/IBehaviors.h | 5 +-- .../GameDatabase/ITables/IPropertyContents.h | 4 +-- dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 7 ++-- .../GameDatabase/MySQL/Tables/Behaviors.cpp | 7 +++- .../MySQL/Tables/PropertyContents.cpp | 13 +++++-- dGame/Entity.cpp | 4 +-- dGame/dComponents/ModelComponent.cpp | 33 +++++++++++++++-- dGame/dComponents/ModelComponent.h | 4 ++- .../PropertyManagementComponent.cpp | 13 ++++++- .../ControlBehaviorMessages/Action.cpp | 35 +++++++++++++++++-- .../ControlBehaviorMessages/Action.h | 1 + .../StripUiPosition.cpp | 5 +++ .../ControlBehaviorMessages/StripUiPosition.h | 1 + dGame/dPropertyBehaviors/PropertyBehavior.cpp | 14 ++++++++ dGame/dPropertyBehaviors/PropertyBehavior.h | 1 + dGame/dPropertyBehaviors/State.cpp | 7 ++++ dGame/dPropertyBehaviors/State.h | 1 + dGame/dPropertyBehaviors/Strip.cpp | 12 +++++++ dGame/dPropertyBehaviors/Strip.h | 1 + 19 files changed, 149 insertions(+), 19 deletions(-) diff --git a/dDatabase/GameDatabase/ITables/IBehaviors.h b/dDatabase/GameDatabase/ITables/IBehaviors.h index 7e42869d..6b0f17b1 100644 --- a/dDatabase/GameDatabase/ITables/IBehaviors.h +++ b/dDatabase/GameDatabase/ITables/IBehaviors.h @@ -8,14 +8,15 @@ class IBehaviors { public: struct Info { - LWOOBJID behaviorId{}; + int32_t behaviorId{}; uint32_t characterId{}; std::string behaviorInfo; }; // This Add also takes care of updating if it exists. virtual void AddBehavior(const Info& info) = 0; - virtual void RemoveBehavior(const uint32_t behaviorId) = 0; + virtual std::string GetBehavior(const int32_t behaviorId) = 0; + virtual void RemoveBehavior(const int32_t behaviorId) = 0; }; #endif //!__IBEHAVIORS__H__ diff --git a/dDatabase/GameDatabase/ITables/IPropertyContents.h b/dDatabase/GameDatabase/ITables/IPropertyContents.h index 25271302..dda8fc11 100644 --- a/dDatabase/GameDatabase/ITables/IPropertyContents.h +++ b/dDatabase/GameDatabase/ITables/IPropertyContents.h @@ -16,7 +16,7 @@ public: LWOOBJID id{}; LOT lot{}; uint32_t ugcId{}; - std::array behaviors{}; + std::array behaviors{}; }; // Inserts a new UGC model into the database. @@ -33,7 +33,7 @@ public: virtual void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) = 0; // Update the model position and rotation for the given property id. - virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) = 0; + virtual void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) = 0; // Remove the model for the given property id. virtual void RemoveModel(const LWOOBJID& modelId) = 0; diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index 29df611c..689622d0 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -74,7 +74,7 @@ public: std::vector GetPropertyModels(const LWOOBJID& propertyId) override; void RemoveUnreferencedUgcModels() override; void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) override; - void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) override; + void UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) override; void RemoveModel(const LWOOBJID& modelId) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void InsertNewBugReport(const IBugReports::Info& info) override; @@ -108,8 +108,9 @@ public: std::vector GetIgnoreList(const uint32_t playerId) override; void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; std::vector GetRewardCodesByAccountID(const uint32_t account_id) override; - void AddBehavior(const IBehaviors::Info& info) override; - void RemoveBehavior(const uint32_t characterId) override; + void AddBehavior(const IBehaviors::Info& info) override; + std::string GetBehavior(const int32_t behaviorId) override; + void RemoveBehavior(const int32_t characterId) override; private: // Generic query functions that can be used for any query. diff --git a/dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp b/dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp index 91bcbd81..f4647865 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp @@ -9,6 +9,11 @@ void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) { ); } -void MySQLDatabase::RemoveBehavior(const uint32_t behaviorId) { +void MySQLDatabase::RemoveBehavior(const int32_t behaviorId) { ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId); } + +std::string MySQLDatabase::GetBehavior(const int32_t behaviorId) { + auto result = ExecuteSelect("SELECT behavior_info FROM behaviors WHERE behavior_id = ?", behaviorId); + return result->next() ? result->getString("behavior_info").c_str() : ""; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp index 4e36e4f6..05998785 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp @@ -1,7 +1,10 @@ #include "MySQLDatabase.h" std::vector MySQLDatabase::GetPropertyModels(const LWOOBJID& propertyId) { - auto result = ExecuteSelect("SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id FROM properties_contents WHERE property_id = ?;", propertyId); + auto result = ExecuteSelect( + "SELECT id, lot, x, y, z, rx, ry, rz, rw, ugc_id, " + "behavior_1, behavior_2, behavior_3, behavior_4, behavior_5 " + "FROM properties_contents WHERE property_id = ?;", propertyId); std::vector toReturn; toReturn.reserve(result->rowsCount()); @@ -17,6 +20,12 @@ std::vector MySQLDatabase::GetPropertyModels(const LWO model.rotation.y = result->getFloat("ry"); model.rotation.z = result->getFloat("rz"); model.ugcId = result->getUInt64("ugc_id"); + model.behaviors[0] = result->getInt("behavior_1"); + model.behaviors[1] = result->getInt("behavior_2"); + model.behaviors[2] = result->getInt("behavior_3"); + model.behaviors[3] = result->getInt("behavior_4"); + model.behaviors[4] = result->getInt("behavior_5"); + toReturn.push_back(std::move(model)); } return toReturn; @@ -43,7 +52,7 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr } } -void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) { +void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation, const std::array, 5>& behaviors) { ExecuteUpdate( "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, " "behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;", diff --git a/dGame/Entity.cpp b/dGame/Entity.cpp index 2210403c..6699c595 100644 --- a/dGame/Entity.cpp +++ b/dGame/Entity.cpp @@ -225,7 +225,7 @@ void Entity::Initialize() { AddComponent(simplePhysicsComponentID); - AddComponent(); + AddComponent()->LoadBehaviors(); AddComponent(); @@ -649,7 +649,7 @@ void Entity::Initialize() { } if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent()) { - AddComponent(); + AddComponent()->LoadBehaviors(); if (!HasComponent(eReplicaComponentType::DESTROYABLE)) { auto* destroyableComponent = AddComponent(); destroyableComponent->SetHealth(1); diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index 91e31e39..733b17ee 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -8,6 +8,8 @@ #include "ControlBehaviorMsgs.h" #include "tinyxml2.h" +#include "Database.h" + ModelComponent::ModelComponent(Entity* parent) : Component(parent) { m_OriginalPosition = m_Parent->GetDefaultPosition(); m_OriginalRotation = m_Parent->GetDefaultRotation(); @@ -15,6 +17,31 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) { m_userModelID = m_Parent->GetVarAs(u"userModelID"); } +void ModelComponent::LoadBehaviors() { + auto behaviors = GeneralUtils::SplitString(m_Parent->GetVar(u"userModelBehaviors"), ','); + for (const auto& behavior : behaviors) { + if (behavior.empty()) continue; + const auto behaviorId = GeneralUtils::TryParse(behavior); + if (!behaviorId.has_value() || behaviorId.value() == 0) continue; + LOG("Loading behavior %d", behaviorId.value()); + auto& inserted = m_Behaviors.emplace_back(); + inserted.SetBehaviorId(*behaviorId); + + const auto behaviorStr = Database::Get()->GetBehavior(behaviorId.value()); + + tinyxml2::XMLDocument behaviorXml; + auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size()); + LOG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str()); + + const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior"); + if (!behaviorRoot) { + LOG("Failed to load behavior %d due to missing behavior root", behaviorId.value()); + continue; + } + inserted.Deserialize(*behaviorRoot); + } +} + void ModelComponent::Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) { // ItemComponent Serialization. Pets do not get this serialization. if (!m_Parent->HasComponent(eReplicaComponentType::PET)) { @@ -74,8 +101,8 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) { // TODO move to the inventory } -std::array, 5> ModelComponent::GetBehaviorsForSave() const { - std::array, 5> toReturn; +std::array, 5> ModelComponent::GetBehaviorsForSave() const { + std::array, 5> toReturn{}; for (auto i = 0; i < m_Behaviors.size(); i++) { const auto& behavior = m_Behaviors.at(i); if (behavior.GetBehaviorId() == -1) continue; @@ -86,7 +113,7 @@ std::array, 5> ModelComponent::GetBehaviorsForS behavior.Serialize(*root); doc.InsertFirstChild(root); - tinyxml2::XMLPrinter printer(0, false, 0); + tinyxml2::XMLPrinter printer(0, true, 0); doc.Print(&printer); behaviorData = printer.CStr(); } diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index 5b76708f..ba225426 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -28,6 +28,8 @@ public: ModelComponent(Entity* parent); + void LoadBehaviors(); + void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override; /** @@ -109,7 +111,7 @@ public: void VerifyBehaviors(); - std::array, 5> GetBehaviorsForSave() const; + std::array, 5> GetBehaviorsForSave() const; private: /** diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index 6c393df1..ae046a26 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -595,6 +595,17 @@ void PropertyManagementComponent::Load() { settings.push_back(new LDFData(u"componentWhitelist", 1)); } + std::ostringstream userModelBehavior; + bool firstAdded = false; + for (const auto& behavior : databaseModel.behaviors) { + if (behavior == LWOOBJID_EMPTY) continue; + if (firstAdded) userModelBehavior << ","; + userModelBehavior << behavior; + firstAdded = true; + } + + settings.push_back(new LDFData(u"userModelBehaviors", userModelBehavior.str())); + node->config = settings; const auto spawnerId = Game::zoneManager->MakeSpawner(info); @@ -638,7 +649,7 @@ void PropertyManagementComponent::Save() { // save the behaviors of the model for (const auto& [behaviorId, behaviorStr] : modelBehaviors) { - if (behaviorStr.empty() || behaviorId == LWOOBJID_EMPTY) continue; + if (behaviorStr.empty() || behaviorId == -1 || behaviorId == 0) continue; IBehaviors::Info info { .behaviorId = behaviorId, .characterId = character->GetID(), diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp index 4c917b48..6a21be9b 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp @@ -40,9 +40,40 @@ void Action::Serialize(tinyxml2::XMLElement& action) const { if (m_ValueParameterName.empty()) return; + action.SetAttribute("ValueParameterName", m_ValueParameterName.c_str()); + if (m_ValueParameterName == "Message") { - action.SetAttribute(m_ValueParameterName.c_str(), m_ValueParameterString.c_str()); + action.SetAttribute("Value", m_ValueParameterString.c_str()); } else { - action.SetAttribute(m_ValueParameterName.c_str(), m_ValueParameterDouble); + action.SetAttribute("Value", m_ValueParameterDouble); + } +} + +void Action::Deserialize(const tinyxml2::XMLElement& action) { + const char* type = nullptr; + action.QueryAttribute("Type", &type); + if (!type) { + LOG("No type found for an action?"); + return; + } + + m_Type = type; + + const char* valueParameterName = nullptr; + action.QueryAttribute("ValueParameterName", &valueParameterName); + if (valueParameterName) { + m_ValueParameterName = valueParameterName; + + if (m_ValueParameterName == "Message") { + const char* value = nullptr; + action.QueryAttribute("Value", &value); + if (value) { + m_ValueParameterString = value; + } else { + LOG("No value found for an action message?"); + } + } else { + action.QueryDoubleAttribute("Value", &m_ValueParameterDouble); + } } } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h index 2f618316..8146e08d 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h @@ -25,6 +25,7 @@ public: void SendBehaviorBlocksToClient(AMFArrayValue& args) const; void Serialize(tinyxml2::XMLElement& action) const; + void Deserialize(const tinyxml2::XMLElement& action); private: double m_ValueParameterDouble{ 0.0 }; std::string m_Type{ "" }; diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp index 02f64879..ae153a5f 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp @@ -27,3 +27,8 @@ void StripUiPosition::Serialize(tinyxml2::XMLElement& position) const { position.SetAttribute("x", m_XPosition); position.SetAttribute("y", m_YPosition); } + +void StripUiPosition::Deserialize(const tinyxml2::XMLElement& position) { + position.QueryDoubleAttribute("x", &m_XPosition); + position.QueryDoubleAttribute("y", &m_YPosition); +} diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h index 7bc2087e..47501ff7 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h @@ -20,6 +20,7 @@ public: [[nodiscard]] double GetY() const noexcept { return m_YPosition; } void Serialize(tinyxml2::XMLElement& position) const; + void Deserialize(const tinyxml2::XMLElement& position); private: double m_XPosition{ 0.0 }; double m_YPosition{ 0.0 }; diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.cpp b/dGame/dPropertyBehaviors/PropertyBehavior.cpp index 278012ea..5bdb5827 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.cpp +++ b/dGame/dPropertyBehaviors/PropertyBehavior.cpp @@ -139,3 +139,17 @@ void PropertyBehavior::Serialize(tinyxml2::XMLElement& behavior) const { state.Serialize(*stateElement); } } + + +void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) { + m_Name = behavior.Attribute("name"); + behavior.QueryBoolAttribute("isLocked", &isLocked); + behavior.QueryBoolAttribute("isLoot", &isLoot); + + for (const auto* stateElement = behavior.FirstChildElement("State"); stateElement; stateElement = stateElement->NextSiblingElement("State")) { + int32_t stateId = -1; + stateElement->QueryIntAttribute("id", &stateId); + if (stateId < 0 || stateId > 5) continue; + m_States[static_cast(stateId)].Deserialize(*stateElement); + } +} diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.h b/dGame/dPropertyBehaviors/PropertyBehavior.h index 50585f98..01eb1968 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.h +++ b/dGame/dPropertyBehaviors/PropertyBehavior.h @@ -30,6 +30,7 @@ public: void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; } void Serialize(tinyxml2::XMLElement& behavior) const; + void Deserialize(const tinyxml2::XMLElement& behavior); private: // The states this behavior has. diff --git a/dGame/dPropertyBehaviors/State.cpp b/dGame/dPropertyBehaviors/State.cpp index 9b321bad..1fb072c1 100644 --- a/dGame/dPropertyBehaviors/State.cpp +++ b/dGame/dPropertyBehaviors/State.cpp @@ -145,3 +145,10 @@ void State::Serialize(tinyxml2::XMLElement& state) const { strip.Serialize(*stripElement); } } + +void State::Deserialize(const tinyxml2::XMLElement& state) { + for (const auto* stripElement = state.FirstChildElement("Strip"); stripElement; stripElement = stripElement->NextSiblingElement("Strip")) { + auto& strip = m_Strips.emplace_back(); + strip.Deserialize(*stripElement); + } +} diff --git a/dGame/dPropertyBehaviors/State.h b/dGame/dPropertyBehaviors/State.h index e866b196..3e8c827f 100644 --- a/dGame/dPropertyBehaviors/State.h +++ b/dGame/dPropertyBehaviors/State.h @@ -18,6 +18,7 @@ public: bool IsEmpty() const; void Serialize(tinyxml2::XMLElement& state) const; + void Deserialize(const tinyxml2::XMLElement& state); private: std::vector m_Strips; }; diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index 8271a240..d6dc050b 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -94,3 +94,15 @@ void Strip::Serialize(tinyxml2::XMLElement& strip) const { action.Serialize(*actionElement); } } + +void Strip::Deserialize(const tinyxml2::XMLElement& strip) { + const auto* positionElement = strip.FirstChildElement("Position"); + if (positionElement) { + m_Position.Deserialize(*positionElement); + } + + for (const auto* actionElement = strip.FirstChildElement("Action"); actionElement; actionElement = actionElement->NextSiblingElement("Action")) { + auto& action = m_Actions.emplace_back(); + action.Deserialize(*actionElement); + } +} diff --git a/dGame/dPropertyBehaviors/Strip.h b/dGame/dPropertyBehaviors/Strip.h index d8c6b9cf..8fd7d0fe 100644 --- a/dGame/dPropertyBehaviors/Strip.h +++ b/dGame/dPropertyBehaviors/Strip.h @@ -21,6 +21,7 @@ public: bool IsEmpty() const noexcept { return m_Actions.empty(); } void Serialize(tinyxml2::XMLElement& strip) const; + void Deserialize(const tinyxml2::XMLElement& strip); private: std::vector m_Actions; StripUiPosition m_Position;