Added better support for kubuntu and fixed some issues with telemetry.

This commit is contained in:
2026-09-09 22:35:13 +02:00
parent 6e6219107e
commit 290d1a5383
8 changed files with 587 additions and 65 deletions

View File

@@ -88,6 +88,7 @@ from .telemetry import (
network_http_status,
shutdown_shared_telemetry,
)
from .system_identity import is_running_inside_steam_runtime
from .umu import (
UmuBootstrapResult,
find_umu_launcher,
@@ -310,12 +311,9 @@ def main(
initialized_telemetry, telemetry_owner = initialize_shared_telemetry(
paths,
session_metadata={"interface": interface, "command": command},
report_launcher_lifecycle=command == "gui",
)
telemetry = initialized_telemetry
if command != "gui":
initialized_telemetry.mark_ready(
{"interface": interface, "command": command}
)
telemetry_events = _command_telemetry_events(command, arguments, paths)
operation_started = time.monotonic()
if telemetry is not None and telemetry_events is not None:
@@ -652,9 +650,24 @@ def _doctor(as_json: bool) -> int:
status = tool.path or "MISSING"
required = "required" if tool.required else "optional"
print(f"{tool.name:12} {status} [{required}] — {tool.purpose}")
optional_missing = [
tool.name for tool in report.tools if not tool.required and tool.path is None
]
if optional_missing:
print(
"Optional tools missing (these do not block launch): "
+ ", ".join(optional_missing)
)
print(f"Host readiness: {'READY' if report.ok else 'BLOCKED'}")
if report.blockers:
print("Blocking diagnostics:")
for blocker in report.blockers:
print(f" - {blocker}")
for warning in report.warnings:
print(f"Warning: {warning}")
if report.package_hint:
if report.package_hint and any(
tool.required and tool.path is None for tool in report.tools
):
print(report.package_hint)
return 0 if report.ok else 1
@@ -1322,6 +1335,28 @@ def _play_locked(
"run pristontale-eu update before playing"
)
settings = _load_or_default_settings(paths, persist=not dry_run)
launcher_nested_in_steam_runtime = is_running_inside_steam_runtime()
if launcher_nested_in_steam_runtime and not dry_run:
print(
"Warning: the launcher itself is running inside Steam Runtime. Start "
"it from the desktop menu or a host terminal; nesting its UMU runtime "
"can cause black game output.",
file=sys.stderr,
flush=True,
)
window_launch_metadata: dict[str, object] = {
"gamebox_enabled": not no_box,
"resizable_window_requested": settings.allow_window_resizing,
"launcher_inside_steam_runtime": launcher_nested_in_steam_runtime,
}
if (
launcher_nested_in_steam_runtime
and settings.allow_window_resizing
and not no_box
):
window_launch_metadata["gamescope_disabled_reason"] = (
"launcher_inside_steam_runtime"
)
start_option = select_depot_start_option(
read_cached_depot_launch_catalog(paths.depot_launch_catalog_cache),
realm or settings.realm,
@@ -1412,11 +1447,13 @@ def _play_locked(
game_started_at = time.monotonic()
launch_success_lock = threading.Lock()
launch_success_reported = False
reported_runtime_warnings: set[str] = set()
if telemetry is not None and not dry_run:
telemetry.send_event(
"game_launch_started",
metadata={
**launch_session_metadata,
**window_launch_metadata,
"launch_duration_ms": round(
(game_started_at - launch_requested_at) * 1000
),
@@ -1438,6 +1475,27 @@ def _play_locked(
"""Timestamp launch success when the supervisor first confirms readiness."""
nonlocal launch_success_reported
presentation_failure = _gamescope_presentation_failure(line)
if telemetry is not None and not dry_run and presentation_failure is not None:
with launch_success_lock:
if presentation_failure not in reported_runtime_warnings:
reported_runtime_warnings.add(presentation_failure)
telemetry.send_event(
"game_window_diagnostic",
severity="warning",
metadata={
**launch_session_metadata,
**window_launch_metadata,
"component": "gamescope_host",
"diagnostic": presentation_failure,
"runtime": selection.kind,
"realm": start_option.name,
},
context={
"operation": "launch_game",
"stage": "presentation",
},
)
window_diagnostic = parse_window_diagnostic(line)
if window_diagnostic is not None:
diagnostic_session_id = window_diagnostic.get("session_id")
@@ -1470,6 +1528,7 @@ def _play_locked(
"game_launch_success",
metadata={
**launch_session_metadata,
**window_launch_metadata,
"launch_duration_ms": round(
(ready_at - launch_requested_at) * 1000
),
@@ -1514,6 +1573,7 @@ def _play_locked(
message="Game launch was interrupted",
metadata={
**launch_session_metadata,
**window_launch_metadata,
"runtime_seconds": round(time.monotonic() - game_started_at),
"clean_exit": False,
"session_duration_class": _session_duration_class(
@@ -1586,6 +1646,7 @@ def _play_locked(
if telemetry is not None and not dry_run:
common_game_metadata: dict[str, object] = {
**launch_session_metadata,
**window_launch_metadata,
"runtime_seconds": round(game_runtime_seconds),
"clean_exit": clean_exit,
"timed_out": outcome.timed_out,
@@ -1610,6 +1671,7 @@ def _play_locked(
"game_launch_success",
metadata={
**launch_session_metadata,
**window_launch_metadata,
"detection_duration_ms": round(game_runtime_seconds * 1000),
"detected_after_exit": True,
"game_window_ready": game_window_ready,
@@ -1896,12 +1958,32 @@ def _runtime_issue_summary(stderr: str) -> dict[str, object]:
"gstreamer_missing_libraries": missing_libraries,
"gstreamer_stream_error": "winegstreamer error" in folded,
"mesa_egl_initialization_failed": "egl: failed to create dri2 screen" in folded,
"gamescope_present_queue_unsupported": (
"physical device queue doesn't support presenting on our surface" in folded
),
"gamescope_vulkan_output_failed_count": folded.count(
"vulkan_make_output failed"
),
"steam_api_app_id_missing": "steamapi_init() failed; no appid found" in folded,
"dbghelp_symdeferred_assertion_count": folded.count(dbghelp_assertion),
"bubblewrap_uid_map_permission_denied": bwrap_uid_map_denied in folded,
}
def _gamescope_presentation_failure(output: str) -> str | None:
folded = output.casefold()
for diagnostic, marker in (
(
"gamescope_present_queue_unsupported",
"physical device queue doesn't support presenting on our surface",
),
("gamescope_vulkan_output_failed", "vulkan_make_output failed"),
):
if marker in folded:
return diagnostic
return None
def _game_window_was_ready(stderr: str) -> bool:
return _game_readiness_signal(stderr) is not None