Saving to database working

This commit is contained in:
David Markowitz 2024-05-18 02:05:55 -07:00
parent c8e0bb0db0
commit f2bf9a2a28
24 changed files with 192 additions and 20 deletions

View File

@ -53,8 +53,8 @@ void Logger::vLog(const char* format, va_list args) {
struct tm* time = localtime(&t); struct tm* time = localtime(&t);
char timeStr[70]; char timeStr[70];
strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time); strftime(timeStr, sizeof(timeStr), "[%d-%m-%y %H:%M:%S ", time);
char message[2048]; char message[131072];
vsnprintf(message, 2048, format, args); vsnprintf(message, 131072, format, args);
for (const auto& writer : m_Writers) { for (const auto& writer : m_Writers) {
writer->Log(timeStr, message); writer->Log(timeStr, message);
} }

View File

@ -23,6 +23,7 @@
#include "IActivityLog.h" #include "IActivityLog.h"
#include "IIgnoreList.h" #include "IIgnoreList.h"
#include "IAccountsRewardCodes.h" #include "IAccountsRewardCodes.h"
#include "IBehaviors.h"
namespace sql { namespace sql {
class Statement; class Statement;
@ -40,7 +41,8 @@ class GameDatabase :
public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports, public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports,
public IPropertyContents, public IProperty, public IPetNames, public ICharXml, public IPropertyContents, public IProperty, public IPetNames, public ICharXml,
public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, 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: public:
virtual ~GameDatabase() = default; virtual ~GameDatabase() = default;
// TODO: These should be made private. // TODO: These should be made private.

View File

@ -0,0 +1,20 @@
#ifndef __IBEHAVIORS__H__
#define __IBEHAVIORS__H__
#include <cstdint>
#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__

View File

@ -16,6 +16,7 @@ public:
LWOOBJID id{}; LWOOBJID id{};
LOT lot{}; LOT lot{};
uint32_t ugcId{}; uint32_t ugcId{};
std::array<LWOOBJID, 5> behaviors{};
}; };
// Inserts a new UGC model into the database. // 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; 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. // 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<std::pair<LWOOBJID, std::string>, 5>& behaviors) = 0;
// Remove the model for the given property id. // Remove the model for the given property id.
virtual void RemoveModel(const LWOOBJID& modelId) = 0; virtual void RemoveModel(const LWOOBJID& modelId) = 0;

View File

@ -74,7 +74,7 @@ public:
std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override; std::vector<IPropertyContents::Model> GetPropertyModels(const LWOOBJID& propertyId) override;
void RemoveUnreferencedUgcModels() override; void RemoveUnreferencedUgcModels() override;
void InsertNewPropertyModel(const LWOOBJID& propertyId, const IPropertyContents::Model& model, const std::string_view name) 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<std::pair<LWOOBJID, std::string>, 5>& behaviors) override;
void RemoveModel(const LWOOBJID& modelId) override; void RemoveModel(const LWOOBJID& modelId) override;
void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override; void UpdatePerformanceCost(const LWOZONEID& zoneId, const float performanceCost) override;
void InsertNewBugReport(const IBugReports::Info& info) override; void InsertNewBugReport(const IBugReports::Info& info) override;
@ -108,6 +108,8 @@ public:
std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override; std::vector<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) override;
void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override;
std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override; std::vector<uint32_t> GetRewardCodesByAccountID(const uint32_t account_id) override;
void AddBehavior(const IBehaviors::Info& info) override;
void RemoveBehavior(const uint32_t characterId) override;
private: private:
// Generic query functions that can be used for any query. // Generic query functions that can be used for any query.

View File

@ -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);
}

View File

@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"Accounts.cpp" "Accounts.cpp"
"AccountsRewardCodes.cpp" "AccountsRewardCodes.cpp"
"ActivityLog.cpp" "ActivityLog.cpp"
"Behaviors.cpp"
"BugReports.cpp" "BugReports.cpp"
"CharInfo.cpp" "CharInfo.cpp"
"CharXml.cpp" "CharXml.cpp"

View File

