forked from 0ad/0ad
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.
This commit is contained in:
parent
b30a9a6384
commit
d117d96d22
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -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<ComponentTypeId, std::map<entity_id_t, IComponent*> >::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<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator cit;
|
||||
|
||||
uint32_t numSystemComponentTypes = 0;
|
||||
uint32_t numComponentTypes = 0;
|
||||
std::set<ComponentTypeId> serializedSystemComponentTypes;
|
||||
std::set<ComponentTypeId> 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<entity_id_t, IComponent*>::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<ComponentTypeId, ComponentType>::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<entity_id_t, IComponent*>::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<entity_id_t, IComponent*>::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<entity_id_t, IComponent*>::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<ICmpTemplateManager*> (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<ICmpTemplateManager*> (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)
|
||||
|
@ -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
|
||||
);
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user