Updated the update system to support multi channels (stable / pre-release).

This commit is contained in:
2026-09-09 20:52:00 +02:00
parent 2a53a59cef
commit 5770969629
12 changed files with 3329 additions and 254 deletions

View File

@@ -66,9 +66,13 @@ from ..overlay import RuntimeCompatibility, inspect_runtime_compatibility
from ..paths import AppPaths
from ..settings import Settings, ensure_settings, load_settings, update_settings
from ..self_update import (
LauncherRemoteHistory,
LauncherUpdateCheck,
cache_launcher_release_history,
cache_launcher_update_check,
check_for_launcher_update,
fetch_launcher_release_history,
read_cached_launcher_release_history,
read_cached_launcher_update_check,
)
from ..server_status import RealmServerStatus
@@ -665,6 +669,7 @@ def run_dashboard(paths: AppPaths) -> None:
latest_installation_status: GameInstallationStatus | None = None
available_start_options: tuple[DepotStartOption, ...] = (DEFAULT_DEPOT_START_OPTION,)
preferred_realm = startup_settings.realm
selected_update_channel = startup_settings.update_channel
latest_server_statuses: tuple[RealmServerStatus, ...] | None = None
latest_server_status_check: ServerStatusCheck | None = None
server_status_placeholder = "Checking official server status…"
@@ -674,6 +679,7 @@ def run_dashboard(paths: AppPaths) -> None:
dashboard_debug_state_error: str | None = None
last_dashboard_debug_render = ""
launcher_update_check: LauncherUpdateCheck | None = None
launcher_release_history: LauncherRemoteHistory | None = None
runtime_compatibility: RuntimeCompatibility | None = None
compatibility_check_generation = 0
controls_are_stacked: bool | None = None
@@ -2282,14 +2288,18 @@ def run_dashboard(paths: AppPaths) -> None:
launcher_update_dialog.refresh()
def refresh_launcher_update_check() -> None:
requested_channel = selected_update_channel
def worker() -> None:
try:
check = check_for_launcher_update()
check = check_for_launcher_update(channel=requested_channel)
except RuntimeError as update_error:
events.put(("launcher_update_error", str(update_error)))
events.put(
("launcher_update_error", (requested_channel, str(update_error)))
)
else:
cache_launcher_update_check(paths.launcher_update_check_cache, check)
events.put(("launcher_update_check", check))
events.put(("launcher_update_check", (requested_channel, check)))
threading.Thread(
target=worker,
@@ -2297,6 +2307,24 @@ def run_dashboard(paths: AppPaths) -> None:
daemon=True,
).start()
def history_worker() -> None:
try:
history = fetch_launcher_release_history()
except RuntimeError as history_error:
events.put(("launcher_history_error", str(history_error)))
else:
cache_launcher_release_history(
paths.launcher_release_history_cache,
history,
)
events.put(("launcher_release_history", history))
threading.Thread(
target=history_worker,
name="ptlinux-release-history",
daemon=True,
).start()
def refresh_remote_content() -> None:
def broadcast_worker() -> None:
events.put(("broadcast", fetch_latest_broadcast(paths.broadcast_cache)))
@@ -2444,7 +2472,8 @@ def run_dashboard(paths: AppPaths) -> None:
progress_text.set("See the activity log for details.")
def poll_events() -> None:
nonlocal game_window_active, launcher_update_check, runtime_compatibility
nonlocal game_window_active, launcher_update_check, launcher_release_history
nonlocal runtime_compatibility
processed = 0
try:
while processed < EVENT_BATCH_SIZE:
@@ -2657,16 +2686,31 @@ def run_dashboard(paths: AppPaths) -> None:
"version",
"Game version check complete",
)
elif kind == "launcher_update_check" and isinstance(
value,
LauncherUpdateCheck,
elif (
kind == "launcher_update_check"
and isinstance(value, tuple)
and len(value) == 2
and isinstance(value[0], str)
and isinstance(value[1], LauncherUpdateCheck)
):
show_launcher_update_check(value)
requested_channel, update_check = value
if requested_channel != selected_update_channel:
continue
show_launcher_update_check(update_check)
mark_startup_component_ready(
"launcher_update",
"Launcher update status ready",
)
elif kind == "launcher_update_error" and isinstance(value, str):
elif (
kind == "launcher_update_error"
and isinstance(value, tuple)
and len(value) == 2
and isinstance(value[0], str)
and isinstance(value[1], str)
):
requested_channel, update_error = value
if requested_channel != selected_update_channel:
continue
if launcher_update_check is None:
launcher_update_button.configure(
text="CHECK LAUNCHER",
@@ -2678,13 +2722,22 @@ def run_dashboard(paths: AppPaths) -> None:
),
)
launcher_update_badge.pack_forget()
append_log(f"Launcher update check unavailable: {value}")
append_log(f"Launcher update check unavailable: {update_error}")
if launcher_update_dialog is not None:
launcher_update_dialog.show_error(value)
launcher_update_dialog.show_error(update_error)
mark_startup_component_ready(
"launcher_update",
"Launcher update check complete",
)
elif kind == "launcher_release_history" and isinstance(
value,
LauncherRemoteHistory,
):
launcher_release_history = value
if launcher_update_dialog is not None:
launcher_update_dialog.refresh()
elif kind == "launcher_history_error" and isinstance(value, str):
append_log(f"Launcher release history unavailable: {value}")
except queue.Empty:
pass
except Exception as event_error:
@@ -2725,7 +2778,7 @@ def run_dashboard(paths: AppPaths) -> None:
def rollback_launcher_and_restart(target_version: str) -> None:
run_operation(
f"Rolling back launcher to {target_version}…",
command_for("launcher-update", "--rollback"),
command_for("launcher-update", "--rollback", target_version),
restart_on_success=True,
)
@@ -2737,6 +2790,8 @@ def run_dashboard(paths: AppPaths) -> None:
window_icon=window_icon,
apply_window_icon=apply_window_icon,
get_check=lambda: launcher_update_check,
get_remote_history=lambda: launcher_release_history,
get_selected_channel=lambda: selected_update_channel,
is_busy=lambda: operation_active or bool(game_clients.running_count),
request_check=refresh_launcher_update_check,
install_and_restart=install_launcher_and_restart,
@@ -2838,6 +2893,9 @@ def run_dashboard(paths: AppPaths) -> None:
)
def apply_saved_settings(updated_settings: Settings) -> None:
nonlocal selected_update_channel, launcher_update_check
previous_update_channel = selected_update_channel
selected_update_channel = updated_settings.update_channel
set_shared_telemetry_enabled(updated_settings.telemetry_enabled)
saved_limit = (
f"{updated_settings.download_speed_limit_mbps:g} MB/s"
@@ -2850,6 +2908,7 @@ def run_dashboard(paths: AppPaths) -> None:
f"proton_sync={updated_settings.proton_sync}, "
f"download_limit={saved_limit}, "
f"low_bandwidth={updated_settings.low_bandwidth_mode}, "
f"update_channel={updated_settings.update_channel}, "
f"telemetry={updated_settings.telemetry_enabled}"
)
status.set("Settings saved.")
@@ -2860,6 +2919,13 @@ def run_dashboard(paths: AppPaths) -> None:
if show_dashboard_debug:
refresh_dashboard_debug_disk_state()
update_dashboard_debug_view(force=True)
if previous_update_channel != selected_update_channel:
launcher_update_check = None
append_log(
f"Launcher update channel changed from {previous_update_channel} "
f"to {selected_update_channel}; checking eligible releases."
)
refresh_launcher_update_check()
def open_settings() -> None:
nonlocal settings_window
@@ -3017,10 +3083,14 @@ def run_dashboard(paths: AppPaths) -> None:
show_news(read_cached_news(paths.news_cache))
show_patch_notes(read_cached_patch_notes(paths.patches_cache))
cached_launcher_update = read_cached_launcher_update_check(
paths.launcher_update_check_cache
paths.launcher_update_check_cache,
channel=selected_update_channel,
)
if cached_launcher_update is not None:
show_launcher_update_check(cached_launcher_update, log_reason=False)
launcher_release_history = read_cached_launcher_release_history(
paths.launcher_release_history_cache,
)
refresh_runtime_compatibility()
mark_startup_component_ready(
"cached_content",