[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>
monroegm-disable-blank-issue-2
amzn-mike 4 years ago committed by GitHub
parent 45abb9f88e
commit 71bb200e0a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -69,6 +69,15 @@ namespace UnitTest
return numAssertsFailed;
}
void ResetSuppressionSettingsToDefault()
{
m_suppressErrors = true;
m_suppressWarnings = true;
m_suppressAsserts = true;
m_suppressOutput = true;
m_suppressPrintf = true;
}
bool m_isAssertTest;
bool m_suppressErrors = true;
bool m_suppressWarnings = true;

@ -73,6 +73,7 @@
#include <AzToolsFramework/Prefab/PrefabPublicInterface.h>
#include <Entity/EntityUtilityComponent.h>
#include <AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h>
#include <Prefab/ProceduralPrefabSystemComponent.h>
#include <QtWidgets/QMessageBox>
AZ_PUSH_DISABLE_WARNING(4251, "-Wunknown-warning-option") // 4251: 'QFileInfo::d_ptr': class 'QSharedDataPointer<QFileInfoPrivate>' needs to have dll-interface to be used by clients of class 'QFileInfo'
@ -272,6 +273,7 @@ namespace AzToolsFramework
azrtti_typeid<ReadOnlyEntitySystemComponent>(),
azrtti_typeid<SliceMetadataEntityContextComponent>(),
azrtti_typeid<Prefab::PrefabSystemComponent>(),
azrtti_typeid<Prefab::ProceduralPrefabSystemComponent>(),
azrtti_typeid<EditorEntityFixupComponent>(),
azrtti_typeid<Components::EditorComponentAPIComponent>(),
azrtti_typeid<Components::EditorLevelComponentAPIComponent>(),

@ -56,6 +56,7 @@
#include <AzToolsFramework/ViewportSelection/EditorInteractionSystemComponent.h>
#include <AzToolsFramework/Entity/EntityUtilityComponent.h>
#include <AzToolsFramework/Script/LuaSymbolsReporterSystemComponent.h>
#include <Prefab/ProceduralPrefabSystemComponent.h>
AZ_DEFINE_BUDGET(AzToolsFramework);
@ -82,6 +83,7 @@ namespace AzToolsFramework
SliceRequestComponent::CreateDescriptor(),
Prefab::PrefabSystemComponent::CreateDescriptor(),
Prefab::EditorPrefabComponent::CreateDescriptor(),
Prefab::ProceduralPrefabSystemComponent::CreateDescriptor(),
Components::EditorEntityActionComponent::CreateDescriptor(),
Components::EditorEntityIconComponent::CreateDescriptor(),
Components::EditorInspectorComponent::CreateDescriptor(),

@ -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

@ -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

@ -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

@ -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

