You've already forked UnrealEngineUWP
mirror of
https://github.com/izzy2lost/UnrealEngineUWP.git
synced 2026-03-26 18:15:20 -07:00
* Ran IWYU on all cpp files. Re-running IWYU will cause a few breaking changes so will handle that in a separate change #preflight 63a1fa7c2540a78d27beadb5 #rb none [CL 23551816 by henrik karlsson in ue5-main branch]
587 lines
21 KiB
C++
587 lines
21 KiB
C++
// Copyright Epic Games, Inc. All Rights Reserved.
|
|
|
|
#include "Engine/MemberReference.h"
|
|
#include "EngineLogs.h"
|
|
#include "Misc/ConfigCacheIni.h"
|
|
#include "Stats/Stats.h"
|
|
#include "UObject/CoreRedirects.h"
|
|
#include "UObject/UObjectIterator.h"
|
|
|
|
#include UE_INLINE_GENERATED_CPP_BY_NAME(MemberReference)
|
|
|
|
#if WITH_EDITOR
|
|
#include "Kismet2/BlueprintEditorUtils.h"
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// FMemberReference
|
|
|
|
void FMemberReference::SetExternalMember(FName InMemberName, TSubclassOf<class UObject> InMemberParentClass)
|
|
{
|
|
MemberName = InMemberName;
|
|
#if WITH_EDITOR
|
|
MemberParent = (InMemberParentClass != nullptr) ? InMemberParentClass->GetAuthoritativeClass() : nullptr;
|
|
#else
|
|
MemberParent = *InMemberParentClass;
|
|
#endif
|
|
MemberScope.Empty();
|
|
bSelfContext = false;
|
|
bWasDeprecated = false;
|
|
}
|
|
|
|
void FMemberReference::SetExternalMember(FName InMemberName, TSubclassOf<class UObject> InMemberParentClass, FGuid& InMemberGuid)
|
|
{
|
|
SetExternalMember(InMemberName, InMemberParentClass);
|
|
MemberGuid = InMemberGuid;
|
|
}
|
|
|
|
void FMemberReference::SetGlobalField(FName InFieldName, UPackage* InParentPackage)
|
|
{
|
|
MemberName = InFieldName;
|
|
MemberParent = InParentPackage;
|
|
MemberScope.Empty();
|
|
bSelfContext = false;
|
|
bWasDeprecated = false;
|
|
}
|
|
|
|
void FMemberReference::SetExternalDelegateMember(FName InMemberName)
|
|
{
|
|
SetExternalMember(InMemberName, nullptr);
|
|
}
|
|
|
|
void FMemberReference::SetSelfMember(FName InMemberName)
|
|
{
|
|
MemberName = InMemberName;
|
|
MemberParent = nullptr;
|
|
MemberScope.Empty();
|
|
bSelfContext = true;
|
|
bWasDeprecated = false;
|
|
}
|
|
|
|
void FMemberReference::SetSelfMember(FName InMemberName, FGuid& InMemberGuid)
|
|
{
|
|
SetSelfMember(InMemberName);
|
|
MemberGuid = InMemberGuid;
|
|
}
|
|
|
|
void FMemberReference::SetDirect(const FName InMemberName, const FGuid InMemberGuid, TSubclassOf<class UObject> InMemberParentClass, bool bIsConsideredSelfContext)
|
|
{
|
|
MemberName = InMemberName;
|
|
MemberGuid = InMemberGuid;
|
|
bSelfContext = bIsConsideredSelfContext;
|
|
bWasDeprecated = false;
|
|
MemberParent = InMemberParentClass;
|
|
MemberScope.Empty();
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
void FMemberReference::SetGivenSelfScope(const FName InMemberName, const FGuid InMemberGuid, TSubclassOf<class UObject> InMemberParentClass, TSubclassOf<class UObject> SelfScope) const
|
|
{
|
|
MemberName = InMemberName;
|
|
MemberGuid = InMemberGuid;
|
|
MemberParent = (InMemberParentClass != nullptr) ? InMemberParentClass->GetAuthoritativeClass() : nullptr;
|
|
MemberScope.Empty();
|
|
|
|
// SelfScope should always be valid, but if it's not ensure and move on, the node will be treated as if it's not self scoped.
|
|
ensure(SelfScope);
|
|
bSelfContext = SelfScope && ((SelfScope->IsChildOf(InMemberParentClass)) || (SelfScope->ClassGeneratedBy == InMemberParentClass->ClassGeneratedBy));
|
|
bWasDeprecated = false;
|
|
|
|
if (bSelfContext)
|
|
{
|
|
MemberParent = nullptr;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void FMemberReference::SetLocalMember(FName InMemberName, UStruct* InScope, const FGuid InMemberGuid)
|
|
{
|
|
SetLocalMember(InMemberName, InScope->GetName(), InMemberGuid);
|
|
}
|
|
|
|
void FMemberReference::SetLocalMember(FName InMemberName, FString InScopeName, const FGuid InMemberGuid)
|
|
{
|
|
MemberName = InMemberName;
|
|
MemberScope = InScopeName;
|
|
MemberGuid = InMemberGuid;
|
|
bSelfContext = false;
|
|
}
|
|
|
|
void FMemberReference::InvalidateScope()
|
|
{
|
|
if( IsSelfContext() )
|
|
{
|
|
MemberParent = nullptr;
|
|
}
|
|
else if(IsLocalScope())
|
|
{
|
|
MemberScope.Empty();
|
|
|
|
// Make it into a member reference since we are clearing the local context
|
|
bSelfContext = true;
|
|
}
|
|
}
|
|
|
|
bool FMemberReference::IsSparseClassData(const UClass* OwningClass) const
|
|
{
|
|
bool bIsSparseClassData = false;
|
|
UScriptStruct* SparseClassDataStruct = OwningClass ? OwningClass->GetSparseClassDataStruct() : nullptr;
|
|
if (SparseClassDataStruct)
|
|
{
|
|
FProperty* VariableProperty = FindFProperty<FProperty>(SparseClassDataStruct, GetMemberName());
|
|
bIsSparseClassData = VariableProperty != nullptr;
|
|
}
|
|
|
|
return bIsSparseClassData;
|
|
}
|
|
|
|
#if WITH_EDITOR
|
|
|
|
FString FMemberReference::GetReferenceSearchString(UClass* InFieldOwner) const
|
|
{
|
|
if (!IsLocalScope())
|
|
{
|
|
if (InFieldOwner)
|
|
{
|
|
if (MemberGuid.IsValid())
|
|
{
|
|
return FString::Printf(TEXT("Nodes(VariableReference(MemberName=+\"%s\" && MemberGuid(A=%i && B=%i && C=%i && D=%i)) || Name=\"(%s)\") || Pins(Binding=\"%s\") || Binding=\"%s\""), *MemberName.ToString(), MemberGuid.A, MemberGuid.B, MemberGuid.C, MemberGuid.D, *MemberName.ToString(), *MemberName.ToString(), *MemberName.ToString());
|
|
}
|
|
else
|
|
{
|
|
FString ExportMemberParentName;
|
|
ExportMemberParentName = InFieldOwner->GetClass()->GetName();
|
|
ExportMemberParentName.AppendChar('\'');
|
|
ExportMemberParentName += InFieldOwner->GetAuthoritativeClass()->GetPathName();
|
|
ExportMemberParentName.AppendChar('\'');
|
|
|
|
return FString::Printf(TEXT("Nodes(VariableReference(MemberName=+\"%s\" && (MemberParent=\"%s\" || bSelfContext=true) ) || Name=\"(%s)\") || Pins(Binding=\"%s\") || Binding=\"%s\""), *MemberName.ToString(), *ExportMemberParentName, *MemberName.ToString(), *MemberName.ToString(), *MemberName.ToString());
|
|
}
|
|
}
|
|
else if (MemberGuid.IsValid())
|
|
{
|
|
return FString::Printf(TEXT("Nodes(VariableReference(MemberName=+\"%s\" && MemberGuid(A=%i && B=%i && C=%i && D=%i)) || Name=\"(%s)\") || Pins(Binding=\"%s\") || Binding=\"%s\""), *MemberName.ToString(), MemberGuid.A, MemberGuid.B, MemberGuid.C, MemberGuid.D, *MemberName.ToString(), *MemberName.ToString(), *MemberName.ToString());
|
|
}
|
|
else
|
|
{
|
|
return FString::Printf(TEXT("Nodes(VariableReference(MemberName=+\"%s\") || Name=\"(%s)\") || Pins(Binding=\"%s\") || Binding=\"%s\""), *MemberName.ToString(), *MemberName.ToString(), *MemberName.ToString(), *MemberName.ToString());
|
|
}
|
|
}
|
|
else
|
|
{
|
|
return FString::Printf(TEXT("Nodes(VariableReference((MemberName=+\"%s\" && MemberScope=+\"%s\"))) || Binding=\"%s\""), *MemberName.ToString(), *GetMemberScopeName(), *MemberName.ToString());
|
|
}
|
|
}
|
|
|
|
FName FMemberReference::RefreshLocalVariableName(UClass* InSelfScope) const
|
|
{
|
|
TArray<UBlueprint*> Blueprints;
|
|
UBlueprint::GetBlueprintHierarchyFromClass(InSelfScope, Blueprints);
|
|
|
|
FName RenamedMemberName = NAME_None;
|
|
for (int32 BPIndex = 0; BPIndex < Blueprints.Num(); ++BPIndex)
|
|
{
|
|
RenamedMemberName = FBlueprintEditorUtils::FindLocalVariableNameByGuid(Blueprints[BPIndex], MemberGuid);
|
|
if (RenamedMemberName != NAME_None)
|
|
{
|
|
MemberName = RenamedMemberName;
|
|
break;
|
|
}
|
|
}
|
|
return RenamedMemberName;
|
|
}
|
|
|
|
bool FMemberReference::bFieldRedirectMapInitialized = false;
|
|
|
|
void FMemberReference::InitFieldRedirectMap()
|
|
{
|
|
// Soft deprecated, replaced by FCoreRedirects but will read the old ini format for the foreseeable future
|
|
if (!bFieldRedirectMapInitialized)
|
|
{
|
|
if (GConfig)
|
|
{
|
|
TArray<FCoreRedirect> NewRedirects;
|
|
|
|
FConfigSection* PackageRedirects = GConfig->GetSectionPrivate( TEXT("/Script/Engine.Engine"), false, true, GEngineIni );
|
|
for (FConfigSection::TIterator It(*PackageRedirects); It; ++It)
|
|
{
|
|
if (It.Key() == TEXT("K2FieldRedirects"))
|
|
{
|
|
FString OldFieldPathString;
|
|
FString NewFieldPathString;
|
|
|
|
FParse::Value( *It.Value().GetValue(), TEXT("OldFieldName="), OldFieldPathString );
|
|
FParse::Value( *It.Value().GetValue(), TEXT("NewFieldName="), NewFieldPathString );
|
|
|
|
// Add both a Property and Function redirect, as we don't know which it's trying to be with the old syntax
|
|
FCoreRedirect* PropertyRedirect = new (NewRedirects) FCoreRedirect(ECoreRedirectFlags::Type_Property, OldFieldPathString, NewFieldPathString);
|
|
FCoreRedirect* FunctionRedirect = new (NewRedirects) FCoreRedirect(ECoreRedirectFlags::Type_Function, OldFieldPathString, NewFieldPathString);
|
|
}
|
|
if (It.Key() == TEXT("K2ParamRedirects"))
|
|
{
|
|
// Ignore NodeName/Title as it's not useful
|
|
FName OldParam = NAME_None;
|
|
FName NewParam = NAME_None;
|
|
|
|
FString OldParamValues;
|
|
FString NewParamValues;
|
|
FString CustomValueMapping;
|
|
|
|
FParse::Value( *It.Value().GetValue(), TEXT("OldParamName="), OldParam );
|
|
FParse::Value( *It.Value().GetValue(), TEXT("NewParamName="), NewParam );
|
|
FParse::Value( *It.Value().GetValue(), TEXT("OldParamValues="), OldParamValues );
|
|
FParse::Value( *It.Value().GetValue(), TEXT("NewParamValues="), NewParamValues );
|
|
FParse::Value( *It.Value().GetValue(), TEXT("CustomValueMapping="), CustomValueMapping );
|
|
|
|
TArray<FString> OldParamValuesList;
|
|
TArray<FString> NewParamValuesList;
|
|
OldParamValues.ParseIntoArray(OldParamValuesList, TEXT(";"), false);
|
|
NewParamValues.ParseIntoArray(NewParamValuesList, TEXT(";"), false);
|
|
|
|
if (OldParamValuesList.Num() != NewParamValuesList.Num())
|
|
{
|
|
UE_LOG(LogBlueprint, Warning, TEXT("Unequal lengths for old and new param values for param redirect '%s' to '%s'."), *(OldParam.ToString()), *(NewParam.ToString()));
|
|
}
|
|
|
|
if (CustomValueMapping.Len() > 0 && (OldParamValuesList.Num() > 0 || NewParamValuesList.Num() > 0))
|
|
{
|
|
UE_LOG(LogBlueprint, Warning, TEXT("Both Custom and Automatic param value remapping specified for param redirect '%s' to '%s'. Only Custom will be applied."), *(OldParam.ToString()), *(NewParam.ToString()));
|
|
}
|
|
|
|
FCoreRedirect* Redirect = new (NewRedirects) FCoreRedirect(ECoreRedirectFlags::Type_Property, OldParam.ToString(), NewParam.ToString());
|
|
|
|
for (int32 i = FMath::Min(OldParamValuesList.Num(), NewParamValuesList.Num()) - 1; i >= 0; --i)
|
|
{
|
|
int32 CurSize = Redirect->ValueChanges.Num();
|
|
Redirect->ValueChanges.Add(OldParamValuesList[i], NewParamValuesList[i]);
|
|
if (CurSize == Redirect->ValueChanges.Num())
|
|
{
|
|
UE_LOG(LogBlueprint, Warning, TEXT("Duplicate old param value '%s' for param redirect '%s' to '%s'."), *(OldParamValuesList[i]), *(OldParam.ToString()), *(NewParam.ToString()));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
FCoreRedirects::AddRedirectList(NewRedirects, TEXT("InitFieldRedirectMap"));
|
|
bFieldRedirectMapInitialized = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
UClass* FMemberReference::GetClassToUse(UClass* InClass, bool bUseUpToDateClass)
|
|
{
|
|
if(bUseUpToDateClass)
|
|
{
|
|
return FBlueprintEditorUtils::GetMostUpToDateClass(InClass);
|
|
}
|
|
else
|
|
{
|
|
return InClass;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
template<class TFieldType>
|
|
TFieldType* ResolveUFunctionImpl(FName MemberName)
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
template<>
|
|
inline UFunction* ResolveUFunctionImpl(FName MemberName)
|
|
{
|
|
UFunction* ReturnField = nullptr;
|
|
FString const StringName = MemberName.ToString();
|
|
for (TObjectIterator<UPackage> PackageIt; PackageIt && (ReturnField == nullptr); ++PackageIt)
|
|
{
|
|
if (PackageIt->HasAnyPackageFlags(PKG_CompiledIn) == false)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// NOTE: this could return the wrong field (if there are
|
|
// two like-named delegates defined in separate packages)
|
|
ReturnField = FindObject<UFunction>(*PackageIt, *StringName);
|
|
}
|
|
return ReturnField;
|
|
}
|
|
|
|
template<class TFieldType>
|
|
TFieldType* ResolveUField(FFieldClass* InClass, UPackage* TargetPackage, FName MemberName)
|
|
{
|
|
return nullptr;
|
|
}
|
|
template<class TFieldType>
|
|
TFieldType* ResolveUField(UClass* InClass, UPackage* TargetPackage, FName MemberName)
|
|
{
|
|
return FindObject<TFieldType>(TargetPackage, *MemberName.ToString());;
|
|
}
|
|
|
|
template<class TFieldType>
|
|
UObject* GetFieldOuter(TFieldType Field)
|
|
{
|
|
return Field->GetOuter();
|
|
}
|
|
|
|
template<>
|
|
inline UObject* GetFieldOuter(FField* Field)
|
|
{
|
|
return Field->GetOwner<UObject>();
|
|
}
|
|
|
|
template<class TFieldType, class TFieldTypeClass>
|
|
TFieldType* FMemberReference::ResolveMemberImpl(UClass* SelfScope, TFieldTypeClass* FieldClass, const bool bAlwaysFollowRedirects, const bool bIsUFunctionOrMulticastDelegate) const
|
|
{
|
|
TFieldType* ReturnField = nullptr;
|
|
|
|
if (bSelfContext && SelfScope == nullptr)
|
|
{
|
|
UE_LOG(LogBlueprint, Warning, TEXT("FMemberReference::ResolveMember (%s) bSelfContext == true, but no scope supplied!"), *MemberName.ToString());
|
|
}
|
|
|
|
// Check if the member reference is function scoped
|
|
if (IsLocalScope())
|
|
{
|
|
UStruct* MemberScopeStruct = FindUField<UStruct>(SelfScope, *MemberScope);
|
|
|
|
// Find in target scope
|
|
ReturnField = FindUFieldOrFProperty<TFieldType>(MemberScopeStruct, MemberName, EFieldIterationFlags::IncludeAll);
|
|
|
|
#if WITH_EDITOR
|
|
if (ReturnField == nullptr)
|
|
{
|
|
// If the property was not found, refresh the local variable name and try again
|
|
const FName RenamedMemberName = RefreshLocalVariableName(SelfScope);
|
|
if (RenamedMemberName != NAME_None)
|
|
{
|
|
ReturnField = FindUFieldOrFProperty<TFieldType>(MemberScopeStruct, MemberName, EFieldIterationFlags::IncludeAll);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
#if WITH_EDITOR
|
|
const bool bCanFollowRedirects = !GIsSavingPackage;
|
|
#else
|
|
const bool bCanFollowRedirects = bAlwaysFollowRedirects;
|
|
#endif
|
|
|
|
// Look for remapped member
|
|
UClass* TargetScope = GetScope(SelfScope);
|
|
if (bCanFollowRedirects && TargetScope)
|
|
{
|
|
ReturnField = static_cast<TFieldType*>(FindRemappedField(FieldClass, TargetScope, MemberName, true));
|
|
}
|
|
|
|
if (ReturnField != nullptr)
|
|
{
|
|
// Fix up this struct, we found a redirect
|
|
MemberName = ReturnField->GetFName();
|
|
MemberParent = Cast<UClass>(GetFieldOuter(static_cast<typename TFieldType::BaseFieldClass*>(ReturnField)));
|
|
|
|
MemberGuid.Invalidate();
|
|
UBlueprint::GetGuidFromClassByFieldName<TFieldType>(TargetScope, MemberName, MemberGuid);
|
|
|
|
if (UClass* ParentAsClass = GetMemberParentClass())
|
|
{
|
|
#if WITH_EDITOR
|
|
ParentAsClass = ParentAsClass->GetAuthoritativeClass();
|
|
#endif
|
|
MemberParent = ParentAsClass;
|
|
|
|
// Re-evaluate self-ness against the redirect if we were given a valid SelfScope
|
|
// For functions and multicast delegates we don't want to go from not-self to self as the target pin type should remain consistent
|
|
if (SelfScope != nullptr &&
|
|
(bSelfContext || !bIsUFunctionOrMulticastDelegate))
|
|
{
|
|
#if WITH_EDITOR
|
|
bSelfContext = SelfScope->IsChildOf(ParentAsClass) || SelfScope->ClassGeneratedBy == ParentAsClass->ClassGeneratedBy;
|
|
#else
|
|
bSelfContext = SelfScope->IsChildOf(ParentAsClass);
|
|
#endif
|
|
|
|
if (bSelfContext)
|
|
{
|
|
MemberParent = nullptr;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (TargetScope != nullptr)
|
|
{
|
|
#if WITH_EDITOR
|
|
bool bUseUpToDateClass = SelfScope && SelfScope->GetAuthoritativeClass() != SelfScope;
|
|
TargetScope = GetClassToUse(TargetScope, bUseUpToDateClass);
|
|
if (TargetScope)
|
|
#endif
|
|
{
|
|
// Find in target scope or in the sparse class data
|
|
ReturnField = FindUFieldOrFProperty<TFieldType>(TargetScope, MemberName, EFieldIterationFlags::IncludeAll);
|
|
if (!ReturnField)
|
|
{
|
|
if (UScriptStruct* SparseClassDataStruct = TargetScope->GetSparseClassDataStruct())
|
|
{
|
|
ReturnField = FindUFieldOrFProperty<TFieldType>(SparseClassDataStruct, MemberName, EFieldIterationFlags::IncludeAll);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
#if !WITH_EDITOR
|
|
if (bAlwaysFollowRedirects)
|
|
#endif
|
|
{
|
|
// If the reference variable is valid we need to make sure that our GUID matches
|
|
if (ReturnField != nullptr)
|
|
{
|
|
UBlueprint::GetGuidFromClassByFieldName<TFieldType>(TargetScope, MemberName, MemberGuid);
|
|
}
|
|
// If we have a GUID find the reference variable and make sure the name is up to date and find the field again
|
|
// For now only variable references will have valid GUIDs. Will have to deal with finding other names subsequently
|
|
else if (MemberGuid.IsValid())
|
|
{
|
|
const FName RenamedMemberName = UBlueprint::GetFieldNameFromClassByGuid<TFieldType>(TargetScope, MemberGuid);
|
|
if (RenamedMemberName != NAME_None)
|
|
{
|
|
MemberName = RenamedMemberName;
|
|
ReturnField = FindUFieldOrFProperty<TFieldType>(TargetScope, MemberName, EFieldIterationFlags::IncludeAll);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (UPackage* TargetPackage = GetMemberParentPackage())
|
|
{
|
|
ReturnField = ResolveUField<TFieldType>(FieldClass, TargetPackage, MemberName);
|
|
}
|
|
// For backwards compatibility: as of CL 2412156, delegate signatures
|
|
// could have had a null MemberParentClass (for those natively
|
|
// declared outside of a class), we used to rely on the following
|
|
// FindObject<>; however this was not reliable (hence the addition
|
|
// of GetMemberParentPackage(), etc.)
|
|
else if (MemberName.ToString().EndsWith(HEADER_GENERATED_DELEGATE_SIGNATURE_SUFFIX))
|
|
{
|
|
ReturnField = ResolveUFunctionImpl<TFieldType>(MemberName);
|
|
if (ReturnField != nullptr)
|
|
{
|
|
UE_LOG(LogBlueprint, Display, TEXT("Generic delegate signature ref (%s). Explicitly setting it to: '%s'. Make sure this is correct (there could be multiple native delegate types with this name)."), *MemberName.ToString(), *ReturnField->GetPathName());
|
|
MemberParent = ReturnField->GetOutermost();
|
|
}
|
|
}
|
|
}
|
|
|
|
// Check to see if the member has been deprecated
|
|
if (FProperty* Property = FFieldVariant(ReturnField).Get<FProperty>())
|
|
{
|
|
bWasDeprecated = Property->HasAnyPropertyFlags(CPF_Deprecated);
|
|
}
|
|
|
|
return ReturnField;
|
|
}
|
|
|
|
FProperty* FMemberReference::ResolveMemberProperty(UClass* SelfScope, const bool bAlwaysFollowRedirects, FFieldClass* FieldClass) const
|
|
{
|
|
return ResolveMemberImpl<FProperty, FFieldClass>(SelfScope, FieldClass, bAlwaysFollowRedirects, FieldClass == FMulticastDelegateProperty::StaticClass());
|
|
}
|
|
|
|
UFunction* FMemberReference::ResolveMemberFunction(UClass* SelfScope, const bool bAlwaysFollowRedirects) const
|
|
{
|
|
const bool bIsAUFunction = true;
|
|
return ResolveMemberImpl<UFunction, UClass>(SelfScope, UFunction::StaticClass(), bAlwaysFollowRedirects, bIsAUFunction);
|
|
}
|
|
|
|
template <typename TFieldType>
|
|
TFieldType* FindRemappedFieldImpl(FName FieldClassOutermostName, FName FieldClassName, UClass* InitialScope, FName InitialName, bool bInitialScopeMustBeOwnerOfField)
|
|
{
|
|
DECLARE_SCOPE_CYCLE_COUNTER(TEXT("FMemberReference::FindRemappedField"), STAT_LinkerLoad_FindRemappedField, STATGROUP_LoadTimeVerbose);
|
|
|
|
#if WITH_EDITOR
|
|
FMemberReference::InitFieldRedirectMap();
|
|
#endif
|
|
|
|
// In the case of a bifurcation of a variable (e.g. moved from a parent into certain children), verify that we don't also define the variable in the current scope first
|
|
if (FindUFieldOrFProperty<TFieldType>(InitialScope, InitialName))
|
|
{
|
|
return nullptr;
|
|
}
|
|
|
|
// Step up the class chain to check if us or any of our parents specify a redirect
|
|
UClass* TestRemapClass = InitialScope;
|
|
while (TestRemapClass != nullptr)
|
|
{
|
|
UClass* SearchClass = TestRemapClass;
|
|
|
|
FName NewFieldName;
|
|
|
|
FCoreRedirectObjectName OldRedirectName = FCoreRedirectObjectName(InitialName, TestRemapClass->GetFName(), TestRemapClass->GetOutermost()->GetFName());
|
|
FCoreRedirectObjectName NewRedirectName = FCoreRedirects::GetRedirectedName(FCoreRedirects::GetFlagsForTypeName(FieldClassOutermostName, FieldClassName), OldRedirectName);
|
|
|
|
if (NewRedirectName != OldRedirectName)
|
|
{
|
|
NewFieldName = NewRedirectName.ObjectName;
|
|
|
|
if (OldRedirectName.OuterName != NewRedirectName.OuterName)
|
|
{
|
|
// Try remapping class, this only works if class is in memory
|
|
FString ClassName = NewRedirectName.OuterName.ToString();
|
|
|
|
if ( !NewRedirectName.PackageName.IsNone() )
|
|
{
|
|
// Use package if it's there
|
|
ClassName = FString::Printf(TEXT("%s.%s"), *NewRedirectName.PackageName.ToString(), *NewRedirectName.OuterName.ToString());
|
|
SearchClass = (UClass*)StaticFindObject(UClass::StaticClass(), nullptr, *ClassName);
|
|
}
|
|
else
|
|
{
|
|
SearchClass = FindFirstObject<UClass>(*ClassName, EFindFirstObjectOptions::None, ELogVerbosity::Fatal, TEXT("FindRemappedFieldImpl"));
|
|
}
|
|
|
|
if (!SearchClass)
|
|
{
|
|
UE_LOG(LogBlueprint, Log, TEXT("UK2Node: Unable to find updated field name for '%s' on unknown class '%s'."), *InitialName.ToString(), *ClassName);
|
|
return nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (NewFieldName != NAME_None)
|
|
{
|
|
// Find the actual field specified by the redirector, so we can return it and update the node that uses it
|
|
TFieldType* NewField = FindUFieldOrFProperty<TFieldType>(SearchClass, NewFieldName);
|
|
if (NewField != nullptr)
|
|
{
|
|
if (bInitialScopeMustBeOwnerOfField && !InitialScope->IsChildOf(SearchClass))
|
|
{
|
|
UE_LOG(LogBlueprint, Log, TEXT("UK2Node: Unable to update field. Remapped field '%s' in not owned by given scope. Scope: '%s', Owner: '%s'."), *InitialName.ToString(), *InitialScope->GetName(), *NewFieldName.ToString());
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogBlueprint, Log, TEXT("UK2Node: Fixed up old field '%s' to new name '%s' on class '%s'."), *InitialName.ToString(), *NewFieldName.ToString(), *SearchClass->GetName());
|
|
return NewField;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
UE_LOG(LogBlueprint, Log, TEXT("UK2Node: Unable to find updated field name for '%s' on class '%s'."), *InitialName.ToString(), *SearchClass->GetName());
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
TestRemapClass = TestRemapClass->GetSuperClass();
|
|
}
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
UField* FMemberReference::FindRemappedField(UClass* FieldClass, UClass* InitialScope, FName InitialName, bool bInitialScopeMustBeOwnerOfField)
|
|
{
|
|
return FindRemappedFieldImpl<UField>(FieldClass->GetOutermost()->GetFName(), FieldClass->GetFName(), InitialScope, InitialName, bInitialScopeMustBeOwnerOfField);
|
|
}
|
|
|
|
FField* FMemberReference::FindRemappedField(FFieldClass* FieldClass, UClass* InitialScope, FName InitialName, bool bInitialScopeMustBeOwnerOfField)
|
|
{
|
|
return FindRemappedFieldImpl<FField>(GLongCoreUObjectPackageName, FieldClass->GetFName(), InitialScope, InitialName, bInitialScopeMustBeOwnerOfField);
|
|
}
|
|
|