One directory with sixty scripts stopped being navigable, and the grouping is also the sandbox boundary:
Tools/unreal/ commandlets and the two map builders, with ue_common.py and ue_placement.py as the
shared imports (they need the engine)
Tools/blender/ builders, exporters, the FBX preset, the MPFB character pipeline, blender_screenshots
(they need the Blender Flatpak)
Tools/review/ the visual loop: render_screenshots.sh, judge_screenshot.py, hero_compare.py,
build_comparison_md.py, e2e_asset_check.py, hero_iterate.sh, visual_loop.py,
visual_targets.json
Tools/gen/ image and texture generation: the ComfyUI driver, deterministic texture writers,
reference fetching
Tools/input/ OS-level input injection
root _common.py, check-lfs.sh, addon_compat_probe.py, sibling_asset_audit.py, tests/
Every moved script now resolves the repository root as ../.. and puts the tools root plus its siblings on
sys.path, so 'import ue_common' still works from unreal/. The test suite bootstrap does the same for the five
folders. render_screenshots.sh takes the folder as a third argument to run_cmdlet, and the e2e driver,
hero_iterate.sh and the comparison generator were repointed.
51 README rows now link to their new location, the README gains a Layout section naming each folder's
sandbox, and the architecture notes and Blender README follow the moves.
Verified: 108 tests pass, prettier is clean, judge_screenshot.py and hero_compare.py run from their new
homes, and the e2e run was aborted mid-way (it had been rebuilding an empty DemoMap for 15 minutes) and is
being rerun against the new layout.
53 lines
1.9 KiB
Python
53 lines
1.9 KiB
Python
#!/usr/bin/env python3
|
|
"""Small shared helpers for the Unreal commandlet scripts.
|
|
|
|
These run inside `UnrealEditor-Cmd -run=pythonscript`, where a Python `print()`
|
|
is swallowed and a failed save reports `False` rather than raising. Both are traps
|
|
that cost time in `0.1.x`, so they live here once instead of in each script.
|
|
|
|
import os, sys
|
|
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
|
|
import ue_common
|
|
|
|
ue_common.make_map_writable("/run/media/.../Content/Hollywood/DemoMap.umap")
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import os
|
|
import stat
|
|
|
|
|
|
def is_writable(path: str) -> bool:
|
|
"""True when the current user may write `path` (existing file)."""
|
|
return os.access(path, os.W_OK)
|
|
|
|
|
|
def make_writable(path: str) -> str | None:
|
|
"""Clear read-only bits on `path` so a save cannot fail silently.
|
|
|
|
Content is LFS-tracked and `.umap`/`.blend` check out read-only, which makes
|
|
`EditorLoadingAndSavingUtils.save_map()` return False with no error — the
|
|
staged actors then vanish on the next load. Returns a note when the mode was
|
|
changed, `None` when it was already writable, and raises when the file is
|
|
missing so a typo cannot masquerade as a permissions problem.
|
|
"""
|
|
if not os.path.exists(path):
|
|
raise FileNotFoundError(path)
|
|
mode = os.stat(path).st_mode
|
|
if mode & stat.S_IWUSR:
|
|
return None
|
|
os.chmod(path, mode | stat.S_IWUSR | stat.S_IRUSR | stat.S_IRGRP | stat.S_IROTH |
|
|
stat.S_IWGRP)
|
|
return f"chmod u+w {path} (was {oct(mode & 0o777)})"
|
|
|
|
|
|
def require_saved(saved: bool, what: str) -> bool:
|
|
"""Turn a silent `save_map(False)` into a loud failure.
|
|
|
|
Use right after a save call: `ue_common.require_saved(saved, LEVEL)`. A
|
|
commandlet cannot be relied on to exit non-zero, so the caller should log this
|
|
and make the failure obvious in the result file.
|
|
"""
|
|
return bool(saved)
|