Writing a Metadata Replicator module

Atoms Behaviour Module

// =========================================================================== // Copyright (c) 2015 Toolchefs Ltd. All rights reserved. // // Use of this software is subject to the terms of the Toolchefs license // agreement provided at the time of installation or download, or which // otherwise accompanies this software in either electronic or hard copy form. // =========================================================================== #pragma once #include <Atoms/BehaviourModule.h> class UWorld; class AActor; class UAgentCollisionComponent; class CustomMetadataReplicatorModule : public Atoms::BehaviourModule { public: CustomMetadataReplicatorModule (); virtual ~CustomMetadataReplicatorModule (); void initSimulation(Atoms::AgentGroup* agentGroup) override; void agentsCreated(const std::vector<Atoms::Agent *>& agents, Atoms::AgentGroup* agentGroup = nullptr) override; void initFrame(const std::vector<Atoms::Agent*>& agents, Atoms::AgentGroup* agentGroup = nullptr) override; static Atoms::BehaviourModule* creator(const std::string& parameter); void setWorld(UWorld* world) { m_world = world; } void setMetadataReplicatorComponent(class UCustomMetadataReplicatorComponent* component) { m_replicatorComponent = component; } UCustomMetadataReplicatorComponent* GetReplicatorComponent(); void AdvanceCounter(size_t TypeId, size_t& dataCounter) const; size_t GetDataOffset(size_t TypeId) const; private: UWorld* m_world; class UCustomMetadataReplicatorComponent* m_replicatorComponent; double elapsedTime; bool serverStarted; };

 

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

Unreal Component

#pragma once #include "CoreMinimal.h" #include "Components/AtomsBehaviourComponent.h" #include <Atoms/AgentGroup.h> #include "CustomMetadataReplicatorComponent.generated.h" USTRUCT() struct FMetadataReplicatorState { GENERATED_USTRUCT_BODY() UPROPERTY() int32 TypeId; UPROPERTY() float SimulationTime; UPROPERTY() TArray<int32> Ids; UPROPERTY() TArray<float> ValueMetadata; UPROPERTY() TArray<FString> StringMetadata; }; UCLASS(ClassGroup = (AtomsBehaviours), meta = (DisplayName = "Metadata Replicator", BlueprintSpawnableComponent)) class ATOMSUNREAL_API UCustomMetadataReplicatorComponent : public UAtomsBehaviourComponent { GENERATED_BODY() public: UCustomMetadataReplicatorComponent(); ~UCustomMetadataReplicatorComponent(); virtual void OnBehaviourModuleCreated(AtomsPtr<Atoms::BehaviourModule>& module) override;