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