Skip to content

Commit 0b62d53

Browse files
chief1983claude
andcommitted
Fix standalone crash when client disconnects before mission loads
When a client connects to a standalone server and backs out before a mission is loaded, multi_standalone_reset_all() calls game_level_close() which fires OnMissionAboutToEnd/OnMissionEnd scripting hooks. If any mod script calls mn.evaluateSEXP() from those hooks, alloc_sexp() crashes because the SEXP system was never initialized (Locked_sexp_true/false are still -1, Sexp_nodes is nullptr). Guard multi_standalone_reset_all() to only call game_level_close() when GM_IN_MISSION is set, and additionally guard game_level_close() to skip scripting hooks when Sexp_nodes is uninitialized. Fixes #7353 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 43a475a commit 0b62d53

2 files changed

Lines changed: 15 additions & 4 deletions

File tree

code/network/multi.cpp

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1671,8 +1671,13 @@ void multi_standalone_reset_all()
16711671
// NETLOG
16721672
ml_string(NOX("Standalone resetting"));
16731673

1674-
// shut all game stuff down
1675-
game_level_close();
1674+
// shut all game stuff down -- but only if a mission was actually in progress,
1675+
// otherwise scripting hooks in game_level_close() may fire into uninitialized
1676+
// subsystems (e.g. SEXP nodes never allocated because no mission was loaded)
1677+
if (Game_mode & GM_IN_MISSION) {
1678+
game_level_close();
1679+
Game_mode &= ~GM_IN_MISSION;
1680+
}
16761681

16771682
// reinitialize the gui
16781683
std_reset_standalone_gui();

freespace2/freespace.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,13 @@ static void game_flash_diminish(float frametime)
910910

911911
void game_level_close()
912912
{
913-
if (scripting::hooks::OnMissionAboutToEndHook->isActive())
913+
// Only fire scripting hooks if the SEXP system was initialized (i.e. a mission was
914+
// actually loaded). If no mission was loaded, Sexp_nodes will be nullptr and
915+
// Locked_sexp_true/False will be -1, so any script calling mn.evaluateSEXP() would
916+
// crash in alloc_sexp().
917+
bool sexp_initialized = (Sexp_nodes != nullptr);
918+
919+
if (sexp_initialized && scripting::hooks::OnMissionAboutToEndHook->isActive())
914920
{
915921
scripting::hooks::OnMissionAboutToEndHook->run();
916922
}
@@ -999,7 +1005,7 @@ void game_level_close()
9991005
Error(LOCATION, "Scripting Mission End override is not fully supported yet.");
10001006
}
10011007

1002-
if (scripting::hooks::OnMissionEndHook->isActive())
1008+
if (sexp_initialized && scripting::hooks::OnMissionEndHook->isActive())
10031009
{
10041010
scripting::hooks::OnMissionEndHook->run();
10051011
}

0 commit comments

Comments
 (0)