Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

Code Block
breakoutModewide
#include "CustomMetadataReplicatorModule.h"
#include "AtomsConversions.h"
#include "Actors/AtomsAgentGroup.h"
#include "CustomMetadataReplicatorComponent.h"
#include "Components/AgentCollisionComponent.h"
#include "GameFramework/GameStateBase.h"
#include <AtomsCore/Metadata/Vector2Metadata.h>
#include <AtomsCore/Metadata/Vector2fMetadata.h>
#include <AtomsCore/Metadata/Vector3Metadata.h>
#include <AtomsCore/Metadata/Vector3fMetadata.h>
#include <AtomsCore/Metadata/Vector4Metadata.h>
#include <AtomsCore/Metadata/Vector4fMetadata.h>
#include <AtomsCore/Metadata/Vector3ArrayMetadata.h>
#include <AtomsCore/Metadata/Vector2fArrayMetadata.h>
#include <AtomsCore/Metadata/Vector3fArrayMetadata.h>
#include <AtomsCore/Metadata/Vector4fArrayMetadata.h>
#include <AtomsCore/Metadata/Vector4ArrayMetadata.h>
#include <AtomsCore/Metadata/MatrixMetadata.h>
#include <AtomsCore/Metadata/MatrixfMetadata.h>
#include <AtomsCore/Metadata/MatrixArrayMetadata.h>
#include <AtomsCore/Metadata/MatrixfArrayMetadata.h>
#include <AtomsCore/Metadata/QuaternionMetadata.h>
#include <AtomsCore/Metadata/QuaternionfMetadata.h>
#include <AtomsCore/Metadata/QuaternionArrayMetadata.h>
#include <AtomsCore/Metadata/QuaternionfArrayMetadata.h>
#include <AtomsCore/Metadata/EulerMetadata.h>
#include <AtomsCore/Metadata/EulerfMetadata.h>
#include <AtomsCore/Metadata/EulerArrayMetadata.h>
#include <AtomsCore/Metadata/EulerfArrayMetadata.h>
#include <AtomsCore/Metadata/IntMetadata.h>
#include <AtomsCore/Metadata/IntArrayMetadata.h>
#include <AtomsCore/Metadata/DoubleMetadata.h>
#include <AtomsCore/Metadata/DoubleArrayMetadata.h>
#include <AtomsCore/Metadata/FloatMetadata.h>
#include <AtomsCore/Metadata/FloatArrayMetadata.h>
#include <AtomsCore/Metadata/MapMetadata.h>
#include <AtomsCore/Metadata/BoolMetadata.h>
#include <AtomsCore/Metadata/BoolArrayMetadata.h>
#include <AtomsCore/Metadata/StringMetadata.h>
#include <AtomsCore/Metadata/StringArrayMetadata.h>
#include <AtomsCore/Metadata/Box3Metadata.h>
#include <AtomsCore/Metadata/ArrayMetadata.h>
#include <AtomsUtils/TaskScheduler.h>
#include <Atoms/Agent.h>
#include <Atoms/AgentGroup.h>
#include <Atoms/GlobalNames.h>
#include <AtomsUtils/Memory.h>

CustomMetadataReplicatorModule::CustomMetadataReplicatorModule() : Atoms::BehaviourModule(),
m_world(nullptr),
m_replicatorComponent(nullptr),
serverStarted(false)
{
	AtomsCore::StringMetadata metadataNames;
	addAttribute("MetadataName", &metadataNames);

	AtomsCore::BoolMetadata enable(false);
	addAttribute("EnableReplication", &enable, true);
}

CustomMetadataReplicatorModule::~CustomMetadataReplicatorModule()
{

}

void CustomMetadataReplicatorModule::initSimulation(Atoms::AgentGroup* agentGroup)
{
	elapsedTime = 0.0;
}


void CustomMetadataReplicatorModule::agentsCreated(const std::vector<Atoms::Agent *>& agents, Atoms::AgentGroup* agentGroup)
{
	if (!m_replicatorComponent)
		return;
	for (auto* agent : agents)
	{
		AtomsCore::BoolMetadata hasAutority(m_replicatorComponent->GetOwnerRole() == ROLE_Authority);
		agent->metadata().addEntry("hasAutority", &hasAutority);
	}
}

