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)