13 collections statiques LifePlay_* (8 561 assets par theme) et une collection transversale LifePlay_Chemins_Fixes ; aucun asset deplace ni modifie. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
117 lines
6.3 KiB
Python
117 lines
6.3 KiB
Python
# Lot O7d : collections thematiques de l'editeur (Docs/Migration/11_Audit_Optimisation.md §16.5).
|
|
#
|
|
# Les dossiers de Content/Heavy (New, New2...) ne peuvent pas etre renommes : le jeu et les mods construisent des chemins
|
|
# a partir de ces noms (§16.2). Ce script regroupe les assets par theme dans des collections statiques de l'editeur,
|
|
# sans deplacer ni modifier aucun asset. Les collections sont des fichiers texte (Content/Collections/*.collection,
|
|
# ignores par git comme dans le depot d'origine) : ce script est leur source et les regenere a l'identique (GUID fixes).
|
|
#
|
|
# Usage :
|
|
# UnrealEditor-Cmd.exe <projet> -run=pythonscript -script="<projet>/Tools/BuildCollections.py" -unattended -nopause -nosplash
|
|
# Ajouter " -dryrun" dans -script="..." pour afficher les comptes sans ecrire.
|
|
# Sortie : Content/Collections/LifePlay*.collection ; resume dans le journal (LogPython).
|
|
|
|
import os
|
|
import sys
|
|
import uuid
|
|
import unreal
|
|
|
|
DRY_RUN = "-dryrun" in [a.lower() for a in sys.argv[1:]]
|
|
NAMESPACE = uuid.UUID("6c1f0e2a-7b1d-4f0e-9a51-0f7d2c4b5e10")
|
|
|
|
# Prefixes ecrits en texte dans les Blueprints et chemins complets cites par Content/Modules (§16.2).
|
|
FIXED_PREFIXES = [
|
|
"/game/heavy/new2/furniture/", "/game/heavy/clothes4/", "/game/heavy/streetmap/", "/game/heavy/new3/tattoos/",
|
|
"/game/heavy/new3/characters/tanlines/", "/game/heavy/daz/bridget/hairs/pubic/", "/game/heavy/daz/michael/hairs/other/",
|
|
"/game/heavy/new3/ui/", "/game/heavy/daz/clothes/", "/game/heavy/new2/bdsm/clothes/", "/game/heavy/new3/clothes/",
|
|
]
|
|
|
|
# (nom, couleur RGB ou None, regle) ; un asset va dans le premier theme dont la regle accepte (chemin minuscule, classe).
|
|
def under(*prefixes):
|
|
return lambda p, c: any(p.startswith("/game/" + x.lower() + "/") for x in prefixes)
|
|
|
|
THEMES = [
|
|
("Animations", (0.2, 0.6, 1.0), lambda p, c: c in ("AnimSequence", "AnimMontage", "BlendSpace", "AnimBlueprint", "PreviewMeshCollection")),
|
|
("Sons", (0.6, 0.4, 1.0), lambda p, c: c in ("SoundWave", "SoundCue")),
|
|
("Cheveux", (0.9, 0.7, 0.2), lambda p, c: "/hairs/" in p),
|
|
("Vetements", (0.9, 0.4, 0.6), under("Heavy/Clothes4", "Heavy/Daz/Clothes", "Heavy/New3/Clothes", "Heavy/New2/BDSM")),
|
|
("Meubles", (0.6, 0.4, 0.2), under("Heavy/New2/Furniture")),
|
|
("Villes", (0.3, 0.8, 0.3), under("Heavy/StreetMap", "Heavy/New2/Buildings")),
|
|
("Tatouages", (0.5, 0.5, 0.5), under("Heavy/New3/Tattoos")),
|
|
("Corps", (1.0, 0.8, 0.7), under("Heavy/Daz", "Heavy/Characters", "Heavy/New3/Characters", "Heavy/New2/BridgetFaces",
|
|
"Heavy/New2/Beasts", "Heavy/New3/Beasts", "Heavy/New4/Tentacles", "Characters")),
|
|
("Icones_UI", (1.0, 1.0, 0.4), under("Heavy/New3/UI", "Heavy/New", "Heavy/New4/UI", "UI/Icons", "Heavy/Backgrounds",
|
|
"UI/Backgrounds", "Heavy/New2/Backgrounds")),
|
|
("Gameplay", (0.4, 0.9, 0.9), under("BPs", "UI", "Structs", "Scenes", "Render", "Heavy/New4/Notifies")),
|
|
("Divers", None, lambda p, c: True),
|
|
]
|
|
FIXED = ("Chemins_Fixes", (1.0, 0.1, 0.1)) # transversal : les assets a ne jamais deplacer
|
|
|
|
def guid(name):
|
|
return str(uuid.uuid5(NAMESPACE, name)).upper()
|
|
|
|
def write_collection(folder, name, parent, color, paths):
|
|
lines = ["FileVersion:2", "Type:Static", "Guid:" + guid(name), "ParentGuid:" + (guid(parent) if parent else "00000000-0000-0000-0000-000000000000")]
|
|
if color:
|
|
lines.append("Color:(R={:.6f},G={:.6f},B={:.6f},A=1.000000)".format(*color))
|
|
text = "\r\n".join(lines) + "\r\n\r\n" + "".join(p + "\r\n" for p in sorted(paths))
|
|
with open(os.path.join(folder, name + ".collection"), "w", encoding="utf-8", newline="") as f:
|
|
f.write(text)
|
|
|
|
registry = unreal.AssetRegistryHelpers.get_asset_registry()
|
|
registry.search_all_assets(True)
|
|
assets = registry.get_assets_by_path("/Game", recursive=True)
|
|
|
|
members = {name: [] for name, _, _ in THEMES}
|
|
fixed = []
|
|
skipped = 0
|
|
for a in assets:
|
|
cls = str(a.asset_class_path.asset_name)
|
|
if cls in ("ObjectRedirector", "MapBuildDataRegistry"):
|
|
skipped += 1
|
|
continue
|
|
package = str(a.package_name)
|
|
lower = package.lower()
|
|
if lower.startswith("/game/collections/") or lower.startswith("/game/developers/") or lower.startswith("/game/saves/"):
|
|
skipped += 1
|
|
continue
|
|
object_path = package + "." + str(a.asset_name)
|
|
for name, _, rule in THEMES:
|
|
if rule(lower + "/", cls):
|
|
members[name].append(object_path)
|
|
break
|
|
if any((lower + "/").startswith(p) for p in FIXED_PREFIXES):
|
|
fixed.append(object_path)
|
|
|
|
total = sum(len(v) for v in members.values())
|
|
for name, _, _ in THEMES:
|
|
unreal.log_warning("BuildCollections : LifePlay_{:<12} {:>5} asset(s)".format(name, len(members[name])))
|
|
unreal.log_warning("BuildCollections : LifePlay_{:<12} {:>5} asset(s) (transversal)".format(FIXED[0], len(fixed)))
|
|
unreal.log_warning("BuildCollections : {} asset(s) classes, {} ignore(s) (redirecteurs, donnees de carte), {} au total".format(total, skipped, len(assets)))
|
|
|
|
if DRY_RUN:
|
|
unreal.log_warning("BuildCollections : -dryrun, aucune ecriture")
|
|
else:
|
|
folder = os.path.join(unreal.Paths.convert_relative_path_to_full(unreal.Paths.project_content_dir()), "Collections")
|
|
os.makedirs(folder, exist_ok=True)
|
|
write_collection(folder, "LifePlay", None, None, [])
|
|
for name, color, _ in THEMES:
|
|
write_collection(folder, "LifePlay_" + name, "LifePlay", color, members[name])
|
|
write_collection(folder, "LifePlay_" + FIXED[0], "LifePlay", FIXED[1], fixed)
|
|
|
|
# Controle : relecture des fichiers, chaque chemin doit designer un asset du registre.
|
|
known = set(str(a.package_name) + "." + str(a.asset_name) for a in assets)
|
|
bad = 0
|
|
count = 0
|
|
for file in sorted(os.listdir(folder)):
|
|
if not file.startswith("LifePlay"):
|
|
continue
|
|
with open(os.path.join(folder, file), encoding="utf-8", newline="") as f:
|
|
body = f.read().split("\r\n\r\n", 1)[1]
|
|
for line in body.split("\r\n"):
|
|
if line:
|
|
count += 1
|
|
if line not in known:
|
|
bad += 1
|
|
unreal.log_error("BuildCollections : chemin inconnu dans {} : {}".format(file, line))
|
|
unreal.log_warning("BuildCollections : {} collection(s) ecrite(s) dans {}, {} chemin(s), {} inconnu(s)".format(
|
|
len(THEMES) + 2, folder, count, bad))
|