@ -670,6 +670,9 @@ set(FILES
Prefab/PrefabSystemComponent.h
Prefab/PrefabSystemComponent.cpp
Prefab/PrefabSystemComponentInterface.h
Prefab/ProceduralPrefabSystemComponent.h
Prefab/ProceduralPrefabSystemComponent.cpp
Prefab/ProceduralPrefabSystemComponentInterface.h
Prefab/PrefabSystemScriptingBus.h
Prefab/PrefabSystemScriptingHandler.h
Prefab/PrefabSystemScriptingHandler.cpp

@ -0,0 +1,302 @@
/*
* 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 <AzCore/IO/FileIO.h>
#include <AzCore/IO/Path/PathReflect.h>
#include <AzCore/Serialization/Json/JsonSystemComponent.h>
#include <AzCore/Serialization/Json/RegistrationContext.h>
#include <AzCore/Settings/SettingsRegistryImpl.h>
#include <AzFramework/IO/LocalFileIO.h>
#include <Prefab/PrefabTestFixture.h>
#include <Prefab/ProceduralPrefabSystemComponent.h>
#include <Utils/Utils.h>
#include <AzCore/Settings/SettingsRegistryMergeUtils.h>
namespace UnitTest
{
struct ProceduralPrefabSystemComponentTests
: AllocatorsTestFixture
, AZ::ComponentApplicationBus::Handler
{
void SetUp() override
{
TestRunner::Instance().m_suppressOutput = false;
TestRunner::Instance().m_suppressPrintf = false;
TestRunner::Instance().m_suppressWarnings = false;
TestRunner::Instance().m_suppressErrors = false;
TestRunner::Instance().m_suppressAsserts = false;
AllocatorsTestFixture::SetUp();
AZ::ComponentApplicationBus::Handler::BusConnect();
ASSERT_TRUE(m_temporaryDirectory.IsValid());
m_localFileIo = AZStd::make_unique<AZ::IO::LocalFileIO>();
m_prevIoBase = AZ::IO::FileIOBase::GetInstance();
AZ::IO::FileIOBase::SetInstance(nullptr); // Need to clear the previous instance first
AZ::IO::FileIOBase::SetInstance(m_localFileIo.get());
AZ::JsonSystemComponent::Reflect(&m_jsonContext);
m_settingsRegistry = AZStd::make_unique<AZ::SettingsRegistryImpl>();
AZ::SettingsRegistry::Register(m_settingsRegistry.get());
m_settingsRegistry->Set(AZ::SettingsRegistryMergeUtils::FilePathKey_ProjectPath, m_temporaryDirectory.GetDirectory());
m_prefabSystem = PrefabSystemComponent::CreateDescriptor();
m_procSystem = ProceduralPrefabSystemComponent::CreateDescriptor();
m_prefabSystem->Reflect(&m_context);
m_prefabSystem->Reflect(&m_jsonContext);
m_procSystem->Reflect(&m_context);
m_procSystem->Reflect(&m_jsonContext);
AZ::Entity::Reflect(&m_context);
AZ::Entity::Reflect(&m_jsonContext);
AZ::IO::PathReflect(&m_context);
m_systemEntity = AZStd::make_unique<AZ::Entity>();
m_systemEntity->CreateComponent<PrefabSystemComponent>();
m_systemEntity->CreateComponent<ProceduralPrefabSystemComponent>();
m_systemEntity->Init();
m_systemEntity->Activate();
AZ::Data::AssetManager::Create({});
}
void TearDown() override
{
AZ::Data::AssetManager::Destroy();
m_systemEntity->Deactivate();
m_systemEntity = nullptr;
m_jsonContext.EnableRemoveReflection();
AZ::JsonSystemComponent::Reflect(&m_jsonContext);
m_prefabSystem->Reflect(&m_jsonContext);
m_procSystem->Reflect(&m_jsonContext);
AZ::Entity::Reflect(&m_jsonContext);
m_jsonContext.DisableRemoveReflection();
AZ::IO::FileIOBase::SetInstance(nullptr); // Clear the previous instance first
AZ::IO::FileIOBase::SetInstance(m_prevIoBase);
m_prevIoBase = nullptr;
AZ::SettingsRegistry::Unregister(m_settingsRegistry.get());
AZ::ComponentApplicationBus::Handler::BusDisconnect();
AllocatorsTestFixture::TearDown();
TestRunner::Instance().ResetSuppressionSettingsToDefault();
}
// ComponentApplicationBus
AZ::ComponentApplication* GetApplication() override
{
return nullptr;
}
void RegisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
void UnregisterComponentDescriptor(const AZ::ComponentDescriptor*) override { }
void RegisterEntityAddedEventHandler(AZ::EntityAddedEvent::Handler&) override { }
void RegisterEntityRemovedEventHandler(AZ::EntityRemovedEvent::Handler&) override { }
void RegisterEntityActivatedEventHandler(AZ::EntityActivatedEvent::Handler&) override { }
void RegisterEntityDeactivatedEventHandler(AZ::EntityDeactivatedEvent::Handler&) override { }
void SignalEntityActivated(AZ::Entity*) override { }
void SignalEntityDeactivated(AZ::Entity*) override { }
bool AddEntity(AZ::Entity*) override
{
return true;
}
bool RemoveEntity(AZ::Entity*) override
{
return true;
}
bool DeleteEntity(const AZ::EntityId&) override
{
return true;
}
AZ::Entity* FindEntity(const AZ::EntityId&) override
{
return nullptr;
}
AZ::SerializeContext* GetSerializeContext() override
{
return &m_context;
}
AZ::BehaviorContext* GetBehaviorContext() override
{
return nullptr;
}
AZ::JsonRegistrationContext* GetJsonRegistrationContext() override
{
return &m_jsonContext;
}
const char* GetEngineRoot() const override
{
return nullptr;
}
const char* GetExecutableFolder() const override
{
return nullptr;
}
void EnumerateEntities(const EntityCallback& /*callback*/) override { }
void QueryApplicationType(AZ::ApplicationTypeQuery& /*appType*/) const override { }
////
AZ::ComponentDescriptor* m_prefabSystem{};
AZ::ComponentDescriptor* m_procSystem{};
AZStd::unique_ptr<AZ::SettingsRegistryImpl> m_settingsRegistry;
AZ::SerializeContext m_context;
AZ::JsonRegistrationContext m_jsonContext;
AZStd::unique_ptr<AZ::IO::LocalFileIO> m_localFileIo;
ScopedTemporaryDirectory m_temporaryDirectory;
AZStd::unique_ptr<AZ::Entity> m_systemEntity;
AZ::IO::FileIOBase* m_prevIoBase{};
};
struct MockCatalog : AZ::Data::AssetCatalogRequestBus::Handler
{
static const inline AZ::Data::AssetId TestId{ AZ::Uuid::CreateRandom(), 1234 };
MockCatalog(AZStd::string testFile)
: m_testFile(AZStd::move(testFile))
{
BusConnect();
}
~MockCatalog() override
{
BusDisconnect();
}
AZStd::string GetAssetPathById(const AZ::Data::AssetId& assetId) override
{
if (assetId == TestId)
{
return m_testFile;
}
return "InvalidAssetId";
}
AZ::Data::AssetId GetAssetIdByPath(const char* path, const AZ::Data::AssetType&, bool) override
{
AZ::IO::PathView pathView{ AZStd::string_view(path) };
if (AZ::IO::PathView(m_testFile) == pathView)
{
return TestId;
}
AZ_Error("MockCatalog", false, "Requested path %s does not match expected asset path of %s", path, m_testFile.c_str());
ADD_FAILURE();
return {};
}
AZStd::string m_testFile;
};
struct PrefabPublicNotificationsListener : PrefabPublicNotificationBus::Handler
{
PrefabPublicNotificationsListener()
{
BusConnect();
}
~PrefabPublicNotificationsListener() override
{
BusDisconnect();
}
void OnPrefabInstancePropagationBegin() override
{
m_updated = true;
}
bool m_updated = false;
};
TEST_F(ProceduralPrefabSystemComponentTests, RegisteredPrefabUpdates)
{
const AZStd::string prefabFile = (AZ::IO::Path(m_temporaryDirectory.GetDirectory()) / "test.prefab").Native();
MockCatalog catalog(prefabFile.c_str());
auto proceduralPrefabSystemComponentInterface = AZ::Interface<ProceduralPrefabSystemComponentInterface>::Get();
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
auto prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
ASSERT_NE(proceduralPrefabSystemComponentInterface, nullptr);
ASSERT_NE(prefabSystemComponentInterface, nullptr);
ASSERT_NE(prefabLoaderInterface, nullptr);
auto entity = aznew AZ::Entity();
AZStd::unique_ptr<Instance> instance = prefabSystemComponentInterface->CreatePrefab({ entity }, {}, prefabFile.c_str());
ASSERT_NE(instance, nullptr);
prefabLoaderInterface->SaveTemplateToFile(instance->GetTemplateId(), prefabFile.c_str());
proceduralPrefabSystemComponentInterface->RegisterProceduralPrefab(prefabFile, instance->GetTemplateId());
AzFramework::AssetCatalogEventBus::Broadcast(&AzFramework::AssetCatalogEventBus::Events::OnCatalogAssetChanged, MockCatalog::TestId);
PrefabPublicNotificationsListener listener;
AZ::SystemTickBus::Broadcast(&AZ::SystemTickBus::Events::OnSystemTick);
EXPECT_TRUE(listener.m_updated);
}
TEST_F(ProceduralPrefabSystemComponentTests, UnregisteredPrefabDoesNotUpdate)
{
PrefabPublicNotificationsListener listener;
const AZStd::string prefabFile = (AZ::IO::Path(m_temporaryDirectory.GetDirectory()) / "test.prefab").Native();
MockCatalog catalog(prefabFile.c_str());
auto proceduralPrefabSystemComponentInterface = AZ::Interface<ProceduralPrefabSystemComponentInterface>::Get();
auto prefabSystemComponentInterface = AZ::Interface<PrefabSystemComponentInterface>::Get();
auto prefabLoaderInterface = AZ::Interface<PrefabLoaderInterface>::Get();
ASSERT_NE(proceduralPrefabSystemComponentInterface, nullptr);
ASSERT_NE(prefabSystemComponentInterface, nullptr);
ASSERT_NE(prefabLoaderInterface, nullptr);
auto entity = aznew AZ::Entity();
AZStd::unique_ptr<Instance> instance = prefabSystemComponentInterface->CreatePrefab({ entity }, {}, prefabFile.c_str());
ASSERT_NE(instance, nullptr);
prefabLoaderInterface->SaveTemplateToFile(instance->GetTemplateId(), prefabFile.c_str());
AzFramework::AssetCatalogEventBus::Broadcast(
&AzFramework::AssetCatalogEventBus::Events::OnCatalogAssetChanged, MockCatalog::TestId);
AZ::SystemTickBus::Broadcast(&AZ::SystemTickBus::Events::OnSystemTick);
EXPECT_FALSE(listener.m_updated);
}
}

