Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 56 additions & 10 deletions app/src/main/java/com/aliothmoon/maafw/maa/MaaFwVersion.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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<String>?,
) : Comparable<SemVer> {
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
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/com/aliothmoon/maafw/remote/MaaRunner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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()))

Expand Down
2 changes: 1 addition & 1 deletion app/src/main/native/bridge_input.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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};
Expand Down
26 changes: 19 additions & 7 deletions app/src/test/java/com/aliothmoon/maafw/maa/MaaFwVersionTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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"))
}
}