LOGCAT
2025-01-08 00:51:14.284 28257-28257 AndroidRuntime com.sampv2.matite E FATAL EXCEPTION: main (Ask Gemini)
Process: com.sampv2.matite, PID: 28257
java.lang.UnsatisfiedLinkError: No implementation found for void com.sampv2.matite.game.SAMPGame.cleanupGame() (tried Java_com_sampv2_matite_game_SAMPGame_cleanupGame and Java_com_sampv2_matite_game_SAMPGame_cleanupGame__) - is the library loaded, e.g. System.loadLibrary?
at com.sampv2.matite.game.SAMPGame.cleanupGame(Native Method)
at com.sampv2.matite.game.SAMPGame.onDestroy(SAMPGame.kt:255)
at android.app.Activity.performDestroy(Activity.java:8504)
at android.app.Instrumentation.callActivityOnDestroy(Instrumentation.java:1377)
at android.app.ActivityThread.performDestroyActivity(ActivityThread.java:5538)
at android.app.ActivityThread.handleDestroyActivity(ActivityThread.java:5596)
at android.app.servertransaction.DestroyActivityItem.execute(DestroyActivityItem.java:47)
at android.app.servertransaction.ActivityTransactionItem.execute(ActivityTransactionItem.java:45)
at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2291)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:211)
at android.os.Looper.loop(Looper.java:300)
at android.app.ActivityThread.main(ActivityThread.java:8322)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:556)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1038)
---------------------------- PROCESS ENDED (28257) for package com.sampv2.matite ----------------------------package com.sampv2.matite.game
import android.app.ActivityManager
import android.content.Context
import android.hardware.SensorEvent
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.View
import android.view.WindowInsets
import android.view.WindowInsetsController
import android.view.WindowManager
import android.widget.Toast
import androidx.core.view.WindowCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import com.nvidia.devtech.NvEventQueueActivity
import com.sampv2.matite.R
import com.sampv2.matite.SampApplication
import com.sampv2.matite.data.repository.GameRepository
import com.sampv2.matite.network.RetrofitInstance
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.json.JSONObject
import java.io.File
import java.io.IOException
import javax.microedition.khronos.egl.EGL10
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.egl.EGLContext
import androidx.appcompat.app.AppCompatActivity
@Suppress("KotlinConstantConditions")
class SAMPGame : NvEventQueueActivity() {
private var isInitialized = false
private val gameJob = SupervisorJob()
private val gameScope = CoroutineScope(Dispatchers.Main + gameJob)
private val gameRepository: GameRepository by lazy { GameRepository(this, RetrofitInstance.api) }
companion object {
private const val TAG = "SAMPGame"
private const val ARM64_LIBS_JSON_URL = "https://sampmobile.com/oyundosya/arm64-v8a/arm64-v8a.json"
private const val ARM32_LIBS_JSON_URL = "https://sampmobile.com/oyundosya/armeabi-v7a/armeabi-v7a.json"
private const val GAME_FILES_JSON_URL = "https://sampmobile.com/oyundosya/files/files.json"
init {
try {
System.loadLibrary("samp")
Log.d(TAG, "SAMP kütüphanesi yüklendi")
} catch (e: UnsatisfiedLinkError) {
Log.e(TAG, "SAMP kütüphanesi yüklenemedi: ${e.message}")
}
}
private fun getGameDir(): String {
return File(SampApplication.instance.getExternalFilesDir(null), "SAMP").absolutePath
}
private fun getLibsJsonUrl(): String {
return if (Build.SUPPORTED_ABIS[0].contains("arm64")) {
ARM64_LIBS_JSON_URL
} else {
ARM32_LIBS_JSON_URL
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
setTheme(R.style.GameTheme)
super.onCreate(savedInstanceState)
// Tam ekran modunu ayarla
WindowCompat.setDecorFitsSystemWindows(window, false)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
window.attributes.layoutInDisplayCutoutMode =
WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES
}
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
val windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
windowInsetsController.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
// Ekranı tam ekran yap
@Suppress("DEPRECATION")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false)
window.insetsController?.let { controller ->
controller.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
}
// OpenGL ES bağlamını oluştur
initializeOpenGL()
// Oyunu başlat
startGame()
}
private fun initializeOpenGL() {
try {
val egl = EGLContext.getEGL() as EGL10
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
if (display == EGL10.EGL_NO_DISPLAY) {
throw RuntimeException("eglGetDisplay başarısız oldu")
}
val version = IntArray(2)
if (!egl.eglInitialize(display, version)) {
throw RuntimeException("eglInitialize başarısız oldu")
}
val configAttribs = intArrayOf(
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_STENCIL_SIZE, 8,
EGL10.EGL_SAMPLE_BUFFERS, 1,
EGL10.EGL_SAMPLES, 4,
EGL10.EGL_NONE
)
val configs = arrayOfNulls<EGLConfig>(1)
val numConfig = IntArray(1)
if (!egl.eglChooseConfig(display, configAttribs, configs, 1, numConfig)) {
throw RuntimeException("eglChooseConfig başarısız oldu")
}
val surfaceAttribs = intArrayOf(
EGL10.EGL_WIDTH, 1,
EGL10.EGL_HEIGHT, 1,
EGL10.EGL_NONE
)
val surface = egl.eglCreatePbufferSurface(display, configs[0], surfaceAttribs)
if (surface == EGL10.EGL_NO_SURFACE) {
throw RuntimeException("eglCreatePbufferSurface başarısız oldu")
}
val contextAttribs = intArrayOf(
0x3098, // EGL_CONTEXT_CLIENT_VERSION değeri
3, // OpenGL ES 3.0 için versiyon
EGL10.EGL_NONE
)
val context = egl.eglCreateContext(display, configs[0], EGL10.EGL_NO_CONTEXT, contextAttribs)
if (context == EGL10.EGL_NO_CONTEXT) {
throw RuntimeException("eglCreateContext başarısız oldu")
}
if (!egl.eglMakeCurrent(display, surface, surface, context)) {
throw RuntimeException("eglMakeCurrent başarısız oldu")
}
Log.d(TAG, "OpenGL ES bağlamı başarıyla oluşturuldu")
isInitialized = true
} catch (e: Exception) {
Log.e(TAG, "OpenGL ES bağlamı oluşturma hatası: ${e.message}", e)
throw e
}
}
private fun startGame() {
gameScope.launch(Dispatchers.Main + CoroutineExceptionHandler { _, exception ->
Log.e(TAG, "Coroutine hatası: ${exception.message}", exception)
when (exception) {
is CancellationException -> {
Log.d(TAG, "İşlem iptal edildi, yeniden deneniyor...")
retryGameInitialization()
}
else -> {
val errorMessage = when (exception) {
is IOException -> "Dosya indirme hatası: ${exception.message}"
else -> "Beklenmeyen hata: ${exception.message}"
}
Toast.makeText(this@SAMPGame, errorMessage, Toast.LENGTH_LONG).show()
finish()
}
}
}) {
try {
withContext(Dispatchers.IO) {
gameRepository.logGameEvent("Oyun açılıyor...")
// Kütüphaneleri kontrol et ve indir
if (!checkRequiredLibraries()) {
downloadRequiredLibraries()
}
// Oyun dosyalarını kontrol et ve indir
downloadGameFiles()
// Oyunu başlat
withContext(Dispatchers.Main) {
initializeGame()
isInitialized = true
}
}
} catch (e: Exception) {
Log.e(TAG, "Oyun başlatma hatası: ${e.message}", e)
withContext(Dispatchers.Main) {
when (e) {
is CancellationException -> {
Log.d(TAG, "İşlem iptal edildi, yeniden deneniyor...")
retryGameInitialization()
}
else -> {
val errorMessage = when (e) {
is IOException -> "Dosya indirme hatası: ${e.message}"
else -> "Beklenmeyen hata: ${e.message}"
}
Toast.makeText(this@SAMPGame, errorMessage, Toast.LENGTH_LONG).show()
finish()
}
}
}
}
}
}
private fun retryGameInitialization() {
gameJob.cancel() // Mevcut işi iptal et
gameScope.launch(Dispatchers.IO) {
delay(1000) // 1 saniye bekle
withContext(Dispatchers.Main) {
onCreate(null) // Başlatma işlemini tekrar dene
}
}
}
override fun onDestroy() {
try {
if (isInitialized) {
Log.d(TAG, "Oyun kapatılıyor...")
gameRepository.logGameEvent("Oyun kapatılıyor...")
cleanupGame()
isInitialized = false
}
} catch (e: Exception) {
Log.e(TAG, "Oyun kapatma hatası: ${e.message}", e)
} finally {
gameJob.cancel()
super.onDestroy()
}
}
private suspend fun downloadRequiredLibraries() {
withContext(Dispatchers.IO) {
try {
Log.d(TAG, "Native kütüphane listesi indiriliyor...")
val response = RetrofitInstance.api.getLibraryList(getLibsJsonUrl())
if (!response.isSuccessful) {
Log.e(TAG, "Kütüphane listesi alınamadı: HTTP ${response.code()}")
// Eğer kütüphaneler zaten varsa devam et
if (checkRequiredLibraries()) {
Log.d(TAG, "Kütüphaneler zaten mevcut, devam ediliyor")
return@withContext
}
throw IOException("Kütüphane listesi alınamadı: HTTP ${response.code()}")
}
val responseBody = response.body() ?: throw IOException("Kütüphane listesi boş")
val jsonString = responseBody.byteStream().bufferedReader().use { it.readText() }
Log.d(TAG, "JSON yanıtı: $jsonString")
try {
val jsonObject = JSONObject(jsonString.trim())
val libsArray = jsonObject.getJSONArray("files")
val gameDir = File(getGameDir())
if (!gameDir.exists()) {
gameDir.mkdirs()
}
Log.d(TAG, "Native kütüphaneleri indiriliyor...")
for (i in 0 until libsArray.length()) {
val lib = libsArray.getJSONObject(i)
val libName = lib.getString("name")
val libUrl = lib.getString("url")
val targetFile = File(gameDir, libName)
if (!targetFile.exists()) {
try {
Log.d(TAG, "Kütüphane indiriliyor: $libName")
val libResponse = RetrofitInstance.api.downloadFile(libUrl)
if (libResponse.isSuccessful) {
libResponse.body()?.byteStream()?.use { input ->
targetFile.outputStream().use { output ->
input.copyTo(output)
}
}
// İzinleri ayarla
targetFile.setExecutable(true, false)
targetFile.setReadable(true, false)
targetFile.setWritable(true, false)
Log.d(TAG, "Kütüphane başarıyla indirildi: $libName")
} else {
Log.e(TAG, "Kütüphane indirilemedi: $libName (HTTP ${libResponse.code()})")
// Eğer kütüphane zaten varsa devam et
if (!targetFile.exists()) {
throw IOException("Kütüphane indirilemedi: $libName")
}
}
} catch (e: Exception) {
Log.e(TAG, "Kütüphane indirme hatası - $libName: ${e.message}")
// Eğer kütüphane zaten varsa devam et
if (!targetFile.exists()) {
throw e
}
}
} else {
Log.d(TAG, "Kütüphane zaten mevcut: $libName")
// İzinleri kontrol et ve ayarla
targetFile.setExecutable(true, false)
targetFile.setReadable(true, false)
targetFile.setWritable(true, false)
}
}
} catch (e: Exception) {
Log.e(TAG, "JSON ayrıştırma hatası: ${e.message}")
Log.e(TAG, "JSON içeriği: $jsonString")
throw e
}
} catch (e: Exception) {
Log.e(TAG, "Native kütüphane indirme hatası", e)
// Eğer kütüphaneler zaten varsa devam et
if (checkRequiredLibraries()) {
Log.d(TAG, "Kütüphaneler zaten mevcut, devam ediliyor")
return@withContext
}
throw e
}
}
}
private fun checkRequiredLibraries(): Boolean {
val gameDir = File(getGameDir())
val cpuArch = Build.SUPPORTED_ABIS[0]
val requiredLibs = if (cpuArch.contains("arm64")) {
listOf(
"libc++_shared.so",
"libGTASA.so",
"libSCAnd.so",
"libbass.so",
"libOpenAL64.so",
"libsamp.so",
"libcrashlytics.so",
"libcrashlytics-common.so",
"libcrashlytics-handler.so",
"libcrashlytics-trampoline.so",
"libshadowhook.so"
)
} else {
listOf(
"libc++_shared.so",
"libGTASA.so",
"libImmEmulatorJ.so",
"libSCAnd.so",
"libbass.so",
"libOpenAL32.so",
"libsamp.so",
"libcrashlytics.so",
"libcrashlytics-common.so",
"libcrashlytics-handler.so",
"libcrashlytics-trampoline.so",
"libshadowhook.so"
)
}
return requiredLibs.all { libName ->
val libFile = File(gameDir, libName)
val exists = libFile.exists()
if (!exists) {
Log.d(TAG, "Eksik kütüphane: $libName")
}
exists
}
}
private suspend fun downloadGameFiles() {
withContext(Dispatchers.IO) {
try {
Log.d(TAG, "Oyun dosyaları listesi indiriliyor...")
val response = RetrofitInstance.api.getLibraryList(GAME_FILES_JSON_URL)
if (!response.isSuccessful) {
Log.e(TAG, "Oyun dosyaları listesi alınamadı: HTTP ${response.code()}")
throw IOException("Oyun dosyaları listesi alınamadı: HTTP ${response.code()}")
}
val responseBody = response.body() ?: throw IOException("Oyun dosyaları listesi boş")
val jsonString = responseBody.byteStream().bufferedReader().use { it.readText() }
try {
val jsonObject = JSONObject(jsonString.trim())
val filesArray = jsonObject.getJSONArray("files")
val gameDir = File(getGameDir())
if (!gameDir.exists()) {
gameDir.mkdirs()
}
Log.d(TAG, "Oyun dosyaları indiriliyor...")
for (i in 0 until filesArray.length()) {
val file = filesArray.getJSONObject(i)
val fileName = file.getString("path")
val fileUrl = file.getString("url")
val targetFile = File(gameDir, fileName)
val targetDir = targetFile.parentFile
targetDir?.let { dir ->
if (!dir.exists()) {
dir.mkdirs()
}
}
if (!targetFile.exists()) {
try {
Log.d(TAG, "Dosya indiriliyor: $fileName")
val fileResponse = RetrofitInstance.api.downloadFile(fileUrl)
if (fileResponse.isSuccessful) {
fileResponse.body()?.byteStream()?.use { input ->
targetFile.outputStream().use { output ->
input.copyTo(output)
}
}
Log.d(TAG, "Dosya başarıyla indirildi: $fileName")
} else {
Log.e(TAG, "Dosya indirilemedi: $fileName (HTTP ${fileResponse.code()})")
throw IOException("Dosya indirilemedi: $fileName")
}
} catch (e: Exception) {
Log.e(TAG, "Dosya indirme hatası - $fileName: ${e.message}")
throw e
}
} else {
Log.d(TAG, "Dosya zaten mevcut: $fileName")
}
}
Log.d(TAG, "Tüm oyun dosyaları indirildi")
} catch (e: Exception) {
Log.e(TAG, "JSON ayrıştırma hatası: ${e.message}")
throw e
}
} catch (e: Exception) {
Log.e(TAG, "Oyun dosyaları indirme hatası", e)
throw e
}
}
}
private fun loadSystemLibraries() {
try {
// Önce sistem kütüphanelerini yükle
System.loadLibrary("c++_shared")
Log.d(TAG, "libc++_shared.so yüklendi")
Thread.sleep(100)
System.loadLibrary("GLESv2")
Log.d(TAG, "libGLESv2.so yüklendi")
Thread.sleep(100)
System.loadLibrary("EGL")
Log.d(TAG, "libEGL.so yüklendi")
Thread.sleep(100)
// ImmEmulator sadece 32-bit için
if (!Build.SUPPORTED_ABIS[0].contains("arm64")) {
System.loadLibrary("ImmEmulatorJ")
Log.d(TAG, "libImmEmulatorJ.so yüklendi")
Thread.sleep(100)
}
} catch (e: UnsatisfiedLinkError) {
Log.e(TAG, "Sistem kütüphaneleri yüklenemedi: ${e.message}")
throw IOException("Sistem kütüphaneleri yüklenemedi: ${e.message}")
}
}
private fun loadGameLibraries() {
val libsToLoad = mutableListOf(
"GTASA",
"SCAnd",
"bass",
if (Build.SUPPORTED_ABIS[0].contains("arm64")) "OpenAL64" else "OpenAL32",
"samp"
)
// Crashlytics kütüphanelerini ekle
libsToLoad.addAll(listOf(
"crashlytics",
"crashlytics-common",
"crashlytics-handler",
"crashlytics-trampoline"
))
// Son olarak shadowhook'u ekle
libsToLoad.add("shadowhook")
for (lib in libsToLoad) {
try {
System.loadLibrary(lib)
Log.d(TAG, "$lib kütüphanesi yüklendi")
Thread.sleep(100) // Her kütüphane yüklemesi arasında kısa bir bekleme
} catch (e: UnsatisfiedLinkError) {
Log.e(TAG, "$lib kütüphanesi yüklenemedi: ${e.message}")
// Eğer kritik bir kütüphane ise hata fırlat
if (lib in listOf("GTASA", "SCAnd", "samp")) {
throw IOException("$lib kütüphanesi yüklenemedi: ${e.message}")
}
}
}
}
private fun checkSAMPData(): String? {
val sampDir = File(getGameDir(), "SAMP")
if (!sampDir.exists() || !sampDir.isDirectory) {
return "SAMP veri dizini bulunamadı"
}
// SAMP veri dosyalarını kontrol et
val requiredSampFiles = listOf(
"samp.dat" to "SAMP Ayar Dosyası",
"handling.cfg" to "Araç Ayarları",
"vehicles.txt" to "Araç Listesi",
"weapons.txt" to "Silah Listesi",
"animations.txt" to "Animasyon Listesi",
"server.cfg" to "Sunucu Ayarları"
)
for ((file, description) in requiredSampFiles) {
val sampFile = File(sampDir, file)
if (!sampFile.exists()) {
Log.e(TAG, "Eksik SAMP dosyası: $description ($file)")
return "Eksik SAMP dosyası: $description"
}
if (sampFile.length() == 0L) {
Log.e(TAG, "Bozuk SAMP dosyası: $description ($file)")
return "Bozuk SAMP dosyası: $description"
}
}
return null
}
private fun checkGameFiles(): String? {
// Önce SAMP verilerini kontrol et
checkSAMPData()?.let { error ->
return error
}
// Diğer oyun dosyalarını kontrol et
val gameDir = File(getGameDir())
// Kritik dosyaların kontrolü
val requiredFiles = mapOf(
"SAMP/gta.dat" to "GTA:SA Temel Ayar Dosyası",
"SAMP/handling.cfg" to "Araç Fizik Ayarları",
"texdb/gta3.img" to "GTA:SA Temel Texture Dosyası",
"texdb/samp.img" to "SAMP Texture Dosyası",
"models/gta3.img" to "GTA:SA Model Dosyası",
"SAMP/script.img" to "SAMP Script Dosyası",
"audio/SFX/GENRL.osw" to "Ses Dosyası"
)
for ((file, description) in requiredFiles) {
val gameFile = File(gameDir, file)
if (!gameFile.exists()) {
Log.e(TAG, "Eksik dosya: $description ($file)")
return "Eksik dosya: $description"
}
if (gameFile.length() == 0L) {
Log.e(TAG, "Bozuk/boş dosya: $description ($file)")
return "Bozuk/boş dosya: $description"
}
}
return null
}
private fun checkOpenGLSupport(): String? {
try {
val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val configurationInfo = activityManager.deviceConfigurationInfo
// OpenGL ES 3.1 kontrolü
if (configurationInfo.reqGlEsVersion < 0x30001) {
return "Bu cihaz OpenGL ES 3.1'i desteklemiyor. Cihazınızın OpenGL ES sürümü: " +
"${configurationInfo.reqGlEsVersion}"
}
// Android Extension Pack kontrolü
val pm = packageManager
val hasAEP = pm.hasSystemFeature("android.hardware.opengles.aep")
if (!hasAEP) {
return "Bu cihaz Android Extension Pack'i (AEP) desteklemiyor"
}
// OpenGL ES uzantılarını kontrol et
val egl = EGLContext.getEGL() as EGL10
val display = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)
egl.eglInitialize(display, null)
val configAttrs = intArrayOf(
EGL10.EGL_RED_SIZE, 8,
EGL10.EGL_GREEN_SIZE, 8,
EGL10.EGL_BLUE_SIZE, 8,
EGL10.EGL_ALPHA_SIZE, 8,
EGL10.EGL_DEPTH_SIZE, 16,
EGL10.EGL_STENCIL_SIZE, 8,
EGL10.EGL_NONE
)
val configs = arrayOfNulls<EGLConfig>(1)
val numConfig = IntArray(1)
egl.eglChooseConfig(display, configAttrs, configs, 1, numConfig)
// OpenGL ES 3.1 context oluştur
val contextAttrs = intArrayOf(
0x3098, // EGL_CONTEXT_CLIENT_VERSION değeri
3, // OpenGL ES 3.1 için versiyon
EGL10.EGL_NONE
)
val context = egl.eglCreateContext(display, configs[0], EGL10.EGL_NO_CONTEXT, contextAttrs)
if (context == EGL10.EGL_NO_CONTEXT) {
return "OpenGL ES 3.1 context oluşturulamadı"
}
val gl = context.gl as javax.microedition.khronos.opengles.GL10
val extensions = gl.glGetString(javax.microedition.khronos.opengles.GL10.GL_EXTENSIONS)
// Gerekli uzantıları kontrol et
val requiredExtensions = listOf(
"GL_ANDROID_extension_pack_es31a",
"GL_KHR_debug",
"GL_KHR_texture_compression_astc_ldr",
"GL_KHR_blend_equation_advanced"
)
for (ext in requiredExtensions) {
if (!extensions.contains(ext)) {
return "Gerekli OpenGL ES uzantısı eksik: $ext"
}
}
// Kaynakları temizle
egl.eglDestroyContext(display, context)
egl.eglTerminate(display)
return null
} catch (e: Exception) {
Log.e(TAG, "OpenGL ES kontrol hatası:", e)
return "OpenGL ES kontrol hatası: ${e.message}"
}
}
private fun initializeGame() {
try {
Log.d(TAG, "Oyun başlatılıyor...")
// Önce sistem kütüphanelerini yükle
loadSystemLibraries()
// Sonra oyun kütüphanelerini yükle
loadGameLibraries()
// Oyun dosyalarını kontrol et
checkGameFiles()?.let { error ->
Log.e(TAG, "Eksik dosya/kütüphane tespit edildi: $error")
throw IOException(error)
}
// OpenGL ES kontrolü
checkOpenGLSupport()?.let { error ->
Log.e(TAG, "OpenGL ES hatası: $error")
throw Exception(error)
}
// GUI'yi başlat
initializeGUI()
// Oyun parametrelerini al
val serverIp = intent.getStringExtra("server_ip") ?: ""
val serverPort = intent.getIntExtra("server_port", 7777)
val nickname = intent.getStringExtra("nickname") ?: ""
// Sunucuya bağlan
Log.d(TAG, "[dbg:raknet:client:connect] : connecting to game server '$serverIp:$serverPort'...")
startSAMP(serverIp, serverPort, nickname)
Log.d(TAG, "[dbg:raknet:client:connect] : connected")
Log.d(TAG, "Oyun başarıyla başlatıldı")
} catch (e: Exception) {
Log.e(TAG, "Oyun başlatma hatası:", e)
runOnUiThread {
val errorMessage = "Hata: ${e.message}\n\n" +
"Lütfen şunları kontrol edin:\n" +
"1. Tüm oyun dosyalarının tam olduğunu\n" +
"2. Cihazınızın OpenGL ES 3.1'i desteklediğini\n" +
"3. Yeterli depolama alanı olduğunu\n" +
"4. Yeterli RAM olduğunu"
Toast.makeText(this, errorMessage, Toast.LENGTH_LONG).show()
}
throw e
}
}
private fun initializeGUI() {
val displayMetrics = resources.displayMetrics
Log.d(TAG, "GUI | Display size: ${displayMetrics.widthPixels}, ${displayMetrics.heightPixels}")
Log.d(TAG, "GUI | Scale factor: ${displayMetrics.density}")
}
override fun onResume() {
try {
super.onResume()
Log.d(TAG, "SAMPGame onResume çağrıldı")
// Tam ekran modunu ayarla
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
window.setDecorFitsSystemWindows(false)
window.insetsController?.let { controller ->
controller.hide(WindowInsets.Type.statusBars() or WindowInsets.Type.navigationBars())
controller.systemBarsBehavior = WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
} else {
@Suppress("DEPRECATION")
window.decorView.systemUiVisibility = (View.SYSTEM_UI_FLAG_FULLSCREEN
or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION)
}
// OpenGL ES bağlamını yeniden oluştur
if (!isInitialized) {
initializeOpenGL()
}
} catch (e: Exception) {
Log.e(TAG, "onResume hatası: ${e.message}", e)
// Kritik hata durumunda aktiviteyi sonlandır
finish()
}
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
return if (keyCode == KeyEvent.KEYCODE_BACK) {
true // Geri tuşunu engelle
} else {
super.onKeyDown(keyCode, event)
}
}
override fun onSensorChanged(event: SensorEvent) {
// Sensör değişikliklerini işle
// Bu metod boş bırakılabilir çünkü oyun sensör kullanmıyor
}
// Native metodları tanımla
private external fun startSAMP(serverIp: String, serverPort: Int, nickname: String)
private external fun cleanupGame()
}