From 32e06a4ce677c51118af4ededc191305792cc3e3 Mon Sep 17 00:00:00 2001 From: Talo Halton Date: Mon, 18 Dec 2023 01:36:37 +0000 Subject: [PATCH] Provide server name, device, and commit hash in handshake --- .gitignore | 3 +- build.gradle.kts | 48 +++++++++++++- .../cinterop/mpv/MpvCommandInterface.kt | 2 +- src/nativeMain/kotlin/main.kt | 2 - src/nativeMain/kotlin/spms/server/SpMs.kt | 65 ++++++++++++++++--- .../kotlin/spms/server/SpMsClient.kt | 9 +++ .../kotlin/spms/server/SpMsCommand.kt | 4 -- .../serveraction/ServerActionGetStatus.kt | 7 +- 8 files changed, 115 insertions(+), 25 deletions(-) diff --git a/.gitignore b/.gitignore index 046c0b9..7891f4d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ local.properties build/ .idea/ +.fleet/ .gradle/ *.gen.kt -src/nativeInterop/cinterop \ No newline at end of file +src/nativeInterop/cinterop diff --git a/build.gradle.kts b/build.gradle.kts index e456625..82ad3ea 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,9 @@ import org.jetbrains.kotlin.gradle.internal.ensureParentDirsCreated import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetWithHostTests import java.io.PrintWriter +import java.io.ByteArrayOutputStream + +val GENERATED_FILE_PREFIX = "// Generated on build in build.gradle.kts\n" plugins { kotlin("multiplatform") version "1.9.10" @@ -81,17 +84,17 @@ tasks.withType { } tasks.register("bundleIcon") { - val in_file = project.file("icon.png") + val in_file: File = project.file("icon.png") inputs.file(in_file) - val out_file = project.file("src/nativeMain/kotlin/icon.gen.kt") + val out_file: File = project.file("src/nativeMain/kotlin/Icon.gen.kt") outputs.file(out_file) doLast { check(in_file.isFile) out_file.writer().use { writer -> - writer.write("// https://youtrack.jetbrains.com/issue/KT-39194\n") + writer.write("${GENERATED_FILE_PREFIX}\n// https://youtrack.jetbrains.com/issue/KT-39194\n") writer.write("val ICON_BYTES: ByteArray = byteArrayOf(") val bytes: ByteArray = in_file.readBytes() @@ -113,8 +116,19 @@ tasks.register("bundleIcon") { } } +tasks.register("bundleGitCommitHash") { + val out_file: File = project.file("src/nativeMain/kotlin/GitCommitHash.gen.kt") + outputs.file(out_file) + + doLast { + val git_commit_hash: String = getCurrentGitCommitHash()!! + out_file.writeText("${GENERATED_FILE_PREFIX}val GIT_COMMIT_HASH: String = \"$git_commit_hash\"\n") + } +} + tasks.getByName("compileKotlinNative") { dependsOn("bundleIcon") + dependsOn("bundleGitCommitHash") } tasks.register("generateCInteropDefinitions") { @@ -219,3 +233,31 @@ fun pkgConfig( } } +fun cmd(vararg args: String): String { + val out = ByteArrayOutputStream() + exec { + commandLine(args.toList()) + standardOutput = out + } + return out.toString().trim() +} + +fun getCurrentGitTag(): String? { + try { + return cmd("git", "tag", "--points-at", "HEAD").ifBlank { null } + } + catch (e: Throwable) { + RuntimeException("Getting Git tag failed", e).printStackTrace() + return null + } +} + +fun getCurrentGitCommitHash(): String? { + try { + return cmd("git", "rev-parse", "HEAD").ifBlank { null } + } + catch (e: Throwable) { + RuntimeException("Getting Git commit hash failed", e).printStackTrace() + return null + } +} diff --git a/src/nativeMain/kotlin/cinterop/mpv/MpvCommandInterface.kt b/src/nativeMain/kotlin/cinterop/mpv/MpvCommandInterface.kt index a60f6fa..27de4c6 100644 --- a/src/nativeMain/kotlin/cinterop/mpv/MpvCommandInterface.kt +++ b/src/nativeMain/kotlin/cinterop/mpv/MpvCommandInterface.kt @@ -18,7 +18,7 @@ data class ServerStatusData( val volume: Double ) -fun Player.getCurrentStatusJson(): JsonElement = +fun Player.getCurrentStateJson(): JsonElement = Json.encodeToJsonElement( ServerStatusData( (0 until item_count).map { getItem(it) ?: "" }, diff --git a/src/nativeMain/kotlin/main.kt b/src/nativeMain/kotlin/main.kt index 5f7d891..1f060b2 100644 --- a/src/nativeMain/kotlin/main.kt +++ b/src/nativeMain/kotlin/main.kt @@ -1,5 +1,4 @@ import com.github.ajalt.clikt.core.subcommands -import kotlinx.cinterop.ExperimentalForeignApi import spms.Command import spms.client.cli.CommandLineClient import spms.client.player.PlayerClient @@ -9,7 +8,6 @@ import kotlin.system.exitProcess fun String.toRed() = "\u001b[31m$this\u001b[0m" -@OptIn(ExperimentalForeignApi::class) fun main(args: Array) { try { val command: Command = SpMsCommand().subcommands(CommandLineClient.get(), PlayerClient.get()) diff --git a/src/nativeMain/kotlin/spms/server/SpMs.kt b/src/nativeMain/kotlin/spms/server/SpMs.kt index bbfcb07..4385efd 100644 --- a/src/nativeMain/kotlin/spms/server/SpMs.kt +++ b/src/nativeMain/kotlin/spms/server/SpMs.kt @@ -1,10 +1,10 @@ package spms.server +import GIT_COMMIT_HASH import cinterop.mpv.MpvClientImpl -import cinterop.mpv.getCurrentStatusJson +import cinterop.mpv.getCurrentStateJson import cinterop.zmq.ZmqRouter -import kotlinx.cinterop.ExperimentalForeignApi -import kotlinx.cinterop.MemScope +import kotlinx.cinterop.* import kotlinx.coroutines.channels.Channel import kotlinx.serialization.Serializable import kotlinx.serialization.SerializationException @@ -13,12 +13,13 @@ import kotlinx.serialization.json.Json import kotlinx.serialization.json.JsonElement import kotlinx.serialization.json.JsonPrimitive import kotlinx.serialization.json.contentOrNull -import libzmq.ZMQ_NOBLOCK +import platform.posix.gethostname import spms.player.HeadlessPlayer import spms.player.Player import spms.player.PlayerEvent import spms.player.StreamProviderServer import spms.serveraction.ServerAction +import kotlin.experimental.ExperimentalNativeApi import kotlin.system.getTimeMillis const val SERVER_EXPECT_REPLY_CHAR: Char = '!' @@ -295,9 +296,9 @@ class SpMs(mem_scope: MemScope, val secondary_port: Int, headless: Boolean = fal return } - val handshake: SpMsClientHandshake + val client_handshake: SpMsClientHandshake try { - handshake = handshake_message.parts.firstOrNull()?.let { Json.decodeFromString(it) } ?: return + client_handshake = handshake_message.parts.firstOrNull()?.let { Json.decodeFromString(it) } ?: return } catch (e: SerializationException) { println("Ignoring SerializationException in onClientMessage") @@ -307,19 +308,27 @@ class SpMs(mem_scope: MemScope, val secondary_port: Int, headless: Boolean = fal val client: SpMsClient = SpMsClient( handshake_message.client_id, SpMsClientInfo( - getNewClientName(handshake.name), - handshake.type, - handshake.getLanguage() + getNewClientName(client_handshake.name), + client_handshake.type, + client_handshake.getLanguage() ), player_event_inc ) clients.add(client) + val server_handshake: SpMsServerHandshake = + SpMsServerHandshake( + SpMs.application_name, + getDeviceName(), + GIT_COMMIT_HASH, + player.getCurrentStateJson() + ) + sendMultipart( Message( client.id_bytes, - listOf(Json.encodeToString(player.getCurrentStatusJson())) + listOf(Json.encodeToString(server_handshake)) ) ) @@ -360,4 +369,40 @@ class SpMs(mem_scope: MemScope, val secondary_port: Int, headless: Boolean = fal override fun toString(): String = "SpMs(player=$player)" + + companion object { + const val application_name: String = "spmp-server" + } +} + +@OptIn(ExperimentalForeignApi::class, ExperimentalNativeApi::class) +fun getDeviceName(): String { + val hostname: String = memScoped { + val str: CPointer> = allocArray(1024) + gethostname(str, 1023U) + return@memScoped str.toKString() + } + + val os: String = + when (Platform.osFamily) { + OsFamily.MACOSX -> "OSX" + OsFamily.IOS -> "iOS" + OsFamily.LINUX -> "Linux" + OsFamily.WINDOWS -> "Windows" + OsFamily.ANDROID -> "Android" + OsFamily.WASM -> "Wasm" + OsFamily.TVOS -> "TV" + OsFamily.WATCHOS -> "WatchOS" + OsFamily.UNKNOWN -> "Unknown" + } + + val architecture: String = + when (Platform.cpuArchitecture) { + CpuArchitecture.X86 -> "x86" + CpuArchitecture.X64 -> "x86-64" + CpuArchitecture.UNKNOWN -> "Unknown" + else -> Platform.cpuArchitecture.name + } + + return "$hostname ($os $architecture)" } diff --git a/src/nativeMain/kotlin/spms/server/SpMsClient.kt b/src/nativeMain/kotlin/spms/server/SpMsClient.kt index 99afb9f..0994a6c 100644 --- a/src/nativeMain/kotlin/spms/server/SpMsClient.kt +++ b/src/nativeMain/kotlin/spms/server/SpMsClient.kt @@ -1,6 +1,7 @@ package spms.server import kotlinx.serialization.Serializable +import kotlinx.serialization.json.JsonElement import spms.localisation.Language typealias SpMsClientID = Int @@ -19,6 +20,14 @@ data class SpMsClientHandshake( Language.fromCode(language) ?: Language.default } +@Serializable +data class SpMsServerHandshake( + val name: String, + val device_name: String, + val spms_commit_hash: String, + val server_state: JsonElement +) + @Serializable data class SpMsClientInfo( val name: String, diff --git a/src/nativeMain/kotlin/spms/server/SpMsCommand.kt b/src/nativeMain/kotlin/spms/server/SpMsCommand.kt index 947b478..4ed61fa 100644 --- a/src/nativeMain/kotlin/spms/server/SpMsCommand.kt +++ b/src/nativeMain/kotlin/spms/server/SpMsCommand.kt @@ -135,8 +135,4 @@ class SpMsCommand: Command( } } } - - companion object { - const val application_name: String = "spmp-server" - } } diff --git a/src/nativeMain/kotlin/spms/serveraction/ServerActionGetStatus.kt b/src/nativeMain/kotlin/spms/serveraction/ServerActionGetStatus.kt index 46be38c..23a6d7a 100644 --- a/src/nativeMain/kotlin/spms/serveraction/ServerActionGetStatus.kt +++ b/src/nativeMain/kotlin/spms/serveraction/ServerActionGetStatus.kt @@ -1,6 +1,6 @@ package spms.serveraction -import cinterop.mpv.getCurrentStatusJson +import cinterop.mpv.getCurrentStateJson import com.github.ajalt.clikt.core.Context import io.ktor.client.HttpClient import kotlinx.cinterop.ExperimentalForeignApi @@ -23,7 +23,6 @@ import platform.posix.getenv import spms.localisation.loc import spms.player.Player import spms.server.SpMs -import spms.server.SpMsCommand @Suppress("OPT_IN_USAGE") @OptIn(ExperimentalForeignApi::class) @@ -31,7 +30,7 @@ fun getCacheDir(): Path = when (Platform.osFamily) { OsFamily.LINUX -> "/home/${getenv("USER")!!.toKString()}/.cache/".toPath() else -> throw NotImplementedError(Platform.osFamily.name) - }.resolve(SpMsCommand.application_name) + }.resolve(SpMs.application_name) class ServerActionGetStatus: ServerAction( identifier = "status", @@ -40,7 +39,7 @@ class ServerActionGetStatus: ServerAction( parameters = emptyList() ) { override fun execute(server: SpMs, context: ActionContext): JsonElement { - return server.player.getCurrentStatusJson() + return server.player.getCurrentStateJson() } private val cache_files: MutableMap = mutableMapOf()