Merge pull request #4425 from aws-lumberyard-dev/NetHierarchyInput
Network hierarchy input processing
This commit is contained in:
@@ -175,6 +175,11 @@ if (PAL_TRAIT_BUILD_TESTS_SUPPORTED)
|
||||
PRIVATE
|
||||
AZ::AzTest
|
||||
Gem::Multiplayer.Static
|
||||
AUTOGEN_RULES
|
||||
*.AutoComponent.xml,AutoComponent_Header.jinja,$path/$fileprefix.AutoComponent.h
|
||||
*.AutoComponent.xml,AutoComponent_Source.jinja,$path/$fileprefix.AutoComponent.cpp
|
||||
*.AutoComponent.xml,AutoComponentTypes_Header.jinja,$path/AutoComponentTypes.h
|
||||
*.AutoComponent.xml,AutoComponentTypes_Source.jinja,$path/AutoComponentTypes.cpp
|
||||
)
|
||||
ly_add_googletest(
|
||||
NAME Gem::Multiplayer.Tests
|
||||
|
||||
+2
@@ -71,6 +71,8 @@ namespace Multiplayer
|
||||
void UpdateAutonomous(AZ::TimeMs deltaTimeMs);
|
||||
void UpdateBankedTime(AZ::TimeMs deltaTimeMs);
|
||||
|
||||
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
|
||||
|
||||
using StateHistoryItem = AZStd::unique_ptr<AzNetworking::StringifySerializer>;
|
||||
AZStd::map<ClientInputId, StateHistoryItem> m_predictiveStateHistory;
|
||||
|
||||
|
||||
@@ -29,6 +29,8 @@ namespace Multiplayer
|
||||
, public NetworkHierarchyRequestBus::Handler
|
||||
{
|
||||
friend class NetworkHierarchyChildComponent;
|
||||
friend class NetworkHierarchyRootComponentController;
|
||||
friend class ServerToClientReplicationWindow;
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(Multiplayer::NetworkHierarchyRootComponent, s_networkHierarchyRootComponentConcreteUuid, Multiplayer::NetworkHierarchyRootComponentBase);
|
||||
|
||||
@@ -57,6 +59,8 @@ namespace Multiplayer
|
||||
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
|
||||
//! @}
|
||||
|
||||
bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer);
|
||||
|
||||
protected:
|
||||
void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot);
|
||||
|
||||
@@ -99,4 +103,24 @@ namespace Multiplayer
|
||||
|
||||
friend class HierarchyBenchmarkBase;
|
||||
};
|
||||
|
||||
|
||||
//! NetworkHierarchyRootComponentController
|
||||
//! This is the network controller for NetworkHierarchyRootComponent.
|
||||
//! Class provides the ability to process input for hierarchies.
|
||||
class NetworkHierarchyRootComponentController final
|
||||
: public NetworkHierarchyRootComponentControllerBase
|
||||
{
|
||||
public:
|
||||
NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent);
|
||||
|
||||
// NetworkHierarchyRootComponentControllerBase
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
//! MultiplayerController interface
|
||||
Multiplayer::MultiplayerController::InputPriorityOrder GetInputOrder() const override;
|
||||
void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
};
|
||||
}
|
||||
|
||||
+1
@@ -9,6 +9,7 @@
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
<ComponentRelation Constraint="Weak" HasController="true" Name="NetworkHierarchyRootComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkHierarchyRootComponent.h" />
|
||||
|
||||
<Include File="Multiplayer/MultiplayerTypes.h"/>
|
||||
<Include File="Multiplayer/NetworkInput/NetworkInput.h"/>
|
||||
|
||||
+6
-2
@@ -4,11 +4,15 @@
|
||||
Name="NetworkHierarchyRootComponent"
|
||||
Namespace="Multiplayer"
|
||||
OverrideComponent="true"
|
||||
OverrideController="false"
|
||||
OverrideController="true"
|
||||
OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<Include File="Source/NetworkInput/NetworkInputChild.h"/>
|
||||
|
||||
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
|
||||
|
||||
|
||||
<NetworkInput Type="NetworkInputChildList" Name="ChildInputs" Init="" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="true" />
|
||||
|
||||
<NetworkProperty Type="NetEntityId" Name="hierarchyRoot" Init="InvalidNetEntityId" ReplicateFrom="Authority" ReplicateTo="Client" IsRewindable="false" IsPredictable="false" IsPublic="true" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="true" />
|
||||
</Component>
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
#include <AzNetworking/Serialization/NetworkOutputSerializer.h>
|
||||
#include <AzNetworking/Serialization/StringifySerializer.h>
|
||||
#include <AzNetworking/Serialization/TrackChangedSerializer.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -211,7 +212,7 @@ namespace Multiplayer
|
||||
m_lastCorrectionSentTimeMs = currentTimeMs;
|
||||
|
||||
AzNetworking::HashSerializer hashSerializer;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(hashSerializer);
|
||||
SerializeEntityCorrection(hashSerializer);
|
||||
|
||||
const AZ::HashValue32 localAuthorityHash = hashSerializer.GetHash();
|
||||
|
||||
@@ -233,7 +234,7 @@ namespace Multiplayer
|
||||
// only deserialize if we have data (for client/server profile/debug mismatches)
|
||||
if (correction.GetSize() > 0)
|
||||
{
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
SerializeEntityCorrection(serializer);
|
||||
}
|
||||
|
||||
correction.Resize(serializer.GetSize());
|
||||
@@ -313,7 +314,7 @@ namespace Multiplayer
|
||||
|
||||
// Apply the correction
|
||||
AzNetworking::TrackChangedSerializer<AzNetworking::NetworkOutputSerializer> serializer(correction.GetBuffer(), static_cast<uint32_t>(correction.GetSize()));
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
SerializeEntityCorrection(serializer);
|
||||
GetNetBindComponent()->NotifyCorrection();
|
||||
|
||||
#ifndef AZ_RELEASE_BUILD
|
||||
@@ -325,7 +326,7 @@ namespace Multiplayer
|
||||
{
|
||||
// Read out state values
|
||||
AzNetworking::StringifySerializer serverValues;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(serverValues);
|
||||
SerializeEntityCorrection(serverValues);
|
||||
PrintCorrectionDifferences(*iter->second, serverValues);
|
||||
}
|
||||
else
|
||||
@@ -452,7 +453,7 @@ namespace Multiplayer
|
||||
|
||||
// Generate a hash based on the current client predicted states
|
||||
AzNetworking::HashSerializer hashSerializer;
|
||||
GetNetBindComponent()->SerializeEntityCorrection(hashSerializer);
|
||||
SerializeEntityCorrection(hashSerializer);
|
||||
|
||||
// Save this input and discard move history outside our client rewind window
|
||||
m_inputHistory.PushBack(input);
|
||||
@@ -480,7 +481,7 @@ namespace Multiplayer
|
||||
{
|
||||
m_predictiveStateHistory.erase(m_predictiveStateHistory.begin());
|
||||
}
|
||||
GetNetBindComponent()->SerializeEntityCorrection(*inputHistory);
|
||||
SerializeEntityCorrection(*inputHistory);
|
||||
m_predictiveStateHistory.emplace(m_clientInputId, AZStd::move(inputHistory));
|
||||
}
|
||||
#endif
|
||||
@@ -493,6 +494,18 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
bool LocalPredictionPlayerInputComponentController::SerializeEntityCorrection(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
bool result = GetNetBindComponent()->SerializeEntityCorrection(serializer);
|
||||
|
||||
NetworkHierarchyRootComponent* hierarchyComponent = GetParent().GetNetworkHierarchyRootComponent();
|
||||
if (result && hierarchyComponent)
|
||||
{
|
||||
result = hierarchyComponent->SerializeEntityCorrection(serializer);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void LocalPredictionPlayerInputComponentController::UpdateBankedTime(AZ::TimeMs deltaTimeMs)
|
||||
{
|
||||
const double deltaTime = static_cast<double>(deltaTimeMs) / 1000.0;
|
||||
|
||||
@@ -326,4 +326,150 @@ namespace Multiplayer
|
||||
RebuildHierarchy();
|
||||
}
|
||||
}
|
||||
|
||||
NetworkHierarchyRootComponentController::NetworkHierarchyRootComponentController(NetworkHierarchyRootComponent& parent)
|
||||
: NetworkHierarchyRootComponentControllerBase(parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
Multiplayer::MultiplayerController::InputPriorityOrder NetworkHierarchyRootComponentController::GetInputOrder() const
|
||||
{
|
||||
return Multiplayer::MultiplayerController::InputPriorityOrder::SubEntities;
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::CreateInput(Multiplayer::NetworkInput& input, float deltaTime)
|
||||
{
|
||||
NetworkHierarchyRootComponent& component = GetParent();
|
||||
if(!component.IsHierarchicalRoot())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
const AZStd::vector<AZ::Entity*>& entities = component.m_hierarchicalEntities;
|
||||
|
||||
auto* networkInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>();
|
||||
networkInput->m_childInputs.clear();
|
||||
networkInput->m_childInputs.reserve(entities.size());
|
||||
|
||||
for (AZ::Entity* child : entities)
|
||||
{
|
||||
if(child == component.GetEntity())
|
||||
{
|
||||
continue; // Avoid infinite recursion
|
||||
}
|
||||
|
||||
NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId());
|
||||
AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
|
||||
ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId);
|
||||
NetBindComponent* netComp = childEntityHandle.GetNetBindComponent();
|
||||
|
||||
AZ_Assert(netComp, "No NetBindComponent, this should be impossible");
|
||||
// Validate we still have a controller and we aren't in the middle of removing them
|
||||
if (netComp->HasController())
|
||||
{
|
||||
NetworkInputChild subInput;
|
||||
subInput.Attach(childEntityHandle);
|
||||
subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId());
|
||||
|
||||
netComp->CreateInput(subInput.GetNetworkInput(), deltaTime);
|
||||
|
||||
// make sure our input sub commands have the same time as the original
|
||||
subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId());
|
||||
networkInput->m_childInputs.emplace_back(subInput);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkHierarchyRootComponentController::ProcessInput(Multiplayer::NetworkInput& input, float deltaTime)
|
||||
{
|
||||
if (auto* networkInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>())
|
||||
{
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
// Build a set of Net IDs for the children
|
||||
AZStd::unordered_set<NetEntityId> currentChildren;
|
||||
NetworkHierarchyRootComponent& component = GetParent();
|
||||
for (AZ::Entity* child : component.m_hierarchicalEntities)
|
||||
{
|
||||
if (child == component.GetEntity()) // Skip the root entity
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId());
|
||||
AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
currentChildren.insert(childNetEntitydId);
|
||||
}
|
||||
|
||||
// Process the input for the child entities
|
||||
for (NetworkInputChild& subInput : networkInput->m_childInputs)
|
||||
{
|
||||
const ConstNetworkEntityHandle& inputOwnerHandle = subInput.GetOwner();
|
||||
NetEntityId inputOwnerNetEntitydId = inputOwnerHandle.GetNetEntityId();
|
||||
|
||||
if (currentChildren.count(inputOwnerNetEntitydId) == 0)
|
||||
{
|
||||
// Skip the input for entities which are not a part of this hierarchy
|
||||
continue;
|
||||
}
|
||||
|
||||
ConstNetworkEntityHandle localEntityHandle = networkEntityManager->GetEntity(inputOwnerNetEntitydId);
|
||||
if (localEntityHandle.Exists())
|
||||
{
|
||||
auto* netComp = localEntityHandle.GetNetBindComponent();
|
||||
AZ_Assert(netComp, "No NetBindComponent, this should be impossible");
|
||||
// We do not rewind entity role changes, so make sure we are the correct role prior to processing
|
||||
if (netComp->HasController())
|
||||
{
|
||||
subInput.GetNetworkInput().SetClientInputId(input.GetClientInputId());
|
||||
netComp->ProcessInput(subInput.GetNetworkInput(), deltaTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool NetworkHierarchyRootComponent::SerializeEntityCorrection(AzNetworking::ISerializer& serializer)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
for (AZ::Entity* child : m_hierarchicalEntities)
|
||||
{
|
||||
if (child == GetEntity())
|
||||
{
|
||||
// Skip the root entity
|
||||
continue;
|
||||
}
|
||||
|
||||
NetEntityId childNetEntitydId = networkEntityManager->GetNetEntityIdById(child->GetId());
|
||||
AZ_Assert(childNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
|
||||
ConstNetworkEntityHandle childEntityHandle = networkEntityManager->GetEntity(childNetEntitydId);
|
||||
NetBindComponent* netBindComponent = childEntityHandle.GetNetBindComponent();
|
||||
AZ_Assert(netBindComponent, "No NetBindComponent, this should be impossible");
|
||||
|
||||
result = result && netBindComponent->SerializeEntityCorrection(serializer);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -685,12 +685,21 @@ namespace Multiplayer
|
||||
if (GetAgentType() == MultiplayerAgentType::ClientServer
|
||||
|| GetAgentType() == MultiplayerAgentType::DedicatedServer)
|
||||
{
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
|
||||
if (controlledEntity.Exists())
|
||||
INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab();
|
||||
for (auto& netEntity : entityList)
|
||||
{
|
||||
controlledEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
|
||||
if (netEntity.Exists())
|
||||
{
|
||||
netEntity.GetNetBindComponent()->SetOwningConnectionId(connection->GetConnectionId());
|
||||
}
|
||||
netEntity.Activate();
|
||||
}
|
||||
|
||||
NetworkEntityHandle controlledEntity;
|
||||
if (entityList.size() > 0)
|
||||
{
|
||||
controlledEntity = entityList[0];
|
||||
}
|
||||
controlledEntity.Activate();
|
||||
|
||||
connection->SetUserData(new ServerToClientConnectionData(connection, *this, controlledEntity));
|
||||
AZStd::unique_ptr<IReplicationWindow> window = AZStd::make_unique<ServerToClientReplicationWindow>(controlledEntity, connection);
|
||||
@@ -800,12 +809,16 @@ namespace Multiplayer
|
||||
// Spawn the default player for this host since the host is also a player (not a dedicated server)
|
||||
if (m_agentType == MultiplayerAgentType::ClientServer)
|
||||
{
|
||||
NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab();
|
||||
if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent())
|
||||
INetworkEntityManager::EntityList entityList = SpawnDefaultPlayerPrefab();
|
||||
|
||||
for (NetworkEntityHandle controlledEntity : entityList)
|
||||
{
|
||||
controlledEntityNetBindComponent->SetAllowAutonomy(true);
|
||||
if (NetBindComponent* controlledEntityNetBindComponent = controlledEntity.GetNetBindComponent())
|
||||
{
|
||||
controlledEntityNetBindComponent->SetAllowAutonomy(true);
|
||||
}
|
||||
controlledEntity.Activate();
|
||||
}
|
||||
controlledEntity.Activate();
|
||||
}
|
||||
|
||||
AZLOG_INFO("Multiplayer operating in %s mode", GetEnumString(m_agentType));
|
||||
@@ -1053,17 +1066,12 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
NetworkEntityHandle MultiplayerSystemComponent::SpawnDefaultPlayerPrefab()
|
||||
INetworkEntityManager::EntityList MultiplayerSystemComponent::SpawnDefaultPlayerPrefab()
|
||||
{
|
||||
PrefabEntityId playerPrefabEntityId(AZ::Name(static_cast<AZ::CVarFixedString>(sv_defaultPlayerSpawnAsset).c_str()));
|
||||
INetworkEntityManager::EntityList entityList = m_networkEntityManager.CreateEntitiesImmediate(playerPrefabEntityId, NetEntityRole::Authority, AZ::Transform::CreateIdentity(), Multiplayer::AutoActivate::DoNotActivate);
|
||||
|
||||
NetworkEntityHandle controlledEntity;
|
||||
if (!entityList.empty())
|
||||
{
|
||||
controlledEntity = entityList[0];
|
||||
}
|
||||
return controlledEntity;
|
||||
return entityList;
|
||||
}
|
||||
|
||||
void host([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments)
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Multiplayer
|
||||
void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds);
|
||||
void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom);
|
||||
void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector<Multiplayer::LongNetworkString, 32>& commands);
|
||||
NetworkEntityHandle SpawnDefaultPlayerPrefab();
|
||||
INetworkEntityManager::EntityList SpawnDefaultPlayerPrefab();
|
||||
|
||||
AZ_CONSOLEFUNC(MultiplayerSystemComponent, DumpStats, AZ::ConsoleFunctorFlags::Null, "Dumps stats for the current multiplayer session");
|
||||
|
||||
|
||||
@@ -32,8 +32,8 @@ namespace Multiplayer
|
||||
|
||||
if (entity)
|
||||
{
|
||||
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity);
|
||||
AZ_Assert(m_networkEntityTracker, "NetworkEntityTracker is not valid");
|
||||
m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(entity);
|
||||
if (m_netBindComponent != nullptr)
|
||||
{
|
||||
m_netEntityId = m_netBindComponent->GetNetEntityId();
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
|
||||
#include <Multiplayer/IMultiplayer.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <Pipeline/NetworkSpawnableHolderComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
@@ -272,15 +274,32 @@ namespace Multiplayer
|
||||
bool safeToExit = true;
|
||||
NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId);
|
||||
|
||||
// We also need special handling for the EntityHierarchyComponent as well, since related entities need to be migrated together
|
||||
//auto* hierarchyController = FindController<EntityHierarchyComponent::Authority>(nonConstExitingEntityPtr);
|
||||
//if (hierarchyController)
|
||||
//{
|
||||
// if (hierarchyController->GetParentRelatedEntity())
|
||||
// {
|
||||
// safeToExit = false;
|
||||
// }
|
||||
//}
|
||||
// We also need special handling for the NetworkHierarchy as well, since related entities need to be migrated together
|
||||
NetworkHierarchyRootComponentController* hierarchyRootController = entityHandle.FindController<NetworkHierarchyRootComponentController>();
|
||||
NetworkHierarchyChildComponentController* hierarchyChildController = entityHandle.FindController<NetworkHierarchyChildComponentController>();
|
||||
|
||||
// Find the root entity
|
||||
AZ::Entity* hierarchyRootEntity = nullptr;
|
||||
if (hierarchyRootController)
|
||||
{
|
||||
hierarchyRootEntity = hierarchyRootController->GetParent().GetHierarchicalRoot();
|
||||
}
|
||||
else if (hierarchyChildController)
|
||||
{
|
||||
hierarchyRootEntity = hierarchyChildController->GetParent().GetHierarchicalRoot();
|
||||
}
|
||||
|
||||
if (hierarchyRootEntity)
|
||||
{
|
||||
NetEntityId rootNetId = GetNetEntityIdById(hierarchyRootEntity->GetId());
|
||||
ConstNetworkEntityHandle rootEntityHandle = GetEntity(rootNetId);
|
||||
|
||||
// Check if the root entity is still tracked by this authority
|
||||
if (rootEntityHandle.Exists() && rootEntityHandle.GetNetBindComponent()->HasController())
|
||||
{
|
||||
safeToExit = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Validate that we aren't already planning to remove this entity
|
||||
if (safeToExit)
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
//! Max number of entities that can be children of our netbound player entity.
|
||||
static constexpr uint32_t MaxEntityHierarchyChildren = 16;
|
||||
|
||||
//! Used by the EntityHierarchyComponent. This component allows the gameplay programmer to specify inputs for dependent entities.
|
||||
//! Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify inputs for dependent entities.
|
||||
//! Since it is possible to for the Client/Server to disagree about the state of related entities,
|
||||
//! this network input encodes the entity that is associated with it.
|
||||
class NetworkInputChild
|
||||
@@ -37,4 +34,6 @@ namespace Multiplayer
|
||||
ConstNetworkEntityHandle m_owner;
|
||||
NetworkInput m_networkInput;
|
||||
};
|
||||
|
||||
using NetworkInputChildList = AZStd::vector<NetworkInputChild>;
|
||||
}
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace Multiplayer
|
||||
auto serializer = [](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool {
|
||||
AZ::IO::ByteContainerStream stream(&output);
|
||||
auto& asset = object.GetAsset();
|
||||
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_JSON, &asset, asset.GetType());
|
||||
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_BINARY, &asset, asset.GetType());
|
||||
};
|
||||
|
||||
auto&& [object, networkSpawnable] =
|
||||
|
||||
+22
-19
@@ -9,6 +9,7 @@
|
||||
#include <Source/ReplicationWindows/ServerToClientReplicationWindow.h>
|
||||
#include <Source/AutoGen/Multiplayer.AutoPackets.h>
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
|
||||
#include <AzFramework/Visibility/IVisibilitySystem.h>
|
||||
#include <AzCore/Component/TransformBus.h>
|
||||
#include <AzCore/Console/ILogger.h>
|
||||
@@ -174,11 +175,11 @@ namespace Multiplayer
|
||||
// Note: Do not add any Client entities after this point, otherwise you stomp over the Autonomous mode
|
||||
m_replicationSet[m_controlledEntity] = { NetEntityRole::Autonomous, 1.0f }; // Always replicate autonomous entities
|
||||
|
||||
//auto hierarchyController = FindController<EntityHierarchyComponent::Authority>(m_ControlledEntity);
|
||||
//if (hierarchyController != nullptr)
|
||||
//{
|
||||
// CollectControlledEntitiesRecursive(m_replicationSet, *hierarchyController);
|
||||
//}
|
||||
auto* hierarchyComponent = m_controlledEntity.FindComponent<NetworkHierarchyRootComponent>();
|
||||
if (hierarchyComponent != nullptr)
|
||||
{
|
||||
UpdateHierarchyReplicationSet(m_replicationSet, *hierarchyComponent);
|
||||
}
|
||||
}
|
||||
|
||||
AzNetworking::PacketId ServerToClientReplicationWindow::SendEntityUpdateMessages(NetworkEntityUpdateVector& entityUpdateVector)
|
||||
@@ -326,18 +327,20 @@ namespace Multiplayer
|
||||
}
|
||||
}
|
||||
|
||||
//void ServerToClientReplicationWindow::CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController)
|
||||
//{
|
||||
// auto controlledEnts = hierarchyController.GetChildrenRelatedEntities();
|
||||
// for (auto& controlledEnt : controlledEnts)
|
||||
// {
|
||||
// AZ_Assert(controlledEnt != nullptr, "We have lost a controlled entity unexpectedly");
|
||||
// replicationSet[controlledEnt.GetConstEntity()] = EntityReplicationData(EntityNetworkRoleT::e_Autonomous, EntityPrioritySystem::k_MaxPriority); // Always replicate controlled entities
|
||||
// auto hierarchyController = controlledEnt.FindController<EntityHierarchyComponent::Authority>();
|
||||
// if (hierarchyController != nullptr)
|
||||
// {
|
||||
// CollectControlledEntitiesRecursive(replicationSet, *hierarchyController);
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
void ServerToClientReplicationWindow::UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent)
|
||||
{
|
||||
INetworkEntityManager* networkEntityManager = AZ::Interface<INetworkEntityManager>::Get();
|
||||
AZ_Assert(networkEntityManager, "NetworkEntityManager must be created.");
|
||||
|
||||
for (const AZ::Entity* controlledEntity : hierarchyComponent.m_hierarchicalEntities)
|
||||
{
|
||||
NetEntityId controlledNetEntitydId = networkEntityManager->GetNetEntityIdById(controlledEntity->GetId());
|
||||
AZ_Assert(controlledNetEntitydId != InvalidNetEntityId, "Unable to find the hierarchy entity in Network Entity Manager");
|
||||
|
||||
ConstNetworkEntityHandle controlledEntityHandle = networkEntityManager->GetEntity(controlledNetEntitydId);
|
||||
AZ_Assert(controlledEntityHandle != nullptr, "We have lost a controlled entity unexpectedly");
|
||||
|
||||
replicationSet[controlledEntityHandle] = { NetEntityRole::Autonomous, 1.0f };
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,6 +20,7 @@
|
||||
namespace Multiplayer
|
||||
{
|
||||
class NetSystemComponent;
|
||||
class NetworkHierarchyRootComponent;
|
||||
|
||||
class ServerToClientReplicationWindow
|
||||
: public IReplicationWindow
|
||||
@@ -56,7 +57,7 @@ namespace Multiplayer
|
||||
void OnEntityActivated(AZ::Entity* entity);
|
||||
void OnEntityDeactivated(AZ::Entity* entity);
|
||||
|
||||
//void CollectControlledEntitiesRecursive(ReplicationSet& replicationSet, EntityHierarchyComponent::Authority& hierarchyController);
|
||||
void UpdateHierarchyReplicationSet(ReplicationSet& replicationSet, NetworkHierarchyRootComponent& hierarchyComponent);
|
||||
|
||||
void EvaluateConnection();
|
||||
void AddEntityToReplicationSet(ConstNetworkEntityHandle& entityHandle, float priority, float distanceSquared);
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<Component
|
||||
Name="TestMultiplayerComponent"
|
||||
Namespace="MultiplayerTest"
|
||||
OverrideComponent="true"
|
||||
OverrideController="true"
|
||||
OverrideInclude="Tests/TestMultiplayerComponent.h"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
|
||||
<NetworkInput Type="uint64_t" Name="OwnerId" Init="0" />
|
||||
|
||||
</Component>
|
||||
@@ -16,6 +16,8 @@
|
||||
#include <Multiplayer/Components/NetBindComponent.h>
|
||||
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <Source/NetworkInput/NetworkInputArray.h>
|
||||
#include <Source/NetworkEntity/NetworkEntityManager.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -175,10 +177,10 @@ namespace Multiplayer
|
||||
void CreateSimpleHierarchy(EntityInfo& root, EntityInfo& child)
|
||||
{
|
||||
PopulateHierarchicalEntity(root);
|
||||
SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Client);
|
||||
SetupEntity(root.m_entity, root.m_netId, NetEntityRole::Autonomous);
|
||||
|
||||
PopulateHierarchicalEntity(child);
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Client);
|
||||
SetupEntity(child.m_entity, child.m_netId, NetEntityRole::Autonomous);
|
||||
|
||||
// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
|
||||
SetParentIdOnNetworkTransform(child.m_entity, root.m_netId);
|
||||
@@ -330,7 +332,7 @@ namespace Multiplayer
|
||||
void CreateDeepHierarchyOnClient(EntityInfo& childOfChild)
|
||||
{
|
||||
PopulateHierarchicalEntity(childOfChild);
|
||||
SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Client);
|
||||
SetupEntity(childOfChild.m_entity, childOfChild.m_netId, NetEntityRole::Autonomous);
|
||||
|
||||
// we need a parent-id value to be present in NetworkTransformComponent (which is in client mode and doesn't have a controller)
|
||||
SetParentIdOnNetworkTransform(childOfChild.m_entity, m_childOfChild->m_netId);
|
||||
@@ -387,4 +389,66 @@ namespace Multiplayer
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(ClientDeepHierarchyTests, CreateProcessInputTest)
|
||||
{
|
||||
using MultiplayerTest::TestMultiplayerComponent;
|
||||
using MultiplayerTest::TestMultiplayerComponentController;
|
||||
using MultiplayerTest::TestMultiplayerComponentNetworkInput;
|
||||
|
||||
auto* rootNetBind = m_root->m_entity->FindComponent<NetBindComponent>();
|
||||
|
||||
NetworkInputArray inputArray(rootNetBind->GetEntityHandle());
|
||||
NetworkInput& input = inputArray[0];
|
||||
|
||||
const float deltaTime = 0.16f;
|
||||
rootNetBind->CreateInput(input, deltaTime);
|
||||
|
||||
auto ValidateCreatedInput = [](const NetworkInput& input, const HierarchyTests::EntityInfo& entityInfo)
|
||||
{
|
||||
// Validate test input for the root entity's TestMultiplayerComponent
|
||||
auto* testInput = input.FindComponentInput<TestMultiplayerComponentNetworkInput>();
|
||||
EXPECT_NE(testInput, nullptr);
|
||||
|
||||
auto* testMultiplayerComponent = entityInfo.m_entity->FindComponent<TestMultiplayerComponent>();
|
||||
EXPECT_NE(testMultiplayerComponent, nullptr);
|
||||
|
||||
EXPECT_EQ(testInput->m_ownerId, testMultiplayerComponent->GetId());
|
||||
};
|
||||
|
||||
// Validate root input
|
||||
ValidateCreatedInput(input, *m_root);
|
||||
|
||||
// Validate children input
|
||||
{
|
||||
NetworkHierarchyRootComponentNetworkInput* rootHierarchyInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>();
|
||||
const AZStd::vector<NetworkInputChild>& childInputs = rootHierarchyInput->m_childInputs;
|
||||
EXPECT_EQ(childInputs.size(), 2);
|
||||
ValidateCreatedInput(childInputs[0].GetNetworkInput(), *m_child);
|
||||
ValidateCreatedInput(childInputs[1].GetNetworkInput(), *m_childOfChild);
|
||||
}
|
||||
|
||||
// Test ProcessInput
|
||||
{
|
||||
AZStd::unordered_set<NetEntityId> inputProcessedEntities;
|
||||
size_t processInputCallCounter = 0;
|
||||
auto processInputCallback = [&inputProcessedEntities, &processInputCallCounter](NetEntityId netEntityId)
|
||||
{
|
||||
inputProcessedEntities.insert(netEntityId);
|
||||
processInputCallCounter++;
|
||||
};
|
||||
|
||||
// Set the callbacks for processing input. This allows us to inspect how many times the input was processed
|
||||
// and which entity's controller was invoked.
|
||||
m_root->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
|
||||
m_child->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
|
||||
m_childOfChild->m_entity->FindComponent<TestMultiplayerComponent>()->m_processInputCallback = processInputCallback;
|
||||
|
||||
rootNetBind->ProcessInput(input, deltaTime);
|
||||
|
||||
EXPECT_EQ(processInputCallCounter, 3);
|
||||
EXPECT_EQ(inputProcessedEntities,
|
||||
AZStd::unordered_set<NetEntityId>({ m_root->m_netId, m_child->m_netId, m_childOfChild->m_netId }));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
|
||||
#include <NetworkEntity/NetworkEntityAuthorityTracker.h>
|
||||
#include <NetworkEntity/NetworkEntityTracker.h>
|
||||
#include <Tests/TestMultiplayerComponent.h>
|
||||
|
||||
namespace Multiplayer
|
||||
{
|
||||
@@ -93,6 +94,12 @@ namespace Multiplayer
|
||||
m_netTransformDescriptor.reset(NetworkTransformComponent::CreateDescriptor());
|
||||
m_netTransformDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_testMultiplayerComponentDescriptor.reset(MultiplayerTest::TestMultiplayerComponent::CreateDescriptor());
|
||||
m_testMultiplayerComponentDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_testInputDriverComponentDescriptor.reset(MultiplayerTest::TestInputDriverComponent::CreateDescriptor());
|
||||
m_testInputDriverComponentDescriptor->Reflect(m_serializeContext.get());
|
||||
|
||||
m_mockMultiplayer = AZStd::make_unique<NiceMock<MockMultiplayer>>();
|
||||
AZ::Interface<IMultiplayer>::Register(m_mockMultiplayer.get());
|
||||
|
||||
@@ -103,6 +110,7 @@ namespace Multiplayer
|
||||
GetMultiplayer()->GetStats().ReserveComponentStats(Multiplayer::InvalidNetComponentId, 50, 0);
|
||||
|
||||
m_mockNetworkEntityManager = AZStd::make_unique<NiceMock<MockNetworkEntityManager>>();
|
||||
AZ::Interface<INetworkEntityManager>::Register(m_mockNetworkEntityManager.get());
|
||||
|
||||
ON_CALL(*m_mockNetworkEntityManager, AddEntityToEntityMap(_, _)).WillByDefault(Invoke(this, &HierarchyTests::AddEntityToEntityMap));
|
||||
ON_CALL(*m_mockNetworkEntityManager, GetEntity(_)).WillByDefault(Invoke(this, &HierarchyTests::GetEntity));
|
||||
@@ -136,6 +144,7 @@ namespace Multiplayer
|
||||
m_multiplayerComponentRegistry = AZStd::make_unique<MultiplayerComponentRegistry>();
|
||||
ON_CALL(*m_mockNetworkEntityManager, GetMultiplayerComponentRegistry()).WillByDefault(Return(m_multiplayerComponentRegistry.get()));
|
||||
RegisterMultiplayerComponents();
|
||||
MultiplayerTest::RegisterMultiplayerComponents();
|
||||
}
|
||||
|
||||
void TearDown() override
|
||||
@@ -157,6 +166,7 @@ namespace Multiplayer
|
||||
|
||||
AZ::Interface<INetworkTime>::Unregister(m_mockNetworkTime.get());
|
||||
AZ::Interface<AZ::ITime>::Unregister(m_mockTime.get());
|
||||
AZ::Interface<INetworkEntityManager>::Unregister(m_mockNetworkEntityManager.get());
|
||||
AZ::Interface<IMultiplayer>::Unregister(m_mockMultiplayer.get());
|
||||
AZ::Interface<AZ::ComponentApplicationRequests>::Unregister(m_mockComponentApplicationRequests.get());
|
||||
|
||||
@@ -165,6 +175,8 @@ namespace Multiplayer
|
||||
m_mockNetworkEntityManager.reset();
|
||||
m_mockMultiplayer.reset();
|
||||
|
||||
m_testInputDriverComponentDescriptor.reset();
|
||||
m_testMultiplayerComponentDescriptor.reset();
|
||||
m_transformDescriptor.reset();
|
||||
m_netTransformDescriptor.reset();
|
||||
m_hierarchyRootDescriptor.reset();
|
||||
@@ -186,6 +198,8 @@ namespace Multiplayer
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyRootDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_hierarchyChildDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_netTransformDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_testMultiplayerComponentDescriptor;
|
||||
AZStd::unique_ptr<AZ::ComponentDescriptor> m_testInputDriverComponentDescriptor;
|
||||
|
||||
AZStd::unique_ptr<NiceMock<MockMultiplayer>> m_mockMultiplayer;
|
||||
AZStd::unique_ptr<MockNetworkEntityManager> m_mockNetworkEntityManager;
|
||||
@@ -394,6 +408,9 @@ namespace Multiplayer
|
||||
entityInfo.m_entity->CreateComponent<AzFramework::TransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetBindComponent>();
|
||||
entityInfo.m_entity->CreateComponent<NetworkTransformComponent>();
|
||||
entityInfo.m_entity->CreateComponent<MultiplayerTest::TestMultiplayerComponent>();
|
||||
entityInfo.m_entity->CreateComponent<MultiplayerTest::TestInputDriverComponent>();
|
||||
|
||||
switch (entityInfo.m_role)
|
||||
{
|
||||
case EntityInfo::Role::Root:
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#include <Tests/TestMultiplayerComponent.h>
|
||||
|
||||
#include <AzCore/Serialization/SerializeContext.h>
|
||||
|
||||
namespace MultiplayerTest
|
||||
{
|
||||
void TestInputDriverComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<TestInputDriverComponent, AZ::Component>()
|
||||
->Version(1);
|
||||
}
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
|
||||
if (serializeContext)
|
||||
{
|
||||
serializeContext->Class<TestMultiplayerComponent, TestMultiplayerComponentBase>()
|
||||
->Version(1);
|
||||
}
|
||||
TestMultiplayerComponentBase::Reflect(context);
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
TestMultiplayerComponentController::TestMultiplayerComponentController(TestMultiplayerComponent& parent)
|
||||
: TestMultiplayerComponentControllerBase(parent)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
|
||||
{
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::CreateInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
|
||||
{
|
||||
auto* networkInput = input.FindComponentInput<TestMultiplayerComponentNetworkInput>();
|
||||
networkInput->m_ownerId = GetParent().GetId();
|
||||
}
|
||||
|
||||
void TestMultiplayerComponentController::ProcessInput(Multiplayer::NetworkInput& input, [[maybe_unused]] float deltaTime)
|
||||
{
|
||||
auto& component = GetParent();
|
||||
[[maybe_unused]] auto* networkInput = input.FindComponentInput<TestMultiplayerComponentNetworkInput>();
|
||||
AZ_Assert(networkInput->m_ownerId == component.GetId(), "Input Id doesn't match the owner component Id");
|
||||
|
||||
if (component.m_processInputCallback)
|
||||
{
|
||||
component.m_processInputCallback(GetNetEntityId());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project. For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <Tests/AutoGen/TestMultiplayerComponent.AutoComponent.h>
|
||||
|
||||
namespace MultiplayerTest
|
||||
{
|
||||
// Dummy class for satisfying "MultiplayerInputDriver" component dependency
|
||||
class TestInputDriverComponent : public AZ::Component
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(TestInputDriverComponent, "{C3877905-3B61-45AE-A636-9845C3AAA39D}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
static void GetProvidedServices(AZ::ComponentDescriptor::DependencyArrayType& provided)
|
||||
{
|
||||
provided.emplace_back(AZ_CRC_CE("MultiplayerInputDriver"));
|
||||
}
|
||||
|
||||
void Activate() override {}
|
||||
void Deactivate() override {}
|
||||
};
|
||||
|
||||
// Test multiplayer component with ability to create and process network input
|
||||
class TestMultiplayerComponent
|
||||
: public TestMultiplayerComponentBase
|
||||
{
|
||||
public:
|
||||
AZ_MULTIPLAYER_COMPONENT(MultiplayerTest::TestMultiplayerComponent, s_testMultiplayerComponentConcreteUuid, MultiplayerTest::TestMultiplayerComponentBase);
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
void OnInit() override;
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
AZStd::function<void(Multiplayer::NetEntityId)> m_processInputCallback;
|
||||
};
|
||||
|
||||
// Multiplayer controller for the test component
|
||||
class TestMultiplayerComponentController
|
||||
: public TestMultiplayerComponentControllerBase
|
||||
{
|
||||
public:
|
||||
TestMultiplayerComponentController(TestMultiplayerComponent& parent);
|
||||
|
||||
//! TestMultiplayerComponentControllerBase
|
||||
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
|
||||
|
||||
//! MultiplayerController interface
|
||||
void CreateInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
void ProcessInput(Multiplayer::NetworkInput& input, float deltaTime) override;
|
||||
};
|
||||
}
|
||||
@@ -7,6 +7,12 @@
|
||||
#
|
||||
|
||||
set(FILES
|
||||
Include/Multiplayer/AutoGen/AutoComponentTypes_Header.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponentTypes_Source.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponent_Common.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponent_Header.jinja
|
||||
Include/Multiplayer/AutoGen/AutoComponent_Source.jinja
|
||||
Tests/AutoGen/TestMultiplayerComponent.AutoComponent.xml
|
||||
Tests/ClientHierarchyTests.cpp
|
||||
Tests/ServerHierarchyBenchmarks.cpp
|
||||
Tests/CommonHierarchySetup.h
|
||||
@@ -20,4 +26,6 @@ set(FILES
|
||||
Tests/RewindableContainerTests.cpp
|
||||
Tests/RewindableObjectTests.cpp
|
||||
Tests/ServerHierarchyTests.cpp
|
||||
Tests/TestMultiplayerComponent.h
|
||||
Tests/TestMultiplayerComponent.cpp
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user