L7b (CX-B11) : 6 noeuds GetRandomPointInNavigableRadius deprecies remplaces par LPNavigationHelpers (R-04, ERR-035)
Compilation 6 -> 0 avertissement, cuisson 3 -> 0 ; diff independant 6/6, 0 autre objet modifie. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
5739f3f4a5
commit
c7f607f094
10 changed files with 465 additions and 85 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
|
@ -142,7 +142,7 @@ Ordre indicatif, à réviser selon le registre :
|
|||
| Lot | Famille | Périmètre |
|
||||
|---|---|---|
|
||||
| L7a | R-03 refait correctement | ✅ CX-B10 (56 nœuds, 8 Blueprints), validé en jeu le 2026-09-30 |
|
||||
| L7b | Erreurs d'exécution fréquentes | `ParseIntoArray`, `getBeautyValue`, `standIdle`, `ContactMenu` |
|
||||
| L7b | R-04 : derniers nœuds dépréciés | ✅ CX-B11 (6 nœuds `GetRandomPointInNavigableRadius`, 3 Blueprints) : compilation 0 avertissement ; validation en jeu en attente |
|
||||
| L7c | Configuration | clés de nativisation, rendu 5.5 explicite (GI, ombres, RHI) |
|
||||
| L7d | Visuel | AtmosphericFog → SkyAtmosphere ; reconstruction de l'éclairage |
|
||||
| L7e | Effets | 4 systèmes Cascade → Niagara (si nécessaire) ; NvCloth → Chaos |
|
||||
|
|
@ -151,6 +151,7 @@ Ordre indicatif, à réviser selon le registre :
|
|||
| L7h | Vérification StreetMap | `LPCheckStreetMap` sur 4.27 et 5.5, comparaison des CSV (ERR-018) |
|
||||
| L7i | Variables DSL `Person` vides | ERR-030/ERR-003 : `GI::Dress` par rafales (0 à 284 par personnage selon les lieux), `ReturnStatIfNeeded` ; traceur par fonction |
|
||||
| L7j | Écran secondaire (amélioration) | ERR-031, préexistant en 4.27 : caméra `SecondScreen` sans racine ; seulement si souhaité |
|
||||
| L7k | Erreurs d'exécution fréquentes (ex-L7b) | `getBeautyValue`, `standIdle` (ERR-006), `ContactMenu` (ERR-007) ; `ParseIntoArray` déjà traité (CX-B08) |
|
||||
|
||||
## 4. Décisions
|
||||
|
||||
|
|
|
|||
|
|
@ -38,10 +38,11 @@ exporté dans `Docs/Migration/correctifs/` pour relecture.
|
|||
| CX-B03 | `LPFixZeroScaleActors -Fix -Scale=0.1` | `Scenes/CentralLondon` : `Wall.StaticMeshComponent0` | échelle X 0 → 0,1 | corps physique invalide ; `ensure InverseFast` (plantage éditeur du 2026-09-07) | 3 | **réimporté** (CHG-023) |
|
||||
| CX-B04 | `LPFixOrphanBoundEvents` | `UI/TopRight` (1), `UI/CharGen` (2) | 3 événements `On Clicked` orphelins supprimés | avertissements ; clics déjà morts | 4 | **réimporté** (CHG-024) |
|
||||
| CX-B05 | `LPFixDeprecatedNodes` (R-03) | 56 nœuds `SkeletalMesh`, 10 Blueprints | `GetSkeletalMeshAsset()` | **rejeté** : aggrave ERR-002 (16,6 → 35 `Accessed None`/perso) car les proxies sont des `USkinnedMeshComponent` | — | à refaire autrement (L7a) |
|
||||
| CX-B06 | `LPFixDeprecatedNodes` (R-04) | 6 nœuds, `AI`, `Person`, `Room` | `LPNavigationHelpers` | avertissements de dépréciation seulement | 5 | reporté (L7) |
|
||||
| CX-B06 | `LPFixDeprecatedNodes` (R-04) | 6 nœuds, `AI`, `Person`, `Room` | `LPNavigationHelpers` | avertissements de dépréciation seulement | 5 | **remplacé par CX-B11** (commandlet sans contrôle, embarquait aussi la variante R-03 rejetée) |
|
||||
| CX-B08 | `LPFixParseIntoArray` (nouveau) | 114 nœuds `ParseIntoArray`, 17 Blueprints | rebranchés sur `LPStringCompat::ParseIntoArray` (algorithme UE 4.27) | UE5 renvoie `[""]` pour une chaîne vide : cause d'ERR-027 (personnages de quête invisibles/dévêtus) | 1 | **réimporté** (CHG-029), validé en jeu |
|
||||
| CX-B09 | `LPFixButtonPadding` (nouveau) | 430 `UButton`, 46 Widget Blueprints | marges des boutons remises au défaut UE 4.27 (Normal 2,2,2,2 ; Pressed 2,3,2,1), composante par composante | le style par défaut de `UButton` est passé à 12 px horizontaux en UE5 (`FUMGCoreStyle`) ; les assets 4.27 ne stockent pas la marge par défaut : cause d'ERR-034 (barre de temps décalée) | 1 | **réimporté** (CHG-033), validé en jeu |
|
||||
| CX-B10 | `LPFixSkeletalMeshGet` (nouveau) | 56 nœuds `Get SkeletalMesh`, 8 Blueprints (`Person` 28, `GI` 17, `CharGen` 3, `Inventory` 3, `Hairstyle` 2, `CharGenSlider`, `Cursor`, `Inventory_Cloth` 1) | remplacés par `LPMeshCompat::GetSkeletalMesh(Component)` = `Cast<USkeletalMesh>(GetSkinnedAsset())`, la sémantique exacte du getter déprécié, valable pour tout `USkinnedMeshComponent` | R-03 refait (L7a) ; remplace CX-B05 rejeté | 1 | **réimporté** (CHG-036), validé en jeu |
|
||||
| CX-B11 | `LPFixNavRandomPoint` (nouveau) | 6 nœuds `GetRandomPointInNavigableRadius` (`Person` 3, `Room` 2, `AI` 1) | remplacés par `LPNavigationHelpers::GetRandomLocationInNavigableRadius` : nœud pur, mêmes broches, même corps que la fonction dépréciée (appel de `K2_GetRandomLocationInNavigableRadius`, identique en 4.27 et 5.5) ; liaisons et valeurs par défaut reportées broche par broche | R-04 (L7b) : 6 derniers avertissements de compilation ; le remplaçant moteur est impur (broches d'exécution), d'où l'enrobage pur | 1 | **réimporté** (CHG-037) ; validation en jeu en attente |
|
||||
| CX-B07 | `LPResavePackages` (nouveau) | 239 paquets UE4 restants (`UI` 147, `Structs` 57, `BPs` 22, `Render` 11, `Characters` 1, `Scenes` 1) | format UE5, contenu inchangé | EX-03 ; T-STAT-01 | 6 | **réimporté** (CHG-035), diff avant/après neutre ; validation en jeu en attente |
|
||||
|
||||
## 3. Protocole de réimport sous contrôle renforcé
|
||||
|
|
|
|||
|
|
@ -546,3 +546,25 @@ sortie normale, sauvegardes `1.sav`/`2.sav` ; tests utilisateur effectués sans
|
|||
signature et mêmes 5 sockets qu'en V1rc, volume lié aux scènes jouées (ERR-011, existe en 4.27) : non imputable à L7a.
|
||||
Fonctions de variables DSL (`getActorVar`, `clearVariable`, `CallFunction`) non touchées par L7a, en hausse par personnage
|
||||
sur cette partie : famille ERR-003, à suivre en L7i.
|
||||
|
||||
### L7b — R-04 : derniers nœuds dépréciés (2026-09-30)
|
||||
|
||||
| CHG | Date | Lot | Fichiers | Raison | Retour arrière |
|
||||
|---|---|---|---|---|---|
|
||||
| CHG-037 | 2026-09-30 | L7b (CX-B11) | `Source/LifePlayEditorTools/LPFixNavRandomPointCommandlet.h/.cpp` (nouveau) ; `Tools/DiffRemplacementNoeuds.py` (profils par lot : `L7a`, `L7b`) ; `Content/BPs/AI`, `Person`, `Room` (`etat/L7b_assets.txt`) | ERR-035 / R-04 : 6 nœuds `GetRandomPointInNavigableRadius` dépréciés | `Tools\Restore.ps1 -Lot L7b` ; `git checkout L7b-avant -- Source Tools` |
|
||||
|
||||
Choix : le remplaçant proposé par le moteur (`K2_GetRandomLocationInNavigableRadius`) est `BlueprintCallable`, alors que
|
||||
l'ancien nœud est pur ; le brancher imposerait de recâbler le flux d'exécution. On réutilise `LPNavigationHelpers`
|
||||
(enrobage pur déjà présent, CX-C09) : mêmes broches, et même corps que la fonction dépréciée en 4.27 comme en 5.5
|
||||
(simple appel de `K2_GetRandomLocationInNavigableRadius`). `LPFixDeprecatedNodes` (CX-B06) n'est pas réutilisée : elle
|
||||
ne contrôle rien et embarque aussi la variante R-03 rejetée.
|
||||
|
||||
| Contrôle | Résultat | Preuve |
|
||||
|---|---|---|
|
||||
| Simulation | 6 nœuds (`Room::moveToBedIfPossible`, `Room::getStandSpots`, `Person::standIdle` ×2, `Person::jumpOffFnt`, `AI::EventGraph`), 0 cas non pris en charge | `etat/L7b_LPFixNavRandomPoint_20260930-183406.txt` |
|
||||
| Application (contrôle intégré : liaisons et valeurs par défaut de chaque broche) | 6/6 remplacés, 3 Blueprints sauvegardés, 0 écart | `etat/L7b_LPFixNavRandomPoint_20260930-183707.txt` |
|
||||
| Diff indépendant `LPDumpAssets` (`DiffRemplacementNoeuds.py … L7b`) | 6 paires ; 7 broches identiques (liaisons et défauts, ex. `Radius` 100 / 200 / 1000 / 2000 / 100000) ; **0 autre objet modifié** ; le profil `L7a` rejoué sur les données L7a donne toujours 56/56 et 0 écart | `Saved/Logs/portage/L7b_assets_{avant,apres}.txt` |
|
||||
| `CompileAllBlueprints` | 0 erreur, **0 avertissement** (6 avant) | `etat/L7b_CompileAllBlueprints_20260930-183909.txt` |
|
||||
| Build empaquetée `LifePlay_Build_5.5_L7b` | cuisson 0 erreur, **0 avertissement** (3 avant) ; T-PKG-01 : 8561 `.uasset` | `Saved/Logs/portage/L7b_BuildCookRun.log` |
|
||||
|
||||
Validation en jeu en attente (déplacements des personnages : `standIdle`, `jumpOffFnt`, `moveToBedIfPossible`, IA).
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@
|
|||
| ERR-003 | `Attempted to access index 0 from array Float_… of length 0` dans `GI::ReturnStatIfNeeded` (variable DSL `Float` vide) | build 5.5 Development | 2 (4.27 : 0) | haute | L5 | ouvert |
|
||||
| ERR-004 | `index N from CallFunc_ParseIntoArray_ReturnValue of length N` (`GI` EventGraph) | PIE 09-28 | 6 510 | moyenne | L7b | ouvert — origine localisée (L3) : `GI.populateClothes4` appelée par `Person.ReceiveBeginPlay` |
|
||||
| ERR-005 | `getBeautyValue` : `Floats_*` index hors bornes | PIE 09-28 | 1 297 | moyenne | L7b | ouvert |
|
||||
| ERR-006 | `Accès à None` `NearVec` dans `Person::standIdle` | PIE 09-28 | 273 | moyenne | L7b | ouvert |
|
||||
| ERR-007 | `Accès à None` `ContactMenu` dans `UpdateDailyPose` | PIE 09-28 | 257 | moyenne | L7b | ouvert |
|
||||
| ERR-006 | `Accès à None` `NearVec` dans `Person::standIdle` | PIE 09-28 | 273 | moyenne | L7k | ouvert |
|
||||
| ERR-007 | `Accès à None` `ContactMenu` dans `UpdateDailyPose` | PIE 09-28 | 257 | moyenne | L7k | ouvert |
|
||||
| ERR-008 | `CallFunc_Array_Get_Item` à None dans `GI` | PIE 09-28 | 229 | moyenne | L5 | ouvert |
|
||||
| ERR-009 | `SpawnActor failed because no class was specified` | PIE 09-28 | 11 | moyenne | L6 | ouvert |
|
||||
| ERR-010 | `LogLPModdingTrace` : instrumentation de portage à chaque `LoadTxt` | tous journaux | ≈ 1 300 à 5 400 / session | faible (bruit) | L3 | clos (CHG-016) : `LogModdingExtra` en Verbose |
|
||||
|
|
@ -39,3 +39,4 @@
|
|||
| ERR-032 | Scintillement à l'approche d'un personnage | retour utilisateur 2026-09-30 | — | moyenne (visuel) | L6a | **corrigé (CHG-031)**, validé en jeu le 2026-09-30 : on peut s'approcher des PNJ sans scintillement |
|
||||
| ERR-033 | Certaines actions demandent deux clics pour démarrer | retour utilisateur 2026-09-30 | — | moyenne (ergonomie) | L6d | **non reproduit** depuis le build L6e (constat utilisateur 2026-09-30) ; aucune modification ciblée — coïncide avec CX-B09 (marges des boutons), sans lien démontré ; sous surveillance, à rouvrir s'il réapparaît |
|
||||
| ERR-034 | La barre de temps est décalée vers la droite | retour utilisateur 2026-09-30 | — | faible (visuel) | L6e | **corrigé (CHG-033, CX-B09)** — cause : marge par défaut des `UButton` passée de 2 px (UE4 `FCoreStyle`) à 12 px horizontaux (UE5 `FUMGCoreStyle`) ; 5 boutons × 20 px = 100 px de débordement ; **validé en jeu le 2026-09-30** (build L6e, confirmé par l'utilisateur) |
|
||||
| ERR-035 | 6 nœuds `GetRandomPointInNavigableRadius` dépréciés (R-04 : `Person` 3, `Room` 2, `AI` 1) | `LogBlueprint` | 6 (compilation), 3 (cuisson) | faible (dette) | L7b | **corrigé (CHG-037, CX-B11)** : remplacés par `LPNavigationHelpers::GetRandomLocationInNavigableRadius` (pur, même corps) ; `CompileAllBlueprints` 6 → **0 avertissement** ; validation en jeu en attente |
|
||||
|
|
|
|||
303
Source/LifePlayEditorTools/LPFixNavRandomPointCommandlet.cpp
Normal file
303
Source/LifePlayEditorTools/LPFixNavRandomPointCommandlet.cpp
Normal file
|
|
@ -0,0 +1,303 @@
|
|||
// Portage UE 5.5 - voir LPFixNavRandomPointCommandlet.h
|
||||
|
||||
#include "LPFixNavRandomPointCommandlet.h"
|
||||
|
||||
#include "AssetRegistry/AssetData.h"
|
||||
#include "AssetRegistry/AssetRegistryModule.h"
|
||||
#include "EdGraph/EdGraph.h"
|
||||
#include "EdGraph/EdGraphPin.h"
|
||||
#include "EdGraphSchema_K2.h"
|
||||
#include "Engine/Blueprint.h"
|
||||
#include "K2Node_CallFunction.h"
|
||||
#include "Kismet2/BlueprintEditorUtils.h"
|
||||
#include "Kismet2/KismetEditorUtilities.h"
|
||||
#include "LPNavigationHelpers.h"
|
||||
#include "Misc/PackageName.h"
|
||||
#include "NavigationSystem.h"
|
||||
#include "UObject/Package.h"
|
||||
#include "UObject/SavePackage.h"
|
||||
|
||||
DEFINE_LOG_CATEGORY_STATIC(LogLPFixNavRandomPoint, Log, All);
|
||||
|
||||
namespace
|
||||
{
|
||||
const FName OldFunctionName(TEXT("K2_GetRandomPointInNavigableRadius"));
|
||||
const FName NewFunctionName(TEXT("GetRandomLocationInNavigableRadius"));
|
||||
|
||||
/** Extremites reliees a une broche, triees : "Noeud.Broche". */
|
||||
TArray<FString> Links(const UEdGraphPin* Pin)
|
||||
{
|
||||
TArray<FString> Out;
|
||||
if (Pin)
|
||||
{
|
||||
for (const UEdGraphPin* L : Pin->LinkedTo)
|
||||
{
|
||||
Out.Add(FString::Printf(TEXT("%s.%s"),
|
||||
L && L->GetOwningNode() ? *L->GetOwningNode()->GetName() : TEXT("?"), L ? *L->PinName.ToString() : TEXT("?")));
|
||||
}
|
||||
}
|
||||
Out.Sort();
|
||||
return Out;
|
||||
}
|
||||
|
||||
/** Etat comparable d'une broche : liaisons et valeur par defaut. */
|
||||
FString PinState(const UEdGraphPin* Pin)
|
||||
{
|
||||
return FString::Printf(TEXT("[%s] defaut='%s' objet=%s texte='%s'"),
|
||||
*FString::Join(Links(Pin), TEXT(", ")), *Pin->DefaultValue,
|
||||
Pin->DefaultObject ? *Pin->DefaultObject->GetPathName() : TEXT("None"), *Pin->DefaultTextValue.ToString());
|
||||
}
|
||||
|
||||
bool IsDeprecatedCall(const UK2Node_CallFunction* Node)
|
||||
{
|
||||
if (!Node || Node->FunctionReference.GetMemberName() != OldFunctionName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
const UClass* Owner = Node->FunctionReference.GetMemberParentClass();
|
||||
return Owner && Owner->IsChildOf(UNavigationSystemV1::StaticClass());
|
||||
}
|
||||
|
||||
bool HasExecPins(const UEdGraphNode* Node)
|
||||
{
|
||||
for (const UEdGraphPin* Pin : Node->Pins)
|
||||
{
|
||||
if (Pin && Pin->PinType.PinCategory == UEdGraphSchema_K2::PC_Exec)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
ULPFixNavRandomPointCommandlet::ULPFixNavRandomPointCommandlet()
|
||||
{
|
||||
IsClient = false;
|
||||
IsServer = false;
|
||||
IsEditor = true;
|
||||
LogToConsole = true;
|
||||
}
|
||||
|
||||
int32 ULPFixNavRandomPointCommandlet::Main(const FString& Params)
|
||||
{
|
||||
const bool bDryRun = Params.Contains(TEXT("DryRun"), ESearchCase::IgnoreCase);
|
||||
|
||||
UFunction* NewFunction = ULPNavigationHelpers::StaticClass()->FindFunctionByName(NewFunctionName);
|
||||
if (!NewFunction)
|
||||
{
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT("ULPNavigationHelpers::GetRandomLocationInNavigableRadius introuvable."));
|
||||
return 1;
|
||||
}
|
||||
|
||||
UE_LOG(LogLPFixNavRandomPoint, Warning, TEXT("=== GetRandomPointInNavigableRadius (deprecie) -> LPNavigationHelpers%s ==="),
|
||||
bDryRun ? TEXT(" [DRY RUN]") : TEXT(""));
|
||||
|
||||
FAssetRegistryModule& AssetRegistryModule =
|
||||
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
|
||||
AssetRegistryModule.Get().SearchAllAssets(true);
|
||||
|
||||
TArray<FAssetData> BlueprintAssets;
|
||||
// Sous-classes incluses : couvre les UWidgetBlueprint.
|
||||
AssetRegistryModule.Get().GetAssetsByClass(UBlueprint::StaticClass()->GetClassPathName(), BlueprintAssets, true);
|
||||
|
||||
const UEdGraphSchema_K2* Schema = GetDefault<UEdGraphSchema_K2>();
|
||||
|
||||
int32 NodesFound = 0;
|
||||
int32 NodesFixed = 0;
|
||||
int32 Unhandled = 0;
|
||||
int32 BlueprintsModified = 0;
|
||||
int32 SaveFailures = 0;
|
||||
|
||||
for (const FAssetData& AssetData : BlueprintAssets)
|
||||
{
|
||||
if (!AssetData.PackageName.ToString().StartsWith(TEXT("/Game/")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
UBlueprint* Blueprint = Cast<UBlueprint>(AssetData.GetAsset());
|
||||
if (!Blueprint)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
TArray<UEdGraph*> Graphs;
|
||||
Blueprint->GetAllGraphs(Graphs);
|
||||
|
||||
int32 FixedHere = 0;
|
||||
int32 FailedHere = 0;
|
||||
|
||||
for (UEdGraph* Graph : Graphs)
|
||||
{
|
||||
if (!Graph)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// Copie : on ajoute et on detruit des noeuds pendant le parcours.
|
||||
TArray<UEdGraphNode*> Snapshot = Graph->Nodes;
|
||||
for (UEdGraphNode* GraphNode : Snapshot)
|
||||
{
|
||||
UK2Node_CallFunction* OldNode = Cast<UK2Node_CallFunction>(GraphNode);
|
||||
if (!IsDeprecatedCall(OldNode))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
++NodesFound;
|
||||
|
||||
UE_LOG(LogLPFixNavRandomPoint, Warning, TEXT(" %s :: %s -> %s (%d, %d)"),
|
||||
*Blueprint->GetName(), *Graph->GetName(), *OldNode->GetName(), OldNode->NodePosX, OldNode->NodePosY);
|
||||
|
||||
// Etat avant, par nom de broche (self exclu : cible implicite de la bibliotheque).
|
||||
TMap<FName, FString> Before;
|
||||
for (const UEdGraphPin* Pin : OldNode->Pins)
|
||||
{
|
||||
if (Pin && Pin->PinName != UEdGraphSchema_K2::PN_Self)
|
||||
{
|
||||
Before.Add(Pin->PinName, PinState(Pin));
|
||||
UE_LOG(LogLPFixNavRandomPoint, Warning, TEXT(" %s %s %s"),
|
||||
Pin->Direction == EGPD_Input ? TEXT("in ") : TEXT("out"), *Pin->PinName.ToString(), *PinState(Pin));
|
||||
}
|
||||
}
|
||||
const UEdGraphPin* OldSelf = OldNode->FindPin(UEdGraphSchema_K2::PN_Self, EGPD_Input);
|
||||
|
||||
if (HasExecPins(OldNode) || (OldSelf && OldSelf->LinkedTo.Num() > 0))
|
||||
{
|
||||
++Unhandled;
|
||||
++FailedHere;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" cas non pris en charge (%s) : noeud laisse en l'etat."),
|
||||
HasExecPins(OldNode) ? TEXT("noeud impur") : TEXT("cible self reliee"));
|
||||
continue;
|
||||
}
|
||||
if (bDryRun)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
UK2Node_CallFunction* NewNode = NewObject<UK2Node_CallFunction>(Graph);
|
||||
NewNode->SetFromFunction(NewFunction);
|
||||
NewNode->NodePosX = OldNode->NodePosX;
|
||||
NewNode->NodePosY = OldNode->NodePosY;
|
||||
NewNode->CreateNewGuid();
|
||||
NewNode->PostPlacedNewNode();
|
||||
NewNode->AllocateDefaultPins();
|
||||
Graph->Modify();
|
||||
Graph->AddNode(NewNode, false, false);
|
||||
|
||||
bool bOk = true;
|
||||
for (UEdGraphPin* OldPin : OldNode->Pins)
|
||||
{
|
||||
if (!OldPin || OldPin->PinName == UEdGraphSchema_K2::PN_Self)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
UEdGraphPin* NewPin = NewNode->FindPin(OldPin->PinName, OldPin->Direction);
|
||||
if (!NewPin)
|
||||
{
|
||||
bOk = false;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" broche %s sans equivalent."), *OldPin->PinName.ToString());
|
||||
break;
|
||||
}
|
||||
NewPin->DefaultValue = OldPin->DefaultValue;
|
||||
NewPin->DefaultObject = OldPin->DefaultObject;
|
||||
NewPin->DefaultTextValue = OldPin->DefaultTextValue;
|
||||
if (OldPin->LinkedTo.Num() > 0
|
||||
&& Schema->MovePinLinks(*OldPin, *NewPin).Response == CONNECT_RESPONSE_DISALLOW)
|
||||
{
|
||||
bOk = false;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" liaison refusee sur %s."), *OldPin->PinName.ToString());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Meme ensemble de broches, memes liaisons (les noms des voisins ne changent pas), memes valeurs.
|
||||
if (bOk)
|
||||
{
|
||||
int32 Compared = 0;
|
||||
for (const UEdGraphPin* NewPin : NewNode->Pins)
|
||||
{
|
||||
if (!NewPin || NewPin->PinName == UEdGraphSchema_K2::PN_Self)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
const FString* Expected = Before.Find(NewPin->PinName);
|
||||
if (!Expected || *Expected != PinState(NewPin))
|
||||
{
|
||||
bOk = false;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" apres : %s %s (attendu %s)"),
|
||||
*NewPin->PinName.ToString(), *PinState(NewPin), Expected ? **Expected : TEXT("<absent>"));
|
||||
}
|
||||
++Compared;
|
||||
}
|
||||
if (Compared != Before.Num())
|
||||
{
|
||||
bOk = false;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" %d broche(s) apres, %d avant."), Compared, Before.Num());
|
||||
}
|
||||
}
|
||||
|
||||
if (!bOk)
|
||||
{
|
||||
// Le Blueprint ne sera pas sauvegarde : le paquet sur disque reste intact.
|
||||
++Unhandled;
|
||||
++FailedHere;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" ecart : Blueprint non sauvegarde."));
|
||||
continue;
|
||||
}
|
||||
|
||||
OldNode->DestroyNode();
|
||||
++NodesFixed;
|
||||
++FixedHere;
|
||||
}
|
||||
}
|
||||
|
||||
if (FailedHere > 0)
|
||||
{
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" -> %s : %d noeud(s) en ecart, Blueprint NON sauvegarde."),
|
||||
*Blueprint->GetName(), FailedHere);
|
||||
continue;
|
||||
}
|
||||
if (FixedHere == 0 || bDryRun)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
++BlueprintsModified;
|
||||
FBlueprintEditorUtils::MarkBlueprintAsStructurallyModified(Blueprint);
|
||||
FKismetEditorUtilities::CompileBlueprint(Blueprint);
|
||||
|
||||
UPackage* Package = Blueprint->GetOutermost();
|
||||
FString Filename;
|
||||
if (!FPackageName::TryConvertLongPackageNameToFilename(
|
||||
Package->GetName(), Filename, FPackageName::GetAssetPackageExtension()))
|
||||
{
|
||||
++SaveFailures;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" chemin introuvable pour %s"), *Package->GetName());
|
||||
continue;
|
||||
}
|
||||
|
||||
ResetLoaders(Package);
|
||||
|
||||
FSavePackageArgs SaveArgs;
|
||||
SaveArgs.TopLevelFlags = RF_Public | RF_Standalone;
|
||||
SaveArgs.SaveFlags = SAVE_None;
|
||||
SaveArgs.bWarnOfLongFilename = true;
|
||||
// GWarn et non GError : en commandlet, GError rend toute erreur de save fatale.
|
||||
SaveArgs.Error = GWarn;
|
||||
|
||||
if (UPackage::SavePackage(Package, nullptr, *Filename, SaveArgs))
|
||||
{
|
||||
UE_LOG(LogLPFixNavRandomPoint, Warning, TEXT(" -> %s sauvegarde (%d noeud(s))."), *Package->GetName(), FixedHere);
|
||||
}
|
||||
else
|
||||
{
|
||||
++SaveFailures;
|
||||
UE_LOG(LogLPFixNavRandomPoint, Error, TEXT(" -> ECHEC de sauvegarde de %s"), *Package->GetName());
|
||||
}
|
||||
}
|
||||
|
||||
UE_LOG(LogLPFixNavRandomPoint, Warning,
|
||||
TEXT("=== Termine : %d noeud(s) trouve(s), %d remplace(s), %d Blueprint(s) modifie(s), %d non traite(s), %d echec(s) de sauvegarde. ==="),
|
||||
NodesFound, NodesFixed, BlueprintsModified, Unhandled, SaveFailures);
|
||||
|
||||
return (SaveFailures > 0 || Unhandled > 0) ? 1 : 0;
|
||||
}
|
||||
32
Source/LifePlayEditorTools/LPFixNavRandomPointCommandlet.h
Normal file
32
Source/LifePlayEditorTools/LPFixNavRandomPointCommandlet.h
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
// Portage UE 5.5 - lot L7b (R-04, CX-B11). Voir Source/LifePlay/LPNavigationHelpers.h.
|
||||
//
|
||||
// Remplace chaque appel a UNavigationSystemV1::K2_GetRandomPointInNavigableRadius (deprecie,
|
||||
// BlueprintPure) par ULPNavigationHelpers::GetRandomLocationInNavigableRadius (pur lui aussi,
|
||||
// memes broches, meme corps qu'en 4.27 et 5.5), au meme emplacement. Liaisons et valeurs par
|
||||
// defaut sont reportees broche par broche, par nom.
|
||||
//
|
||||
// Remplace la partie R-04 de LPFixDeprecatedNodes (CX-B06), qui reaiguillait sans controle et
|
||||
// embarquait aussi la variante R-03 rejetee (CX-B05).
|
||||
//
|
||||
// Controle integre : l'etat de chaque broche (liaisons, valeur par defaut) est compare avant et
|
||||
// apres. Tout ecart, broche sans equivalent, cible self reliee ou noeud impur : le noeud est laisse
|
||||
// en l'etat, compte comme non traite, et son Blueprint n'est pas sauvegarde.
|
||||
//
|
||||
// Usage :
|
||||
// UnrealEditor-Cmd.exe <projet> -run=LPFixNavRandomPoint [-DryRun]
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CoreMinimal.h"
|
||||
#include "Commandlets/Commandlet.h"
|
||||
#include "LPFixNavRandomPointCommandlet.generated.h"
|
||||
|
||||
UCLASS()
|
||||
class ULPFixNavRandomPointCommandlet : public UCommandlet
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
ULPFixNavRandomPointCommandlet();
|
||||
virtual int32 Main(const FString& Params) override;
|
||||
};
|
||||
|
|
@ -8,6 +8,17 @@ def parse(p):
|
|||
if m: key=(pkg,m.group(1)); objs[key]={'cls':m.group(2),'lines':[]}; continue
|
||||
if key: objs[key]['lines'].append(line)
|
||||
return objs
|
||||
# Profils de remplacement : selection des anciens et nouveaux noeuds, correspondance des broches.
|
||||
# Usage : DiffRemplacementNoeuds.py avant.txt apres.txt [L7a|L7b] (L7a par defaut)
|
||||
PROFILS={
|
||||
'L7a':{'old':('K2Node_VariableGet',lambda l:'Engine.SkinnedMeshComponent\'",MemberName="SkeletalMesh"' in l),
|
||||
'new':('K2Node_CallFunction',lambda l:'MemberName="GetSkeletalMesh"' in l and 'LPMeshCompat' in l),
|
||||
'pins':[('self','Component'),('SkeletalMesh','ReturnValue')]},
|
||||
'L7b':{'old':('K2Node_CallFunction',lambda l:'MemberName="K2_GetRandomPointInNavigableRadius"' in l),
|
||||
'new':('K2Node_CallFunction',lambda l:'MemberName="GetRandomLocationInNavigableRadius"' in l and 'LPNavigationHelpers' in l),
|
||||
'pins':[(x,x) for x in ('WorldContextObject','Origin','RandomLocation','Radius','NavData','FilterClass','ReturnValue')]},
|
||||
}
|
||||
PROFIL=PROFILS[sys.argv[3] if len(sys.argv)>3 else 'L7a']
|
||||
A=parse(sys.argv[1]); B=parse(sys.argv[2])
|
||||
def graph(path): return path.split(':')[-1].split('.')[0] if ':' in path else ''
|
||||
def iscopy(path):
|
||||
|
|
@ -18,8 +29,8 @@ def pos(o):
|
|||
if l.startswith(' NodePosX = '): x=l.split('= ')[1]
|
||||
if l.startswith(' NodePosY = '): y=l.split('= ')[1]
|
||||
return x,y
|
||||
old={k:v for k,v in A.items() if v['cls']=='K2Node_VariableGet' and not iscopy(k[1]) and any('Engine.SkinnedMeshComponent\'",MemberName="SkeletalMesh"' in l for l in v['lines'])}
|
||||
new={k:v for k,v in B.items() if v['cls']=='K2Node_CallFunction' and not iscopy(k[1]) and any('MemberName="GetSkeletalMesh"' in l and 'LPMeshCompat' in l for l in v['lines'])}
|
||||
def select(D,sel): return {k:v for k,v in D.items() if v['cls']==sel[0] and not iscopy(k[1]) and any(sel[1](l) for l in v['lines'])}
|
||||
old=select(A,PROFIL['old']); new=select(B,PROFIL['new'])
|
||||
print('anciens noeuds',len(old),'nouveaux',len(new))
|
||||
idx={(k[0],k[1].rsplit('.',1)[0],pos(v)):k for k,v in new.items()}
|
||||
ren={}; miss=0
|
||||
|
|
@ -35,7 +46,7 @@ def norm(pkg,g,lines):
|
|||
if skip.match(l): continue
|
||||
for (p,gg,o),n in ren.items():
|
||||
if p==pkg and gg==g:
|
||||
l=l.replace(o+'.SkeletalMesh',n+'.ReturnValue').replace(o+'.self',n+'.Component')
|
||||
for op,np_ in PROFIL['pins']: l=l.replace(o+'.'+op,n+'.'+np_)
|
||||
out.append(canon(l,pkg,g))
|
||||
return out
|
||||
def canon(l,pkg=None,g=None):
|
||||
|
|
@ -76,8 +87,14 @@ for k,v in sorted(diffs.items()): print(' ',v,*k)
|
|||
for e in ex: print(e[0]); [print(' -',x[:160]) for x in e[1]]; [print(' +',x[:160]) for x in e[2]]
|
||||
def pinlinks(o,name):
|
||||
for l in o['lines']:
|
||||
m=re.match(r'^ pin (\S+) \[.*\] defaut=.* -> (.*)$',l)
|
||||
if m and m.group(1)==name: return sorted(x for x in m.group(2).split(', ') if x)
|
||||
m=re.match(r'^ pin (\S+) (\[.*\] defaut=.*) -> (.*)$',l)
|
||||
if m and m.group(1)==name: return sorted(x for x in m.group(3).split(', ') if x)
|
||||
return None
|
||||
def pinhead(o,name):
|
||||
# Categorie et valeurs par defaut de la broche (sans les liaisons).
|
||||
for l in o['lines']:
|
||||
m=re.match(r'^ pin (\S+) (\[.*\] defaut=.*) -> (.*)$',l)
|
||||
if m and m.group(1)==name: return m.group(2)
|
||||
return None
|
||||
ok=bad=0
|
||||
for k,v in old.items():
|
||||
|
|
@ -90,7 +107,10 @@ for k,v in old.items():
|
|||
if p2==k[0] and gg==g: x=x.replace(o+'.',nn+'.') if x.split('.')[0]==o else x
|
||||
out.append(x)
|
||||
return sorted(out)
|
||||
if rn(pinlinks(v,'self'))==pinlinks(n,'Component') and rn(pinlinks(v,'SkeletalMesh'))==pinlinks(n,'ReturnValue'): ok+=1
|
||||
same=all(rn(pinlinks(v,op))==pinlinks(n,np_) for op,np_ in PROFIL['pins'])
|
||||
# Profil a broches homonymes : les valeurs par defaut doivent aussi etre reportees.
|
||||
if same and all(op==np_ for op,np_ in PROFIL['pins']): same=all(pinhead(v,op)==pinhead(n,op) for op,_ in PROFIL['pins'])
|
||||
if same: ok+=1
|
||||
else:
|
||||
bad+=1; print('ECART',k[1],pinlinks(v,'self'),pinlinks(n,'Component'),pinlinks(v,'SkeletalMesh'),pinlinks(n,'ReturnValue'))
|
||||
bad+=1; print('ECART',k[1],[(op,pinhead(v,op),rn(pinlinks(v,op)),pinhead(n,np_),pinlinks(n,np_)) for op,np_ in PROFIL['pins']])
|
||||
print('noeuds remplaces : liaisons identiques',ok,'/ ecarts',bad)
|
||||
|
|
|
|||
Loading…
Reference in a new issue