Fixed issues with encodings and wine / proton compatibilities.
This commit is contained in:
157
ptlinux/cli.py
157
ptlinux/cli.py
@@ -34,7 +34,7 @@ from .launcher import (
|
||||
)
|
||||
from .locking import launcher_operation_lock
|
||||
from .login_diagnostics import inspect_login_environment
|
||||
from .login_trace import DEFAULT_TRACE_TIMEOUT_SECONDS, run_login_trace
|
||||
from .login_trace import DEFAULT_TRACE_TIMEOUT_SECONDS, LOGIN_TRACE_DISABLED_REASON
|
||||
from .overlay import cache_compatibility_binaries, prepare_runtime_view
|
||||
from .paths import AppPaths
|
||||
from .runtime import (
|
||||
@@ -42,12 +42,13 @@ from .runtime import (
|
||||
run_game,
|
||||
select_runtime,
|
||||
)
|
||||
from .settings import Settings, ensure_settings, load_settings, save_settings
|
||||
from .settings import Settings, ensure_settings, load_settings, reset_settings
|
||||
from .self_update import (
|
||||
check_for_launcher_update,
|
||||
install_launcher_update,
|
||||
rollback_launcher_update,
|
||||
update_check_to_dict,
|
||||
verify_launcher_update_channel,
|
||||
)
|
||||
|
||||
|
||||
@@ -76,6 +77,15 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
default=5.0,
|
||||
help="TCP connection timeout in seconds (default: 5)",
|
||||
)
|
||||
login_diagnostics.add_argument(
|
||||
"--runtime",
|
||||
choices=("configured", "auto", "proton7", "proton7-container", "umu"),
|
||||
default="configured",
|
||||
help=(
|
||||
"probe one runtime without changing settings or starting the game "
|
||||
"(default: configured)"
|
||||
),
|
||||
)
|
||||
|
||||
fetch = subparsers.add_parser("fetch", help="fetch and safely prepare the latest official launcher")
|
||||
fetch.add_argument("--dry-run", action="store_true")
|
||||
@@ -151,7 +161,7 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
play.add_argument("--skip-runtime-setup", action="store_true")
|
||||
login_trace = subparsers.add_parser(
|
||||
"login-trace",
|
||||
help="trace the post-authentication account check without retaining credentials",
|
||||
help="retired unsafe authentication trace (always refuses to launch)",
|
||||
)
|
||||
login_trace.add_argument("--game-dir", type=Path)
|
||||
login_trace.add_argument("--no-box", action="store_true", help="launch without X11 window styling")
|
||||
@@ -163,12 +173,22 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
help="maximum game runtime in seconds (default: 600)",
|
||||
)
|
||||
|
||||
settings = subparsers.add_parser("settings", help="show or initialize settings")
|
||||
settings.add_argument("--init", action="store_true", help="write conservative defaults")
|
||||
settings = subparsers.add_parser("settings", help="show, initialize, or reset settings")
|
||||
settings_action = settings.add_mutually_exclusive_group()
|
||||
settings_action.add_argument(
|
||||
"--init",
|
||||
action="store_true",
|
||||
help="create missing settings and migrate legacy files without resetting choices",
|
||||
)
|
||||
settings_action.add_argument(
|
||||
"--reset",
|
||||
action="store_true",
|
||||
help="explicitly replace all launcher settings with defaults",
|
||||
)
|
||||
|
||||
launcher_update = subparsers.add_parser(
|
||||
"launcher-update",
|
||||
help="check, install, or roll back the native Linux launcher",
|
||||
help="check, verify, install, or roll back the native Linux launcher",
|
||||
)
|
||||
launcher_action = launcher_update.add_mutually_exclusive_group()
|
||||
launcher_action.add_argument(
|
||||
@@ -176,6 +196,11 @@ def build_parser() -> argparse.ArgumentParser:
|
||||
action="store_true",
|
||||
help="authenticate the update manifest without installing it",
|
||||
)
|
||||
launcher_action.add_argument(
|
||||
"--verify",
|
||||
action="store_true",
|
||||
help="download and fully validate the published release without installing it",
|
||||
)
|
||||
launcher_action.add_argument(
|
||||
"--rollback",
|
||||
action="store_true",
|
||||
@@ -207,7 +232,12 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
if command == "doctor":
|
||||
return _doctor(arguments.json)
|
||||
if command == "login-diagnostics":
|
||||
return _login_diagnostics(paths, arguments.timeout, arguments.json)
|
||||
return _login_diagnostics(
|
||||
paths,
|
||||
arguments.timeout,
|
||||
arguments.runtime,
|
||||
arguments.json,
|
||||
)
|
||||
if command == "fetch":
|
||||
return _fetch(paths, arguments.dry_run, arguments.json)
|
||||
if command == "setup":
|
||||
@@ -277,11 +307,12 @@ def main(argv: Sequence[str] | None = None) -> int:
|
||||
arguments.json,
|
||||
)
|
||||
if command == "settings":
|
||||
return _settings(paths, arguments.init, arguments.json)
|
||||
return _settings(paths, arguments.init, arguments.reset, arguments.json)
|
||||
if command == "launcher-update":
|
||||
return _launcher_update(
|
||||
paths,
|
||||
arguments.check,
|
||||
arguments.verify,
|
||||
arguments.rollback,
|
||||
arguments.manifest_url,
|
||||
arguments.public_key,
|
||||
@@ -326,13 +357,24 @@ def _doctor(as_json: bool) -> int:
|
||||
return 0 if report.ok else 1
|
||||
|
||||
|
||||
def _login_diagnostics(paths: AppPaths, timeout_seconds: float, as_json: bool) -> int:
|
||||
def _login_diagnostics(
|
||||
paths: AppPaths,
|
||||
timeout_seconds: float,
|
||||
runtime_choice: str,
|
||||
as_json: bool,
|
||||
) -> int:
|
||||
settings = _load_or_default_settings(paths, persist=False)
|
||||
report = inspect_login_environment(
|
||||
paths,
|
||||
settings,
|
||||
timeout_seconds=timeout_seconds,
|
||||
)
|
||||
runner = _runner(paths, "login-diagnostics", False)
|
||||
with _operation_guard(paths, "login-diagnostics", False):
|
||||
report = inspect_login_environment(
|
||||
paths,
|
||||
settings,
|
||||
timeout_seconds=timeout_seconds,
|
||||
runner=runner,
|
||||
runtime_override=(
|
||||
None if runtime_choice == "configured" else runtime_choice
|
||||
),
|
||||
)
|
||||
if as_json:
|
||||
_print_json(report.to_dict())
|
||||
else:
|
||||
@@ -340,12 +382,30 @@ def _login_diagnostics(paths: AppPaths, timeout_seconds: float, as_json: bool) -
|
||||
print(f"Launcher: {report.launcher_version} ({report.launcher_module})")
|
||||
print(f"Python: {report.python_executable}")
|
||||
print(f"Host: {report.distribution_id} ({report.kernel_release})")
|
||||
print(
|
||||
"Runtime test: "
|
||||
f"{report.runtime_requested} "
|
||||
f"(configured: {report.runtime_configured}; settings unchanged)"
|
||||
)
|
||||
status = "REACHABLE" if probe.reachable else "UNREACHABLE"
|
||||
latency = f", {probe.latency_ms:.1f} ms" if probe.latency_ms is not None else ""
|
||||
print(f"Login server: {status} ({probe.host}:{probe.port}{latency})")
|
||||
if probe.error:
|
||||
print(f"Network error: {probe.error}")
|
||||
print("Authentication: not tested; no credentials or game-protocol data were sent")
|
||||
print(f"Login responses: {report.login_response_ownership}")
|
||||
identity_status = (
|
||||
"ready"
|
||||
if report.client_identity_ready is True
|
||||
else "NOT READY"
|
||||
if report.client_identity_ready is False
|
||||
else "not probed"
|
||||
)
|
||||
print(f"Client identity: {identity_status}; raw identity values were not collected")
|
||||
print(f"Privacy: {report.client_identity_privacy}")
|
||||
if not report.login_trace_enabled:
|
||||
print("Login trace: retired (will not launch or instrument the game)")
|
||||
print(f"Publisher test: {report.publisher_comparison_command}")
|
||||
print(f"Game profile: {report.game_profile}")
|
||||
print(f"Game.exe: {report.game_exe}")
|
||||
if report.game_exe_sha256:
|
||||
@@ -738,6 +798,11 @@ def _current_game_log(
|
||||
"module_code_sync_right": analysis.module_code_sync_right,
|
||||
"module_code_sync_delta": analysis.module_code_sync_delta,
|
||||
"banned_code_1": analysis.banned_code_1,
|
||||
"banned_code_1_in_native_log": analysis.banned_code_1,
|
||||
"banned_code_1_observation_scope": (
|
||||
"native-game-log-only; server-provided login-screen text is not "
|
||||
"written to this log"
|
||||
),
|
||||
"stage_without_new_stage_events": [
|
||||
event.to_dict()
|
||||
for event in analysis.events
|
||||
@@ -790,40 +855,24 @@ def _login_trace(
|
||||
timeout_seconds: float,
|
||||
as_json: bool,
|
||||
) -> int:
|
||||
settings = _load_or_default_settings(paths, persist=True)
|
||||
source = _source_game(paths, settings, game_dir)
|
||||
runner = _runner(paths, "login-trace", False)
|
||||
with _operation_guard(paths, "login-trace", False):
|
||||
_print_operation_status(as_json, 1, 4, "Preparing the managed game view…")
|
||||
prepare_runtime_view(paths, source)
|
||||
_print_operation_status(as_json, 2, 4, "Selecting the configured Proton runtime…")
|
||||
selection = select_runtime(settings)
|
||||
if not skip_runtime_setup:
|
||||
_print_operation_status(as_json, 3, 4, "Checking Proton and DirectX components…")
|
||||
prepare_game_runtime(paths, selection, runner)
|
||||
_print_operation_status(as_json, 4, 4, "Starting the privacy-safe login trace…")
|
||||
if not as_json:
|
||||
print(
|
||||
"Enter the account name with its registered capitalization, press Login, "
|
||||
"leave the approval screen visible for five seconds, then close the game.",
|
||||
flush=True,
|
||||
)
|
||||
report = run_login_trace(
|
||||
paths,
|
||||
settings,
|
||||
selection,
|
||||
runner,
|
||||
use_gamebox=not no_box,
|
||||
timeout_seconds=timeout_seconds,
|
||||
)
|
||||
_emit({"ok": True, "login_trace": report.to_dict()}, as_json)
|
||||
return 0
|
||||
# Preserve the command name so old instructions fail safely before touching
|
||||
# the prefix, preparing the runtime view, or starting Game.exe.
|
||||
_ = paths, game_dir, no_box, skip_runtime_setup, timeout_seconds, as_json
|
||||
raise RuntimeError(LOGIN_TRACE_DISABLED_REASON)
|
||||
|
||||
|
||||
def _settings(paths: AppPaths, initialize: bool, as_json: bool) -> int:
|
||||
if initialize:
|
||||
save_settings(paths, Settings())
|
||||
settings = ensure_settings(paths) if initialize else load_settings(paths)
|
||||
def _settings(
|
||||
paths: AppPaths,
|
||||
initialize: bool,
|
||||
reset: bool,
|
||||
as_json: bool,
|
||||
) -> int:
|
||||
if reset:
|
||||
settings = reset_settings(paths)
|
||||
elif initialize:
|
||||
settings = ensure_settings(paths)
|
||||
else:
|
||||
settings = load_settings(paths)
|
||||
_emit(
|
||||
{
|
||||
"ok": True,
|
||||
@@ -838,6 +887,7 @@ def _settings(paths: AppPaths, initialize: bool, as_json: bool) -> int:
|
||||
def _launcher_update(
|
||||
paths: AppPaths,
|
||||
check_only: bool,
|
||||
verify: bool,
|
||||
rollback: bool,
|
||||
manifest_url: str | None,
|
||||
public_key: Path | None,
|
||||
@@ -866,6 +916,21 @@ def _launcher_update(
|
||||
as_json,
|
||||
)
|
||||
return 0
|
||||
if verify:
|
||||
_print_operation_status(as_json, 1, 2, "Authenticating the launcher release…")
|
||||
result = verify_launcher_update_channel(
|
||||
manifest_url=manifest_url,
|
||||
public_key_path=public_key,
|
||||
)
|
||||
_print_operation_status(as_json, 2, 2, "Published launcher release verified.")
|
||||
_emit(
|
||||
{
|
||||
"ok": True,
|
||||
"launcher_update_verification": asdict(result),
|
||||
},
|
||||
as_json,
|
||||
)
|
||||
return 0
|
||||
_print_operation_status(as_json, 1, 2, "Verifying the signed launcher release…")
|
||||
result = install_launcher_update(
|
||||
paths,
|
||||
|
||||
Reference in New Issue
Block a user