-
Notifications
You must be signed in to change notification settings - Fork 0
Rubric Validation
Rubric never fails a load because a value is invalid. Instead, invalid values are corrected — replaced with a suggested or default value — and the correction is logged on LoadResult.corrections(). This is the "correct-and-log" model.
The alternatives are worse:
- Reject the load and drop the user into a broken game state. Users hate this. Mod authors hate this.
- Reject the entry and use its default but silently. Now the user's config file has a value they can't tell was ignored.
Correct-and-log picks up the pieces without losing information: the running config is always internally consistent (guaranteed valid), and every fix is recorded for the mod author to surface.
Rubric loads and applies corrections. What you do with them is up to you.
Every constraint annotation attaches a validator:
| Annotation | Behavior on invalid |
|---|---|
@Range |
Clamps to [min, max]. |
@OneOf |
Resets to the entry's default value. |
@Pattern |
Resets to default. |
@Length |
Truncates over-long collections; resets over-long strings to default. |
@NotNull |
Resets to default. |
Implement com.oliveryasuna.mc.rubric.validation.Validator<T>:
public interface Validator<T> {
ValidationResult validate(T value);
default ValidationResult validateRaw(Object value) {
try {
@SuppressWarnings("unchecked")
final T typed = (T) value;
return validate(typed);
} catch(ClassCastException e) {
return ValidationResult.invalid(ValidationIssue.of("type mismatch"));
}
}
}Return ValidationResult.ok() for good values. Return ValidationResult.invalid(ValidationIssue.corrected("message", replacement)) when the value should be corrected to a specific replacement, or ValidationResult.invalid(ValidationIssue.of("message")) when the entry should be reset to its default.
-
Annotation configs: write a matching annotation and update
AnnotationSchemaReaderto attach your validator. (Or, more commonly, wrap your logic into@Pattern's regex,@Length's bounds, etc.) -
Builder configs: call
.validator(new MyValidator())on theConfigSpec.EntryBuilder.
LoadResult.corrections() returns a List<Correction>. Each entry has:
-
path— dotted path of the corrected value. -
rejected— the value as read from the file. -
replacement— what Rubric substituted. -
reason— the message from the offending validator.
Typical usage: log every correction on manager.load(), or surface them in a toast/notification on first world load.
final LoadResult result = manager.load();
for(final Correction c : result.corrections()) {
LOGGER.warn("[mymod] corrected {}: {} -> {} ({})",
c.path(), c.rejected(), c.replacement(), c.reason());
}Corrections cause a write-back on load — the file is rewritten with the corrected values so the next boot doesn't re-log the same problem.
Some mod packs and CI environments want a strict mode: any correction = load failed = boot aborted. That's the intent of the validation.strict knob in Self-config. When set, Rubric will (once wired) throw on non-empty corrections instead of applying them.
Similarly, validation.dumpCorrections (once wired) writes a sibling <config>.corrections.log on every load with corrections, for post-hoc inspection.
Both are placeholders in the current release — the flags exist, the enforcement doesn't yet. Follow the repo issues for progress.
For the curious: Corrector.correct(Schema, instance) walks every SchemaEntry, runs each attached Validator against the value read from the instance, and — on invalid — writes back either the first suggested replacement or the entry's defaultValue. It always returns a list of Corrections and never throws — a faulty validator that raises is treated as a hard failure for that entry and the default is used.
- Annotations — the annotation set includes every built-in validator.
-
Concepts —
Validator,ValidationResult,ValidationIssue,Correction.
Rubric
Reference
Runtime
GUI
Help