Bithack / principia

Open source physics-based sandbox game.
https://principia-web.se
Other
260 stars 25 forks source link

crash on Android when loading prevoius levels #171

Open Luciogi opened 1 month ago

Luciogi commented 1 month ago
Principia version

24.02.29

OS / Hardware
Summary

When I load any previous level, game crashes

Steps to reproduce

https://github.com/Bithack/principia/assets/84625092/4915f6da-073a-4f02-a373-098d5c3ea31d

rollerozxa commented 1 week ago

I've disabled prompting for puzzle solutions in https://github.com/Bithack/principia/commit/1c506dded7ca56dd694365494633d4e671cc44ed on Android which fixes the crash but doesn't solve the underlying issue that some sort of mangling that happens when data is passed from the dialog prompt between C++ and Java. (This issue is exclusive to the Android version and doesn't occur with the GTK3 dialogs on desktop)

rollerozxa commented 1 week ago

Some technical details:

When the dialog opens (src/src/menu_pkg.cc:483) it sends a pointer to some data (open_play_data) within a principia_action to accompany the button label.

open_play_data *opd = new open_play_data(LEVEL_LOCAL, level_id, &pkg, false, 1);
ui::confirm("Do you want to load your last saved solution?",
    "Yes",    principia_action(ACTION_OPEN_MAIN_PUZZLE_SOLUTION, opd),
    "No",     principia_action(ACTION_CREATE_MAIN_PUZZLE_SOLUTION, opd),
    "Cancel", principia_action(ACTION_IGNORE, 0));

For Android ui::confirm is implemented in src/src/ui_android.hh at ~line 81 which passes a reference to the action data over the JNI from C++ to Java:

env->CallStaticVoidMethod(cls, mid,
        _text,
        _button1, (jint)action1.action_id, (jlong)action1.action_data,
        _button2, (jint)action2.action_id, (jlong)action2.action_data,
        _button3, (jint)action3.action_id, (jlong)action3.action_data,
        (jboolean)_confirm_data.confirm_type == CONFIRM_TYPE_BACK_SANDBOX);

This in turn calls org.libsdl.app.SDLActivity.confirm, which is a custom method added in there by Principia (gross, I know). What it effectively does is send the action of the pressed button back to C++-land which is either ACTION_OPEN_MAIN_PUZZLE_SOLUTION or ACTION_CREATE_MAIN_PUZZLE_SOLUTION. It should keep the same pointer to the action data which it casts to open_play_data and then retrieves fields from:

open_play_data *opd = static_cast<open_play_data*>(data);
uint8_t pkg_type = opd->id_type;

Then, segfault. The pointer is garbage and it fails when trying to get the package type.

Clue: The crash only occurs on 64-bit. I can reproduce the issue on my 64-bit ARM phone or on an x86_64 emulator. I cannot reproduce the issue on 32-bit ARM (namely my Nexus 7 tablet), which would make me believe it has something about pointer sizes differing that causes issues when sending it over the JNI in ui::confirm. This would also explain why it never caused issues in 2014 since the game did not have a 64-bit build back then. But I really do not know where the issue exactly lies. Maybe someone smarter than me could point to how to actually fix it.