CleverRaven / Cataclysm-DDA

Cataclysm - Dark Days Ahead. A turn-based survival game set in a post-apocalyptic world.
http://cataclysmdda.org
Other
9.73k stars 4.05k forks source link

Anaerobic digester causes the game to freeze itself if excessively full. #73509

Closed SomeGuyIGues closed 1 week ago

SomeGuyIGues commented 1 week ago

Describe the bug

After waiting 60 days for the anaerobic digester to finish fermenting the game will likely freeze itself if the tank is too full.

Attach save file

N/A

Steps to reproduce

  1. Debug spawn an empty anaerobic digester.
  2. Spawn 2000 units (1000 liters) of fermentable liquid fluid then interact with the digester. You will fill it up with the liquid fluid to its fullest capacity
  3. Debug change time for 60 or more days.
  4. Interact with the digester again, complete fermenting. Don't complete fermenting because the game froze.

Expected behavior

The game shouldn't shit itself. If I can load the tank with that much fluid, then I should be able to complete fermenting after 60 days.

Screenshots

No response

Versions and configuration

Additional context

I'm using a laptop computer to play DDA. However considering my computer didn't become hotter and the game just froze, I doubt it was a hardware issue.

NetSysFire commented 1 week ago

Can confirm and apparently this is related to pockets. Apparently this is not a full freeze by the way as I could coax the game into exiting cleanly.

    0x563,f83,a15,a59    src/debug.cpp:966    bt_full
    0x563,f83,a15,a59    src/debug.cpp:1,231    debug_write_backtrace(std::ostream&)
    0x563,f83,9ed,b98    src/crash.cpp:89    log_crash
    0x563,f83,9ed,ee5    src/crash.cpp:146    signal_handler
    0x7f0,15a,56e,76f    [unknown src]:0    [unknown func]
    0x7f0,15a,5bf,32c    [unknown src]:0    [unknown func]
    0x7f0,15a,56e,6c7    [unknown src]:0    [unknown func]
    0x7f0,15a,556,4b7    [unknown src]:0    [unknown func]
    0x7f0,15a,556,3db    [unknown src]:0    [unknown func]
    0x7f0,15a,566,d45    [unknown src]:0    [unknown func]
    0x563,f84,4cf,720    src/third-party/imgui/imgui_impl_sdl2.cpp:645    ImGui_ImplSDL2_NewFrame()
    0x563,f83,871,6da    src/cata_imgui.cpp:256    cataimgui::client::new_frame()
    0x563,f84,539,15b    src/ui_manager.cpp:355    ui_adaptor::redraw_invalidated()
    0x563,f83,e60,e36    src/main.cpp:176    exit_handler
    0x7f0,15a,56e,76f    [unknown src]:0    [unknown func]
    0x563,f83,d5b,3d9    src/item_pocket.cpp:609    item_pocket::item_size_modifier() const
    0x563,f83,cf9,b4e    src/item_contents.cpp:2,547    item_contents::item_size_modifier() const
    0x563,f83,ca4,3a6    src/item.cpp:7,414    item::volume(bool, bool, int) const
    0x563,f83,d5b,bef    src/item_pocket.cpp:2,312    item_pocket::contains_volume() const
    0x563,f83,d5b,c0e    src/item_pocket.cpp:580    item_pocket::remaining_volume() const
    0x563,f83,ca5,277    src/item.cpp:10,161    item::can_contain(item const&, int&, bool, bool, bool, bool, item_location const&, units::quantity<int, units::volume_in_milliliter_tag>, bool) const
    0x563,f83,ca5,432    src/item.cpp:10,109    item::can_contain(item const&, bool, bool, bool, bool, item_location const&, units::quantity<int, units::volume_in_milliliter_tag>, bool) const
    0x563,f83,8f7,a01    src/character_attire.cpp:1,801    outfit::can_pickVolume(item const&, bool, bool) const
    0x563,f83,8a6,cd6    src/character.cpp:3,230    Character::can_pickVolume(item const&, bool, item const*, bool) const
    0x563,f83,91b,f61    src/character_inventory.cpp:460    Character::i_add_or_drop(item&, int, item const*, item const*)
    0x563,f83,c07,f72    src/iexamine.cpp:3,983    iexamine::compost_full(Character&, tripoint const&)
    0x563,f83,b91,5ab    src/game.cpp:6,263    game::examine(tripoint const&, bool)
    0x563,f83,b91,c67    src/game.cpp:6,116    game::examine(bool)
    0x563,f83,bde,162    src/handle_action.cpp:2,336    game::do_regular_action(action_id&, avatar&, std::optional<tripoint> const&)
    0x563,f83,be1,f0b    src/handle_action.cpp:3,146    game::handle_action()
    0x563,f83,a76,a63    src/do_turn.cpp:578    do_turn()
    0x563,f83,578,8fa    src/main.cpp:868    main
    0x7f0,15a,557,ccf    [unknown src]:0    [unknown func]
    0x7f0,15a,557,d89    [unknown src]:0    [unknown func]
    0x563,f83,6e0,1bd    [unknown src]:0    [unknown func]
    0xf,fff,fff,fff,fff,fff    [unknown src]:0    [unknown func]

