Avoid an assert in replay::add_start_if_not_there_yet with corrupt files

With this it puts the player back to the title screen after showing
an error dialog, so it's not much better, but at least it's not a
crash.

Fixes issue 7164. We weren't able to work out what caused the file corruption
reported, but I believe it's a race condition about saving while the AI is
recruiting. The file in the bug report has a `[snapshot]` tag with
`init_side_done=yes` but without a `playing_team` attribute, which must be the
result of `game_state::write()` when not in the `PLAY` phase. Loading such a
file causes `game_state::start_event_fired_ == false`, and triggers
`play_controller::start_game` to call `replay::add_start_if_not_there_yet`.

The i18n'd string is reused from `game_launcher.cpp`.
This commit is contained in:
Steve Cotton 2023-10-20 17:28:14 +02:00 committed by Steve Cotton
parent c5cfebb5cc
commit f5f76ade9a
2 changed files with 8 additions and 4 deletions

View file

@ -0,0 +1,2 @@
### Miscellaneous and Bug Fixes
* Avoid an assert in `replay::add_start_if_not_there_yet` with corrupt files (issue #7154)

View file

@ -25,18 +25,20 @@
#include "actions/undo.hpp"
#include "display_chat_manager.hpp"
#include "game_display.hpp"
#include "preferences/game.hpp"
#include "game_data.hpp"
#include "gettext.hpp"
#include "lexical_cast.hpp"
#include "log.hpp"
#include "map/label.hpp"
#include "map/location.hpp"
#include "play_controller.hpp"
#include "synced_context.hpp"
#include "preferences/game.hpp"
#include "replay_recorder_base.hpp"
#include "resources.hpp"
#include "synced_context.hpp"
#include "units/unit.hpp"
#include "whiteboard/manager.hpp"
#include "replay_recorder_base.hpp"
#include "wml_exception.hpp"
#include <array>
#include <set>
@ -661,7 +663,7 @@ void replay::add_config(const config& cfg, MARK_SENT mark)
bool replay::add_start_if_not_there_yet()
{
//this method would confuse the value of 'pos' otherwise
assert(base_->get_pos() == 0);
VALIDATE(base_->get_pos() == 0, _("The file you have tried to load is corrupt"));
//since pos is 0, at_end() is equivalent to empty()
if(at_end() || !base_->get_command_at(0).has_child("start"))
{