@ -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<uint32_t>(model.lot), model.id, propertyId, model.ugcId == 0 ? std::nullopt : std::optional(model.ugcId), static_cast<uint32_t>(model.lot),
model.position.x, model.position.y, model.position.z, model.rotation.x, model.rotation.y, model.rotation.z, model.rotation.w, 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. name, "", // Model description. TODO implement this.
0, // behavior 1. TODO implement this. model.behaviors[0], // behavior 1
0, // behavior 2. TODO implement this. model.behaviors[1], // behavior 2
0, // behavior 3. TODO implement this. model.behaviors[2], // behavior 3
0, // behavior 4. TODO implement this. model.behaviors[3], // behavior 4
0 // behavior 5. TODO implement this. model.behaviors[4] // behavior 5
); );
} catch (sql::SQLException& e) { } catch (sql::SQLException& e) {
LOG("Error inserting new property model: %s", e.what()); 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<std::pair<LWOOBJID, std::string>, 5>& behaviors) {
ExecuteUpdate( ExecuteUpdate(
"UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ? WHERE id = ?;", "UPDATE properties_contents SET x = ?, y = ?, z = ?, rx = ?, ry = ?, rz = ?, rw = ?, "
position.x, position.y, position.z, rotation.x, rotation.y, rotation.z, rotation.w, propertyId); "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) { void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {

View File

@ -239,7 +239,7 @@ void Character::SaveXMLToDatabase() {
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID();
// lzid garbage, binary concat of zoneID, zoneInstance and zoneClone // 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(); uint64_t lzidConcat = zoneInfo.GetCloneID();
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID()); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetInstanceID());
lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID()); lzidConcat = (lzidConcat << 16) | uint16_t(zoneInfo.GetMapID());
@ -251,7 +251,7 @@ void Character::SaveXMLToDatabase() {
// Set the target scene, custom attribute // Set the target scene, custom attribute
character->SetAttribute("tscene", m_TargetScene.c_str()); character->SetAttribute("tscene", m_TargetScene.c_str());
} // }
auto emotes = character->FirstChildElement("ue"); auto emotes = character->FirstChildElement("ue");
if (!emotes) emotes = m_Doc.NewElement("ue"); if (!emotes) emotes = m_Doc.NewElement("ue");

View File

@ -187,7 +187,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
auto zoneInfo = Game::zoneManager->GetZone()->GetZoneID(); 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("lzx", m_Position.x);
character->SetAttribute("lzy", m_Position.y); character->SetAttribute("lzy", m_Position.y);
character->SetAttribute("lzz", m_Position.z); character->SetAttribute("lzz", m_Position.z);
@ -195,7 +195,7 @@ void ControllablePhysicsComponent::UpdateXml(tinyxml2::XMLDocument& doc) {
character->SetAttribute("lzry", m_Rotation.y); character->SetAttribute("lzry", m_Rotation.y);
character->SetAttribute("lzrz", m_Rotation.z); character->SetAttribute("lzrz", m_Rotation.z);
character->SetAttribute("lzrw", m_Rotation.w); character->SetAttribute("lzrw", m_Rotation.w);
} // }
} }
void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) { void ControllablePhysicsComponent::SetPosition(const NiPoint3& pos) {

View File

@ -6,6 +6,7 @@
#include "BehaviorStates.h" #include "BehaviorStates.h"
#include "ControlBehaviorMsgs.h" #include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
ModelComponent::ModelComponent(Entity* parent) : Component(parent) { ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_OriginalPosition = m_Parent->GetDefaultPosition(); m_OriginalPosition = m_Parent->GetDefaultPosition();
@ -72,3 +73,22 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) {
m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex()); m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex());
// TODO move to the inventory // TODO move to the inventory
} }
std::array<std::pair<LWOOBJID, std::string>, 5> ModelComponent::GetBehaviorsForSave() const {
std::array<std::pair<LWOOBJID, std::string>, 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;
}

View File

