Skip to content

release: v1.3.15-alpha — Cast All gate + stop-after-reload crash - #39

Merged
Chrizhermann merged 1 commit into
mainfrom
fix/stop-after-reload-crash
Apr 30, 2026
Merged

release: v1.3.15-alpha — Cast All gate + stop-after-reload crash#39
Chrizhermann merged 1 commit into
mainfrom
fix/stop-after-reload-crash

Conversation

@Chrizhermann

Copy link
Copy Markdown
Owner

Summary

  • Cast All gate: Cast All is no longer greyed out when the currently-selected character has no spells in the active preset; the new BfBot.UI._CanCastAll() walks every portrait slot, matching the cross-party scope of BuildQueueFromPreset. Cast Character keeps the original char-scoped gate.
  • Stop-after-reload crash (Crash: pressing Stop after reloading a save mid-cast #38, reported by sov_): pressing Stop after reloading a save mid-cast no longer crashes. The cleanup loops in BfBot.Exec.Stop and BfBot.Exec._Complete now re-resolve the caster sprite from the live portrait slot instead of dereferencing the cached caster.sprite, which holds a freed CGameSprite pointer post-reload (and pcall doesn't catch C++ access violations). BfBot.Exec._SafetyTick proactively detects party-composition changes via cached-name vs live-portrait-name comparison and hard-resets execution state, so the user doesn't have to press Stop to unstick the UI.

Closes #38.

Why name-based stale detection (not userdata ==)

Empirically verified in this branch: two consecutive calls to EEex_Sprite_GetInPortrait(0) return distinct userdata wrappers, and the __eq metamethod evaluates them as not-equal even when they point at the same CGameSprite. Sprite identity is unreliable; name comparison + portrait re-resolution is robust.

Test Plan

🤖 Generated with Claude Code

Cast All gate
- Cast All was greyed out whenever the currently-selected character had
  no spells in the active preset, even when other party members had
  spells in the same preset. Cast Character was correctly char-scoped.
- New BfBot.UI._CanCastAll() walks every portrait slot and matches the
  cross-party scope of BuildQueueFromPreset; the bbCast button now
  uses _CanCastAll while bbCastChar keeps the char-scoped _CanCast.

Stop-after-reload crash (#38, reported by sov_)
- After reloading a save mid-cast, BfBot.Exec._casters[].sprite still
  held freed CGameSprite pointers from the pre-reload party; pressing
  Stop dereferenced them via EEex_Action_QueueResponseStringOnAIBase,
  which segfaults at the engine level. pcall does NOT catch C++ access
  violations.
- Stop() and _Complete() now re-resolve the caster sprite from the
  current portrait slot in their cleanup loops, so they never deref
  the freed pointer. BFBTCR is a no-op on targets without an active
  BFBTCH effect, so the cleanup is safe even when the slot now holds
  a different character.
- BfBot.Exec._HardReset clears state without touching cached sprites.
- BfBot.Exec._IsStateStale compares cached caster names against the
  live portrait sprites and proactively hard-resets execution state
  from _SafetyTick when party composition changed across the reload,
  so Cast / Cast Character re-enable themselves on the next safety
  tick instead of leaving the user stuck pressing Stop.
- Userdata equality on EEex_Sprite_GetInPortrait is unreliable: two
  consecutive calls return distinct userdata wrappers and the __eq
  metamethod evaluates them as not equal even when they point at the
  same CGameSprite. The implementation deliberately avoids sprite
  identity in favor of name comparison + portrait re-resolution.

Tests
- New BfBot.Test.StaleState phase covers _HardReset, _IsStateStale,
  Stop / _Complete / _SafetyTick stale recovery, and Stop's
  re-resolve safety on a same-save reload (8 assertions, all pass).

Versions
- BfBot.VERSION 1.3.14-alpha → 1.3.15-alpha
- setup-buffbot.tp2 VERSION ~v1.3.14-alpha~ → ~v1.3.15-alpha~

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@Chrizhermann
Chrizhermann merged commit df49b32 into main Apr 30, 2026
1 check passed
@Chrizhermann
Chrizhermann deleted the fix/stop-after-reload-crash branch April 30, 2026 10:23
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.

Crash: pressing Stop after reloading a save mid-cast

1 participant