Pyrogenesis  13997
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ComponentManagerSerialization.cpp
Go to the documentation of this file.
1 /* Copyright (C) 2013 Wildfire Games.
2  * This file is part of 0 A.D.
3  *
4  * 0 A.D. is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * 0 A.D. is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16  */
17 
18 #include "precompiled.h"
19 
20 #include "ComponentManager.h"
21 #include "IComponent.h"
22 #include "ParamNode.h"
23 
28 
30 
31 #include "ps/CLogger.h"
32 
33 std::string SerializeRNG(const boost::rand48& rng)
34 {
35  std::stringstream s;
36  s << rng;
37  return s.str();
38 }
39 
40 void DeserializeRNG(const std::string& str, boost::rand48& rng)
41 {
42  std::stringstream s;
43  s << str;
44  s >> rng;
45 }
46 
47 bool CComponentManager::DumpDebugState(std::ostream& stream, bool includeDebugInfo)
48 {
49  CDebugSerializer serializer(m_ScriptInterface, stream, includeDebugInfo);
50 
51  serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32);
52 
53  serializer.TextLine("entities:");
54 
55  // We want the output to be grouped by entity ID, so invert the CComponentManager data structures
56  std::map<entity_id_t, std::map<ComponentTypeId, IComponent*> > components;
57  //std::map<ComponentTypeId, std::string> names;
58 
59  std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator ctit = m_ComponentsByTypeId.begin();
60  for (; ctit != m_ComponentsByTypeId.end(); ++ctit)
61  {
62  std::map<entity_id_t, IComponent*>::const_iterator eit = ctit->second.begin();
63  for (; eit != ctit->second.end(); ++eit)
64  {
65  components[eit->first][ctit->first] = eit->second;
66  }
67  }
68 
69  std::map<entity_id_t, std::map<ComponentTypeId, IComponent*> >::const_iterator cit = components.begin();
70  for (; cit != components.end(); ++cit)
71  {
72  std::stringstream n;
73  n << "- id: " << cit->first;
74  serializer.TextLine(n.str());
75 
76  if (ENTITY_IS_LOCAL(cit->first))
77  serializer.TextLine(" type: local");
78 
79  std::map<ComponentTypeId, IComponent*>::const_iterator ctit = cit->second.begin();
80  for (; ctit != cit->second.end(); ++ctit)
81  {
82  std::stringstream n;
83  n << " " << LookupComponentTypeName(ctit->first) << ":";
84  serializer.TextLine(n.str());
85  serializer.Indent(4);
86  ctit->second->Serialize(serializer);
87  serializer.Dedent(4);
88  }
89  serializer.TextLine("");
90  }
91 
92  // TODO: catch exceptions
93  return true;
94 }
95 
96 bool CComponentManager::ComputeStateHash(std::string& outHash, bool quick)
97 {
98  // Hash serialization: this includes the minimal data necessary to detect
99  // differences in the state, and ignores things like counts and names
100 
101  // If 'quick' is set, this checks even fewer things, so that it will
102  // be fast enough to run every turn but will typically detect any
103  // out-of-syncs fairly soon
104 
106 
107  serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32);
108 
109  std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator cit = m_ComponentsByTypeId.begin();
110  for (; cit != m_ComponentsByTypeId.end(); ++cit)
111  {
112  // In quick mode, only check unit positions
113  if (quick && !(cit->first == CID_Position))
114  continue;
115 
116  // Only emit component types if they have a component that will be serialized
117  bool needsSerialization = false;
118  for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
119  {
120  // Don't serialize local entities
121  if (ENTITY_IS_LOCAL(eit->first))
122  continue;
123 
124  needsSerialization = true;
125  break;
126  }
127 
128  if (!needsSerialization)
129  continue;
130 
131  serializer.NumberI32_Unbounded("component type id", cit->first);
132 
133  for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
134  {
135  // Don't serialize local entities
136  if (ENTITY_IS_LOCAL(eit->first))
137  continue;
138 
139  serializer.NumberU32_Unbounded("entity id", eit->first);
140  eit->second->Serialize(serializer);
141  }
142  }
143 
144  outHash = std::string((const char*)serializer.ComputeHash(), serializer.GetHashLength());
145 
146  // TODO: catch exceptions
147  return true;
148 }
149 
150 /*
151  * Simulation state serialization format:
152  *
153  * TODO: Global version number.
154  * Number of (non-empty) component types.
155  * For each component type:
156  * Component type name.
157  * TODO: Component type version number.
158  * Number of entities.
159  * For each entity:
160  * Entity id.
161  * Component state.
162  *
163  * Rationale:
164  * Saved games should be valid across patches, which might change component
165  * type IDs. Thus the names are serialized, not the IDs.
166  * Version numbers are used so saved games from future versions can be rejected,
167  * and those from older versions can be fixed up to work with the latest version.
168  * (These aren't really needed for networked games (where everyone will have the same
169  * version), but it doesn't seem worth having a separate codepath for that.)
170  */
171 
172 bool CComponentManager::SerializeState(std::ostream& stream)
173 {
174  CStdSerializer serializer(m_ScriptInterface, stream);
175 
176  // We don't serialize the destruction queue, since we'd have to be careful to skip local entities etc
177  // and it's (hopefully) easier to just expect callers to flush the queue before serializing
178  ENSURE(m_DestructionQueue.empty());
179 
180  serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32);
181  serializer.NumberU32_Unbounded("next entity id", m_NextEntityId);
182 
183  std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator cit;
184 
185  uint32_t numComponentTypes = 0;
186  std::set<ComponentTypeId> serializedComponentTypes;
187 
188  for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit)
189  {
190  // Only emit component types if they have a component that will be serialized
191  bool needsSerialization = false;
192  for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
193  {
194  // Don't serialize local entities
195  if (ENTITY_IS_LOCAL(eit->first))
196  continue;
197 
198  needsSerialization = true;
199  break;
200  }
201 
202  if (!needsSerialization)
203  continue;
204 
205  numComponentTypes++;
206  serializedComponentTypes.insert(cit->first);
207  }
208 
209  serializer.NumberU32_Unbounded("num component types", numComponentTypes);
210 
211  for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit)
212  {
213  if (serializedComponentTypes.find(cit->first) == serializedComponentTypes.end())
214  continue;
215 
216  std::map<ComponentTypeId, ComponentType>::const_iterator ctit = m_ComponentTypesById.find(cit->first);
217  if (ctit == m_ComponentTypesById.end())
218  {
219  debug_warn(L"Invalid ctit"); // this should never happen
220  return false;
221  }
222 
223  serializer.StringASCII("name", ctit->second.name, 0, 255);
224 
225  // Count the components before serializing any of them
226  uint32_t numComponents = 0;
227  for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
228  {
229  // Don't serialize local entities
230  if (ENTITY_IS_LOCAL(eit->first))
231  continue;
232 
233  numComponents++;
234  }
235 
236  // Emit the count
237  serializer.NumberU32_Unbounded("num components", numComponents);
238 
239  // Serialize the components now
240  for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
241  {
242  // Don't serialize local entities
243  if (ENTITY_IS_LOCAL(eit->first))
244  continue;
245 
246  serializer.NumberU32_Unbounded("entity id", eit->first);
247  eit->second->Serialize(serializer);
248  }
249  }
250 
251  // TODO: catch exceptions
252  return true;
253 }
254 
255 bool CComponentManager::DeserializeState(std::istream& stream)
256 {
257  try
258  {
259 
260  CStdDeserializer deserializer(m_ScriptInterface, stream);
261 
262  ResetState();
264 
265  std::string rng;
266  deserializer.StringASCII("rng", rng, 0, 32);
267  DeserializeRNG(rng, m_RNG);
268 
269  deserializer.NumberU32_Unbounded("next entity id", m_NextEntityId); // TODO: use sensible bounds
270 
271  uint32_t numComponentTypes;
272  deserializer.NumberU32_Unbounded("num component types", numComponentTypes);
273 
274  ICmpTemplateManager* templateManager = NULL;
275  CParamNode noParam;
276 
277  for (size_t i = 0; i < numComponentTypes; ++i)
278  {
279  std::string ctname;
280  deserializer.StringASCII("name", ctname, 0, 255);
281 
282  ComponentTypeId ctid = LookupCID(ctname);
283  if (ctid == CID__Invalid)
284  {
285  LOGERROR(L"Deserialization saw unrecognised component type '%hs'", ctname.c_str());
286  return false;
287  }
288 
289  uint32_t numComponents;
290  deserializer.NumberU32_Unbounded("num components", numComponents);
291 
292  for (size_t j = 0; j < numComponents; ++j)
293  {
294  entity_id_t ent;
295  deserializer.NumberU32_Unbounded("entity id", ent);
296  IComponent* component = ConstructComponent(LookupEntityHandle(ent, true), ctid);
297  if (!component)
298  return false;
299 
300  // Try to find the template for this entity
301  const CParamNode* entTemplate = NULL;
302  if (templateManager && ent != SYSTEM_ENTITY) // (system entities don't use templates)
303  entTemplate = templateManager->LoadLatestTemplate(ent);
304 
305  // Deserialize, with the appropriate template for this component
306  if (entTemplate)
307  component->Deserialize(entTemplate->GetChild(ctname.c_str()), deserializer);
308  else
309  component->Deserialize(noParam, deserializer);
310 
311  // If this was the template manager, remember it so we can use it when
312  // deserializing any further non-system entities
313  if (ent == SYSTEM_ENTITY && ctid == CID_TemplateManager)
314  templateManager = static_cast<ICmpTemplateManager*> (component);
315  }
316  }
317 
318  if (stream.peek() != EOF)
319  {
320  LOGERROR(L"Deserialization didn't reach EOF");
321  return false;
322  }
323 
324  return true;
325  }
326  catch (PSERROR_Deserialize& e)
327  {
328  LOGERROR(L"Deserialization failed: %hs", e.what());
329  return false;
330  }
331 }
An entity initialisation parameter node.
Definition: ParamNode.h:112
virtual void StringASCII(const char *name, std::string &out, uint32_t minlength, uint32_t maxlength)
void ResetState()
Resets the dynamic simulation state (deletes all entities, resets entity ID counters; doesn&#39;t unload/...
#define LOGERROR
Definition: CLogger.h:35
#define ENTITY_IS_LOCAL(id)
Definition: Entity.h:60
std::string SerializeRNG(const boost::rand48 &rng)
virtual void NumberU32_Unbounded(const char *name, uint32_t &out)
Serialize to a human-readable YAML-like format.
void Dedent(int spaces)
bool SerializeState(std::ostream &stream)
entity_id_t m_NextEntityId
const entity_id_t SYSTEM_ENTITY
Entity ID for singleton &#39;system&#39; components.
Definition: Entity.h:44
CEntityHandle LookupEntityHandle(entity_id_t ent, bool allowCreate=false)
Returns a CEntityHandle with id ent.
boost::rand48 m_RNG
#define ENSURE(expr)
ensure the expression &lt;expr&gt; evaluates to non-zero.
Definition: debug.h:282
const CParamNode & GetChild(const char *name) const
Returns the (unique) child node with the given name, or a node with IsOk() == false if there is none...
Definition: ParamNode.cpp:185
std::map< ComponentTypeId, std::map< entity_id_t, IComponent * > > m_ComponentsByTypeId
void StringASCII(const char *name, const std::string &value, uint32_t minlength, uint32_t maxlength)
Serialize an ASCII string.
Definition: ISerializer.cpp:70
boost::mt19937 rng
Random number generator (Boost Mersenne Twister)
Definition: Noise.cpp:34
void Indent(int spaces)
bool ComputeStateHash(std::string &outHash, bool quick)
virtual const char * what() const
Definition: Errors.cpp:453
ScriptInterface m_ScriptInterface
void InitSystemEntity()
Set up an empty SYSTEM_ENTITY.
void TextLine(const std::string &text)
void NumberU32_Unbounded(const char *name, uint32_t value)
Serialize a number.
Definition: ISerializer.h:171
bool DeserializeState(std::istream &stream)
virtual void Deserialize(const CParamNode &paramNode, IDeserializer &deserialize)=0
std::string LookupComponentTypeName(ComponentTypeId cid) const
bool DumpDebugState(std::ostream &stream, bool includeDebugInfo)
size_t GetHashLength()
IComponent * ConstructComponent(CEntityHandle ent, ComponentTypeId cid)
Allocates a component object of type &#39;cid&#39;, and attaches it to entity &#39;ent&#39;.
Template manager: Handles the loading of entity template files for the initialisation and deserializa...
std::vector< entity_id_t > m_DestructionQueue
unsigned int uint32_t
Definition: wposix_types.h:53
ComponentTypeId LookupCID(const std::string &cname) const
std::map< ComponentTypeId, ComponentType > m_ComponentTypesById
void DeserializeRNG(const std::string &str, boost::rand48 &rng)
void NumberI32_Unbounded(const char *name, int32_t value)
Serialize a number.
Definition: ISerializer.h:176
#define debug_warn(expr)
display the error dialog with the given text.
Definition: debug.h:324
virtual const CParamNode * LoadLatestTemplate(entity_id_t ent)=0
Returns the template most recently specified for the entity &#39;ent&#39;.
u32 entity_id_t
Entity ID type.
Definition: Entity.h:24
const u8 * ComputeHash()