When I attempt to examine a Fire Engine (aka short firetruck), the game segfaults. The Fire Engine can still be driven as normal, and I was even able to siphon fuel into it (I think. I can't exactly tell if the fuel showed up due to the bug.)
The normal vehicle menu shows up in all of its glory.
On the off chance that the name is cursed, I renamed some other vehicle to "Fire Engine" and then examined it. That failed to segfault my game.
Am neglecting to provide a save file because it's probably faster to recreate the conditions on your own than to download and install a save file.
Just to provide some additional information here from my duplicate report:
I am experiencing this bug with the Fire Engine, as well as the Atomic SUV, and Survivor's Militarized RV. The crash is only happening when I try to examine a vehicle that has spawned naturally in the world. Spawning a vehicle in with the debug menu results in me being able to examine the vehicle as normal.
I'm looking into this right now and Fire Engine spawned in via debug triggers a crash, Atom SUV spawned similarly does not. Trying to trace where in the code the crash happens. Thus far I'm brought to veh_interact.cpp, do_main_loop()
Looks like the ammo_restriction
is nonsense:
ammo_restriction = size=1 {
[0] = {
first = (_id = error: summary string parsing error, _cid = -1)
second = 100
}
}
Crashes on:
455 int item_pocket::ammo_capacity( const ammotype &ammo ) const
456 {
-> 457 const auto found_ammo = data->ammo_restriction.find( ammo );
Full bt:
frame #0: 0x00007ffff7d2a39e libstdc++.so.6'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::compare(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) const [inlined] std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::size(this=0x0000000000000020) const at basic_string.h:902:16
frame #1: 0x00007ffff7d2a39e libstdc++.so.6'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::compare(this="water", __str=0x0000000000000020) const at basic_string.h:2855
frame #2: 0x00005555561ce8f8 cataclysm-tiles'std::_Rb_tree<string_id<ammunition_type>, std::pair<string_id<ammunition_type> const, int>, std::_Select1st<std::pair<string_id<ammunition_type> const, int> >, std::less<string_id<ammunition_type> >, std::allocator<std::pair<string_id<ammunition_type> const, int> > >::find(string_id<ammunition_type> const&) const [inlined] bool std::operator<<char, std::char_traits<char>, std::allocator<char> >(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&)(__lhs=<unavailable>, __rhs=0x0000000000000020) at basic_string.h:6270:20
frame #3: 0x00005555561ce8f0 cataclysm-tiles'std::_Rb_tree<string_id<ammunition_type>, std::pair<string_id<ammunition_type> const, int>, std::_Select1st<std::pair<string_id<ammunition_type> const, int> >, std::less<string_id<ammunition_type> >, std::allocator<std::pair<string_id<ammunition_type> const, int> > >::find(string_id<ammunition_type> const&) const [inlined] string_id<ammunition_type>::operator<(this=<unavailable>, rhs=0x0000000000000020) const at string_id.h:71
frame #4: 0x00005555561ce8f0 cataclysm-tiles'std::_Rb_tree<string_id<ammunition_type>, std::pair<string_id<ammunition_type> const, int>, std::_Select1st<std::pair<string_id<ammunition_type> const, int> >, std::less<string_id<ammunition_type> >, std::allocator<std::pair<string_id<ammunition_type> const, int> > >::find(string_id<ammunition_type> const&) const [inlined] std::less<string_id<ammunition_type> >::operator(this=<unavailable>, __x=<unavailable>, __y=0x0000000000000020)(string_id<ammunition_type> const&, string_id<ammunition_type> const&) const at stl_function.h:386
frame #5: 0x00005555561ce8f0 cataclysm-tiles'std::_Rb_tree<string_id<ammunition_type>, std::pair<string_id<ammunition_type> const, int>, std::_Select1st<std::pair<string_id<ammunition_type> const, int> >, std::less<string_id<ammunition_type> >, std::allocator<std::pair<string_id<ammunition_type> const, int> > >::find(string_id<ammunition_type> const&) const [inlined] std::_Rb_tree<string_id<ammunition_type>, std::pair<string_id<ammunition_type> const, int>, std::_Select1st<std::pair<string_id<ammunition_type> const, int> >, std::less<string_id<ammunition_type> >, std::allocator<std::pair<string_id<ammunition_type> const, int> > >::_M_lower_bound(this=<unavailable>, __x=0x000055555c39d000, __y=0x000055555ad31828, __k=<unavailable>) const at stl_tree.h:1951
frame #6: 0x00005555561ce8e1 cataclysm-tiles'std::_Rb_tree<string_id<ammunition_type>, std::pair<string_id<ammunition_type> const, int>, std::_Select1st<std::pair<string_id<ammunition_type> const, int> >, std::less<string_id<ammunition_type> >, std::allocator<std::pair<string_id<ammunition_type> const, int> > >::find(this=<unavailable>, __k=0x0000000000000020) const at stl_tree.h:2566
frame #7: 0x00005555561c90fb cataclysm-tiles'item_pocket::ammo_capacity(string_id<ammunition_type> const&) const [inlined] std::map<string_id<ammunition_type>, int, std::less<string_id<ammunition_type> >, std::allocator<std::pair<string_id<ammunition_type> const, int> > >::find(this=<unavailable>, __x=<unavailable>) const at stl_map.h:1195:21
frame #8: 0x00005555561c90f2 cataclysm-tiles'item_pocket::ammo_capacity(this=0x000055556d282d60, ammo=<unavailable>) const at item_pocket.cpp:457
frame #9: 0x000055555617c011 cataclysm-tiles'item_contents::ammo_capacity(this=0x000055556d2933d8, ammo=0x0000000000000020) const at item_contents.cpp:416:27
frame #10: 0x0000555556880663 cataclysm-tiles'vehicle_part::ammo_capacity(this=0x000055556d293340, ammo=0x0000000000000020) const at vehicle_part.cpp:224:21
frame #11: 0x0000555556880e6b cataclysm-tiles'vehicle_part::can_reload(this=0x000055556d293340, obj=<unavailable>) const at vehicle_part.cpp:369:31
frame #12: 0x000055555680e012 cataclysm-tiles'veh_interact::cant_do(char) [inlined] $_4::operator(this=<unavailable>, pt=0x000055556d293340)(vehicle_part const&) const at veh_interact.cpp:103:15
frame #13: 0x000055555680dfff cataclysm-tiles'veh_interact::cant_do(char) [inlined] bool __gnu_cxx::__ops::_Iter_pred<$_4>::operator(__it=__normal_iterator<vehicle_part *, std::vector<vehicle_part, std::allocator<vehicle_part> > > @ scalar)<__gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > > >(__gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > >) at predefined_ops.h:316
frame #14: 0x000055555680dfff cataclysm-tiles'veh_interact::cant_do(char) [inlined] __gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > > std::__find_if<__gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > >, __gnu_cxx::__ops::_Iter_pred<$_4> >(__first=__normal_iterator<vehicle_part *, std::vector<vehicle_part, std::allocator<vehicle_part> > > @ scalar, __last=__normal_iterator<vehicle_part *, std::vector<vehicle_part, std::allocator<vehicle_part> > > @ scalar) at stl_algobase.h:1923
frame #15: 0x000055555680dfc8 cataclysm-tiles'veh_interact::cant_do(char) [inlined] __gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > > std::__find_if<__gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > >, __gnu_cxx::__ops::_Iter_pred<$_4> >(__first=<unavailable>, __last=__normal_iterator<vehicle_part *, std::vector<vehicle_part, std::allocator<vehicle_part> > > @ scalar) at stl_algobase.h:1965
frame #16: 0x000055555680dfc8 cataclysm-tiles'veh_interact::cant_do(char) [inlined] __gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > > std::find_if<__gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > >, $_4>(__first=<unavailable>, __last=__normal_iterator<vehicle_part *, std::vector<vehicle_part, std::allocator<vehicle_part> > > @ scalar) at stl_algo.h:3928
frame #17: 0x000055555680dfc8 cataclysm-tiles'veh_interact::cant_do(char) [inlined] bool std::none_of<__gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > >, $_4>(__first=<unavailable>, __last=__normal_iterator<vehicle_part *, std::vector<vehicle_part, std::allocator<vehicle_part> > > @ scalar) at stl_algo.h:471
frame #18: 0x000055555680dfc8 cataclysm-tiles'veh_interact::cant_do(char) [inlined] bool std::any_of<__gnu_cxx::__normal_iterator<vehicle_part*, std::vector<vehicle_part, std::allocator<vehicle_part> > >, $_4>(__first=<unavailable>, __last=__normal_iterator<vehicle_part *, std::vector<vehicle_part, std::allocator<vehicle_part> > > @ scalar) at stl_algo.h:490
frame #19: 0x000055555680dfc8 cataclysm-tiles'veh_interact::cant_do(this=0x00007fffffff9af0, mode=<unavailable>) at veh_interact.cpp:559
frame #20: 0x0000555556814c70 cataclysm-tiles'veh_interact::display_mode(this=0x00007fffffff9af0) at veh_interact.cpp:2539:18
frame #21: 0x000055555681a716 cataclysm-tiles'std::_Function_handler<void (ui_adaptor const&), veh_interact::create_or_get_ui_adaptor()::$_2>::_M_invoke(std::_Any_data const&, ui_adaptor const&) [inlined] veh_interact::create_or_get_ui_adaptor(this=<unavailable>, (null)=<unavailable>)::$_2::operator()(ui_adaptor const&) const at veh_interact.cpp:371:13
frame #22: 0x000055555681a5ab cataclysm-tiles'std::_Function_handler<void (ui_adaptor const&), veh_interact::create_or_get_ui_adaptor()::$_2>::_M_invoke(std::_Any_data const&, ui_adaptor const&) [inlined] void std::__invoke_impl<void, veh_interact::create_or_get_ui_adaptor()::$_2&, ui_adaptor const&>(__f=<unavailable>)::$_2&, ui_adaptor const&) at invoke.h:60
frame #23: 0x000055555681a5ab cataclysm-tiles'std::_Function_handler<void (ui_adaptor const&), veh_interact::create_or_get_ui_adaptor()::$_2>::_M_invoke(std::_Any_data const&, ui_adaptor const&) [inlined] std::enable_if<__and_<std::is_void<void>, std::__is_invocable<veh_interact::create_or_get_ui_adaptor()::$_2&, ui_adaptor const&> >::value, void>::type std::__invoke_r<void, veh_interact::create_or_get_ui_adaptor(__fn=<unavailable>)::$_2&, ui_adaptor const&>(veh_interact::create_or_get_ui_adaptor()::$_2&, ui_adaptor const&) at invoke.h:153
frame #24: 0x000055555681a5ab cataclysm-tiles'std::_Function_handler<void (ui_adaptor const&), veh_interact::create_or_get_ui_adaptor()::$_2>::_M_invoke(__functor=<unavailable>, __args=<unavailable>) at std_function.h:291
frame #25: 0x0000555556801bf5 cataclysm-tiles'ui_adaptor::redraw_invalidated() [inlined] std::function<void (ui_adaptor const&)>::operator(this=0x000055556d25f7a0, __args=0x000055556d25f790)(ui_adaptor const&) const at std_function.h:622:14
frame #26: 0x0000555556801bea cataclysm-tiles'ui_adaptor::redraw_invalidated() at ui_manager.cpp:217
frame #27: 0x0000555556804c31 cataclysm-tiles'veh_interact::do_main_loop(this=0x00007fffffff9af0) at veh_interact.cpp:392:9
frame #28: 0x0000555556804a91 cataclysm-tiles'veh_interact::run(veh=<unavailable>, p=<unavailable>) at veh_interact.cpp:166:12
frame #29: 0x00005555560235a4 cataclysm-tiles'game::exam_vehicle(this=0x00005555575db580, veh=0x000055556d27ca60, c=<unavailable>) at game.cpp:5235:16
frame #30: 0x0000555556892cd8 cataclysm-tiles'vehicle::interact_with(this=0x000055556d27ca60, pos=0x00007fffffffd5b0, interact_part=20) at vehicle_use.cpp:2150:16
frame #31: 0x000055555602655f cataclysm-tiles'game::examine(this=0x00005555575db580, examp=0x00007fffffffd5b0) at game.cpp:5736:23
frame #32: 0x000055555602625e cataclysm-tiles'game::examine(this=0x00005555575db580) at game.cpp:5627:5
frame #33: 0x0000555556096b5a cataclysm-tiles'game::handle_action(this=<unavailable>) at handle_action.cpp:1880:21
frame #34: 0x000055555600a51c cataclysm-tiles'game::do_turn(this=<unavailable>) at game.cpp:1536:21
frame #35: 0x00005555562cc7a7 cataclysm-tiles'main(argc=<unavailable>, argv=<unavailable>) at main.cpp:697:20
frame #36: 0x00007ffff78e9002 libc.so.6'__libc_start_main + 242
frame #37: 0x0000555555c854be cataclysm-tiles'_start + 46
I think the value being passed back from ammo_current() in vehicle_part.cpp is whats causing the exception to be thrown. Line 200.
Scratch that. Figured that whatever was causing the seg fault was whatever was being returned by vehicle_part::can_reload(), specifically whatever ammo_current() evaluated to in that return statement being the cause of an unusual value.
It seems this vehicle part, going through ammo_current(), is returning base.contents.legacy_front().typeId() (as on line 207). Still trying to figure out what that means and how to remedy this whole thing.
Before we end up at 457 with that crash we have a bad item, an ammunition_type I think. I did some deeper debugging and found out that the problem occurs when processing the water cannon at { x=1, y=-2}, specifically running through item::magazine_current, line 7265.
Hey @Hauntmachine , by any chance did that "survivor's militarized rv" you mentioned in your issue have a flamethrower installed?
@Nannaquin It doesn't appear so. Here's the layout of it:
Also, I saw that you said that spawning in certain vehicles made you crash or not crash, which is weird. For me, these vehicles are only crashing if I encounter them naturally ingame. I'm getting no crashes at all if I actually spawn the vehicles in through debug.
EDIT: Atomic compact is apparently now causing a crash now as well... when it wasn't before. This particular segfault issue seems really odd.
2nd EDIT: ... So atomic compact is crashing in one of my test saves, but not in the other test save. Only difference I can see between the two is that the one with z-levels turned off is the one that's crashing.
3rd EDIT: ... And now the atomic compact is no longer crashing me in either test world. Might have just been a one time thing I guess. I don't have the crash log, so no way to know if the atomic compact segfault was related to the others.
Thanks for the meaty response. All the details help. My findings have me believe this is just another bug from the new inventory system. The watercannon vehicle_part's base is, naturally, the watercannon item, which has 4 contents elements, none of which are of the enum value CONTAINER, which causes item_contents::can_contain_liquid() (line 297) to ultimately return false on the question of it being water tight despite the weapon item's pocket_data json having the water tight property set to true.
I think I have a solution at least for the fire engine case but it needs testing to ensure nothing else broke.
@Hauntmachine Upon further comparison between the fire engine's examine screen and the one you shared I think the overlap may be the mounted weapons, specifically the mounted ups rifle being considered a tank in the same way the fire engine's water cannon is.
@anothersimulacrum I'm doing some digging into the reactor case and will share my findings today.
Does it not have the same cause as this? I assumed it did, my bad.
Sort of. My PR resolved the fire engine issue although it might just be hacky. Theres alot of similarity between this and the reactor situation.
Checking save data, heres a comparsion of two different parts from the bubble car, a vehicle that has a minireactor (and causes a crash when processing that part during the display drawing:
{
"id": "medium_storage_battery",
"base": {
"typeid": "medium_storage_battery",
"bday": 5215646,
"last_rot_check": 0,
"last_temp_check": 0,
"item_tags": [
"VEHICLE"
],
"relic_data": null,
"contents": {
"contents": [
{
"pocket_type": 1,
"contents": [
{
"typeid": "battery",
"charges": 3430,
"bday": 5212800,
"last_rot_check": 0,
"last_temp_check": 0,
"item_tags": [
"IRREMOVABLE",
"NO_DROP"
],
"relic_data": null
}
]
}
]
}
},
"mount_dx": -2,
"mount_dy": 0,
"open": false,
"direction": 0,
"blood": 0,
"enabled": false,
"flags": 0,
"passenger_id": -1,
"crew_id": -1,
"items": [],
"ammo_pref": "null"
},
{
"id": "minireactor",
"base": {
"typeid": "minireactor",
"bday": 5215646,
"last_rot_check": 0,
"last_temp_check": 0,
"item_tags": [
"VEHICLE"
],
"relic_data": null
},
"mount_dx": -1,
"mount_dy": 0,
"open": false,
"direction": 0,
"blood": 0,
"enabled": true,
"flags": 0,
"passenger_id": -1,
"crew_id": -1,
"items": [],
"ammo_pref": "null"
},
Notice how the battery's base item's content's content's pocket's content's have something in them - the battery charge. Take note that the minireactor's base item doesn't go that deep at all, lacking a contents member despite having a magazine pocket in the json, with a listed ammo restriction. During processing, the part's type (item template I guess) member makes note of this pocket with the appropriate ammo data so the info is there, but basically theres no plutonium loaded into the reactor for some reason. I'm unsure if this is a matter of it just not being generated the same way the battery charge is, or for example the magazine in a rifle found as a drop or even improper saving (doubtful since the battery charge is present in the battery), but the code is acting up during the last line/return statement of vehicle_part::can_reload() const. Specifically its the ammo_current() function that first brings the nullptr garbage (as there is nothing to find "inside" it) in that cascades through the evaluations and member accesses, culminating to the exception being thrown in item_pocket::ammo_capacity (with the whole ammo_restriction problem).
The watercannon part from the fire engine goes through this same path but obviously shouldn't contain any ammo, instead drawing from the tanks. Its an odd situation surely.
I think at this point my first PR regarding this (the fire truck crash) is technically correct due to how radically different the situations are for these two parts.
@anothersimulacrum I dont know if you're the right person to ask about this but do you know where I'd look for code that generates vehicles spawned in the map? I have a hunch regarding the minireactor case I want to pursue.
jmapgen_vehicle::apply()
, which calls map::add_vehicle()
(which has a billion overloads) would be where I start.
@anothersimulacrum Is there a function in place that converts plutonium cells into plutonium (charges)? I think I found the problem with the minireactor case.
I don't believe so.
This issue has been mentioned on Cataclysm: Dark Days Ahead. There might be relevant details there:
https://discourse.cataclysmdda.org/t/crash-when-examining-vehicle/23868/7
I've altered a line in the code specifically for spawning a reactor fueled vehicle, and the examination crash has stopped but now I can't refill the minireactor with the plut cells (items consumed, charges not increased). Working on that right now, then I'll double back on the fire engine/watercannon problem.
Okay so the minireactor case is solved. Should I make a separate PR or wait until I have the watercannon situation handled?
If the fixes are independent, you can make a PR for each - or if you don't think it'll be too long, you can make them together, up to you.
Most helpful comment
I've altered a line in the code specifically for spawning a reactor fueled vehicle, and the examination crash has stopped but now I can't refill the minireactor with the plut cells (items consumed, charges not increased). Working on that right now, then I'll double back on the fire engine/watercannon problem.