Skip to content

Dynamic object limit (Again)#1225

Open
Sunketchupm wants to merge 10 commits into
coop-deluxe:devfrom
Sunketchupm:objects
Open

Dynamic object limit (Again)#1225
Sunketchupm wants to merge 10 commits into
coop-deluxe:devfrom
Sunketchupm:objects

Conversation

@Sunketchupm

Copy link
Copy Markdown
Contributor

I decided to look into growing arrays again and this time found out how to deallocate in the growing array. The dynamic object limit this time seems to be very stable. There still is a bug where having more than 4096 objects (+ some more) causes newly spawned objects to not load their model, so a limit as been set anyways.

This might be slower performance wise than just increasing the size of the object pool, since every deallocation needs to run a loop to find the object that needs to be deallocated. It may be solvable with a hash map, but I am not able to think of a way to do so that is faster.

@AgentXLP

Copy link
Copy Markdown
Contributor

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai

coderabbitai Bot commented May 13, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Global object pool converted from a fixed 1200-entry array to a dynamic GrowingArray* (capacity increased to 4096, init 256). Allocation/deallocation and iterations migrated to the new container. Added u32 obj_get_count() with Lua binding. Several spawn prechecks removed and NULL-safety guards added.

Changes

Object Pool Migration & New Counter API

