mirror of
				https://github.com/DarkflameUniverse/DarkflameServer.git
				synced 2025-11-03 22:21:59 +00:00 
			
		
		
		
	Merge pull request #1586 from DarkflameUniverse/property_behavior_saving
feat: Property Behavior Saving
This commit is contained in:
		@@ -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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										22
									
								
								dDatabase/GameDatabase/ITables/IBehaviors.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								dDatabase/GameDatabase/ITables/IBehaviors.h
									
									
									
									
									
										Normal file
									
								
							@@ -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
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
#ifndef __IPROPERTIESCONTENTS__H__
 | 
			
		||||
#define __IPROPERTIESCONTENTS__H__
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <string_view>
 | 
			
		||||
 | 
			
		||||
@@ -16,6 +17,7 @@ public:
 | 
			
		||||
		LWOOBJID id{};
 | 
			
		||||
		LOT lot{};
 | 
			
		||||
		uint32_t ugcId{};
 | 
			
		||||
		std::array<int32_t, 5> behaviors{};
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	// Inserts a new UGC model into the database.
 | 
			
		||||
@@ -32,7 +34,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<int32_t, 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<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;
 | 
			
		||||
@@ -108,6 +108,9 @@ 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;
 | 
			
		||||
	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.
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										19
									
								
								dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								dDatabase/GameDatabase/MySQL/Tables/Behaviors.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -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() : "";
 | 
			
		||||
}
 | 
			
		||||
@@ -2,6 +2,7 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES
 | 
			
		||||
	"Accounts.cpp"
 | 
			
		||||
	"AccountsRewardCodes.cpp"
 | 
			
		||||
	"ActivityLog.cpp"
 | 
			
		||||
	"Behaviors.cpp"
 | 
			
		||||
	"BugReports.cpp"
 | 
			
		||||
	"CharInfo.cpp"
 | 
			
		||||
	"CharXml.cpp"
 | 
			
		||||
 
 | 
			
		||||
@@ -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());
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -225,7 +225,7 @@ void Entity::Initialize() {
 | 
			
		||||
 | 
			
		||||
		AddComponent<SimplePhysicsComponent>(simplePhysicsComponentID);
 | 
			
		||||
 | 
			
		||||
		AddComponent<ModelComponent>();
 | 
			
		||||
		AddComponent<ModelComponent>()->LoadBehaviors();
 | 
			
		||||
 | 
			
		||||
		AddComponent<RenderComponent>();
 | 
			
		||||
 | 
			
		||||
@@ -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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
@@ -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)) {
 | 
			
		||||
@@ -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;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -1,5 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <array>
 | 
			
		||||
#include <map>
 | 
			
		||||
 | 
			
		||||
#include "dCommonVars.h"
 | 
			
		||||
@@ -28,6 +29,8 @@ public:
 | 
			
		||||
 | 
			
		||||
	ModelComponent(Entity* parent);
 | 
			
		||||
 | 
			
		||||
	void LoadBehaviors();
 | 
			
		||||
 | 
			
		||||
	void Serialize(RakNet::BitStream& outBitStream, bool bIsInitialUpdate) override;
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
@@ -109,6 +112,8 @@ public:
 | 
			
		||||
	
 | 
			
		||||
	void VerifyBehaviors();
 | 
			
		||||
 | 
			
		||||
	std::array<std::pair<int32_t, 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;
 | 
			
		||||
 | 
			
		||||
@@ -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);
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -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();
 | 
			
		||||
