diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 92ad524711..56fa769517 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -1673,12 +1673,18 @@ namespace {{ Component.attrib['Namespace'] }} void {{ ComponentBaseName }}::ActivateController(Multiplayer::EntityIsMigrating entityIsMigrating) { - m_controller.get()->Activate(entityIsMigrating); + if (m_controller) + { + m_controller->Activate(entityIsMigrating); + } } void {{ ComponentBaseName }}::DeactivateController(Multiplayer::EntityIsMigrating entityIsMigrating) { - m_controller.get()->Deactivate(entityIsMigrating); + if (m_controller) + { + m_controller->Deactivate(entityIsMigrating); + } } void {{ ComponentBaseName }}::NetworkAttach(Multiplayer::NetBindComponent* netBindComponent, Multiplayer::ReplicationRecord& currentEntityRecord, Multiplayer::ReplicationRecord& predictableEntityRecord) diff --git a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp index f93109e3ed..18370c0f09 100644 --- a/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp +++ b/Gems/Multiplayer/Code/Source/ConnectionData/ServerToClientConnectionData.cpp @@ -6,6 +6,7 @@ */ #include +#include namespace Multiplayer { @@ -13,6 +14,7 @@ namespace Multiplayer AZ_CVAR(uint32_t, sv_ClientMaxRemoteEntitiesPendingCreationCount, AZStd::numeric_limits::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we have sent to the client, but have not had a confirmation back from the client"); AZ_CVAR(uint32_t, sv_ClientMaxRemoteEntitiesPendingCreationCountPostInit, AZStd::numeric_limits::max(), nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Maximum number of entities that we will send to clients after gameplay has begun"); AZ_CVAR(AZ::TimeMs, sv_ClientEntityReplicatorPendingRemovalTimeMs, AZ::TimeMs{ 10000 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "How long should wait prior to removing an entity for the client through a change in the replication window, entity deletes are still immediate"); + AZ_CVAR(bool, sv_removeDefaultPlayerSpawnableOnDisconnect, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether to remove player's default spawnable when a player disconnects"); ServerToClientConnectionData::ServerToClientConnectionData ( @@ -39,6 +41,11 @@ namespace Multiplayer ServerToClientConnectionData::~ServerToClientConnectionData() { + if (sv_removeDefaultPlayerSpawnableOnDisconnect) + { + AZ::Interface::Get()->GetNetworkEntityManager()->MarkForRemoval(m_controlledEntity); + } + m_entityReplicationManager.Clear(false); m_controlledEntityRemovedHandler.Disconnect(); } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h index e63eeb43e9..2565b81eb4 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.h @@ -34,7 +34,9 @@ namespace Multiplayer { class IEntityDomain; class EntityReplicator; - + + //! @class EntityReplicationManager + //! @brief Handles replication of relevant entities for one connection. class EntityReplicationManager final { public: diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index 4a27ceab03..1468e034c0 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -277,15 +277,6 @@ namespace Multiplayer { //RewindableObjectState::ClearRewoundEntities(); - // Keystone has refactored these API's, rewrite required - //AZ::SliceComponent* rootSlice = nullptr; - //{ - // AzFramework::EntityContextId gameContextId = AzFramework::EntityContextId::CreateNull(); - // AzFramework::GameEntityContextRequestBus::BroadcastResult(gameContextId, &AzFramework::GameEntityContextRequests::GetGameEntityContextId); - // AzFramework::EntityContextRequestBus::BroadcastResult(rootSlice, &AzFramework::EntityContextRequests::GetRootSlice); - // AZ_Assert(rootSlice != nullptr, "Root slice returned was NULL"); - //} - AZStd::vector removeList; removeList.swap(m_removeList); for (NetEntityId entityId : removeList) @@ -299,13 +290,12 @@ namespace Multiplayer AZ_Assert(netBindComponent != nullptr, "NetBindComponent not found on networked entity"); netBindComponent->StopEntity(); - // Delete Entity, method depends on how it was loaded - // Try slice removal first, then force delete - //AZ::Entity* rawEntity = removeEntity.GetEntity(); - //if (!rootSlice->RemoveEntity(rawEntity)) - //{ - // delete rawEntity; - //} + // At the moment, we spawn one entity at a time and avoid Prefab API calls and never get a spawn ticket, + // so this is the right way for now. Once we support prefabs we can use AzFramework::SpawnableEntitiesContainer + // Additionally, prefabs spawning is async! Whereas we currently create entities immediately, see: + // @NetworkEntityManager::CreateEntitiesImmediate + AzFramework::GameEntityContextRequestBus::Broadcast( + &AzFramework::GameEntityContextRequestBus::Events::DestroyGameEntity, netBindComponent->GetEntityId()); } m_networkEntityTracker.erase(entityId); diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp index be960ce757..307619bb94 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityUpdateMessage.cpp @@ -59,10 +59,11 @@ namespace Multiplayer NetworkEntityUpdateMessage::NetworkEntityUpdateMessage(NetEntityId entityId, bool wasMigrated, bool takeOwnership) : m_entityId(entityId) + , m_isDelete(true) , m_wasMigrated(wasMigrated) , m_takeOwnership(takeOwnership) { - ; + // this is a delete entity message c-tor } NetworkEntityUpdateMessage& NetworkEntityUpdateMessage::operator =(NetworkEntityUpdateMessage&& rhs) diff --git a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp index 867a548001..25455808af 100644 --- a/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkTime/NetworkTime.cpp @@ -123,8 +123,10 @@ namespace Multiplayer for (NetworkEntityHandle entityHandle : m_rewoundEntities) { - NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent(); - netBindComponent->NotifySyncRewindState(); + if (NetBindComponent* netBindComponent = entityHandle.GetNetBindComponent()) + { + netBindComponent->NotifySyncRewindState(); + } } m_rewoundEntities.clear(); }