Skip to content

Commit ced4fd3

Browse files
authored
Merge pull request scp-fs2open#7370 from Goober5000/fix/repulse
Fix stale subsystem model_num references across mission boundaries
2 parents 1c25ed7 + 2dcf851 commit ced4fd3

2 files changed

Lines changed: 44 additions & 25 deletions

File tree

code/model/modelread.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -277,6 +277,15 @@ void model_unload(int modelnum, int force)
277277
for (auto& si : Ship_info) {
278278
if (pm->id == si.model_num) {
279279
si.model_num = -1;
280+
281+
// also reset any subsystem model_num references that pointed to this model,
282+
// otherwise stale ids can survive across missions and cause subsystems to fail
283+
// to re-link when the model is reloaded with a different id.
284+
for (int k = 0; k < si.n_subsystems; k++) {
285+
if (si.subsystems[k].model_num == pm->id) {
286+
si.subsystems[k].model_num = -1;
287+
}
288+
}
280289
}
281290

282291
if (pm->id == si.cockpit_model_num) {
@@ -592,7 +601,11 @@ void model_copy_subsystems( int n_subsystems, model_subsystem *d_sp, model_subsy
592601
}
593602
}
594603
if ( j == n_subsystems )
595-
Int3(); // get allender -- something is amiss with models
604+
Error(LOCATION, "Subsystem '%s' could not be matched between two ship classes that share a model. "
605+
"The destination ship will be missing this subsystem at runtime. "
606+
"Check that subsystem names are spelled identically on both ship classes, "
607+
"and that any modular table extensions to one ship are mirrored on the other.",
608+
source->subobj_name);
596609

597610
}
598611
}

code/ship/ship.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -7917,6 +7917,30 @@ static void ship_copy_subsystem_fixup(ship_info *sip)
79177917

79187918
}
79197919

7920+
// Verify that every model_subsystem on this ship_info is linked into the same
7921+
// model the ship is using. If any subsystem still points at a different
7922+
// model_num, fail with a diagnostic naming both polymodels. This is the
7923+
// canonical post-condition check for both ship_copy_subsystem_fixup() and
7924+
// model_load() (which is supposed to link subsystems via do_new_subsystem()).
7925+
// context_label disambiguates the call site in the resulting Error text.
7926+
static void verify_ship_subsystems_linked(const ship_info *sip, const char *context_label)
7927+
{
7928+
for (int i = 0; i < sip->n_subsystems; i++) {
7929+
if (sip->subsystems[i].model_num == sip->model_num)
7930+
continue;
7931+
7932+
polymodel *sip_pm = (sip->model_num >= 0) ? model_get(sip->model_num) : nullptr;
7933+
polymodel *subsys_pm = (sip->subsystems[i].model_num >= 0) ? model_get(sip->subsystems[i].model_num) : nullptr;
7934+
Error(LOCATION, "%s, ship '%s' does not have subsystem '%s' linked into the model file, '%s'.\n\n(Ship_info model is '%s' and subsystem model is '%s'.)",
7935+
context_label,
7936+
sip->name,
7937+
sip->subsystems[i].subobj_name,
7938+
sip->pof_file,
7939+
(sip_pm != nullptr) ? sip_pm->filename : "NULL",
7940+
(subsys_pm != nullptr) ? subsys_pm->filename : "NULL");
7941+
}
7942+
}
7943+
79207944
// as with object, don't set next and prev to NULL because they keep the object on the free and used lists
79217945
void ship_subsys::clear()
79227946
{
@@ -11504,6 +11528,8 @@ int ship_create(matrix* orient, vec3d* pos, int ship_type, const char* ship_name
1150411528
polymodel *pm = model_get(sip->model_num);
1150511529

1150611530
ship_copy_subsystem_fixup(sip);
11531+
verify_ship_subsystems_linked(sip, "After ship_copy_subsystem_fixup in ship_create");
11532+
1150711533
show_ship_subsys_count();
1150811534

1150911535
if ( sip->num_detail_levels != pm->n_detail_levels )
@@ -19139,42 +19165,22 @@ void ship_page_in()
1913919165
sip->subsystems[j].model_num = -1;
1914019166

1914119167
ship_copy_subsystem_fixup(&(*sip));
19142-
19143-
#ifndef NDEBUG
19144-
for (j = 0; j < sip->n_subsystems; j++) {
19145-
if (sip->subsystems[j].model_num != sip->model_num) {
19146-
polymodel *sip_pm = (sip->model_num >= 0) ? model_get(sip->model_num) : NULL;
19147-
polymodel *subsys_pm = (sip->subsystems[j].model_num >= 0) ? model_get(sip->subsystems[j].model_num) : NULL;
19148-
Warning(LOCATION, "After ship_copy_subsystem_fixup, ship '%s' does not have subsystem '%s' linked into the model file, '%s'.\n\n(Ship_info model is '%s' and subsystem model is '%s'.)", sip->name, sip->subsystems[j].subobj_name, sip->pof_file, (sip_pm != NULL) ? sip_pm->filename : "NULL", (subsys_pm != NULL) ? subsys_pm->filename : "NULL");
19149-
}
19150-
}
19151-
#endif
19168+
verify_ship_subsystems_linked(&(*sip), "After ship_copy_subsystem_fixup in ship_page_in");
1915219169
} else {
1915319170
// Just to be safe (I mean to check that my code works...)
1915419171
Assert( sip->model_num >= 0 );
1915519172
Assert( sip->model_num == model_previously_loaded );
1915619173

19157-
#ifndef NDEBUG
19158-
for (j = 0; j < sip->n_subsystems; j++) {
19159-
if (sip->subsystems[j].model_num != sip->model_num) {
19160-
polymodel *sip_pm = (sip->model_num >= 0) ? model_get(sip->model_num) : NULL;
19161-
polymodel *subsys_pm = (sip->subsystems[j].model_num >= 0) ? model_get(sip->subsystems[j].model_num) : NULL;
19162-
Warning(LOCATION, "Without ship_copy_subsystem_fixup, ship '%s' does not have subsystem '%s' linked into the model file, '%s'.\n\n(Ship_info model is '%s' and subsystem model is '%s'.)", sip->name, sip->subsystems[j].subobj_name, sip->pof_file, (sip_pm != NULL) ? sip_pm->filename : "NULL", (subsys_pm != NULL) ? subsys_pm->filename : "NULL");
19163-
}
19164-
}
19165-
#endif
19174+
verify_ship_subsystems_linked(&(*sip), "Without ship_copy_subsystem_fixup in ship_page_in");
1916619175
}
1916719176
} else {
1916819177
// Model not loaded, so load it
1916919178
sip->model_num = model_load(sip->pof_file, &*sip);
1917019179

1917119180
Assert( sip->model_num >= 0 );
1917219181

19173-
#ifndef NDEBUG
19174-
// Verify that all the subsystem model numbers are updated
19175-
for (j = 0; j < sip->n_subsystems; j++)
19176-
Assertion( sip->subsystems[j].model_num == sip->model_num, "Model reference for subsystem %s (model num: %d) on model %s (model num: %d) is invalid.\n", sip->subsystems[j].name, sip->subsystems[j].model_num, sip->pof_file, sip->model_num ); // JAS
19177-
#endif
19182+
// Verify that all the subsystem model numbers were updated by model_load
19183+
verify_ship_subsystems_linked(&(*sip), "After model_load in ship_page_in");
1917819184
}
1917919185

1918019186
// more weapon marking, the weapon info in Ship_info[] is the default

0 commit comments

Comments
 (0)