]> git.tdb.fi Git - libs/game.git/blob - source/game/eventbus.h
Provide a way to request events for existing entities and components
[libs/game.git] / source / game / eventbus.h
1 #ifndef MSP_GAME_EVENTBUS_H_
2 #define MSP_GAME_EVENTBUS_H_
3
4 #include <functional>
5 #include <vector>
6
7 namespace Msp::Game {
8
9 class EventObserver;
10
11 template<typename T>
12 struct EventDispatcher
13 {
14         struct Handler
15         {
16                 EventObserver *observer;
17                 std::function<void(const T &)> callback;
18         };
19
20         std::vector<Handler> handlers;
21
22         void add_observer(EventObserver *obs, std::function<void(const T &)> &&cb)
23         { handlers.emplace_back(obs, std::move(cb)); }
24
25         void remove_observer(EventObserver *obs)
26         { std::erase_if(handlers, [obs](auto &h){ return h.observer==obs; }); }
27
28         void dispatch(const T &) const;
29         void dispatch_to(EventObserver &, const T &) const;
30 };
31
32
33 class EventBus
34 {
35 private:
36         using DeleteFunc = void(void *);
37         using RemoveFunc = void(void *, EventObserver &);
38
39         struct Dispatcher
40         {
41                 void *dispatcher = nullptr;
42                 DeleteFunc *deleter = nullptr;
43                 RemoveFunc *remover = nullptr;
44         };
45
46         std::vector<Dispatcher> dispatchers;
47
48         static unsigned get_next_id();
49
50 public:
51         template<typename T>
52         static unsigned get_event_id();
53
54 private:
55         template<typename T>
56         EventDispatcher<T> &get_dispatcher();
57
58 public:
59         template<typename T>
60         void add_observer(EventObserver &obs, std::function<void(const T &)> cb)
61         { get_dispatcher<T>().add_observer(&obs, std::move(cb)); }
62
63         void replace_observer(EventObserver &, EventObserver &);
64         void remove_observer(EventObserver &);
65
66         template<typename T>
67         void dispatch(const T &) const;
68
69         template<typename T>
70         void dispatch_to(EventObserver &, const T &) const;
71 };
72
73
74 template<typename T>
75 void EventDispatcher<T>::dispatch(const T &event) const
76 {
77         for(const Handler &h: handlers)
78                 h.callback(event);
79 }
80
81 template<typename T>
82 void EventDispatcher<T>::dispatch_to(EventObserver &obs, const T &event) const
83 {
84         for(const Handler &h: handlers)
85                 if(h.observer==&obs)
86                         h.callback(event);
87 }
88
89
90 template<typename T>
91 inline unsigned EventBus::get_event_id()
92 {
93         static unsigned id = get_next_id();
94         return id;
95 }
96
97 template<typename T>
98 inline EventDispatcher<T> &EventBus::get_dispatcher()
99 {
100         unsigned id = get_event_id<T>();
101         if(dispatchers.size()<=id)
102                 dispatchers.resize(id+1);
103
104         Dispatcher &event = dispatchers[id];
105         if(!event.dispatcher)
106         {
107                 event.dispatcher = new EventDispatcher<T>;
108                 event.deleter = [](void *p){ delete static_cast<EventDispatcher<T> *>(p); };
109                 event.remover = [](void *p, EventObserver &o){ static_cast<EventDispatcher<T> *>(p)->remove_observer(&o); };
110         }
111
112         return *static_cast<EventDispatcher<T> *>(event.dispatcher);
113 }
114
115 template<typename T>
116 inline void EventBus::dispatch(const T &event) const
117 {
118         unsigned id = get_event_id<T>();
119         if(id<dispatchers.size() && dispatchers[id].dispatcher)
120                 static_cast<EventDispatcher<T> *>(dispatchers[id].dispatcher)->dispatch(event);
121 }
122
123 template<typename T>
124 inline void EventBus::dispatch_to(EventObserver &obs, const T &event) const
125 {
126         unsigned id = get_event_id<T>();
127         if(id<dispatchers.size() && dispatchers[id].dispatcher)
128                 static_cast<EventDispatcher<T> *>(dispatchers[id].dispatcher)->dispatch_to(obs, event);
129 }
130
131 } // namespace Msp::Game
132
133 #endif