Always check external levels for connectivity errors

Players keep reporting errors that trace to the game rejecting invalid
data from user-made maps.  In this case, asymmetric connectivity in
_Descent Vignettes_[1] level _Stalactite Volcano_ (level 15 in Descent,
level 14 in Descent 2) caused an exception when trying to render part of
the first big room.

The debug build shipped with function `check_segment_connections` to
detect some types of connectivity errors.  Expose this function to
release builds, and call it for user-made maps.  It already checked for
asymmetric segment links.  Extend that to hotfix those links by breaking
the asymmetric connection.  Log an URGENT class message when this
happens, so that users are aware that the level was modified at load.
Move the `NDEBUG` guards into `check_segment_connections` so that
non-debug builds check only for segment symmetry, but do not check for
normals and face count errors.

[1] http://www.enspiar.com/dmdb/viewMission.php?id=724
```
sha1sum vignette.hog vignette.msn
3cd659e6dd5927b41157dfb7d1dd87d90e781f01  vignette.hog
1f7d140ffab11816364040dd6da71a1568393a16  vignette.msn

stat -c '%s %Y %n' vignette.hog vignette.msn
5717889 1251643308 vignette.hog
956 1250212930 vignette.msn
```

Reported-by: MegaDescent <http://forum.dxx-rebirth.com/showthread.php?tid=970>
This commit is contained in:
Kp 2017-07-26 03:15:59 +00:00
parent e8d82d7d85
commit c816a79c47
2 changed files with 19 additions and 5 deletions

View file

@ -1434,13 +1434,18 @@ int load_level(const char * filename_passed)
editor_status_fmt("Loaded NEW mine %s, \"%s\"", filename, static_cast<const char *>(Current_level_name));
#endif
#if !defined(NDEBUG) && !defined(COMPACT_SEGS)
#ifdef NDEBUG
if (!PLAYING_BUILTIN_MISSION)
#endif
if (check_segment_connections())
{
#ifndef NDEBUG
nm_messagebox( "ERROR", 1, "Ok",
"Connectivity errors detected in\n"
"mine. See monochrome screen for\n"
"details, and contact Matt or Mike." );
#endif
}
#if defined(DXX_BUILD_DESCENT_II)

View file

@ -486,18 +486,21 @@ static int check_norms(const vcsegptr_t segp,int sidenum,int facenum,const vcseg
else
return 0;
}
#endif
//heavy-duty error checking
int check_segment_connections(void)
{
int errors=0;
range_for (const auto &&seg, vcsegptridx)
range_for (const auto &&seg, vmsegptridx)
{
for (int sidenum=0;sidenum<6;sidenum++) {
#ifndef NDEBUG
const auto v = create_abs_vertex_lists(seg, sidenum);
const auto &num_faces = v.first;
const auto &vertex_list = v.second;
#endif
auto csegnum = seg->children[sidenum];
if (IS_CHILD(csegnum)) {
auto cseg = vcsegptr(csegnum);
@ -505,15 +508,22 @@ int check_segment_connections(void)
if (csidenum == side_none)
{
auto &rseg = *seg;
auto &rcseg = *cseg;
const unsigned segi = seg.get_unchecked_index();
LevelError("Segment #%u side %u has asymmetric link to segment %u. Coercing to segment_none; Segments[%u].children={%hu, %hu, %hu, %hu, %hu, %hu}, Segments[%u].children={%hu, %hu, %hu, %hu, %hu, %hu}.", segi, sidenum, csegnum, segi, rseg.children[0], rseg.children[1], rseg.children[2], rseg.children[3], rseg.children[4], rseg.children[5], csegnum, rcseg.children[0], rcseg.children[1], rcseg.children[2], rcseg.children[3], rcseg.children[4], rcseg.children[5]);
rseg.children[sidenum] = segment_none;
errors = 1;
continue;
}
#ifndef NDEBUG
const auto cv = create_abs_vertex_lists(cseg, csidenum);
const auto &con_num_faces = cv.first;
const auto &con_vertex_list = cv.second;
if (con_num_faces != num_faces) {
LevelError("Segment #%u side %u: wrong faces: con_num_faces=%lu num_faces=%lu.", seg.get_unchecked_index(), sidenum, con_num_faces, num_faces);
errors = 1;
}
else
@ -527,6 +537,7 @@ int check_segment_connections(void)
vertex_list[1] != con_vertex_list[(t+3)%4] ||
vertex_list[2] != con_vertex_list[(t+2)%4] ||
vertex_list[3] != con_vertex_list[(t+1)%4]) {
LevelError("Segment #%u side %u: bad vertices.", seg.get_unchecked_index(), sidenum);
errors = 1;
}
else
@ -565,14 +576,12 @@ int check_segment_connections(void)
}
}
}
}
}
}
return errors;
}
#endif
}
}
}
return errors;
}
// Used to become a constant based on editor, but I wanted to be able to set
// this for omega blob find_point_seg calls.