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
@@ -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();