LCOV - code coverage report
Current view: top level - source/simulation2/system - ComponentManagerSerialization.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 152 172 88.4 %
Date: 2023-01-19 00:18:29 Functions: 8 9 88.9 %

          Line data    Source code
       1             : /* Copyright (C) 2020 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             : 
      24             : #include "simulation2/MessageTypes.h"
      25             : 
      26             : #include "simulation2/serialization/DebugSerializer.h"
      27             : #include "simulation2/serialization/HashSerializer.h"
      28             : #include "simulation2/serialization/StdSerializer.h"
      29             : #include "simulation2/serialization/StdDeserializer.h"
      30             : 
      31             : #include "simulation2/components/ICmpTemplateManager.h"
      32             : 
      33             : #include "ps/CLogger.h"
      34             : 
      35           7 : std::string SerializeRNG(const boost::random::rand48& rng)
      36             : {
      37          14 :     std::stringstream s;
      38           7 :     s << rng;
      39          14 :     return s.str();
      40             : }
      41             : 
      42           3 : void DeserializeRNG(const std::string& str, boost::random::rand48& rng)
      43             : {
      44           6 :     std::stringstream s;
      45           3 :     s << str;
      46           3 :     s >> rng;
      47           3 : }
      48             : 
      49           2 : bool CComponentManager::DumpDebugState(std::ostream& stream, bool includeDebugInfo) const
      50             : {
      51           4 :     CDebugSerializer serializer(m_ScriptInterface, stream, includeDebugInfo);
      52             : 
      53           2 :     serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32);
      54             : 
      55           2 :     serializer.TextLine("entities:");
      56             : 
      57             :     // We want the output to be grouped by entity ID, so invert the CComponentManager data structures
      58           4 :     std::map<entity_id_t, std::map<ComponentTypeId, IComponent*> > components;
      59             :     //std::map<ComponentTypeId, std::string> names;
      60             : 
      61           2 :     std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator ctit = m_ComponentsByTypeId.begin();
      62          14 :     for (; ctit != m_ComponentsByTypeId.end(); ++ctit)
      63             :     {
      64           6 :         std::map<entity_id_t, IComponent*>::const_iterator eit = ctit->second.begin();
      65          22 :         for (; eit != ctit->second.end(); ++eit)
      66             :         {
      67           8 :             components[eit->first][ctit->first] = eit->second;
      68             :         }
      69             :     }
      70             : 
      71           2 :     std::map<entity_id_t, std::map<ComponentTypeId, IComponent*> >::const_iterator cit = components.begin();
      72          16 :     for (; cit != components.end(); ++cit)
      73             :     {
      74          14 :         std::stringstream n;
      75           7 :         n << "- id: " << cit->first;
      76           7 :         serializer.TextLine(n.str());
      77             : 
      78           7 :         if (ENTITY_IS_LOCAL(cit->first))
      79           1 :             serializer.TextLine("  type: local");
      80             : 
      81           7 :         std::map<ComponentTypeId, IComponent*>::const_iterator it = cit->second.begin();
      82          23 :         for (; it != cit->second.end(); ++it)
      83             :         {
      84          16 :             std::stringstream st;
      85           8 :             st << "  " << LookupComponentTypeName(it->first) << ":";
      86           8 :             serializer.TextLine(st.str());
      87           8 :             serializer.Indent(4);
      88           8 :             it->second->Serialize(serializer);
      89           8 :             serializer.Dedent(4);
      90             :         }
      91           7 :         serializer.TextLine("");
      92             :     }
      93             : 
      94             :     // TODO: catch exceptions
      95           4 :     return true;
      96             : }
      97             : 
      98           1 : bool CComponentManager::ComputeStateHash(std::string& outHash, bool quick) const
      99             : {
     100             :     // Hash serialization: this includes the minimal data necessary to detect
     101             :     // differences in the state, and ignores things like counts and names
     102             : 
     103             :     // If 'quick' is set, this checks even fewer things, so that it will
     104             :     // be fast enough to run every turn but will typically detect any
     105             :     // out-of-syncs fairly soon
     106             : 
     107           2 :     CHashSerializer serializer(m_ScriptInterface);
     108             : 
     109           1 :     serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32);
     110           1 :     serializer.NumberU32_Unbounded("next entity id", m_NextEntityId);
     111             : 
     112           1 :     std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator cit = m_ComponentsByTypeId.begin();
     113           5 :     for (; cit != m_ComponentsByTypeId.end(); ++cit)
     114             :     {
     115             :         // In quick mode, only check unit positions
     116           2 :         if (quick && !(cit->first == CID_Position))
     117           0 :             continue;
     118             : 
     119             :         // Only emit component types if they have a component that will be serialized
     120           2 :         bool needsSerialization = false;
     121           2 :         for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
     122             :         {
     123             :             // Don't serialize local entities
     124           2 :             if (ENTITY_IS_LOCAL(eit->first))
     125           0 :                 continue;
     126             : 
     127           2 :             needsSerialization = true;
     128           2 :             break;
     129             :         }
     130             : 
     131           2 :         if (!needsSerialization)
     132           0 :             continue;
     133             : 
     134           2 :         serializer.NumberI32_Unbounded("component type id", cit->first);
     135             : 
     136           6 :         for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
     137             :         {
     138             :             // Don't serialize local entities
     139           4 :             if (ENTITY_IS_LOCAL(eit->first))
     140           1 :                 continue;
     141             : 
     142           3 :             serializer.NumberU32_Unbounded("entity id", eit->first);
     143           3 :             eit->second->Serialize(serializer);
     144             :         }
     145             :     }
     146             : 
     147           1 :     outHash = std::string((const char*)serializer.ComputeHash(), serializer.GetHashLength());
     148             : 
     149             :     // TODO: catch exceptions
     150           2 :     return true;
     151             : }
     152             : 
     153             : /*
     154             :  * Simulation state serialization format:
     155             :  *
     156             :  * TODO: Global version number.
     157             :  * Number of SYSTEM_ENTITY component types
     158             :  * For each SYSTEM_ENTITY component type:
     159             :  *   Component type name
     160             :  *   TODO: Component type version number.
     161             :  *   Component state.
     162             :  * Number of (non-empty, non-SYSTEM_ENTITY-only) component types.
     163             :  * For each component type:
     164             :  *   Component type name.
     165             :  *   TODO: Component type version number.
     166             :  *   Number of entities.
     167             :  *   For each entity:
     168             :  *     Entity id.
     169             :  *     Component state.
     170             :  *
     171             :  * Rationale:
     172             :  * Saved games should be valid across patches, which might change component
     173             :  * type IDs. Thus the names are serialized, not the IDs.
     174             :  * Version numbers are used so saved games from future versions can be rejected,
     175             :  * and those from older versions can be fixed up to work with the latest version.
     176             :  * (These aren't really needed for networked games (where everyone will have the same
     177             :  * version), but it doesn't seem worth having a separate codepath for that.)
     178             :  */
     179             : 
     180           4 : bool CComponentManager::SerializeState(std::ostream& stream) const
     181             : {
     182           8 :     CStdSerializer serializer(m_ScriptInterface, stream);
     183             : 
     184             :     // We don't serialize the destruction queue, since we'd have to be careful to skip local entities etc.
     185             :     // This means we cannot have non-local entities in the destruction queue at this point.
     186           4 :     ENSURE(m_DestructionQueue.empty() || std::find_if(m_DestructionQueue.begin(), m_DestructionQueue.end(),
     187             :            [](entity_id_t ent) { return !ENTITY_IS_LOCAL(ent); }) == m_DestructionQueue.end());
     188             : 
     189           4 :     serializer.StringASCII("rng", SerializeRNG(m_RNG), 0, 32);
     190           4 :     serializer.NumberU32_Unbounded("next entity id", m_NextEntityId);
     191             : 
     192           4 :     std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator cit;
     193             : 
     194           4 :     uint32_t numSystemComponentTypes = 0;
     195           4 :     uint32_t numComponentTypes = 0;
     196           8 :     std::set<ComponentTypeId> serializedSystemComponentTypes;
     197           8 :     std::set<ComponentTypeId> serializedComponentTypes;
     198             : 
     199          13 :     for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit)
     200             :     {
     201             :         // Only emit component types if they have a component that will be serialized
     202           9 :         bool needsSerialization = false;
     203          12 :         for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
     204             :         {
     205             :             // Don't serialize local entities, and handle SYSTEM_ENTITY separately
     206           9 :             if (ENTITY_IS_LOCAL(eit->first) || eit->first == SYSTEM_ENTITY)
     207           3 :                 continue;
     208             : 
     209           6 :             needsSerialization = true;
     210           6 :             break;
     211             :         }
     212             : 
     213           9 :         if (needsSerialization)
     214             :         {
     215           6 :             numComponentTypes++;
     216           6 :             serializedComponentTypes.insert(cit->first);
     217             :         }
     218             : 
     219           9 :         if (cit->second.find(SYSTEM_ENTITY) != cit->second.end())
     220             :         {
     221           3 :             numSystemComponentTypes++;
     222           3 :             serializedSystemComponentTypes.insert(cit->first);
     223             :         }
     224             :     }
     225             : 
     226           4 :     serializer.NumberU32_Unbounded("num system component types", numSystemComponentTypes);
     227             : 
     228          12 :     for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit)
     229             :     {
     230           9 :         if (serializedSystemComponentTypes.find(cit->first) == serializedSystemComponentTypes.end())
     231           6 :             continue;
     232             : 
     233           3 :         std::map<ComponentTypeId, ComponentType>::const_iterator ctit = m_ComponentTypesById.find(cit->first);
     234           3 :         if (ctit == m_ComponentTypesById.end())
     235             :         {
     236           0 :             debug_warn(L"Invalid ctit"); // this should never happen
     237           0 :             return false;
     238             :         }
     239             : 
     240           3 :         serializer.StringASCII("name", ctit->second.name, 0, 255);
     241             : 
     242           3 :         std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.find(SYSTEM_ENTITY);
     243           3 :         if (eit == cit->second.end())
     244             :         {
     245           0 :             debug_warn(L"Invalid eit"); // this should never happen
     246           0 :             return false;
     247             :         }
     248           3 :         eit->second->Serialize(serializer);
     249             :     }
     250             : 
     251           3 :     serializer.NumberU32_Unbounded("num component types", numComponentTypes);
     252             : 
     253          11 :     for (cit = m_ComponentsByTypeId.begin(); cit != m_ComponentsByTypeId.end(); ++cit)
     254             :     {
     255           8 :         if (serializedComponentTypes.find(cit->first) == serializedComponentTypes.end())
     256           2 :             continue;
     257             : 
     258           6 :         std::map<ComponentTypeId, ComponentType>::const_iterator ctit = m_ComponentTypesById.find(cit->first);
     259           6 :         if (ctit == m_ComponentTypesById.end())
     260             :         {
     261           0 :             debug_warn(L"Invalid ctit"); // this should never happen
     262           0 :             return false;
     263             :         }
     264             : 
     265           6 :         serializer.StringASCII("name", ctit->second.name, 0, 255);
     266             : 
     267             :         // Count the components before serializing any of them
     268           6 :         uint32_t numComponents = 0;
     269          14 :         for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
     270             :         {
     271             :             // Don't serialize local entities or SYSTEM_ENTITY
     272           8 :             if (ENTITY_IS_LOCAL(eit->first) || eit->first == SYSTEM_ENTITY)
     273           1 :                 continue;
     274             : 
     275           7 :             numComponents++;
     276             :         }
     277             : 
     278             :         // Emit the count
     279           6 :         serializer.NumberU32_Unbounded("num components", numComponents);
     280             : 
     281             :         // Serialize the components now
     282          14 :         for (std::map<entity_id_t, IComponent*>::const_iterator eit = cit->second.begin(); eit != cit->second.end(); ++eit)
     283             :         {
     284             :             // Don't serialize local entities or SYSTEM_ENTITY
     285           8 :             if (ENTITY_IS_LOCAL(eit->first) || eit->first == SYSTEM_ENTITY)
     286           1 :                 continue;
     287             : 
     288           7 :             serializer.NumberU32_Unbounded("entity id", eit->first);
     289           7 :             eit->second->Serialize(serializer);
     290             :         }
     291             :     }
     292             : 
     293             :     // TODO: catch exceptions
     294           3 :     return true;
     295             : }
     296             : 
     297           3 : bool CComponentManager::DeserializeState(std::istream& stream)
     298             : {
     299             :     try
     300             :     {
     301           6 :         CStdDeserializer deserializer(m_ScriptInterface, stream);
     302             : 
     303           3 :         ResetState();
     304           3 :         InitSystemEntity();
     305             : 
     306           6 :         std::string rng;
     307           3 :         deserializer.StringASCII("rng", rng, 0, 32);
     308           3 :         DeserializeRNG(rng, m_RNG);
     309             : 
     310           3 :         deserializer.NumberU32_Unbounded("next entity id", m_NextEntityId); // TODO: use sensible bounds
     311             : 
     312             :         uint32_t numSystemComponentTypes;
     313           3 :         deserializer.NumberU32_Unbounded("num system component types", numSystemComponentTypes);
     314             : 
     315           3 :         ICmpTemplateManager* templateManager = NULL;
     316           6 :         CParamNode noParam;
     317             : 
     318           5 :         for (size_t i = 0; i < numSystemComponentTypes; ++i)
     319             :         {
     320           4 :             std::string ctname;
     321           2 :             deserializer.StringASCII("name", ctname, 0, 255);
     322             : 
     323           2 :             ComponentTypeId ctid = LookupCID(ctname);
     324           2 :             if (ctid == CID__Invalid)
     325             :             {
     326           0 :                 LOGERROR("Deserialization saw unrecognised component type '%s'", ctname.c_str());
     327           0 :                 return false;
     328             :             }
     329             : 
     330           2 :             IComponent* component = ConstructComponent(m_SystemEntity, ctid);
     331           2 :             if (!component)
     332           0 :                 return false;
     333             : 
     334           2 :             component->Deserialize(noParam, deserializer);
     335             : 
     336             :             // If this was the template manager, remember it so we can use it when
     337             :             // deserializing any further non-system entities
     338           2 :             if (ctid == CID_TemplateManager)
     339           1 :                 templateManager = static_cast<ICmpTemplateManager*> (component);
     340             :         }
     341             : 
     342             :         uint32_t numComponentTypes;
     343           3 :         deserializer.NumberU32_Unbounded("num component types", numComponentTypes);
     344             : 
     345           9 :         for (size_t i = 0; i < numComponentTypes; ++i)
     346             :         {
     347          12 :             std::string ctname;
     348           6 :             deserializer.StringASCII("name", ctname, 0, 255);
     349             : 
     350           6 :             ComponentTypeId ctid = LookupCID(ctname);
     351           6 :             if (ctid == CID__Invalid)
     352             :             {
     353           0 :                 LOGERROR("Deserialization saw unrecognised component type '%s'", ctname.c_str());
     354           0 :                 return false;
     355             :             }
     356             : 
     357             :             uint32_t numComponents;
     358           6 :             deserializer.NumberU32_Unbounded("num components", numComponents);
     359             : 
     360          13 :             for (size_t j = 0; j < numComponents; ++j)
     361             :             {
     362             :                 entity_id_t ent;
     363           7 :                 deserializer.NumberU32_Unbounded("entity id", ent);
     364           7 :                 IComponent* component = ConstructComponent(LookupEntityHandle(ent, true), ctid);
     365           7 :                 if (!component)
     366           0 :                     return false;
     367             : 
     368             :                 // Try to find the template for this entity
     369           7 :                 const CParamNode* entTemplate = NULL;
     370           7 :                 if (templateManager)
     371           1 :                     entTemplate = templateManager->LoadLatestTemplate(ent);
     372             : 
     373             :                 // Deserialize, with the appropriate template for this component
     374           7 :                 if (entTemplate)
     375           1 :                     component->Deserialize(entTemplate->GetChild(ctname.c_str()), deserializer);
     376             :                 else
     377           6 :                     component->Deserialize(noParam, deserializer);
     378             :             }
     379             :         }
     380             : 
     381           3 :         if (stream.peek() != EOF)
     382             :         {
     383           0 :             LOGERROR("Deserialization didn't reach EOF");
     384           0 :             return false;
     385             :         }
     386             : 
     387             :         // Allow components to do some final reinitialisation after everything is loaded
     388           6 :         CMessageDeserialized msg;
     389           3 :         BroadcastMessage(msg);
     390             : 
     391           3 :         return true;
     392             :     }
     393           0 :     catch (PSERROR_Deserialize& e)
     394             :     {
     395           0 :         LOGERROR("Deserialization failed: %s", e.what());
     396           0 :         return false;
     397             :     }
     398           3 : }

Generated by: LCOV version 1.13