First pass for network hierarchy input processing

Signed-off-by: pereslav <pereslav@amazon.com>
This commit is contained in:
pereslav
2021-10-01 00:07:23 +01:00
parent bee4346df8
commit ef3552df69
5 changed files with 199 additions and 1 deletions
@@ -57,6 +57,8 @@ namespace Multiplayer
void BindNetworkHierarchyLeaveEventHandler(NetworkHierarchyLeaveEvent::Handler& handler) override;
//! @}
const AZStd::vector<AZ::Entity*>& GetHierarchicalEntitiesRef() const;
protected:
void SetTopLevelHierarchyRootEntity(AZ::Entity* hierarchyRoot);
@@ -97,4 +99,24 @@ namespace Multiplayer
//! Set to false when deactivating or otherwise not to be included in hierarchy considerations.
bool m_isHierarchyEnabled = true;
};
//! NetworkCharacterComponentController
//! This is the network controller for NetworkHierarchyRootComponent.
//! Class provides the ability to process input for hierarchies.
class NetworkHierarchyRootComponentController
: 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;
};
}
@@ -29,6 +29,7 @@ namespace Multiplayer
friend class NetworkInputMigrationVector;
friend class NetworkInputHistory;
friend class NetworkInputChild;
friend class NetworkSubInput;
NetworkInput(const NetworkInput&);
NetworkInput& operator= (const NetworkInput&);
@@ -80,4 +81,28 @@ namespace Multiplayer
ConstNetworkEntityHandle m_owner;
bool m_wasAttached = false;
};
// Used by the NetworkHierarchyRootComponent. This component allows the gameplay programmer to specify dependent entities
// Since it is possible to for the Client/Server to disagree about the state of related entities, this input encodes the entity that
// is associated with it.
class NetworkSubInput final
{
public:
NetworkSubInput() = default;
NetworkSubInput(const NetworkSubInput&) = default;
NetworkSubInput(const ConstNetworkEntityHandle& entityHandle);
NetworkSubInput& operator=(const NetworkSubInput&) = default;
void Attach(const ConstNetworkEntityHandle& entityHandle);
const ConstNetworkEntityHandle& GetOwner() const;
NetworkInput& GetNetworkInput();
const NetworkInput& GetNetworkInput() const;
bool Serialize(AzNetworking::ISerializer& serializer);
private:
ConstNetworkEntityHandle m_owner;
NetworkInput m_networkInput;
};
}
@@ -4,11 +4,13 @@
Name="NetworkHierarchyRootComponent"
Namespace="Multiplayer"
OverrideComponent="true"
OverrideController="false"
OverrideController="true"
OverrideInclude="Multiplayer/Components/NetworkHierarchyRootComponent.h"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<NetworkInput Type="AZStd::vector&lt;NetworkSubInput&gt;" Name="ChildInputs" Init="" />
<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>
@@ -139,6 +139,11 @@ namespace Multiplayer
return m_hierarchicalEntities;
}
const AZStd::vector<AZ::Entity*>& NetworkHierarchyRootComponent::GetHierarchicalEntitiesRef() const
{
return m_hierarchicalEntities;
}
AZ::Entity* NetworkHierarchyRootComponent::GetHierarchicalRoot() const
{
if (m_rootEntity)
@@ -326,4 +331,99 @@ 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;
}
const AZStd::vector<AZ::Entity*>& entities = component.GetHierarchicalEntitiesRef();
auto* networkInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>();
networkInput->m_childInputs.clear();
networkInput->m_childInputs.reserve(entities.size());
for (AZ::Entity* child : entities)
{
if(child == component.GetEntity())
{
return; // Avoid infinite recursion
}
auto* netComp = child->FindComponent<NetBindComponent>();
AZ_Assert(netComp, "No NetSystemComponent, this should be impossible");
// Validate we still have a controller and we aren't in the middle of removing them
if (netComp->HasController())
{
ConstNetworkEntityHandle childEntityHandle = netComp->GetEntityHandle();
NetworkSubInput 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)
{
// Prevent replaying process input commands for child entities that weren't part of the hierarchy at that time
//if (!m_firstProcessInputOccurred)
//{
// m_firstProcessInputOccurred = true;
// m_firstProcessInputTime = input.GetInputId().GetGameTimePoint();
//}
//else if (m_firstProcessInputTime > input.GetInputId().GetGameTimePoint())
//{
// return;
//}
if (auto* networkInput = input.FindComponentInput<NetworkHierarchyRootComponentNetworkInput>())
{
for (NetworkSubInput& subInput : networkInput->m_childInputs)
{
const ConstNetworkEntityHandle& childEntity = subInput.GetOwner();
if (auto* localChild = childEntity.GetEntity())
{
auto* netComp = localChild->FindComponent<NetBindComponent>();
AZ_Assert(netComp, "No NetSystemComponent, 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);
}
}
}
}
}
}
@@ -188,4 +188,53 @@ namespace Multiplayer
}
m_wasAttached = rhs.m_wasAttached;
}
NetworkSubInput::NetworkSubInput(const ConstNetworkEntityHandle& entityHandle)
: m_owner(entityHandle)
{
Attach(m_owner);
}
void NetworkSubInput::Attach(const ConstNetworkEntityHandle& entityHandle)
{
m_owner = entityHandle;
if (auto* localEnt = entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = localEnt->FindComponent<NetBindComponent>();
if (netBindComponent)
{
m_networkInput.AttachNetBindComponent(netBindComponent);
}
}
}
const ConstNetworkEntityHandle& NetworkSubInput::GetOwner() const
{
return m_owner;
}
NetworkInput& NetworkSubInput::GetNetworkInput()
{
return m_networkInput;
}
const NetworkInput& NetworkSubInput::GetNetworkInput() const
{
return m_networkInput;
}
bool NetworkSubInput::Serialize(AzNetworking::ISerializer& serializer)
{
NetEntityId tmpId = m_owner.GetNetEntityId();
serializer.Serialize(tmpId, "OwnerId");
if (serializer.GetSerializerMode() == AzNetworking::SerializerMode::WriteToObject)
{
m_owner = AZ::Interface<INetworkEntityManager>::Get()->GetEntity(tmpId);
}
serializer.Serialize(m_networkInput, "NetInput");
return serializer.IsValid();
}
}