LCOV - code coverage report
Current view: top level - source/simulation2/system - ComponentManager.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 454 555 81.8 %
Date: 2022-06-14 00:41:00 Functions: 45 59 76.3 %

          Line data    Source code
       1             : /* Copyright (C) 2022 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             : 
      22             : #include "lib/utf8.h"
      23             : #include "ps/CLogger.h"
      24             : #include "ps/Filesystem.h"
      25             : #include "ps/Profile.h"
      26             : #include "ps/scripting/JSInterface_VFS.h"
      27             : #include "scriptinterface/FunctionWrapper.h"
      28             : #include "simulation2/components/ICmpTemplateManager.h"
      29             : #include "simulation2/MessageTypes.h"
      30             : #include "simulation2/system/DynamicSubscription.h"
      31             : #include "simulation2/system/IComponent.h"
      32             : #include "simulation2/system/ParamNode.h"
      33             : #include "simulation2/system/SimContext.h"
      34             : 
      35             : /**
      36             :  * Used for script-only message types.
      37             :  */
      38             : class CMessageScripted final : public CMessage
      39             : {
      40             : public:
      41           0 :     virtual int GetType() const { return mtid; }
      42           0 :     virtual const char* GetScriptHandlerName() const { return handlerName.c_str(); }
      43           0 :     virtual const char* GetScriptGlobalHandlerName() const { return globalHandlerName.c_str(); }
      44           0 :     virtual JS::Value ToJSVal(const ScriptInterface& UNUSED(scriptInterface)) const { return msg.get(); }
      45             : 
      46           0 :     CMessageScripted(const ScriptInterface& scriptInterface, int mtid, const std::string& name, JS::HandleValue msg) :
      47           0 :         mtid(mtid), handlerName("On" + name), globalHandlerName("OnGlobal" + name), msg(scriptInterface.GetGeneralJSContext(), msg)
      48             :     {
      49           0 :     }
      50             : 
      51             :     int mtid;
      52             :     std::string handlerName;
      53             :     std::string globalHandlerName;
      54             :     JS::PersistentRootedValue msg;
      55             : };
      56             : 
      57         128 : CComponentManager::CComponentManager(CSimContext& context, std::shared_ptr<ScriptContext> cx, bool skipScriptFunctions) :
      58             :     m_NextScriptComponentTypeId(CID__LastNative),
      59             :     m_ScriptInterface("Engine", "Simulation", cx),
      60         128 :     m_SimContext(context), m_CurrentlyHotloading(false)
      61             : {
      62         128 :     context.SetComponentManager(this);
      63             : 
      64         128 :     m_ScriptInterface.SetCallbackData(static_cast<void*> (this));
      65         128 :     m_ScriptInterface.ReplaceNondeterministicRNG(m_RNG);
      66             : 
      67             :     // For component script tests, the test system sets up its own scripted implementation of
      68             :     // these functions, so we skip registering them here in those cases
      69         128 :     if (!skipScriptFunctions)
      70             :     {
      71          62 :         JSI_VFS::RegisterScriptFunctions_ReadOnlySimulation(m_ScriptInterface);
      72          62 :         ScriptRequest rq(m_ScriptInterface);
      73          62 :         constexpr ScriptFunction::ObjectGetter<CComponentManager> Getter = &ScriptInterface::ObjectFromCBData<CComponentManager>;
      74          62 :         ScriptFunction::Register<&CComponentManager::Script_RegisterComponentType, Getter>(rq, "RegisterComponentType");
      75          62 :         ScriptFunction::Register<&CComponentManager::Script_RegisterSystemComponentType, Getter>(rq, "RegisterSystemComponentType");
      76          62 :         ScriptFunction::Register<&CComponentManager::Script_ReRegisterComponentType, Getter>(rq, "ReRegisterComponentType");
      77          62 :         ScriptFunction::Register<&CComponentManager::Script_RegisterInterface, Getter>(rq, "RegisterInterface");
      78          62 :         ScriptFunction::Register<&CComponentManager::Script_RegisterMessageType, Getter>(rq, "RegisterMessageType");
      79          62 :         ScriptFunction::Register<&CComponentManager::Script_RegisterGlobal, Getter>(rq, "RegisterGlobal");
      80          62 :         ScriptFunction::Register<&CComponentManager::Script_GetEntitiesWithInterface, Getter>(rq, "GetEntitiesWithInterface");
      81          62 :         ScriptFunction::Register<&CComponentManager::Script_GetComponentsWithInterface, Getter>(rq, "GetComponentsWithInterface");
      82          62 :         ScriptFunction::Register<&CComponentManager::Script_PostMessage, Getter>(rq, "PostMessage");
      83          62 :         ScriptFunction::Register<&CComponentManager::Script_BroadcastMessage, Getter>(rq, "BroadcastMessage");
      84          62 :         ScriptFunction::Register<&CComponentManager::Script_AddEntity, Getter>(rq, "AddEntity");
      85          62 :         ScriptFunction::Register<&CComponentManager::Script_AddLocalEntity, Getter>(rq, "AddLocalEntity");
      86          62 :         ScriptFunction::Register<&CComponentManager::QueryInterface, Getter>(rq, "QueryInterface");
      87          62 :         ScriptFunction::Register<&CComponentManager::DestroyComponentsSoon, Getter>(rq, "DestroyEntity");
      88          62 :         ScriptFunction::Register<&CComponentManager::FlushDestroyedComponents, Getter>(rq, "FlushDestroyedEntities");
      89          62 :         ScriptFunction::Register<&CComponentManager::Script_GetTemplate, Getter>(rq, "GetTemplate");
      90             : 
      91             :     }
      92             : 
      93             :     // Globalscripts may use VFS script functions
      94         128 :     m_ScriptInterface.LoadGlobalScripts();
      95             : 
      96             :     // Define MT_*, IID_* as script globals, and store their names
      97             : #define MESSAGE(name) m_ScriptInterface.SetGlobal("MT_" #name, (int)MT_##name);
      98             : #define INTERFACE(name) \
      99             :     m_ScriptInterface.SetGlobal("IID_" #name, (int)IID_##name); \
     100             :     m_InterfaceIdsByName[#name] = IID_##name;
     101             : #define COMPONENT(name)
     102             : #include "simulation2/TypeList.h"
     103             : #undef MESSAGE
     104             : #undef INTERFACE
     105             : #undef COMPONENT
     106             : 
     107         128 :     m_ScriptInterface.SetGlobal("INVALID_ENTITY", (int)INVALID_ENTITY);
     108         128 :     m_ScriptInterface.SetGlobal("INVALID_PLAYER", (int)INVALID_PLAYER);
     109         128 :     m_ScriptInterface.SetGlobal("SYSTEM_ENTITY", (int)SYSTEM_ENTITY);
     110             : 
     111         128 :     m_ComponentsByInterface.resize(IID__LastNative);
     112             : 
     113         128 :     ResetState();
     114         128 : }
     115             : 
     116         128 : CComponentManager::~CComponentManager()
     117             : {
     118         128 :     ResetState();
     119         128 : }
     120             : 
     121         115 : void CComponentManager::LoadComponentTypes()
     122             : {
     123             : #define MESSAGE(name) \
     124             :     RegisterMessageType(MT_##name, #name);
     125             : #define INTERFACE(name) \
     126             :     extern void RegisterComponentInterface_##name(ScriptInterface&); \
     127             :     RegisterComponentInterface_##name(m_ScriptInterface);
     128             : #define COMPONENT(name) \
     129             :     extern void RegisterComponentType_##name(CComponentManager&); \
     130             :     m_CurrentComponent = CID_##name; \
     131             :     RegisterComponentType_##name(*this);
     132             : 
     133             : #include "simulation2/TypeList.h"
     134             : 
     135         115 :     m_CurrentComponent = CID__Invalid;
     136             : 
     137             : #undef MESSAGE
     138             : #undef INTERFACE
     139             : #undef COMPONENT
     140         115 : }
     141             : 
     142             : 
     143         488 : bool CComponentManager::LoadScript(const VfsPath& filename, bool hotload)
     144             : {
     145         488 :     m_CurrentlyHotloading = hotload;
     146         976 :     CVFSFile file;
     147         488 :     PSRETURN loadOk = file.Load(g_VFS, filename);
     148         488 :     if (loadOk != PSRETURN_OK) // VFS will log the failed file and the reason
     149             :         return false;
     150         976 :     std::string content = file.DecodeUTF8(); // assume it's UTF-8
     151         488 :     bool ok = m_ScriptInterface.LoadScript(filename, content);
     152             : 
     153         488 :     m_CurrentlyHotloading = false;
     154         488 :     return ok;
     155             : }
     156             : 
     157          71 : void CComponentManager::Script_RegisterComponentType_Common(int iid, const std::string& cname, JS::HandleValue ctor, bool reRegister, bool systemComponent)
     158             : {
     159          71 :     ScriptRequest rq(m_ScriptInterface);
     160             : 
     161             :     // Find the C++ component that wraps the interface
     162          71 :     int cidWrapper = GetScriptWrapper(iid);
     163          71 :     if (cidWrapper == CID__Invalid)
     164             :     {
     165           1 :         ScriptException::Raise(rq, "Invalid interface id");
     166           1 :         return;
     167             :     }
     168          70 :     const ComponentType& ctWrapper = m_ComponentTypesById[cidWrapper];
     169             : 
     170          70 :     bool mustReloadComponents = false; // for hotloading
     171             : 
     172         140 :     ComponentTypeId cid = LookupCID(cname);
     173          70 :     if (cid == CID__Invalid)
     174             :     {
     175          63 :         if (reRegister)
     176             :         {
     177           0 :             ScriptException::Raise(rq, "ReRegistering component type that was not registered before '%s'", cname.c_str());
     178             :             return;
     179             :         }
     180             :         // Allocate a new cid number
     181          63 :         cid = m_NextScriptComponentTypeId++;
     182          63 :         m_ComponentTypeIdsByName[cname] = cid;
     183          63 :         if (systemComponent)
     184           0 :             MarkScriptedComponentForSystemEntity(cid);
     185             :     }
     186             :     else
     187             :     {
     188             :         // Component type is already loaded, so do hotloading:
     189             : 
     190           7 :         if (!m_CurrentlyHotloading && !reRegister)
     191             :         {
     192           0 :             ScriptException::Raise(rq, "Registering component type with already-registered name '%s'", cname.c_str());
     193             :             return;
     194             :         }
     195             : 
     196           7 :         const ComponentType& ctPrevious = m_ComponentTypesById[cid];
     197             : 
     198             :         // We can only replace scripted component types, not native ones
     199           7 :         if (ctPrevious.type != CT_Script)
     200             :         {
     201           0 :             ScriptException::Raise(rq, "Loading script component type with same name '%s' as native component", cname.c_str());
     202             :             return;
     203             :         }
     204             : 
     205             :         // We don't support changing the IID of a component type (it would require fiddling
     206             :         // around with m_ComponentsByInterface and being careful to guarantee uniqueness per entity)
     207           7 :         if (ctPrevious.iid != iid)
     208             :         {
     209             :             // ...though it only matters if any components exist with this type
     210           0 :             if (!m_ComponentsByTypeId[cid].empty())
     211             :             {
     212           0 :                 ScriptException::Raise(rq, "Hotloading script component type mustn't change interface ID");
     213             :                 return;
     214             :             }
     215             :         }
     216             : 
     217             :         // Remove the old component type's message subscriptions
     218           7 :         std::map<MessageTypeId, std::vector<ComponentTypeId> >::iterator it;
     219         161 :         for (it = m_LocalMessageSubscriptions.begin(); it != m_LocalMessageSubscriptions.end(); ++it)
     220             :         {
     221         147 :             std::vector<ComponentTypeId>& types = it->second;
     222         441 :             std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid);
     223         441 :             if (ctit != types.end())
     224           0 :                 types.erase(ctit);
     225             :         }
     226          77 :         for (it = m_GlobalMessageSubscriptions.begin(); it != m_GlobalMessageSubscriptions.end(); ++it)
     227             :         {
     228          63 :             std::vector<ComponentTypeId>& types = it->second;
     229         189 :             std::vector<ComponentTypeId>::iterator ctit = find(types.begin(), types.end(), cid);
     230         189 :             if (ctit != types.end())
     231           0 :                 types.erase(ctit);
     232             :         }
     233             : 
     234             :         mustReloadComponents = true;
     235             :     }
     236             : 
     237         140 :     JS::RootedValue protoVal(rq.cx);
     238         140 :     if (!Script::GetProperty(rq, ctor, "prototype", &protoVal))
     239             :     {
     240           0 :         ScriptException::Raise(rq, "Failed to get property 'prototype'");
     241           0 :         return;
     242             :     }
     243         140 :     if (!protoVal.isObject())
     244             :     {
     245           0 :         ScriptException::Raise(rq, "Component has no constructor");
     246             :         return;
     247             :     }
     248         210 :     std::string schema = "<empty/>";
     249             : 
     250         140 :     if (Script::HasProperty(rq, protoVal, "Schema"))
     251          32 :         Script::GetProperty(rq, protoVal, "Schema", schema);
     252             : 
     253             :     // Construct a new ComponentType, using the wrapper's alloc functions
     254          70 :     ComponentType ct{
     255             :         CT_Script,
     256             :         iid,
     257          70 :         ctWrapper.alloc,
     258          70 :         ctWrapper.dealloc,
     259             :         cname,
     260             :         schema,
     261             :         std::make_unique<JS::PersistentRootedValue>(rq.cx, ctor)
     262         140 :     };
     263          70 :     m_ComponentTypesById[cid] = std::move(ct);
     264             : 
     265          70 :     m_CurrentComponent = cid; // needed by Subscribe
     266             : 
     267             :     // Find all the ctor prototype's On* methods, and subscribe to the appropriate messages:
     268         140 :     std::vector<std::string> methods;
     269             : 
     270         140 :     if (!Script::EnumeratePropertyNames(rq, protoVal, false, methods))
     271             :     {
     272           0 :         ScriptException::Raise(rq, "Failed to enumerate component properties.");
     273           0 :         return;
     274             :     }
     275             : 
     276        1264 :     for (std::vector<std::string>::const_iterator it = methods.begin(); it != methods.end(); ++it)
     277             :     {
     278             :         // TODO C++17: string_view
     279        2108 :         if (strncmp((it->c_str()), "On", 2) != 0)
     280        1049 :             continue;
     281             : 
     282          10 :         std::string name = (*it).substr(2); // strip the "On" prefix
     283             : 
     284             :         // Handle "OnGlobalFoo" functions specially
     285           5 :         bool isGlobal = false;
     286          10 :         if (strncmp(name.c_str(), "Global", 6) == 0)
     287             :         {
     288           1 :             isGlobal = true;
     289           1 :             name = name.substr(6);
     290             :         }
     291             : 
     292           5 :         std::map<std::string, MessageTypeId>::const_iterator mit = m_MessageTypeIdsByName.find(name);
     293          10 :         if (mit == m_MessageTypeIdsByName.end())
     294             :         {
     295           0 :             ScriptException::Raise(rq, "Registered component has unrecognized '%s' message handler method", it->c_str());
     296           0 :             return;
     297             :         }
     298             : 
     299           5 :         if (isGlobal)
     300           2 :             SubscribeGloballyToMessageType(mit->second);
     301             :         else
     302           8 :             SubscribeToMessageType(mit->second);
     303             :     }
     304             : 
     305          70 :     m_CurrentComponent = CID__Invalid;
     306             : 
     307          70 :     if (mustReloadComponents)
     308             :     {
     309             :         // For every script component with this cid, we need to switch its
     310             :         // prototype from the old constructor's prototype property to the new one's
     311           7 :         const std::map<entity_id_t, IComponent*>& comps = m_ComponentsByTypeId[cid];
     312          14 :         std::map<entity_id_t, IComponent*>::const_iterator eit = comps.begin();
     313          24 :         for (; eit != comps.end(); ++eit)
     314             :         {
     315          20 :             JS::RootedValue instance(rq.cx, eit->second->GetJSInstance());
     316          10 :             if (!instance.isNull())
     317          15 :                 m_ScriptInterface.SetPrototype(instance, protoVal);
     318             :         }
     319             :     }
     320             : }
     321             : 
     322          70 : void CComponentManager::Script_RegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
     323             : {
     324          70 :     Script_RegisterComponentType_Common(iid, cname, ctor, false, false);
     325         140 :     m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading);
     326          70 : }
     327             : 
     328           0 : void CComponentManager::Script_RegisterSystemComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
     329             : {
     330           0 :     Script_RegisterComponentType_Common(iid, cname, ctor, false, true);
     331           0 :     m_ScriptInterface.SetGlobal(cname.c_str(), ctor, m_CurrentlyHotloading);
     332           0 : }
     333             : 
     334           1 : void CComponentManager::Script_ReRegisterComponentType(int iid, const std::string& cname, JS::HandleValue ctor)
     335             : {
     336           1 :     Script_RegisterComponentType_Common(iid, cname, ctor, true, false);
     337           1 : }
     338             : 
     339           6 : void CComponentManager::Script_RegisterInterface(const std::string& name)
     340             : {
     341           6 :     std::map<std::string, InterfaceId>::iterator it = m_InterfaceIdsByName.find(name);
     342          12 :     if (it != m_InterfaceIdsByName.end())
     343             :     {
     344             :         // Redefinitions are fine (and just get ignored) when hotloading; otherwise
     345             :         // they're probably unintentional and should be reported
     346           3 :         if (!m_CurrentlyHotloading)
     347             :         {
     348           0 :             ScriptRequest rq(m_ScriptInterface);
     349           0 :             ScriptException::Raise(rq, "Registering interface with already-registered name '%s'", name.c_str());
     350             :         }
     351           3 :         return;
     352             :     }
     353             : 
     354             :     // IIDs start at 1, so size+1 is the next unused one
     355           3 :     size_t id = m_InterfaceIdsByName.size() + 1;
     356           3 :     m_InterfaceIdsByName[name] = (InterfaceId)id;
     357           3 :     m_ComponentsByInterface.resize(id+1); // add one so we can index by InterfaceId
     358           6 :     m_ScriptInterface.SetGlobal(("IID_" + name).c_str(), (int)id);
     359             : }
     360             : 
     361           0 : void CComponentManager::Script_RegisterMessageType(const std::string& name)
     362             : {
     363           0 :     std::map<std::string, MessageTypeId>::iterator it = m_MessageTypeIdsByName.find(name);
     364           0 :     if (it != m_MessageTypeIdsByName.end())
     365             :     {
     366             :         // Redefinitions are fine (and just get ignored) when hotloading; otherwise
     367             :         // they're probably unintentional and should be reported
     368           0 :         if (!m_CurrentlyHotloading)
     369             :         {
     370           0 :             ScriptRequest rq(m_ScriptInterface);
     371           0 :             ScriptException::Raise(rq, "Registering message type with already-registered name '%s'", name.c_str());
     372             :         }
     373           0 :         return;
     374             :     }
     375             : 
     376             :     // MTIDs start at 1, so size+1 is the next unused one
     377           0 :     size_t id = m_MessageTypeIdsByName.size() + 1;
     378           0 :     RegisterMessageType((MessageTypeId)id, name.c_str());
     379           0 :     m_ScriptInterface.SetGlobal(("MT_" + name).c_str(), (int)id);
     380             : }
     381             : 
     382           6 : void CComponentManager::Script_RegisterGlobal(const std::string& name, JS::HandleValue value)
     383             : {
     384          12 :     m_ScriptInterface.SetGlobal(name.c_str(), value, m_CurrentlyHotloading);
     385           6 : }
     386             : 
     387           0 : const CParamNode& CComponentManager::Script_GetTemplate(const std::string& templateName)
     388             : {
     389           0 :     static CParamNode nullNode(false);
     390             : 
     391           0 :     ICmpTemplateManager* cmpTemplateManager = static_cast<ICmpTemplateManager*> (QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
     392           0 :     if (!cmpTemplateManager)
     393             :     {
     394           0 :         LOGERROR("Template manager is not loaded");
     395           0 :         return nullNode;
     396             :     }
     397             : 
     398           0 :     const CParamNode* tmpl = cmpTemplateManager->GetTemplate(templateName);
     399           0 :     if (!tmpl)
     400           0 :         return nullNode;
     401             :     return *tmpl;
     402             : }
     403             : 
     404           0 : std::vector<int> CComponentManager::Script_GetEntitiesWithInterface(int iid)
     405             : {
     406           0 :     std::vector<int> ret;
     407           0 :     const InterfaceListUnordered& ents = GetEntitiesWithInterfaceUnordered(iid);
     408           0 :     for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it)
     409           0 :         if (!ENTITY_IS_LOCAL(it->first))
     410           0 :             ret.push_back(it->first);
     411           0 :     std::sort(ret.begin(), ret.end());
     412           0 :     return ret;
     413             : }
     414             : 
     415           0 : std::vector<IComponent*> CComponentManager::Script_GetComponentsWithInterface(int iid)
     416             : {
     417           0 :     std::vector<IComponent*> ret;
     418           0 :     InterfaceList ents = GetEntitiesWithInterface(iid);
     419           0 :     for (InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it)
     420           0 :         ret.push_back(it->second); // TODO: maybe we should exclude local entities
     421           0 :     return ret;
     422             : }
     423             : 
     424           3 : CMessage* CComponentManager::ConstructMessage(int mtid, JS::HandleValue data)
     425             : {
     426           3 :     if (mtid == MT__Invalid || mtid > (int)m_MessageTypeIdsByName.size()) // (IDs start at 1 so use '>' here)
     427           0 :         LOGERROR("PostMessage with invalid message type ID '%d'", mtid);
     428             : 
     429           3 :     if (mtid < MT__LastNative)
     430             :     {
     431           3 :         return CMessageFromJSVal(mtid, m_ScriptInterface, data);
     432             :     }
     433             :     else
     434             :     {
     435           0 :         return new CMessageScripted(m_ScriptInterface, mtid, m_MessageTypeNamesById[mtid], data);
     436             :     }
     437             : }
     438             : 
     439           2 : void CComponentManager::Script_PostMessage(int ent, int mtid, JS::HandleValue data)
     440             : {
     441           2 :     CMessage* msg = ConstructMessage(mtid, data);
     442           2 :     if (!msg)
     443             :         return; // error
     444             : 
     445           2 :     PostMessage(ent, *msg);
     446             : 
     447           2 :     delete msg;
     448             : }
     449             : 
     450           1 : void CComponentManager::Script_BroadcastMessage(int mtid, JS::HandleValue data)
     451             : {
     452           1 :     CMessage* msg = ConstructMessage(mtid, data);
     453           1 :     if (!msg)
     454             :         return; // error
     455             : 
     456           1 :     BroadcastMessage(*msg);
     457             : 
     458           1 :     delete msg;
     459             : }
     460             : 
     461           2 : int CComponentManager::Script_AddEntity(const std::wstring& templateName)
     462             : {
     463             :     // TODO: should validate the string to make sure it doesn't contain scary characters
     464             :     // that will let it access non-component-template files
     465           6 :     return AddEntity(templateName, AllocateNewEntity());
     466             : }
     467             : 
     468           2 : int CComponentManager::Script_AddLocalEntity(const std::wstring& templateName)
     469             : {
     470             :     // TODO: should validate the string to make sure it doesn't contain scary characters
     471             :     // that will let it access non-component-template files
     472           6 :     return AddEntity(templateName, AllocateNewLocalEntity());
     473             : }
     474             : 
     475         263 : void CComponentManager::ResetState()
     476             : {
     477             :     // Delete all dynamic message subscriptions
     478         263 :     m_DynamicMessageSubscriptionsNonsync.clear();
     479         263 :     m_DynamicMessageSubscriptionsNonsyncByComponent.clear();
     480             : 
     481             :     // Delete all IComponents in reverse order of creation.
     482         263 :     std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::reverse_iterator iit = m_ComponentsByTypeId.rbegin();
     483        1185 :     for (; iit != m_ComponentsByTypeId.rend(); ++iit)
     484             :     {
     485         396 :         std::map<entity_id_t, IComponent*>::iterator eit = iit->second.begin();
     486         540 :         for (; eit != iit->second.end(); ++eit)
     487             :         {
     488         288 :             eit->second->Deinit();
     489         288 :             m_ComponentTypesById[iit->first].dealloc(eit->second);
     490             :         }
     491             :     }
     492             : 
     493         526 :     std::vector<std::unordered_map<entity_id_t, IComponent*> >::iterator ifcit = m_ComponentsByInterface.begin();
     494       40251 :     for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
     495       13154 :         ifcit->clear();
     496             : 
     497         263 :     m_ComponentsByTypeId.clear();
     498             : 
     499             :     // Delete all SEntityComponentCaches
     500         526 :     std::unordered_map<entity_id_t, SEntityComponentCache*>::iterator ccit = m_ComponentCaches.begin();
     501         730 :     for (; ccit != m_ComponentCaches.end(); ++ccit)
     502         204 :         free(ccit->second);
     503         263 :     m_ComponentCaches.clear();
     504         263 :     m_SystemEntity = CEntityHandle();
     505             : 
     506         263 :     m_DestructionQueue.clear();
     507             : 
     508             :     // Reset IDs
     509         263 :     m_NextEntityId = SYSTEM_ENTITY + 1;
     510         263 :     m_NextLocalEntityId = FIRST_LOCAL_ENTITY;
     511         263 : }
     512             : 
     513           2 : void CComponentManager::SetRNGSeed(u32 seed)
     514             : {
     515           4 :     m_RNG.seed(seed);
     516           2 : }
     517             : 
     518        3795 : void CComponentManager::RegisterComponentType(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc,
     519             :         const char* name, const std::string& schema)
     520             : {
     521        7590 :     ComponentType c{ CT_Native, iid, alloc, dealloc, name, schema, std::unique_ptr<JS::PersistentRootedValue>() };
     522        3795 :     m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
     523        7590 :     m_ComponentTypeIdsByName[name] = cid;
     524        3795 : }
     525             : 
     526        2415 : void CComponentManager::RegisterComponentTypeScriptWrapper(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc,
     527             :         DeallocFunc dealloc, const char* name, const std::string& schema)
     528             : {
     529        4830 :     ComponentType c{ CT_ScriptWrapper, iid, alloc, dealloc, name, schema, std::unique_ptr<JS::PersistentRootedValue>() };
     530        2415 :     m_ComponentTypesById.insert(std::make_pair(cid, std::move(c)));
     531        4830 :     m_ComponentTypeIdsByName[name] = cid;
     532             :     // TODO: merge with RegisterComponentType
     533        2415 : }
     534             : 
     535           0 : void CComponentManager::MarkScriptedComponentForSystemEntity(CComponentManager::ComponentTypeId cid)
     536             : {
     537           0 :     m_ScriptedSystemComponents.push_back(cid);
     538           0 : }
     539             : 
     540        3680 : void CComponentManager::RegisterMessageType(MessageTypeId mtid, const char* name)
     541             : {
     542        7360 :     m_MessageTypeIdsByName[name] = mtid;
     543        3680 :     m_MessageTypeNamesById[mtid] = name;
     544        3680 : }
     545             : 
     546        8744 : void CComponentManager::SubscribeToMessageType(MessageTypeId mtid)
     547             : {
     548             :     // TODO: verify mtid
     549        8744 :     ENSURE(m_CurrentComponent != CID__Invalid);
     550        8744 :     std::vector<ComponentTypeId>& types = m_LocalMessageSubscriptions[mtid];
     551        8744 :     types.push_back(m_CurrentComponent);
     552       26232 :     std::sort(types.begin(), types.end()); // TODO: just sort once at the end of LoadComponents
     553        8744 : }
     554             : 
     555        1496 : void CComponentManager::SubscribeGloballyToMessageType(MessageTypeId mtid)
     556             : {
     557             :     // TODO: verify mtid
     558        1496 :     ENSURE(m_CurrentComponent != CID__Invalid);
     559        1496 :     std::vector<ComponentTypeId>& types = m_GlobalMessageSubscriptions[mtid];
     560        1496 :     types.push_back(m_CurrentComponent);
     561        4488 :     std::sort(types.begin(), types.end()); // TODO: just sort once at the end of LoadComponents
     562        1496 : }
     563             : 
     564           4 : void CComponentManager::FlattenDynamicSubscriptions()
     565             : {
     566           4 :     std::map<MessageTypeId, CDynamicSubscription>::iterator it;
     567           8 :     for (it = m_DynamicMessageSubscriptionsNonsync.begin();
     568           9 :          it != m_DynamicMessageSubscriptionsNonsync.end(); ++it)
     569             :     {
     570           2 :         it->second.Flatten();
     571             :     }
     572           4 : }
     573             : 
     574           5 : void CComponentManager::DynamicSubscriptionNonsync(MessageTypeId mtid, IComponent* component, bool enable)
     575             : {
     576           5 :     if (enable)
     577             :     {
     578           2 :         bool newlyInserted = m_DynamicMessageSubscriptionsNonsyncByComponent[component].insert(mtid).second;
     579           2 :         if (newlyInserted)
     580           2 :             m_DynamicMessageSubscriptionsNonsync[mtid].Add(component);
     581             :     }
     582             :     else
     583             :     {
     584           3 :         size_t numRemoved = m_DynamicMessageSubscriptionsNonsyncByComponent[component].erase(mtid);
     585           3 :         if (numRemoved)
     586           1 :             m_DynamicMessageSubscriptionsNonsync[mtid].Remove(component);
     587             :     }
     588           5 : }
     589             : 
     590           7 : void CComponentManager::RemoveComponentDynamicSubscriptions(IComponent* component)
     591             : {
     592           7 :     std::map<IComponent*, std::set<MessageTypeId> >::iterator it = m_DynamicMessageSubscriptionsNonsyncByComponent.find(component);
     593          14 :     if (it == m_DynamicMessageSubscriptionsNonsyncByComponent.end())
     594             :         return;
     595             : 
     596           4 :     std::set<MessageTypeId>::iterator mtit;
     597          13 :     for (mtit = it->second.begin(); mtit != it->second.end(); ++mtit)
     598             :     {
     599           2 :         m_DynamicMessageSubscriptionsNonsync[*mtit].Remove(component);
     600             : 
     601             :         // Need to flatten the subscription lists immediately to avoid dangling IComponent* references
     602           2 :         m_DynamicMessageSubscriptionsNonsync[*mtit].Flatten();
     603             :     }
     604             : 
     605           4 :     m_DynamicMessageSubscriptionsNonsyncByComponent.erase(it);
     606             : }
     607             : 
     608          48 : CComponentManager::ComponentTypeId CComponentManager::LookupCID(const std::string& cname) const
     609             : {
     610         118 :     std::map<std::string, ComponentTypeId>::const_iterator it = m_ComponentTypeIdsByName.find(cname);
     611         266 :     if (it == m_ComponentTypeIdsByName.end())
     612             :         return CID__Invalid;
     613         125 :     return it->second;
     614             : }
     615             : 
     616           8 : std::string CComponentManager::LookupComponentTypeName(ComponentTypeId cid) const
     617             : {
     618           8 :     std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.find(cid);
     619          16 :     if (it == m_ComponentTypesById.end())
     620           0 :         return "";
     621          16 :     return it->second.name;
     622             : }
     623             : 
     624          71 : CComponentManager::ComponentTypeId CComponentManager::GetScriptWrapper(InterfaceId iid)
     625             : {
     626          71 :     if (iid >= IID__LastNative && iid <= (int)m_InterfaceIdsByName.size()) // use <= since IDs start at 1
     627             :         return CID_UnknownScript;
     628             : 
     629         195 :     std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.begin();
     630         326 :     for (; it != m_ComponentTypesById.end(); ++it)
     631         520 :         if (it->second.iid == iid && it->second.type == CT_ScriptWrapper)
     632         128 :             return it->first;
     633             : 
     634           3 :     std::map<std::string, InterfaceId>::const_iterator iiit = m_InterfaceIdsByName.begin();
     635          51 :     for (; iiit != m_InterfaceIdsByName.end(); ++iiit)
     636          98 :         if (iiit->second == iid)
     637             :         {
     638           0 :             LOGERROR("No script wrapper found for interface id %d '%s'", iid, iiit->first.c_str());
     639           0 :             return CID__Invalid;
     640             :         }
     641             : 
     642           2 :     LOGERROR("No script wrapper found for interface id %d", iid);
     643           1 :     return CID__Invalid;
     644             : }
     645             : 
     646          13 : entity_id_t CComponentManager::AllocateNewEntity()
     647             : {
     648          15 :     entity_id_t id = m_NextEntityId++;
     649             :     // TODO: check for overflow
     650           2 :     return id;
     651             : }
     652             : 
     653           4 : entity_id_t CComponentManager::AllocateNewLocalEntity()
     654             : {
     655           6 :     entity_id_t id = m_NextLocalEntityId++;
     656             :     // TODO: check for overflow
     657           2 :     return id;
     658             : }
     659             : 
     660           2 : entity_id_t CComponentManager::AllocateNewEntity(entity_id_t preferredId)
     661             : {
     662             :     // TODO: ensure this ID hasn't been allocated before
     663             :     // (this might occur with broken map files)
     664             :     // Trying to actually add two entities with the same id will fail in AddEntitiy
     665           2 :     entity_id_t id = preferredId;
     666             : 
     667             :     // Ensure this ID won't be allocated again
     668           2 :     if (id >= m_NextEntityId)
     669           2 :         m_NextEntityId = id+1;
     670             :     // TODO: check for overflow
     671             : 
     672           2 :     return id;
     673             : }
     674             : 
     675         139 : bool CComponentManager::AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode& paramNode)
     676             : {
     677         139 :     IComponent* component = ConstructComponent(ent, cid);
     678         139 :     if (!component)
     679             :         return false;
     680             : 
     681         137 :     component->Init(paramNode);
     682         137 :     return true;
     683             : }
     684             : 
     685           3 : void CComponentManager::AddSystemComponents(bool skipScriptedComponents, bool skipAI)
     686             : {
     687           6 :     CParamNode noParam;
     688           3 :     AddComponent(m_SystemEntity, CID_TemplateManager, noParam);
     689           3 :     AddComponent(m_SystemEntity, CID_CinemaManager, noParam);
     690           3 :     AddComponent(m_SystemEntity, CID_CommandQueue, noParam);
     691           3 :     AddComponent(m_SystemEntity, CID_ObstructionManager, noParam);
     692           3 :     AddComponent(m_SystemEntity, CID_ParticleManager, noParam);
     693           3 :     AddComponent(m_SystemEntity, CID_Pathfinder, noParam);
     694           3 :     AddComponent(m_SystemEntity, CID_ProjectileManager, noParam);
     695           3 :     AddComponent(m_SystemEntity, CID_RangeManager, noParam);
     696           3 :     AddComponent(m_SystemEntity, CID_SoundManager, noParam);
     697           3 :     AddComponent(m_SystemEntity, CID_Terrain, noParam);
     698           3 :     AddComponent(m_SystemEntity, CID_TerritoryManager, noParam);
     699           3 :     AddComponent(m_SystemEntity, CID_UnitMotionManager, noParam);
     700           3 :     AddComponent(m_SystemEntity, CID_UnitRenderer, noParam);
     701           3 :     AddComponent(m_SystemEntity, CID_WaterManager, noParam);
     702             : 
     703             :     // Add scripted system components:
     704           3 :     if (!skipScriptedComponents)
     705             :     {
     706           0 :         for (uint32_t i = 0; i < m_ScriptedSystemComponents.size(); ++i)
     707           0 :             AddComponent(m_SystemEntity, m_ScriptedSystemComponents[i], noParam);
     708           0 :         if (!skipAI)
     709           0 :             AddComponent(m_SystemEntity, CID_AIManager, noParam);
     710             :     }
     711           3 : }
     712             : 
     713         153 : IComponent* CComponentManager::ConstructComponent(CEntityHandle ent, ComponentTypeId cid)
     714             : {
     715         306 :     ScriptRequest rq(m_ScriptInterface);
     716             : 
     717         306 :     std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.find(cid);
     718         306 :     if (it == m_ComponentTypesById.end())
     719             :     {
     720           2 :         LOGERROR("Invalid component id %d", cid);
     721           1 :         return NULL;
     722             :     }
     723             : 
     724         152 :     const ComponentType& ct = it->second;
     725             : 
     726         304 :     ENSURE((size_t)ct.iid < m_ComponentsByInterface.size());
     727             : 
     728         152 :     std::unordered_map<entity_id_t, IComponent*>& emap1 = m_ComponentsByInterface[ct.iid];
     729         608 :     if (emap1.find(ent.GetId()) != emap1.end())
     730             :     {
     731           2 :         LOGERROR("Multiple components for interface %d", ct.iid);
     732           1 :         return NULL;
     733             :     }
     734             : 
     735         151 :     std::map<entity_id_t, IComponent*>& emap2 = m_ComponentsByTypeId[cid];
     736             : 
     737             :     // If this is a scripted component, construct the appropriate JS object first
     738         453 :     JS::RootedValue obj(rq.cx);
     739         151 :     if (ct.type == CT_Script)
     740             :     {
     741         245 :         m_ScriptInterface.CallConstructor(*ct.ctor, JS::HandleValueArray::empty(), &obj);
     742          98 :         if (obj.isNull())
     743             :         {
     744           0 :             LOGERROR("Script component constructor failed");
     745           0 :             return NULL;
     746             :         }
     747             :     }
     748             : 
     749             :     // Construct the new component
     750             :     // NB: The unit motion manager relies on components not moving in memory once constructed.
     751         302 :     IComponent* component = ct.alloc(m_ScriptInterface, obj);
     752         151 :     ENSURE(component);
     753             : 
     754         151 :     component->SetEntityHandle(ent);
     755         151 :     component->SetSimContext(m_SimContext);
     756             : 
     757             :     // Store a reference to the new component
     758         453 :     emap1.insert(std::make_pair(ent.GetId(), component));
     759         453 :     emap2.insert(std::make_pair(ent.GetId(), component));
     760             :     // TODO: We need to more careful about this - if an entity is constructed by a component
     761             :     // while we're iterating over all components, this will invalidate the iterators and everything
     762             :     // will break.
     763             :     // We probably need some kind of delayed addition, so they get pushed onto a queue and then
     764             :     // inserted into the world later on. (Be careful about immediation deletion in that case, too.)
     765             : 
     766         151 :     SEntityComponentCache* cache = ent.GetComponentCache();
     767         151 :     ENSURE(cache != NULL && ct.iid < (int)cache->numInterfaces && cache->interfaces[ct.iid] == NULL);
     768         151 :     cache->interfaces[ct.iid] = component;
     769             : 
     770         151 :     return component;
     771             : }
     772             : 
     773          19 : void CComponentManager::AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent& component)
     774             : {
     775             :     // Just add it into the by-interface map, not the by-component-type map,
     776             :     // so it won't be considered for messages or deletion etc
     777             : 
     778          19 :     std::unordered_map<entity_id_t, IComponent*>& emap1 = m_ComponentsByInterface.at(iid);
     779          76 :     if (emap1.find(ent.GetId()) != emap1.end())
     780           0 :         debug_warn(L"Multiple components for interface");
     781          57 :     emap1.insert(std::make_pair(ent.GetId(), &component));
     782             : 
     783          19 :     SEntityComponentCache* cache = ent.GetComponentCache();
     784          19 :     ENSURE(cache != NULL && iid < (int)cache->numInterfaces && cache->interfaces[iid] == NULL);
     785          19 :     cache->interfaces[iid] = &component;
     786          19 : }
     787             : 
     788         106 : CEntityHandle CComponentManager::AllocateEntityHandle(entity_id_t ent)
     789             : {
     790           0 :     ENSURE(!EntityExists(ent));
     791             : 
     792             :     // Interface IDs start at 1, and SEntityComponentCache is defined with a 1-sized array,
     793             :     // so we need space for an extra m_InterfaceIdsByName.size() items
     794         212 :     SEntityComponentCache* cache = (SEntityComponentCache*)calloc(1,
     795         106 :         sizeof(SEntityComponentCache) + sizeof(IComponent*) * m_InterfaceIdsByName.size());
     796         106 :     ENSURE(cache != NULL);
     797         106 :     cache->numInterfaces = m_InterfaceIdsByName.size() + 1;
     798             : 
     799         106 :     m_ComponentCaches[ent] = cache;
     800             : 
     801         106 :     return CEntityHandle(ent, cache);
     802             : }
     803             : 
     804          50 : CEntityHandle CComponentManager::LookupEntityHandle(entity_id_t ent, bool allowCreate)
     805             : {
     806          50 :     std::unordered_map<entity_id_t, SEntityComponentCache*>::iterator it;
     807          50 :     it = m_ComponentCaches.find(ent);
     808         100 :     if (it == m_ComponentCaches.end())
     809             :     {
     810          27 :         if (allowCreate)
     811          27 :             return AllocateEntityHandle(ent);
     812             :         else
     813           0 :             return CEntityHandle(ent, NULL);
     814             :     }
     815             :     else
     816          46 :         return CEntityHandle(ent, it->second);
     817             : }
     818             : 
     819          26 : void CComponentManager::InitSystemEntity()
     820             : {
     821          52 :     ENSURE(m_SystemEntity.GetId() == INVALID_ENTITY);
     822          26 :     m_SystemEntity = AllocateEntityHandle(SYSTEM_ENTITY);
     823          52 :     m_SimContext.SetSystemEntity(m_SystemEntity);
     824          26 : }
     825             : 
     826          10 : entity_id_t CComponentManager::AddEntity(const std::wstring& templateName, entity_id_t ent)
     827             : {
     828          10 :     ICmpTemplateManager *cmpTemplateManager = static_cast<ICmpTemplateManager*> (QueryInterface(SYSTEM_ENTITY, IID_TemplateManager));
     829          10 :     if (!cmpTemplateManager)
     830             :     {
     831           0 :         debug_warn(L"No ICmpTemplateManager loaded");
     832           0 :         return INVALID_ENTITY;
     833             :     }
     834             : 
     835          10 :     const CParamNode* tmpl = cmpTemplateManager->LoadTemplate(ent, utf8_from_wstring(templateName));
     836          10 :     if (!tmpl)
     837             :         return INVALID_ENTITY; // LoadTemplate will have reported the error
     838             : 
     839             :     // This also ensures that ent does not exist
     840           8 :     CEntityHandle handle = AllocateEntityHandle(ent);
     841             : 
     842             :     // Construct a component for each child of the root element
     843           8 :     const CParamNode::ChildrenMap& tmplChilds = tmpl->GetChildren();
     844          32 :     for (CParamNode::ChildrenMap::const_iterator it = tmplChilds.begin(); it != tmplChilds.end(); ++it)
     845             :     {
     846             :         // Ignore attributes on the root element
     847          32 :         if (it->first.length() && it->first[0] == '@')
     848             :             continue;
     849             : 
     850          30 :         CComponentManager::ComponentTypeId cid = LookupCID(it->first);
     851          15 :         if (cid == CID__Invalid)
     852             :         {
     853           0 :             LOGERROR("Unrecognized component type name '%s' in entity template '%s'", it->first, utf8_from_wstring(templateName));
     854           0 :             return INVALID_ENTITY;
     855             :         }
     856             : 
     857          30 :         if (!AddComponent(handle, cid, it->second))
     858             :         {
     859           0 :             LOGERROR("Failed to construct component type name '%s' in entity template '%s'", it->first, utf8_from_wstring(templateName));
     860           0 :             return INVALID_ENTITY;
     861             :         }
     862             :         // TODO: maybe we should delete already-constructed components if one of them fails?
     863             :     }
     864             : 
     865           8 :     CMessageCreate msg(ent);
     866           8 :     PostMessage(ent, msg);
     867             : 
     868           8 :     return ent;
     869             : }
     870             : 
     871           0 : bool CComponentManager::EntityExists(entity_id_t ent) const
     872             : {
     873         325 :     return m_ComponentCaches.find(ent) != m_ComponentCaches.end();
     874             : }
     875             : 
     876             : 
     877           7 : void CComponentManager::DestroyComponentsSoon(entity_id_t ent)
     878             : {
     879           7 :     m_DestructionQueue.push_back(ent);
     880           7 : }
     881             : 
     882           7 : void CComponentManager::FlushDestroyedComponents()
     883             : {
     884          14 :     PROFILE2("Flush Destroyed Components");
     885          26 :     while (!m_DestructionQueue.empty())
     886             :     {
     887             :         // Make a copy of the destruction queue, so that the iterators won't be invalidated if the
     888             :         // CMessageDestroy handlers try to destroy more entities themselves
     889          18 :         std::vector<entity_id_t> queue;
     890           6 :         queue.swap(m_DestructionQueue);
     891             : 
     892          19 :         for (std::vector<entity_id_t>::iterator it = queue.begin(); it != queue.end(); ++it)
     893             :         {
     894           7 :             entity_id_t ent = *it;
     895             : 
     896             :             // Do nothing if invalid, destroyed, etc.
     897           7 :             if (!EntityExists(ent))
     898           3 :                 continue;
     899             : 
     900           4 :             CEntityHandle handle = LookupEntityHandle(ent);
     901             : 
     902           8 :             CMessageDestroy msg(ent);
     903           4 :             PostMessage(ent, msg);
     904             : 
     905             :             // Flatten all the dynamic subscriptions to ensure there are no dangling
     906             :             // references in the 'removed' lists to components we're going to delete
     907             :             // Some components may have dynamically unsubscribed following the Destroy message
     908           4 :             FlattenDynamicSubscriptions();
     909             : 
     910             :             // Destroy the components, and remove from m_ComponentsByTypeId:
     911           8 :             std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::iterator iit = m_ComponentsByTypeId.begin();
     912          43 :             for (; iit != m_ComponentsByTypeId.end(); ++iit)
     913             :             {
     914          70 :                 std::map<entity_id_t, IComponent*>::iterator eit = iit->second.find(ent);
     915         105 :                 if (eit != iit->second.end())
     916             :                 {
     917          14 :                     eit->second->Deinit();
     918          14 :                     RemoveComponentDynamicSubscriptions(eit->second);
     919          14 :                     m_ComponentTypesById[iit->first].dealloc(eit->second);
     920          14 :                     iit->second.erase(ent);
     921          21 :                     handle.GetComponentCache()->interfaces[m_ComponentTypesById[iit->first].iid] = NULL;
     922             :                 }
     923             :             }
     924             : 
     925           4 :             free(handle.GetComponentCache());
     926           4 :             m_ComponentCaches.erase(ent);
     927             : 
     928             :             // Remove from m_ComponentsByInterface
     929           8 :             std::vector<std::unordered_map<entity_id_t, IComponent*> >::iterator ifcit = m_ComponentsByInterface.begin();
     930         612 :             for (; ifcit != m_ComponentsByInterface.end(); ++ifcit)
     931             :             {
     932         200 :                 ifcit->erase(ent);
     933             :             }
     934             :         }
     935             :     }
     936           7 : }
     937             : 
     938         293 : IComponent* CComponentManager::QueryInterface(entity_id_t ent, InterfaceId iid) const
     939             : {
     940         586 :     if ((size_t)iid >= m_ComponentsByInterface.size())
     941             :     {
     942             :         // Invalid iid
     943             :         return NULL;
     944             :     }
     945             : 
     946         586 :     std::unordered_map<entity_id_t, IComponent*>::const_iterator eit = m_ComponentsByInterface[iid].find(ent);
     947         586 :     if (eit == m_ComponentsByInterface[iid].end())
     948             :     {
     949             :         // This entity doesn't implement this interface
     950             :         return NULL;
     951             :     }
     952             : 
     953         522 :     return eit->second;
     954             : }
     955             : 
     956           0 : CComponentManager::InterfaceList CComponentManager::GetEntitiesWithInterface(InterfaceId iid) const
     957             : {
     958           0 :     std::vector<std::pair<entity_id_t, IComponent*> > ret;
     959             : 
     960           0 :     if ((size_t)iid >= m_ComponentsByInterface.size())
     961             :     {
     962             :         // Invalid iid
     963             :         return ret;
     964             :     }
     965             : 
     966           0 :     ret.reserve(m_ComponentsByInterface[iid].size());
     967             : 
     968           0 :     std::unordered_map<entity_id_t, IComponent*>::const_iterator it = m_ComponentsByInterface[iid].begin();
     969           0 :     for (; it != m_ComponentsByInterface[iid].end(); ++it)
     970           0 :         ret.push_back(*it);
     971             : 
     972           0 :     std::sort(ret.begin(), ret.end()); // lexicographic pair comparison means this'll sort by entity ID
     973             : 
     974             :     return ret;
     975             : }
     976             : 
     977             : static CComponentManager::InterfaceListUnordered g_EmptyEntityMap;
     978           0 : const CComponentManager::InterfaceListUnordered& CComponentManager::GetEntitiesWithInterfaceUnordered(InterfaceId iid) const
     979             : {
     980           0 :     if ((size_t)iid >= m_ComponentsByInterface.size())
     981             :     {
     982             :         // Invalid iid
     983             :         return g_EmptyEntityMap;
     984             :     }
     985             : 
     986           0 :     return m_ComponentsByInterface[iid];
     987             : }
     988             : 
     989          54 : void CComponentManager::PostMessage(entity_id_t ent, const CMessage& msg)
     990             : {
     991         108 :     PROFILE2_IFSPIKE("Post Message", 0.0005);
     992          54 :     PROFILE2_ATTR("%s", msg.GetScriptHandlerName());
     993             :     // Send the message to components of ent, that subscribed locally to this message
     994          54 :     std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
     995          54 :     it = m_LocalMessageSubscriptions.find(msg.GetType());
     996         108 :     if (it != m_LocalMessageSubscriptions.end())
     997             :     {
     998         156 :         std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
     999         354 :         for (; ctit != it->second.end(); ++ctit)
    1000             :         {
    1001             :             // Find the component instances of this type (if any)
    1002         292 :             std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
    1003         292 :             if (emap == m_ComponentsByTypeId.end())
    1004             :                 continue;
    1005             : 
    1006             :             // Send the message to all of them
    1007          24 :             std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.find(ent);
    1008          36 :             if (eit != emap->second.end())
    1009          16 :                 eit->second->HandleMessage(msg, false);
    1010             :         }
    1011             :     }
    1012             : 
    1013          54 :     SendGlobalMessage(ent, msg);
    1014          54 : }
    1015             : 
    1016          10 : void CComponentManager::BroadcastMessage(const CMessage& msg)
    1017             : {
    1018             :     // Send the message to components of all entities that subscribed locally to this message
    1019          10 :     std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
    1020          10 :     it = m_LocalMessageSubscriptions.find(msg.GetType());
    1021          20 :     if (it != m_LocalMessageSubscriptions.end())
    1022             :     {
    1023          30 :         std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
    1024         110 :         for (; ctit != it->second.end(); ++ctit)
    1025             :         {
    1026             :             // Find the component instances of this type (if any)
    1027         140 :             std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
    1028         140 :             if (emap == m_ComponentsByTypeId.end())
    1029             :                 continue;
    1030             : 
    1031             :             // Send the message to all of them
    1032          42 :             std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
    1033          61 :             for (; eit != emap->second.end(); ++eit)
    1034          38 :                 eit->second->HandleMessage(msg, false);
    1035             :         }
    1036             :     }
    1037             : 
    1038          10 :     SendGlobalMessage(INVALID_ENTITY, msg);
    1039          10 : }
    1040             : 
    1041          64 : void CComponentManager::SendGlobalMessage(entity_id_t ent, const CMessage& msg)
    1042             : {
    1043         128 :     PROFILE2_IFSPIKE("SendGlobalMessage", 0.001);
    1044          64 :     PROFILE2_ATTR("%s", msg.GetScriptHandlerName());
    1045             :     // (Common functionality for PostMessage and BroadcastMessage)
    1046             : 
    1047             :     // Send the message to components of all entities that subscribed globally to this message
    1048          64 :     std::map<MessageTypeId, std::vector<ComponentTypeId> >::const_iterator it;
    1049          64 :     it = m_GlobalMessageSubscriptions.find(msg.GetType());
    1050         128 :     if (it != m_GlobalMessageSubscriptions.end())
    1051             :     {
    1052          90 :         std::vector<ComponentTypeId>::const_iterator ctit = it->second.begin();
    1053         167 :         for (; ctit != it->second.end(); ++ctit)
    1054             :         {
    1055             :             // Special case: Messages for local entities shouldn't be sent to script
    1056             :             // components that subscribed globally, so that we don't have to worry about
    1057             :             // them accidentally picking up non-network-synchronised data.
    1058          47 :             if (ENTITY_IS_LOCAL(ent))
    1059             :             {
    1060           2 :                 std::map<ComponentTypeId, ComponentType>::const_iterator cit = m_ComponentTypesById.find(*ctit);
    1061           2 :                 if (cit != m_ComponentTypesById.end() && cit->second.type == CT_Script)
    1062             :                     continue;
    1063             :             }
    1064             : 
    1065             :             // Find the component instances of this type (if any)
    1066          94 :             std::map<ComponentTypeId, std::map<entity_id_t, IComponent*> >::const_iterator emap = m_ComponentsByTypeId.find(*ctit);
    1067          94 :             if (emap == m_ComponentsByTypeId.end())
    1068             :                 continue;
    1069             : 
    1070             :             // Send the message to all of them
    1071          45 :             std::map<entity_id_t, IComponent*>::const_iterator eit = emap->second.begin();
    1072          60 :             for (; eit != emap->second.end(); ++eit)
    1073          30 :                 eit->second->HandleMessage(msg, true);
    1074             :         }
    1075             :     }
    1076             : 
    1077             :     // Send the message to component instances that dynamically subscribed to this message
    1078          64 :     std::map<MessageTypeId, CDynamicSubscription>::iterator dit = m_DynamicMessageSubscriptionsNonsync.find(msg.GetType());
    1079         128 :     if (dit != m_DynamicMessageSubscriptionsNonsync.end())
    1080             :     {
    1081           0 :         dit->second.Flatten();
    1082           0 :         const std::vector<IComponent*>& dynamic = dit->second.GetComponents();
    1083           0 :         for (size_t i = 0; i < dynamic.size(); i++)
    1084           0 :             dynamic[i]->HandleMessage(msg, false);
    1085             :     }
    1086          64 : }
    1087             : 
    1088          11 : std::string CComponentManager::GenerateSchema() const
    1089             : {
    1090          11 :     std::string schema =
    1091             :         "<grammar xmlns='http://relaxng.org/ns/structure/1.0' xmlns:a='http://ns.wildfiregames.com/entity' datatypeLibrary='http://www.w3.org/2001/XMLSchema-datatypes'>"
    1092             :             "<define name='decimal'>"
    1093             :                 "<data type='decimal'/>"
    1094             :             "</define>"
    1095             :             "<define name='nonNegativeDecimal'>"
    1096             :                 "<data type='decimal'><param name='minInclusive'>0</param></data>"
    1097             :             "</define>"
    1098             :             "<define name='positiveDecimal'>"
    1099             :                 "<data type='decimal'><param name='minExclusive'>0</param></data>"
    1100             :             "</define>"
    1101             :             "<define name='anything'>"
    1102             :                 "<zeroOrMore>"
    1103             :                       "<choice>"
    1104             :                             "<attribute><anyName/></attribute>"
    1105             :                             "<text/>"
    1106             :                             "<element>"
    1107             :                                 "<anyName/>"
    1108             :                                 "<ref name='anything'/>"
    1109             :                             "</element>"
    1110             :                       "</choice>"
    1111             :                 "</zeroOrMore>"
    1112          22 :             "</define>";
    1113             : 
    1114          11 :     std::map<InterfaceId, std::vector<std::string> > interfaceComponentTypes;
    1115             : 
    1116          22 :     std::vector<std::string> componentTypes;
    1117             : 
    1118         639 :     for (std::map<ComponentTypeId, ComponentType>::const_iterator it = m_ComponentTypesById.begin(); it != m_ComponentTypesById.end(); ++it)
    1119             :     {
    1120         617 :         schema +=
    1121        1234 :             "<define name='component." + it->second.name + "'>"
    1122        1851 :                 "<element name='" + it->second.name + "'>"
    1123        2468 :                     "<interleave>" + it->second.schema + "</interleave>"
    1124             :                 "</element>"
    1125         617 :             "</define>";
    1126             : 
    1127        1234 :         interfaceComponentTypes[it->second.iid].push_back(it->second.name);
    1128        1234 :         componentTypes.push_back(it->second.name);
    1129             :     }
    1130             : 
    1131             :     // Declare the implementation of each interface, for documentation
    1132        1102 :     for (std::map<std::string, InterfaceId>::const_iterator it = m_InterfaceIdsByName.begin(); it != m_InterfaceIdsByName.end(); ++it)
    1133             :     {
    1134        1620 :         schema += "<define name='interface." + it->first + "'><choice>";
    1135        1080 :         std::vector<std::string>& cts = interfaceComponentTypes[it->second];
    1136        2314 :         for (size_t i = 0; i < cts.size(); ++i)
    1137        1851 :             schema += "<ref name='component." + cts[i] + "'/>";
    1138         540 :         schema += "</choice></define>";
    1139             :     }
    1140             : 
    1141             :     // List all the component types, in alphabetical order (to match the reordering performed by CParamNode).
    1142             :     // (We do it this way, rather than <interleave>ing all the interface definitions (which would additionally perform
    1143             :     // a check that we don't use multiple component types of the same interface in one file), because libxml2 gives
    1144             :     // useless error messages in the latter case; this way lets it report the real error.)
    1145          33 :     std::sort(componentTypes.begin(), componentTypes.end());
    1146          11 :     schema +=
    1147             :         "<start>"
    1148             :             "<element name='Entity'>"
    1149          11 :                 "<optional><attribute name='parent'/></optional>";
    1150        1884 :     for (std::vector<std::string>::const_iterator it = componentTypes.begin(); it != componentTypes.end(); ++it)
    1151        1234 :         schema += "<optional><ref name='component." + *it + "'/></optional>";
    1152          11 :     schema +=
    1153             :         "</element>"
    1154          11 :     "</start>";
    1155             : 
    1156          11 :     schema += "</grammar>";
    1157             : 
    1158          22 :     return schema;
    1159           0 : }

Generated by: LCOV version 1.13