Index: source/simulation2/components/CCmpTemplateManager.h =================================================================== --- source/simulation2/components/CCmpTemplateManager.h +++ source/simulation2/components/CCmpTemplateManager.h @@ -18,7 +18,6 @@ #ifndef INCLUDED_CCMPTEMPLATEMANAGER #define INCLUDED_CCMPTEMPLATEMANAGER -#include "simulation2/system/Component.h" #include "ICmpTemplateManager.h" #include "ps/TemplateLoader.h" @@ -34,7 +33,10 @@ public: static void ClassInit(CComponentManager& componentManager); - DEFAULT_COMPONENT_ALLOCATOR(TemplateManager) + int GetComponentTypeId() const override + { + return CID_TemplateManager; + } static std::string GetSchema(); Index: source/simulation2/components/CCmpTemplateManager.cpp =================================================================== --- source/simulation2/components/CCmpTemplateManager.cpp +++ source/simulation2/components/CCmpTemplateManager.cpp @@ -21,11 +21,12 @@ #include "simulation2/MessageTypes.h" #include "simulation2/serialization/SerializedTypes.h" +#include "simulation2/system/Component.h" #include "lib/utf8.h" #include "ps/CLogger.h" -REGISTER_COMPONENT_TYPE(TemplateManager) +REGISTER_STATIC_COMPONENT_TYPE(TemplateManager) void CCmpTemplateManager::ClassInit(CComponentManager& componentManager) { Index: source/simulation2/system/Component.h =================================================================== --- source/simulation2/system/Component.h +++ source/simulation2/system/Component.h @@ -36,6 +36,13 @@ CCmp##cname::ClassInit(mgr); \ } +#define REGISTER_STATIC_COMPONENT_TYPE(cname) \ + void RegisterComponentType_##cname(CComponentManager& mgr) \ + { \ + IComponent::RegisterComponentType(mgr, CCmp##cname::GetInterfaceId(), CID_##cname, nullptr, [](auto*){}, #cname, CCmp##cname::GetSchema()); \ + CCmp##cname::ClassInit(mgr); \ + } + #define DEFAULT_COMPONENT_ALLOCATOR(cname) \ static IComponent* Allocate(const ScriptInterface&, JS::HandleValue) { return new CCmp##cname(); } \ static void Deallocate(IComponent* cmp) { delete static_cast (cmp); } \ Index: source/simulation2/system/ComponentManager.h =================================================================== --- source/simulation2/system/ComponentManager.h +++ source/simulation2/system/ComponentManager.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2022 Wildfire Games. +/* Copyright (C) 2023 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -27,6 +27,7 @@ #include #include +#include #include #include @@ -73,6 +74,8 @@ std::unique_ptr ctor; // only valid if type == CT_Script }; + struct SystemComponents; + public: CComponentManager(CSimContext&, std::shared_ptr cx, bool skipScriptFunctions = false); ~CComponentManager(); @@ -179,13 +182,20 @@ entity_id_t AllocateNewEntity(entity_id_t preferredId); /** - * Constructs a component of type 'cid', initialised with data 'paramNode', - * and attaches it to entity 'ent'. + * Constructs a component of type 'cid', attaches it to entity 'ent' and + * initializes it with data 'paramNode'. * * @return true on success; false on failure, and logs an error message */ bool AddComponent(CEntityHandle ent, ComponentTypeId cid, const CParamNode& paramNode); + /** + * Attaches a component to the entity 'ent' and initializes it with + * `paramNode`. + */ + void AddConstructedComponent(const CEntityHandle ent, IComponent& component, + const CParamNode& paramNode); + /** * Add all system components to the system entity (skip the scripted components or the AI components on demand) */ @@ -206,6 +216,11 @@ */ IComponent* ConstructComponent(CEntityHandle ent, ComponentTypeId cid); + /** + * Attaches a component to the entity 'ent'. + */ + void AttachComponent(const CEntityHandle ent, IComponent* component, const ComponentTypeId cid); + /** * Constructs an entity based on the given template, and adds it the world with * entity ID @p ent. There should not be any existing components with that entity ID. @@ -306,6 +321,8 @@ ComponentTypeId m_CurrentComponent; // used when loading component types bool m_CurrentlyHotloading; + std::unique_ptr m_SystemComponents; + // TODO: some of these should be vectors std::map m_ComponentTypesById; std::vector m_ScriptedSystemComponents; Index: source/simulation2/system/ComponentManager.cpp =================================================================== --- source/simulation2/system/ComponentManager.cpp +++ source/simulation2/system/ComponentManager.cpp @@ -32,6 +32,7 @@ #include "simulation2/system/IComponent.h" #include "simulation2/system/ParamNode.h" #include "simulation2/system/SimContext.h" +#include "simulation2/system/SystemComponents.h" #include @@ -495,6 +496,8 @@ } } + m_SystemComponents.reset(); + std::vector >::iterator ifcit = m_ComponentsByInterface.begin(); for (; ifcit != m_ComponentsByInterface.end(); ++ifcit) ifcit->clear(); @@ -687,10 +690,21 @@ return true; } +void CComponentManager::AddConstructedComponent(CEntityHandle ent, IComponent& component, const CParamNode& paramNode) +{ + AttachComponent(ent, &component, component.GetComponentTypeId()); + + component.Init(paramNode); +} + void CComponentManager::AddSystemComponents(bool skipScriptedComponents, bool skipAI) { + ENSURE(!m_SystemComponents); + m_SystemComponents = std::make_unique(); + CParamNode noParam; - AddComponent(m_SystemEntity, CID_TemplateManager, noParam); + AddConstructedComponent(m_SystemEntity, m_SystemComponents->templateManager, noParam); + AddComponent(m_SystemEntity, CID_CinemaManager, noParam); AddComponent(m_SystemEntity, CID_CommandQueue, noParam); AddComponent(m_SystemEntity, CID_ObstructionManager, noParam); @@ -737,8 +751,6 @@ return NULL; } - std::map& emap2 = m_ComponentsByTypeId[cid]; - // If this is a scripted component, construct the appropriate JS object first JS::RootedValue obj(rq.cx); if (ct.type == CT_Script) @@ -754,11 +766,24 @@ // Construct the new component // NB: The unit motion manager relies on components not moving in memory once constructed. IComponent* component = ct.alloc(m_ScriptInterface, obj); + + AttachComponent(ent, component, cid); + + return component; +} + +void CComponentManager::AttachComponent(const CEntityHandle ent, IComponent* component, + const ComponentTypeId cid) +{ ENSURE(component); component->SetEntityHandle(ent); component->SetSimContext(m_SimContext); + const ComponentType& ct = m_ComponentTypesById.at(cid); + std::unordered_map& emap1 = m_ComponentsByInterface[ct.iid]; + std::map& emap2 = m_ComponentsByTypeId[cid]; + // Store a reference to the new component emap1.insert(std::make_pair(ent.GetId(), component)); emap2.insert(std::make_pair(ent.GetId(), component)); @@ -771,8 +796,6 @@ SEntityComponentCache* cache = ent.GetComponentCache(); ENSURE(cache != NULL && ct.iid < (int)cache->numInterfaces && cache->interfaces[ct.iid] == NULL); cache->interfaces[ct.iid] = component; - - return component; } void CComponentManager::AddMockComponent(CEntityHandle ent, InterfaceId iid, IComponent& component) Index: source/simulation2/system/ComponentManagerSerialization.cpp =================================================================== --- source/simulation2/system/ComponentManagerSerialization.cpp +++ source/simulation2/system/ComponentManagerSerialization.cpp @@ -27,6 +27,7 @@ #include "simulation2/serialization/HashSerializer.h" #include "simulation2/serialization/StdSerializer.h" #include "simulation2/serialization/StdDeserializer.h" +#include "simulation2/system/SystemComponents.h" #include "simulation2/components/ICmpTemplateManager.h" @@ -315,6 +316,7 @@ ICmpTemplateManager* templateManager = NULL; CParamNode noParam; + m_SystemComponents = std::make_unique(); for (size_t i = 0; i < numSystemComponentTypes; ++i) { std::string ctname; @@ -327,9 +329,15 @@ return false; } - IComponent* component = ConstructComponent(m_SystemEntity, ctid); - if (!component) - return false; + IComponent* component{&m_SystemComponents->templateManager}; + if (component) + AttachComponent(m_SystemEntity, component, ctid); + else + { + component = ConstructComponent(m_SystemEntity, ctid); + if (!component) + return false; + } component->Deserialize(noParam, deserializer); Index: source/simulation2/system/SystemComponents.h =================================================================== --- /dev/null +++ source/simulation2/system/SystemComponents.h @@ -0,0 +1,40 @@ +/* Copyright (C) 2023 Wildfire Games. + * This file is part of 0 A.D. + * + * 0 A.D. is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * 0 A.D. is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with 0 A.D. If not, see . + */ + +#ifndef INCLUDED_SYSTEMCOMPONENTS +#define INCLUDED_SYSTEMCOMPONENTS + +#include "simulation2/components/CCmpTemplateManager.h" + +struct CComponentManager::SystemComponents +{ + CCmpTemplateManager templateManager; + + IComponent* GetComponentByCid(const ComponentTypeId cid) + { + switch(cid) + { + case CID_TemplateManager: + return &templateManager; + + default: + return nullptr; + } + } +}; + +#endif // INCLUDED_SYSTEMCOMPONENTS Index: source/simulation2/tests/test_CmpTemplateManager.h =================================================================== --- source/simulation2/tests/test_CmpTemplateManager.h +++ source/simulation2/tests/test_CmpTemplateManager.h @@ -19,7 +19,7 @@ #include "simulation2/system/ComponentManager.h" -#include "simulation2/components/ICmpTemplateManager.h" +#include "simulation2/components/CCmpTemplateManager.h" #include "simulation2/components/ICmpTest.h" #include "simulation2/MessageTypes.h" #include "simulation2/system/ParamNode.h" @@ -54,6 +54,7 @@ void test_LoadTemplate() { CSimContext context; + CCmpTemplateManager templateMan; CComponentManager man(context, g_ScriptContext); man.LoadComponentTypes(); @@ -61,7 +62,7 @@ CEntityHandle hnd1 = man.LookupEntityHandle(ent1, true); CParamNode noParam; - TS_ASSERT(man.AddComponent(hnd1, CID_TemplateManager, noParam)); + man.AddConstructedComponent(hnd1, templateMan, noParam); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); @@ -88,6 +89,7 @@ void test_LoadTemplate_scriptcache() { CSimContext context; + CCmpTemplateManager templateMan; CComponentManager man(context, g_ScriptContext); man.LoadComponentTypes(); @@ -95,7 +97,7 @@ CEntityHandle hnd1 = man.LookupEntityHandle(ent1, true); CParamNode noParam; - TS_ASSERT(man.AddComponent(hnd1, CID_TemplateManager, noParam)); + man.AddConstructedComponent(hnd1, templateMan, noParam); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); @@ -148,6 +150,7 @@ void test_LoadTemplate_errors() { CSimContext context; + CCmpTemplateManager templateMan; CComponentManager man(context, g_ScriptContext); man.LoadComponentTypes(); @@ -155,7 +158,7 @@ CEntityHandle hnd1 = man.LookupEntityHandle(ent1, true); CParamNode noParam; - TS_ASSERT(man.AddComponent(hnd1, CID_TemplateManager, noParam)); + man.AddConstructedComponent(hnd1, templateMan, noParam); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); @@ -180,6 +183,7 @@ void test_LoadTemplate_multiple() { CSimContext context; + CCmpTemplateManager templateMan; CComponentManager man(context, g_ScriptContext); man.LoadComponentTypes(); @@ -187,7 +191,7 @@ CEntityHandle hnd1 = man.LookupEntityHandle(ent1, true); CParamNode noParam; - TS_ASSERT(man.AddComponent(hnd1, CID_TemplateManager, noParam)); + man.AddConstructedComponent(hnd1, templateMan, noParam); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(ent1, IID_TemplateManager)); TS_ASSERT(tempMan != NULL); Index: source/simulation2/tests/test_ComponentManager.h =================================================================== --- source/simulation2/tests/test_ComponentManager.h +++ source/simulation2/tests/test_ComponentManager.h @@ -24,7 +24,7 @@ #include "simulation2/system/SimContext.h" #include "simulation2/serialization/ISerializer.h" #include "simulation2/components/ICmpTest.h" -#include "simulation2/components/ICmpTemplateManager.h" +#include "simulation2/components/CCmpTemplateManager.h" #include "ps/CLogger.h" #include "ps/Filesystem.h" @@ -419,6 +419,7 @@ void test_script_AddEntity() { CSimContext context; + CCmpTemplateManager templateMan; CComponentManager man(context, g_ScriptContext); man.LoadComponentTypes(); TS_ASSERT(man.LoadScript(L"simulation/components/test-addentity.js")); @@ -430,7 +431,7 @@ CEntityHandle hnd1 = man.AllocateEntityHandle(ent1); CParamNode noParam; - TS_ASSERT(man.AddComponent(man.GetSystemEntity(), CID_TemplateManager, noParam)); + man.AddConstructedComponent(man.GetSystemEntity(), templateMan, noParam); TS_ASSERT(man.AddComponent(hnd1, man.LookupCID("TestScript1_AddEntity"), noParam)); @@ -452,6 +453,7 @@ void test_script_AddLocalEntity() { CSimContext context; + CCmpTemplateManager templateMan; CComponentManager man(context, g_ScriptContext); man.LoadComponentTypes(); TS_ASSERT(man.LoadScript(L"simulation/components/test-addentity.js")); @@ -463,7 +465,7 @@ CEntityHandle hnd1 = man.AllocateEntityHandle(ent1); CParamNode noParam; - TS_ASSERT(man.AddComponent(man.GetSystemEntity(), CID_TemplateManager, noParam)); + man.AddConstructedComponent(man.GetSystemEntity(), templateMan, noParam); TS_ASSERT(man.AddComponent(hnd1, man.LookupCID("TestScript1_AddLocalEntity"), noParam)); @@ -846,7 +848,7 @@ void test_script_serialization_template() { CSimContext context; - + CCmpTemplateManager templateMan; CComponentManager man(context, g_ScriptContext); man.LoadComponentTypes(); TS_ASSERT(man.LoadScript(L"simulation/components/test-serialize.js")); @@ -858,7 +860,7 @@ // The template manager takes care of reloading templates on deserialization, // so we need to use it here - TS_ASSERT(man.AddComponent(man.GetSystemEntity(), CID_TemplateManager, noParam)); + man.AddConstructedComponent(man.GetSystemEntity(), templateMan, noParam); ICmpTemplateManager* tempMan = static_cast (man.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager)); const CParamNode* testParam = tempMan->LoadTemplate(ent2, "template-serialize");