From 0cf6071d5e04b05e5528895f0128afa933fa6a84 Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:00:01 +0200 Subject: [PATCH 1/9] Try to resolve the engine using the env var last --- src/uepyscripts/internal/engine_resolver.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/uepyscripts/internal/engine_resolver.py b/src/uepyscripts/internal/engine_resolver.py index 8f9b9c9..219f160 100644 --- a/src/uepyscripts/internal/engine_resolver.py +++ b/src/uepyscripts/internal/engine_resolver.py @@ -84,10 +84,10 @@ def resolve_engine_from_path(project: Project) -> Optional[Path]: def resolve_engine_path(project: Project) -> Path: resolvers = [ - resolve_engine_from_env_var, resolve_engine_from_registry, resolve_engine_from_egs, resolve_engine_from_path, + resolve_engine_from_env_var, ] for resolver in resolvers: From f0bd20710242642b420825004aa7c329929b5e9b Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:23:26 +0200 Subject: [PATCH 2/9] Use logger instead of printf --- src/uepyscripts/internal/engine_resolver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/uepyscripts/internal/engine_resolver.py b/src/uepyscripts/internal/engine_resolver.py index 219f160..5b7299b 100644 --- a/src/uepyscripts/internal/engine_resolver.py +++ b/src/uepyscripts/internal/engine_resolver.py @@ -5,6 +5,7 @@ from pydantic import BaseModel +from .. import logger from ..internal.project import Project from ..tools.helpers import get_engine_root_folder_from_env_var, is_engine_from_egs from ..tools.winreg import get_registry_value @@ -69,7 +70,7 @@ def get_dat_file_path() -> Path: return Path(item.InstallLocation) except Exception as e: - print(f"Error parsing manifest: {e}") + logger.error(f"Error parsing manifest: {e}") return None @@ -93,6 +94,7 @@ def resolve_engine_path(project: Project) -> Path: for resolver in resolvers: path = resolver(project) if path: + logger.info(f"Engine path resolved via '{resolver.__name__}': {path}") break else: raise FileNotFoundError("Impossible to locate the engine") From 84deaa653b057a032befe91ddb3cbeb41e7a07c8 Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:23:42 +0200 Subject: [PATCH 3/9] Added comment --- src/uepyscripts/internal/engine_resolver.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/uepyscripts/internal/engine_resolver.py b/src/uepyscripts/internal/engine_resolver.py index 5b7299b..35be7df 100644 --- a/src/uepyscripts/internal/engine_resolver.py +++ b/src/uepyscripts/internal/engine_resolver.py @@ -88,6 +88,8 @@ def resolve_engine_path(project: Project) -> Path: resolve_engine_from_registry, resolve_engine_from_egs, resolve_engine_from_path, + # Resolve last with the environment variable to avoid failing the resolution on a machine + # where there's the environment variable but the engine is installed using the launcher resolve_engine_from_env_var, ] From cc4b10b2bc322724a6d373caae5f1dcf6abefc88 Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:24:10 +0200 Subject: [PATCH 4/9] Added optional uproject_path argument to check_engine_installation --- src/uepyscripts/tools/ue/check_engine_installation.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/uepyscripts/tools/ue/check_engine_installation.py b/src/uepyscripts/tools/ue/check_engine_installation.py index 700cf0c..045a2b7 100644 --- a/src/uepyscripts/tools/ue/check_engine_installation.py +++ b/src/uepyscripts/tools/ue/check_engine_installation.py @@ -16,6 +16,7 @@ """ import argparse +from pathlib import Path from ... import logger from ...internal.engine import resolve_engine @@ -26,6 +27,7 @@ def parse_arguments() -> argparse.Namespace: """Parse command line arguments.""" parser = argparse.ArgumentParser(description="Check and install Unreal Engine installation for the given project.") + parser.add_argument("--uproject_path", type=Path, help="Path to a uproject file. If not set, resolve_project will try to find one in its folder hierarchy") parser.add_argument("--unattended", action="store_true", help="Disable interactive prompts") return parser.parse_args() @@ -37,7 +39,7 @@ def main() -> None: args = parse_arguments() try: - project = resolve_project() + project = resolve_project(args.uproject_path) except Exception as e: logger.fatal(f"Project resolution failed: {e}") exit(1) From c6acd06864aedf374d3cc28ff5e004bdf9cdb19a Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:30:35 +0200 Subject: [PATCH 5/9] Don't check for path existence in the resolvers as it's done in resolve_engine_path --- src/uepyscripts/internal/engine_resolver.py | 8 ++------ src/uepyscripts/tools/helpers.py | 5 +---- 2 files changed, 3 insertions(+), 10 deletions(-) diff --git a/src/uepyscripts/internal/engine_resolver.py b/src/uepyscripts/internal/engine_resolver.py index 35be7df..a3119f5 100644 --- a/src/uepyscripts/internal/engine_resolver.py +++ b/src/uepyscripts/internal/engine_resolver.py @@ -18,9 +18,7 @@ def resolve_engine_from_env_var(project: Project) -> Optional[Path]: def resolve_engine_from_registry(project: Project) -> Optional[Path]: path = get_registry_value(winreg.HKEY_CURRENT_USER, r"SOFTWARE\Epic Games\Unreal Engine\Builds", project.engine_association) if path: - path = Path(path) - if path.exists(): - return path + return Path(path) return None @@ -31,9 +29,7 @@ def resolve_engine_from_egs(project: Project) -> Optional[Path]: winreg.HKEY_LOCAL_MACHINE, rf"SOFTWARE\EpicGames\Unreal Engine\{project.engine_association}", "InstalledDirectory" ) if registry_value: - path = Path(registry_value) - if path.exists(): - return path + return Path(registry_value) # Some installations are listed in LauncherInstalled.dat class EpicInstallation(BaseModel): diff --git a/src/uepyscripts/tools/helpers.py b/src/uepyscripts/tools/helpers.py index 9e18fc8..69cfc14 100644 --- a/src/uepyscripts/tools/helpers.py +++ b/src/uepyscripts/tools/helpers.py @@ -32,10 +32,7 @@ def get_engine_root_folder_from_env_var(project_engine_association: str) -> Opti path = Path(node_ue_root) if project_engine_association: path = path.joinpath(project_engine_association) - if path.exists(): - return path - - raise FileNotFoundError(f"The environment variable is set to {node_ue_root} but the folder {path} does not exist") + return path return None From 6123628bf8346398091e44ec26158fd198d3cd7a Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:31:11 +0200 Subject: [PATCH 6/9] Handle relative engine paths --- src/uepyscripts/internal/engine_resolver.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/uepyscripts/internal/engine_resolver.py b/src/uepyscripts/internal/engine_resolver.py index a3119f5..a61fcbb 100644 --- a/src/uepyscripts/internal/engine_resolver.py +++ b/src/uepyscripts/internal/engine_resolver.py @@ -73,6 +73,9 @@ def get_dat_file_path() -> Path: def resolve_engine_from_path(project: Project) -> Optional[Path]: path = Path(project.engine_association) + if not os.path.isabs(path): + path = (project.root_folder / path).resolve() + if os.path.isabs(path): return path From 7423e0fb5aa4e47e6cece5593cc2f676f1106f7b Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:35:51 +0200 Subject: [PATCH 7/9] Fixed check of the validity of the resolved engine path --- src/uepyscripts/internal/engine_resolver.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/uepyscripts/internal/engine_resolver.py b/src/uepyscripts/internal/engine_resolver.py index a61fcbb..4b632c1 100644 --- a/src/uepyscripts/internal/engine_resolver.py +++ b/src/uepyscripts/internal/engine_resolver.py @@ -100,7 +100,9 @@ def resolve_engine_path(project: Project) -> Path: else: raise FileNotFoundError("Impossible to locate the engine") - if not (path.exists() and str(path).replace(" ", "") not in ["", ".", "\\"]): + path_str = str(path).strip() + # Check that the path exists and is not a degenerate path containing only . or \\ + if not (path.exists() and path_str not in ["", ".", "\\"]): raise FileNotFoundError("Impossible to locate the engine") return path From da543b818fe17c56eed95f0dca9d220903e49e7d Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:36:49 +0200 Subject: [PATCH 8/9] Added towncrier entry --- .changelog/21.fixed.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 .changelog/21.fixed.md diff --git a/.changelog/21.fixed.md b/.changelog/21.fixed.md new file mode 100644 index 0000000..1213bc3 --- /dev/null +++ b/.changelog/21.fixed.md @@ -0,0 +1 @@ +Various fixes for the engine resolver From 48cbca9997451c3407f1d1e3ca8457eff6e29fe5 Mon Sep 17 00:00:00 2001 From: Michael Delva Date: Thu, 25 Jun 2026 15:38:52 +0200 Subject: [PATCH 9/9] ruff format --- src/uepyscripts/tools/ue/check_engine_installation.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/uepyscripts/tools/ue/check_engine_installation.py b/src/uepyscripts/tools/ue/check_engine_installation.py index 045a2b7..a934e22 100644 --- a/src/uepyscripts/tools/ue/check_engine_installation.py +++ b/src/uepyscripts/tools/ue/check_engine_installation.py @@ -27,7 +27,11 @@ def parse_arguments() -> argparse.Namespace: """Parse command line arguments.""" parser = argparse.ArgumentParser(description="Check and install Unreal Engine installation for the given project.") - parser.add_argument("--uproject_path", type=Path, help="Path to a uproject file. If not set, resolve_project will try to find one in its folder hierarchy") + parser.add_argument( + "--uproject_path", + type=Path, + help=("Optional path to a uproject file.If not set, resolve_project will try to find one in its folder hierarchy"), + ) parser.add_argument("--unattended", action="store_true", help="Disable interactive prompts") return parser.parse_args()