--- /dev/null
+#ifndef MSP_GAME_ARCHETYPE_H_
+#define MSP_GAME_ARCHETYPE_H_
+
+#include <vector>
+#include <msp/core/algorithm.h>
+#include <msp/core/meta.h>
+#include "eventobserver.h"
+#include "events.h"
+#include "handle.h"
+
+namespace Msp::Game {
+
+template<auto first, auto... rest>
+ requires (std::is_same_v<typename PointerToMemberTraits<decltype(first)>::Class, typename PointerToMemberTraits<decltype(rest)>::Class> && ...)
+struct CommonClassOfMembers
+{
+ using Type = typename PointerToMemberTraits<decltype(first)>::Class;
+};
+
+template<auto first, auto... rest>
+struct ArchetypeComponents
+{
+ using Wrapper = typename CommonClassOfMembers<first, rest...>::Type;
+
+ template<typename F>
+ static void dispatch(const F &, Handle<Component>);
+
+ static bool all_null(const Wrapper &);
+};
+
+
+template<typename T>
+concept ValidArchetype =
+ std::is_same_v<typename T::Components::Wrapper, T> &&
+ requires(T t) { { t.entity } -> std::convertible_to<Handle<Entity>>; };
+
+
+template<typename T>
+ requires ValidArchetype<T>
+class ArchetypeMonitor
+{
+private:
+ EventObserver &event_observer;
+ std::vector<T> &entities;
+ std::function<void(T &)> changed_callback;
+
+public:
+ ArchetypeMonitor(EventObserver &, std::vector<T> &);
+
+ void set_changed_callback(std::function<void(T &)>);
+
+private:
+ void component_created(const Events::ComponentCreated &);
+ void component_destroyed(const Events::ComponentDestroyed &);
+
+ T &get_or_create_wrapper(Handle<Entity>);
+};
+
+
+template<auto first, auto... rest>
+template<typename F>
+void ArchetypeComponents<first, rest...>::dispatch(const F &func, Handle<Component> comp)
+{
+ if(auto cast_comp = dynamic_handle_cast<typename PointerToMemberTraits<decltype(first)>::Type::element_type>(comp))
+ func(cast_comp, first);
+ else if constexpr(sizeof...(rest)>0)
+ ArchetypeComponents<rest...>::dispatch(func, comp);
+}
+
+template<auto first, auto... rest>
+bool ArchetypeComponents<first, rest...>::all_null(const Wrapper &wrapper)
+{
+ return (!(wrapper.*first) && ... && !(wrapper.*rest));
+}
+
+
+template<typename T>
+ requires ValidArchetype<T>
+ArchetypeMonitor<T>::ArchetypeMonitor(EventObserver &o, std::vector<T> &v):
+ event_observer(o),
+ entities(v)
+{
+ event_observer.observe<Events::ComponentCreated>([this](auto &e){ component_created(e); });
+ event_observer.observe<Events::ComponentDestroyed>([this](auto &e){ component_destroyed(e); });
+}
+
+template<typename T>
+ requires ValidArchetype<T>
+void ArchetypeMonitor<T>::set_changed_callback(std::function<void(T &)> cb)
+{
+ changed_callback = cb;
+}
+
+template<typename T>
+ requires ValidArchetype<T>
+void ArchetypeMonitor<T>::component_created(const Events::ComponentCreated &event)
+{
+ T::Components::dispatch([this]<typename C>(Handle<C> comp, Handle<C> T::*member){
+ Handle<Entity> entity = comp->get_entity();
+ T &wrapper = get_or_create_wrapper(entity);
+ wrapper.*member = comp;
+
+ if(changed_callback)
+ changed_callback(wrapper);
+ }, event.component);
+}
+
+template<typename T>
+ requires ValidArchetype<T>
+void ArchetypeMonitor<T>::component_destroyed(const Events::ComponentDestroyed &event)
+{
+ T::Components::dispatch([this]<typename C>(Handle<C> comp, Handle<C> T::*member){
+ Handle<Entity> entity = comp->get_entity();
+ auto i = lower_bound_member(entities, entity, &T::entity);
+ if(i!=entities.end() && i->entity==entity)
+ {
+ (*i).*member = nullptr;
+ if(changed_callback)
+ changed_callback(*i);
+ }
+
+ if(T::Components::all_null(*i))
+ entities.erase(i);
+ }, event.component);
+}
+
+template<typename T>
+ requires ValidArchetype<T>
+T &ArchetypeMonitor<T>::get_or_create_wrapper(Handle<Entity> entity)
+{
+ auto i = lower_bound_member(entities, entity, &T::entity);
+ if(i!=entities.end() && i->entity==entity)
+ return *i;
+
+ if constexpr(std::is_constructible_v<T, Handle<Entity>>)
+ return *entities.emplace(i, entity);
+ else
+ {
+ auto &e = entities.emplace(i);
+ e.entity = entity;
+ return e;
+ }
+}
+
+} // namespace Msp::Game
+
+#endif