aboutsummaryrefslogtreecommitdiffstats
path: root/deps/entityx/entityx/Event.h
diff options
context:
space:
mode:
Diffstat (limited to 'deps/entityx/entityx/Event.h')
-rw-r--r--deps/entityx/entityx/Event.h216
1 files changed, 216 insertions, 0 deletions
diff --git a/deps/entityx/entityx/Event.h b/deps/entityx/entityx/Event.h
new file mode 100644
index 0000000..cb7b3ab
--- /dev/null
+++ b/deps/entityx/entityx/Event.h
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2012 Alec Thomas <alec@swapoff.org>
+ * All rights reserved.
+ *
+ * This software is licensed as described in the file COPYING, which
+ * you should have received as part of this distribution.
+ *
+ * Author: Alec Thomas <alec@swapoff.org>
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <cstddef>
+#include <vector>
+#include <list>
+#include <unordered_map>
+#include <memory>
+#include <utility>
+#include "entityx/config.h"
+#include "entityx/3rdparty/simplesignal.h"
+#include "entityx/help/NonCopyable.h"
+
+
+namespace entityx {
+
+
+/// Used internally by the EventManager.
+class BaseEvent {
+ public:
+ typedef std::size_t Family;
+
+ virtual ~BaseEvent();
+
+ protected:
+ static Family family_counter_;
+};
+
+
+typedef Simple::Signal<void (const void*)> EventSignal;
+typedef std::shared_ptr<EventSignal> EventSignalPtr;
+typedef std::weak_ptr<EventSignal> EventSignalWeakPtr;
+
+
+/**
+ * Event types should subclass from this.
+ *
+ * struct Explosion : public Event<Explosion> {
+ * Explosion(int damage) : damage(damage) {}
+ * int damage;
+ * };
+ */
+template <typename Derived>
+class Event : public BaseEvent {
+ public:
+ /// Used internally for registration.
+ static Family family() {
+ static Family family = family_counter_++;
+ return family;
+ }
+};
+
+
+class BaseReceiver {
+ public:
+ virtual ~BaseReceiver() {
+ for (auto connection : connections_) {
+ auto &ptr = connection.second.first;
+ if (!ptr.expired()) {
+ ptr.lock()->disconnect(connection.second.second);
+ }
+ }
+ }
+
+ // Return number of signals connected to this receiver.
+ std::size_t connected_signals() const {
+ std::size_t size = 0;
+ for (auto connection : connections_) {
+ if (!connection.second.first.expired()) {
+ size++;
+ }
+ }
+ return size;
+ }
+
+ private:
+ friend class EventManager;
+ std::unordered_map<BaseEvent::Family, std::pair<EventSignalWeakPtr, std::size_t>> connections_;
+};
+
+
+template <typename Derived>
+class Receiver : public BaseReceiver {
+ public:
+ virtual ~Receiver() {}
+};
+
+
+/**
+ * Handles event subscription and delivery.
+ *
+ * Subscriptions are automatically removed when receivers are destroyed..
+ */
+class EventManager : entityx::help::NonCopyable {
+ public:
+ EventManager();
+ virtual ~EventManager();
+
+ /**
+ * Subscribe an object to receive events of type E.
+ *
+ * Receivers must be subclasses of Receiver and must implement a receive() method accepting the given event type.
+ *
+ * eg.
+ *
+ * struct ExplosionReceiver : public Receiver<ExplosionReceiver> {
+ * void receive(const Explosion &explosion) {
+ * }
+ * };
+ *
+ * ExplosionReceiver receiver;
+ * em.subscribe<Explosion>(receiver);
+ */
+ template <typename E, typename Receiver>
+ void subscribe(Receiver &receiver) {
+ void (Receiver::*receive)(const E &) = &Receiver::receive;
+ auto sig = signal_for(Event<E>::family());
+ auto wrapper = EventCallbackWrapper<E>(std::bind(receive, &receiver, std::placeholders::_1));
+ auto connection = sig->connect(wrapper);
+ BaseReceiver &base = receiver;
+ base.connections_.insert(std::make_pair(Event<E>::family(), std::make_pair(EventSignalWeakPtr(sig), connection)));
+ }
+
+ /**
+ * Unsubscribe an object in order to not receive events of type E anymore.
+ *
+ * Receivers must have subscribed for event E before unsubscribing from event E.
+ *
+ */
+ template <typename E, typename Receiver>
+ void unsubscribe(Receiver &receiver) {
+ BaseReceiver &base = receiver;
+ // Assert that it has been subscribed before
+ assert(base.connections_.find(Event<E>::family()) != base.connections_.end());
+ auto pair = base.connections_[Event<E>::family()];
+ auto connection = pair.second;
+ auto &ptr = pair.first;
+ if (!ptr.expired()) {
+ ptr.lock()->disconnect(connection);
+ }
+ base.connections_.erase(Event<E>::family());
+ }
+
+ template <typename E>
+ void emit(const E &event) {
+ auto sig = signal_for(Event<E>::family());
+ sig->emit(&event);
+ }
+
+ /**
+ * Emit an already constructed event.
+ */
+ template <typename E>
+ void emit(std::unique_ptr<E> event) {
+ auto sig = signal_for(Event<E>::family());
+ sig->emit(event.get());
+ }
+
+ /**
+ * Emit an event to receivers.
+ *
+ * This method constructs a new event object of type E with the provided arguments, then delivers it to all receivers.
+ *
+ * eg.
+ *
+ * std::shared_ptr<EventManager> em = new EventManager();
+ * em->emit<Explosion>(10);
+ *
+ */
+ template <typename E, typename ... Args>
+ void emit(Args && ... args) {
+ // Using 'E event(std::forward...)' causes VS to fail with an internal error. Hack around it.
+ E event = E(std::forward<Args>(args) ...);
+ auto sig = signal_for(std::size_t(Event<E>::family()));
+ sig->emit(&event);
+ }
+
+ std::size_t connected_receivers() const {
+ std::size_t size = 0;
+ for (EventSignalPtr handler : handlers_) {
+ if (handler) size += handler->size();
+ }
+ return size;
+ }
+
+ private:
+ EventSignalPtr &signal_for(std::size_t id) {
+ if (id >= handlers_.size())
+ handlers_.resize(id + 1);
+ if (!handlers_[id])
+ handlers_[id] = std::make_shared<EventSignal>();
+ return handlers_[id];
+ }
+
+ // Functor used as an event signal callback that casts to E.
+ template <typename E>
+ struct EventCallbackWrapper {
+ explicit EventCallbackWrapper(std::function<void(const E &)> callback) : callback(callback) {}
+ void operator()(const void *event) { callback(*(static_cast<const E*>(event))); }
+ std::function<void(const E &)> callback;
+ };
+
+ std::vector<EventSignalPtr> handlers_;
+};
+
+} // namespace entityx