Improve interface accessibility and UI reliability

This commit is contained in:
Alexander Sellite 2026-07-30 00:54:53 -04:00
parent ba4476511f
commit 3734d4a168
29 changed files with 514 additions and 224 deletions

View file

@ -24,9 +24,9 @@ static func migrate_legacy_to(
data_root: PlayerDataRoot,
destination: String,
) -> Dictionary:
var normalized := destination.simplify_path().trim_suffix("/")
var normalized: String = destination.simplify_path().trim_suffix("/")
if DirAccess.dir_exists_absolute(normalized):
var existing_manifest := normalized.path_join(
var existing_manifest: String = normalized.path_join(
PlayerDataRoot.MANIFEST_FILENAME
)
if FileAccess.file_exists(existing_manifest):
@ -40,24 +40,24 @@ static func migrate_legacy_to(
"ok": false,
"message": "Choose an empty folder or create a NETFISHING subfolder.",
}
var staging := "%s.migration-%s" % [
var staging: String = "%s.migration-%s" % [
normalized, Crypto.new().generate_random_bytes(8).hex_encode()
]
if DirAccess.make_dir_recursive_absolute(staging) != OK:
return {"ok": false, "message": "Migration staging could not be created."}
var created := data_root.create_unbound_root(staging)
var created: Dictionary = data_root.create_unbound_root(staging)
if not bool(created.get("ok", false)):
_remove_tree(staging)
return {"ok": false, "message": str(created.get("message", ""))}
var copied: Array[String] = []
for source_name: String in LEGACY_FILES:
var source := ProjectSettings.globalize_path(
var source: String = ProjectSettings.globalize_path(
"user://".path_join(source_name)
)
if not FileAccess.file_exists(source):
continue
var target := staging.path_join(LEGACY_FILES[source_name])
var result := _copy_verified_json(source, target)
var target: String = staging.path_join(str(LEGACY_FILES[source_name]))
var result: Dictionary = _copy_verified_json(source, target)
if not bool(result.get("ok", false)):
_remove_tree(staging)
return {
@ -70,11 +70,15 @@ static func migrate_legacy_to(
if DirAccess.rename_absolute(staging, normalized) != OK:
_remove_tree(staging)
return {"ok": false, "message": "Migration could not activate its destination."}
var manifest := _read_json(normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME))
var migrated_root_id := str(manifest.get("root_id", ""))
var manifest: Dictionary = _read_json(
normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)
)
var migrated_root_id: String = str(manifest.get("root_id", ""))
if migrated_root_id.is_empty() or not data_root.use_existing_root(normalized):
return {"ok": false, "message": "Migration completed but could not switch roots."}
var recovery := ProjectSettings.globalize_path("user://migration-recovery").path_join(
var recovery: String = ProjectSettings.globalize_path(
"user://migration-recovery"
).path_join(
Time.get_datetime_string_from_system().replace(":", "-")
)
DirAccess.make_dir_recursive_absolute(recovery)
@ -94,7 +98,7 @@ static func migrate_active_to(
data_root: PlayerDataRoot,
destination: String,
) -> Dictionary:
var normalized := destination.simplify_path().trim_suffix("/")
var normalized: String = destination.simplify_path().trim_suffix("/")
if normalized == data_root.root_path:
return {"ok": true, "message": "This data folder is already active."}
if DirAccess.dir_exists_absolute(normalized) and not _directory_empty(normalized):
@ -108,12 +112,12 @@ static func migrate_active_to(
),
}
return {"ok": false, "message": "Choose an empty folder or a NETFISHING subfolder."}
var staging := "%s.migration-%s" % [
var staging: String = "%s.migration-%s" % [
normalized, Crypto.new().generate_random_bytes(8).hex_encode()
]
if DirAccess.make_dir_recursive_absolute(staging) != OK:
return {"ok": false, "message": "Migration staging could not be created."}
var created := data_root.create_unbound_root(staging)
var created: Dictionary = data_root.create_unbound_root(staging)
if not bool(created.get("ok", false)):
_remove_tree(staging)
return {"ok": false, "message": str(created.get("message", ""))}
@ -127,7 +131,7 @@ static func migrate_active_to(
"social/server_trust.json",
"social/host_bans.json",
]:
var source := data_root.root_path.path_join(relative)
var source: String = data_root.root_path.path_join(relative)
if not FileAccess.file_exists(source):
continue
if not bool(_copy_verified_json(source, staging.path_join(relative)).get("ok", false)):
@ -138,7 +142,7 @@ static func migrate_active_to(
if DirAccess.rename_absolute(staging, normalized) != OK:
_remove_tree(staging)
return {"ok": false, "message": "Migration could not activate its destination."}
var old_root := data_root.root_path
var old_root: String = data_root.root_path
if not data_root.use_existing_root(normalized):
return {"ok": false, "message": "Migration copied data but did not change the pointer."}
return {
@ -149,18 +153,20 @@ static func migrate_active_to(
static func adopt_legacy_app_data(data_root: PlayerDataRoot) -> Dictionary:
var source_root := ProjectSettings.globalize_path("user://").trim_suffix("/")
var root := ProjectSettings.globalize_path(PlayerDataRoot.APP_DATA_PORTABLE_PATH)
var source_root: String = ProjectSettings.globalize_path("user://").trim_suffix("/")
var root: String = ProjectSettings.globalize_path(
PlayerDataRoot.APP_DATA_PORTABLE_PATH
)
if DirAccess.make_dir_recursive_absolute(root) != OK:
return {"ok": false, "message": "The app-data folder could not be created."}
var created := data_root.create_app_data_layout_for_migration(root)
var created: Dictionary = data_root.create_app_data_layout_for_migration(root)
if not bool(created.get("ok", false)):
return created
for source_name: String in LEGACY_FILES:
var source := source_root.path_join(source_name)
var source: String = source_root.path_join(source_name)
if not FileAccess.file_exists(source):
continue
var target := root.path_join(LEGACY_FILES[source_name])
var target: String = root.path_join(str(LEGACY_FILES[source_name]))
if not bool(_copy_verified_json(source, target).get("ok", false)):
return {
"ok": false,
@ -179,17 +185,19 @@ static func replace_existing_with_legacy(
data_root: PlayerDataRoot,
destination: String,
) -> Dictionary:
var normalized := destination.simplify_path().trim_suffix("/")
var manifest := normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)
var normalized: String = destination.simplify_path().trim_suffix("/")
var manifest: String = normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)
if not FileAccess.file_exists(manifest):
return {"ok": false, "message": "The selected folder is not a NETFISHING data root."}
var recovery := ProjectSettings.globalize_path("user://migration-recovery").path_join(
var recovery: String = ProjectSettings.globalize_path(
"user://migration-recovery"
).path_join(
"replaced-root-" + Time.get_datetime_string_from_system().replace(":", "-")
)
if not _copy_tree(normalized, recovery):
return {"ok": false, "message": "The selected data could not be backed up."}
_remove_tree(normalized)
var result := migrate_legacy_to(data_root, normalized)
var result: Dictionary = migrate_legacy_to(data_root, normalized)
if bool(result.get("ok", false)):
result["replaced_root_backup"] = recovery
return result
@ -199,26 +207,28 @@ static func replace_existing_with_active(
data_root: PlayerDataRoot,
destination: String,
) -> Dictionary:
var normalized := destination.simplify_path().trim_suffix("/")
var normalized: String = destination.simplify_path().trim_suffix("/")
if not FileAccess.file_exists(normalized.path_join(PlayerDataRoot.MANIFEST_FILENAME)):
return {"ok": false, "message": "The selected folder is not a NETFISHING data root."}
var recovery := ProjectSettings.globalize_path("user://migration-recovery").path_join(
var recovery: String = ProjectSettings.globalize_path(
"user://migration-recovery"
).path_join(
"replaced-root-" + Time.get_datetime_string_from_system().replace(":", "-")
)
if not _copy_tree(normalized, recovery):
return {"ok": false, "message": "The selected data could not be backed up."}
_remove_tree(normalized)
var result := migrate_active_to(data_root, normalized)
var result: Dictionary = migrate_active_to(data_root, normalized)
if bool(result.get("ok", false)):
result["replaced_root_backup"] = recovery
return result
static func _copy_verified_json(source: String, destination: String) -> Dictionary:
var bytes := PortableFileGuard.read_bytes(source)
var bytes: PackedByteArray = PortableFileGuard.read_bytes(source)
if bytes.is_empty() and FileAccess.get_open_error() != OK:
return {"ok": false}
var json := JSON.new()
var json: JSON = JSON.new()
if (
json.parse(bytes.get_string_from_utf8()) != OK
or typeof(json.data) != TYPE_DICTIONARY
@ -229,7 +239,7 @@ static func _copy_verified_json(source: String, destination: String) -> Dictiona
return {"ok": false}
if not _write_bytes(destination, bytes):
return {"ok": false}
var copied := PortableFileGuard.read_bytes(destination)
var copied: PackedByteArray = PortableFileGuard.read_bytes(destination)
return {
"ok": (
PortableFileGuard.hash_bytes(copied)
@ -240,7 +250,7 @@ static func _copy_verified_json(source: String, destination: String) -> Dictiona
static func _valid_owned_data(filename: String, data: Dictionary) -> bool:
if filename == "player_save.json":
var version := int(data.get("save_version", -1))
var version: int = int(data.get("save_version", -1))
return version >= 1 and version <= PlayerSaveManager.SAVE_VERSION
return (
filename not in LEGACY_FILES
@ -255,14 +265,14 @@ static func _copy_bytes(source: String, destination: String) -> bool:
static func _copy_tree(source: String, destination: String) -> bool:
if DirAccess.make_dir_recursive_absolute(destination) != OK:
return false
var access := DirAccess.open(source)
var access: DirAccess = DirAccess.open(source)
if access == null:
return false
access.list_dir_begin()
var name := access.get_next()
var name: String = access.get_next()
while not name.is_empty():
var from := source.path_join(name)
var to := destination.path_join(name)
var from: String = source.path_join(name)
var to: String = destination.path_join(name)
if access.current_is_dir():
if not _copy_tree(from, to):
access.list_dir_end()
@ -277,19 +287,19 @@ static func _copy_tree(source: String, destination: String) -> bool:
static func _write_bytes(path: String, bytes: PackedByteArray) -> bool:
DirAccess.make_dir_recursive_absolute(path.get_base_dir())
var file := FileAccess.open(path, FileAccess.WRITE)
var file: FileAccess = FileAccess.open(path, FileAccess.WRITE)
if file == null:
return false
file.store_buffer(bytes)
file.flush()
var ok := file.get_error() == OK
var ok: bool = file.get_error() == OK
file.close()
return ok
static func _read_json(path: String) -> Dictionary:
var bytes := PortableFileGuard.read_bytes(path)
var json := JSON.new()
var bytes: PackedByteArray = PortableFileGuard.read_bytes(path)
var json: JSON = JSON.new()
return (
json.data
if json.parse(bytes.get_string_from_utf8()) == OK
@ -299,23 +309,23 @@ static func _read_json(path: String) -> Dictionary:
static func _directory_empty(path: String) -> bool:
var access := DirAccess.open(path)
var access: DirAccess = DirAccess.open(path)
if access == null:
return true
access.list_dir_begin()
var name := access.get_next()
var name: String = access.get_next()
access.list_dir_end()
return name.is_empty()
static func _remove_tree(path: String) -> void:
var access := DirAccess.open(path)
var access: DirAccess = DirAccess.open(path)
if access == null:
return
access.list_dir_begin()
var name := access.get_next()
var name: String = access.get_next()
while not name.is_empty():
var child := path.path_join(name)
var child: String = path.path_join(name)
if access.current_is_dir():
_remove_tree(child)
else: