mirror of
https://github.com/ynput/ayon-core.git
synced 2026-01-01 16:34:53 +01:00
fix unreal launch on linux
This commit is contained in:
parent
31eb53b256
commit
8b0036cf53
2 changed files with 139 additions and 83 deletions
|
|
@ -1,38 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Unreal launching and project tools."""
|
||||
import sys
|
||||
import os
|
||||
import platform
|
||||
import json
|
||||
from distutils import dir_util
|
||||
import subprocess
|
||||
import re
|
||||
from collections import OrderedDict
|
||||
from openpype.api import get_project_settings
|
||||
|
||||
|
||||
def get_engine_versions():
|
||||
"""
|
||||
def get_engine_versions(env=None):
|
||||
"""Detect Unreal Engine versions.
|
||||
|
||||
This will try to detect location and versions of installed Unreal Engine.
|
||||
Location can be overridden by `UNREAL_ENGINE_LOCATION` environment
|
||||
variable.
|
||||
|
||||
Returns:
|
||||
Args:
|
||||
env (dict, optional): Environment to use.
|
||||
|
||||
dict: dictionary with version as a key and dir as value.
|
||||
Returns:
|
||||
OrderedDict: dictionary with version as a key and dir as value.
|
||||
so the highest version is first.
|
||||
|
||||
Example:
|
||||
|
||||
>>> get_engine_version()
|
||||
>>> get_engine_versions()
|
||||
{
|
||||
"4.23": "C:/Epic Games/UE_4.23",
|
||||
"4.24": "C:/Epic Games/UE_4.24"
|
||||
}
|
||||
"""
|
||||
try:
|
||||
engine_locations = {}
|
||||
root, dirs, files = next(os.walk(os.environ["UNREAL_ENGINE_LOCATION"]))
|
||||
|
||||
for dir in dirs:
|
||||
if dir.startswith("UE_"):
|
||||
ver = dir.split("_")[1]
|
||||
engine_locations[ver] = os.path.join(root, dir)
|
||||
"""
|
||||
env = env or os.environ
|
||||
engine_locations = {}
|
||||
try:
|
||||
root, dirs, _ = next(os.walk(env["UNREAL_ENGINE_LOCATION"]))
|
||||
|
||||
for directory in dirs:
|
||||
if directory.startswith("UE"):
|
||||
try:
|
||||
ver = re.split(r"[-_]", directory)[1]
|
||||
except IndexError:
|
||||
continue
|
||||
engine_locations[ver] = os.path.join(root, directory)
|
||||
except KeyError:
|
||||
# environment variable not set
|
||||
pass
|
||||
|
|
@ -40,32 +52,37 @@ def get_engine_versions():
|
|||
# specified directory doesn't exists
|
||||
pass
|
||||
|
||||
# if we've got something, terminate autodetection process
|
||||
# if we've got something, terminate auto-detection process
|
||||
if engine_locations:
|
||||
return engine_locations
|
||||
return OrderedDict(sorted(engine_locations.items()))
|
||||
|
||||
# else kick in platform specific detection
|
||||
if platform.system().lower() == "windows":
|
||||
return _win_get_engine_versions()
|
||||
return OrderedDict(sorted(_win_get_engine_versions().items()))
|
||||
elif platform.system().lower() == "linux":
|
||||
# on linux, there is no installation and getting Unreal Engine involves
|
||||
# git clone. So we'll probably depend on `UNREAL_ENGINE_LOCATION`.
|
||||
pass
|
||||
elif platform.system().lower() == "darwin":
|
||||
return _darwin_get_engine_version()
|
||||
return OrderedDict(sorted(_darwin_get_engine_version(env).items()))
|
||||
|
||||
return {}
|
||||
return OrderedDict()
|
||||
|
||||
|
||||
def _win_get_engine_versions():
|
||||
"""
|
||||
"""Get Unreal Engine versions on Windows.
|
||||
|
||||
If engines are installed via Epic Games Launcher then there is:
|
||||
`%PROGRAMDATA%/Epic/UnrealEngineLauncher/LauncherInstalled.dat`
|
||||
This file is JSON file listing installed stuff, Unreal engines
|
||||
are marked with `"AppName" = "UE_X.XX"`` like `UE_4.24`
|
||||
|
||||
Returns:
|
||||
dict: version as a key and path as a value.
|
||||
|
||||
"""
|
||||
install_json_path = os.path.join(
|
||||
os.environ.get("PROGRAMDATA"),
|
||||
os.getenv("PROGRAMDATA"),
|
||||
"Epic",
|
||||
"UnrealEngineLauncher",
|
||||
"LauncherInstalled.dat",
|
||||
|
|
@ -75,11 +92,19 @@ def _win_get_engine_versions():
|
|||
|
||||
|
||||
def _darwin_get_engine_version() -> dict:
|
||||
"""
|
||||
"""Get Unreal Engine versions on MacOS.
|
||||
|
||||
It works the same as on Windows, just JSON file location is different.
|
||||
|
||||
Returns:
|
||||
dict: version as a key and path as a value.
|
||||
|
||||
See Aslo:
|
||||
:func:`_win_get_engine_versions`.
|
||||
|
||||
"""
|
||||
install_json_path = os.path.join(
|
||||
os.environ.get("HOME"),
|
||||
os.getenv("HOME"),
|
||||
"Library",
|
||||
"Application Support",
|
||||
"Epic",
|
||||
|
|
@ -91,25 +116,26 @@ def _darwin_get_engine_version() -> dict:
|
|||
|
||||
|
||||
def _parse_launcher_locations(install_json_path: str) -> dict:
|
||||
"""
|
||||
This will parse locations from json file.
|
||||
"""This will parse locations from json file.
|
||||
|
||||
Args:
|
||||
install_json_path (str): Path to `LauncherInstalled.dat`.
|
||||
|
||||
Returns:
|
||||
dict: with unreal engine versions as keys and
|
||||
paths to those engine installations as value.
|
||||
|
||||
:param install_json_path: path to `LauncherInstalled.dat`
|
||||
:type install_json_path: str
|
||||
:returns: returns dict with unreal engine versions as keys and
|
||||
paths to those engine installations as value.
|
||||
:rtype: dict
|
||||
"""
|
||||
engine_locations = {}
|
||||
if os.path.isfile(install_json_path):
|
||||
with open(install_json_path, "r") as ilf:
|
||||
try:
|
||||
install_data = json.load(ilf)
|
||||
except json.JSONDecodeError:
|
||||
except json.JSONDecodeError as e:
|
||||
raise Exception(
|
||||
"Invalid `LauncherInstalled.dat file. `"
|
||||
"Cannot determine Unreal Engine location."
|
||||
)
|
||||
) from e
|
||||
|
||||
for installation in install_data.get("InstallationList", []):
|
||||
if installation.get("AppName").startswith("UE_"):
|
||||
|
|
@ -123,36 +149,43 @@ def create_unreal_project(project_name: str,
|
|||
ue_version: str,
|
||||
pr_dir: str,
|
||||
engine_path: str,
|
||||
dev_mode: bool = False) -> None:
|
||||
"""
|
||||
This will create `.uproject` file at specified location. As there is no
|
||||
way I know to create project via command line, this is easiest option.
|
||||
Unreal project file is basically JSON file. If we find
|
||||
dev_mode: bool = False,
|
||||
env: dict = None) -> None:
|
||||
"""This will create `.uproject` file at specified location.
|
||||
|
||||
As there is no way I know to create project via command line, this is
|
||||
easiest option. Unreal project file is basically JSON file. If we find
|
||||
`AVALON_UNREAL_PLUGIN` environment variable we assume this is location
|
||||
of Avalon Integration Plugin and we copy its content to project folder
|
||||
and enable this plugin.
|
||||
|
||||
:param project_name: project name
|
||||
:type project_name: str
|
||||
:param ue_version: unreal engine version (like 4.23)
|
||||
:type ue_version: str
|
||||
:param pr_dir: path to directory where project will be created
|
||||
:type pr_dir: str
|
||||
:param engine_path: Path to Unreal Engine installation
|
||||
:type engine_path: str
|
||||
:param dev_mode: Flag to trigger C++ style Unreal project needing
|
||||
Visual Studio and other tools to compile plugins from
|
||||
sources. This will trigger automatically if `Binaries`
|
||||
directory is not found in plugin folders as this indicates
|
||||
this is only source distribution of the plugin. Dev mode
|
||||
is also set by preset file `unreal/project_setup.json` in
|
||||
**OPENPYPE_CONFIG**.
|
||||
:type dev_mode: bool
|
||||
:returns: None
|
||||
"""
|
||||
preset = get_project_settings(project_name)["unreal"]["project_setup"]
|
||||
Args:
|
||||
project_name (str): Name of the project.
|
||||
ue_version (str): Unreal engine version (like 4.23).
|
||||
pr_dir (str): Path to directory where project will be created.
|
||||
engine_path (str): Path to Unreal Engine installation.
|
||||
dev_mode (bool, optional): Flag to trigger C++ style Unreal project
|
||||
needing Visual Studio and other tools to compile plugins from
|
||||
sources. This will trigger automatically if `Binaries`
|
||||
directory is not found in plugin folders as this indicates
|
||||
this is only source distribution of the plugin. Dev mode
|
||||
is also set by preset file `unreal/project_setup.json` in
|
||||
**OPENPYPE_CONFIG**.
|
||||
env (dict, optional): Environment to use. If not set, `os.environ`.
|
||||
|
||||
if os.path.isdir(os.environ.get("AVALON_UNREAL_PLUGIN", "")):
|
||||
Throws:
|
||||
NotImplemented: For unsupported platforms.
|
||||
|
||||
Returns:
|
||||
None
|
||||
|
||||
"""
|
||||
env = env or os.environ
|
||||
preset = get_project_settings(project_name)["unreal"]["project_setup"]
|
||||
plugins_path = None
|
||||
uep_path = None
|
||||
|
||||
if os.path.isdir(env.get("AVALON_UNREAL_PLUGIN", "")):
|
||||
# copy plugin to correct path under project
|
||||
plugins_path = os.path.join(pr_dir, "Plugins")
|
||||
avalon_plugin_path = os.path.join(plugins_path, "Avalon")
|
||||
|
|
@ -180,17 +213,17 @@ def create_unreal_project(project_name: str,
|
|||
}
|
||||
|
||||
if preset["install_unreal_python_engine"]:
|
||||
# If `OPENPYPE_UNREAL_ENGINE_PYTHON_PLUGIN` is set, copy it from there
|
||||
# to support offline installation.
|
||||
# If `PYPE_UNREAL_ENGINE_PYTHON_PLUGIN` is set, copy it from there to
|
||||
# support offline installation.
|
||||
# Otherwise clone UnrealEnginePython to Plugins directory
|
||||
# https://github.com/20tab/UnrealEnginePython.git
|
||||
uep_path = os.path.join(plugins_path, "UnrealEnginePython")
|
||||
if os.environ.get("OPENPYPE_UNREAL_ENGINE_PYTHON_PLUGIN"):
|
||||
if env.get("PYPE_UNREAL_ENGINE_PYTHON_PLUGIN"):
|
||||
|
||||
os.makedirs(uep_path, exist_ok=True)
|
||||
dir_util._path_created = {}
|
||||
dir_util.copy_tree(
|
||||
os.environ.get("OPENPYPE_UNREAL_ENGINE_PYTHON_PLUGIN"),
|
||||
env.get("PYPE_UNREAL_ENGINE_PYTHON_PLUGIN"),
|
||||
uep_path)
|
||||
else:
|
||||
# WARNING: this will trigger dev_mode, because we need to compile
|
||||
|
|
@ -246,25 +279,40 @@ def create_unreal_project(project_name: str,
|
|||
with open(project_file, mode="w") as pf:
|
||||
json.dump(data, pf, indent=4)
|
||||
|
||||
# UE < 4.26 have Python2 by default, so we need PySide
|
||||
# but we will not need it in 4.26 and up
|
||||
if int(ue_version.split(".")[1]) < 26:
|
||||
# ensure we have PySide installed in engine
|
||||
# TODO: make it work for other platforms 🍎 🐧
|
||||
# ensure we have PySide installed in engine
|
||||
# this won't work probably as pyside is no longer on pypi
|
||||
# DEPRECATED: support for python 2 in UE4 is dropped.
|
||||
python_path = None
|
||||
if int(ue_version.split(".")[0]) == 4 and \
|
||||
int(ue_version.split(".")[1]) < 25:
|
||||
if platform.system().lower() == "windows":
|
||||
python_path = os.path.join(engine_path, "Engine", "Binaries",
|
||||
"ThirdParty", "Python", "Win64",
|
||||
"python.exe")
|
||||
|
||||
if platform.system().lower() == "linux":
|
||||
python_path = os.path.join(engine_path, "Engine", "Binaries",
|
||||
"ThirdParty", "Python", "Linux",
|
||||
"bin", "python")
|
||||
|
||||
if platform.system().lower() == "darwin":
|
||||
python_path = os.path.join(engine_path, "Engine", "Binaries",
|
||||
"ThirdParty", "Python", "Mac",
|
||||
"bin", "python")
|
||||
|
||||
if python_path:
|
||||
subprocess.run([python_path, "-m",
|
||||
"pip", "install", "pyside"])
|
||||
else:
|
||||
raise NotImplemented("Unsupported platform")
|
||||
|
||||
if dev_mode or preset["dev_mode"]:
|
||||
_prepare_cpp_project(project_file, engine_path)
|
||||
|
||||
|
||||
def _prepare_cpp_project(project_file: str, engine_path: str) -> None:
|
||||
"""
|
||||
"""Prepare CPP Unreal Project.
|
||||
|
||||
This function will add source files needed for project to be
|
||||
rebuild along with the avalon integration plugin.
|
||||
|
||||
|
|
@ -273,12 +321,12 @@ def _prepare_cpp_project(project_file: str, engine_path: str) -> None:
|
|||
by some generator. This needs more research as manually writing
|
||||
those files is rather hackish. :skull_and_crossbones:
|
||||
|
||||
:param project_file: path to .uproject file
|
||||
:type project_file: str
|
||||
:param engine_path: path to unreal engine associated with project
|
||||
:type engine_path: str
|
||||
"""
|
||||
|
||||
Args:
|
||||
project_file (str): Path to .uproject file.
|
||||
engine_path (str): Path to unreal engine associated with project.
|
||||
|
||||
"""
|
||||
project_name = os.path.splitext(os.path.basename(project_file))[0]
|
||||
project_dir = os.path.dirname(project_file)
|
||||
targets_dir = os.path.join(project_dir, "Source")
|
||||
|
|
@ -388,19 +436,23 @@ class {1}_API A{0}GameModeBase : public AGameModeBase
|
|||
sources_dir, f"{project_name}GameModeBase.h"), mode="w") as f:
|
||||
f.write(game_mode_h)
|
||||
|
||||
u_build_tool = (f"{engine_path}/Engine/Binaries/DotNET/"
|
||||
"UnrealBuildTool.exe")
|
||||
u_header_tool = None
|
||||
|
||||
if platform.system().lower() == "windows":
|
||||
u_build_tool = (f"{engine_path}/Engine/Binaries/DotNET/"
|
||||
"UnrealBuildTool.exe")
|
||||
u_header_tool = (f"{engine_path}/Engine/Binaries/Win64/"
|
||||
f"UnrealHeaderTool.exe")
|
||||
elif platform.system().lower() == "linux":
|
||||
# WARNING: there is no UnrealBuildTool on linux?
|
||||
u_build_tool = ""
|
||||
u_header_tool = ""
|
||||
u_header_tool = (f"{engine_path}/Engine/Binaries/Linux/"
|
||||
f"UnrealHeaderTool")
|
||||
elif platform.system().lower() == "darwin":
|
||||
# WARNING: there is no UnrealBuildTool on Mac?
|
||||
u_build_tool = ""
|
||||
u_header_tool = ""
|
||||
# we need to test this out
|
||||
u_header_tool = (f"{engine_path}/Engine/Binaries/Mac/"
|
||||
f"UnrealHeaderTool")
|
||||
|
||||
if not u_header_tool:
|
||||
raise NotImplemented("Unsupported platform")
|
||||
|
||||
u_build_tool = u_build_tool.replace("\\", "/")
|
||||
u_header_tool = u_header_tool.replace("\\", "/")
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Hook to launch Unreal and prepare projects."""
|
||||
import os
|
||||
|
||||
from openpype.lib import (
|
||||
|
|
@ -8,19 +10,21 @@ from openpype.hosts.unreal.api import lib as unreal_lib
|
|||
|
||||
|
||||
class UnrealPrelaunchHook(PreLaunchHook):
|
||||
"""
|
||||
"""Hook to handle launching Unreal.
|
||||
|
||||
This hook will check if current workfile path has Unreal
|
||||
project inside. IF not, it initialize it and finally it pass
|
||||
path to the project by environment variable to Unreal launcher
|
||||
shell script.
|
||||
"""
|
||||
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
self.signature = "( {} )".format(self.__class__.__name__)
|
||||
|
||||
def execute(self):
|
||||
"""Hook entry method."""
|
||||
asset_name = self.data["asset_name"]
|
||||
task_name = self.data["task_name"]
|
||||
workdir = self.launch_context.env["AVALON_WORKDIR"]
|
||||
|
|
@ -52,7 +56,7 @@ class UnrealPrelaunchHook(PreLaunchHook):
|
|||
f"[ {engine_version} ]"
|
||||
))
|
||||
|
||||
detected = unreal_lib.get_engine_versions()
|
||||
detected = unreal_lib.get_engine_versions(self.launch_context.env)
|
||||
detected_str = ', '.join(detected.keys()) or 'none'
|
||||
self.log.info((
|
||||
f"{self.signature} detected UE4 versions: "
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue