diff --git a/BetaHubBugReporter.uplugin b/BetaHubBugReporter.uplugin index 8deadf9..2389b21 100644 --- a/BetaHubBugReporter.uplugin +++ b/BetaHubBugReporter.uplugin @@ -6,7 +6,7 @@ "Description": "Bug reporting functionality for BetaHub.io", "Category": "Other", "CreatedBy": "Upsoft sp. z o. o.", - "EngineVersion": "5.3.0", + "EngineVersion": [ "5.3.0", "5.4.0", "5.5.0" ], "CreatedByURL": "https://betahub.io/", "DocsURL": "https://www.betahub.io/docs/integration-guides/#unreal-plugin-integration", "MarketplaceURL": "", @@ -25,6 +25,12 @@ ] } ], + "Plugins": [ + { + "Name": "EnhancedInput", + "Enabled": true + } + ], "AdditionalFiles": [ "ThirdParty/FFmpeg/Windows/ffmpeg.exe" ] diff --git a/Content/BugReportForm.uasset b/Content/BugReportForm.uasset index 9d49d0c..b63c14e 100644 Binary files a/Content/BugReportForm.uasset and b/Content/BugReportForm.uasset differ diff --git a/Source/BetaHubBugReporter/BetaHubBugReporter.Build.cs b/Source/BetaHubBugReporter/BetaHubBugReporter.Build.cs index ac4ba32..dc4792f 100644 --- a/Source/BetaHubBugReporter/BetaHubBugReporter.Build.cs +++ b/Source/BetaHubBugReporter/BetaHubBugReporter.Build.cs @@ -47,6 +47,7 @@ public BetaHubBugReporter(ReadOnlyTargetRules Target) : base(Target) "JsonUtilities", "RenderCore", "RHI", + "EnhancedInput" // ... add private dependencies that you statically link with here ... } ); diff --git a/Source/BetaHubBugReporter/Private/BH_BugReport.cpp b/Source/BetaHubBugReporter/Private/BH_BugReport.cpp index ad53d95..094c034 100644 --- a/Source/BetaHubBugReporter/Private/BH_BugReport.cpp +++ b/Source/BetaHubBugReporter/Private/BH_BugReport.cpp @@ -24,6 +24,7 @@ void UBH_BugReport::SubmitReport( UBH_GameRecorder* GameRecorder, const FString& Description, const FString& StepsToReproduce, + const FString& UserEmail, const FString& ScreenshotPath, const FString& LogFileContents, bool bIncludeVideo, @@ -35,11 +36,11 @@ void UBH_BugReport::SubmitReport( { Async(EAsyncExecution::Thread, [this, - Settings, GameRecorder, Description, StepsToReproduce, ScreenshotPath, LogFileContents, + Settings, GameRecorder, Description, StepsToReproduce, UserEmail, ScreenshotPath, LogFileContents, bIncludeVideo, bIncludeLogs, bIncludeScreenshot, OnSuccess, OnFailure]() { - SubmitReportAsync(Settings, GameRecorder, Description, StepsToReproduce, ScreenshotPath, LogFileContents, + SubmitReportAsync(Settings, GameRecorder, Description, StepsToReproduce, UserEmail, ScreenshotPath, LogFileContents, bIncludeVideo, bIncludeLogs, bIncludeScreenshot, OnSuccess, OnFailure); }); @@ -50,6 +51,7 @@ void UBH_BugReport::SubmitReportAsync( UBH_GameRecorder* GameRecorder, const FString& Description, const FString& StepsToReproduce, + const FString& UserEmail, const FString& ScreenshotPath, const FString& LogFileContents, bool bIncludeVideo, @@ -74,6 +76,8 @@ void UBH_BugReport::SubmitReportAsync( InitialRequest->SetHeader(TEXT("Accept"), TEXT("application/json")); InitialRequest->AddField(TEXT("issue[description]"), Description); InitialRequest->AddField(TEXT("issue[unformatted_steps_to_reproduce]"), StepsToReproduce); + //TODO enable adding user email as field when endpoint allows it + //InitialRequest->AddField(TEXT("issue[user_email]"), UserEmail); InitialRequest->FinalizeFormData(); InitialRequest->ProcessRequest( diff --git a/Source/BetaHubBugReporter/Private/BH_GameInstanceSubsystem.cpp b/Source/BetaHubBugReporter/Private/BH_GameInstanceSubsystem.cpp index 88aabca..bb1cd30 100644 --- a/Source/BetaHubBugReporter/Private/BH_GameInstanceSubsystem.cpp +++ b/Source/BetaHubBugReporter/Private/BH_GameInstanceSubsystem.cpp @@ -33,4 +33,19 @@ void UBH_GameInstanceSubsystem::Deinitialize() { Manager->StopService(); } +} + +void UBH_GameInstanceSubsystem::HideScreenAreaFromReport(FVector4 AreaToHide) +{ + Manager->HideScreenAreaFromReport(AreaToHide); +} + +void UBH_GameInstanceSubsystem::HideScreenAreaFromReportArray(TArray AreasToHide) +{ + Manager->HideScreenAreaFromReportArray(AreasToHide); +} + +void UBH_GameInstanceSubsystem::SetHiddenAreaColor(FLinearColor NewColor) +{ + Manager->SetHiddenAreaColor(NewColor.ToFColor(false)); } \ No newline at end of file diff --git a/Source/BetaHubBugReporter/Private/BH_GameRecorder.cpp b/Source/BetaHubBugReporter/Private/BH_GameRecorder.cpp index 94eb06a..407b8eb 100644 --- a/Source/BetaHubBugReporter/Private/BH_GameRecorder.cpp +++ b/Source/BetaHubBugReporter/Private/BH_GameRecorder.cpp @@ -32,9 +32,10 @@ UBH_GameRecorder::UBH_GameRecorder(const FObjectInitializer& ObjectInitializer) , ViewportHeight(0) , FrameWidth(0) , FrameHeight(0) + , RawFrameBufferPool(3) + , HiddenAreaColor(FColor::Black) , LastCaptureTime(0) , RawFrameBufferQueue() - , RawFrameBufferPool(3) , MainEditorWindow(nullptr) , LargestSize(0, 0) , MaxVideoWidth(512) // Initialize with minimum value @@ -193,6 +194,18 @@ void UBH_GameRecorder::Tick(float DeltaTime) { PendingPixels[i] = PendingLinearPixels[i].ToFColor(false); } + + TArray HiddenIdices; + + for (FVector4 DisabledRect : HiddenAreas) + { + HiddenIdices.Append(GetPixelIndicesFromViewportRectangle(FVector2D(DisabledRect.X, DisabledRect.Y), FVector2D(DisabledRect.Z, DisabledRect.W), TextureBuffer->GetWidth(), TextureBuffer->GetHeight())); + } + + for (int32 Index : HiddenIdices) + { + PendingPixels[Index] = HiddenAreaColor; + } // Resize image to frame ResizeImageToFrame(PendingPixels, TextureBuffer->GetWidth(), TextureBuffer->GetHeight(), FrameWidth, FrameHeight, ResizedPixels); @@ -235,6 +248,56 @@ void UBH_GameRecorder::ResizeImageToFrame( } } +TArray UBH_GameRecorder::GetPixelIndicesFromViewportRectangle(const FVector2D& TopLeftViewportCoords, const FVector2D& BottomRightViewportCoords, int32 TextureWidth, int32 TextureHeight) +{ + TArray PixelIndices; + + // Get viewport size + FVector2D ViewportSize; + GEngine->GameViewport->GetViewportSize(ViewportSize); + + // Check if viewport size and texture size are valid + if (ViewportSize.X <= 0 || ViewportSize.Y <= 0 || TextureWidth <= 0 || TextureHeight <= 0) + { + return PixelIndices; // Return empty if sizes are invalid + } + + // Normalize the viewport coordinates (0 to 1) + float NormalizedTopLeftX = TopLeftViewportCoords.X / ViewportSize.X; + float NormalizedTopLeftY = TopLeftViewportCoords.Y / ViewportSize.Y; + float NormalizedBottomRightX = BottomRightViewportCoords.X / ViewportSize.X; + float NormalizedBottomRightY = BottomRightViewportCoords.Y / ViewportSize.Y; + + // Convert normalized coordinates to texture coordinates + int32 TextureX1 = FMath::Clamp(static_cast(NormalizedTopLeftX * TextureWidth), 0, TextureWidth - 1); + int32 TextureY1 = FMath::Clamp(static_cast(NormalizedTopLeftY * TextureHeight), 0, TextureHeight - 1); + int32 TextureX2 = FMath::Clamp(static_cast(NormalizedBottomRightX * TextureWidth), 0, TextureWidth - 1); + int32 TextureY2 = FMath::Clamp(static_cast(NormalizedBottomRightY * TextureHeight), 0, TextureHeight - 1); + + // Ensure coordinates are properly ordered (Top-left and bottom-right) + int32 StartX = FMath::Min(TextureX1, TextureX2); + int32 EndX = FMath::Max(TextureX1, TextureX2); + int32 StartY = FMath::Min(TextureY1, TextureY2); + int32 EndY = FMath::Max(TextureY1, TextureY2); + + // Iterate through the rectangle area to collect indices + for (int32 Y = StartY; Y <= EndY; ++Y) + { + for (int32 X = StartX; X <= EndX; ++X) + { + int32 PixelIndex = Y * TextureWidth + X; + + // Check if the pixel index is within the bounds of the array + if (PixelIndex >= 0 && PixelIndex < PendingPixels.Num()) + { + PixelIndices.Add(PixelIndex); + } + } + } + + return PixelIndices; +} + bool UBH_GameRecorder::IsTickable() const { return bIsRecording; @@ -245,7 +308,7 @@ TStatId UBH_GameRecorder::GetStatId() const RETURN_QUICK_DECLARE_CYCLE_STAT(UBH_GameRecorder, STATGROUP_Tickables); } -void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTexture2DRHIRef& BackBuffer) +void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTextureRHIRef& BackBuffer) { #if WITH_EDITOR // Log window title and size for debugging @@ -293,7 +356,7 @@ void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTexture2DRHIRef { FViewport* Viewport = GEngine->GameViewport->GetGameViewport(); - FTexture2DRHIRef GameBuffer = BackBuffer; + FTextureRHIRef GameBuffer = BackBuffer; if (!GameBuffer) { UE_LOG(LogBetaHub, Error, TEXT("Failed to get game buffer. Will try next time...")); @@ -331,11 +394,18 @@ void UBH_GameRecorder::OnBackBufferReady(SWindow& Window, const FTexture2DRHIRef if (!bCopyTextureStarted && StagingTexture != nullptr) { +#if ENGINE_MINOR_VERSION >= 5 + AsyncTask(ENamedThreads::GameThread, [this, BackBuffer]() + { + ReadPixels(BackBuffer); + }); +#else ReadPixels(BackBuffer); +#endif } } -void UBH_GameRecorder::ReadPixels(const FTexture2DRHIRef& BackBuffer) +void UBH_GameRecorder::ReadPixels(const FTextureRHIRef& BackBuffer) { if (!GEngine || !GEngine->GameViewport) return; @@ -364,7 +434,7 @@ void UBH_GameRecorder::ReadPixels(const FTexture2DRHIRef& BackBuffer) return; } - FTexture2DRHIRef Texture = BackBuffer; + FTextureRHIRef Texture = BackBuffer; FRHICopyTextureInfo CopyInfo; RHICmdList.CopyTexture(Texture, StagingTexture, CopyInfo); @@ -386,6 +456,7 @@ void UBH_GameRecorder::ReadPixels(const FTexture2DRHIRef& BackBuffer) RawFrameBufferQueue.Enqueue(TextureBuffer); RHICmdList.UnmapStagingSurface(StagingTexture); + } ); @@ -393,7 +464,7 @@ void UBH_GameRecorder::ReadPixels(const FTexture2DRHIRef& BackBuffer) bCopyTextureStarted = true; } -void UBH_GameRecorder::OnBackBufferResized(const FTexture2DRHIRef& BackBuffer) +void UBH_GameRecorder::OnBackBufferResized(const FTextureRHIRef& BackBuffer) { FIntVector OriginalSize = BackBuffer->GetDesc().GetSize(); @@ -481,6 +552,21 @@ void UBH_GameRecorder::SetMaxVideoDimensions(int32 InMaxWidth, int32 InMaxHeight MaxVideoHeight = FMath::Max(InMaxHeight, 512); } +void UBH_GameRecorder::HideScreenAreaFromReport(FVector4 AreaToHide) +{ + HiddenAreas.Add(AreaToHide); +} + +void UBH_GameRecorder::HideScreenAreaFromReportArray(TArray AreasToHide) +{ + HiddenAreas.Append(AreasToHide); +} + +void UBH_GameRecorder::SetHiddenAreaColor(FColor NewColor) +{ + HiddenAreaColor = NewColor; +} + #if ENGINE_MINOR_VERSION < 4 bool ConvertRAWSurfaceDataToFLinearColor(EPixelFormat Format, uint32 Width, uint32 Height, uint8 *In, uint32 SrcPitch, FLinearColor* Out, FReadSurfaceDataFlags InFlags) { diff --git a/Source/BetaHubBugReporter/Private/BH_GameRecorder.h b/Source/BetaHubBugReporter/Private/BH_GameRecorder.h index 47fca29..5f3ccaa 100644 --- a/Source/BetaHubBugReporter/Private/BH_GameRecorder.h +++ b/Source/BetaHubBugReporter/Private/BH_GameRecorder.h @@ -38,6 +38,10 @@ class BETAHUBBUGREPORTER_API UBH_GameRecorder : public UObject, public FTickable virtual bool IsTickable() const override; virtual TStatId GetStatId() const override; + void HideScreenAreaFromReport(FVector4 AreaToHide); + void HideScreenAreaFromReportArray(TArray AreasToHide); + void SetHiddenAreaColor(FColor NewColor); + // Sets the maximum video dimensions while maintaining aspect ratio void SetMaxVideoDimensions(int32 InMaxWidth, int32 InMaxHeight); @@ -48,6 +52,9 @@ class BETAHUBBUGREPORTER_API UBH_GameRecorder : public UObject, public FTickable UPROPERTY() ABH_SceneCaptureActor* SceneCaptureActor; + TArray HiddenAreas; + FColor HiddenAreaColor; + TSharedPtr VideoEncoder; int32 TargetFPS; FTimespan RecordingDuration; @@ -61,7 +68,7 @@ class BETAHUBBUGREPORTER_API UBH_GameRecorder : public UObject, public FTickable TArray PendingPixels; TArray ResizedPixels; - FTexture2DRHIRef StagingTexture; + FTextureRHIRef StagingTexture; EPixelFormat StagingTextureFormat; int32 ViewportWidth; @@ -85,9 +92,11 @@ class BETAHUBBUGREPORTER_API UBH_GameRecorder : public UObject, public FTickable void SetFrameData(int32 Width, int32 Height, const TArray& Data); void ResizeImageToFrame(const TArray& ImageData, uint32 ImageWidth, uint32 ImageHeight, uint32 FrameWidth, uint32 FrameHeight, TArray& ResizedData); - void OnBackBufferReady(SWindow& Window, const FTexture2DRHIRef& BackBuffer); + TArray GetPixelIndicesFromViewportRectangle(const FVector2D& TopLeftViewportCoords, const FVector2D& BottomRightViewportCoords, int32 TextureWidth, int32 TextureHeight); + + void OnBackBufferReady(SWindow& Window, const FTextureRHIRef& BackBuffer); - void OnBackBufferResized(const FTexture2DRHIRef& BackBuffer); + void OnBackBufferResized(const FTextureRHIRef& BackBuffer); //Hack TODO TSet CreatedWindows; diff --git a/Source/BetaHubBugReporter/Private/BH_Manager.cpp b/Source/BetaHubBugReporter/Private/BH_Manager.cpp index 7de9599..fa0f5a3 100644 --- a/Source/BetaHubBugReporter/Private/BH_Manager.cpp +++ b/Source/BetaHubBugReporter/Private/BH_Manager.cpp @@ -1,5 +1,8 @@ #include "BH_Manager.h" #include "BH_Log.h" +#include "EnhancedInputComponent.h" +#include "EnhancedInputSubsystems.h" +#include "InputMappingContext.h" #include "HttpModule.h" #include "Engine/GameInstance.h" #include "Interfaces/IHttpRequest.h" @@ -12,6 +15,13 @@ UBH_Manager::UBH_Manager() : InputComponent(nullptr) { Settings = GetMutableDefault(); + + IA_ShowReportForm = GetMutableDefault(); + IA_ShowReportForm->bTriggerWhenPaused = true; + IA_ShowReportForm->bReserveAllMappings = true; + BetaHubMappingContext = GetMutableDefault(); + + BetaHubMappingContext->MapKey(IA_ShowReportForm, Settings->ShortcutKey); } void UBH_Manager::StartService(UGameInstance* GI) @@ -97,15 +107,36 @@ void UBH_Manager::OnPlayerControllerChanged(APlayerController* PC) { CurrentPlayerController = PC; - if (PC) + //Add Input Mapping Context + UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem(PC->GetLocalPlayer()); + + if (Subsystem && BetaHubMappingContext) + { + Subsystem->AddMappingContext(BetaHubMappingContext, 0); + } + + if (PC && IA_ShowReportForm) { - InputComponent = NewObject(PC); - InputComponent->RegisterComponent(); - InputComponent->BindKey(Settings->ShortcutKey, IE_Pressed, this, &UBH_Manager::OnBackgroundServiceRequestWidget); - PC->PushInputComponent(InputComponent); + InputComponent = Cast(PC->InputComponent); + InputComponent->BindAction(IA_ShowReportForm, ETriggerEvent::Completed, this, &UBH_Manager::OnBackgroundServiceRequestWidget); } } +void UBH_Manager::HideScreenAreaFromReport(FVector4 AreaToHide) +{ + BackgroundService->GetGameRecorder()->HideScreenAreaFromReport(AreaToHide); +} + +void UBH_Manager::HideScreenAreaFromReportArray(TArray AreasToHide) +{ + BackgroundService->GetGameRecorder()->HideScreenAreaFromReportArray(AreasToHide); +} + +void UBH_Manager::SetHiddenAreaColor(FColor NewColor) +{ + BackgroundService->GetGameRecorder()->SetHiddenAreaColor(NewColor); +} + void UBH_Manager::FetchAllReleases() { if (!Settings) @@ -301,4 +332,4 @@ void UBH_Manager::OnFetchReleaseByIdResponse(FHttpRequestPtr Request, FHttpRespo { UE_LOG(LogBetaHub, Error, TEXT("Invalid JSON response for release by ID. Check your endpoint, your project ID and if the project is not set to private.")); } -} \ No newline at end of file +} diff --git a/Source/BetaHubBugReporter/Private/BH_PopupWidget.cpp b/Source/BetaHubBugReporter/Private/BH_PopupWidget.cpp index 2bbaf7e..e799cb2 100644 --- a/Source/BetaHubBugReporter/Private/BH_PopupWidget.cpp +++ b/Source/BetaHubBugReporter/Private/BH_PopupWidget.cpp @@ -3,9 +3,9 @@ #include "BH_PopupWidget.h" -void UBH_PopupWidget::NativeConstruct() +void UBH_PopupWidget::NativeOnInitialized() { - Super::NativeConstruct(); + Super::NativeOnInitialized(); if (this->CloseButton) { diff --git a/Source/BetaHubBugReporter/Private/BH_PopupWidget.h b/Source/BetaHubBugReporter/Private/BH_PopupWidget.h index 4b63c70..c98a578 100644 --- a/Source/BetaHubBugReporter/Private/BH_PopupWidget.h +++ b/Source/BetaHubBugReporter/Private/BH_PopupWidget.h @@ -20,7 +20,7 @@ class UBH_PopupWidget : public UUserWidget void OnCloseClicked(); protected: - virtual void NativeConstruct() override; + virtual void NativeOnInitialized() override; public: UPROPERTY(meta = (BindWidget)) diff --git a/Source/BetaHubBugReporter/Private/BH_ReportFormWidget.cpp b/Source/BetaHubBugReporter/Private/BH_ReportFormWidget.cpp index 9e32a5d..ca37843 100644 --- a/Source/BetaHubBugReporter/Private/BH_ReportFormWidget.cpp +++ b/Source/BetaHubBugReporter/Private/BH_ReportFormWidget.cpp @@ -14,6 +14,7 @@ UBH_ReportFormWidget::UBH_ReportFormWidget(const FObjectInitializer& ObjectIniti , bCursorStateModified(false) , bWasCursorVisible(false) , bWasCursorLocked(false) + , bRequireUserEmail(false) { SetIsFocusable(true); } @@ -54,23 +55,27 @@ void UBH_ReportFormWidget::SubmitReport() { FString BugDescription = BugDescriptionEdit->GetText().ToString(); FString StepsToReproduce = StepsToReproduceEdit->GetText().ToString(); + FString UserEmail = UserEmailEdit->GetText().ToString(); - UE_LOG(LogBetaHub, Log, TEXT("Bug Description: %s"), *BugDescription); - UE_LOG(LogBetaHub, Log, TEXT("Steps to Reproduce: %s"), *StepsToReproduce); + UE_LOG(LogBetaHub, Log, TEXT("Bug Description: %s\n"), *BugDescription); + UE_LOG(LogBetaHub, Log, TEXT("Steps to Reproduce: %s\n"), *StepsToReproduce); + UE_LOG(LogBetaHub, Log, TEXT("User e-mail: %s\n"), *UserEmail); + + if (bRequireUserEmail && UserEmail.IsEmpty()) + { + OnFailure()("Please input your e-mail!"); + return; + } UBH_BugReport* BugReport = NewObject(); - BugReport->SubmitReport(Settings, GameRecorder, BugDescription, StepsToReproduce, ScreenshotPath, LogFileContents, + BugReport->SubmitReport(Settings, GameRecorder, BugDescription, StepsToReproduce, UserEmail, ScreenshotPath, LogFileContents, IncludeVideoCheckbox->IsChecked(), IncludeLogsCheckbox->IsChecked(), IncludeScreenshotCheckbox->IsChecked(), [this]() { ShowPopup("Success", "Report submitted successfully!"); RemoveFromParent(); }, - [this](const FString& ErrorMessage) - { - ShowPopup("Error", ErrorMessage); - SubmitLabel->SetText(FText::FromString("Submit")); - } + OnFailure() ); } @@ -139,6 +144,7 @@ void UBH_ReportFormWidget::OnSubmitButtonClicked() void UBH_ReportFormWidget::OnCloseClicked() { GameRecorder->StartRecording(Settings->MaxRecordedFrames, Settings->MaxRecordingDuration); + RestoreCursorState(); RemoveFromParent(); } @@ -146,7 +152,7 @@ void UBH_ReportFormWidget::ShowPopup(const FString& Title, const FString& Descri { if (Settings->PopupWidgetClass) { - UBH_PopupWidget* PopupWidget = CreateWidget(GetWorld(), Settings->PopupWidgetClass); + UBH_PopupWidget* PopupWidget = CreateWidget(GetOwningPlayer(), Settings->PopupWidgetClass); if (PopupWidget) { PopupWidget->SetMessage(Title, Description); @@ -157,4 +163,14 @@ void UBH_ReportFormWidget::ShowPopup(const FString& Title, const FString& Descri { UE_LOG(LogBetaHub, Error, TEXT("PopupWidgetClass is null.")); } +} + +TFunction UBH_ReportFormWidget::OnFailure() +{ + return [this](const FString& ErrorMessage) + { + ShowPopup("Error", ErrorMessage); + SubmitLabel->SetText(FText::FromString("Submit")); + }; + } \ No newline at end of file diff --git a/Source/BetaHubBugReporter/Private/BH_VideoEncoder.cpp b/Source/BetaHubBugReporter/Private/BH_VideoEncoder.cpp index 62a3b15..b8774f2 100644 --- a/Source/BetaHubBugReporter/Private/BH_VideoEncoder.cpp +++ b/Source/BetaHubBugReporter/Private/BH_VideoEncoder.cpp @@ -62,14 +62,7 @@ BH_VideoEncoder::BH_VideoEncoder( PlatformFile.CreateDirectoryTree(*segmentsDir); } - // Remove all existing segment files - IFileManager& FileManager = IFileManager::Get(); - TArray SegmentFiles; - FileManager.FindFiles(SegmentFiles, *(segmentsDir / (segmentPrefix + TEXT("*.mp4"))), true, false); - for (const FString& SegmentFile : SegmentFiles) - { - FileManager.Delete(*(segmentsDir / SegmentFile)); - } + RemoveAllSegments(); outputFile = FPaths::Combine(segmentsDir, (segmentPrefix + TEXT("%06d.mp4"))); encodingSettings = TEXT("-y -f rawvideo -pix_fmt bgra -s ") + @@ -133,6 +126,8 @@ void BH_VideoEncoder::StartRecording() UE_LOG(LogBetaHub, Log, TEXT("Preferred FFmpeg options: %s"), *PreferredFfmpegOptions); } + RemoveAllSegments(); + thread = FRunnableThread::Create(this, TEXT("BH_VideoEncoderThread"), 0, TPri_Normal); } } @@ -343,7 +338,11 @@ FString BH_VideoEncoder::MergeSegments(int32 MaxSegments) SegmentFiles.Sort(); if (SegmentFiles.Num() > MaxSegments) { +#if ENGINE_MINOR_VERSION >= 5 + SegmentFiles.RemoveAt(0, SegmentFiles.Num() - MaxSegments, EAllowShrinking::Yes); +#else SegmentFiles.RemoveAt(0, SegmentFiles.Num() - MaxSegments, true); +#endif } // Check if there are any segments to merge @@ -410,6 +409,27 @@ FString BH_VideoEncoder::MergeSegments(int32 MaxSegments) } } +void BH_VideoEncoder::FindAllSegments(TArray& FileNames) +{ + IFileManager& FileManager = IFileManager::Get(); + FileManager.FindFiles(FileNames, *(segmentsDir / TEXT("segment_*.mp4")), true, false); +} + +void BH_VideoEncoder::RemoveAllSegments() +{ + UE_LOG(LogBetaHub, Log, TEXT("Removing all segments!")); + + // Remove all existing segment files + IFileManager& FileManager = IFileManager::Get(); + TArray SegmentFiles; + FindAllSegments(SegmentFiles); + + for (const FString& SegmentFile : SegmentFiles) + { + FileManager.Delete(*(segmentsDir / SegmentFile)); + } +} + void BH_VideoEncoder::RemoveOldSegments() { // Removing by count instead of age, because video can be paused and we don't @@ -417,7 +437,7 @@ void BH_VideoEncoder::RemoveOldSegments() IFileManager& FileManager = IFileManager::Get(); TArray SegmentFiles; - FileManager.FindFiles(SegmentFiles, *(segmentsDir / (segmentPrefix + TEXT("*.mp4"))), true, false); + FindAllSegments(SegmentFiles); // Sort segment files based on their numerical part SegmentFiles.Sort([](const FString& A, const FString& B) diff --git a/Source/BetaHubBugReporter/Private/BH_VideoEncoder.h b/Source/BetaHubBugReporter/Private/BH_VideoEncoder.h index bee7976..8c4574a 100644 --- a/Source/BetaHubBugReporter/Private/BH_VideoEncoder.h +++ b/Source/BetaHubBugReporter/Private/BH_VideoEncoder.h @@ -37,6 +37,8 @@ class BH_VideoEncoder : public FRunnable FDateTime LastSegmentCheckTime; void RunEncoding(); + void FindAllSegments(TArray& FileNames); + void RemoveAllSegments(); void RemoveOldSegments(); int32 GetSegmentCountToKeep(); diff --git a/Source/BetaHubBugReporter/Public/BH_BugReport.h b/Source/BetaHubBugReporter/Public/BH_BugReport.h index 11ab96d..d7133cc 100644 --- a/Source/BetaHubBugReporter/Public/BH_BugReport.h +++ b/Source/BetaHubBugReporter/Public/BH_BugReport.h @@ -20,6 +20,7 @@ class BETAHUBBUGREPORTER_API UBH_BugReport : public UObject UBH_GameRecorder* GameRecorder, const FString& Description, const FString& StepsToReproduce, + const FString& UserEmail, const FString& ScreenshotPath, const FString& LogFileContents, bool bIncludeVideo, @@ -34,6 +35,7 @@ class BETAHUBBUGREPORTER_API UBH_BugReport : public UObject UBH_GameRecorder* GameRecorder, const FString& Description, const FString& StepsToReproduce, + const FString& UserEmail, const FString& ScreenshotPath, const FString& LogFileContents, bool bIncludeVideo, diff --git a/Source/BetaHubBugReporter/Public/BH_GameInstanceSubsystem.h b/Source/BetaHubBugReporter/Public/BH_GameInstanceSubsystem.h index 6a06b86..45aa7ae 100644 --- a/Source/BetaHubBugReporter/Public/BH_GameInstanceSubsystem.h +++ b/Source/BetaHubBugReporter/Public/BH_GameInstanceSubsystem.h @@ -16,6 +16,15 @@ class BETAHUBBUGREPORTER_API UBH_GameInstanceSubsystem : public UGameInstanceSub virtual void Initialize(FSubsystemCollectionBase& Collection) override; virtual void Deinitialize() override; + UFUNCTION(BlueprintCallable, Category = BetaHub, meta = (ToolTip = "Specify rect (top left and bottom right viewport cords) to hide this area when submitting bug")) + void HideScreenAreaFromReport(FVector4 AreaToHide); + + UFUNCTION(BlueprintCallable, Category = BetaHub, meta = (ToolTip = "Specify array of rects (top left and bottom right viewport cords) to hide this area when submitting bug")) + void HideScreenAreaFromReportArray(TArray AreasToHide); + + UFUNCTION(BlueprintCallable, Category = BetaHub, meta = (ToolTip = "Specify color for the hidden area on the screen\nDefault: Black")) + void SetHiddenAreaColor(FLinearColor NewColor); + private: UPROPERTY() UBH_Manager* Manager; diff --git a/Source/BetaHubBugReporter/Public/BH_Manager.h b/Source/BetaHubBugReporter/Public/BH_Manager.h index 5076c51..2fdbc05 100644 --- a/Source/BetaHubBugReporter/Public/BH_Manager.h +++ b/Source/BetaHubBugReporter/Public/BH_Manager.h @@ -50,10 +50,13 @@ class BETAHUBBUGREPORTER_API UBH_Manager : public UObject void OnLocalPlayerAdded(ULocalPlayer* Player); void OnPlayerControllerChanged(APlayerController* PC); - TObjectPtr InputComponent; + TObjectPtr InputComponent; TObjectPtr CurrentPlayerController; + TObjectPtr IA_ShowReportForm; + TObjectPtr BetaHubMappingContext; + public: UBH_Manager(); @@ -70,6 +73,9 @@ class BETAHUBBUGREPORTER_API UBH_Manager : public UObject UFUNCTION(BlueprintCallable, Category="Bug Report") void OnBackgroundServiceRequestWidget(); + void HideScreenAreaFromReport(FVector4 AreaToHide); + void HideScreenAreaFromReportArray(TArray AreasToHide); + void SetHiddenAreaColor(FColor NewColor); UFUNCTION(BlueprintCallable, Category="Bug Report") void FetchAllReleases(); diff --git a/Source/BetaHubBugReporter/Public/BH_PluginSettings.h b/Source/BetaHubBugReporter/Public/BH_PluginSettings.h index cb612f3..52dc0e2 100644 --- a/Source/BetaHubBugReporter/Public/BH_PluginSettings.h +++ b/Source/BetaHubBugReporter/Public/BH_PluginSettings.h @@ -4,6 +4,7 @@ #include "UObject/NoExportTypes.h" #include "BH_ReportFormWidget.h" #include "BH_PopupWidget.h" +#include "InputAction.h" #include "BH_PluginSettings.generated.h" UCLASS(Config=Game, defaultconfig) diff --git a/Source/BetaHubBugReporter/Public/BH_ReportFormWidget.h b/Source/BetaHubBugReporter/Public/BH_ReportFormWidget.h index 2984934..eba7afe 100644 --- a/Source/BetaHubBugReporter/Public/BH_ReportFormWidget.h +++ b/Source/BetaHubBugReporter/Public/BH_ReportFormWidget.h @@ -7,6 +7,7 @@ #include "Components/MultiLineEditableTextBox.h" #include "Components/TextBlock.h" #include "Components/CheckBox.h" +#include "Components/EditableTextBox.h" #include "BH_ReportFormWidget.generated.h" UCLASS() @@ -36,6 +37,8 @@ class BETAHUBBUGREPORTER_API UBH_ReportFormWidget : public UUserWidget virtual void NativeOnInitialized() override; virtual void NativeDestruct() override; + virtual TFunction OnFailure(); + public: // constructor UBH_ReportFormWidget(const FObjectInitializer& ObjectInitializer); @@ -58,12 +61,18 @@ class BETAHUBBUGREPORTER_API UBH_ReportFormWidget : public UUserWidget UPROPERTY(meta = (BindWidget)) UCheckBox* IncludeScreenshotCheckbox; + UPROPERTY(BlueprintReadOnly, meta = (BindWidgetOptional)) + UEditableTextBox* UserEmailEdit; + UPROPERTY(meta = (BindWidget)) UButton* SubmitButton; UPROPERTY(meta = (BindWidget)) UTextBlock* SubmitLabel; + UPROPERTY(BlueprintReadOnly, EditAnywhere, Category = "Settings") + bool bRequireUserEmail; + UFUNCTION(BlueprintCallable, Category="BugReport") void Setup(UBH_PluginSettings* InSettings, UBH_GameRecorder* InGameRecorder, const FString& InScreenshotPath, const FString& InLogFileContents, bool bTryCaptureMouse);