Init
This commit is contained in:
92
ptlinux/install_status.py
Normal file
92
ptlinux/install_status.py
Normal file
@@ -0,0 +1,92 @@
|
||||
"""Read-only installation state used by the native launcher."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from dataclasses import dataclass
|
||||
from enum import Enum
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
from .paths import AppPaths
|
||||
|
||||
|
||||
REQUIRED_GAME_FILES = (
|
||||
Path("Game.exe"),
|
||||
Path("game.dll"),
|
||||
Path("game.ini"),
|
||||
Path("game/maps/WhisperingVale.rovug"),
|
||||
)
|
||||
|
||||
|
||||
class PrimaryAction(str, Enum):
|
||||
"""Action presented by the launcher's large primary button."""
|
||||
|
||||
INSTALL = "INSTALL"
|
||||
UPDATE = "UPDATE"
|
||||
PLAY = "PLAY"
|
||||
|
||||
|
||||
@dataclass(frozen=True)
|
||||
class GameInstallationStatus:
|
||||
"""Local game completeness and its relationship to the official depot."""
|
||||
|
||||
action: PrimaryAction
|
||||
installed: bool
|
||||
local_build: int | None
|
||||
official_build: int | None
|
||||
missing_files: tuple[Path, ...]
|
||||
|
||||
|
||||
def inspect_game_installation(
|
||||
paths: AppPaths,
|
||||
official_build: int | None = None,
|
||||
) -> GameInstallationStatus:
|
||||
"""Select Install, Update, or Play without modifying the installation.
|
||||
|
||||
A completed native sync is the verified source for the local depot build.
|
||||
When the official build cannot be checked, a complete local game remains
|
||||
playable so temporary network failures do not lock users out.
|
||||
"""
|
||||
|
||||
if official_build is not None and (
|
||||
isinstance(official_build, bool)
|
||||
or not isinstance(official_build, int)
|
||||
or official_build < 0
|
||||
):
|
||||
raise ValueError("official build must be a non-negative integer")
|
||||
|
||||
missing_files = tuple(
|
||||
relative
|
||||
for relative in REQUIRED_GAME_FILES
|
||||
if not (paths.source_game / relative).is_file()
|
||||
)
|
||||
installed = not missing_files
|
||||
local_build = _read_verified_local_build(paths.native_depot_state)
|
||||
if not installed:
|
||||
action = PrimaryAction.INSTALL
|
||||
elif official_build is not None and (
|
||||
local_build is None or local_build < official_build
|
||||
):
|
||||
action = PrimaryAction.UPDATE
|
||||
else:
|
||||
action = PrimaryAction.PLAY
|
||||
return GameInstallationStatus(
|
||||
action=action,
|
||||
installed=installed,
|
||||
local_build=local_build,
|
||||
official_build=official_build,
|
||||
missing_files=missing_files,
|
||||
)
|
||||
|
||||
|
||||
def _read_verified_local_build(path: Path) -> int | None:
|
||||
try:
|
||||
value = json.loads(path.read_text(encoding="utf-8"))
|
||||
except (OSError, UnicodeError, json.JSONDecodeError):
|
||||
return None
|
||||
if not isinstance(value, dict) or value.get("dry_run") is not False:
|
||||
return None
|
||||
build = value.get("build")
|
||||
if isinstance(build, bool) or not isinstance(build, int) or build < 0:
|
||||
return None
|
||||
return build
|
||||
Reference in New Issue
Block a user