Skip to content

Commit

Permalink
Merge pull request #1586 from DarkflameUniverse/property_behavior_saving
Browse files Browse the repository at this point in the history
feat: Property Behavior Saving
  • Loading branch information
DarwinAnim8or authored Jun 16, 2024
2 parents bcf1058 + b984cd6 commit 9400ee1
Show file tree
Hide file tree
Showing 22 changed files with 328 additions and 17 deletions.
4 changes: 3 additions & 1 deletion dDatabase/GameDatabase/GameDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
#include "IActivityLog.h"
#include "IIgnoreList.h"
#include "IAccountsRewardCodes.h"
#include "IBehaviors.h"

namespace sql {
class Statement;
Expand All @@ -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.
Expand Down
22 changes: 22 additions & 0 deletions dDatabase/GameDatabase/ITables/IBehaviors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#ifndef IBEHAVIORS_H
#define IBEHAVIORS_H

#include <cstdint>

#include "dCommonVars.h"

class IBehaviors {
public:
struct Info {
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 std::string GetBehavior(const int32_t behaviorId) = 0;
virtual void RemoveBehavior(const int32_t behaviorId) = 0;
};

#endif //!IBEHAVIORS_H
4 changes: 3 additions & 1 deletion dDatabase/GameDatabase/ITables/IPropertyContents.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#ifndef __IPROPERTIESCONTENTS__H__
#define __IPROPERTIESCONTENTS__H__

#include <array>
#include <cstdint>
#include <string_view>

Expand All @@ -16,6 +17,7 @@ class IPropertyContents {
LWOOBJID id{};
LOT lot{};
uint32_t ugcId{};
std::array<int32_t, 5> behaviors{};
};

// Inserts a new UGC model into the database.
Expand All @@ -32,7 +34,7 @@ class IPropertyContents {
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<int32_t, std::string>, 5>& behaviors) = 0;

// Remove the model for the given property id.
virtual void RemoveModel(const LWOOBJID& modelId) = 0;
Expand Down
5 changes: 4 additions & 1 deletion dDatabase/GameDatabase/MySQL/MySQLDatabase.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ class MySQLDatabase : public GameDatabase {
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<int32_t, 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;
Expand Down Expand Up @@ -108,6 +108,9 @@ class MySQLDatabase : public GameDatabase {
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;
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.
Expand Down
19 changes: 19 additions & 0 deletions dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
#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 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() : "";
}
1 change: 1 addition & 0 deletions dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
"Accounts.cpp"
"AccountsRewardCodes.cpp"
"ActivityLog.cpp"
"Behaviors.cpp"
"BugReports.cpp"
"CharInfo.cpp"
"CharXml.cpp"
Expand Down
29 changes: 20 additions & 9 deletions dDatabase/GameDatabase/MySQL/Tables/PropertyContents.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
#include "MySQLDatabase.h"

std::vector<IPropertyContents::Model> 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<IPropertyContents::Model> toReturn;
toReturn.reserve(result->rowsCount());
Expand All @@ -17,6 +20,12 @@ std::vector<IPropertyContents::Model> 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;
Expand All @@ -32,21 +41,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<int32_t, 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,
behaviors[0].first, behaviors[1].first, behaviors[2].first, behaviors[3].first, behaviors[4].first, propertyId);
}

void MySQLDatabase::RemoveModel(const LWOOBJID& modelId) {
Expand Down
4 changes: 2 additions & 2 deletions dGame/Entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@ void Entity::Initialize() {

AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);

AddComponent<ModelComponent>();
AddComponent<ModelComponent>()->LoadBehaviors();

AddComponent<RenderComponent>();

Expand Down Expand Up @@ -649,7 +649,7 @@ void Entity::Initialize() {
}

if (compRegistryTable->GetByIDAndType(m_TemplateID, eReplicaComponentType::MODEL, -1) != -1 && !GetComponent<PetComponent>()) {
AddComponent<ModelComponent>();
AddComponent<ModelComponent>()->LoadBehaviors();
if (!HasComponent(eReplicaComponentType::DESTROYABLE)) {
auto* destroyableComponent = AddComponent<DestroyableComponent>();
destroyableComponent->SetHealth(1);
Expand Down
50 changes: 50 additions & 0 deletions dGame/dComponents/ModelComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@

#include "BehaviorStates.h"
#include "ControlBehaviorMsgs.h"
#include "tinyxml2.h"

#include "Database.h"

ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_OriginalPosition = m_Parent->GetDefaultPosition();
Expand All @@ -14,6 +17,33 @@ ModelComponent::ModelComponent(Entity* parent) : Component(parent) {
m_userModelID = m_Parent->GetVarAs<LWOOBJID>(u"userModelID");
}

void ModelComponent::LoadBehaviors() {
auto behaviors = GeneralUtils::SplitString(m_Parent->GetVar<std::string>(u"userModelBehaviors"), ',');
for (const auto& behavior : behaviors) {
if (behavior.empty()) continue;

const auto behaviorId = GeneralUtils::TryParse<int32_t>(behavior);
if (!behaviorId.has_value() || behaviorId.value() == 0) continue;

LOG_DEBUG("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_DEBUG("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)) {
Expand Down Expand Up @@ -72,3 +102,23 @@ void ModelComponent::MoveToInventory(MoveToInventoryMessage& msg) {
m_Behaviors.erase(m_Behaviors.begin() + msg.GetBehaviorIndex());
// TODO move to the inventory
}

std::array<std::pair<int32_t, std::string>, 5> ModelComponent::GetBehaviorsForSave() const {
std::array<std::pair<int32_t, 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, true, 0);
doc.Print(&printer);
behaviorData = printer.CStr();
}
return toReturn;
}
5 changes: 5 additions & 0 deletions dGame/dComponents/ModelComponent.h
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#pragma once

#include <array>
#include <map>

#include "dCommonVars.h"
Expand Down Expand Up @@ -28,6 +29,8 @@ class ModelComponent final : public Component {

ModelComponent(Entity* parent);

void LoadBehaviors();

void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;

/**
Expand Down Expand Up @@ -109,6 +112,8 @@ class ModelComponent final : public Component {

void VerifyBehaviors();

std::array<std::pair<int32_t, std::string>, 5> GetBehaviorsForSave() const;

private:
/**
* The behaviors of the model
Expand Down
41 changes: 40 additions & 1 deletion dGame/dComponents/PropertyManagementComponent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -593,6 +595,20 @@ void PropertyManagementComponent::Load() {
settings.push_back(new LDFData<int>(u"componentWhitelist", 1));
}

std::ostringstream userModelBehavior;
bool firstAdded = false;
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;
}

settings.push_back(new LDFData<std::string>(u"userModelBehaviors", userModelBehavior.str()));

node->config = settings;

const auto spawnerId = Game::zoneManager->MakeSpawner(info);
Expand All @@ -610,6 +626,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;
Expand All @@ -624,6 +646,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 == -1 || behaviorId == 0) 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();
Expand All @@ -635,10 +671,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);
}
}

Expand Down
Loading

0 comments on commit 9400ee1

Please sign in to comment.