Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Property Behavior Saving #1586

Merged
merged 9 commits into from
Jun 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading