From d117d96d22c83d06c058021fe1308cce7073c9cb Mon Sep 17 00:00:00 2001 From: Ykkrosh Date: Sun, 1 Jun 2014 18:14:09 +0000 Subject: [PATCH] Simplify component deserialization. Deserialize SYSTEM_ENTITY before any other entities. This makes it safe for Deserialize() methods to access system components (mirroring how Init() can already access system components). Add a Deserialized message, sent after all entities have been deserialized, to help with some other sequencing problems. This was SVN commit r15264. --- source/simulation2/MessageTypes.h | 18 +++ source/simulation2/TypeList.h | 1 + .../scripting/MessageTypeConversions.cpp | 14 +++ .../system/ComponentManagerSerialization.cpp | 104 ++++++++++++++---- .../simulation2/tests/test_ComponentManager.h | 21 ++-- 5 files changed, 129 insertions(+), 29 deletions(-) diff --git a/source/simulation2/MessageTypes.h b/source/simulation2/MessageTypes.h index a97f8bd50c..72a989a502 100644 --- a/source/simulation2/MessageTypes.h +++ b/source/simulation2/MessageTypes.h @@ -183,6 +183,24 @@ public: int* progress; }; +/** + * Broadcast after the entire simulation state has been deserialized. + * Components should do all their self-contained work in their Deserialize + * function when possible. But any reinitialisation that depends on other + * components or other entities, that might not be constructed until later + * in the deserialization process, may be done in response to this message + * instead. + */ +class CMessageDeserialized : public CMessage +{ +public: + DEFAULT_MESSAGE_IMPL(Deserialized) + + CMessageDeserialized() + { + } +}; + /** * This is sent immediately after a new entity's components have all been created diff --git a/source/simulation2/TypeList.h b/source/simulation2/TypeList.h index a85ff17bf4..4865581fda 100644 --- a/source/simulation2/TypeList.h +++ b/source/simulation2/TypeList.h @@ -38,6 +38,7 @@ MESSAGE(Update_Final) MESSAGE(Interpolate) // non-deterministic (use with caution) MESSAGE(RenderSubmit) // non-deterministic (use with caution) MESSAGE(ProgressiveLoad) // non-deterministic (use with caution) +MESSAGE(Deserialized) // non-deterministic (use with caution) MESSAGE(Create) MESSAGE(Destroy) MESSAGE(OwnershipChanged) diff --git a/source/simulation2/scripting/MessageTypeConversions.cpp b/source/simulation2/scripting/MessageTypeConversions.cpp index 186e10d54a..d0f265e510 100644 --- a/source/simulation2/scripting/MessageTypeConversions.cpp +++ b/source/simulation2/scripting/MessageTypeConversions.cpp @@ -148,6 +148,20 @@ CMessage* CMessageProgressiveLoad::FromJSVal(ScriptInterface& UNUSED(scriptInter //////////////////////////////// +jsval CMessageDeserialized::ToJSVal(ScriptInterface& UNUSED(scriptInterface)) const +{ + LOGWARNING(L"CMessageDeserialized::ToJSVal not implemented"); + return JSVAL_VOID; +} + +CMessage* CMessageDeserialized::FromJSVal(ScriptInterface& UNUSED(scriptInterface), jsval UNUSED(val)) +{ + LOGWARNING(L"CMessageDeserialized::FromJSVal not implemented"); + return NULL; +} + +//////////////////////////////// + jsval CMessageCreate::ToJSVal(ScriptInterface& scriptInterface) const { TOJSVAL_SETUP(); diff --git a/source/simulation2/system/ComponentManagerSerialization.cpp b/source/simulation2/system/ComponentManagerSerialization.cpp index 07e397d9de..78b2cd3fd6 100644 --- a/source/simulation2/system/ComponentManagerSerialization.cpp +++ b/source/simulation2/system/ComponentManagerSerialization.cpp @@ -21,6 +21,8 @@ #include "IComponent.h" #include "ParamNode.h" +#include "simulation2/MessageTypes.h" + #include "simulation2/serialization/DebugSerializer.h" #include "simulation2/serialization/HashSerializer.h" #include "simulation2/serialization/StdSerializer.h" @@ -105,6 +107,7 @@ bool CComponentManager::ComputeStateHash(std::string& outHash, bool quick) CHashSerializer serializer(m_ScriptInterface); serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32); + serializer.NumberU32_Unbounded("next entity id", m_NextEntityId); std::map >::const_iterator cit = m_ComponentsByTypeId.begin(); for (; cit != m_ComponentsByTypeId.end(); ++cit) @@ -151,7 +154,12 @@ bool CComponentManager::ComputeStateHash(std::string& outHash, bool quick) * Simulation state serialization format: * * TODO: Global version number. - * Number of (non-empty) component types. + * Number of SYSTEM_ENTITY component types + * For each SYSTEM_ENTITY component type: + * Component type name + * TODO: Component type version number. + * Component state. + * Number of (non-empty, non-SYSTEM_ENTITY-only) component types. * For each component type: * Component type name. * TODO: Component type version number. @@ -182,7 +190,9 @@ bool CComponentManager::SerializeState(std::ostream& stream) std::map >::const_iterator cit; + uint32_t numSystemComponentTypes = 0; uint32_t numComponentTypes = 0; + std::set serializedSystemComponentTypes; std::set serializedComponentTypes; for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit) @@ -191,19 +201,50 @@ bool CComponentManager::SerializeState(std::ostream& stream) bool needsSerialization = false; for (std::map::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit) { - // Don't serialize local entities - if (ENTITY_IS_LOCAL(eit->first)) + // Don't serialize local entities, and handle SYSTEM_ENTITY separately + if (ENTITY_IS_LOCAL(eit->first) || eit->first == SYSTEM_ENTITY) continue; needsSerialization = true; break; } - if (!needsSerialization) + if (needsSerialization) + { + numComponentTypes++; + serializedComponentTypes.insert(cit->first); + } + + if (cit->second.find(SYSTEM_ENTITY) != cit->second.end()) + { + numSystemComponentTypes++; + serializedSystemComponentTypes.insert(cit->first); + } + } + + serializer.NumberU32_Unbounded("num system component types", numSystemComponentTypes); + + for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit) + { + if (serializedSystemComponentTypes.find(cit->first) == serializedSystemComponentTypes.end()) continue; - numComponentTypes++; - serializedComponentTypes.insert(cit->first); + std::map::const_iterator ctit = m_ComponentTypesById.find(cit->first); + if (ctit == m_ComponentTypesById.end()) + { + debug_warn(L"Invalid ctit"); // this should never happen + return false; + } + + serializer.StringASCII("name", ctit->second.name, 0, 255); + + std::map::const_iterator eit = cit->second.find(SYSTEM_ENTITY); + if (eit == cit->second.end()) + { + debug_warn(L"Invalid eit"); // this should never happen + return false; + } + eit->second->Serialize(serializer); } serializer.NumberU32_Unbounded("num component types", numComponentTypes); @@ -226,8 +267,8 @@ bool CComponentManager::SerializeState(std::ostream& stream) uint32_t numComponents = 0; for (std::map::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit) { - // Don't serialize local entities - if (ENTITY_IS_LOCAL(eit->first)) + // Don't serialize local entities or SYSTEM_ENTITY + if (ENTITY_IS_LOCAL(eit->first) || eit->first == SYSTEM_ENTITY) continue; numComponents++; @@ -239,8 +280,8 @@ bool CComponentManager::SerializeState(std::ostream& stream) // Serialize the components now for (std::map::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit) { - // Don't serialize local entities - if (ENTITY_IS_LOCAL(eit->first)) + // Don't serialize local entities or SYSTEM_ENTITY + if (ENTITY_IS_LOCAL(eit->first) || eit->first == SYSTEM_ENTITY) continue; serializer.NumberU32_Unbounded("entity id", eit->first); @@ -256,7 +297,6 @@ bool CComponentManager::DeserializeState(std::istream& stream) { try { - CStdDeserializer deserializer(m_ScriptInterface, stream); ResetState(); @@ -268,12 +308,39 @@ bool CComponentManager::DeserializeState(std::istream& stream) deserializer.NumberU32_Unbounded("next entity id", m_NextEntityId); // TODO: use sensible bounds - uint32_t numComponentTypes; - deserializer.NumberU32_Unbounded("num component types", numComponentTypes); + uint32_t numSystemComponentTypes; + deserializer.NumberU32_Unbounded("num system component types", numSystemComponentTypes); ICmpTemplateManager* templateManager = NULL; CParamNode noParam; + for (size_t i = 0; i < numSystemComponentTypes; ++i) + { + std::string ctname; + deserializer.StringASCII("name", ctname, 0, 255); + + ComponentTypeId ctid = LookupCID(ctname); + if (ctid == CID__Invalid) + { + LOGERROR(L"Deserialization saw unrecognised component type '%hs'", ctname.c_str()); + return false; + } + + IComponent* component = ConstructComponent(m_SystemEntity, ctid); + if (!component) + return false; + + component->Deserialize(noParam, deserializer); + + // If this was the template manager, remember it so we can use it when + // deserializing any further non-system entities + if (ctid == CID_TemplateManager) + templateManager = static_cast (component); + } + + uint32_t numComponentTypes; + deserializer.NumberU32_Unbounded("num component types", numComponentTypes); + for (size_t i = 0; i < numComponentTypes; ++i) { std::string ctname; @@ -299,7 +366,7 @@ bool CComponentManager::DeserializeState(std::istream& stream) // Try to find the template for this entity const CParamNode* entTemplate = NULL; - if (templateManager && ent != SYSTEM_ENTITY) // (system entities don't use templates) + if (templateManager) entTemplate = templateManager->LoadLatestTemplate(ent); // Deserialize, with the appropriate template for this component @@ -307,11 +374,6 @@ bool CComponentManager::DeserializeState(std::istream& stream) component->Deserialize(entTemplate->GetChild(ctname.c_str()), deserializer); else component->Deserialize(noParam, deserializer); - - // If this was the template manager, remember it so we can use it when - // deserializing any further non-system entities - if (ent == SYSTEM_ENTITY && ctid == CID_TemplateManager) - templateManager = static_cast (component); } } @@ -321,6 +383,10 @@ bool CComponentManager::DeserializeState(std::istream& stream) return false; } + // Allow components to do some final reinitialisation after everything is loaded + CMessageDeserialized msg; + BroadcastMessage(msg); + return true; } catch (PSERROR_Deserialize& e) diff --git a/source/simulation2/tests/test_ComponentManager.h b/source/simulation2/tests/test_ComponentManager.h index d496578918..c84fe34208 100644 --- a/source/simulation2/tests/test_ComponentManager.h +++ b/source/simulation2/tests/test_ComponentManager.h @@ -597,7 +597,7 @@ public: CComponentManager man(context, g_ScriptRuntime); man.LoadComponentTypes(); - entity_id_t ent1 = 1, ent2 = 2, ent3 = FIRST_LOCAL_ENTITY; + entity_id_t ent1 = 10, ent2 = 20, ent3 = FIRST_LOCAL_ENTITY; CEntityHandle hnd1 = man.AllocateEntityHandle(ent1); CEntityHandle hnd2 = man.AllocateEntityHandle(ent2); CEntityHandle hnd3 = man.AllocateEntityHandle(ent3); @@ -621,13 +621,13 @@ public: TS_ASSERT_STR_EQUALS(debugStream.str(), "rng: \"78606\"\n" "entities:\n" - "- id: 1\n" + "- id: 10\n" " Test1A:\n" " x: 11000\n" " Test2A:\n" " x: 21000\n" "\n" - "- id: 2\n" + "- id: 20\n" " Test1A:\n" " x: 1234\n" "\n" @@ -641,25 +641,26 @@ public: std::string hash; TS_ASSERT(man.ComputeStateHash(hash, false)); TS_ASSERT_EQUALS(hash.length(), (size_t)16); - TS_ASSERT_SAME_DATA(hash.data(), "\x1c\x45\x2b\x20\x1f\x0c\x00\x93\x60\x78\xe2\x63\xb1\x47\x08\x19", 16); - // echo -en "\x05\x00\x00\x0078606\x01\0\0\0\x01\0\0\0\xf8\x2a\0\0\x02\0\0\0\xd2\x04\0\0\x04\0\0\0\x01\0\0\0\x08\x52\0\0" | openssl md5 | perl -pe 's/(..)/\\x$1/g' - // ^^^^^^^^ rng ^^^^^^^^ ^^Test1A^^ ^^^ent1^^ ^^^11000^^^ ^^^ent2^^ ^^^1234^^^ ^^Test2A^^ ^^ent1^^ ^^^21000^^^ + TS_ASSERT_SAME_DATA(hash.data(), "\x3c\x25\x6e\x22\x58\x23\x09\x58\x38\xca\xb2\x1e\x0b\x8c\xac\xcf", 16); + // echo -en "\x05\x00\x00\x0078606\x02\0\0\0\x01\0\0\0\x0a\0\0\0\xf8\x2a\0\0\x14\0\0\0\xd2\x04\0\0\x04\0\0\0\x0a\0\0\0\x08\x52\0\0" | md5sum | perl -pe 's/([0-9a-f]{2})/\\x$1/g' + // ^^^^^^^^ rng ^^^^^^^^ ^^next^^ ^^Test1A^^ ^^^ent1^^ ^^^11000^^^ ^^^ent2^^ ^^^1234^^^ ^^Test2A^^ ^^ent1^^ ^^^21000^^^ std::stringstream stateStream; TS_ASSERT(man.SerializeState(stateStream)); - TS_ASSERT_STREAM(stateStream, 69, + TS_ASSERT_STREAM(stateStream, 73, "\x05\x00\x00\x00\x37\x38\x36\x30\x36" // RNG "\x02\x00\x00\x00" // next entity ID + "\x00\x00\x00\x00" // num system component types "\x02\x00\x00\x00" // num component types "\x06\x00\x00\x00Test1A" "\x02\x00\x00\x00" // num ents - "\x01\x00\x00\x00" // ent1 + "\x0a\x00\x00\x00" // ent1 "\xf8\x2a\x00\x00" // 11000 - "\x02\x00\x00\x00" // ent2 + "\x14\x00\x00\x00" // ent2 "\xd2\x04\x00\x00" // 1234 "\x06\x00\x00\x00Test2A" "\x01\x00\x00\x00" // num ents - "\x01\x00\x00\x00" // ent1 + "\x0a\x00\x00\x00" // ent1 "\x08\x52\x00\x00" // 21000 );