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

@@ -42,19 +42,71 @@ class DoctorReport:
package_hint: str | None
warnings: list[str]
@property
def blockers(self) -> tuple[str, ...]:
return host_readiness_blockers(self)
def to_dict(self) -> dict[str, object]:
return asdict(self)
payload = asdict(self)
payload["blockers"] = list(self.blockers)
return payload
def inspect_host(environment: Mapping[str, str] | None = None) -> DoctorReport:
def host_readiness_blockers(report: DoctorReport) -> tuple[str, ...]:
"""Explain every condition that makes a host report fail readiness."""
blockers = [
f"{tool.name} is missing ({tool.purpose})."
for tool in report.tools
if tool.required and tool.path is None
]
bubblewrap = next(
(tool for tool in report.tools if tool.name == "bubblewrap"),
None,
)
if (
bubblewrap is not None
and bubblewrap.required
and bubblewrap.path is not None
and not report.container_host.ready
):
issue = report.container_host.issue or "probe failed"
message = f"Bubblewrap cannot create the Steam Runtime sandbox ({issue})."
if report.container_host.recommendation:
message += f" {report.container_host.recommendation}"
blockers.append(message)
if report.container_host.broken_umu_profile_link:
blockers.append(
"UMU's AppArmor profile link is broken; run "
"`pristontale-eu container-host --repair`."
)
if not report.ok and not blockers:
blockers.append(
"The host readiness check failed for an unclassified reason; "
"run `pristontale-eu --json doctor` and report its output."
)
return tuple(blockers)
def inspect_host(
environment: Mapping[str, str] | None = None,
*,
vulkan_support: Mapping[str, object] | None = None,
) -> DoctorReport:
env = os.environ if environment is None else environment
os_release = _read_os_release()
distribution = os_release.get("ID", "unknown")
version = os_release.get("VERSION_ID", "unknown")
architecture = platform.machine()
wine_tools = find_wine_tools()
umu_path = _umu_path(env)
container_host = inspect_container_host(environment=env)
container_required = umu_path is not None
detected_vulkan = (
dict(vulkan_support)
if vulkan_support is not None
else _inspect_vulkan_support(env)
)
tools = [
ToolStatus(
"python3",
@@ -117,9 +169,11 @@ def inspect_host(environment: Mapping[str, str] | None = None) -> DoctorReport:
"Vulkan diagnostics",
),
]
tools.extend(_platform_optional_tools(distribution, architecture=platform.machine()))
if architecture.casefold() in {"x86_64", "amd64"}:
tools.extend(_vulkan_tool_statuses(detected_vulkan, distribution))
tools.extend(_platform_optional_tools(distribution, architecture=architecture))
warnings: list[str] = []
if platform.machine().casefold() not in {"x86_64", "amd64"}:
if architecture.casefold() not in {"x86_64", "amd64"}:
warnings.append("PristonTale EU requires an x86-64 host with 32-bit Vulkan support.")
if not env.get("WAYLAND_DISPLAY") and not env.get("DISPLAY"):
warnings.append("No graphical X11 or Wayland session is visible.")
@@ -138,7 +192,7 @@ def inspect_host(environment: Mapping[str, str] | None = None) -> DoctorReport:
"UMU's AppArmor profile link is broken and may prevent profiles from "
"loading after reboot; run `pristontale-eu container-host --repair`."
)
warnings.extend(_arch_runtime_warnings(distribution, platform.machine()))
warnings.extend(_arch_runtime_warnings(distribution, architecture))
required_ok = all(item.path is not None for item in tools if item.required)
return DoctorReport(
ok=(
@@ -148,7 +202,7 @@ def inspect_host(environment: Mapping[str, str] | None = None) -> DoctorReport:
),
distribution=distribution,
version=version,
architecture=platform.machine(),
architecture=architecture,
display_backend="wayland" if env.get("WAYLAND_DISPLAY") else "x11",
tools=tools,
container_host=container_host,
@@ -228,11 +282,6 @@ def _platform_optional_tools(distribution: str, *, architecture: str) -> list[To
return []
if distribution in ARCH_FAMILY_DISTRIBUTIONS:
return [
_optional_tool_status(
"lib32-vulkan",
"/usr/lib32/libvulkan.so.1",
"32-bit Vulkan runtime compatibility layer",
),
_optional_tool_status(
"lib32-gstreamer",
"/usr/lib32/gstreamer-1.0",
@@ -250,11 +299,6 @@ def _arch_runtime_warnings(distribution: str, architecture: str) -> list[str]:
"Non-x86_64 Linux hosts are not a supported runtime architecture for this game."
]
warnings: list[str] = []
if not _path_exists("/usr/lib32/libvulkan.so.1"):
warnings.append(
"Install lib32-vulkan-icd-loader and the GPU vendor's matching 32-bit "
"Vulkan driver; this 32-bit D3D9 client otherwise cannot initialize DXVK."
)
if not _path_exists("/usr/lib32/gstreamer-1.0"):
warnings.append(
"Install lib32-gstreamer and lib32-gst-plugins-base if the login video or "
@@ -264,6 +308,77 @@ def _arch_runtime_warnings(distribution: str, architecture: str) -> list[str]:
return warnings
def _inspect_vulkan_support(environment: Mapping[str, str]) -> dict[str, object]:
try:
from .telemetry_environment import inspect_vulkan_support
return inspect_vulkan_support(environment)
except Exception:
return {}
def _vulkan_tool_statuses(
support: Mapping[str, object],
distribution: str,
) -> list[ToolStatus]:
return [
_vulkan_tool_status(64, support, distribution),
_vulkan_tool_status(32, support, distribution),
]
def _vulkan_tool_status(
bits: int,
support: Mapping[str, object],
distribution: str,
) -> ToolStatus:
available = support.get(f"vulkan_{bits}bit") is True
loader_available = support.get(f"vulkan_{bits}bit_loader_available") is True
icd_available = support.get(f"vulkan_{bits}bit_icd_available") is True
purpose = f"{bits}-bit Vulkan loader and GPU ICD required by DXVK"
if not available:
missing_parts: list[str] = []
if not loader_available:
missing_parts.append("loader")
if not icd_available:
missing_parts.append("GPU driver/ICD")
purpose = (
f"{bits}-bit Vulkan {' and '.join(missing_parts)} required by DXVK; "
+ _vulkan_package_guidance(distribution, bits)
)
return ToolStatus(
f"vulkan-{bits}bit",
"loader+ICD" if available else None,
True,
purpose,
)
def _vulkan_package_guidance(distribution: str, bits: int) -> str:
suffix = ":i386" if bits == 32 else ""
if distribution == "ubuntu":
return (
"on Ubuntu/Kubuntu install "
f"libvulkan1{suffix} and the matching GPU package "
f"(mesa-vulkan-drivers{suffix} or libnvidia-gl-<version>{suffix})"
)
if distribution in {"debian", "linuxmint", "pop", "elementary", "zorin"}:
return (
f"with APT install libvulkan1{suffix} and the matching {bits}-bit GPU ICD"
)
if distribution in {"fedora", "nobara"}:
return f"install the matching {bits}-bit Vulkan loader and GPU driver with DNF"
if distribution in ARCH_FAMILY_DISTRIBUTIONS:
return (
"install vulkan-icd-loader and the GPU vendor driver"
if bits == 64
else "install lib32-vulkan-icd-loader and the matching lib32 GPU driver"
)
if distribution in {"opensuse-tumbleweed", "opensuse-leap", "suse"}:
return f"install the matching {bits}-bit Vulkan loader and GPU driver with Zypper"
return f"install the matching {bits}-bit Vulkan loader and GPU driver"
def _optional_tool_status(name: str, path: str, purpose: str) -> ToolStatus:
if _path_exists(path):
return ToolStatus(name, path, required=False, purpose=purpose)