Rearrange FormatDataLibrary to have less module coupling.

This commit is contained in:
2026-02-25 16:49:37 -05:00
parent 199a6bb813
commit 5c69883222
10 changed files with 134 additions and 60 deletions

View File

@@ -797,3 +797,12 @@ void UlxAnimationStepLibrary::AnimationStepApplySkeletalMesh(const FlxAnimationS
}
}
FFormatArgumentData UlxAnimationStepLibrary::FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(AnimationStepDebugString(AutoConvertedValue));
return Result;
}

View File

@@ -149,6 +149,11 @@ public:
UFUNCTION(BlueprintCallable, Category = "Luprex|Animation Step")
static void AnimationStepApplySkeletalMesh(const FlxAnimationStep& step, bool FallbackToBP,
USkeletalMeshComponent* MeshComp, USkeletalMesh* Fallback = nullptr);
// Allows you to pass an animation step to Format Message.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Animation Step")
static FFormatArgumentData FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name);
};
////////////////////////////////////////////////////////////

View File

@@ -1,9 +1,51 @@
#include "FormatDataLibrary.h"
#include "LuaCall.h"
#include "AnimQueue.h"
#include "MovementComponentState.h"
#include "Common.h"
#include "Kismet/KismetTextLibrary.h"
#include "UObject/UObjectIterator.h"
void UlxFormatDataLibrary::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
ScanForConverters();
}
void UlxFormatDataLibrary::ScanForConverters()
{
Converters.Empty();
for (TObjectIterator<UClass> It; It; ++It)
{
UClass* Class = *It;
for (TFieldIterator<UFunction> FuncIt(Class, EFieldIteratorFlags::ExcludeSuper); FuncIt; ++FuncIt)
{
UFunction* Function = *FuncIt;
// Must have an AutoConvertedValue parameter.
if (Function->FindPropertyByName(TEXT("AutoConvertedValue")) == nullptr) continue;
// Must have a Name parameter that is a string.
FProperty* NameProp = Function->FindPropertyByName(TEXT("Name"));
if (NameProp == nullptr) continue;
if (CastField<FStrProperty>(NameProp) == nullptr) continue;
// Must return FFormatArgumentData.
FStructProperty* ReturnProp = CastField<FStructProperty>(Function->GetReturnProperty());
if (ReturnProp == nullptr) continue;
if (ReturnProp->Struct->GetFName() != TEXT("FormatArgumentData")) continue;
// Must have exactly three properties: AutoConvertedValue, Name, and ReturnValue.
int PropCount = 0;
for (TFieldIterator<FProperty> PropIt(Function); PropIt; ++PropIt) PropCount++;
if (PropCount != 3) continue;
Converters.Add(Function);
}
}
for (UFunction* Func : Converters)
{
UE_LOG(LogLuprexIntegration, Display, TEXT("FormatData converter: %s::%s"), *Func->GetOuterUClass()->GetName(), *Func->GetName());
}
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name)
{
@@ -149,33 +191,6 @@ FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataTransform(const FTra
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(AutoConvertedValue->DebugString());
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(UlxAnimationStepLibrary::AnimationStepDebugString(AutoConvertedValue));
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(UlxMovementComponentStateLibrary::DebugString(AutoConvertedValue));
return Result;
}
FFormatArgumentData UlxFormatDataLibrary::FormatArgumentDataBlank(const FString &Name)
{
FFormatArgumentData Result;

View File

@@ -2,9 +2,26 @@
//
// FormatDataLibrary.h
//
// Functions that convert data into
// FFormatArgumentData structs, so that the data
// can be passed to FText::Format.
// The 'Format Message' and 'Format Log Message' nodes can format
// ints, strings, vectors, and more. To support a new data type,
// all you need to do is implement a blueprint-callable conversion
// function like this one:
//
// UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"))
// static FFormatArgumentData FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name);
//
// This conversion function must have three things:
//
// * It returns FFormatArgumentData
// * It accepts 'AutoConvertedValue', the value you want to format,
// * It accepts 'Name', which must be copied into the FFormatArgumentData.
//
// The parameter names are required: you must use exactly these parameter
// names if you want this to act as a conversion function. You can put a
// conversion function like that anywhere in the system, in any UCLASS.
// This module will find and use it.
//
// This file also contains the built-in converters for standard types.
//
////////////////////////////////////////////////////////////
@@ -15,31 +32,40 @@
#include "HAL/Platform.h"
#include "UObject/ObjectMacros.h"
#include "UObject/UObjectGlobals.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "EditorSubsystem.h"
#include "FormatDataLibrary.generated.h"
class UlxLuaValues;
struct FlxAnimationStep;
struct FlxMovementComponentState;
////////////////////////////////////////////////////////////
//
// UlxFormatDataLibrary
//
// The FormatLogMessage K2Node scans this library using
// reflection, looking for functions that have a parameter
// named "AutoConvertedValue". It uses the type of that
// parameter to determine which function to call for a given
// pin type.
//
////////////////////////////////////////////////////////////
UCLASS(MinimalAPI)
class UlxFormatDataLibrary : public UBlueprintFunctionLibrary
class UlxFormatDataLibrary : public UEditorSubsystem
{
GENERATED_BODY()
public:
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
// Get all converter functions (functions with an
// "AutoConvertedValue" parameter) found across all
// loaded classes.
//
const TArray<UFunction*>& GetConverters() const { return Converters; }
private:
// Scan all loaded classes for converter functions.
//
void ScanForConverters();
// Cached list of converter functions.
//
TArray<UFunction*> Converters;
public:
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataBool(bool AutoConvertedValue, const FString &Name);
@@ -89,15 +115,6 @@ public:
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataTransform(const FTransform &AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataAnimationStep(const FlxAnimationStep &AutoConvertedValue, const FString &Name);
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")
static FFormatArgumentData FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name);
// For pins that were never connected.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Utility")

View File

@@ -2,6 +2,7 @@
#include "FormatMessage.h"
#include "Editor.h"
#include "Internationalization/TextFormatter.h"
#include "BlueprintActionDatabaseRegistrar.h"
@@ -305,11 +306,11 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataBlank));
}
// Try to find a match in the UlxFormatDataLibrary.
// Scan the cached converter list for a matching type.
//
for (auto It = TFieldIterator<UFunction>(UlxFormatDataLibrary::StaticClass()); It; ++It)
UlxFormatDataLibrary* FormatDataLib = GEditor->GetEditorSubsystem<UlxFormatDataLibrary>();
for (UFunction* Function : FormatDataLib->GetConverters())
{
UFunction* Function = *It;
FProperty* ValueProperty = Function->FindPropertyByName(TEXT("AutoConvertedValue"));
FEdGraphPinType ValuePinType;
bool Convertible = Schema->ConvertPropertyToPinType(ValueProperty, ValuePinType);
@@ -317,9 +318,9 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
if (!Schema->ArePinTypesEquivalent(PinType, ValuePinType)) continue;
return Function;
}
// A general handler for Enums. You can override this for specific enums by
// putting that particular enum into the UlxFormatDataLibrary.
// putting that particular enum into any class with an AutoConvertedValue function.
//
if ((PinType.PinCategory == UEdGraphSchema_K2::PC_Byte) && (nullptr != Cast<const UEnum>(PinType.PinSubCategoryObject)))
{
@@ -332,7 +333,7 @@ UFunction *ToFormatArgumentData(const UEdGraphSchema_K2 *Schema, const FEdGraphP
{
return UlxFormatDataLibrary::StaticClass()->FindFunctionByName(GET_MEMBER_NAME_CHECKED(UlxFormatDataLibrary, FormatArgumentDataObject));
}
// We don't have a match.
//
return nullptr;

View File

@@ -31,6 +31,7 @@ public class Integration : ModuleRules
"BlueprintGraph",
"UMG",
"UMGEditor",
"EditorSubsystem",
});
// Uncomment if you are using Slate UI

View File

@@ -543,5 +543,12 @@ void UlxLuaValues::ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result, bool
Result = FlxStreamBuffer(Data[Cursor++]).read_bool();
}
FFormatArgumentData UlxLuaValues::FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(AutoConvertedValue->DebugString());
return Result;
}

View File

@@ -277,4 +277,9 @@ public:
UFUNCTION(BlueprintCallable, meta = (ExpandEnumAsExecs = "Status"), Category = "Luprex|Lua Value Array")
void ReadBoolean(ElxSuccessOrWrongType &Status, bool &Result, bool LogErrorOnWrongType = false);
// Allows you to pass a LuaValues to Format Message.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Lua Value Array")
static FFormatArgumentData FormatArgumentDataLuaValues(const UlxLuaValues *AutoConvertedValue, const FString &Name);
};

View File

@@ -62,3 +62,12 @@ FlxMovementComponentState UlxMovementComponentStateLibrary::SetFakeMovementCompo
}
return State;
}
FFormatArgumentData UlxMovementComponentStateLibrary::FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name)
{
FFormatArgumentData Result;
Result.ArgumentValueType = EFormatArgumentType::Text;
Result.ArgumentName = Name;
Result.ArgumentValue = FText::FromString(DebugString(AutoConvertedValue));
return Result;
}

View File

@@ -126,4 +126,9 @@ public:
//
UFUNCTION(BlueprintCallable, Category = "Luprex|Movement Component State", meta = (DefaultToSelf = "Actor", AutoCreateRefTerm = "State"))
static FlxMovementComponentState SetFakeMovementComponentState(AActor *Actor, const FlxMovementComponentState &State);
// Allows you to pass a MovementComponentState to Format Message.
//
UFUNCTION(BlueprintPure, meta = (BlueprintInternalUseOnly = "true"), Category = "Luprex|Movement Component State")
static FFormatArgumentData FormatArgumentDataMovementComponentState(const FlxMovementComponentState &AutoConvertedValue, const FString &Name);
};