"""Rebuild both Unreal maps from an empty level, deleting whatever was there before. /UnrealEditor-Cmd -run=pythonscript \ -script=/Tools/unreal/ue_recreate_maps.py -nullrhi -unattended -nopause -stdout Why: `Tools/unreal/ue_build_demo_map.py` and `Tools/unreal/ue_build_review_map.py` load the existing map and destroy the actors they know about. Anything else that accumulated - an actor from an experiment, a material override, a stale world setting - survives every rebuild, which is how a stale road mesh and a duplicated vehicle actor both got saved back into the map. Deleting the `.umap` and creating an empty level makes every run start from the same place. The umap files are LFS-tracked and check out read-only, so chmod comes first; and a level cannot be deleted while it is the one the editor has open, so a scratch level is created to release it. Result: /tmp/gigline/recreate-maps.txt """ import os import unreal MAPS = ("/Game/Hollywood/DemoMap", "/Game/Test/ReviewMap") SCRATCH = "/Game/Test/_RecreateScratch" RESULT = "/tmp/gigline/recreate-maps.txt" lines = [] def log(message): lines.append(str(message)) unreal.log(str(message)) def make_writable(path): """A .umap under Content/ that is read-only cannot be deleted; LFS checks it out that way.""" disk = os.path.join(unreal.Paths.project_content_dir(), path.replace("/Game/", "") + ".umap") try: mode = os.stat(disk).st_mode & 0o777 if not mode & 0o200: os.chmod(disk, 0o644) log(f"{path}: chmod u+w ({oct(mode)} -> 0o644)") except FileNotFoundError: pass def main(): les = unreal.get_editor_subsystem(unreal.LevelEditorSubsystem) scratch_made = False for path in MAPS: make_writable(path) current = unreal.EditorLevelLibrary.get_editor_world() current_path = current.get_path_name().split(".")[0] if current else "" if path == current_path: # The editor cannot delete the map it has open: park it on a scratch level first. if not scratch_made: les.new_level(SCRATCH) scratch_made = True else: les.load_level(SCRATCH) log(f"{path}: released by switching to {SCRATCH}") if unreal.EditorAssetLibrary.does_asset_exist(path): if unreal.EditorAssetLibrary.delete_asset(path): log(f"{path}: deleted") else: log(f"{path}: could not delete") if les.new_level(path): log(f"{path}: created empty") else: log(f"{path}: FAILED to create") if unreal.EditorAssetLibrary.does_asset_exist(SCRATCH): unreal.EditorAssetLibrary.delete_asset(SCRATCH) ok = all(f"{path}: created empty" in line for path in MAPS for line in lines) lines.append(f"VERDICT: {'PASS' if ok else 'FAIL'}") log(f"recreate {'ok' if ok else 'FAILED'}") try: main() except Exception as exc: # noqa: BLE001 import traceback log(f"FAILED: {type(exc).__name__}: {exc}") log(traceback.format_exc()) lines.append("VERDICT: FAIL") with open(RESULT, "w") as handle: handle.write("\n".join(lines) + "\n")