304 lines
9.3 KiB
C++
304 lines
9.3 KiB
C++
// Lot STM1 (STM1-SPEC-01, T2). Voir LPCityTraceSubsystem.h.
|
|
|
|
#include "LPCityTraceSubsystem.h"
|
|
|
|
#include "LifePlay.h"
|
|
|
|
#if !UE_BUILD_SHIPPING
|
|
|
|
#include "Components/StaticMeshComponent.h"
|
|
#include "Engine/GameInstance.h"
|
|
#include "Engine/StaticMesh.h"
|
|
#include "GameFramework/Actor.h"
|
|
#include "HAL/IConsoleManager.h"
|
|
#include "HAL/PlatformTime.h"
|
|
#include "UObject/UnrealType.h"
|
|
|
|
namespace LPCityTrace
|
|
{
|
|
// Noms etablis par les dumps des graphes du GI (lots STB, STE, L6f ; STM1-CART-02 § 1).
|
|
const TCHAR* const CommandName = TEXT("LP.CityTrace");
|
|
|
|
// Valeur d'une propriete en texte lisible : FText et FString tels quels, le reste par ExportText.
|
|
FString ValueToString(const FProperty* Prop, const void* ValuePtr)
|
|
{
|
|
if (!Prop || !ValuePtr)
|
|
{
|
|
return FString();
|
|
}
|
|
if (const FTextProperty* TextProp = CastField<FTextProperty>(Prop))
|
|
{
|
|
return TextProp->GetPropertyValue(ValuePtr).ToString();
|
|
}
|
|
if (const FStrProperty* StrProp = CastField<FStrProperty>(Prop))
|
|
{
|
|
return StrProp->GetPropertyValue(ValuePtr);
|
|
}
|
|
if (const FNameProperty* NameProp = CastField<FNameProperty>(Prop))
|
|
{
|
|
return NameProp->GetPropertyValue(ValuePtr).ToString();
|
|
}
|
|
if (const FObjectPropertyBase* ObjectProp = CastField<FObjectPropertyBase>(Prop))
|
|
{
|
|
const UObject* Object = ObjectProp->GetObjectPropertyValue(ValuePtr);
|
|
return Object ? Object->GetPathName() : FString(TEXT("None"));
|
|
}
|
|
FString Out;
|
|
Prop->ExportTextItem_Direct(Out, ValuePtr, nullptr, nullptr, PPF_None);
|
|
return Out;
|
|
}
|
|
|
|
// Membre d'une structure Blueprint : son nom porte un suffixe de GUID (DisplayName_2_EBDEAF...), cherche par prefixe.
|
|
const FProperty* FindMemberByPrefix(const UStruct* Struct, const TCHAR* Prefix)
|
|
{
|
|
if (!Struct)
|
|
{
|
|
return nullptr;
|
|
}
|
|
for (TFieldIterator<FProperty> It(Struct); It; ++It)
|
|
{
|
|
if (It->GetName().StartsWith(Prefix, ESearchCase::IgnoreCase))
|
|
{
|
|
return *It;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
FString Member(const UStruct* Struct, const void* StructPtr, const TCHAR* Prefix)
|
|
{
|
|
const FProperty* Prop = FindMemberByPrefix(Struct, Prefix);
|
|
return Prop ? ValueToString(Prop, Prop->ContainerPtrToValuePtr<void>(StructPtr)) : FString(TEXT("?"));
|
|
}
|
|
|
|
// Une ville (structure City) en une ligne : nom affiche, donnees StreetMap, maillage.
|
|
FString DescribeCity(const UStruct* Struct, const void* StructPtr)
|
|
{
|
|
return FString::Printf(TEXT("'%s' sm='%s' mesh='%s'"),
|
|
*Member(Struct, StructPtr, TEXT("DisplayName_")),
|
|
*Member(Struct, StructPtr, TEXT("StreetMapName_")),
|
|
*Member(Struct, StructPtr, TEXT("StaticMeshName_")));
|
|
}
|
|
}
|
|
|
|
void ULPCityTraceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
StartSeconds = FPlatformTime::Seconds();
|
|
TickHandle = FTSTicker::GetCoreTicker().AddTicker(FTickerDelegate::CreateUObject(this, &ULPCityTraceSubsystem::Tick), 0.25f);
|
|
|
|
// Une instance de jeu par PIE : la commande n'est enregistree que par la premiere.
|
|
if (!IConsoleManager::Get().FindConsoleObject(LPCityTrace::CommandName))
|
|
{
|
|
ConsoleCommand = IConsoleManager::Get().RegisterConsoleCommand(LPCityTrace::CommandName,
|
|
TEXT("LifePlay (STM1) : ecrit au journal l'etat du choix de la ville ([LPCity])."),
|
|
FConsoleCommandDelegate::CreateUObject(this, &ULPCityTraceSubsystem::DumpNow), ECVF_Default);
|
|
}
|
|
UE_LOG(LogLPCity, Log, TEXT("[LPCity] trace active (releve toutes les 0,25 s, ecrit au changement)"));
|
|
}
|
|
|
|
void ULPCityTraceSubsystem::Deinitialize()
|
|
{
|
|
if (TickHandle.IsValid())
|
|
{
|
|
FTSTicker::GetCoreTicker().RemoveTicker(TickHandle);
|
|
TickHandle.Reset();
|
|
}
|
|
if (ConsoleCommand)
|
|
{
|
|
IConsoleManager::Get().UnregisterConsoleObject(ConsoleCommand);
|
|
ConsoleCommand = nullptr;
|
|
}
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
bool ULPCityTraceSubsystem::Tick(float DeltaTime)
|
|
{
|
|
Sample(false);
|
|
return true;
|
|
}
|
|
|
|
void ULPCityTraceSubsystem::DumpNow()
|
|
{
|
|
Sample(true);
|
|
}
|
|
|
|
void ULPCityTraceSubsystem::Sample(bool bForce)
|
|
{
|
|
const UGameInstance* GI = GetGameInstance();
|
|
if (!GI)
|
|
{
|
|
return;
|
|
}
|
|
const UClass* GIClass = GI->GetClass();
|
|
|
|
auto Missing = [this](const UObject* Owner, const TCHAR* Name)
|
|
{
|
|
const FString Key = FString::Printf(TEXT("%s.%s"), Owner ? *Owner->GetClass()->GetName() : TEXT("?"), Name);
|
|
bool bAlreadyReported = false;
|
|
ReportedMissing.Add(Key, &bAlreadyReported);
|
|
if (!bAlreadyReported)
|
|
{
|
|
UE_LOG(LogLPCity, Log, TEXT("[LPCity] PROPRIETE ABSENTE %s"), *Key);
|
|
}
|
|
};
|
|
|
|
// T2a : liste des villes (GI.Cities, tableau de City).
|
|
const FArrayProperty* CitiesProp = CastField<FArrayProperty>(GIClass->FindPropertyByName(TEXT("Cities")));
|
|
const FStructProperty* CityInner = CitiesProp ? CastField<FStructProperty>(CitiesProp->Inner) : nullptr;
|
|
if (!CityInner)
|
|
{
|
|
Missing(GI, TEXT("Cities"));
|
|
}
|
|
else
|
|
{
|
|
FScriptArrayHelper Cities(CitiesProp, CitiesProp->ContainerPtrToValuePtr<void>(GI));
|
|
const int32 Count = Cities.Num();
|
|
if (Count > 0 && (bForce || Count != LastCityCount))
|
|
{
|
|
LastCityCount = Count;
|
|
int32 LondonIndex = INDEX_NONE;
|
|
for (int32 Index = 0; Index < Count; ++Index)
|
|
{
|
|
if (LPCityTrace::Member(CityInner->Struct, Cities.GetRawPtr(Index), TEXT("StreetMapName_")).Contains(TEXT("/CentralLondon")))
|
|
{
|
|
LondonIndex = Index;
|
|
break;
|
|
}
|
|
}
|
|
const FString LondonText = LondonIndex == INDEX_NONE ? FString(TEXT("absent")) : FString::FromInt(LondonIndex);
|
|
UE_LOG(LogLPCity, Log, TEXT("[LPCity] CITIES n=%d [0]=%s ; CentralLondon a l'indice %s"),
|
|
Count, *LPCityTrace::DescribeCity(CityInner->Struct, Cities.GetRawPtr(0)), *LondonText);
|
|
}
|
|
}
|
|
|
|
// T2b : ville sauvegardee (GI.SaveGameInstance.City) et correspondance, comme GI.GetCityByName :
|
|
// nom affiche (DisplayName) compare sans tenir compte de la casse.
|
|
const FObjectPropertyBase* SaveProp = CastField<FObjectPropertyBase>(GIClass->FindPropertyByName(TEXT("SaveGameInstance")));
|
|
const UObject* Save = SaveProp ? SaveProp->GetObjectPropertyValue_InContainer(GI) : nullptr;
|
|
if (!SaveProp)
|
|
{
|
|
Missing(GI, TEXT("SaveGameInstance"));
|
|
}
|
|
else if (Save)
|
|
{
|
|
const FProperty* SaveCityProp = Save->GetClass()->FindPropertyByName(TEXT("City"));
|
|
if (!SaveCityProp)
|
|
{
|
|
Missing(Save, TEXT("City"));
|
|
}
|
|
else
|
|
{
|
|
const FString SavedCity = LPCityTrace::ValueToString(SaveCityProp, SaveCityProp->ContainerPtrToValuePtr<void>(Save));
|
|
FString Match = TEXT("liste des villes illisible");
|
|
if (CityInner)
|
|
{
|
|
Match = TEXT("AUCUNE correspondance (GetCityByName rendra la City par defaut)");
|
|
FScriptArrayHelper Cities(CitiesProp, CitiesProp->ContainerPtrToValuePtr<void>(GI));
|
|
for (int32 Index = 0; Index < Cities.Num(); ++Index)
|
|
{
|
|
const FString DisplayName = LPCityTrace::Member(CityInner->Struct, Cities.GetRawPtr(Index), TEXT("DisplayName_"));
|
|
if (DisplayName.Equals(SavedCity, ESearchCase::IgnoreCase))
|
|
{
|
|
Match = FString::Printf(TEXT("trouvee indice %d ('%s')"), Index, *DisplayName);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
const FString Line = FString::Printf(TEXT("ville='%s' -> %s"), *SavedCity, *Match);
|
|
if (bForce || Line != LastSave)
|
|
{
|
|
LastSave = Line;
|
|
UE_LOG(LogLPCity, Log, TEXT("[LPCity] SAUVEGARDE %s"), *Line);
|
|
}
|
|
}
|
|
}
|
|
|
|
// T2c : ville demandee (GI.CityToMoveTo).
|
|
const FStructProperty* RequestProp = CastField<FStructProperty>(GIClass->FindPropertyByName(TEXT("CityToMoveTo")));
|
|
if (!RequestProp)
|
|
{
|
|
Missing(GI, TEXT("CityToMoveTo"));
|
|
}
|
|
else
|
|
{
|
|
const FString Line = LPCityTrace::DescribeCity(RequestProp->Struct, RequestProp->ContainerPtrToValuePtr<void>(GI));
|
|
if (bForce || Line != LastRequest)
|
|
{
|
|
LastRequest = Line;
|
|
UE_LOG(LogLPCity, Log, TEXT("[LPCity] DEMANDE %s"), *Line);
|
|
}
|
|
}
|
|
|
|
// T2d et T2e : donnees StreetMap et maillage poses sur l'acteur de la carte (GI.StreetMapStatic).
|
|
const FObjectPropertyBase* StreetProp = CastField<FObjectPropertyBase>(GIClass->FindPropertyByName(TEXT("StreetMapStatic")));
|
|
const AActor* Street = StreetProp ? Cast<AActor>(StreetProp->GetObjectPropertyValue_InContainer(GI)) : nullptr;
|
|
if (!StreetProp)
|
|
{
|
|
Missing(GI, TEXT("StreetMapStatic"));
|
|
}
|
|
if (!Street)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const FObjectPropertyBase* MapProp = CastField<FObjectPropertyBase>(Street->GetClass()->FindPropertyByName(TEXT("StreetMap")));
|
|
if (!MapProp)
|
|
{
|
|
Missing(Street, TEXT("StreetMap"));
|
|
}
|
|
else
|
|
{
|
|
const UObject* Map = MapProp->GetObjectPropertyValue_InContainer(Street);
|
|
const FString Line = Map ? Map->GetPathName() : FString(TEXT("None"));
|
|
if (bForce || Line != LastStreetMap)
|
|
{
|
|
LastStreetMap = Line;
|
|
UE_LOG(LogLPCity, Log, TEXT("[LPCity] STREETMAP %s"), *Line);
|
|
}
|
|
}
|
|
|
|
const FObjectPropertyBase* MeshCompProp = CastField<FObjectPropertyBase>(Street->GetClass()->FindPropertyByName(TEXT("StaticMeshComponent")));
|
|
if (!MeshCompProp)
|
|
{
|
|
Missing(Street, TEXT("StaticMeshComponent"));
|
|
}
|
|
else
|
|
{
|
|
const UStaticMeshComponent* MeshComp = Cast<UStaticMeshComponent>(MeshCompProp->GetObjectPropertyValue_InContainer(Street));
|
|
const UStaticMesh* Mesh = MeshComp ? MeshComp->GetStaticMesh() : nullptr;
|
|
const FString Line = Mesh ? Mesh->GetPathName() : FString(TEXT("None"));
|
|
if (bForce || Line != LastPose)
|
|
{
|
|
LastPose = Line;
|
|
UE_LOG(LogLPCity, Log, TEXT("[LPCity] POSE %s (t=%.1f s)"), *Line, FPlatformTime::Seconds() - StartSeconds);
|
|
}
|
|
}
|
|
}
|
|
|
|
#else
|
|
|
|
void ULPCityTraceSubsystem::Initialize(FSubsystemCollectionBase& Collection)
|
|
{
|
|
Super::Initialize(Collection);
|
|
}
|
|
|
|
void ULPCityTraceSubsystem::Deinitialize()
|
|
{
|
|
Super::Deinitialize();
|
|
}
|
|
|
|
bool ULPCityTraceSubsystem::Tick(float DeltaTime)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void ULPCityTraceSubsystem::Sample(bool bForce)
|
|
{
|
|
}
|
|
|
|
void ULPCityTraceSubsystem::DumpNow()
|
|
{
|
|
}
|
|
|
|
#endif
|