Skip to content
Validation
On this page

Validation

How the SDK checks a level against the format's rules, repairs what can be repaired and reports what cannot

You call validation yourself. It is not built into saving or loading

The game decides where to run it. Opening a level in the editor reports problems, and playback skips the check. So an author waits for validation, a player never does

A level author meets validation when offering a level to a service. More - Publish profiles

How to call it

ValidationFacade is the one entry point. A caller does not need to know how many passes there are:

var facade = new ValidationFacade();
var report = facade.Validate(level);

if (report.HasErrors)
    Console.WriteLine(report);
MethodWhat it does
Validate(root, settings)checks any root: Level, LevelMeta, UserSettings, a standalone Prefab or EffectData. The graph pass runs for a Level only
ValidateAndFix(root, analyzerSettings, fixerSettings)repairs what can be repaired, then reports what is left
ValidateForPublish(meta, profile, level, now, payload, settings)all three passes at once, repairs nothing. level may be null to grade the metadata alone

ValidationReport holds RuleIssues and GraphIssues:

  • IsValid - no findings at all
  • HasErrors - at least one finding of the Error group

Severity

Every rule belongs to a RuleGroup:

  • Error - the file cannot be played as written
  • Warning - it plays, but not as authored (content that never appears, a broken reference taking a fallback). Or the only repair is destructive
  • Advice - playback does not change

Three passes

PassWhat it asksRuns on
RuleAnalyzeris each value in its range, not null, a known enum memberany root
LevelGraphAnalyzerdo the objects agree with each otherLevel only
PublishReadinessAnalyzermay the level be handed to strangers under a given profileLevelMeta, optionally Level

Rules

A type joins the check with the [RuleContainer] marker. Its properties carry rule attributes:

  • [RuleNotNull], [RuleInRange], [RuleMinValue], [RuleEnumValid], [RuleCollectionMaxCount] and others
  • [RuleEnumFlagsValid] - for flag enums
  • [RuleOptional] - for a member that may be absent

The limits themselves are constants in the Rules/ tables: FrameRules, ValueRules, LevelRules and others. For example, LevelRules.MaxObjects is 262 144

The walk over all rule containers is generated by BH.SDK.Roslyn, with no reflection. That roughly halves its time on large levels

Fixers

Every rule attribute knows how to repair its own value (Fix). RuleFixer applies repairs in reverse order of the trace: a repair can shift deeper findings. ValidateAndFix repeats the pass until the result stops changing

The graph pass runs after the repairs. A repair can itself create a graph problem, for example a new id colliding with an existing one

Graph findings are never repaired: DuplicateObjectId, MissingParent, ParentCycle, PrefabRemapBroken, IdCounterNearExhaustion and the rest. Each repair of that kind is a content decision (which of two objects keeps its id). A guess would rewrite the level

Caution

ValidateAndFix changes the object you pass in. Run it on a copy when you need the original, for example to show the author what exactly was changed