L7i : AI vide via AIDummy (scripts .lpai), Quick dress (ERR-037) ; outils LPDumpSave et releve sur motif du journal
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
parent
0376fb7861
commit
d6d2c569c7
6 changed files with 278 additions and 0 deletions
|
|
@ -610,3 +610,22 @@ la partie, pas seulement au chargement.
|
||||||
|
|
||||||
Build de diagnostic `LifePlay_Build_5.5_L7i` (contenu identique à L7b, 8561 `.uasset`), lancement par
|
Build de diagnostic `LifePlay_Build_5.5_L7i` (contenu identique à L7b, 8561 `.uasset`), lancement par
|
||||||
`LifePlay_diagnostic_L7i.bat` (300 relevés, 4 cadres appelants, `-ScriptStackOnWarnings`).
|
`LifePlay_diagnostic_L7i.bat` (300 relevés, 4 cadres appelants, `-ScriptStackOnWarnings`).
|
||||||
|
|
||||||
|
Session capteur n° 1 (`L7i_partie2_20261001.log`, 53 min, 300 relevés) : les variables vides viennent surtout des
|
||||||
|
scripts d'IA des PNJ (`.lpai` : `meal`, `church`, `grocery`, `entertainment`…), via `UpdateAI → PlayScene →
|
||||||
|
ReadSceneLine → CallFunction → getActorVar/setActorVar`. La variable vide est **`AI`**, alimentée par `GI.AIDummy`
|
||||||
|
(personnage factice du pool `DummyPersons`, attribué par `SpawnDummy`). `GI.PlayerCharacter` n'est pas None. Dans
|
||||||
|
`GetVariable`, toutes les variables spéciales (`Player`, `Ptr`, `Interacting`, `CurrentCompanion`, `A1`…`A5`)
|
||||||
|
passent par le même `MakeStruct` dont le nom est figé à « Player » : l'étiquette relevée en L6a était trompeuse.
|
||||||
|
|
||||||
|
Nouveau défaut signalé (utilisateur, 2026-10-01) : **Quick dress laisse souvent le personnage principal nu ; correct
|
||||||
|
en 4.27** → ERR-037, régression UE5. Chemin : `Action` (bouton « Quick dress ») → `GI::Dress(PersonVariable="Player",
|
||||||
|
ClothVariables=[], QuickDress=true)` → pour chaque emplacement obligatoire (`Top`, `Bottom`, `Foot`), filtre de
|
||||||
|
l'inventaire du joueur (sexe, emplacement, `Overlaps`, `Daz == (Race == "Daz")`, exclusion `Wedding/Nurse/Doctor`).
|
||||||
|
Inventaire de la sauvegarde `1.sav` cohérent (80 vêtements, `Daz=True`).
|
||||||
|
|
||||||
|
| CHG | Date | Lot | Fichiers | Raison | Retour arrière |
|
||||||
|
|---|---|---|---|---|---|
|
||||||
|
| CHG-041 | 2026-10-01 | L7i | `Source/LifePlayEditorTools/LPDumpSaveCommandlet.h/.cpp` (nouveau : contenu d'un `.sav` en texte, lecture seule) ; `Source/LifePlay/LPSessionLogSubsystem.h/.cpp` : relevé déclenché par un motif du journal (`-LPDumpOnLog`, `-LPDumpPaths`, `-LPDumpMax`), inactif sans l'option | ERR-037 : relever l'état du joueur au moment du Quick dress ; ERR-003 : état d'`AIDummy` | `git checkout L7i-capteur -- Source` |
|
||||||
|
|
||||||
|
Build `LifePlay_Build_5.5_L7i` reconstruite (contenu identique), lancement `LifePlay_diagnostic_L7i_2.bat`.
|
||||||
|
|
|
||||||
|
|
@ -41,3 +41,4 @@
|
||||||
| 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-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)**, retiré à tort (CHG-038), réappliqué (CHG-039) : compilation et cuisson 0 avertissement ; **validé en jeu le 2026-09-30** (build L7b) |
|
| 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)**, retiré à tort (CHG-038), réappliqué (CHG-039) : compilation et cuisson 0 avertissement ; **validé en jeu le 2026-09-30** (build L7b) |
|
||||||
| ERR-036 | En extérieur (ex. `forest___vin01.lpworld`), personnages et objets (bois du feu de camp) enfoncés sous la surface du sol ; ils traversent la matière | retour utilisateur 2026-09-30 (capture) | — | faible (visuel) | L7l | **préexistant en 4.27** (confirmé par l'utilisateur), présent aussi en V1/L7a : hors périmètre de la migration ; amélioration possible si souhaitée |
|
| ERR-036 | En extérieur (ex. `forest___vin01.lpworld`), personnages et objets (bois du feu de camp) enfoncés sous la surface du sol ; ils traversent la matière | retour utilisateur 2026-09-30 (capture) | — | faible (visuel) | L7l | **préexistant en 4.27** (confirmé par l'utilisateur), présent aussi en V1/L7a : hors périmètre de la migration ; amélioration possible si souhaitée |
|
||||||
|
| ERR-037 | Quick dress (menu Action) laisse souvent le personnage principal nu | retour utilisateur 2026-10-01 | — | moyenne (visible) | L7i | **ouvert**, régression UE5 (correct en 4.27 selon l'utilisateur) ; capteur de relevé en place (CHG-041) |
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@
|
||||||
#include "Misc/OutputDevice.h"
|
#include "Misc/OutputDevice.h"
|
||||||
#include "Misc/Paths.h"
|
#include "Misc/Paths.h"
|
||||||
#include "Misc/ScopeLock.h"
|
#include "Misc/ScopeLock.h"
|
||||||
|
#include "Containers/Ticker.h"
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
{
|
{
|
||||||
|
|
@ -113,6 +114,48 @@ private:
|
||||||
int32 Overflow = 0;
|
int32 Overflow = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/** Repere un motif dans le journal et planifie un releve sur le fil de jeu. N'ecrit jamais dans le journal. */
|
||||||
|
class FLPLogTrigger : public FOutputDevice
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FLPLogTrigger(const FString& InPattern, int32 InMax, TFunction<void(int32, const FString&)> InOnMatch)
|
||||||
|
: Pattern(InPattern), Max(InMax), OnMatch(MoveTemp(InOnMatch)) {}
|
||||||
|
|
||||||
|
virtual void Serialize(const TCHAR* V, ELogVerbosity::Type Verbosity, const FName& Category) override
|
||||||
|
{
|
||||||
|
if (Category == LogLifePlay.GetCategoryName() || !FCString::Strstr(V, *Pattern))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FScopeLock Lock(&Mutex);
|
||||||
|
const double Now = FPlatformTime::Seconds();
|
||||||
|
if (Count >= Max || Now - Last < 5.0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
Last = Now;
|
||||||
|
const int32 Index = ++Count;
|
||||||
|
const FString Line(V);
|
||||||
|
// Releve differe : le Blueprint a le temps de terminer l'habillage, et on quitte le journal.
|
||||||
|
FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateLambda([OnMatch = OnMatch, Index, Line](float)
|
||||||
|
{
|
||||||
|
OnMatch(Index, Line);
|
||||||
|
return false;
|
||||||
|
}), 1.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool CanBeUsedOnAnyThread() const override { return true; }
|
||||||
|
virtual bool CanBeUsedOnMultipleThreads() const override { return true; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
FCriticalSection Mutex;
|
||||||
|
FString Pattern;
|
||||||
|
int32 Max = 20;
|
||||||
|
int32 Count = 0;
|
||||||
|
double Last = -1000.0;
|
||||||
|
TFunction<void(int32, const FString&)> OnMatch;
|
||||||
|
};
|
||||||
|
|
||||||
void ULPSessionLogSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
void ULPSessionLogSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
{
|
{
|
||||||
Super::Initialize(Collection);
|
Super::Initialize(Collection);
|
||||||
|
|
@ -133,6 +176,7 @@ void ULPSessionLogSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
||||||
LogStartupSummary();
|
LogStartupSummary();
|
||||||
DumpRequestedVariables(TEXT("demarrage"));
|
DumpRequestedVariables(TEXT("demarrage"));
|
||||||
StartScriptExceptionTrace();
|
StartScriptExceptionTrace();
|
||||||
|
StartLogTriggeredDump();
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace
|
namespace
|
||||||
|
|
@ -300,8 +344,92 @@ void ULPSessionLogSubsystem::DumpRequestedVariables(const TCHAR* When) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ULPSessionLogSubsystem::StartLogTriggeredDump()
|
||||||
|
{
|
||||||
|
FString Pattern;
|
||||||
|
if (!FParse::Value(FCommandLine::Get(), TEXT("LPDumpOnLog="), Pattern, false) || Pattern.IsEmpty())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
FString Paths;
|
||||||
|
FParse::Value(FCommandLine::Get(), TEXT("LPDumpPaths="), Paths, false);
|
||||||
|
Paths.ParseIntoArray(DumpPathList, TEXT(","), true);
|
||||||
|
int32 Max = 20;
|
||||||
|
FParse::Value(FCommandLine::Get(), TEXT("LPDumpMax="), Max);
|
||||||
|
|
||||||
|
UE_LOG(LogLifePlay, Display, TEXT("Releve sur motif du journal : '%s', chemins [%s], %d releve(s) au plus"), *Pattern, *Paths, Max);
|
||||||
|
|
||||||
|
TWeakObjectPtr<ULPSessionLogSubsystem> WeakThis(this);
|
||||||
|
Trigger = MakeShared<FLPLogTrigger>(Pattern, Max, [WeakThis](int32 Index, const FString& Line)
|
||||||
|
{
|
||||||
|
if (const ULPSessionLogSubsystem* Self = WeakThis.Get())
|
||||||
|
{
|
||||||
|
Self->DumpPaths(Index, Line);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
GLog->AddOutputDevice(Trigger.Get());
|
||||||
|
}
|
||||||
|
|
||||||
|
void ULPSessionLogSubsystem::DumpPaths(int32 Index, const FString& Line) const
|
||||||
|
{
|
||||||
|
UE_LOG(LogLifePlay, Warning, TEXT("[releve %d] declencheur : %s"), Index, *Line.Left(300));
|
||||||
|
UObject* Root = GetGameInstance();
|
||||||
|
for (const FString& Path : DumpPathList)
|
||||||
|
{
|
||||||
|
TArray<FString> Parts;
|
||||||
|
Path.ParseIntoArray(Parts, TEXT("."), true);
|
||||||
|
const UObject* Obj = Root;
|
||||||
|
FString Value = TEXT("<introuvable>");
|
||||||
|
for (int32 i = 0; i < Parts.Num() && Obj; ++i)
|
||||||
|
{
|
||||||
|
const FProperty* Prop = Obj->GetClass()->FindPropertyByName(FName(*Parts[i]));
|
||||||
|
if (!Prop)
|
||||||
|
{
|
||||||
|
Value = FString::Printf(TEXT("<%s introuvable sur %s>"), *Parts[i], *Obj->GetClass()->GetName());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const void* Ptr = Prop->ContainerPtrToValuePtr<void>(Obj);
|
||||||
|
if (i + 1 < Parts.Num())
|
||||||
|
{
|
||||||
|
const FObjectPropertyBase* ObjProp = CastField<FObjectPropertyBase>(Prop);
|
||||||
|
Obj = ObjProp ? ObjProp->GetObjectPropertyValue(Ptr) : nullptr;
|
||||||
|
if (!Obj)
|
||||||
|
{
|
||||||
|
Value = FString::Printf(TEXT("<%s vaut None>"), *Parts[i]);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Value.Reset();
|
||||||
|
if (const FArrayProperty* ArrayProp = CastField<FArrayProperty>(Prop))
|
||||||
|
{
|
||||||
|
FScriptArrayHelper Helper(ArrayProp, Ptr);
|
||||||
|
UE_LOG(LogLifePlay, Warning, TEXT("[releve %d] %s : %d element(s)"), Index, *Path, Helper.Num());
|
||||||
|
for (int32 j = 0; j < Helper.Num() && j < 120; ++j)
|
||||||
|
{
|
||||||
|
FString Item;
|
||||||
|
ArrayProp->Inner->ExportTextItem_Direct(Item, Helper.GetRawPtr(j), nullptr, const_cast<UObject*>(Obj), PPF_None);
|
||||||
|
UE_LOG(LogLifePlay, Warning, TEXT("[releve %d] %s[%d] = %s"), Index, *Path, j, *Item.Left(700));
|
||||||
|
}
|
||||||
|
Value = TEXT("");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Prop->ExportTextItem_Direct(Value, Ptr, nullptr, const_cast<UObject*>(Obj), PPF_None);
|
||||||
|
}
|
||||||
|
if (!Value.IsEmpty())
|
||||||
|
{
|
||||||
|
UE_LOG(LogLifePlay, Warning, TEXT("[releve %d] %s = %s"), Index, *Path, *Value.Left(700));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void ULPSessionLogSubsystem::Deinitialize()
|
void ULPSessionLogSubsystem::Deinitialize()
|
||||||
{
|
{
|
||||||
|
if (Trigger.IsValid())
|
||||||
|
{
|
||||||
|
GLog->RemoveOutputDevice(Trigger.Get());
|
||||||
|
GLog->Flush();
|
||||||
|
Trigger.Reset();
|
||||||
|
}
|
||||||
DumpRequestedVariables(TEXT("fin"));
|
DumpRequestedVariables(TEXT("fin"));
|
||||||
if (ScriptExceptionHandle.IsValid())
|
if (ScriptExceptionHandle.IsValid())
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,20 @@ private:
|
||||||
*/
|
*/
|
||||||
void StartScriptExceptionTrace();
|
void StartScriptExceptionTrace();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Diagnostic (lot L7i) : avec -LPDumpOnLog=<texte>, chaque fois qu'une ligne du journal contient
|
||||||
|
* <texte> (au plus une fois toutes les 5 s, -LPDumpMax=N releves, 20 par defaut), ecrit 1,5 s plus
|
||||||
|
* tard les proprietes nommees par -LPDumpPaths=<a,b.c> a partir de l'instance de jeu (GI) : un
|
||||||
|
* chemin "PlayerCharacter.Race" suit la propriete objet PlayerCharacter puis lit Race.
|
||||||
|
* Sans l'option, rien n'est branche.
|
||||||
|
*/
|
||||||
|
void StartLogTriggeredDump();
|
||||||
|
void DumpPaths(int32 Index, const FString& Line) const;
|
||||||
|
|
||||||
FDelegateHandle ScriptExceptionHandle;
|
FDelegateHandle ScriptExceptionHandle;
|
||||||
|
|
||||||
TSharedPtr<FLPLogCounter> Counter;
|
TSharedPtr<FLPLogCounter> Counter;
|
||||||
|
TSharedPtr<class FLPLogTrigger> Trigger;
|
||||||
|
TArray<FString> DumpPathList;
|
||||||
double StartTime = 0.0;
|
double StartTime = 0.0;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
94
Source/LifePlayEditorTools/LPDumpSaveCommandlet.cpp
Normal file
94
Source/LifePlayEditorTools/LPDumpSaveCommandlet.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
||||||
|
// Portage UE 5.5 - voir LPDumpSaveCommandlet.h
|
||||||
|
|
||||||
|
#include "LPDumpSaveCommandlet.h"
|
||||||
|
|
||||||
|
#include "GameFramework/SaveGame.h"
|
||||||
|
#include "Kismet/GameplayStatics.h"
|
||||||
|
#include "Misc/FileHelper.h"
|
||||||
|
#include "UObject/UnrealType.h"
|
||||||
|
|
||||||
|
DEFINE_LOG_CATEGORY_STATIC(LogLPDumpSave, Log, All);
|
||||||
|
|
||||||
|
ULPDumpSaveCommandlet::ULPDumpSaveCommandlet()
|
||||||
|
{
|
||||||
|
IsClient = false;
|
||||||
|
IsServer = false;
|
||||||
|
IsEditor = true;
|
||||||
|
LogToConsole = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int32 ULPDumpSaveCommandlet::Main(const FString& Params)
|
||||||
|
{
|
||||||
|
TArray<FString> Tokens, Switches;
|
||||||
|
TMap<FString, FString> ParamsMap;
|
||||||
|
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
|
||||||
|
|
||||||
|
const FString File = ParamsMap.FindRef(TEXT("File"));
|
||||||
|
const FString Out = ParamsMap.FindRef(TEXT("Out"));
|
||||||
|
TArray<FString> Wanted;
|
||||||
|
ParamsMap.FindRef(TEXT("Props")).ParseIntoArray(Wanted, TEXT(","), true);
|
||||||
|
if (File.IsEmpty() || Out.IsEmpty())
|
||||||
|
{
|
||||||
|
UE_LOG(LogLPDumpSave, Error, TEXT("Usage : -File=<chemin.sav> -Out=<fichier.txt> [-Props=Nom1,Nom2]"));
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<uint8> Bytes;
|
||||||
|
if (!FFileHelper::LoadFileToArray(Bytes, *File))
|
||||||
|
{
|
||||||
|
UE_LOG(LogLPDumpSave, Error, TEXT("Lecture impossible : %s"), *File);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
USaveGame* Save = UGameplayStatics::LoadGameFromMemory(Bytes);
|
||||||
|
if (!Save)
|
||||||
|
{
|
||||||
|
UE_LOG(LogLPDumpSave, Error, TEXT("Sauvegarde illisible : %s"), *File);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
TArray<FString> Lines;
|
||||||
|
Lines.Add(FString::Printf(TEXT("##### %s [%s]"), *File, *Save->GetClass()->GetPathName()));
|
||||||
|
for (TFieldIterator<FProperty> It(Save->GetClass()); It; ++It)
|
||||||
|
{
|
||||||
|
const FProperty* Prop = *It;
|
||||||
|
if (Prop->GetOwnerClass() && !Prop->GetOwnerClass()->IsChildOf(USaveGame::StaticClass()))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Prop->GetOwnerClass() == USaveGame::StaticClass())
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const FString Name = Prop->GetName();
|
||||||
|
if (Wanted.Num() > 0 && !Wanted.ContainsByPredicate([&](const FString& W) { return Name.StartsWith(W); }))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const void* Value = Prop->ContainerPtrToValuePtr<void>(Save);
|
||||||
|
if (const FArrayProperty* ArrayProp = CastField<FArrayProperty>(Prop))
|
||||||
|
{
|
||||||
|
FScriptArrayHelper Helper(ArrayProp, Value);
|
||||||
|
Lines.Add(FString::Printf(TEXT("%s [%s] : %d element(s)"), *Name, *ArrayProp->Inner->GetCPPType(), Helper.Num()));
|
||||||
|
for (int32 i = 0; i < Helper.Num(); ++i)
|
||||||
|
{
|
||||||
|
FString Text;
|
||||||
|
ArrayProp->Inner->ExportTextItem_Direct(Text, Helper.GetRawPtr(i), nullptr, Save, PPF_None);
|
||||||
|
Lines.Add(FString::Printf(TEXT(" %s[%d] = %s"), *Name, i, *Text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
FString Text;
|
||||||
|
Prop->ExportTextItem_Direct(Text, Value, nullptr, Save, PPF_None);
|
||||||
|
Lines.Add(FString::Printf(TEXT("%s [%s] = %s"), *Name, *Prop->GetCPPType(), *Text));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!FFileHelper::SaveStringArrayToFile(Lines, *Out, FFileHelper::EEncodingOptions::ForceUTF8WithoutBOM))
|
||||||
|
{
|
||||||
|
UE_LOG(LogLPDumpSave, Error, TEXT("Ecriture impossible : %s"), *Out);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
UE_LOG(LogLPDumpSave, Warning, TEXT("=== %s : %d ligne(s) -> %s ==="), *File, Lines.Num(), *Out);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
24
Source/LifePlayEditorTools/LPDumpSaveCommandlet.h
Normal file
24
Source/LifePlayEditorTools/LPDumpSaveCommandlet.h
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
// Portage UE 5.5 - lot L7i (diagnostic Quick dress).
|
||||||
|
//
|
||||||
|
// Ecrit en texte le contenu d'une sauvegarde (.sav) : chaque propriete de l'objet USaveGame
|
||||||
|
// (classe Blueprint du projet), un element de tableau par ligne. Lecture seule : le fichier
|
||||||
|
// n'est jamais reecrit.
|
||||||
|
//
|
||||||
|
// Usage :
|
||||||
|
// UnrealEditor-Cmd.exe <projet> -run=LPDumpSave -File=<chemin.sav> -Out=<fichier.txt> [-Props=Nom1,Nom2]
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "CoreMinimal.h"
|
||||||
|
#include "Commandlets/Commandlet.h"
|
||||||
|
#include "LPDumpSaveCommandlet.generated.h"
|
||||||
|
|
||||||
|
UCLASS()
|
||||||
|
class ULPDumpSaveCommandlet : public UCommandlet
|
||||||
|
{
|
||||||
|
GENERATED_BODY()
|
||||||
|
|
||||||
|
public:
|
||||||
|
ULPDumpSaveCommandlet();
|
||||||
|
virtual int32 Main(const FString& Params) override;
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue