Server Cleanup (#13598)

* update server dependencies and do necessary cleanup for chat support later

* resolve versions issue
This commit is contained in:
Md. Touhidur Rahman
2025-07-11 19:52:39 +06:00
committed by GitHub
parent cbf5f551f2
commit 3616a39caa
4 changed files with 56 additions and 33 deletions

3
.gitignore vendored
View File

@ -170,3 +170,6 @@ detekt/reports.html
# When you test the wiki by running mkdocs locally:
/site/
# When running the desktop app locally using "gradlew desktop:run"
GameSettings.json

View File

@ -1,9 +1,10 @@
import com.unciv.build.BuildConfig.appVersion
import com.unciv.build.BuildConfig.coroutinesVersion
import com.unciv.build.BuildConfig.gdxVersion
import com.unciv.build.BuildConfig.jnaVersion
import com.unciv.build.BuildConfig.kotlinVersion
import com.unciv.build.BuildConfig.ktorVersion
import com.unciv.build.BuildConfig.appVersion
buildscript {
@ -89,21 +90,28 @@ project(":desktop") {
"implementation"("com.github.MinnDevelopment:java-discord-rpc:v2.0.1")
// Needed for Windows turn notifiers
"implementation"("net.java.dev.jna:jna:5.11.0")
"implementation"("net.java.dev.jna:jna-platform:5.11.0")
"implementation"("net.java.dev.jna:jna:$jnaVersion")
"implementation"("net.java.dev.jna:jna-platform:$jnaVersion")
}
}
// For server-side
project(":server") {
apply(plugin = "kotlin")
apply(plugin = "org.jetbrains.kotlin.plugin.serialization")
dependencies {
// For server-side
"implementation"("io.ktor:ktor-server-core:1.6.8")
"implementation"("io.ktor:ktor-server-netty:1.6.8")
"implementation"("ch.qos.logback:logback-classic:1.2.5")
"implementation"("com.github.ajalt.clikt:clikt:3.4.0")
"implementation"("io.ktor:ktor-server-core:$ktorVersion")
"implementation"("io.ktor:ktor-server-netty:$ktorVersion")
"implementation"("io.ktor:ktor-server-content-negotiation:$ktorVersion")
"implementation"("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
"implementation"("ch.qos.logback:logback-classic:1.5.18")
"implementation"("com.github.ajalt.clikt:clikt:4.4.0")
// clikt somehow needs this
"implementation"("net.java.dev.jna:jna:$jnaVersion")
"implementation"("net.java.dev.jna:jna-platform:$jnaVersion")
}
}

View File

@ -8,8 +8,9 @@ object BuildConfig {
const val appVersion = "4.17.4"
const val gdxVersion = "1.13.1"
const val ktorVersion = "2.3.12"
const val ktorVersion = "2.3.13"
const val coroutinesVersion = "1.8.1"
const val jnaVersion = "5.17.0"
const val identifier = "com.unciv.app"
}

View File

@ -6,31 +6,38 @@ import com.github.ajalt.clikt.parameters.options.flag
import com.github.ajalt.clikt.parameters.options.option
import com.github.ajalt.clikt.parameters.types.int
import com.github.ajalt.clikt.parameters.types.restrictTo
import io.ktor.application.call
import io.ktor.application.log
import io.ktor.http.HttpStatusCode
import io.ktor.request.receiveText
import io.ktor.response.respond
import io.ktor.response.respondText
import io.ktor.routing.get
import io.ktor.routing.put
import io.ktor.routing.routing
import io.ktor.serialization.kotlinx.json.json
import io.ktor.server.application.call
import io.ktor.server.application.install
import io.ktor.server.application.log
import io.ktor.server.engine.embeddedServer
import io.ktor.server.engine.stop
import io.ktor.server.netty.Netty
import io.ktor.server.plugins.contentnegotiation.ContentNegotiation
import io.ktor.server.request.receiveText
import io.ktor.server.response.respond
import io.ktor.server.response.respondText
import io.ktor.server.routing.get
import io.ktor.server.routing.put
import io.ktor.server.routing.routing
import io.ktor.utils.io.jvm.javaio.toInputStream
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.serialization.Serializable
import java.io.File
import java.util.Base64
import java.util.concurrent.TimeUnit
import kotlin.io.encoding.Base64
import kotlin.io.encoding.ExperimentalEncodingApi
internal object UncivServer {
@JvmStatic
fun main(args: Array<String>) = UncivServerRunner().main(args)
}
@Serializable
data class IsAliveInfo(val authVersion: Int)
private class UncivServerRunner : CliktCommand() {
private val port by option(
"-p", "-port",
@ -102,6 +109,7 @@ private class UncivServerRunner : CliktCommand() {
return authMap[userId] == null || authMap[userId] == password
}
@OptIn(ExperimentalEncodingApi::class)
private fun extractAuth(authHeader: String?): Pair<String, String>? {
if (!authV1Enabled)
return null
@ -110,8 +118,8 @@ private class UncivServerRunner : CliktCommand() {
if (authHeader == null || !authHeader.startsWith("Basic "))
return null
val decodedString = String(Base64.getDecoder().decode(authHeader.drop(6)))
val splitAuthString = decodedString.split(":", limit=2)
val decodedString = Base64.Default.decode(authHeader.drop(6)).decodeToString()
val splitAuthString = decodedString.split(":", limit = 2)
if (splitAuthString.size != 2)
return null
@ -121,24 +129,27 @@ private class UncivServerRunner : CliktCommand() {
private fun serverRun(serverPort: Int, fileFolderName: String) {
val portStr: String = if (serverPort == 80) "" else ":$serverPort"
val isAliveInfo = IsAliveInfo(authVersion = if (authV1Enabled) 1 else 0)
val file = File(fileFolderName)
echo("Starting UncivServer for ${file.absolutePath} on http://localhost$portStr")
if (!file.exists()) file.mkdirs()
val server = embeddedServer(Netty, port = serverPort) {
install(ContentNegotiation) { json() }
routing {
get("/isalive") {
log.info("Received isalive request from ${call.request.local.remoteHost}")
call.respondText("{authVersion: ${if (authV1Enabled) "1" else "0"}}")
call.application.log.info("Received isalive request from ${call.request.local.remoteHost}")
call.respond(isAliveInfo)
}
put("/files/{fileName}") {
val fileName = call.parameters["fileName"] ?: throw Exception("No fileName!")
// If IdentifyOperators is enabled an Operator IP is displayed
if (identifyOperators) {
log.info("Receiving file: $fileName --Operation sourced from ${call.request.local.remoteHost}")
}else{
log.info("Receiving file: $fileName")
call.application.log.info("Receiving file: $fileName --Operation sourced from ${call.request.local.remoteHost}")
} else {
call.application.log.info("Receiving file: $fileName")
}
val file = File(fileFolderName, fileName)
@ -160,9 +171,9 @@ private class UncivServerRunner : CliktCommand() {
// If IdentifyOperators is enabled an Operator IP is displayed
if (identifyOperators) {
log.info("File requested: $fileName --Operation sourced from ${call.request.local.remoteHost}")
}else{
log.info("File requested: $fileName")
call.application.log.info("File requested: $fileName --Operation sourced from ${call.request.local.remoteHost}")
} else {
call.application.log.info("File requested: $fileName")
}
val file = File(fileFolderName, fileName)
@ -170,9 +181,9 @@ private class UncivServerRunner : CliktCommand() {
// If IdentifyOperators is enabled an Operator IP is displayed
if (identifyOperators) {
log.info("File $fileName not found --Operation sourced from ${call.request.local.remoteHost}")
call.application.log.info("File $fileName not found --Operation sourced from ${call.request.local.remoteHost}")
} else {
log.info("File $fileName not found")
call.application.log.info("File $fileName not found")
}
call.respond(HttpStatusCode.NotFound, "File does not exist")
return@get
@ -183,7 +194,7 @@ private class UncivServerRunner : CliktCommand() {
}
if (authV1Enabled) {
get("/auth") {
log.info("Received auth request from ${call.request.local.remoteHost}")
call.application.log.info("Received auth request from ${call.request.local.remoteHost}")
val authHeader = call.request.headers["Authorization"] ?: run {
call.respond(HttpStatusCode.BadRequest, "Missing authorization header!")
@ -202,7 +213,7 @@ private class UncivServerRunner : CliktCommand() {
}
}
put("/auth") {
log.info("Received auth password set from ${call.request.local.remoteHost}")
call.application.log.info("Received auth password set from ${call.request.local.remoteHost}")
val authHeader = call.request.headers["Authorization"]
if (validateAuth(authHeader)) {
val (userId, _) = extractAuth(authHeader) ?: Pair(null, null)