Skip to content

Spec Overrides: apply a NIL_SENT on a defaults-backed key as the registered default - #1250

Open
dfrisone wants to merge 2 commits into
EllesmereGaming:mainfrom
dfrisone:specov-nilsent-apply-default
Open

Spec Overrides: apply a NIL_SENT on a defaults-backed key as the registered default#1250
dfrisone wants to merge 2 commits into
EllesmereGaming:mainfrom
dfrisone:specov-nilsent-apply-default

Conversation

@dfrisone

@dfrisone dfrisone commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

The bug

Reported on a Devourer spec-override profile: set the class-resource (soul) bar to a custom colour, close the EUI settings window, reopen it, and the bar is back to the class resource colour. The stored custom colour is still there, it just never gets applied again.

Narrowed with the reporter:

  • Colour only. Size and other overridden settings on the same profile stay put.
  • The save works. The colour survives a /reload, and merely reopening the menu is not enough on its own.
  • The trigger is a Default-view round trip. Opening the settings window returns to the Default view (showing the class colour), and leaving it re-applies the override, at which point the colour is gone.

Root cause

Three facts combine, and none is a bug on its own.

1. The Custom swatch expressed its state by key REMOVAL. The class-resource Fill Color swatches wrote:

Custom         c.resourceColored = nil ; c.classColored = false  (+ fillR/G/B)
Class          c.resourceColored = nil ; c.classColored = true
Class Resource c.resourceColored = true; c.classColored = true

resourceColored was the only key in that trio written as = nil, and at runtime a truthy resourceColored takes precedence over classColored entirely.

2. A live nil harvests as NIL_SENT. So the spec map stored the custom-colour override as resourceColored = NIL_SENT, a key-removal marker.

3. The nil-poison guard made that marker unapplyable. Every apply site skipped writing a nil for any fkey that has a registered default, on the grounds that such markers are harvest residue. resourceColored has one (false).

The result is deterministic once values.default[resourceColored] is truthy, which auto-capture seeds by itself if the baseline was in Class Resource mode when the user first clicked Custom:

  • Open the panel. The Default view writes resourceColored = true live. The bar shows the resource colour.
  • Close the panel. The spec values are restored: classColored = false writes, fillR/G/B write, but the NIL_SENT for resourceColored hits the skip, so live keeps true. The precedence rule then shows the class resource colour with the custom RGB sitting unused.
  • It is permanent. Every later harvest sees live true equal to the recorded default true, so equality-retention keeps the marker in the spec map, and every later apply skips again.

The store is never corrupted. The override simply can never be re-applied.

The fix

At the nil-poison sites, a NIL_SENT on a defaults-backed key now applies the registered default value instead of being skipped.

That is byte-for-byte the live state a logout/login round trip already produces: SavedVariables drop nils, and the login defaults merge refills the registered default. The reporter confirmed a /reload renders the colour correctly, which is the same path. Existing poisoned profiles therefore self-heal on the first login with this build, with no migration.

Deleting the guard outright was not an option: it exists to stop an imported NIL_SENT stripping keys that consumers read raw (the new-character SetFont crash). Writing the default value keeps that protection while making the marker applyable.

Supporting changes:

  • HasRegisteredDefault becomes ReadRegisteredDefault, returning the value rather than a boolean, and is used at all five apply sites (WriteSpecValues, WriteDefaultValues, both conditional-overlay sites, and the unlock apply).
  • WriteDefaultValues gained the same table guards its siblings already had.
  • The two swatches now write resourceColored = false rather than = nil, so removals stop being minted in the first place. The runtime tests truthiness, so false and nil are behaviourally identical there.

Scope

Two files, +52/−36. No new events, no timers, no API calls added. The behaviour change is confined to what an apply does with a stored key-removal marker on a key that has a registered default, which previously did nothing at all.

Testing

Not yet confirmed in game by the original reporter. The diagnosis is a code read, re-verified against current main after this branch was brought up to date: the resourceColored = nil idiom is gone from the tree, the registered default is still false, and all five apply sites route through ReadRegisteredDefault.

…istered default

A stored key-removal marker (NIL_SENT) for a key with a registered default
was skipped at every apply site, so whatever value sat live could never be
cleared by an override restore. The class-resource bar's Custom fill mode
writes resourceColored = nil, the override stores NIL_SENT for it, and after
the Default Editing Mode view wrote the recorded default (true) live, the
skip left it there forever: resourceColored takes precedence over
classColored, so the spec's custom colour rendered as the class resource
colour after any settings open/close round trip.

Replace HasRegisteredDefault with ReadRegisteredDefault and substitute the
registered default value for the marker at all five apply sites, which is
the exact live state a logout/login produces (saved vars drop nils, the
defaults merge refills). Existing poisoned stores heal on their next apply
with no migration. WriteDefaultValues also gains the same table guards as
its sibling sites. The swatches now store false instead of removing the key
so new edits stay plain scalars.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant