From 9e5a45ecddc83cac3b1cd8431777a85c7aa4db4b Mon Sep 17 00:00:00 2001 From: PeachyPeach <72323920+PeachyPeachSM64@users.noreply.github.com> Date: Wed, 20 Apr 2022 20:20:45 +0200 Subject: [PATCH] Added lua profiler; Added useful object functions; Bug fixes (#65) Added a basic lua profiler If the game is compiled with LUA_PROFILER=1, displays on screen the average execution time per frame of each active lua mod, in microseconds. Added object functions For some reasons, accessing the object fields obj.o* via lua is rather slow, and can drastically increase execution time of custom behaviors. For basic stuff like setting an object's velocity or moving it, some functions, missing from the original code, have been added: s32 obj_is_valid_for_interaction(struct Object *o): returns 1 if an object is valid for interaction, i.e. active, tangible and not interacted. s32 obj_check_hitbox_overlap(struct Object *o1, struct Object *o2): returns 1 if two objects hitboxes overlap. Doesn't check tangibility, only hitbox values. void obj_set_vel(struct Object *o, f32 vx, f32 vy, f32 vz): sets an object's velocity. void obj_move_xyz(struct Object *o, f32 dx, f32 dy, f32 dz): moves an object position by (dx, dy, dz). Bug fixes: Disable collisions with walls and ceilings after Mario exits a warp pipe to prevent softlocks in narrow places. Make the koopa shell exclamation box respawn after some time. Quicksand no longer downwarps and instant-kills Mario if he's shocked while being above it. --- Makefile | 9 +++ autogen/lua_definitions/functions.lua | 31 ++++++++ docs/lua/functions.md | 91 ++++++++++++++++++++++++ src/game/behaviors/exclamation_box.inc.c | 2 +- src/game/hud.c | 5 ++ src/game/mario_actions_cutscene.c | 33 ++++++++- src/pc/lua/smlua_functions_autogen.c | 62 ++++++++++++++++ src/pc/lua/smlua_hooks.c | 76 ++++++++++++++++++++ src/pc/lua/utils/smlua_obj_utils.c | 31 ++++++++ src/pc/lua/utils/smlua_obj_utils.h | 5 ++ 10 files changed, 343 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index 51b9cbe4..ef19c9ee 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,9 @@ DEBUG ?= 0 # Enable development/testing flags DEVELOPMENT ?= 0 +# Enable lua profiler +LUA_PROFILER ?= 0 + # Build for the N64 (turn this off for ports) TARGET_N64 = 0 @@ -972,6 +975,12 @@ ifeq ($(DEVELOPMENT),1) CFLAGS += -DDEVELOPMENT endif +# Check for lua profiler option +ifeq ($(LUA_PROFILER),1) + CC_CHECK_CFLAGS += -DLUA_PROFILER + CFLAGS += -DLUA_PROFILER +endif + # Check for texture fix option ifeq ($(TEXTURE_FIX),1) CC_CHECK_CFLAGS += -DTEXTURE_FIX diff --git a/autogen/lua_definitions/functions.lua b/autogen/lua_definitions/functions.lua index 21c793ed..44b33687 100644 --- a/autogen/lua_definitions/functions.lua +++ b/autogen/lua_definitions/functions.lua @@ -7176,6 +7176,13 @@ function get_trajectory(name) -- ... end +--- @param o1 Object +--- @param o2 Object +--- @return integer +function obj_check_hitbox_overlap(o1, o2) + -- ... +end + --- @param objList ObjectList --- @return Object function obj_get_first(objList) @@ -7252,6 +7259,21 @@ function obj_has_model_extended(o, modelId) -- ... end +--- @param o Object +--- @return integer +function obj_is_valid_for_interaction(o) + -- ... +end + +--- @param o Object +--- @param dx number +--- @param dy number +--- @param dz number +--- @return nil +function obj_move_xyz(o, dx, dy, dz) + -- ... +end + --- @param o Object --- @param modelId ModelExtendedId --- @return nil @@ -7259,6 +7281,15 @@ function obj_set_model_extended(o, modelId) -- ... end +--- @param o Object +--- @param vx number +--- @param vy number +--- @param vz number +--- @return nil +function obj_set_vel(o, vx, vy, vz) + -- ... +end + --- @param behaviorId BehaviorId --- @param modelId ModelExtendedId --- @param x number diff --git a/docs/lua/functions.md b/docs/lua/functions.md index 4b3e461d..4df2b360 100644 --- a/docs/lua/functions.md +++ b/docs/lua/functions.md @@ -1344,6 +1344,7 @@ - smlua_obj_utils.h - [get_temp_object_hitbox](#get_temp_object_hitbox) - [get_trajectory](#get_trajectory) + - [obj_check_hitbox_overlap](#obj_check_hitbox_overlap) - [obj_get_first](#obj_get_first) - [obj_get_first_with_behavior_id](#obj_get_first_with_behavior_id) - [obj_get_first_with_behavior_id_and_field_f32](#obj_get_first_with_behavior_id_and_field_f32) @@ -1355,7 +1356,10 @@ - [obj_get_temp_spawn_particles_info](#obj_get_temp_spawn_particles_info) - [obj_has_behavior_id](#obj_has_behavior_id) - [obj_has_model_extended](#obj_has_model_extended) + - [obj_is_valid_for_interaction](#obj_is_valid_for_interaction) + - [obj_move_xyz](#obj_move_xyz) - [obj_set_model_extended](#obj_set_model_extended) + - [obj_set_vel](#obj_set_vel) - [spawn_non_sync_object](#spawn_non_sync_object) - [spawn_sync_object](#spawn_sync_object) @@ -25086,6 +25090,27 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [obj_check_hitbox_overlap](#obj_check_hitbox_overlap) + +### Lua Example +`local integerValue = obj_check_hitbox_overlap(o1, o2)` + +### Parameters +| Field | Type | +| ----- | ---- | +| o1 | [Object](structs.md#Object) | +| o2 | [Object](structs.md#Object) | + +### Returns +- `integer` + +### C Prototype +`s32 obj_check_hitbox_overlap(struct Object *o1, struct Object *o2);` + +[:arrow_up_small:](#) + +
+ ## [obj_get_first](#obj_get_first) ### Lua Example @@ -25316,6 +25341,49 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [obj_is_valid_for_interaction](#obj_is_valid_for_interaction) + +### Lua Example +`local integerValue = obj_is_valid_for_interaction(o)` + +### Parameters +| Field | Type | +| ----- | ---- | +| o | [Object](structs.md#Object) | + +### Returns +- `integer` + +### C Prototype +`s32 obj_is_valid_for_interaction(struct Object *o);` + +[:arrow_up_small:](#) + +
+ +## [obj_move_xyz](#obj_move_xyz) + +### Lua Example +`obj_move_xyz(o, dx, dy, dz)` + +### Parameters +| Field | Type | +| ----- | ---- | +| o | [Object](structs.md#Object) | +| dx | `number` | +| dy | `number` | +| dz | `number` | + +### Returns +- None + +### C Prototype +`void obj_move_xyz(struct Object *o, f32 dx, f32 dy, f32 dz);` + +[:arrow_up_small:](#) + +
+ ## [obj_set_model_extended](#obj_set_model_extended) ### Lua Example @@ -25337,6 +25405,29 @@ The `reliable` field will ensure that the packet arrives, but should be used spa
+## [obj_set_vel](#obj_set_vel) + +### Lua Example +`obj_set_vel(o, vx, vy, vz)` + +### Parameters +| Field | Type | +| ----- | ---- | +| o | [Object](structs.md#Object) | +| vx | `number` | +| vy | `number` | +| vz | `number` | + +### Returns +- None + +### C Prototype +`void obj_set_vel(struct Object *o, f32 vx, f32 vy, f32 vz);` + +[:arrow_up_small:](#) + +
+ ## [spawn_non_sync_object](#spawn_non_sync_object) ### Lua Example diff --git a/src/game/behaviors/exclamation_box.inc.c b/src/game/behaviors/exclamation_box.inc.c index 25d97b44..30e46db6 100644 --- a/src/game/behaviors/exclamation_box.inc.c +++ b/src/game/behaviors/exclamation_box.inc.c @@ -167,7 +167,7 @@ void exclamation_box_act_4(void) { spawn_mist_particles_variable(0, 0, 46.0f); spawn_triangle_break_particles(20, 139, 0.3f, o->oAnimState); create_sound_spawner(SOUND_GENERAL_BREAK_BOX); - if (o->oBehParams2ndByte < 3) { + if (o->oBehParams2ndByte <= 3) { o->oAction = 5; cur_obj_hide(); } else { diff --git a/src/game/hud.c b/src/game/hud.c index 90e0d289..1f4263e8 100644 --- a/src/game/hud.c +++ b/src/game/hud.c @@ -535,5 +535,10 @@ void render_hud(void) { { print_text(10, 60, "SURFACE NODE POOL FULL"); } + +#if defined(LUA_PROFILER) + extern void lua_profiler_update_counters(); + lua_profiler_update_counters(); +#endif } } diff --git a/src/game/mario_actions_cutscene.c b/src/game/mario_actions_cutscene.c index dcdb0c0f..eafe1ddc 100644 --- a/src/game/mario_actions_cutscene.c +++ b/src/game/mario_actions_cutscene.c @@ -1103,6 +1103,27 @@ s32 act_warp_door_spawn(struct MarioState *m) { return FALSE; } +static s32 launch_mario_until_land_no_collision(struct MarioState *m, s32 endAction, s32 animation, f32 forwardVel) { + mario_set_forward_vel(m, forwardVel); + set_mario_animation(m, animation); + m->pos[0] += m->vel[0]; + m->pos[1] += m->vel[1]; + m->pos[2] += m->vel[2]; + m->vel[1] -= 4.f; + if (m->vel[1] < -75.f) { + m->vel[1] = -75.f; + } + f32 floorHeight = find_floor_height(m->pos[0], m->pos[1], m->pos[2]); + s32 landed = floorHeight >= m->pos[1]; + if (landed) { + m->pos[1] = floorHeight; + set_mario_action(m, endAction, 0); + } + vec3f_copy(m->marioObj->header.gfx.pos, m->pos); + vec3s_set(m->marioObj->header.gfx.angle, 0, m->faceAngle[1], 0); + return landed; +} + s32 act_emerge_from_pipe(struct MarioState *m) { struct Object *marioObj = m->marioObj; @@ -1123,6 +1144,16 @@ s32 act_emerge_from_pipe(struct MarioState *m) { } } + // After Mario has exited the pipe, disable wall and ceiling collision until Mario lands + // Fix softlocks in narrow places + if (m->actionTimer > 15) { + if (launch_mario_until_land_no_collision(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) { + mario_set_forward_vel(m, 0.0f); + play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); + } + return FALSE; + } + if (launch_mario_until_land(m, ACT_JUMP_LAND_STOP, MARIO_ANIM_SINGLE_JUMP, 8.0f)) { mario_set_forward_vel(m, 0.0f); play_mario_landing_sound(m, SOUND_ACTION_TERRAIN_LANDING); @@ -2894,7 +2925,7 @@ static s32 check_for_instant_quicksand(struct MarioState *m) { if (m->action == ACT_BUBBLED) { return FALSE; } if (m->floor->type == SURFACE_INSTANT_QUICKSAND && m->action & ACT_FLAG_INVULNERABLE - && m->action != ACT_QUICKSAND_DEATH) { + && m->action != ACT_QUICKSAND_DEATH && m->action != ACT_SHOCKED) { update_mario_sound_and_camera(m); return drop_and_set_mario_action(m, ACT_QUICKSAND_DEATH, 0); } diff --git a/src/pc/lua/smlua_functions_autogen.c b/src/pc/lua/smlua_functions_autogen.c index 95913fdc..aae414dd 100644 --- a/src/pc/lua/smlua_functions_autogen.c +++ b/src/pc/lua/smlua_functions_autogen.c @@ -14928,6 +14928,19 @@ int smlua_func_get_trajectory(lua_State* L) { return 1; } +int smlua_func_obj_check_hitbox_overlap(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 2)) { return 0; } + + struct Object* o1 = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT); + if (!gSmLuaConvertSuccess) { return 0; } + struct Object* o2 = (struct Object*)smlua_to_cobject(L, 2, LOT_OBJECT); + if (!gSmLuaConvertSuccess) { return 0; } + + lua_pushinteger(L, obj_check_hitbox_overlap(o1, o2)); + + return 1; +} + int smlua_func_obj_get_first(lua_State* L) { if(!smlua_functions_valid_param_count(L, 1)) { return 0; } @@ -15069,6 +15082,34 @@ int smlua_func_obj_has_model_extended(lua_State* L) { return 1; } +int smlua_func_obj_is_valid_for_interaction(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 1)) { return 0; } + + struct Object* o = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT); + if (!gSmLuaConvertSuccess) { return 0; } + + lua_pushinteger(L, obj_is_valid_for_interaction(o)); + + return 1; +} + +int smlua_func_obj_move_xyz(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 4)) { return 0; } + + struct Object* o = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT); + if (!gSmLuaConvertSuccess) { return 0; } + f32 dx = smlua_to_number(L, 2); + if (!gSmLuaConvertSuccess) { return 0; } + f32 dy = smlua_to_number(L, 3); + if (!gSmLuaConvertSuccess) { return 0; } + f32 dz = smlua_to_number(L, 4); + if (!gSmLuaConvertSuccess) { return 0; } + + obj_move_xyz(o, dx, dy, dz); + + return 1; +} + int smlua_func_obj_set_model_extended(lua_State* L) { if(!smlua_functions_valid_param_count(L, 2)) { return 0; } @@ -15082,6 +15123,23 @@ int smlua_func_obj_set_model_extended(lua_State* L) { return 1; } +int smlua_func_obj_set_vel(lua_State* L) { + if(!smlua_functions_valid_param_count(L, 4)) { return 0; } + + struct Object* o = (struct Object*)smlua_to_cobject(L, 1, LOT_OBJECT); + if (!gSmLuaConvertSuccess) { return 0; } + f32 vx = smlua_to_number(L, 2); + if (!gSmLuaConvertSuccess) { return 0; } + f32 vy = smlua_to_number(L, 3); + if (!gSmLuaConvertSuccess) { return 0; } + f32 vz = smlua_to_number(L, 4); + if (!gSmLuaConvertSuccess) { return 0; } + + obj_set_vel(o, vx, vy, vz); + + return 1; +} + int smlua_func_spawn_non_sync_object(lua_State* L) { if(!smlua_functions_valid_param_count(L, 6)) { return 0; } @@ -17002,6 +17060,7 @@ void smlua_bind_functions_autogen(void) { // smlua_obj_utils.h smlua_bind_function(L, "get_temp_object_hitbox", smlua_func_get_temp_object_hitbox); smlua_bind_function(L, "get_trajectory", smlua_func_get_trajectory); + smlua_bind_function(L, "obj_check_hitbox_overlap", smlua_func_obj_check_hitbox_overlap); smlua_bind_function(L, "obj_get_first", smlua_func_obj_get_first); smlua_bind_function(L, "obj_get_first_with_behavior_id", smlua_func_obj_get_first_with_behavior_id); smlua_bind_function(L, "obj_get_first_with_behavior_id_and_field_f32", smlua_func_obj_get_first_with_behavior_id_and_field_f32); @@ -17013,7 +17072,10 @@ void smlua_bind_functions_autogen(void) { smlua_bind_function(L, "obj_get_temp_spawn_particles_info", smlua_func_obj_get_temp_spawn_particles_info); smlua_bind_function(L, "obj_has_behavior_id", smlua_func_obj_has_behavior_id); smlua_bind_function(L, "obj_has_model_extended", smlua_func_obj_has_model_extended); + smlua_bind_function(L, "obj_is_valid_for_interaction", smlua_func_obj_is_valid_for_interaction); + smlua_bind_function(L, "obj_move_xyz", smlua_func_obj_move_xyz); smlua_bind_function(L, "obj_set_model_extended", smlua_func_obj_set_model_extended); + smlua_bind_function(L, "obj_set_vel", smlua_func_obj_set_vel); smlua_bind_function(L, "spawn_non_sync_object", smlua_func_spawn_non_sync_object); smlua_bind_function(L, "spawn_sync_object", smlua_func_spawn_sync_object); diff --git a/src/pc/lua/smlua_hooks.c b/src/pc/lua/smlua_hooks.c index b69f6060..bea50c2b 100644 --- a/src/pc/lua/smlua_hooks.c +++ b/src/pc/lua/smlua_hooks.c @@ -3,6 +3,76 @@ #include "pc/djui/djui_chat_message.h" #include "pc/crash_handler.h" +#if defined(LUA_PROFILER) +#include "../mods/mods.h" +#include "game/print.h" +#include "gfx_dimensions.h" + +extern u64 SDL_GetPerformanceCounter(void); +extern u64 SDL_GetPerformanceFrequency(void); + +#define MAX_PROFILED_MODS 16 +#define REFRESH_RATE 15 + +static struct { + f64 start; + f64 end; + f64 sum; + f64 disp; +} sLuaProfilerCounters[MAX_PROFILED_MODS]; + +static void lua_profiler_start_counter(struct Mod *mod) { + for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) { + if (gActiveMods.entries[i] == mod) { + f64 freq = SDL_GetPerformanceFrequency(); + f64 curr = SDL_GetPerformanceCounter(); + sLuaProfilerCounters[i].start = curr / freq; + return; + } + } +} + +static void lua_profiler_stop_counter(struct Mod *mod) { + for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) { + if (gActiveMods.entries[i] == mod) { + f64 freq = SDL_GetPerformanceFrequency(); + f64 curr = SDL_GetPerformanceCounter(); + sLuaProfilerCounters[i].end = curr / freq; + sLuaProfilerCounters[i].sum += sLuaProfilerCounters[i].end - sLuaProfilerCounters[i].start; + return; + } + } +} + +void lua_profiler_update_counters() { + if (gGlobalTimer % REFRESH_RATE == 0) { + for (s32 i = 0; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i) { + sLuaProfilerCounters[i].disp = sLuaProfilerCounters[i].sum / (f64) REFRESH_RATE; + sLuaProfilerCounters[i].sum = 0; + } + } + for (s32 i = 0, y = SCREEN_HEIGHT - 60; i != MIN(MAX_PROFILED_MODS, gActiveMods.entryCount); ++i, y -= 18) { + const char *modName = gActiveMods.entries[i]->relativePath; + s32 modCounterUs = (s32) (sLuaProfilerCounters[i].disp * 1000000.0); + char text[256]; + snprintf(text, 256, " %05d", modCounterUs); + memcpy(text, modName, MIN(12, strlen(modName) - (gActiveMods.entries[i]->isDirectory ? 0 : 4))); + for (s32 j = 0; j != 12; ++j) { + char c = text[j]; + if (c >= 'a' && c <= 'z') c -= ('a' - 'A'); + if (c == 'J') c = 'I'; + if (c == 'Q') c = 'O'; + if (c == 'V') c = 'U'; + if (c == 'X') c = '*'; + if (c == 'Z') c = '2'; + if ((c < '0' || c > '9') && (c < 'A' || c > 'Z')) c = ' '; + text[j] = c; + } + print_text(GFX_DIMENSIONS_FROM_LEFT_EDGE(4), y, text); + } +} +#endif + #define MAX_HOOKED_REFERENCES 64 #define LUA_BEHAVIOR_FLAG (1 << 15) @@ -20,7 +90,13 @@ static int smlua_call_hook(lua_State* L, int nargs, int nresults, int errfunc, s struct Mod* prev = gLuaActiveMod; gLuaActiveMod = activeMod; gLuaLastHookMod = activeMod; +#if defined(LUA_PROFILER) + lua_profiler_start_counter(activeMod); +#endif int rc = lua_pcall(L, nargs, nresults, errfunc); +#if defined(LUA_PROFILER) + lua_profiler_stop_counter(activeMod); +#endif gLuaActiveMod = prev; return rc; } diff --git a/src/pc/lua/utils/smlua_obj_utils.c b/src/pc/lua/utils/smlua_obj_utils.c index e8de9d24..712e1480 100644 --- a/src/pc/lua/utils/smlua_obj_utils.c +++ b/src/pc/lua/utils/smlua_obj_utils.c @@ -2,6 +2,8 @@ #include "object_constants.h" #include "object_fields.h" #include "src/game/object_helpers.h" +#include "src/game/interaction.h" +#include "engine/math_util.h" #include "pc/lua/smlua.h" #include "smlua_obj_utils.h" @@ -219,3 +221,32 @@ struct ObjectHitbox* get_temp_object_hitbox(void) { memset(&sTmpHitbox, 0, sizeof(struct ObjectHitbox)); return &sTmpHitbox; } + +s32 obj_is_valid_for_interaction(struct Object *o) { + return o->activeFlags != ACTIVE_FLAG_DEACTIVATED && o->oIntangibleTimer == 0 && (o->oInteractStatus & INT_STATUS_INTERACTED) == 0; +} + +s32 obj_check_hitbox_overlap(struct Object *o1, struct Object *o2) { + f32 r2 = sqr(max(o1->hitboxRadius, o1->hurtboxRadius) + max(o2->hitboxRadius, o2->hurtboxRadius)); + f32 d2 = sqr(o1->oPosX - o2->oPosX) + sqr(o1->oPosZ - o2->oPosZ); + if (d2 > r2) return FALSE; + f32 hb1lb = o1->oPosY - o1->hitboxDownOffset; + f32 hb1ub = hb1lb + max(o1->hitboxHeight, o1->hurtboxHeight); + f32 hb2lb = o2->oPosY - o2->hitboxDownOffset; + f32 hb2ub = hb2lb + max(o2->hitboxHeight, o2->hurtboxHeight); + f32 hbsoh = max(o1->hitboxHeight, o1->hurtboxHeight) + max(o2->hitboxHeight, o2->hurtboxHeight); + if (hb2ub - hb1lb > hbsoh || hb1ub - hb2lb > hbsoh) return FALSE; + return TRUE; +} + +void obj_set_vel(struct Object *o, f32 vx, f32 vy, f32 vz) { + o->oVelX = vx; + o->oVelY = vy; + o->oVelZ = vz; +} + +void obj_move_xyz(struct Object *o, f32 dx, f32 dy, f32 dz) { + o->oPosX += dx; + o->oPosY += dy; + o->oPosZ += dz; +} diff --git a/src/pc/lua/utils/smlua_obj_utils.h b/src/pc/lua/utils/smlua_obj_utils.h index d9c268d6..76996633 100644 --- a/src/pc/lua/utils/smlua_obj_utils.h +++ b/src/pc/lua/utils/smlua_obj_utils.h @@ -33,4 +33,9 @@ struct Object *obj_get_next_with_same_behavior_id_and_field_f32(struct Object *o struct SpawnParticlesInfo* obj_get_temp_spawn_particles_info(enum ModelExtendedId modelId); struct ObjectHitbox* get_temp_object_hitbox(void); +s32 obj_is_valid_for_interaction(struct Object *o); +s32 obj_check_hitbox_overlap(struct Object *o1, struct Object *o2); +void obj_set_vel(struct Object *o, f32 vx, f32 vy, f32 vz); +void obj_move_xyz(struct Object *o, f32 dx, f32 dy, f32 dz); + #endif