+#ifndef MSP_GAME_EVENTBUS_H_
+#define MSP_GAME_EVENTBUS_H_
+
+#include <functional>
+#include <vector>
+
+namespace Msp::Game {
+
+class EventObserver;
+
+template<typename T>
+struct EventDispatcher
+{
+ struct Handler
+ {
+ EventObserver *observer;
+ std::function<void(const T &)> callback;
+ };
+
+ std::vector<Handler> handlers;
+
+ void add_observer(EventObserver *obs, std::function<void(const T &)> &&cb)
+ { handlers.emplace_back(obs, std::move(cb)); }
+
+ void remove_observer(EventObserver *obs)
+ { std::erase_if(handlers, [obs](const Handler &h){ return h.observer==obs; }); }
+
+ void dispatch(const T &) const;
+};
+
+
+class EventBus
+{
+private:
+ using DeleteFunc = void(void *);
+ using RemoveFunc = void(void *, EventObserver &);
+
+ struct Dispatcher
+ {
+ void *dispatcher = nullptr;
+ DeleteFunc *deleter = nullptr;
+ RemoveFunc *remover = nullptr;
+ };
+
+ std::vector<Dispatcher> dispatchers;
+
+ static unsigned get_next_id();
+
+public:
+ template<typename T>
+ static unsigned get_event_id();
+
+private:
+ template<typename T>
+ EventDispatcher<T> &get_emitter();
+
+public:
+ template<typename T>
+ void add_observer(EventObserver &obs, std::function<void(const T &)> cb)
+ { get_emitter<T>().add_observer(obs, std::move(cb)); }
+
+ void replace_observer(EventObserver &, EventObserver &);
+ void remove_observer(EventObserver &);
+
+ template<typename T>
+ void dispatch(const T &) const;
+};
+
+
+template<typename T>
+void EventDispatcher<T>::dispatch(const T &event) const
+{
+ for(const Handler &h: handlers)
+ h.callback(event);
+}
+
+
+template<typename T>
+inline unsigned EventBus::get_event_id()
+{
+ static unsigned id = get_next_id();
+ return id;
+}
+
+template<typename T>
+inline EventDispatcher<T> &EventBus::get_emitter()
+{
+ unsigned id = get_event_id<T>();
+ if(dispatchers.size()<=id)
+ dispatchers.resize(id+1);
+
+ Dispatcher &event = dispatchers[id];
+ if(!event.dispatcher)
+ {
+ event.dispatcher = new EventDispatcher<T>;
+ event.deleter = [](void *p){ delete static_cast<EventDispatcher<T> *>(p); };
+ event.remover = [](void *p, EventObserver &o){ static_cast<EventDispatcher<T> *>(p)->remove_observer(o); };
+ }
+
+ return static_cast<EventDispatcher<T> *>(event.dispatcher);
+}
+
+template<typename T>
+inline void EventBus::dispatch(const T &event) const
+{
+ unsigned id = get_event_id<T>();
+ if(id<dispatchers.size() && dispatchers[id].dispatcher)
+ static_cast<EventDispatcher<T> *>(dispatchers[id].dispatcher)->dispatch(event);
+}
+
+} // namespace Msp::Game
+
+#endif