Added better support for kubuntu and fixed some issues with telemetry.
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user