]> git.tdb.fi Git - libs/game.git/blob - source/game/system.h
Implement base support for buffered components
[libs/game.git] / source / game / system.h
1 #ifndef MSP_GAME_SYSTEM_H_
2 #define MSP_GAME_SYSTEM_H_
3
4 #include <functional>
5 #include <msp/time/timedelta.h>
6 #include "component.h"
7 #include "reflection.h"
8 #include "stage.h"
9
10 namespace Msp::Game {
11
12 class System
13 {
14 public:
15         enum DependencyFlags
16         {
17                 NO_DEPENDENCY = 0,
18                 READ_OLD = 1,
19                 READ_FRESH = 3,
20                 WRITE = 4,
21                 UPDATE = READ_OLD | WRITE,
22                 CHAINED_UPDATE = READ_FRESH | WRITE
23         };
24
25         struct Dependency
26         {
27                 DependencyFlags flags = NO_DEPENDENCY;
28                 const Reflection::ClassBase &type;
29                 void (*prepare)(Stage &) = nullptr;
30                 void (*commit)(Stage &) = nullptr;
31
32                 Dependency(const Reflection::ClassBase &t): type(t) { }
33         };
34
35         class Activator: public NonCopyable
36         {
37         private:
38                 System &system;
39
40         public:
41                 Activator(System &s): system(s) { system.begin_tick(); }
42                 ~Activator() { system.end_tick(); }
43         };
44
45 protected:
46         Stage &stage;
47         std::vector<std::function<void()>> deferred_queue;
48         std::vector<Dependency> dependencies;
49
50         static thread_local System *active;
51
52         System(Stage &s): stage(s) { }
53 public:
54         virtual ~System() = default;
55
56         Stage &get_stage() const { return stage; }
57
58 protected:
59         template<typename T>
60         void declare_dependency(DependencyFlags);
61
62 public:
63         void begin_tick();
64         virtual void tick(Time::TimeDelta) = 0;
65         void end_tick();
66         virtual void deferred_tick();
67
68 protected:
69         template<typename F>
70         void defer(F &&f) { deferred_queue.emplace_back(std::forward<F>(f)); }
71 };
72
73
74 template<typename T>
75 inline void System::declare_dependency(DependencyFlags flags)
76 {
77         if(!std::is_base_of_v<Component, T>)
78                 throw std::invalid_argument("System::declare_dependency");
79
80         const Reflection::ClassBase &type = stage.get_reflector().get_or_create_class<T>();
81         auto i = find_if(dependencies, [&type](const Dependency &d){ return &d.type==&type; });
82
83         if(i!=dependencies.end())
84                 flags = static_cast<DependencyFlags>(flags|i->flags);
85
86         Dependency &dep = (i!=dependencies.end() ? *i : dependencies.emplace_back(type));
87         dep.flags = flags;
88         if constexpr(requires(T &c) { typename T::Data; c.prepare_tick(); c.commit_tick(); })
89                 if(flags&WRITE)
90                 {
91                         dep.prepare = +[](Stage &s){ s.iterate_objects<T>([](T &c){ c.prepare_tick(); }); };
92                         dep.commit = +[](Stage &s){ s.iterate_objects<T>([](T &c){ c.commit_tick(); }); };
93                 }
94 }
95
96 } // namespace Msp::Game
97
98 #endif