size_t CustomMetadataReplicatorModule::GetDataOffset(size_t TypeId) const
{
	switch (TypeId)
	{
	case kMetadataBoolTypeId:
	case kMetadataStringTypeId:
	case kMetadataIntTypeId:
	case kMetadataDoubleTypeId:
	case kMetadataFloatTypeId:
	{
		return 1;
		break;
	}
	case kMetadataVector3fTypeId:
	case kMetadataVector3TypeId:
	case kMetadataEulerfTypeId:
	case kMetadataEulerTypeId:
	{
		return 3;
		break;
	}
	case kMetadataVector4fTypeId:
	case kMetadataVector4TypeId:
	case kMetadataQuaternionfTypeId:
	case kMetadataQuaternionTypeId:
	{
		return 4;
		break;
	}
	case kMetadataMatrixfTypeId:
	{
		return 16;
		break;
	}
	default:
		break;
	}

	return 0;
}

void CustomMetadataReplicatorModule::AdvanceCounter(size_t TypeId, size_t& dataCounter) const
{
	dataCounter += GetDataOffset(TypeId);
}

void CustomMetadataReplicatorModule::initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup)
{
	if (!m_replicatorComponent)
		return;

	auto metadataName = attributes().getTypedEntry<AtomsCore::StringMetadata>("MetadataName");
	if (!metadataName)
		return;

	auto metadataInterpolation = attributes().getTypedEntry<AtomsCore::BoolMetadata>("MetadataInterpolation");
	if (!metadataInterpolation)
		return;

	auto enable = attributes().getTypedEntry<AtomsCore::BoolMetadata>("EnableReplication")->value();
	auto enableOverride = attributes().getTypedEntry<AtomsCore::MapMetadata>("EnableReplication_override");
	const std::string enableMetaName = name() + "@EnableReplication";
	bool interpolation = metadataInterpolation->value();
	double currentSeconds = agentGroup->simulationTime().seconds();
	
	if (m_replicatorComponent->GetOwnerRole() == ROLE_Authority)
	{
		if (!m_replicatorComponent->EnableReplication)
			return;

		if (m_replicatorComponent->UseRpc)
		{
			if (currentSeconds - elapsedTime < m_replicatorComponent->UpdateTime)
			{
				return;
			}
		}
		elapsedTime = agentGroup->simulationTime().seconds();

		FMetadataReplicatorState State;
		State.SimulationTime = agentGroup->simulationTime().time();
		bool typeIdFound = false;
		for (size_t i = 0; i < agents.size(); ++i)
		{
			auto* agent = agents[i];
			if (!agent)
				continue;
			auto CurrentData = agent->metadata().getEntry(metadataName->get());
			if (!CurrentData)
				continue;

			if (!getAttributePerAgent(enable, enableOverride.get(), agent->groupIdStr()->value(), agent->metadata(), enableMetaName))
				continue;

			if (!typeIdFound)
				State.TypeId = CurrentData->typeId();

			if (State.TypeId != CurrentData->typeId())
				continue;

			if (!agent->groupId())
				continue;

			State.Ids.Add(agent->groupId()->value());

			switch (CurrentData->typeId())
			{
			case kMetadataBoolTypeId:
			{
				State.ValueMetadata.Add(std::static_pointer_cast<AtomsCore::BoolMetadata>(CurrentData)->get() ? 1.0 : 0.0);
				break;
			}
			case kMetadataIntTypeId:
			{
				State.ValueMetadata.Add(std::static_pointer_cast<AtomsCore::IntMetadata>(CurrentData)->get());
				break;
			}
			case kMetadataFloatTypeId:
			{
				State.ValueMetadata.Add(std::static_pointer_cast<AtomsCore::FloatMetadata>(CurrentData)->get());
				break;
			}
			case kMetadataDoubleTypeId:
			{
				State.ValueMetadata.Add(std::static_pointer_cast<AtomsCore::DoubleMetadata>(CurrentData)->get());
				break;
			}
			case kMetadataStringTypeId:
			{
				State.StringMetadata.Add(std::static_pointer_cast<AtomsCore::StringMetadata>(CurrentData)->get().c_str());
				break;
			}
			case kMetadataVector2fTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::Vector2fMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				break;
			}
			case kMetadataVector2TypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::Vector2Metadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				break;
			}
			case kMetadataVector3fTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::Vector3fMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				State.ValueMetadata.Add(value.z);
				break;
			}
			case kMetadataVector3TypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::Vector3Metadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				State.ValueMetadata.Add(value.z);
				break;
			}
			case kMetadataVector4fTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::Vector4fMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				State.ValueMetadata.Add(value.z);
				State.ValueMetadata.Add(value.w);
				break;
			}
			case kMetadataVector4TypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::Vector4Metadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				State.ValueMetadata.Add(value.z);
				State.ValueMetadata.Add(value.w);
				break;
			}
			case kMetadataMatrixfTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::MatrixfMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value[0][0]);
				State.ValueMetadata.Add(value[0][1]);
				State.ValueMetadata.Add(value[0][2]);
				State.ValueMetadata.Add(value[0][3]);
				State.ValueMetadata.Add(value[1][0]);
				State.ValueMetadata.Add(value[1][1]);
				State.ValueMetadata.Add(value[1][2]);
				State.ValueMetadata.Add(value[1][3]);
				State.ValueMetadata.Add(value[2][0]);
				State.ValueMetadata.Add(value[2][1]);
				State.ValueMetadata.Add(value[2][2]);
				State.ValueMetadata.Add(value[2][3]);
				State.ValueMetadata.Add(value[3][0]);
				State.ValueMetadata.Add(value[3][1]);
				State.ValueMetadata.Add(value[3][2]);
				State.ValueMetadata.Add(value[3][3]);
				break;
			}
			case kMetadataMatrixTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::MatrixMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value[0][0]);
				State.ValueMetadata.Add(value[0][1]);
				State.ValueMetadata.Add(value[0][2]);
				State.ValueMetadata.Add(value[0][3]);
				State.ValueMetadata.Add(value[1][0]);
				State.ValueMetadata.Add(value[1][1]);
				State.ValueMetadata.Add(value[1][2]);
				State.ValueMetadata.Add(value[1][3]);
				State.ValueMetadata.Add(value[2][0]);
				State.ValueMetadata.Add(value[2][1]);
				State.ValueMetadata.Add(value[2][2]);
				State.ValueMetadata.Add(value[2][3]);
				State.ValueMetadata.Add(value[3][0]);
				State.ValueMetadata.Add(value[3][1]);
				State.ValueMetadata.Add(value[3][2]);
				State.ValueMetadata.Add(value[3][3]);
				break;
			}
			case kMetadataEulerfTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::EulerfMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				State.ValueMetadata.Add(value.z);
				break;
			}
			case kMetadataEulerTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::EulerMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.x);
				State.ValueMetadata.Add(value.y);
				State.ValueMetadata.Add(value.z);
				break;
			}
			case kMetadataQuaternionfTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::QuaternionfMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.v.x);
				State.ValueMetadata.Add(value.v.y);
				State.ValueMetadata.Add(value.v.z);
				State.ValueMetadata.Add(value.r);
				break;
			}
			case kMetadataQuaternionTypeId:
			{
				auto value = std::static_pointer_cast<AtomsCore::QuaternionMetadata>(CurrentData)->get();
				State.ValueMetadata.Add(value.v.x);
				State.ValueMetadata.Add(value.v.y);
				State.ValueMetadata.Add(value.v.z);
				State.ValueMetadata.Add(value.r);
				break;
			}
			default:
				break;
			}

		}

		if (m_replicatorComponent->EnableReplication && m_replicatorComponent->UseRpc)
		{
			if (m_replicatorComponent->UseReliableRpc)
				m_replicatorComponent->UpdatedMetadata(State);	
			else
				m_replicatorComponent->UnreliableUpdatedMetadata(State);
		}
		else
		{
			m_replicatorComponent->ReplicatedMetadata = State;
		}
	}
	else if (m_replicatorComponent->ClientReplicatedData.Num() > 1)
	{
		auto Actor = m_replicatorComponent->GetOwner();
		if (!Actor)
			return;

		UAgentCollisionComponent* CollisionComponent = nullptr;
#if ENGINE_MINOR_VERSION < 24
		auto ExistingRagdollComponents = Actor->GetComponentsByClass(UAgentCollisionComponent::StaticClass());
#else
		TArray<UActorComponent*> ExistingRagdollComponents;
		Actor->GetComponents(UAgentCollisionComponent::StaticClass(), ExistingRagdollComponents);
#endif
		for (auto It : ExistingRagdollComponents)
		{
			auto RagdollComp = Cast<UAgentCollisionComponent>(It);
			if (RagdollComp)
			{
				CollisionComponent = RagdollComp;
				break;
			}
		}

		// if there is no data just exit
		if (m_replicatorComponent->ClientReplicatedData.Num() == 0)
			return;

		float CurrentSimTime = agentGroup->simulationTime().time();
		
		auto* Tail = m_replicatorComponent->ClientReplicatedData.GetTail();
		auto& LastData = Tail->GetValue();
		
		while (Tail->GetPrevNode())
		{
			auto* NextNode = Tail->GetPrevNode();
			if (!NextNode)
				break;

			auto CurrTime = Tail->GetValue().SimulationTime;
			auto NextTime = NextNode->GetValue().SimulationTime;
			if (CurrentSimTime > NextTime)
			{
				m_replicatorComponent->ClientReplicatedData.RemoveNode(Tail);
			}

			Tail = NextNode;
		}

		Tail = m_replicatorComponent->ClientReplicatedData.GetTail();
		auto* Head = Tail->GetPrevNode();
		if (!Tail)
			return;
		

		float TimeTail = Tail->GetValue().SimulationTime;
		float TimeHead = Head ? Head->GetValue().SimulationTime : CurrentSimTime;

		float Alpha = (TimeHead > TimeTail) ? (CurrentSimTime - TimeTail) / (TimeHead - TimeTail) : 0.0;

		auto& TailState = Tail->GetValue();
		auto& ids = TailState.Ids;
		if (ids.Num() == 0)
			return;

		size_t DataStride = GetDataOffset(TailState.TypeId);

		size_t dataCounter = 0;
		for (size_t i = 0; i < ids.Num(); ++i)
		{
			auto agentId = ids[i];
			auto* agent = agentGroup->agent(agentId);
			if (!agent)
			{
				dataCounter += DataStride;
				continue;
			}

			auto CurrentData = agent->metadata().getEntry(metadataName->get());
			if (!CurrentData)
			{
				dataCounter += DataStride;
				continue;
			}

			// the metadata type id doesn't metch so continue;
			if (CurrentData->typeId() != TailState.TypeId)
			{
				dataCounter += DataStride;
				continue;
			}

			int32 HeadOffset = INDEX_NONE;
			FMetadataReplicatorState* HeadState = nullptr;
			if (interpolation && Head)
			{
				HeadState = &Head->GetValue();
				HeadOffset = HeadState->Ids.Find(agentId);
				if (HeadOffset == INDEX_NONE)
				{
					continue;
				}

				HeadOffset *= DataStride;
			}

			if (TailState.ValueMetadata.Num() <= (dataCounter + DataStride))
			{
				break;
			}

			switch (TailState.TypeId)
			{
			case kMetadataBoolTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::BoolMetadata>(CurrentData);
				agentData->set(TailState.ValueMetadata[dataCounter++] > 0.1);
				break;
			}
			case kMetadataStringTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::StringMetadata>(CurrentData);
				agentData->get() = TCHAR_TO_UTF8(*TailState.StringMetadata[dataCounter++]);
				break;
			}
			case kMetadataIntTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::IntMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get() = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get() = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					dataCounter += 1;
				}

				break;
			}
			case kMetadataDoubleTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::DoubleMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get() = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get() = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					dataCounter += 1;
				}
				break;
			}
			case kMetadataFloatTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::FloatMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get() = TailState.ValueMetadata[dataCounter++];
				}
				else 
				{
					agentData->get() = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					dataCounter += 1;
				}
				break;
			}
			case kMetadataVector3fTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::Vector3fMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter++];
					agentData->get().y = TailState.ValueMetadata[dataCounter++];
					agentData->get().z = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					dataCounter += 3;
				}

				if (CollisionComponent && metadataName->get() == Atoms::GlobalNameKeys::ATOMS_AGENT_POSITION_KEY)
				{
					auto InstanceBody = CollisionComponent->InstanceBodies.Find(agentId);
					if (InstanceBody && *InstanceBody)
					{
						auto CurrentTransform = (*InstanceBody)->GetUnrealWorldTransform();
						CurrentTransform.SetLocation(AtomsConverter::FromVector3(agentData->get()));
						(*InstanceBody)->SetBodyTransform(CurrentTransform, ETeleportType::TeleportPhysics, false);
					}
				}
				break;
			}
			case kMetadataVector3TypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::Vector3Metadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter++];
					agentData->get().y = TailState.ValueMetadata[dataCounter++];
					agentData->get().z = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					dataCounter += 3;
				}

				if (CollisionComponent && metadataName->get() == Atoms::GlobalNameKeys::ATOMS_AGENT_POSITION_KEY)
				{
					auto InstanceBody = CollisionComponent->InstanceBodies.Find(agentId);
					if (InstanceBody && *InstanceBody)
					{
						auto CurrentTransform = (*InstanceBody)->GetUnrealWorldTransform();
						CurrentTransform.SetLocation(AtomsConverter::FromVector3(agentData->get()));
						(*InstanceBody)->SetBodyTransform(CurrentTransform, ETeleportType::TeleportPhysics, false);
					}
				}
				break;
			}
			case kMetadataEulerfTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::EulerfMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter++];
					agentData->get().y = TailState.ValueMetadata[dataCounter++];
					agentData->get().z = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					dataCounter += 3;
				}
				break;
			}
			case kMetadataEulerTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::EulerMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter++];
					agentData->get().y = TailState.ValueMetadata[dataCounter++];
					agentData->get().z = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					dataCounter += 3;
				}
				break;
			}
			case kMetadataVector4fTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::Vector4fMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter++];
					agentData->get().y = TailState.ValueMetadata[dataCounter++];
					agentData->get().z = TailState.ValueMetadata[dataCounter++];
					agentData->get().w = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					agentData->get().w = TailState.ValueMetadata[dataCounter + 3] + Alpha * (HeadState->ValueMetadata[HeadOffset + 3] - TailState.ValueMetadata[dataCounter + 3]);
					dataCounter += 4;
				}
				break;
			}
			case kMetadataVector4TypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::Vector4Metadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter++];
					agentData->get().y = TailState.ValueMetadata[dataCounter++];
					agentData->get().z = TailState.ValueMetadata[dataCounter++];
					agentData->get().w = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					agentData->get().w = TailState.ValueMetadata[dataCounter + 3] + Alpha * (HeadState->ValueMetadata[HeadOffset + 3] - TailState.ValueMetadata[dataCounter + 3]);
					dataCounter += 4;
				}
				break;
			}
			case kMetadataQuaternionfTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::QuaternionfMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().v.x = TailState.ValueMetadata[dataCounter++];
					agentData->get().v.y = TailState.ValueMetadata[dataCounter++];
					agentData->get().v.z = TailState.ValueMetadata[dataCounter++];
					agentData->get().r = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().v.x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().v.y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().v.z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					agentData->get().r = TailState.ValueMetadata[dataCounter + 3] + Alpha * (HeadState->ValueMetadata[HeadOffset + 3] - TailState.ValueMetadata[dataCounter + 3]);
					dataCounter += 4;
				}
				break;
			}
			case kMetadataQuaternionTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::QuaternionMetadata>(CurrentData);
				if (!HeadState)
				{
					agentData->get().v.x = TailState.ValueMetadata[dataCounter++];
					agentData->get().v.y = TailState.ValueMetadata[dataCounter++];
					agentData->get().v.z = TailState.ValueMetadata[dataCounter++];
					agentData->get().r = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					agentData->get().v.x = TailState.ValueMetadata[dataCounter] + Alpha * (HeadState->ValueMetadata[HeadOffset] - TailState.ValueMetadata[dataCounter]);
					agentData->get().v.y = TailState.ValueMetadata[dataCounter + 1] + Alpha * (HeadState->ValueMetadata[HeadOffset + 1] - TailState.ValueMetadata[dataCounter + 1]);
					agentData->get().v.z = TailState.ValueMetadata[dataCounter + 2] + Alpha * (HeadState->ValueMetadata[HeadOffset + 2] - TailState.ValueMetadata[dataCounter + 2]);
					agentData->get().r = TailState.ValueMetadata[dataCounter + 3] + Alpha * (HeadState->ValueMetadata[HeadOffset + 3] - TailState.ValueMetadata[dataCounter + 3]);
					dataCounter += 4;
				}
				break;
			}
			case kMetadataMatrixfTypeId:
			{
				auto agentData = std::static_pointer_cast<AtomsCore::MatrixfMetadata>(CurrentData);
				if (!HeadState)
				{
					for (int32 xi = 0; xi < 4; ++xi)
						for (int32 yi = 0; yi < 4; ++yi)
							agentData->get()[xi][yi] = TailState.ValueMetadata[dataCounter++];
				}
				else
				{
					for (int32 xi = 0; xi < 4; ++xi)
						for (int32 yi = 0; yi < 4; ++yi)
							agentData->get()[xi][yi] = TailState.ValueMetadata[dataCounter + xi * 4 + yi] + Alpha * (HeadState->ValueMetadata[HeadOffset + xi * 4 + yi] - TailState.ValueMetadata[dataCounter + xi * 4 + yi]);
					dataCounter += 15;
				}
				break;
			}
			default:
				break;
			}
		}

		if (!serverStarted)
		{

			auto* UniqueHead = m_replicatorComponent->ClientReplicatedData.GetHead();
			if (UniqueHead)
			{
				float ServerTime = UniqueHead->GetValue().SimulationTime;
				auto AgentGroupActor = Cast<AAtomsAgentGroup>(Actor);
				if (AgentGroupActor)
					AgentGroupActor->CurrentFrame = ServerTime;
			}
			serverStarted = true;
		}
	}
	
}

Atoms::BehaviourModule* CustomMetadataReplicatorModule::creator(const std::string& parameter)
{
	return new MetadataReplicatorModule();
}

UCustomMetadataReplicatorComponent* CustomMetadataReplicatorModule::GetReplicatorComponent()
{
	return m_replicatorComponent;
}

...