Skip to content
Documentation
On this page

Validation

You call validation yourself through ValidationFacade, it is not built into saving and loading. Findings are split into Error, Warning and Advice, and ValidateAndFix repairs what it can

Where to run it is up to the game. Opening a level in the editor reports problems, while playback skips the check. So the author waits for validation, and the 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 single entry point. The caller does not need to know how many passes exist:

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 only for Level
ValidateAndFix(root, analyzerSettings, fixerSettings)repairs what it can and reports what is left
ValidateForPublish(meta, profile, level, now, payload, settings)all three passes at once, repairs nothing. level can be null to assess the metadata only

ValidationReport holds RuleIssues and GraphIssues:

  • IsValid - there are no findings at all
  • HasErrors - there is at least one finding in the Error group

Severity

Every rule belongs to a RuleGroup:

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

Three passes

PassWhat it asksWhat it works on
RuleAnalyzerwhether every value is in its range, not null, a known enum memberany root
LevelGraphAnalyzerwhether the objects agree with each otherLevel only
PublishReadinessAnalyzerwhether the level can be given to strangers under the given profileLevelMeta, optionally Level

Rules

A type is included in the check by the [RuleContainer] marker. Its properties carry rule attributes:

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

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, without reflection. On large levels this cuts its time roughly in half

Repairs

Every rule attribute can repair its own value (Fix). RuleFixer applies repairs in reverse trace order: 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 problem in the graph, for example a new id that matches an existing one

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

Caution

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