Does 'Data Validation' not work for Blueprints?

We’re trying to make use of the Data Validation feature of 4.19. Our use case is to make sure certain kinds of blueprints, with C++ base classes, are set up correctly.

Our IsDataValid() function iterates all components of a certain type and makes sure they’re in a reasonable state. However, at the time IsDataValid() is called, our actor does not appear to have any components on it. I can see in the debugger that I’m dealing with the default instance because this->NamePrivate starts with Default__.

I also tried dragging an instance of the blueprint into a map and then validate the map with all its dependencies. The IsDataValid() function is hit, but it’s the same as before, I only get the default instance without any components.

I got most of my knowledge from the following article. However, their example is about data assets: A new, community-hosted Unreal Engine Wiki - Announcements - Unreal Engine Forums

Am I doing something wrong or is this system just not made for this kind of validation?

Any ideas about alternatives to validate blueprints are welcome!

By look of it this system is only inteded to use with assets not actor classes or even objects on the level

This is what is happening and calling IsDataValid on your actor:

EDataValidationResult UBlueprint::IsDataValid(TArray<FText>& ValidationErrors)
{
	return GeneratedClass ? GeneratedClass->GetDefaultObject()->IsDataValid(ValidationErrors) : EDataValidationResult::Invalid;
}

The blueprint assets call IsDataValid on Class Default Object (CDO) of generated class of blueprint, so you can validate default variable settings of class but only inside of blueprint. CDO is default object which is hold inside UClass and is there to store default values of the class. CDO is in permanent uninitiated state thats why you not getting any component information and CDO should not run any modifying code as it will mess up defaults of you class. UObject in this preemptive state holds data of all components as default subobjects and you can get those via:

This is definitly not called on actors on the level, as you see in the code the UBlueprint (Blueprint asset) getting IsDataValid and it forward the call to CDO of your class. Actors on the level are already out of there UBlueprint shell

I know this post is a bit old, but if you want to check for errors in an actor-derived class you may want to override the AActor::CheckForErrors() function. This is called both when you validate the data of an asset, from the content browser, and when you check the map (performed when you build the level or select ‘Map Check’ from the build button drop-down). Also consider that each component also has its own CheckForErrors() which is called from the parent actor. Here you should report errors to the “MapCheck” log channel through FMessageLog(“MapCheck”).Warning() or FMessageLog(“MapCheck”).Error(). The AActor implementation of IsValidData() reports a generic error message sending back to the Map Check section for further details.

As said, when using the validate asset feature, the function is called on the CDO, so you don’t have access to the components, but you can validate default values of the actor itself. When you instantiate an actor in the level, on the other hand, you should use the above mentioned Map Check. In this case you do have access to the attributes and components of the instance in the level.

However, data validation routines are not currently exposed to blueprint, so I’m afraid they are intended for validation of data accessible from code, in any case.

Cheers

Edit:

4.23 has this now built-in. For older Engine Versions, please look at the following:


In the end we solved this by implementing IsDataValid for UBlueprintGeneratedClass and calling this method instead of directly calling the CDO of the generated class:

EDataValidationResult UBlueprintGeneratedClass::IsDataValid(TArray<FText>& ValidationErrors)
{
	EDataValidationResult Result = EDataValidationResult::NotValidated;
	if (SimpleConstructionScript)
	{
		for (const USCS_Node* Node : SimpleConstructionScript->GetAllNodes())
		{
			if (Node)
			{
				UActorComponent* ActorComponent = Node->GetActualComponentTemplate(this);
				if (ActorComponent)
				{
					const EDataValidationResult ValidationResult = ActorComponent->IsDataValid(ValidationErrors);
					// Only allow upward escalation from NotValidated -> Valid -> Invalid
					if (static_cast<uint8>(ValidationResult) < static_cast<uint8>(Result))
					{
						Result = ValidationResult;
					}
				}
			}
		}
		
	}
	const EDataValidationResult DefaultObjectResult = GetDefaultObject()->IsDataValid(ValidationErrors);
	// Only allow upward escalation from NotValidated -> Valid -> Invalid
	if (static_cast<uint8>(DefaultObjectResult) < static_cast<uint8>(Result))
	{
		Result = DefaultObjectResult;
	}
	return Result;
}

This is called by the associated blueprint:

EDataValidationResult UBlueprint::IsDataValid(TArray<FText>& ValidationErrors)
{
	return GeneratedClass ? GeneratedClass->IsDataValid(ValidationErrors) : EDataValidationResult::Invalid;
}

Now data validation is called for all Components, which are spawned by blueprints (these are handled in C++ using the SimpleConstructionScript). The only thing which is missing for those components are the attachment setups, which are set, when the blueprint is actually spawned.

2 Likes