Save the last turn for bug reports.

We often get save games uploaded with bug reports that are already in a
broken state with nothing we can do about it. This saves that turn to
`last_turn.liberation` so users are less likely to have clobbered the
useful data before filing the report.
This commit is contained in:
Dan Albert 2022-12-20 13:35:28 -08:00
parent de9236e93a
commit 22503d4e95
5 changed files with 20 additions and 8 deletions

View File

@ -73,7 +73,8 @@ body:
The `state.json` file for the most recently completed turn, located at
`<Liberation install directory>/state.json`. This file is essential for
investigating any issues with end-of-turn results processing.
investigating any issues with end-of-turn results processing. **If you
include this file, also include `last_turn.liberation`.**
You can attach files to the bug by dragging and dropping the file into

View File

@ -17,6 +17,7 @@ Saves from 6.0.0 are compatible with 6.1.0
* **[Factions]** Added Peru.
* **[Flight Planning]** Refueling flights planned on aircraft carriers will act as a recovery tanker for the carrier.
* **[Loadouts]** Adjusted F-15E loadouts.
* **[Mission Generation]** The previous turn will now be saved as last_turn.liberation when submitting mission results. This is often essential for debugging bug reports. **Include this file in the bug report whenever it is available.**
* **[Modding]** Added support for the HMS Ariadne, Achilles, and Castle class.
* **[Modding]** Added HMS Invincible to the game data as a helicopter carrier.

View File

@ -332,6 +332,8 @@ class Game:
from .server import EventStream
from .sim import GameUpdateEvents
persistency.save_last_turn_state(self)
events = GameUpdateEvents()
logging.info("Pass turn")

View File

@ -31,8 +31,8 @@ def save_dir() -> Path:
return Path(base_path()) / "Liberation" / "Saves"
def _temporary_save_file() -> str:
return str(save_dir() / "tmpsave.liberation")
def _temporary_save_file() -> Path:
return save_dir() / "tmpsave.liberation"
def _autosave_path() -> str:
@ -54,16 +54,18 @@ def load_game(path: str) -> Optional[Game]:
return None
def save_game(game: Game) -> bool:
def save_game(game: Game, destination: Path | None = None) -> None:
if destination is None:
destination = Path(game.savepath)
temp_save_file = _temporary_save_file()
with logged_duration("Saving game"):
try:
with open(_temporary_save_file(), "wb") as f:
with temp_save_file.open("wb") as f:
pickle.dump(game, f)
shutil.copy(_temporary_save_file(), game.savepath)
return True
shutil.copy(temp_save_file, destination)
except Exception:
logging.exception("Could not save game")
return False
def autosave(game: Game) -> bool:
@ -79,3 +81,7 @@ def autosave(game: Game) -> bool:
except Exception:
logging.exception("Could not save game")
return False
def save_last_turn_state(game: Game) -> None:
save_game(game, save_dir() / "last_turn.liberation")

View File

@ -5,6 +5,7 @@ from datetime import timedelta
from pathlib import Path
from typing import Optional, TYPE_CHECKING
from game import persistency
from game.debriefing import Debriefing
from game.missiongenerator import MissionGenerator
from game.unitmap import UnitMap
@ -73,6 +74,7 @@ class MissionSimulation:
"was generated."
)
persistency.save_last_turn_state(self.game)
MissionResultsProcessor(self.game).commit(debriefing, events)
def finish(self) -> None: