Index: source/graphics/ObjectManager.cpp =================================================================== --- source/graphics/ObjectManager.cpp +++ source/graphics/ObjectManager.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -30,6 +30,7 @@ #include "simulation2/Simulation2.h" #include "simulation2/components/ICmpTerrain.h" #include "simulation2/components/ICmpVisual.h" +#include "simulation2/system/ComponentDataHolder.h" bool CObjectManager::ObjectKey::operator< (const CObjectManager::ObjectKey& a) const { @@ -167,7 +168,7 @@ changed = true; } - const CSimulation2::InterfaceListUnordered& cmps = m_Simulation.GetEntitiesWithInterfaceUnordered(IID_Visual); + SComponentDataGenerator cmps = m_Simulation.GetEntitiesWithInterfaceUnordered(IID_Visual); // Reload actors that use a changed object for (std::pair>& actor : m_ActorDefs) @@ -181,8 +182,9 @@ // object with all correct variations, and we don't want to waste space storing it just for the // rare occurrence of hotloading, so we'll tell the component (which does preserve the information) // to do the reloading itself - for (CSimulation2::InterfaceListUnordered::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit) - static_cast(eit->second)->Hotload(actor.first); + while (IComponent* cmp = cmps.Next()) + static_cast(cmp)->Hotload(actor.first); + cmps.Reset(); } } @@ -203,9 +205,9 @@ m_QualityLevel = quality > 255 ? 255 : quality < 0 ? 0 : quality; // No need to reload entries or actors, but we do need to reload all units. - const CSimulation2::InterfaceListUnordered& cmps = m_Simulation.GetEntitiesWithInterfaceUnordered(IID_Visual); - for (CSimulation2::InterfaceListUnordered::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit) - static_cast(eit->second)->Hotload(); + SComponentDataGenerator cmps = m_Simulation.GetEntitiesWithInterfaceUnordered(IID_Visual); + while (IComponent* cmp = cmps.Next()) + static_cast(cmp)->Hotload(); // Trigger an interpolate call - needed because the game is generally paused & models disappear otherwise. m_Simulation.Interpolate(0.f, 0.f, 0.f); @@ -236,9 +238,9 @@ actor.second.outdated = true; // Reload visual actor components. - const CSimulation2::InterfaceListUnordered& cmps = m_Simulation.GetEntitiesWithInterfaceUnordered(IID_Visual); - for (CSimulation2::InterfaceListUnordered::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit) - static_cast(eit->second)->Hotload(); + SComponentDataGenerator cmps = m_Simulation.GetEntitiesWithInterfaceUnordered(IID_Visual); + while (IComponent* cmp = cmps.Next()) + static_cast(cmp)->Hotload(); // Trigger an interpolate call - needed because the game is generally paused & models disappear otherwise. m_Simulation.Interpolate(0.f, 0.f, 0.f); Index: source/simulation2/Simulation2.h =================================================================== --- source/simulation2/Simulation2.h +++ source/simulation2/Simulation2.h @@ -22,6 +22,7 @@ #include "simulation2/helpers/SimulationCommand.h" #include "simulation2/system/CmpPtr.h" #include "simulation2/system/Components.h" +#include "simulation2/system/ComponentDataHolder.h" #include #include @@ -221,7 +222,7 @@ * Returns a list of components implementing the given interface, and their * associated entities, as an unordered map. */ - const InterfaceListUnordered& GetEntitiesWithInterfaceUnordered(int iid); + SComponentDataGenerator GetEntitiesWithInterfaceUnordered(int iid); const CSimContext& GetSimContext() const; ScriptInterface& GetScriptInterface() const; Index: source/simulation2/Simulation2.cpp =================================================================== --- source/simulation2/Simulation2.cpp +++ source/simulation2/Simulation2.cpp @@ -713,7 +713,7 @@ return m->m_ComponentManager.GetEntitiesWithInterface(iid); } -const CSimulation2::InterfaceListUnordered& CSimulation2::GetEntitiesWithInterfaceUnordered(int iid) +SComponentDataGenerator CSimulation2::GetEntitiesWithInterfaceUnordered(int iid) { return m->m_ComponentManager.GetEntitiesWithInterfaceUnordered(iid); } Index: source/simulation2/helpers/Selection.h =================================================================== --- source/simulation2/helpers/Selection.h +++ source/simulation2/helpers/Selection.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2019 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -94,13 +94,13 @@ std::vector hitEnts; Filter filter; - const CSimulation2::InterfaceListUnordered& entities = simulation.GetEntitiesWithInterfaceUnordered(cid); - for (const std::pair& item : entities) + SComponentDataGenerator entities = simulation.GetEntitiesWithInterfaceUnordered(cid); + while (IComponent* cmp = entities.Next()) { - if (!filter(item.second)) + if (!filter(cmp)) continue; - if (CheckEntityInRect(item.second->GetEntityHandle(), camera, sx0, sy0, sx1, sy1, false)) - hitEnts.push_back(item.first); + if (CheckEntityInRect(cmp->GetEntityHandle(), camera, sx0, sy0, sx1, sy1, false)) + hitEnts.push_back(cmp->GetEntityId()); } return hitEnts; Index: source/simulation2/helpers/Selection.cpp =================================================================== --- source/simulation2/helpers/Selection.cpp +++ source/simulation2/helpers/Selection.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2020 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -158,11 +158,11 @@ } else // owner == INVALID_PLAYER; Used when selecting units in Atlas or other mods that allow all kinds of selectables to be selected. { - const CSimulation2::InterfaceListUnordered& selectableEnts = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); - for (CSimulation2::InterfaceListUnordered::const_iterator it = selectableEnts.begin(); it != selectableEnts.end(); ++it) + SComponentDataGenerator selectableEnts = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); + while (IComponent* cmp = selectableEnts.Next()) { - if (CheckEntityVisibleAndInRect(it->second->GetEntityHandle(), cmpRangeManager, camera, sx0, sy0, sx1, sy1, owner, allowEditorSelectables)) - hitEnts.push_back(it->first); + if (CheckEntityVisibleAndInRect(cmp->GetEntityHandle(), cmpRangeManager, camera, sx0, sy0, sx1, sy1, owner, allowEditorSelectables)) + hitEnts.push_back(cmp->GetEntityId()); } } @@ -202,14 +202,14 @@ std::vector hitEnts; - const CSimulation2::InterfaceListUnordered& ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); - for (CSimulation2::InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) + SComponentDataGenerator ents = simulation.GetEntitiesWithInterfaceUnordered(IID_Selectable); + while (IComponent* cmp = ents.Next()) { - entity_id_t ent = it->first; - CEntityHandle handle = it->second->GetEntityHandle(); + CEntityHandle handle = cmp->GetEntityHandle(); + entity_id_t ent = handle.GetId(); // Check if this entity is only selectable in Atlas - if (static_cast(it->second)->IsEditorOnly() && !allowEditorSelectables) + if (static_cast(cmp)->IsEditorOnly() && !allowEditorSelectables) continue; if (matchRank) Index: source/simulation2/scripting/ScriptComponent.h =================================================================== --- source/simulation2/scripting/ScriptComponent.h +++ source/simulation2/scripting/ScriptComponent.h @@ -82,20 +82,20 @@ #define REGISTER_COMPONENT_SCRIPT_WRAPPER(cname) \ void RegisterComponentType_##cname(CComponentManager& mgr) \ { \ - IComponent::RegisterComponentTypeScriptWrapper(mgr, CCmp##cname::GetInterfaceId(), CID_##cname, CCmp##cname::Allocate, CCmp##cname::Deallocate, #cname, CCmp##cname::GetSchema()); \ + IComponent::RegisterComponentTypeScriptWrapper(mgr, CCmp##cname::GetInterfaceId(), CID_##cname, CCmp##cname::Allocate, CCmp##cname::Deallocate, sizeof(CCmp##cname), alignof(CCmp##cname), #cname, CCmp##cname::GetSchema()); \ CCmp##cname::ClassInit(mgr); \ } #define DEFAULT_SCRIPT_WRAPPER(cname) \ static void ClassInit(CComponentManager& UNUSED(componentManager)) { } \ - static IComponent* Allocate(const ScriptInterface& scriptInterface, JS::HandleValue instance) \ + static IComponent* Allocate(void* slot, const ScriptInterface& scriptInterface, JS::HandleValue instance) \ { \ - return new CCmp##cname(scriptInterface, instance); \ + return new (slot) CCmp##cname(scriptInterface, instance); \ } \ static void Deallocate(IComponent* cmp) \ { \ - delete static_cast (cmp); \ + static_cast(cmp)->~CCmp##cname(); \ } \ CCmp##cname(const ScriptInterface& scriptInterface, JS::HandleValue instance) : m_Script(scriptInterface, instance) { } \ static std::string GetSchema() \ Index: source/simulation2/system/Component.h =================================================================== --- source/simulation2/system/Component.h +++ source/simulation2/system/Component.h @@ -32,13 +32,15 @@ #define REGISTER_COMPONENT_TYPE(cname) \ void RegisterComponentType_##cname(CComponentManager& mgr) \ { \ - IComponent::RegisterComponentType(mgr, CCmp##cname::GetInterfaceId(), CID_##cname, CCmp##cname::Allocate, CCmp##cname::Deallocate, #cname, CCmp##cname::GetSchema()); \ + IComponent::RegisterComponentType(mgr, CCmp##cname::GetInterfaceId(), CID_##cname, CCmp##cname::Allocate, CCmp##cname::Deallocate, sizeof(CCmp##cname), alignof(CCmp##cname), #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); } \ + static IComponent* Allocate(void* slot, const ScriptInterface&, JS::HandleValue) { return new (slot) CCmp##cname(); } \ + static void Deallocate(IComponent* cmp) { \ + static_cast(cmp)->~CCmp##cname(); /* deallocation is handled by ComponentDataHolder */\ + } \ int GetComponentTypeId() const override \ { \ return CID_##cname; \ Index: source/simulation2/system/ComponentDataHolder.h =================================================================== --- /dev/null +++ source/simulation2/system/ComponentDataHolder.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2022 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_COMPONENTDATAHOLDER +#define INCLUDED_COMPONENTDATAHOLDER +#include "simulation2/system/IComponent.h" +#include + + +// this is a pool allocator for a type with constant size +class CComponentDataHolder final +{ +private: + std::byte* m_Start; + std::byte* m_Back; + std::byte* m_End; + size_t m_Offset; + size_t m_PoolSize; + std::vector m_OpenSlots; +public: + CComponentDataHolder(size_t size, size_t align, uint32_t capacity = 10000); + ~CComponentDataHolder(); + void ResetState(); + std::byte* Allocate(); + void Deallocate(IComponent* ptr); + std::byte* Start() const; + size_t Offset() const; + std::byte* Back() const; +}; + +struct SComponentDataGenerator final +{ +private: + std::byte* m_Ptr; + CComponentDataHolder* m_Data; +public: + SComponentDataGenerator(CComponentDataHolder* data); + IComponent* Next(); + void Reset(); +}; +#endif // INCLUDED_COMPONENTDATAHOLDER Index: source/simulation2/system/ComponentDataHolder.cpp =================================================================== --- /dev/null +++ source/simulation2/system/ComponentDataHolder.cpp @@ -0,0 +1,92 @@ +/* Copyright (C) 2022 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 . + */ + +#include "precompiled.h" +#include "ComponentDataHolder.h" +#include + +size_t AlignRequirement(size_t size, size_t align) +{ + // https://stackoverflow.com/questions/61647725/custom-allocator-and-memory-align + return size % align == 0 ? size : size + (align - size % align); +} + +// this is a pool allocator for a type with constant size +CComponentDataHolder::CComponentDataHolder(size_t size, size_t align, uint32_t capacity) +{ + m_Offset = AlignRequirement(size, align); + m_OpenSlots.reserve(capacity / 10); + m_PoolSize = capacity * m_Offset; + m_Start = malloc(m_PoolSize); + m_Back = m_Start; + m_End = m_Start + m_PoolSize; +} +CComponentDataHolder::~CComponentDataHolder() +{ + free(m_Start); +} +void CComponentDataHolder::ResetState() +{ + m_Back = m_Start; + m_OpenSlots.clear(); +} +std::byte* CComponentDataHolder::Allocate() +{ + std::byte* output; + if(m_OpenSlots.empty()) + { + if(m_Back >= m_End) + { + // if space exceeded double it, better to give enough in the first place and avoid copy + // this is just for emergency use, would it be better to just let an overun segfault? + m_Start = realloc(m_Start, m_PoolSize *= 2 ); + m_End += m_PoolSize; + } + output = m_Back; + m_Back += m_Offset; + } + else + { + output = m_OpenSlots.back(); + m_OpenSlots.pop_back(); + } + return output; +} +void CComponentDataHolder::Deallocate(IComponent* ptr) +{ + ptr->SetInvalid(); + m_OpenSlots.push_back(ptr); +} +std::byte* CComponentDataHolder::Start() const { return m_Start; } +size_t CComponentDataHolder::Offset() const { return m_Offset; } +std::byte* CComponentDataHolder::Back() const { return m_Back; } + +// generator to iterate over the allocator pool, performs best under high strain due to density +explicit SComponentDataGenerator::SComponentDataGenerator(CComponentDataHolder* data) : m_Data(data) { Reset(); } +void SComponentDataGenerator::Reset() { m_Ptr = m_Data->Start(); } +IComponent* SComponentDataGenerator::Next() +{ + IComponent* output; + while(m_Ptr < m_Data->Back()) + { + output = static_cast(m_Ptr); + m_Ptr += m_Data->Offset(); + if(output->IsValid()) + return output; + } + return nullptr; +} Index: source/simulation2/system/ComponentManager.h =================================================================== --- source/simulation2/system/ComponentManager.h +++ source/simulation2/system/ComponentManager.h @@ -24,6 +24,7 @@ #include "simulation2/system/Components.h" #include "simulation2/system/Entity.h" #include "simulation2/system/IComponent.h" +#include "simulation2/system/ComponentDataHolder.h" #include #include @@ -68,10 +69,13 @@ InterfaceId iid; AllocFunc alloc; DeallocFunc dealloc; + size_t size; + size_t align; std::string name; std::string schema; // RelaxNG fragment std::unique_ptr ctor; // only valid if type == CT_Script }; + std::unordered_map m_ComponentData; public: CComponentManager(CSimContext&, std::shared_ptr cx, bool skipScriptFunctions = false); @@ -89,8 +93,8 @@ void RegisterMessageType(MessageTypeId mtid, const char* name); - void RegisterComponentType(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema); - void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, const char*, const std::string& schema); + void RegisterComponentType(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, size_t size, size_t align, const char*, const std::string& schema); + void RegisterComponentTypeScriptWrapper(InterfaceId, ComponentTypeId, AllocFunc, DeallocFunc, size_t size, size_t align, const char*, const std::string& schema); void MarkScriptedComponentForSystemEntity(CComponentManager::ComponentTypeId cid); @@ -233,7 +237,7 @@ using InterfaceListUnordered = std::unordered_map; InterfaceList GetEntitiesWithInterface(InterfaceId iid) const; - const InterfaceListUnordered& GetEntitiesWithInterfaceUnordered(InterfaceId iid) const; + SComponentDataGenerator GetEntitiesWithInterfaceUnordered(InterfaceId iid) const; /** * Send a message, targeted at a particular entity. The message will be received by any @@ -310,6 +314,7 @@ std::map m_ComponentTypesById; std::vector m_ScriptedSystemComponents; std::vector > m_ComponentsByInterface; // indexed by InterfaceId + std::vector m_ComponentDataByInterface; // indexed by InterfaceId std::map > m_ComponentsByTypeId; std::map > m_LocalMessageSubscriptions; std::map > m_GlobalMessageSubscriptions; Index: source/simulation2/system/ComponentManager.cpp =================================================================== --- source/simulation2/system/ComponentManager.cpp +++ source/simulation2/system/ComponentManager.cpp @@ -31,6 +31,7 @@ #include "simulation2/system/IComponent.h" #include "simulation2/system/ParamNode.h" #include "simulation2/system/SimContext.h" +#include "simulation2/system/ComponentDataHolder.h" /** * Used for script-only message types. @@ -109,7 +110,7 @@ m_ScriptInterface.SetGlobal("SYSTEM_ENTITY", (int)SYSTEM_ENTITY); m_ComponentsByInterface.resize(IID__LastNative); - + m_ComponentDataByInterface.resize(IID__LastNative); ResetState(); } @@ -256,12 +257,15 @@ iid, ctWrapper.alloc, ctWrapper.dealloc, + ctWrapper.size, + ctWrapper.align, cname, schema, std::make_unique(rq.cx, ctor) }; m_ComponentTypesById[cid] = std::move(ct); - + m_ComponentData.try_emplace(cid, ct.size, ct.align); + m_ComponentDataByInterface[iid] = &m_ComponentData.at(cid); m_CurrentComponent = cid; // needed by Subscribe // Find all the ctor prototype's On* methods, and subscribe to the appropriate messages: @@ -355,6 +359,7 @@ size_t id = m_InterfaceIdsByName.size() + 1; m_InterfaceIdsByName[name] = (InterfaceId)id; m_ComponentsByInterface.resize(id+1); // add one so we can index by InterfaceId + m_ComponentDataByInterface.resize(id+1); m_ScriptInterface.SetGlobal(("IID_" + name).c_str(), (int)id); } @@ -404,10 +409,10 @@ std::vector CComponentManager::Script_GetEntitiesWithInterface(int iid) { std::vector ret; - const InterfaceListUnordered& ents = GetEntitiesWithInterfaceUnordered(iid); - for (InterfaceListUnordered::const_iterator it = ents.begin(); it != ents.end(); ++it) - if (!ENTITY_IS_LOCAL(it->first)) - ret.push_back(it->first); + SComponentDataGenerator cmps = GetEntitiesWithInterfaceUnordered(iid); + while (IComponent* cmp = cmps.Next()) + if (!ENTITY_IS_LOCAL(cmp->GetEntityId())) + ret.push_back(cmp->GetEntityId()); std::sort(ret.begin(), ret.end()); return ret; } @@ -415,7 +420,7 @@ std::vector CComponentManager::Script_GetComponentsWithInterface(int iid) { std::vector ret; - InterfaceList ents = GetEntitiesWithInterface(iid); + const InterfaceList& ents = GetEntitiesWithInterface(iid); for (InterfaceList::const_iterator it = ents.begin(); it != ents.end(); ++it) ret.push_back(it->second); // TODO: maybe we should exclude local entities return ret; @@ -496,6 +501,10 @@ m_ComponentsByTypeId.clear(); + std::unordered_map::iterator it = m_ComponentData.begin(); + for (;it != m_ComponentData.end(); it++) + it->second.ResetState(); + // Delete all SEntityComponentCaches std::unordered_map::iterator ccit = m_ComponentCaches.begin(); for (; ccit != m_ComponentCaches.end(); ++ccit) @@ -516,19 +525,23 @@ } void CComponentManager::RegisterComponentType(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, - const char* name, const std::string& schema) + size_t size, size_t align, const char* name, const std::string& schema) { - ComponentType c{ CT_Native, iid, alloc, dealloc, name, schema, std::unique_ptr() }; + ComponentType c{ CT_Native, iid, alloc, dealloc, size, align, name, schema, std::unique_ptr() }; m_ComponentTypesById.insert(std::make_pair(cid, std::move(c))); m_ComponentTypeIdsByName[name] = cid; + m_ComponentData.try_emplace(cid, size, align); + m_ComponentDataByInterface[iid] = &m_ComponentData.at(cid); } void CComponentManager::RegisterComponentTypeScriptWrapper(InterfaceId iid, ComponentTypeId cid, AllocFunc alloc, - DeallocFunc dealloc, const char* name, const std::string& schema) + DeallocFunc dealloc, size_t size, size_t align, const char* name, const std::string& schema) { - ComponentType c{ CT_ScriptWrapper, iid, alloc, dealloc, name, schema, std::unique_ptr() }; + ComponentType c{ CT_ScriptWrapper, iid, alloc, dealloc, size, align, name, schema, std::unique_ptr() }; m_ComponentTypesById.insert(std::make_pair(cid, std::move(c))); m_ComponentTypeIdsByName[name] = cid; + m_ComponentData.try_emplace(cid, size, align); + m_ComponentDataByInterface[iid] = &m_ComponentData.at(cid); // TODO: merge with RegisterComponentType } @@ -748,7 +761,8 @@ // 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); + void* address = m_ComponentData.at(cid).Allocate(); + IComponent* component = ct.alloc(address, m_ScriptInterface, obj); ENSURE(component); component->SetEntityHandle(ent); @@ -917,6 +931,7 @@ eit->second->Deinit(); RemoveComponentDynamicSubscriptions(eit->second); m_ComponentTypesById[iit->first].dealloc(eit->second); + m_ComponentData.at(iit->first).Deallocate(eit->second); iit->second.erase(ent); handle.GetComponentCache()->interfaces[m_ComponentTypesById[iit->first].iid] = NULL; } @@ -965,25 +980,24 @@ ret.reserve(m_ComponentsByInterface[iid].size()); - std::unordered_map::const_iterator it = m_ComponentsByInterface[iid].begin(); - for (; it != m_ComponentsByInterface[iid].end(); ++it) - ret.push_back(*it); + SComponentDataGenerator gen = GetEntitiesWithInterfaceUnordered(iid); + while (IComponent* cmp = gen.Next()) + ret.push_back(std::pair(cmp->GetEntityId(), cmp)); std::sort(ret.begin(), ret.end()); // lexicographic pair comparison means this'll sort by entity ID return ret; } -static CComponentManager::InterfaceListUnordered g_EmptyEntityMap; -const CComponentManager::InterfaceListUnordered& CComponentManager::GetEntitiesWithInterfaceUnordered(InterfaceId iid) const +SComponentDataGenerator CComponentManager::GetEntitiesWithInterfaceUnordered(InterfaceId iid) const { if ((size_t)iid >= m_ComponentsByInterface.size()) { // Invalid iid - return g_EmptyEntityMap; + return nullptr; } - return m_ComponentsByInterface[iid]; + return SComponentDataGenerator(m_ComponentDataByInterface[iid]); } void CComponentManager::PostMessage(entity_id_t ent, const CMessage& msg) @@ -1024,17 +1038,12 @@ for (; ctit != it->second.end(); ++ctit) { // Find the component instances of this type (if any) - std::map >::const_iterator emap = m_ComponentsByTypeId.find(*ctit); - if (emap == m_ComponentsByTypeId.end()) - continue; - + SComponentDataGenerator gen = SComponentDataGenerator(&m_ComponentData.at(*ctit)); // Send the message to all of them - std::map::const_iterator eit = emap->second.begin(); - for (; eit != emap->second.end(); ++eit) - eit->second->HandleMessage(msg, false); + while (IComponent* cmp = gen.Next()) + cmp->HandleMessage(msg, false); } } - SendGlobalMessage(INVALID_ENTITY, msg); } @@ -1063,14 +1072,10 @@ } // Find the component instances of this type (if any) - std::map >::const_iterator emap = m_ComponentsByTypeId.find(*ctit); - if (emap == m_ComponentsByTypeId.end()) - continue; - + SComponentDataGenerator gen = SComponentDataGenerator(&m_ComponentData.at(*ctit)); // Send the message to all of them - std::map::const_iterator eit = emap->second.begin(); - for (; eit != emap->second.end(); ++eit) - eit->second->HandleMessage(msg, true); + while (IComponent* cmp = gen.Next()) + cmp->HandleMessage(msg, true); } } Index: source/simulation2/system/Entity.h =================================================================== --- source/simulation2/system/Entity.h +++ source/simulation2/system/Entity.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2017 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -87,6 +87,7 @@ entity_id_t GetId() const { return m_Id; } SEntityComponentCache* GetComponentCache() const { return m_ComponentCache; } + void SetInvalid() { m_Id = INVALID_ENTITY; } private: entity_id_t m_Id; Index: source/simulation2/system/IComponent.h =================================================================== --- source/simulation2/system/IComponent.h +++ source/simulation2/system/IComponent.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -33,15 +33,15 @@ { public: // Component allocation types - using AllocFunc = IComponent* (*)(const ScriptInterface& scriptInterface, JS::HandleValue ctor); + using AllocFunc = IComponent* (*)(void* slot, const ScriptInterface& scriptInterface, JS::HandleValue ctor); using DeallocFunc = void (*)(IComponent*); virtual ~IComponent(); static std::string GetSchema(); - static void RegisterComponentType(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, const char* name, const std::string& schema); - static void RegisterComponentTypeScriptWrapper(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, const char* name, const std::string& schema); + static void RegisterComponentType(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, size_t size, size_t align, const char* name, const std::string& schema); + static void RegisterComponentTypeScriptWrapper(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, size_t size, size_t align, const char* name, const std::string& schema); virtual void Init(const CParamNode& paramNode) = 0; virtual void Deinit() = 0; @@ -69,6 +69,8 @@ virtual bool NewJSObject(const ScriptInterface& scriptInterface, JS::MutableHandleObject out) const; virtual JS::Value GetJSInstance() const; virtual int GetComponentTypeId() const = 0; + void SetInvalid() { m_EntityHandle.SetInvalid(); } + bool IsValid() const { return GetEntityId() != INVALID_ENTITY; } private: CEntityHandle m_EntityHandle; Index: source/simulation2/system/IComponent.cpp =================================================================== --- source/simulation2/system/IComponent.cpp +++ source/simulation2/system/IComponent.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -33,14 +33,14 @@ return ""; } -void IComponent::RegisterComponentType(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, const char* name, const std::string& schema) +void IComponent::RegisterComponentType(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, size_t size, size_t align, const char* name, const std::string& schema) { - mgr.RegisterComponentType(iid, cid, alloc, dealloc, name, schema); + mgr.RegisterComponentType(iid, cid, alloc, dealloc, size, align, name, schema); } -void IComponent::RegisterComponentTypeScriptWrapper(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, const char* name, const std::string& schema) +void IComponent::RegisterComponentTypeScriptWrapper(CComponentManager& mgr, EInterfaceId iid, EComponentTypeId cid, AllocFunc alloc, DeallocFunc dealloc, size_t size, size_t align, const char* name, const std::string& schema) { - mgr.RegisterComponentTypeScriptWrapper(iid, cid, alloc, dealloc, name, schema); + mgr.RegisterComponentTypeScriptWrapper(iid, cid, alloc, dealloc, size, align, name, schema); } void IComponent::HandleMessage(const CMessage& UNUSED(msg), bool UNUSED(global)) Index: source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp =================================================================== --- source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp +++ source/tools/atlas/GameInterface/Handlers/MapHandlers.cpp @@ -518,10 +518,10 @@ const int offsetZ = ((m_NewPatches - m_OldPatches) / 2 + m_OffsetY) * PATCH_SIZE * TERRAIN_TILE_SIZE; const CFixedVector3D offset = CFixedVector3D(fixed::FromInt(offsetX), fixed::FromInt(0), fixed::FromInt(offsetZ)); - const CSimulation2::InterfaceListUnordered& ents = sim.GetEntitiesWithInterfaceUnordered(IID_Selectable); - for (const std::pair& ent : ents) + SComponentDataGenerator ents = sim.GetEntitiesWithInterfaceUnordered(IID_Selectable); + while (IComponent* cmp = ents.Next()) { - const entity_id_t entityId = ent.first; + const entity_id_t entityId = cmp->GetEntityId(); CmpPtr cmpPosition(sim, entityId); if (cmpPosition && cmpPosition->IsInWorld() && Within(cmpPosition->GetPosition(), mapCenterX, mapCenterZ, radiusInTerrainUnits)) Index: source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp =================================================================== --- source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp +++ source/tools/atlas/GameInterface/Handlers/ObjectHandlers.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2021 Wildfire Games. +/* Copyright (C) 2022 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -1093,12 +1093,12 @@ std::vector ids; player_id_t playerID = msg->player; - const CSimulation2::InterfaceListUnordered& cmps = g_Game->GetSimulation2()->GetEntitiesWithInterfaceUnordered(IID_Ownership); - for (CSimulation2::InterfaceListUnordered::const_iterator eit = cmps.begin(); eit != cmps.end(); ++eit) + SComponentDataGenerator cmps = g_Game->GetSimulation2()->GetEntitiesWithInterfaceUnordered(IID_Ownership); + while (IComponent* cmp = cmps.Next()) { - if (static_cast(eit->second)->GetOwner() == playerID) + if (static_cast(cmp)->GetOwner() == playerID) { - ids.push_back(eit->first); + ids.push_back(cmp->GetEntityId()); } }