diff --git a/app/src/main/java/com/aliothmoon/maafw/maa/MaaFwVersion.kt b/app/src/main/java/com/aliothmoon/maafw/maa/MaaFwVersion.kt index b4e6f7ed..228fd1a0 100644 --- a/app/src/main/java/com/aliothmoon/maafw/maa/MaaFwVersion.kt +++ b/app/src/main/java/com/aliothmoon/maafw/maa/MaaFwVersion.kt @@ -8,21 +8,67 @@ package com.aliothmoon.maafw.maa */ object MaaFwVersion { - /** 开始填充 bridge 合约 TouchArgs.contact 的 fw 版本;上游钉出更早版本可下调 */ - private const val MIN_CONTACT_FILL = "5.12.3" + /** AndroidNative 从这个版本起才会填充 bridge 合约的 TouchArgs.contact */ + private const val MIN_CONTACT_FILL = "5.13.0-beta.3" - private val NUMERIC = Regex("""\s*v?(\d+)\.(\d+)\.(\d+)\s*""") + private val SEMVER = Regex( + """\s*v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?""" + + """(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?\s*""", + ) fun fillsTouchContact(raw: String?): Boolean = atLeast(raw, MIN_CONTACT_FILL) fun atLeast(raw: String?, min: String): Boolean { - // 全串匹配才认:带 -beta/-rc 后缀的预发布按「低于其所标版本」处理 - val m = NUMERIC.matchEntire(raw ?: return false) ?: return false - val cur = m.groupValues.drop(1).map(String::toInt) - val floor = min.split('.').map(String::toInt) - for (i in cur.indices) { - if (cur[i] != floor[i]) return cur[i] > floor[i] + val current = parse(raw) ?: return false + val floor = parse(min) ?: return false + return current >= floor + } + + private fun parse(raw: String?): SemVer? { + val match = SEMVER.matchEntire(raw ?: return null) ?: return null + val core = match.groupValues.drop(1).take(3) + if (core.any(::hasInvalidLeadingZero)) return null + val numbers = core.map { it.toLongOrNull() ?: return null } + val prerelease = match.groupValues[4].takeIf(String::isNotEmpty)?.split('.') + if (prerelease?.any(::hasInvalidLeadingZero) == true) return null + return SemVer(numbers[0], numbers[1], numbers[2], prerelease) + } + + private fun hasInvalidLeadingZero(identifier: String): Boolean = + identifier.length > 1 && identifier[0] == '0' && identifier.all(Char::isDigit) + + private data class SemVer( + val major: Long, + val minor: Long, + val patch: Long, + val prerelease: List?, + ) : Comparable { + override fun compareTo(other: SemVer): Int { + major.compareTo(other.major).takeIf { it != 0 }?.let { return it } + minor.compareTo(other.minor).takeIf { it != 0 }?.let { return it } + patch.compareTo(other.patch).takeIf { it != 0 }?.let { return it } + + if (prerelease == null) return if (other.prerelease == null) 0 else 1 + if (other.prerelease == null) return -1 + for (index in 0 until minOf(prerelease.size, other.prerelease.size)) { + compareIdentifier(prerelease[index], other.prerelease[index]) + .takeIf { it != 0 } + ?.let { return it } + } + return prerelease.size.compareTo(other.prerelease.size) + } + + private fun compareIdentifier(left: String, right: String): Int { + val leftNumeric = left.all(Char::isDigit) + val rightNumeric = right.all(Char::isDigit) + return when { + leftNumeric && rightNumeric -> + left.length.compareTo(right.length).takeIf { it != 0 } + ?: left.compareTo(right) + leftNumeric -> -1 + rightNumeric -> 1 + else -> left.compareTo(right) + } } - return true } } diff --git a/app/src/main/java/com/aliothmoon/maafw/remote/MaaRunner.kt b/app/src/main/java/com/aliothmoon/maafw/remote/MaaRunner.kt index 7716971b..07e4aab3 100644 --- a/app/src/main/java/com/aliothmoon/maafw/remote/MaaRunner.kt +++ b/app/src/main/java/com/aliothmoon/maafw/remote/MaaRunner.kt @@ -289,7 +289,7 @@ class MaaRunner(private val agentHost: AgentHost) { return "libbridge.so 未加载,无法建立 native controller" } - // TouchArgs.contact 是 v5.12.3 起 fw 才填的合约字段,旧 fw 那 4 字节是栈残值, + // TouchArgs.contact 是 v5.13.0-beta.3 起 fw 才填的合约字段,旧 fw 那 4 字节是栈残值, // 不过闸直接读会得到幻影手指;低于配对版本一律压 0(等价旧 bridge 单指行为) NativeBridgeLib.setContactSupport(MaaFwVersion.fillsTouchContact(lib.MaaVersion())) diff --git a/app/src/main/native/bridge_input.cpp b/app/src/main/native/bridge_input.cpp index 47ca64d3..701be07d 100644 --- a/app/src/main/native/bridge_input.cpp +++ b/app/src/main/native/bridge_input.cpp @@ -12,7 +12,7 @@ static jmethodID g_key_down_method = nullptr; static jmethodID g_key_up_method = nullptr; static jmethodID g_start_app_method = nullptr; -/* TouchArgs.contact 是随 MaaFramework v5.12.3 才由 fw 填的合约字段;旧 fw 不写它, +/* TouchArgs.contact 是随 MaaFramework v5.13.0-beta.3 才由 fw 填的合约字段;旧 fw 不写它, * 那 4 字节是调用方栈上的残值,可能落进 [0,16) 变成幻影手指。默认 false: * Kotlin 判完 fw 版本之前,触摸一律按单指 contact 0 注入 */ static std::atomic_bool g_read_contact{false}; diff --git a/app/src/test/java/com/aliothmoon/maafw/maa/MaaFwVersionTest.kt b/app/src/test/java/com/aliothmoon/maafw/maa/MaaFwVersionTest.kt index 05f7d707..380e017a 100644 --- a/app/src/test/java/com/aliothmoon/maafw/maa/MaaFwVersionTest.kt +++ b/app/src/test/java/com/aliothmoon/maafw/maa/MaaFwVersionTest.kt @@ -11,23 +11,32 @@ class MaaFwVersionTest { fun `低于配对版本不读 contact`() { assertFalse(MaaFwVersion.fillsTouchContact("5.11.9")) assertFalse(MaaFwVersion.fillsTouchContact("v5.12.2")) + assertFalse(MaaFwVersion.fillsTouchContact("5.12.3")) + assertFalse(MaaFwVersion.fillsTouchContact("5.12.10")) + assertFalse(MaaFwVersion.fillsTouchContact("5.13.0-beta.2")) + assertFalse(MaaFwVersion.fillsTouchContact("5.13.0-alpha.9")) assertFalse(MaaFwVersion.fillsTouchContact("5.12")) } @Test fun `配对版本起过闸`() { - assertTrue(MaaFwVersion.fillsTouchContact("5.12.3")) - assertTrue(MaaFwVersion.fillsTouchContact("v5.12.3")) - assertTrue(MaaFwVersion.fillsTouchContact(" 5.12.3 ")) - assertTrue(MaaFwVersion.fillsTouchContact("5.12.10")) + assertTrue(MaaFwVersion.fillsTouchContact("5.13.0-beta.3")) + assertTrue(MaaFwVersion.fillsTouchContact("v5.13.0-beta.4")) + assertTrue(MaaFwVersion.fillsTouchContact("v5.13.0-beta.5")) + assertTrue(MaaFwVersion.fillsTouchContact(" 5.13.0-rc.1 ")) assertTrue(MaaFwVersion.fillsTouchContact("5.13.0")) + assertTrue(MaaFwVersion.fillsTouchContact("5.13.0+android.1")) + assertTrue(MaaFwVersion.fillsTouchContact("5.13.1-alpha.1")) assertTrue(MaaFwVersion.fillsTouchContact("6.0.0")) } @Test - fun `预发布按低于所标版本处理`() { - assertFalse(MaaFwVersion.fillsTouchContact("v5.12.0-beta.1")) - assertFalse(MaaFwVersion.fillsTouchContact("v5.12.3-rc.1")) + fun `SemVer 预发布标识按标准顺序比较`() { + assertFalse(MaaFwVersion.atLeast("5.13.0-beta.2", "5.13.0-beta.3")) + assertFalse(MaaFwVersion.atLeast("5.13.0-beta.3.1", "5.13.0-beta.3.a")) + assertTrue(MaaFwVersion.atLeast("5.13.0-beta.10", "5.13.0-beta.3")) + assertTrue(MaaFwVersion.atLeast("5.13.0-rc.1", "5.13.0-beta.4")) + assertTrue(MaaFwVersion.atLeast("5.13.0", "5.13.0-rc.9")) } @Test @@ -36,5 +45,8 @@ class MaaFwVersionTest { assertFalse(MaaFwVersion.fillsTouchContact("")) assertFalse(MaaFwVersion.fillsTouchContact("garbage")) assertFalse(MaaFwVersion.fillsTouchContact("version 5.12.3")) + assertFalse(MaaFwVersion.fillsTouchContact("5.13.0-beta.03")) + assertFalse(MaaFwVersion.fillsTouchContact("5.13.0-beta.")) + assertFalse(MaaFwVersion.fillsTouchContact("05.13.0")) } }