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