Skip to content

Keep saves loadable when a weapon without a view GUI queues events - #193

Open
akacross wants to merge 1 commit into
themuffinator:mainfrom
akacross:fix/weapon-gui-event-backlog
Open

akacross wants to merge 1 commit into
themuffinator:mainfrom
akacross:fix/weapon-gui-event-backlog

Conversation

@akacross

Copy link
Copy Markdown
Contributor

Addresses #192: rvViewWeapon::Restore: invalid pending GUI event count 372 makes a save impossible to load.

Cause

rvWeapon posts named events (weapon_init, weapon_ammo, weapon_lower, weapon_raise, ...) to the shared view model on every shot and weapon switch, and rvWeapon::UpdateGUI hands them to the weapon's view GUI. The blaster, gauntlet, grenade launcher, lightning gun and dark matter gun have no view GUI (no gui key in their defs), so UpdateGUI returned early and the queue was never drained. It kept growing for as long as one of those weapons was in hand, and rvViewWeapon::Save wrote all of it into every save. Since c2b4a7a the restore rejects more than 256 entries (MAX_SAVEGAME_GUI_EVENTS), so the same build refuses a save it has just written.

Fix

  • UpdateGUI drops queued events when the view model has no GUI to take them. Nothing could ever receive them, and the next weapon with a GUI posts its own weapon_init and weapon_ammo. A holstered weapon with a GUI still keeps its events for the raise, as before.
  • rvViewWeapon::Restore bounds the count by the bytes left in the save (each event is at least its 4-byte length), via a new idRestoreGame::RemainingBytes(), instead of the fixed cap. Existing saves carrying a backlog load again. Negative counts and counts the file cannot hold are still rejected, and each string is still length-checked by ReadString.
  • The write side and save layout are unchanged. The now-unused MAX_SAVEGAME_GUI_EVENTS is removed. SP and MP are identical.

Verification (Linux x64, GCC 16, OpenGL)

Scripted on game/airdefense2 with devmap, give all and openq4_testInput: alternate between the grenade launcher and the blaster, firing each, for 70 rounds, then save.

Build Events in the save Loading that save
main (cbe967f2) 996 fails: rvViewWeapon::Restore: invalid pending GUI event count 996
this branch, same save n/a restores; a fresh save made after the load holds 0 queued events
this branch, same script 0 restores

tools/tests/weapon_gui_event_queue_contract.py (wired into both workflows and openq4_validate.py) checks the drop, the restore bound against a small model (372 and 996 accepted, negative and impossible counts rejected), the unchanged save writer and SP/MP parity. Reverting the drop makes it fail. savegame_corruption_contract.py, savegame_v3_contract.py, savegame_pointer_width_safety.py and validation_hardening.py pass.

Not verified: the reporter's own save (none attached), and a full playthrough. The reporter mentions it usually follows loading after a death. That fits the mechanism (any long stretch on a GUI-less weapon), but I haven't confirmed that specific sequence.

🤖 Generated with Claude Code

The blaster, gauntlet, grenade launcher, lightning gun and dark matter gun
have no view GUI, so the events rvWeapon posts on every shot and weapon
switch were never delivered: the shared view model's queue grew for as long
as one of them was in hand and was written into every save. The 256-event
restore cap then rejected saves the same build had just written (themuffinator#192,
"rvViewWeapon::Restore: invalid pending GUI event count 372").

UpdateGUI now drops queued events when the view model has no GUI to take
them; the next weapon with a GUI posts its own weapon_init. A holstered
weapon with a GUI still keeps its events for the raise. The restore bounds
the count by the bytes left in the save, each event being at least its
length, so existing saves carrying such a backlog load again while corrupt
counts are still rejected. The write side and save layout are unchanged.

weapon_gui_event_queue_contract.py checks both halves and SP/MP parity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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