LCOV - code coverage report
Current view: top level - source/simulation2/components - CCmpTemplateManager.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 61 105 58.1 %
Date: 2023-01-19 00:18:29 Functions: 19 28 67.9 %

          Line data    Source code
       1             : /* Copyright (C) 2023 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 "simulation2/system/Component.h"
      21             : #include "ICmpTemplateManager.h"
      22             : 
      23             : #include "simulation2/MessageTypes.h"
      24             : #include "simulation2/serialization/SerializedTypes.h"
      25             : 
      26             : #include "lib/utf8.h"
      27             : #include "ps/CLogger.h"
      28             : #include "ps/TemplateLoader.h"
      29             : #include "ps/XML/RelaxNG.h"
      30             : 
      31          33 : class CCmpTemplateManager final : public ICmpTemplateManager
      32             : {
      33             : public:
      34         116 :     static void ClassInit(CComponentManager& componentManager)
      35             :     {
      36         116 :         componentManager.SubscribeGloballyToMessageType(MT_Destroy);
      37         116 :     }
      38             : 
      39          22 :     DEFAULT_COMPONENT_ALLOCATOR(TemplateManager)
      40             : 
      41         116 :     static std::string GetSchema()
      42             :     {
      43         116 :         return "<a:component type='system'/><empty/>";
      44             :     }
      45             : 
      46          11 :     void Init(const CParamNode& UNUSED(paramNode)) override
      47             :     {
      48          11 :         m_DisableValidation = false;
      49             : 
      50          11 :         m_Validator.LoadGrammar(GetSimContext().GetComponentManager().GenerateSchema());
      51             :         // TODO: handle errors loading the grammar here?
      52             :         // TODO: support hotloading changes to the grammar
      53          11 :     }
      54             : 
      55          11 :     void Deinit() override
      56             :     {
      57          11 :     }
      58             : 
      59           1 :     void Serialize(ISerializer& serialize) override
      60             :     {
      61           2 :         std::map<std::string, std::vector<entity_id_t>> templateMap;
      62             : 
      63           2 :         for (const std::pair<const entity_id_t, std::string>& templateEnt : m_LatestTemplates)
      64           1 :             if (!ENTITY_IS_LOCAL(templateEnt.first))
      65           1 :                 templateMap[templateEnt.second].push_back(templateEnt.first);
      66             : 
      67           1 :         Serializer(serialize, "templates", templateMap);
      68           1 :     }
      69             : 
      70           1 :     void Deserialize(const CParamNode& paramNode, IDeserializer& deserialize) override
      71             :     {
      72           1 :         Init(paramNode);
      73             : 
      74           2 :         std::map<std::string, std::vector<entity_id_t>> templateMap;
      75           1 :         Serializer(deserialize, "templates", templateMap);
      76           2 :         for (const std::pair<const std::string, std::vector<entity_id_t>>& mapEl : templateMap)
      77           2 :             for (entity_id_t id : mapEl.second)
      78           1 :                 m_LatestTemplates[id] = mapEl.first;
      79           1 :     }
      80             : 
      81           2 :     void HandleMessage(const CMessage& msg, bool UNUSED(global)) override
      82             :     {
      83           2 :         switch (msg.GetType())
      84             :         {
      85           2 :         case MT_Destroy:
      86             :         {
      87           2 :             const CMessageDestroy& msgData = static_cast<const CMessageDestroy&> (msg);
      88             : 
      89             :             // Clean up m_LatestTemplates so it doesn't record any data for destroyed entities
      90           2 :             m_LatestTemplates.erase(msgData.entity);
      91             : 
      92           2 :             break;
      93             :         }
      94             :         }
      95           2 :     }
      96             : 
      97           1 :     void DisableValidation() override
      98             :     {
      99           1 :         m_DisableValidation = true;
     100           1 :     }
     101             : 
     102             :     const CParamNode* LoadTemplate(entity_id_t ent, const std::string& templateName) override;
     103             : 
     104             :     const CParamNode* GetTemplate(const std::string& templateName) override;
     105             : 
     106             :     const CParamNode* GetTemplateWithoutValidation(const std::string& templateName) override;
     107             : 
     108             :     bool TemplateExists(const std::string& templateName) const override;
     109             : 
     110             :     const CParamNode* LoadLatestTemplate(entity_id_t ent) override;
     111             : 
     112             :     std::string GetCurrentTemplateName(entity_id_t ent) const override;
     113             : 
     114             :     std::vector<std::string> FindAllTemplates(bool includeActors) const override;
     115             : 
     116             :     std::vector<std::string> FindTemplatesWithRootName(bool includeActors, const std::string& rootName) override;
     117             : 
     118             :     std::vector<std::vector<std::wstring>> GetCivData() override;
     119             : 
     120             :     std::vector<std::string> FindUsedTemplates() const override;
     121             : 
     122             :     std::vector<entity_id_t> GetEntitiesUsingTemplate(const std::string& templateName) const override;
     123             : 
     124             : private:
     125             :     // Template loader
     126             :     CTemplateLoader m_templateLoader;
     127             : 
     128             :     // Entity template XML validator
     129             :     RelaxNGValidator m_Validator;
     130             : 
     131             :     // Disable validation, for test cases
     132             :     bool m_DisableValidation;
     133             : 
     134             :     // Map from template name to schema validation status.
     135             :     // (Some files, e.g. inherited parent templates, may not be valid themselves but we still need to load
     136             :     // them and use them; we only reject invalid templates that were requested directly by GetTemplate/etc)
     137             :     std::map<std::string, bool> m_TemplateSchemaValidity;
     138             : 
     139             :     // Remember the template used by each entity, so we can return them
     140             :     // again for deserialization.
     141             :     std::map<entity_id_t, std::string> m_LatestTemplates;
     142             : };
     143             : 
     144         116 : REGISTER_COMPONENT_TYPE(TemplateManager)
     145             : 
     146          41 : const CParamNode* CCmpTemplateManager::LoadTemplate(entity_id_t ent, const std::string& templateName)
     147             : {
     148          41 :     m_LatestTemplates[ent] = templateName;
     149             : 
     150          41 :     return GetTemplate(templateName);
     151             : }
     152             : 
     153          41 : const CParamNode* CCmpTemplateManager::GetTemplate(const std::string& templateName)
     154             : {
     155          41 :     const CParamNode& fileData = m_templateLoader.GetTemplateFileData(templateName);
     156          41 :     if (!fileData.IsOk())
     157          13 :         return NULL;
     158             : 
     159          28 :     if (!m_DisableValidation)
     160             :     {
     161             :         // Compute validity, if it's not computed before
     162          20 :         if (m_TemplateSchemaValidity.find(templateName) == m_TemplateSchemaValidity.end())
     163             :         {
     164          16 :             m_TemplateSchemaValidity[templateName] = m_Validator.Validate(templateName, fileData.ToXMLString());
     165             : 
     166             :             // Show error on the first failure to validate the template
     167          16 :             if (!m_TemplateSchemaValidity[templateName])
     168           2 :                 LOGERROR("Failed to validate entity template '%s'", templateName.c_str());
     169             :         }
     170             :         // Refuse to return invalid templates
     171          20 :         if (!m_TemplateSchemaValidity[templateName])
     172           2 :             return NULL;
     173             :     }
     174             : 
     175          26 :     const CParamNode& templateRoot = fileData.GetOnlyChild();
     176          26 :     if (!templateRoot.IsOk())
     177             :     {
     178             :         // The validator should never let this happen
     179           0 :         LOGERROR("Invalid root element in entity template '%s'", templateName.c_str());
     180           0 :         return NULL;
     181             :     }
     182             : 
     183          26 :     return &templateRoot;
     184             : }
     185             : 
     186           0 : const CParamNode* CCmpTemplateManager::GetTemplateWithoutValidation(const std::string& templateName)
     187             : {
     188           0 :     const CParamNode& templateRoot = m_templateLoader.GetTemplateFileData(templateName).GetOnlyChild();
     189           0 :     if (!templateRoot.IsOk())
     190           0 :         return NULL;
     191             : 
     192           0 :     return &templateRoot;
     193             : }
     194             : 
     195           0 : bool CCmpTemplateManager::TemplateExists(const std::string& templateName) const
     196             : {
     197           0 :     return m_templateLoader.TemplateExists(templateName);
     198             : }
     199             : 
     200           1 : const CParamNode* CCmpTemplateManager::LoadLatestTemplate(entity_id_t ent)
     201             : {
     202           1 :     std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.find(ent);
     203           1 :     if (it == m_LatestTemplates.end())
     204           0 :         return NULL;
     205           1 :     return LoadTemplate(ent, it->second);
     206             : }
     207             : 
     208           0 : std::string CCmpTemplateManager::GetCurrentTemplateName(entity_id_t ent) const
     209             : {
     210           0 :     std::map<entity_id_t, std::string>::const_iterator it = m_LatestTemplates.find(ent);
     211           0 :     if (it == m_LatestTemplates.end())
     212           0 :         return "";
     213           0 :     return it->second;
     214             : }
     215             : 
     216           0 : std::vector<std::string> CCmpTemplateManager::FindAllTemplates(bool includeActors) const
     217             : {
     218           0 :     ETemplatesType templatesType = includeActors ? ALL_TEMPLATES : SIMULATION_TEMPLATES;
     219           0 :     return m_templateLoader.FindTemplates("", true, templatesType);
     220             : }
     221             : 
     222           0 : std::vector<std::string> CCmpTemplateManager::FindTemplatesWithRootName(bool includeActors, const std::string& rootName)
     223             : {
     224           0 :     ETemplatesType templatesType = includeActors ? ALL_TEMPLATES : SIMULATION_TEMPLATES;
     225           0 :     return m_templateLoader.FindTemplatesWithRootName(std::string(), true, templatesType, rootName);
     226             : }
     227             : 
     228           0 : std::vector<std::vector<std::wstring>> CCmpTemplateManager::GetCivData()
     229             : {
     230           0 :     std::vector<std::vector<std::wstring>> data;
     231             : 
     232           0 :     std::vector<std::string> names = FindTemplatesWithRootName(false, "Player");
     233           0 :     data.reserve(names.size());
     234           0 :     for (const std::string& name : names)
     235             :     {
     236           0 :         const CParamNode& identity = GetTemplate(name)->GetChild("Identity");
     237           0 :         data.push_back(std::vector<std::wstring> {
     238           0 :             identity.GetChild("Civ").ToWString(),
     239           0 :             identity.GetChild("GenericName").ToWString()
     240           0 :         });
     241             :     }
     242           0 :     return data;
     243             : }
     244             : 
     245           0 : std::vector<std::string> CCmpTemplateManager::FindUsedTemplates() const
     246             : {
     247           0 :     std::vector<std::string> usedTemplates;
     248           0 :     for (const std::pair<const entity_id_t, std::string>& p : m_LatestTemplates)
     249           0 :         if (std::find(usedTemplates.begin(), usedTemplates.end(), p.second) == usedTemplates.end())
     250           0 :             usedTemplates.push_back(p.second);
     251           0 :     return usedTemplates;
     252             : }
     253             : 
     254             : /**
     255             :  * Get the list of entities using the specified template
     256             :  */
     257           0 : std::vector<entity_id_t> CCmpTemplateManager::GetEntitiesUsingTemplate(const std::string& templateName) const
     258             : {
     259           0 :     std::vector<entity_id_t> entities;
     260           0 :     for (const std::pair<const entity_id_t, std::string>& p : m_LatestTemplates)
     261           0 :         if (p.second == templateName)
     262           0 :             entities.push_back(p.first);
     263             : 
     264           0 :     return entities;
     265           3 : }

Generated by: LCOV version 1.13