Event in lua (partial)
This commit is contained in:
@@ -2,6 +2,7 @@ module('platform')
|
||||
module('input')
|
||||
module('scene')
|
||||
module('locale')
|
||||
module('event')
|
||||
|
||||
-- Default Input bindings.
|
||||
if PLATFORM == "psp" then
|
||||
@@ -40,4 +41,10 @@ end
|
||||
|
||||
-- sceneSet('map')
|
||||
-- mapLoad('map/testmap/testmap.dmf')
|
||||
localeSet(DUSK_LOCALE_EN_US)
|
||||
localeSet(DUSK_LOCALE_EN_US)
|
||||
|
||||
function eventTest()
|
||||
print("Event system is working!")
|
||||
end
|
||||
|
||||
eventSubscribe(EVENT_TEST, eventTest)
|
||||
@@ -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();
|
||||
|
||||
@@ -24,17 +24,34 @@ 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++;
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
14
src/event/eventcallback.h
Normal 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
30
src/event/eventuser.h
Normal 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;
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
51
src/script/module/moduleevent.h
Normal file
51
src/script/module/moduleevent.h
Normal 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
|
||||
);
|
||||
}
|
||||
@@ -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");
|
||||
@@ -195,6 +196,12 @@ errorret_t scriptContextExecFile(scriptcontext_t *ctx, const char_t *fname) {
|
||||
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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
/**
|
||||
|
||||
@@ -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 ( \
|
||||
|
||||
Reference in New Issue
Block a user