// @lint-ignore-every LICENSELINT // Copyright Epic Games, Inc. All Rights Reserved. #include "OculusXRBuildAnalytics.h" #include "GameProjectGenerationModule.h" #include "OculusXRHMDModule.h" #include "Runtime/Core/Public/HAL/FileManager.h" FOculusBuildAnalytics* FOculusBuildAnalytics::instance = 0; FOculusBuildAnalytics* FOculusBuildAnalytics::GetInstance() { if (IOculusXRHMDModule::IsAvailable()) { if (instance == nullptr) { instance = new FOculusBuildAnalytics(); } } return instance; } bool FOculusBuildAnalytics::IsOculusXRHMDAvailable() { return IOculusXRHMDModule::IsAvailable() && FOculusXRHMDModule::Get().PreInit(); } void FOculusBuildAnalytics::Shutdown() { } FOculusBuildAnalytics::FOculusBuildAnalytics() { bool TelemetryEnabled = false; if (!GConfig->GetBool(TEXT("/Script/OculusXREditor.OculusXREditorSettings"), TEXT("bEnableOculusBuildTelemetry"), TelemetryEnabled, GEditorIni)) { GConfig->SetBool(TEXT("/Script/OculusXREditor.OculusXREditorSettings"), TEXT("bEnableOculusBuildTelemetry"), TelemetryEnabled, GEditorIni); GConfig->Flush(0); } if (TelemetryEnabled) { RegisterLauncherCallback(); } } void FOculusBuildAnalytics::OnTelemetryToggled(bool Enabled) { if (Enabled) { RegisterLauncherCallback(); } else { if (LauncherCallbackHandle.IsValid()) { ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked("LauncherServices"); ProjectLauncherServicesModule.OnCreateLauncherDelegate.Remove(LauncherCallbackHandle); } } } void FOculusBuildAnalytics::RegisterLauncherCallback() { ILauncherServicesModule& ProjectLauncherServicesModule = FModuleManager::LoadModuleChecked("LauncherServices"); LauncherCallbackHandle = ProjectLauncherServicesModule.OnCreateLauncherDelegate.AddRaw(this, &FOculusBuildAnalytics::OnLauncherCreated); } void FOculusBuildAnalytics::OnLauncherCreated(ILauncherRef Launcher) { // Add callback for when launcher worker is started Launcher->FLauncherWorkerStartedDelegate.AddRaw(this, &FOculusBuildAnalytics::OnLauncherWorkerStarted); } void FOculusBuildAnalytics::OnLauncherWorkerStarted(ILauncherWorkerPtr LauncherWorker, ILauncherProfileRef Profile) { TArray Platforms = Profile.Get().GetCookedPlatforms(); if (Platforms.Num() == 1) { if (Platforms[0].Equals("Android_ASTC") || Platforms[0].Contains("Windows")) { CurrentBuildStage = UNDEFINED_STAGE; AndroidPackageTime = 0; UATLaunched = false; BuildCompleted = false; CurrentBuildPlatform = Platforms[0]; TotalBuildTime = 0; BuildStepCount = 0; FOculusXRHMDModule::GetPluginWrapper().SetDeveloperMode(true); OutputDirectory = Profile.Get().GetPackageDirectory(); // Assign callbacks for stages LauncherWorker.Get()->OnStageCompleted().AddRaw(this, &FOculusBuildAnalytics::OnStageCompleted); LauncherWorker.Get()->OnOutputReceived().AddRaw(this, &FOculusBuildAnalytics::OnBuildOutputReceived); LauncherWorker.Get()->OnStageStarted().AddRaw(this, &FOculusBuildAnalytics::OnStageStarted); LauncherWorker.Get()->OnCompleted().AddRaw(this, &FOculusBuildAnalytics::OnCompleted); // Get information on what oculus platform we are building for and also the OS platform FString OculusPlatform; if (CurrentBuildPlatform.Equals("Android_ASTC")) { UEnum* OculusMobileDevices = StaticEnum(); UAndroidRuntimeSettings* Settings = GetMutableDefault(); TArray> TargetOculusDevices = Settings->PackageForOculusMobile; TArray Devices; if (TargetOculusDevices.Contains(EOculusMobileDevice::Quest2)) { Devices.Add("quest2"); } OculusPlatform = FString::Join(Devices, TEXT("_")); } else if (CurrentBuildPlatform.Contains("Windows")) { CurrentBuildPlatform = "Windows"; OculusPlatform = "rift"; } // Count user asset files UserAssetCount = 0; TArray FileNames; IFileManager::Get().FindFilesRecursive(FileNames, *FPaths::ProjectContentDir(), TEXT("*.*"), true, false, false); UserAssetCount = FileNames.Num(); // Count user script files FGameProjectGenerationModule& GameProjectModule = FModuleManager::LoadModuleChecked(TEXT("GameProjectGeneration")); SourceFileCount = 0; SourceFileDirectorySize = 0; GameProjectModule.Get().GetProjectSourceDirectoryInfo(SourceFileCount, SourceFileDirectorySize); // Generate build GUID FGuid guid = FGuid::NewGuid(); FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("build_guid", TCHAR_TO_ANSI(*guid.ToString())); // Send build start event with corresponding metadata FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("asset_count", TCHAR_TO_ANSI(*FString::FromInt(UserAssetCount))); FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("script_count", TCHAR_TO_ANSI(*FString::FromInt(SourceFileCount))); FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("target_platform", TCHAR_TO_ANSI(*CurrentBuildPlatform)); FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("target_oculus_platform", TCHAR_TO_ANSI(*OculusPlatform)); TArray TaskList; LauncherWorker->GetTasks(TaskList); BuildStepCount = TaskList.Num(); } } } void FOculusBuildAnalytics::OnCompleted(bool Succeeded, double TotalTime, int32 ErrorCode) { if (!BuildCompleted && Succeeded) { SendBuildCompleteEvent(TotalTime); } } void FOculusBuildAnalytics::OnStageCompleted(const FString& StageName, double Time) { if (CurrentBuildStage != UNDEFINED_STAGE) { FString TaskName; switch (CurrentBuildStage) { case COOK_IN_EDITOR_STAGE: TaskName = "build_step_editor_cook"; break; case LAUNCH_UAT_STAGE: TaskName = "build_step_launch_uat"; break; case COMPILE_STAGE: TaskName = "build_step_compile"; break; case COOK_STAGE: TaskName = "build_step_cook"; break; case DEPLOY_STAGE: TaskName = "build_step_deploy"; break; case PACKAGE_STAGE: TaskName = "build_step_package"; break; case RUN_STAGE: return; default: TaskName = "build_step_undefined"; break; } if (AndroidPackageTime > 0) { Time -= AndroidPackageTime; } TotalBuildTime += Time; FOculusXRHMDModule::GetPluginWrapper().SendEvent2(TCHAR_TO_ANSI(*TaskName), TCHAR_TO_ANSI(*FString::SanitizeFloat(Time)), "ovrbuild"); } } void FOculusBuildAnalytics::OnStageStarted(const FString& StageName) { if (StageName.Equals("Cooking in the editor")) { CurrentBuildStage = COOK_IN_EDITOR_STAGE; } else if (StageName.Equals("Build Task") && CurrentBuildStage == LAUNCH_UAT_STAGE) { CurrentBuildStage = COMPILE_STAGE; } else if (StageName.Equals("Build Task")) { CurrentBuildStage = LAUNCH_UAT_STAGE; } else if (StageName.Equals("Cook Task")) { CurrentBuildStage = COOK_STAGE; } else if (StageName.Equals("Package Task")) { CurrentBuildStage = PACKAGE_STAGE; } else if (StageName.Equals("Deploy Task")) { CurrentBuildStage = DEPLOY_STAGE; } else if (StageName.Equals("Run Task")) { CurrentBuildStage = RUN_STAGE; SendBuildCompleteEvent(TotalBuildTime); BuildCompleted = true; } else { CurrentBuildStage = UNDEFINED_STAGE; } } void FOculusBuildAnalytics::OnBuildOutputReceived(const FString& Message) { if (CurrentBuildPlatform.Equals("Android_ASTC") && (CurrentBuildStage == DEPLOY_STAGE || CurrentBuildStage == PACKAGE_STAGE)) { if (Message.Contains("BUILD SUCCESSFUL")) { FString Text, Time; Message.Split("in", &Text, &Time); if (!Time.IsEmpty()) { FString SMinutes, SSeconds; if (Time.Contains("m")) { Time.Split("m", &SMinutes, &SSeconds); } else { SSeconds = Time; } int Minutes = FCString::Atoi(*SMinutes); int Seconds = FCString::Atoi(*SSeconds); AndroidPackageTime = Minutes * 60 + Seconds; FOculusXRHMDModule::GetPluginWrapper().SendEvent2("build_step_gradle_build", TCHAR_TO_ANSI(*FString::SanitizeFloat(AndroidPackageTime)), "ovrbuild"); } } } } void FOculusBuildAnalytics::SendBuildCompleteEvent(float TotalTime) { if (CurrentBuildPlatform.Equals("Android_ASTC")) { int64 APKTotalSize = 0; TArray FoundAPKs; OutputDirectory = FPaths::ProjectDir() + "Binaries/Android"; OutputDirectory = FPaths::ConvertRelativePathToFull(OutputDirectory); IFileManager::Get().FindFiles(FoundAPKs, *FPaths::Combine(OutputDirectory, TEXT("*.apk")), true, false); FDateTime LatestTime = FDateTime(0); FString LatestAPK; for (int i = 0; i < FoundAPKs.Num(); i++) { FDateTime APKCreationTime = IFileManager::Get().GetTimeStamp(*FPaths::Combine(OutputDirectory, FoundAPKs[i])); if (APKCreationTime > LatestTime) { LatestTime = APKCreationTime; LatestAPK = FoundAPKs[i]; } } TArray FoundOBBs; LatestTime = FDateTime(0); FString LatestOBB; IFileManager::Get().FindFiles(FoundOBBs, *FPaths::Combine(OutputDirectory, TEXT("*.obb")), true, false); for (int i = 0; i < FoundOBBs.Num(); i++) { FDateTime OBBCreationTime = IFileManager::Get().GetTimeStamp(*FPaths::Combine(OutputDirectory, FoundOBBs[i])); if (OBBCreationTime > LatestTime) { LatestTime = OBBCreationTime; LatestOBB = FoundOBBs[i]; } } if (!LatestAPK.IsEmpty()) { APKTotalSize += IFileManager::Get().FileSize(*FPaths::Combine(OutputDirectory, LatestAPK)); } if (!LatestOBB.IsEmpty()) { APKTotalSize += IFileManager::Get().FileSize(*FPaths::Combine(OutputDirectory, LatestOBB)); } if (APKTotalSize > 0) { FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("build_output_size", TCHAR_TO_ANSI(*FString::FromInt(APKTotalSize))); } } FOculusXRHMDModule::GetPluginWrapper().AddCustomMetadata("build_step_count", TCHAR_TO_ANSI(*FString::FromInt(BuildStepCount))); FOculusXRHMDModule::GetPluginWrapper().SendEvent2("build_complete", TCHAR_TO_ANSI(*FString::SanitizeFloat(TotalTime)), "ovrbuild"); }