From 9f230c3cd4bdd506c51e9b972b0ea828f9ab6519 Mon Sep 17 00:00:00 2001 From: PatR Date: Sun, 5 Sep 2021 15:44:00 -0700 Subject: [PATCH] fix #K3436 - crash during restore triggered by Grayswandir's hallucination resistance. If the game is saved while hero is hallucinating but having that be suppressed by wielding Grayswandir, is riding, and the steed is on an object, then during restore the hero's location will be updated because of the presence of the object but the attempt to display the hero there is made before u.usteed has been restored and fails. --- doc/fixes37.0 | 6 ++++++ include/extern.h | 1 + src/botl.c | 20 +++++++++++++++++--- src/display.c | 25 ++++++++++++++++++++----- 4 files changed, 44 insertions(+), 8 deletions(-) diff --git a/doc/fixes37.0 b/doc/fixes37.0 index 0ad00083c..efda5f29d 100644 --- a/doc/fixes37.0 +++ b/doc/fixes37.0 @@ -599,6 +599,12 @@ don't include time spent suspended in background (^Z) or in shell escape (!) if a monster is starting to turn into green slime, eat the corpse, tin, or egg of any creature that might polymorph into a fiery monster to cure the slime, not just of one of a chameleon +yet another fix for display problems during restore: if game is saved while + hero is hallucinating but that's suppressed because of wielding + Grayswandir, hero is riding, and the steed is on or over an object, + restore will try to update hero's spot when making sure objects aren't + being obfuscated by hallucination, but when displaying the hero there + instead it would access steed pointer before that has been set up Fixes to 3.7.0-x Problems that Were Exposed Via git Repository diff --git a/include/extern.h b/include/extern.h index 6bf82cd25..591645763 100644 --- a/include/extern.h +++ b/include/extern.h @@ -349,6 +349,7 @@ extern void map_invisible(xchar, xchar); extern boolean unmap_invisible(int, int); extern void unmap_object(int, int); extern void map_location(int, int, int); +extern boolean suppress_map_output(void); extern void feel_newsym(xchar, xchar); extern void feel_location(xchar, xchar); extern void newsym(int, int); diff --git a/src/botl.c b/src/botl.c index d6a1ebf0c..fa30cd5be 100644 --- a/src/botl.c +++ b/src/botl.c @@ -57,6 +57,9 @@ do_statusline1(void) register char *nb; register int i, j; + if (suppress_map_output()) + return strcpy(newbot1, ""); + Strcpy(newbot1, g.plname); if ('a' <= newbot1[0] && newbot1[0] <= 'z') newbot1[0] += 'A' - 'a'; @@ -112,6 +115,9 @@ do_statusline2(void) int hp, hpmax, cap; long money; + if (suppress_map_output()) + return strcpy(newbot2, ""); + /* * Various min(x,9999)'s are to avoid having excessive values * violate the field width assumptions in botl.h and should not @@ -241,9 +247,10 @@ do_statusline2(void) void bot(void) { - /* dosave() flags completion by setting u.uhp to -1 */ + /* dosave() flags completion by setting u.uhp to -1; supprss_map_output() + covers program_state.restoring and is used for status as well as map */ if (u.uhp != -1 && g.youmonst.data && iflags.status_updates - && !g.program_state.saving && !g.program_state.restoring) { + && !g.program_state.saving && !suppress_map_output()) { if (VIA_WINDOWPORT()) { bot_via_windowport(); } else { @@ -256,11 +263,18 @@ bot(void) g.context.botl = g.context.botlx = iflags.time_botl = FALSE; } +/* special purpose status update: move counter ('time' status) only */ void timebot(void) { + /* we're called when iflags.time_botl is set and general g.context.botl + is clear; iflags.time_botl gets set whenever g.moves changes value + so there's no benefit in tracking previous value to decide whether + to skip update; suppress_map_output() handles program_state.restoring + and program_state.done_hup (tty hangup => no further output at all) + and we use it for maybe skipping status as well as for the map */ if (flags.time && iflags.status_updates - && !g.program_state.saving && !g.program_state.restoring) { + && !g.program_state.saving && !suppress_map_output()) { if (VIA_WINDOWPORT()) { stat_update_time(); } else { diff --git a/src/display.c b/src/display.c index d52bc9633..a9555d0c4 100644 --- a/src/display.c +++ b/src/display.c @@ -529,6 +529,20 @@ warning_of(struct monst *mon) return wl; } +/* map or status window might not be ready for output during level creation + or game restoration (something like u.usteed which affects display of + the hero and also a status condition might not be set up yet) */ +boolean +suppress_map_output(void) +{ + if (g.in_mklev || g.program_state.restoring) + return TRUE; +#ifdef HANGUPHANDLING + if (g.program_state.done_hup) + return TRUE; +#endif + return FALSE; +} /* * feel_newsym() @@ -562,6 +576,10 @@ feel_location(xchar x, xchar y) struct obj *boulder; register struct monst *mon; + /* replicate safeguards used by newsym(); might not be required here */ + if (suppress_map_output()) + return; + if (!isok(x, y)) return; lev = &(levl[x][y]); @@ -721,12 +739,9 @@ newsym(register int x, register int y) boolean worm_tail; register struct rm *lev = &(levl[x][y]); - if (g.in_mklev) + /* don't try to produce map output when level is in a state of flux */ + if (suppress_map_output()) return; -#ifdef HANGUPHANDLING - if (g.program_state.done_hup) - return; -#endif /* only permit updating the hero when swallowed */ if (u.uswallow) {