Commit 2dcf851
Fix stale subsystem model_num references across mission boundaries (#3147)
Fixes the long-standing intermittent bug where the GTD Orion#Repulse in
Silent Threat: Reborn's "Forced Hand" mission spawns with all subsystems
unlinked, leaving the ship untargetable and undisableable. The same root
cause has been reported since at least 2015 (HLP topic 89403) and was
provisionally closed in #3147 as fixed by #3858, but #3858 was an unrelated
parse_ship_values memory leak fix and never actually addressed this
behavior. Recently the underlying check at ship.cpp ~8043 was promoted
from Warning to Error (#5591), which is why Release players are now
hitting a hard error dialog instead of just seeing missing subsystems.
Root cause
----------
model_unload() walks Ship_info and resets si.model_num = -1 on every
ship_info that referenced the model, but does not touch
si.subsystems[k].model_num. Subsystem entries are left pointing at the
freed model id. When a subsequent mission reloads the same pof under a
new id, ship variants that share the model rely on
ship_copy_subsystem_fixup() to re-link their subsystem arrays, but the
fixup matches by name via model_copy_subsystems() and any unmatched
destination subsystem stays at -1. The mismatch case in
model_copy_subsystems() was guarded only by Int3(), which is a no-op in
Release builds, so the failure was completely silent until subsys_set()
errored out much later with no useful context.
The bug is intermittent because it depends on three independent ordering
factors: which prior mission was last played, which Orion variant that
mission used, and the iteration order of Ship_info during the next
mission's ship_page_in().
Changes
-------
* model_unload(): when clearing si.model_num for an unloaded model, also
clear every si.subsystems[k].model_num that pointed at the same id.
This is the structural fix and eliminates the entire class of stale
subsystem pointers surviving across mission boundaries.
* model_copy_subsystems(): replace the silent Int3() in the name-mismatch
branch with Error(LOCATION, ...) naming the unmatched subsystem. This
surfaces a failure mode that has been invisible in Release builds for
over a decade and gives modders an actionable diagnostic when sibling
ship classes that share a model have inconsistent subsystem names.
* ship.cpp: introduce verify_ship_subsystems_linked() helper that checks
every model_subsystem on a ship_info is linked into the same model the
ship is using, and fails with Error(LOCATION, ...) naming both
polymodels if not. This is the canonical post-condition for both
ship_copy_subsystem_fixup() and model_load() (which is supposed to
link subsystems via do_new_subsystem()).
* ship_create(): call verify_ship_subsystems_linked() after
ship_copy_subsystem_fixup() so that any failure to link a subsystem
is surfaced with full context (ship name, subsystem name, both model
filenames) instead of propagating to a generic error in subsys_set()
much later. This closes a long-standing asymmetry where ship_page_in()
had a guard (added by zookeeper in 2015) but ship_create() did not.
* ship_page_in(): three post-fixup verifications were gated by
#ifndef NDEBUG with Warning() or Assertion() calls, both of which are
no-ops in Release builds. Replace each with a verify_ship_subsystems_linked()
call so they fire in Release as well.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>1 parent 219f692 commit 2dcf851
2 files changed
Lines changed: 44 additions & 25 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
277 | 277 | | |
278 | 278 | | |
279 | 279 | | |
| 280 | + | |
| 281 | + | |
| 282 | + | |
| 283 | + | |
| 284 | + | |
| 285 | + | |
| 286 | + | |
| 287 | + | |
| 288 | + | |
280 | 289 | | |
281 | 290 | | |
282 | 291 | | |
| |||
592 | 601 | | |
593 | 602 | | |
594 | 603 | | |
595 | | - | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
596 | 609 | | |
597 | 610 | | |
598 | 611 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
7917 | 7917 | | |
7918 | 7918 | | |
7919 | 7919 | | |
| 7920 | + | |
| 7921 | + | |
| 7922 | + | |
| 7923 | + | |
| 7924 | + | |
| 7925 | + | |
| 7926 | + | |
| 7927 | + | |
| 7928 | + | |
| 7929 | + | |
| 7930 | + | |
| 7931 | + | |
| 7932 | + | |
| 7933 | + | |
| 7934 | + | |
| 7935 | + | |
| 7936 | + | |
| 7937 | + | |
| 7938 | + | |
| 7939 | + | |
| 7940 | + | |
| 7941 | + | |
| 7942 | + | |
| 7943 | + | |
7920 | 7944 | | |
7921 | 7945 | | |
7922 | 7946 | | |
| |||
11504 | 11528 | | |
11505 | 11529 | | |
11506 | 11530 | | |
| 11531 | + | |
| 11532 | + | |
11507 | 11533 | | |
11508 | 11534 | | |
11509 | 11535 | | |
| |||
19139 | 19165 | | |
19140 | 19166 | | |
19141 | 19167 | | |
19142 | | - | |
19143 | | - | |
19144 | | - | |
19145 | | - | |
19146 | | - | |
19147 | | - | |
19148 | | - | |
19149 | | - | |
19150 | | - | |
19151 | | - | |
| 19168 | + | |
19152 | 19169 | | |
19153 | 19170 | | |
19154 | 19171 | | |
19155 | 19172 | | |
19156 | 19173 | | |
19157 | | - | |
19158 | | - | |
19159 | | - | |
19160 | | - | |
19161 | | - | |
19162 | | - | |
19163 | | - | |
19164 | | - | |
19165 | | - | |
| 19174 | + | |
19166 | 19175 | | |
19167 | 19176 | | |
19168 | 19177 | | |
19169 | 19178 | | |
19170 | 19179 | | |
19171 | 19180 | | |
19172 | 19181 | | |
19173 | | - | |
19174 | | - | |
19175 | | - | |
19176 | | - | |
19177 | | - | |
| 19182 | + | |
| 19183 | + | |
19178 | 19184 | | |
19179 | 19185 | | |
19180 | 19186 | | |
| |||
0 commit comments