mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2024-12-22 13:33:35 +00:00
Saving to database working
This commit is contained in:
parent
c8e0bb0db0
commit
f2bf9a2a28
@ -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);
|
||||
}
|
||||
|
@ -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.
|
||||
|
20
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal file
20
dDatabase/GameDatabase/ITables/IBehaviors.h
Normal 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__
|
@ -16,6 +16,7 @@ public:
|
||||
LWOOBJID id{};
|
||||
LOT lot{};
|
||||
uint32_t ugcId{};
|
||||
std::array<LWOOBJID, 5> 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<std::pair<LWOOBJID, std::string>, 5>& behaviors) = 0;
|
||||
|
||||
// Remove the model for the given property id.
|
||||
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
|
||||
|
@ -74,7 +74,7 @@ public:
|
||||
std::vector<IPropertyContents::Model> 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<std::pair<LWOOBJID, std::string>, 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<IIgnoreList::Info> GetIgnoreList(const uint32_t playerId) 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;
|
||||
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.
|
||||
|
14
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal file
14
dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Normal 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);
|
||||
}
|
@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
|
||||
"Accounts.cpp"
|
||||
"AccountsRewardCodes.cpp"
|
||||
"ActivityLog.cpp"
|
||||
"Behaviors.cpp"
|
||||
"BugReports.cpp"
|
||||
"CharInfo.cpp"
|
||||
"CharXml.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<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,
|
||||
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<std::pair<LWOOBJID, std::string>, 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) {
|
||||
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
@ -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<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;
|
||||
}
|
||||
|
@ -109,6 +109,8 @@ public:
|
||||
|
||||
void VerifyBehaviors();
|
||||
|
||||
std::array<std::pair<LWOOBJID, std::string>, 5> GetBehaviorsForSave() const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* The behaviors of the model
|
||||
|
@ -21,9 +21,11 @@
|
||||
#include "eObjectBits.h"
|
||||
#include "CharacterComponent.h"
|
||||
#include "PlayerManager.h"
|
||||
#include "ModelComponent.h"
|
||||
|
||||
#include <vector>
|
||||
#include "CppScripts.h"
|
||||
#include <ranges>
|
||||
|
||||
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<LWOOBJID> modelIds;
|
||||
@ -624,6 +632,20 @@ void PropertyManagementComponent::Save() {
|
||||
if (entity == nullptr) {
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -3,6 +3,10 @@
|
||||
|
||||
#include <string>
|
||||
|
||||
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{ "" };
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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 };
|
||||
|
@ -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<uint32_t>(stateId));
|
||||
state.Serialize(*stateElement);
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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<Strip> m_Strips;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,10 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
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<Action> m_Actions;
|
||||
StripUiPosition m_Position;
|
||||
|
3
migrations/dlu/15_behavior_owner.sql
Normal file
3
migrations/dlu/15_behavior_owner.sql
Normal 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;
|
Loading…
Reference in New Issue
Block a user