mirror of
https://github.com/coop-deluxe/sm64coopdx.git
synced 2025-10-30 08:01:01 +00:00
905 lines
28 KiB
C
905 lines
28 KiB
C
#include "smlua.h"
|
|
#include "src/game/object_list_processor.h"
|
|
#include "pc/djui/djui_chat_message.h"
|
|
#include "pc/crash_handler.h"
|
|
|
|
#define MAX_HOOKED_REFERENCES 64
|
|
#define LUA_BEHAVIOR_FLAG (1 << 15)
|
|
|
|
static u64* sBehaviorOffset = &gPcDebug.bhvOffset;
|
|
|
|
struct LuaHookedEvent {
|
|
int reference[MAX_HOOKED_REFERENCES];
|
|
struct Mod* mod[MAX_HOOKED_REFERENCES];
|
|
int count;
|
|
};
|
|
|
|
static struct LuaHookedEvent sHookedEvents[HOOK_MAX] = { 0 };
|
|
|
|
static int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, struct Mod* activeMod) {
|
|
struct Mod* prev = gLuaActiveMod;
|
|
gLuaActiveMod = activeMod;
|
|
gLuaLastHookMod = activeMod;
|
|
int rc = lua_pcall(L, nargs, nresults, errfunc);
|
|
gLuaActiveMod = prev;
|
|
return rc;
|
|
}
|
|
|
|
int smlua_hook_event(lua_State* L) {
|
|
if (L == NULL) { return 0; }
|
|
if (!smlua_functions_valid_param_count(L, 2)) { return 0; }
|
|
|
|
u16 hookType = smlua_to_integer(L, -2);
|
|
if (!gSmLuaConvertSuccess) { return 0; }
|
|
|
|
if (hookType >= HOOK_MAX) {
|
|
LOG_LUA("Hook Type: %d exceeds max!", hookType);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
if (hook->count >= MAX_HOOKED_REFERENCES) {
|
|
LOG_LUA("Hook Type: %s exceeded maximum references!", LuaHookedEventTypeName[hookType]);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
if (ref == -1) {
|
|
LOG_LUA("tried to hook undefined function to '%s'", LuaHookedEventTypeName[hookType]);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
hook->reference[hook->count] = ref;
|
|
hook->mod[hook->count] = gLuaActiveMod;
|
|
hook->count++;
|
|
|
|
return 1;
|
|
}
|
|
|
|
void smlua_call_event_hooks(enum LuaHookedEventType hookType) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 0, 0, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the event_hook callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_bool_param(enum LuaHookedEventType hookType, bool value) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push value
|
|
lua_pushboolean(L, value);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_bool_param_ret_bool(enum LuaHookedEventType hookType, bool value, bool* returnValue) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
s32 prevTop = lua_gettop(L);
|
|
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push value
|
|
lua_pushboolean(L, value);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
|
|
// output the return value
|
|
if (lua_type(L, -1) == LUA_TBOOLEAN) {
|
|
*returnValue = smlua_to_boolean(L, -1);
|
|
}
|
|
lua_settop(L, prevTop);
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_mario_param(enum LuaHookedEventType hookType, struct MarioState* m) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, m->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_mario_params(enum LuaHookedEventType hookType, struct MarioState* m1, struct MarioState* m2) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, m1->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, m2->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 2, 0, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_mario_params_ret_bool(enum LuaHookedEventType hookType, struct MarioState* m1, struct MarioState* m2, bool* returnValue) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
s32 prevTop = lua_gettop(L);
|
|
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, m1->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, m2->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 2, 1, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
|
|
// output the return value
|
|
if (lua_type(L, -1) == LUA_TBOOLEAN) {
|
|
*returnValue = smlua_to_boolean(L, -1);
|
|
}
|
|
lua_settop(L, prevTop);
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_interact_params(enum LuaHookedEventType hookType, struct MarioState* m, struct Object* obj, u32 interactType, bool interactValue) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, m->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// push object
|
|
smlua_push_object(L, LOT_OBJECT, obj);
|
|
|
|
// push interact type
|
|
lua_pushinteger(L, interactType);
|
|
|
|
// push interact value
|
|
lua_pushboolean(L, interactValue);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 4, 0, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_object_param(enum LuaHookedEventType hookType, struct Object* obj) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push object
|
|
smlua_push_object(L, LOT_OBJECT, obj);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool smlua_call_event_hooks_ret_int(enum LuaHookedEventType hookType, s32* returnValue) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return false; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
s32 prevTop = lua_gettop(L);
|
|
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 0, 1, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
|
|
// output the return value
|
|
if (lua_type(L, -1) == LUA_TNUMBER) {
|
|
*returnValue = smlua_to_integer(L, -1);
|
|
}
|
|
lua_settop(L, prevTop);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void smlua_call_event_hooks_network_player_param(enum LuaHookedEventType hookType, struct NetworkPlayer* np) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gNetworkPlayers");
|
|
lua_pushinteger(L, np->localIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 0, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
|
|
void smlua_call_event_hooks_set_camera_mode_params(enum LuaHookedEventType hookType, struct Camera *c, s16 mode, s16 frames, bool* returnValue) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return; }
|
|
*returnValue = true;
|
|
|
|
struct LuaHookedEvent* hook = &sHookedEvents[hookType];
|
|
for (int i = 0; i < hook->count; i++) {
|
|
s32 prevTop = lua_gettop(L);
|
|
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference[i]);
|
|
|
|
// push params
|
|
smlua_push_object(L, LOT_CAMERA, c);
|
|
lua_pushinteger(L, mode);
|
|
lua_pushinteger(L, frames);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 3, 1, 0, hook->mod[i])) {
|
|
LOG_LUA("Failed to call the callback: %u, %s", hookType, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
|
|
// output the return value
|
|
if (lua_type(L, -1) == LUA_TBOOLEAN && *returnValue) {
|
|
*returnValue = smlua_to_boolean(L, -1);
|
|
}
|
|
lua_settop(L, prevTop);
|
|
}
|
|
}
|
|
|
|
////////////////////
|
|
// hooked actions //
|
|
////////////////////
|
|
|
|
struct LuaHookedMarioAction {
|
|
u32 action;
|
|
u32 interactionType;
|
|
int reference;
|
|
struct Mod* mod;
|
|
};
|
|
|
|
#define MAX_HOOKED_ACTIONS 128
|
|
|
|
static struct LuaHookedMarioAction sHookedMarioActions[MAX_HOOKED_ACTIONS] = { 0 };
|
|
static int sHookedMarioActionsCount = 0;
|
|
u32 gLuaMarioActionIndex = 0;
|
|
|
|
int smlua_hook_mario_action(lua_State* L) {
|
|
if (L == NULL) { return 0; }
|
|
if (!smlua_functions_valid_param_range(L, 2, 3)) { return 0; }
|
|
|
|
if (gLuaLoadingMod == NULL) {
|
|
LOG_LUA("hook_mario_action() can only be called on load.");
|
|
return 0;
|
|
}
|
|
|
|
int paramCount = lua_gettop(L);
|
|
|
|
if (sHookedMarioActionsCount >= MAX_HOOKED_ACTIONS) {
|
|
LOG_LUA("Hooked mario actions exceeded maximum references!");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
lua_Integer action = smlua_to_integer(L, 1);
|
|
if (action == 0 || !gSmLuaConvertSuccess) {
|
|
LOG_LUA("Hook Action: tried to hook invalid action: %lld, %u", action, gSmLuaConvertSuccess);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
lua_pushvalue(L, 2);
|
|
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
|
|
if (ref == -1) {
|
|
LOG_LUA("Hook Action: %lld tried to hook undefined function", action);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
lua_Integer interactionType = 0;
|
|
if (paramCount >= 3) {
|
|
interactionType = smlua_to_integer(L, 3);
|
|
if (interactionType == 0 || !gSmLuaConvertSuccess) {
|
|
LOG_LUA("Hook Action: tried to hook invalid interactionType: %lld, %u", interactionType, gSmLuaConvertSuccess);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
struct LuaHookedMarioAction* hooked = &sHookedMarioActions[sHookedMarioActionsCount];
|
|
hooked->action = action;
|
|
hooked->interactionType = interactionType;
|
|
hooked->reference = ref;
|
|
hooked->mod = gLuaActiveMod;
|
|
if (!gSmLuaConvertSuccess) { return 0; }
|
|
|
|
sHookedMarioActionsCount++;
|
|
return 1;
|
|
}
|
|
|
|
bool smlua_call_action_hook(struct MarioState* m, s32* returnValue) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return false; }
|
|
for (int i = 0; i < sHookedMarioActionsCount; i++) {
|
|
struct LuaHookedMarioAction* hook = &sHookedMarioActions[i];
|
|
if (hook->action == m->action) {
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference);
|
|
|
|
// push mario state
|
|
lua_getglobal(L, "gMarioStates");
|
|
lua_pushinteger(L, m->playerIndex);
|
|
lua_gettable(L, -2);
|
|
lua_remove(L, -2);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod)) {
|
|
LOG_LUA("Failed to call the action callback: %u, %s", m->action, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
|
|
// output the return value
|
|
*returnValue = false;
|
|
if (lua_type(L, -1) == LUA_TBOOLEAN || lua_type(L, -1) == LUA_TNUMBER) {
|
|
*returnValue = smlua_to_integer(L, -1);
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
u32 smlua_get_action_interaction_type(struct MarioState* m) {
|
|
u32 interactionType = 0;
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return false; }
|
|
for (int i = 0; i < sHookedMarioActionsCount; i++) {
|
|
if (sHookedMarioActions[i].action == m->action) {
|
|
interactionType |= sHookedMarioActions[i].interactionType;
|
|
}
|
|
}
|
|
return interactionType;
|
|
}
|
|
|
|
//////////////////////
|
|
// hooked behaviors //
|
|
//////////////////////
|
|
|
|
struct LuaHookedBehavior {
|
|
u32 behaviorId;
|
|
u32 overrideId;
|
|
BehaviorScript behavior[2];
|
|
const BehaviorScript* originalBehavior;
|
|
int initReference;
|
|
int loopReference;
|
|
bool replace;
|
|
struct Mod* mod;
|
|
};
|
|
|
|
#define MAX_HOOKED_BEHAVIORS 256
|
|
|
|
static struct LuaHookedBehavior sHookedBehaviors[MAX_HOOKED_BEHAVIORS] = { 0 };
|
|
static int sHookedBehaviorsCount = 0;
|
|
|
|
enum BehaviorId smlua_get_original_behavior_id(const BehaviorScript* behavior) {
|
|
enum BehaviorId id = get_id_from_behavior(behavior);
|
|
for (int i = 0; i < sHookedBehaviorsCount; i++) {
|
|
struct LuaHookedBehavior* hooked = &sHookedBehaviors[i];
|
|
if (hooked->behavior == behavior) {
|
|
id = hooked->overrideId;
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
|
|
const BehaviorScript* smlua_override_behavior(const BehaviorScript* behavior) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return behavior; }
|
|
|
|
enum BehaviorId id = get_id_from_behavior(behavior);
|
|
const BehaviorScript* luaBehavior = get_lua_behavior_from_id(id, false);
|
|
if (luaBehavior != NULL) { return luaBehavior; }
|
|
return behavior + *sBehaviorOffset;
|
|
}
|
|
|
|
const BehaviorScript* get_lua_behavior_from_id(enum BehaviorId id, bool returnOriginal) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return false; }
|
|
for (int i = 0; i < sHookedBehaviorsCount; i++) {
|
|
struct LuaHookedBehavior* hooked = &sHookedBehaviors[i];
|
|
if (hooked->behaviorId != id && hooked->overrideId != id) { continue; }
|
|
if (returnOriginal && !hooked->replace) { return hooked->originalBehavior; }
|
|
return hooked->behavior;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
int smlua_hook_behavior(lua_State* L) {
|
|
if (L == NULL) { return 0; }
|
|
if (!smlua_functions_valid_param_count(L, 5)) { return 0; }
|
|
|
|
if (gLuaLoadingMod == NULL) {
|
|
LOG_LUA("hook_behavior() can only be called on load.");
|
|
return 0;
|
|
}
|
|
|
|
if (sHookedBehaviorsCount >= MAX_HOOKED_BEHAVIORS) {
|
|
LOG_LUA("Hooked behaviors exceeded maximum references!");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
bool noOverrideId = (lua_type(L, 1) == LUA_TNIL);
|
|
gSmLuaConvertSuccess = true;
|
|
lua_Integer overrideBehaviorId = noOverrideId ? 0xFFFFFF : smlua_to_integer(L, 1);
|
|
if (!gSmLuaConvertSuccess) {
|
|
LOG_LUA("Hook behavior: tried to override invalid behavior: %lld, %u", overrideBehaviorId, gSmLuaConvertSuccess);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
lua_Integer objectList = smlua_to_integer(L, 2);
|
|
if (objectList <= 0 || objectList >= NUM_OBJ_LISTS || !gSmLuaConvertSuccess) {
|
|
LOG_LUA("Hook behavior: tried use invalid object list: %lld, %u", objectList, gSmLuaConvertSuccess);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
bool replaceBehavior = smlua_to_boolean(L, 3);
|
|
if (!gSmLuaConvertSuccess) {
|
|
LOG_LUA("Hook behavior: could not parse replaceBehavior");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
const BehaviorScript* originalBehavior = noOverrideId ? NULL : get_behavior_from_id(overrideBehaviorId);
|
|
if (originalBehavior == NULL) {
|
|
replaceBehavior = true;
|
|
}
|
|
|
|
int initReference = 0;
|
|
int initReferenceType = lua_type(L, 4);
|
|
if (initReferenceType == LUA_TNIL) {
|
|
// nothing
|
|
} else if (initReferenceType == LUA_TFUNCTION) {
|
|
// get reference
|
|
lua_pushvalue(L, 4);
|
|
initReference = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
} else {
|
|
LOG_LUA("Hook behavior: tried to reference non-function for init");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
int loopReference = 0;
|
|
int loopReferenceType = lua_type(L, 5);
|
|
if (loopReferenceType == LUA_TNIL) {
|
|
// nothing
|
|
} else if (loopReferenceType == LUA_TFUNCTION) {
|
|
// get reference
|
|
lua_pushvalue(L, 5);
|
|
loopReference = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
} else {
|
|
LOG_LUA("Hook behavior: tried to reference non-function for loop");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
struct LuaHookedBehavior* hooked = &sHookedBehaviors[sHookedBehaviorsCount];
|
|
u16 customBehaviorId = (sHookedBehaviorsCount & 0xFFFF) | LUA_BEHAVIOR_FLAG;
|
|
hooked->behaviorId = customBehaviorId;
|
|
hooked->overrideId = noOverrideId ? customBehaviorId : overrideBehaviorId;
|
|
hooked->behavior[0] = (((unsigned int) (((unsigned int)(0x00) & ((0x01 << (8)) - 1)) << (24))) | ((unsigned int) (((unsigned int)(objectList) & ((0x01 << (8)) - 1)) << (16)))); // gross. this is BEGIN(objectList)
|
|
hooked->behavior[1] = (((unsigned int) (((unsigned int)(0x39) & ((0x01 << (8)) - 1)) << (24))) | ((unsigned int) (((unsigned int)(customBehaviorId) & ((0x01 << (16)) - 1)) << (0)))); // gross. this is ID(customBehaviorId)
|
|
hooked->originalBehavior = originalBehavior ? originalBehavior : hooked->behavior;
|
|
hooked->initReference = initReference;
|
|
hooked->loopReference = loopReference;
|
|
hooked->replace = replaceBehavior;
|
|
hooked->mod = gLuaActiveMod;
|
|
|
|
sHookedBehaviorsCount++;
|
|
|
|
// return behavior ID
|
|
lua_pushinteger(L, customBehaviorId);
|
|
|
|
return 1;
|
|
}
|
|
|
|
bool smlua_call_behavior_hook(const BehaviorScript** behavior, struct Object* object, bool before) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return false; }
|
|
for (int i = 0; i < sHookedBehaviorsCount; i++) {
|
|
struct LuaHookedBehavior* hooked = &sHookedBehaviors[i];
|
|
|
|
// find behavior
|
|
if (object->behavior != hooked->behavior) {
|
|
continue;
|
|
}
|
|
|
|
// figure out whether to run before or after
|
|
if (before && !hooked->replace) {
|
|
return false;
|
|
}
|
|
if (!before && hooked->replace) {
|
|
return false;
|
|
}
|
|
|
|
// retrieve and remember first run
|
|
bool firstRun = (object->curBhvCommand == hooked->originalBehavior) || (object->curBhvCommand == hooked->behavior);
|
|
if (firstRun && hooked->replace) { *behavior = &hooked->behavior[1]; }
|
|
|
|
// get function and null check it
|
|
int reference = firstRun ? hooked->initReference : hooked->loopReference;
|
|
if (reference == 0) {
|
|
return true;
|
|
}
|
|
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, reference);
|
|
|
|
// push object
|
|
smlua_push_object(L, LOT_OBJECT, object);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 0, 0, hooked->mod)) {
|
|
LOG_LUA("Failed to call the behavior callback: %u, %s", hooked->behaviorId, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
return true;
|
|
}
|
|
|
|
return hooked->replace;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/////////////////////////
|
|
// hooked chat command //
|
|
/////////////////////////
|
|
|
|
struct LuaHookedChatCommand {
|
|
char* command;
|
|
char* description;
|
|
int reference;
|
|
struct Mod* mod;
|
|
};
|
|
|
|
#define MAX_HOOKED_CHAT_COMMANDS 64
|
|
|
|
static struct LuaHookedChatCommand sHookedChatCommands[MAX_HOOKED_CHAT_COMMANDS] = { 0 };
|
|
static int sHookedChatCommandsCount = 0;
|
|
|
|
int smlua_hook_chat_command(lua_State* L) {
|
|
if (L == NULL) { return 0; }
|
|
if (!smlua_functions_valid_param_count(L, 3)) { return 0; }
|
|
|
|
if (gLuaLoadingMod == NULL) {
|
|
LOG_LUA("hook_chat_command() can only be called on load.");
|
|
return 0;
|
|
}
|
|
|
|
if (sHookedChatCommandsCount >= MAX_HOOKED_CHAT_COMMANDS) {
|
|
LOG_LUA("Hooked chat command exceeded maximum references!");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
const char* command = smlua_to_string(L, 1);
|
|
if (command == NULL || strlen(command) == 0 || !gSmLuaConvertSuccess) {
|
|
LOG_LUA("Hook chat command: tried to hook invalid command");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
const char* description = smlua_to_string(L, 2);
|
|
if (description == NULL || strlen(description) == 0 || !gSmLuaConvertSuccess) {
|
|
LOG_LUA("Hook chat command: tried to hook invalid description");
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
int ref = luaL_ref(L, LUA_REGISTRYINDEX);
|
|
if (ref == -1) {
|
|
LOG_LUA("Hook chat command: tried to hook undefined function '%s'", command);
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
struct LuaHookedChatCommand* hooked = &sHookedChatCommands[sHookedChatCommandsCount];
|
|
hooked->command = strdup(command);
|
|
hooked->description = strdup(description);
|
|
hooked->reference = ref;
|
|
hooked->mod = gLuaActiveMod;
|
|
if (!gSmLuaConvertSuccess) { return 0; }
|
|
|
|
sHookedChatCommandsCount++;
|
|
return 1;
|
|
}
|
|
|
|
bool smlua_call_chat_command_hook(char* command) {
|
|
lua_State* L = gLuaState;
|
|
if (L == NULL) { return false; }
|
|
for (int i = 0; i < sHookedChatCommandsCount; i++) {
|
|
struct LuaHookedChatCommand* hook = &sHookedChatCommands[i];
|
|
size_t commandLength = strlen(hook->command);
|
|
for (size_t j = 0; j < commandLength; j++) {
|
|
if (hook->command[j] != command[j + 1]) {
|
|
goto NEXT_HOOK;
|
|
}
|
|
}
|
|
|
|
char* params = &command[commandLength + 1];
|
|
if (*params != '\0' && *params != ' ') {
|
|
goto NEXT_HOOK;
|
|
}
|
|
if (*params == ' ') {
|
|
params++;
|
|
}
|
|
|
|
// push the callback onto the stack
|
|
lua_rawgeti(L, LUA_REGISTRYINDEX, hook->reference);
|
|
|
|
// push parameter
|
|
lua_pushstring(L, params);
|
|
|
|
// call the callback
|
|
if (0 != smlua_call_hook(L, 1, 1, 0, hook->mod)) {
|
|
LOG_LUA("Failed to call the chat command callback: %s, %s", command, lua_tostring(L, -1));
|
|
smlua_logline();
|
|
continue;
|
|
}
|
|
|
|
// output the return value
|
|
bool returnValue = false;
|
|
if (lua_type(L, -1) == LUA_TBOOLEAN) {
|
|
returnValue = smlua_to_boolean(L, -1);
|
|
}
|
|
lua_pop(L, 1);
|
|
|
|
if (!gSmLuaConvertSuccess) { return false; }
|
|
|
|
return returnValue;
|
|
|
|
NEXT_HOOK:;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void smlua_display_chat_commands(void) {
|
|
for (int i = 0; i < sHookedChatCommandsCount; i++) {
|
|
struct LuaHookedChatCommand* hook = &sHookedChatCommands[i];
|
|
char msg[256] = { 0 };
|
|
snprintf(msg, 256, "/%s %s", hook->command, hook->description);
|
|
djui_chat_message_create(msg);
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////
|
|
// hooked sync table change //
|
|
//////////////////////////////
|
|
|
|
int smlua_hook_on_sync_table_change(lua_State* L) {
|
|
LUA_STACK_CHECK_BEGIN();
|
|
if (L == NULL) { return 0; }
|
|
if(!smlua_functions_valid_param_count(L, 4)) { return 0; }
|
|
|
|
int syncTableIndex = 1;
|
|
int keyIndex = 2;
|
|
int tagIndex = 3;
|
|
int funcIndex = 4;
|
|
|
|
if (gLuaLoadingMod == NULL) {
|
|
LOG_LUA("hook_on_sync_table_change() can only be called on load.");
|
|
return 0;
|
|
}
|
|
|
|
if (lua_type(L, syncTableIndex) != LUA_TTABLE) {
|
|
LOG_LUA("Tried to attach a non-table to hook_on_sync_table_change: %d", lua_type(L, syncTableIndex));
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
if (lua_type(L, funcIndex) != LUA_TFUNCTION) {
|
|
LOG_LUA("Tried to attach a non-function to hook_on_sync_table_change: %d", lua_type(L, funcIndex));
|
|
smlua_logline();
|
|
return 0;
|
|
}
|
|
|
|
// set hook's table
|
|
lua_newtable(L);
|
|
int valTableIndex = lua_gettop(L);
|
|
|
|
lua_pushstring(L, "_func");
|
|
lua_pushvalue(L, funcIndex);
|
|
lua_settable(L, valTableIndex);
|
|
|
|
lua_pushstring(L, "_tag");
|
|
lua_pushvalue(L, tagIndex);
|
|
lua_settable(L, valTableIndex);
|
|
|
|
// get _hook_on_changed
|
|
lua_pushstring(L, "_hook_on_changed");
|
|
lua_rawget(L, syncTableIndex);
|
|
int hookOnChangedIndex = lua_gettop(L);
|
|
|
|
// attach
|
|
lua_pushvalue(L, keyIndex);
|
|
lua_pushvalue(L, valTableIndex);
|
|
lua_settable(L, hookOnChangedIndex);
|
|
|
|
// clean up
|
|
lua_remove(L, hookOnChangedIndex);
|
|
lua_remove(L, valTableIndex);
|
|
|
|
LUA_STACK_CHECK_END();
|
|
return 1;
|
|
}
|
|
|
|
|
|
//////////
|
|
// misc //
|
|
//////////
|
|
|
|
static void smlua_clear_hooks(void) {
|
|
for (int i = 0; i < HOOK_MAX; i++) {
|
|
struct LuaHookedEvent* hooked = &sHookedEvents[i];
|
|
for (int j = 0; j < hooked->count; j++) {
|
|
hooked->reference[j] = 0;
|
|
hooked->mod[j] = NULL;
|
|
}
|
|
hooked->count = 0;
|
|
}
|
|
|
|
for (int i = 0; i < sHookedMarioActionsCount; i++) {
|
|
struct LuaHookedMarioAction* hooked = &sHookedMarioActions[i];
|
|
hooked->action = 0;
|
|
hooked->mod = NULL;
|
|
hooked->reference = 0;
|
|
}
|
|
sHookedMarioActionsCount = 0;
|
|
|
|
for (int i = 0; i < sHookedChatCommandsCount; i++) {
|
|
struct LuaHookedChatCommand* hooked = &sHookedChatCommands[i];
|
|
if (hooked->command != NULL) { free(hooked->command); }
|
|
hooked->command = NULL;
|
|
|
|
if (hooked->description != NULL) { free(sHookedChatCommands[i].description); }
|
|
hooked->description = NULL;
|
|
|
|
hooked->reference = 0;
|
|
hooked->mod = NULL;
|
|
}
|
|
sHookedChatCommandsCount = 0;
|
|
|
|
for (int i = 0; i < sHookedBehaviorsCount; i++) {
|
|
struct LuaHookedBehavior* hooked = &sHookedBehaviors[i];
|
|
hooked->behaviorId = 0;
|
|
hooked->behavior[0] = 0;
|
|
hooked->behavior[1] = 0;
|
|
hooked->originalBehavior = NULL;
|
|
hooked->initReference = 0;
|
|
hooked->loopReference = 0;
|
|
hooked->replace = false;
|
|
hooked->mod = NULL;
|
|
}
|
|
sHookedBehaviorsCount = 0;
|
|
gLuaMarioActionIndex = 0;
|
|
}
|
|
|
|
void smlua_bind_hooks(void) {
|
|
lua_State* L = gLuaState;
|
|
smlua_clear_hooks();
|
|
|
|
smlua_bind_function(L, "hook_event", smlua_hook_event);
|
|
smlua_bind_function(L, "hook_mario_action", smlua_hook_mario_action);
|
|
smlua_bind_function(L, "hook_chat_command", smlua_hook_chat_command);
|
|
smlua_bind_function(L, "hook_on_sync_table_change", smlua_hook_on_sync_table_change);
|
|
smlua_bind_function(L, "hook_behavior", smlua_hook_behavior);
|
|
}
|