mirror of
https://github.com/Omni-guides/Jackify.git
synced 2026-08-14 00:23:42 +02:00
189 lines
6.2 KiB
Python
189 lines
6.2 KiB
Python
"""
|
|
Engine Invoker
|
|
|
|
Resolves the active install engine and builds the appropriate subprocess command.
|
|
Keeps engine-specific CLI differences isolated from install workflow code.
|
|
"""
|
|
import logging
|
|
import os
|
|
from typing import List, Optional, Tuple
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def get_active_engine_id() -> str:
|
|
from jackify.backend.services.tool_registry import get_active_engine_id as _get
|
|
return _get()
|
|
|
|
|
|
def is_clf3_active() -> bool:
|
|
return get_active_engine_id() == "clf3"
|
|
|
|
|
|
def ensure_engine_available(engine_id: str = "jackify-engine") -> Tuple[bool, str]:
|
|
"""
|
|
Check that the given engine binary is present and download it if not.
|
|
Returns (True, path) on success, (False, error_message) on failure.
|
|
Call this at startup before the first install attempt.
|
|
"""
|
|
from jackify.backend.services.tool_registry import ToolRegistry
|
|
path = get_engine_path(engine_id)
|
|
if path:
|
|
return True, path
|
|
logger.info("Engine %s not found, attempting download via Tools Hub", engine_id)
|
|
ok, msg = ToolRegistry().install(engine_id)
|
|
if not ok:
|
|
return False, msg
|
|
path = get_engine_path(engine_id)
|
|
if not path:
|
|
return False, f"{engine_id} downloaded but binary not found after install"
|
|
return True, path
|
|
|
|
|
|
def get_engine_path(engine_id: str) -> Optional[str]:
|
|
"""Return the filesystem path to the engine binary for the given engine_id."""
|
|
from jackify.backend.services.tool_registry import ToolRegistry
|
|
path = ToolRegistry().get_binary_path(engine_id)
|
|
if path and path.is_file():
|
|
return str(path)
|
|
|
|
if engine_id == "jackify-engine":
|
|
from jackify.backend.core.modlist_operations import get_jackify_engine_path
|
|
return get_jackify_engine_path()
|
|
|
|
logger.warning("Engine binary not found for engine_id=%s", engine_id)
|
|
return None
|
|
|
|
|
|
def get_active_engine_path() -> Optional[str]:
|
|
"""Return the binary path for the currently active engine."""
|
|
return get_engine_path(get_active_engine_id())
|
|
|
|
|
|
def resolve_game_dir(game_type: Optional[str], modlist_path: Optional[str] = None) -> Optional[str]:
|
|
"""
|
|
Resolve the vanilla game installation directory for CLF3's --game argument.
|
|
Searches Steam and Heroic-managed stores in order.
|
|
Returns None if the path cannot be determined.
|
|
Use resolve_game_location() when the store identity is also needed.
|
|
"""
|
|
result = resolve_game_location(game_type)
|
|
return result[0] if result else None
|
|
|
|
|
|
def resolve_game_location(game_type: Optional[str]) -> Optional[tuple]:
|
|
"""
|
|
Return (path_str, store) for the detected game installation, or None.
|
|
store is one of: 'steam', 'gog', 'epic', 'unknown'.
|
|
"""
|
|
if not game_type:
|
|
return None
|
|
try:
|
|
from jackify.backend.handlers.vanilla_game_finder import VanillaGameFinder
|
|
result = VanillaGameFinder().find(game_type)
|
|
if result:
|
|
path, store = result
|
|
return str(path), store
|
|
except Exception as e:
|
|
logger.debug("Game dir detection failed for %s: %s", game_type, e)
|
|
return None
|
|
|
|
|
|
def build_install_command(
|
|
engine_id: str,
|
|
engine_path: str,
|
|
wabbajack: str,
|
|
install_dir: str,
|
|
downloads_dir: str,
|
|
game_dir: Optional[str] = None,
|
|
install_mode: str = "online",
|
|
debug: bool = False,
|
|
) -> List[str]:
|
|
"""Build the subprocess install command for the given engine."""
|
|
if engine_id == "clf3":
|
|
return _build_clf3_command(engine_path, wabbajack, install_dir, downloads_dir, game_dir)
|
|
return _build_jackify_engine_command(engine_path, wabbajack, install_dir, downloads_dir, install_mode, debug, game_dir)
|
|
|
|
|
|
def _build_jackify_engine_command(
|
|
engine_path: str,
|
|
wabbajack: str,
|
|
install_dir: str,
|
|
downloads_dir: str,
|
|
install_mode: str,
|
|
debug: bool,
|
|
game_dir: Optional[str] = None,
|
|
) -> List[str]:
|
|
cmd = [engine_path, "install", "--show-file-progress"]
|
|
if wabbajack.endswith(".wabbajack") and os.path.isfile(wabbajack):
|
|
cmd += ["-w", wabbajack]
|
|
else:
|
|
cmd += ["-m", wabbajack]
|
|
cmd += ["-o", install_dir, "-d", downloads_dir]
|
|
if game_dir:
|
|
cmd += ["-g", game_dir]
|
|
if debug:
|
|
cmd.append("--debug")
|
|
return cmd
|
|
|
|
|
|
def _read_resource_settings() -> dict:
|
|
"""Read resource_settings.json from the Jackify config dir; return empty dict on any failure."""
|
|
try:
|
|
from jackify.shared.paths import get_jackify_config_dir
|
|
import json
|
|
path = get_jackify_config_dir() / "resource_settings.json"
|
|
with open(path, "r", encoding="utf-8") as f:
|
|
return json.load(f)
|
|
except Exception:
|
|
return {}
|
|
|
|
|
|
def _clf3_default_workers() -> int:
|
|
"""Default worker count matching jackify-engine: full cpu_count."""
|
|
import multiprocessing
|
|
return max(1, multiprocessing.cpu_count() or 4)
|
|
|
|
|
|
def _build_clf3_command(
|
|
engine_path: str,
|
|
wabbajack: str,
|
|
install_dir: str,
|
|
downloads_dir: str,
|
|
game_dir: Optional[str] = None,
|
|
) -> List[str]:
|
|
# Positional order: <WABBAJACK_FILE> <DOWNLOADS> <OUTPUT>
|
|
# CLF3 performs its own game detection (Steam + Heroic) with file verification.
|
|
# game_dir is reserved for explicit edge-case overrides only.
|
|
cmd = [engine_path, "install", "--jackify"]
|
|
if os.environ.get("JACKIFY_CLF3_VERBOSE"):
|
|
cmd.append("--verbose")
|
|
logger.info("CLF3 verbose mode enabled (JACKIFY_CLF3_VERBOSE)")
|
|
if game_dir:
|
|
cmd += ["--game", game_dir]
|
|
|
|
res = _read_resource_settings()
|
|
default = _clf3_default_workers()
|
|
|
|
def _tasks(key: str) -> int:
|
|
val = res.get(key, {}).get("MaxTasks", 0)
|
|
return val if val > 0 else default
|
|
|
|
concurrent = _tasks("Downloads")
|
|
install_workers = _tasks("Installer")
|
|
sevenzip_workers = _tasks("File Extractor")
|
|
|
|
cmd += [
|
|
"--concurrent", str(concurrent),
|
|
"--install-workers", str(install_workers),
|
|
"--sevenzip-workers", str(sevenzip_workers),
|
|
]
|
|
logger.debug(
|
|
"CLF3 resource flags: concurrent=%d install_workers=%d sevenzip_workers=%d (from %s)",
|
|
concurrent, install_workers, sevenzip_workers,
|
|
"resource_settings.json" if res else "default (cpu_count)",
|
|
)
|
|
|
|
cmd += [wabbajack, downloads_dir, install_dir]
|
|
return cmd
|