LifePlay-Revival/Source/LifePlayEditorTools/LPCheckStreetMapCommandlet.cpp

193 lines
6.3 KiB
C++
Raw Normal View History

// Portage UE 5.5 - voir LPCheckStreetMapCommandlet.h
#include "LPCheckStreetMapCommandlet.h"
#include "AssetRegistry/AssetRegistryModule.h"
#include "HAL/FileManager.h"
#include "Misc/FileHelper.h"
#include "Misc/Paths.h"
#include "StreetMap.h"
#include "UObject/UObjectGlobals.h"
DEFINE_LOG_CATEGORY_STATIC(LogLPCheckStreetMap, Log, All);
namespace
{
/** Ce qu'on a trouve dans une ville. */
struct FCityReport
{
FString Name;
int32 Roads = 0;
int32 Nodes = 0;
int32 Buildings = 0;
int32 Waters = 0;
int32 Parks = 0;
int32 NamedBuildings = 0; // batiments avec un nom non vide
int32 WithAmenity = 0; // batiments avec un Amenity non vide
int32 DistinctAmenities = 0;
bool bLoaded = false;
};
}
ULPCheckStreetMapCommandlet::ULPCheckStreetMapCommandlet()
{
IsClient = false;
IsServer = false;
IsEditor = true;
LogToConsole = true;
}
int32 ULPCheckStreetMapCommandlet::Main(const FString& Params)
{
TArray<FString> Tokens;
TArray<FString> Switches;
TMap<FString, FString> ParamsMap;
ParseCommandLine(*Params, Tokens, Switches, ParamsMap);
const bool bVerbose = Switches.Contains(TEXT("Verbose"));
const bool bDumpAmenities = Switches.Contains(TEXT("DumpAmenities"));
const FString CsvPath = ParamsMap.FindRef(TEXT("CSV"));
// Lot L7h : limite la verification a un dossier (ex. copie temporaire des assets 4.27).
const FString PathFilter = ParamsMap.FindRef(TEXT("Path"));
UE_LOG(LogLPCheckStreetMap, Warning, TEXT("=== Verification des assets UStreetMap ==="));
FAssetRegistryModule& AssetRegistryModule =
FModuleManager::LoadModuleChecked<FAssetRegistryModule>(TEXT("AssetRegistry"));
AssetRegistryModule.Get().SearchAllAssets(true);
TArray<FAssetData> Assets;
AssetRegistryModule.Get().GetAssetsByClass(UStreetMap::StaticClass()->GetClassPathName(), Assets, true);
if (!PathFilter.IsEmpty())
{
Assets.RemoveAll([&PathFilter](const FAssetData& A) { return !A.PackageName.ToString().StartsWith(PathFilter); });
}
// Ordre stable : indispensable pour diffing entre deux executions.
Assets.Sort([](const FAssetData& A, const FAssetData& B)
{
return A.AssetName.LexicalLess(B.AssetName);
});
UE_LOG(LogLPCheckStreetMap, Warning, TEXT("%d asset(s) UStreetMap trouve(s)."), Assets.Num());
TArray<FCityReport> Reports;
Reports.Reserve(Assets.Num());
int32 LoadFailures = 0;
int32 EmptyCities = 0;
for (const FAssetData& AssetData : Assets)
{
FCityReport Report;
Report.Name = AssetData.AssetName.ToString();
UStreetMap* StreetMap = Cast<UStreetMap>(AssetData.GetAsset());
if (StreetMap == nullptr)
{
++LoadFailures;
UE_LOG(LogLPCheckStreetMap, Error, TEXT(" ECHEC de chargement : %s"), *Report.Name);
Reports.Add(Report);
continue;
}
Report.bLoaded = true;
Report.Roads = StreetMap->GetRoads().Num();
Report.Nodes = StreetMap->GetNodes().Num();
Report.Buildings = StreetMap->GetBuildings().Num();
Report.Waters = StreetMap->GetWaters().Num();
Report.Parks = StreetMap->GetParks().Num();
// Amenity est le mecanisme par lequel le jeu identifie bars, salles de sport et
// commerces : c'est la donnee dont la perte serait la plus grave.
TSet<FString> Amenities;
for (const FStreetMapBuilding& Building : StreetMap->GetBuildings())
{
if (!Building.BuildingName.IsEmpty())
{
++Report.NamedBuildings;
}
if (!Building.Amenity.IsEmpty())
{
++Report.WithAmenity;
Amenities.Add(Building.Amenity);
}
}
Report.DistinctAmenities = Amenities.Num();
// Les comptes seuls ne prouvent pas que la donnee est la bonne : on releve les
// valeurs pour pouvoir les croiser avec les types de lieux du jeu (*.lpworld).
if (bDumpAmenities && Amenities.Num() > 0)
{
TArray<FString> Sorted = Amenities.Array();
Sorted.Sort();
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" --- amenities de %s (%d) : %s"),
*Report.Name, Sorted.Num(), *FString::Join(Sorted, TEXT(", ")));
}
if (Report.Roads == 0 && Report.Buildings == 0)
{
++EmptyCities;
}
if (bVerbose)
{
UE_LOG(LogLPCheckStreetMap, Warning,
TEXT(" %-24s routes=%-7d noeuds=%-7d batiments=%-7d eaux=%-4d parcs=%-4d nommes=%-6d amenity=%-6d (%d types)"),
*Report.Name, Report.Roads, Report.Nodes, Report.Buildings,
Report.Waters, Report.Parks, Report.NamedBuildings,
Report.WithAmenity, Report.DistinctAmenities);
}
Reports.Add(Report);
// Sans cela, charger les 280 villes fait exploser la memoire (CentralLondon pese
// 33 Mo de donnees a lui seul).
CollectGarbage(RF_NoFlags);
}
// --- Synthese
int64 TotalRoads = 0, TotalBuildings = 0, TotalWaters = 0, TotalParks = 0, TotalAmenity = 0;
for (const FCityReport& R : Reports)
{
TotalRoads += R.Roads;
TotalBuildings += R.Buildings;
TotalWaters += R.Waters;
TotalParks += R.Parks;
TotalAmenity += R.WithAmenity;
}
UE_LOG(LogLPCheckStreetMap, Warning, TEXT("=== Synthese ==="));
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" villes chargees : %d / %d"), Assets.Num() - LoadFailures, Assets.Num());
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" echecs de chargement : %d"), LoadFailures);
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" villes sans donnees : %d"), EmptyCities);
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" routes : %lld"), TotalRoads);
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" batiments : %lld"), TotalBuildings);
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" batiments avec amenity : %lld"), TotalAmenity);
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" plans d'eau : %lld"), TotalWaters);
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" parcs : %lld"), TotalParks);
if (!CsvPath.IsEmpty())
{
FString Csv = TEXT("Ville;Charge;Routes;Noeuds;Batiments;Eaux;Parcs;BatimentsNommes;AvecAmenity;TypesAmenity\n");
for (const FCityReport& R : Reports)
{
Csv += FString::Printf(TEXT("%s;%d;%d;%d;%d;%d;%d;%d;%d;%d\n"),
*R.Name, R.bLoaded ? 1 : 0, R.Roads, R.Nodes, R.Buildings,
R.Waters, R.Parks, R.NamedBuildings, R.WithAmenity, R.DistinctAmenities);
}
if (FFileHelper::SaveStringToFile(Csv, *CsvPath))
{
UE_LOG(LogLPCheckStreetMap, Warning, TEXT(" CSV ecrit : %s"), *CsvPath);
}
else
{
UE_LOG(LogLPCheckStreetMap, Error, TEXT(" ECHEC d'ecriture du CSV : %s"), *CsvPath);
}
}
// Code de retour non nul si une ville n'a pas pu etre chargee : exploitable en CI.
return LoadFailures > 0 ? 1 : 0;
}