However I find this trace more verbose than actually running it in gdb (are the debug symbols faulty?). gdb.txt

osuphobia commented 1 week ago

Didn't reproduce it at the first run, tried again and the game freezed. In the first time I debug spawned the fermentable liquid mixture without container, and only experienced a small lag when finishing fermenting. In the second time I debug spawned the fermentable liquid mixture with container(2000 jug), and first experienced a lag when adding liquid to the digester, then a freeze when finishing fermenting. Tested again, found out that if the avatar is carrying more than 30 containers, the game will freeze. If only less than 30 containers carried, the game will not freeze, but lag for a second.

https://github.com/CleverRaven/Cataclysm-DDA/blob/0b5152225c8f0db46c736782cf228b765c6d333c/src/character_inventory.cpp#L451-L470 Seems that the reason is I used Character::i_add_or_drop to collect the generated biogas, as the amount of biogas generated by 2000 units of fermentable liquid mixture is 6666500 charge, the loop will be called for ridiculous times🤐 How can I fix this, is there another function usable?

NetSysFire commented 1 week ago

CC @mqrause, since you are our charges specialist, maybe you know what to optimize here.

PatrikLundell commented 1 week ago

If it's count by charges the item ought to contain all the charges and the quantity should be 1. If you feed millions of single charge items into the call the item construction is probably incorrect. It might also be that a different operation (presumably geared towards the collection of some or all charges) should be called, but you still shouldn't feed it with millions of single charge items.

osuphobia commented 1 week ago

If it's count by charges the item ought to contain all the charges and the quantity should be 1. If you feed millions of single charge items into the call the item construction is probably incorrect. It might also be that a different operation (presumably geared towards the collection of some or all charges) should be called, but you still shouldn't feed it with millions of single charge items.

Thanks, I realized how to fix this.

osuphobia commented 1 week ago

Well, tested again, and unfortunately, you can't set the quantity to 1, otherwise you are unable to gather biogas properly without a debug pocket or something that big. To perfectly resolve this, we still need to find a replacement.

I can then modify my function to improve the performance a bit, but lag or freeze is still a problem when you are carrying too many containers.

osuphobia commented 1 week ago

OK, I made a compromise solution. Now the game will still freeze if you are wearing the debug pocket with 2000 jugs in it when gathering biogas. On the other hand, if you are carrying your backpack and other normal gears, but with some extra containers (like 500 condoms) , only minor lag occurs. Just not to walk around carrying thousands of containers, and everything should be fine.

osuphobia commented 1 week ago

@PatrikLundell Forgot to explain why you can't set the quantity to 1. In Character::i_add_or_drop, Character::can_pickWeight and Character::can_pickVolume are called to check if your have enough room for the item. If you want to add items count by charges, the functions need to check per charge. If you set the quantity to 1, the functions will see if you have a big big pocket for the whole bulk of item. If not, not a single charge will be added to your pocket.

PatrikLundell commented 1 week ago

Annoying. It seems like you'd need similar logic to the one used to collect fluids, so rather than trying to pour everything in the source to the destination, you instead try to fill the destination with as much as you can from the source (and all of it if it's less than the destination can hold). I don't know where that code is located, though.

mqrause commented 1 week ago

I would need to look at how the digester works to give real advice here, but ultimately we probably want something like the liquid_handler but for gases.

osuphobia commented 1 week ago

Annoying. It seems like you'd need similar logic to the one used to collect fluids, so rather than trying to pour everything in the source to the destination, you instead try to fill the destination with as much as you can from the source (and all of it if it's less than the destination can hold). I don't know where that code is located, though.

There are two differences in the case of biogas and biomass fluids. First, your don't need to "pick up" the fluids at once, they are kept in the tank. You can use handle_liquid_from_ground to obtain them at any time. Second, the amount of the fluids is smaller, 2000 vs 6666500 max charges. Anyway, after changing my function, I'm also trying to modify Character::i_add_or_drop, to make the loop be called less, without breaking the tests.