@ -109,6 +109,8 @@ public:
void VerifyBehaviors(); void VerifyBehaviors();
std::array<std::pair<LWOOBJID, std::string>, 5> GetBehaviorsForSave() const;
private: private:
/** /**
* The behaviors of the model * The behaviors of the model

View File

@ -21,9 +21,11 @@
#include "eObjectBits.h" #include "eObjectBits.h"
#include "CharacterComponent.h" #include "CharacterComponent.h"
#include "PlayerManager.h" #include "PlayerManager.h"
#include "ModelComponent.h"
#include <vector> #include <vector>
#include "CppScripts.h" #include "CppScripts.h"
#include <ranges>
PropertyManagementComponent* PropertyManagementComponent::instance = nullptr; PropertyManagementComponent* PropertyManagementComponent::instance = nullptr;
@ -610,6 +612,12 @@ void PropertyManagementComponent::Save() {
return; return;
} }
const auto* const owner = GetOwner();
if (!owner) return;
const auto* const character = owner->GetCharacter();
if (!character) return;
auto present = Database::Get()->GetPropertyModels(propertyId); auto present = Database::Get()->GetPropertyModels(propertyId);
std::vector<LWOOBJID> modelIds; std::vector<LWOOBJID> modelIds;
@ -624,6 +632,20 @@ void PropertyManagementComponent::Save() {
if (entity == nullptr) { if (entity == nullptr) {
continue; continue;
} }
auto* modelComponent = entity->GetComponent<ModelComponent>();
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 position = entity->GetPosition();
const auto rotation = entity->GetRotation(); const auto rotation = entity->GetRotation();
@ -635,10 +657,13 @@ void PropertyManagementComponent::Save() {
model.position = position; model.position = position;
model.rotation = rotation; model.rotation = rotation;
model.ugcId = 0; 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"); Database::Get()->InsertNewPropertyModel(propertyId, model, "Objects_" + std::to_string(model.lot) + "_name");
} else { } else {
Database::Get()->UpdateModelPositionRotation(id, position, rotation); Database::Get()->UpdateModel(id, position, rotation, modelBehaviors);
} }
} }

View File

@ -1,6 +1,8 @@
#include "Action.h" #include "Action.h"
#include "Amf3.h" #include "Amf3.h"
#include "tinyxml2.h"
Action::Action(const AMFArrayValue& arguments) { Action::Action(const AMFArrayValue& arguments) {
for (const auto& [paramName, paramValue] : arguments.GetAssociative()) { for (const auto& [paramName, paramValue] : arguments.GetAssociative()) {
if (paramName == "Type") { if (paramName == "Type") {
@ -32,3 +34,15 @@ void Action::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
actionArgs->Insert(m_ValueParameterName, m_ValueParameterDouble); 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);
}
}

View File

@ -3,6 +3,10 @@
#include <string> #include <string>
namespace tinyxml2 {
class XMLElement;
};
class AMFArrayValue; class AMFArrayValue;
/** /**
@ -20,6 +24,7 @@ public:
void SendBehaviorBlocksToClient(AMFArrayValue& args) const; void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
void Serialize(tinyxml2::XMLElement& action) const;
private: private:
double m_ValueParameterDouble{ 0.0 }; double m_ValueParameterDouble{ 0.0 };
std::string m_Type{ "" }; std::string m_Type{ "" };

View File

@ -1,6 +1,7 @@
#include "StripUiPosition.h" #include "StripUiPosition.h"
#include "Amf3.h" #include "Amf3.h"
#include "tinyxml2.h"
StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) { StripUiPosition::StripUiPosition(const AMFArrayValue& arguments, const std::string& uiKeyName) {
const auto* const uiArray = arguments.GetArray(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("x", m_XPosition);
uiArgs->Insert("y", m_YPosition); uiArgs->Insert("y", m_YPosition);
} }
void StripUiPosition::Serialize(tinyxml2::XMLElement& position) const {
position.SetAttribute("x", m_XPosition);
position.SetAttribute("y", m_YPosition);
}

View File

@ -3,6 +3,10 @@
class AMFArrayValue; class AMFArrayValue;
namespace tinyxml2 {
class XMLElement;
}
/** /**
* @brief The position of the first Action in a Strip * @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 GetX() const noexcept { return m_XPosition; }
[[nodiscard]] double GetY() const noexcept { return m_YPosition; } [[nodiscard]] double GetY() const noexcept { return m_YPosition; }
void Serialize(tinyxml2::XMLElement& position) const;
private: private:
double m_XPosition{ 0.0 }; double m_XPosition{ 0.0 };
double m_YPosition{ 0.0 }; double m_YPosition{ 0.0 };

View File

@ -3,6 +3,7 @@
#include "Amf3.h" #include "Amf3.h"
#include "BehaviorStates.h" #include "BehaviorStates.h"
#include "ControlBehaviorMsgs.h" #include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
PropertyBehavior::PropertyBehavior() { PropertyBehavior::PropertyBehavior() {
m_LastEditedState = BehaviorState::HOME_STATE; m_LastEditedState = BehaviorState::HOME_STATE;
@ -124,3 +125,17 @@ void PropertyBehavior::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
// TODO Serialize the execution state of the behavior // 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<uint32_t>(stateId));
state.Serialize(*stateElement);
}
}

View File

@ -3,6 +3,10 @@
#include "State.h" #include "State.h"
namespace tinyxml2 {
class XMLElement;
}
enum class BehaviorState : uint32_t; enum class BehaviorState : uint32_t;
class AMFArrayValue; class AMFArrayValue;
@ -25,6 +29,7 @@ public:
[[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; } [[nodiscard]] int32_t GetBehaviorId() const noexcept { return m_BehaviorId; }
void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; } void SetBehaviorId(int32_t id) noexcept { m_BehaviorId = id; }
void Serialize(tinyxml2::XMLElement& behavior) const;
private: private:
// The states this behavior has. // The states this behavior has.

View File

@ -2,6 +2,7 @@
#include "Amf3.h" #include "Amf3.h"
#include "ControlBehaviorMsgs.h" #include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
template <> template <>
void State::HandleMsg(AddStripMessage& msg) { void State::HandleMsg(AddStripMessage& msg) {
@ -134,4 +135,13 @@ void State::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
strip.SendBehaviorBlocksToClient(*stripArgs); 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);
}
}

View File

@ -3,6 +3,10 @@
#include "Strip.h" #include "Strip.h"
namespace tinyxml2 {
class XMLElement;
}
class AMFArrayValue; class AMFArrayValue;
class State { class State {
@ -13,6 +17,7 @@ public:
void SendBehaviorBlocksToClient(AMFArrayValue& args) const; void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
bool IsEmpty() const; bool IsEmpty() const;
void Serialize(tinyxml2::XMLElement& state) const;
private: private:
std::vector<Strip> m_Strips; std::vector<Strip> m_Strips;
}; };

View File

@ -2,6 +2,7 @@
#include "Amf3.h" #include "Amf3.h"
#include "ControlBehaviorMsgs.h" #include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"
template <> template <>
void Strip::HandleMsg(AddStripMessage& msg) { void Strip::HandleMsg(AddStripMessage& msg) {
@ -83,4 +84,13 @@ void Strip::SendBehaviorBlocksToClient(AMFArrayValue& args) const {
for (const auto& action : m_Actions) { for (const auto& action : m_Actions) {
action.SendBehaviorBlocksToClient(*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);
}
}

View File

@ -6,6 +6,10 @@
#include <vector> #include <vector>
namespace tinyxml2 {
class XMLElement;
}
class AMFArrayValue; class AMFArrayValue;
class Strip { class Strip {
@ -16,6 +20,7 @@ public:
void SendBehaviorBlocksToClient(AMFArrayValue& args) const; void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
bool IsEmpty() const noexcept { return m_Actions.empty(); } bool IsEmpty() const noexcept { return m_Actions.empty(); }
void Serialize(tinyxml2::XMLElement& strip) const;
private: private:
std::vector<Action> m_Actions; std::vector<Action> m_Actions;
StripUiPosition m_Position; StripUiPosition m_Position;

View File

@ -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;