--- /dev/null
+#include "eventbus.h"
+
+namespace Msp::Game {
+
+unsigned EventBus::get_next_id()
+{
+ static unsigned next_id = 0;
+ return next_id++;
+}
+
+void EventBus::remove_observer(EventObserver &obs)
+{
+ for(const Dispatcher &e: dispatchers)
+ if(e.dispatcher)
+ e.remover(e.dispatcher, obs);
+}
+
+} // namespace Msp::Game
--- /dev/null
+#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
--- /dev/null
+#include "eventobserver.h"
+#include "eventsource.h"
+
+namespace Msp::Game {
+
+EventObserver::EventObserver(EventBus &b):
+ bus(b)
+{ }
+
+EventObserver::~EventObserver()
+{
+ bus.remove_observer(*this);
+ for(EventSourceBase *s: observed_sources)
+ s->remove_observer(*this);
+}
+
+void EventObserver::remove_source(EventSourceBase &src)
+{
+ auto i = lower_bound(observed_sources, &src);
+ if(i!=observed_sources.end() && *i==&src)
+ observed_sources.erase(i);
+}
+
+} // namespace Msp::Game
--- /dev/null
+#ifndef MSP_GAME_EVENTOBSERVER_H_
+#define MSP_GAME_EVENTOBSERVER_H_
+
+#include <msp/core/algorithm.h>
+#include <msp/core/noncopyable.h>
+#include "eventbus.h"
+
+namespace Msp::Game {
+
+class EventSourceBase;
+
+class EventObserver: public NonCopyable
+{
+private:
+ EventBus &bus;
+ std::vector<EventSourceBase *> observed_sources;
+
+public:
+ EventObserver(EventBus &);
+ ~EventObserver();
+
+ template<typename T>
+ void observe(std::function<void(const T &)> cb)
+ { bus.add_observer<T>(*this, std::move(cb)); }
+
+ template<typename T, typename S>
+ void observe(S &, std::function<void(const T &)> );
+
+ void remove_source(EventSourceBase &);
+};
+
+template<typename T, typename S>
+void EventObserver::observe(S &src, std::function<void(const T &)> cb)
+{
+ src.add_observer(*this, std::move(cb));
+ auto i = lower_bound(observed_sources, &src);
+ if(i==observed_sources.end() || *i!=&src)
+ observed_sources.insert(i, &src);
+}
+
+} // namespace Msp::Game
+
+#endif
--- /dev/null
+#ifndef MSP_GAME_EVENTSOURCE_H_
+#define MSP_GAME_EVENTSOURCE_H_
+
+#include "eventbus.h"
+#include "eventobserver.h"
+
+namespace Msp::Game {
+
+class EventObserver;
+
+class EventSourceBase
+{
+protected:
+ EventBus &bus;
+
+public:
+ EventSourceBase(EventBus &b): bus(b) { }
+
+ virtual void remove_observer(EventObserver &) = 0;
+};
+
+
+template<typename... E>
+class EventSource: public EventSourceBase, private EventDispatcher<E>...
+{
+public:
+ EventSource(EventBus &b): EventSourceBase(b) { }
+ ~EventSource() { (cancel_observation<E>(), ...); }
+
+ template<typename T>
+ void add_observer(EventObserver &obs, std::function<void(const T &)> cb)
+ { static_cast<EventDispatcher<T> &>(*this).add_observer(obs, std::move(cb)); }
+
+ void remove_observer(EventObserver &obs) override
+ { (static_cast<EventDispatcher<E> &>(*this).remove_observer(&obs), ...); }
+
+private:
+ template<typename T>
+ void cancel_observation();
+
+public:
+ template<typename T, typename... Args>
+ void emit(Args &&...) const;
+};
+
+
+template<typename... E>
+template<typename T, typename... Args>
+inline void EventSource<E...>::emit(Args &&... args) const
+{
+ T event(std::forward<Args>(args)...);
+ static_cast<const EventDispatcher<T> &>(*this).dispatch(event);
+ bus.dispatch(event);
+}
+
+template<typename... E>
+template<typename T>
+inline void EventSource<E...>::cancel_observation()
+{
+ for(const auto &h: static_cast<EventDispatcher<T> &>(*this).handlers)
+ h.observer->remove_source(*this);
+}
+
+} // namespace Msp::Game
+
+#endif
#include <memory>
#include <msp/time/timedelta.h>
+#include "eventbus.h"
#include "handle.h"
namespace Msp::Game {
{
private:
PoolPool pools;
+ EventBus event_bus;
/* Use unique_ptr because there's only one root per stage so it's pointless
to put it in a pool. */
std::unique_ptr<Root> root;
~Stage();
PoolPool &get_pools() { return pools; }
+ EventBus &get_event_bus() { return event_bus; }
Handle<Root> get_root() { return Handle<Root>::from_object(root.get()); }
template<typename T, typename F>