diff --git a/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt b/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt index c3ed4cb73..d6d138e8b 100644 --- a/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt +++ b/LavalinkServer/src/main/java/lavalink/server/bootstrap/PluginManager.kt @@ -9,9 +9,12 @@ import java.io.FileOutputStream import java.io.InputStream import java.net.URL import java.net.URLClassLoader +import java.net.HttpURLConnection import java.nio.channels.Channels import java.util.* import java.util.jar.JarFile +import javax.xml.parsers.DocumentBuilderFactory +import dev.arbjerg.lavalink.protocol.v4.Version @SpringBootApplication class PluginManager(val config: PluginsConfig) { @@ -83,6 +86,34 @@ class PluginManager(val config: PluginsConfig) { val file = File(directory, declaration.canonicalJarName) downloadJar(file, url) } + + checkPluginForUpdates(declaration) + } + } + + private fun checkPluginForUpdates(declaration: Declaration) { + val splitPath = declaration.url.split('/') + + val baseSplitPath = splitPath.dropLast(2) + val basePath = baseSplitPath.joinToString("/") + "/maven-metadata.xml" + val documentBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() + val document = documentBuilder.parse(basePath) + + var elements = document.getElementsByTagName("latest") + if(elements.length == 0) { + elements = document.getElementsByTagName("release") + } + + if (elements.length > 0) { + val latest = elements.item(0).textContent + val latestVersion = Version.fromSemver(latest) + val currentVersion = Version.fromSemver(declaration.version) + if(latestVersion > currentVersion) { + log.warn("A newer version of ${declaration.name} was found: $latestVersion, " + + "The current version is $currentVersion") + } else { + log.info("Plugin ${declaration.name} is up to date") + } } } diff --git a/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt b/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt index d739c53e3..2040c25cc 100644 --- a/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt +++ b/protocol/src/commonMain/kotlin/dev/arbjerg/lavalink/protocol/v4/info.kt @@ -62,6 +62,70 @@ data class Version( return Version(semver, major.toInt(), minor.toInt(), patch.toInt(), preRelease) } } + + override fun toString(): String { + var baseSemver = "${major}.${minor}.${patch}" + + if(!preRelease.isNullOrEmpty()) + baseSemver += "-${preRelease}" + + return baseSemver + } + + operator fun compareTo(other: Version): Int { + // Compare major, minor, and patch + val majorDiff = major - other.major + if (majorDiff != 0) return majorDiff + + val minorDiff = minor - other.minor + if (minorDiff != 0) return minorDiff + + val patchDiff = patch - other.patch + if (patchDiff != 0) return patchDiff + + // Compare prerelease (null means no prerelease and is greater) + return when { + preRelease.isNullOrEmpty() && other.preRelease.isNullOrEmpty() -> 0 + preRelease.isNullOrEmpty() && !other.preRelease.isNullOrEmpty() -> 1 + !preRelease.isNullOrEmpty() && other.preRelease.isNullOrEmpty() -> -1 + !preRelease.isNullOrEmpty() && !other.preRelease.isNullOrEmpty() -> comparePreRelease(preRelease, other.preRelease) + else -> 0 + } + } + + private fun comparePreRelease(part1: String, part2: String): Int { + val components1 = part1.split(".") + val components2 = part2.split(".") + val maxLength = maxOf(components1.size, components2.size) + + for (i in 0 until maxLength) { + val comp1 = components1.getOrNull(i) + val comp2 = components2.getOrNull(i) + + if (comp1 == null) return -1 // `part1` is shorter and considered smaller + if (comp2 == null) return 1 // `part2` is shorter and considered smaller + + val isNumeric1 = comp1.all { it.isDigit() } + val isNumeric2 = comp2.all { it.isDigit() } + + when { + isNumeric1 && isNumeric2 -> { + // Compare numerically + val diff = comp1.toInt() - comp2.toInt() + if (diff != 0) return diff + } + isNumeric1 -> return -1 // Numeric parts come before string parts + isNumeric2 -> return 1 // String parts come after numeric parts + else -> { + // Compare lexicographically + val diff = comp1.compareTo(comp2) + if (diff != 0) return diff + } + } + } + + return 0 // Parts are equal + } } @Serializable