ArtifactEngine/Shared/Events.h
2026-04-06 20:37:58 -04:00

91 lines
2.7 KiB
C++

#pragma once
#include <unordered_map>
#include <vector>
#include <functional>
#include <typeindex>
#include <any>
#include <algorithm>
class EventTarget {
public:
template<typename T>
using Callback = std::function<void(const T &)>;
// Subscribe to an event type.
template<typename T>
uint64_t listen(Callback<T> callback) {
auto type = std::type_index(typeid(T));
auto & vec = callbacks[type];
uint64_t id = nextEventId++;
vec.emplace_back([cb = std::move(callback)](const std::any & e) {
cb(std::any_cast<const T &>(e));
});
return id;
}
// Unsubscribe a specific listener.
template<typename T>
void unlisten(uint64_t id) {
_unlisten(std::type_index(typeid(T)), id);
}
// Synchronously dispatch an event.
template<typename T>
void dispatch(const T & event) {
auto it = callbacks.find(std::type_index(typeid(T)));
if (it == callbacks.end()) return;
// Copy the list in case a callback unsubscribes during dispatch.
auto copy = it->second;
for (const auto & cb : copy) {
cb(event);
}
}
// Should be called on a main loop to dispatch queued events.
void processQueue() {
for (auto & [anyEvent, copy] : queued) {
auto it = callbacks.find(anyEvent.type());
if (it != callbacks.end()) {
for (const auto & cb : copy) {
cb(anyEvent);
}
}
}
queued.clear();
}
// Queue an event to be dispatched on the next call to `processQueue`.
template<typename T>
void queue(const T & event) {
auto type = std::type_index(typeid(T));
auto it = callbacks.find(type);
if (it == callbacks.end()) return;
queued.emplace_back(std::any(event), it->second);
}
private:
using AnyCallback = std::function<void(const std::any &)>;
using CallbackWithId = std::pair<AnyCallback, uint64_t>;
std::unordered_map<std::type_index, std::vector<AnyCallback>> callbacks {};
std::vector<std::pair<std::any, std::vector<AnyCallback>>> queued {};
uint64_t nextEventId = 0;
void _unlisten(std::type_index type, uint64_t id) {
// auto it = callbacks.find(type);
// if (it == callbacks.end()) return;
//
// auto & vec = it->second;
// auto erase_it = std::find_if(vec.begin(), vec.end(),
// [id](const auto& p) { return p.second == id; });
//
// if (erase_it != vec.end()) {
// *erase_it = std::move(vec.back());
// vec.pop_back();
// }
}
};