@@ -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);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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,46 @@ 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;
 | 
			
		||||
 | 
			
		||||
	action.SetAttribute("ValueParameterName", m_ValueParameterName.c_str());
 | 
			
		||||
 | 
			
		||||
	if (m_ValueParameterName == "Message") {
 | 
			
		||||
		action.SetAttribute("Value", m_ValueParameterString.c_str());
 | 
			
		||||
	} else {
 | 
			
		||||
		action.SetAttribute("Value", m_ValueParameterDouble);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Action::Deserialize(const tinyxml2::XMLElement& action) {
 | 
			
		||||
	const char* type = nullptr;
 | 
			
		||||
	action.QueryAttribute("Type", &type);
 | 
			
		||||
	if (!type) {
 | 
			
		||||
		LOG("No type found for an action?");
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	m_Type = type;
 | 
			
		||||
 | 
			
		||||
	const char* valueParameterName = nullptr;
 | 
			
		||||
	action.QueryAttribute("ValueParameterName", &valueParameterName);
 | 
			
		||||
	if (valueParameterName) {
 | 
			
		||||
		m_ValueParameterName = valueParameterName;
 | 
			
		||||
 | 
			
		||||
		if (m_ValueParameterName == "Message") {
 | 
			
		||||
			const char* value = nullptr;
 | 
			
		||||
			action.QueryAttribute("Value", &value);
 | 
			
		||||
			if (value) {
 | 
			
		||||
				m_ValueParameterString = value;
 | 
			
		||||
			} else {
 | 
			
		||||
				LOG("No value found for an action message?");
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			action.QueryDoubleAttribute("Value", &m_ValueParameterDouble);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,10 @@
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
 | 
			
		||||
namespace tinyxml2 {
 | 
			
		||||
	class XMLElement;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class AMFArrayValue;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
@@ -20,6 +24,8 @@ public:
 | 
			
		||||
 | 
			
		||||
	void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
 | 
			
		||||
 | 
			
		||||
	void Serialize(tinyxml2::XMLElement& action) const;
 | 
			
		||||
	void Deserialize(const tinyxml2::XMLElement& action);
 | 
			
		||||
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,13 @@ 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void StripUiPosition::Deserialize(const tinyxml2::XMLElement& position) {
 | 
			
		||||
	position.QueryDoubleAttribute("x", &m_XPosition);
 | 
			
		||||
	position.QueryDoubleAttribute("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,8 @@ public:
 | 
			
		||||
	[[nodiscard]] double GetX() const noexcept { return m_XPosition; }
 | 
			
		||||
	[[nodiscard]] double GetY() const noexcept { return m_YPosition; }
 | 
			
		||||
 | 
			
		||||
	void Serialize(tinyxml2::XMLElement& position) const;
 | 
			
		||||
	void Deserialize(const tinyxml2::XMLElement& position);
 | 
			
		||||
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,31 @@ 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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void PropertyBehavior::Deserialize(const tinyxml2::XMLElement& behavior) {
 | 
			
		||||
	m_Name = behavior.Attribute("name");
 | 
			
		||||
	behavior.QueryBoolAttribute("isLocked", &isLocked);
 | 
			
		||||
	behavior.QueryBoolAttribute("isLoot", &isLoot);
 | 
			
		||||
 | 
			
		||||
	for (const auto* stateElement = behavior.FirstChildElement("State"); stateElement; stateElement = stateElement->NextSiblingElement("State")) {
 | 
			
		||||
		int32_t stateId = -1;
 | 
			
		||||
		stateElement->QueryIntAttribute("id", &stateId);
 | 
			
		||||
		if (stateId < 0 || stateId > 5) continue;
 | 
			
		||||
		m_States[static_cast<BehaviorState>(stateId)].Deserialize(*stateElement);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,10 @@
 | 
			
		||||
 | 
			
		||||
#include "State.h"
 | 
			
		||||
 | 
			
		||||
namespace tinyxml2 {
 | 
			
		||||
	class XMLElement;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
enum class BehaviorState : uint32_t;
 | 
			
		||||
 | 
			
		||||
class AMFArrayValue;
 | 
			
		||||
@@ -25,6 +29,8 @@ 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;
 | 
			
		||||
	void Deserialize(const tinyxml2::XMLElement& behavior);
 | 
			
		||||
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,20 @@ 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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void State::Deserialize(const tinyxml2::XMLElement& state) {
 | 
			
		||||
	for (const auto* stripElement = state.FirstChildElement("Strip"); stripElement; stripElement = stripElement->NextSiblingElement("Strip")) {
 | 
			
		||||
		auto& strip = m_Strips.emplace_back();
 | 
			
		||||
		strip.Deserialize(*stripElement);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -3,6 +3,10 @@
 | 
			
		||||
 | 
			
		||||
#include "Strip.h"
 | 
			
		||||
 | 
			
		||||
namespace tinyxml2 {
 | 
			
		||||
	class XMLElement;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AMFArrayValue;
 | 
			
		||||
 | 
			
		||||
class State {
 | 
			
		||||
@@ -13,6 +17,8 @@ public:
 | 
			
		||||
	void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
 | 
			
		||||
	bool IsEmpty() const;
 | 
			
		||||
 | 
			
		||||
	void Serialize(tinyxml2::XMLElement& state) const;
 | 
			
		||||
	void Deserialize(const tinyxml2::XMLElement& state);
 | 
			
		||||
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,25 @@ 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);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Strip::Deserialize(const tinyxml2::XMLElement& strip) {
 | 
			
		||||
	const auto* positionElement = strip.FirstChildElement("Position");
 | 
			
		||||
	if (positionElement) {
 | 
			
		||||
		m_Position.Deserialize(*positionElement);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for (const auto* actionElement = strip.FirstChildElement("Action"); actionElement; actionElement = actionElement->NextSiblingElement("Action")) {
 | 
			
		||||
		auto& action = m_Actions.emplace_back();
 | 
			
		||||
		action.Deserialize(*actionElement);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -6,6 +6,10 @@
 | 
			
		||||
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
namespace tinyxml2 {
 | 
			
		||||
	class XMLElement;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
class AMFArrayValue;
 | 
			
		||||
 | 
			
		||||
class Strip {
 | 
			
		||||
@@ -16,6 +20,8 @@ public:
 | 
			
		||||
	void SendBehaviorBlocksToClient(AMFArrayValue& args) const;
 | 
			
		||||
	bool IsEmpty() const noexcept { return m_Actions.empty(); }
 | 
			
		||||
 | 
			
		||||
	void Serialize(tinyxml2::XMLElement& strip) const;
 | 
			
		||||
	void Deserialize(const tinyxml2::XMLElement& strip);
 | 
			
		||||
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; 
 | 
			
		||||
		Reference in New Issue
	
	Block a user