import shutil from pathlib import Path from typing import Callable from l4d2host.paths import get_left4me_root from l4d2host.process import run_command from l4d2host.logging import emit_step def _resolve_steamcmd_siblings(steamcmd: str) -> tuple[Path, Path] | None: resolved = shutil.which(steamcmd) base = Path(resolved).resolve() if resolved else Path(steamcmd) if base.is_symlink(): base = base.resolve() parent = base.parent linux32 = parent / "linux32" linux64 = parent / "linux64" if linux32.is_dir() and linux64.is_dir(): return linux32, linux64 return None def _set_symlink(link: Path, target: Path, on_stderr: Callable[[str], None] | None) -> None: if link.is_symlink(): try: current = link.resolve() except OSError: current = None if current == target.resolve(): return link.unlink() elif link.exists(): if on_stderr is not None: on_stderr(f"refusing to replace non-symlink at {link}") return link.symlink_to(target) def _ensure_steam_sdk_symlinks( install_dir: Path, steamcmd: str, *, on_stdout: Callable[[str], None] | None, on_stderr: Callable[[str], None] | None, passthrough: bool, ) -> None: emit_step("ensuring steam sdk symlinks...", on_stdout, passthrough) siblings = _resolve_steamcmd_siblings(steamcmd) if siblings is not None: sdk32_target, sdk64_target = siblings else: fallback = install_dir / "bin" sdk32_target, sdk64_target = fallback, fallback steam_dir = Path.home() / ".steam" steam_dir.mkdir(parents=True, exist_ok=True) _set_symlink(steam_dir / "sdk32", sdk32_target, on_stderr) _set_symlink(steam_dir / "sdk64", sdk64_target, on_stderr) class SteamInstaller: def __init__(self, install_dir: Path | None = None, steamcmd: str = "steamcmd"): self.install_dir = get_left4me_root() / "installation" if install_dir is None else Path(install_dir) self.steamcmd = steamcmd def install_or_update( self, *, on_stdout: Callable[[str], None] | None = None, on_stderr: Callable[[str], None] | None = None, passthrough: bool = False, should_cancel: Callable[[], bool] | None = None, ) -> None: for platform in ("windows", "linux"): emit_step(f"downloading {platform} platform payload...", on_stdout, passthrough) run_command( [ self.steamcmd, "+force_install_dir", str(self.install_dir), "+login", "anonymous", "+@sSteamCmdForcePlatformType", platform, "+app_update", "222860", "validate", "+quit", ], on_stdout=on_stdout, on_stderr=on_stderr, passthrough=passthrough, should_cancel=should_cancel, ) _ensure_steam_sdk_symlinks( self.install_dir, self.steamcmd, on_stdout=on_stdout, on_stderr=on_stderr, passthrough=passthrough, ) emit_step("installation complete.", on_stdout, passthrough)