Merged MultiplayerPipeline from CodeCommit

main
pereslav 5 years ago
parent 59252235d5
commit a5fdbddeda

@ -18,6 +18,8 @@
#include <AzCore/std/typetraits/is_enum.h> #include <AzCore/std/typetraits/is_enum.h>
#include <AzCore/RTTI/TypeInfo.h> #include <AzCore/RTTI/TypeInfo.h>
#include <AzCore/RTTI/TypeSafeIntegral.h> #include <AzCore/RTTI/TypeSafeIntegral.h>
#include "AzCore/Name/Name.h"
#include "AzCore/Name/NameDictionary.h"
namespace AzNetworking namespace AzNetworking
{ {
@ -173,6 +175,22 @@ namespace AzNetworking
return true; return true;
} }
}; };
template<>
struct SerializeObjectHelper<AZ::Name>
{
static bool SerializeObject(ISerializer& serializer, AZ::Name& value)
{
AZ::Name::Hash nameHash = value.GetHash();
bool result = serializer.Serialize(nameHash, "NameHash");
if (result && serializer.GetSerializerMode() == SerializerMode::WriteToObject)
{
value = AZ::NameDictionary::Instance().FindName(nameHash);
}
return result;
}
};
} }
#include <AzNetworking/Serialization/AzContainerSerializers.h> #include <AzNetworking/Serialization/AzContainerSerializers.h>

@ -56,6 +56,27 @@ ly_add_target(
Gem::CertificateManager Gem::CertificateManager
) )
if (PAL_TRAIT_BUILD_HOST_TOOLS)
ly_add_target(
NAME Multiplayer.Tools MODULE
NAMESPACE Gem
OUTPUT_NAME Gem.Multiplayer.Tools
FILES_CMAKE
multiplayer_tools_files.cmake
INCLUDE_DIRECTORIES
PRIVATE
Source
.
PUBLIC
Include
BUILD_DEPENDENCIES
PRIVATE
AZ::AzToolsFramework
Gem::Multiplayer.Static
)
endif()
################################################################################ ################################################################################
# Tests # Tests
################################################################################ ################################################################################

@ -16,6 +16,8 @@
#include <Source/Components/NetBindComponent.h> #include <Source/Components/NetBindComponent.h>
#include <Source/AutoGen/AutoComponentTypes.h> #include <Source/AutoGen/AutoComponentTypes.h>
#include <AzNetworking/Framework/NetworkingSystemComponent.h> #include <AzNetworking/Framework/NetworkingSystemComponent.h>
#include <Source/Pipeline/NetBindMarkerComponent.h>
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
namespace Multiplayer namespace Multiplayer
{ {
@ -26,6 +28,8 @@ namespace Multiplayer
AzNetworking::NetworkingSystemComponent::CreateDescriptor(), AzNetworking::NetworkingSystemComponent::CreateDescriptor(),
MultiplayerSystemComponent::CreateDescriptor(), MultiplayerSystemComponent::CreateDescriptor(),
NetBindComponent::CreateDescriptor(), NetBindComponent::CreateDescriptor(),
NetBindMarkerComponent::CreateDescriptor(),
NetworkSpawnableHolderComponent::CreateDescriptor(),
}); });
CreateComponentDescriptors(m_descriptors); CreateComponentDescriptors(m_descriptors);

@ -0,0 +1,65 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Multiplayer_precompiled.h>
#include <Source/MultiplayerToolsModule.h>
#include "Pipeline/NetworkPrefabProcessor.h"
#include "AzCore/Serialization/Json/RegistrationContext.h"
#include "Prefab/Instance/InstanceSerializer.h"
namespace Multiplayer
{
//! Multiplayer system component wraps the bridging logic between the game and transport layer.
class MultiplayerToolsSystemComponent final
: public AZ::Component
{
public:
AZ_COMPONENT(MultiplayerToolsSystemComponent, "{65AF5342-0ECE-423B-B646-AF55A122F72B}");
static void Reflect(AZ::ReflectContext* context)
{
NetworkPrefabProcessor::Reflect(context);
}
MultiplayerToolsSystemComponent() = default;
~MultiplayerToolsSystemComponent() override = default;
/// AZ::Component overrides.
void Activate() override
{
}
void Deactivate() override
{
}
};
MultiplayerToolsModule::MultiplayerToolsModule()
: AZ::Module()
{
m_descriptors.insert(m_descriptors.end(), {
MultiplayerToolsSystemComponent::CreateDescriptor(),
});
}
AZ::ComponentTypeList MultiplayerToolsModule::GetRequiredSystemComponents() const
{
return AZ::ComponentTypeList
{
azrtti_typeid<MultiplayerToolsSystemComponent>(),
};
}
} // namespace Multiplayer
AZ_DECLARE_MODULE_CLASS(Gem_Multiplayer2_Tools, Multiplayer::MultiplayerToolsModule);

