Skip to content

Commit

Permalink
StandardRunner.process_create can now return None in order to cancel …
Browse files Browse the repository at this point in the history
…starting. This allows CLI to print the command line without starting in "dry mode". Also added various verbose prints.
  • Loading branch information
mindstorm38 committed Apr 21, 2024
1 parent ae6e9be commit eb690c9
Show file tree
Hide file tree
Showing 3 changed files with 44 additions and 37 deletions.
68 changes: 36 additions & 32 deletions portablemc/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,11 +306,11 @@ def cmd_search_handler(ns: SearchNs, kind: str, table: OutputTable):
def cmd_start(ns: StartNs):

version_parts = ns.version.split(":")

# If no split, the kind of version is "standard": parts have at least 2 elements.
if len(version_parts) == 1:
version_parts = ["standard", version_parts[0]]

# No handler means that the format is invalid.
version = cmd_start_handler(ns, version_parts[0], version_parts[1:])
if version is None:
Expand Down Expand Up @@ -376,33 +376,28 @@ def filter_libraries(libs: Dict[LibrarySpecifier, Any]) -> None:

env = version.install(watcher=StartWatcher(ns))

if ns.verbose >= 1 and len(env.fixes):
ns.out.task("INFO", "start.fixes")
ns.out.finish()
if ns.verbose >= 1:
for fix, fix_value in env.fixes.items():
ns.out.task(None, f"start.fix.{fix}", value=fix_value)
ns.out.task("INFO", f"start.fix.{fix}", value=fix_value)
ns.out.finish()

# If not dry run, run it!
if not ns.dry:

# Included binaries
if ns.include_bin is not None:
for bin_path in ns.include_bin:
if not bin_path.is_file():
ns.out.task("FAILED", "start.additional_binary_not_found", path=bin_path)
ns.out.finish()
sys.exit(EXIT_FAILURE)
env.native_libs.append(bin_path)
# Included binaries
if ns.include_bin is not None:
for bin_path in ns.include_bin:
if not bin_path.is_file():
ns.out.task("FAILED", "start.additional_binary_not_found", path=bin_path)
ns.out.finish()
sys.exit(EXIT_FAILURE)
env.native_libs.append(bin_path)

# Extend JVM arguments with given arguments, or defaults
if ns.jvm_args is None:
env.jvm_args.extend(DEFAULT_JVM_ARGS)
elif len(ns.jvm_args):
env.jvm_args.extend(ns.jvm_args.split())

env.run(CliRunner(ns))

# Extend JVM arguments with given arguments, or defaults
if ns.jvm_args is None:
env.jvm_args.extend(DEFAULT_JVM_ARGS)
elif len(ns.jvm_args):
env.jvm_args.extend(ns.jvm_args.split())

# This CliRunner will abort running if in dry mode.
env.run(CliRunner(ns))
sys.exit(EXIT_OK)

