diff --git a/gradle.properties b/gradle.properties index 3f633af..d191d44 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,4 +1,4 @@ kotlin.code.style=official kotlin.stdlib.default.dependency=false org.gradle.parallel=true -version=2.4.5 +version=2.4.6 diff --git a/surf-core-launcher/surf-core-launcher-server/build.gradle.kts b/surf-core-launcher/surf-core-launcher-server/build.gradle.kts index 5e8c352..321fe45 100644 --- a/surf-core-launcher/surf-core-launcher-server/build.gradle.kts +++ b/surf-core-launcher/surf-core-launcher-server/build.gradle.kts @@ -12,6 +12,7 @@ dependencies { tasks.jar { manifest { attributes["Main-Class"] = "dev.slne.surf.core.launcher.server.CoreLauncherKt" + attributes["Implementation-Version"] = project.version } } diff --git a/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/CoreLauncher.kt b/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/CoreLauncher.kt index c208ae6..1d034d4 100644 --- a/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/CoreLauncher.kt +++ b/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/CoreLauncher.kt @@ -116,6 +116,9 @@ object CoreLauncher { serverOnline.value = true println("$LOG_PREFIX Server is now online.") printStartupErrorReport() + launch { + PluginUpdater.gitHubClient.checkForCoreLauncherUpdate() + } } } } diff --git a/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/github/GitHubClient.kt b/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/github/GitHubClient.kt index 44f9ef4..2f6bd05 100644 --- a/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/github/GitHubClient.kt +++ b/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/github/GitHubClient.kt @@ -2,27 +2,39 @@ package dev.slne.surf.core.launcher.server.updater.github import com.fasterxml.jackson.core.type.TypeReference import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper -import dev.slne.surf.core.launcher.server.updater.UpdatablePlugin +import dev.slne.surf.core.launcher.api.LauncherConstants +import dev.slne.surf.core.launcher.server.CoreLauncher +import dev.slne.surf.core.launcher.server.LOG_PREFIX +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.withContext import java.net.HttpURLConnection import java.net.URI import java.nio.file.Files import java.nio.file.Path import java.nio.file.StandardCopyOption +private const val CORE_REPOSITORY = + "https://api.github.com/repos/SLNE-Development/surf-core/releases/latest" + class GitHubClient(private val token: String?) { private val mapper = jacksonObjectMapper() - fun fetchLatestRelease(plugin: UpdatablePlugin): Map? = runCatching { - val connection = openConnection(plugin.latestReleaseUrl, "application/vnd.github+json") - connection.setRequestProperty("X-GitHub-Api-Version", "2022-11-28") - connection.connect() + suspend fun fetchLatestRelease(url: String): Map? = runCatching { + withContext(Dispatchers.IO) { + val connection = openConnection(url, "application/vnd.github+json") + connection.setRequestProperty("X-GitHub-Api-Version", "2022-11-28") + connection.connect() - if (connection.responseCode != 200) { - return null - } + if (connection.responseCode != 200) { + if (CoreLauncher.config.logGithubReleaseFetchFailures) { + println("$LOG_PREFIX (GitHubClient) Failed to fetch latest release from $url, response code: ${connection.responseCode}") + } + return@withContext null + } - connection.inputStream.use { input -> - mapper.readValue(input, object : TypeReference>() {}) + connection.inputStream.use { + mapper.readValue(it, object : TypeReference>() {}) + } } }.getOrNull() @@ -44,4 +56,57 @@ class GitHubClient(private val token: String?) { token?.let { connection.setRequestProperty("Authorization", "Bearer $it") } return connection } + + suspend fun checkForCoreLauncherUpdate() = withContext(Dispatchers.IO) { + val currentVersion = LauncherConstants::class.java.`package`?.implementationVersion + ?: return@withContext + + val release = fetchLatestRelease(CORE_REPOSITORY) ?: return@withContext + val latestVersion = release["tag_name"] as? String ?: return@withContext + + if (!isNewerVersion(currentVersion, latestVersion)) { + return@withContext + } + + val asset = (release["assets"] as? List<*>) + ?.filterIsInstance>() + ?.firstOrNull { + (it["name"] as? String) + ?.startsWith("surf-core-launcher-server-") == true + } + + val userUrl = asset?.get("html_url") as? String + ?: release["html_url"] as? String + ?: return@withContext + + println("$LOG_PREFIX " + "*".repeat(80)) + println("$LOG_PREFIX There is a new version of CoreLauncher available (v$currentVersion -> $latestVersion).") + println("$LOG_PREFIX Download here: $userUrl") + println("$LOG_PREFIX " + "*".repeat(80)) + } + + private fun isNewerVersion(current: String, latest: String): Boolean { + fun normalize(version: String) = version + .removePrefix("v") + .substringBefore('-') + .split('.') + .map { it.toIntOrNull() ?: 0 } + + val currentParts = normalize(current) + val latestParts = normalize(latest) + + val max = maxOf(currentParts.size, latestParts.size) + + for (i in 0 until max) { + val currentPart = currentParts.getOrElse(i) { 0 } + val latestPart = latestParts.getOrElse(i) { 0 } + + when { + latestPart > currentPart -> return true + latestPart < currentPart -> return false + } + } + + return false + } } \ No newline at end of file diff --git a/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/process/PluginUpdater.kt b/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/process/PluginUpdater.kt index b94c5d0..99a313b 100644 --- a/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/process/PluginUpdater.kt +++ b/surf-core-launcher/surf-core-launcher-server/src/main/kotlin/dev/slne/surf/core/launcher/server/updater/process/PluginUpdater.kt @@ -16,7 +16,7 @@ object PluginUpdater { private val oldPath = pluginsPath.resolve(".old") private val scanner = PluginScanner(pluginsPath) - private val gitHubClient = GitHubClient(CoreLauncher.config.personalAccessToken) + internal val gitHubClient = GitHubClient(CoreLauncher.config.personalAccessToken) private val cooldownTracker = UpdateCooldownTracker(pluginsPath.resolve(".last-updates")) suspend fun start() { @@ -48,12 +48,15 @@ object PluginUpdater { println("$LOG_PREFIX (Updater) Update check completed in ${duration}ms") } + + private val assetMappings = mapOf("surf-paper-paper" to "surf-api-paper") + private suspend fun checkAndUpdate(plugin: UpdatablePlugin) = withContext(Dispatchers.IO) { if (cooldownTracker.isOnCooldown(plugin.name)) { return@withContext } - val release = gitHubClient.fetchLatestRelease(plugin) + val release = gitHubClient.fetchLatestRelease(plugin.latestReleaseUrl) val latestVersion = release?.get("tag_name")?.toString()?.trimStart('v') if (latestVersion == null) { @@ -70,7 +73,7 @@ object PluginUpdater { @Suppress("UNCHECKED_CAST") val assets = release["assets"] as? List> ?: return@withContext - val expectedPrefix = buildString { + val buildedPrefix = buildString { append("surf-") append(plugin.findPluginName(false)) @@ -80,13 +83,15 @@ object PluginUpdater { } } + val mappedPrefix = assetMappings[buildedPrefix] ?: buildedPrefix + val matchingAsset = assets.firstOrNull { asset -> val assetName = asset["name"]?.toString() ?: return@firstOrNull false assetName.endsWith(".jar") && - assetName.startsWith(expectedPrefix) + assetName.startsWith(mappedPrefix) } ?: run { - println("$LOG_PREFIX (Updater) No matching asset found for ${plugin.name}: $expectedPrefix in release $latestVersion") + println("$LOG_PREFIX (Updater) No matching asset found for ${plugin.name}: $mappedPrefix in release $latestVersion") return@withContext }