Compare commits

...

38 commits

Author SHA1 Message Date
0fb1da0984
Merge branch 'release/v1.1.0' into Stable 2024-07-08 22:52:02 +01:00
821b673666
Merge branch 'Dev' into release/v1.1.0 2024-07-08 22:50:24 +01:00
a6de000c5a
Add copyright notice to files 2024-07-08 22:50:08 +01:00
5e3ccc5ba9
Version Bump 2024-07-08 22:44:17 +01:00
Jordon
ac09bd419b
Merge pull request #1 from Jordonbc/Feature/GraphicsProfiles
Add automatic profile switching
2024-07-08 22:22:09 +01:00
98fb068e29
Fixed compile issue in Unreal Engine 5 2024-07-08 21:55:13 +01:00
5fac24b973
Removed unused module dependency 2024-07-08 21:54:25 +01:00
52ae204afe
Added new line 2024-07-08 21:53:56 +01:00
9a5194dc3a
Rename plugin to HarmonyLinkUE 2024-07-08 00:32:52 +01:00
8ba9caeb27
Discard return from GetConfig 2024-07-07 19:27:17 +01:00
a2d4452691
Fixed world delegates 2024-07-07 19:22:41 +01:00
6ba25c1b0d
Native Linux compile working and works on steam deck 2024-06-06 21:11:12 +01:00
e5cca6b23f
Successfully compiled and working on Win32 + tested on steam deck 2024-05-31 23:46:36 +01:00
47008fcefe
Successful compile, untested 2024-05-27 19:43:19 +01:00
15a2622991
Working profiles & Updated HarmonyLinkLib 2024-05-25 00:44:14 +01:00
3f435a284f
Merge branch 'Feature/Update_DLL' into Feature/GraphicsProfiles 2024-05-16 11:17:47 +01:00
80ea24a694
Working 2024-05-16 01:39:54 +01:00
3f43bdb16b
Added getter and delegate for automatic profile switching 2024-05-15 21:31:08 +01:00
16061e077b
Added delegates and automatic switching 2024-05-15 21:28:34 +01:00
d4ac87e36d
I believe we now have a semi-working graphics profiles and the ability to switch between them! 2024-05-15 17:44:04 +01:00
b8f67c33dc
Merge branch 'Dev' into Feature/GraphicsProfiles 2024-05-15 16:34:14 +01:00
9955136737
Oops forgot to make all the functions publicly accessible in C++🤦 2024-05-15 16:33:04 +01:00
1a1b445302
Added detection of battery and proceeds to load battery profile 2024-05-15 16:31:02 +01:00
5c9a3256d6
Working loading, Saving and default config functions 2024-05-15 16:20:05 +01:00
49d65d576f
Merge branch 'Dev' into Feature/Update_DLL
# Conflicts:
#	Source/ThirdParty/HarmonyLinkLib/bin/Linux/libHarmonyLinkLib.so
#	Source/ThirdParty/HarmonyLinkLib/bin/Win64/HarmonyLinkLib.dll
#	Source/ThirdParty/HarmonyLinkLib/bin/Win64/HarmonyLinkLib.lib
2024-05-15 02:49:09 +01:00
996dd0a232
Merge branch 'Dev' into Feature/GraphicsProfiles 2024-05-15 02:45:09 +01:00
727e51f620
Added LFS 2024-05-15 02:43:46 +01:00
e9b04607ec
Update core files 2024-05-15 02:42:36 +01:00
96d5f5c762
Add Graphics settings 2024-05-15 02:38:38 +01:00
Jordon Brooks
f079124c18
Add marketplace link 2024-04-24 20:22:26 +01:00
Jordon Brooks
05d9e10e54
Update "CreatedByURL" 2024-04-24 20:14:15 +01:00
d2e2d73004
Merge branch 'Dev' into Stable 2024-01-10 19:36:37 +00:00
Jordon Brooks
dc29a04156
Update HarmonyLink.Build.cs 2024-01-10 17:04:09 +00:00
Jordon Brooks
2ed0cefa3a
Update HarmonyLink.cpp 2024-01-10 17:03:36 +00:00
Jordon Brooks
d01ae5209e
Update HarmonyLink.h 2024-01-10 17:02:51 +00:00
Jordon Brooks
832f5ec8cd
Update README.md 2024-01-09 22:55:57 +00:00
bfa9030c10
Added copyright notice 2024-01-09 20:54:04 +00:00
fb8a01915e
Added marketplace stuff 2024-01-08 16:53:13 +00:00
58 changed files with 2430 additions and 328 deletions

39
.gitattributes vendored Normal file
View file

@ -0,0 +1,39 @@
* text=auto
# Sources
*.c text diff=cpp
*.cc text diff=cpp
*.cxx text diff=cpp
*.cpp text diff=cpp
*.cpi text diff=cpp
*.c++ text diff=cpp
*.hpp text diff=cpp
*.h text diff=cpp
*.h++ text diff=cpp
*.hh text diff=cpp
# Compiled Object files
*.slo binary filter=lfs diff=lfs merge=lfs
*.lo binary filter=lfs diff=lfs merge=lfs
*.o binary filter=lfs diff=lfs merge=lfs
*.obj binary filter=lfs diff=lfs merge=lfs
# Precompiled Headers
*.gch binary filter=lfs diff=lfs merge=lfs
*.pch binary filter=lfs diff=lfs merge=lfs
# Compiled Dynamic libraries
*.so binary filter=lfs diff=lfs merge=lfs
*.dylib binary filter=lfs diff=lfs merge=lfs
*.dll binary filter=lfs diff=lfs merge=lfs
# Compiled Static libraries
*.lai binary filter=lfs diff=lfs merge=lfs
*.la binary filter=lfs diff=lfs merge=lfs
*.a binary filter=lfs diff=lfs merge=lfs
*.lib binary filter=lfs diff=lfs merge=lfs
# Executables
*.exe binary filter=lfs diff=lfs merge=lfs
*.out binary filter=lfs diff=lfs merge=lfs
*.app binary filter=lfs diff=lfs merge=lfs

8
Config/FilterPlugin.ini Normal file
View file

@ -0,0 +1,8 @@
[FilterPlugin]
; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and
; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively.
;
; Examples:
; /README.txt
; /Extras/...
; /Binaries/ThirdParty/*.dll

View file

@ -1,24 +0,0 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.0",
"FriendlyName": "HarmonyLink",
"Description": "",
"Category": "Other",
"CreatedBy": "Jordon Brooks",
"CreatedByURL": "https://jordongamedev.co.uk",
"DocsURL": "",
"MarketplaceURL": "",
"SupportURL": "",
"CanContainContent": true,
"IsBetaVersion": true,
"IsExperimentalVersion": true,
"Installed": false,
"Modules": [
{
"Name": "HarmonyLink",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}

33
HarmonyLinkUE.uplugin Normal file
View file

@ -0,0 +1,33 @@
{
"FileVersion": 3,
"Version": 1,
"VersionName": "1.1.0",
"FriendlyName": "HarmonyLinkUE",
"Description": "Revolutionize handheld gaming with adaptive game settings. Optimize graphics and gameplay experience based on real-time system metrics. Open-source project empowering developers to enhance games on portable devices",
"Category": "Handheld",
"CreatedBy": "Jordon Brooks",
"CreatedByURL": "https://bbgames.dev",
"DocsURL": "https://github.com/Jordonbc/HarmonyLink",
"MarketplaceURL": "com.epicgames.launcher://ue/marketplace/product/92fd511971274d1f955abb7197485041",
"SupportURL": "",
"CanContainContent": false,
"IsBetaVersion": false,
"IsExperimentalVersion": false,
"Installed": false,
"Modules": [
{
"Name": "HarmonyLinkUE",
"Type": "Runtime",
"LoadingPhase": "PreDefault",
"WhitelistPlatforms": [
"Win64",
"Linux"
]
},
{
"Name": "HarmonyLinkSettings",
"Type": "Runtime",
"LoadingPhase": "Default"
}
]
}

View file

@ -6,6 +6,11 @@
This repository hosts the Unreal Engine 5 plugin for HarmonyLink, designed to provide real-time device metrics for handheld gaming, enhancing both gameplay and immersion. This repository hosts the Unreal Engine 5 plugin for HarmonyLink, designed to provide real-time device metrics for handheld gaming, enhancing both gameplay and immersion.
<p align="center">
<a align="center" href="https://www.unrealengine.com/marketplace/en-US/product/harmonylink">Download the Marketplace plugin here</a>
</p>
## Features ## Features
- **Intelligent Hardware Recognition**: HarmonyLink 2.0 can accurately identify specific devices like the Steam Deck without relying on manual environment variables or command line arguments. - **Intelligent Hardware Recognition**: HarmonyLink 2.0 can accurately identify specific devices like the Steam Deck without relying on manual environment variables or command line arguments.
@ -27,7 +32,11 @@ This repository hosts the Unreal Engine 5 plugin for HarmonyLink, designed to pr
Watch the HarmonyLink Plugin in action, demonstrating its impact on gameplay through a Minecraft mod example: Watch the HarmonyLink Plugin in action, demonstrating its impact on gameplay through a Minecraft mod example:
<p align="center"> <p align="center">
<a href="https://www.youtube.com/watch?v=Uttf-lcE-Jk"><img src="https://img.youtube.com/vi/Uttf-lcE-Jk/0.jpg" alt="HarmonyLink Demo"/></a> <a href="https://www.youtube.com/watch?v=lc7a6hpp7g8"><img src="https://img.youtube.com/vi/lc7a6hpp7g8/0.jpg" alt="HarmonyLink 2.0 Demo"/></a>
</p>
<p align="center">
<a href="https://www.youtube.com/watch?v=bxFBTU4nLUo"><img src="https://img.youtube.com/vi/bxFBTU4nLUo/0.jpg" alt="HarmonyLink 2.0 Demo"/></a>
</p> </p>
<p align="center"> <p align="center">
<img src="Resources/Blueprints.png" alt="Blueprint Nodes"/> <img src="Resources/Blueprints.png" alt="Blueprint Nodes"/>

View file

@ -1,18 +0,0 @@
// Copyright Epic Games, Inc. All Rights Reserved.
#include "HarmonyLink.h"
#include "Modules/ModuleManager.h"
#define LOCTEXT_NAMESPACE "FHarmonyLinkModule"
void FHarmonyLinkModule::StartupModule()
{
}
void FHarmonyLinkModule::ShutdownModule()
{
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHarmonyLinkModule, HarmonyLink)

View file

@ -1,47 +0,0 @@
// Copyright (C) 2023 Jordon Brooks
#include "HarmonyLinkLibrary.h"
#include <HarmonyLinkLib.h>
bool UHarmonyLinkLibrary::IsWine()
{
return HarmonyLinkLib::get_is_wine();
}
bool UHarmonyLinkLibrary::IsLinux()
{
#if PLATFORM_WINDOWS
return IsWine();
#elif PLATFORM_LINUX
return true;
#else
return false;
#endif
}
bool UHarmonyLinkLibrary::IsSteamDeck()
{
return GetDeviceInfo().Device == EDeviceEnum::STEAM_DECK;
}
FCPUInfo UHarmonyLinkLibrary::GetCPUInfo()
{
return FCPUInfo(HarmonyLinkLib::get_cpu_info());
}
FDevice UHarmonyLinkLibrary::GetDeviceInfo()
{
return FDevice(HarmonyLinkLib::get_device_info());
}
FOSVerInfo UHarmonyLinkLibrary::GetOSInfo()
{
return FOSVerInfo(HarmonyLinkLib::get_os_version());
}
FBattery UHarmonyLinkLibrary::GetBatteryStatus()
{
return FBattery(HarmonyLinkLib::get_battery_status());
}

View file

@ -1,41 +0,0 @@
// Copyright (C) 2023 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "Structs/Battery.h"
#include "Structs/CPUInfo.h"
#include "Structs/Device.h"
#include "Structs/OSVerInfo.h"
#include "HarmonyLinkLibrary.generated.h"
/**
*
*/
UCLASS()
class HARMONYLINK_API UHarmonyLinkLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
UFUNCTION(BlueprintCallable, Category="HarmonyLink")
static bool IsWine();
UFUNCTION(BlueprintCallable, Category="HarmonyLink")
static bool IsLinux();
UFUNCTION(BlueprintCallable, Category="HarmonyLink")
static bool IsSteamDeck();
UFUNCTION(BlueprintCallable, Category="HarmonyLink")
static FCPUInfo GetCPUInfo();
UFUNCTION(BlueprintCallable, Category="HarmonyLink")
static FDevice GetDeviceInfo();
UFUNCTION(BlueprintCallable, Category="HarmonyLink")
static FOSVerInfo GetOSInfo();
UFUNCTION(BlueprintCallable, Category="HarmonyLink")
static FBattery GetBatteryStatus();
};

