Skip to content

fix: guard secret unit frame values and optimize portrait fallback#1830

Closed
mjodheim wants to merge 11 commits into
tukui-org:mainfrom
mjodheim:fix/secret-values-and-portrait-fallback
Closed

fix: guard secret unit frame values and optimize portrait fallback#1830
mjodheim wants to merge 11 commits into
tukui-org:mainfrom
mjodheim:fix/secret-values-and-portrait-fallback

Conversation

@mjodheim

@mjodheim mjodheim commented May 22, 2026

Copy link
Copy Markdown
Contributor

Problem

In WoW Midnight, unit-frame values (such as GUIDs, class info, item levels, nameplate variables, etc.) can be secret or temporarily unavailable in instanced content, which led to:

  1. 3D Portraits failing to load or crashing for secret/protected target units.
  2. Aura Highlight and Range fading crashing or timing out on nil or secret values (producing "script ran too long" errors).
  3. Pet Bar spell data callbacks being registered repeatedly during pet updates, which could fill Blizzard's async callback system and produce a "script ran too long" error from AsyncCallbackSystem.lua.

Resolution

This PR addresses all previous maintainer feedback from PR #1826 and PR #1829:

  1. Pristine stock oUF (Objection 1 Resolved):

    • We reverted all changes to the stock oUF library core (ElvUI_Libraries/Game/Shared/oUF/elements/portrait.lua), keeping the library 100% pristine and untouched.
    • Instead, we cleanly relocated all the Midnight-specific secret checks, 2D fallback textures, and asynchronous timer retries directly to the ElvUI side using oUF's standard Override hook inside ElvUI/Game/Shared/Modules/UnitFrames/Elements/Portrait.lua (portrait.Override = UF.PortraitOverride).
    • This keeps the stock oUF library completely stock, and houses ElvUI's internal secret checking API (E:IsSecretValue) cleanly within ElvUI.
  2. Optimized Aura Highlight iteration (Objection 2 Resolved):

    • We removed the arbitrary checked > 40 loop count limit from oUF_AuraHighlight.lua. The looper now executes naturally and efficiently on the cached filtered list.
  3. Range Checks Guard:

    • Added safety guards to ElvUI/Game/Shared/Modules/UnitFrames/Elements/Range.lua to safely handle nil ranges or lists when values are temporarily unavailable.
  4. Pet Bar callback guard:

    • Pet action spell load callbacks are now registered only when a button's spellID changes, with stale callbacks cancelled when the spell changes, disappears, or the bar hides.

mjodheim and others added 11 commits May 21, 2026 00:35
…p hooks

Removed EmbeddedItemTooltip_SetItemByQuestReward and
EmbeddedItemTooltip_SetSpellByQuestReward hooks. In WoW Midnight (12.x),
EmbeddedItemTooltip_UpdateSize receives secret frame sizes; running that
code path through ElvUI's hooked execution taints arithmetic and causes
SetWidth to reject the secret value.

Also reverts a previous workaround (GameTooltip_AddWidgetSet wrapper via
securecallfunction) that was applied only to installed files. That wrapper
was counterproductive: replacing the global with a tainted closure taints
the call even when Blizzard's untainted AreaPoiUtil.lua is the caller,
causing the very crash it was meant to prevent.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ust model retry

The HasPortraitModel guard (secretGUID and not HasPortraitModel) was too
conservative: when switching targets, the previous unit's 3D model stayed
loaded, so HasPortraitModel returned true and the 2D fallback was never
shown — leaving a stale portrait from the prior target.

For secret-GUID units, always clear the model and show the 2D texture on
genuine unit changes (needsLoad=true). The OnModelLoaded hook and
RetryPortraitModel loop will dismiss the fallback once a 3D model
successfully streams in.

Also introduces RetryPortraitModel (ClearModel+SetUnit loop up to 10
attempts) and HidePortraitFallbackIfLoaded to make 3D model loading more
reliable when streaming is slow, and cleans up retry state on GUID change
and on the unavailable (question-mark) path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
SetUnit("target") for a non-grouped instance NPC with a secret GUID
loads a stale cached model from Midnight's model cache (typically the
previous target's portrait). This makes the old 3D portrait persist and
hides the 2D fallback, despite ClearModel having been called.

The 2D texture fallback is the correct and permanent solution for secret-
GUID units. Split the needsLoad path: secret-GUID units get ClearModel
+ 2D fallback only; non-secret units get SetUnit + RetryPortraitModel.
Also call HidePortraitFallback before SetUnit on the non-secret path so
a stale 2D texture from a previous secret unit is cleared immediately.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ction

Instead of permanently skipping SetUnit for secret-GUID units (non-grouped
instance NPCs), show a 2D cover frame immediately and simultaneously try
to load the NPC's 3D model. If the model file ID changes after SetUnit,
the real NPC model streamed in — hide the cover and show 3D. If the ID
stays the same (stale cache hit) or no OnModelLoaded fires, the 2D cover
stays visible, masking any wrong model.

Key design decisions:
- Fallback2D is now a child Frame (frame level +5) placed above the
  PlayerModel, so it renders on top of any stale 3D content regardless
  of draw layers.
- SetUnit is called WITHOUT ClearModel to preserve the current model as
  a placeholder while the NPC model streams asynchronously.
- secretPreloadModelID snapshots the file ID before SetUnit so
  OnModelLoadedCheck can distinguish a real new load from a stale hit.
- needsLoad excludes needsModel and newGUID for secret-GUID units since
  secret values are always unequal; genuine triggers remain (prevState,
  OnShow, non-noUnitChange events) to avoid OnUpdate storm.
- hasSecretUnit is set at function entry so all helpers (including
  HidePortraitFallbackIfLoaded) skip their hide logic for secret units.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mjodheim

mjodheim commented Jun 6, 2026

Copy link
Copy Markdown
Contributor Author

Superseded by smaller, focused PRs rebased on current main: #1843 (secret-value taint: fade alpha, raid frames, menu skin, quest tooltips), #1844 (2D portrait fallback, draft), and #1845 (nil-safe aura/range iteration + pet spell-load caching). This branch had drifted ~157 commits behind main; closing in favor of those.

@mjodheim mjodheim closed this Jun 6, 2026
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