Hello, I try get batterey level of controllers in (UE5.4.4) + Meta Fork, but BatteryPercentRemaining[2] always return 0
#pragma once
#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "VRBatteryUtils.generated.h"
UCLASS()
class VRWEAPONS_API UVRBatteryUtils : public UBlueprintFunctionLibrary
{
GENERATED_BODY()
public:
// BlueprintPure убирает пины выполнения. FString& добавляет выходной пин с текстом.
UFUNCTION(BlueprintPure, Category = "VR|Hardware")
static int32 GetVRControllerBattery(bool bIsLeftController, FString& OutDebugMessage);
};
#include "VRBatteryUtils.h"
#include "UObject/UObjectGlobals.h"
#include "UObject/Class.h"
#include "UObject/UnrealType.h"
#if PLATFORM_WINDOWS
#include "Windows/AllowWindowsPlatformTypes.h"
#include <windows.h>
#include "Windows/HideWindowsPlatformTypes.h"
#endif
#if PLATFORM_ANDROID
#include <dlfcn.h>
#endif
struct ovrpVector2f { float x, y; };
struct ovrpControllerState4 {
unsigned int ConnectedControllerTypes;
unsigned int Buttons;
unsigned int Touches;
unsigned int NearTouches;
float IndexTrigger[2];
float HandTrigger[2];
ovrpVector2f Thumbstick[2];
ovrpVector2f Touchpad[2];
unsigned char BatteryPercentRemaining[2];
unsigned char RecenterCount[2];
unsigned char Reserved[28];
};
typedef int32(*PFN_ovrpGetControllerState4)(unsigned int controllerMask, ovrpControllerState4* controllerState);
int32 UVRBatteryUtils::GetVRControllerBattery(bool bIsLeftController, FString& OutDebugMessage)
{
auto GetMetaSymbol = [](const char* SymbolName) -> void*
{
#if PLATFORM_WINDOWS
const wchar_t* DllNames[] = {
L"MetaXRPlugin.dll", L"OVRPlugin.dll",
L"UnrealEditor-MetaXR.dll", L"UnrealEditor-OculusXRPlugin.dll", nullptr
};
for (int i = 0; DllNames[i] != nullptr; ++i)
{
if (HMODULE hMod = GetModuleHandleW(DllNames[i]))
{
if (void* Proc = (void*)GetProcAddress(hMod, SymbolName)) return Proc;
}
}
return (void*)GetProcAddress(GetModuleHandleW(NULL), SymbolName);
#elif PLATFORM_ANDROID
const char* SoNames[] = {
"libMetaXRPlugin.so", "libOVRPlugin.so", "libopenxr_loader.so", nullptr
};
for (int i = 0; SoNames[i] != nullptr; ++i)
{
void* Handle = dlopen(SoNames[i], RTLD_LAZY | RTLD_NOLOAD);
if (!Handle) Handle = dlopen(SoNames[i], RTLD_LAZY);
if (Handle)
{
if (void* Symbol = dlsym(Handle, SymbolName)) return Symbol;
}
}
return dlsym(RTLD_DEFAULT, SymbolName);
#else
return nullptr;
#endif
};
PFN_ovrpGetControllerState4 FuncState4 = (PFN_ovrpGetControllerState4)GetMetaSymbol("ovrp_GetControllerState4");
unsigned int CombinedMask = 0x80000003;
if (FuncState4)
{
ovrpControllerState4 State;
FMemory::Memzero(&State, sizeof(ovrpControllerState4));
int32 Res = FuncState4(CombinedMask, &State);
if (Res >= 0)
{
int32 ResultIdx = bIsLeftController ? 0 : 1;
int32 BatteryLevel = (int32)State.BatteryPercentRemaining[ResultIdx];
// “еперь всегда возвращаем полученное значение
OutDebugMessage = FString::Printf(TEXT("Meta: %d%% (Mask: %d)"), BatteryLevel, State.ConnectedControllerTypes);
return BatteryLevel;
}
}
if (UClass* PicoClass = FindFirstObject<UClass>(TEXT("PICOXRInputFunctionLibrary")))
{
if (UFunction* PicoFunc = PicoClass->FindFunctionByName(TEXT("PXR_GetControllerPower")))
{
uint8* Parms = (uint8*)FMemory_Alloca(PicoFunc->ParmsSize);
FMemory::Memzero(Parms, PicoFunc->ParmsSize);
FProperty* InputProp = nullptr;
FProperty* ReturnProp = nullptr;
for (TFieldIterator<FProperty> It(PicoFunc); It; ++It)
{
if (It->HasAnyPropertyFlags(CPF_ReturnParm)) ReturnProp = *It;
else if (It->HasAnyPropertyFlags(CPF_Parm)) InputProp = *It;
}
if (InputProp && ReturnProp)
{
int32 TargetValue = bIsLeftController ? 0 : 1;
if (FNumericProperty* NumProp = CastField<FNumericProperty>(InputProp))
{
NumProp->SetIntPropertyValue(InputProp->ContainerPtrToValuePtr<void>(Parms), (int64)TargetValue);
}
PicoClass->GetDefaultObject()->ProcessEvent(PicoFunc, Parms);
if (FNumericProperty* RetNumProp = CastField<FNumericProperty>(ReturnProp))
{
int32 PicoPower = (int32)RetNumProp->GetSignedIntPropertyValue(ReturnProp->ContainerPtrToValuePtr<void>(Parms));
if (PicoPower >= 0)
{
int32 FinalPercent = PicoPower > 0 && PicoPower <= 5 ? PicoPower * 20 : PicoPower;
OutDebugMessage = FString::Printf(TEXT("PICO SDK: %d%%"), FinalPercent);
return FinalPercent;
}
}
}
}
}
OutDebugMessage = TEXT("State4 & PICO APIs failed");
return -1;
}