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

View File

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

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{};
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;

View File

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

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"
"AccountsRewardCodes.cpp"
"ActivityLog.cpp"
"Behaviors.cpp"
"BugReports.cpp"
"CharInfo.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.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) {

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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

View File

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

View File

@ -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{ "" };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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;