Adding an Editor-Only Validator Project in Unreal Engine 5

by | Oct 16, 2025 | Programming, Tech, Unreal Engine

Sometimes you need an automatic solution for sanity-checking your project content. In our case, we wanted a quick way to make sure none of our maps accidentally included a specific level in their sub-level list — a simple rule, but one that can quietly break builds if missed. It sounds like a small thing, but having it run on a build machine and inside the editor saves hours of debugging “why is the game behaving in a super weird way?” problems.


The Need

We had game-specific C++ code, but no editor tooling yet.
When you start adding automated checks, the first instinct is just to drop the code into your existing game module. Unfortunately, that doesn’t work here — the Data Validation system that Unreal uses for asset checks lives entirely in editor modules (UnrealEd), which aren’t included in packaged games.

That meant one thing: we needed a separate editor-only module (or project) that sits alongside our game code but never ships with the build.


The Validator

Unreal’s Data Validation framework is built around subclasses of UEditorValidatorBase. Each validator tells the editor what kind of asset it can handle and how to check it. Here’s the stripped-down version of ours:

#include "EditorValidatorBase.h"
#include "Engine/World.h"
#include "Engine/LevelStreaming.h"
#include "LevelInstance/LevelInstanceActor.h"
#include "MyForbiddenLevelValidator.generated.h"

UCLASS()
class UMyForbiddenLevelValidator : public UEditorValidatorBase
{
    GENERATED_BODY()
public:
    UMyForbiddenLevelValidator()
    {
        bIsEnabled = true;
        ForbiddenLevel = FSoftObjectPath(TEXT("/Game/Maps/Forbidden.Forbidden"));
    }

    UPROPERTY(EditAnywhere, Category="Validation")
    FSoftObjectPath ForbiddenLevel;

    virtual bool CanValidateAsset_Implementation(const FAssetData& InAssetData, UObject*, FDataValidationContext&) const override
    {
        return InAssetData.AssetClassPath == UWorld::StaticClass()->GetClassPathName();
    }

    virtual EDataValidationResult ValidateLoadedAsset_Implementation(const FAssetData&, UObject* InAsset, FDataValidationContext& Context) override
    {
        const UWorld* World = Cast<UWorld>(InAsset);
        if (!World) return EDataValidationResult::NotValidated;

        const FString ForbiddenPkg = ForbiddenLevel.ToString();
        for (const ULevelStreaming* Streaming : World->GetStreamingLevels())
        {
            if (Streaming && Streaming->GetWorldAssetPackageName() == ForbiddenPkg)
            {
                Context.AddError(FText::FromString(TEXT("Level includes forbidden sub-level.")));
                return EDataValidationResult::Invalid;
            }
        }
        return EDataValidationResult::Valid;
    }
};
Code language: C++ (cpp)

When the validator runs, it looks at every map, walks through its streaming levels, and fails if it finds the forbidden one.


Making It Editor-Only

This is where many small teams trip up. If you try to put that class in your main game module, Unreal will complain that UnrealEd headers don’t exist in non-editor builds. The fix is to give it its own module (or even a lightweight Editor project).

Create a folder

Source/
  MyGame/
  MyGameEditor/

Add to the .uproject

"Modules": [
  { "Name": "MyGame", "Type": "Runtime" },
  { "Name": "MyGameEditor", "Type": "Editor" }
]
Code language: JSON / JSON with Comments (json)

Build rules (MyGameEditor.Build.cs)

public class MyGameEditor : ModuleRules
{
    public MyGameEditor(ReadOnlyTargetRules Target) : base(Target)
    {
        Type = ModuleType.Editor;
        PrivateDependencyModuleNames.AddRange(new[]
        {
            "Core", "CoreUObject", "Engine",
            "UnrealEd", "EditorFramework", "AssetRegistry",
            "MyGame"
        });
    }
}
Code language: C# (cs)

Regenerate the sln file

  • Right-click on your .uproject file.
  • Choose “Generate Visual Studio project files.”
  • Wait a few seconds — UnrealBuildTool will recreate the .sln, .vcxproj, and module files.

There are other ways to do this (command line tools, editor), but this one worked for me.

Now everything in the MyGameEditor module only compiles into the editor build. When you cook or ship the game, Unreal automatically strips it out.

Running It

In the editor, right-click on a level and select Asset Actions → Validate
If the map is invalid, the Message Log window’s Asset Check sections and the Log will show the error.

On a build machine, you can run the same thing headlessly:

UnrealEditor-Cmd.exe MyGame.uproject -run=ValidateAssets -ProjectContentOnly -ValidationFlags=Load -unattended -NullRHI

The command exits with a non-zero code if any asset failed validation — perfect for CI.


Why Bother?

For indie or small studio teams, putting checks like this in place pays off quickly:

  • You catch content mistakes before they reach QA or builds.
  • Designers and artists can run validation without opening source files.
  • The same ruleset runs locally and in automation.

And you don’t have to pollute your runtime codebase — all editor logic stays cleanly separated.


TL;DR

  • Need: stop a forbidden level from being referenced anywhere.
  • Solution: a UEditorValidatorBase subclass.
  • Placement: its own Editor module, not your main game code.
  • Execution: through Unreal’s Data Validation UI or command line.

Related posts