diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp index 814c4e2a7f..36b39f3a72 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Instance/InstanceToTemplatePropagator.cpp @@ -46,7 +46,6 @@ namespace AzToolsFramework bool InstanceToTemplatePropagator::GenerateDomForEntity(PrefabDom& generatedEntityDom, const AZ::Entity& entity) { - //grab the owning instance so we can use the entityIdMapper in settings InstanceOptionalReference owningInstance = m_instanceEntityMapperInterface->FindOwningInstance(entity.GetId()); if (!owningInstance) @@ -54,19 +53,8 @@ namespace AzToolsFramework AZ_Error("Prefab", false, "Entity does not belong to an instance"); return false; } - - InstanceEntityIdMapper entityIdMapper; - entityIdMapper.SetStoringInstance(owningInstance->get()); - //create settings so that the serialized entity dom undergoes mapping from entity id to entity alias - AZ::JsonSerializerSettings settings; - settings.m_metadata.Add(static_cast(&entityIdMapper)); - - //generate PrefabDom using Json serialization system - AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Store( - generatedEntityDom, generatedEntityDom.GetAllocator(), entity, settings); - - return result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success; + return PrefabDomUtils::StoreEntityInPrefabDomFormat(entity, owningInstance->get(), generatedEntityDom); } bool InstanceToTemplatePropagator::GenerateDomForInstance(PrefabDom& generatedInstanceDom, const Prefab::Instance& instance) @@ -185,8 +173,10 @@ namespace AzToolsFramework else { AZ_Error( - "Prefab", result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::PartialSkip, - "Some of the patches are not successfully applied."); + "Prefab", + (result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::Skipped) && + (result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::PartialSkip), + "Some of the patches were not successfully applied."); m_prefabSystemComponentInterface->SetTemplateDirtyFlag(templateId, true); m_prefabSystemComponentInterface->PropagateTemplateChanges(templateId, instanceToExclude); return true; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp index 3c7e5ff3f8..940a2787cb 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Link/Link.cpp @@ -196,7 +196,8 @@ namespace AzToolsFramework m_sourceTemplateId, m_targetTemplateId); return false; } - if (applyPatchResult.GetOutcome() == AZ::JsonSerializationResult::Outcomes::PartialSkip) + if (applyPatchResult.GetOutcome() == AZ::JsonSerializationResult::Outcomes::PartialSkip || + applyPatchResult.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Skipped) { AZ_Error( "Prefab", false, diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp index 86983d7912..1ad88e53d2 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.cpp @@ -26,6 +26,30 @@ namespace AzToolsFramework { namespace PrefabDomUtils { + namespace Internal + { + AZ::JsonSerializationResult::ResultCode JsonIssueReporter(AZStd::string& scratchBuffer, + AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, AZStd::string_view path) + { + namespace JSR = AZ::JsonSerializationResult; + + if (result.GetProcessing() == JSR::Processing::Halted) + { + scratchBuffer.append(message.begin(), message.end()); + scratchBuffer.append("\n Reason: "); + result.AppendToString(scratchBuffer, path); + scratchBuffer.append("."); + AZ_Warning("Prefab Serialization", false, "%s", scratchBuffer.c_str()); + + scratchBuffer.clear(); + + return JSR::ResultCode(result.GetTask(), JSR::Outcomes::Skipped); + } + + return result; + } + } + PrefabDomValueReference FindPrefabDomValue(PrefabDomValue& parentValue, const char* valueName) { PrefabDomValue::MemberIterator valueIterator = parentValue.FindMember(valueName); @@ -48,7 +72,7 @@ namespace AzToolsFramework return valueIterator->value; } - bool StoreInstanceInPrefabDom(const Instance& instance, PrefabDom& prefabDom, StoreInstanceFlags flags) + bool StoreInstanceInPrefabDom(const Instance& instance, PrefabDom& prefabDom, StoreFlags flags) { InstanceEntityIdMapper entityIdMapper; entityIdMapper.SetStoringInstance(instance); @@ -59,11 +83,21 @@ namespace AzToolsFramework settings.m_metadata.Add(static_cast(&entityIdMapper)); settings.m_metadata.Add(&entityIdMapper); - if ((flags & StoreInstanceFlags::StripDefaultValues) != StoreInstanceFlags::StripDefaultValues) + if ((flags & StoreFlags::StripDefaultValues) != StoreFlags::StripDefaultValues) { settings.m_keepDefaults = true; } + AZStd::string scratchBuffer; + auto issueReportingCallback = [&scratchBuffer] + (AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, + AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode + { + return Internal::JsonIssueReporter(scratchBuffer, message, result, path); + }; + + settings.m_reporting = AZStd::move(issueReportingCallback); + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Store(prefabDom, prefabDom.GetAllocator(), instance, settings); @@ -80,7 +114,38 @@ namespace AzToolsFramework return true; } - bool LoadInstanceFromPrefabDom(Instance& instance, const PrefabDom& prefabDom, LoadInstanceFlags flags) + bool StoreEntityInPrefabDomFormat(const AZ::Entity& entity, Instance& owningInstance, PrefabDom& prefabDom, StoreFlags flags) + { + InstanceEntityIdMapper entityIdMapper; + entityIdMapper.SetStoringInstance(owningInstance); + + //create settings so that the serialized entity dom undergoes mapping from entity id to entity alias + AZ::JsonSerializerSettings settings; + settings.m_metadata.Add(static_cast(&entityIdMapper)); + + if ((flags & StoreFlags::StripDefaultValues) != StoreFlags::StripDefaultValues) + { + settings.m_keepDefaults = true; + } + + AZStd::string scratchBuffer; + auto issueReportingCallback = [&scratchBuffer] + (AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, + AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode + { + return Internal::JsonIssueReporter(scratchBuffer, message, result, path); + }; + + settings.m_reporting = AZStd::move(issueReportingCallback); + + //generate PrefabDom using Json serialization system + AZ::JsonSerializationResult::ResultCode result = AZ::JsonSerialization::Store( + prefabDom, prefabDom.GetAllocator(), entity, settings); + + return result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success; + } + + bool LoadInstanceFromPrefabDom(Instance& instance, const PrefabDom& prefabDom, LoadFlags flags) { // When entities are rebuilt they are first destroyed. As a result any assets they were exclusively holding on to will // be released and reloaded once the entities are built up again. By suspending asset release temporarily the asset reload @@ -89,7 +154,7 @@ namespace AzToolsFramework InstanceEntityIdMapper entityIdMapper; entityIdMapper.SetLoadingInstance(instance); - if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId) + if ((flags & LoadFlags::AssignRandomEntityId) == LoadFlags::AssignRandomEntityId) { entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random); } @@ -119,7 +184,7 @@ namespace AzToolsFramework } bool LoadInstanceFromPrefabDom( - Instance& instance, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets, LoadInstanceFlags flags) + Instance& instance, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets, LoadFlags flags) { // When entities are rebuilt they are first destroyed. As a result any assets they were exclusively holding on to will // be released and reloaded once the entities are built up again. By suspending asset release temporarily the asset reload @@ -128,7 +193,7 @@ namespace AzToolsFramework InstanceEntityIdMapper entityIdMapper; entityIdMapper.SetLoadingInstance(instance); - if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId) + if ((flags & LoadFlags::AssignRandomEntityId) == LoadFlags::AssignRandomEntityId) { entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random); } @@ -161,7 +226,7 @@ namespace AzToolsFramework } bool LoadInstanceFromPrefabDom( - Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadInstanceFlags flags) + Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, LoadFlags flags) { // When entities are rebuilt they are first destroyed. As a result any assets they were exclusively holding on to will // be released and reloaded once the entities are built up again. By suspending asset release temporarily the asset reload @@ -170,7 +235,7 @@ namespace AzToolsFramework InstanceEntityIdMapper entityIdMapper; entityIdMapper.SetLoadingInstance(instance); - if ((flags & LoadInstanceFlags::AssignRandomEntityId) == LoadInstanceFlags::AssignRandomEntityId) + if ((flags & LoadFlags::AssignRandomEntityId) == LoadFlags::AssignRandomEntityId) { entityIdMapper.SetEntityIdGenerationApproach(InstanceEntityIdMapper::EntityIdGenerationApproach::Random); } @@ -240,15 +305,12 @@ namespace AzToolsFramework AZ::JsonSerializationResult::ResultCode ApplyPatches( PrefabDomValue& prefabDomToApplyPatchesOn, PrefabDom::AllocatorType& allocator, const PrefabDomValue& patches) { - auto issueReportingCallback = [](AZStd::string_view, AZ::JsonSerializationResult::ResultCode result, - AZStd::string_view) -> AZ::JsonSerializationResult::ResultCode + AZStd::string scratchBuffer; + auto issueReportingCallback = [&scratchBuffer] + (AZStd::string_view message, AZ::JsonSerializationResult::ResultCode result, + AZStd::string_view path) -> AZ::JsonSerializationResult::ResultCode { - using namespace AZ::JsonSerializationResult; - if (result.GetProcessing() == Processing::Halted) - { - return ResultCode(result.GetTask(), Outcomes::PartialSkip); - } - return result; + return Internal::JsonIssueReporter(scratchBuffer, message, result, path); }; AZ::JsonApplyPatchSettings applyPatchSettings; diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h index e773b581dd..b04cf9f57d 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabDomUtils.h @@ -38,7 +38,7 @@ namespace AzToolsFramework PrefabDomValueReference FindPrefabDomValue(PrefabDomValue& parentValue, const char* valueName); PrefabDomValueConstReference FindPrefabDomValue(const PrefabDomValue& parentValue, const char* valueName); - enum class StoreInstanceFlags : uint8_t + enum class StoreFlags : uint8_t { //! No flags used during the call to LoadInstanceFromPrefabDom. None = 0, @@ -47,7 +47,7 @@ namespace AzToolsFramework //! such as saving to disk, this flag will control that behavior. StripDefaultValues = 1 << 0 }; - AZ_DEFINE_ENUM_BITWISE_OPERATORS(StoreInstanceFlags); + AZ_DEFINE_ENUM_BITWISE_OPERATORS(StoreFlags); /** * Stores a valid Prefab Instance within a Prefab Dom. Useful for generating Templates @@ -56,9 +56,21 @@ namespace AzToolsFramework * @param flags Controls behavior such as whether to store default values * @return bool on whether the operation succeeded */ - bool StoreInstanceInPrefabDom(const Instance& instance, PrefabDom& prefabDom, StoreInstanceFlags flags = StoreInstanceFlags::None); + bool StoreInstanceInPrefabDom(const Instance& instance, PrefabDom& prefabDom, StoreFlags flags = StoreFlags::None); - enum class LoadInstanceFlags : uint8_t + /** + * Stores a valid entity in Prefab Dom format. + * @param entity The entity to store + * @param owningInstance The instance owning the passed in entity. + * Used for contextualizing the entity's place in a Prefab hierarchy. + * @param prefabDom The prefabDom that will be used to store the entity data + * @param flags controls behavior such as whether to store default values + * @return bool on whether the operation succeeded + */ + bool StoreEntityInPrefabDomFormat(const AZ::Entity& entity, Instance& owningInstance, PrefabDom& prefabDom, + StoreFlags flags = StoreFlags::None); + + enum class LoadFlags : uint8_t { //! No flags used during the call to LoadInstanceFromPrefabDom. None = 0, @@ -66,7 +78,7 @@ namespace AzToolsFramework //! unique, e.g. when they are duplicates of live entities, this flag will assign them a random new id. AssignRandomEntityId = 1 << 0 }; - AZ_DEFINE_ENUM_BITWISE_OPERATORS(LoadInstanceFlags); + AZ_DEFINE_ENUM_BITWISE_OPERATORS(LoadFlags); /** * Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances. @@ -76,7 +88,7 @@ namespace AzToolsFramework * @return bool on whether the operation succeeded. */ bool LoadInstanceFromPrefabDom( - Instance& instance, const PrefabDom& prefabDom, LoadInstanceFlags flags = LoadInstanceFlags::None); + Instance& instance, const PrefabDom& prefabDom, LoadFlags flags = LoadFlags::None); /** * Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances. @@ -88,7 +100,7 @@ namespace AzToolsFramework */ bool LoadInstanceFromPrefabDom( Instance& instance, const PrefabDom& prefabDom, AZStd::vector>& referencedAssets, - LoadInstanceFlags flags = LoadInstanceFlags::None); + LoadFlags flags = LoadFlags::None); /** * Loads a valid Prefab Instance from a Prefab Dom. Useful for generating Instances. @@ -101,7 +113,7 @@ namespace AzToolsFramework */ bool LoadInstanceFromPrefabDom( Instance& instance, Instance::EntityList& newlyAddedEntities, const PrefabDom& prefabDom, - LoadInstanceFlags flags = LoadInstanceFlags::None); + LoadFlags flags = LoadFlags::None); inline PrefabDomPath GetPrefabDomInstancePath(const char* instanceName) { diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp index ea54c9d10c..5091a2d1bc 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabLoader.cpp @@ -328,7 +328,7 @@ namespace AzToolsFramework PrefabDom storedPrefabDom(&savingTemplateDom->get().GetAllocator()); if (!PrefabDomUtils::StoreInstanceInPrefabDom(savingPrefabInstance, storedPrefabDom, - PrefabDomUtils::StoreInstanceFlags::StripDefaultValues)) + PrefabDomUtils::StoreFlags::StripDefaultValues)) { return false; } diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp index 955af18a66..fc64e0b5b3 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/PrefabUndo.cpp @@ -266,8 +266,8 @@ namespace AzToolsFramework AZ_Error( "Prefab", - result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::PartialSkip || - result.GetOutcome() == AZ::JsonSerializationResult::Outcomes::Success, + (result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::Skipped) && + (result.GetOutcome() != AZ::JsonSerializationResult::Outcomes::PartialSkip), "Some of the patches are not successfully applied."); //remove the link id placed into the instance diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp index 1d856fa395..dfba1d54ba 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/EditorInfoRemover.cpp @@ -513,7 +513,7 @@ exportComponent, prefabProcessorContext); // convert Prefab DOM into Prefab Instance. AZStd::unique_ptr instance(aznew Instance()); if (!Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(*instance, prefab, - Prefab::PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId)) + Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) { PrefabDomValueReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp index 1373d2fa75..902f439280 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/Prefab/Spawnable/SpawnableUtils.cpp @@ -30,7 +30,7 @@ namespace AzToolsFramework::Prefab::SpawnableUtils { Instance instance; if (Prefab::PrefabDomUtils::LoadInstanceFromPrefabDom(instance, prefabDom, referencedAssets, - Prefab::PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is + Prefab::PrefabDomUtils::LoadFlags::AssignRandomEntityId)) // Always assign random entity ids because the spawnable is // going to be used to create clones of the entities. { AzFramework::Spawnable::EntityList& entities = spawnable.GetEntities(); diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabInstanceToTemplatePropagatorTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabInstanceToTemplatePropagatorTests.cpp index 8379992553..ebe35a5402 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabInstanceToTemplatePropagatorTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabInstanceToTemplatePropagatorTests.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -18,6 +19,98 @@ namespace UnitTest { using PrefabInstanceToTemplateTests = PrefabTestFixture; + TEST_F(PrefabInstanceToTemplateTests, GenerateEntityDom_InvalidType_InvalidTypeSkipped) + { + const char* newEntityName = "New Entity"; + AZ::Entity* newEntity = CreateEntity(newEntityName, false); + ASSERT_TRUE(newEntity); + + // Add a component with a member that is missing reflection info + // and a member that is properly reflected + PrefabTestComponentWithUnReflectedTypeMember* newComponent = + newEntity->CreateComponent(); + + ASSERT_TRUE(newComponent); + + AZStd::unique_ptr prefabInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path"); + ASSERT_TRUE(prefabInstance); + + PrefabDom entityDom; + m_instanceToTemplateInterface->GenerateDomForEntity(entityDom, *newEntity); + + auto componentListDom = entityDom.FindMember("Components"); + + // Confirm that there is only one component in the entityDom + ASSERT_NE(componentListDom, entityDom.MemberEnd()); + ASSERT_TRUE(componentListDom->value.IsObject()); + ASSERT_EQ(componentListDom->value.MemberCount(), 1); + + auto testComponentDom = componentListDom->value.MemberBegin(); + ASSERT_TRUE(testComponentDom->value.IsObject()); + + // Confirm that the componentDom does not contained the invalid UnReflectedType + // We want to skip over it and produce a best effort entityDom + auto unReflectedTypeDom = testComponentDom->value.FindMember("UnReflectedType"); + ASSERT_EQ(unReflectedTypeDom, testComponentDom->value.MemberEnd()); + + // Confirm the presence of the valid ReflectedType + auto reflectedTypeDom = testComponentDom->value.FindMember("ReflectedType"); + ASSERT_NE(reflectedTypeDom, testComponentDom->value.MemberEnd()); + + // Confirm the reflected type has the correct type and value + ASSERT_TRUE(reflectedTypeDom->value.IsInt()); + EXPECT_EQ(reflectedTypeDom->value.GetInt(), newComponent->m_reflectedType); + } + + TEST_F(PrefabInstanceToTemplateTests, GenerateInstanceDom_InvalidType_InvalidTypeSkipped) + { + const char* newEntityName = "New Entity"; + AZ::Entity* newEntity = CreateEntity(newEntityName, false); + ASSERT_TRUE(newEntity); + + // Add a component with a member that is missing reflection info + // and a member that is properly reflected + PrefabTestComponentWithUnReflectedTypeMember* newComponent = + newEntity->CreateComponent(); + + ASSERT_TRUE(newComponent); + + AZStd::unique_ptr prefabInstance = m_prefabSystemComponent->CreatePrefab({ newEntity }, {}, "test/path"); + ASSERT_TRUE(prefabInstance); + + PrefabDom instanceDom; + m_instanceToTemplateInterface->GenerateDomForInstance(instanceDom, *prefabInstance); + + // Acquire the entity out of the instanceDom + auto entitiesDom = instanceDom.FindMember(PrefabDomUtils::EntitiesName); + ASSERT_NE(entitiesDom, instanceDom.MemberEnd()); + ASSERT_EQ(entitiesDom->value.MemberCount(), 1); + + auto entityDom = entitiesDom->value.MemberBegin(); + auto componentListDom = entityDom->value.FindMember("Components"); + + // Confirm that there is only one component in the entityDom + ASSERT_NE(componentListDom, entityDom->value.MemberEnd()); + ASSERT_TRUE(componentListDom->value.IsObject()); + ASSERT_EQ(componentListDom->value.MemberCount(), 1); + + auto testComponentDom = componentListDom->value.MemberBegin(); + ASSERT_TRUE(testComponentDom->value.IsObject()); + + // Confirm that the componentDom does not contained the invalid UnReflectedType + // We want to skip over it and produce a best effort entityDom + auto unReflectedTypeDom = testComponentDom->value.FindMember("UnReflectedType"); + ASSERT_EQ(unReflectedTypeDom, testComponentDom->value.MemberEnd()); + + // Confirm the presence of the valid ReflectedType + auto reflectedTypeDom = testComponentDom->value.FindMember("ReflectedType"); + ASSERT_NE(reflectedTypeDom, testComponentDom->value.MemberEnd()); + + // Confirm the reflected type has the correct type and value + ASSERT_TRUE(reflectedTypeDom->value.IsInt()); + EXPECT_EQ(reflectedTypeDom->value.GetInt(), newComponent->m_reflectedType); + } + TEST_F(PrefabInstanceToTemplateTests, PrefabUpdateTemplate_UpdateEntityOnInstance) { //create template with single entity diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.cpp index 8f7ee398a0..5d29b179c4 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.cpp @@ -28,4 +28,18 @@ namespace UnitTest : m_boolProperty(boolProperty) { } + + void PrefabTestComponentWithUnReflectedTypeMember::Reflect(AZ::ReflectContext* reflection) + { + AZ::SerializeContext* serializeContext = AZ::RttiCast(reflection); + + // We reflect our member but not its type this will result in missing reflection data + // when we try to store or load this field + if (serializeContext) + { + serializeContext->Class() + ->Field("UnReflectedType", &PrefabTestComponentWithUnReflectedTypeMember::m_unReflectedType) + ->Field("ReflectedType", &PrefabTestComponentWithUnReflectedTypeMember::m_reflectedType); + } + } } diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.h b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.h index 0ed8f6d9c9..e0e986b7db 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.h +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestComponent.h @@ -27,4 +27,22 @@ namespace UnitTest int m_intProperty = 0; AZ::EntityId m_entityIdProperty; }; + + class UnReflectedType + { + public: + AZ_TYPE_INFO(UnReflectedType, "{FB65262C-CE9A-45CA-99EB-4DDCB19B32DB}"); + int m_unReflectedInt = 42; + }; + + class PrefabTestComponentWithUnReflectedTypeMember + : public AzToolsFramework::Components::EditorComponentBase + { + public: + AZ_EDITOR_COMPONENT(PrefabTestComponentWithUnReflectedTypeMember, "{726281E1-8E47-46AB-8018-D3F4BA823D74}"); + static void Reflect(AZ::ReflectContext* reflection); + + UnReflectedType m_unReflectedType; + int m_reflectedType = 52; + }; } diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestFixture.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestFixture.cpp index ded88d2da3..ed30de09c4 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestFixture.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabTestFixture.cpp @@ -49,6 +49,7 @@ namespace UnitTest EXPECT_TRUE(m_instanceToTemplateInterface); GetApplication()->RegisterComponentDescriptor(PrefabTestComponent::CreateDescriptor()); + GetApplication()->RegisterComponentDescriptor(PrefabTestComponentWithUnReflectedTypeMember::CreateDescriptor()); } AZStd::unique_ptr PrefabTestFixture::CreateTestApplication() diff --git a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp index 72e88c6336..c17763f778 100644 --- a/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp +++ b/Code/Framework/AzToolsFramework/Tests/Prefab/PrefabUpdateWithPatchesTests.cpp @@ -136,7 +136,10 @@ namespace UnitTest PrefabDomValueReference wheelEntityComponentBoolPropertyValue = PrefabDomUtils::FindPrefabDomValue(wheelEntityComponentValue->get(), PrefabTestDomUtils::BoolPropertyName); - ASSERT_FALSE(wheelEntityComponentBoolPropertyValue.has_value()); + + ASSERT_TRUE(wheelEntityComponentBoolPropertyValue.has_value()); + ASSERT_TRUE(wheelEntityComponentBoolPropertyValue->get().IsBool()); + ASSERT_FALSE(wheelEntityComponentBoolPropertyValue->get().GetBool()); // Validate that the axles under the car have the same DOM as the axle template. PrefabTestDomUtils::ValidatePrefabDomInstances(axleInstanceAliasesUnderCar, carTemplateDom, axleTemplateDom); diff --git a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp index d49f6901f3..acfec5eb38 100644 --- a/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp +++ b/Gems/Multiplayer/Code/Source/Pipeline/NetworkPrefabProcessor.cpp @@ -56,7 +56,7 @@ namespace Multiplayer // convert Prefab DOM into Prefab Instance. AZStd::unique_ptr sourceInstance(aznew Instance()); - if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*sourceInstance, prefab, PrefabDomUtils::LoadInstanceFlags::AssignRandomEntityId)) + if (!PrefabDomUtils::LoadInstanceFromPrefabDom(*sourceInstance, prefab, PrefabDomUtils::LoadFlags::AssignRandomEntityId)) { PrefabDomValueConstReference sourceReference = PrefabDomUtils::FindPrefabDomValue(prefab, PrefabDomUtils::SourceName);