From ac151181d2e5f322a0f0e5c7f36c3859aa55161c Mon Sep 17 00:00:00 2001 From: nhmall Date: Mon, 6 Jul 2026 14:16:15 -0400 Subject: [PATCH] more u.ustuck issues on save/restore During a synchronous save operation initiated by the player (or other trigger for dosave0()), the u.usteed_mid and u.ustuck_mid values get set by savemonch(), and the monst pointers that u.ustuck and u.usteed point to are no longer valid, but not cleared. The checkpoint operation, which also needs to ensure that u.ustuck_mid and u.usteed_mid are set, must set them during the checkpoint, which is okay because the u.usteed and u.ustuck pointers _are_ valid during a checkpoint operation. So, we need to distinguish between a save game sequence, and a checkpoint sequence when writing out the u struct. --- src/mon.c | 2 ++ src/restore.c | 3 +-- src/save.c | 29 ++++++++++++++++++++++++----- 3 files changed, 27 insertions(+), 7 deletions(-) diff --git a/src/mon.c b/src/mon.c index f04e2418c..6ad8a0fb5 100644 --- a/src/mon.c +++ b/src/mon.c @@ -3433,6 +3433,8 @@ set_ustuck(struct monst *mtmp) disp.botl = TRUE; u.ustuck = mtmp; + if (u.ustuck_mid) + u.ustuck_mid = 0; if (!u.ustuck) { u.uswallow = 0; u.uswldtim = 0; diff --git a/src/restore.c b/src/restore.c index 5ab2342c7..8a7ad8743 100644 --- a/src/restore.c +++ b/src/restore.c @@ -1219,8 +1219,7 @@ getlev(NHFILE *nhfp, int pid, xint8 lev) u.usteed_mid = 0; } else { if (mtmp->m_id == u.ustuck_mid) { - set_ustuck(mtmp); - u.ustuck_mid = 0; + set_ustuck(mtmp); /* set_ustuck clears u.ustuck_mid */ } place_monster(mtmp, mtmp->mx, mtmp->my); if (mtmp->wormno) diff --git a/src/save.c b/src/save.c index d03000408..7eb7f5eb6 100644 --- a/src/save.c +++ b/src/save.c @@ -285,13 +285,32 @@ savegamestate(NHFILE *nhfp) Sfo_long(nhfp, &svw.wreserve, "wreserve"); Sfo_int32(nhfp, &svw.wtreserved, "wtreserved"); /* - * It is critical to ensure that u.ustuck_mid and u.usteed_mid - * hold current and correct data, in case this is needed by - * recover. + * In a normal save operation initiated by the player, + * the u.ustuck_mid and u.usteed_mid were already set + * by savemonchn() called from savelev_core(). Under + * that operation, u.ustuck and u.usteed pointers no longer + * point to valid locations here. Their non-zero values serve + * only to flag that they are were set, but the pointers + * must not be dereferenced. */ - u.ustuck_mid = (u.ustuck) ? u.ustuck->m_id : 0; - u.usteed_mid = (u.usteed) ? u.usteed->m_id : 0; + if (program_state.in_checkpoint) { + /* + * It is critical to ensure that u.ustuck_mid and u.usteed_mid + * hold current and correct data, in case this is needed by + * recover. The pointers, if set, are still pointing at + * valid data during a checkpoint operation, unlike during + * a synchronized save operation. + */ + u.ustuck_mid = (u.ustuck) ? u.ustuck->m_id : 0; + u.usteed_mid = (u.usteed) ? u.usteed->m_id : 0; + } Sfo_you(nhfp, &u, "gamestate-you"); + + /* clear the in-memory value of these, now that they have been + * successfully written out. + */ + u.ustuck_mid = 0; + u.usteed_mid = 0; Sfo_char(nhfp, yyyymmddhhmmss(ubirthday), "gamestate-ubirthday", 14); Sfo_long(nhfp, &urealtime.realtime, "gamestate-realtime"); Sfo_char(nhfp, yyyymmddhhmmss(urealtime.start_timing), "gamestate-start_timing", 14);