@ -0,0 +1,33 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Module/Module.h>
namespace Multiplayer
{
class MultiplayerToolsModule
: public AZ::Module
{
public:
AZ_RTTI(MultiplayerToolsModule, "{3F726172-21FC-48FA-8CFA-7D87EBA07E55}", AZ::Module);
AZ_CLASS_ALLOCATOR(MultiplayerToolsModule, AZ::SystemAllocator, 0);
MultiplayerToolsModule();
~MultiplayerToolsModule() override = default;
AZ::ComponentTypeList GetRequiredSystemComponents() const override;
};
} // namespace Multiplayer

@ -17,6 +17,7 @@
#include <AzCore/std/string/fixed_string.h> #include <AzCore/std/string/fixed_string.h>
#include <AzNetworking/Serialization/ISerializer.h> #include <AzNetworking/Serialization/ISerializer.h>
#include <AzNetworking/ConnectionLayer/ConnectionEnums.h> #include <AzNetworking/ConnectionLayer/ConnectionEnums.h>
#include <AzCore/Name/Name.h>
namespace Multiplayer namespace Multiplayer
{ {
@ -69,14 +70,54 @@ namespace Multiplayer
True True
}; };
template<typename TYPE>
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<int32_t>(a_ServerId) << 24) & 0xFF000000) | (a_NextId & MAX_ENTITYID));
return ret;
}
// This is just a placeholder // This is just a placeholder
// The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab // The level/prefab cooking will devise the actual solution for identifying a dynamically spawnable entity within a prefab
struct PrefabEntityId struct PrefabEntityId
{ {
AZ_TYPE_INFO(PrefabEntityId, "{EFD37465-CCAC-4E87-A825-41B4010A2C75}"); AZ_TYPE_INFO(PrefabEntityId, "{EFD37465-CCAC-4E87-A825-41B4010A2C75}");
bool operator==(const PrefabEntityId&) const { return true; }
bool operator!=(const PrefabEntityId& rhs) const { return !(*this == rhs); } static constexpr uint32_t AllIndices = AZStd::numeric_limits<uint32_t>::max();
bool Serialize(AzNetworking::ISerializer&) { return true; }
AZ::Name m_prefabName;
uint32_t m_entityOffset = AllIndices;
PrefabEntityId() = default;
explicit PrefabEntityId(AZ::Name name, uint32_t entityOffset = AllIndices)
: m_prefabName(name)
, m_entityOffset(entityOffset)
{
}
bool operator==(const PrefabEntityId& rhs) const
{
return m_prefabName == rhs.m_prefabName && m_entityOffset == rhs.m_entityOffset;
}
bool operator!=(const PrefabEntityId& rhs) const
{
return !(*this == rhs);
}
bool Serialize(AzNetworking::ISerializer& serializer)
{
serializer.Serialize(m_prefabName, "prefabName");
serializer.Serialize(m_entityOffset, "entityOffset");
return serializer.IsValid();
}
}; };
} }

@ -544,7 +544,7 @@ namespace Multiplayer
if (createEntity) if (createEntity)
{ {
//replicatorEntity = GetNetworkEntityManager()->CreateSingleEntityImmediateInternal(prefabEntityId, EntitySpawnType::Replicate, AutoActivate::DoNotActivate, netEntityId, localNetworkRole, AZ::Transform::Identity()); //replicatorEntity = GetNetworkEntityManager()->CreateSingleEntityImmediateInternal(prefabEntityId, EntitySpawnType::Replicate, AutoActivate::DoNotActivate, netEntityId, localNetworkRole, AZ::Transform::Identity());
AZ_Assert(replicatorEntity != nullptr, "Failed to create entity from prefab");// %s", prefabEntityId.GetString()); AZ_Assert(replicatorEntity != nullptr, "Failed to create entity from prefab %s", prefabEntityId.m_prefabName.GetCStr());
if (replicatorEntity == nullptr) if (replicatorEntity == nullptr)
{ {
return false; return false;

@ -16,6 +16,7 @@
#include <Source/NetworkEntity/NetworkEntityHandle.h> #include <Source/NetworkEntity/NetworkEntityHandle.h>
#include <AzCore/Component/Entity.h> #include <AzCore/Component/Entity.h>
#include <AzCore/EBus/Event.h> #include <AzCore/EBus/Event.h>
#include <AzCore/Asset/AssetCommon.h>
namespace Multiplayer namespace Multiplayer
{ {
@ -35,6 +36,7 @@ namespace Multiplayer
AZ_RTTI(INetworkEntityManager, "{109759DE-9492-439C-A0B1-AE46E6FD029C}"); AZ_RTTI(INetworkEntityManager, "{109759DE-9492-439C-A0B1-AE46E6FD029C}");
using OwnedEntitySet = AZStd::unordered_set<ConstNetworkEntityHandle>; using OwnedEntitySet = AZStd::unordered_set<ConstNetworkEntityHandle>;
using EntityList = AZStd::vector<NetworkEntityHandle>;
virtual ~INetworkEntityManager() = default; virtual ~INetworkEntityManager() = default;
@ -50,7 +52,9 @@ namespace Multiplayer
//! @return the HostId for this INetworkEntityManager instance //! @return the HostId for this INetworkEntityManager instance
virtual HostId GetHostId() const = 0; virtual HostId GetHostId() const = 0;
// TODO: Spawn methods for entities within slices/prefabs/levels //! Creates new entities of the given archetype
//! @param prefabEntryId the name of the spawnable to spawn
virtual void CreateEntitiesImmediate(const PrefabEntityId& prefabEntryId) = 0;
//! Returns an ConstEntityPtr for the provided entityId. //! Returns an ConstEntityPtr for the provided entityId.
//! @param netEntityId the netEntityId to get an ConstEntityPtr for //! @param netEntityId the netEntityId to get an ConstEntityPtr for

@ -32,12 +32,15 @@ namespace Multiplayer
, m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event")) , m_updateEntityDomainEvent([this] { UpdateEntityDomain(); }, AZ::Name("NetworkEntityManager update entity domain event"))
, m_entityAddedEventHandler([this](AZ::Entity* entity) { OnEntityAdded(entity); }) , m_entityAddedEventHandler([this](AZ::Entity* entity) { OnEntityAdded(entity); })
, m_entityRemovedEventHandler([this](AZ::Entity* entity) { OnEntityRemoved(entity); }) , m_entityRemovedEventHandler([this](AZ::Entity* entity) { OnEntityRemoved(entity); })
, m_rootSpawnableMonitor(*this)
{ {
AZ::Interface<INetworkEntityManager>::Register(this); AZ::Interface<INetworkEntityManager>::Register(this);
AzFramework::RootSpawnableNotificationBus::Handler::BusConnect();
} }
NetworkEntityManager::~NetworkEntityManager() NetworkEntityManager::~NetworkEntityManager()
{ {
AzFramework::RootSpawnableNotificationBus::Handler::BusDisconnect();
AZ::Interface<INetworkEntityManager>::Unregister(this); AZ::Interface<INetworkEntityManager>::Unregister(this);
} }
@ -147,7 +150,6 @@ namespace Multiplayer
//{ //{
// rootSlice->RemoveEntity(entity); // rootSlice->RemoveEntity(entity);
//} //}
m_nonNetworkedEntities.clear();
m_networkEntityTracker.clear(); m_networkEntityTracker.clear();
} }
@ -282,7 +284,7 @@ namespace Multiplayer
NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>(); NetBindComponent* netBindComponent = entity->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr) if (netBindComponent != nullptr)
{ {
const NetEntityId netEntityId = m_nextEntityId++; const NetEntityId netEntityId = NextId();
netBindComponent->PreInit(entity, PrefabEntityId(), netEntityId, NetEntityRole::Authority); netBindComponent->PreInit(entity, PrefabEntityId(), netEntityId, NetEntityRole::Authority);
} }
} }
@ -334,4 +336,108 @@ namespace Multiplayer
m_networkEntityTracker.erase(entityId); m_networkEntityTracker.erase(entityId);
} }
} }
INetworkEntityManager::EntityList NetworkEntityManager::CreateEntitiesImmediate(const AzFramework::Spawnable& spawnable)
{
INetworkEntityManager::EntityList returnList;
AZ::SerializeContext* serializeContext = nullptr;
AZ::ComponentApplicationBus::BroadcastResult(serializeContext, &AZ::ComponentApplicationBus::Events::GetSerializeContext);
const AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities();
size_t entitiesSize = entities.size();
for (size_t i = 0; i < entitiesSize; ++i)
{
AZ::Entity* clone = serializeContext->CloneObject(entities[i].get());
AZ_Assert(clone != nullptr, "Failed to clone spawnable entity.");
clone->SetId(AZ::Entity::MakeId());
NetBindComponent* netBindComponent = clone->FindComponent<NetBindComponent>();
if (netBindComponent != nullptr)
{
PrefabEntityId prefabEntityId;
prefabEntityId.m_prefabName = m_networkPrefabLibrary.GetPrefabNameFromAssetId(spawnable.GetId());
prefabEntityId.m_entityOffset = aznumeric_cast<uint32_t>(i);
const NetEntityId netEntityId = NextId();
netBindComponent->PreInit(clone, prefabEntityId, netEntityId, NetEntityRole::Authority);
AzFramework::GameEntityContextRequestBus::Broadcast(
&AzFramework::GameEntityContextRequestBus::Events::AddGameEntity, clone);
returnList.push_back(netBindComponent->GetEntityHandle());
}
else
{
delete clone;
}
}
return returnList;
}
void NetworkEntityManager::CreateEntitiesImmediate([[maybe_unused]] const PrefabEntityId& a_SliceEntryId)
{
}
Multiplayer::NetEntityId NetworkEntityManager::NextId()
{
const NetEntityId netEntityId = m_nextEntityId++;
return netEntityId;
}
void NetworkEntityManager::OnRootSpawnableAssigned(
[[maybe_unused]] AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, [[maybe_unused]] uint32_t generation)
{
AZStd::string hint = rootSpawnable.GetHint();
size_t extensionPos = hint.find(".spawnable");
if (extensionPos == AZStd::string::npos)
{
AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Root spawnable hint doesn't have .spawnable extension");
return;
}
AZStd::string newhint = hint.replace(extensionPos, 0, ".network");
auto rootSpawnableAssetId = m_networkPrefabLibrary.GetAssetIdByName(AZ::Name(newhint));
if (!rootSpawnableAssetId.IsValid())
{
AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Network spawnable asset ID is invalid");
return;
}
m_rootSpawnableAsset = AZ::Data::Asset<AzFramework::Spawnable>(
rootSpawnableAssetId, azrtti_typeid<AzFramework::Spawnable>(), newhint);
if (m_rootSpawnableAsset.QueueLoad())
{
m_rootSpawnableMonitor.Connect(rootSpawnableAssetId);
}
else
{
AZ_Error("NetworkEntityManager", false, "OnRootSpawnableAssigned: Unable to queue networked root spawnable '%s' for loading.",
m_rootSpawnableAsset.GetHint().c_str());
}
}
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<AZ::Data::AssetData> asset)
{
AzFramework::Spawnable* spawnable = asset.GetAs<AzFramework::Spawnable>();
AZ_Assert(spawnable, "NetworkSpawnableMonitor: Loaded asset data didn't contain a Spawanble.");
m_entityManager.CreateEntitiesImmediate(*spawnable);
}
} }

@ -14,11 +14,15 @@
#include <AzCore/EBus/ScheduledEvent.h> #include <AzCore/EBus/ScheduledEvent.h>
#include <AzCore/Component/ComponentApplicationBus.h> #include <AzCore/Component/ComponentApplicationBus.h>
#include <AzFramework/Spawnable/RootSpawnableInterface.h>
#include <AzFramework/Spawnable/SpawnableMonitor.h>
#include <Source/NetworkEntity/INetworkEntityManager.h> #include <Source/NetworkEntity/INetworkEntityManager.h>
#include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h> #include <Source/NetworkEntity/NetworkEntityAuthorityTracker.h>
#include <Source/NetworkEntity/NetworkEntityTracker.h> #include <Source/NetworkEntity/NetworkEntityTracker.h>
#include <Source/NetworkEntity/NetworkEntityRpcMessage.h> #include <Source/NetworkEntity/NetworkEntityRpcMessage.h>
#include <Source/EntityDomains/IEntityDomain.h> #include <Source/EntityDomains/IEntityDomain.h>
#include <Source/NetworkEntity/NetworkSpawnableLibrary.h>
namespace Multiplayer namespace Multiplayer
{ {
@ -26,6 +30,7 @@ namespace Multiplayer
//! This class creates and manages all networked entities. //! This class creates and manages all networked entities.
class NetworkEntityManager final class NetworkEntityManager final
: public INetworkEntityManager : public INetworkEntityManager
, public AzFramework::RootSpawnableNotificationBus::Handler
{ {
public: public:
NetworkEntityManager(); NetworkEntityManager();
@ -40,6 +45,11 @@ namespace Multiplayer
NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override; NetworkEntityAuthorityTracker* GetNetworkEntityAuthorityTracker() override;
HostId GetHostId() const override; HostId GetHostId() const override;
ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override; ConstNetworkEntityHandle GetEntity(NetEntityId netEntityId) const override;
EntityList CreateEntitiesImmediate(const AzFramework::Spawnable& spawnable);
void CreateEntitiesImmediate(const PrefabEntityId& a_SliceEntryId) override;
uint32_t GetEntityCount() const override; uint32_t GetEntityCount() const override;
NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override; NetworkEntityHandle AddEntityToEntityMap(NetEntityId netEntityId, AZ::Entity* entity) override;
void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) override; void MarkForRemoval(const ConstNetworkEntityHandle& entityHandle) override;
@ -61,19 +71,32 @@ namespace Multiplayer
void DispatchLocalDeferredRpcMessages(); void DispatchLocalDeferredRpcMessages();
void UpdateEntityDomain(); void UpdateEntityDomain();
void OnEntityExitDomain(NetEntityId entityId); void OnEntityExitDomain(NetEntityId entityId);
//! RootSpawnableNotificationBus
//! @{
void OnRootSpawnableAssigned(AZ::Data::Asset<AzFramework::Spawnable> rootSpawnable, uint32_t generation) override;
void OnRootSpawnableReleased(uint32_t generation) override;
//! @}
private: private:
class NetworkSpawnableMonitor final : public AzFramework::SpawnableMonitor
{
public:
explicit NetworkSpawnableMonitor(NetworkEntityManager& entityManager);
void OnAssetReady(AZ::Data::Asset<AZ::Data::AssetData> asset) override;
NetworkEntityManager& m_entityManager;
};
void OnEntityAdded(AZ::Entity* entity); void OnEntityAdded(AZ::Entity* entity);
void OnEntityRemoved(AZ::Entity* entity); void OnEntityRemoved(AZ::Entity* entity);
void RemoveEntities(); void RemoveEntities();
NetEntityId NextId();
NetworkEntityTracker m_networkEntityTracker; NetworkEntityTracker m_networkEntityTracker;
NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker; NetworkEntityAuthorityTracker m_networkEntityAuthorityTracker;
AZ::ScheduledEvent m_removeEntitiesEvent; AZ::ScheduledEvent m_removeEntitiesEvent;
AZStd::vector<NetEntityId> m_removeList; AZStd::vector<NetEntityId> m_removeList;
AZStd::vector<AZ::Entity*> m_nonNetworkedEntities; // Contains entities that we've instantiated, but are not networked entities
AZStd::unique_ptr<IEntityDomain> m_entityDomain; AZStd::unique_ptr<IEntityDomain> m_entityDomain;
AZ::ScheduledEvent m_updateEntityDomainEvent; AZ::ScheduledEvent m_updateEntityDomainEvent;
@ -95,5 +118,9 @@ namespace Multiplayer
// This is done to prevent local and network sent RPC's from having different dispatch behaviours // This is done to prevent local and network sent RPC's from having different dispatch behaviours
typedef AZStd::deque<NetworkEntityRpcMessage> DeferredRpcMessages; typedef AZStd::deque<NetworkEntityRpcMessage> DeferredRpcMessages;
DeferredRpcMessages m_localDeferredRpcMessages; DeferredRpcMessages m_localDeferredRpcMessages;
NetworkSpawnableLibrary m_networkPrefabLibrary;
NetworkSpawnableMonitor m_rootSpawnableMonitor;
AZ::Data::Asset<AzFramework::Spawnable> m_rootSpawnableAsset;
}; };
} }

