ArrayMax>=ArrayNum Check failure

Hey folks,

We’ve ran into this check failure a few times from various sources since upgrading to 5.6, and this is the most 100% repro I’ve found so far.

From digging, it appears that there’s some assignment of data into a TScriptArray, and the ArrayMax value doesn’t get updated. When the “Identical()” function is then called to copy from the CDO of this object, a Num() call follows which causes that check to fail. I may have some details incorrect, but I wanted to flag this before I started making changes this deep in the engine. :slight_smile:

I’ve managed to repro this on vanilla 5.6.1 and 5.7.1.

We’re able to repro this 100% with the debug build of the editor, but in some circumstances that check manifests in Development and can bring down the editor for our teams.

Any help would be appreciated,

Many thanks,

David

[Attachment Removed]

Steps to Reproduce

  • Compile and launch Debug Editor
  • Open any map
  • Create an HLOD Layer asset
  • Set it to be Simplified type
  • Open Nanite Settings
  • Wait a couple of seconds
  • Observe check failure
    [Attachment Removed]

Hi David! Thanks for reporting this problem.

I just submitted a fix that should resolve this issue, let me know if this works for you.

It’s not yet live on GitHub, so I’m pasting the change here. It’s in

\Engine\Source\Editor\DetailCustomizations\Private\OverrideResetToDefault.h

template<typename UStructType>
class TOverrideResetToDefaultWithStaticUStruct
{
...
private:
	/**
	 * Resolves the correct default value pointer for a property that may be nested within UStructType.
	 * Walks the property handle parent chain to figure out the correct offset from DefaultObject.
	 */
	static const void* ResolveDefaultValuePtr(TSharedPtr<IPropertyHandle> InPropertyHandle);
 
	static const UStructType DefaultObject;
};
 
...
 
template<typename UStructType>
const void* TOverrideResetToDefaultWithStaticUStruct<UStructType>::ResolveDefaultValuePtr(TSharedPtr<IPropertyHandle> InPropertyHandle)
{
	FProperty* Property = InPropertyHandle->GetProperty();
	if (!Property)
	{
		return nullptr;
	}
 
	// If this property is a direct member of UStructType, use DefaultObject as the container
	if (Property->GetOwnerStruct() == UStructType::StaticStruct())
	{
		return Property->ContainerPtrToValuePtr<void>(&DefaultObject);
	}
 
	// Property is nested deeper within UStructType - resolve the parent's default value to use as container
	TSharedPtr<IPropertyHandle> ParentHandle = InPropertyHandle->GetParentHandle();
	if (!ParentHandle.IsValid())
	{
		return nullptr;
	}
 
	const void* ParentDefaultValue = ResolveDefaultValuePtr(ParentHandle);
	if (!ParentDefaultValue)
	{
		return nullptr;
	}
 
	return Property->ContainerPtrToValuePtr<void>(ParentDefaultValue);
}
 
template<typename UStructType>
bool TOverrideResetToDefaultWithStaticUStruct<UStructType>::IsResetToDefaultVisible(TSharedPtr<IPropertyHandle> InPropertyHandle)
{
	FProperty*		Property = InPropertyHandle->GetProperty();
	const void*		DefaultValuePtr = nullptr;
	void*			ValuePtr = nullptr;
 
	check(Property != nullptr);
 
	DefaultValuePtr = ResolveDefaultValuePtr(InPropertyHandle);
	InPropertyHandle->GetValueData(ValuePtr);
 
	if ((DefaultValuePtr != nullptr) && (ValuePtr != nullptr))
	{
		return !Property->Identical(DefaultValuePtr, ValuePtr);
	}
 
	return false;
}
 
template<typename UStructType>
void TOverrideResetToDefaultWithStaticUStruct<UStructType>::OnResetToDefault(TSharedPtr<IPropertyHandle> InPropertyHandle)
{
	FProperty*	Property = InPropertyHandle->GetProperty();
	const void* DefaultValuePtr = nullptr;
	void*		ValuePtr = nullptr;
 
	check(Property != nullptr);
 
	DefaultValuePtr = ResolveDefaultValuePtr(InPropertyHandle);
	InPropertyHandle->GetValueData(ValuePtr);
 
	if ((DefaultValuePtr != nullptr) && (ValuePtr != nullptr))
	{
		// SetPropertyValue_DirectSingle when called on an archetype can propagate to instances.
		// See class comments: this ResetToDefault override was not written for contexts involving
		// inheritance, so pass in empty archetype instances list.
		TArray<void*> NoArchetypeInsts;
		PropertyAccessUtil::SetPropertyValue_DirectSingle(Property, DefaultValuePtr, Property, ValuePtr, NoArchetypeInsts, 0, Property->HasAnyFlags(RF_ArchetypeObject), []() { return nullptr; });
	}
}

Regards,

Sebastien

[Attachment Removed]

Hi David! No these issues are not related.

Thanks!

[Attachment Removed]

Hey Sebastian, thank you very much for this. Does this potentially also address the follow up question I posed in [this [Content removed]

I’ve just been blindsided by some OOM cooker issues and so may need a day or so to investigate this change specifically and get back to you.

Many thanks,

David

[Attachment Removed]