SDL: Improve controllable mitigation

Now continously polls the directory where controllable is expected to
extract natives instead of waiting on inotify. This theoretically should
consume quite a bit of the CPU but it seems to not when testing.

This should be much more consistent than waiting on inotify, testing
seems to corroborate that. Anyone who's crashing can send a bug report.
This commit is contained in:
alexytomi
2025-09-21 22:58:06 +08:00
parent 29e2d21b84
commit 04fb109eb9
2 changed files with 59 additions and 28 deletions

View File

@@ -314,7 +314,7 @@ public final class Tools {
}
LauncherProfiles.load();
File gamedir = Tools.getGameDirPath(minecraftProfile);
startControllableMitigation(gamedir);
startControllableMitigation(activity, gamedir);
startOldLegacy4JMitigation(activity, gamedir);
if(checkRenderDistance(gamedir)) {
LifecycleAwareAlertDialog.DialogCreator dialogCreator = ((alertDialog, dialogBuilder) ->
@@ -380,37 +380,68 @@ public final class Tools {
// If we returned, this means that the JVM exit dialog has been shown and we don't need to be active anymore.
// We never return otherwise. The process will be killed anyway, and thus we will become inactive
}
private static Logger.eventLogListener controllableMitigationLogListener;
/*
* This is does not work when debugging. This is not reliable.
* This is a monstrosity that races the mod, trying to ensure that when the folder is checked
* after extraction but before dlopen, it is empty, so it loads the bundled SDL2 we have instead
*/
private static void startControllableMitigation(File gamedir) {
File deleted = new File(gamedir + "/" + "controllable_natives");
try {
org.apache.commons.io.FileUtils.deleteDirectory(deleted);
} catch (IOException e) {
Log.i("ModMitigation", "Failed to execute Controllable mitigation");
}
FileObserver controllableMitigation = new FileObserver(gamedir) {
@Override
public void onEvent(int event, @Nullable String path) {
Log.i("ModMitigation", "onEvent: " + Integer.toHexString(event) + ", " + path);
if (path != null && path.equals("controllable_natives")){
Log.i("ModMitigation", "Attempting MrCrayfish's Controllable forced incorrect native extraction mitigation");
try {
org.apache.commons.io.FileUtils.deleteDirectory(deleted);
} catch (IOException e) {
Log.i("ModMitigation", "Failed to execute Controllable mitigation");
}
private static void startControllableMitigation(Activity activity ,File gamedir) {
String TAG = "ControllableMitigation";
File deleted = new File(gamedir + "/controllable_natives/SDL");
boolean hasControllable = false;
File modsDir = new File(gamedir, "mods");
File[] mods = modsDir.listFiles(file -> file.isFile() && file.getName().endsWith(".jar"));
if (mods != null) {
for (File file : mods) {
String name = file.getName();
if (name.contains("controllable")) {
hasControllable = true;
break;
}
}
};
controllableMitigation.startWatching();
}
if (hasControllable) {
Tools.runOnUiThread(() -> {
Tools.dialog(activity, activity.getString(R.string.global_warning), activity.getString(R.string.controllableFound));
});
Thread mitigationThread = new Thread(() -> {
// This is total garbage but it seems to be the best jank for the job
Log.i(TAG, "Controllable detected! Starting mitigation thread");
try {org.apache.commons.io.FileUtils.deleteDirectory(deleted);} catch (IOException ignored) {}
while (!Thread.currentThread().isInterrupted()) {
// Looks for controllable_natives/SDL/<sdl_version_number>/libSDL2.so and
// deletes it. We can assume array index 0 because this dir gets fully deleted
// before the loop is started.
if (deleted.isDirectory()) {
if (deleted.listFiles().length > 0) {
if (deleted.listFiles()[0].listFiles().length > 0) {
if (deleted.listFiles()[0].listFiles()[0].exists()) {
deleted.listFiles()[0].listFiles()[0].delete();
break;
}
}
}
}
}
// We can end here because SdlNativeLibraryLoader only extracts libSDL2.so once
// If NativeLibrary can't find it in the folder to load() it uses java.library.path
Log.i(TAG, "Success! Ending Controllable crash mitigation..");
});
mitigationThread.start();
controllableMitigationLogListener = loggedLine -> {
// Hard off switch if it somehow didn't delete anything, just in case.
if (loggedLine.contains("Sound engine started") && mitigationThread.isAlive()) {
Log.i(TAG, "Nothing happened. Ending Controllable crash mitigation..");
Logger.removeLogListener(controllableMitigationLogListener);
mitigationThread.interrupt();
}
};
Logger.addLogListener(controllableMitigationLogListener);
}
}
private static Logger.eventLogListener eventLogListener;
private static Logger.eventLogListener oldL4JMitigationLogListener;
/// TODO: Remove when the time is right
/**
* Legacy4J for a long time had broken SDL detection for android, we need to check and
@@ -436,21 +467,20 @@ public final class Tools {
if (hasLegacy4J) {
String TAG = "OldLegacy4JMitigation";
Log.i(TAG, "Legacy4J detected!");
eventLogListener = loggedLine -> {
Log.i(TAG, loggedLine);
oldL4JMitigationLogListener = loggedLine -> {
if (LauncherPreferences.PREF_GAMEPAD_SDL_PASSTHRU && loggedLine.contains("literal{SDL3 (isXander's libsdl4j)} isn't supported in this system. GLFW will be used instead.")) {
Log.i(TAG, "Old version of Legacy4J detected! Force enabling SDL");
Tools.SDL.initializeControllerSubsystems();
Tools.runOnUiThread(() -> {
Tools.dialog(activity, activity.getString(R.string.global_warning), activity.getString(R.string.oldL4JFound));
});
Logger.removeLogListener(eventLogListener);
Logger.removeLogListener(oldL4JMitigationLogListener);
} else if (LauncherPreferences.PREF_GAMEPAD_SDL_PASSTHRU && loggedLine.contains("Added SDL Controller Mappings")) {
Log.i(TAG, "Fixed version of Legacy4J detected! Have fun!");
Logger.removeLogListener(eventLogListener);
Logger.removeLogListener(oldL4JMitigationLogListener);
}
};
Logger.addLogListener(eventLogListener);
Logger.addLogListener(oldL4JMitigationLogListener);
}
}

View File

@@ -481,4 +481,5 @@
<string name="preference_category_SDL_settings">SDL Settings</string>
<string name="oldL4JFound">You are using a version of Legacy4J without the android fixes! You will need to manually enable SDL in the controller settings to get the best experience!</string>
<string name="global_warning">Warning!</string>
<string name="controllableFound">Controllable currently has a random chance of crashing on launch. Either keep trying again and hope it launches or use another mod like Controlify.</string>
</resources>