roleplay/client/include/events.h

132 lines
4.8 KiB
C

#ifndef EVENTS_H
#define EVENTS_H
#include "ui.h"
// Source id for events/writes originating from C. Container ids are nonzero
// (load_container rejects 0), so 0 is free to mean "the engine".
#define EVENT_SOURCE_ENGINE 0
// Names under "engine." can only be emitted by C; app.emit rejects them.
// Property writes announce themselves as "engine.changed.<property>".
#define EVENT_ENGINE_PREFIX "engine."
#define EVENT_CHANGED_PREFIX "engine.changed."
// Dispatched directly (never queued) once per drain after the queue is
// exhausted, so handlers reliably run after every change event of the
// frame — the coalescing point for dirty-flag subscribers. Events emitted
// by its handlers queue for the next frame.
#define EVENT_FRAME "engine.frame"
// Max events processed per drain; hitting it means a handler cycle is
// endlessly re-emitting, so the remainder is dumped to stderr and dropped.
#define EVENT_DRAIN_MAX 1024
typedef enum PropertyTypeEnum {
PROPERTY_NUMBER,
PROPERTY_STRING,
PROPERTY_BOOL,
} PropertyType;
typedef struct PropertyStruct {
char* name;
PropertyType type;
union {
double number;
char* string; // owned; NULL until first set
bool boolean;
} value;
} Property;
// C subscriber. The event's packed args table is on the Lua stack at
// args_index (t[1..t.n]); it must still be there when the handler returns.
typedef void (*EventHandler)(
void* userdata,
const char* event,
uint32_t source,
lua_State* L,
int args_index);
typedef struct EventSubscriptionStruct {
char* event;
// Registry ref of the script this subscription was made from, restored as
// the ambient "current script" before invoking a Lua handler — so
// ui.create_overlay called from inside it attributes correctly. Not used
// for lifecycle: subscriptions currently live for the process, since
// nothing unloads a script (only overlays, which are a separate
// lifecycle — see events-design memory).
int script_env;
int lua_ref; // handler function ref, LUA_NOREF for C handlers
EventHandler handler; // NULL for Lua handlers
void* userdata;
// Removal during a drain marks instead of compacting so in-flight
// iteration stays valid; compaction happens when the drain finishes
uint8_t dead;
} EventSubscription;
typedef struct QueuedEventStruct {
char* name;
uint32_t source;
int args_ref; // registry ref to the packed args table
} QueuedEvent;
struct EventBusStruct {
lua_State* L;
EventSubscription* subs;
uint32_t sub_count;
uint32_t sub_cap;
QueuedEvent* queue;
uint32_t queue_count;
uint32_t queue_cap;
Property* properties;
uint32_t property_count;
uint32_t property_cap;
bool draining;
};
// Registers the `app` global (emit/subscribe/get/set) in the Lua state
VkResult event_bus_init(EventBus* bus, lua_State* L);
// Frees subs/queue/properties. Must run while bus->L is still open - it
// luaL_unrefs each subscription's handler and each queued event's args
// table out of the registry.
void event_bus_destroy(EventBus* bus);
// Pops nargs values off the top of the Lua stack into the event's packed
// args and appends to the queue. Never dispatches; the queue drains once
// per frame from the top level.
VkResult event_emit(EventBus* bus, const char* name, uint32_t source, int nargs);
// Subscribes a C handler; Lua handlers subscribe via app.subscribe
VkResult event_subscribe(EventBus* bus, const char* name, EventHandler handler, void* userdata);
// Dispatches queued events in emit order, including events emitted by
// handlers during the drain, up to EVENT_DRAIN_MAX
void event_bus_drain(EventBus* bus, UIContext* ui, RenderContext* gpu, double delta_time);
VkResult event_property_register(EventBus* bus, const char* name, PropertyType type);
// Direct read access; NULL if unregistered
Property* event_property(EventBus* bus, const char* name);
// Setters no-op when the value is unchanged; otherwise they store the value
// and emit engine.changed.<name> with the new value as the single argument.
// Emission is unconditional on real changes regardless of who wrote.
VkResult event_property_set_number(EventBus* bus, const char* name, double value, uint32_t source);
VkResult event_property_set_string(EventBus* bus, const char* name, const char* value, uint32_t source);
VkResult event_property_set_bool(EventBus* bus, const char* name, bool value, uint32_t source);
// Arg accessors for C EventHandler bodies
int event_arg_count(lua_State* L, int args_index);
double event_arg_number(lua_State* L, int args_index, int i);
bool event_arg_bool(lua_State* L, int args_index, int i);
// Returned pointer is anchored by the args table; valid until the drain
// releases the event
const char* event_arg_string(lua_State* L, int args_index, int i);
#endif