Skip to content

Commit 8dd1226

Browse files
committed
run shared library initialization on separate context
1 parent 6918807 commit 8dd1226

3 files changed

Lines changed: 165 additions & 95 deletions

File tree

app/src/main/java/com/geode/launcher/GeometryDashActivity.kt

Lines changed: 131 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,10 @@ import androidx.core.view.WindowCompat
2424
import androidx.core.view.WindowInsetsCompat
2525
import androidx.core.view.WindowInsetsControllerCompat
2626
import androidx.core.view.updatePadding
27+
import androidx.lifecycle.lifecycleScope
2728
import com.customRobTop.BaseRobTopActivity
2829
import com.customRobTop.JniToCpp
30+
import com.geode.launcher.main.FullScreenLoadingIndicator
2931
import com.geode.launcher.main.LaunchNotification
3032
import com.geode.launcher.main.determineDisplayedCards
3133
import com.geode.launcher.utils.Constants
@@ -35,13 +37,14 @@ import com.geode.launcher.utils.GamePackageUtils
3537
import com.geode.launcher.utils.GeodeUtils
3638
import com.geode.launcher.utils.LaunchUtils
3739
import com.geode.launcher.utils.PreferenceUtils
40+
import kotlinx.coroutines.Dispatchers
41+
import kotlinx.coroutines.launch
42+
import kotlinx.coroutines.withContext
3843
import org.cocos2dx.lib.Cocos2dxGLSurfaceView
3944
import org.cocos2dx.lib.Cocos2dxHelper
4045
import org.cocos2dx.lib.Cocos2dxRenderer
4146
import org.fmod.FMOD
4247
import java.io.File
43-
import java.io.FileInputStream
44-
import java.io.FileOutputStream
4548
import java.io.IOException
4649
import javax.microedition.khronos.egl.EGL10
4750
import javax.microedition.khronos.egl.EGLConfig
@@ -91,97 +94,88 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
9194
return
9295
}
9396

94-
try {
95-
createVersionFile()
96-
tryLoadGame()
97-
} catch (e: UnsatisfiedLinkError) {
98-
Log.e("GeodeLauncher", "Library linkage failure", e)
99-
100-
// generates helpful information for use in debugging library load failures
101-
val gdPackageInfo = packageManager.getPackageInfo(Constants.PACKAGE_NAME, 0)
102-
val abiMismatch = GamePackageUtils.detectAbiMismatch(this, gdPackageInfo, e)
103-
104-
val is64bit = LaunchUtils.is64bit
105-
val errorMessage = when {
106-
abiMismatch && is64bit -> LaunchUtils.LauncherError.LINKER_NEEDS_32BIT
107-
abiMismatch -> LaunchUtils.LauncherError.LINKER_NEEDS_64BIT
108-
e.message?.contains("__gxx_personality_v0") == true ->
109-
LaunchUtils.LauncherError.LINKER_FAILED_STL
110-
else -> LaunchUtils.LauncherError.LINKER_FAILED
97+
val rootLayout = setupContext()
98+
val loadingView = ComposeView(this).apply {
99+
setContent {
100+
FullScreenLoadingIndicator()
111101
}
102+
}
112103

113-
returnToMain(errorMessage, e.message, e.stackTraceToString())
104+
if (rootLayout != null) {
105+
rootLayout.addView(loadingView)
106+
} else {
107+
setContentView(loadingView)
108+
}
114109

115-
return
116-
} catch (e: Exception) {
117-
Log.e("GeodeLauncher", "Uncaught error during game load", e)
110+
lifecycleScope.launch {
111+
val error = preloadGame(rootLayout)
112+
if (error != null) {
113+
returnToMain(error)
114+
}
118115

119-
returnToMain(
120-
LaunchUtils.LauncherError.GENERIC,
121-
e.message ?: "Unknown Exception",
122-
e.stackTraceToString()
123-
)
116+
rootLayout?.removeView(loadingView)
117+
}
118+
}
124119

125-
return
120+
suspend fun preloadGame(rootLayout: FrameLayout?): LaunchUtils.FullLauncherError? = try {
121+
createVersionFile()
122+
val loadFailed = tryLoadGame()
123+
124+
val baseView = createView()
125+
126+
if (rootLayout != null) {
127+
rootLayout.addView(baseView)
128+
} else {
129+
setContentView(baseView)
126130
}
127131

128-
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
129-
override fun handleOnBackPressed() {
130-
mGLSurfaceView?.sendKeyBack()
131-
}
132-
})
133132
mGLSurfaceView?.manualBackEvents = true
134-
}
135133

136-
private fun createVersionFile() {
137-
val versionPath = File(filesDir, "game_version.txt")
138-
val gameVersion = GamePackageUtils.getGameVersionCode(packageManager)
134+
setupNotificationView(baseView, loadFailed)
139135

140-
versionPath.writeText("$gameVersion")
141-
}
136+
null
137+
} catch (e: UnsatisfiedLinkError) {
138+
Log.e("GeodeLauncher", "Library linkage failure", e)
142139

143-
private fun returnToMain(
144-
error: LaunchUtils.LauncherError? = null,
145-
returnMessage: String? = null,
146-
returnExtendedMessage: String? = null
147-
) {
148-
val launchIntent = Intent(this, MainActivity::class.java).apply {
149-
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
140+
// generates helpful information for use in debugging library load failures
141+
val gdPackageInfo = packageManager.getPackageInfo(Constants.PACKAGE_NAME, 0)
142+
val abiMismatch = GamePackageUtils.detectAbiMismatch(this, gdPackageInfo, e)
150143

151-
if (error != null && !returnMessage.isNullOrEmpty()) {
152-
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_ERROR, error)
153-
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_MESSAGE, returnMessage)
154-
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE, returnExtendedMessage)
155-
}
144+
val is64bit = LaunchUtils.is64bit
145+
val errorMessage = when {
146+
abiMismatch && is64bit -> LaunchUtils.LauncherError.LINKER_NEEDS_32BIT
147+
abiMismatch -> LaunchUtils.LauncherError.LINKER_NEEDS_64BIT
148+
e.message?.contains("__gxx_personality_v0") == true ->
149+
LaunchUtils.LauncherError.LINKER_FAILED_STL
150+
else -> LaunchUtils.LauncherError.LINKER_FAILED
156151
}
157152

158-
startActivity(launchIntent)
159-
}
160-
161-
private fun tryLoadGame() {
162-
val gdPackageInfo = packageManager.getPackageInfo(Constants.PACKAGE_NAME, 0)
153+
LaunchUtils.FullLauncherError(
154+
errorMessage,
155+
e.message ?: "Unknown exception",
156+
e.stackTraceToString()
157+
)
158+
} catch (e: Exception) {
159+
Log.e("GeodeLauncher", "Uncaught error during game load", e)
163160

164-
setupRedirection(gdPackageInfo)
161+
LaunchUtils.FullLauncherError(
162+
LaunchUtils.LauncherError.GENERIC,
163+
e.message ?: "Unknown Exception",
164+
e.stackTraceToString()
165+
)
166+
}
165167

168+
private fun setupContext(): FrameLayout? {
166169
Cocos2dxHelper.init(this, this)
167170

168171
GeodeUtils.setContext(this)
169172
GeodeUtils.setCapabilityListener(this)
170173

171-
tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME)
172-
tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME)
173-
174-
if (GamePackageUtils.getGameVersionCode(packageManager) >= 39L) {
175-
// this fix requires geode v3, which is 2.206+
176-
// there is a short period in which 2.206 users will still have geode v2, but whatever. ig
177-
LauncherFix.enableExceptionsRenaming()
178-
}
179-
180-
LauncherFix.performPatches()
181-
182-
loadInternalMods()
183-
184-
val baseView = createView()
174+
onBackPressedDispatcher.addCallback(this, object : OnBackPressedCallback(true) {
175+
override fun handleOnBackPressed() {
176+
mGLSurfaceView?.sendKeyBack()
177+
}
178+
})
185179

186180
if (!GeodeUtils.handleSafeArea && Build.VERSION.SDK_INT >= Build.VERSION_CODES.BAKLAVA) {
187181
val frameLayoutParams = ViewGroup.LayoutParams(
@@ -204,15 +198,61 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
204198
WindowInsets.CONSUMED
205199
}
206200

207-
frameLayout.addView(baseView)
208201
setContentView(frameLayout)
209-
} else {
210-
setContentView(baseView)
202+
return frameLayout
203+
}
204+
205+
return null
206+
}
207+
208+
private suspend fun createVersionFile() = runCatching {
209+
val gameVersion = GamePackageUtils.getGameVersionCode(packageManager)
210+
211+
val versionPath = File(filesDir, "game_version.txt")
212+
213+
withContext(Dispatchers.IO) {
214+
if (versionPath.exists()) {
215+
versionPath.delete()
216+
}
217+
218+
versionPath.writeText("$gameVersion")
219+
}
220+
}
221+
222+
private fun returnToMain(message: LaunchUtils.FullLauncherError? = null, ) {
223+
val launchIntent = Intent(this, MainActivity::class.java).apply {
224+
flags = Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK
225+
226+
if (message != null) {
227+
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_ERROR, message.error)
228+
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_MESSAGE, message.returnMessage)
229+
putExtra(LaunchUtils.LAUNCHER_KEY_RETURN_EXTENDED_MESSAGE, message.extendedMessage)
230+
}
211231
}
212232