except VersionNotFoundError as error:
Expand Down Expand Up @@ -453,6 +448,10 @@ def cmd_start_handler(ns: StartNs, kind: str, parts: List[str]) -> Optional[Vers

version = parts[0] or "release"
ns.socket_error_tips.append("version_manifest")

if ns.verbose >= 1:
ns.out.task("INFO", "start.global_version", kind=kind, version=version, remaining=" ".join(parts[1:]))
ns.out.finish()

if kind == "standard":
if len(parts) != 1:
Expand All @@ -465,9 +464,9 @@ def cmd_start_handler(ns: StartNs, kind: str, parts: List[str]) -> Optional[Vers
return None

# Legacy fabric has a special case because it will never be supported for
# versions past 1.12.2, it is not made for latest release version.
# versions past 1.13.2, it is not made for latest release version.
if kind == "legacyfabric" and version == "release":
version = "1.12.2"
version = "1.13.2"

if kind == "fabric":
constructor = FabricVersion.with_fabric
Expand Down Expand Up @@ -800,7 +799,7 @@ def finish_task(key: str, **kwargs) -> None:
ns.out.finish()

def features(e: FeaturesEvent) -> None:
if ns.verbose >= 1 and len(e.features):
if ns.verbose >= 1:
ns.out.task("INFO", "start.features", features=", ".join(e.features))
ns.out.finish()

Expand Down Expand Up @@ -896,16 +895,21 @@ def download_complete(self, e: DownloadCompleteEvent) -> None:

class CliRunner(StreamRunner):

def __init__(self, ns: RootNs) -> None:
def __init__(self, ns: StartNs) -> None:
super().__init__()
self.ns = ns

def process_create(self, args: List[str], work_dir: Path) -> Popen:
def process_create(self, args: List[str], work_dir: Path) -> Optional[Popen]:

self.ns.out.print("\n")
if self.ns.verbose >= 1:
if not self.ns.dry or self.ns.verbose >= 2:
self.ns.out.print("\n")

if self.ns.verbose >= 2:
self.ns.out.print(" ".join(args) + "\n")

if self.ns.dry:
return None

return super().process_create(args, work_dir)

def process_stream_event(self, event: Any) -> None:
Expand Down
4 changes: 2 additions & 2 deletions portablemc/cli/lang.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def get(key: str, **kwargs) -> str:
"logout.success": "Logged out {email}",
"logout.unknown_session": "No session for {email}",
# Command start
"start.global_version": "Global version: {kind} {version} {remaining}",
"start.version.invalid_id": "Invalid version id, expected: {expected}",
"start.version.invalid_id_unknown_kind": "Invalid version id, unknown kind: {kind}.",
"start.version.loading": "Loading version {version}... ",
Expand All @@ -200,7 +201,7 @@ def get(key: str, **kwargs) -> str:
"start.version.loaded.fetched": "Loaded version {version} (fetched)",
"start.version.not_found": "Version {version} not found",
"start.version.too_much_parents": "Too much parents while resolving versions.",
"start.features": "Features: {features}",
"start.features": "Features: [{features}]",
"start.jar.found": "Checked version jar",
"start.jar.not_found": "Version jar not found",
"start.assets.resolving": "Checking assets version {index_version}... ",
Expand All @@ -223,7 +224,6 @@ def get(key: str, **kwargs) -> str:
"use --jvm argument to manually set the path to your JVM executable.",
f"start.jvm.not_found_error.{JvmNotFoundError.BUILTIN_INVALID_VERSION}": f"The builtin JVM ({jvm_bin_filename}) is not compatible "
"with selected game version.",
"start.fixes": "Applied the following fixes:",
f"start.fix.{Version.FIX_LEGACY_PROXY}": "Using legacy proxy for online resources: {value}",
f"start.fix.{Version.FIX_LEGACY_MERGE_SORT}": "Using legacy merge sort: {value}",
f"start.fix.{Version.FIX_LEGACY_RESOLUTION}": "Included resolution into game arguments: {value}",
Expand Down
9 changes: 6 additions & 3 deletions portablemc/standard.py
Original file line number Diff line number Diff line change
Expand Up @@ -1561,17 +1561,20 @@ def run(self, env: Environment) -> None:
*replace_list_vars(env.game_args, replacements)
], env.context.work_dir)

self.process_wait(process)
if process is not None:
self.process_wait(process)

finally:
# Any error while setting up the binary directory cause it to be deleted.
shutil.rmtree(bin_dir, ignore_errors=True)

def process_create(self, args: List[str], work_dir: Path) -> Popen:
def process_create(self, args: List[str], work_dir: Path) -> Optional[Popen]:
"""This function is called when process needs to be created with the given
arguments in the given working directory. The default implementation does nothing
special but this can be used to create the process with enabled output piping,
to later use in `process_wait`.
None can be returned to abort starting the game.
"""
return Popen(args, cwd=work_dir)

Expand All @@ -1597,7 +1600,7 @@ class StreamRunner(StandardRunner):
its completion.
"""

def process_create(self, args: List[str], work_dir: Path) -> Popen:
def process_create(self, args: List[str], work_dir: Path) -> Optional[Popen]:
return Popen(args, cwd=work_dir, stdout=PIPE, stderr=STDOUT, bufsize=1, universal_newlines=True, encoding="utf-8", errors="replace")

def process_wait(self, process: Popen) -> None:
Expand Down

0 comments on commit eb690c9

Please sign in to comment.