diff --git a/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp b/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp index ad88487..33832a6 100644 --- a/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp +++ b/Source/HarmonyLink/Private/Objects/HarmonyLinkGraphics.cpp @@ -8,7 +8,6 @@ #include "GameFramework/GameUserSettings.h" UHarmonyLinkGraphics* UHarmonyLinkGraphics::_INSTANCE = nullptr; -FString UHarmonyLinkGraphics::_IniLocation = "HarmonyLink"; int32 UHarmonyLinkGraphics::_TickRate = 1; TMap> UHarmonyLinkGraphics::_DefaultSettings = { @@ -38,6 +37,7 @@ UHarmonyLinkGraphics::UHarmonyLinkGraphics() _bAutomaticSwitch = true; FWorldDelegates::OnPostWorldInitialization.AddUObject(this, &UHarmonyLinkGraphics::OnPostWorldInitialization); + FWorldDelegates::OnPreWorldFinishDestroy.AddUObject(this, &UHarmonyLinkGraphics::OnWorldEnd); } UHarmonyLinkGraphics::~UHarmonyLinkGraphics() @@ -64,7 +64,7 @@ bool UHarmonyLinkGraphics::LoadSettingsFromConfig() // Load the configuration from the INI file FConfigFile ConfigFile; - const FString Filename = GetDefaultConfigFilename(); + const FString Filename = GetConfigDirectoryFile(); if (!GConfig) { @@ -158,7 +158,7 @@ void UHarmonyLinkGraphics::SaveConfig() const SaveSection(Profile.Value); } - const FString Filename = GetDefaultConfigFilename(); + const FString Filename = GetConfigDirectoryFile(); UE_LOG(LogHarmonyLink, Log, TEXT("Flushing file: '%s'"), *Filename); GConfig->Flush(false, Filename); } @@ -272,7 +272,9 @@ void UHarmonyLinkGraphics::DestroySettings() { if (_INSTANCE) { + UE_LOG(LogHarmonyLink, Log, TEXT("Destroying UHarmonyLinkGraphics.")) FWorldDelegates::OnPostWorldInitialization.RemoveAll(_INSTANCE); + FWorldDelegates::OnPreWorldFinishDestroy.RemoveAll(_INSTANCE); _INSTANCE->RemoveFromRoot(); _INSTANCE->MarkAsGarbage(); _INSTANCE = nullptr; @@ -290,36 +292,54 @@ void UHarmonyLinkGraphics::Init() // BUG: Remove this before release! if (!BatteryStatus.HasBattery) { - ApplyProfile(EProfile::BATTERY); + ApplyProfileInternal(EProfile::BATTERY); } } void UHarmonyLinkGraphics::Tick() { - if (!_bAutomaticSwitch) + Async(EAsyncExecution::Thread, [this]() { - return; - } + const FBattery BatteryStatus = UHarmonyLinkLibrary::GetBatteryStatus(); - const FBattery BatteryStatus = UHarmonyLinkLibrary::GetBatteryStatus(); - - if (BatteryStatus.HasBattery) - { - if (BatteryStatus.IsACConnected) + if (BatteryStatus.BatteryPercent != _LastBatteryPercentage) { - if (_ActiveProfile != EProfile::CHARGING) + // Ensure thread-safe broadcasting + Async(EAsyncExecution::TaskGraphMainThread, [this, BatteryStatus]() { - ApplyProfile(EProfile::CHARGING); + OnBatteryLevelChanged.Broadcast(BatteryStatus.BatteryPercent); + }); + } + + if (!_bAutomaticSwitch) + { + return; + } + + if (BatteryStatus.HasBattery) + { + if (BatteryStatus.IsACConnected) + { + if (_ActiveProfile != EProfile::CHARGING) + { + Async(EAsyncExecution::TaskGraphMainThread, [this]() + { + ApplyProfileInternal(EProfile::CHARGING); + }); + } + } + else + { + if (_ActiveProfile != EProfile::BATTERY) + { + Async(EAsyncExecution::TaskGraphMainThread, [this]() + { + ApplyProfileInternal(EProfile::BATTERY); + }); + } } } - else - { - if (_ActiveProfile != EProfile::BATTERY) - { - ApplyProfile(EProfile::BATTERY); - } - } - } + }); } void UHarmonyLinkGraphics::CreateDefaultConfigFile() @@ -332,11 +352,22 @@ void UHarmonyLinkGraphics::CreateDefaultConfigFile() UE_LOG(LogHarmonyLink, Log, TEXT("Default config file created.")); } -void UHarmonyLinkGraphics::SaveSection(FSettingsProfile& SettingsProfile, const bool bFlush) const +FString UHarmonyLinkGraphics::GetConfigDirectoryFile() +{ + FString ConfigFileName = "HarmonyLink.ini"; // Replace with your actual config file name + +#if WITH_EDITOR + return FPaths::Combine(FPaths::ProjectSavedDir(), TEXT("Config"), ConfigFileName); +#else + return FPaths::Combine(FPaths::ProjectConfigDir(), ConfigFileName); +#endif +} + +void UHarmonyLinkGraphics::SaveSection(FSettingsProfile& SettingsProfile, const bool bFlush) { if (GConfig) { - const FString Filename = GetDefaultConfigFilename(); + const FString Filename = GetConfigDirectoryFile(); for (const auto& Setting : SettingsProfile.Settings) { FString TypeString; @@ -396,35 +427,7 @@ void UHarmonyLinkGraphics::LoadDefaults() } } -void UHarmonyLinkGraphics::OnPostWorldInitialization(UWorld* World, UWorld::InitializationValues IVS) -{ - if (!World) - { - UE_LOG(LogHarmonyLink, Error, TEXT("Failed to Hook into World Initialisation!")) - return; - } - - if (World->IsGameWorld()) - { - if (UHarmonyLinkGraphics* Settings = GetSettings()) - { - if (World->IsPlayInEditor()) - { - Settings->LoadConfig(true); - } - - if (!World->GetTimerManager().TimerExists(Settings->_TickTimerHandle) || !World->GetTimerManager().IsTimerActive(Settings->_TickTimerHandle)) - { - World->GetTimerManager().SetTimer(Settings->_TickTimerHandle, [Settings] - { - Settings->Tick(); - }, _TickRate, true); - } - } - } -} - -bool UHarmonyLinkGraphics::ApplyProfile(const EProfile Profile) +bool UHarmonyLinkGraphics::ApplyProfileInternal(const EProfile Profile) { // If the profile is None, revert to the original user game settings if (Profile == EProfile::NONE) @@ -485,6 +488,62 @@ bool UHarmonyLinkGraphics::ApplyProfile(const EProfile Profile) return true; } +void UHarmonyLinkGraphics::OnPostWorldInitialization(UWorld* World, UWorld::InitializationValues IVS) +{ + if (!World) + { + UE_LOG(LogHarmonyLink, Error, TEXT("Failed to Hook into World Initialisation!")) + return; + } + + if (World->IsGameWorld()) + { + if (UHarmonyLinkGraphics* Settings = GetSettings()) + { + if (World->IsPlayInEditor()) + { + Settings->LoadConfig(true); + } + + if (!World->GetTimerManager().TimerExists(Settings->_TickTimerHandle) || !World->GetTimerManager().IsTimerActive(Settings->_TickTimerHandle)) + { + World->GetTimerManager().SetTimer(Settings->_TickTimerHandle, [Settings] + { + Settings->Tick(); + }, _TickRate, true); + } + } + } +} + +void UHarmonyLinkGraphics::OnWorldEnd(UWorld* World) +{ + if (!World) + { + UE_LOG(LogHarmonyLink, Error, TEXT("World Already destroyed")) + return; + } + + if(World->GetTimerManager().TimerExists(_TickTimerHandle)) + { + World->GetTimerManager().ClearTimer(_TickTimerHandle); + } + + // Ensure we safely destroy our singleton instance + DestroySettings(); +} + +bool UHarmonyLinkGraphics::ApplyProfile(const EProfile Profile, const bool bDisableAuto) +{ + // Manual profile change, turn off automatic switching + if (bDisableAuto) + { + _bAutomaticSwitch = false; + } + + return ApplyProfileInternal(Profile); +} + void UHarmonyLinkGraphics::ApplySetting(const TPair& Setting) { // Apply the setting based on the key (CVar) diff --git a/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h b/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h index 810a071..e9a486e 100644 --- a/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h +++ b/Source/HarmonyLink/Public/Objects/HarmonyLinkGraphics.h @@ -11,6 +11,7 @@ DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProfileChanged, EProfile, Profile); DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAutomaticSwitchChanged, bool, bAutomaticSwich); +DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnBatteryLevelChanged, int32, BatteryPercent); /** * @@ -29,6 +30,9 @@ public: UPROPERTY(BlueprintAssignable) FOnAutomaticSwitchChanged OnAutomaticSwitchChanged; + + UPROPERTY(BlueprintAssignable) + FOnBatteryLevelChanged OnBatteryLevelChanged; UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") void LoadConfig(const bool bForceReload = false); @@ -40,7 +44,7 @@ public: void SetSetting(EProfile Profile, FName Setting, FHLConfigValue Value); UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") - bool ApplyProfile(EProfile Profile); + bool ApplyProfile(EProfile Profile, const bool bDisableAuto = true); /** Returns the game local machine settings (resolution, windowing mode, scalability settings, etc...) */ UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings") @@ -64,25 +68,30 @@ private: void Init(); void Tick(); void CreateDefaultConfigFile(); + static FString GetConfigDirectoryFile(); bool LoadSettingsFromConfig(); bool LoadSection(const FConfigFile& ConfigFile, const TPair Profile); - void SaveSection(FSettingsProfile& SettingsProfile, const bool bFlush = false) const; + static void SaveSection(FSettingsProfile& SettingsProfile, const bool bFlush = false); void LoadDefaults(); + bool ApplyProfileInternal(EProfile Profile); + void OnPostWorldInitialization(UWorld* World, UWorld::InitializationValues IVS); + void OnWorldEnd(UWorld* World); static void ResetInstance(); + // Note: Turning off HarmonyLink Settings requires game restart static void ApplySetting(const TPair& Setting); // Debugging void DebugPrintProfiles() const; static void PrintDebugSection(FSettingsProfile& SettingsProfile); - static FString _IniLocation; - uint8 _bAutomaticSwitch :1; + int32 _LastBatteryPercentage = 0; + // How many times to query HarmonyLinkLib for hardware info static int32 _TickRate;