@ -0,0 +1,81 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/NetworkEntity/NetworkSpawnableLibrary.h>
#include <AzCore/Asset/AssetManagerBus.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzCore/StringFunc/StringFunc.h>
namespace Multiplayer
{
NetworkSpawnableLibrary::NetworkSpawnableLibrary()
{
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
}
NetworkSpawnableLibrary::~NetworkSpawnableLibrary()
{
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
}
void NetworkSpawnableLibrary::BuildPrefabsList()
{
auto enumerateCallback = [this](const AZ::Data::AssetId id, const AZ::Data::AssetInfo& info)
{
if (info.m_assetType == AZ::AzTypeInfo<AzFramework::Spawnable>::Uuid())
{
ProcessSpawnableAsset(info.m_relativePath, id);
}
};
AZ::Data::AssetCatalogRequestBus::Broadcast(&AZ::Data::AssetCatalogRequests::EnumerateAssets, nullptr,
enumerateCallback, nullptr);
}
void NetworkSpawnableLibrary::ProcessSpawnableAsset(const AZStd::string& relativePath, const AZ::Data::AssetId id)
{
const AZ::Name name = AZ::Name(relativePath);
m_spawnables[name] = id;
m_spawnablesReverseLookup[id] = name;
}
void NetworkSpawnableLibrary::OnCatalogLoaded([[maybe_unused]] const char* catalogFile)
{
BuildPrefabsList();
}
AZ::Name NetworkSpawnableLibrary::GetPrefabNameFromAssetId(AZ::Data::AssetId assetId)
{
if (assetId.IsValid())
{
auto it = m_spawnablesReverseLookup.find(assetId);
if (it != m_spawnablesReverseLookup.end())
{
return it->second;
}
}
return {};
}
AZ::Data::AssetId NetworkSpawnableLibrary::GetAssetIdByName(AZ::Name name)
{
auto it = m_spawnables.find(name);
if (it != m_spawnables.end())
{
return it->second;
}
return {};
}
}

@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Asset/AssetCommon.h>
#include <AzCore/std/string/string.h>
#include <AzFramework/Asset/AssetCatalogBus.h>
#include <AzCore/Name/Name.h>
namespace Multiplayer
{
/// Implementation of the network prefab library interface.
class NetworkSpawnableLibrary final
: private AzFramework::AssetCatalogEventBus::Handler
{
public:
NetworkSpawnableLibrary();
~NetworkSpawnableLibrary();
void BuildPrefabsList();
void ProcessSpawnableAsset(const AZStd::string& relativePath, AZ::Data::AssetId id);
/// AssetCatalogEventBus overrides.
void OnCatalogLoaded(const char* catalogFile) override;
AZ::Name GetPrefabNameFromAssetId(AZ::Data::AssetId assetId);
AZ::Data::AssetId GetAssetIdByName(AZ::Name name);
private:
AZStd::unordered_map<AZ::Name, AZ::Data::AssetId> m_spawnables;
AZStd::unordered_map<AZ::Data::AssetId, AZ::Name> m_spawnablesReverseLookup;
};
}

