Add some sanity checks to prevent bugs and exploits. (#767)

* Add some sanity checks to prevent bugs and exploits.

* Improve sanity check for Koopa Shell Riding.

* Sort and optimize MarioState structure.
This commit is contained in:
Prince Frizzy 2025-04-27 17:02:15 -04:00 committed by GitHub
parent 0e00fe7ad3
commit 15d6f6ae07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
16 changed files with 2078 additions and 1972 deletions

View file

@ -4706,6 +4706,13 @@ function does_mario_have_normal_cap_on_head(m)
-- ...
end
--- @param m MarioState
--- @return boolean
--- Checks if Mario has already had a cap blown off of his head in the current level, Returns true if a blown cap can be found for Mario, false if not. Useful to check if a blown cap exists in the level currently.
function does_mario_have_blown_cap(m)
-- ...
end
--- @param m MarioState
--- @param capSpeed number
--- Makes Mario blow off his normal cap at a given speed. Removes the normal cap from Mario's head and spawns it as a collectible object in the game world. Useful for simulating events where Mario loses his cap due to enemy attacks or environmental forces

View file

@ -1071,6 +1071,7 @@
--- @field public fixCollisionBugsGroundPoundBonks integer
--- @field public fixCollisionBugsPickBestWall integer
--- @field public fixCollisionBugsRoundedCorners integer
--- @field public fixInvalidShellRides integer
--- @field public fixVanishFloors integer
--- @field public floatingStarDance integer
--- @field public floorLowerLimit integer

View file

@ -5640,6 +5640,29 @@ Checks if Mario is currently wearing his normal cap on his head. Returns true if
<br />
## [does_mario_have_blown_cap](#does_mario_have_blown_cap)
### Description
Checks if Mario has already had a cap blown off of his head in the current level, Returns true if a blown cap can be found for Mario, false if not. Useful to check if a blown cap exists in the level currently.
### Lua Example
`local booleanValue = does_mario_have_blown_cap(m)`
### Parameters
| Field | Type |
| ----- | ---- |
| m | [MarioState](structs.md#MarioState) |
### Returns
- `boolean`
### C Prototype
`bool does_mario_have_blown_cap(struct MarioState *m);`
[:arrow_up_small:](#)
<br />
## [mario_blow_off_cap](#mario_blow_off_cap)
### Description

View file

@ -911,6 +911,7 @@
- [mario_throw_held_object](functions-3.md#mario_throw_held_object)
- [mario_stop_riding_and_holding](functions-3.md#mario_stop_riding_and_holding)
- [does_mario_have_normal_cap_on_head](functions-3.md#does_mario_have_normal_cap_on_head)
- [does_mario_have_blown_cap](functions-3.md#does_mario_have_blown_cap)
- [mario_blow_off_cap](functions-3.md#mario_blow_off_cap)
- [mario_lose_cap_to_enemy](functions-3.md#mario_lose_cap_to_enemy)
- [mario_retrieve_cap](functions-3.md#mario_retrieve_cap)

View file

@ -1642,6 +1642,7 @@
| fixCollisionBugsGroundPoundBonks | `integer` | |
| fixCollisionBugsPickBestWall | `integer` | |
| fixCollisionBugsRoundedCorners | `integer` | |
| fixInvalidShellRides | `integer` | |
| fixVanishFloors | `integer` | |
| floatingStarDance | `integer` | |
| floorLowerLimit | `integer` | |

File diff suppressed because it is too large Load diff

View file

@ -355,90 +355,119 @@ struct MarioAnimation
struct MarioState
{
/*0x00*/ u16 playerIndex;
/*0x02*/ u16 input;
/*0x04*/ u32 flags;
/*0x08*/ u32 particleFlags;
/*0x0C*/ u32 action;
/*0x10*/ u32 prevAction;
/*0x14*/ u32 terrainSoundAddend;
/*0x18*/ u16 actionState;
/*0x1A*/ u16 actionTimer;
/*0x1C*/ u32 actionArg;
/*0x20*/ f32 intendedMag;
/*0x24*/ s16 intendedYaw;
/*0x26*/ s16 invincTimer;
/*0x28*/ u8 framesSinceA;
/*0x29*/ u8 framesSinceB;
/*0x2A*/ u8 wallKickTimer;
/*0x2B*/ u8 doubleJumpTimer;
/*0x2C*/ Vec3s faceAngle;
/*0x32*/ Vec3s angleVel;
/*0x38*/ s16 slideYaw;
/*0x3A*/ s16 twirlYaw;
/*0x3C*/ Vec3f pos;
/*0x48*/ Vec3f vel;
/*0x54*/ f32 forwardVel;
/*0x58*/ f32 slideVelX;
/*0x5C*/ f32 slideVelZ;
/*0x60*/ struct Surface *wall;
/*0x64*/ struct Surface *ceil;
/*0x68*/ struct Surface *floor;
/*0x6C*/ f32 ceilHeight;
/*0x70*/ f32 floorHeight;
/*0x74*/ s16 floorAngle;
/*0x76*/ s16 waterLevel;
/*0x78*/ struct Object *interactObj;
/*0x7C*/ struct Object *heldObj;
/*0x80*/ struct Object *usedObj;
/*0x84*/ struct Object *riddenObj;
/*0x88*/ struct Object *marioObj;
/*0x8C*/ struct SpawnInfo *spawnInfo;
/*0x90*/ struct Area *area;
/*0x94*/ struct PlayerCameraState *statusForCamera;
/*0x98*/ struct MarioBodyState *marioBodyState;
/*0x9C*/ struct Controller *controller;
/*0xA0*/ struct MarioAnimation *animation;
/*0xA4*/ u32 collidedObjInteractTypes;
/*0xA8*/ s16 numCoins;
/*0xAA*/ s16 numStars;
/*0xAC*/ s8 numKeys; // Unused key mechanic
/*0xAD*/ s8 numLives;
/*0xAE*/ s16 health;
/*0xB0*/ s16 unkB0;
/*0xB2*/ u8 hurtCounter;
/*0xB3*/ u8 healCounter;
/*0xB4*/ u8 squishTimer;
/*0xB5*/ u8 fadeWarpOpacity;
/*0xB6*/ u16 capTimer;
/*0xB8*/ s16 prevNumStarsForDialog;
/*0xBC*/ f32 peakHeight;
/*0xC0*/ f32 quicksandDepth;
/*0xC4*/ f32 unkC4;
/*0xC8*/ s16 currentRoom;
/*0xCA*/ struct Object* heldByObj;
/*????*/ u8 isSnoring;
/*????*/ struct Object* bubbleObj;
/*????*/ u8 freeze;
// Please try to keep this 32/64 bit aligned.
// Bit alignment can increase perforamance and
// reduce the memory footprint.
//
// Structure size was reduced by 32 bytes and fields
// and been moved for performance and size.
// https://en.wikipedia.org/wiki/Data_structure_alignment
//
// I personally also find it easier to read now.
// - Prince Frizzy
u16 playerIndex;
u16 input;
s16 numCoins;
s16 numStars;
s8 numLives;
s8 numKeys; // Unused key mechanic
s16 health;
u8 hurtCounter;
u8 healCounter;
u8 isSnoring;
u8 freeze;
u32 cap;
u16 capTimer;
s16 invincTimer;
u8 skipWarpInteractionsTimer;
u8 squishTimer;
u8 bounceSquishTimer;
s8 knockbackTimer;
u8 wallKickTimer;
u8 doubleJumpTimer;
u8 specialTripleJump;
u8 fadeWarpOpacity;
u8 visibleToEnemies;
u8 wasNetworkVisible;
s16 dialogId;
s16 prevNumStarsForDialog;
s16 unkB0;
u32 action;
u32 prevAction;
u32 actionArg;
u16 actionTimer;
u16 actionState;
u32 flags;
f32 quicksandDepth;
struct Controller *controller;
struct MarioBodyState *marioBodyState;
struct Character *character;
u32 terrainSoundAddend;
Vec3f pos;
Vec3f nonInstantWarpPos;
Vec3f vel;
f32 slideVelX;
f32 slideVelZ;
f32 forwardVel;
f32 peakHeight;
f32 intendedMag;
s16 intendedYaw;
u8 framesSinceA;
u8 framesSinceB;
Vec3s faceAngle;
Vec3s angleVel;
s16 slideYaw;
s16 twirlYaw;
struct Object *heldObj;
struct Object *heldByObj;
struct Object *interactObj;
struct Object *riddenObj;
struct Object *usedObj;
struct Object *marioObj;
struct Object *bubbleObj;
u32 collidedObjInteractTypes;
u32 particleFlags;
struct MarioAnimation *animation;
// Variables for a spline curve animation (used for the flight path in the grand star cutscene)
/*????*/ Vec4s* splineKeyframe;
/*????*/ f32 splineKeyframeFraction;
/*????*/ s32 splineState;
/*????*/ Vec3f nonInstantWarpPos;
/*????*/ struct Character* character;
/*????*/ u8 wasNetworkVisible;
/*????*/ f32 minimumBoneY;
/*????*/ f32 curAnimOffset;
/*????*/ s8 knockbackTimer;
/*????*/ u8 specialTripleJump;
/*????*/ Vec3f wallNormal;
/*????*/ u8 visibleToEnemies;
/*????*/ u32 cap;
/*????*/ u8 bounceSquishTimer;
/*????*/ u8 skipWarpInteractionsTimer;
/*????*/ s16 dialogId;
Vec4s *splineKeyframe;
f32 splineKeyframeFraction;
s32 splineState;
f32 curAnimOffset;
f32 minimumBoneY;
struct Surface *wall;
struct Surface *ceil;
struct Surface *floor;
struct SpawnInfo *spawnInfo;
struct Area *area;
struct PlayerCameraState *statusForCamera;
f32 ceilHeight;
f32 floorHeight;
Vec3f wallNormal;
f32 unkC4;
s16 floorAngle;
s16 waterLevel;
s16 currentRoom;
};
struct TextureInfo

View file

@ -50,6 +50,7 @@ struct LevelValues gDefaultLevelValues = {
.fixCollisionBugsGroundPoundBonks = TRUE,
.fixCollisionBugsPickBestWall = TRUE,
.fixVanishFloors = FALSE,
.fixInvalidShellRides = TRUE,
.hudCapTimer = FALSE,
.hudRedCoinsRadar = FALSE,
.hudSecretsRadar = FALSE,

View file

@ -48,6 +48,7 @@ struct LevelValues {
u8 fixCollisionBugsGroundPoundBonks;
u8 fixCollisionBugsPickBestWall;
u8 fixVanishFloors;
u8 fixInvalidShellRides;
u8 hudCapTimer;
u8 hudRedCoinsRadar;
u8 hudSecretsRadar;

View file

@ -25,12 +25,14 @@
#include "sound_init.h"
#include "rumble_init.h"
#include "object_collision.h"
#include "object_list_processor.h"
#include "hardcoded.h"
#include "pc/configfile.h"
#include "pc/network/network.h"
#include "pc/network/lag_compensation.h"
#include "pc/lua/smlua_hooks.h"
#include "pc/lua/utils/smlua_obj_utils.h"
u8 sDelayInvincTimer;
s16 gInteractionInvulnerable;
@ -279,15 +281,14 @@ u32 attack_object(struct MarioState* m, struct Object *o, s32 interaction) {
}
void mario_stop_riding_object(struct MarioState *m) {
if (!m) { return; }
if (m->riddenObj != NULL && m->playerIndex == 0) {
m->riddenObj->oInteractStatus = INT_STATUS_STOP_RIDING;
if (m->riddenObj->oSyncID != 0) {
network_send_object_reliability(m->riddenObj, TRUE);
}
stop_shell_music();
m->riddenObj = NULL;
if (!m || m->riddenObj == NULL || m->playerIndex != 0) { return; }
m->riddenObj->oInteractStatus = INT_STATUS_STOP_RIDING;
if (m->riddenObj->oSyncID != 0) {
network_send_object_reliability(m->riddenObj, TRUE);
}
stop_shell_music();
m->riddenObj = NULL;
}
void mario_grab_used_object(struct MarioState *m) {
@ -375,37 +376,40 @@ u32 does_mario_have_normal_cap_on_head(struct MarioState *m) {
return (m->flags & (MARIO_CAPS | MARIO_CAP_ON_HEAD)) == (MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
}
bool does_mario_have_blown_cap(struct MarioState *m) {
if (!m) { return FALSE; }
return obj_get_first_with_behavior_id_and_field_s32(id_bhvNormalCap, 0x40, m->playerIndex + 1) != NULL;
}
void mario_blow_off_cap(struct MarioState *m, f32 capSpeed) {
if (!m) { return; }
if (m->playerIndex != 0) { return; }
struct Object *capObject;
if (!does_mario_have_normal_cap_on_head(m) || does_mario_have_blown_cap(m)) { return; }
m->cap = SAVE_FLAG_CAP_ON_MR_BLIZZARD;
if (does_mario_have_normal_cap_on_head(m)) {
m->cap = SAVE_FLAG_CAP_ON_MR_BLIZZARD;
m->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
m->flags &= ~(MARIO_NORMAL_CAP | MARIO_CAP_ON_HEAD);
u8 capModel = m->character->capModelId;
struct Object *capObject = spawn_object(m->marioObj, capModel, bhvNormalCap);
if (capObject == NULL) { return; }
capObject->globalPlayerIndex = gNetworkPlayers[m->playerIndex].globalIndex;
capObject->oBehParams = m->playerIndex + 1;
u8 capModel = m->character->capModelId;
capObject = spawn_object(m->marioObj, capModel, bhvNormalCap);
if (capObject == NULL) { return; }
capObject->globalPlayerIndex = gNetworkPlayers[m->playerIndex].globalIndex;
capObject->oBehParams = m->playerIndex + 1;
capObject->oPosY += (m->action & ACT_FLAG_SHORT_HITBOX) ? 120.0f : 180.0f;
capObject->oForwardVel = capSpeed;
capObject->oMoveAngleYaw = (s16)(m->faceAngle[1] + 0x400);
capObject->oPosY += (m->action & ACT_FLAG_SHORT_HITBOX) ? 120.0f : 180.0f;
capObject->oForwardVel = capSpeed;
capObject->oMoveAngleYaw = (s16)(m->faceAngle[1] + 0x400);
if (m->forwardVel < 0.0f) {
capObject->oMoveAngleYaw = (s16)(capObject->oMoveAngleYaw + 0x8000);
}
// set as it's own parent so we can spawn it over the network
capObject->parentObj = capObject;
struct Object* spawn_objects[] = { capObject };
u32 models[] = { capModel };
network_send_spawn_objects(spawn_objects, models, 1);
if (m->forwardVel < 0.0f) {
capObject->oMoveAngleYaw = (s16)(capObject->oMoveAngleYaw + 0x8000);
}
// set as it's own parent so we can spawn it over the network
capObject->parentObj = capObject;
struct Object* spawn_objects[] = { capObject };
u32 models[] = { capModel };
network_send_spawn_objects(spawn_objects, models, 1);
}
u32 mario_lose_cap_to_enemy(struct MarioState* m, u32 arg) {

View file

@ -352,6 +352,13 @@ Useful for determining Mario's cap status
|descriptionEnd| */
u32 does_mario_have_normal_cap_on_head(struct MarioState *m);
/* |description|
Checks if Mario has already had a cap blown off of his head in the current level,
Returns true if a blown cap can be found for Mario, false if not.
Useful to check if a blown cap exists in the level currently.
|descriptionEnd| */
bool does_mario_have_blown_cap(struct MarioState *m);
/* |description|
Makes Mario blow off his normal cap at a given speed.
Removes the normal cap from Mario's head and spawns it as a collectible object in the game world.

View file

@ -1611,10 +1611,6 @@ void update_mario_inputs(struct MarioState *m) {
#endif
/* End of developer stuff */
if ((m->action == ACT_END_PEACH_CUTSCENE || m->action == ACT_CREDITS_CUTSCENE) && m->controller->buttonPressed & START_BUTTON) {
lvl_skip_credits();
}
if (m->playerIndex == 0) {
if (!localIsPaused && (gCameraMovementFlags & CAM_MOVE_C_UP_MODE)) {
if (m->action & ACT_FLAG_ALLOW_FIRST_PERSON) {

View file

@ -3003,6 +3003,10 @@ static s32 act_end_peach_cutscene(struct MarioState *m) {
m->actionTimer++;
if (m->playerIndex == 0) {
if (m->controller->buttonPressed & START_BUTTON) {
lvl_skip_credits();
}
sEndCutsceneVp.vp.vscale[0] = 640;
sEndCutsceneVp.vp.vscale[1] = 360;
sEndCutsceneVp.vp.vtrans[0] = 640;
@ -3049,6 +3053,10 @@ static s32 act_credits_cutscene(struct MarioState *m) {
stop_and_set_height_to_floor(m);
}
}
if (m->playerIndex == 0 && m->controller->buttonPressed & START_BUTTON) {
lvl_skip_credits();
}
if (m->actionTimer >= TIMER_CREDITS_SHOW) {
if (m->actionState < 40) {

View file

@ -12,6 +12,7 @@
#include "memory.h"
#include "behavior_data.h"
#include "rumble_init.h"
#include "hardcoded.h"
#include "pc/debuglog.h"
#include "pc/configfile.h"
#include "pc/network/network.h"
@ -1344,7 +1345,12 @@ s32 act_hold_decelerating(struct MarioState *m) {
s32 act_riding_shell_ground(struct MarioState *m) {
if (!m) { return FALSE; }
s16 startYaw = m->faceAngle[1];
// If we don't have an object we're riding or if the interaction was with something
// not a Koopa Shell-Then we abort the riding state.
if (gLevelValues.fixInvalidShellRides && (m->riddenObj == NULL || m->riddenObj->oInteractType != INTERACT_KOOPA_SHELL)) {
return set_mario_action(m, ACT_IDLE, 0);
}
if (m->input & INPUT_A_PRESSED) {
return set_mario_action(m, ACT_RIDING_SHELL_JUMP, 0);
@ -1358,6 +1364,7 @@ s32 act_riding_shell_ground(struct MarioState *m) {
return set_mario_action(m, ACT_CROUCH_SLIDE, 0);
}
s16 startYaw = m->faceAngle[1];
update_shell_speed(m);
set_character_animation(m, m->actionArg == 0 ? CHAR_ANIM_START_RIDING_SHELL : CHAR_ANIM_RIDING_SHELL);

View file

@ -1327,7 +1327,7 @@ static struct LuaObjectField sLakituStateFields[LUA_LAKITU_STATE_FIELD_COUNT] =
{ "yaw", LVT_S16, offsetof(struct LakituState, yaw), false, LOT_NONE, 1, sizeof(s16) },
};
#define LUA_LEVEL_VALUES_FIELD_COUNT 51
#define LUA_LEVEL_VALUES_FIELD_COUNT 52
static struct LuaObjectField sLevelValuesFields[LUA_LEVEL_VALUES_FIELD_COUNT] = {
{ "bubbleOnDeathBarrierInCapStages", LVT_U8, offsetof(struct LevelValues, bubbleOnDeathBarrierInCapStages), false, LOT_NONE, 1, sizeof(u8) },
{ "cellHeightLimit", LVT_S16, offsetof(struct LevelValues, cellHeightLimit), false, LOT_NONE, 1, sizeof(s16) },
@ -1343,6 +1343,7 @@ static struct LuaObjectField sLevelValuesFields[LUA_LEVEL_VALUES_FIELD_COUNT] =
{ "fixCollisionBugsGroundPoundBonks", LVT_U8, offsetof(struct LevelValues, fixCollisionBugsGroundPoundBonks), false, LOT_NONE, 1, sizeof(u8) },
{ "fixCollisionBugsPickBestWall", LVT_U8, offsetof(struct LevelValues, fixCollisionBugsPickBestWall), false, LOT_NONE, 1, sizeof(u8) },
{ "fixCollisionBugsRoundedCorners", LVT_U8, offsetof(struct LevelValues, fixCollisionBugsRoundedCorners), false, LOT_NONE, 1, sizeof(u8) },
{ "fixInvalidShellRides", LVT_U8, offsetof(struct LevelValues, fixInvalidShellRides), false, LOT_NONE, 1, sizeof(u8) },
{ "fixVanishFloors", LVT_U8, offsetof(struct LevelValues, fixVanishFloors), false, LOT_NONE, 1, sizeof(u8) },
{ "floatingStarDance", LVT_U8, offsetof(struct LevelValues, floatingStarDance), false, LOT_NONE, 1, sizeof(u8) },
{ "floorLowerLimit", LVT_S16, offsetof(struct LevelValues, floorLowerLimit), false, LOT_NONE, 1, sizeof(s16) },

View file

@ -14716,6 +14716,24 @@ int smlua_func_does_mario_have_normal_cap_on_head(lua_State* L) {
return 1;
}
int smlua_func_does_mario_have_blown_cap(lua_State* L) {
if (L == NULL) { return 0; }
int top = lua_gettop(L);
if (top != 1) {
LOG_LUA_LINE("Improper param count for '%s': Expected %u, Received %u", "does_mario_have_blown_cap", 1, top);
return 0;
}
if (lua_isnil(L, 1)) { return 0; }
struct MarioState* m = (struct MarioState*)smlua_to_cobject(L, 1, LOT_MARIOSTATE);
if (!gSmLuaConvertSuccess) { LOG_LUA("Failed to convert parameter %u for function '%s'", 1, "does_mario_have_blown_cap"); return 0; }
lua_pushboolean(L, does_mario_have_blown_cap(m));
return 1;
}
int smlua_func_mario_blow_off_cap(lua_State* L) {
if (L == NULL) { return 0; }
@ -35024,6 +35042,7 @@ void smlua_bind_functions_autogen(void) {
smlua_bind_function(L, "mario_throw_held_object", smlua_func_mario_throw_held_object);
smlua_bind_function(L, "mario_stop_riding_and_holding", smlua_func_mario_stop_riding_and_holding);
smlua_bind_function(L, "does_mario_have_normal_cap_on_head", smlua_func_does_mario_have_normal_cap_on_head);
smlua_bind_function(L, "does_mario_have_blown_cap", smlua_func_does_mario_have_blown_cap);
smlua_bind_function(L, "mario_blow_off_cap", smlua_func_mario_blow_off_cap);
smlua_bind_function(L, "mario_lose_cap_to_enemy", smlua_func_mario_lose_cap_to_enemy);
smlua_bind_function(L, "mario_retrieve_cap", smlua_func_mario_retrieve_cap);