From 2a1eedecef9dc65f52622c4fe43886b69a35ec4c Mon Sep 17 00:00:00 2001 From: edujiro5972 <336072401+edujiro5972@users.noreply.github.com> Date: Fri, 2 Oct 2026 14:11:56 +0200 Subject: [PATCH] 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 --- Docs/Migration/08_Guide_Developpeur.md | 5 + Docs/Migration/09_Commandes_Utiles.md | 4 + Docs/Migration/11_Audit_Optimisation.md | 26 ++++++ Docs/Migration/Journal_Modifications.md | 1 + Tools/BuildCollections.py | 117 ++++++++++++++++++++++++ 5 files changed, 153 insertions(+) create mode 100644 Tools/BuildCollections.py diff --git a/Docs/Migration/08_Guide_Developpeur.md b/Docs/Migration/08_Guide_Developpeur.md index d47231f..098b2dd 100644 --- a/Docs/Migration/08_Guide_Developpeur.md +++ b/Docs/Migration/08_Guide_Developpeur.md @@ -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 diff --git a/Docs/Migration/09_Commandes_Utiles.md b/Docs/Migration/09_Commandes_Utiles.md index 738999e..1ce8130 100644 --- a/Docs/Migration/09_Commandes_Utiles.md +++ b/Docs/Migration/09_Commandes_Utiles.md @@ -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 CX-B12 ``` diff --git a/Docs/Migration/11_Audit_Optimisation.md b/Docs/Migration/11_Audit_Optimisation.md index c9ea8d5..2f2a1d7 100644 --- a/Docs/Migration/11_Audit_Optimisation.md +++ b/Docs/Migration/11_Audit_Optimisation.md @@ -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 diff --git a/Docs/Migration/Journal_Modifications.md b/Docs/Migration/Journal_Modifications.md index 69f1fab..0a55425 100644 --- a/Docs/Migration/Journal_Modifications.md +++ b/Docs/Migration/Journal_Modifications.md @@ -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) | diff --git a/Tools/BuildCollections.py b/Tools/BuildCollections.py new file mode 100644 index 0000000..c2cc848 --- /dev/null +++ b/Tools/BuildCollections.py @@ -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 -run=pythonscript -script="/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))