Init
This commit is contained in:
184
ptlinux/doctor.py
Normal file
184
ptlinux/doctor.py
Normal file
@@ -0,0 +1,184 @@
|
||||
"""Cross-distribution dependency and host diagnostics."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import asdict, dataclass
|
||||
from importlib.util import find_spec
|
||||
import os
|
||||
from pathlib import Path
|
||||
import platform
|
||||
import shutil
|
||||
from typing import Mapping
|
||||
|
||||
from .launcher import find_wine_tools
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class ToolStatus:
|
||||
name: str
|
||||
path: str | None
|
||||
required: bool
|
||||
purpose: str
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class DoctorReport:
|
||||
ok: bool
|
||||
distribution: str
|
||||
version: str
|
||||
architecture: str
|
||||
display_backend: str
|
||||
tools: list[ToolStatus]
|
||||
package_hint: str | None
|
||||
warnings: list[str]
|
||||
|
||||
def to_dict(self) -> dict[str, object]:
|
||||
return asdict(self)
|
||||
|
||||
|
||||
def inspect_host(environment: Mapping[str, str] | 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")
|
||||
wine_tools = find_wine_tools()
|
||||
tools = [
|
||||
ToolStatus(
|
||||
"python3",
|
||||
shutil.which("python3"),
|
||||
True,
|
||||
"native installer and command-line interface",
|
||||
),
|
||||
ToolStatus(
|
||||
"python-tk",
|
||||
_python_tk_path(),
|
||||
True,
|
||||
"native dashboard",
|
||||
),
|
||||
ToolStatus(
|
||||
"wine",
|
||||
str(wine_tools.wine) if wine_tools else None,
|
||||
True,
|
||||
"legacy updater support and game compatibility utilities",
|
||||
),
|
||||
ToolStatus(
|
||||
"winetricks",
|
||||
str(wine_tools.winetricks) if wine_tools else shutil.which("winetricks"),
|
||||
True,
|
||||
"native D3D helper DLLs for the Proton game prefix",
|
||||
),
|
||||
ToolStatus(
|
||||
"gamescope",
|
||||
shutil.which("gamescope"),
|
||||
True,
|
||||
"borderless X11/Wayland game container",
|
||||
),
|
||||
ToolStatus(
|
||||
"cabextract",
|
||||
shutil.which("cabextract"),
|
||||
True,
|
||||
"Microsoft runtime extraction through Winetricks",
|
||||
),
|
||||
ToolStatus(
|
||||
"openssl",
|
||||
shutil.which("openssl"),
|
||||
True,
|
||||
"signed native-launcher update verification",
|
||||
),
|
||||
ToolStatus(
|
||||
"umu-run",
|
||||
shutil.which("umu-run"),
|
||||
False,
|
||||
"portable Proton runtime when Steam Proton 7 is unavailable",
|
||||
),
|
||||
ToolStatus(
|
||||
"vulkaninfo",
|
||||
shutil.which("vulkaninfo"),
|
||||
False,
|
||||
"Vulkan diagnostics",
|
||||
),
|
||||
]
|
||||
warnings: list[str] = []
|
||||
if platform.machine().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.")
|
||||
if shutil.which("umu-run") is None and not _has_steam_proton7(env):
|
||||
warnings.append(
|
||||
"No game runtime was found: install umu-launcher or Steam Proton 7.0."
|
||||
)
|
||||
required_ok = all(item.path is not None for item in tools if item.required)
|
||||
return DoctorReport(
|
||||
ok=required_ok and not any("No game runtime" in warning for warning in warnings),
|
||||
distribution=distribution,
|
||||
version=version,
|
||||
architecture=platform.machine(),
|
||||
display_backend="wayland" if env.get("WAYLAND_DISPLAY") else "x11",
|
||||
tools=tools,
|
||||
package_hint=_package_hint(distribution),
|
||||
warnings=warnings,
|
||||
)
|
||||
|
||||
|
||||
def _read_os_release() -> dict[str, str]:
|
||||
result: dict[str, str] = {}
|
||||
try:
|
||||
lines = Path("/etc/os-release").read_text(encoding="utf-8").splitlines()
|
||||
except OSError:
|
||||
return result
|
||||
for line in lines:
|
||||
if "=" not in line or line.startswith("#"):
|
||||
continue
|
||||
key, value = line.split("=", 1)
|
||||
result[key] = value.strip().strip('"')
|
||||
return result
|
||||
|
||||
|
||||
def _python_tk_path() -> str | None:
|
||||
try:
|
||||
specification = find_spec("_tkinter")
|
||||
except (ImportError, ValueError):
|
||||
return None
|
||||
if specification is None:
|
||||
return None
|
||||
return specification.origin or "available"
|
||||
|
||||
|
||||
def _package_hint(distribution: str) -> str | None:
|
||||
if distribution in {"ubuntu", "debian", "linuxmint", "pop"}:
|
||||
return (
|
||||
"Install Python/Tk, Wine (64- and 32-bit), Winetricks, Gamescope, "
|
||||
"Cabextract, OpenSSL, and both 64- and 32-bit Vulkan drivers with APT."
|
||||
)
|
||||
if distribution in {"fedora", "nobara"}:
|
||||
return (
|
||||
"Install python3-tkinter, wine, winetricks, gamescope, cabextract, openssl, "
|
||||
"and the matching 32-bit Vulkan driver with DNF."
|
||||
)
|
||||
if distribution in {"arch", "cachyos", "endeavouros", "manjaro"}:
|
||||
return (
|
||||
"Install python, tk, wine, winetricks, gamescope, cabextract, openssl, and "
|
||||
"the matching lib32 Vulkan driver with Pacman."
|
||||
)
|
||||
if distribution in {"opensuse-tumbleweed", "opensuse-leap", "suse"}:
|
||||
return (
|
||||
"Install Python/Tk, Wine, Winetricks, Gamescope, Cabextract, OpenSSL, and "
|
||||
"32-bit Vulkan support with Zypper."
|
||||
)
|
||||
return None
|
||||
|
||||
|
||||
def _has_steam_proton7(environment: Mapping[str, str]) -> bool:
|
||||
home_value = environment.get("HOME")
|
||||
if not home_value:
|
||||
return False
|
||||
home = Path(home_value)
|
||||
for steam_root in (
|
||||
home / ".local/share/Steam",
|
||||
home / ".steam/steam",
|
||||
home / "snap/steam/common/.local/share/Steam",
|
||||
home / ".var/app/com.valvesoftware.Steam/.local/share/Steam",
|
||||
):
|
||||
if (steam_root / "steamapps/common/Proton 7.0/proton").is_file():
|
||||
return True
|
||||
return False
|
||||
Reference in New Issue
Block a user