|
|
|
|
@ -54,34 +54,15 @@ namespace Multiplayer
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
static AZStd::unique_ptr<AzToolsFramework::Prefab::Instance> LoadInstanceFromPrefab(const 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))
|
|
|
|
|
if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*sourceInstance, prefab, PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId))
|
|
|
|
|
{
|
|
|
|
|
PrefabDomValueReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);
|
|
|
|
|
PrefabDomValueConstReference 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)
|
|
|
|
|
@ -90,6 +71,38 @@ namespace Multiplayer
|
|
|
|
|
errorMessage += AZStd::string::format("Prefab Source: %.*s", AZ_STRING_ARG(source));
|
|
|
|
|
}
|
|
|
|
|
AZ_Error("NetworkPrefabProcessor", false, errorMessage.c_str());
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
return sourceInstance;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void GatherNetEntities(
|
|
|
|
|
AzToolsFramework::Prefab::Instance* instance,
|
|
|
|
|
AZStd::vector<AZStd::pair<AZ::Entity*, AzToolsFramework::Prefab::Instance*>>& output)
|
|
|
|
|
{
|
|
|
|
|
instance->GetEntities([instance, &output](AZStd::unique_ptr<AZ::Entity>& prefabEntity)
|
|
|
|
|
{
|
|
|
|
|
if (prefabEntity->FindComponent<NetBindComponent>())
|
|
|
|
|
{
|
|
|
|
|
output.push_back(AZStd::make_pair(prefabEntity.get(), instance));
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
instance->GetNestedInstances([&output](AZStd::unique_ptr<AzToolsFramework::Prefab::Instance>& nestedInstance)
|
|
|
|
|
{
|
|
|
|
|
GatherNetEntities(nestedInstance.get(), output);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 = LoadInstanceFromPrefab(prefab);
|
|
|
|
|
if (!sourceInstance)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -105,36 +118,37 @@ namespace Multiplayer
|
|
|
|
|
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());
|
|
|
|
|
// Grab all net entities with their corresponding Instances to handle nested prefabs correctly
|
|
|
|
|
AZStd::vector<AZStd::pair<AZ::Entity*, AzToolsFramework::Prefab::Instance*>> netEntities;
|
|
|
|
|
GatherNetEntities(sourceInstance.get(), netEntities);
|
|
|
|
|
|
|
|
|
|
for (auto* sourceEntity : sourceEntities)
|
|
|
|
|
{
|
|
|
|
|
if (sourceEntity->FindComponent<NetBindComponent>())
|
|
|
|
|
{
|
|
|
|
|
networkedEntityIds.push_back(sourceEntity->GetId());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Instance container for net entities
|
|
|
|
|
AZStd::unique_ptr<Instance> networkInstance(aznew Instance());
|
|
|
|
|
|
|
|
|
|
if (networkedEntityIds.empty())
|
|
|
|
|
// Create an asset for our future network spawnable: this allows us to put references to the asset in the components
|
|
|
|
|
AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset;
|
|
|
|
|
networkSpawnableAsset.Create(networkSpawnable->GetId());
|
|
|
|
|
networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
|
|
|
|
|
|
|
|
|
|
if (netEntities.empty())
|
|
|
|
|
{
|
|
|
|
|
// No networked entities in the prefab, no need to do anything in this processor.
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AZStd::unique_ptr<Instance> networkInstance(aznew Instance());
|
|
|
|
|
// Each spawnable has a root meta-data entity at position 0, so starting net indices from 1
|
|
|
|
|
size_t netEntitiesIndexCounter = 1;
|
|
|
|
|
|
|
|
|
|
AZ::Data::Asset<AzFramework::Spawnable> networkSpawnableAsset;
|
|
|
|
|
networkSpawnableAsset.Create(networkSpawnable->GetId());
|
|
|
|
|
networkSpawnableAsset.SetAutoLoadBehavior(AZ::Data::AssetLoadBehavior::PreLoad);
|
|
|
|
|
|
|
|
|
|
for (size_t entityIndex = 0; entityIndex < networkedEntityIds.size(); ++entityIndex)
|
|
|
|
|
for (auto& entityInstancePair : netEntities)
|
|
|
|
|
{
|
|
|
|
|
AZ::EntityId entityId = networkedEntityIds[entityIndex];
|
|
|
|
|
AZ::Entity* prefabEntity = entityInstancePair.first;
|
|
|
|
|
Instance* instance = entityInstancePair.second;
|
|
|
|
|
|
|
|
|
|
AZ::EntityId entityId = prefabEntity->GetId();
|
|
|
|
|
AZ::Entity* netEntity = instance->DetachEntity(entityId).release();
|
|
|
|
|
AZ_Assert(netEntity, "Unable to detach entity %s [%s] from the source prefab instance",
|
|
|
|
|
prefabEntity->GetName().c_str(), entityId.ToString().c_str());
|
|
|
|
|
|
|
|
|
|
AZ::Entity* netEntity = sourceInstance->DetachEntity(entityId).release();
|
|
|
|
|
// Net entity will need a new ID to avoid IDs collision
|
|
|
|
|
netEntity->SetId(AZ::Entity::MakeId());
|
|
|
|
|
networkInstance->AddEntity(*netEntity);
|
|
|
|
|
@ -143,17 +157,21 @@ namespace Multiplayer
|
|
|
|
|
AZ::Entity* breadcrumbEntity = aznew AZ::Entity(entityId, netEntity->GetName());
|
|
|
|
|
breadcrumbEntity->SetRuntimeActiveByDefault(netEntity->IsRuntimeActiveByDefault());
|
|
|
|
|
|
|
|
|
|
// Marker component is what is responsible to spawning entities based on the index.
|
|
|
|
|
NetBindMarkerComponent* netBindMarkerComponent = breadcrumbEntity->CreateComponent<NetBindMarkerComponent>();
|
|
|
|
|
// Each spawnable has a root meta-data entity at position 0, so starting net indices from 1
|
|
|
|
|
netBindMarkerComponent->SetNetEntityIndex(entityIndex + 1);
|
|
|
|
|
netBindMarkerComponent->SetNetEntityIndex(netEntitiesIndexCounter);
|
|
|
|
|
netBindMarkerComponent->SetNetworkSpawnableAsset(networkSpawnableAsset);
|
|
|
|
|
|
|
|
|
|
// Copy the transform component from the original entity to have the correct transform and parent-child relationship
|
|
|
|
|
AzFramework::TransformComponent* transformComponent = netEntity->FindComponent<AzFramework::TransformComponent>();
|
|
|
|
|
breadcrumbEntity->CreateComponent<AzFramework::TransformComponent>(*transformComponent);
|
|
|
|
|
|
|
|
|
|
sourceInstance->AddEntity(*breadcrumbEntity);
|
|
|
|
|
instance->AddEntity(*breadcrumbEntity);
|
|
|
|
|
|
|
|
|
|
netEntitiesIndexCounter++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Add net spawnable asset holder
|
|
|
|
|
// Add net spawnable asset holder to the prefab root
|
|
|
|
|
{
|
|
|
|
|
EntityOptionalReference containerEntityRef = sourceInstance->GetContainerEntity();
|
|
|
|
|
if (containerEntityRef.has_value())
|
|
|
|
|
@ -184,7 +202,6 @@ namespace Multiplayer
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool result = SpawnableUtils::CreateSpawnable(*networkSpawnable, networkPrefab);
|
|
|
|
|
if (result)
|
|
|
|
|
{
|
|
|
|
|
|