88 lines
4.1 KiB
Python
88 lines
4.1 KiB
Python
"""Puts the current sea material on a level that already exists, and saves it.
|
|
|
|
UnrealEditor-Cmd.exe <abs>/Salty.uproject -run=pythonscript \
|
|
-script="<abs>/Scripts/Authoring/fix_sea_material.py --level /Game/Maps/L_World" \
|
|
-AllowCommandletRendering -unattended -nopause -abslog=<abs>/Saved/Logs/sea.log
|
|
|
|
Why this is a script of its own rather than a flag on the world builders. `ensure_dressing` spawns the sea
|
|
only when the level has none, which is the right rule - a rerun must not leave a second sun behind it - and
|
|
the consequence is that changing `rocky_meadows.SEA_GREY` cannot reach a world that already has a sea. The
|
|
alternative to this file is rebuilding ninety-eight landscapes to change one material reference, which is
|
|
about forty minutes to avoid writing twenty lines.
|
|
|
|
It changes exactly one thing: the material on every actor labelled `World_Sea_Proto`. The plane's size and
|
|
height are a product of the manifest and are already correct, so they are not touched, and neither is
|
|
anything else in the level.
|
|
|
|
The arguments go *inside* the quoted `-script=` value. UPythonScriptCommandlet::Main reads `-Script=` as one
|
|
string and hands it to the Python plugin, which splits it into a filename and arguments; anything put after
|
|
it on the command line is parsed by the engine and never reaches Python, so a level named the wrong way round
|
|
is silently ignored and the default is used instead.
|
|
"""
|
|
import os
|
|
import sys
|
|
|
|
import unreal
|
|
|
|
HERE = os.path.dirname(os.path.abspath(__file__))
|
|
sys.path.insert(0, HERE)
|
|
sys.path.insert(0, os.path.join(HERE, ".pylib"))
|
|
import rocky_meadows # noqa: E402
|
|
|
|
DEFAULT_LEVELS = ("/Game/Maps/L_World",)
|
|
|
|
level_subsystem = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem)
|
|
|
|
|
|
def parse_levels():
|
|
"""`--level A --level B`, or every level in DEFAULT_LEVELS. Read out of sys.argv because the engine puts
|
|
its own arguments there too."""
|
|
argv = sys.argv[1:]
|
|
levels = [argv[i + 1] for i, item in enumerate(argv) if item == "--level" and i + 1 < len(argv)]
|
|
return levels or list(DEFAULT_LEVELS)
|
|
|
|
|
|
def level_file(level_path):
|
|
content = os.path.abspath(unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_content_dir()))
|
|
return os.path.join(content, level_path.replace("/Game/", "", 1) + ".umap")
|
|
|
|
|
|
def require_writable(level_path):
|
|
"""The same probe create_region_world.py makes, and for the same reason: the save is the last thing this
|
|
does, an editor holding the level makes it fail with a sharing violation, and finding that out at the end
|
|
is finding it out too late. `r+b` asks the operating system what a save asks and writes nothing."""
|
|
path = level_file(level_path)
|
|
if not os.path.exists(path):
|
|
raise RuntimeError(f"{path} does not exist; build the level before repairing its sea")
|
|
try:
|
|
open(path, "r+b").close()
|
|
except OSError as error:
|
|
raise RuntimeError(
|
|
f"{path} is locked by another process ({error.__class__.__name__}): an editor with {level_path} "
|
|
f"open, or playing it, holds it. Load another level in the editor and run again."
|
|
) from error
|
|
|
|
|
|
def main():
|
|
levels = parse_levels()
|
|
unreal.log(f"sea material: {'grey placeholder' if rocky_meadows.SEA_GREY else 'the engine water material'}")
|
|
for level_path in levels:
|
|
require_writable(level_path)
|
|
for level_path in levels:
|
|
if not level_subsystem.load_level(level_path):
|
|
raise RuntimeError(f"could not load {level_path}")
|
|
world = unreal.get_editor_subsystem(unreal.UnrealEditorSubsystem).get_editor_world()
|
|
if not world.get_path_name().startswith(level_path):
|
|
raise RuntimeError(f"the editor world is {world.get_path_name()}, not {level_path}; "
|
|
f"a save would go nowhere")
|
|
changed = rocky_meadows.repair_sea(None)
|
|
if not changed:
|
|
continue
|
|
if not level_subsystem.save_current_level():
|
|
raise RuntimeError(f"saving {level_path} failed; see the log above")
|
|
unreal.EditorLoadingAndSavingUtils.save_dirty_packages(True, True)
|
|
unreal.log(f"{level_path} saved: {changed} sea plane(s) repainted")
|
|
|
|
|
|
main()
|