1 #ifndef MSP_GAME_SYSTEM_H_
2 #define MSP_GAME_SYSTEM_H_
5 #include <msp/time/timedelta.h>
6 #include "accessguard.h"
8 #include "reflection.h"
22 UPDATE = READ_OLD | WRITE,
23 CHAINED_UPDATE = READ_FRESH | WRITE,
35 DependencyFlags flags = NO_DEPENDENCY;
36 const Reflection::ClassBase &type;
37 void (*prepare)(Stage &) = nullptr;
38 void (*commit)(Stage &) = nullptr;
39 void (*unblock)(DependencyFlags) = nullptr;
40 void (*block)(DependencyFlags) = nullptr;
43 Dependency(const Reflection::ClassBase &t): type(t) { }
45 DependencyFlags get_flags() const { return flags; }
46 const Reflection::ClassBase &get_type() const { return type; }
49 class Activator: public NonCopyable
55 Activator(System &s): system(s) { system.begin_tick(); }
56 ~Activator() { system.end_tick(); }
61 std::vector<std::function<void()>> deferred_queue;
62 std::vector<Dependency> dependencies;
64 static thread_local System *active;
66 System(Stage &s): stage(s) { }
68 virtual ~System() = default;
70 Stage &get_stage() const { return stage; }
74 void declare_dependency(DependencyFlags);
77 const std::vector<Dependency> &get_dependencies() const { return dependencies; }
80 virtual void tick(Time::TimeDelta) = 0;
82 virtual void deferred_tick();
86 void defer(F &&f) { deferred_queue.emplace_back(std::forward<F>(f)); }
91 inline void System::declare_dependency(DependencyFlags flags)
93 if((flags&DATA_MASK) && !std::is_base_of_v<Component, T>)
94 throw std::invalid_argument("System::declare_dependency");
95 if((flags&ORDER_MASK) && !std::is_base_of_v<System, T>)
96 throw std::invalid_argument("System::declare_dependency");
98 const Reflection::ClassBase &type = stage.get_reflector().get_or_create_class<T>();
99 auto i = find_if(dependencies, [&type](const Dependency &d){ return &d.type==&type; });
101 if(i!=dependencies.end())
102 flags = static_cast<DependencyFlags>(flags|i->flags);
103 if((flags&RUN_BEFORE) && (flags&RUN_AFTER))
104 throw std::logic_error("conflicting order flags");
106 Dependency &dep = (i!=dependencies.end() ? *i : dependencies.emplace_back(type));
108 if constexpr(requires(T &c) { typename T::Data; c.prepare_tick(); c.commit_tick(); })
111 dep.unblock = +[](DependencyFlags f){
112 if(f&READ_OLD) AccessGuard::get_instance().unblock<AccessGuard::Read<typename T::Data>>();
113 if(f&WRITE) AccessGuard::get_instance().unblock<AccessGuard::Write<typename T::Data>>();
115 dep.block = +[](DependencyFlags f){
116 if(f&READ_OLD) AccessGuard::get_instance().block<AccessGuard::Read<typename T::Data>>();
117 if(f&WRITE) AccessGuard::get_instance().block<AccessGuard::Write<typename T::Data>>();
122 dep.prepare = +[](Stage &s){ s.iterate_objects<T>([](T &c){ c.prepare_tick(); }); };
123 dep.commit = +[](Stage &s){ s.iterate_objects<T>([](T &c){ c.commit_tick(); }); };
128 } // namespace Msp::Game