From 51cd4572a85233d86e874808386ba88daefef237 Mon Sep 17 00:00:00 2001 From: Steven Wroblewski Date: Thu, 1 Oct 2026 21:23:49 +0200 Subject: [PATCH] =?UTF-8?q?Gesperrte=20Einstellungen=20nicht=20=C3=BCbersc?= =?UTF-8?q?hreiben?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wer eine Einstellung ändert, liest die Einstellungsdatei jetzt an derselben Stelle wie Schlüssel und Speicherstand. Ist sie gerade nicht lesbar, etwa weil ein anderes Programm sie hält, bleibt sie unverändert, statt nur mit dem neuen Wert überschrieben zu werden. Von einer beschädigten Datei bleibt, was Godot lesen konnte. Ein neuer Test deckt die gehaltene Datei ab. Der Parameter von set_player_name heißt jetzt new_name, damit er die Methode player_name nicht verdeckt. Co-Authored-By: Claude Haiku 4.5 --- game/scripts/save_store.gd | 38 ++++++++++++++++++++++++++--------- game/tests/test_save_store.gd | 15 ++++++++++++++ 2 files changed, 44 insertions(+), 9 deletions(-) diff --git a/game/scripts/save_store.gd b/game/scripts/save_store.gd index 7a507a0..985fc09 100644 --- a/game/scripts/save_store.gd +++ b/game/scripts/save_store.gd @@ -14,9 +14,10 @@ const KEEP_RECORDINGS := 10 const KEY_BYTES := 16 var base_dir: String -## The result of the last attempt to read the key or the save: OK, or why -## the file could not be read. A file that exists but cannot be read right -## now, for example because another program holds it, is never replaced. +## The result of the last attempt to read the key, the save or the settings: +## OK, or why the file could not be read. A file that exists but cannot be +## read right now, for example because another program holds it, is never +## replaced. var last_read_error: Error = OK @@ -132,8 +133,8 @@ func player_name() -> String: return _setting("player", "name") -func set_player_name(player_name: String) -> void: - _set_setting("player", "name", player_name) +func set_player_name(new_name: String) -> void: + _set_setting("player", "name", new_name) ## The address of the host the player joined last time, or an empty text. @@ -146,21 +147,40 @@ func set_last_address(address: String) -> void: func _setting(section: String, key: String) -> String: - var settings := ConfigFile.new() - if settings.load(base_dir.path_join(SETTINGS_FILE)) != OK: + var settings := _read_settings(base_dir.path_join(SETTINGS_FILE)) + if settings == null: return "" return str(settings.get_value(section, key, "")) +## Changes one setting and keeps the others. Settings that cannot be read +## right now stay as they are, because writing them would lose the others. func _set_setting(section: String, key: String, value: String) -> void: var path := base_dir.path_join(SETTINGS_FILE) - var settings := ConfigFile.new() - settings.load(path) + var settings := _read_settings(path) + if settings == null: + push_warning("settings could not be read at %s and stay as they are: %s" % [path, error_string(last_read_error)]) + return settings.set_value(section, key, value) if settings.save(path) != OK: push_warning("settings could not be stored at %s" % path) +## The settings on disk, or null if the file exists but cannot be read right +## now; last_read_error then says why. A missing file gives empty settings. +## Of a damaged file, Godot reports the error and keeps what it read before +## it, and the next change of a setting writes the file anew. +func _read_settings(path: String) -> ConfigFile: + var settings := ConfigFile.new() + if not FileAccess.file_exists(path): + return settings + var text := _read_file(path).get_string_from_utf8() + if last_read_error != OK: + return null + settings.parse(text) + return settings + + ## Moves the backup back to the save if there is no save, see write_save. func _restore_backup() -> void: var path := base_dir.path_join(SAVE_FILE) diff --git a/game/tests/test_save_store.gd b/game/tests/test_save_store.gd index 47c64e3..7f88387 100644 --- a/game/tests/test_save_store.gd +++ b/game/tests/test_save_store.gd @@ -174,3 +174,18 @@ func test_the_settings_are_kept() -> void: check_eq(later.player_name(), "Ada", "name") check_eq(later.last_address(), "192.168.1.20", "address") TestFiles.remove_dir(directory) + + +func test_settings_that_cannot_be_read_right_now_are_not_overwritten() -> void: + var directory := TestFiles.temporary_dir("held-settings") + var store := SaveStore.new(directory) + store.set_player_name("Ada") + store.set_last_address("192.168.1.20") + var held := HeldFileStore.new(directory) + held.unreadable = SaveStore.SETTINGS_FILE + check_eq(held.player_name(), "", "nothing read for now") + held.set_player_name("Grace") + var later := SaveStore.new(directory) + check_eq(later.player_name(), "Ada", "the name on disk is the old one") + check_eq(later.last_address(), "192.168.1.20", "the address is kept") + TestFiles.remove_dir(directory)