double FFileManagerGeneric::GetFileAgeSeconds( const TCHAR* Filename ) { // make sure it exists if (!GetLowLevel().FileExists(Filename)) { return -1.0; } // get difference in time between now (UTC) and the filetime FTimespan Age = FDateTime::UtcNow() - GetTimeStamp(Filename); return Age.GetTotalSeconds(); }
float AFPSGPlayerController::timeUntilRespawn() { float timeToRespawn = 0.0f; if (!isAlive) { if (timeOfDeath != FDateTime::MinValue()) { FTimespan timeSinceDeath = FDateTime::Now() - timeOfDeath; timeToRespawn = respawnTime - static_cast<float>(timeSinceDeath.GetTotalSeconds()); } } return timeToRespawn; }
EIPv6SocketInternalState::Return FSocketBSDIPv6::HasState(EIPv6SocketInternalState::Param State, FTimespan WaitTime) { #if PLATFORM_HAS_BSD_SOCKET_FEATURE_SELECT // convert WaitTime to a timeval timeval Time; Time.tv_sec = (int32)WaitTime.GetTotalSeconds(); Time.tv_usec = WaitTime.GetMilliseconds() * 1000; fd_set SocketSet; // Set up the socket sets we are interested in (just this one) FD_ZERO(&SocketSet); FD_SET(Socket, &SocketSet); // Check the status of the state int32 SelectStatus = 0; switch (State) { case EIPv6SocketInternalState::CanRead: SelectStatus = select(Socket + 1, &SocketSet, NULL, NULL, &Time); break; case EIPv6SocketInternalState::CanWrite: SelectStatus = select(Socket + 1, NULL, &SocketSet, NULL, &Time); break; case EIPv6SocketInternalState::HasError: SelectStatus = select(Socket + 1, NULL, NULL, &SocketSet, &Time); break; } // if the select returns a positive number, the socket had the state, 0 means didn't have it, and negative is API error condition (not socket's error state) return SelectStatus > 0 ? EIPv6SocketInternalState::Yes : SelectStatus == 0 ? EIPv6SocketInternalState::No : EIPv6SocketInternalState::EncounteredError; #else UE_LOG(LogSockets, Fatal, TEXT("This platform doesn't support select(), but FSocketBSD::HasState was not overridden")); return EIPv6SocketInternalState::EncounteredError; #endif }
TArray<FColor> USceneCapturer::SaveAtlas(FString Folder, const TArray<FColor>& SurfaceData) { SCOPE_CYCLE_COUNTER( STAT_SPSavePNG ); TArray<FColor> SphericalAtlas; SphericalAtlas.AddZeroed(SphericalAtlasWidth * SphericalAtlasHeight); const FVector2D slicePlaneDim = FVector2D( 2.0f * FMath::Tan(FMath::DegreesToRadians(sliceHFov) / 2.0f), 2.0f * FMath::Tan(FMath::DegreesToRadians(sliceVFov) / 2.0f)); //For each direction, // Find corresponding slice // Calculate intersection of slice plane // Calculate intersection UVs by projecting onto plane tangents // Supersample that UV coordinate from the unprojected atlas { SCOPE_CYCLE_COUNTER(STAT_SPSampleSpherical); // Dump out how long the process took const FDateTime SamplingStartTime = FDateTime::UtcNow(); UE_LOG(LogStereoPanorama, Log, TEXT("Sampling atlas...")); for (int32 y = 0; y < SphericalAtlasHeight; y++) { for (int32 x = 0; x < SphericalAtlasWidth; x++) { FLinearColor samplePixelAccum = FLinearColor(0, 0, 0, 0); //TODO: ikrimae: Seems that bilinear filtering sans supersampling is good enough. Supersampling sans bilerp seems best. // After more tests, come back to optimize by folding supersampling in and remove this outer sampling loop. const auto& ssPattern = g_ssPatterns[SSMethod]; for (int32 SampleCount = 0; SampleCount < ssPattern.numSamples; SampleCount++) { const float sampleU = ((float)x + ssPattern.ssOffsets[SampleCount].X) / SphericalAtlasWidth; const float sampleV = ((float)y + ssPattern.ssOffsets[SampleCount].Y) / SphericalAtlasHeight; const float sampleTheta = sampleU * 360.0f; const float samplePhi = sampleV * 180.0f; const FVector sampleDir = FVector( FMath::Sin(FMath::DegreesToRadians(samplePhi)) * FMath::Cos(FMath::DegreesToRadians(sampleTheta)), FMath::Sin(FMath::DegreesToRadians(samplePhi)) * FMath::Sin(FMath::DegreesToRadians(sampleTheta)), FMath::Cos(FMath::DegreesToRadians(samplePhi))); //TODO: ikrimae: ugh, ugly. const int32 sliceXIndex = FMath::TruncToInt(FRotator::ClampAxis(sampleTheta + hAngIncrement / 2.0f) / hAngIncrement); int32 sliceYIndex = 0; //Slice Selection = slice with max{sampleDir dot sliceNormal } { float largestCosAngle = 0; for (int VerticalStep = 0; VerticalStep < NumberOfVerticalSteps; VerticalStep++) { const FVector2D sliceCenterThetaPhi = FVector2D( hAngIncrement * sliceXIndex, vAngIncrement * VerticalStep); //TODO: ikrimae: There has got to be a faster way. Rethink reparametrization later const FVector sliceDir = FVector( FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.Y))); const float cosAngle = sampleDir | sliceDir; if (cosAngle > largestCosAngle) { largestCosAngle = cosAngle; sliceYIndex = VerticalStep; } } } const FVector2D sliceCenterThetaPhi = FVector2D( hAngIncrement * sliceXIndex, vAngIncrement * sliceYIndex); //TODO: ikrimae: Reparameterize with an inverse mapping (e.g. project from slice pixels onto final u,v coordinates. // Should make code simpler and faster b/c reduces to handful of sin/cos calcs per slice. // Supersampling will be more difficult though. const FVector sliceDir = FVector( FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.Y))); const FPlane slicePlane = FPlane(sliceDir, -sliceDir); //Tangents from partial derivatives of sphere equation const FVector slicePlanePhiTangent = FVector( FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), -FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.Y))).GetSafeNormal(); //Should be reconstructed to get around discontinuity of theta tangent at nodal points const FVector slicePlaneThetaTangent = (sliceDir ^ slicePlanePhiTangent).GetSafeNormal(); //const FVector slicePlaneThetaTangent = FVector( // -FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), // FMath::Sin(FMath::DegreesToRadians(sliceCenterThetaPhi.Y)) * FMath::Cos(FMath::DegreesToRadians(sliceCenterThetaPhi.X)), // 0).SafeNormal(); check(!slicePlaneThetaTangent.IsZero() && !slicePlanePhiTangent.IsZero()); const double t = (double)-slicePlane.W / (sampleDir | sliceDir); const FVector sliceIntersection = FVector(t * sampleDir.X, t * sampleDir.Y, t * sampleDir.Z); //Calculate scalar projection of sliceIntersection onto tangent vectors. a dot b / |b| = a dot b when tangent vectors are normalized //Then reparameterize to U,V of the sliceplane based on slice plane dimensions const float sliceU = (sliceIntersection | slicePlaneThetaTangent) / slicePlaneDim.X; const float sliceV = (sliceIntersection | slicePlanePhiTangent) / slicePlaneDim.Y; check(sliceU >= -(0.5f + KINDA_SMALL_NUMBER) && sliceU <= (0.5f + KINDA_SMALL_NUMBER)); check(sliceV >= -(0.5f + KINDA_SMALL_NUMBER) && sliceV <= (0.5f + KINDA_SMALL_NUMBER)); //TODO: ikrimae: Supersample/bilinear filter const int32 slicePixelX = FMath::TruncToInt(dbgMatchCaptureSliceFovToAtlasSliceFov ? sliceU * StripWidth : sliceU * CaptureWidth); const int32 slicePixelY = FMath::TruncToInt(dbgMatchCaptureSliceFovToAtlasSliceFov ? sliceV * StripHeight : sliceV * CaptureHeight); FLinearColor slicePixelSample; if (bEnableBilerp) { //TODO: ikrimae: Clean up later; too tired now const int32 sliceCenterPixelX = (sliceXIndex + 0.5f) * StripWidth; const int32 sliceCenterPixelY = (sliceYIndex + 0.5f) * StripHeight; const FIntPoint atlasSampleTL(sliceCenterPixelX + FMath::Clamp(slicePixelX , -StripWidth/2, StripWidth/2), sliceCenterPixelY + FMath::Clamp(slicePixelY , -StripHeight/2, StripHeight/2)); const FIntPoint atlasSampleTR(sliceCenterPixelX + FMath::Clamp(slicePixelX + 1, -StripWidth/2, StripWidth/2), sliceCenterPixelY + FMath::Clamp(slicePixelY , -StripHeight/2, StripHeight/2)); const FIntPoint atlasSampleBL(sliceCenterPixelX + FMath::Clamp(slicePixelX , -StripWidth/2, StripWidth/2), sliceCenterPixelY + FMath::Clamp(slicePixelY + 1, -StripHeight/2, StripHeight/2)); const FIntPoint atlasSampleBR(sliceCenterPixelX + FMath::Clamp(slicePixelX + 1, -StripWidth/2, StripWidth/2), sliceCenterPixelY + FMath::Clamp(slicePixelY + 1, -StripHeight/2, StripHeight/2)); const FColor pixelColorTL = SurfaceData[atlasSampleTL.Y * UnprojectedAtlasWidth + atlasSampleTL.X]; const FColor pixelColorTR = SurfaceData[atlasSampleTR.Y * UnprojectedAtlasWidth + atlasSampleTR.X]; const FColor pixelColorBL = SurfaceData[atlasSampleBL.Y * UnprojectedAtlasWidth + atlasSampleBL.X]; const FColor pixelColorBR = SurfaceData[atlasSampleBR.Y * UnprojectedAtlasWidth + atlasSampleBR.X]; const float fracX = FMath::Frac(dbgMatchCaptureSliceFovToAtlasSliceFov ? sliceU * StripWidth : sliceU * CaptureWidth); const float fracY = FMath::Frac(dbgMatchCaptureSliceFovToAtlasSliceFov ? sliceV * StripHeight : sliceV * CaptureHeight); //Reinterpret as linear (a.k.a dont apply srgb inversion) slicePixelSample = FMath::BiLerp( pixelColorTL.ReinterpretAsLinear(), pixelColorTR.ReinterpretAsLinear(), pixelColorBL.ReinterpretAsLinear(), pixelColorBR.ReinterpretAsLinear(), fracX, fracY); } else { const int32 sliceCenterPixelX = (sliceXIndex + 0.5f) * StripWidth; const int32 sliceCenterPixelY = (sliceYIndex + 0.5f) * StripHeight; const int32 atlasSampleX = sliceCenterPixelX + slicePixelX; const int32 atlasSampleY = sliceCenterPixelY + slicePixelY; slicePixelSample = SurfaceData[atlasSampleY * UnprojectedAtlasWidth + atlasSampleX].ReinterpretAsLinear(); } samplePixelAccum += slicePixelSample; ////Output color map of projections //const FColor debugEquiColors[12] = { // FColor(205, 180, 76), // FColor(190, 88, 202), // FColor(127, 185, 194), // FColor(90, 54, 47), // FColor(197, 88, 53), // FColor(197, 75, 124), // FColor(130, 208, 72), // FColor(136, 211, 153), // FColor(126, 130, 207), // FColor(83, 107, 59), // FColor(200, 160, 157), // FColor(80, 66, 106) //}; //samplePixelAccum = ssPattern.numSamples * debugEquiColors[sliceYIndex * 4 + sliceXIndex]; } SphericalAtlas[y * SphericalAtlasWidth + x] = (samplePixelAccum / ssPattern.numSamples).Quantize(); // Force alpha value if (bForceAlpha) { SphericalAtlas[y * SphericalAtlasWidth + x].A = 255; } } } //Blit the first column into the last column to make the stereo image seamless at theta=360 for (int32 y = 0; y < SphericalAtlasHeight; y++) { SphericalAtlas[y * SphericalAtlasWidth + (SphericalAtlasWidth - 1)] = SphericalAtlas[y * SphericalAtlasWidth + 0]; } const FTimespan SamplingDuration = FDateTime::UtcNow() - SamplingStartTime; UE_LOG(LogStereoPanorama, Log, TEXT("...done! Duration: %g seconds"), SamplingDuration.GetTotalSeconds()); } // Generate name FString FrameString = FString::Printf( TEXT( "%s_%05d.png" ), *Folder, CurrentFrameCount ); FString AtlasName = OutputDir / Timestamp / FrameString; UE_LOG( LogStereoPanorama, Log, TEXT( "Writing atlas: %s" ), *AtlasName ); // Write out PNG //TODO: ikrimae: Use threads to write out the images for performance IImageWrapperPtr ImageWrapper = ImageWrapperModule.CreateImageWrapper( EImageFormat::PNG ); ImageWrapper->SetRaw(SphericalAtlas.GetData(), SphericalAtlas.GetAllocatedSize(), SphericalAtlasWidth, SphericalAtlasHeight, ERGBFormat::BGRA, 8); const TArray<uint8>& PNGData = ImageWrapper->GetCompressed(100); FFileHelper::SaveArrayToFile( PNGData, *AtlasName ); if (FStereoPanoramaManager::GenerateDebugImages->GetInt() != 0) { FString FrameStringUnprojected = FString::Printf(TEXT("%s_%05d_Unprojected.png"), *Folder, CurrentFrameCount); FString AtlasNameUnprojected = OutputDir / Timestamp / FrameStringUnprojected; ImageWrapper->SetRaw(SurfaceData.GetData(), SurfaceData.GetAllocatedSize(), UnprojectedAtlasWidth, UnprojectedAtlasHeight, ERGBFormat::BGRA, 8); const TArray<uint8>& PNGDataUnprojected = ImageWrapper->GetCompressed(100); FFileHelper::SaveArrayToFile(PNGData, *AtlasNameUnprojected); } ImageWrapper.Reset(); UE_LOG( LogStereoPanorama, Log, TEXT( " ... done!" ), *AtlasName ); return SphericalAtlas; }
float UKismetMathLibrary::GetTotalSeconds( FTimespan A ) { return A.GetTotalSeconds(); }
void USocketIOClientComponent::SetupCallbacks() { //Sync current connected state bIsConnected = NativeClient->bIsConnected; if (bIsConnected) { SessionId = NativeClient->SessionId; AddressAndPort = NativeClient->AddressAndPort; } NativeClient->OnConnectedCallback = [this](const FString& InSessionId) { FLambdaRunnable::RunShortLambdaOnGameThread([this, InSessionId] { if (this) { bIsConnected = true; SessionId = InSessionId; OnConnected.Broadcast(SessionId, bIsHavingConnectionProblems); bIsHavingConnectionProblems = false; } }); }; const FSIOCCloseEventSignature OnDisconnectedSafe = OnDisconnected; NativeClient->OnDisconnectedCallback = [OnDisconnectedSafe, this](const ESIOConnectionCloseReason Reason) { FLambdaRunnable::RunShortLambdaOnGameThread([OnDisconnectedSafe, this, Reason] { if (this && OnDisconnectedSafe.IsBound()) { bIsConnected = false; OnDisconnectedSafe.Broadcast(Reason); } }); }; NativeClient->OnNamespaceConnectedCallback = [this](const FString& Namespace) { FLambdaRunnable::RunShortLambdaOnGameThread([this, Namespace] { if (this && OnSocketNamespaceConnected.IsBound()) { OnSocketNamespaceConnected.Broadcast(Namespace); } }); }; const FSIOCSocketEventSignature OnSocketNamespaceDisconnectedSafe = OnSocketNamespaceDisconnected; NativeClient->OnNamespaceDisconnectedCallback = [this, OnSocketNamespaceDisconnectedSafe](const FString& Namespace) { FLambdaRunnable::RunShortLambdaOnGameThread([OnSocketNamespaceDisconnectedSafe, this, Namespace] { if (this && OnSocketNamespaceDisconnectedSafe.IsBound()) { OnSocketNamespaceDisconnectedSafe.Broadcast(Namespace); } }); }; NativeClient->OnReconnectionCallback = [this](const uint32 AttemptCount, const uint32 DelayInMs) { FLambdaRunnable::RunShortLambdaOnGameThread([this, AttemptCount, DelayInMs] { //First time we know about this problem? if (!bIsHavingConnectionProblems) { TimeWhenConnectionProblemsStarted = FDateTime::Now(); bIsHavingConnectionProblems = true; } FTimespan Difference = FDateTime::Now() - TimeWhenConnectionProblemsStarted; float ElapsedInSec = Difference.GetTotalSeconds(); if (ReconnectionTimeout > 0 && ElapsedInSec>ReconnectionTimeout) { //Let's stop trying and disconnect if we're using timeouts Disconnect(); } if (this && OnConnectionProblems.IsBound()) { OnConnectionProblems.Broadcast(AttemptCount, DelayInMs, ElapsedInSec); } }); }; NativeClient->OnFailCallback = [this]() { FLambdaRunnable::RunShortLambdaOnGameThread([this] { OnFail.Broadcast(); }); }; }
UUpdateManager::EUpdateStartResult UUpdateManager::StartCheckInternal(bool bInCheckHotfixOnly) { EUpdateStartResult Result = EUpdateStartResult::None; if (!ChecksEnabled()) { UE_LOG(LogHotfixManager, Display, TEXT("Update checks disabled!")); bInitialUpdateFinished = true; auto StartDelegate = [this]() { CheckComplete(EUpdateCompletionStatus::UpdateSuccess_NoChange); }; DelayResponse(StartDelegate, 0.1f); return Result; } if (CurrentUpdateState == EUpdateState::UpdateIdle || CurrentUpdateState == EUpdateState::UpdatePending || CurrentUpdateState == EUpdateState::UpdateComplete) { bCheckHotfixAvailabilityOnly = bInCheckHotfixOnly; // Immediately move into a pending state so the UI state trigger fires SetUpdateState(EUpdateState::UpdatePending); const EUpdateCompletionStatus LastResult = LastCompletionResult[bCheckHotfixAvailabilityOnly]; const FTimespan DeltaTime = FDateTime::UtcNow() - LastUpdateCheck[bCheckHotfixAvailabilityOnly]; const bool bForceCheck = LastResult == EUpdateCompletionStatus::UpdateUnknown || LastResult == EUpdateCompletionStatus::UpdateFailure_PatchCheck || LastResult == EUpdateCompletionStatus::UpdateFailure_HotfixCheck || LastResult == EUpdateCompletionStatus::UpdateFailure_NotLoggedIn; static double CacheTimer = UPDATE_CHECK_SECONDS; const double TimeSinceCheck = DeltaTime.GetTotalSeconds(); if (bForceCheck || TimeSinceCheck >= CacheTimer) { auto StartDelegate = [this]() { // Check for a patch first, then hotfix application StartPatchCheck(); }; // Give the UI state widget a chance to start listening for delegates DelayResponse(StartDelegate, 0.2f); Result = EUpdateStartResult::UpdateStarted; } else { UE_LOG(LogHotfixManager, Display, TEXT("Returning cached update result %d"), (int32)LastResult); auto StartDelegate = [this, LastResult]() { CheckComplete(LastResult, false); }; DelayResponse(StartDelegate, 0.1f); Result = EUpdateStartResult::UpdateCached; } } else { UE_LOG(LogHotfixManager, Display, TEXT("Update already in progress")); } return Result; }
void UHTNPlannerComponent::ProcessExecutionRequest() { bRequestedExecutionUpdate = false; if(!IsRegistered()) { // it shouldn't be called, component is no longer valid return; } if(bIsPaused) { UE_VLOG(GetOwner(), LogHTNPlanner, Verbose, TEXT("Ignoring ProcessExecutionRequest call due to HTNPlannerComponent still being paused")); return; } //GEngine->AddOnScreenDebugMessage(-1, 1.5f, FColor::Yellow, TEXT("UHTNPlannerComponent::ProcessExecutionRequest()")); if(PendingExecution.IsValid()) { ProcessPendingExecution(); return; } if(NumStackElements == 0) { //BestPlan = nullptr; if(CurrentPlannerAsset->bLoop) { // finished execution of plan and we want to loop, so re-start RestartPlanner(); } else { bIsRunning = false; } return; } #if HTN_LOG_RUNTIME_STATS if(StartPlanningTime == FDateTime::MinValue()) { StartPlanningTime = FDateTime::UtcNow(); } #endif // HTN_LOG_RUNTIME_STATS FDateTime PlanningStart = FDateTime::UtcNow(); while(NumStackElements > 0) { // our stack is not empty FTimespan TimePlanned = FDateTime::UtcNow() - PlanningStart; if(TimePlanned.GetTotalSeconds() >= CurrentPlannerAsset->MaxSearchTime) { // we've spent our maximum allowed search time for this tick on planning, so need to continue some other time ScheduleExecutionUpdate(); #if HTN_LOG_RUNTIME_STATS CumulativeSearchTimeMs += TimePlanned.GetTotalMilliseconds(); ++CumulativeFrameCount; #endif return; } #if HTN_LOG_RUNTIME_STATS ++NumNodesExpanded; #endif if(PreviousPlan.IsValid()) { UE_LOG(LogHTNPlanner, Warning, TEXT("%d nodes in data structure(s)"), NumStackElements); } if(bProbabilisticPlanReuse && bHitLeaf) { // we've hit a leaf node, so it's time to re-evaluate whether we're ignoring plan reuse probabilistically bHitLeaf = false; if(NumStackElements == PlanningStack.Num()) { bIgnoringPlanReuse = false; // not ignoring plan reuse if everything's still in the non-prioritized stack } else { bIgnoringPlanReuse = (FMath::FRand() <= ProbabilityIgnorePlanReuse); } } const FHTNStackElement StackTop = PopStackElement(); if(StackTop.Cost + Cast<UTaskNetwork>(StackTop.TaskNetwork->Task)-> GetHeuristicCost(StackTop.WorldState, StackTop.TaskNetwork->GetMemory()) >= BestCost) { if(!bDepthFirstSearch) { // everything remaining in the heap will be at least as bad, and maybe worse PlanningStack.Empty(); NumStackElements = 0; } if(PreviousPlan.IsValid()) // we're doing plan reuse { // verify that all of our values of maximum streak lengths among unprocessed nodes are still correct UpdateMaxStreakLengths(); } continue; // we won't find any improvements down this path } UTaskNetwork* TopNetwork = Cast<UTaskNetwork>(StackTop.TaskNetwork->Task); if(TopNetwork->IsEmpty(StackTop.TaskNetwork->GetMemory())) { // we've found a path leading to a legal, complete Plan #if HTN_LOG_RUNTIME_STATS CumulativeSearchTimeMsTillLastImprovement = CumulativeSearchTimeMs + (FDateTime::UtcNow() - PlanningStart).GetTotalMilliseconds(); if(BestCost == TNumericLimits<float>::Max()) // this means that this is the first time we find a valid plan { CumulativeSearchTimeMsTillFirstPlan = CumulativeSearchTimeMsTillLastImprovement; FirstPlanCost = StackTop.Cost; } #endif BestPlan = StackTop.Plan; BestPlan->SetComplete(true); BestCost = StackTop.Cost; #if HTN_LOG_RUNTIME_STATS DataCollector->FoundSolution(BestCost, StackTop.Plan->GetPlanSize(), StackTop.Plan->GetSearchHistory().Num(), CumulativeSearchTimeMsTillLastImprovement, NumNodesExpanded); #endif if(CurrentPlannerAsset->bIgnoreTaskCosts) { // the HTN Planner doesn't care about finding optimal plans, only about finding the first one PlanningStack.Empty(); NumStackElements = 0; } else if(!bDepthFirstSearch) { // best-first search finds an optimal solution as first solution PlanningStack.Empty(); NumStackElements = 0; } if(PreviousPlan.IsValid()) // we're doing plan reuse { if(bProbabilisticPlanReuse) { bHitLeaf = true; } // verify that all of our values of maximum streak lengths among unprocessed nodes are still correct UpdateMaxStreakLengths(); } continue; } // find all tasks that share the highest priority amongst the tasks in the task network TArray<TSharedPtr<FHTNTaskInstance>> TaskInstances = TopNetwork->FindTasksWithoutPredecessors(StackTop.TaskNetwork->GetMemory()); for(const TSharedPtr<FHTNTaskInstance>& TaskInstance : TaskInstances) { UHTNTask* Task = TaskInstance->Task; if(UPrimitiveTask* PrimitiveTask = Cast<UPrimitiveTask>(Task)) { if(PrimitiveTask->IsApplicable(StackTop.WorldState, TaskInstance->GetMemory())) { // prepare a new element for the stack where we'll have applied this primitive task FHTNStackElement NewStackElement; NewStackElement.Cost = FMath::Max(0.f, StackTop.Cost) + PrimitiveTask->GetCost(StackTop.WorldState, TaskInstance->GetMemory()); TSharedPtr<FHTNPlan> Plan = StackTop.Plan->Copy(); Plan->AppendTaskInstance(TaskInstance); Plan->AppendSearchHistory(TaskInstance); TSharedPtr<FHTNTaskInstance> TaskNetwork = TopNetwork->Copy(StackTop.TaskNetwork); Cast<UTaskNetwork>(TaskNetwork->Task)->Remove(TaskInstance, TaskNetwork->GetMemory()); TSharedPtr<FHTNWorldState> WorldState = StackTop.WorldState->Copy(); PrimitiveTask->ApplyTo(WorldState, TaskInstance->GetMemory()); NewStackElement.Plan = Plan; NewStackElement.TaskNetwork = TaskNetwork; NewStackElement.WorldState = WorldState; if(PreviousPlan.IsValid()) { // we're doing plan reuse CurrentMatchingStreakLength = Plan->GetMatchingStreak(PreviousPlan, MinMatchingStreakLength); } AddStackElement(NewStackElement); // TO DO maybe should explicitly Move the NewStackElement? } else if(PreviousPlan.IsValid()) { if(bProbabilisticPlanReuse) { bHitLeaf = true; } } } else if(UCompoundTask* CompoundTask = Cast<UCompoundTask>(Task)) { TArray<TSharedPtr<FHTNTaskInstance>> Decompositions = CompoundTask->FindDecompositions(*this, StackTop.WorldState, TaskInstance->GetMemory()); // regardless of which decomposition we pick, effect on plan will be the same TSharedPtr<FHTNPlan> Plan = StackTop.Plan->Copy(); Plan->AppendSearchHistory(TaskInstance); if(PreviousPlan.IsValid()) { // we're doing plan reuse if(Decompositions.Num() == 0) // leaf node { if(bProbabilisticPlanReuse) { bHitLeaf = true; } } CurrentMatchingStreakLength = Plan->GetMatchingStreak(PreviousPlan, MinMatchingStreakLength); TArray<FHTNStackElement> NewStackElements; TArray<FHTNStackElement> NewFifoElements; for(int32 Idx = 0; Idx < Decompositions.Num(); ++Idx) { const TSharedPtr<FHTNTaskInstance>& Decomposition = Decompositions[Idx]; // prepare a new element where we'll have decomposed this compound task FHTNStackElement NewStackElement; NewStackElement.WorldState = StackTop.WorldState; NewStackElement.Cost = StackTop.Cost; TSharedPtr<FHTNTaskInstance> TaskNetwork = TopNetwork->Copy(StackTop.TaskNetwork); Cast<UTaskNetwork>(TaskNetwork->Task)->Replace(TaskInstance, Decomposition, TaskNetwork->GetMemory()); NewStackElement.Plan = Plan->Copy(); NewStackElement.TaskNetwork = TaskNetwork; if(bProbabilisticPlanReuse && bIgnoringPlanReuse) { // probabilistically ignoring plan reuse, so no need to waste time computing streak lengths NewStackElements.Push(NewStackElement); } else { if(MaxCurrentMatchingStreakLength > 0 && CurrentMatchingStreakLength == 0) { // this element belongs in a FIFO queue NewFifoElements.Push(NewStackElement); } else { // this element belongs in a stack NewStackElements.Push(NewStackElement); } } } // first we'll add all the elements that belong in FIFO queues to their FIFO queues (in given order) for(int32 Idx = 0; Idx < NewFifoElements.Num(); ++Idx) { AddStackElement(NewFifoElements[Idx]); } // now we'll add all the elements that belong in some stack to those stacks (reverse order) while(NewStackElements.Num() > 0) { AddStackElement(NewStackElements.Pop()); } } else { // we're not doing plan reuse // looping through Decompositions in reverse order so that they'll be popped off stack in correct order again for(int32 Idx = Decompositions.Num() - 1; Idx >= 0; --Idx) { const TSharedPtr<FHTNTaskInstance>& Decomposition = Decompositions[Idx]; // prepare a new element for the stack where we'll have decomposed this compound task FHTNStackElement NewStackElement; NewStackElement.WorldState = StackTop.WorldState; NewStackElement.Cost = StackTop.Cost; TSharedPtr<FHTNTaskInstance> TaskNetwork = TopNetwork->Copy(StackTop.TaskNetwork); Cast<UTaskNetwork>(TaskNetwork->Task)->Replace(TaskInstance, Decomposition, TaskNetwork->GetMemory()); NewStackElement.Plan = Plan->Copy(); NewStackElement.TaskNetwork = TaskNetwork; AddStackElement(NewStackElement); // TO DO maybe should explicitly Move the NewStackElement? } } } else { UE_LOG(LogHTNPlanner, Error, TEXT("UHTNPlannerComponent::ProcessExecutionRequest() encountered a Task that was neither Primitive nor Compound!")) } } if(PreviousPlan.IsValid()) // we're doing plan reuse { // verify that all of our values of maximum streak lengths among unprocessed nodes are still correct UpdateMaxStreakLengths(); } } if(BestPlan.IsValid()) { #if HTN_LOG_RUNTIME_STATS CumulativeSearchTimeMs += (FDateTime::UtcNow() - PlanningStart).GetTotalMilliseconds(); ++CumulativeFrameCount; CumulativeSearchTimespan = FDateTime::UtcNow() - StartPlanningTime; // print runtime stats //UE_LOG(LogHTNPlanner, Warning, TEXT("Cumulative Search Timespan = %.2f ms"), CumulativeSearchTimespan.GetTotalMilliseconds()); UE_LOG(LogHTNPlanner, Warning, TEXT("Cumulative Search Time = %.2f ms"), CumulativeSearchTimeMs); UE_LOG(LogHTNPlanner, Warning, TEXT("Cumulative Search Time Till Last Improvement = %.2f ms"), CumulativeSearchTimeMsTillLastImprovement); UE_LOG(LogHTNPlanner, Warning, TEXT("Cumulative Search Time First Plan = %.2f ms"), CumulativeSearchTimeMsTillFirstPlan); //UE_LOG(LogHTNPlanner, Warning, TEXT("Cumulative Frame Count = %d frames"), CumulativeFrameCount); UE_LOG(LogHTNPlanner, Warning, TEXT("Num Nodes Expanded = %d"), NumNodesExpanded); UE_LOG(LogHTNPlanner, Warning, TEXT("Cost of first plan found = %.2f"), FirstPlanCost); if(PreviousPlan.IsValid()) { UE_LOG(LogHTNPlanner, Warning, TEXT("Longest matching streak = %d"), BestPlan->GetLongestMatchingStreak(PreviousPlan, MinMatchingStreakLength)); } DataCollector->OptimalityProven(CumulativeSearchTimeMs, NumNodesExpanded); #endif // we have a complete plan, so we'll want to execute the next task in the plan UE_LOG(LogHTNPlanner, Warning, TEXT("Found Plan with size = %d, cost = %.2f, search history size = %d!"), BestPlan->GetPlanSize(), BestCost, BestPlan->GetSearchHistory().Num()); if(CurrentPlannerAsset->bExecutePlan) { PendingExecution = BestPlan->GetTaskInstanceToExecute(); ProcessPendingExecution(); } else { bIsRunning = false; } } }
void FNetworkPlatformFile::InitializeAfterSetActive() { double NetworkFileStartupTime = 0.0; { SCOPE_SECONDS_COUNTER(NetworkFileStartupTime); // send the filenames and timestamps to the server FNetworkFileArchive Payload(NFS_Messages::GetFileList); FillGetFileList(Payload, false); // send the directories over, and wait for a response FArrayReader Response; if (!SendPayloadAndReceiveResponse(Payload, Response)) { delete Transport; return; } else { // receive the cooked version information int32 ServerPackageVersion = 0; int32 ServerPackageLicenseeVersion = 0; ProcessServerInitialResponse(Response, ServerPackageVersion, ServerPackageLicenseeVersion); // receive a list of the cache files and their timestamps TMap<FString, FDateTime> ServerCachedFiles; Response << ServerCachedFiles; bool bDeleteAllFiles = true; // Check the stored cooked version FString CookedVersionFile = FPaths::GeneratedConfigDir() / TEXT("CookedVersion.txt"); if (InnerPlatformFile->FileExists(*CookedVersionFile) == true) { IFileHandle* FileHandle = InnerPlatformFile->OpenRead(*CookedVersionFile); if (FileHandle != NULL) { int32 StoredPackageCookedVersion; int32 StoredPackageCookedLicenseeVersion; if (FileHandle->Read((uint8*)&StoredPackageCookedVersion, sizeof(int32)) == true) { if (FileHandle->Read((uint8*)&StoredPackageCookedLicenseeVersion, sizeof(int32)) == true) { if ((ServerPackageVersion == StoredPackageCookedVersion) && (ServerPackageLicenseeVersion == StoredPackageCookedLicenseeVersion)) { bDeleteAllFiles = false; } else { UE_LOG(LogNetworkPlatformFile, Display, TEXT("Engine version mismatch: Server %d.%d, Stored %d.%d\n"), ServerPackageVersion, ServerPackageLicenseeVersion, StoredPackageCookedVersion, StoredPackageCookedLicenseeVersion); } } } delete FileHandle; } } else { UE_LOG(LogNetworkPlatformFile, Display, TEXT("Cooked version file missing: %s\n"), *CookedVersionFile); } if (bDeleteAllFiles == true) { // Make sure the config file exists... InnerPlatformFile->CreateDirectoryTree(*(FPaths::GeneratedConfigDir())); // Update the cooked version file IFileHandle* FileHandle = InnerPlatformFile->OpenWrite(*CookedVersionFile); if (FileHandle != NULL) { FileHandle->Write((const uint8*)&ServerPackageVersion, sizeof(int32)); FileHandle->Write((const uint8*)&ServerPackageLicenseeVersion, sizeof(int32)); delete FileHandle; } } // list of directories to skip TArray<FString> DirectoriesToSkip; TArray<FString> DirectoriesToNotRecurse; // use the timestamp grabbing visitor to get all the content times FLocalTimestampDirectoryVisitor Visitor(*InnerPlatformFile, DirectoriesToSkip, DirectoriesToNotRecurse, false); TArray<FString> RootContentPaths; FPackageName::QueryRootContentPaths( RootContentPaths ); for( TArray<FString>::TConstIterator RootPathIt( RootContentPaths ); RootPathIt; ++RootPathIt ) { const FString& RootPath = *RootPathIt; const FString& ContentFolder = FPackageName::LongPackageNameToFilename(RootPath); InnerPlatformFile->IterateDirectory( *ContentFolder, Visitor); } // delete out of date files using the server cached files for (TMap<FString, FDateTime>::TIterator It(ServerCachedFiles); It; ++It) { bool bDeleteFile = bDeleteAllFiles; FString ServerFile = It.Key(); // Convert the filename to the client version ConvertServerFilenameToClientFilename(ServerFile); // Set it in the visitor file times list Visitor.FileTimes.Add(ServerFile, FDateTime::MinValue()); if (bDeleteFile == false) { // Check the time stamps... // get local time FDateTime LocalTime = InnerPlatformFile->GetTimeStamp(*ServerFile); // If local time == MinValue than the file does not exist in the cache. if (LocalTime != FDateTime::MinValue()) { FDateTime ServerTime = It.Value(); // delete if out of date // We will use 1.0 second as the tolerance to cover any platform differences in resolution FTimespan TimeDiff = LocalTime - ServerTime; double TimeDiffInSeconds = TimeDiff.GetTotalSeconds(); bDeleteFile = (TimeDiffInSeconds > 1.0) || (TimeDiffInSeconds < -1.0); if (bDeleteFile == true) { if (InnerPlatformFile->FileExists(*ServerFile) == true) { UE_LOG(LogNetworkPlatformFile, Display, TEXT("Deleting cached file: TimeDiff %5.3f, %s"), TimeDiffInSeconds, *It.Key()); } else { // It's a directory bDeleteFile = false; } } } } if (bDeleteFile == true) { InnerPlatformFile->DeleteFile(*ServerFile); } } // Any content files we have locally that were not cached, delete them for (TMap<FString, FDateTime>::TIterator It(Visitor.FileTimes); It; ++It) { if (It.Value() != FDateTime::MinValue()) { // This was *not* found in the server file list... delete it UE_LOG(LogNetworkPlatformFile, Display, TEXT("Deleting cached file: %s"), *It.Key()); InnerPlatformFile->DeleteFile(*It.Key()); } } // make sure we can sync a file FString TestSyncFile = FPaths::Combine(*(FPaths::EngineDir()), TEXT("Config/BaseEngine.ini")); InnerPlatformFile->SetReadOnly(*TestSyncFile, false); InnerPlatformFile->DeleteFile(*TestSyncFile); if (InnerPlatformFile->FileExists(*TestSyncFile)) { UE_LOG(LogNetworkPlatformFile, Fatal, TEXT("Could not delete file sync test file %s."), *TestSyncFile); } EnsureFileIsLocal(TestSyncFile); if (!InnerPlatformFile->FileExists(*TestSyncFile) || InnerPlatformFile->FileSize(*TestSyncFile) < 1) { UE_LOG(LogNetworkPlatformFile, Fatal, TEXT("Could not sync test file %s."), *TestSyncFile); } } } FPlatformMisc::LowLevelOutputDebugStringf(TEXT("Network file startup time: %5.3f seconds\n"), NetworkFileStartupTime); }
/** * Dumps the information held within the EditorPerfCaptureParameters struct into a CSV file. * @param EditorPerfStats is the name of the struct that holds the needed performance information. */ void EditorPerfDump(EditorPerfCaptureParameters& EditorPerfStats) { UE_LOG(LogEditorAutomationTests, Log, TEXT("Begin generating the editor performance charts.")); //The file location where to save the data. FString DataFileLocation = FPaths::Combine(*FPaths::AutomationLogDir(), TEXT("Performance"), *EditorPerfStats.MapName); //Get the map load time (in seconds) from the text file that is created when the load map latent command is ran. EditorPerfStats.MapLoadTime = 0; FString MapLoadTimeFileLocation = FPaths::Combine(*DataFileLocation, TEXT("RAWMapLoadTime.txt")); if (FPaths::FileExists(*MapLoadTimeFileLocation)) { TArray<FString> SavedMapLoadTimes; FAutomationEditorCommonUtils::CreateArrayFromFile(MapLoadTimeFileLocation, SavedMapLoadTimes); EditorPerfStats.MapLoadTime = FCString::Atof(*SavedMapLoadTimes.Last()); } //Filename for the RAW csv which holds the data gathered from a single test ran. FString RAWCSVFilePath = FString::Printf(TEXT("%s/RAW_%s_%s.csv"), *DataFileLocation, *EditorPerfStats.MapName, *FDateTime::Now().ToString()); //Filename for the pretty csv file. FString PerfCSVFilePath = FString::Printf(TEXT("%s/%s_Performance.csv"), *DataFileLocation, *EditorPerfStats.MapName); //Create the raw csv and then add the title row it. FArchive* RAWCSVArchive = IFileManager::Get().CreateFileWriter(*RAWCSVFilePath); FString RAWCSVLine = (TEXT("Map Name, Changelist, Time Stamp, Map Load Time, Average FPS, Frame Time, Used Physical Memory, Used Virtual Memory, Used Peak Physical, Used Peak Virtual, Available Physical Memory, Available Virtual Memory\n")); RAWCSVArchive->Serialize(TCHAR_TO_ANSI(*RAWCSVLine), RAWCSVLine.Len()); //Dump the stats from each run to the raw csv file and then close it. for (int32 i = 0; i < EditorPerfStats.TimeStamp.Num(); i++) { //If the raw file isn't available to write to then we'll fail back this test. if ( FAutomationEditorCommonUtils::IsArchiveWriteable(RAWCSVFilePath, RAWCSVArchive)) { RAWCSVLine = FString::Printf(TEXT("%s,%s,%s,%.3f,%.1f,%.1f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f%s"), *EditorPerfStats.MapName, *FEngineVersion::Current().ToString(EVersionComponent::Changelist), *EditorPerfStats.FormattedTimeStamp[i], EditorPerfStats.MapLoadTime, EditorPerfStats.AverageFPS[i], EditorPerfStats.AverageFrameTime[i], EditorPerfStats.UsedPhysical[i], EditorPerfStats.UsedVirtual[i], EditorPerfStats.PeakUsedPhysical[i], EditorPerfStats.PeakUsedVirtual[i], EditorPerfStats.AvailablePhysical[i], EditorPerfStats.AvailableVirtual[i], LINE_TERMINATOR); RAWCSVArchive->Serialize(TCHAR_TO_ANSI(*RAWCSVLine), RAWCSVLine.Len()); } } RAWCSVArchive->Close(); //Get the final pretty data for the Performance csv file. float AverageFPS = FAutomationEditorCommonUtils::TotalFromFloatArray(EditorPerfStats.AverageFPS, true); float AverageFrameTime = FAutomationEditorCommonUtils::TotalFromFloatArray(EditorPerfStats.AverageFrameTime, true); float MemoryUsedPhysical = FAutomationEditorCommonUtils::TotalFromFloatArray(EditorPerfStats.UsedPhysical, true); float MemoryAvailPhysAvg = FAutomationEditorCommonUtils::TotalFromFloatArray(EditorPerfStats.AvailablePhysical, true); float MemoryAvailVirtualAvg = FAutomationEditorCommonUtils::TotalFromFloatArray(EditorPerfStats.AvailableVirtual, true); float MemoryUsedVirtualAvg = FAutomationEditorCommonUtils::TotalFromFloatArray(EditorPerfStats.UsedVirtual, true); float MemoryUsedPeak = FAutomationEditorCommonUtils::LargestValueInFloatArray(EditorPerfStats.PeakUsedPhysical); float MemoryUsedPeakVirtual = FAutomationEditorCommonUtils::LargestValueInFloatArray(EditorPerfStats.PeakUsedVirtual); //TestRunDuration is the length of time the test lasted in ticks. FTimespan TestRunDuration = (EditorPerfStats.TimeStamp.Last().GetTicks() - EditorPerfStats.TimeStamp[0].GetTicks()) + ETimespan::TicksPerSecond; //The performance csv file will be created if it didn't exist prior to the start of this test. if (!FPaths::FileExists(*PerfCSVFilePath)) { FArchive* FinalCSVArchive = IFileManager::Get().CreateFileWriter(*PerfCSVFilePath); if ( FAutomationEditorCommonUtils::IsArchiveWriteable(PerfCSVFilePath, FinalCSVArchive)) { FString FinalCSVLine = (TEXT("Date, Map Name, Changelist, Test Run Time , Map Load Time, Average FPS, Average MS, Used Physical KB, Used Virtual KB, Used Peak Physcial KB, Used Peak Virtual KB, Available Physical KB, Available Virtual KB\n")); FinalCSVArchive->Serialize(TCHAR_TO_ANSI(*FinalCSVLine), FinalCSVLine.Len()); FinalCSVArchive->Close(); } } //Load the existing performance csv so that it doesn't get saved over and lost. FString OldPerformanceCSVFile; FFileHelper::LoadFileToString(OldPerformanceCSVFile, *PerfCSVFilePath); FArchive* FinalCSVArchive = IFileManager::Get().CreateFileWriter(*PerfCSVFilePath); if ( FAutomationEditorCommonUtils::IsArchiveWriteable(PerfCSVFilePath, FinalCSVArchive)) { //Dump the old performance csv file data to the new csv file. FinalCSVArchive->Serialize(TCHAR_TO_ANSI(*OldPerformanceCSVFile), OldPerformanceCSVFile.Len()); //Dump the pretty stats to the Performance CSV file and then close it so we can edit it while the engine is still running. FString FinalCSVLine = FString::Printf(TEXT("%s,%s,%s,%.0f,%.3f,%.1f,%.1f,%.0f,%.0f,%.0f,%.0f,%.0f,%.0f%s"), *FDateTime::Now().ToString(), *EditorPerfStats.MapName, *FEngineVersion::Current().ToString(EVersionComponent::Changelist), TestRunDuration.GetTotalSeconds(), EditorPerfStats.MapLoadTime, AverageFPS, AverageFrameTime, MemoryUsedPhysical, MemoryUsedVirtualAvg, MemoryUsedPeak, MemoryUsedPeakVirtual, MemoryAvailPhysAvg, MemoryAvailVirtualAvg, LINE_TERMINATOR); FinalCSVArchive->Serialize(TCHAR_TO_ANSI(*FinalCSVLine), FinalCSVLine.Len()); FinalCSVArchive->Close(); } //Display the test results to the user. UE_LOG(LogEditorAutomationTests, Display, TEXT("AVG FPS: '%.1f'"), AverageFPS); UE_LOG(LogEditorAutomationTests, Display, TEXT("AVG Frame Time: '%.1f' ms"), AverageFrameTime); UE_LOG(LogEditorAutomationTests, Display, TEXT("AVG Used Physical Memory: '%.0f' kb"), MemoryUsedPhysical); UE_LOG(LogEditorAutomationTests, Display, TEXT("AVG Used Virtual Memory: '%.0f' kb"), MemoryUsedVirtualAvg); UE_LOG(LogEditorAutomationTests, Display, TEXT("Performance csv file is located here: %s"), *FPaths::ConvertRelativePathToFull(PerfCSVFilePath)); UE_LOG(LogEditorAutomationTests, Log, TEXT("Performance csv file is located here: %s"), *FPaths::ConvertRelativePathToFull(PerfCSVFilePath)); UE_LOG(LogEditorAutomationTests, Log, TEXT("Raw performance csv file is located here: %s"), *FPaths::ConvertRelativePathToFull(RAWCSVFilePath)); }