@ -0,0 +1,35 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Pipeline/NetBindMarkerComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Multiplayer
{
void NetBindMarkerComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetBindMarkerComponent, AZ::Component>()
->Version(1);
}
}
void NetBindMarkerComponent::Activate()
{
}
void NetBindMarkerComponent::Deactivate()
{
}
}

@ -0,0 +1,39 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
namespace Multiplayer
{
//! @class NetBindMarkerComponent
//! @brief Component for tracking net entities in the original non-networked spawnable.
class NetBindMarkerComponent final : public AZ::Component
{
public:
AZ_COMPONENT(NetBindMarkerComponent, "{40612C1B-427D-45C6-A2F0-04E16DF5B718}");
static void Reflect(AZ::ReflectContext* context);
NetBindMarkerComponent() = default;
~NetBindMarkerComponent() override = default;
//! AZ::Component overrides.
//! @{
void Activate() override;
void Deactivate() override;
//! @}
private:
};
} // namespace Multiplayer

@ -0,0 +1,184 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Pipeline/NetworkPrefabProcessor.h>
#include <AzCore/Serialization/Utils.h>
#include <AzFramework/Components/TransformComponent.h>
#include <AzFramework/Spawnable/Spawnable.h>
#include <AzToolsFramework/Prefab/Instance/Instance.h>
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
#include <Prefab/Spawnable/SpawnableUtils.h>
#include <Source/Components/NetBindComponent.h>
#include <Source/Pipeline/NetBindMarkerComponent.h>
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
namespace Multiplayer
{
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessor;
using AzToolsFramework::Prefab::PrefabConversionUtils::ProcessedObjectStore;
void NetworkPrefabProcessor::Process(PrefabProcessorContext& context)
{
context.ListPrefabs([&context](AZStd::string_view prefabName, PrefabDom& prefab) {
ProcessPrefab(context, prefabName, prefab);
});
}
void NetworkPrefabProcessor::Reflect(AZ::ReflectContext* context)
{
if (auto* serializeContext = azrtti_cast<AZ::SerializeContext*>(context); serializeContext != nullptr)
{
serializeContext->Class<NetworkPrefabProcessor, PrefabProcessor>()->Version(1);
}
}
static AZStd::vector<AZ::Entity*> GetEntitiesFromInstance(AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& instance)
{
AZStd::vector<AZ::Entity*> result;
instance->GetNestedEntities([&result](const AZStd::unique_ptr<AZ::Entity>& entity) {
result.emplace_back(entity.get());
return true;
});
if (instance->HasContainerEntity())
{
auto containerEntityReference = instance->GetContainerEntity();
result.emplace_back(&containerEntityReference->get());
}
return result;
}
void NetworkPrefabProcessor::ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab)
{
using namespace AzToolsFramework::Prefab;
// convert Prefab DOM into Prefab Instance.
AZStd::unique_ptr<Instance> sourceInstance(aznew Instance());
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*sourceInstance, prefab,
PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId))
{
PrefabDomValueReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
AZStd::string errorMessage("NetworkPrefabProcessor: Failed to Load Prefab Instance from given Prefab Dom.");
if (sourceReference.has_value() && sourceReference->get().IsString() && sourceReference->get().GetStringLength() != 0)
{
AZStd::string_view source(sourceReference->get().GetString(), sourceReference->get().GetStringLength());
errorMessage += AZStd::string::format("Prefab Source: %.*s", AZ_STRING_ARG(source));
}
AZ_Error("NetworkPrefabProcessor", false, errorMessage.c_str());
return;
}
AZStd::string uniqueName = prefabName;
uniqueName += ".network.spawnable";
auto serializer = [](AZStd::vector<uint8_t>& output, const ProcessedObjectStore& object) -> bool {
AZ::IO::ByteContainerStream stream(&output);
auto& asset = object.GetAsset();
return AZ::Utils::SaveObjectToStream(stream, AZ::DataStream::ST_JSON, &asset, asset.GetType());
};
auto&& [object, networkSpawnable] =
ProcessedObjectStore::Create<AzFramework::Spawnable>(uniqueName, context.GetSourceUuid(), AZStd::move(serializer));
// grab all nested entities from the Instance as source entities.
AZStd::vector<AZ::Entity*> sourceEntities = GetEntitiesFromInstance(sourceInstance);
AZStd::vector<AZ::EntityId> networkedEntityIds;
networkedEntityIds.reserve(sourceEntities.size());
for (auto* sourceEntity : sourceEntities)
{
if (sourceEntity->FindComponent<NetBindComponent>())
{
networkedEntityIds.push_back(sourceEntity->GetId());
}
}
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefab))
{
AZ_Error("NetworkPrefabProcessor", false, "Saving exported Prefab Instance within a Prefab Dom failed.");
return;
}
AZStd::unique_ptr<Instance> networkInstance(aznew Instance());
for (auto entityId : networkedEntityIds)
{
AZ::Entity* netEntity = sourceInstance->DetachEntity(entityId).release();
networkInstance->AddEntity(*netEntity);
AZ::Entity* breadcrumbEntity = aznew AZ::Entity(netEntity->GetName());
breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault());
breadcrumbEntity->CreateComponent<NetBindMarkerComponent>();
AzFramework::TransformComponent* transformComponent = netEntity->FindComponent<AzFramework::TransformComponent>();
breadcrumbEntity->CreateComponent<AzFramework::TransformComponent>(*transformComponent);
// TODO: Add NetBindMarkerComponent here referring to the net entity
sourceInstance->AddEntity(*breadcrumbEntity);
}
// Add net spawnable asset holder
{
AZ::Data::AssetId assetId = networkSpawnable->GetId();
AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset;
networkSpawnableAsset.Create(assetId);
EntityOptionalReference containerEntityRef = sourceInstance->GetContainerEntity();
if (containerEntityRef.has_value())
{
auto* networkSpawnableHolderComponent = containerEntityRef.value().get().CreateComponent<NetworkSpawnableHolderComponent>();
networkSpawnableHolderComponent->SetNetworkSpawnableAsset(networkSpawnableAsset);
}
else
{
AZ::Entity* networkSpawnableHolderEntity = aznew AZ::Entity(uniqueName);
auto* networkSpawnableHolderComponent = networkSpawnableHolderEntity->CreateComponent<NetworkSpawnableHolderComponent>();
networkSpawnableHolderComponent->SetNetworkSpawnableAsset(networkSpawnableAsset);
sourceInstance->AddEntity(*networkSpawnableHolderEntity);
}
}
// save the final result in the target Prefab DOM.
PrefabDom networkPrefab;
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*networkInstance, networkPrefab))
{
AZ_Error("NetworkPrefabProcessor", false, "Saving exported Prefab Instance within a Prefab Dom failed.");
return;
}
if (!PrefabDomUtils::StoreInstanceInPrefabDom(*sourceInstance, prefab))
{
AZ_Error("NetworkPrefabProcessor", false, "Saving exported Prefab Instance within a Prefab Dom failed.");
return;
}
bool result = SpawnableUtils::CreateSpawnable(*networkSpawnable, networkPrefab);
if (result)
{
AzFramework::Spawnable::EntityList& entities = networkSpawnable->GetEntities();
for (auto it = entities.begin(); it != entities.end(); ++it)
{
(*it)->InvalidateDependencies();
(*it)->EvaluateDependencies();
}
context.GetProcessedObjects().push_back(AZStd::move(object));
}
else
{
AZ_Error("Prefabs", false, "Failed to convert prefab '%.*s' to a spawnable.", AZ_STRING_ARG(prefabName));
context.ErrorEncountered();
}
}
}

