[LYN-7064] Hot Loading of Procedural Prefabs (#4923)
* Prefab hotloading - load and maintain asset references Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Working hotloading Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Clean up code, add comments Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add missing includes Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add event for template removal. Clear ProceduralPrefabSystemComponent lookup when templates are removed. Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Add unit test. Fix missing include, make sure source name is set Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix missing include Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix missing include Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Update comments to make ebus event behavior more clear Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix missing include Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix missing includes Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix missing include Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix temp directory usage which doesn't work on linux Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix temp directory usage which doesn't work on linux Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Use AZ::IO::Path for comparison. Print paths on failure Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com> * Fix unit test - register asset with catalog so it can be looked up later Signed-off-by: amzn-mike <80125227+amzn-mike@users.noreply.github.com>
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include <AzToolsFramework/API/ToolsApplicationAPI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabDomUtils.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <Prefab/ProceduralPrefabSystemComponentInterface.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
@@ -185,6 +186,20 @@ namespace AzToolsFramework
|
||||
TemplateReference newTemplateReference = m_prefabSystemComponentInterface->FindTemplate(newTemplateId);
|
||||
Template& newTemplate = newTemplateReference->get();
|
||||
|
||||
if(newTemplate.IsProcedural())
|
||||
{
|
||||
auto proceduralPrefabSystemComponentInterface = AZ::Interface<ProceduralPrefabSystemComponentInterface>::Get();
|
||||
|
||||
if (!proceduralPrefabSystemComponentInterface)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to find ProceduralPrefabSystemComponentInterface. Procedural Prefab will not be tracked for hotloading.");
|
||||
}
|
||||
else
|
||||
{
|
||||
proceduralPrefabSystemComponentInterface->RegisterProceduralPrefab(relativePath.Native(), newTemplateId);
|
||||
}
|
||||
}
|
||||
|
||||
// Mark the file as being in progress.
|
||||
progressedFilePathsSet.emplace(relativePath);
|
||||
|
||||
|
||||
@@ -26,6 +26,14 @@ namespace AzToolsFramework
|
||||
|
||||
virtual void OnPrefabTemplateDirtyFlagUpdated(
|
||||
[[maybe_unused]] TemplateId templateId, [[maybe_unused]] bool status) {}
|
||||
|
||||
// Sent after a single template has been removed
|
||||
// Does not get sent when all templates are being removed at once. Be sure to handle OnAllTemplatesRemoved as well.
|
||||
virtual void OnTemplateRemoved([[maybe_unused]] TemplateId templateId) {}
|
||||
|
||||
// Sent after all templates have been removed
|
||||
// Does not trigger individual OnTemplateRemoved events
|
||||
virtual void OnAllTemplatesRemoved() {}
|
||||
};
|
||||
|
||||
using PrefabPublicNotificationBus = AZ::EBus<PrefabPublicNotifications>;
|
||||
|
||||
@@ -160,7 +160,7 @@ namespace AzToolsFramework
|
||||
newInstance->SetTemplateId(newTemplateId);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PrefabSystemComponent::PropagateTemplateChanges(TemplateId templateId, InstanceOptionalConstReference instanceToExclude)
|
||||
{
|
||||
auto templateIdToLinkIdsIterator = m_templateToLinkIdsMap.find(templateId);
|
||||
@@ -442,7 +442,7 @@ namespace AzToolsFramework
|
||||
}
|
||||
|
||||
m_templateFilePathToIdMap.emplace(AZStd::make_pair(filePath, newTemplateId));
|
||||
|
||||
|
||||
return newTemplateId;
|
||||
}
|
||||
|
||||
@@ -469,7 +469,7 @@ namespace AzToolsFramework
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
m_templateFilePathToIdMap.erase(templateToChange.GetFilePath());
|
||||
if (!m_templateFilePathToIdMap.try_emplace(filePath, templateId).second)
|
||||
{
|
||||
@@ -500,7 +500,7 @@ namespace AzToolsFramework
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//Remove all Links owned by the Template from TemplateToLinkIdsMap.
|
||||
Template& templateToDelete = findTemplateResult->get();
|
||||
const Template::Links& linkIdsToDelete = templateToDelete.GetLinks();
|
||||
@@ -546,7 +546,7 @@ namespace AzToolsFramework
|
||||
templateId, templateToDelete.GetFilePath().c_str());
|
||||
|
||||
m_templateInstanceMapper.UnregisterTemplate(templateId);
|
||||
|
||||
|
||||
result = m_templateIdMap.erase(templateId) != 0;
|
||||
AZ_Assert(result,
|
||||
"Prefab - PrefabSystemComponent::RemoveTemplate - "
|
||||
@@ -554,7 +554,10 @@ namespace AzToolsFramework
|
||||
"from Template Id Map.",
|
||||
templateId, templateToDelete.GetFilePath().c_str());
|
||||
|
||||
return;
|
||||
if (!m_removingAllTemplates)
|
||||
{
|
||||
PrefabPublicNotificationBus::Broadcast(&PrefabPublicNotificationBus::Events::OnTemplateRemoved, templateId);
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabSystemComponent::RemoveAllTemplates()
|
||||
@@ -568,10 +571,15 @@ namespace AzToolsFramework
|
||||
templateIds.emplace_back(id);
|
||||
}
|
||||
|
||||
m_removingAllTemplates = true;
|
||||
|
||||
for (auto id : templateIds)
|
||||
{
|
||||
RemoveTemplate(id);
|
||||
}
|
||||
|
||||
m_removingAllTemplates = false;
|
||||
PrefabPublicNotificationBus::Broadcast(&PrefabPublicNotificationBus::Events::OnAllTemplatesRemoved);
|
||||
}
|
||||
|
||||
LinkId PrefabSystemComponent::AddLink(
|
||||
@@ -859,7 +867,7 @@ namespace AzToolsFramework
|
||||
|
||||
void PrefabSystemComponent::SaveAllDirtyTemplates(TemplateId rootTemplateId)
|
||||
{
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = GetDirtyTemplatePaths(rootTemplateId);
|
||||
AZStd::set<AZ::IO::PathView> dirtyTemplatePaths = GetDirtyTemplatePaths(rootTemplateId);
|
||||
|
||||
for (AZ::IO::PathView dirtyTemplatePath : dirtyTemplatePaths)
|
||||
{
|
||||
|
||||
@@ -56,7 +56,7 @@ namespace AzToolsFramework
|
||||
public:
|
||||
|
||||
using TargetTemplateIdToLinkIdMap = AZStd::unordered_map<TemplateId, AZStd::pair<AZStd::unordered_set<LinkId>, bool>>;
|
||||
|
||||
|
||||
AZ_COMPONENT(PrefabSystemComponent, "{27203AE6-A398-4614-881B-4EEB5E9B34E9}");
|
||||
|
||||
PrefabSystemComponent() = default;
|
||||
@@ -220,7 +220,7 @@ namespace AzToolsFramework
|
||||
const AZStd::vector<AZ::Entity*>& entities, AZStd::vector<AZStd::unique_ptr<Instance>>&& instancesToConsume,
|
||||
AZ::IO::PathView filePath, AZStd::unique_ptr<AZ::Entity> containerEntity = nullptr,
|
||||
InstanceOptionalReference parent = AZStd::nullopt, bool shouldCreateLinks = true) override;
|
||||
|
||||
|
||||
PrefabDom& FindTemplateDom(TemplateId templateId) override;
|
||||
|
||||
/**
|
||||
@@ -244,7 +244,7 @@ namespace AzToolsFramework
|
||||
|
||||
private:
|
||||
AZ_DISABLE_COPY_MOVE(PrefabSystemComponent);
|
||||
|
||||
|
||||
/**
|
||||
* Builds a new Prefab Template out of entities and instances and returns the first instance comprised of
|
||||
* these entities and instances.
|
||||
@@ -263,14 +263,14 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* Updates all the linked Instances corresponding to the linkIds in the provided queue.
|
||||
* Queue gets populated with more linkId lists as linked instances are updated. Updating stops when the queue is empty.
|
||||
*
|
||||
*
|
||||
* @param linkIdsQueue A queue of vector of link-Ids to update.
|
||||
*/
|
||||
void UpdateLinkedInstances(AZStd::queue<LinkIds>& linkIdsQueue);
|
||||
|
||||
/**
|
||||
* Given a vector of link ids to update, splits them into smaller lists based on the target template id of the links.
|
||||
*
|
||||
*
|
||||
* @param linkIdsToUpdate The list of link ids to update.
|
||||
* @param targetTemplateIdToLinkIdMap The map of target templateIds to a pair of lists of linkIds and a bool flag indicating
|
||||
* whether any of the instances of the target template were updated.
|
||||
@@ -279,9 +279,9 @@ namespace AzToolsFramework
|
||||
TargetTemplateIdToLinkIdMap& targetTemplateIdToLinkIdMap);
|
||||
|
||||
/**
|
||||
* Updates a single linked instance corresponding to the given link Id and adds more linkIds to the
|
||||
* Updates a single linked instance corresponding to the given link Id and adds more linkIds to the
|
||||
* template change propagation queue(linkIdsQueue) when necessary.
|
||||
*
|
||||
*
|
||||
* @param linkIdToUpdate The id of the linked instance to update
|
||||
* @param targetTemplateIdToLinkIdMap The map of target templateIds to a pair of lists of linkIds and a bool flag indicating
|
||||
* whether any of the instances of the target template were updated.
|
||||
@@ -293,7 +293,7 @@ namespace AzToolsFramework
|
||||
/**
|
||||
* If all linked instances of a target template are updated and if the content of any of the linked instances changed,
|
||||
* this method fetches all the linked instances sourced by it and adds their corresponding ids to the LinkIdsQueue.
|
||||
*
|
||||
*
|
||||
* @param targetTemplateIdToLinkIdMap The map of target templateIds to a pair of lists of linkIds and a bool flag indicating
|
||||
* whether any of the instances of the target template were updated.
|
||||
* @param targetTemplateId The id of the template, whose linked instances we need to find if the template was updated.
|
||||
@@ -414,6 +414,9 @@ namespace AzToolsFramework
|
||||
PrefabPublicRequestHandler m_prefabPublicRequestHandler;
|
||||
|
||||
PrefabSystemScriptingHandler m_prefabSystemScriptingHandler;
|
||||
|
||||
// If true, individual template-remove messages will be suppressed
|
||||
bool m_removingAllTemplates = false;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#include <AssetCatalog/PlatformAddressedAssetCatalogBus.h>
|
||||
#include <AzCore/Serialization/Json/JsonUtils.h>
|
||||
#include <AzCore/Utils/Utils.h>
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
#include <AzToolsFramework/Prefab/ProceduralPrefabSystemComponent.h>
|
||||
#include <Prefab/PrefabDomUtils.h>
|
||||
#include <Prefab/PrefabLoaderInterface.h>
|
||||
#include <Prefab/PrefabSystemComponentInterface.h>
|
||||
#include <Prefab/Procedural/ProceduralPrefabAsset.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
void ProceduralPrefabSystemComponent::Reflect(AZ::ReflectContext* context)
|
||||
{
|
||||
if (auto serializationContext = azrtti_cast<AZ::SerializeContext*>(context))
|
||||
{
|
||||
serializationContext->Class<ProceduralPrefabSystemComponent>();
|
||||
}
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::Activate()
|
||||
{
|
||||
AZ::Interface<ProceduralPrefabSystemComponentInterface>::Register(this);
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusConnect();
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::Deactivate()
|
||||
{
|
||||
AzFramework::AssetCatalogEventBus::Handler::BusDisconnect();
|
||||
AZ::Interface<ProceduralPrefabSystemComponentInterface>::Unregister(this);
|
||||
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
m_assetIdToTemplateLookup.clear();
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::OnCatalogAssetChanged(const AZ::Data::AssetId& assetId)
|
||||
{
|
||||
TemplateId templateId = InvalidTemplateId;
|
||||
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
auto itr = m_assetIdToTemplateLookup.find(assetId);
|
||||
|
||||
if (itr != m_assetIdToTemplateLookup.end())
|
||||
{
|
||||
templateId = itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
if (templateId != InvalidTemplateId)
|
||||
{
|
||||
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
|
||||
|
||||
if (!prefabSystemComponentInterface)
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to get PrefabSystemComponentInterface");
|
||||
return;
|
||||
}
|
||||
|
||||
AZStd::string assetPath;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
assetPath, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetPathById, assetId);
|
||||
|
||||
auto readResult = AZ::Utils::ReadFile(assetPath, AZStd::numeric_limits<size_t>::max());
|
||||
if (!readResult.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"ProceduralPrefabSystemComponent::OnCatalogAssetChanged - Failed to read Prefab file from '%.*s'."
|
||||
"Error message: '%s'",
|
||||
AZ_STRING_ARG(assetPath), readResult.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::Outcome<PrefabDom, AZStd::string> readPrefabFileResult = AZ::JsonSerializationUtils::ReadJsonString(readResult.TakeValue());
|
||||
if (!readPrefabFileResult.IsSuccess())
|
||||
{
|
||||
AZ_Error(
|
||||
"Prefab", false,
|
||||
"ProceduralPrefabSystemComponent::OnCatalogAssetChanged - Failed to read Prefab json from '%.*s'."
|
||||
"Error message: '%s'",
|
||||
AZ_STRING_ARG(assetPath), readPrefabFileResult.GetError().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
AZ::IO::Path relativePath = AZ::Interface<PrefabLoaderInterface>::Get()->GenerateRelativePath(assetPath.c_str());
|
||||
PrefabDomPath sourcePath = PrefabDomPath((AZStd::string("/") + PrefabDomUtils::SourceName).c_str());
|
||||
sourcePath.Set(readPrefabFileResult.GetValue(), relativePath.Native().c_str());
|
||||
|
||||
prefabSystemComponentInterface->UpdatePrefabTemplate(templateId, readPrefabFileResult.TakeValue());
|
||||
}
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::OnTemplateRemoved(TemplateId removedTemplateId)
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
|
||||
for (const auto& [assetId, templateId] : m_assetIdToTemplateLookup)
|
||||
{
|
||||
if (templateId == removedTemplateId)
|
||||
{
|
||||
m_assetIdToTemplateLookup.erase(assetId);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::OnAllTemplatesRemoved()
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
|
||||
m_assetIdToTemplateLookup.clear();
|
||||
}
|
||||
|
||||
void ProceduralPrefabSystemComponent::RegisterProceduralPrefab(const AZStd::string& prefabFilePath, TemplateId templateId)
|
||||
{
|
||||
AZ::Data::AssetId assetId;
|
||||
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
|
||||
assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, prefabFilePath.c_str(),
|
||||
azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>(), false);
|
||||
|
||||
if (assetId.IsValid())
|
||||
{
|
||||
AZStd::scoped_lock lock(m_lookupMutex);
|
||||
m_assetIdToTemplateLookup[assetId] = templateId;
|
||||
}
|
||||
else
|
||||
{
|
||||
AZ_Error("Prefab", false, "Failed to find AssetId for prefab %s", prefabFilePath.c_str());
|
||||
}
|
||||
}
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/Component/Component.h>
|
||||
#include <Prefab/ProceduralPrefabSystemComponentInterface.h>
|
||||
#include <AzFramework/Asset/AssetCatalogBus.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabPublicNotificationBus.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class ProceduralPrefabSystemComponent
|
||||
: public AZ::Component
|
||||
, ProceduralPrefabSystemComponentInterface
|
||||
, AzFramework::AssetCatalogEventBus::Handler
|
||||
, PrefabPublicNotificationBus::Handler
|
||||
{
|
||||
public:
|
||||
AZ_COMPONENT(ProceduralPrefabSystemComponent, "{81211818-088A-49E6-894B-7A11764106B1}");
|
||||
|
||||
static void Reflect(AZ::ReflectContext* context);
|
||||
|
||||
protected:
|
||||
void Activate() override;
|
||||
void Deactivate() override;
|
||||
|
||||
void OnCatalogAssetChanged(const AZ::Data::AssetId&) override;
|
||||
|
||||
void OnTemplateRemoved(TemplateId templateId) override;
|
||||
void OnAllTemplatesRemoved() override;
|
||||
|
||||
void RegisterProceduralPrefab(const AZStd::string& prefabFilePath, TemplateId templateId) override;
|
||||
|
||||
AZStd::mutex m_lookupMutex;
|
||||
AZStd::unordered_map<AZ::Data::AssetId, TemplateId> m_assetIdToTemplateLookup;
|
||||
};
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
/*
|
||||
* Copyright (c) Contributors to the Open 3D Engine Project.
|
||||
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0 OR MIT
|
||||
*
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AzCore/RTTI/RTTI.h>
|
||||
#include <AzToolsFramework/Prefab/PrefabIdTypes.h>
|
||||
|
||||
namespace AzToolsFramework
|
||||
{
|
||||
namespace Prefab
|
||||
{
|
||||
class ProceduralPrefabSystemComponentInterface
|
||||
{
|
||||
public:
|
||||
AZ_RTTI(ProceduralPrefabSystemComponentInterface, "{C403B89C-63DD-418C-B821-FBCBE1EF42AE}");
|
||||
|
||||
virtual ~ProceduralPrefabSystemComponentInterface() = default;
|
||||
|
||||
// Registers a procedural prefab file + templateId so the system can track changes and handle updates
|
||||
virtual void RegisterProceduralPrefab(const AZStd::string& prefabFilePath, TemplateId templateId) = 0;
|
||||
};
|
||||
|
||||
} // namespace Prefab
|
||||
} // namespace AzToolsFramework
|
||||
|
||||
Reference in New Issue
Block a user