Add struct metafield
This commit is contained in:
@@ -43,8 +43,9 @@ end
|
|||||||
-- mapLoad('map/testmap/testmap.dmf')
|
-- mapLoad('map/testmap/testmap.dmf')
|
||||||
localeSet(DUSK_LOCALE_EN_US)
|
localeSet(DUSK_LOCALE_EN_US)
|
||||||
|
|
||||||
function eventTest()
|
function eventTest(data)
|
||||||
print("Event system is working!")
|
print("Pressed")
|
||||||
|
data.action = 2
|
||||||
end
|
end
|
||||||
|
|
||||||
eventSubscribe(EVENT_TEST, eventTest)
|
eventSubscribe(INPUT_EVENT_PRESSED, eventTest)
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
#include "item/backpack.h"
|
#include "item/backpack.h"
|
||||||
|
|
||||||
engine_t ENGINE;
|
engine_t ENGINE;
|
||||||
|
scriptcontext_t ctx;
|
||||||
|
|
||||||
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
||||||
memoryZero(&ENGINE, sizeof(engine_t));
|
memoryZero(&ENGINE, sizeof(engine_t));
|
||||||
@@ -41,10 +42,8 @@ errorret_t engineInit(const int32_t argc, const char_t **argv) {
|
|||||||
backpackInit();
|
backpackInit();
|
||||||
|
|
||||||
// Run the initial script.
|
// Run the initial script.
|
||||||
scriptcontext_t ctx;
|
|
||||||
errorChain(scriptContextInit(&ctx));
|
errorChain(scriptContextInit(&ctx));
|
||||||
errorChain(scriptContextExecFile(&ctx, "init.dsf"));
|
errorChain(scriptContextExecFile(&ctx, "init.dsf"));
|
||||||
scriptContextDispose(&ctx);
|
|
||||||
|
|
||||||
errorOk();
|
errorOk();
|
||||||
}
|
}
|
||||||
@@ -68,6 +67,8 @@ void engineExit(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
errorret_t engineDispose(void) {
|
errorret_t engineDispose(void) {
|
||||||
|
scriptContextDispose(&ctx);
|
||||||
|
|
||||||
localeManagerDispose();
|
localeManagerDispose();
|
||||||
// sceneManagerDispose();
|
// sceneManagerDispose();
|
||||||
// rpgDispose();
|
// rpgDispose();
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include "event.h"
|
#include "event.h"
|
||||||
#include "assert/assert.h"
|
#include "assert/assert.h"
|
||||||
#include "util/memory.h"
|
#include "util/memory.h"
|
||||||
|
#include "script/scriptstruct.h"
|
||||||
|
|
||||||
void eventInit(
|
void eventInit(
|
||||||
event_t *event,
|
event_t *event,
|
||||||
@@ -196,7 +197,11 @@ void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx) {
|
|||||||
} while(i < event->listenerCount);
|
} while(i < event->listenerCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
void eventInvoke(event_t *event, const void *eventParams) {
|
void eventInvoke(
|
||||||
|
event_t *event,
|
||||||
|
const void *eventParams,
|
||||||
|
const char_t *metatableName
|
||||||
|
) {
|
||||||
assertNotNull(event, "Event cannot be NULL");
|
assertNotNull(event, "Event cannot be NULL");
|
||||||
|
|
||||||
if(event->listenerCount == 0) return;
|
if(event->listenerCount == 0) return;
|
||||||
@@ -221,8 +226,14 @@ void eventInvoke(event_t *event, const void *eventParams) {
|
|||||||
|
|
||||||
// Push function
|
// Push function
|
||||||
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
|
lua_rawgeti(L, LUA_REGISTRYINDEX, listener->user.script.luaFunctionRef);
|
||||||
// Push eventdata as lightuserdata
|
|
||||||
lua_pushlightuserdata(L, &data);
|
if(eventParams != NULL && metatableName != NULL) {
|
||||||
|
scriptStructPush(
|
||||||
|
listener->user.script.context,
|
||||||
|
metatableName,
|
||||||
|
(void *)eventParams
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Call function with 1 arg, 0 return values
|
// Call function with 1 arg, 0 return values
|
||||||
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
|
if(lua_pcall(L, 1, 0, 0) != LUA_OK) {
|
||||||
|
|||||||
@@ -104,9 +104,18 @@ void eventUnsubscribe(event_t *event, const eventsub_t subscription);
|
|||||||
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx);
|
void eventUnsubscribeScriptContext(event_t *event, const scriptcontext_t *ctx);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoke an event, calling all subscribed listeners.
|
* Invoke an event, calling all subscribed listeners. Optionally provide event
|
||||||
|
* parameters, and if desired pass a metatable name. Event listeners will be
|
||||||
|
* able to read event params, and if metatable name is provided the script
|
||||||
|
* listeners will lookup metatable information matching that name to access the
|
||||||
|
* params as a structure within the script.
|
||||||
*
|
*
|
||||||
* @param event The event to invoke.
|
* @param event The event to invoke.
|
||||||
* @param eventParams Parameters to pass to the event listeners.
|
* @param eventParams Parameters to pass to the event listeners.
|
||||||
|
* @param metatableName Metatable name. See details.
|
||||||
*/
|
*/
|
||||||
void eventInvoke(event_t *event, const void *eventParams);
|
void eventInvoke(
|
||||||
|
event_t *event,
|
||||||
|
const void *eventParams,
|
||||||
|
const char_t *metatableName
|
||||||
|
);
|
||||||
@@ -98,12 +98,14 @@ void inputUpdate(void) {
|
|||||||
} while(cur->name);
|
} while(cur->name);
|
||||||
|
|
||||||
// Do we need to fire off events?
|
// Do we need to fire off events?
|
||||||
|
if(TIME.dynamicUpdate) return;
|
||||||
|
|
||||||
if(INPUT.eventPressed.listenerCount > 0) {
|
if(INPUT.eventPressed.listenerCount > 0) {
|
||||||
action = &INPUT.actions[0];
|
action = &INPUT.actions[0];
|
||||||
do {
|
do {
|
||||||
if(inputPressed(action->action)) {
|
if(inputPressed(action->action)) {
|
||||||
inputevent_t inputEvent = { .action = action->action };
|
inputevent_t inputEvent = { .action = action->action };
|
||||||
eventInvoke(&INPUT.eventPressed, &inputEvent);
|
eventInvoke(&INPUT.eventPressed, &inputEvent, "input_mt");
|
||||||
}
|
}
|
||||||
action++;
|
action++;
|
||||||
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||||
@@ -114,7 +116,7 @@ void inputUpdate(void) {
|
|||||||
do {
|
do {
|
||||||
if(inputReleased(action->action)) {
|
if(inputReleased(action->action)) {
|
||||||
inputevent_t inputEvent = { .action = action->action };
|
inputevent_t inputEvent = { .action = action->action };
|
||||||
eventInvoke(&INPUT.eventReleased, &inputEvent);
|
eventInvoke(&INPUT.eventReleased, &inputEvent, "input_mt");
|
||||||
}
|
}
|
||||||
action++;
|
action++;
|
||||||
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
} while(action < &INPUT.actions[INPUT_ACTION_COUNT]);
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ target_sources(${DUSK_LIBRARY_TARGET_NAME}
|
|||||||
scriptmanager.c
|
scriptmanager.c
|
||||||
scriptcontext.c
|
scriptcontext.c
|
||||||
scriptmodule.c
|
scriptmodule.c
|
||||||
|
scriptstruct.c
|
||||||
)
|
)
|
||||||
@@ -40,12 +40,6 @@ int moduleEventSubscribe(lua_State *L) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void moduleEvent(scriptcontext_t *context) {
|
void moduleEvent(scriptcontext_t *context) {
|
||||||
|
// Reg functions
|
||||||
scriptContextRegFunc(context, "eventSubscribe", moduleEventSubscribe);
|
scriptContextRegFunc(context, "eventSubscribe", moduleEventSubscribe);
|
||||||
|
|
||||||
// register test event constant
|
|
||||||
scriptContextRegPointer(
|
|
||||||
context,
|
|
||||||
"EVENT_TEST",
|
|
||||||
(void *)&EVENT_TEST
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "script/scriptcontext.h"
|
#include "script/scriptcontext.h"
|
||||||
#include "input/input.h"
|
#include "input/input.h"
|
||||||
|
#include "script/scriptstruct.h"
|
||||||
|
|
||||||
int moduleInputBind(lua_State *L) {
|
int moduleInputBind(lua_State *L) {
|
||||||
assertNotNull(L, "Lua state cannot be NULL");
|
assertNotNull(L, "Lua state cannot be NULL");
|
||||||
@@ -44,6 +45,19 @@ int moduleInputBind(lua_State *L) {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void moduleInputEventGetter(
|
||||||
|
const scriptcontext_t *context,
|
||||||
|
const char_t *key,
|
||||||
|
const void *structPtr,
|
||||||
|
scriptvalue_t *outValue
|
||||||
|
) {
|
||||||
|
if(stringCompare(key, "action") == 0) {
|
||||||
|
outValue->type = SCRIPT_VALUE_TYPE_INT;
|
||||||
|
outValue->value.intValue = ((const inputevent_t*)structPtr)->action;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void moduleInput(scriptcontext_t *context) {
|
void moduleInput(scriptcontext_t *context) {
|
||||||
assertNotNull(context, "Script context cannot be NULL");
|
assertNotNull(context, "Script context cannot be NULL");
|
||||||
|
|
||||||
@@ -63,6 +77,18 @@ void moduleInput(scriptcontext_t *context) {
|
|||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Script structure
|
||||||
|
scriptStructRegister(
|
||||||
|
context,
|
||||||
|
"input_mt",
|
||||||
|
&moduleInputEventGetter,
|
||||||
|
NULL
|
||||||
|
);
|
||||||
|
|
||||||
|
// Events
|
||||||
|
scriptContextRegPointer(context,"INPUT_EVENT_PRESSED",&INPUT.eventPressed);
|
||||||
|
scriptContextRegPointer(context,"INPUT_EVENT_RELEASED",&INPUT.eventReleased);
|
||||||
|
|
||||||
// Bind methods
|
// Bind methods
|
||||||
scriptContextRegFunc(context, "inputBind", moduleInputBind);
|
scriptContextRegFunc(context, "inputBind", moduleInputBind);
|
||||||
}
|
}
|
||||||
165
src/script/scriptstruct.c
Normal file
165
src/script/scriptstruct.c
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "scriptstruct.h"
|
||||||
|
#include "assert/assert.h"
|
||||||
|
|
||||||
|
void scriptStructRegister(
|
||||||
|
scriptcontext_t *context,
|
||||||
|
const char_t *metatableName,
|
||||||
|
const scriptstructgetter_t getter,
|
||||||
|
const scriptstructsetter_t setter
|
||||||
|
) {
|
||||||
|
assertNotNull(context, "Script context cannot be NULL");
|
||||||
|
assertNotNull(metatableName, "Metatable name cannot be NULL");
|
||||||
|
|
||||||
|
// Create metatable
|
||||||
|
if(!luaL_newmetatable(context->luaState, metatableName)) return;
|
||||||
|
|
||||||
|
// Create a Lua owned structure for holding the metatable context.
|
||||||
|
structmetatablecontext_t *metaContext = lua_newuserdata(
|
||||||
|
context->luaState,
|
||||||
|
sizeof(structmetatablecontext_t)
|
||||||
|
);
|
||||||
|
|
||||||
|
metaContext->context = context;
|
||||||
|
metaContext->getter = getter;
|
||||||
|
metaContext->setter = setter;
|
||||||
|
|
||||||
|
// Store in the metatable.
|
||||||
|
lua_setfield(context->luaState, -2, "__structmetatablecontext");
|
||||||
|
|
||||||
|
// Set __index and __newindex metamethods for Lua.
|
||||||
|
lua_pushcfunction(context->luaState, scriptStructIndex);
|
||||||
|
lua_setfield(context->luaState, -2, "__index");
|
||||||
|
|
||||||
|
lua_pushcfunction(context->luaState, scriptStructNewIndex);
|
||||||
|
lua_setfield(context->luaState, -2, "__newindex");
|
||||||
|
}
|
||||||
|
|
||||||
|
int scriptStructIndex(lua_State *l) {
|
||||||
|
structmetatablecontext_t *ctx = scriptStructGetMetatableContext(l);
|
||||||
|
if(!ctx->getter) {
|
||||||
|
luaL_error(l, "Attempt to read from write-only structure field");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char_t *key = lua_tostring(l, 2);
|
||||||
|
|
||||||
|
void *structPtr = *(void **)lua_touserdata(l, 1);
|
||||||
|
assertNotNull(structPtr, "Structure pointer cannot be NULL");
|
||||||
|
|
||||||
|
scriptvalue_t outValue = { .type = SCRIPT_VALUE_TYPE_NIL };
|
||||||
|
ctx->getter(ctx->context, key, structPtr, &outValue);
|
||||||
|
|
||||||
|
switch(outValue.type) {
|
||||||
|
case SCRIPT_VALUE_TYPE_INT:
|
||||||
|
lua_pushinteger(l, outValue.value.intValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCRIPT_VALUE_TYPE_FLOAT:
|
||||||
|
lua_pushnumber(l, outValue.value.floatValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCRIPT_VALUE_TYPE_STRING:
|
||||||
|
lua_pushstring(l, outValue.value.strValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SCRIPT_VALUE_TYPE_BOOL:
|
||||||
|
lua_pushboolean(l, outValue.value.boolValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
lua_pushnil(l);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int scriptStructNewIndex(lua_State *l) {
|
||||||
|
structmetatablecontext_t *ctx = scriptStructGetMetatableContext(l);
|
||||||
|
if(ctx->setter == NULL) {
|
||||||
|
luaL_error(l, "Attempt to set read-only structure field");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char_t *key = lua_tostring(l, 2);
|
||||||
|
|
||||||
|
void *structPtr = *(void **)lua_touserdata(l, 1);
|
||||||
|
assertNotNull(structPtr, "Structure pointer cannot be NULL");
|
||||||
|
|
||||||
|
scriptvalue_t inValue;
|
||||||
|
int t = lua_type(l, 3);
|
||||||
|
switch(t) {
|
||||||
|
case LUA_TNUMBER:
|
||||||
|
if(lua_isinteger(l, 3)) {
|
||||||
|
inValue.type = SCRIPT_VALUE_TYPE_INT;
|
||||||
|
inValue.value.intValue = (int32_t)lua_tointeger(l, 3);
|
||||||
|
} else {
|
||||||
|
inValue.type = SCRIPT_VALUE_TYPE_FLOAT;
|
||||||
|
inValue.value.floatValue = (float)lua_tonumber(l, 3);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUA_TSTRING:
|
||||||
|
inValue.type = SCRIPT_VALUE_TYPE_STRING;
|
||||||
|
inValue.value.strValue = lua_tostring(l, 3);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LUA_TBOOLEAN:
|
||||||
|
inValue.type = SCRIPT_VALUE_TYPE_BOOL;
|
||||||
|
inValue.value.boolValue = lua_toboolean(l, 3);
|
||||||
|
break;
|
||||||
|
case LUA_TNIL:
|
||||||
|
inValue.type = SCRIPT_VALUE_TYPE_NIL;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
assertUnreachable("Unsupported value type for struct field assignment");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx->setter(ctx->context, key, structPtr, &inValue);
|
||||||
|
|
||||||
|
lua_pushnil(l);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
structmetatablecontext_t * scriptStructGetMetatableContext(lua_State *L) {
|
||||||
|
assertNotNull(L, "Lua state cannot be NULL");
|
||||||
|
|
||||||
|
if(!lua_getmetatable(L, 1)) {
|
||||||
|
assertUnreachable("Expected metatable on structure");
|
||||||
|
}
|
||||||
|
|
||||||
|
lua_getfield(L, -1, "__structmetatablecontext");
|
||||||
|
structmetatablecontext_t *metaContext = (
|
||||||
|
(structmetatablecontext_t *)lua_touserdata(L, -1)
|
||||||
|
);
|
||||||
|
assertNotNull(metaContext, "Metatable context userdata cannot be NULL");
|
||||||
|
lua_pop(L, 2);
|
||||||
|
return metaContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
void scriptStructPush(
|
||||||
|
scriptcontext_t *context,
|
||||||
|
const char_t *metatableName,
|
||||||
|
void *structPtr
|
||||||
|
) {
|
||||||
|
assertNotNull(context, "Script context cannot be NULL");
|
||||||
|
assertNotNull(metatableName, "Metatable name cannot be NULL");
|
||||||
|
assertNotNull(structPtr, "Structure pointer cannot be NULL");
|
||||||
|
|
||||||
|
// Create userdata
|
||||||
|
void **ud = (void **)lua_newuserdata(context->luaState, sizeof(void *));
|
||||||
|
*ud = structPtr;
|
||||||
|
|
||||||
|
// Set metatable
|
||||||
|
luaL_getmetatable(context->luaState, metatableName);
|
||||||
|
lua_setmetatable(context->luaState, -2);
|
||||||
|
}
|
||||||
85
src/script/scriptstruct.h
Normal file
85
src/script/scriptstruct.h
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
/**
|
||||||
|
* Copyright (c) 2026 Dominic Masters
|
||||||
|
*
|
||||||
|
* This software is released under the MIT License.
|
||||||
|
* https://opensource.org/licenses/MIT
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include "scriptcontext.h"
|
||||||
|
|
||||||
|
typedef void (*scriptstructgetter_t)(
|
||||||
|
const scriptcontext_t *context,
|
||||||
|
const char_t *key,
|
||||||
|
const void *structPtr,
|
||||||
|
scriptvalue_t *outValue
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef void (*scriptstructsetter_t)(
|
||||||
|
const scriptcontext_t *context,
|
||||||
|
const char_t *key,
|
||||||
|
void *structPtr,
|
||||||
|
const scriptvalue_t *inValue
|
||||||
|
);
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
scriptcontext_t *context;
|
||||||
|
scriptstructgetter_t getter;
|
||||||
|
scriptstructsetter_t setter;
|
||||||
|
} structmetatablecontext_t;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a script structure to allow a script to access its fields. You will
|
||||||
|
* be creating a new metatable in Lua with the given name, so Ideally use a name
|
||||||
|
* like Ideally struct_mt e.g. player_t or entity_t.
|
||||||
|
*
|
||||||
|
* @param context The script context.
|
||||||
|
* @param metatableName The name of the metatable to register
|
||||||
|
* @param getter The getter function for retrieving field values.
|
||||||
|
* @param setter The setter function for setting field values.
|
||||||
|
*/
|
||||||
|
void scriptStructRegister(
|
||||||
|
scriptcontext_t *context,
|
||||||
|
const char_t *metatableName,
|
||||||
|
const scriptstructgetter_t getter,
|
||||||
|
const scriptstructsetter_t setter
|
||||||
|
);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback received from Lua to index (get) a structure field.
|
||||||
|
*
|
||||||
|
* @param l The Lua state.
|
||||||
|
* @return The number of return values.
|
||||||
|
*/
|
||||||
|
int scriptStructIndex(lua_State *l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback received from Lua to newindex (set) a structure field.
|
||||||
|
*
|
||||||
|
* @param l The Lua state.
|
||||||
|
* @return The number of return values.
|
||||||
|
*/
|
||||||
|
int scriptStructNewIndex(lua_State *l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pops the metatable context from the given Lua state. This is only valid from
|
||||||
|
* within the index or newindex methods.
|
||||||
|
*
|
||||||
|
* @param l The Lua state.
|
||||||
|
* @return The metatable context.
|
||||||
|
*/
|
||||||
|
structmetatablecontext_t *scriptStructGetMetatableContext(lua_State *l);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pushes a structure onto the Lua stack, associating it with the given
|
||||||
|
* metatable.
|
||||||
|
*
|
||||||
|
* @param context The script context.
|
||||||
|
* @param metatableName The name of the metatable to associate with.
|
||||||
|
* @param structPtr Pointer to the structure to push.
|
||||||
|
*/
|
||||||
|
void scriptStructPush(
|
||||||
|
scriptcontext_t *context,
|
||||||
|
const char_t *metatableName,
|
||||||
|
void *structPtr
|
||||||
|
);
|
||||||
Reference in New Issue
Block a user