From 754d7f1e92d7c5b4c58bbf4ca86a5b66d7cbef05 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Thu, 1 Oct 2026 12:15:05 +0200 Subject: [PATCH] =?UTF-8?q?Speichern=20meldet=20Fehler=20und=20sch=C3=BCtz?= =?UTF-8?q?t=20unbrauchbare=20St=C3=A4nde?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Lässt sich ein unbrauchbarer Spielstand nicht beiseitelegen, bleibt das Speichern gesperrt, bis ein Stand erfolgreich geladen wird; so wird er nie überschrieben. Ein gescheitertes automatisches Speichern zeigt SAVE_FAILED, und jedes gescheiterte Speichern wird protokolliert. Co-Authored-By: Claude Haiku 4.5 --- game/scripts/main.gd | 23 ++++++++++++++++++----- game/tests/test_main.gd | 16 ++++++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/game/scripts/main.gd b/game/scripts/main.gd index 8ef9828..81cb828 100644 --- a/game/scripts/main.gd +++ b/game/scripts/main.gd @@ -25,6 +25,9 @@ var hud: Hud var _player_key: PackedByteArray var _next_dig_tick := 0 +## True while an unusable save could not be set aside. Saving is refused +## then, so that the unusable save is never overwritten. +var _save_blocked := false func _ready() -> void: @@ -72,7 +75,8 @@ func _unhandled_input(event: InputEvent) -> void: func _notification(what: int) -> void: if what == NOTIFICATION_WM_CLOSE_REQUEST and session != null: - save_game() + if not save_game(): + push_warning("the game could not be saved on close") _write_recording() @@ -90,9 +94,14 @@ func _start_from_save() -> bool: var loaded := GwSession.new() if not loaded.start_loaded(store.read_save()): var aside := store.set_aside_save() - push_warning("save not usable (%s), kept as %s" % [loaded.last_error(), aside]) + if aside.is_empty(): + _save_blocked = true + push_warning("save not usable (%s) and could not be set aside; saving is off" % loaded.last_error()) + else: + push_warning("save not usable (%s), kept as %s" % [loaded.last_error(), aside]) hud.flash("LOAD_FAILED") return false + _save_blocked = false _enter(loaded) return true @@ -112,8 +121,9 @@ func _enter(next: GwSession) -> void: func _run_tick() -> void: _apply(session.step()) - if session.tick() % AUTOSAVE_TICKS == 0: - save_game() + if session.tick() % AUTOSAVE_TICKS == 0 and not save_game(): + hud.flash("SAVE_FAILED") + push_warning("the automatic save failed") func _apply(events: Array[Dictionary]) -> void: @@ -153,8 +163,11 @@ func dig_at(tile: Vector2i) -> void: _next_dig_tick = session.tick() + GwSession.DIG_COOLDOWN_TICKS -## Writes the running world as the save. Returns false if nothing was written. +## Writes the running world as the save. Returns false if nothing was +## written, also while an unusable save could not be set aside. func save_game() -> bool: + if _save_blocked: + return false return store.write_save(session.save()) diff --git a/game/tests/test_main.gd b/game/tests/test_main.gd index 103d87f..ab79971 100644 --- a/game/tests/test_main.gd +++ b/game/tests/test_main.gd @@ -58,3 +58,19 @@ func test_an_unusable_save_is_set_aside_and_a_new_world_starts() -> void: check_eq(main.hud.message(), tr("LOAD_FAILED"), "the player is told") _stop_main(main) TestFiles.remove_dir(directory) + + +func test_an_unusable_save_that_cannot_be_set_aside_is_never_overwritten() -> void: + var directory := TestFiles.temporary_dir("stuck") + var store := SaveStore.new(directory) + store.write_save(PackedByteArray([1, 2, 3])) + # A folder under the set-aside name keeps the unusable save from moving. + DirAccess.make_dir_absolute(directory.path_join(SaveStore.SAVE_FILE + ".broken")) + var main := _start_main(directory) + await tree.process_frame + check(main.session.is_running(), "a new world runs") + check(not main.save_game(), "saving is refused") + check_eq(store.read_save(), PackedByteArray([1, 2, 3]), "the unusable save is untouched") + check_eq(main.hud.message(), tr("LOAD_FAILED"), "the player is told") + _stop_main(main) + TestFiles.remove_dir(directory)