diff --git a/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl b/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl index 2720f09f4b..df3b08f798 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl +++ b/Code/Framework/AzNetworking/AzNetworking/Serialization/ISerializer.inl @@ -18,8 +18,8 @@ #include #include #include -#include "AzCore/Name/Name.h" -#include "AzCore/Name/NameDictionary.h" +#include +#include namespace AzNetworking { diff --git a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp index 15d4e2f6f0..596a1b40fa 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerGem.cpp @@ -15,9 +15,9 @@ #include #include #include -#include #include #include +#include namespace Multiplayer { diff --git a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp index 16f13f9ee8..71b04585ad 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerToolsModule.cpp @@ -12,13 +12,13 @@ #include #include -#include "Pipeline/NetworkPrefabProcessor.h" -#include "AzCore/Serialization/Json/RegistrationContext.h" -#include "Prefab/Instance/InstanceSerializer.h" +#include +#include +#include namespace Multiplayer { - //! Multiplayer system component wraps the bridging logic between the game and transport layer. + //! Multiplayer Tools system component provides serialize context reflection for tools-only systems. class MultiplayerToolsSystemComponent final : public AZ::Component { diff --git a/Gems/Multiplayer/Code/Source/MultiplayerTypes.h b/Gems/Multiplayer/Code/Source/MultiplayerTypes.h index ad491f4016..7ffaa8a56e 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerTypes.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerTypes.h @@ -13,11 +13,11 @@ #pragma once #include +#include #include #include #include #include -#include namespace Multiplayer { @@ -70,19 +70,6 @@ namespace Multiplayer True }; - template - bool Serialize(TYPE& value, const char* name); - - inline NetEntityId MakeEntityId(uint8_t a_ServerId, int32_t a_NextId) - { - constexpr int32_t MAX_ENTITYID = 0x00FFFFFF; - - AZ_Assert((a_NextId < MAX_ENTITYID) && (a_NextId > 0), "Requested Id out of range"); - - NetEntityId ret = NetEntityId(((static_cast(a_ServerId) << 24) & 0xFF000000) | (a_NextId & MAX_ENTITYID)); - return ret; - } - // This is just a placeholder // The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab struct PrefabEntityId diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index e19bc7685b..3295f9e45e 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -29,6 +29,7 @@ #include #include #include +#include namespace Multiplayer { @@ -532,7 +533,7 @@ namespace Multiplayer NetEntityId netEntityId, NetEntityRole localNetworkRole, AzNetworking::ISerializer& serializer, - [[maybe_unused]] const PrefabEntityId& prefabEntityId + const PrefabEntityId& prefabEntityId ) { ConstNetworkEntityHandle replicatorEntity = GetNetworkEntityManager()->GetEntity(netEntityId); @@ -544,6 +545,15 @@ namespace Multiplayer if (createEntity) { //replicatorEntity = GetNetworkEntityManager()->CreateSingleEntityImmediateInternal(prefabEntityId, EntitySpawnType::Replicate, AutoActivate::DoNotActivate, netEntityId, localNetworkRole, AZ::Transform::Identity()); + INetworkEntityManager::EntityList entityList = GetNetworkEntityManager()->CreateEntitiesImmediate( + prefabEntityId, netEntityId, localNetworkRole, + AZ::Transform::Identity()); + + if (entityList.size() == 1) + { + replicatorEntity = entityList[0]; + } + AZ_Assert(replicatorEntity != nullptr, "Failed to create entity from prefab %s", prefabEntityId.m_prefabName.GetCStr()); if (replicatorEntity == nullptr) { diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/INetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/INetworkEntityManager.h index be4f32752d..557a912a31 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/INetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/INetworkEntityManager.h @@ -54,7 +54,8 @@ namespace Multiplayer //! Creates new entities of the given archetype //! @param prefabEntryId the name of the spawnable to spawn - virtual void CreateEntitiesImmediate(const PrefabEntityId& prefabEntryId) = 0; + virtual EntityList CreateEntitiesImmediate( + const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole, const AZ::Transform& transform) = 0; //! Returns an ConstEntityPtr for the provided entityId. //! @param netEntityId the netEntityId to get an ConstEntityPtr for diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp index d92becfb97..80ad654cae 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.cpp @@ -20,6 +20,9 @@ #include #include #include +#include +#include +#include namespace Multiplayer { @@ -32,7 +35,6 @@ namespace Multiplayer , m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event")) , m_entityAddedEventHandler([this](AZ::Entity* entity) { OnEntityAdded(entity); }) , m_entityRemovedEventHandler([this](AZ::Entity* entity) { OnEntityRemoved(entity); }) - , m_rootSpawnableMonitor(*this) { AZ::Interface::Register(this); AzFramework::RootSpawnableNotificationBus::Handler::BusConnect(); @@ -284,8 +286,8 @@ namespace Multiplayer NetBindComponent* netBindComponent = entity->FindComponent(); if (netBindComponent != nullptr) { - const NetEntityId netEntityId = NextId(); - netBindComponent->PreInit(entity, PrefabEntityId(), netEntityId, NetEntityRole::Authority); + //const NetEntityId netEntityId = NextId(); + //netBindComponent->PreInit(entity, PrefabEntityId(), netEntityId, NetEntityRole::Authority); } } @@ -337,7 +339,8 @@ namespace Multiplayer } } - INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate(const AzFramework::Spawnable& spawnable) + INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate( + const AzFramework::Spawnable& spawnable, NetEntityRole netEntityRole) { INetworkEntityManager::EntityList returnList; @@ -361,7 +364,7 @@ namespace Multiplayer prefabEntityId.m_entityOffset = aznumeric_cast(i); const NetEntityId netEntityId = NextId(); - netBindComponent->PreInit(clone, prefabEntityId, netEntityId, NetEntityRole::Authority); + netBindComponent->PreInit(clone, prefabEntityId, netEntityId, netEntityRole); AzFramework::GameEntityContextRequestBus::Broadcast( &AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone); @@ -378,8 +381,62 @@ namespace Multiplayer return returnList; } - void NetworkEntityManager::CreateEntitiesImmediate([[maybe_unused]] const PrefabEntityId& a_SliceEntryId) + INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate( + const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole, + const AZ::Transform& transform) { + INetworkEntityManager::EntityList returnList; + + // TODO: Implement for non-root spawnables + auto spawnableAssetId = m_networkPrefabLibrary.GetAssetIdByName(prefabEntryId.m_prefabName); + if (spawnableAssetId == m_rootSpawnableAsset.GetId()) + { + AzFramework::Spawnable* netSpawnable = m_rootSpawnableAsset.GetAs(); + if (!netSpawnable) + { + return returnList; + } + + const uint32_t entityIndex = prefabEntryId.m_entityOffset; + + if (entityIndex == PrefabEntityId::AllIndices) + { + return CreateEntitiesImmediate(*netSpawnable, netEntityRole); + } + + const AzFramework::Spawnable::EntityList& entities = netSpawnable->GetEntities(); + size_t entitiesSize = entities.size(); + if (entityIndex >= entitiesSize) + { + return returnList; + } + + AZ::SerializeContext* serializeContext = nullptr; + AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext); + + AZ::Entity* clone = serializeContext->CloneObject(entities[entityIndex].get()); + AZ_Assert(clone != nullptr, "Failed to clone spawnable entity."); + clone->SetId(AZ::Entity::MakeId()); + + NetBindComponent* netBindComponent = clone->FindComponent(); + if (netBindComponent) + { + netBindComponent->PreInit(clone, prefabEntryId, netEntityId, netEntityRole); + + auto* transformComponent = clone->FindComponent(); + if (transformComponent) + { + transformComponent->SetWorldTM(transform); + } + + AzFramework::GameEntityContextRequestBus::Broadcast( + &AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone); + + returnList.push_back(netBindComponent->GetEntityHandle()); + } + } + + return returnList; } Multiplayer::NetEntityId NetworkEntityManager::NextId() @@ -389,55 +446,57 @@ namespace Multiplayer } void NetworkEntityManager::OnRootSpawnableAssigned( - [[maybe_unused]] AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) + AZ::Data::Asset rootSpawnable, [[maybe_unused]] uint32_t generation) { - AZStd::string hint = rootSpawnable.GetHint(); - - size_t extensionPos = hint.find(".spawnable"); - if (extensionPos == AZStd::string::npos) + AzFramework::Spawnable* rootSpawnableData = rootSpawnable.GetAs(); + const auto& entityList = rootSpawnableData->GetEntities(); + if (entityList.size() == 0) { - AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Root spawnable hint doesn't have .spawnable extension"); + AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Root spawnable doesn't have any entities."); return; } - AZStd::string newhint = hint.replace(extensionPos, 0, ".network"); - auto rootSpawnableAssetId = m_networkPrefabLibrary.GetAssetIdByName(AZ::Name(newhint)); - if (!rootSpawnableAssetId.IsValid()) + const auto& rootEntity = entityList[0]; + auto* spawnableHolder = rootEntity->FindComponent(); + if (!spawnableHolder) { - AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Network spawnable asset ID is invalid"); + AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Root entity doesn't have NetworkSpawnableHolderComponent."); return; } - m_rootSpawnableAsset = AZ::Data::Asset( - rootSpawnableAssetId, azrtti_typeid(), newhint); - if (m_rootSpawnableAsset.QueueLoad()) + AZ::Data::Asset netSpawnableAsset = spawnableHolder->GetNetworkSpawnableAsset(); + AzFramework::Spawnable* netSpawnable = netSpawnableAsset.GetAs(); + if (!netSpawnable) { - m_rootSpawnableMonitor.Connect(rootSpawnableAssetId); + // TODO: Temp sync load until JsonSerialization of loadBehavior is fixed. + netSpawnableAsset = AZ::Data::AssetManager::Instance().GetAsset( + netSpawnableAsset.GetId(), AZ::Data::AssetLoadBehavior::PreLoad); + AZ::Data::AssetManager::Instance().BlockUntilLoadComplete(netSpawnableAsset); + + netSpawnable = netSpawnableAsset.GetAs(); } - else + + if (!netSpawnable) { - AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Unable to queue networked root spawnable '%s' for loading.", - m_rootSpawnableAsset.GetHint().c_str()); + AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Net spawnable doesn't have any data."); + return; + } + + m_rootSpawnableAsset = netSpawnableAsset; + + const auto agentType = AZ::Interface::Get()->GetAgentType(); + const bool spawnImmediately = + (agentType == MultiplayerAgentType::ClientServer || agentType == MultiplayerAgentType::DedicatedServer); + + if (spawnImmediately) + { + CreateEntitiesImmediate(*netSpawnable, NetEntityRole::Authority); } } void NetworkEntityManager::OnRootSpawnableReleased([[maybe_unused]] uint32_t generation) { - m_rootSpawnableMonitor.Disconnect(); - } - - - NetworkEntityManager::NetworkSpawnableMonitor::NetworkSpawnableMonitor( - NetworkEntityManager& entityManager) - : m_entityManager(entityManager) - { - } - - void NetworkEntityManager::NetworkSpawnableMonitor::OnAssetReady(AZ::Data::Asset asset) - { - AzFramework::Spawnable* spawnable = asset.GetAs(); - AZ_Assert(spawnable, "NetworkSpawnableMonitor: Loaded asset data didn't contain a Spawanble."); - - m_entityManager.CreateEntitiesImmediate(*spawnable); + // TODO: Do we need to clear all entities here? + m_rootSpawnableAsset.Release(); } } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h index 20c67a74fd..d9d21d6b7b 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/NetworkEntityManager.h @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include @@ -46,9 +45,11 @@ namespace Multiplayer HostId GetHostId() const override; ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override; - EntityList CreateEntitiesImmediate(const AzFramework::Spawnable& spawnable); + EntityList CreateEntitiesImmediate(const AzFramework::Spawnable& spawnable, NetEntityRole netEntityRole); - void CreateEntitiesImmediate(const PrefabEntityId& a_SliceEntryId) override; + EntityList CreateEntitiesImmediate( + const PrefabEntityId& prefabEntryId, NetEntityId netEntityId, NetEntityRole netEntityRole, + const AZ::Transform& transform) override; uint32_t GetEntityCount() const override; NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override; @@ -78,15 +79,6 @@ namespace Multiplayer //! @} private: - class NetworkSpawnableMonitor final : public AzFramework::SpawnableMonitor - { - public: - explicit NetworkSpawnableMonitor(NetworkEntityManager& entityManager); - void OnAssetReady(AZ::Data::Asset asset) override; - - NetworkEntityManager& m_entityManager; - }; - void OnEntityAdded(AZ::Entity* entity); void OnEntityRemoved(AZ::Entity* entity); void RemoveEntities(); @@ -120,7 +112,6 @@ namespace Multiplayer DeferredRpcMessages m_localDeferredRpcMessages; NetworkSpawnableLibrary m_networkPrefabLibrary; - NetworkSpawnableMonitor m_rootSpawnableMonitor; AZ::Data::Asset m_rootSpawnableAsset; }; } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index 0995ec29d8..2006272135 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -124,7 +124,7 @@ namespace Multiplayer AzFramework::TransformComponent* transformComponent = netEntity->FindComponent(); breadcrumbEntity->CreateComponent(*transformComponent); - // TODO: Add NetBindMarkerComponent here referring to the net entity + // TODO: Configure NetBindMarkerComponent to refer to the net entity sourceInstance->AddEntity(*breadcrumbEntity); } @@ -133,6 +133,7 @@ namespace Multiplayer AZ::Data::AssetId assetId = networkSpawnable->GetId(); AZ::Data::Asset networkSpawnableAsset; networkSpawnableAsset.Create(assetId); + networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad); EntityOptionalReference containerEntityRef = sourceInstance->GetContainerEntity(); if (containerEntityRef.has_value()) diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp index 26592fb935..3c3d1f079d 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.cpp @@ -39,4 +39,13 @@ namespace Multiplayer m_networkSpawnableAsset = networkSpawnableAsset; } + AZ::Data::Asset NetworkSpawnableHolderComponent::GetNetworkSpawnableAsset() + { + return m_networkSpawnableAsset; + } + + NetworkSpawnableHolderComponent::NetworkSpawnableHolderComponent() + { + } + } diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h index 81f0959c74..54a9a4e42f 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkSpawnableHolderComponent.h @@ -27,7 +27,7 @@ namespace Multiplayer static void Reflect(AZ::ReflectContext* context); - NetworkSpawnableHolderComponent() = default; + NetworkSpawnableHolderComponent();; ~NetworkSpawnableHolderComponent() override = default; //! AZ::Component overrides. @@ -37,6 +37,7 @@ namespace Multiplayer //! @} void SetNetworkSpawnableAsset(AZ::Data::Asset networkSpawnableAsset); + AZ::Data::Asset GetNetworkSpawnableAsset(); private: AZ::Data::Asset m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad };