2026-09-28 22:56:10 +00:00
|
|
|
// 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"));
|
2026-10-01 18:20:41 +00:00
|
|
|
// Lot L7h : limite la verification a un dossier (ex. copie temporaire des assets 4.27).
|
|
|
|
|
const FString PathFilter = ParamsMap.FindRef(TEXT("Path"));
|
2026-09-28 22:56:10 +00:00
|
|
|
|
|
|
|
|
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);
|
2026-10-01 18:20:41 +00:00
|
|
|
if (!PathFilter.IsEmpty())
|
|
|
|
|
{
|
|
|
|
|
Assets.RemoveAll([&PathFilter](const FAssetData& A) { return !A.PackageName.ToString().StartsWith(PathFilter); });
|
|
|
|
|
}
|
2026-09-28 22:56:10 +00:00
|
|
|
|
|
|
|
|
// 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;
|
|
|
|
|
}
|