This commit is contained in:
2026-10-25 17:52:52 +08:00
parent eea818f5f4
commit 5ec7815a13
10 changed files with 361 additions and 199 deletions
+131 -2
View File
@@ -1,8 +1,13 @@
import java.util.Properties
import org.gradle.internal.os.OperatingSystem
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.plugin.compose")
}
val minSdk = 28
android {
namespace = "net.bluemangoo.indpjsk"
compileSdk {
@@ -11,10 +16,10 @@ android {
defaultConfig {
applicationId = "net.bluemangoo.indpjsk"
minSdk = 28
minSdk = minSdk
targetSdk = 37
versionCode = 1
versionName = "1.0"
versionName = "1.1"
}
@@ -43,4 +48,128 @@ dependencies {
implementation("androidx.compose.ui:ui-tooling-preview")
implementation("androidx.compose.material3:material3")
debugImplementation("androidx.compose.ui:ui-tooling")
}
val cargoTarget = "aarch64-linux-android"
val cargoSoName = "libcore_vpn.so"
val cargoDir = rootProject.file("core_vpn")
val cargoSo = File(cargoDir, "target/$cargoTarget/release/$cargoSoName")
val jniLibsArm64 = file("src/main/jniLibs/arm64-v8a")
val cargoBin: String = providers.gradleProperty("cargoBin").getOrElse("cargo")
fun resolveNdkDirectory(project: Project): File {
val localPropertiesFile = project.rootProject.file("local.properties")
val properties = Properties()
if (localPropertiesFile.exists()) {
localPropertiesFile.inputStream().use { properties.load(it) }
}
properties.getProperty("ndk.dir")?.let { path ->
val file = File(path)
if (file.exists()) return file
}
val sdkDirPath = properties.getProperty("sdk.dir")
?: System.getenv("ANDROID_SDK_ROOT")
?: System.getenv("ANDROID_HOME")
?: throw GradleException("Failed to resolve Android SDK path")
val ndkParentDir = File(sdkDirPath, "ndk")
if (!ndkParentDir.exists() || !ndkParentDir.isDirectory) {
throw GradleException("Failed to resolve NDK directory: ${ndkParentDir.absolutePath}")
}
val installedVersions = ndkParentDir.listFiles { f -> f.isDirectory }
?.sortedWith(compareBy { it.name })
?: emptyList()
if (installedVersions.isEmpty()) {
throw GradleException("Failed to find any installed NDK versions in ${ndkParentDir.absolutePath}")
}
// 取最新安装的版本
return installedVersions.last()
}
fun resolveLlvmBinDirectory(ndkDir: File): File {
val prebuiltDir = File(ndkDir, "toolchains/llvm/prebuilt")
val hostDir = prebuiltDir.listFiles { file -> file.isDirectory }?.firstOrNull()
?: throw IllegalStateException("Ndk llvm not found: ${prebuiltDir.absolutePath}")
val binDir = File(hostDir, "bin")
if (!binDir.exists()) {
throw IllegalStateException("Ndk llvm not found: ${binDir.absolutePath}")
}
return binDir
}
val cargoBuildVpnCore = tasks.register<Exec>("cargoBuildVpnCore") {
group = "build"
description = "Rust build core_vpn"
workingDir = cargoDir
doFirst {
try {
val ndkDir = resolveNdkDirectory(project)
val ndkHomePath = ndkDir.absolutePath
val ndkBin = resolveLlvmBinDirectory(ndkDir)
val scriptExt = if (OperatingSystem.current().isWindows) ".cmd" else ""
val execExt = if (OperatingSystem.current().isWindows) ".exe" else ""
environment("NDK_HOME", ndkHomePath)
environment("PATH", "$ndkHomePath:${System.getenv("PATH")}")
environment(
"CC_aarch64-linux-android",
ndkBin.resolve("aarch64-linux-android$minSdk-clang$scriptExt").absolutePath
)
environment(
"CXX_aarch64-linux-android",
ndkBin.resolve("aarch64-linux-android$minSdk-clang++$scriptExt").absolutePath
)
environment("AR_aarch64-linux-android", ndkBin.resolve("llvm-ar$execExt").absolutePath)
environment(
"CARGO_TARGET_AARCH64_LINUX_ANDROID_LINKER",
ndkBin.resolve("aarch64-linux-android$minSdk-clang$scriptExt").absolutePath
)
} catch (e: Exception) {
logger.warn("[cargoBuildVpnCore] resolveNdkDirectory failed: ${e.message}")
}
}
commandLine(cargoBin, "build", "--release", "--target", cargoTarget)
inputs.files(
File(cargoDir, "Cargo.toml"),
File(cargoDir, "Cargo.lock"),
File(cargoDir, "build.rs"),
).withPropertyName("rustConfig")
inputs.dir(File(cargoDir, "src")).withPropertyName("rustSrc")
outputs.file(cargoSo).withPropertyName("rustSo")
}
val copyVpnSo = tasks.register("copyVpnSo") {
group = "build"
description = "Sync core_vpn.so"
dependsOn(cargoBuildVpnCore)
inputs.file(cargoSo).withPropertyName("rustSo")
outputs.dir(jniLibsArm64).withPropertyName("jniLibsArm64")
doLast {
check(cargoSo.isFile) {
"File $cargoSo not exist"
}
if (jniLibsArm64.exists()) {
jniLibsArm64.deleteRecursively()
}
jniLibsArm64.mkdirs()
val dest = File(jniLibsArm64, cargoSoName)
cargoSo.copyTo(dest, overwrite = true)
logger.lifecycle("[copyVpnSo] ${cargoSo.absolutePath} -> ${dest.absolutePath}")
}
}
tasks.named("preBuild") {
dependsOn(copyVpnSo)
}
@@ -31,6 +31,14 @@ object PredefinedProfiles {
fileName = "mysekai"
)
private fun mysekaiBirthday(hosts: List<String>) = CaptureProfileStep(
id = "mysekai_birthday",
name = "烤森生日双叶",
hosts = hosts,
path = "/api/user/(\\d+)/mysekai/birthday-party/(\\d+)/delivery",
fileName = "mysekai_birthday"
)
private val CN_HOSTS = listOf("mkcn-prod-public-60001-1.dailygn.com")
private val JP_HOSTS = listOf("production-game-api.sekai.colorfulpalette.org")
private val EN_HOSTS = listOf("n-production-game-api.sekai-en.com")
@@ -44,7 +52,7 @@ object PredefinedProfiles {
)
val all: List<CaptureProfile> = listOf(
// 国服不需要烤森
// 国服不支持烤森
CaptureProfile(
id = "cn",
name = "国服",
@@ -53,22 +61,22 @@ object PredefinedProfiles {
CaptureProfile(
id = "jp",
name = "日服",
steps = listOf(suiteStep(JP_HOSTS), mysekaiStep(JP_HOSTS))
steps = listOf(suiteStep(JP_HOSTS), mysekaiStep(JP_HOSTS), mysekaiBirthday(JP_HOSTS))
),
CaptureProfile(
id = "en",
name = "国际服",
steps = listOf(suiteStep(EN_HOSTS), mysekaiStep(EN_HOSTS))
steps = listOf(suiteStep(EN_HOSTS), mysekaiStep(EN_HOSTS), mysekaiBirthday(EN_HOSTS))
),
CaptureProfile(
id = "tw",
name = "台服",
steps = listOf(suiteStep(TW_HOSTS), mysekaiStep(TW_HOSTS))
steps = listOf(suiteStep(TW_HOSTS), mysekaiStep(TW_HOSTS), mysekaiBirthday(TW_HOSTS))
),
CaptureProfile(
id = "kr",
name = "韩服",
steps = listOf(suiteStep(KR_HOSTS), mysekaiStep(KR_HOSTS))
steps = listOf(suiteStep(KR_HOSTS), mysekaiStep(KR_HOSTS), mysekaiBirthday(KR_HOSTS))
),
)