From f2bf9a2a28fe76603a204482eee945416cac3cab Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 02:05:55 -0700 Subject: [PATCH 1/8] Saving to database working --- dCommon/Logger.cpp | 4 +-- dDatabase/GameDatabase/GameDatabase.h | 4 ++- dDatabase/GameDatabase/ITables/IBehaviors.h | 20 ++++++++++++++ .../GameDatabase/ITables/IPropertyContents.h | 3 ++- dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 4 ++- .../GameDatabase/MySQL/Tables/Behaviors.cpp | 14 ++++++++++ .../GameDatabase/MySQL/Tables/CMakeLists.txt | 1 + .../MySQL/Tables/PropertyContents.cpp | 18 +++++++------ dGame/Character.cpp | 4 +-- .../ControllablePhysicsComponent.cpp | 4 +-- dGame/dComponents/ModelComponent.cpp | 20 ++++++++++++++ dGame/dComponents/ModelComponent.h | 2 ++ .../PropertyManagementComponent.cpp | 27 ++++++++++++++++++- .../ControlBehaviorMessages/Action.cpp | 14 ++++++++++ .../ControlBehaviorMessages/Action.h | 5 ++++ .../StripUiPosition.cpp | 6 +++++ .../ControlBehaviorMessages/StripUiPosition.h | 5 ++++ dGame/dPropertyBehaviors/PropertyBehavior.cpp | 15 +++++++++++ dGame/dPropertyBehaviors/PropertyBehavior.h | 5 ++++ dGame/dPropertyBehaviors/State.cpp | 12 ++++++++- dGame/dPropertyBehaviors/State.h | 5 ++++ dGame/dPropertyBehaviors/Strip.cpp | 12 ++++++++- dGame/dPropertyBehaviors/Strip.h | 5 ++++ migrations/dlu/15_behavior_owner.sql | 3 +++ 24 files changed, 192 insertions(+), 20 deletions(-) create mode 100644 dDatabase/GameDatabase/ITables/IBehaviors.h create mode 100644 dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp create mode 100644 migrations/dlu/15_behavior_owner.sql diff --git a/dCommon/Logger.cpp b/dCommon/Logger.cpp index 1888f1eb..571b9661 100644 --- a/dCommon/Logger.cpp +++ b/dCommon/Logger.cpp @@ -53,8 +53,8 @@ void Logger::vLog(const char* format, va_list args) { struct tm* time = localtime(&t); char timeStr[70]; strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time); - char message[2048]; - vsnprintf(message, 2048, format, args); + char message[131072]; + vsnprintf(message, 131072, format, args); for (const auto& writer : m_Writers) { writer->Log(timeStr, message); } diff --git a/dDatabase/GameDatabase/GameDatabase.h b/dDatabase/GameDatabase/GameDatabase.h index bcd8550b..f52c8c4e 100644 --- a/dDatabase/GameDatabase/GameDatabase.h +++ b/dDatabase/GameDatabase/GameDatabase.h @@ -23,6 +23,7 @@ #include "IActivityLog.h" #include "IIgnoreList.h" #include "IAccountsRewardCodes.h" +#include "IBehaviors.h" namespace sql { class Statement; @@ -40,7 +41,8 @@ class GameDatabase : public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports, public IPropertyContents, public IProperty, public IPetNames, public ICharXml, public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, - public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList { + public IAccounts, public IActivityLog, public IAccountsRewardCodes, public IIgnoreList, + public IBehaviors { public: virtual ~GameDatabase() = default; // TODO: These should be made private. diff --git a/dDatabase/GameDatabase/ITables/IBehaviors.h b/dDatabase/GameDatabase/ITables/IBehaviors.h new file mode 100644 index 00000000..02b5178b --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IBehaviors.h @@ -0,0 +1,20 @@ +#ifndef __IBEHAVIORS__H__ +#define __IBEHAVIORS__H__ + +#include + +#include "dCommonVars.h" + +class IBehaviors { +public: + struct Info { + LWOOBJID behaviorId{}; + uint32_t characterId{}; + std::string behaviorInfo; + }; + + virtual void AddBehavior(const Info& info) = 0; + virtual void RemoveBehavior(const uint32_t behaviorId) = 0; +}; + +#endif //!__IBEHAVIORS__H__ diff --git a/dDatabase/GameDatabase/ITables/IPropertyContents.h b/dDatabase/GameDatabase/ITables/IPropertyContents.h index c862ca94..25271302 100644 --- a/dDatabase/GameDatabase/ITables/IPropertyContents.h +++ b/dDatabase/GameDatabase/ITables/IPropertyContents.h @@ -16,6 +16,7 @@ public: LWOOBJID id{}; LOT lot{}; uint32_t ugcId{}; + std::array behaviors{}; }; // Inserts a new UGC model into the database. @@ -32,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 UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) = 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 836ab56c..29df611c 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 UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) 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,6 +108,8 @@ 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; 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 new file mode 100644 index 00000000..91bcbd81 --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp @@ -0,0 +1,14 @@ +#include "IBehaviors.h" + +#include "MySQLDatabase.h" + +void MySQLDatabase::AddBehavior(const IBehaviors::Info& info) { + ExecuteInsert( + "INSERT INTO behaviors (behavior_info, character_id, behavior_id) VALUES (?, ?, ?) ON DUPLICATE KEY UPDATE behavior_info = ?", + info.behaviorInfo, info.characterId, info.behaviorId, info.behaviorInfo + ); +} + +void MySQLDatabase::RemoveBehavior(const uint32_t behaviorId) { + ExecuteDelete("DELETE FROM behaviors WHERE behavior_id = ?", behaviorId); +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt index 9f0e7baa..47cd220e 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt +++ b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt @@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES "Accounts.cpp" "AccountsRewardCodes.cpp" "ActivityLog.cpp" + "Behaviors.cpp" "BugReports.cpp" "CharInfo.cpp" "CharXml.cpp" diff --git a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp index dba82d56..2f80b9df 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp @@ -32,21 +32,23 @@ void MySQLDatabase::InsertNewPropertyModel(const LWOOBJID& propertyId, const IPr model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast(model.lot), model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w, name, "", // Model description. TODO implement this. - 0, // behavior 1. TODO implement this. - 0, // behavior 2. TODO implement this. - 0, // behavior 3. TODO implement this. - 0, // behavior 4. TODO implement this. - 0 // behavior 5. TODO implement this. + model.behaviors[0], // behavior 1 + model.behaviors[1], // behavior 2 + model.behaviors[2], // behavior 3 + model.behaviors[3], // behavior 4 + model.behaviors[4] // behavior 5 ); } catch (sql::SQLException& e) { LOG("Error inserting new property model: %s", e.what()); } } -void MySQLDatabase::UpdateModelPositionRotation(const LWOOBJID& propertyId, const NiPoint3& position, const NiQuaternion& rotation) { +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 = ? WHERE id = ?;", - position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId); + "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, " + "behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;", + position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId, + behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first); } void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) { diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 59a67462..7e0f73ec 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -239,7 +239,7 @@ void Character::SaveXMLToDatabase() { auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); // lzid garbage, binary concat of zoneID, zoneInstance and zoneClone - if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { + // if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { uint64_t lzidConcat = zoneInfo.GetCloneID(); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID()); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID()); @@ -251,7 +251,7 @@ void Character::SaveXMLToDatabase() { // Set the target scene, custom attribute character->SetAttribute("tscene", m_TargetScene.c_str()); - } + // } auto emotes = character->FirstChildElement("ue"); if (!emotes) emotes = m_Doc.NewElement("ue"); diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index 18e4b19d..c969a7dd 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -187,7 +187,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) { auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); - if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { + // if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { character->SetAttribute("lzx", m_Position.x); character->SetAttribute("lzy", m_Position.y); character->SetAttribute("lzz", m_Position.z); @@ -195,7 +195,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) { character->SetAttribute("lzry", m_Rotation.y); character->SetAttribute("lzrz", m_Rotation.z); character->SetAttribute("lzrw", m_Rotation.w); - } + // } } void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) { diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index 75f2a019..91e31e39 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -6,6 +6,7 @@ #include "BehaviorStates.h" #include "ControlBehaviorMsgs.h" +#include "tinyxml2.h" ModelComponent::ModelComponent(Entity* parent) : Component(parent) { m_OriginalPosition = m_Parent->GetDefaultPosition(); @@ -72,3 +73,22 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) { m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex()); // TODO move to the inventory } + +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; + auto& [id, behaviorData] = toReturn[i]; + id = behavior.GetBehaviorId(); + tinyxml2::XMLDocument doc; + auto* root = doc.NewElement("Behavior"); + behavior.Serialize(*root); + doc.InsertFirstChild(root); + + tinyxml2::XMLPrinter printer(0, false, 0); + doc.Print(&printer); + behaviorData = printer.CStr(); + } + return toReturn; +} diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index dc6810eb..5b76708f 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -109,6 +109,8 @@ public: void VerifyBehaviors(); + std::array, 5> GetBehaviorsForSave() const; + private: /** * The behaviors of the model diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index ad96097b..6c393df1 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -21,9 +21,11 @@ #include "eObjectBits.h" #include "CharacterComponent.h" #include "PlayerManager.h" +#include "ModelComponent.h" #include #include "CppScripts.h" +#include PropertyManagementComponent* PropertyManagementComponent::instance = nullptr; @@ -610,6 +612,12 @@ void PropertyManagementComponent::Save() { return; } + const auto* const owner = GetOwner(); + if (!owner) return; + + const auto* const character = owner->GetCharacter(); + if (!character) return; + auto present = Database::Get()->GetPropertyModels(propertyId); std::vector modelIds; @@ -624,6 +632,20 @@ void PropertyManagementComponent::Save() { if (entity == nullptr) { continue; } + auto* modelComponent = entity->GetComponent(); + if (!modelComponent) continue; + const auto modelBehaviors = modelComponent->GetBehaviorsForSave(); + + // save the behaviors of the model + for (const auto& [behaviorId, behaviorStr] : modelBehaviors) { + if (behaviorStr.empty() || behaviorId == LWOOBJID_EMPTY) continue; + IBehaviors::Info info { + .behaviorId = behaviorId, + .characterId = character->GetID(), + .behaviorInfo = behaviorStr + }; + Database::Get()->AddBehavior(info); + } const auto position = entity->GetPosition(); const auto rotation = entity->GetRotation(); @@ -635,10 +657,13 @@ void PropertyManagementComponent::Save() { model.position = position; model.rotation = rotation; model.ugcId = 0; + for (auto i = 0; i < model.behaviors.size(); i++) { + model.behaviors[i] = modelBehaviors[i].first; + } Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name"); } else { - Database::Get()->UpdateModelPositionRotation(id, position, rotation); + Database::Get()->UpdateModel(id, position, rotation, modelBehaviors); } } diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp index 3e62a2d7..4c917b48 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.cpp @@ -1,6 +1,8 @@ #include "Action.h" #include "Amf3.h" +#include "tinyxml2.h" + Action::Action(const AMFArrayValue& arguments) { for (const auto& [paramName, paramValue] : arguments.GetAssociative()) { if (paramName == "Type") { @@ -32,3 +34,15 @@ void Action::SendBehaviorBlocksToClient(AMFArrayValue& args) const { actionArgs->Insert(m_ValueParameterName, m_ValueParameterDouble); } } + +void Action::Serialize(tinyxml2::XMLElement& action) const { + action.SetAttribute("Type", m_Type.c_str()); + + if (m_ValueParameterName.empty()) return; + + if (m_ValueParameterName == "Message") { + action.SetAttribute(m_ValueParameterName.c_str(), m_ValueParameterString.c_str()); + } else { + action.SetAttribute(m_ValueParameterName.c_str(), m_ValueParameterDouble); + } +} diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h index 988e616c..2f618316 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/Action.h @@ -3,6 +3,10 @@ #include +namespace tinyxml2 { + class XMLElement; +}; + class AMFArrayValue; /** @@ -20,6 +24,7 @@ public: void SendBehaviorBlocksToClient(AMFArrayValue& args) const; + void Serialize(tinyxml2::XMLElement& action) const; 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 56dc43ff..02f64879 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.cpp @@ -1,6 +1,7 @@ #include "StripUiPosition.h" #include "Amf3.h" +#include "tinyxml2.h" StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) { const auto* const uiArray = arguments.GetArray(uiKeyName); @@ -21,3 +22,8 @@ void StripUiPosition::SendBehaviorBlocksToClient(AMFArrayValue& args) const { uiArgs->Insert("x", m_XPosition); uiArgs->Insert("y", m_YPosition); } + +void StripUiPosition::Serialize(tinyxml2::XMLElement& position) const { + position.SetAttribute("x", m_XPosition); + position.SetAttribute("y", m_YPosition); +} diff --git a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h index f202210d..7bc2087e 100644 --- a/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h +++ b/dGame/dPropertyBehaviors/ControlBehaviorMessages/StripUiPosition.h @@ -3,6 +3,10 @@ class AMFArrayValue; +namespace tinyxml2 { + class XMLElement; +} + /** * @brief The position of the first Action in a Strip * @@ -15,6 +19,7 @@ public: [[nodiscard]] double GetX() const noexcept { return m_XPosition; } [[nodiscard]] double GetY() const noexcept { return m_YPosition; } + void Serialize(tinyxml2::XMLElement& position) const; private: double m_XPosition{ 0.0 }; double m_YPosition{ 0.0 }; diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.cpp b/dGame/dPropertyBehaviors/PropertyBehavior.cpp index 423751c4..278012ea 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.cpp +++ b/dGame/dPropertyBehaviors/PropertyBehavior.cpp @@ -3,6 +3,7 @@ #include "Amf3.h" #include "BehaviorStates.h" #include "ControlBehaviorMsgs.h" +#include "tinyxml2.h" PropertyBehavior::PropertyBehavior() { m_LastEditedState = BehaviorState::HOME_STATE; @@ -124,3 +125,17 @@ void PropertyBehavior::SendBehaviorBlocksToClient(AMFArrayValue& args) const { // TODO Serialize the execution state of the behavior } + +void PropertyBehavior::Serialize(tinyxml2::XMLElement& behavior) const { + behavior.SetAttribute("id", m_BehaviorId); + behavior.SetAttribute("name", m_Name.c_str()); + behavior.SetAttribute("isLocked", isLocked); + behavior.SetAttribute("isLoot", isLoot); + + for (const auto& [stateId, state] : m_States) { + if (state.IsEmpty()) continue; + auto* const stateElement = behavior.InsertNewChildElement("State"); + stateElement->SetAttribute("id", static_cast(stateId)); + state.Serialize(*stateElement); + } +} diff --git a/dGame/dPropertyBehaviors/PropertyBehavior.h b/dGame/dPropertyBehaviors/PropertyBehavior.h index c9cb4b98..50585f98 100644 --- a/dGame/dPropertyBehaviors/PropertyBehavior.h +++ b/dGame/dPropertyBehaviors/PropertyBehavior.h @@ -3,6 +3,10 @@ #include "State.h" +namespace tinyxml2 { + class XMLElement; +} + enum class BehaviorState : uint32_t; class AMFArrayValue; @@ -25,6 +29,7 @@ public: [[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; } void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; } + void Serialize(tinyxml2::XMLElement& behavior) const; private: // The states this behavior has. diff --git a/dGame/dPropertyBehaviors/State.cpp b/dGame/dPropertyBehaviors/State.cpp index 0c8a11d9..9b321bad 100644 --- a/dGame/dPropertyBehaviors/State.cpp +++ b/dGame/dPropertyBehaviors/State.cpp @@ -2,6 +2,7 @@ #include "Amf3.h" #include "ControlBehaviorMsgs.h" +#include "tinyxml2.h" template <> void State::HandleMsg(AddStripMessage& msg) { @@ -134,4 +135,13 @@ void State::SendBehaviorBlocksToClient(AMFArrayValue& args) const { strip.SendBehaviorBlocksToClient(*stripArgs); } -}; +} + +void State::Serialize(tinyxml2::XMLElement& state) const { + for (const auto& strip : m_Strips) { + if (strip.IsEmpty()) continue; + + auto* const stripElement = state.InsertNewChildElement("Strip"); + strip.Serialize(*stripElement); + } +} diff --git a/dGame/dPropertyBehaviors/State.h b/dGame/dPropertyBehaviors/State.h index f0425763..e866b196 100644 --- a/dGame/dPropertyBehaviors/State.h +++ b/dGame/dPropertyBehaviors/State.h @@ -3,6 +3,10 @@ #include "Strip.h" +namespace tinyxml2 { + class XMLElement; +} + class AMFArrayValue; class State { @@ -13,6 +17,7 @@ public: void SendBehaviorBlocksToClient(AMFArrayValue& args) const; bool IsEmpty() const; + void Serialize(tinyxml2::XMLElement& state) const; private: std::vector m_Strips; }; diff --git a/dGame/dPropertyBehaviors/Strip.cpp b/dGame/dPropertyBehaviors/Strip.cpp index 0f459e46..8271a240 100644 --- a/dGame/dPropertyBehaviors/Strip.cpp +++ b/dGame/dPropertyBehaviors/Strip.cpp @@ -2,6 +2,7 @@ #include "Amf3.h" #include "ControlBehaviorMsgs.h" +#include "tinyxml2.h" template <> void Strip::HandleMsg(AddStripMessage& msg) { @@ -83,4 +84,13 @@ void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const { for (const auto& action : m_Actions) { action.SendBehaviorBlocksToClient(*actions); } -}; +} + +void Strip::Serialize(tinyxml2::XMLElement& strip) const { + auto* const positionElement = strip.InsertNewChildElement("Position"); + m_Position.Serialize(*positionElement); + for (const auto& action : m_Actions) { + auto* const actionElement = strip.InsertNewChildElement("Action"); + action.Serialize(*actionElement); + } +} diff --git a/dGame/dPropertyBehaviors/Strip.h b/dGame/dPropertyBehaviors/Strip.h index 107fee11..d8c6b9cf 100644 --- a/dGame/dPropertyBehaviors/Strip.h +++ b/dGame/dPropertyBehaviors/Strip.h @@ -6,6 +6,10 @@ #include +namespace tinyxml2 { + class XMLElement; +} + class AMFArrayValue; class Strip { @@ -16,6 +20,7 @@ public: void SendBehaviorBlocksToClient(AMFArrayValue& args) const; bool IsEmpty() const noexcept { return m_Actions.empty(); } + void Serialize(tinyxml2::XMLElement& strip) const; private: std::vector m_Actions; StripUiPosition m_Position; diff --git a/migrations/dlu/15_behavior_owner.sql b/migrations/dlu/15_behavior_owner.sql new file mode 100644 index 00000000..53e76f82 --- /dev/null +++ b/migrations/dlu/15_behavior_owner.sql @@ -0,0 +1,3 @@ +ALTER TABLE behaviors ADD COLUMN character_id BIGINT NOT NULL DEFAULT 0; +ALTER TABLE behaviors ADD COLUMN behavior_id BIGINT NOT NULL PRIMARY KEY; +ALTER TABLE behaviors DROP COLUMN id; From fd1c6ab2ea1c20dda82c0aeb27d4d43f3d29dfec Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 02:12:23 -0700 Subject: [PATCH 2/8] Saving actually works this time --- dDatabase/GameDatabase/ITables/IBehaviors.h | 1 + dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/dDatabase/GameDatabase/ITables/IBehaviors.h b/dDatabase/GameDatabase/ITables/IBehaviors.h index 02b5178b..7e42869d 100644 --- a/dDatabase/GameDatabase/ITables/IBehaviors.h +++ b/dDatabase/GameDatabase/ITables/IBehaviors.h @@ -13,6 +13,7 @@ public: 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; }; diff --git a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp index 2f80b9df..4e36e4f6 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp +++ b/dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp @@ -47,8 +47,8 @@ void MySQLDatabase::UpdateModel(const LWOOBJID& propertyId, const NiPoint3& posi ExecuteUpdate( "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, " "behavior_1 = ?, behavior_2 = ?, behavior_3 = ?, behavior_4 = ?, behavior_5 = ? WHERE id = ?;", - position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId, - behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first); + position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, + behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId); } void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) { From 0c4108e730bf54d78fe4c466527177c5de225e0a Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 03:36:29 -0700 Subject: [PATCH 3/8] 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; From 387c37505c4a58cbea203fd43b3b844efc02d575 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 03:39:25 -0700 Subject: [PATCH 4/8] undo debug changes --- dCommon/Logger.cpp | 4 ++-- dGame/Character.cpp | 4 ++-- dGame/dComponents/ControllablePhysicsComponent.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dCommon/Logger.cpp b/dCommon/Logger.cpp index 571b9661..1888f1eb 100644 --- a/dCommon/Logger.cpp +++ b/dCommon/Logger.cpp @@ -53,8 +53,8 @@ void Logger::vLog(const char* format, va_list args) { struct tm* time = localtime(&t); char timeStr[70]; strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time); - char message[131072]; - vsnprintf(message, 131072, format, args); + char message[2048]; + vsnprintf(message, 2048, format, args); for (const auto& writer : m_Writers) { writer->Log(timeStr, message); } diff --git a/dGame/Character.cpp b/dGame/Character.cpp index 7e0f73ec..59a67462 100644 --- a/dGame/Character.cpp +++ b/dGame/Character.cpp @@ -239,7 +239,7 @@ void Character::SaveXMLToDatabase() { auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); // lzid garbage, binary concat of zoneID, zoneInstance and zoneClone - // if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { + if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { uint64_t lzidConcat = zoneInfo.GetCloneID(); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID()); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID()); @@ -251,7 +251,7 @@ void Character::SaveXMLToDatabase() { // Set the target scene, custom attribute character->SetAttribute("tscene", m_TargetScene.c_str()); - // } + } auto emotes = character->FirstChildElement("ue"); if (!emotes) emotes = m_Doc.NewElement("ue"); diff --git a/dGame/dComponents/ControllablePhysicsComponent.cpp b/dGame/dComponents/ControllablePhysicsComponent.cpp index c969a7dd..18e4b19d 100644 --- a/dGame/dComponents/ControllablePhysicsComponent.cpp +++ b/dGame/dComponents/ControllablePhysicsComponent.cpp @@ -187,7 +187,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) { auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); - // if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { + if (zoneInfo.GetMapID() != 0 && zoneInfo.GetCloneID() == 0 && !Game::zoneManager->GetDisableSaveLocation()) { character->SetAttribute("lzx", m_Position.x); character->SetAttribute("lzy", m_Position.y); character->SetAttribute("lzz", m_Position.z); @@ -195,7 +195,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) { character->SetAttribute("lzry", m_Rotation.y); character->SetAttribute("lzrz", m_Rotation.z); character->SetAttribute("lzrw", m_Rotation.w); - // } + } } void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) { From b3548de7da93f6bdbc6af4105df7c99c465540f6 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 03:52:36 -0700 Subject: [PATCH 5/8] debug logs and spacing --- dGame/dComponents/ModelComponent.cpp | 7 +++++-- dGame/dComponents/PropertyManagementComponent.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/dGame/dComponents/ModelComponent.cpp b/dGame/dComponents/ModelComponent.cpp index 733b17ee..91680987 100644 --- a/dGame/dComponents/ModelComponent.cpp +++ b/dGame/dComponents/ModelComponent.cpp @@ -21,9 +21,11 @@ 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()); + + LOG_DEBUG("Loading behavior %d", behaviorId.value()); auto& inserted = m_Behaviors.emplace_back(); inserted.SetBehaviorId(*behaviorId); @@ -31,7 +33,7 @@ void ModelComponent::LoadBehaviors() { tinyxml2::XMLDocument behaviorXml; auto res = behaviorXml.Parse(behaviorStr.c_str(), behaviorStr.size()); - LOG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str()); + LOG_DEBUG("Behavior %i %d: %s", res, behaviorId.value(), behaviorStr.c_str()); const auto* const behaviorRoot = behaviorXml.FirstChildElement("Behavior"); if (!behaviorRoot) { @@ -108,6 +110,7 @@ std::array, 5> ModelComponent::GetBehaviorsForSa if (behavior.GetBehaviorId() == -1) continue; auto& [id, behaviorData] = toReturn[i]; id = behavior.GetBehaviorId(); + tinyxml2::XMLDocument doc; auto* root = doc.NewElement("Behavior"); behavior.Serialize(*root); diff --git a/dGame/dComponents/PropertyManagementComponent.cpp b/dGame/dComponents/PropertyManagementComponent.cpp index ae046a26..d7be22ac 100644 --- a/dGame/dComponents/PropertyManagementComponent.cpp +++ b/dGame/dComponents/PropertyManagementComponent.cpp @@ -597,8 +597,11 @@ void PropertyManagementComponent::Load() { std::ostringstream userModelBehavior; bool firstAdded = false; - for (const auto& behavior : databaseModel.behaviors) { - if (behavior == LWOOBJID_EMPTY) continue; + for (auto behavior : databaseModel.behaviors) { + if (behavior < 0) { + LOG("Invalid behavior ID: %d, removing behavior reference from model", behavior); + behavior = 0; + } if (firstAdded) userModelBehavior << ","; userModelBehavior << behavior; firstAdded = true; From a50b2566897f9138f50dad02f777258d9451856c Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 03:54:09 -0700 Subject: [PATCH 6/8] Update IBehaviors.h --- dDatabase/GameDatabase/ITables/IBehaviors.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/dDatabase/GameDatabase/ITables/IBehaviors.h b/dDatabase/GameDatabase/ITables/IBehaviors.h index 6b0f17b1..531167e2 100644 --- a/dDatabase/GameDatabase/ITables/IBehaviors.h +++ b/dDatabase/GameDatabase/ITables/IBehaviors.h @@ -1,5 +1,5 @@ -#ifndef __IBEHAVIORS__H__ -#define __IBEHAVIORS__H__ +#ifndef IBEHAVIORS_H +#define IBEHAVIORS_H #include @@ -19,4 +19,4 @@ public: virtual void RemoveBehavior(const int32_t behaviorId) = 0; }; -#endif //!__IBEHAVIORS__H__ +#endif //!IBEHAVIORS_H From 00f36f3f285efdf125af5819b3e62ea7e487a4a3 Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 04:15:07 -0700 Subject: [PATCH 7/8] missing include for windows --- dDatabase/GameDatabase/ITables/IPropertyContents.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dDatabase/GameDatabase/ITables/IPropertyContents.h b/dDatabase/GameDatabase/ITables/IPropertyContents.h index dda8fc11..0d8d8b5c 100644 --- a/dDatabase/GameDatabase/ITables/IPropertyContents.h +++ b/dDatabase/GameDatabase/ITables/IPropertyContents.h @@ -1,6 +1,7 @@ #ifndef __IPROPERTIESCONTENTS__H__ #define __IPROPERTIESCONTENTS__H__ +#include #include #include From db2d4f02b5a4592d9c0fa0cd610835b9551ae89b Mon Sep 17 00:00:00 2001 From: David Markowitz Date: Sat, 18 May 2024 04:16:07 -0700 Subject: [PATCH 8/8] preemptive include for windows --- dGame/dComponents/ModelComponent.h | 1 + 1 file changed, 1 insertion(+) diff --git a/dGame/dComponents/ModelComponent.h b/dGame/dComponents/ModelComponent.h index ba225426..9e23eafb 100644 --- a/dGame/dComponents/ModelComponent.h +++ b/dGame/dComponents/ModelComponent.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include "dCommonVars.h"