@ -105,6 +105,7 @@ set(FILES
Prefab/SpawnableSortEntitiesTests.cpp
Prefab/PrefabScriptingTests.cpp
Prefab/ProceduralPrefabAssetTests.cpp
Prefab/ProceduralPrefabSystemComponentTests.cpp
PropertyIntCtrlCommonTests.cpp
PropertyIntCtrlCommonTests.h
PropertyIntSliderCtrlTests.cpp

@ -136,6 +136,12 @@ namespace UnitTest
auto jsonOutcome = AZ::JsonSerializationUtils::ReadJsonString(Data::jsonPrefab);
ASSERT_TRUE(jsonOutcome);
// Register the asset to generate an AssetId in the catalog
AZ::Data::AssetId assetId;
AZ::Data::AssetCatalogRequestBus::BroadcastResult(
assetId, &AZ::Data::AssetCatalogRequestBus::Events::GetAssetIdByPath, "fake_prefab.procprefab",
azrtti_typeid<AZ::Prefab::ProceduralPrefabAsset>(), true);
auto prefabGroup = AZStd::make_shared<AZ::SceneAPI::SceneData::PrefabGroup>();
prefabGroup.get()->SetId(AZ::Uuid::CreateRandom());
prefabGroup.get()->SetName("fake_prefab");

Loading…
Cancel
Save