@ -0,0 +1,43 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzToolsFramework/Prefab/Spawnable/PrefabProcessor.h>
namespace AzToolsFramework::Prefab::PrefabConversionUtils
{
class PrefabProcessorContext;
}
namespace Multiplayer
{
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessor;
using AzToolsFramework::Prefab::PrefabConversionUtils::PrefabProcessorContext;
using AzToolsFramework::Prefab::PrefabDom;
class NetworkPrefabProcessor : public PrefabProcessor
{
public:
AZ_CLASS_ALLOCATOR(NetworkPrefabProcessor, AZ::SystemAllocator, 0);
AZ_RTTI(NetworkPrefabProcessor, "{AF6C36DA-CBB9-4DF4-AE2D-7BC6CCE65176}", PrefabProcessor);
~NetworkPrefabProcessor() override = default;
void Process(PrefabProcessorContext& context) override;
static void Reflect(AZ::ReflectContext* context);
protected:
static void ProcessPrefab(PrefabProcessorContext& context, AZStd::string_view prefabName, PrefabDom& prefab);
};
}

@ -0,0 +1,42 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#include <Source/Pipeline/NetworkSpawnableHolderComponent.h>
#include <AzCore/Serialization/SerializeContext.h>
namespace Multiplayer
{
void NetworkSpawnableHolderComponent::Reflect(AZ::ReflectContext* context)
{
AZ::SerializeContext* serializeContext = azrtti_cast<AZ::SerializeContext*>(context);
if (serializeContext)
{
serializeContext->Class<NetworkSpawnableHolderComponent, AZ::Component>()
->Version(1)
->Field("AssetRef", &NetworkSpawnableHolderComponent::m_networkSpawnableAsset);
}
}
void NetworkSpawnableHolderComponent::Activate()
{
}
void NetworkSpawnableHolderComponent::Deactivate()
{
}
void NetworkSpawnableHolderComponent::SetNetworkSpawnableAsset(AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset)
{
m_networkSpawnableAsset = networkSpawnableAsset;
}
}

