Tooling
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
#include "World/WorldMapSubsystem.h"
|
||||
|
||||
#include "Core/TelemetrySubsystem.h"
|
||||
#include "Engine/AssetManager.h"
|
||||
#include "Engine/Engine.h"
|
||||
#include "Engine/GameInstance.h"
|
||||
#include "Engine/World.h"
|
||||
#include "GameFramework/Pawn.h"
|
||||
#include "GameFramework/PlayerController.h"
|
||||
#include "Salty.h"
|
||||
#include "Telemetry/TelemetryEvent.h"
|
||||
#include "Telemetry/TelemetryEvents.h"
|
||||
#include "World/WorldMapDefinition.h"
|
||||
|
||||
bool UWorldMapSubsystem::ShouldCreateSubsystem(UObject* Outer) const
|
||||
{
|
||||
if (!Super::ShouldCreateSubsystem(Outer))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// Game and PIE worlds only. An editor preview world or a thumbnail scene has no map and no one to show it to.
|
||||
const UWorld* World = Cast<UWorld>(Outer);
|
||||
return World && (World->IsGameWorld());
|
||||
}
|
||||
|
||||
void UWorldMapSubsystem::Deinitialize()
|
||||
{
|
||||
Markers.Reset();
|
||||
OnMarkersChanged.Clear();
|
||||
Definition = nullptr;
|
||||
bResolved = false;
|
||||
Super::Deinitialize();
|
||||
}
|
||||
|
||||
void UWorldMapSubsystem::ResolveDefinition() const
|
||||
{
|
||||
bResolved = true;
|
||||
Definition = nullptr;
|
||||
|
||||
const UWorld* World = GetWorld();
|
||||
if (!World)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// PIE renames the level package (/Game/Maps/UEDPIE_0_L_World), so the definition's own path would never
|
||||
// match what the world says it is. Strip it, or the map works in a cooked build and not in the editor.
|
||||
const FString LevelPackage = UWorld::RemovePIEPrefix(World->GetOutermost()->GetName());
|
||||
|
||||
// IsInitialized, not IsValid: the manager is always constructed now and IsValid is deprecated for saying
|
||||
// so. The guard stays because this can be reached before UEngine::InitializeObjectReferences has run.
|
||||
if (!UAssetManager::IsInitialized())
|
||||
{
|
||||
return;
|
||||
}
|
||||
UAssetManager& Manager = UAssetManager::Get();
|
||||
|
||||
TArray<FPrimaryAssetId> Ids;
|
||||
Manager.GetPrimaryAssetIdList(UWorldMapDefinition::AssetType, Ids);
|
||||
if (Ids.Num() == 0)
|
||||
{
|
||||
UE_LOG(LogSalty, Verbose,
|
||||
TEXT("World map: no %s assets are registered. Add a PrimaryAssetTypesToScan entry in DefaultGame.ini "
|
||||
"and run Scripts/Authoring/build_world_map.sh."),
|
||||
*UWorldMapDefinition::AssetType.ToString());
|
||||
return;
|
||||
}
|
||||
|
||||
for (const FPrimaryAssetId& Id : Ids)
|
||||
{
|
||||
const FSoftObjectPath Path = Manager.GetPrimaryAssetPath(Id);
|
||||
// Synchronous, because this happens once when a map is first opened and an asynchronous load would
|
||||
// mean a frame of blank map for no gain.
|
||||
const UWorldMapDefinition* Candidate = Cast<UWorldMapDefinition>(Path.TryLoad());
|
||||
if (Candidate && Candidate->Level.GetLongPackageName() == LevelPackage)
|
||||
{
|
||||
Definition = Candidate;
|
||||
UE_LOG(LogSalty, Log, TEXT("World map: %s covers %s - %s"),
|
||||
*Id.ToString(), *LevelPackage, *Candidate->Projection.ToString());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Deliberately not "fall back to the only one there is". L_Gym showing L_World's map would be a map that
|
||||
// lies about where you are, which is worse than no map.
|
||||
UE_LOG(LogSalty, Verbose, TEXT("World map: none of the %d definitions is a map of %s."), Ids.Num(), *LevelPackage);
|
||||
}
|
||||
|
||||
const UWorldMapDefinition* UWorldMapSubsystem::GetDefinition() const
|
||||
{
|
||||
if (!bResolved)
|
||||
{
|
||||
ResolveDefinition();
|
||||
}
|
||||
return Definition;
|
||||
}
|
||||
|
||||
FGuid UWorldMapSubsystem::AddMarker(const FWorldMapMarker& Marker)
|
||||
{
|
||||
const FGuid Handle = FGuid::NewGuid();
|
||||
Markers.Add(Handle, Marker);
|
||||
OnMarkersChanged.Broadcast();
|
||||
return Handle;
|
||||
}
|
||||
|
||||
bool UWorldMapSubsystem::UpdateMarker(const FGuid& Handle, const FWorldMapMarker& Marker)
|
||||
{
|
||||
if (FWorldMapMarker* Existing = Markers.Find(Handle))
|
||||
{
|
||||
*Existing = Marker;
|
||||
OnMarkersChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool UWorldMapSubsystem::RemoveMarker(const FGuid& Handle)
|
||||
{
|
||||
if (Markers.Remove(Handle) > 0)
|
||||
{
|
||||
OnMarkersChanged.Broadcast();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UWorldMapSubsystem::NoteMapOpened(FName LayerId, float MetresPerPixel, const FString& OpenedBy)
|
||||
{
|
||||
const UWorld* World = GetWorld();
|
||||
const UGameInstance* GameInstance = World ? World->GetGameInstance() : nullptr;
|
||||
UTelemetrySubsystem* Telemetry = GameInstance ? GameInstance->GetSubsystem<UTelemetrySubsystem>() : nullptr;
|
||||
if (!Telemetry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
Telemetry->Emit(TelemetryEvents::WorldMapOpened, FTelemetryPayload()
|
||||
.Set(TEXT("layer"), LayerId)
|
||||
.Set(TEXT("metres_per_pixel"), MetresPerPixel)
|
||||
.Set(TEXT("opened_by"), OpenedBy));
|
||||
}
|
||||
|
||||
bool UWorldMapSubsystem::GetLocalPlayerMarker(FWorldMapMarker& OutMarker) const
|
||||
{
|
||||
const UWorld* World = GetWorld();
|
||||
const APlayerController* Controller = World ? World->GetFirstPlayerController() : nullptr;
|
||||
if (!Controller)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// The spectator counts: someone flying the level with no pawn is still somewhere, and that is exactly when
|
||||
// a map is most useful.
|
||||
const AActor* Body = Controller->GetPawnOrSpectator();
|
||||
if (!Body)
|
||||
{
|
||||
Body = Controller->GetViewTarget();
|
||||
}
|
||||
if (!Body)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
OutMarker = FWorldMapMarker();
|
||||
OutMarker.WorldLocation = Body->GetActorLocation();
|
||||
OutMarker.Shape = EWorldMapMarkerShape::Arrow;
|
||||
OutMarker.Colour = FLinearColor(1.f, 0.95f, 0.35f);
|
||||
OutMarker.SizePx = 13.f;
|
||||
OutMarker.HeadingDegrees = static_cast<float>(Controller->GetControlRotation().Yaw);
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user