View file

@ -1,29 +0,0 @@
// Copyright (C) 2023 Jordon Brooks
#pragma once
#include <HarmonyLinkLib.h>
#include "CoreMinimal.h"
#include "Battery.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct FBattery
{
GENERATED_BODY()
FBattery() {}
UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool HasBattery = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
bool IsACConnected = false;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int32 BatteryPercent = 0;
FBattery(HarmonyLinkLib::FBattery* battery);
};

View file

@ -1,36 +0,0 @@
// Copyright (C) 2023 Jordon Brooks
#pragma once
#include <HarmonyLinkLib.h>
#include "CoreMinimal.h"
#include "CPUInfo.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct FCPUInfo
{
GENERATED_BODY()
FCPUInfo() {}
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString VendorID;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString ModelName;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int32 PhysicalCores = 0;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int32 LogicalCores = 0;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
TSet<FString> Flags;
FCPUInfo(HarmonyLinkLib::FCPUInfo* cpu_info);
};

View file

@ -1,30 +0,0 @@
#pragma once
#include "CoreMinimal.h"
#include "Enums/DeviceEnum.h"
#include "Enums/Platform.h"
#include <Structs/FDevice.h>
#include "Device.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct FDevice
{
GENERATED_BODY()
FDevice() {}
UPROPERTY(BlueprintReadWrite, EditAnywhere)
EPlatform Platform = EPlatform::WINDOWS;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
EDeviceEnum Device = EDeviceEnum::DESKTOP;
FDevice(HarmonyLinkLib::FDevice* oldDevice);
static EDeviceEnum Convert(HarmonyLinkLib::EDevice Device);
static EPlatform Convert(HarmonyLinkLib::EPlatform Platform);
};

View file

@ -1,42 +0,0 @@
// Copyright (C) 2023 Jordon Brooks
#pragma once
#include <HarmonyLinkLib.h>
#include "CoreMinimal.h"
#include "OSVerInfo.generated.h"
/**
*
*/
USTRUCT(BlueprintType)
struct FOSVerInfo
{
GENERATED_BODY()
FOSVerInfo() {}
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString Name;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
int32 Version = 0;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString ID;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString VersionID;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString VersionCodename;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString PrettyName;
UPROPERTY(BlueprintReadWrite, EditAnywhere)
FString VariantID;
FOSVerInfo(HarmonyLinkLib::FOSVerInfo* oldInfo);
};

View file

@ -0,0 +1,28 @@
// Copyright (C) 2024 Jordon Brooks
using UnrealBuildTool;
public class HarmonyLinkSettings : ModuleRules
{
public HarmonyLinkSettings(ReadOnlyTargetRules Target) : base(Target)
{
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
PublicDependencyModuleNames.AddRange(
new string[]
{
"Core",
}
);
PrivateDependencyModuleNames.AddRange(
new string[]
{
"CoreUObject",
"Engine",
"HarmonyLinkUE"
}
);
}
}

View file

@ -0,0 +1,24 @@
// Copyright (C) 2024 Jordon Brooks
#include "HarmonyLinkSettings.h"
#include "Objects/HarmonyLinkGraphics.h"
#define LOCTEXT_NAMESPACE "FHarmonyLinkSettingsModule"
DEFINE_LOG_CATEGORY(LogHarmonyLinkSettings);
void FHarmonyLinkSettingsModule::StartupModule()
{
}
void FHarmonyLinkSettingsModule::ShutdownModule()
{
// Ensure we safely destroy our singleton instance
UHarmonyLinkGraphics::DestroySettings();
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHarmonyLinkSettingsModule, HarmonyLinkSettings)

View file

@ -0,0 +1,798 @@
// Copyright (C) 2024 Jordon Brooks
#include "Objects/HarmonyLinkGraphics.h"
#include "ComponentRecreateRenderStateContext.h"
#include "HarmonyLinkSettings.h"
#include "HarmonyLinkLibrary.h"
#include "GameFramework/GameUserSettings.h"
#include "Kismet/GameplayStatics.h"
UHarmonyLinkGraphics* UHarmonyLinkGraphics::_INSTANCE = nullptr;
int32 UHarmonyLinkGraphics::_TickRate = 1;
FTimerHandle UHarmonyLinkGraphics::_TickTimerHandle = FTimerHandle();
TSharedPtr<FConfigFile> UHarmonyLinkGraphics::_ConfigFile = nullptr;
bool UHarmonyLinkGraphics::_bAutomaticSwitch = false;
int32 UHarmonyLinkGraphics::_LastBatteryPercentage = 0;
EProfile UHarmonyLinkGraphics::_ActiveProfile = EProfile::NONE;
TMap<EProfile, FSettingsProfile> UHarmonyLinkGraphics::_Profiles = TMap<EProfile, FSettingsProfile>();
TMap<EProfile, FName> UHarmonyLinkGraphics::_ProfileNames =
{
{EProfile::BATTERY, "Battery"},
{EProfile::CHARGING, "Charging"},
{EProfile::DOCKED, "Docked"},
};
/**
* @brief Default graphics settings for different power states in HarmonyLink.
*
* This map defines the default configuration values for various graphics settings based on the power state of the device.
* The outer map's key is the power state (e.g., "Battery", "Charging", "Docked").
* The inner map contains key-value pairs where the key is the setting name and the value is the corresponding configuration value.
*
* Power States:
* - "Battery": Settings for when the device is running on battery power.
* - "Charging": Settings for when the device is charging.
* - "Docked": Settings for when the device is docked.
*/
TMap<FName, TMap<FName, FHLConfigValue>> UHarmonyLinkGraphics::_DefaultSettings = {
{ "Battery", {
// { TEXT("r.ReflectionMethod"), FHLConfigValue(0) },
// { TEXT("r.DynamicGlobalIlluminationMethod"), FHLConfigValue(2) },
// { TEXT("r.Shadow.Virtual.Enable"), FHLConfigValue(0) },
// { TEXT("r.ScreenPercentage"), FHLConfigValue(50) },
}},
{ "Charging", {
// { TEXT("r.ReflectionMethod"), FHLConfigValue(2) },
// { TEXT("r.DynamicGlobalIlluminationMethod"), FHLConfigValue(2) },
// { TEXT("r.Shadow.Virtual.Enable"), FHLConfigValue(1) },
// { TEXT("r.ScreenPercentage"), FHLConfigValue(100) },
}},
{ "Docked", {
// { TEXT("r.ReflectionMethod"), FHLConfigValue(2) },
// { TEXT("r.DynamicGlobalIlluminationMethod"), FHLConfigValue(2) },
// { TEXT("r.Shadow.Virtual.Enable"), FHLConfigValue(1) },
// { TEXT("r.ScreenPercentage"), FHLConfigValue(100) },
}}
};
UHarmonyLinkGraphics::~UHarmonyLinkGraphics()
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("~UHarmonyLinkGraphics called."));
FWorldDelegates::OnPostWorldInitialization.RemoveAll(this);
FWorldDelegates::OnPreWorldFinishDestroy.RemoveAll(this);
}
void UHarmonyLinkGraphics::LoadConfig(const bool bForceReload)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("LoadConfig called."));
QUICK_SCOPE_CYCLE_COUNTER(HarmonyLinkGraphics_LoadSettings);
// Load the settings into the map
FConfigFile* _ = GetConfig();
DebugPrintProfiles();
}
bool UHarmonyLinkGraphics::LoadSettingsFromConfig(FConfigFile* ConfigFile) const
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("LoadSettingsFromConfig called."));
//const FString Filename = "HarmonyLink"; //GetConfigDirectoryFile(bLoadDefaults);
// Load each profile section
bool bLoaded = true;
// Load the _bAutomaticSwitch variable
const FString SectionName = TEXT("HarmonyLink");
const FString KeyName = TEXT("AutomaticProfileSwitch");
if (!ConfigFile->GetBool(*SectionName, *KeyName, _bAutomaticSwitch))
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Failed to load bAutomaticSwitch from config"));
bLoaded = false;
}
else
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Loaded bAutomaticSwitch: %s"), _bAutomaticSwitch ? TEXT("true") : TEXT("false"));
}
for (const TPair<EProfile, FName>& Profile : _ProfileNames)
{
if (!LoadSection(ConfigFile, Profile))
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Failed to load section: '%s'"), *Profile.Value.ToString());
bLoaded = false;
}
}
// Check if all profiles and settings were loaded successfully
if (bLoaded)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Successfully loaded config."));
return true;
}
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Failed to load config file."));
return false;
}
bool UHarmonyLinkGraphics::LoadSection(FConfigFile* ConfigFile, const TPair<EProfile, FName> Profile)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("LoadSection called."));
if (!ensureMsgf(ConfigFile, TEXT("ConfigFile is nullptr!"))) return false;
const FName& SectionName = Profile.Value;
const EProfile ProfileKey = Profile.Key;
const FConfigSection* Section = nullptr;
#if (ENGINE_MAJOR_VERSION >= 5)
Section = ConfigFile->FindSection(*SectionName.ToString());
#elif (ENGINE_MAJOR_VERSION == 4) && (ENGINE_MINOR_VERSION >= 27)
Section = ConfigFile->FindOrAddSection(*SectionName.ToString());
#else
#error "Unsupported Unreal Engine version"
#endif
if (Section)
{
FSettingsProfile& SettingsProfile = _Profiles.FindOrAdd(ProfileKey);
SettingsProfile.SectionName = SectionName;
SettingsProfile.Settings.Empty(); // Clear previous settings
for (const auto& ValueIt : *Section)
{
FString ValueString = ValueIt.Value.GetValue();
FString Value, Type;
// Parse the Value and Type from the string
if (ValueString.Split(TEXT(", Type="), &Value, &Type))
{
Value = Value.RightChop(7); // Remove "(Value=" prefix
Type = Type.LeftChop(1); // Remove ")" suffix
if (Type == "int")
{
const int32 IntValue = FCString::Atoi(*Value);
SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(IntValue));
}
else if (Type == "float")
{
const float FloatValue = FCString::Atof(*Value);
SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(FloatValue));
}
else if (Type == "bool")
{
const bool BoolValue = Value == "true";
SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(BoolValue));
}
else if (Type == "string")
{
SettingsProfile.Settings.Add(ValueIt.Key, FHLConfigValue(Value));
}
}
}
return true;
}
return false;
}
void UHarmonyLinkGraphics::SaveConfig() const
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("SaveConfig called."));
Intermal_SaveConfig(false);
}
void UHarmonyLinkGraphics::SetSetting(const EProfile Profile, const FName Setting, const FHLConfigValue& Value)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("SetSetting called."));
// Ignore if HarmonyLinkSettings is disabled
if (Profile == EProfile::NONE)
{
return;
}
// Find the profile name associated with the given EProfile
const FName* ProfileName = _ProfileNames.Find(Profile);
if (!ProfileName)
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Profile not found."));
return;
}
FString TypeString;
FString ValueString;
switch (Value.GetType())
{
case EConfigValueType::Int:
ValueString = FString::FromInt(Value.GetValue<int32>());
TypeString = TEXT("int");
break;
case EConfigValueType::Float:
ValueString = FString::SanitizeFloat(Value.GetValue<float>());
TypeString = TEXT("float");
break;
case EConfigValueType::Bool:
ValueString = Value.GetValue<bool>() ? TEXT("True") : TEXT("False");
TypeString = TEXT("bool");
break;
case EConfigValueType::String:
ValueString = Value.GetValue<FString>();
TypeString = TEXT("string");
break;
}
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Applying '%s': Value='%s', Type='%s' to profile '%s'."), *Setting.ToString(), *TypeString, *ValueString, *ProfileName->ToString());
// Find the settings associated with the profile
FSettingsProfile* SettingsProfile = _Profiles.Find(Profile);
if (!SettingsProfile)
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("No settings found for profile %s."), *ProfileName->ToString());
return;
}
SettingsProfile->Settings.FindOrAdd(Setting) = Value;
}
UHarmonyLinkGraphics* UHarmonyLinkGraphics::GetSettings()
{
UE_LOG(LogHarmonyLinkSettings, VeryVerbose, TEXT("GetSettings called."));
// Check if we already initialised
if (_INSTANCE)
{
return _INSTANCE;
}
// Proceed to create a new singleton instance
_INSTANCE = NewObject<UHarmonyLinkGraphics>();
_INSTANCE->Init();
return _INSTANCE;
}
FSettingsProfile UHarmonyLinkGraphics::GetSettingProfile(const EProfile Profile)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("GetSettingProfile called."));
// Ignore if HarmonyLinkSettings is disabled
if (Profile == EProfile::NONE)
{
return FSettingsProfile();
}
// Find the profile name associated with the given EProfile
const FName* ProfileName = _ProfileNames.Find(Profile);
if (!ProfileName)
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Profile not found."));
return FSettingsProfile();
}
// Find the settings associated with the profile
FSettingsProfile* SettingsProfile = _Profiles.Find(Profile);
if (!SettingsProfile)
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("No settings found for profile %s."), *ProfileName->ToString());
return FSettingsProfile();
}
return *SettingsProfile;
}
EProfile UHarmonyLinkGraphics::GetActiveProfile() const
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("GetActiveProfile called."));
return _ActiveProfile;
}
void UHarmonyLinkGraphics::SetAutomaticSwitching(const bool bAutomaticSwitch)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("SetAutomaticSwitching called."));
_bAutomaticSwitch = bAutomaticSwitch;
OnAutomaticSwitchChanged.Broadcast(_bAutomaticSwitch);
}
bool UHarmonyLinkGraphics::GetAutomaticSwitching() const
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("GetAutomaticSwitching called."));
return _bAutomaticSwitch;
}
void UHarmonyLinkGraphics::DestroySettings()
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("DestroySettings called."));
if (_INSTANCE)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Destroying UHarmonyLinkGraphics."))
FWorldDelegates::OnPostWorldInitialization.RemoveAll(_INSTANCE);
FWorldDelegates::OnPreWorldFinishDestroy.RemoveAll(_INSTANCE);
_INSTANCE->RemoveFromRoot();
#if (ENGINE_MAJOR_VERSION >= 5)
_INSTANCE->MarkAsGarbage(); // For UE 5.0 and above
#elif (ENGINE_MAJOR_VERSION == 4) && (ENGINE_MINOR_VERSION >= 27)
_INSTANCE->MarkPendingKill(); // For UE 4.27 and above
#else
#error "Unsupported Unreal Engine version"
#endif
_INSTANCE = nullptr;
}
}
void UHarmonyLinkGraphics::Init()
{
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("HarmonyLinkGraphics initialized."));
if (_INSTANCE != this)
{
if (_INSTANCE)
{
DestroySettings();
}
_INSTANCE = this;
}
AddToRoot();
FWorldDelegates::OnPostWorldInitialization.AddStatic(&UHarmonyLinkGraphics::OnPostWorldInitialization);
FWorldDelegates::OnPreWorldFinishDestroy.AddStatic(&UHarmonyLinkGraphics::OnWorldEnd);
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Init called."));
if (!UHarmonyLinkLibrary::IsInitialised())
{
UE_LOG(LogHarmonyLinkSettings, Fatal, TEXT("Failed to initialise HarmonyLinkLib!"));
return;
}
LoadConfig();
const FBattery BatteryStatus = UHarmonyLinkLibrary::GetBatteryStatus();
if (BatteryStatus.HasBattery)
{
if (BatteryStatus.IsACConnected)
{
ApplyProfileInternal(EProfile::BATTERY);
}
else
{
ApplyProfileInternal(EProfile::CHARGING);
}
}
}
void UHarmonyLinkGraphics::Intermal_SaveConfig(const bool bDefaultConfig) const
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Intermal_SaveConfig called."));
QUICK_SCOPE_CYCLE_COUNTER(HarmonyLinkGraphics_SaveConfig);
const FString Filename = GetConfigDirectoryFile(bDefaultConfig);
const FString SectionName = TEXT("HarmonyLink");
const FString KeyName = TEXT("AutomaticProfileSwitch");
// Save the _bAutomaticSwitch variable
GConfig->SetBool(*SectionName, *KeyName, _bAutomaticSwitch, Filename);
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Saving bAutomaticSwitch: %s"), _bAutomaticSwitch ? TEXT("true") : TEXT("false"));
for (const TPair<EProfile, FSettingsProfile>& Profile : _Profiles)
{
SaveSection(Profile.Value, bDefaultConfig);
}
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Flushing file: '%s'"), *Filename);
GetConfig()->Dirty = true;
// You'd think that Write would actually write something but for some
// reason even if it outputs a success the file doesn't actually get created.
// For this reason, Flush seems to work for now.
//GetConfig()->Write(Filename);
GConfig->Flush(true, Filename);
}
void UHarmonyLinkGraphics::Tick()
{
UE_LOG(LogHarmonyLinkSettings, VeryVerbose, TEXT("Tick called."));
const FBattery BatteryStatus = UHarmonyLinkLibrary::GetBatteryStatus();
if (BatteryStatus.BatteryPercent != _LastBatteryPercentage)
{
// Ensure thread-safe broadcasting
OnBatteryLevelChanged.Broadcast(BatteryStatus.BatteryPercent);
}
if (!_bAutomaticSwitch)
{
return;
}
if (BatteryStatus.HasBattery)
{
if (BatteryStatus.IsACConnected)
{
if (_ActiveProfile != EProfile::CHARGING)
{
ApplyProfileInternal(EProfile::CHARGING);
}
}
else
{
if (_ActiveProfile != EProfile::BATTERY)
{
ApplyProfileInternal(EProfile::BATTERY);
}
}
}
}
void UHarmonyLinkGraphics::CreateDefaultConfigFile() const
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("CreateDefaultConfigFile called."));
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Creating default config file."));
LoadDefaults();
Intermal_SaveConfig(true);
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Default config file created."));
}
FString UHarmonyLinkGraphics::GetConfigDirectoryFile(const bool bDefaultFolder)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("GetConfigDirectoryFile called."));
FString ConfigFileName = bDefaultFolder ? TEXT("DefaultHarmonyLink.ini") : TEXT("HarmonyLink.ini");
FString ConfigDirectory = bDefaultFolder ? FPaths::ProjectConfigDir() : FPaths::Combine(FPaths::GeneratedConfigDir(), UGameplayStatics::GetPlatformName());
return FPaths::Combine(ConfigDirectory, ConfigFileName);
}
void UHarmonyLinkGraphics::SaveSection(const FSettingsProfile& SettingsProfile, const bool bDefaultConfig, const bool bFlush) const
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("SaveSection called."));
if (GConfig)
{
const FString Filename = GetConfigDirectoryFile(bDefaultConfig);
for (const auto& Setting : SettingsProfile.Settings)
{
FString TypeString;
FString ValueString;
switch (Setting.Value.GetType())
{
case EConfigValueType::Int:
ValueString = FString::FromInt(Setting.Value.GetValue<int32>());
TypeString = TEXT("int");
break;
case EConfigValueType::Float:
ValueString = FString::SanitizeFloat(Setting.Value.GetValue<float>());
TypeString = TEXT("float");
break;
case EConfigValueType::Bool:
ValueString = Setting.Value.GetValue<bool>() ? TEXT("True") : TEXT("False");
TypeString = TEXT("bool");
break;
case EConfigValueType::String:
ValueString = Setting.Value.GetValue<FString>();
TypeString = TEXT("string");
break;
}
const FString ConfigValue = FString::Printf(TEXT("(Value=%s, Type=%s)"), *ValueString, *TypeString);
GConfig->SetString(*SettingsProfile.SectionName.ToString(), *Setting.Key.ToString(), *ConfigValue, Filename);
}
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Saving config file: '%s'"), *Filename);
if (bFlush)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Flushing file: '%s'"), *Filename);
GConfig->Flush(false, Filename);
}
}
}
void UHarmonyLinkGraphics::LoadDefaults() const
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("LoadDefaults called."));
_Profiles.Reset();
// Iterate over ProfileNames to create default settings
for (const TPair<EProfile, FName>& Profile : _ProfileNames)
{
FSettingsProfile NewProfileSettings;
NewProfileSettings.SectionName = Profile.Value;
if (const TMap<FName, FHLConfigValue>* Settings = _DefaultSettings.Find(Profile.Value))
{
NewProfileSettings.Settings = *Settings;
}
_Profiles.Add(Profile.Key, NewProfileSettings);
}
}
bool UHarmonyLinkGraphics::ApplyProfileInternal(const EProfile Profile)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("ApplyProfileInternal called."));
// If the profile is None, revert to the original user game settings
if (Profile == EProfile::NONE)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Reverting to original user game settings."));
if (UGameUserSettings* UserSettings = GEngine->GetGameUserSettings())
{
UserSettings->LoadSettings(true);
UserSettings->ApplySettings(true);
_ActiveProfile = EProfile::NONE;
OnProfileChanged.Broadcast(_ActiveProfile);
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Original user game settings applied."));
return true;
}
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("Failed to get user game settings."));
return false;
}
// Find the profile name associated with the given EProfile
const FName* ProfileName = _ProfileNames.Find(Profile);
if (!ProfileName)
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Profile not found."));
return false;
}
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Applying profile %s."), *ProfileName->ToString());
// Find the settings associated with the profile
FSettingsProfile* SettingsProfile = _Profiles.Find(Profile);
if (!SettingsProfile)
{
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("No settings found for profile %s."), *ProfileName->ToString());
return false;
}
{
FGlobalComponentRecreateRenderStateContext Context;
// Example of applying settings (actual application depends on your implementation)
for (const TPair<FName, FHLConfigValue>& Setting : SettingsProfile->Settings)
{
// Example of logging each setting being applied
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Patching CVar override: %s = %s"),
*Setting.Key.ToString(), *Setting.Value.ToString());
ApplySetting(Setting);
}
}
_ActiveProfile = Profile;
OnProfileChanged.Broadcast(_ActiveProfile);
return true;
}
void UHarmonyLinkGraphics::OnPostWorldInitialization(UWorld* World, UWorld::InitializationValues IVS)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("OnPostWorldInitialization called."));
if (!World || !World->IsValidLowLevel())
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Failed to Hook into World Initialisation!"))
return;
}
if (World->IsGameWorld())
{
if (IsValid(GetSettings()))
{
FTimerManager* TimerManager = &World->GetTimerManager();
if (!TimerManager)
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Failed get TimerManager!"))
return;
}
if (!TimerManager->TimerExists(_TickTimerHandle) || !TimerManager->IsTimerActive(_TickTimerHandle))
{
World->GetTimerManager().SetTimer(_TickTimerHandle, [TimerManager]
{
if (!GetSettings())
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("'This' is destroyed, Clearing timer."))
if (TimerManager)
{
TimerManager->ClearTimer(_TickTimerHandle);
}
return;
}
GetSettings()->Tick();
}, _TickRate, true);
}
else
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Error: Timer already exists."));
}
}
else
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("'This' is nullptr!"));
}
}
else
{
//UE_LOG(LogHarmonyLink, Error, TEXT("Failed to bring up tick!"));
}
}
void UHarmonyLinkGraphics::OnWorldEnd(UWorld* World)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("OnWorldEnd(UWorld* World) called."));
if (!World)
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("World Already destroyed"))
return;
}
if (!World->IsGameWorld())
{
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 bDisableAutomaticSwitch)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Applying Profile."));
// Manual profile change, turn off automatic switching
if (bDisableAutomaticSwitch)
{
_bAutomaticSwitch = false;
}
return ApplyProfileInternal(Profile);
}
void UHarmonyLinkGraphics::ApplySetting(const TPair<FName, FHLConfigValue>& Setting)
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Applying settings."));
// Apply the setting based on the key (CVar)
IConsoleManager& ConsoleManager = IConsoleManager::Get();
IConsoleVariable* CVar = ConsoleManager.FindConsoleVariable(*Setting.Key.ToString());
if (CVar)
{
switch (Setting.Value.GetType())
{
case EConfigValueType::Bool:
CVar->Set(Setting.Value.GetValue<bool>(), ECVF_SetByGameSetting);
break;
case EConfigValueType::Float:
CVar->Set(Setting.Value.GetValue<float>(), ECVF_SetByGameSetting);
break;
case EConfigValueType::Int:
CVar->Set(Setting.Value.GetValue<int32>(), ECVF_SetByGameSetting);
break;
default:
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("Unsupported value type for setting: %s"), *Setting.Key.ToString());
break;
}
}
else
{
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("Console variable not found: %s"), *Setting.Key.ToString());
}
}
void UHarmonyLinkGraphics::DebugPrintProfiles() const
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("DebugPrintProfiles started."));
for (TPair<EProfile, FSettingsProfile> Profile : _Profiles)
{
PrintDebugSection(Profile.Value);
}
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("DebugPrintProfiles completed."));
}
FConfigFile* UHarmonyLinkGraphics::GetConfig() const
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("GetConfig Called."));
if (_ConfigFile)
{
return _ConfigFile.Get();
}
FConfigFile* ConfigFile = GConfig->Find(GetConfigDirectoryFile(false), false);
if (!ConfigFile)
{
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("Config file not found, attempting to read DefaultHarmonyLink.ini."));
// Look in ProjectFolder->Config->DefaultHarmonyLink.ini
ConfigFile = GConfig->Find(GetConfigDirectoryFile(true), true);
}
/*if (!ConfigFile)
{
CreateDefaultConfigFile();
ConfigFile = GConfig->Find(GetConfigDirectoryFile(true), true);
}*/
if (ConfigFile)
{
UE_LOG(LogHarmonyLinkSettings, Verbose, TEXT("Setting up config."));
ConfigFile->Name = "HarmonyLink";
LoadSettingsFromConfig(ConfigFile);
_ConfigFile = MakeShareable(ConfigFile);
}
else
{
UE_LOG(LogHarmonyLinkSettings, Error, TEXT("Failed to make config variable!"))
return nullptr;
}
return _ConfigFile.Get();
}
void UHarmonyLinkGraphics::PrintDebugSection(FSettingsProfile& SettingsProfile)
{
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("[%s]"), *SettingsProfile.SectionName.ToString());
for (const auto& Setting : SettingsProfile.Settings)
{
FString ValueString;
FString TypeString;
switch (Setting.Value.GetType())
{
case EConfigValueType::Int:
ValueString = FString::FromInt(Setting.Value.GetValue<int32>());
TypeString = TEXT("int");
break;
case EConfigValueType::Float:
ValueString = FString::SanitizeFloat(Setting.Value.GetValue<float>());
TypeString = TEXT("float");
break;
case EConfigValueType::Bool:
ValueString = Setting.Value.GetValue<bool>() ? TEXT("true") : TEXT("false");
TypeString = TEXT("bool");
break;
case EConfigValueType::String:
ValueString = Setting.Value.GetValue<FString>();
TypeString = TEXT("string");
break;
}
UE_LOG(LogHarmonyLinkSettings, Warning, TEXT("Key: %s = V=%s, T=%s "), *Setting.Key.ToString(), *ValueString, *TypeString);
}
}
void UHarmonyLinkGraphics::ResetInstance()
{
UE_LOG(LogHarmonyLinkSettings, Log, TEXT("Resetting instance."));
_INSTANCE->DestroySettings();
GetSettings();
}

View file

@ -0,0 +1,3 @@
// Copyright (C) 2024 Jordon Brooks
#include "Structs/HLConfigValue.h"

View file

@ -0,0 +1,18 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Profile.generated.h"
/*
* Enum representing different operating system platforms.
*/
UENUM(BlueprintType)
enum class EProfile : uint8
{
NONE = 0 UMETA(DisplayName = "NONE"),
BATTERY UMETA(DisplayName = "BATTERY"),
CHARGING UMETA(DisplayName = "CHARGING"),
DOCKED UMETA(DisplayName = "DOCKED"),
};

View file

@ -0,0 +1,15 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Modules/ModuleManager.h"
DECLARE_LOG_CATEGORY_EXTERN(LogHarmonyLinkSettings, Log, All);
class FHarmonyLinkSettingsModule : public IModuleInterface
{
public:
virtual void StartupModule() override;
virtual void ShutdownModule() override;
};

View file

@ -0,0 +1,499 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Enums/Profile.h"
#include "Structs/SettingsProfile.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "HarmonyLinkGraphics.generated.h"
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnProfileChanged, EProfile, Profile);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnAutomaticSwitchChanged, bool, bAutomaticSwich);
DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnBatteryLevelChanged, int32, BatteryPercent);
/**
*
*/
UCLASS(Blueprintable, config="HarmonyLink")
class HARMONYLINKSETTINGS_API UHarmonyLinkGraphics : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
virtual ~UHarmonyLinkGraphics() override;
UPROPERTY(BlueprintAssignable)
FOnProfileChanged OnProfileChanged;
UPROPERTY(BlueprintAssignable)
FOnAutomaticSwitchChanged OnAutomaticSwitchChanged;
UPROPERTY(BlueprintAssignable)
FOnBatteryLevelChanged OnBatteryLevelChanged;
/**
* @brief Loads the graphics configuration settings.
*
* This function loads the graphics settings into the appropriate map. If the initial loading attempt fails,
* it retries a second time. The settings are loaded using the `LoadSettingsFromConfig` method.
* After loading the settings, the function prints debug information about the profiles.
*
* @param bForceReload Indicates whether to force reload the settings. Defaults to false.
*
* @note This function uses the QUICK_SCOPE_CYCLE_COUNTER macro for performance profiling.
*/
UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings")
void LoadConfig(const bool bForceReload = false);
/**
* @brief Saves the current graphics configuration settings to the configuration file.
*
* This function iterates through all profiles and saves their settings to the configuration file.
* Additionally, it saves the `_bAutomaticSwitch` variable to the configuration file and flushes the changes.
*
* @details
* - Uses the QUICK_SCOPE_CYCLE_COUNTER macro for performance profiling.
* - Iterates through the `_Profiles` map and calls `SaveSection` for each profile to save its settings.
* - Saves the `_bAutomaticSwitch` variable under the "HarmonyLink" section with the key "AutomaticProfileSwitch".
* - Logs the status of `_bAutomaticSwitch` and flushes the configuration file to ensure all changes are written.
*
* @note Uses UE_LOG for logging the save process and status messages.
*/
UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings")
void SaveConfig() const;
/**
* @brief Sets a specific configuration value for a given profile.
*
* This function updates a setting for a specified profile with a new value. It handles various data types (int, float, bool, string)
* and logs the process. If the profile or settings cannot be found, it logs an error message.
*
* @param Profile The profile identifier for which the setting is to be updated.
* @param Setting The name of the setting to update.
* @param Value The new value to set for the specified setting.
*
* @details
* - Finds the profile name associated with the given `EProfile`.
* - Determines the type of the value and converts it to a string.
* - Logs the application of the setting with its value and type.
* - Finds the settings profile associated with the given profile.
* - Updates the setting with the new value or adds it if it does not exist.
*
* @note Uses UE_LOG for logging the update process and any errors.
*/
UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings")
void SetSetting(EProfile Profile, FName Setting, const FHLConfigValue& Value);
/**
* @brief Applies the specified graphics profile.
*
* This function applies the given graphics profile. If automatic profile switching is enabled, it can be disabled by setting the `bDisableAutomaticSwitch` parameter to true.
*
* @param Profile The profile to apply.
* @param bDisableAutomaticSwitch Indicates whether to disable automatic profile switching. Defaults to true.
* @return bool Returns true if the profile was successfully applied; false otherwise.
*
* @details
* - If `bDisableAutomaticSwitch` is true, sets `_bAutomaticSwitch` to false to disable automatic profile switching.
* - Calls `ApplyProfileInternal` to apply the specified profile.
* - Returns the result of `ApplyProfileInternal`.
*/
UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings")
bool ApplyProfile(EProfile Profile, const bool bDisableAutomaticSwitch = true);
/**
* @brief Retrieves the singleton instance of the UHarmonyLinkGraphics settings.
*
* This function returns the singleton instance of the `UHarmonyLinkGraphics` class. If the instance has not been
* initialized, it creates a new instance, adds it to the root to prevent garbage collection, initializes it, and
* then returns the instance.
*
* @return UHarmonyLinkGraphics* The singleton instance of the `UHarmonyLinkGraphics` class.
*
* @details
* - Checks if the singleton instance (`_INSTANCE`) is already initialized.
* - If not, creates a new instance using `NewObject<UHarmonyLinkGraphics>`.
* - Adds the new instance to the root to prevent it from being garbage collected.
* - Calls the `Init` function on the new instance to perform any necessary initialization.
* - Returns the singleton instance.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink Settings")
static UHarmonyLinkGraphics* GetSettings();
/**
* @brief Retrieves the settings profile for a specified profile.
*
* This function returns the settings profile associated with the given `EProfile`. If the profile name or the settings
* cannot be found, it logs an error message and returns an empty `FSettingsProfile`.
*
* @param Profile The profile identifier for which the settings profile is to be retrieved.
* @return FSettingsProfile The settings profile associated with the specified profile. If the profile is not found, returns an empty `FSettingsProfile`.
*
* @details
* - Finds the profile name associated with the given `EProfile`.
* - Logs an error and returns an empty `FSettingsProfile` if the profile name is not found.
* - Finds the settings profile associated with the profile.
* - Logs an error and returns an empty `FSettingsProfile` if the settings profile is not found.
* - Returns the settings profile if found.
*
* @note Uses UE_LOG for logging errors.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, BlueprintPure, Category="HarmonyLink Settings")
FSettingsProfile GetSettingProfile(const EProfile Profile);
/**
* @brief Retrieves the currently active profile.
*
* This function returns the currently active profile of the `UHarmonyLinkGraphics` settings.
*
* @return EProfile The currently active profile.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink Settings")
EProfile GetActiveProfile() const;
/**
* @brief Sets the automatic profile switching state.
*
* This function updates the automatic profile switching setting and broadcasts the change.
*
* @param bAutomaticSwitch Indicates whether automatic profile switching should be enabled (true) or disabled (false).
*
* @details
* - Updates the `_bAutomaticSwitch` member variable with the new value.
* - Broadcasts the change to any listeners using the `OnAutomaticSwitchChanged` delegate.
*/
UFUNCTION(BlueprintCallable, Category="HarmonyLink Settings")
void SetAutomaticSwitching(const bool bAutomaticSwitch);
/**
* @brief Retrieves the current state of automatic profile switching.
*
* This function returns the state of the automatic profile switching setting.
*
* @return bool True if automatic profile switching is enabled; false otherwise.
*/
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink Settings")
bool GetAutomaticSwitching() const;
/**
* @brief Destroys the singleton instance of the UHarmonyLinkGraphics settings.
*
* This function safely destroys the singleton instance of the `UHarmonyLinkGraphics` class by removing it from root,
* marking it as garbage for cleanup, and setting the instance pointer to null.
*
* @details
* - Checks if the singleton instance (`_INSTANCE`) exists.
* - Logs the destruction of the `UHarmonyLinkGraphics` instance.
* - Removes all delegate bindings from `_INSTANCE`.
* - Removes `_INSTANCE` from the root to allow garbage collection.
* - Marks `_INSTANCE` as garbage to enable cleanup.
* - Sets `_INSTANCE` to null.
*
* @note Uses UE_LOG for logging the destruction process.
*/
static void DestroySettings();
private:
/**
* @brief Initializes the UHarmonyLinkGraphics settings.
*
* This function initializes the `UHarmonyLinkGraphics` settings by loading the configuration and applying the
* appropriate profile based on the battery status.
*
* @details
* - Calls `LoadConfig` to load the configuration settings.
* - Retrieves the current battery status using `UHarmonyLinkLibrary::GetBatteryStatus`.
* - If the device has a battery, applies the `BATTERY` profile by calling `ApplyProfileInternal`.
*/
void Init();
void Intermal_SaveConfig(const bool bDefaultConfig) const;
/**
* @brief Periodically checks and updates the battery status and profile settings.
*
* This function is executed based on a timer determined by `_TickRate`. It asynchronously checks the current battery status
* and updates the battery percentage and profile settings as needed. If the battery percentage has changed, it broadcasts
* this change. If automatic profile switching is enabled, it applies the appropriate profile based on the AC connection status.
*
* @details
* - Runs asynchronously on a separate thread to fetch the current battery status using `UHarmonyLinkLibrary::GetBatteryStatus`.
* - If the battery percentage has changed, broadcasts the new battery percentage in a thread-safe manner.
* - If automatic profile switching (`_bAutomaticSwitch`) is disabled, exits the function.
* - If the device has a battery:
* - Checks the AC connection status and applies the `CHARGING` profile if connected to AC.
* - Applies the `BATTERY` profile if not connected to AC.
* - Ensures all profile applications and broadcasts are executed on the main thread for thread safety.
*
* @note Uses UE_LOG for logging and `Async` for asynchronous execution.
*/
void Tick();
/**
* @brief Creates a default configuration file.
*
* This function creates a default configuration file by loading the default settings and then saving them to the config file.
*
* @details
* - Logs the start of the default configuration file creation process.
* - Calls `LoadDefaults` to load the default settings into the configuration.
* - Calls `SaveConfig` to save the default settings to the configuration file.
* - Logs the completion of the default configuration file creation process.
*
* @note Uses UE_LOG for logging the creation process.
*/
void CreateDefaultConfigFile() const;
/**
* @brief Retrieves the path to the configuration file.
*
* This function constructs and returns the full path to the configuration file used by the `UHarmonyLinkGraphics` settings.
*
* @return FString The full path to the configuration file.
*
* @details
* - Defines the configuration file name as "HarmonyLink.ini".
* - Combines the generated config directory path with the configuration file name to form the full path.
* - Returns the full path to the configuration file.
*/
static FString GetConfigDirectoryFile(const bool bDefaultFolder);
/**
* @brief Loads the settings from the configuration file.
*
* This function loads the graphics settings from an INI configuration file into the appropriate structures.
* It attempts to load each profile section and the automatic switch setting. If any section fails to load,
* it logs an error message and indicates failure. If loading is successful, it logs a success message.
*
* @return true if the settings were successfully loaded; false otherwise.
*
* @details
* - Loads the configuration file specified by `GetConfigDirectoryFile`.
* - Iterates over the `_ProfileNames` to load each profile section.
* - Loads the `_bAutomaticSwitch` variable from the config.
* - If loading fails, it attempts to create a default configuration file.
*
* @note Uses UE_LOG for logging errors and success messages.
*/
bool LoadSettingsFromConfig(FConfigFile* ConfigFile) const;
/**
* @brief Loads a specific section from the configuration file into a settings profile.
*
* This function extracts settings from a specified section of the configuration file and stores them in the corresponding profile.
* It handles different data types (int, float, bool, string) and parses them accordingly. If the section is found and loaded successfully,
* the settings are added to the profile's settings map.
*
* @param ConfigFile The configuration file from which to load the section.
* @param Profile The profile key-value pair containing the profile enum and the section name.
* @return true if the section was found and loaded successfully; false otherwise.
*
* @details
* - Finds the section in the configuration file using the section name from the profile.
* - Clears any previous settings in the profile.
* - Iterates through the settings in the section, parsing the value and type, and adds them to the profile's settings map.
*
* @note The function handles settings of types int, float, bool, and string.
*/
static bool LoadSection(FConfigFile* ConfigFile, const TPair<EProfile, FName> Profile);
/**
* @brief Saves the settings of a given profile section to the configuration file.
*
* This function saves the settings of a specified profile section to the configuration file. It handles various data types
* (int, float, bool, string) and formats them accordingly before saving. If the `bFlush` parameter is true, the configuration
* file is flushed to ensure all changes are written to disk.
*
* @param SettingsProfile The settings profile to be saved.
* @param bFlush Indicates whether to flush the configuration file after saving. Defaults to false.
*
* @details
* - Checks if the global configuration object (`GConfig`) is available.
* - Constructs the full path to the configuration file using `GetConfigDirectoryFile`.
* - Iterates through the settings in the provided `SettingsProfile`.
* - Determines the type and value of each setting and formats them as strings.
* - Sets the formatted string in the configuration file for each setting.
* - Logs the save operation.
* - If `bFlush` is true, flushes the configuration file to ensure all changes are written.
*
* @note Uses UE_LOG for logging the save and flush operations.
*/
void SaveSection(const FSettingsProfile& SettingsProfile, const bool bDefaultConfig, const bool bFlush = false) const;
/**
* @brief Loads the default settings into the profiles.
*
* This function resets the current profiles and loads the default settings into the profiles. It iterates over the profile names
* and assigns the default settings to each profile.
*
* @details
* - Logs the start of the `LoadDefaults` process.
* - Resets the `_Profiles` map to clear any existing settings.
* - Iterates over `_ProfileNames` to create default settings for each profile.
* - For each profile, initializes a `FSettingsProfile` with the section name.
* - If default settings are found for the profile in `_DefaultSettings`, assigns these settings to the profile.
* - Adds the newly created profile settings to `_Profiles`.
*
* @note Uses UE_LOG for logging the start of the default settings loading process.
*/
void LoadDefaults() const;
/**
* @brief Applies the specified graphics profile internally.
*
* This function applies the given graphics profile by either reverting to the original user game settings or
* applying the settings associated with the specified profile.
*
* @param Profile The profile to apply. If the profile is `EProfile::NONE`, the function reverts to the original user game settings.
* @return bool Returns true if the profile was successfully applied; false otherwise.
*
* @details
* - If the `Profile` is `EProfile::NONE`:
* - Logs a message indicating reversion to original user game settings.
* - Retrieves the user game settings and applies them.
* - Sets `_ActiveProfile` to `EProfile::NONE` and broadcasts the profile change.
* - Returns true if successful, false otherwise.
* - Finds the profile name associated with the given `EProfile`.
* - Logs an error and returns false if the profile name is not found.
* - Logs a message indicating the profile being applied.
* - Finds the settings associated with the profile.
* - Logs a warning and returns false if the settings profile is not found.
* - Creates a render state context for applying settings.
* - Iterates through each setting in the profile and applies it using `ApplySetting`.
* - Sets `_ActiveProfile` to the specified profile and broadcasts the profile change.
* - Returns true indicating the profile was successfully applied.
*
* @note Uses UE_LOG for logging messages, warnings, and errors. Uses `OnProfileChanged` to broadcast profile changes.
*/
bool ApplyProfileInternal(EProfile Profile);
/**
* @brief Handles actions to be performed after the world initialization.
*
* This function hooks into the world initialization process to set up the `UHarmonyLinkGraphics` settings.
* It checks if the world is valid and is a game world, then initializes or reloads the settings as necessary and sets up a timer for periodic updates.
*
* @param World The world that has just been initialized.
* @param IVS Initialization values for the world.
*
* @details
* - Checks if the `World` pointer is valid. Logs an error and returns if the world is invalid.
* - If the world is a game world:
* - Retrieves the singleton settings instance using `GetSettings`.
* - If the world is a play-in-editor world, reloads the settings configuration by calling `LoadConfig` with `bForceReload` set to true.
* - Checks if the tick timer is already set or active. If not, sets a timer to call the `Tick` function at intervals specified by `_TickRate`.
*
* @note Uses UE_LOG for logging errors and informational messages.
*/
static void OnPostWorldInitialization(UWorld* World, UWorld::InitializationValues IVS);
/**
* @brief Handles actions to be performed when the world ends.
*
* This function is called when the world is about to be destroyed. It clears the tick timer and safely destroys the singleton instance of the `UHarmonyLinkGraphics` settings.
*
* @param World The world that is ending.
*
* @details
* - Checks if the `World` pointer is valid. Logs an error and returns if the world is already destroyed.
* - If the tick timer exists, clears the timer using the world's timer manager.
* - Calls `DestroySettings` to safely destroy the singleton instance of `UHarmonyLinkGraphics`.
*
* @note Uses UE_LOG for logging errors and informational messages.
*/
static void OnWorldEnd(UWorld* World);
/**
* @brief Resets the singleton instance of the UHarmonyLinkGraphics settings.
*
* This function resets the singleton instance by destroying the current instance and creating a new one.
*
* @details
* - Calls `DestroySettings` on the current instance to safely destroy it.
* - Calls `GetSettings` to create and initialize a new singleton instance.
*/
static void ResetInstance();
/**
* @brief Applies a specific configuration setting to the console variable.
*
* This function applies the given configuration setting to the corresponding console variable (CVar) based on its key. It supports different data types such as bool, float, and int.
*
* @param Setting The key-value pair representing the setting to apply. The key is the name of the console variable, and the value is the configuration value.
*
* @details
* - Uses the console manager to find the console variable associated with the given setting key.
* - Depending on the type of the configuration value, sets the console variable to the corresponding value.
* - If the console variable is not found, logs a warning message.
* - If the value type is unsupported, logs a warning message.
*
* @note Uses UE_LOG for logging warnings about unsupported value types or missing console variables.
*/
static void ApplySetting(const TPair<FName, FHLConfigValue>& Setting);
/**
* @brief Prints debug information for all profiles.
*
* This function logs the debug information for all profiles stored in `_Profiles`. It iterates through each profile and calls `PrintDebugSection` to print the details.
*
* @details
* - Logs the start of the `DebugPrintProfiles` process.
* - Iterates over each profile in the `_Profiles` map.
* - Calls `PrintDebugSection` for each profile to print its details.
* - Logs the completion of the `DebugPrintProfiles` process.
*
* @note Uses UE_LOG for logging the start and completion of the debug print process.
*/
void DebugPrintProfiles() const;
FConfigFile* GetConfig() const;
/**
* @brief Prints debug information for a specific settings profile.
*
* This function logs the debug information for a given settings profile, including the section name and each setting's key, value, and type.
*
* @param SettingsProfile The settings profile to print debug information for.
*
* @details
* - Logs the section name of the `SettingsProfile`.
* - Iterates through each setting in the `SettingsProfile`.
* - Depending on the type of each setting, converts the value to a string and logs the key, value, and type.
*
* @note Uses UE_LOG for logging the section name and settings details.
*/
static void PrintDebugSection(FSettingsProfile& SettingsProfile);
// Indicates whether automatic profile switching is enabled.
static bool _bAutomaticSwitch;
// Stores the last recorded battery percentage.
static int32 _LastBatteryPercentage;
// The rate at which to query HarmonyLinkLib for hardware info.
static int32 _TickRate;
// Timer handle for managing the periodic tick function.
static FTimerHandle _TickTimerHandle;
// The currently active profile.
static EProfile _ActiveProfile;
// Maps profile enums to their corresponding names.
static TMap<EProfile, FName> _ProfileNames;
// Stores the settings profiles.
static TMap<EProfile, FSettingsProfile> _Profiles;
static TSharedPtr<FConfigFile> _ConfigFile;
// The default settings for profiles.
static TMap<FName, TMap<FName, FHLConfigValue>> _DefaultSettings;
// Singleton instance of UHarmonyLinkGraphics.
static UHarmonyLinkGraphics* _INSTANCE;
};

View file

@ -0,0 +1,158 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "HLConfigValue.generated.h"
UENUM(BlueprintType)
enum class EConfigValueType : uint8
{
Int,
Float,
Bool,
String
};
/**
* Note: In Blueprints, all values will be visible, but only the value corresponding to the 'Type' will be used.
*/
USTRUCT(BlueprintType)
struct FHLConfigValue
{
GENERATED_BODY()
private:
// Allow Blueprint access to these private variables
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ConfigValue", meta = (AllowPrivateAccess = "true"))
EConfigValueType Type;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ConfigValue", meta = (AllowPrivateAccess = "true", EditCondition = "Type == EConfigValueType::Int", EditConditionHides))
int32 IntValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ConfigValue", meta = (AllowPrivateAccess = "true", EditCondition = "Type == EConfigValueType::Float", EditConditionHides))
float FloatValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ConfigValue", meta = (AllowPrivateAccess = "true", EditCondition = "Type == EConfigValueType::Bool", EditConditionHides))
bool BoolValue;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "ConfigValue", meta = (AllowPrivateAccess = "true", EditCondition = "Type == EConfigValueType::String", EditConditionHides))
FString StringValue;
public:
FHLConfigValue() : Type(EConfigValueType::String), IntValue(0), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(int32 Value) : Type(EConfigValueType::Int), IntValue(Value), FloatValue(0.0f), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(float Value) : Type(EConfigValueType::Float), IntValue(0), FloatValue(Value), BoolValue(false), StringValue(TEXT("")) {}
FHLConfigValue(bool Value) : Type(EConfigValueType::Bool), IntValue(0), FloatValue(0.0f), BoolValue(Value), StringValue(TEXT("")) {}
FHLConfigValue(const FString& Value) : Type(EConfigValueType::String), IntValue(0), FloatValue(0.0f), BoolValue(false), StringValue(Value) {}
FString ToString() const
{
switch (Type)
{
case EConfigValueType::Int:
return FString::Printf(TEXT("Int: %d"), IntValue);
case EConfigValueType::Float:
return FString::Printf(TEXT("Float: %f"), FloatValue);
case EConfigValueType::Bool:
return BoolValue ? TEXT("Bool: true") : TEXT("Bool: false");
case EConfigValueType::String:
return FString::Printf(TEXT("String: %s"), *StringValue);
default:
return TEXT("Unknown Type");
}
}
EConfigValueType GetType() const
{
return Type;
}
template <typename T>
T GetValue() const;
// Equality operators
bool operator==(const FHLConfigValue& Other) const
{
if (Type != Other.Type)
{
return false;
}
switch (Type)
{
case EConfigValueType::Int:
return IntValue == Other.IntValue;
case EConfigValueType::Float:
return FloatValue == Other.FloatValue;
case EConfigValueType::Bool:
return BoolValue == Other.BoolValue;
case EConfigValueType::String:
return StringValue == Other.StringValue;
default:
return false;
}
}
bool operator!=(const FHLConfigValue& Other) const
{
return !(*this == Other);
}
};
// Specializations of the templated getter
template<>
inline int32 FHLConfigValue::GetValue<int32>() const
{
ensure(Type == EConfigValueType::Int);
return IntValue;
}
template<>
inline float FHLConfigValue::GetValue<float>() const
{
ensure(Type == EConfigValueType::Float);
return FloatValue;
}
template<>
inline bool FHLConfigValue::GetValue<bool>() const
{
ensure(Type == EConfigValueType::Bool);
return BoolValue;
}
template<>
inline FString FHLConfigValue::GetValue<FString>() const
{
ensure(Type == EConfigValueType::String);
return StringValue;
}
// Hash function
FORCEINLINE uint32 GetTypeHash(const FHLConfigValue& Value)
{
uint32 Hash = GetTypeHash(Value.GetType());
switch (Value.GetType())
{
case EConfigValueType::Int:
Hash = HashCombine(Hash, GetTypeHash(Value.GetValue<int32>()));
break;
case EConfigValueType::Float:
Hash = HashCombine(Hash, GetTypeHash(Value.GetValue<float>()));
break;
case EConfigValueType::Bool:
Hash = HashCombine(Hash, GetTypeHash(Value.GetValue<bool>()));
break;
case EConfigValueType::String:
Hash = HashCombine(Hash, GetTypeHash(Value.GetValue<FString>()));
break;
}
return Hash;
}

View file

@ -0,0 +1,45 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "HLConfigValue.h"
#include "SettingsProfile.generated.h"
USTRUCT(BlueprintType)
struct FSettingsProfile
{
GENERATED_BODY()
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
FName SectionName;
UPROPERTY(EditAnywhere, BlueprintReadWrite, Category="Settings")
TMap<FName, FHLConfigValue> Settings;
// Equality operators
bool operator==(const FSettingsProfile& Other) const
{
return SectionName == Other.SectionName && Settings.OrderIndependentCompareEqual(Other.Settings);
}
bool operator!=(const FSettingsProfile& Other) const
{
return !(*this == Other);
}
};
// Hash function
FORCEINLINE uint32 GetTypeHash(const FSettingsProfile& Profile)
{
uint32 Hash = GetTypeHash(Profile.SectionName);
for (const auto& Pair : Profile.Settings)
{
Hash = HashCombine(Hash, GetTypeHash(Pair.Key));
Hash = HashCombine(Hash, GetTypeHash(Pair.Value));
}
return Hash;
}

View file

@ -1,14 +1,16 @@
// Copyright Epic Games, Inc. All Rights Reserved. // Copyright (C) 2024 Jordon Brooks
using UnrealBuildTool; using UnrealBuildTool;
using System.IO; using System.IO;
public class HarmonyLink : ModuleRules public class HarmonyLinkUE : ModuleRules
{ {
public HarmonyLink(ReadOnlyTargetRules Target) : base(Target) public HarmonyLinkUE(ReadOnlyTargetRules Target) : base(Target)
{ {
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
//IWYUSupport = IWYUSupport.Full;
PublicIncludePaths.AddRange( PublicIncludePaths.AddRange(
new string[] { new string[] {
// ... add public include paths required here ... // ... add public include paths required here ...
@ -19,7 +21,6 @@ public class HarmonyLink : ModuleRules
PrivateIncludePaths.AddRange( PrivateIncludePaths.AddRange(
new string[] { new string[] {
// ... add other private include paths required here ... // ... add other private include paths required here ...
"ThirdParty/HarmonyLinkLib/include"
} }
); );
@ -30,7 +31,8 @@ public class HarmonyLink : ModuleRules
"Core", "Core",
"CoreUObject", "CoreUObject",
"Engine", "Engine",
"Projects"
"HarmonyLinkLib",
// ... add other public dependencies that you statically link with here ... // ... add other public dependencies that you statically link with here ...
} }
); );
@ -43,17 +45,5 @@ public class HarmonyLink : ModuleRules
} }
); );
DynamicallyLoadedModuleNames.AddRange(
new string[]
{
// ... add any modules that your module loads dynamically here ...
}
);
PublicAdditionalLibraries.Add(Path.Combine(PluginDirectory, "Source/ThirdParty/HarmonyLinkLib/bin/Win64/HarmonyLinkLib.lib"));
RuntimeDependencies.Add("$(BinaryOutputDir)/HarmonyLinkLib.dll", Path.Combine(PluginDirectory, "Source/ThirdParty/HarmonyLinkLib/bin/Win64/HarmonyLinkLib.dll"));
} }
} }

View file

@ -0,0 +1,107 @@
// Copyright (C) 2024 Jordon Brooks
#include "HarmonyLinkLibrary.h"
#include "HarmonyLinkUE.h"
#include "HarmonyLinkLib.h"
bool UHarmonyLinkLibrary::bIsWineCached = false;
bool UHarmonyLinkLibrary::bIsLinuxCached = false;
bool UHarmonyLinkLibrary::bIsSteamDeckCached = false;
bool UHarmonyLinkLibrary::bCPUInfoCached = false;
bool UHarmonyLinkLibrary::bDeviceInfoCached = false;
bool UHarmonyLinkLibrary::bOSInfoCached = false;
bool UHarmonyLinkLibrary::bIsWine = false;
bool UHarmonyLinkLibrary::bIsLinux = false;
bool UHarmonyLinkLibrary::bIsSteamDeck = false;
FCPUInfo UHarmonyLinkLibrary::CachedCPUInfo = FCPUInfo();
FDevice UHarmonyLinkLibrary::CachedDeviceInfo = FDevice();
FOSVerInfo UHarmonyLinkLibrary::CachedOSInfo = FOSVerInfo();
bool UHarmonyLinkLibrary::bIsInitialised = false;
UHarmonyLinkLibrary::UHarmonyLinkLibrary()
{
bIsInitialised = HarmonyLinkLib::HL_Init();
if (!bIsInitialised)
{
UE_LOG(LogHarmonyLink, Fatal, TEXT("Failed to initialise HarmonyLinkLib!"));
return;
}
UE_LOG(LogHarmonyLink, Log, TEXT("HarmonyLinkLib Initialised!"));
}
bool UHarmonyLinkLibrary::IsInitialised()
{
return bIsInitialised;
}
bool UHarmonyLinkLibrary::IsWine(bool bForce)
{
if (!bIsWineCached || bForce)
{
bIsWine = HarmonyLinkLib::get_is_wine();
bIsWineCached = true;
}
return bIsWine;
}
bool UHarmonyLinkLibrary::IsLinux(bool bForce)
{
if (!bIsLinuxCached || bForce)
{
bIsLinux = HarmonyLinkLib::get_is_linux();
bIsLinuxCached = true;
}
return bIsLinux;
}
bool UHarmonyLinkLibrary::IsSteamDeck(bool bForce)
{
if (!bIsSteamDeckCached || bForce)
{
bIsSteamDeck = GetDeviceInfo().Device == EDevice::STEAM_DECK;
bIsSteamDeckCached = true;
}
return bIsSteamDeck;
}
FCPUInfo UHarmonyLinkLibrary::GetCPUInfo(bool bForce)
{
if (!bCPUInfoCached || bForce)
{
CachedCPUInfo = FCPUInfo(HarmonyLinkLib::get_cpu_info());
bCPUInfoCached = true;
}
return CachedCPUInfo;
}
FDevice UHarmonyLinkLibrary::GetDeviceInfo(bool bForce)
{
if (!bDeviceInfoCached || bForce)
{
CachedDeviceInfo = FDevice(HarmonyLinkLib::get_device_info());
bDeviceInfoCached = true;
}
return CachedDeviceInfo;
}
FOSVerInfo UHarmonyLinkLibrary::GetOSInfo(bool bForce)
{
if (!bOSInfoCached || bForce)
{
CachedOSInfo = FOSVerInfo(HarmonyLinkLib::get_os_version());
bOSInfoCached = true;
}
return CachedOSInfo;
}
FBattery UHarmonyLinkLibrary::GetBatteryStatus()
{
return FBattery(HarmonyLinkLib::get_battery_status());
}

View file

@ -0,0 +1,22 @@
// Copyright (C) 2024 Jordon Brooks
#include "HarmonyLinkUE.h"
#include "Modules/ModuleManager.h"
#define LOCTEXT_NAMESPACE "FHarmonyLinkUEModule"
DEFINE_LOG_CATEGORY(LogHarmonyLink);
void FHarmonyLinkUEModule::StartupModule()
{
}
void FHarmonyLinkUEModule::ShutdownModule()
{
}
#undef LOCTEXT_NAMESPACE
IMPLEMENT_MODULE(FHarmonyLinkUEModule, HarmonyLinkUE)

View file

@ -1,4 +1,4 @@
// Copyright (C) 2023 Jordon Brooks // Copyright (C) 2024 Jordon Brooks
#include "Structs/Battery.h" #include "Structs/Battery.h"

View file

@ -1,4 +1,4 @@
// Copyright (C) 2023 Jordon Brooks // Copyright (C) 2024 Jordon Brooks
#include "Structs/CPUInfo.h" #include "Structs/CPUInfo.h"
@ -11,11 +11,6 @@ FCPUInfo::FCPUInfo(HarmonyLinkLib::FCPUInfo* cpu_info)
PhysicalCores = cpu_info->Physical_Cores; PhysicalCores = cpu_info->Physical_Cores;
LogicalCores = cpu_info->Logical_Cores; LogicalCores = cpu_info->Logical_Cores;
for (const HarmonyLinkLib::FString& Flag : cpu_info->Flags)
{
Flags.Add(Flag.c_str());
}
cpu_info->free(); cpu_info->free();
} }
else else

View file

@ -1,4 +1,6 @@
#include "Structs/Device.h" // Copyright (C) 2024 Jordon Brooks
#include "Structs/Device.h"
#include <Structs/FDevice.h> #include <Structs/FDevice.h>
@ -17,20 +19,20 @@ FDevice::FDevice(HarmonyLinkLib::FDevice* oldDevice)
} }
} }
EDeviceEnum FDevice::Convert(HarmonyLinkLib::EDevice Device) EDevice FDevice::Convert(HarmonyLinkLib::EDevice Device)
{ {
switch (Device) switch (Device)
{ {
case HarmonyLinkLib::EDevice::DESKTOP: case HarmonyLinkLib::EDevice::DESKTOP:
return EDeviceEnum::DESKTOP; return EDevice::DESKTOP;
case HarmonyLinkLib::EDevice::LAPTOP: case HarmonyLinkLib::EDevice::LAPTOP:
return EDeviceEnum::LAPTOP; return EDevice::LAPTOP;
case HarmonyLinkLib::EDevice::HANDHELD: case HarmonyLinkLib::EDevice::HANDHELD:
return EDeviceEnum::HANDHELD; return EDevice::HANDHELD;
case HarmonyLinkLib::EDevice::STEAM_DECK: case HarmonyLinkLib::EDevice::STEAM_DECK:
return EDeviceEnum::STEAM_DECK; return EDevice::STEAM_DECK;
default: default:
return EDeviceEnum::DESKTOP; return EDevice::DESKTOP;
} }
} }

View file

@ -1,4 +1,6 @@
#include "Structs/OSVerInfo.h" // Copyright (C) 2024 Jordon Brooks
#include "Structs/OSVerInfo.h"
FOSVerInfo::FOSVerInfo(HarmonyLinkLib::FOSVerInfo* oldInfo) FOSVerInfo::FOSVerInfo(HarmonyLinkLib::FOSVerInfo* oldInfo)
{ {

View file

@ -1,13 +1,15 @@
#pragma once // Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "DeviceEnum.generated.h" #include "DeviceEnum.generated.h"
/** /*
* * Enum representing different operating system platforms.
*/ */
UENUM(BlueprintType) UENUM(BlueprintType)
enum class EDeviceEnum : uint8 enum class EDevice : uint8
{ {
DESKTOP UMETA(DisplayName = "Desktop"), DESKTOP UMETA(DisplayName = "Desktop"),
LAPTOP UMETA(DisplayName = "Laptop"), LAPTOP UMETA(DisplayName = "Laptop"),

View file

@ -1,10 +1,15 @@
#pragma once // Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h" #include "CoreMinimal.h"
#include "Platform.generated.h" #include "Platform.generated.h"
/** // Undefine the LINUX macro to avoid conflicts with the enum definition.
* #undef LINUX
/*
* Enum representing different operating system platforms.
*/ */
UENUM(BlueprintType) UENUM(BlueprintType)
enum class EPlatform : uint8 enum class EPlatform : uint8

View file

@ -0,0 +1,75 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Structs/Battery.h"
#include "Structs/CPUInfo.h"
#include "Structs/Device.h"
#include "Structs/OSVerInfo.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "HarmonyLinkLibrary.generated.h"
/**
* Library of static functions for accessing various system information, particularly for the HarmonyLink project.
*/
UCLASS()
class HARMONYLINKUE_API UHarmonyLinkLibrary : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
UHarmonyLinkLibrary();
// IsInitialised
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static bool IsInitialised();
// Checks if the game is running under Wine.
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static bool IsWine(bool bForce = false);
// Checks if the operating system is Linux.
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static bool IsLinux(bool bForce = false);
// Checks if the game is running on a Steam Deck.
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static bool IsSteamDeck(bool bForce = false);
// Retrieves information about the CPU of the current device.
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static FCPUInfo GetCPUInfo(bool bForce = false);
// Retrieves information about the current device.
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static FDevice GetDeviceInfo(bool bForce = false);
// Retrieves information about the operating system of the current device.
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static FOSVerInfo GetOSInfo(bool bForce = false);
// Retrieves the current battery status of the device.
UFUNCTION(BlueprintCallable, BlueprintPure, Category="HarmonyLink")
static FBattery GetBatteryStatus();
private:
static bool bIsWineCached;
static bool bIsWine;
static bool bIsLinuxCached;
static bool bIsLinux;
static bool bIsSteamDeckCached;
static bool bIsSteamDeck;
static bool bCPUInfoCached;
static bool bDeviceInfoCached;
static bool bOSInfoCached;
static FCPUInfo CachedCPUInfo;
static FDevice CachedDeviceInfo;
static FOSVerInfo CachedOSInfo;
static bool bIsInitialised;
};

View file

@ -1,10 +1,12 @@
// Copyright Epic Games, Inc. All Rights Reserved. // Copyright (C) 2024 Jordon Brooks
#pragma once #pragma once
#include "Modules/ModuleManager.h" #include "Modules/ModuleManager.h"
class FHarmonyLinkModule : public IModuleInterface DECLARE_LOG_CATEGORY_EXTERN(LogHarmonyLink, All, All);
class FHarmonyLinkUEModule : public IModuleInterface
{ {
public: public:

View file

@ -0,0 +1,35 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Structs/FBattery.h"
#include "Battery.generated.h"
/*
* Represents the battery status and information of a device.
*/
USTRUCT(BlueprintType)
struct FBattery
{
GENERATED_BODY()
FBattery() {}
// Indicates whether the device has a battery.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
bool HasBattery = false;
// Indicates whether the device is connected to AC power.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
bool IsACConnected = false;
// The current battery percentage of the device.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
int32 BatteryPercent = 0;
// Constructor that initializes the struct with information from an external battery source.
// @param battery Pointer to an external FBattery structure to copy data from.
FBattery(HarmonyLinkLib::FBattery* battery);
};

View file

@ -0,0 +1,40 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Structs/FCPUInfo.h"
#include "CPUInfo.generated.h"
/*
* Represents information about the CPU of a device.
*/
USTRUCT(BlueprintType)
struct FCPUInfo
{
GENERATED_BODY()
FCPUInfo() {}
// The vendor identifier for the CPU.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString VendorID;
// The model name of the CPU.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString ModelName;
// The number of physical cores in the CPU.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
int32 PhysicalCores = 0;
// The number of logical cores in the CPU (may be different from physical cores).
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
int32 LogicalCores = 0;
// Constructor that initializes the struct with information from an external CPU info source.
// @param cpu_info Pointer to an external FCPUInfo structure to copy data from.
FCPUInfo(HarmonyLinkLib::FCPUInfo* cpu_info);
};

View file

@ -0,0 +1,45 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Enums/DeviceEnum.h"
#include "Enums/Platform.h"
#include "Structs/FDevice.h"
#include "Device.generated.h"
// Represents information about a specific device.
USTRUCT(BlueprintType)
struct FDevice
{
GENERATED_BODY()
FDevice() {}
// The platform on which the device operates. Note: This can differ from the build platform.
// For example, if the device is identified as running on Wine, this will show Linux,
// regardless of the build being an executable for Windows.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
EPlatform Platform = EPlatform::WINDOWS;
// The type of the device.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
EDevice Device = EDevice::DESKTOP;
// Constructor that initializes the struct with information from an external source.
// @param oldDevice Pointer to an external FDevice structure to copy data from.
FDevice(HarmonyLinkLib::FDevice* oldDevice);
private:
// Converts an external device enum to the internal EDeviceEnum type.
// @param Device External device enum to convert.
// @returns Converted EDeviceEnum value.
static EDevice Convert(HarmonyLinkLib::EDevice Device);
// Converts an external platform enum to the internal EPlatform type.
// @param Platform External platform enum to convert.
// @returns Converted EPlatform value.
static EPlatform Convert(HarmonyLinkLib::EPlatform Platform);
};

View file

@ -0,0 +1,49 @@
// Copyright (C) 2024 Jordon Brooks
#pragma once
#include "CoreMinimal.h"
#include "Structs/FOSVerInfo.h"
#include "OSVerInfo.generated.h"
// Represents information about an operating system version.
USTRUCT(BlueprintType)
struct FOSVerInfo
{
GENERATED_BODY()
FOSVerInfo() {}
// The name of the operating system.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString Name;
// Numerical version of the operating system.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
int32 Version = 0;
// Unique identifier for the operating system.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString ID;
// Identifier for the specific version of the operating system.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString VersionID;
// Codename for the operating system version.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString VersionCodename;
// User-friendly name for the operating system version.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString PrettyName;
// Variant identifier of the operating system.
UPROPERTY(BlueprintReadWrite, EditAnywhere, Category="HarmonyLink")
FString VariantID;
// Constructor that initializes the struct with information from an external source.
// @param oldInfo Pointer to an external FOSVerInfo structure to copy data from.
FOSVerInfo(HarmonyLinkLib::FOSVerInfo* oldInfo);
};

View file

@ -0,0 +1,72 @@
// Copyright (C) 2024 Jordon Brooks
using UnrealBuildTool;
using System.IO;
using Internal;
public class HarmonyLinkLib : ModuleRules
{
public HarmonyLinkLib(ReadOnlyTargetRules Target) : base(Target)
{
//Console.WriteLine("Building HarmonyLinkLib");
PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs;
Type = ModuleType.External;
// Add the standard library
//bUseRTTI = true;
//bEnableExceptions = true;
// Optionally, if you need C++17 features
//CppStandard = CppStandardVersion.Cpp17;
string includePath = Path.Combine(ModuleDirectory, "include");
//Console.WriteLine("Include Path: " + includePath);
PublicIncludePaths.Add(includePath);
string platformString = Target.Platform.ToString();
if (Target.Platform == UnrealTargetPlatform.Win64)
{
PublicDefinitions.Add("HARMONYLINKLIB_STATIC=1");
PublicDefinitions.Add("BUILD_WINDOWS=1");
string libpath = Path.Combine(ModuleDirectory, "lib", platformString, "HarmonyLinkLibStatic.lib");
PublicAdditionalLibraries.Add(libpath);
//string dllPath = Path.Combine(ModuleDirectory, "bin", platformString, "HarmonyLinkLibShared.dll");
//string dllTargetPath = "$(TargetOutputDir)/HarmonyLinkLibShared.dll";
//Console.WriteLine("DLL Path: " + dllPath);
//RuntimeDependencies.Add(dllTargetPath, dllPath);
//PublicDelayLoadDLLs.Add("HarmonyLinkLibShared.dll");
}
else if (Target.Platform == UnrealTargetPlatform.Linux)
{
PublicDefinitions.Add("HARMONYLINKLIB_STATIC=1");
//Console.WriteLine("Building Linux");
PublicDefinitions.Add("BUILD_LINUX=1");
string dllPath = Path.Combine(ModuleDirectory, "lib", platformString, "libHarmonyLinkLibStatic_clang++.a");
//string dllTargetPath = "$(TargetOutputDir)/libHarmonyLinkLibShared.so";
//Console.WriteLine("Library Path: " + libPath);
PublicAdditionalLibraries.Add(dllPath);
// Ensure the proper linking of standard C++ libraries
//PublicSystemLibraries.Add("stdc++");
//PublicSystemLibraries.Add("c++abi");
//PublicSystemLibraries.Add("m"); // Math library
//PublicSystemLibraries.Add("pthread"); // POSIX threads library
// Add the C++ standard library explicitly if needed
PublicSystemLibraries.Add("c++");
// Ensure linking with libc and other necessary libraries
//PublicSystemLibraries.Add("dl"); // Dynamic linking loader
//PublicSystemLibraries.Add("rt"); // Real-time library
// Add any other libraries that might be needed
//PublicSystemLibraries.Add("gcc_s"); // GCC support library
//PublicSystemLibraries.Add("gcc"); // GCC compiler support library
}
}
}

BIN
Source/ThirdParty/HarmonyLinkLib/bin/Linux/libHarmonyLinkLibShared.so (Stored with Git LFS) vendored Normal file

Binary file not shown.

BIN
Source/ThirdParty/HarmonyLinkLib/bin/Win64/HarmonyLinkLibShared.dll (Stored with Git LFS) vendored Normal file

Binary file not shown.

View file

@ -1,14 +1,38 @@
// Copyright (C) 2023 Jordon Brooks // Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once
// Use a preprocessor definition to switch between export and import declarations // Use a preprocessor definition to switch between export and import declarations
#ifdef _WIN32 #ifdef BUILD_WINDOWS
#ifdef HARMONYLINKLIB_EXPORTS #ifdef HARMONYLINKLIB_STATIC
#define HARMONYLINKLIB_API
#else
#ifdef HARMONYLINKLIB_SHARED
#define HARMONYLINKLIB_API __declspec(dllexport) #define HARMONYLINKLIB_API __declspec(dllexport)
#else #else
#define HARMONYLINKLIB_API __declspec(dllimport) #define HARMONYLINKLIB_API __declspec(dllimport)
#endif #endif
#endif
#else #else
#ifdef HARMONYLINKLIB_SHARED
#ifdef __clang__
#define HARMONYLINKLIB_API __attribute__((visibility("default")))
#else
#define HARMONYLINKLIB_API #define HARMONYLINKLIB_API
#endif
#else
#define HARMONYLINKLIB_API
#endif
#endif #endif

View file

@ -1,5 +1,22 @@
// Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once
// Undefine the LINUX macro to avoid conflicts with the enum definition.
#undef LINUX
#include <cstdint> #include <cstdint>
// Enum class for representing different types of devices // Enum class for representing different types of devices
@ -7,6 +24,7 @@ namespace HarmonyLinkLib
{ {
enum class EDevice : uint8_t enum class EDevice : uint8_t
{ {
UNKNOWN,
DESKTOP, DESKTOP,
LAPTOP, LAPTOP,
HANDHELD, HANDHELD,

View file

@ -1,3 +1,17 @@
// Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once
#include <cstdint> #include <cstdint>
@ -7,6 +21,7 @@ namespace HarmonyLinkLib
{ {
enum class EPlatform : uint8_t enum class EPlatform : uint8_t
{ {
UNKNOWN,
WINDOWS, WINDOWS,
LINUX, LINUX,
MAC, MAC,

View file

@ -0,0 +1,29 @@
// Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cstdint>
// Enum class for representing different types of devices
namespace HarmonyLinkLib
{
enum class ESteamDeck : uint8_t
{
NONE, // Device is not a steam deck
UNKNOWN, // Device is a steam deck but model cannot be determined
LCD,
OLED,
};
}

View file

@ -1,4 +1,16 @@
// Copyright (C) 2024 Jordon Brooks // Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once

View file

@ -1,4 +1,35 @@
// Copyright (C) 2023 Jordon Brooks // Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
/**
* IMPORTANT REMINDER:
* Do NOT use standard output functions like std::cout and printf anywhere in this codebase.
*
* Reason:
* Unreal Engine 5's packaging tool encounters issues with these functions, leading to
* packaging failures. The engine sets stdout to UTF-8, which can cause conflicts with
* these standard functions, resulting in a "SECURE CRT: Invalid parameter detected" error
* during packaging.
*
* This issue once required an extensive debugging effort that lasted over 8 hours.
* To prevent similar issues in the future and ensure smooth packaging, always use
* wide-character versions of these functions, such as wprintf and std::wcout, when working
* within the DLL. These functions are compatible with the UTF-8 setting in Unreal Engine 5.
*
*/
#pragma once #pragma once
@ -13,13 +44,19 @@ class IPlatformUtilities;
namespace HarmonyLinkLib namespace HarmonyLinkLib
{ {
extern "C" HARMONYLINKLIB_API bool get_is_wine(); HARMONYLINKLIB_API bool HL_Init();
extern "C" HARMONYLINKLIB_API FCPUInfo* get_cpu_info(); HARMONYLINKLIB_API bool get_is_wine();
extern "C" HARMONYLINKLIB_API FDevice* get_device_info(); HARMONYLINKLIB_API bool get_is_linux();
extern "C" HARMONYLINKLIB_API FOSVerInfo* get_os_version(); HARMONYLINKLIB_API bool get_is_docked();
extern "C" HARMONYLINKLIB_API FBattery* get_battery_status(); HARMONYLINKLIB_API FCPUInfo* get_cpu_info();
HARMONYLINKLIB_API FDevice* get_device_info();
HARMONYLINKLIB_API FOSVerInfo* get_os_version();
HARMONYLINKLIB_API FBattery* get_battery_status();
} }

View file

@ -1,3 +1,17 @@
// Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once
struct HarmonyLinkStruct struct HarmonyLinkStruct

View file

@ -1,4 +1,18 @@
#pragma once // Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include <cstdint> #include <cstdint>
#include <iostream> #include <iostream>

View file

@ -1,3 +1,17 @@
// Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once
#include <cstdint> #include <cstdint>

View file

@ -1,16 +1,32 @@
// Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once
#include <HarmonyLinkStruct.h> #include <HarmonyLinkStruct.h>
#include "Enums/EDevice.h" #include "Enums/EDevice.h"
#include "Enums/EPlatform.h" #include "Enums/EPlatform.h"
#include "Enums/ESteamDeck.h"
namespace HarmonyLinkLib namespace HarmonyLinkLib
{ {
// Struct to represent a specific device with both platform and device type // Struct to represent a specific device with both platform and device type
struct FDevice : HarmonyLinkStruct struct FDevice : HarmonyLinkStruct
{ {
EPlatform platform; EPlatform platform = EPlatform::UNKNOWN;
EDevice device; EDevice device = EDevice::UNKNOWN;
ESteamDeck steam_deck_model = ESteamDeck::NONE;
}; };
} }

View file

@ -1,4 +1,18 @@
#pragma once // Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once
#include "FString.h" #include "FString.h"
#include "HarmonyLinkStruct.h" #include "HarmonyLinkStruct.h"

View file

@ -1,4 +1,17 @@
// Copyright (C) 2024 Jordon Brooks // Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#pragma once #pragma once
#include "Core.h" #include "Core.h"

View file

@ -1,3 +1,17 @@
// Copyright (c) 2024 Jordon Brooks
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// Version.h.in // Version.h.in
#pragma once #pragma once

BIN
Source/ThirdParty/HarmonyLinkLib/lib/Linux/libHarmonyLinkLibStatic.a (Stored with Git LFS) vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
Source/ThirdParty/HarmonyLinkLib/lib/Win64/HarmonyLinkLibStatic.lib (Stored with Git LFS) vendored Normal file

Binary file not shown.