Toast Engine
Loading...
Searching...
No Matches
lua_event.hpp
Go to the documentation of this file.
1
7
8#pragma once
9
10#include <concepts>
11#include <functional>
12#include <glm/fwd.hpp>
13#include <lua.hpp>
14#include <luabridge3/LuaBridge/LuaBridge.h>
15#include <memory>
16#include <string>
17#include <string_view>
19#include <toast/events/listener.hpp>
20#include <toast/export.hpp>
21#include <toast/uid.hpp>
22#include <toast/world/box.hpp>
23#include <tuple>
24#include <type_traits>
25#include <utility>
26#include <vector>
27
28namespace toast {
29class Node;
30}
31
32namespace input {
33class Action;
34}
35
36namespace physics {
37struct BroadPhasePair;
38}
39
40namespace event {
41struct ContactEventData;
42}
43
44namespace scripting {
45
47 std::string name;
48};
49
51 using Pusher = std::function<void(lua_State*)>;
52 using Callback = std::function<bool(const Pusher&)>;
53
54 std::string name;
55 std::function<bool(lua_State*, int, std::string&)> send;
56 std::function<void(event::Listener&, std::string, Callback)> subscribe;
57 std::function<void(event::Listener&, std::string_view)> unsubscribe;
58};
59
60class TOAST_API LuaEventRegistry {
61public:
62 static void registerBinding(std::shared_ptr<LuaEventBinding> binding);
63 [[nodiscard]]
64 static auto find(std::string_view name) -> std::shared_ptr<const LuaEventBinding>;
65 [[nodiscard]]
66 static auto descriptors() -> std::vector<LuaEventDescriptor>;
67 static void installDescriptors(lua_State* state);
68};
69
70template<typename Owner, typename Get, typename Set>
72 using value_type = std::remove_cvref_t<std::invoke_result_t<const Get&, const Owner&>>;
73
74 std::string_view name;
75 std::string_view type;
76 Get get;
77 Set set;
78};
79
80template<typename Owner, typename Get, typename Set>
81constexpr auto luaEventField(std::string_view name, std::string_view type, Get get, Set set) -> LuaEventField<Owner, Get, Set> {
82 return {name, type, std::move(get), std::move(set)};
83}
84
85template<typename Owner, typename Member>
86constexpr auto luaEventField(std::string_view name, Member Owner::* member) {
87 return luaEventField<Owner>(
88 name,
89 {},
90 [member](const Owner& owner) -> const Member& { return owner.*member; },
91 [member](Owner& owner, Member value) { owner.*member = std::move(value); }
92 );
93}
94
95namespace _detail {
96TOAST_API auto readEventPrimitive(lua_State* state, int index, bool& out) -> bool;
97TOAST_API auto readEventPrimitive(lua_State* state, int index, int64_t& out) -> bool;
98TOAST_API auto readEventPrimitive(lua_State* state, int index, double& out) -> bool;
99TOAST_API auto readEventPrimitive(lua_State* state, int index, std::string& out) -> bool;
100TOAST_API auto readEventPrimitive(lua_State* state, int index, toast::UID& out) -> bool;
101TOAST_API auto readEventPrimitive(lua_State* state, int index, toast::Box<toast::Node>& out) -> bool;
102TOAST_API auto readEventPrimitive(lua_State* state, int index, toast::Node*& out) -> bool;
103TOAST_API auto readEventPrimitive(lua_State* state, int index, glm::vec2& out) -> bool;
104TOAST_API auto readEventPrimitive(lua_State* state, int index, glm::vec3& out) -> bool;
105TOAST_API auto readEventPrimitive(lua_State* state, int index, glm::vec4& out) -> bool;
106TOAST_API auto readEventPrimitive(lua_State* state, int index, glm::ivec2& out) -> bool;
107TOAST_API auto readEventPrimitive(lua_State* state, int index, glm::ivec3& out) -> bool;
108TOAST_API auto readEventPrimitive(lua_State* state, int index, glm::ivec4& out) -> bool;
109TOAST_API auto readEventPrimitive(lua_State* state, int index, glm::quat& out) -> bool;
110TOAST_API auto readEventHandle(lua_State* state, int index, std::string_view type, assets::HandleBase& out) -> bool;
111
112TOAST_API void pushEventPrimitive(lua_State* state, bool value);
113TOAST_API void pushEventPrimitive(lua_State* state, int64_t value);
114TOAST_API void pushEventPrimitive(lua_State* state, double value);
115TOAST_API void pushEventPrimitive(lua_State* state, std::string_view value);
116TOAST_API void pushEventPrimitive(lua_State* state, toast::UID value);
117TOAST_API void pushEventPrimitive(lua_State* state, const toast::Box<toast::Node>& value);
118TOAST_API void pushEventPrimitive(lua_State* state, const toast::Node* value);
119TOAST_API void pushEventPrimitive(lua_State* state, const glm::vec2& value);
120TOAST_API void pushEventPrimitive(lua_State* state, const glm::vec3& value);
121TOAST_API void pushEventPrimitive(lua_State* state, const glm::vec4& value);
122TOAST_API void pushEventPrimitive(lua_State* state, const glm::ivec2& value);
123TOAST_API void pushEventPrimitive(lua_State* state, const glm::ivec3& value);
124TOAST_API void pushEventPrimitive(lua_State* state, const glm::ivec4& value);
125TOAST_API void pushEventPrimitive(lua_State* state, const glm::quat& value);
126TOAST_API void pushEventPrimitive(lua_State* state, const assets::HandleBase& value);
127TOAST_API void pushEventPrimitive(lua_State* state, const input::Action& value);
128TOAST_API void pushEventPrimitive(lua_State* state, const physics::BroadPhasePair& value);
129TOAST_API void pushEventPrimitive(lua_State* state, const event::ContactEventData& value);
130TOAST_API void pushEventTable(lua_State* state, int field_count);
131TOAST_API void setEventTableField(lua_State* state, std::string_view field);
132
133TOAST_API auto pushEventField(lua_State* state, int table, std::string_view field) -> int;
134TOAST_API auto pushEventElement(lua_State* state, int table, int64_t i) -> int;
135TOAST_API void popEventValue(lua_State* state);
136TOAST_API auto eventArrayLength(lua_State* state, int index) -> int64_t;
137TOAST_API void pushEventArray(lua_State* state, size_t size);
138TOAST_API void setEventArrayElement(lua_State* state, int64_t i);
139
140template<typename T>
141inline constexpr bool is_vector_v = false;
142template<typename T, typename Alloc>
143inline constexpr bool is_vector_v<std::vector<T, Alloc>> = true;
144
145template<typename T>
146inline constexpr bool is_handle_v = false;
147template<typename T>
148inline constexpr bool is_handle_v<assets::Handle<T>> = true;
149
150template<typename T>
151auto readEventIndex(lua_State* state, int index, std::string_view type, T& out) -> bool {
152 if constexpr (!std::same_as<T, bool> && (std::is_enum_v<T> || std::integral<T>)) {
153 int64_t value = 0;
154 if (!readEventPrimitive(state, index, value)) {
155 return false;
156 }
157 out = static_cast<T>(value);
158 return true;
159 } else if constexpr (std::floating_point<T>) {
160 double value = 0.0;
161 if (!readEventPrimitive(state, index, value)) {
162 return false;
163 }
164 out = static_cast<T>(value);
165 return true;
166 } else if constexpr (is_vector_v<T>) {
167 const int64_t count = eventArrayLength(state, index);
168 if (count < 0) {
169 return false;
170 }
171 out.clear();
172 out.reserve(static_cast<size_t>(count));
173 for (int64_t i = 1; i <= count; ++i) {
174 typename T::value_type element {};
175 const bool ok = readEventIndex(state, pushEventElement(state, index, i), type, element);
176 popEventValue(state);
177 if (!ok) {
178 return false;
179 }
180 out.push_back(std::move(element));
181 }
182 return true;
183 } else if constexpr (is_handle_v<T>) {
184 assets::HandleBase handle;
185 if (!readEventHandle(state, index, type, handle)) {
186 return false;
187 }
188 out = T(handle.hasValue() ? handle.operator->() : nullptr, handle.uid(), handle.path());
189 return true;
190 } else {
191 return readEventPrimitive(state, index, out);
192 }
193}
194
195template<typename T>
196auto readEventValue(lua_State* state, int table, std::string_view field, std::string_view type, T& out, std::string& error)
197 -> bool {
198 const bool ok = readEventIndex(state, pushEventField(state, table, field), type, out);
199 popEventValue(state);
200 if (!ok) {
201 error = "invalid or missing field '" + std::string(field) + "'";
202 }
203 return ok;
204}
205
206template<typename T>
207void pushEventValue(lua_State* state, const T& value) {
208 if constexpr (!std::same_as<T, bool> && (std::is_enum_v<T> || std::integral<T>)) {
209 pushEventPrimitive(state, static_cast<int64_t>(value));
210 } else if constexpr (std::floating_point<T>) {
211 pushEventPrimitive(state, static_cast<double>(value));
212 } else if constexpr (std::same_as<T, std::string>) {
213 pushEventPrimitive(state, std::string_view(value));
214 } else if constexpr (is_vector_v<T>) {
215 pushEventArray(state, value.size());
216 int64_t i = 0;
217 for (const auto& element : value) {
218 pushEventValue<typename T::value_type>(state, element);
219 setEventArrayElement(state, ++i);
220 }
221 } else if constexpr (is_handle_v<T>) {
222 pushEventPrimitive(state, static_cast<const assets::HandleBase&>(value));
223 } else {
224 pushEventPrimitive(state, value);
225 }
226}
227
228template<typename Event, typename... Fields, size_t... I>
229auto sendEvent(lua_State* state, int payload, std::string& error, const std::tuple<Fields...>& fields, std::index_sequence<I...>)
230 -> bool {
231 std::tuple<typename Fields::value_type...> values;
232 const bool valid =
233 (readEventValue(state, payload, std::get<I>(fields).name, std::get<I>(fields).type, std::get<I>(values), error) && ...);
234 if (!valid) {
235 return false;
236 }
237 if constexpr (std::is_constructible_v<Event, decltype(std::move(std::get<I>(values)))...>) {
238 std::apply([](auto&&... args) { event::send<Event>(std::move(args)...); }, values);
239 } else {
240 Event built {};
241 (std::get<I>(fields).set(built, std::move(std::get<I>(values))), ...);
242 event::send<Event>(std::move(built));
243 }
244 return true;
245}
246
247template<typename Event, typename... Fields, size_t... I>
248void pushEvent(lua_State* state, const Event& value, const std::tuple<Fields...>& fields, std::index_sequence<I...>) {
249 pushEventTable(state, static_cast<int>(sizeof...(Fields)));
250 ((pushEventValue(state, std::get<I>(fields).get(value)), setEventTableField(state, std::get<I>(fields).name)), ...);
251}
252}
253
258template<typename Event, bool Sendable = true, typename... Fields>
259void registerLuaEvent(std::string_view name, Fields... fields) {
260 if constexpr (Sendable) {
261 static_assert(
262 std::is_default_constructible_v<Event> || std::is_constructible_v<Event, typename Fields::value_type...>,
263 "Lua events need a constructor taking the public fields in order, or a default constructor"
264 );
265 }
266 auto metadata = std::make_shared<std::tuple<Fields...>>(fields...);
267 auto binding = std::make_shared<LuaEventBinding>();
268 binding->name = name;
269 if constexpr (Sendable) {
270 binding->send = [metadata](lua_State* state, int payload, std::string& error) {
271 return _detail::sendEvent<Event>(state, payload, error, *metadata, std::index_sequence_for<Fields...> {});
272 };
273 }
274 binding->subscribe = [metadata](event::Listener& listener, std::string subscription_name, LuaEventBinding::Callback callback) {
275 listener.subscribe<Event>(std::move(subscription_name), [metadata, callback = std::move(callback)](Event& value) {
276 return callback([metadata, &value](lua_State* state) {
277 _detail::pushEvent(state, value, *metadata, std::index_sequence_for<Fields...> {});
278 });
279 });
280 };
281 binding->unsubscribe = [](event::Listener& listener, std::string_view subscription_name) {
282 listener.unsubscribe<Event>(subscription_name);
283 };
284 LuaEventRegistry::registerBinding(std::move(binding));
285}
286
287class ListenerProxy {
288public:
289 ListenerProxy() = default;
290
291 explicit ListenerProxy(toast::Box<toast::Node> node) : m_node(std::move(node)) { }
292
293 void subscribe(const LuaEventDescriptor& descriptor, const luabridge::LuaRef& callback, lua_State* state);
294 void unsubscribe(const LuaEventDescriptor& descriptor);
295 void unsubscribe(const LuaEventDescriptor& descriptor, const std::string& method);
296
297private:
299};
300
301void clearLuaEventSubscriptions(void* runtime) noexcept;
302void clearAllLuaEventSubscriptions() noexcept;
303
304}
pseudo shared pointer for Nodes
manages the lifetime, subscription, and unsubscription of callbacks
Definition listener.hpp:39
void unsubscribe(std::string_view name) noexcept
unsubscribes every event in type TEvent with that name
Definition listener.inl:10
void subscribe(std::string name, F &&callback, char priority=0) noexcept
subscribes a callback to an TEvent
Definition listener.inl:24
Definition action.hpp:21
Definition lua_event.hpp:60
Intrusive reference-counted handle to a Node.
Definition box.hpp:35
Definition node.hpp:81
Base types for the asset system.
void registerLuaEvent(std::string_view name, Fields... fields)
Definition lua_event.hpp:259
Definition contact_events.hpp:18
Definition collision.hpp:24
Definition lua_event.hpp:50
Definition lua_event.hpp:46
Definition lua_event.hpp:71
Unique identifier for everything that exists in our engine.