@ -0,0 +1,44 @@
/*
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
* its licensors.
*
* For complete copyright and license terms please see the LICENSE at the root of this
* distribution (the "License"). All use of this software is governed by the License,
* or, if provided, by the license below or the license accompanying this file. Do not
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
*/
#pragma once
#include <AzCore/Component/Component.h>
#include <AzCore/Asset/AssetCommon.h>
#include <AzFramework/Spawnable/Spawnable.h>
namespace Multiplayer
{
//! @class NetworkSpawnableHolderComponent
//! @brief Component for holding a reference to the network spawnable to make sure it is loaded with the original one.
class NetworkSpawnableHolderComponent final : public AZ::Component
{
public:
AZ_COMPONENT(NetworkSpawnableHolderComponent, "{B0E3ADEE-FCB4-4A32-8D4F-6920F1CB08E4}");
static void Reflect(AZ::ReflectContext* context);
NetworkSpawnableHolderComponent() = default;
~NetworkSpawnableHolderComponent() override = default;
//! AZ::Component overrides.
//! @{
void Activate() override;
void Deactivate() override;
//! @}
void SetNetworkSpawnableAsset(AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset);
private:
AZ::Data::Asset<AzFramework::Spawnable> m_networkSpawnableAsset{ AZ::Data::AssetLoadBehavior::PreLoad };
};
} // namespace Multiplayer

@ -60,6 +60,8 @@ set(FILES
Source/NetworkEntity/NetworkEntityHandle.inl Source/NetworkEntity/NetworkEntityHandle.inl
Source/NetworkEntity/NetworkEntityManager.cpp Source/NetworkEntity/NetworkEntityManager.cpp
Source/NetworkEntity/NetworkEntityManager.h Source/NetworkEntity/NetworkEntityManager.h
Source/NetworkEntity/NetworkSpawnableLibrary.cpp
Source/NetworkEntity/NetworkSpawnableLibrary.h
Source/NetworkEntity/NetworkEntityRpcMessage.cpp Source/NetworkEntity/NetworkEntityRpcMessage.cpp
Source/NetworkEntity/NetworkEntityRpcMessage.h Source/NetworkEntity/NetworkEntityRpcMessage.h
Source/NetworkEntity/NetworkEntityTracker.cpp Source/NetworkEntity/NetworkEntityTracker.cpp
@ -81,6 +83,10 @@ set(FILES
Source/NetworkTime/NetworkTime.h Source/NetworkTime/NetworkTime.h
Source/NetworkTime/RewindableObject.h Source/NetworkTime/RewindableObject.h
Source/NetworkTime/RewindableObject.inl Source/NetworkTime/RewindableObject.inl
Source/Pipeline/NetBindMarkerComponent.cpp
Source/Pipeline/NetBindMarkerComponent.h
Source/Pipeline/NetworkSpawnableHolderComponent.cpp
Source/Pipeline/NetworkSpawnableHolderComponent.h
Source/ReplicationWindows/IReplicationWindow.h Source/ReplicationWindows/IReplicationWindow.h
Source/ReplicationWindows/ServerToClientReplicationWindow.cpp Source/ReplicationWindows/ServerToClientReplicationWindow.cpp
Source/ReplicationWindows/ServerToClientReplicationWindow.h Source/ReplicationWindows/ServerToClientReplicationWindow.h

@ -0,0 +1,19 @@
#
# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
# its licensors.
#
# For complete copyright and license terms please see the LICENSE at the root of this
# distribution (the "License"). All use of this software is governed by the License,
# or, if provided, by the license below or the license accompanying this file. Do not
# remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#
set(FILES
Source/Multiplayer_precompiled.cpp
Source/Multiplayer_precompiled.h
Source/Pipeline/NetworkPrefabProcessor.cpp
Source/Pipeline/NetworkPrefabProcessor.h
Source/MultiplayerToolsModule.h
Source/MultiplayerToolsModule.cpp
)

@ -0,0 +1,26 @@
{
"Amazon":
{
"Tools":
{
"Prefab":
{
"Processing":
{
"Stack":
{
"GameObjectCreation":
[
{ "$type": "AzToolsFramework::Prefab::PrefabConversionUtils::EditorInfoRemover" },
{ "$type": "{AF6C36DA-CBB9-4DF4-AE2D-7BC6CCE65176}" },
{
"$type": "AzToolsFramework::Prefab::PrefabConversionUtils::PrefabCatchmentProcessor",
"SerializationFormat": "Text" // Options are "Binary" (default) or "Text". Prefer "Binary" for performance.
}
]
}
}
}
}
}
}
Loading…
Cancel
Save