Layer / File(s) Summary
Pool constants, header & autogen sync
src/game/object_list_processor.h, autogen/lua_definitions/constants.lua, src/pc/lua/smlua_constants_autogen.c, docs/lua/constants.md
OBJECT_POOL_CAPACITY increased to 4096, OBJECT_POOL_INIT_CAPACITY added (256); gObjectPool declaration changed to struct GrowingArray*; autogenerated Lua constants and docs updated.
Pool initialization, alloc & dealloc
src/game/object_list_processor.c, src/game/spawn_object.c, src/game/spawn_object.h
Pool is initialized at runtime with growing_array_init() in clear_objects(); allocations use growing_array_alloc; deallocation compacts via growing_array_swap_and_pop; init_free_object_list() was removed.
Iterate & usage migration
src/engine/surface_load.c, src/pc/network/packets/packet_level_macro.c, src/pc/network/packets/packet_level_spawn_info.c
All sites iterating the pool now use growing_array_for_each_ over gObjectPool instead of OBJECT_POOL_CAPACITY and indexed access.
obj_get_count() Implementation & Bindings
src/pc/lua/utils/smlua_obj_utils.h, src/pc/lua/utils/smlua_obj_utils.c, src/pc/lua/smlua_functions_autogen.c, autogen/lua_definitions/functions.lua, docs/lua/functions-7.md, docs/lua/functions.md
Added u32 obj_get_count(void) reading gObjectCounter, Lua binding smlua_func_obj_get_count, registration, and documentation entries.
NULL Safety Guards & Spawn/Throttling Changes
src/engine/graph_node.c, src/game/behaviors/tree_particles.inc.c, src/game/object_helpers.c, src/game/platform_displacement.c, src/game/behaviors/*
Added NULL guards in geo_add_child and platform displacement; removed several gFreeObjectList availability guards so spawns are attempted unconditionally; particle-throttling thresholds adjusted.

Possibly related PRs

  • coop-deluxe/sm64coopdx#1150: Changes to growing-array init/free functions that affect the dynamic object pool initialization used here.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 55.17% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Dynamic object limit (Again)' directly summarizes the main change: refactoring the object pool from fixed-size to dynamic allocation with a capacity limit.
Description check ✅ Passed The description explains the implementation approach (growing arrays with deallocation), notes on stability, and performance considerations—all directly related to the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
src/engine/graph_node.c (1)

595-600: ⚡ Quick win

Consider adding debug logging for the NULL parentLastChild case.

The NULL guard correctly prevents a crash when the parent's child list is corrupted or improperly initialized. However, this creates a silent failure mode where the child is not actually added to the parent's list, but the function still returns childNode as if it succeeded.

In debug builds, consider logging this condition or adding an assertion to help identify graph corruption during development.

🔍 Optional: Add debug assertion
         } else {
             parentLastChild = parentFirstChild->prev;
             if (parentLastChild != NULL) {
                 childNode->prev = parentLastChild;
                 childNode->next = parentFirstChild;
                 parentFirstChild->prev = childNode;
                 parentLastChild->next = childNode;
+            } else {
+#ifdef DEBUG
+                log_to_stdout("geo_add_child: parentLastChild is NULL, graph may be corrupted");
+#endif
             }
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/engine/graph_node.c` around lines 595 - 600, The code silently skips
linking childNode when parentLastChild is NULL; add a debug-time guard to
surface this corruption: inside the same function/block that references
parentLastChild, parentFirstChild, and childNode add either an assertion (e.g.,
assert(parentLastChild != NULL)) or a debug log (using the project's debug/log
macro) that prints parent pointer/id and childNode pointer/id when
parentLastChild == NULL so developers can detect corrupted/unenitialized parent
child lists during development; ensure normal release behavior unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/lua/functions-7.md`:
- Line 3666: The markdown contains an empty-fragment link target "(#)" that
triggers MD042; replace the "(#)" token with a concrete anchor that exists in
the document (for example the heading ID you want to jump to) so the link points
to a real section instead of an empty fragment — locate the "(#)" occurrence and
update it to the appropriate anchor target (e.g., "#<existing-heading-slug>") to
resolve the lint error.

In `@src/engine/surface_load.c`:
- Around line 862-865: The loop dereferences gObjectPool without checking for
NULL; guard gObjectPool before iterating to avoid crashes when the pool isn't
initialized. Change the code around the loop that uses gObjectPool->count and
gObjectPool->buffer (and sets obj->numSurfaces) to first check if gObjectPool is
non-NULL (e.g., if (gObjectPool) { ... }), then iterate over gObjectPool->count
and reset each struct Object* in gObjectPool->buffer by setting obj->numSurfaces
= 0; otherwise skip the loop.

In `@src/game/object_list_processor.c`:
- Around line 595-599: The init currently overwrites gObjectPool with the result
of growing_array_init and on NULL calls clear_objects() then returns, leaving
gObjectPool NULL and causing later derefs; change this so failure is terminal
for the init path or preserves the previous pool: either (a) check the return of
growing_array_init into a local variable, and on NULL call LOG_ERROR and
propagate a failure code (or abort level initialization) instead of returning
after clearing globals, or (b) if preserving state is preferred, do not
overwrite gObjectPool on failure—keep the old gObjectPool and log the error;
specifically modify the block around growing_array_init/gObjectPool and replace
the unconditional assignment/return with a local temp (e.g., newPool) and proper
failure handling so gObjectPool is never left NULL for subsequent code that
dereferences it (also adjust callers to handle the propagated error if you
choose the terminal-failure approach).

In `@src/game/spawn_object.c`:
- Around line 153-158: The code currently pushes obj onto the free list
(freeList / gFreeObjectList) and then calls
growing_array_swap_and_pop(gObjectPool, obj) which frees the pool slot and makes
freeList->next and any raw pointers into the object dangling; remove the call to
growing_array_swap_and_pop here and instead keep the object slot allocated and
stable in gObjectPool (or mark it as free in-place) so pointers like
parentObj/platform pointers remain valid; implement reuse by popping from
gFreeObjectList when allocating new objects rather than immediately freeing the
backing allocation, and defer any actual freeing of the backing store until you
can guarantee no external references remain.

---

Nitpick comments:
In `@src/engine/graph_node.c`:
- Around line 595-600: The code silently skips linking childNode when
parentLastChild is NULL; add a debug-time guard to surface this corruption:
inside the same function/block that references parentLastChild,
parentFirstChild, and childNode add either an assertion (e.g.,
assert(parentLastChild != NULL)) or a debug log (using the project's debug/log
macro) that prints parent pointer/id and childNode pointer/id when
parentLastChild == NULL so developers can detect corrupted/unenitialized parent
child lists during development; ensure normal release behavior unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: dfc87cc4-0442-4510-9221-202ac5e7244d

📥 Commits

Reviewing files that changed from the base of the PR and between 95782f4 and 75656f5.

📒 Files selected for processing (20)
  • autogen/lua_definitions/constants.lua
  • autogen/lua_definitions/functions.lua
  • docs/lua/constants.md
  • docs/lua/functions-7.md
  • docs/lua/functions.md
  • src/engine/graph_node.c
  • src/engine/surface_load.c
  • src/game/behaviors/tree_particles.inc.c
  • src/game/object_helpers.c
  • src/game/object_list_processor.c
  • src/game/object_list_processor.h
  • src/game/platform_displacement.c
  • src/game/spawn_object.c
  • src/game/spawn_object.h
  • src/pc/lua/smlua_constants_autogen.c
  • src/pc/lua/smlua_functions_autogen.c
  • src/pc/lua/utils/smlua_obj_utils.c
  • src/pc/lua/utils/smlua_obj_utils.h
  • src/pc/network/packets/packet_level_macro.c
  • src/pc/network/packets/packet_level_spawn_info.c
💤 Files with no reviewable changes (1)
  • src/game/spawn_object.h

Comment thread docs/lua/functions-7.md
Comment thread src/engine/surface_load.c Outdated
Comment thread src/game/object_list_processor.c
Comment thread src/game/spawn_object.c Outdated
As a consequence, cloning is no longer possible. I will see if I could add that back in.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
src/game/spawn_object.c (1)

153-158: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Keep struct Object storage stable across unload.

growing_array_swap_and_pop(gObjectPool, obj) still reclaims/compacts object storage during unload. Engine state keeps raw struct Object * references outside the pool lists, so this turns stale references into dangling/UAF pointers instead of the old stable-slot behavior. Reuse freed slots in place, or defer reclamation until external references can no longer exist.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/game/spawn_object.c` around lines 153 - 158, The current call to
growing_array_swap_and_pop(gObjectPool, obj) compacts the pool and can move
other struct Object pointers, creating dangling external references; instead
locate the object's slot in gObjectPool->buffer (search for pointer equality),
do not swap or compact, set that buffer[index] = NULL, mark the object as freed
(e.g., obj->alive = false or set a freed flag) and record the index for reuse
(or push it onto a pool free-list) so future allocations reuse the same slot in
place; update any allocation function (the pool allocator) to prefer free-list
indices when creating new objects so storage remains stable across unload.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/game/object_helpers.c`:
- Around line 2526-2536: Reintroduce a headroom throttle so cosmetic emitters
can't consume the last object slots: before emitting (using variables
numParticles, gPrevFrameObjectCount, OBJECT_POOL_CAPACITY), compute a small
reserve (e.g., reserve = 8–10) and clamp numParticles to at most max(0,
OBJECT_POOL_CAPACITY - gPrevFrameObjectCount - reserve). Also restore the
previous soft cap fallback (the 150/240 threshold limiting numParticles to ~10)
so bursts are reduced early; keep allocate_object() hard-fail protection in
place but ensure emitters never try to allocate the reserved slots.

---

Duplicate comments:
In `@src/game/spawn_object.c`:
- Around line 153-158: The current call to
growing_array_swap_and_pop(gObjectPool, obj) compacts the pool and can move
other struct Object pointers, creating dangling external references; instead
locate the object's slot in gObjectPool->buffer (search for pointer equality),
do not swap or compact, set that buffer[index] = NULL, mark the object as freed
(e.g., obj->alive = false or set a freed flag) and record the index for reuse
(or push it onto a pool free-list) so future allocations reuse the same slot in
place; update any allocation function (the pool allocator) to prefer free-list
indices when creating new objects so storage remains stable across unload.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 08ce9a90-d114-40c7-b728-121e231cd739

📥 Commits

Reviewing files that changed from the base of the PR and between 75656f5 and e5a0cfe.

📒 Files selected for processing (9)
  • src/engine/surface_load.c
  • src/game/behaviors/bubble.inc.c
  • src/game/behaviors/water_objs.inc.c
  • src/game/object_helpers.c
  • src/game/object_list_processor.c
  • src/game/object_list_processor.h
  • src/game/spawn_object.c
  • src/pc/network/packets/packet_level_macro.c
  • src/pc/network/packets/packet_level_spawn_info.c
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/pc/network/packets/packet_level_spawn_info.c
  • src/pc/network/packets/packet_level_macro.c
  • src/game/object_list_processor.h
  • src/engine/surface_load.c
  • src/game/object_list_processor.c

Comment thread src/game/object_helpers.c Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (1)
src/game/spawn_object.c (1)

157-159: ⚠️ Potential issue | 🔴 Critical | 🏗️ Heavy lift

Don't free the struct Object storage here.

growing_array_swap_and_pop() changes the old "inactive but address-stable slot" behavior into immediate reclamation. This path only clears a few references before Line 157, so any surviving raw Object * elsewhere becomes a UAF instead of a benign pointer to a deactivated slot. Keep unloaded objects recyclable in place and reuse them on the next allocation rather than reclaiming the backing storage here.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/game/spawn_object.c` around lines 157 - 159, Summary: The code currently
reclaims the Object's backing storage via growing_array_swap_and_pop and
clearing gObjectPool->buffer, turning other raw Object* references into UAFs;
instead keep the slot address-stable and mark the object as inactive for reuse.
Fix: stop calling growing_array_swap_and_pop(gObjectPool, obj) and stop clearing
gObjectPool->buffer[gObjectPool->count]; leave the Object* in the pool and set
an inactive flag on the object (e.g., obj->active = false or obj->loaded =
false) so the slot remains address-stable and can be recycled on next
allocation; remove any obj = NULL assignment and ensure no free()/deallocation
is performed in spawn_object.c so existing raw pointers remain valid. Referenced
symbols: growing_array_swap_and_pop, gObjectPool, obj, gObjectPool->buffer,
gObjectPool->count.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@src/game/spawn_object.c`:
- Around line 157-159: Summary: The code currently reclaims the Object's backing
storage via growing_array_swap_and_pop and clearing gObjectPool->buffer, turning
other raw Object* references into UAFs; instead keep the slot address-stable and
mark the object as inactive for reuse. Fix: stop calling
growing_array_swap_and_pop(gObjectPool, obj) and stop clearing
gObjectPool->buffer[gObjectPool->count]; leave the Object* in the pool and set
an inactive flag on the object (e.g., obj->active = false or obj->loaded =
false) so the slot remains address-stable and can be recycled on next
allocation; remove any obj = NULL assignment and ensure no free()/deallocation
is performed in spawn_object.c so existing raw pointers remain valid. Referenced
symbols: growing_array_swap_and_pop, gObjectPool, obj, gObjectPool->buffer,
gObjectPool->count.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: b97d8664-98ea-4921-9d6b-b16d656e5bde

📥 Commits

Reviewing files that changed from the base of the PR and between e5a0cfe and 73da2e3.

📒 Files selected for processing (3)
  • src/game/behaviors/tree_particles.inc.c
  • src/game/object_helpers.c
  • src/game/spawn_object.c

@Sunketchupm

Copy link
Copy Markdown
Contributor Author

Part of the reason why I'm pretty much avoiding the "reuse stale or unload later" approach is that fake objects don't crash or cause any issues, which is really the only way to maintain a reference to an unloaded object. So I don't need to complicate the code any further.

@PeachyPeachSM64 PeachyPeachSM64 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty solid so far
The 4096 objects bug should probably be investigated, but not in this PR

Comment thread src/engine/surface_load.c
Comment on lines +862 to +865
if (gObjectPool == NULL) {
fprintf(stderr, "FATAL ERROR: The object pool was not created but object surfaces were attempted to be cleared.\n");
return;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it a fatal error though?
It doesn't really matter if there is no object pool, since what the following code does is simply setting the objects numSurfaces to 0
Could be replaced by:
LOG_ERROR("The object pool was not created but object surfaces were attempted to be cleared.");

Comment thread src/engine/surface_load.c Outdated
Comment on lines 866 to 869
for (u16 i = 0; i < gObjectPool->count; i++) {
struct Object* obj = gObjectPool->buffer[i];
obj->numSurfaces = 0;
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can do this now:

growing_array_for_each_(gObjectPool, struct Object, obj) {
    obj->numSurfaces = 0;
}

Comment thread src/game/behaviors/tree_particles.inc.c Outdated
if (o->oTimer > 100)
obj_mark_for_deletion(o);
if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 212 / 240))
if (gPrevFrameObjectCount > OBJECT_POOL_CAPACITY - 200)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be good to have a macro for this, like:
#define OBJECT_POOL_PARTICLES_THRESHOLD (OBJECT_POOL_CAPACITY - 200)

Comment thread src/game/object_helpers.c Outdated

// If there are a lot of objects already, limit the number of particles
if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 150 / 240) && numParticles > 10) {
if (gPrevFrameObjectCount > OBJECT_POOL_CAPACITY - 200 && numParticles > 10) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Comment thread src/game/object_helpers.c Outdated
// We're close to running out of object slots, so don't spawn particles at
// all
if (gPrevFrameObjectCount > (OBJECT_POOL_CAPACITY * 210 / 240)) {
if (gPrevFrameObjectCount > OBJECT_POOL_CAPACITY - 50) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same here

Comment on lines +25 to +26
for (u32 i = 0; i < gObjectPool->count; i++) {
struct Object* o = gObjectPool->buffer[i];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

growing_array_for_each_

Comment on lines +203 to +204
for (u32 i = 0; i < gObjectPool->count; i++) {
struct Object* o2 = gObjectPool->buffer[i];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

growing_array_for_each_

Comment on lines +214 to +215
for (u32 i = 0; i < gObjectPool->count; i++) {
struct Object* o2 = gObjectPool->buffer[i];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

growing_array_for_each_

Comment on lines +23 to +24
for (u32 i = 0; i < gObjectPool->count; i++) {
struct Object* o = gObjectPool->buffer[i];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

growing_array_for_each_

for (s32 i = 0; i < OBJECT_POOL_CAPACITY; i++) {
struct Object* o = &gObjectPool[i];
if (gObjectPool == NULL) {
fprintf(stderr, "FATAL ERROR: The object pool was not created but object respawn infos were attempted to be checked.\n");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LOG_ERROR

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/game/platform_displacement.c`:
- Around line 177-179: The code reads player->platform into platform before
ensuring player is non-NULL, risking a null dereference; change the logic in the
function containing m->marioObj so that you first check player != NULL (and the
time-stop condition) and only then access player->platform, or alternatively
move the assignment "struct Object *platform = player->platform;" to after the
guard that verifies player is not NULL; reference the local variables
m->marioObj, player, and platform when making this change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: f136e697-2b5d-42b2-b274-861e85874be3

📥 Commits

Reviewing files that changed from the base of the PR and between 73da2e3 and 0aed8fc.

📒 Files selected for processing (17)
  • autogen/lua_definitions/constants.lua
  • autogen/lua_definitions/functions.lua
  • docs/lua/constants.md
  • docs/lua/functions-7.md
  • docs/lua/functions.md
  • src/engine/surface_load.c
  • src/game/behaviors/tree_particles.inc.c
  • src/game/object_helpers.c
  • src/game/object_list_processor.c
  • src/game/object_list_processor.h
  • src/game/platform_displacement.c
  • src/game/spawn_object.c
  • src/pc/lua/smlua_constants_autogen.c
  • src/pc/lua/smlua_functions_autogen.c
  • src/pc/lua/utils/smlua_obj_utils.h
  • src/pc/network/packets/packet_level_macro.c
  • src/pc/network/packets/packet_level_spawn_info.c
💤 Files with no reviewable changes (1)
  • autogen/lua_definitions/functions.lua
✅ Files skipped from review due to trivial changes (3)
  • src/pc/lua/smlua_functions_autogen.c
  • docs/lua/functions.md
  • src/pc/lua/smlua_constants_autogen.c

Comment on lines +177 to 179
struct Object *player = m->marioObj;
struct Object *platform = player->platform;
if (!(gTimeStopState & TIME_STOP_ACTIVE) && player != NULL && platform != NULL) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Potential null dereference before guard

At Line 178, platform is read via player->platform before confirming player != NULL (the check is only at Line 179). This can crash when m->marioObj is NULL.

Suggested fix
-        struct Object *player = m->marioObj;
-        struct Object *platform = player->platform;
-        if (!(gTimeStopState & TIME_STOP_ACTIVE) && player != NULL && platform != NULL) {
+        struct Object *player = m->marioObj;
+        if (player == NULL) { continue; }
+        struct Object *platform = player->platform;
+        if (!(gTimeStopState & TIME_STOP_ACTIVE) && platform != NULL) {
             apply_platform_displacement(player, platform);
         }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
struct Object *player = m->marioObj;
struct Object *platform = player->platform;
if (!(gTimeStopState & TIME_STOP_ACTIVE) && player != NULL && platform != NULL) {
struct Object *player = m->marioObj;
if (player == NULL) { continue; }
struct Object *platform = player->platform;
if (!(gTimeStopState & TIME_STOP_ACTIVE) && platform != NULL) {
apply_platform_displacement(player, platform);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/game/platform_displacement.c` around lines 177 - 179, The code reads
player->platform into platform before ensuring player is non-NULL, risking a
null dereference; change the logic in the function containing m->marioObj so
that you first check player != NULL (and the time-stop condition) and only then
access player->platform, or alternatively move the assignment "struct Object
*platform = player->platform;" to after the guard that verifies player is not
NULL; reference the local variables m->marioObj, player, and platform when
making this change.

gDoorAdjacentRooms[i][1] = 0;
}

gObjectPool = growing_array_init(gObjectPool, OBJECT_POOL_INIT_CAPACITY, malloc, free);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the object pool should be initialised on startup, to do this use the AT_STARTUP flag with a new function.
This is because the object pool used to exist at all times during program execution, so let's try to keep that.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants