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.
This commit is contained in:
PeachyPeach 2022-04-20 20:20:45 +02:00 committed by GitHub
parent 950aeb0e28
commit 9e5a45ecdd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 343 additions and 2 deletions

View file

@ -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

View file

@ -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

View file

@ -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
<br />
## [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:](#)
<br />
## [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
<br />
## [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:](#)
<br />
## [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:](#)
<br />
## [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
<br />
## [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:](#)
<br />
## [spawn_non_sync_object](#spawn_non_sync_object)
### Lua Example

View file

@ -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 {

View file

@ -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
}
}

View file

@ -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);
}

View file

@ -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);

View file

@ -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;
}

View file

@ -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;
}

View file

@ -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