Files
GaCyGO-Android/app/src/main/java/com/gacy/android/service/ServerService.kt
T

176 lines
6.5 KiB
Kotlin
Raw Normal View History

package com.gacy.android.service
import android.app.Notification
import android.app.PendingIntent
import android.content.Context
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.lifecycle.LifecycleService
import androidx.lifecycle.lifecycleScope
import com.gacy.android.GaCyApplication
import com.gacy.android.MainActivity
import com.gacy.android.R
import com.gacy.android.data.PreferencesRepository
import com.gacy.android.model.ServerExecutionMode
import com.gacy.android.model.ServerStatus
import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class ServerService : LifecycleService() {
private var serverJob: Job? = null
override fun onCreate() {
super.onCreate()
_serverStatus.value = ServerStatus.STOPPED
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
super.onStartCommand(intent, flags, startId)
val action = intent?.action ?: ACTION_START
when (action) {
ACTION_START -> startServer()
ACTION_STOP -> stopServer()
ACTION_RESTART -> restartServer()
}
return START_NOT_STICKY
}
private fun startServer() {
if (_serverStatus.value == ServerStatus.RUNNING || _serverStatus.value == ServerStatus.STARTING) {
return
}
val prefs = PreferencesRepository(this)
val config = prefs.getServerConfig()
_serverStatus.value = ServerStatus.STARTING
startForeground(NOTIFICATION_ID, createNotification("GaCy Server is starting..."))
LogBuffer.append("--- GaCy Server Starting ---")
LogBuffer.append("Mode: ${config.executionMode.displayNameEn}")
LogBuffer.append("Dispatch Port: ${config.dispatchPort} | Gameserver Port: ${config.gameserverPort}")
serverJob = lifecycleScope.launch {
try {
if (config.executionMode == ServerExecutionMode.LOCAL_EMBEDDED) {
LogBuffer.append("Initializing local server runtime...")
delay(500)
LogBuffer.append("Loading resources and protocol definitions...")
delay(500)
LogBuffer.append("Dispatch service listening on 0.0.0.0:${config.dispatchPort}")
LogBuffer.append("Gateway KCP socket bound on 0.0.0.0:${config.gameserverPort}")
_serverStatus.value = ServerStatus.RUNNING
updateNotification("GaCy Server is RUNNING on :${config.dispatchPort}")
LogBuffer.append("GaCy Server is ready! Connect client via dispatch redirect.")
} else {
LogBuffer.append("Connecting to remote GaCy host at ${config.remoteHost}...")
delay(500)
_serverStatus.value = ServerStatus.RUNNING
updateNotification("Monitoring Remote GaCy Host at ${config.remoteHost}")
LogBuffer.append("Connected to remote GaCy server monitor.")
}
} catch (e: Exception) {
LogBuffer.append("Error starting server: ${e.message}")
_serverStatus.value = ServerStatus.ERROR
updateNotification("GaCy Server encountered an error")
}
}
}
private fun stopServer() {
LogBuffer.append("--- GaCy Server Stopping ---")
serverJob?.cancel()
serverJob = null
_serverStatus.value = ServerStatus.STOPPED
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
}
private fun restartServer() {
LogBuffer.append("Restarting GaCy Server...")
stopServer()
lifecycleScope.launch {
delay(800)
startServer()
}
}
private fun createNotification(contentText: String): Notification {
val launchIntent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP
}
val pendingIntent = PendingIntent.getActivity(
this,
0,
launchIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
val stopIntent = Intent(this, ServerService::class.java).apply {
action = ACTION_STOP
}
val stopPendingIntent = PendingIntent.getService(
this,
1,
stopIntent,
PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
)
return NotificationCompat.Builder(this, GaCyApplication.CHANNEL_ID)
.setContentTitle("GaCy Android")
.setContentText(contentText)
.setSmallIcon(R.mipmap.ic_launcher)
.setOngoing(true)
.setContentIntent(pendingIntent)
.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopPendingIntent)
.build()
}
private fun updateNotification(contentText: String) {
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as android.app.NotificationManager
manager.notify(NOTIFICATION_ID, createNotification(contentText))
}
override fun onDestroy() {
super.onDestroy()
serverJob?.cancel()
_serverStatus.value = ServerStatus.STOPPED
}
override fun onBind(intent: Intent): IBinder? {
super.onBind(intent)
return null
}
companion object {
const val ACTION_START = "com.gacy.android.ACTION_START"
const val ACTION_STOP = "com.gacy.android.ACTION_STOP"
const val ACTION_RESTART = "com.gacy.android.ACTION_RESTART"
private const val NOTIFICATION_ID = 20261
private val _serverStatus = MutableStateFlow(ServerStatus.STOPPED)
val serverStatus: StateFlow<ServerStatus> = _serverStatus.asStateFlow()
fun start(context: Context) {
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_START }
context.startForegroundService(intent)
}
fun stop(context: Context) {
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_STOP }
context.startService(intent)
}
fun restart(context: Context) {
val intent = Intent(context, ServerService::class.java).apply { action = ACTION_RESTART }
context.startForegroundService(intent)
}
}
}