Skip to content

Commit

Permalink
!XB (CryEntitySystem) Fix entities being Initialized before all other…
Browse files Browse the repository at this point in the history
… components had become deserialized

Copied from Perforce
 Change: 1539089
  • Loading branch information
Cry-59 committed Jul 26, 2017
1 parent 76f391f commit 9055e59
Show file tree
Hide file tree
Showing 3 changed files with 109 additions and 46 deletions.
57 changes: 33 additions & 24 deletions Code/CryEngine/CryEntitySystem/Entity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand All @@ -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();
}
}

Expand Down Expand Up @@ -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<IEntityPropertyGroup*>(rawPointer)->SerializeProperties(ar);
Expand Down Expand Up @@ -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);
Expand All @@ -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);
}
Expand All @@ -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))
{
Expand Down
5 changes: 3 additions & 2 deletions Code/CryEngine/CryEntitySystem/Entity.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
//////////////////////////////////////////////////////////////////////////

Expand All @@ -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
{
Expand Down
93 changes: 73 additions & 20 deletions Code/CryEngine/CryEntitySystem/EntityUnitTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IUnifiedEntityComponent>& desc)
{
desc.SetGUID(cryiidof<IUnifiedEntityComponent>());
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<CUnifiedEntityComponent>& desc)
{
desc.AddBase<IUnifiedEntityComponent>();
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");
Expand Down Expand Up @@ -237,13 +221,82 @@ CRY_UNIT_TEST_SUITE(EntityTestsSuit)
// Querying the lowest level GUIDs is disallowed
CRY_UNIT_TEST_CHECK_EQUAL(pEntity->GetComponent<IEntityComponent>(), nullptr);
}

class CComponent2 : public IEntityComponent
{
public:
static void ReflectType(Schematyc::CTypeDesc<CComponent2>& 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<CComponent1>& 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<CComponent2>();
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<CComponent1>();
CComponent2 *pComponent2 = pEntity->GetOrCreateComponent<CComponent2>();

// 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<CComponent1>(), nullptr);
CRY_UNIT_TEST_CHECK_DIFFERENT(pEntity->GetComponent<CComponent2>(), 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));
}
}

0 comments on commit 9055e59

Please sign in to comment.