Using DataValidation framework for differential validation of Contend Directory UAsset, vs UWorld UObject

Okay, so I don’t believe I’m doing anything crazy, but I feel crazy.

  • I have a class which I’ve implemented in CPP.
    • I have a BP class, which inherits from my CPP class.
    • My BP class, lives in my Content Directory
  • I want to Drag-And-Drop my BP class, into the UWorld when I need it
    • When it’s in the UWorld, this class should is NOT permitted to have a nullptr to AFoo.
      When it’s in the Content Directory, it MUST have a nullptr to AFoo.
    • My UWorld has several AFoo in it.
      • But there is no valid “default” AFoo

So, perilously, validating this asset requires two incompatible states.

  1. When it’s in the Content Directory, m_pFoo must be nullptr.
  2. When it’s in a UWorld, m_pFoo must NOT be nullptr

I can distinguish between these two states.

Notably, the uasset in the Content directory has “Default__” on the front, when you call GetName(). In addition, that object shows ActorGuid as all zeros.

However, this is not the same thing as the Class Default Object, and when comparing against the CDO, it returns not-equals.

So, I’m not entirely sure what I ought to be doing in this situation. Comparing against the zeroe’d out ActorGuid doesn’t seem correct. Comparing against the name also doesn’t seem correct. Doing both of these at once does seem to successfully distinguish the Content Directory uasset from an in map UObject

Question 1:

How can I vary my logic, based on whether or not this BP uasset lives in a UWorld, or just lives in the Content directory?

Question 2:

Is this a bad approach to solving the problem in the first place? I’m looking for something I can just drag-and-drop into the UWorld, which will enforce data validation when that object is saved. But I don’t want it to start with suggested settings, I want it to be empty.

Side Note:

In principle, one could just make some acceptable default value for m_pFoo. However, if I did that, then, when these objects are placed in the UWorld, it would be easy to save them with the default value only—circumventing the use of the DataValdiation system. So, although I can imagine answers of this type, I don’t think it’s an answer I’d be able to use in my circumstance.

I would appreciate any help y’all could offer. Thank you!

#pragma

#include <GameFramework/Actor.h>
#include <Misc/DataValidation.h>

#include <MyGame/Foo.h>


UCLASS()
class MYGAME_API AMyClass : public AActor
{
	GENERATED_BODY()
public:
	AMyClass(){}

private:
	TWeakObjectPtr<AFoo> m_pFoo;

public:

#ifdef WITH_EDITOR
	virtual EDataValidationResult IsDataValid(class FDataValidationContext& context) const
	{
		const bool isClassDefaultObject = GetDefault<AMyClass>() == this;
		const bool isEditorContentObject = GetName().StartsWith(TEXT("Default__")) && !ActorGuid.IsValid();

		ensureMsgf(isObjectWithoutUWorld && isClassDefaultObject, TEXT("These are not both true"));

		const bool isPtrToFooValid = m_pFoo.IsValid();

		if(isEditorContentObject)
		{
			// if it's the EditorContent object, there's no valid setting for m_pFoo, so we want it to be nullptr
			return isPtrToFooValid ? EDataValidationResult::Invalid : EDataValidationResult::Valid;
		}
		else
		{
			// if it's not an EditorContent object, then it should be in a UWorld.  In a UWorld, m_pFoo must NOT be nullptr
			return isPtrToFooValid ? EDataValidationResult::Valid : EDataValidationResult::Invalid;
		}

	}
#endif
}

In this case, you should still compare against the CDO, but you don’t do that by using GetDefault.

instead, this is the comparison:

const bool isCDO = this == GetClass()->GetDefaultObject<AMyClass>();