Entity migrations now totally functional again, plus some fixes to network rigid bodies to make them work properly as they migrate around

Signed-off-by: kberg-amzn <karlberg@amazon.com>
This commit is contained in:
kberg-amzn
2021-09-30 22:53:51 -07:00
parent 02bc89cd92
commit 8d993494f6
27 changed files with 277 additions and 155 deletions
@@ -61,10 +61,11 @@ namespace Multiplayer
{
public:
NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent);
void OnActivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override;
void HandleSendApplyImpulse(AzNetworking::IConnection* invokingConnection, const AZ::Vector3& impulse, const AZ::Vector3& worldPoint) override;
private:
void OnTransformUpdate();
AZ::TransformChangedEvent::Handler m_transformChangedHandler;
};
} // namespace Multiplayer
@@ -39,8 +39,7 @@ namespace Multiplayer
virtual void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) = 0;
//! Return the set of netbound entities not included in this domain.
//! @param outEntitiesNotInDomain the set of known networked entities not included in this domain
virtual void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const = 0;
virtual const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const = 0;
//! Debug draw to visualize host entity domains.
virtual void DebugDraw() const = 0;
@@ -46,6 +46,7 @@ namespace Multiplayer
using ClientMigrationEndEvent = AZ::Event<>;
using ClientDisconnectedEvent = AZ::Event<>;
using NotifyClientMigrationEvent = AZ::Event<const HostId&, uint64_t, ClientInputId>;
using NotifyEntityMigrationEvent = AZ::Event<const ConstNetworkEntityHandle&, const HostId&>;
using ConnectionAcquiredEvent = AZ::Event<MultiplayerAgentDatum>;
using SessionInitEvent = AZ::Event<AzNetworking::INetworkInterface*>;
using SessionShutdownEvent = AZ::Event<AzNetworking::INetworkInterface*>;
@@ -113,6 +114,10 @@ namespace Multiplayer
//! @param handler The NotifyClientMigrationEvent Handler to add
virtual void AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler) = 0;
//! Adds a NotifyEntityMigrationEvent Handler which is invoked when an entity migrates from one host to another.
//! @param handler The NotifyEntityMigrationEvent Handler to add
virtual void AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler) = 0;
//! Adds a ConnectionAcquiredEvent Handler which is invoked when a new endpoint connects to the session.
//! @param handler The ConnectionAcquiredEvent Handler to add
virtual void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) = 0;
@@ -131,6 +136,11 @@ namespace Multiplayer
//! @param lastClientInputId the last processed clientInputId by the current host
virtual void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) = 0;
//! Signals a NotifyEntityMigrationEvent with the provided parameters.
//! @param entityHandle the network entity handle of the entity being migrated
//! @param remoteHostId the host id of the host the entity is migrating to
virtual void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) = 0;
//! Sends a packet telling if entity update messages can be sent.
//! @param readyForEntityUpdates Ready for entity updates or not
virtual void SendReadyForEntityUpdates(bool readyForEntityUpdates) = 0;
@@ -8,6 +8,7 @@
#pragma once
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/EntityDomains/IEntityDomain.h>
@@ -71,8 +72,8 @@ namespace Multiplayer
bool HasRemoteAuthority(const ConstNetworkEntityHandle& entityHandle) const;
void SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain);
IEntityDomain* GetEntityDomain();
void SetRemoteEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain);
IEntityDomain* GetRemoteEntityDomain();
void SetReplicationWindow(AZStd::unique_ptr<IReplicationWindow> replicationWindow);
IReplicationWindow* GetReplicationWindow();
@@ -125,7 +126,7 @@ namespace Multiplayer
void MigrateEntityInternal(NetEntityId entityId);
void OnEntityExitDomain(const ConstNetworkEntityHandle& entityHandle);
void OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, AzNetworking::ConnectionId connectionId);
void OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId);
EntityReplicator* AddEntityReplicator(const ConstNetworkEntityHandle& entityHandle, NetEntityRole netEntityRole);
@@ -193,6 +194,7 @@ namespace Multiplayer
AZ::Event<NetEntityId> m_autonomousEntityReplicatorCreated;
EntityExitDomainEvent::Handler m_entityExitDomainEventHandler;
SendMigrateEntityEvent m_sendMigrateEntityEvent;
NotifyEntityMigrationEvent::Handler m_notifyEntityMigrationHandler;
AZ::ScheduledEvent m_clearRemovedReplicators;
AZ::ScheduledEvent m_updateWindow;
@@ -180,5 +180,8 @@ namespace Multiplayer
//! Handle a local rpc message.
//! @param entityRpcMessage the local rpc message to handle
virtual void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) = 0;
//! Visualization of network entity manager state.
virtual void DebugDraw() const = 0;
};
}
@@ -30,18 +30,8 @@ namespace Multiplayer
//! Constructs a ConstNetworkEntityHandle given an entity, an entity tracker
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* entityTracker);
//! Constructs a ConstNetworkEntityHandle given an entity, a networkEntityId, and an entity tracker
//! @param entity pointer to the entity to construct a ConstNetworkEntityHandle for
//! @param netEntityId the networkEntityId of the entity
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* entityTracker);
//! Constructs a ConstNetworkEntityHandle given an entity, a networked entityId, an entity tracker, and a dirty version
//! @param netBindComponent pointer to the entities NetBindComponent
//! @param entityTracker pointer to the entity tracker that tracks the entity
ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* entityTracker);
//! can optionally be null in which case the entity tracker will be looked up
ConstNetworkEntityHandle(AZ::Entity* entity, const NetworkEntityTracker* entityTracker = nullptr);
ConstNetworkEntityHandle(const ConstNetworkEntityHandle&) = default;
@@ -10,9 +10,11 @@
<ComponentRelation Constraint="Required" HasController="true" Name="NetworkTransformComponent" Namespace="Multiplayer" Include="Multiplayer/Components/NetworkTransformComponent.h" />
<NetworkProperty Type="AZ::Vector3" Name="LinearVelocity" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
<NetworkProperty Type="AZ::Vector3" Name="AngularVelocity" Init="AZ::Vector3::CreateZero()" ReplicateFrom="Authority" ReplicateTo="Server" IsRewindable="false" IsPredictable="false" IsPublic="false" Container="Object" ExposeToEditor="false" ExposeToScript="false" GenerateEventBindings="false" />
<RemoteProcedure Name="SendApplyImpulse" InvokeFrom="Server" HandleOn="Authority" IsPublic="true" IsReliable="true" GenerateEventBindings="false" Description="Applies an impulse">
<Param Type="AZ::Vector3" Name="impulse" />
<Param Type="AZ::Vector3" Name="worldPoint" />
</RemoteProcedure>
</Component>
@@ -13,6 +13,7 @@
#include <Multiplayer/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Multiplayer/NetworkEntity/NetworkEntityUpdateMessage.h>
#include <Multiplayer/NetworkInput/NetworkInput.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
#include <AzCore/Interface/Interface.h>
@@ -58,7 +59,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity-> FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning( "NetBindComponent", false, "NetBindComponent IsNetEntityRoleAuthority failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -75,7 +76,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleAutonomous failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -92,7 +93,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleClient failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -109,7 +110,7 @@ namespace Multiplayer
return false;
}
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (!netBindComponent)
{
AZ_Warning("NetBindComponent", false, "NetBindComponent IsNetEntityRoleServer failed. Entity '%s' (id: %s) is missing a NetBindComponent, make sure this entity contains a component which derives from NetBindComponent.", entity->GetName().c_str(), id.ToString().c_str())
@@ -171,6 +172,8 @@ namespace Multiplayer
{
GetNetworkEntityManager()->NotifyControllersDeactivated(m_netEntityHandle, EntityIsMigrating::False);
}
GetNetworkEntityTracker()->UnregisterNetBindComponent(this);
}
NetEntityRole NetBindComponent::GetNetEntityRole() const
@@ -502,6 +505,9 @@ namespace Multiplayer
m_netEntityId = netEntityId;
m_netEntityRole = netEntityRole;
m_prefabEntityId = prefabEntityId;
GetNetworkEntityTracker()->RegisterNetBindComponent(entity, this);
m_netEntityHandle = GetNetworkEntityManager()->AddEntityToEntityMap(m_netEntityId, entity);
for (AZ::Component* component : entity->GetComponents())
@@ -57,16 +57,13 @@ namespace Multiplayer
NetworkRigidBodyRequestBus::Handler::BusConnect(GetEntityId());
GetNetBindComponent()->AddEntitySyncRewindEventHandler(m_syncRewindHandler);
GetEntity()->FindComponent<AzFramework::TransformComponent>()->BindTransformChangedEventHandler(m_transformChangedHandler);
GetEntity()->GetTransform()->BindTransformChangedEventHandler(m_transformChangedHandler);
m_physicsRigidBodyComponent =
Physics::RigidBodyRequestBus::FindFirstHandler(GetEntity()->GetId());
AZ_Assert(m_physicsRigidBodyComponent, "PhysX Rigid Body Component is required on entity %s", GetEntity()->GetName().c_str());
if (!HasController())
{
m_physicsRigidBodyComponent->SetKinematic(true);
}
// By default we're kinematic, activating a controller will allow us to simulate
m_physicsRigidBodyComponent->SetKinematic(true);
}
void NetworkRigidBodyComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
@@ -121,18 +118,26 @@ namespace Multiplayer
NetworkRigidBodyComponentController::NetworkRigidBodyComponentController(NetworkRigidBodyComponent& parent)
: NetworkRigidBodyComponentControllerBase(parent)
, m_transformChangedHandler([this](const AZ::Transform&, const AZ::Transform&) { OnTransformUpdate(); })
{
;
}
void NetworkRigidBodyComponentController::OnActivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
GetParent().m_physicsRigidBodyComponent->SetKinematic(false);
if (IsAuthority())
{
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
rigidBody->SetLinearVelocity(GetLinearVelocity());
rigidBody->SetAngularVelocity(GetAngularVelocity());
GetEntity()->GetTransform()->BindTransformChangedEventHandler(m_transformChangedHandler);
}
}
void NetworkRigidBodyComponentController::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating)
{
;
GetParent().m_physicsRigidBodyComponent->SetKinematic(true);
}
void NetworkRigidBodyComponentController::HandleSendApplyImpulse
@@ -145,4 +150,11 @@ namespace Multiplayer
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
rigidBody->ApplyLinearImpulseAtWorldPoint(impulse, worldPoint);
}
void NetworkRigidBodyComponentController::OnTransformUpdate()
{
AzPhysics::RigidBody* rigidBody = GetParent().m_physicsRigidBodyComponent->GetRigidBody();
SetLinearVelocity(rigidBody->GetLinearVelocity());
SetAngularVelocity(rigidBody->GetAngularVelocity());
}
} // namespace Multiplayer
@@ -31,9 +31,9 @@ namespace Multiplayer
;
}
void FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain([[maybe_unused]] EntitiesNotInDomain& outEntitiesNotInDomain) const
const IEntityDomain::EntitiesNotInDomain& FullOwnershipEntityDomain::RetrieveEntitiesNotInDomain() const
{
;
return m_entitiesNotInDomain;
}
void FullOwnershipEntityDomain::DebugDraw() const
@@ -25,8 +25,11 @@ namespace Multiplayer
const AZ::Aabb& GetAabb() const override;
bool IsInDomain(const ConstNetworkEntityHandle& entityHandle) const override;
void ActivateTracking(const INetworkEntityManager::OwnedEntitySet& ownedEntitySet) override;
void RetrieveEntitiesNotInDomain(EntitiesNotInDomain& outEntitiesNotInDomain) const override;
const EntitiesNotInDomain& RetrieveEntitiesNotInDomain() const override;
void DebugDraw() const override;
//! @}
private:
EntitiesNotInDomain m_entitiesNotInDomain;
};
}
@@ -85,6 +85,7 @@ namespace Multiplayer
AZ_CVAR(float, cl_renderTickBlendBase, 0.15f, nullptr, AZ::ConsoleFunctorFlags::Null,
"The base used for blending between network updates, 0.1 will be quite linear, 0.2 or 0.3 will "
"slow down quicker and may be better suited to connections with highly variable latency");
AZ_CVAR(bool, bg_multiplayerDebugDraw, false, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables debug draw for the multiplayer gem");
void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context)
{
@@ -390,6 +391,11 @@ namespace Multiplayer
{
m_networkInterface->GetConnectionSet().VisitConnections(visitor);
}
if (bg_multiplayerDebugDraw)
{
m_networkEntityManager.DebugDraw();
}
}
int MultiplayerSystemComponent::GetTickOrder()
@@ -802,6 +808,11 @@ namespace Multiplayer
handler.Connect(m_notifyClientMigrationEvent);
}
void MultiplayerSystemComponent::AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler)
{
handler.Connect(m_notifyEntityMigrationEvent);
}
void MultiplayerSystemComponent::AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler)
{
handler.Connect(m_connectionAcquiredEvent);
@@ -822,6 +833,11 @@ namespace Multiplayer
m_notifyClientMigrationEvent.Signal(hostId, userIdentifier, lastClientInputId);
}
void MultiplayerSystemComponent::SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
{
m_notifyEntityMigrationEvent.Signal(entityHandle, remoteHostId);
}
void MultiplayerSystemComponent::SendReadyForEntityUpdates(bool readyForEntityUpdates)
{
IConnectionSet& connectionSet = m_networkInterface->GetConnectionSet();
@@ -937,7 +953,7 @@ namespace Multiplayer
// Unfortunately necessary, as NotifyPreRender can update transforms and thus cause a deadlock inside the vis system
AZStd::vector<NetBindComponent*> gatheredEntities;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(viewFrustum,
[&gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
[this, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
@@ -945,7 +961,7 @@ namespace Multiplayer
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_networkEntityManager.GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (netBindComponent != nullptr)
{
gatheredEntities.push_back(netBindComponent);
@@ -965,7 +981,7 @@ namespace Multiplayer
for (auto& iter : *(m_networkEntityManager.GetNetworkEntityTracker()))
{
AZ::Entity* entity = iter.second;
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_networkEntityManager.GetNetworkEntityTracker()->GetNetBindComponent(entity);
if (netBindComponent != nullptr)
{
netBindComponent->NotifyPreRender(deltaTime);
@@ -112,10 +112,12 @@ namespace Multiplayer
void AddClientMigrationEndEventHandler(ClientMigrationEndEvent::Handler& handler) override;
void AddClientDisconnectedHandler(ClientDisconnectedEvent::Handler& handler) override;
void AddNotifyClientMigrationHandler(NotifyClientMigrationEvent::Handler& handler) override;
void AddNotifyEntityMigrationEventHandler(NotifyEntityMigrationEvent::Handler& handler) override;
void AddConnectionAcquiredHandler(ConnectionAcquiredEvent::Handler& handler) override;
void AddSessionInitHandler(SessionInitEvent::Handler& handler) override;
void AddSessionShutdownHandler(SessionShutdownEvent::Handler& handler) override;
void SendNotifyClientMigrationEvent(const HostId& hostId, uint64_t userIdentifier, ClientInputId lastClientInputId) override;
void SendNotifyEntityMigrationEvent(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) override;
void SendReadyForEntityUpdates(bool readyForEntityUpdates) override;
AZ::TimeMs GetCurrentHostTimeMs() const override;
float GetCurrentBlendFactor() const override;
@@ -159,6 +161,7 @@ namespace Multiplayer
ClientMigrationStartEvent m_clientMigrationStartEvent;
ClientMigrationEndEvent m_clientMigrationEndEvent;
NotifyClientMigrationEvent m_notifyClientMigrationEvent;
NotifyEntityMigrationEvent m_notifyEntityMigrationEvent;
AZStd::queue<AZStd::string> m_pendingConnectionTickets;
@@ -45,6 +45,7 @@ namespace Multiplayer
, m_clearRemovedReplicators([this]() { ClearRemovedReplicators(); }, AZ::Name("EntityReplicationManager::ClearRemovedReplicators"))
, m_updateWindow([this]() { UpdateWindow(); }, AZ::Name("EntityReplicationManager::UpdateWindow"))
, m_entityExitDomainEventHandler([this](const ConstNetworkEntityHandle& entityHandle) { OnEntityExitDomain(entityHandle); })
, m_notifyEntityMigrationHandler([this](const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId) { OnPostEntityMigration(entityHandle, remoteHostId); })
{
// Our max payload size is whatever is passed in, minus room for a udp packetheader
m_maxPayloadSize = connection.GetConnectionMtu() - UdpPacketHeaderSerializeSize - ReplicationManagerPacketOverhead;
@@ -60,6 +61,8 @@ namespace Multiplayer
{
networkEntityManager->AddEntityExitDomainHandler(m_entityExitDomainEventHandler);
}
GetMultiplayer()->AddNotifyEntityMigrationEventHandler(m_notifyEntityMigrationHandler);
}
void EntityReplicationManager::SetRemoteHostId(const HostId& hostId)
@@ -355,8 +358,9 @@ namespace Multiplayer
entityReplicator = GetEntityReplicator(entityHandle);
if (entityReplicator)
{
// Check if we changed our remote role - this can happen during server entity migration. After we migrate ownership to the new server, we hold onto our entity replicator until we are sure
// the other side has received all the packets (and we haven't had to do resends). At this point, it is possible hear back from the remote side we migrated to on the old replicator prior to the timeout and cleanup on the old one
// Check if we changed our remote role - this can happen during server entity migration.
// Retain our replicator after migration until we are sure the other side has received all the packets (and we haven't had to do resends).
// At this point, the remote host should inform us we've migrated prior to the timeout and cleanup of the old replicator
const bool changedRemoteRole = (remoteNetworkRole != entityReplicator->GetRemoteNetworkRole());
// Check if we've changed our bound local role - this can occur when we gain Autonomous or lose Autonomous on a client
bool changedLocalRole(false);
@@ -1068,12 +1072,12 @@ namespace Multiplayer
return false;
}
void EntityReplicationManager::SetEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain)
void EntityReplicationManager::SetRemoteEntityDomain(AZStd::unique_ptr<IEntityDomain> entityDomain)
{
m_remoteEntityDomain = AZStd::move(entityDomain);
}
IEntityDomain* EntityReplicationManager::GetEntityDomain()
IEntityDomain* EntityReplicationManager::GetRemoteEntityDomain()
{
return m_remoteEntityDomain.get();
}
@@ -1143,6 +1147,9 @@ namespace Multiplayer
m_sendMigrateEntityEvent.Signal(m_connection, message);
AZLOG(NET_RepDeletes, "Migration packet sent %u to remote host %s", netEntityId, GetRemoteHostId().GetString().c_str());
// Notify all other EntityReplicationManagers that this entity has migrated so they can adjust their own replicators given our new proxy status
GetMultiplayer()->SendNotifyEntityMigrationEvent(entityHandle, GetRemoteHostId());
// Immediately add a new replicator so that we catch RPC invocations, the remote side will make us a new one, and then remove us if needs be
AddEntityReplicator(entityHandle, NetEntityRole::Authority);
}
@@ -1206,11 +1213,11 @@ namespace Multiplayer
}
}
void EntityReplicationManager::OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId, [[maybe_unused]] AzNetworking::ConnectionId connectionId)
void EntityReplicationManager::OnPostEntityMigration(const ConstNetworkEntityHandle& entityHandle, const HostId& remoteHostId)
{
if (remoteHostId == GetRemoteHostId())
{
// don't handle self sent messages
// Don't handle self sent messages
return;
}
@@ -54,7 +54,7 @@ namespace Multiplayer
{
if (auto localEnt = m_entityHandle.GetEntity())
{
m_netBindComponent = localEnt->FindComponent<NetBindComponent>();
m_netBindComponent = m_entityHandle.GetNetBindComponent();
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
}
}
@@ -94,7 +94,7 @@ namespace Multiplayer
m_entityHandle = entityHandle;
if (auto localEntity = m_entityHandle.GetEntity())
{
m_netBindComponent = localEntity->FindComponent<NetBindComponent>();
m_netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(m_netBindComponent, "No Multiplayer::NetBindComponent");
m_boundLocalNetworkRole = m_netBindComponent->GetNetEntityRole();
SetPrefabEntityId(m_netBindComponent->GetPrefabEntityId());
@@ -125,7 +125,8 @@ namespace Multiplayer
!RemoteManagerOwnsEntityLifetime() ? PropertyPublisher::OwnsLifetime::True : PropertyPublisher::OwnsLifetime::False,
m_netBindComponent,
*m_connection
);
);
m_onEntityDirtiedHandler.Disconnect();
m_netBindComponent->AddEntityDirtiedEventHandler(m_onEntityDirtiedHandler);
}
else
@@ -146,8 +147,9 @@ namespace Multiplayer
// Prepare event handlers
if (auto localEntity = m_entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = localEntity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
m_onEntityStopHandler.Disconnect();
netBindComponent->AddEntityStopEventHandler(m_onEntityStopHandler);
AttachRPCHandlers();
}
@@ -168,7 +170,7 @@ namespace Multiplayer
if (auto localEntity = m_entityHandle.GetEntity())
{
NetBindComponent* netBindComponent = localEntity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_entityHandle.GetNetBindComponent();
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
switch (GetBoundLocalNetworkRole())
@@ -479,10 +481,10 @@ namespace Multiplayer
}
NetBindComponent* netBindComponent = GetNetBindComponent();
const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
//const bool sendSliceName = !m_propertyPublisher->IsRemoteReplicatorEstablished();
NetworkEntityUpdateMessage updateMessage(GetRemoteNetworkRole(), GetEntityHandle().GetNetEntityId());
if (sendSliceName)
//if (sendSliceName)
{
updateMessage.SetPrefabEntityId(netBindComponent->GetPrefabEntityId());
}
@@ -20,6 +20,11 @@ namespace Multiplayer
: m_entity(entity)
, m_networkEntityTracker(networkEntityTracker)
{
if (m_networkEntityTracker == nullptr)
{
m_networkEntityTracker = GetNetworkEntityTracker();
}
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
@@ -28,12 +33,10 @@ namespace Multiplayer
if (entity)
{
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
NetBindComponent* netBindComponent = m_entity->template FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
m_netBindComponent = networkEntityTracker->GetNetBindComponent(entity);
if (m_netBindComponent != nullptr)
{
AZ_Assert(netBindComponent, "No Multiplayer::NetBindComponent");
m_netBindComponent = netBindComponent;
m_netEntityId = netBindComponent->GetNetEntityId();
m_netEntityId = m_netBindComponent->GetNetEntityId();
}
else
{
@@ -42,30 +45,6 @@ namespace Multiplayer
}
}
ConstNetworkEntityHandle::ConstNetworkEntityHandle(AZ::Entity* entity, NetEntityId netEntityId, const NetworkEntityTracker* networkEntityTracker)
: m_entity(entity)
, m_netEntityId(netEntityId)
, m_networkEntityTracker(networkEntityTracker)
{
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
}
ConstNetworkEntityHandle::ConstNetworkEntityHandle(NetBindComponent* netBindComponent, const NetworkEntityTracker* networkEntityTracker)
: m_entity(netBindComponent->GetEntity())
, m_netBindComponent(netBindComponent)
, m_networkEntityTracker(networkEntityTracker)
, m_netEntityId(netBindComponent->GetNetEntityId())
{
if (m_networkEntityTracker)
{
m_changeDirty = m_networkEntityTracker->GetChangeDirty(m_entity);
}
AZ_Assert(networkEntityTracker, "NetworkEntityTracker is not valid");
}
bool ConstNetworkEntityHandle::Exists() const
{
if (!m_networkEntityTracker)
@@ -151,7 +130,7 @@ namespace Multiplayer
}
if (m_netBindComponent == nullptr)
{
m_netBindComponent = m_entity->template FindComponent<NetBindComponent>();
m_netBindComponent = m_networkEntityTracker->GetNetBindComponent(m_entity);
}
return m_netBindComponent;
}
@@ -16,6 +16,8 @@
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Entity/EntityContextBus.h>
#include <AzFramework/Entity/GameEntityContextBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
#include <AzFramework/Spawnable/SpawnableEntitiesInterface.h>
#include <Multiplayer/IMultiplayer.h>
#include <Multiplayer/Components/NetBindComponent.h>
@@ -46,6 +48,7 @@ namespace Multiplayer
m_hostId = hostId;
m_entityDomain = AZStd::move(entityDomain);
m_updateEntityDomainEvent.Enqueue(net_EntityDomainUpdateMs, true);
m_entityDomain->ActivateTracking(m_ownedEntities);
}
bool NetworkEntityManager::IsInitialized() const
@@ -96,7 +99,7 @@ namespace Multiplayer
NetworkEntityHandle NetworkEntityManager::AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity)
{
m_networkEntityTracker.Add(netEntityId, entity);
return NetworkEntityHandle(entity, netEntityId, &m_networkEntityTracker);
return NetworkEntityHandle(entity, &m_networkEntityTracker);
}
void NetworkEntityManager::MarkForRemoval(const ConstNetworkEntityHandle& entityHandle)
@@ -212,6 +215,29 @@ namespace Multiplayer
m_localDeferredRpcMessages.emplace_back(AZStd::move(message));
}
void NetworkEntityManager::DebugDraw() const
{
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
AzFramework::DebugDisplayRequests* debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
for (NetworkEntityTracker::const_iterator it = m_networkEntityTracker.begin(); it != m_networkEntityTracker.end(); ++it)
{
AZ::Entity* entity = it->second;
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
if (netBindComponent->GetNetEntityRole() == NetEntityRole::Authority)
{
const AZ::Aabb entityBounds = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get()->GetEntityWorldBoundsUnion(entity->GetId());
debugDisplay->DrawWireBox(entityBounds.GetMin(), entityBounds.GetMax());
}
}
if (m_entityDomain != nullptr)
{
m_entityDomain->DebugDraw();
}
}
void NetworkEntityManager::DispatchLocalDeferredRpcMessages()
{
for (NetworkEntityRpcMessage& rpcMessage : m_localDeferredRpcMessages)
@@ -219,7 +245,7 @@ namespace Multiplayer
AZ::Entity* entity = m_networkEntityTracker.GetRaw(rpcMessage.GetEntityId());
if (entity != nullptr)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
NetBindComponent* netBindComponent = m_networkEntityTracker.GetNetBindComponent(entity);
AZ_Assert(netBindComponent != nullptr, "Attempting to send an RPC to an entity with no NetBindComponent");
netBindComponent->HandleRpcMessage(nullptr, NetEntityRole::Server, rpcMessage);
}
@@ -234,9 +260,8 @@ namespace Multiplayer
return;
}
m_entitiesNotInDomain.clear();
m_entityDomain->RetrieveEntitiesNotInDomain(m_entitiesNotInDomain);
for (NetEntityId exitingId : m_entitiesNotInDomain)
const IEntityDomain::EntitiesNotInDomain& entitiesNotInDomain = m_entityDomain->RetrieveEntitiesNotInDomain();
for (NetEntityId exitingId : entitiesNotInDomain)
{
OnEntityExitDomain(exitingId);
}
@@ -247,18 +272,6 @@ namespace Multiplayer
bool safeToExit = true;
NetworkEntityHandle entityHandle = m_networkEntityTracker.Get(entityId);
// ClientAutonomous entities need special handling here. When we migrate a player's entity the player's client must tell the new server which
// entity they were controlling. If we tell them to migrate before they know which entity they control it results in them requesting a new entity
// from the new server, resulting in an orphaned PlayerChar. PlayerControllerComponentServerAuthority::PlayerClientHasControlledEntity()
// will tell us whether the client sent an RPC acknowledging that they now know which entity is theirs.
if (AZ::Entity* entity = entityHandle.GetEntity())
{
//if (PlayerComponent::Authority* playerController = FindController<PlayerComponent::Authority>(nonConstExitingEntityPtr))
//{
// safeToExit = playerController->PlayerClientHasControlledEntity();
//}
}
// 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)
@@ -338,6 +351,7 @@ namespace Multiplayer
originalToCloneIdMap[originalEntity->GetId()] = clone->GetId();
// Can't use NetworkEntityTracker to do the lookup since the entity has not activated yet
NetBindComponent* netBindComponent = clone->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
{
@@ -62,9 +62,7 @@ namespace Multiplayer
AZStd::unique_ptr<AzFramework::EntitySpawnTicket> RequestNetSpawnableInstantiation(
const AZ::Data::Asset<AzFramework::Spawnable>& netSpawnable, const AZ::Transform& transform) override;
void SetupNetEntity(AZ::Entity* netEntity, PrefabEntityId prefabEntityId, NetEntityRole netEntityRole) override;
uint32_t GetEntityCount() const override;
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override;
void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) override;
@@ -81,11 +79,13 @@ namespace Multiplayer
void NotifyControllersActivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void NotifyControllersDeactivated(const ConstNetworkEntityHandle& entityHandle, EntityIsMigrating entityIsMigrating) override;
void HandleLocalRpcMessage(NetworkEntityRpcMessage& message) override;
void DebugDraw() const override;
//! @}
void DispatchLocalDeferredRpcMessages();
void UpdateEntityDomain();
void OnEntityExitDomain(NetEntityId entityId);
//! RootSpawnableNotificationBus
//! @{
void OnRootSpawnableAssigned(AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, uint32_t generation) override;
@@ -105,7 +105,6 @@ namespace Multiplayer
AZStd::unique_ptr<IEntityDomain> m_entityDomain;
AZ::ScheduledEvent m_updateEntityDomainEvent;
IEntityDomain::EntitiesNotInDomain m_entitiesNotInDomain;
OwnedEntitySet m_ownedEntities;
EntityExitDomainEvent m_entityExitDomainEvent;
@@ -8,6 +8,7 @@
#include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Multiplayer/NetworkEntity/NetworkEntityHandle.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <AzCore/Console/IConsole.h>
#include <AzCore/Console/ILogger.h>
@@ -21,16 +22,26 @@ namespace Multiplayer
m_netEntityIdMap[entity->GetId()] = netEntityId;
}
void NetworkEntityTracker::RegisterNetBindComponent(AZ::Entity* entity, NetBindComponent* component)
{
m_netBindingMap[entity] = component;
}
void NetworkEntityTracker::UnregisterNetBindComponent(NetBindComponent* component)
{
m_netBindingMap.erase(component->GetEntity());
}
NetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId)
{
AZ::Entity* entity = GetRaw(netEntityId);
return NetworkEntityHandle(entity, netEntityId, this);
return NetworkEntityHandle(entity, this);
}
ConstNetworkEntityHandle NetworkEntityTracker::Get(NetEntityId netEntityId) const
{
AZ::Entity* entity = GetRaw(netEntityId);
return ConstNetworkEntityHandle(entity, netEntityId, this);
return ConstNetworkEntityHandle(entity, this);
}
NetEntityId NetworkEntityTracker::Get(const AZ::EntityId& entityId) const
@@ -15,6 +15,8 @@
namespace Multiplayer
{
class NetBindComponent;
//! @class NetworkEntityTracker
//! @brief The responsibly of this class is to allow entity netEntityId's to be looked up.
class NetworkEntityTracker
@@ -23,16 +25,26 @@ namespace Multiplayer
using EntityMap = AZStd::unordered_map<NetEntityId, AZ::Entity*>;
using NetEntityIdMap = AZStd::unordered_map<AZ::EntityId, NetEntityId>;
using NetBindingMap = AZStd::unordered_map<AZ::Entity*, NetBindComponent*>;
using iterator = EntityMap::iterator;
using const_iterator = EntityMap::const_iterator;
NetworkEntityTracker() = default;
//! Adds a networked entity to the tracker
//! Adds a networked entity to the tracker.
//! @param netEntityId the networkId of the entity to add
//! @param entity pointer to the entity corresponding to the networkId
void Add(NetEntityId netEntityId, AZ::Entity* entity);
//! Registers a new NetBindComponent with the NetworkEntityTracker.
//! @param entity pointer to the entity we are registering the NetBindComponent for
//! @param component pointer to the NetBindComponent being registered
void RegisterNetBindComponent(AZ::Entity* entity, NetBindComponent* component);
//! Unregisters a NetBindComponent from the NetworkEntityTracker.
//! @param component pointer to the NetBindComponent being removed
void UnregisterNetBindComponent(NetBindComponent* component);
//! Returns an entity handle which can validate entity existence.
NetworkEntityHandle Get(NetEntityId netEntityId);
ConstNetworkEntityHandle Get(NetEntityId netEntityId) const;
@@ -46,9 +58,14 @@ namespace Multiplayer
//! Get a raw pointer of an entity.
AZ::Entity *GetRaw(NetEntityId netEntityId) const;
//! Moves the given iterator out of the entity holder and returns the ptr
//! Moves the given iterator out of the entity holder and returns the ptr.
AZ::Entity *Move(EntityMap::iterator iter);
//! Retrieves the NetBindComponent for the provided AZ::Entity, nullptr if the entity does not have netbinding.
//! @param entity pointer to the entity to retrieve the NetBindComponent for
//! @return pointer to the entities NetBindComponent, or nullptr if the entity doesn't exist or does not have netbinding
NetBindComponent* GetNetBindComponent(AZ::Entity* rawEntity) const;
//! Container overloads
//!@{
iterator begin();
@@ -79,6 +96,7 @@ namespace Multiplayer
EntityMap m_entityMap;
NetEntityIdMap m_netEntityIdMap;
NetBindingMap m_netBindingMap;
uint32_t m_deleteChangeDirty = 0;
uint32_t m_addChangeDirty = 0;
};
@@ -10,6 +10,16 @@
namespace Multiplayer
{
inline NetBindComponent* NetworkEntityTracker::GetNetBindComponent(AZ::Entity* rawEntity) const
{
auto found = m_netBindingMap.find(rawEntity);
if (found != m_netBindingMap.end())
{
return found->second;
}
return nullptr;
}
inline NetworkEntityTracker::iterator NetworkEntityTracker::begin()
{
return m_entityMap.begin();
@@ -13,10 +13,12 @@
#include <AzCore/Math/ShapeIntersection.h>
#include <AzFramework/Visibility/IVisibilitySystem.h>
#include <AzFramework/Visibility/EntityBoundsUnionBus.h>
#include <AzFramework/Entity/EntityDebugDisplayBus.h>
namespace Multiplayer
{
AZ_CVAR(float, sv_RewindVolumeExtrudeDistance, 50.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "The amount to increase rewind volume checks to account for fast moving entities");
AZ_CVAR(bool, bg_RewindDebugDraw, false, nullptr, AZ::ConsoleFunctorFlags::Null, "If true enables debug draw of rewind operations");
NetworkTime::NetworkTime()
{
@@ -94,21 +96,46 @@ namespace Multiplayer
// Since the vis system doesn't support rewound queries, first query with an expanded volume to catch any fast moving entities
const AZ::Aabb expandedVolume = rewindVolume.GetExpanded(AZ::Vector3(sv_RewindVolumeExtrudeDistance));
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
AZStd::vector<NetBindComponent*> gatheredEntities;
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
[entityBoundsUnion, rewindVolume, &gatheredEntities](const AzFramework::IVisibilityScene::NodeData& nodeData)
AzFramework::DebugDisplayRequests* debugDisplay = nullptr;
if (bg_RewindDebugDraw)
{
gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size());
AzFramework::DebugDisplayRequestBus::BusPtr debugDisplayBus;
AzFramework::DebugDisplayRequestBus::Bind(debugDisplayBus, AzFramework::g_defaultSceneEntityDebugDisplayId);
debugDisplay = AzFramework::DebugDisplayRequestBus::FindFirstHandler(debugDisplayBus);
}
if (debugDisplay)
{
debugDisplay->SetColor(AZ::Colors::Red);
debugDisplay->DrawWireBox(expandedVolume.GetMin(), expandedVolume.GetMax());
}
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface<AzFramework::IEntityBoundsUnion>::Get();
AZ::Interface<AzFramework::IVisibilitySystem>::Get()->GetDefaultVisibilityScene()->Enumerate(expandedVolume,
[this, debugDisplay, networkEntityTracker, entityBoundsUnion, rewindVolume](const AzFramework::IVisibilityScene::NodeData& nodeData)
{
m_rewoundEntities.reserve(m_rewoundEntities.size() + nodeData.m_entries.size());
for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries)
{
if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
NetworkEntityHandle entityHandle(entity, networkEntityTracker);
if (entityHandle.GetNetBindComponent() == nullptr)
{
// Not a net-bound entity, terminate processing of this entity
return;
}
const AZ::Aabb currentBounds = entityBoundsUnion->GetEntityWorldBoundsUnion(entity->GetId());
const AZ::Vector3 currentCenter = currentBounds.GetCenter();
NetworkTransformComponent* networkTransform = entity->template FindComponent<NetworkTransformComponent>();
if (debugDisplay)
{
debugDisplay->SetColor(AZ::Colors::White);
debugDisplay->DrawWireBox(currentBounds.GetMin(), currentBounds.GetMax());
}
if (networkTransform != nullptr)
{
@@ -123,24 +150,20 @@ namespace Multiplayer
}
const AZ::Vector3 rewindOffset = rewindCenter - currentCenter; // Compute offset between rewound and current positions
const AZ::Aabb rewoundAabb = currentBounds.GetTranslated(rewindOffset); // Apply offset to the entity aabb
if (debugDisplay)
{
debugDisplay->SetColor(AZ::Colors::Grey);
debugDisplay->DrawWireBox(rewoundAabb.GetMin(), rewoundAabb.GetMax());
}
if (AZ::ShapeIntersection::Overlaps(rewoundAabb, rewindVolume)) // Validate the rewound aabb intersects our rewind volume
{
// Due to component constraints, netBindComponent must exist if networkTransform exists
NetBindComponent* netBindComponent = entity->template FindComponent<NetBindComponent>();
gatheredEntities.push_back(netBindComponent);
m_rewoundEntities.push_back(entityHandle);
}
}
}
}
});
NetworkEntityTracker* networkEntityTracker = GetNetworkEntityTracker();
for (NetBindComponent* netBindComponent : gatheredEntities)
{
netBindComponent->NotifySyncRewindState();
m_rewoundEntities.push_back(NetworkEntityHandle(netBindComponent, networkEntityTracker));
}
}
void NetworkTime::ClearRewoundEntities()
@@ -107,7 +107,7 @@ namespace Multiplayer
void ServerToClientReplicationWindow::UpdateWindow()
{
// clear the candidate queue, we're going to rebuild it
// Clear the candidate queue, we're going to rebuild it
ReplicationCandidateQueue::container_type clearQueueContainer;
clearQueueContainer.reserve(sv_MaxEntitiesToTrackReplication);
// Move the clearQueueContainer into the ReplicationCandidateQueue to maintain the reserved memory
@@ -118,7 +118,7 @@ namespace Multiplayer
NetBindComponent* netBindComponent = m_controlledEntity.GetNetBindComponent();
if (!netBindComponent || !netBindComponent->HasController())
{
// if we don't have a controlled entity, or we no longer have control of the entity, don't run the update
// If we don't have a controlled entity, or we no longer have control of the entity, don't run the update
return;
}
@@ -149,24 +149,25 @@ namespace Multiplayer
for (AzFramework::VisibilityEntry* visEntry : gatheredEntries)
{
AZ::Entity* entity = static_cast<AZ::Entity*>(visEntry->m_userData);
NetworkEntityHandle entityHandle(entity, networkEntityTracker);
if (entityHandle.GetNetBindComponent() == nullptr)
{
// Entity does not have netbinding, skip this entity
continue;
}
if (filterEntityManager && filterEntityManager->IsEntityFiltered(entity, m_controlledEntity, m_connection->GetConnectionId()))
{
continue;
}
NetBindComponent* entryNetBindComponent = entity->template FindComponent<NetBindComponent>();
if (entryNetBindComponent != nullptr)
{
// We want to find the closest extent to the player and prioritize using that distance
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
NetworkEntityHandle entityHandle(entryNetBindComponent, networkEntityTracker);
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
}
// We want to find the closest extent to the player and prioritize using that distance
const AZ::Vector3 supportNormal = controlledEntityPosition - visEntry->m_boundingVolume.GetCenter();
const AZ::Vector3 closestPosition = visEntry->m_boundingVolume.GetSupport(supportNormal);
const float gatherDistanceSquared = controlledEntityPosition.GetDistanceSq(closestPosition);
const float priority = (gatherDistanceSquared > 0.0f) ? 1.0f / gatherDistanceSquared : 0.0f;
AddEntityToReplicationSet(entityHandle, priority, gatherDistanceSquared);
}
// Add in Autonomous Entities
@@ -228,11 +229,10 @@ namespace Multiplayer
void ServerToClientReplicationWindow::OnEntityActivated(AZ::Entity* entity)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
ConstNetworkEntityHandle entityHandle(entity);
NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent();
if (netBindComponent != nullptr)
{
ConstNetworkEntityHandle entityHandle(netBindComponent, GetNetworkEntityTracker());
if (netBindComponent->HasController())
{
if (IFilterEntityManager* filter = GetMultiplayer()->GetFilterEntityManager())
@@ -261,10 +261,9 @@ namespace Multiplayer
void ServerToClientReplicationWindow::OnEntityDeactivated(AZ::Entity* entity)
{
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
ConstNetworkEntityHandle entityHandle(entity);
if (entityHandle.GetNetBindComponent() != nullptr)
{
ConstNetworkEntityHandle entityHandle(netBindComponent, GetNetworkEntityTracker());
m_replicationSet.erase(entityHandle);
}
}
@@ -15,7 +15,7 @@
#include <AzTest/AzTest.h>
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
#include <multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
namespace Multiplayer
{
@@ -25,10 +25,10 @@
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <Multiplayer/Components/NetworkTransformComponent.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
#include <NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <NetworkEntity/NetworkEntityTracker.h>
#include <NetworkEntity/EntityReplication/EntityReplicationManager.h>
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
namespace Multiplayer
{
@@ -205,7 +205,7 @@ namespace Multiplayer
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity)
{
m_networkEntityMap[netEntityId] = entity;
return NetworkEntityHandle(entity, netEntityId, m_networkEntityTracker.get());
return NetworkEntityHandle(entity, m_networkEntityTracker.get());
}
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const
+22 -9
View File
@@ -19,42 +19,53 @@ namespace UnitTest
class MockMultiplayer : public Multiplayer::IMultiplayer
{
public:
MOCK_CONST_METHOD0(GetCurrentBlendFactor, float ());
MOCK_CONST_METHOD0(GetAgentType, Multiplayer::MultiplayerAgentType());
MOCK_METHOD1(InitializeMultiplayer, void(Multiplayer::MultiplayerAgentType));
MOCK_METHOD2(StartHosting, bool(uint16_t, bool));
MOCK_METHOD2(Connect, bool(AZStd::string, uint16_t));
MOCK_METHOD2(Connect, bool(const AZStd::string&, uint16_t));
MOCK_METHOD1(Terminate, void(AzNetworking::DisconnectReason));
MOCK_METHOD1(AddClientMigrationStartEventHandler, void(Multiplayer::ClientMigrationStartEvent::Handler&));
MOCK_METHOD1(AddClientMigrationEndEventHandler, void(Multiplayer::ClientMigrationEndEvent::Handler&));
MOCK_METHOD1(AddClientDisconnectedHandler, void(AZ::Event<>::Handler&));
MOCK_METHOD1(AddNotifyClientMigrationHandler, void(Multiplayer::NotifyClientMigrationEvent::Handler&));
MOCK_METHOD1(AddNotifyEntityMigrationEventHandler, void(Multiplayer::NotifyEntityMigrationEvent::Handler&));
MOCK_METHOD1(AddConnectionAcquiredHandler, void(AZ::Event<Multiplayer::MultiplayerAgentDatum>::Handler&));
MOCK_METHOD1(AddSessionInitHandler, void(AZ::Event<AzNetworking::INetworkInterface*>::Handler&));
MOCK_METHOD1(AddSessionShutdownHandler, void(AZ::Event<AzNetworking::INetworkInterface*>::Handler&));
MOCK_METHOD3(SendNotifyClientMigrationEvent, void(const Multiplayer::HostId&, uint64_t, Multiplayer::ClientInputId));
MOCK_METHOD2(SendNotifyEntityMigrationEvent, void(const Multiplayer::ConstNetworkEntityHandle&, const Multiplayer::HostId&));
MOCK_METHOD1(SendReadyForEntityUpdates, void(bool));
MOCK_CONST_METHOD0(GetCurrentHostTimeMs, AZ::TimeMs());
MOCK_CONST_METHOD0(GetCurrentBlendFactor, float());
MOCK_METHOD0(GetNetworkTime, Multiplayer::INetworkTime* ());
MOCK_METHOD0(GetNetworkEntityManager, Multiplayer::INetworkEntityManager* ());
MOCK_METHOD1(SetFilterEntityManager, void(Multiplayer::IFilterEntityManager*));
MOCK_METHOD0(GetFilterEntityManager, Multiplayer::IFilterEntityManager* ());
MOCK_METHOD1(SetShouldSpawnNetworkEntities, void(bool));
MOCK_CONST_METHOD0(GetShouldSpawnNetworkEntities, bool());
};
class MockNetworkEntityManager : public Multiplayer::INetworkEntityManager
{
public:
MOCK_METHOD2(RequestNetSpawnableInstantiation, AZStd::unique_ptr<AzFramework::EntitySpawnTicket> (const AZ::Data::Asset<AzFramework::Spawnable>&, const AZ::Transform&));
MOCK_METHOD4(
CreateEntitiesImmediate,
EntityList (const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate));
MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId (const AZ::EntityId&));
MOCK_METHOD2(Initialize, void(const Multiplayer::HostId&, AZStd::unique_ptr<Multiplayer::IEntityDomain>));
MOCK_CONST_METHOD0(IsInitialized, bool());
MOCK_CONST_METHOD0(GetEntityDomain, Multiplayer::IEntityDomain*());
MOCK_METHOD0(GetNetworkEntityTracker, Multiplayer::NetworkEntityTracker* ());
MOCK_METHOD0(GetNetworkEntityAuthorityTracker, Multiplayer::NetworkEntityAuthorityTracker* ());
MOCK_METHOD0(GetMultiplayerComponentRegistry, Multiplayer::MultiplayerComponentRegistry* ());
MOCK_CONST_METHOD0(GetHostId, Multiplayer::HostId());
MOCK_CONST_METHOD0(GetHostId, const Multiplayer::HostId&());
MOCK_CONST_METHOD1(GetEntity, Multiplayer::ConstNetworkEntityHandle(Multiplayer::NetEntityId));
MOCK_CONST_METHOD1(GetNetEntityIdById, Multiplayer::NetEntityId(const AZ::EntityId&));
MOCK_METHOD3(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::
Transform&));
MOCK_METHOD4(
CreateEntitiesImmediate,
EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityRole, const AZ::Transform&, Multiplayer::AutoActivate));
MOCK_METHOD5(CreateEntitiesImmediate, EntityList(const Multiplayer::PrefabEntityId&, Multiplayer::NetEntityId, Multiplayer::
NetEntityRole, Multiplayer::AutoActivate, const AZ::Transform&));
MOCK_METHOD2(RequestNetSpawnableInstantiation, AZStd::unique_ptr<AzFramework::EntitySpawnTicket>(const AZ::Data::Asset<AzFramework::Spawnable>&, const AZ::Transform&));
MOCK_METHOD3(SetupNetEntity, void(AZ::Entity*, Multiplayer::PrefabEntityId, Multiplayer::NetEntityRole));
MOCK_CONST_METHOD1(GetEntity, Multiplayer::ConstNetworkEntityHandle(Multiplayer::NetEntityId));
MOCK_CONST_METHOD0(GetEntityCount, uint32_t());
MOCK_METHOD2(AddEntityToEntityMap, Multiplayer::NetworkEntityHandle(Multiplayer::NetEntityId, AZ::Entity*));
MOCK_METHOD1(MarkForRemoval, void(const Multiplayer::ConstNetworkEntityHandle&));
@@ -73,6 +84,7 @@ namespace UnitTest
MOCK_METHOD2(NotifyControllersActivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating));
MOCK_METHOD2(NotifyControllersDeactivated, void(const Multiplayer::ConstNetworkEntityHandle&, Multiplayer::EntityIsMigrating));
MOCK_METHOD1(HandleLocalRpcMessage, void(Multiplayer::NetworkEntityRpcMessage&));
MOCK_CONST_METHOD0(DebugDraw, void());
};
class MockConnectionListener : public AzNetworking::IConnectionListener
@@ -88,6 +100,7 @@ namespace UnitTest
class MockTime : public AZ::ITime
{
public:
MOCK_CONST_METHOD0(GetElapsedTimeUs, AZ::TimeUs());
MOCK_CONST_METHOD0(GetElapsedTimeMs, AZ::TimeMs());
};
@@ -18,7 +18,7 @@
#include <Multiplayer/Components/NetBindComponent.h>
#include <Multiplayer/Components/NetworkHierarchyChildComponent.h>
#include <Multiplayer/Components/NetworkHierarchyRootComponent.h>
#include <NetworkEntity/EntityReplication/EntityReplicator.h>
#include <Multiplayer/NetworkEntity/EntityReplication/EntityReplicator.h>
namespace Multiplayer
{