From 3761966303ce952f3be85276451020131629f8d6 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Thu, 1 Oct 2026 11:42:31 +0200 Subject: [PATCH] =?UTF-8?q?SaveStore=20pr=C3=BCft=20jeden=20Schreibvorgang?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ein Speicherstand ersetzt den vorigen nur noch, wenn er vollständig geschrieben und zurückgelesen wurde. Ein nicht gespeicherter Spielerschlüssel wird gemeldet, und set_aside_save meldet, wenn nichts verschoben wurde. Co-Authored-By: Claude Haiku 4.5 --- game/scripts/save_store.gd | 21 +++++++++++++-------- game/tests/test_save_store.gd | 19 +++++++++++++++++++ 2 files changed, 32 insertions(+), 8 deletions(-) diff --git a/game/scripts/save_store.gd b/game/scripts/save_store.gd index a789d99..b742c5f 100644 --- a/game/scripts/save_store.gd +++ b/game/scripts/save_store.gd @@ -25,7 +25,8 @@ func player_key() -> PackedByteArray: var key := FileAccess.get_file_as_bytes(path) if key.size() != KEY_BYTES: key = Crypto.new().generate_random_bytes(KEY_BYTES) - _write(path, key) + if not _write(path, key): + push_error("player key could not be stored at %s" % path) return key @@ -37,24 +38,28 @@ func read_save() -> PackedByteArray: return FileAccess.get_file_as_bytes(base_dir.path_join(SAVE_FILE)) -## Writes the save through a temporary file, so that a crash while writing -## never destroys the previous save. Returns false if nothing was written. +## Writes the save through a temporary file, so that a crash or a failed +## write never destroys the previous save. Returns false if the new save was +## not stored completely; the previous save then stays as it was. func write_save(bytes: PackedByteArray) -> bool: if bytes.is_empty(): return false var path := base_dir.path_join(SAVE_FILE) var temporary := path + ".tmp" - if not _write(temporary, bytes): + if not _write(temporary, bytes) or FileAccess.get_file_as_bytes(temporary) != bytes: + DirAccess.remove_absolute(temporary) return false return DirAccess.rename_absolute(temporary, path) == OK ## Moves an unusable save out of the way, so that the next save does not -## overwrite it. An older set-aside save is replaced. Returns the new path. +## overwrite it. An older set-aside save is replaced. Returns the new path, +## or an empty text if there was no save or it could not be moved. func set_aside_save() -> String: var path := base_dir.path_join(SAVE_FILE) var aside := path + ".broken" - DirAccess.rename_absolute(path, aside) + if DirAccess.rename_absolute(path, aside) != OK: + return "" return aside @@ -88,6 +93,6 @@ func _write(path: String, bytes: PackedByteArray) -> bool: var file := FileAccess.open(path, FileAccess.WRITE) if file == null: return false - file.store_buffer(bytes) + var stored := file.store_buffer(bytes) file.close() - return true + return stored diff --git a/game/tests/test_save_store.gd b/game/tests/test_save_store.gd index 7eb655a..e9dd236 100644 --- a/game/tests/test_save_store.gd +++ b/game/tests/test_save_store.gd @@ -30,6 +30,25 @@ func test_an_empty_save_is_not_written() -> void: TestFiles.remove_dir(directory) +func test_a_save_that_cannot_be_written_keeps_the_previous_one() -> void: + var directory := TestFiles.temporary_dir("blocked") + var store := SaveStore.new(directory) + store.write_save(PackedByteArray([1, 2, 3])) + # A folder in place of the temporary file makes the write fail. + DirAccess.make_dir_absolute(directory.path_join(SaveStore.SAVE_FILE + ".tmp")) + check(not store.write_save(PackedByteArray([4, 5])), "the failed save is reported") + check_eq(store.read_save(), PackedByteArray([1, 2, 3]), "the previous save is untouched") + check(not store.write_save(PackedByteArray()), "an empty save is refused") + check_eq(store.read_save(), PackedByteArray([1, 2, 3]), "still untouched") + TestFiles.remove_dir(directory) + + +func test_setting_aside_without_a_save_reports_nothing_moved() -> void: + var directory := TestFiles.temporary_dir("nothing") + check_eq(SaveStore.new(directory).set_aside_save(), "", "no save, nothing moved") + TestFiles.remove_dir(directory) + + func test_an_unusable_save_can_be_set_aside() -> void: var directory := TestFiles.temporary_dir("aside") var store := SaveStore.new(directory)