import logging
import os
import stat
from pathlib import Path
from cgl.core.config.query import AlchemyConfigManager
from cgl.core.utils.general import cgl_copy, load_json, save_json
from cgl.core.utils.general import cgl_execute
from cgl.core.utils.read_write import unzip
from cgl.plugins.perforce.utils.workspace import get_workspace_path
from cgl.plugins.unreal.utils.engine import (
get_unreal_config_path,
get_unreal_exe_path,
get_unreal_python_path,
)
from cgl.plugins.unreal.utils.uproject import get_uproject_path
from cgl.plugins.unreal.utils.world import are_plugins_enabled
CFG = AlchemyConfigManager()
[docs]
def unreal_pip_install():
unreal_python = get_unreal_python_path()
code_root = CFG.get_code_root()
requirements_path = os.path.join(code_root, "requirements.txt")
if not os.path.exists(requirements_path):
requirements_path = os.path.join(code_root, "requirements", "requirements.txt")
os.chdir(code_root)
command = f'"{unreal_python}" -m pip install -r {requirements_path}'
os.system(command)
[docs]
def remove_read_only_attribute(file_path):
"""
Removes read only attribute from file
Args:
file_path (str): Absolute file path to file
"""
os.chmod(file_path, stat.S_IWRITE)
[docs]
def has_python_settings_string(file_lines):
"""
Checks to see if the project config file has registered python settings already
Args:
file_lines (str): Lines from the config txt file
Returns:
True if there already are python settings, False if no python settings
"""
for line in file_lines:
if line.strip() == "[/Script/PythonScriptPlugin.PythonScriptPluginSettings]":
return True
return False
[docs]
def get_enabled_plugins(json_obj):
"""
Get list of plugins
Args:
json_obj (arr): json object from uproject file
Returns:
List of enabled plugins
"""
enabled_list = []
try:
for plugin_obj in json_obj["Plugins"]:
plugin_name = plugin_obj["Name"]
enabled_list.append(plugin_name)
except KeyError:
print("Plugins Not Found in .uproject File")
return None
return enabled_list
[docs]
def enable_plugins(project, world):
"""
Enables the python plugins for the uproject asset being opened
Args:
project (str): Project name
world (str): World name
"""
unzip_storyboard_plugin()
installed_plugins = install_marketplace_plugins()
uproject_path = get_uproject_path(project=project, world=world)
remove_read_only_attribute(uproject_path)
plugins_to_enable = (
CFG.project_config["unreal_info"]["built_in_plugins"] + installed_plugins
)
json_obj = load_json(uproject_path)
enabled_plugins = get_enabled_plugins(json_obj)
if are_plugins_enabled(json_obj):
plugins_list = json_obj["Plugins"]
else:
plugins_list = []
for plugin_name in plugins_to_enable:
if plugin_name not in enabled_plugins:
plugin_dict = {"Name": plugin_name, "Enabled": True}
plugins_list.append(plugin_dict)
json_obj["Plugins"] = plugins_list
save_json(uproject_path, json_obj)
[docs]
def install_marketplace_plugins():
cfg = CFG
storyboard_zip_path = ""
market_place_plugin_list = cfg.project_config["unreal_info"]["marketplace_plugins"]
installed_plugins = []
for json_dict in market_place_plugin_list:
plugin_name = json_dict["name"]
plugin_path = json_dict["network_path"]
if not os.path.exists(plugin_path):
logging.info(
f"Could Not Load Plugin: {plugin_name}\nPath Does not Exist: {plugin_path}"
)
continue
epic_games_dir = os.path.join(os.getenv("ProgramFiles"), "Epic Games")
for dir_name in os.listdir(epic_games_dir):
if "5.1" or "5.0" in dir_name:
marketplace_plugin_path = os.path.join(
epic_games_dir, dir_name, "Engine", "Plugins", "Marketplace"
)
if not os.path.exists(marketplace_plugin_path):
os.mkdir(marketplace_plugin_path)
dest_path = os.path.join(marketplace_plugin_path, plugin_name)
if not os.path.exists(dest_path):
if os.path.isfile(plugin_path):
if os.path.splitext(marketplace_plugin_path)[1] == ".zip":
unzip(
zipped_file=storyboard_zip_path, destination=plugin_path
)
installed_plugins.append(plugin_name)
else:
cgl_copy(
source=plugin_path,
destination=dest_path,
dest_is_folder=True,
)
installed_plugins.append(plugin_name)
else:
installed_plugins.append(plugin_name)
return installed_plugins
[docs]
def unzip_storyboard_plugin():
alc_code_root = CFG.get_code_root()
if not alc_code_root:
logging.error("ALC ROOT not set as environment variable")
return False
else:
storyboard_zip_path = os.path.join(
alc_code_root,
"cgl",
"plugins",
"unreal_engine",
"resources",
"StoryBoardHelpers-1.0-windows.zip",
)
epic_games_dir = os.path.join(os.getenv("ProgramFiles"), "Epic Games")
for dir_name in os.listdir(epic_games_dir):
if "5.1" or "5.0" in dir_name:
plugin_path = os.path.join(
epic_games_dir, dir_name, "Engine", "Plugins"
)
if not os.path.exists(os.path.join(plugin_path, "StoryBoardHelpers")):
unzip(zipped_file=storyboard_zip_path, destination=plugin_path)
[docs]
def set_engine_settings(company, project, world):
workspace_path = get_workspace_path(company, project, world)
settings_file_path = os.path.join(workspace_path, "Config", "DefaultEngine.ini")
if os.path.exists(settings_file_path):
remove_read_only_attribute(settings_file_path)
lines = open(settings_file_path, "r").readlines()
if not has_engine_settings_string(lines):
engine_string = (
"\n[/Script/Engine.PhysicsSettings]\nbSupportUVFromHitResults=True"
)
with open(settings_file_path, "a+") as f:
f.write(engine_string)
f.close()
[docs]
def has_engine_settings_string(file_lines):
for line in file_lines:
if line.strip() == "[/Script/Engine.PhysicsSettings]":
return True
return False
[docs]
def engine_setup():
unreal_pip_install()
[docs]
def location_setup(company, project, world):
configure_unreal_python_env(company, project, world)
[docs]
def open_project_in_unreal(
company: str, project: str, world: str, *, new_console: bool = False
):
"""
Launch Unreal Editor for the given .uproject in a *detached* process.
- Detaches from the current (Alchemy) process group so closing Alchemy won't kill UE.
- Avoids shell=True (safer + faster quoting).
- On Windows: choose between a new visible console window (new_console=True) or no console.
Returns (pid) on success; raises RuntimeError on failure.
"""
uproject_path = Path(get_uproject_path(company, project, world=world))
unreal_exe = Path(get_unreal_exe_path())
if not unreal_exe or not unreal_exe.exists():
raise RuntimeError(f"Unreal executable not found: {unreal_exe!s}")
if not uproject_path or not uproject_path.exists():
raise RuntimeError(f".uproject not found: {uproject_path!s}")
cmd = [str(unreal_exe), str(uproject_path)]
proc_info = cgl_execute(
command=cmd,
return_output=False, # <- non-blocking!
new_window=new_console,
detach=True,
working_directory=uproject_path.parent,
)
return proc_info
if __name__ == "__main__":
open_project_in_unreal("jhcs", "ttas", "test")