Skip to content

Instantly share code, notes, and snippets.

@iDevelopThings
Last active June 14, 2024 01:47
Show Gist options
  • Save iDevelopThings/e7fb05d78b14c12197f4ec4e26db65a5 to your computer and use it in GitHub Desktop.
Save iDevelopThings/e7fb05d78b14c12197f4ec4e26db65a5 to your computer and use it in GitHub Desktop.
#include "Reflection/FunctionCallUtil.h"
FFunctionCallParamInfo::FFunctionCallParamInfo() = default;
FFunctionCallParamInfo::FFunctionCallParamInfo(bool InIsReturnParam, const FString& InName, int InIndex, FProperty* InProperty):
IsReturnParam(InIsReturnParam),
Name(InName),
Index(InIndex),
Property(InProperty) {}
FFunctionCall::FFunctionCall() {
Init();
}
FFunctionCall::FFunctionCall(UObject* InObject, const FString& FunctionName):
Object(InObject),
Function(InObject->FindFunction(*FunctionName)) {
Init();
}
FFunctionCall::FFunctionCall(UObject* InObject, UFunction* InFunction):
Object(InObject),
Function(InFunction) {
Init();
}
FFunctionCall& FFunctionCall::PushParam(const FString& ParamName, const void* Value) {
// if (!ParamsBuffer) return *this;
if (!ParamMap.Contains(ParamName)) {
UE_GAMELOG(LogCommonGame, Error, "Param %s not found", *ParamName);
return *this;
}
SetParamValue(ParamMap[ParamName], Value);
return *this;
}
FFunctionCall& FFunctionCall::PushParam(const int Index, const void* Value) {
// if (!ParamsBuffer) return *this;
if (!ParamArray.IsValidIndex(Index)) {
UE_GAMELOG(LogCommonGame, Error, "Param index %d out of range", Index);
return *this;
}
SetParamValue(ParamArray[Index], Value);
return *this;
}
void FFunctionCall::Call() {
if (!Function) {
UE_GAMELOG(LogCommonGame, Error, "Function not found");
return;
}
Object->ProcessEvent(Function, ParamsBuffer.GetData());
}
void FFunctionCall::Init() {
if (!Function) {
UE_GAMELOG(LogCommonGame, Error, "Function not found");
return;
}
if (Function->ParmsSize > 0) {
ParamsBuffer.AddUninitialized(Function->ParmsSize);
Function->InitializeStruct(ParamsBuffer.GetData());
int Index = 0;
auto PropIterator = TFieldRange<FProperty>(
Function,
static_cast<EFieldIterationFlags>(EFieldIteratorFlags::ExcludeSuper | EFieldIteratorFlags::ExcludeDeprecated | EFieldIteratorFlags::ExcludeInterfaces)
);
for (FProperty* Property : PropIterator) {
if (!Property->HasAnyPropertyFlags(CPF_Parm)) {
continue;
}
FFunctionCallParamInfo ParamInfo;
ParamInfo.Name = Property->GetName();
ParamInfo.Index = Index;
ParamInfo.Property = Property;
if (Property->PropertyFlags & CPF_ReturnParm) {
ParamInfo.IsReturnParam = true;
ReturnParam = ParamInfo;
}
ParamMap.Add(ParamInfo.Name, ParamInfo);
ParamArray.Add(ParamInfo);
if (!Property->HasAnyPropertyFlags(CPF_ZeroConstructor)) {
Property->InitializeValue_InContainer(ParamsBuffer.GetData());
}
Index++;
}
}
}
bool FFunctionCall::SetParamValue(FFunctionCallParamInfo& Param, const void* Value) {
if (!Param.Property) {
return false;
}
Param.Property->SetValue_InContainer(ParamsBuffer.GetData(), Value);
return true;
}
void FFunctionCall::CopyValueToBuffer(FProperty* Param, const void* Value) {
}
bool operator==(const FFunctionCallParamInfo& Lhs, const FFunctionCallParamInfo& RHS) { return Lhs.Property == RHS.Property; }
bool operator!=(const FFunctionCallParamInfo& Lhs, const FFunctionCallParamInfo& RHS) { return !(Lhs == RHS); }
#pragma once
#include "CoreMinimal.h"
#include "CommonGame.h"
#include "LoggerMacros.h"
#include "Traits/IsVoidType.h"
#include "UObject/UnrealTypePrivate.h"
struct COMMONGAME_API FFunctionCallParamInfo
{
bool IsReturnParam = false;
FString Name = "";
int Index = -1;
FProperty* Property = nullptr;
FFunctionCallParamInfo();
FFunctionCallParamInfo(bool InIsReturnParam, const FString& InName, int InIndex, FProperty* InProperty);
friend bool operator==(const FFunctionCallParamInfo& Lhs, const FFunctionCallParamInfo& RHS);
friend bool operator!=(const FFunctionCallParamInfo& Lhs, const FFunctionCallParamInfo& RHS);
};
struct COMMONGAME_API FFunctionCall
{
private:
TArray<uint8> ParamsBuffer;
FFunctionCallParamInfo ReturnParam;
TMap<FString, FFunctionCallParamInfo> ParamMap;
TArray<FFunctionCallParamInfo> ParamArray;
public:
UObject* Object = nullptr;
UFunction* Function = nullptr;
FFunctionCall();
FFunctionCall(UObject* InObject, const FString& FunctionName);
FFunctionCall(UObject* InObject, UFunction* InFunction);
FFunctionCall& PushParam(const FString& ParamName, const void* Value);
FFunctionCall& PushParam(const int Index, const void* Value);
template <typename T>
FFunctionCall& PushParamIfExists(T* ParamValue) {
// If T is a UObject
if constexpr (TIsDerivedFrom<T, UObject>::IsDerived) {
UClass* TClass = T::StaticClass();
for (FFunctionCallParamInfo& ParamInfo : ParamArray) {
FProperty* Property = ParamInfo.Property;
if (FObjectPropertyBase* ObjProperty = CastField<FObjectPropertyBase>(Property)) {
if (ObjProperty->PropertyClass == TClass) {
SetParamValue(ParamInfo, ParamValue);
break;
}
}
}
} else if constexpr (TIsDerivedFrom<T, UScriptStruct>::IsDerived) {
UScriptStruct* TClass = T::StaticStruct();
for (FFunctionCallParamInfo& ParamInfo : ParamArray) {
FProperty* Property = ParamInfo.Property;
if (FStructProperty* StructProperty = CastField<FStructProperty>(Property)) {
if (StructProperty->Struct == TClass) {
SetParamValue(ParamInfo, ParamValue);
break;
}
}
}
}
return *this;
}
void Call();
template <typename T>
T Call() {
if (!Function) {
UE_GAMELOG(LogCommonGame, Error, "Function not found");
return T();
}
T ReturnValue;
FFrame Stack(Object, Function, ParamsBuffer.GetData(), nullptr, Function->ChildProperties);
Function->Invoke(Object, Stack, &ReturnValue);
return ReturnValue;
}
private:
void Init();
bool SetParamValue(FFunctionCallParamInfo& Param, const void* Value);
void CopyValueToBuffer(FProperty* Param, const void* Value);
};
UFunction* DrawPaneFn = ObjClass->FindFunctionByName(TEXT("DrawDebugPane"), EIncludeSuperFlag::IncludeSuper)
void UDebugPanesManager::CallPaneDrawFunc(UObject* Obj, UFunction* DrawPaneFn, UDebugPanesManager* Manager, UDebugPane* Pane, UCanvas* Canvas, APlayerController* PlayerController) {
FFunctionCall FnCall(Obj, DrawPaneFn);
FnCall.PushParam(0, &Manager);
FnCall.PushParam(1, &Pane);
FnCall.PushParam(2, &Canvas);
FnCall.PushParam(3, &PlayerController);
FnCall.Call();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment