diff --git a/Code/CryEngine/CryEntitySystem/Entity.cpp b/Code/CryEngine/CryEntitySystem/Entity.cpp index d1a38cfe1f..14d9cf66b4 100644 --- a/Code/CryEngine/CryEntitySystem/Entity.cpp +++ b/Code/CryEngine/CryEntitySystem/Entity.cpp @@ -1438,9 +1438,6 @@ void CEntity::LoadComponent(Serialization::IArchive& archive) // Add the component to the entity AddComponentInternal(pComponent, typeGUID, &initParams, &componentClassDesc); - - // Initialize the component - pComponent->Initialize(); } else { @@ -1462,9 +1459,6 @@ void CEntity::LoadComponent(Serialization::IArchive& archive) // Apply loaded properties values on the members of the component //classProperties.Apply(componentClassDesc, pComponent.get()); Schematyc::Utils::SerializeClass(archive, componentClassDesc, pComponent.get(), "properties", "properties"); - - // Call component Initialize again - pComponent->Initialize(); } } @@ -1516,21 +1510,6 @@ void CEntity::SaveComponent(Serialization::IArchive& archive, IEntityComponent& Schematyc::Utils::SerializeClass(archive, component.GetClassDesc(), &component, "properties", "properties"); } -struct SEntityComponentSerializationHelper -{ - CEntity& entity; - IEntityComponent* pComponent = nullptr; - SEntityComponentSerializationHelper(CEntity& e) : entity(e) {}; - SEntityComponentSerializationHelper(CEntity& e, IEntityComponent* comp) : entity(e), pComponent(comp) {}; - void Serialize(Serialization::IArchive& archive) - { - if (archive.isInput()) - entity.LoadComponent(archive); - else - entity.SaveComponent(archive, *pComponent); - } -}; - static bool SerializePropertiesWrapper(void* rawPointer, yasli::Archive& ar) { static_cast(rawPointer)->SerializeProperties(ar); @@ -1647,6 +1626,7 @@ void CEntity::SerializeXML(XmlNodeRef& node, bool bLoading, bool bIncludeScriptP { if (XmlNodeRef componentsNode = node->findChild("Components")) { + // Load components into the m_components vector (without initializing them!) for (int i = 0, n = componentsNode->getChildCount(); i < n; ++i) { XmlNodeRef componentNode = componentsNode->getChild(i); @@ -1656,11 +1636,28 @@ void CEntity::SerializeXML(XmlNodeRef& node, bool bLoading, bool bIncludeScriptP } else { - SEntityComponentSerializationHelper componentSerializationHelper(*this); + struct SEntityComponentLoadHelper + { + CEntity& entity; + void Serialize(Serialization::IArchive& archive) + { + entity.LoadComponent(archive); + } + }; - Serialization::LoadXmlNode(componentSerializationHelper, componentNode); + SEntityComponentLoadHelper loadHelper = SEntityComponentLoadHelper{ *this }; + Serialization::LoadXmlNode(loadHelper, componentNode); } } + + // Now that all components are in place, initialize them (does not apply to legacy proxies). + m_components.ForEach([&](const SEntityComponentRecord& record) + { + if (record.proxyType == ENTITY_PROXY_LAST) + { + record.pComponent->Initialize(); + } + }); } m_physics.SerializeXML(node, bLoading); } @@ -1684,7 +1681,19 @@ void CEntity::SerializeXML(XmlNodeRef& node, bool bLoading, bool bIncludeScriptP componentsNode = node->newChild("Components"); } XmlNodeRef componentNode = componentsNode->newChild("Component"); - Serialization::SaveXmlNode(componentNode, Serialization::SStruct(SEntityComponentSerializationHelper(*this, &component))); + + struct SEntityComponentSaveHelper + { + CEntity& entity; + IEntityComponent* pComponent; + void Serialize(Serialization::IArchive& archive) + { + entity.SaveComponent(archive, *pComponent); + } + }; + + SEntityComponentSaveHelper saveHelper{ *this, &component }; + Serialization::SaveXmlNode(componentNode, Serialization::SStruct(saveHelper)); } else if (component.GetPropertyGroup() || component.GetComponentFlags().Check(EEntityComponentFlags::Legacy)) { diff --git a/Code/CryEngine/CryEntitySystem/Entity.h b/Code/CryEngine/CryEntitySystem/Entity.h index 89ca44d30e..3a647b89cc 100644 --- a/Code/CryEngine/CryEntitySystem/Entity.h +++ b/Code/CryEngine/CryEntitySystem/Entity.h @@ -454,9 +454,11 @@ class CEntity : public IEntity ////////////////////////////////////////////////////////////////////////// // Component Save/Load ////////////////////////////////////////////////////////////////////////// + // Loads a component, but leaves it uninitialized void LoadComponent(Serialization::IArchive& archive); void SaveComponent(Serialization::IArchive& archive,IEntityComponent &component); - bool LoadComponentLegacy(XmlNodeRef& entityNode,XmlNodeRef& componentNode); + // Loads a component with the legacy XML format, but leaves it uninitialized + bool LoadComponentLegacy(XmlNodeRef& entityNode, XmlNodeRef& componentNode); void SaveComponentLegacy(CryGUID typeId,XmlNodeRef& entityNode,XmlNodeRef& componentNode,IEntityComponent &component,bool bIncludeScriptProxy); ////////////////////////////////////////////////////////////////////////// @@ -470,7 +472,6 @@ class CEntity : public IEntity friend class CEntityComponentTriggerBounds; friend class CEntityPhysics; friend class CNetEntity; // CNetEntity iterates all components on serialization. - friend struct SEntityComponentSerializationHelper; enum class EBindingType { diff --git a/Code/CryEngine/CryEntitySystem/EntityUnitTests.cpp b/Code/CryEngine/CryEntitySystem/EntityUnitTests.cpp index e1c071a1a8..244907667e 100644 --- a/Code/CryEngine/CryEntitySystem/EntityUnitTests.cpp +++ b/Code/CryEngine/CryEntitySystem/EntityUnitTests.cpp @@ -17,35 +17,19 @@ CRY_UNIT_TEST_SUITE(EntityTestsSuit) { struct IUnifiedEntityComponent : public IEntityComponent { - static CryGUID& IID() - { - static CryGUID id = "{C89DD0DD-9850-43BA-BF52-86EFB7C9D0A5}"_cry_guid; - return id; - } - static void ReflectType(Schematyc::CTypeDesc& desc) { - desc.SetGUID(cryiidof()); + desc.SetGUID("{C89DD0DD-9850-43BA-BF52-86EFB7C9D0A5}"_cry_guid); } }; class CUnifiedEntityComponent : public IUnifiedEntityComponent { public: - static CryGUID& IID() - { - static CryGUID id = "{C89DD0DD-9850-43BA-BF52-86EFB7C9D0A5}"_cry_guid; - return id; - } - - static void Register(Schematyc::CEnvRegistrationScope& componentScope) - { - } - static void ReflectType(Schematyc::CTypeDesc& desc) { desc.AddBase(); - desc.SetGUID(CUnifiedEntityComponent::IID()); + desc.SetGUID("{C89DD0DD-9850-43BA-BF52-86EFB7C9D0A5}"_cry_guid); desc.SetEditorCategory("Unit Tests"); desc.SetLabel("Unified Entity Component"); desc.SetDescription("Does stuff"); @@ -237,13 +221,82 @@ CRY_UNIT_TEST_SUITE(EntityTestsSuit) // Querying the lowest level GUIDs is disallowed CRY_UNIT_TEST_CHECK_EQUAL(pEntity->GetComponent(), nullptr); } + + class CComponent2 : public IEntityComponent + { + public: + static void ReflectType(Schematyc::CTypeDesc& desc) + { + desc.SetGUID("{8B80B6EA-3A85-48F0-89DC-EDE69E72BC08}"_cry_guid); + desc.SetLabel("CComponent2"); + } + + virtual void Initialize() override + { + m_bInitialized = true; + } + + bool m_bInitialized = false; + }; + + class CComponent1 : public IEntityComponent + { + public: + static void ReflectType(Schematyc::CTypeDesc& desc) + { + desc.SetGUID("{00235E09-55EC-46AD-A6C7-606EA4A40A6B}"_cry_guid); + desc.SetLabel("CComponent1"); + desc.AddMember(&CComponent1::m_bLoadingFromDisk, 'load', "Loading", "Loading", "", false); + } + + virtual void Initialize() override + { + if (m_bLoadingFromDisk) + { + // Make sure that CComponent2 is already available (but uninitialized) + CComponent2* pOtherComponent = m_pEntity->GetComponent(); + CRY_UNIT_TEST_CHECK_DIFFERENT(pOtherComponent, nullptr); + if (pOtherComponent != nullptr) + { + CRY_UNIT_TEST_CHECK_EQUAL(pOtherComponent->m_bInitialized, false); + } + } + } + + bool m_bLoadingFromDisk = false; + }; + + // Test whether two components will be deserialized into CEntity::m_components before being Initialized + // Initialize must never be called during loading from disk if another component (in the same entity) has yet to be loaded + CRY_UNIT_TEST(LoadMultipleComponents) + { + IEntity *pEntity = SpawnTestEntity("SaveMultipleComponents"); + CComponent1 *pComponent1 = pEntity->GetOrCreateComponent(); + CComponent2 *pComponent2 = pEntity->GetOrCreateComponent(); + + // Set boolean to true so we can assert existence of CComponent2 in CComponent1::Initialize + pComponent1->m_bLoadingFromDisk = true; + + // Save + XmlNodeRef xmlNode = gEnv->pSystem->CreateXmlNode("Entity"); + pEntity->SerializeXML(xmlNode, false); + gEnv->pEntitySystem->RemoveEntity(pEntity->GetId()); + + // Load + pEntity = SpawnTestEntity("LoadMultipleComponents"); + pEntity->SerializeXML(xmlNode, true); + + CRY_UNIT_TEST_CHECK_DIFFERENT(pEntity->GetComponent(), nullptr); + CRY_UNIT_TEST_CHECK_DIFFERENT(pEntity->GetComponent(), nullptr); + } } void RegisterUnitTestComponents(Schematyc::IEnvRegistrar& registrar) { Schematyc::CEnvRegistrationScope scope = registrar.Scope(IEntity::GetEntityScopeGUID()); { - Schematyc::CEnvRegistrationScope componentScope = scope.Register(SCHEMATYC_MAKE_ENV_COMPONENT(EntityTestsSuit::CUnifiedEntityComponent)); - EntityTestsSuit::CUnifiedEntityComponent::Register(componentScope); + scope.Register(SCHEMATYC_MAKE_ENV_COMPONENT(EntityTestsSuit::CUnifiedEntityComponent)); + scope.Register(SCHEMATYC_MAKE_ENV_COMPONENT(EntityTestsSuit::CComponent1)); + scope.Register(SCHEMATYC_MAKE_ENV_COMPONENT(EntityTestsSuit::CComponent2)); } } \ No newline at end of file