Dependency position
#648 (FieldType / ComponentMeta types)
└──► THIS ISSUE ──► #650 (Register built-in components)
└──► #651 (Inspector migration)
Start after #648 is merged. Can be worked immediately after #648 since that PR is just headers.
Why
The registry is the central store all component metadata flows through. The inspector calls ForEach on it. Registration code in component .cpp files calls Register on it. Without it, neither #650 nor #651 can proceed.
Work
ZEngine/ZEngine/ECS/Reflection/ComponentReflectionRegistry.h
#pragma once
#include <ZEngine/ECS/Reflection/ComponentMeta.h>
#include <ZEngine/ECS/ComponentTypeID.h>
#include <ZEngine/Core/Containers/Array.h>
#include <ZEngine/Core/Memory/Allocator.h>
namespace ZEngine::ECS {
class ComponentReflectionRegistry {
public:
static ComponentReflectionRegistry& Get();
void Initialize(Core::Memory::ArenaAllocator* arena);
// Register metadata. meta.Fields must outlive this registry
// (use static arrays at the call site).
// Second registration with the same TypeID is a no-op (debug warning logged).
void Register(const ComponentMeta& meta);
[[nodiscard]] const ComponentMeta* Lookup(ComponentTypeID id) const;
[[nodiscard]] const ComponentMeta* LookupByName(const char* type_name) const;
template<typename Fn>
void ForEach(Fn&& fn) const {
for (uint32_t i = 0; i < m_metas.size(); ++i)
fn(m_metas[i]);
}
[[nodiscard]] uint32_t Count() const;
private:
Core::Containers::Array<ComponentMeta> m_metas;
Core::Memory::ArenaAllocator* m_arena = nullptr;
};
}
ZEngine/ZEngine/ECS/Reflection/ComponentReflectionRegistry.cpp
Get() returns a static instance (initialized once).
Initialize(arena) stores the arena and calls m_metas.init(arena, 64).
Register checks for duplicate TypeID (debug assert/warn), then m_metas.push(meta).
Lookup iterates m_metas linearly (N ≤ 64, cache-friendly, no hash needed).
LookupByName uses secure_strcmp on meta.TypeName.
- Call
ComponentReflectionRegistry::Get().Initialize(arena) from Engine::Initialize after the ECS arena is set up.
Acceptance criteria
Register then Lookup(TypeID) returns the same metadata
LookupByName("TransformComponent") returns the right entry
ForEach visits all registered types in insertion order
- Double registration with the same TypeID logs a debug warning and skips
- Thread safety: registration only happens at startup (single-threaded); no locks needed
Dependency position
Start after #648 is merged. Can be worked immediately after #648 since that PR is just headers.
Why
The registry is the central store all component metadata flows through. The inspector calls
ForEachon it. Registration code in component.cppfiles callsRegisteron it. Without it, neither #650 nor #651 can proceed.Work
ZEngine/ZEngine/ECS/Reflection/ComponentReflectionRegistry.hZEngine/ZEngine/ECS/Reflection/ComponentReflectionRegistry.cppGet()returns a static instance (initialized once).Initialize(arena)stores the arena and callsm_metas.init(arena, 64).Registerchecks for duplicate TypeID (debug assert/warn), thenm_metas.push(meta).Lookupiteratesm_metaslinearly (N ≤ 64, cache-friendly, no hash needed).LookupByNameusessecure_strcmponmeta.TypeName.ComponentReflectionRegistry::Get().Initialize(arena)fromEngine::Initializeafter the ECS arena is set up.Acceptance criteria
RegisterthenLookup(TypeID)returns the same metadataLookupByName("TransformComponent")returns the right entryForEachvisits all registered types in insertion order