O7d : collections thematiques de l'editeur (Tools/BuildCollections.py)

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>
This commit is contained in:
edujiro5972 2026-10-02 14:11:56 +02:00
parent f241099388
commit 2a1eedecef
5 changed files with 153 additions and 0 deletions

View file

@ -66,6 +66,11 @@ données, et les mods en dépendent.
`Content/Modules` cite aussi 17 chemins complets de `New2` et `New3`.
Pour chercher par thème plutôt que par dossier, utiliser les collections `LifePlay_*` du Content Browser (panneau
Collections) : animations, vêtements, meubles, cheveux, icônes, villes, sons… La collection rouge
`LifePlay_Chemins_Fixes` liste les assets à ne jamais déplacer. Après ajout d'assets, les régénérer avec
`Tools/BuildCollections.py` (lot O7d, `11_Audit_Optimisation.md` §16.5).
`Content/Saves` reçoit les sauvegardes des joueurs : il doit exister, mais reste vide dans le dépôt et l'archive. Les
parties de test de l'éditeur ont été déplacées vers `D:\Game\LifePlay_Backup_5.5\O7\Content_Saves` (lot O7a).
## 3. Compiler

View file

@ -50,6 +50,10 @@ git status; git log --oneline -5; git tag --list '*-valide' # où en est-on
& $Cmd $Proj -run=LPCheckStreetMap -CSV=Docs/Migration/etat/streetmap.csv -unattended -nopause
& $Cmd $Proj -run=Cook -TargetPlatform=Windows -unattended # T-LOAD-05
# Collections thématiques de l'éditeur (O7d), essai à blanc puis écriture
& $Cmd $Proj -run=pythonscript "-script=$($Proj | Split-Path)/Tools/BuildCollections.py -dryrun" -unattended -nopause
& $Cmd $Proj -run=pythonscript "-script=$($Proj | Split-Path)/Tools/BuildCollections.py" -unattended -nopause
# Diff indépendant d'un rebranchement de nœuds
python Tools\DiffRemplacementNoeuds.py <avant.txt> <apres.txt> CX-B12
```

View file

@ -470,6 +470,32 @@ de modding.
Aucun asset déplacé, aucun Blueprint modifié : ni build ni test en jeu nécessaires.
### 16.5 O7d : collections thématiques (2026-10-02)
Pour retrouver les assets par thème sans renommer de dossier, `Tools/BuildCollections.py` crée 13 collections statiques
de l'éditeur (`Content/Collections/LifePlay*.collection`), rangées sous une collection parente `LifePlay` :
| Collection | Assets | Contenu |
|---|---:|---|
| `LifePlay_Animations` | 1 709 | toutes les animations (`Daz/Anims`, `New2/Anims2`, `New2/Daily`, `New3/Anims`, `New4/Anims`…) |
| `LifePlay_Vetements` | 2 926 | `Clothes4` et ses matériaux, `Daz/Clothes`, `New3/Clothes`, `New2/BDSM` |
| `LifePlay_Meubles` | 803 | `New2/Furniture` |
| `LifePlay_Cheveux` | 776 | tous les dossiers `Hairs` (dont pilosité Daz) |
| `LifePlay_Icones_UI` | 796 | icônes et fonds d'écran |
| `LifePlay_Villes` | 594 | `StreetMap` et `New2/Buildings` |
| `LifePlay_Sons` | 419 | sons |
| `LifePlay_Corps` | 236 | corps Daz, squelettes, visages, créatures |
| `LifePlay_Gameplay` | 169 | Blueprints, widgets, structures, carte, rendu |
| `LifePlay_Tatouages` | 71 | `New3/Tattoos` |
| `LifePlay_Divers` | 62 | `Misc`, particules, accessoires d'animation |
| `LifePlay_Chemins_Fixes` (rouge) | 4 984 | **transversale** : assets des dossiers à chemin écrit en dur (§16.2), à ne jamais déplacer |
Chaque asset du projet (8 561, hors redirecteur et données de carte) est dans exactement une collection thématique.
Les collections ne sont que des listes : aucun asset n'est modifié, rien n'est cuit ni livré dans le jeu. Le dossier
`Content/Collections` est ignoré par git (comme dans le dépôt d'origine) : le script est la source et les régénère à
l'identique (GUID fixes). Contrôles : relecture des 13 fichiers, 13 545 chemins, 0 inconnu du registre ; l'éditeur les
charge (`LogCollectionManager: Loaded 13 collections`).
## 17. Couche de données : stratégie (2026-10-02)
Proposition utilisateur : convertir les fichiers texte de `Modules` en fichiers JSON groupés par type, avec un calque de

View file

@ -925,3 +925,4 @@ Validation L7c/L7d (2026-10-02, parc, même cadrage) : la nuit reste claire (sol
| CHG-078 | 2026-10-02 | DATA4b | nouveaux `Source/LifePlay/LPDataCompat.h/.cpp` (`WhatWhereToArray` en C++, vérification `-LPVerifyWhatWhere` contre la fonction Blueprint d'origine) et `Source/LifePlayEditorTools/LPFixWhatWhereCommandlet.h/.cpp` ; `Content/BPs/GI.uasset` : 2 appels de l'EventGraph rebranchés (diff : 22 lignes, uniquement les 2 nœuds et les liaisons de leurs voisins) | `WhatWhereToArray` = 94 % de `PopulateScenes` (3,4 s par passage) ; premier essai refusé par le contrôle intégré (broche `Target` non exclue), rien sauvegardé, commandlet corrigée ; build `LifePlay_Build_5.5_DATA4b` ; validé en jeu le 2026-10-02 : plus de 3 000 appels vérifiés, 0 écart ; `PopulateRaws` 12,6 s → 1,26 s | `Tools\Restore.ps1 -Lot DATA4b` |
| CHG-079 | 2026-10-02 | V1.3 | `README.md` (section V1.3, branche `ue5.5-v1.3`) ; tag annoté `v1.3` ; build `LifePlay_Build_5.5_Shipping_V1.3` validé en jeu ; archive `LifePlay_Revival_V1.3_UE5.5_Full_Source_Code.7z` (4 945 770 158 octets, 11 583 fichiers, SHA-256 `24e7d58f759ca8998fe0d7ddc8c6a4c30262978806903e6d3deb68b37c46512a`, contrôles : test 7-Zip, aucun secret ni outil local, 14 fichiers identiques au tag) | publication de la V1.3 (couche de données, `WhatWhereToArray` en C++, ERR-039) | — |
| CHG-080 | 2026-10-02 | V1.3 | `README.md` : V1.3 en téléchargement principal (lien Mega), V1.2 et V1.1 en versions précédentes ; lien de la V1 retiré (archive supprimée de Mega faute d'espace, état git conservé par le tag `v1.0`) | lien V1.3 reçu | — |
| CHG-081 | 2026-10-02 | O7d | nouveau `Tools/BuildCollections.py` ; 13 collections statiques `Content/Collections/LifePlay*.collection` (hors dépôt, régénérables) ; `11_Audit_Optimisation.md` §16.5, `08_Guide_Developpeur.md` §2.1, `09_Commandes_Utiles.md` §3 | suite d'O7 (choix utilisateur) : regrouper les 8 561 assets par thème dans l'éditeur sans renommer de dossier ; collection transversale `LifePlay_Chemins_Fixes` (4 984 assets à ne pas déplacer) ; 13 545 chemins, 0 inconnu, chargées par l'éditeur ; aucun asset modifié, pas de build | supprimer `Content/Collections/LifePlay*.collection` (`git checkout O7d-avant` pour le reste) |

117
Tools/BuildCollections.py Normal file
View file

@ -0,0 +1,117 @@
# 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))