Expose globally custom behaviors defined with hook_behavior (#345)
* Expose globally custom behaviors defined with hook_behavior; macros for mod strings max length * fixes * made customBehaviorIndex a mod field to be more relevant
This commit is contained in:
parent
df4226fdd7
commit
999ea1dd42
|
@ -61,8 +61,9 @@ gServerSettings = {}
|
|||
--- @param replaceBehavior boolean
|
||||
--- @param initFunction fun(obj:Object)
|
||||
--- @param loopFunction fun(obj:Object)
|
||||
--- @param behaviorName string
|
||||
--- @return BehaviorId
|
||||
function hook_behavior(behaviorId, objectList, replaceBehavior, initFunction, loopFunction)
|
||||
function hook_behavior(behaviorId, objectList, replaceBehavior, initFunction, loopFunction, behaviorName)
|
||||
-- ...
|
||||
end
|
||||
|
||||
|
|
|
@ -24,6 +24,7 @@ Hooks are a way for SM64 to trigger Lua code, whereas the functions listed in [f
|
|||
| replaceBehavior | `bool` | Prevents the original behavior code from running |
|
||||
| initFunction | `Lua Function` ([Object](structs.md#Object) obj) | Runs once per object |
|
||||
| loopFunction | `Lua Function` ([Object](structs.md#Object) obj) | Runs once per frame per object |
|
||||
| behaviorName | `string` | Optional, name to give to the behavior |
|
||||
|
||||
### Returns
|
||||
- [enum BehaviorId](constants.md#enum-BehaviorId)
|
||||
|
@ -40,7 +41,7 @@ function bhv_example_loop(obj)
|
|||
obj.oPosY = obj.oPosY + 1
|
||||
end
|
||||
|
||||
id_bhvExample = hook_behavior(0, OBJ_LIST_DEFAULT, bhv_example_init, bhv_example_loop)
|
||||
id_bhvExample = hook_behavior(nil, OBJ_LIST_DEFAULT, true, bhv_example_init, bhv_example_loop, "bhvExample")
|
||||
```
|
||||
|
||||
[:arrow_up_small:](#)
|
||||
|
|
|
@ -163,6 +163,7 @@ void smlua_init(void) {
|
|||
gLuaLoadingMod = mod;
|
||||
gLuaActiveMod = mod;
|
||||
gLuaLastHookMod = mod;
|
||||
gLuaLoadingMod->customBehaviorIndex = 0;
|
||||
gPcDebug.lastModRun = gLuaActiveMod;
|
||||
for (int j = 0; j < mod->fileCount; j++) {
|
||||
struct ModFile* file = &mod->files[j];
|
||||
|
|
|
@ -964,6 +964,7 @@ int smlua_hook_custom_bhv(BehaviorScript *bhvScript, const char *bhvName) {
|
|||
if (L != NULL) {
|
||||
lua_pushinteger(L, customBehaviorId);
|
||||
lua_setglobal(L, bhvName);
|
||||
LOG_INFO("Registered custom behavior: %04hX - %s", customBehaviorId, bhvName);
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -971,13 +972,15 @@ int smlua_hook_custom_bhv(BehaviorScript *bhvScript, const char *bhvName) {
|
|||
|
||||
int smlua_hook_behavior(lua_State* L) {
|
||||
if (L == NULL) { return 0; }
|
||||
if (!smlua_functions_valid_param_count(L, 5)) { return 0; }
|
||||
if (!smlua_functions_valid_param_range(L, 5, 6)) { return 0; }
|
||||
|
||||
if (gLuaLoadingMod == NULL) {
|
||||
LOG_LUA_LINE("hook_behavior() can only be called on load.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int paramCount = lua_gettop(L);
|
||||
|
||||
if (sHookedBehaviorsCount >= MAX_HOOKED_BEHAVIORS) {
|
||||
LOG_LUA_LINE("Hooked behaviors exceeded maximum references!");
|
||||
return 0;
|
||||
|
@ -1033,6 +1036,49 @@ int smlua_hook_behavior(lua_State* L) {
|
|||
return 0;
|
||||
}
|
||||
|
||||
const char *bhvName = NULL;
|
||||
if (paramCount >= 6) {
|
||||
int bhvNameType = lua_type(L, 6);
|
||||
if (bhvNameType == LUA_TNIL) {
|
||||
// nothing
|
||||
} else if (bhvNameType == LUA_TSTRING) {
|
||||
bhvName = smlua_to_string(L, 6);
|
||||
if (!bhvName || !gSmLuaConvertSuccess) {
|
||||
LOG_LUA_LINE("Hook behavior: could not parse bhvName");
|
||||
return 0;
|
||||
}
|
||||
} else {
|
||||
LOG_LUA_LINE("Hook behavior: invalid type passed for argument bhvName: %u", bhvNameType);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// If not provided, generate generic behavior name: bhv<ModName>Custom<Index>
|
||||
// - <ModName> is the mod name in CamelCase format, alphanumeric chars only
|
||||
// - <Index> is in 3-digit numeric format, ranged from 001 to 256
|
||||
// For example, the 4th unnamed behavior of the mod "my-great_MOD" will be named "bhvMyGreatMODCustom004"
|
||||
if (!bhvName) {
|
||||
static char sGenericBhvName[MOD_NAME_MAX_LENGTH + 16];
|
||||
s32 i = 3;
|
||||
snprintf(sGenericBhvName, 4, "bhv");
|
||||
for (char caps = TRUE, *c = gLuaLoadingMod->name; *c && i < MOD_NAME_MAX_LENGTH + 3; ++c) {
|
||||
if ('0' <= *c && *c <= '9') {
|
||||
sGenericBhvName[i++] = *c;
|
||||
caps = TRUE;
|
||||
} else if ('A' <= *c && *c <= 'Z') {
|
||||
sGenericBhvName[i++] = *c;
|
||||
caps = FALSE;
|
||||
} else if ('a' <= *c && *c <= 'z') {
|
||||
sGenericBhvName[i++] = *c + (caps ? 'A' - 'a' : 0);
|
||||
caps = FALSE;
|
||||
} else {
|
||||
caps = TRUE;
|
||||
}
|
||||
}
|
||||
snprintf(sGenericBhvName + i, 12, "Custom%03u", (u32) (gLuaLoadingMod->customBehaviorIndex++) + 1);
|
||||
bhvName = sGenericBhvName;
|
||||
}
|
||||
|
||||
struct LuaHookedBehavior* hooked = &sHookedBehaviors[sHookedBehaviorsCount];
|
||||
u16 customBehaviorId = (sHookedBehaviorsCount & 0xFFFF) | LUA_BEHAVIOR_FLAG;
|
||||
hooked->behavior = calloc(3, sizeof(BehaviorScript));
|
||||
|
@ -1051,6 +1097,12 @@ int smlua_hook_behavior(lua_State* L) {
|
|||
|
||||
sHookedBehaviorsCount++;
|
||||
|
||||
// We want to push the behavior into the global LUA state. So mods can access it.
|
||||
// It's also used for some things that would normally access a LUA behavior instead.
|
||||
lua_pushinteger(L, customBehaviorId);
|
||||
lua_setglobal(L, bhvName);
|
||||
LOG_INFO("Registered custom behavior: %04hX - %s", customBehaviorId, bhvName);
|
||||
|
||||
// return behavior ID
|
||||
lua_pushinteger(L, customBehaviorId);
|
||||
|
||||
|
|
|
@ -391,9 +391,10 @@ static void mod_extract_fields(struct Mod* mod) {
|
|||
mod->description = NULL;
|
||||
|
||||
// read line-by-line
|
||||
char buffer[512] = { 0 };
|
||||
#define BUFFER_SIZE MAX(MAX(MOD_NAME_MAX_LENGTH, MOD_INCOMPATIBLE_MAX_LENGTH), MOD_DESCRIPTION_MAX_LENGTH)
|
||||
char buffer[BUFFER_SIZE] = { 0 };
|
||||
while (!feof(f)) {
|
||||
file_get_line(buffer, 512, f);
|
||||
file_get_line(buffer, BUFFER_SIZE, f);
|
||||
|
||||
// no longer in header
|
||||
if (buffer[0] != '-' || buffer[1] != '-') {
|
||||
|
@ -404,18 +405,18 @@ static void mod_extract_fields(struct Mod* mod) {
|
|||
// extract the field
|
||||
char* extracted = NULL;
|
||||
if (mod->name == NULL && (extracted = extract_lua_field("-- name:", buffer))) {
|
||||
mod->name = calloc(33, sizeof(char));
|
||||
if (snprintf(mod->name, 32, "%s", extracted) < 0) {
|
||||
mod->name = calloc(MOD_NAME_MAX_LENGTH + 1, sizeof(char));
|
||||
if (snprintf(mod->name, MOD_NAME_MAX_LENGTH, "%s", extracted) < 0) {
|
||||
LOG_INFO("Truncated mod name field '%s'", mod->name);
|
||||
}
|
||||
} else if (mod->incompatible == NULL && (extracted = extract_lua_field("-- incompatible:", buffer))) {
|
||||
mod->incompatible = calloc(257, sizeof(char));
|
||||
if (snprintf(mod->incompatible, 256, "%s", extracted) < 0) {
|
||||
mod->incompatible = calloc(MOD_INCOMPATIBLE_MAX_LENGTH + 1, sizeof(char));
|
||||
if (snprintf(mod->incompatible, MOD_INCOMPATIBLE_MAX_LENGTH, "%s", extracted) < 0) {
|
||||
LOG_INFO("Truncated mod incompatible field '%s'", mod->incompatible);
|
||||
}
|
||||
} else if (mod->description == NULL && (extracted = extract_lua_field("-- description:", buffer))) {
|
||||
mod->description = calloc(513, sizeof(char));
|
||||
if (snprintf(mod->description, 512, "%s", extracted) < 0) {
|
||||
mod->description = calloc(MOD_DESCRIPTION_MAX_LENGTH + 1, sizeof(char));
|
||||
if (snprintf(mod->description, MOD_DESCRIPTION_MAX_LENGTH, "%s", extracted) < 0) {
|
||||
LOG_INFO("Truncated mod description field '%s'", mod->description);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,10 @@
|
|||
#include <types.h>
|
||||
#include "src/pc/platform.h"
|
||||
|
||||
#define MOD_NAME_MAX_LENGTH 32
|
||||
#define MOD_INCOMPATIBLE_MAX_LENGTH 256
|
||||
#define MOD_DESCRIPTION_MAX_LENGTH 512
|
||||
|
||||
struct Mods;
|
||||
|
||||
struct ModFile {
|
||||
|
@ -30,6 +34,7 @@ struct Mod {
|
|||
bool enabled;
|
||||
bool selectable;
|
||||
size_t size;
|
||||
u8 customBehaviorIndex;
|
||||
};
|
||||
|
||||
void mod_activate(struct Mod* mod);
|
||||
|
|
|
@ -57,12 +57,12 @@ void network_send_mod_list(void) {
|
|||
struct Mod* mod = gActiveMods.entries[i];
|
||||
|
||||
u16 nameLength = strlen(mod->name);
|
||||
if (nameLength > 31) { nameLength = 31; }
|
||||
if (nameLength > MOD_NAME_MAX_LENGTH) { nameLength = MOD_NAME_MAX_LENGTH; }
|
||||
|
||||
u16 incompatibleLength = 0;
|
||||
if (mod->incompatible) {
|
||||
incompatibleLength = strlen(mod->incompatible);
|
||||
if (incompatibleLength > 31) { incompatibleLength = 31; }
|
||||
if (incompatibleLength > MOD_INCOMPATIBLE_MAX_LENGTH) { incompatibleLength = MOD_INCOMPATIBLE_MAX_LENGTH; }
|
||||
}
|
||||
|
||||
u16 relativePathLength = strlen(mod->relativePath);
|
||||
|
@ -188,27 +188,27 @@ void network_receive_mod_list_entry(struct Packet* p) {
|
|||
// get name length
|
||||
u16 nameLength = 0;
|
||||
packet_read(p, &nameLength, sizeof(u16));
|
||||
if (nameLength > 31) {
|
||||
if (nameLength > MOD_NAME_MAX_LENGTH) {
|
||||
LOG_ERROR("Received name with invalid length!");
|
||||
return;
|
||||
}
|
||||
|
||||
// get name
|
||||
char name[32] = { 0 };
|
||||
char name[MOD_NAME_MAX_LENGTH + 1] = { 0 };
|
||||
packet_read(p, name, nameLength * sizeof(u8));
|
||||
mod->name = strdup(name);
|
||||
|
||||
// get incompatible length
|
||||
u16 incompatibleLength = 0;
|
||||
packet_read(p, &incompatibleLength, sizeof(u16));
|
||||
if (incompatibleLength > 31) {
|
||||
if (incompatibleLength > MOD_INCOMPATIBLE_MAX_LENGTH) {
|
||||
LOG_ERROR("Received name with invalid length!");
|
||||
return;
|
||||
}
|
||||
|
||||
// get incompatible
|
||||
if (incompatibleLength > 0) {
|
||||
char incompatible[32] = { 0 };
|
||||
char incompatible[MOD_INCOMPATIBLE_MAX_LENGTH + 1] = { 0 };
|
||||
packet_read(p, incompatible, incompatibleLength * sizeof(u8));
|
||||
mod->incompatible = strdup(incompatible);
|
||||
} else {
|
||||
|
|
Loading…
Reference in New Issue