Event in lua (partial)
Some checks failed
Build Dusk / run-tests (push) Failing after 2m30s
Build Dusk / build-linux (push) Failing after 2m37s
Build Dusk / build-psp (push) Failing after 1m54s

This commit is contained in:
2026-01-27 12:47:15 -06:00
parent 9b73f1717f
commit 6e78ee188d
12 changed files with 345 additions and 19 deletions

View File

@@ -2,6 +2,7 @@ module('platform')
module('input')
module('scene')
module('locale')
module('event')
-- Default Input bindings.
if PLATFORM == "psp" then
@@ -41,3 +42,9 @@ end
-- sceneSet('map')
-- mapLoad('map/testmap/testmap.dmf')
localeSet(DUSK_LOCALE_EN_US)
function eventTest()
print("Event system is working!")
end
eventSubscribe(EVENT_TEST, eventTest)

View File

@@ -27,7 +27,6 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
ENGINE.argc = argc;
ENGINE.argv = argv;
// Init systems. Order is important.
timeInit();
inputInit();

View File

@@ -24,18 +24,35 @@ void eventInit(
event->maxListeners = maxListeners;
}
eventsub_t eventSubscribe(
eventsub_t eventSubscribeUser(
event_t *event,
const eventcallback_t callback,
const void *user
const eventtype_t type,
const eventuserdata_t user
) {
assertNotNull(event, "Event cannot be NULL");
assertNotNull(callback, "Callback cannot be NULL");
assertTrue(
event->listenerCount < event->maxListeners,
"Maximum number of listeners reached"
);
if(type == EVENT_TYPE_C) {
assertNotNull(
user.c.callback,
"C event listener callback cannot be NULL"
);
} else if(type == EVENT_TYPE_SCRIPT) {
assertNotNull(
user.script.context,
"Script event listener context cannot be NULL"
);
assertTrue(
user.script.luaFunctionRef != LUA_NOREF,
"Script event listener function reference is invalid"
);
} else {
assertUnreachable("Unknown event listener type");
}
// Gen a new ID
eventsub_t id = event->nextId++;
// Did we wrap?
@@ -44,13 +61,86 @@ eventsub_t eventSubscribe(
// Append listener
eventlistener_t *listener = &event->listenerArray[event->listenerCount++];
memoryZero(listener, sizeof(eventlistener_t));
listener->callback = callback;
listener->user = (void*)user;
listener->user = user;
listener->id = id;
listener->type = type;
return id;
}
eventsub_t eventSubscribe(
event_t *event,
const eventcallback_t callback,
const void *user
) {
eventSubscribeUser(
event,
EVENT_TYPE_C,
(eventuserdata_t){ .c = { .callback = callback, .user = (void *)user } }
);
}
eventsub_t eventSubscribeScriptContext(
event_t *event,
scriptcontext_t *context,
const int functionIndex
) {
assertNotNull(context, "Script context cannot be NULL");
assertTrue(
lua_isfunction(context->luaState, functionIndex),
"Expected function at given index"
);
// Create a reference to the function
lua_pushvalue(context->luaState, functionIndex);
int funcRef = luaL_ref(context->luaState, LUA_REGISTRYINDEX);
eventscript_t scriptUser = {
.context = context,
.luaFunctionRef = funcRef
};
// Note to the context that it is now a part of this event
bool_t alreadySubbed = false;
uint8_t i;
i = 0;
do {
if(context->subscribedEvents[i] != event) {
i++;
continue;
}
if(context->subscribedEvents[i] == NULL) break;
alreadySubbed = true;
break;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
if(!alreadySubbed) {
i = 0;
do {
if(context->subscribedEvents[i] != NULL) {
i++;
continue;
}
context->subscribedEvents[i] = event;
break;
} while(i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS);
assertTrue(
i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS,
"Script context has reached maximum event subscriptions"
);
}
return eventSubscribeUser(
event,
EVENT_TYPE_SCRIPT,
(eventuserdata_t){ .script = scriptUser }
);
}
void eventUnsubscribe(event_t *event, const eventsub_t id) {
assertNotNull(event, "Event cannot be NULL");
assertFalse(event->isInvoking, "Cannot unsubscribe while invoking event");
@@ -83,6 +173,29 @@ void eventUnsubscribe(event_t *event, const eventsub_t id) {
event->listenerCount--;
}
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
assertNotNull(event, "Event cannot be NULL");
assertNotNull(ctx, "Script context cannot be NULL");
if(event->listenerCount == 0) return;
uint16_t i = 0;
do {
eventlistener_t *listener = &event->listenerArray[i];
if(
listener->type != EVENT_TYPE_SCRIPT ||
listener->user.script.context != ctx
) {
i++;
continue;
}
// This listener belongs to the context and will need to go away. This will
// in turn decrement the listener count so we don't increment i here.
eventUnsubscribe(event, listener->id);
} while(i < event->listenerCount);
}
void eventInvoke(event_t *event, const void *eventParams) {
assertNotNull(event, "Event cannot be NULL");
@@ -97,8 +210,30 @@ void eventInvoke(event_t *event, const void *eventParams) {
};
do {
data.user = event->listenerArray[i].user;
event->listenerArray[i].callback(&data);
eventlistener_t *listener = &event->listenerArray[i];
if(listener->type == EVENT_TYPE_C) {
listener->user.c.callback(&data, listener->user.c);
} else if(listener->type == EVENT_TYPE_SCRIPT) {
// Call Lua function
lua_State *L = listener->user.script.context->luaState;
assertNotNull(L, "Lua state in event listener cannot be NULL");
// Push function
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
// Push eventdata as lightuserdata
lua_pushlightuserdata(L, &data);
// Call function with 1 arg, 0 return values
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
const char_t *strErr = lua_tostring(L, -1);
lua_pop(L, 1);
// Log error but continue
printf("Error invoking Lua event listener: %s\n", strErr);
}
} else {
assertUnreachable("Unknown event listener type");
}
i++;
} while(i < event->listenerCount);

View File

@@ -6,21 +6,18 @@
*/
#pragma once
#include "dusk.h"
#include "eventuser.h"
typedef struct event_s event_t;
typedef struct {
void *user;
typedef struct eventdata_s {
const void *eventParams;
const event_t *event;
} eventdata_t;
typedef void (*eventcallback_t)(eventdata_t *data);
typedef struct {
eventcallback_t callback;
void *user;
typedef struct eventlistener_s {
eventuserdata_t user;
eventtype_t type;
uint16_t id;
} eventlistener_t;
@@ -52,16 +49,44 @@ void eventInit(
* Subscribe to an event.
*
* @param event The event to subscribe to.
* @param callback The callback function to invoke.
* @param type The type of the event (C or Lua).
* @param user User data to pass to the callback.
* @return The subscription ID, used to unsubscribe later.
*/
eventsub_t eventSubscribeUser(
event_t *event,
const eventtype_t type,
const eventuserdata_t user
);
/**
* Subscribe to an event with a C function callback.
*
* @param event The event to subscribe to.
* @param callback The C function callback.
* @param user User data pointer to pass to the callback.
* @return The subscription ID, used to unsubscribe later.
*/
eventsub_t eventSubscribe(
event_t *event,
const eventcallback_t callback,
const void *user
);
/**
* Subscribe to an event with a script function callback.
*
* @param event The event to subscribe to.
* @param context The script context.
* @param functionIndex The index of the Lua function on the stack.
* @return The subscription ID, used to unsubscribe later.
*/
eventsub_t eventSubscribeScriptContext(
event_t *event,
scriptcontext_t *context,
const int functionIndex
);
/**
* Unsubscribe from an event.
*
@@ -70,6 +95,14 @@ eventsub_t eventSubscribe(
*/
void eventUnsubscribe(event_t *event, const eventsub_t subscription);
/**
* Unsubscribe all event listeners associated with a script context.
*
* @param event The event to unsubscribe from.
* @param context The script context whose listeners should be removed.
*/
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx);
/**
* Invoke an event, calling all subscribed listeners.
*

14
src/event/eventcallback.h Normal file
View File

@@ -0,0 +1,14 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "dusk.h"
typedef struct eventdata_s eventdata_t;
typedef struct eventc_s eventc_t;
typedef void (*eventcallback_t)(eventdata_t *data, eventc_t user);

30
src/event/eventuser.h Normal file
View File

@@ -0,0 +1,30 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "eventcallback.h"
#include "script/scriptcontext.h"
typedef enum {
EVENT_TYPE_C = 0,
EVENT_TYPE_SCRIPT = 1
} eventtype_t;
typedef struct {
scriptcontext_t *context;
int luaFunctionRef;
} eventscript_t;
typedef struct eventc_s {
void *user;
eventcallback_t callback;
} eventc_t;
typedef union eventuserdata_u {
eventscript_t script;
eventc_t c;
} eventuserdata_t;

View File

@@ -24,6 +24,13 @@ void inputInit(void) {
}
INPUT.deadzone = 0.2f;
eventInit(
&INPUT.eventPressed, INPUT.pressedListeners, INPUT_LISTENER_PRESSED_MAX
);
eventInit(
&INPUT.eventReleased, INPUT.releasedListeners, INPUT_LISTENER_RELEASED_MAX
);
}
void inputUpdate(void) {
@@ -89,6 +96,29 @@ void inputUpdate(void) {
cur++;
} while(cur->name);
// Do we need to fire off events?
if(INPUT.eventPressed.listenerCount > 0) {
action = &INPUT.actions[0];
do {
if(inputPressed(action->action)) {
inputevent_t inputEvent = { .action = action->action };
eventInvoke(&INPUT.eventPressed, &inputEvent);
}
action++;
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
}
if(INPUT.eventReleased.listenerCount > 0) {
action = &INPUT.actions[0];
do {
if(inputReleased(action->action)) {
inputevent_t inputEvent = { .action = action->action };
eventInvoke(&INPUT.eventReleased, &inputEvent);
}
action++;
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
}
}
float_t inputGetCurrentValue(const inputaction_t action) {

View File

@@ -8,10 +8,23 @@
#pragma once
#include "inputbutton.h"
#include "inputaction.h"
#include "event/event.h"
#define INPUT_LISTENER_PRESSED_MAX 16
#define INPUT_LISTENER_RELEASED_MAX INPUT_LISTENER_PRESSED_MAX
typedef struct {
const inputaction_t action;
} inputevent_t;
typedef struct {
inputactiondata_t actions[INPUT_ACTION_COUNT];
eventlistener_t pressedListeners[INPUT_LISTENER_PRESSED_MAX];
event_t eventPressed;
eventlistener_t releasedListeners[INPUT_LISTENER_RELEASED_MAX];
event_t eventReleased;
#if INPUT_GAMEPAD == 1
float_t deadzone;
#endif

View File

@@ -0,0 +1,51 @@
/**
* Copyright (c) 2026 Dominic Masters
*
* This software is released under the MIT License.
* https://opensource.org/licenses/MIT
*/
#pragma once
#include "script/scriptcontext.h"
#include "event/event.h"
#include "engine/engine.h"
int moduleEventSubscribe(lua_State *L) {
assertNotNull(L, "Lua state cannot be NULL");
// State has user pointer to owning scriptcontext_t
scriptcontext_t *context = *(scriptcontext_t **)lua_getextraspace(L);
assertNotNull(context, "Script context cannot be NULL");
// Expecting event pointer
if(!lua_islightuserdata(L, 1)) {
luaL_error(L, "eventSubscribe: Expected event pointer as first argument");
return 0;
}
// Expecting callback function (Lua function)
if(!lua_isfunction(L, 2)) {
luaL_error(L, "eventSubscribe: Expected function as second argument");
return 0;
}
event_t *event = (event_t *)lua_touserdata(L, 1);
assertNotNull(event, "Event cannot be NULL");
eventsub_t id = eventSubscribeScriptContext(event, context, 2);
// Pass back to lua.
lua_pushinteger(L, id);
return 1;
}
void moduleEvent(scriptcontext_t *context) {
scriptContextRegFunc(context, "eventSubscribe", moduleEventSubscribe);
// register test event constant
scriptContextRegPointer(
context,
"EVENT_TEST",
(void *)&EVENT_TEST
);
}

View File

@@ -11,6 +11,7 @@
#include "util/memory.h"
#include "debug/debug.h"
#include "script/scriptmodule.h"
#include "event/event.h"
errorret_t scriptContextInit(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
@@ -196,6 +197,12 @@ void scriptContextDispose(scriptcontext_t *context) {
assertNotNull(context, "Script context cannot be NULL");
assertNotNull(context->luaState, "Lua state is not initialized");
for(uint8_t i = 0; i < SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS; i++) {
event_t *event = context->subscribedEvents[i];
if(event == NULL) continue;
eventUnsubscribeScriptContext(event, context);
}
if(context->luaState != NULL) {
lua_close(context->luaState);
context->luaState = NULL;

View File

@@ -12,8 +12,13 @@
#include <lauxlib.h>
#include <lualib.h>
typedef struct event_s event_t;
#define SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS 64
typedef struct scriptcontext_s {
lua_State *luaState;
event_t* subscribedEvents[SCRIPT_CONTEXT_MAX_EVENT_SUBSCRIPTIONS];
} scriptcontext_t;
/**

View File

@@ -13,6 +13,7 @@
#include "script/module/moduleitem.h"
#include "script/module/modulelocale.h"
#include "script/module/moduletime.h"
#include "script/module/moduleevent.h"
const scriptmodule_t SCRIPT_MODULE_LIST[] = {
{ .name = "system", .callback = moduleSystem },
@@ -22,6 +23,7 @@ const scriptmodule_t SCRIPT_MODULE_LIST[] = {
{ .name = "item", .callback = moduleItem },
{ .name = "locale", .callback = moduleLocale },
{ .name = "time", .callback = moduleTime },
{ .name = "event", .callback = moduleEvent },
};
#define SCRIPT_MODULE_COUNT ( \