VoltMod
C++23 framework for CS2 server plugins
Loading...
Searching...
No Matches
Event.hpp
Go to the documentation of this file.
1#pragma once
2
5#include <cstddef>
6#include <cstdint>
7#include <functional>
8#include <utility>
9
10namespace VoltMod
11{
12
13/**
14 * @brief Starting and stopping an @ref Event source, driven by whether anything is listening.
15 *
16 * @ref OnFirst runs before the first handler is stored; false refuses the subscription, and the
17 * owner is expected to have said why. @ref OnLast runs after the last handler is removed. Both run
18 * on the game thread, and OnLast may run from inside @ref Event::Raise when the last handler drops
19 * itself.
20 */
22{
23 std::function<bool()> OnFirst;
24 std::function<void()> OnLast;
25};
26
27/**
28 * @brief A multicast signal with a fixed handler signature: the one way to subscribe in VoltMod.
29 *
30 * The owner declares it as a public member and is the only caller of @ref Raise. Everyone else
31 * adds a handler with `+=` and keeps the returned @ref Subscription beside the state that handler
32 * captured:
33 *
34 * @code
35 * _spawnSub = runtime.Slots.Changed += [this](int slot) { _cache.Reset(slot); };
36 * @endcode
37 *
38 * A handler never runs after its Subscription drops, not even from inside a @ref Raise already
39 * under way.
40 *
41 * **Lifetime.** The Subscription points at the event, so the event has to outlive it. Declaring
42 * the subscription in the object that owns the handler state gives that for free; storing it above
43 * the service it points at does not.
44 *
45 * **Lazy install.** An event whose source costs something to run - a vtable hook, an engine-wide
46 * callback - takes an @ref EventLifecycle. Subscribing starts the source and dropping the last
47 * subscription stops it; nothing else installs it. An engine hook, alone or feeding several
48 * events, is a @ref LazyHook.
49 *
50 * Not copyable or movable: subscriptions point at one address for their whole life.
51 */
52template <class... Args>
53class Event
54{
55public:
56 using Handler = std::function<void(Args...)>;
57 /** @ref EventLifecycle, reachable as `Event<...>::Lifecycle` where that reads better. */
59
60 Event() = default;
61 explicit Event(Lifecycle lifecycle) : _lifecycle(std::move(lifecycle)) {}
62
63 Event(const Event&) = delete;
64 Event& operator=(const Event&) = delete;
65
66 /** Subscribe @p handler for as long as the returned Subscription lives. An empty Subscription
67 * means nothing was stored: @p handler was empty, or a @ref Lifecycle refused. */
69 {
70 if (!handler)
71 {
72 return {};
73 }
74
75 if (_handlers.Empty() && _lifecycle.OnFirst && !_lifecycle.OnFirst())
76 {
77 return {};
78 }
79
80 const uint64_t id = _handlers.Add(std::move(handler));
81 return Subscription([this, id] {
82 if (_handlers.Remove(id) && _handlers.Empty() && _lifecycle.OnLast)
83 {
84 _lifecycle.OnLast();
85 }
86 });
87 }
88
89 bool Empty() const noexcept { return _handlers.Empty(); }
90 size_t Count() const noexcept { return _handlers.Size(); }
91
92 /**
93 * Invoke every handler. The owner raises; a consumer with a `+=` subscription does not.
94 *
95 * Re-entrancy safe: a handler may subscribe, unsubscribe another, or drop its own
96 * Subscription while this runs. One added during the raise first fires on the next one.
97 */
98 void Raise(Args... args)
99 {
100 _handlers.Dispatch([&](Handler& handler) { handler(args...); });
101 }
102
103private:
105 Lifecycle _lifecycle;
106};
107
108} // namespace VoltMod
void Dispatch(std::invocable< T & > auto &&fn)
A multicast signal with a fixed handler signature: the one way to subscribe in VoltMod.
Definition Event.hpp:54
Event()=default
Subscription operator+=(Handler handler)
Definition Event.hpp:68
size_t Count() const noexcept
Definition Event.hpp:90
Event(const Event &)=delete
std::function< void(Args...)> Handler
Definition Event.hpp:56
void Raise(Args... args)
Definition Event.hpp:98
EventLifecycle Lifecycle
Definition Event.hpp:58
Event(Lifecycle lifecycle)
Definition Event.hpp:61
bool Empty() const noexcept
Definition Event.hpp:89
Event & operator=(const Event &)=delete
Owns one registration and releases it on destruction.
static std::string ReadFile(const std::filesystem::path &path)
Definition Loader.cpp:56
Starting and stopping an Event source, driven by whether anything is listening.
Definition Event.hpp:22
std::function< bool()> OnFirst
Definition Event.hpp:23
std::function< void()> OnLast
Definition Event.hpp:24