FVisualLogEntry* FVisualLogger::GetEntryToWrite(const class UObject* Object, float TimeStamp, ECreateIfNeeded ShouldCreate) { FVisualLogEntry* CurrentEntry = nullptr; UObject * LogOwner = FVisualLogger::FindRedirection(Object); if (LogOwner == nullptr || (LogOwner != Object && CurrentEntryPerObject.Contains(LogOwner) == false)) { return nullptr; } bool InitializeNewEntry = false; TWeakObjectPtr<UWorld> World = GetWorld(Object); if (CurrentEntryPerObject.Contains(LogOwner)) { CurrentEntry = &CurrentEntryPerObject[LogOwner]; InitializeNewEntry = TimeStamp > CurrentEntry->TimeStamp && ShouldCreate == ECreateIfNeeded::Create; if (World.IsValid()) { World->GetTimerManager().ClearTimer(VisualLoggerCleanupTimerHandle); for (auto& CurrentPair : CurrentEntryPerObject) { FVisualLogEntry* Entry = &CurrentPair.Value; if (Entry->TimeStamp >= 0 && Entry->TimeStamp < TimeStamp) { for (auto* Device : OutputDevices) { Device->Serialize(CurrentPair.Key, ObjectToNameMap[CurrentPair.Key], ObjectToClassNameMap[CurrentPair.Key], *Entry); } Entry->Reset(); } } } } if (!CurrentEntry) { // It's first and only one usage of LogOwner as regular object to get names. We assume once that LogOwner is correct here and only here. CurrentEntry = &CurrentEntryPerObject.Add(LogOwner); ObjectToNameMap.Add(LogOwner, LogOwner->GetFName()); ObjectToClassNameMap.Add(LogOwner, *(LogOwner->GetClass()->GetName())); ObjectToPointerMap.Add(LogOwner, LogOwner); InitializeNewEntry = true; } if (InitializeNewEntry) { CurrentEntry->Reset(); CurrentEntry->TimeStamp = TimeStamp; if (RedirectionMap.Contains(LogOwner)) { if (ObjectToPointerMap.Contains(LogOwner) && ObjectToPointerMap[LogOwner].IsValid()) { const class AActor* LogOwnerAsActor = Cast<class AActor>(LogOwner); if (LogOwnerAsActor) { LogOwnerAsActor->GrabDebugSnapshot(CurrentEntry); } } for (auto Child : RedirectionMap[LogOwner]) { if (Child.IsValid()) { const class AActor* ChildAsActor = Cast<class AActor>(Child.Get()); if (ChildAsActor) { ChildAsActor->GrabDebugSnapshot(CurrentEntry); } } } } else { const class AActor* ObjectAsActor = Cast<class AActor>(Object); if (ObjectAsActor) { CurrentEntry->Location = ObjectAsActor->GetActorLocation(); ObjectAsActor->GrabDebugSnapshot(CurrentEntry); } } } if (World.IsValid()) { //set next tick timer to flush obsolete/old entries World->GetTimerManager().SetTimer(VisualLoggerCleanupTimerHandle, FTimerDelegate::CreateLambda( [this, World](){ for (auto& CurrentPair : CurrentEntryPerObject) { FVisualLogEntry* Entry = &CurrentPair.Value; if (Entry->TimeStamp >= 0 && (!World.IsValid() || Entry->TimeStamp < World->GetTimeSeconds())) // CurrentEntry->TimeStamp == -1 means it's not initialized entry information { for (auto* Device : OutputDevices) { Device->Serialize(CurrentPair.Key, ObjectToNameMap[CurrentPair.Key], ObjectToClassNameMap[CurrentPair.Key], *Entry); } Entry->Reset(); } } } ), 0.1, false); } return CurrentEntry; }