Description

Child blueprints of a blueprint of a native actor class with sparse data can have an incorrectly null SparseClassDataStruct value, and thus fail to initialzie their SparseClassData. This affects editor-time, PIE, but not standalone games.

Example native actor class with sparse data:

 

USTRUCT(BlueprintType)
struct FMySparseDataStruct
{
    GENERATED_BODY()
    FMySparseDataStruct() {}
    UPROPERTY(EditDefaultsOnly)
    int32 SomeValue;
};

UCLASS(SparseClassDataTypes = MySparseDataStruct)
class SPARSEDATANULL54R_API AMySparseDataActor : public AActor
{
    GENERATED_BODY()
public:
    AMySparseDataActor();
    virtual void BeginPlay() override;
}; 

If you create a blueprint 'BP_MySparseDataActor' of the above, and a child blueprint 'BP_MySparseDataActor_Child' of that, the child blueprint will misbehave after editor restarts until either the parent or child blueprint have been recompiled.

This can be observed in multiple ways:

  • Despite SomeValue being an editable property, you can't see it when  editing BP_MySparseDataActor_Child until it's compiled.
  • While in this state, if a BP_MySparseDataActor_Child is placed on the map and you start PIE, any access of sparse data causes a crash. Example:
void AMySparseDataActor::BeginPlay()
{
    Super::BeginPlay();

    // These ensures will fail in an editor session for child blueprints unless the child BP itself or a parent BP has been manually compiled
    UClass* MyClass = GetClass();
    ensure(MyClass->GetOrCreateSparseClassData() != nullptr);
    ensure(MyClass->GetSparseClassDataStruct() != nullptr);

    // This getter will crash under the same conditions
    const int32 SomeValue = GetSomeValue();
    UE_LOG(LogTemp, Warning, TEXT("BeginPlay(%s) My Value = %d"), *GetPathName(), SomeValue);
}
 

The problem does not affect Standalone builds. It also doesn't affect blueprints that directly inherit from the native class.

Steps to Reproduce

(Internal) repro project attached.

  • Create a native actor class AMySparseDataActor with a SparseDataClass (see description)
  • Create a blueprint BP of that: BP_MySparseDataActor
  • Create a child blueprint of that: BP_MySparseDataActor_Child
  • Have the native actor class access some sparse data on BeginPlay
  • Restart the editor so none of the BP assets have been loaded or compiled
  • Drag a BP_MySparseDataActor_Child into the level and start PIE
  • Observe: Crash with provided callstack, because:
    • The class's SparseClassData pointer is null
    • The class's SparseClassDataStruct is also null (but it shouldn't be because the native grandparent class defines one)
  • Expected: Sparse data is accessible
  • Alternatively, after restarting the editor simply open BP_MySparseDataActor_Child and see that the sparse data properties aren't editable until the child or parent BP is recompiled.
Callstack

>    [Inline Frame] UnrealEditor-SparseDataNull54R.dll!AMySparseDataActor::GetSomeValue() Line 23    C++
     UnrealEditor-SparseDataNull54R.dll!AMySparseDataActor::BeginPlay() Line 21    C++
     UnrealEditor-Engine.dll!AActor::DispatchBeginPlay(bool bFromLevelStreaming) Line 4193    C++
     UnrealEditor-Engine.dll!AWorldSettings::NotifyBeginPlay() Line 305    C++
     UnrealEditor-Engine.dll!AGameStateBase::HandleBeginPlay() Line 228    C++
     UnrealEditor-Engine.dll!AGameModeBase::StartPlay() Line 206    C++
     UnrealEditor-Engine.dll!UWorld::BeginPlay() Line 5330    C++
     UnrealEditor-Engine.dll!UGameInstance::StartPlayInEditorGameInstance(ULocalPlayer * LocalPlayer, const FGameInstancePIEParameters & Params) Line 568    C++
     UnrealEditor-UnrealEd.dll!UEditorEngine::CreateInnerProcessPIEGameInstance(FRequestPlaySessionParams & InParams, const FGameInstancePIEParameters & InPIEParameters, int InPIEInstanceIndex) Line 3141    C++
     UnrealEditor-UnrealEd.dll!UEditorEngine::OnLoginPIEComplete_Deferred(int LocalUserNum, bool bWasSuccessful, FString ErrorString, FPieLoginStruct DataStruct) Line 1590    C++
     UnrealEditor-UnrealEd.dll!UEditorEngine::CreateNewPlayInEditorInstance(FRequestPlaySessionParams & InRequestParams, const bool bInDedicatedInstance, const EPlayNetMode InNetMode) Line 1853    C++
     UnrealEditor-UnrealEd.dll!UEditorEngine::StartPlayInEditorSession(FRequestPlaySessionParams & InRequestParams) Line 2872    C++
     UnrealEditor-UnrealEd.dll!UEditorEngine::StartQueuedPlaySessionRequestImpl() Line 1167    C++
     UnrealEditor-UnrealEd.dll!UEditorEngine::StartQueuedPlaySessionRequest() Line 1068    C++
     UnrealEditor-UnrealEd.dll!UEditorEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 1902    C++
     UnrealEditor-UnrealEd.dll!UUnrealEdEngine::Tick(float DeltaSeconds, bool bIdleMode) Line 550    C++
     UnrealEditor.exe!FEngineLoop::Tick() Line 5910    C++
     [Inline Frame] UnrealEditor.exe!EngineTick() Line 61    C++
     UnrealEditor.exe!GuardedMain(const wchar_t * CmdLine) Line 180    C++
     UnrealEditor.exe!LaunchWindowsStartup(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * __formal, int nCmdShow, const wchar_t * CmdLine) Line 247    C++
     UnrealEditor.exe!WinMain(HINSTANCE__ * hInInstance, HINSTANCE__ * hPrevInstance, char * pCmdLine, int nCmdShow) Line 298    C++
     [External Code]    

Have Comments or More Details?

There's no existing public thread on this issue, so head over to Questions & Answers just mention UE-215992 in the post.

1
Login to Vote

Unresolved
ComponentUE - Gameplay - Blueprint
Affects Versions5.4
Target Fix5.6
CreatedMay 29, 2024
UpdatedOct 1, 2024
View Jira Issue