Files
aee-arcade/source/game/entities/event_bus.hpp
T

84 lines
2.7 KiB
C++

#pragma once
#include <cstddef>
#include <cstdint>
#include <functional>
#include <string>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
// Forward: els events poden dur un punter a `Entity` (p. ex. quin sarcòfag
// s'ha obert). No cal incloure el header complet aquí.
class Entity;
// Event pub/sub minimalista. Hi ha un únic `EventBus` per mundus (propietat
// de l'`EntityManager`); les entitats que el necessiten en reben un punter
// via `Entity::setEventBus`. Aquest sistema cobreix les necessitats
// actuals (sarcophagus_opened, level_complete, player_hit, on_tile_entered)
// sense portar-hi senyalització més pesada.
//
// Decisió: `std::variant` per al payload — evita any/void* i facilita
// consumir events tipats amb `std::get_if`. Si apareixen més tipus de
// payload, s'amplia aquí sense tocar cridants existents.
// Tile (col, row) — payload de `on_tile_entered`. No és un `pair<int,int>`
// per evitar alias ambigus al variant (dos ints sense nom).
struct TilePos {
int col{0};
int row{0};
};
struct Event {
std::string name;
using Payload = std::variant<std::monostate, int, std::string, Entity*, TilePos>;
Payload payload;
};
class EventBus {
public:
using Handler = std::function<void(const Event&)>;
using SubId = std::size_t;
EventBus() = default;
EventBus(const EventBus&) = delete;
EventBus(EventBus&&) = delete;
auto operator=(const EventBus&) -> EventBus& = delete;
auto operator=(EventBus&&) -> EventBus& = delete;
~EventBus() = default;
// Retorna un identificador únic per poder-se donar de baixa amb
// `unsubscribe`. Els identificadors s'assignen globalment i no es
// reutilitzen fins a overflow (acceptable per a la vida d'un nivell).
auto subscribe(const std::string& name, Handler handler) -> SubId;
void unsubscribe(SubId id);
// Publica a tots els subscriptors registrats per al nom. Si no n'hi ha
// cap, no fa res.
void publish(const Event& event);
// Forma ergonòmica: construeix un Event amb nom + payload opcional.
void publish(const std::string& name) {
publish(Event{.name = name, .payload = std::monostate{}});
}
void publish(const std::string& name, Entity* entity) {
publish(Event{.name = name, .payload = entity});
}
void publish(const std::string& name, int value) {
publish(Event{.name = name, .payload = value});
}
void publish(const std::string& name, TilePos tile) {
publish(Event{.name = name, .payload = tile});
}
private:
struct Subscription {
SubId id;
Handler handler;
};
std::unordered_map<std::string, std::vector<Subscription>> subs_;
std::uint64_t next_id_{1};
};