233+
startActivity(launchIntent)
234+
}
235+
236+
private suspend fun tryLoadGame(): Boolean {
237+
val gdPackageInfo = packageManager.getPackageInfo(Constants.PACKAGE_NAME, 0)
238+
setupRedirection(gdPackageInfo)
239+
240+
tryLoadLibrary(gdPackageInfo, Constants.FMOD_LIB_NAME)
241+
tryLoadLibrary(gdPackageInfo, Constants.COCOS_LIB_NAME)
242+
243+
if (GamePackageUtils.getGameVersionCode(packageManager) >= 39L) {
244+
// this fix requires geode v3, which is 2.206+
245+
// there is a short period in which 2.206 users will still have geode v2, but whatever. ig
246+
LauncherFix.enableExceptionsRenaming()
247+
}
248+
249+
LauncherFix.performPatches()
250+
251+
loadInternalMods()
252+
213253
setupPostLibraryLoad(gdPackageInfo)
214254

215-
val geodeFailed = try {
255+
return try {
216256
loadGeodeLibrary()
217257
false
218258
} catch (e: UnsatisfiedLinkError) {
@@ -222,8 +262,6 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
222262
handleGeodeException(e)
223263
true
224264
}
225-
226-
setupNotificationView(baseView, geodeFailed)
227265
}
228266

229267
private fun setupNotificationView(baseView: FrameLayout, geodeFailed: Boolean) {
@@ -283,7 +321,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
283321
}
284322

285323
@SuppressLint("UnsafeDynamicallyLoadedCode")
286-
private fun tryLoadLibrary(packageInfo: PackageInfo, libraryName: String) {
324+
private suspend fun tryLoadLibrary(packageInfo: PackageInfo, libraryName: String) {
287325
val nativeDir = getNativeLibraryDirectory(packageInfo.applicationInfo!!)
288326
val libraryPath = if (nativeDir.endsWith('/')) "${nativeDir}lib$libraryName.so" else "$nativeDir/lib$libraryName.so"
289327

@@ -302,7 +340,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
302340
}
303341

304342
@SuppressLint("UnsafeDynamicallyLoadedCode")
305-
private fun loadLibraryCopy(libraryName: String, libraryPath: String) {
343+
private suspend fun loadLibraryCopy(libraryName: String, libraryPath: String) {
306344
if (libraryPath.contains("!/")) {
307345
// library in apk can't be loaded directly
308346
loadLibraryFromAssetsCopy(libraryName)
@@ -312,19 +350,15 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
312350
val library = File(libraryPath)
313351
val libraryCopy = File(cacheDir, "lib$libraryName.so")
314352

315-
libraryCopy.outputStream().use { libraryOutput ->
316-
library.inputStream().use { inputStream ->
317-
DownloadUtils.copyFile(inputStream, libraryOutput)
318-
}
319-
}
353+
DownloadUtils.copyFile(library, libraryCopy)
320354

321355
System.load(libraryCopy.path)
322356

323357
return
324358
}
325359

326360
@SuppressLint("UnsafeDynamicallyLoadedCode")
327-
private fun loadLibraryFromAssetsCopy(libraryName: String) {
361+
private suspend fun loadLibraryFromAssetsCopy(libraryName: String) {
328362
// loads a library loaded in assets
329363
// this copies the library to a non-compressed directory
330364

@@ -339,9 +373,11 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
339373
// there doesn't seem to be a way to load a library from a file descriptor
340374
val libraryCopy = File(cacheDir, "lib$libraryName.so")
341375

342-
libraryCopy.outputStream().use { libraryOutput ->
343-
libraryFd.createInputStream().use { inputStream ->
344-
DownloadUtils.copyFile(inputStream, libraryOutput)
376+
withContext(Dispatchers.IO) {
377+
libraryCopy.outputStream().use { libraryOutput ->
378+
libraryFd.createInputStream().use { inputStream ->
379+
DownloadUtils.copyFile(inputStream, libraryOutput)
380+
}
345381
}
346382
}
347383

@@ -376,7 +412,7 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
376412
}
377413

378414
@SuppressLint("UnsafeDynamicallyLoadedCode")
379-
private fun loadGeodeLibrary() {
415+
private suspend fun loadGeodeLibrary() {
380416
// Load Geode if exists
381417
// bundling the object with the application allows for nicer backtraces
382418
try {
@@ -397,22 +433,23 @@ class GeometryDashActivity : AppCompatActivity(), Cocos2dxHelper.Cocos2dxHelperL
397433
// also i get 20 million permission denied errors
398434
val externalGeodePath = LaunchUtils.getInstalledGeodePath(this)!!
399435

400-
val copiedPath = File(filesDir.path, "copied")
436+
val copiedPath = File(filesDir, "copied")
401437
if (copiedPath.exists()) {
402438
copiedPath.deleteRecursively()
403439
}
404440
copiedPath.mkdir()
405441

406-
val copiedGeodePath = File(copiedPath.path, "Geode.so")
442+
val copiedGeodePath = File(copiedPath, "Geode.so")
407443

408444
if (externalGeodePath.exists()) {
409445
DownloadUtils.copyFile(
410-
FileInputStream(externalGeodePath),
411-
FileOutputStream(copiedGeodePath)
446+
externalGeodePath,
447+
copiedGeodePath
412448
)
413449

414450
if (copiedGeodePath.exists()) {
415451
println("Loading Geode from ${externalGeodePath.name}")
452+
416453
System.load(copiedGeodePath.path)
417454
return
418455
}

0 commit comments

Comments
 (0)