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 The `state.json` file for the most recently completed turn, located at
`<Liberation install directory>/state.json`. This file is essential for `<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 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. * **[Factions]** Added Peru.
* **[Flight Planning]** Refueling flights planned on aircraft carriers will act as a recovery tanker for the carrier. * **[Flight Planning]** Refueling flights planned on aircraft carriers will act as a recovery tanker for the carrier.
* **[Loadouts]** Adjusted F-15E loadouts. * **[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 support for the HMS Ariadne, Achilles, and Castle class.
* **[Modding]** Added HMS Invincible to the game data as a helicopter carrier. * **[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 .server import EventStream
from .sim import GameUpdateEvents from .sim import GameUpdateEvents
persistency.save_last_turn_state(self)
events = GameUpdateEvents() events = GameUpdateEvents()
logging.info("Pass turn") logging.info("Pass turn")

View File

@ -31,8 +31,8 @@ def save_dir() -> Path:
return Path(base_path()) / "Liberation" / "Saves" return Path(base_path()) / "Liberation" / "Saves"
def _temporary_save_file() -> str: def _temporary_save_file() -> Path:
return str(save_dir() / "tmpsave.liberation") return save_dir() / "tmpsave.liberation"
def _autosave_path() -> str: def _autosave_path() -> str:
@ -54,16 +54,18 @@ def load_game(path: str) -> Optional[Game]:
return None 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"): with logged_duration("Saving game"):
try: try:
with open(_temporary_save_file(), "wb") as f: with temp_save_file.open("wb") as f:
pickle.dump(game, f) pickle.dump(game, f)
shutil.copy(_temporary_save_file(), game.savepath) shutil.copy(temp_save_file, destination)
return True
except Exception: except Exception:
logging.exception("Could not save game") logging.exception("Could not save game")
return False
def autosave(game: Game) -> bool: def autosave(game: Game) -> bool:
@ -79,3 +81,7 @@ def autosave(game: Game) -> bool:
except Exception: except Exception:
logging.exception("Could not save game") logging.exception("Could not save game")
return False 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 pathlib import Path
from typing import Optional, TYPE_CHECKING from typing import Optional, TYPE_CHECKING
from game import persistency
from game.debriefing import Debriefing from game.debriefing import Debriefing
from game.missiongenerator import MissionGenerator from game.missiongenerator import MissionGenerator
from game.unitmap import UnitMap from game.unitmap import UnitMap
@ -73,6 +74,7 @@ class MissionSimulation:
"was generated." "was generated."
) )
persistency.save_last_turn_state(self.game)
MissionResultsProcessor(self.game).commit(debriefing, events) MissionResultsProcessor(self.game).commit(debriefing, events)
def finish(self) -> None: def finish(self) -> None: