diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/VersionsManager.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/VersionsManager.kt index dfb7a0e71..26d1160fa 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/VersionsManager.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/VersionsManager.kt @@ -40,27 +40,25 @@ private const val TAG = "VersionsManager" object VersionsManager { private val scope = CoroutineScope(Dispatchers.IO) private val mutex = Mutex() - private val listeners: MutableList) -> Unit> = mutableListOf() + private val listeners: MutableList Unit> = mutableListOf() /** * 注册版本列表刷新监听器 */ - fun registerListener(listener: suspend (List) -> Unit) { + fun registerListener(listener: suspend () -> Unit) { listeners.add(listener) } /** * 移除版本列表刷新监听器 */ - fun unregisterListener(listener: suspend (List) -> Unit) { + fun unregisterListener(listener: suspend () -> Unit) { listeners.remove(listener) } - /** - * 当前所有的游戏版本 - */ - var versions: List = emptyList() - private set + private val _versions = MutableStateFlow>(emptyList()) + /** 当前所有的游戏版本 */ + val versions = _versions.asStateFlow() /** * 当前的游戏信息 @@ -104,7 +102,7 @@ object VersionsManager { Logger.debug(TAG, "Has attempted to save the current version: $trySetVersion") } - versions = emptyList() + _versions.update { emptyList() } val newVersions = mutableListOf() File(getVersionsHome()).listFiles()?.forEach { versionFile -> @@ -115,13 +113,13 @@ object VersionsManager { } } - versions = newVersions.sortedWith(VersionComparator) + _versions.update { newVersions.sortedWith(VersionComparator) } gameInfo = refreshCurrentInfo() Logger.debug(TAG, "Version list refreshed, refreshing the current version now.") refreshCurrentVersion() - listeners.forEach { it.invoke(versions) } + listeners.forEach { it() } _isRefreshing.update { false } } @@ -174,10 +172,11 @@ object VersionsManager { private fun refreshCurrentVersion() { val version = run { - if (versions.isEmpty()) return@run null + val currentList = _versions.value + if (currentList.isEmpty()) return@run null fun getVersionByFirst(): Version? { - return versions.find { it.isValid() }?.apply { + return currentList.find { it.isValid() }?.apply { //确保版本有效 saveCurrentVersion(getVersionName(), refresh = false) } @@ -185,7 +184,7 @@ object VersionsManager { runCatching { val versionString = gameInfo!!.version - getVersion(versionString) ?: run { + currentList.getVersion(versionString) ?: run { Logger.debug(TAG, "Stored version $versionString not found, using the first available version instead.") getVersionByFirst() } @@ -201,18 +200,22 @@ object VersionsManager { _currentVersion.update { version } } - fun getVersion(name: String?): Version? { + private fun List.getVersion(name: String?): Version? { name?.let { versionName -> - return versions.find { it.getVersionName() == versionName }?.takeIf { it.isValid() } + return find { it.getVersionName() == versionName }?.takeIf { it.isValid() } } return null } + fun getVersion(name: String?): Version? { + return _versions.value.getVersion(name) + } + /** * @return 通过版本名,判断其版本是否存在 */ fun checkVersionExistsByName(versionName: String?) = - versionName?.let { name -> versions.any { it.getVersionName() == name } } ?: false + versionName?.let { name -> _versions.value.any { it.getVersionName() == name } } ?: false /** * @return 获取 Zalith 启动器版本标识文件夹 diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/cleanup/GameAssetCleaner.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/cleanup/GameAssetCleaner.kt index 8a9873999..c662c2ef6 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/cleanup/GameAssetCleaner.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/game/version/installed/cleanup/GameAssetCleaner.kt @@ -144,8 +144,7 @@ class GameAssetCleaner( ) { task -> task.updateProgress(-1f) - val allVersions = VersionsManager.versions.toList() - + val allVersions = VersionsManager.versions.value allVersions.forEach { version -> ensureActive() diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/LauncherScreen.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/LauncherScreen.kt index 91ee68307..dd7ffc995 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/LauncherScreen.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/LauncherScreen.kt @@ -333,7 +333,8 @@ private fun RightMenuContent( ), shape = MaterialTheme.shapes.extraLarge ) { - VersionsManager.versions.forEach { version0 -> + val versions by VersionsManager.versions.collectAsStateWithLifecycle() + versions.forEach { version0 -> DropdownMenuItem( text = { Row( diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionExportScreen.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionExportScreen.kt index 36426791b..28f298337 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionExportScreen.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionExportScreen.kt @@ -435,8 +435,8 @@ fun VersionExportScreen( val cBackToMainScreen by rememberUpdatedState(backToMainScreen) DisposableEffect(key) { - val listener = object : suspend (List) -> Unit { - override suspend fun invoke(versions: List) { + val listener = object : suspend () -> Unit { + override suspend fun invoke() { cBackToMainScreen() } } diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionSettingsScreen.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionSettingsScreen.kt index 68e8332c5..3b80e40f1 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionSettingsScreen.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionSettingsScreen.kt @@ -207,8 +207,8 @@ fun VersionSettingsScreen( val cBackToMainScreen by rememberUpdatedState(backToMainScreen) DisposableEffect(key) { - val listener = object : suspend (List) -> Unit { - override suspend fun invoke(versions: List) { + val listener = object : suspend () -> Unit { + override suspend fun invoke() { cBackToMainScreen() } } diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionsManageScreen.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionsManageScreen.kt index ad0f5ebd2..9334e3ea9 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionsManageScreen.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/VersionsManageScreen.kt @@ -41,6 +41,8 @@ import androidx.compose.material3.LoadingIndicator import androidx.compose.material3.MaterialTheme import androidx.compose.material3.scrollbar import androidx.compose.runtime.Composable +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf @@ -90,79 +92,36 @@ import com.movtery.zalithlauncher.viewmodel.ErrorViewModel import com.movtery.zalithlauncher.viewmodel.EventViewModel import com.movtery.zalithlauncher.viewmodel.ScreenBackStackViewModel import com.movtery.zalithlauncher.viewmodel.sendKeepScreen -import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.asStateFlow -import kotlinx.coroutines.flow.update +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.launch import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext private class VersionsScreenViewModel : ViewModel() { /** 版本类别分类 */ var versionCategory by mutableStateOf(VersionCategory.ALL) private set + /** 重排序刷新key */ + var resortKey by mutableIntStateOf(0) + private set /** 游戏路径相关操作 */ var gamePathOperation by mutableStateOf(GamePathOperation.None) - private val _versions = MutableStateFlow>(emptyList()) - val versions = _versions.asStateFlow() - /** 全部版本的数量 */ var allVersionsCount by mutableIntStateOf(0) - private set /** 原版版本数量 */ var vanillaVersionsCount by mutableIntStateOf(0) - private set /** 模组加载器版本数量 */ var modloaderVersionsCount by mutableIntStateOf(0) - private set fun startRefreshVersions() { if (!VersionsManager.isRefreshing.value) { - _versions.update { emptyList() } VersionsManager.refresh("VersionsScreenViewModel.startRefreshVersions") } } - /** - * 刷新当前版本列表 - */ - suspend fun refreshVersions( - currentVersions: List, - clearCurrent: Boolean = true - ) { - withContext(Dispatchers.Main) { - if (clearCurrent) { - _versions.update { emptyList() } - } - - val filteredVersions = withContext(Dispatchers.Default) { - allVersionsCount = currentVersions.size - - val vanillaVersions = currentVersions - .filter { ver -> ver.versionType == VersionType.VANILLA } - .also { vanillaVersionsCount = it.size } - val modloaderVersions = currentVersions - .filter { ver -> ver.versionType == VersionType.MODLOADERS } - .also { modloaderVersionsCount = it.size } - - when (versionCategory) { - VersionCategory.ALL -> currentVersions - VersionCategory.VANILLA -> vanillaVersions - VersionCategory.MODLOADER -> modloaderVersions - } - } - - _versions.update { - filteredVersions.sortedWith(VersionComparator) - } - } - } - private var currentJob: Job? = null private var mutex: Mutex = Mutex() @@ -174,7 +133,6 @@ private class VersionsScreenViewModel : ViewModel() { currentJob = viewModelScope.launch { mutex.withLock { this@VersionsScreenViewModel.versionCategory = category - refreshVersions(VersionsManager.versions, false) } } } @@ -183,9 +141,7 @@ private class VersionsScreenViewModel : ViewModel() { * 重新排序当前版本列表 */ fun resortVersions() { - _versions.update { - it.sortedWith(VersionComparator) - } + resortKey++ } /** 清理游戏文件操作 */ @@ -226,22 +182,8 @@ private class VersionsScreenViewModel : ViewModel() { cleanupOperation = CleanupOperation.None } - private val listener: suspend (List) -> Unit = { versions -> - refreshVersions(versions) - } - - init { - viewModelScope.launch { - //初始化时刷新一次版本 - refreshVersions(VersionsManager.versions) - } - - VersionsManager.registerListener(listener) - } - override fun onCleared() { cancelCleaner() - VersionsManager.unregisterListener(listener) currentJob?.cancel() } } @@ -255,6 +197,35 @@ private fun rememberVersionViewModel() : VersionsScreenViewModel { } } +@Composable +private fun rememberVersions( + versions: StateFlow>, + viewModel: VersionsScreenViewModel, +): State> { + val vers by versions.collectAsStateWithLifecycle() + val category = viewModel.versionCategory + val resortKey = viewModel.resortKey + + return remember(vers, category, resortKey) { + derivedStateOf { + viewModel.allVersionsCount = vers.size + + val vanillaVersions = vers + .filter { ver -> ver.versionType == VersionType.VANILLA } + .also { viewModel.vanillaVersionsCount = it.size } + val modloaderVersions = vers + .filter { ver -> ver.versionType == VersionType.MODLOADERS } + .also { viewModel.modloaderVersionsCount = it.size } + + when (category) { + VersionCategory.ALL -> vers + VersionCategory.VANILLA -> vanillaVersions + VersionCategory.MODLOADER -> modloaderVersions + }.sortedWith(VersionComparator) + } + } +} + @Composable fun VersionsManageScreen( backScreenViewModel: ScreenBackStackViewModel, @@ -266,7 +237,7 @@ fun VersionsManageScreen( val viewModel = rememberVersionViewModel() val context = LocalContext.current - val versions by viewModel.versions.collectAsStateWithLifecycle() + val versions by rememberVersions(VersionsManager.versions, viewModel) val currentVersion by VersionsManager.currentVersion.collectAsStateWithLifecycle() val isRefreshing by VersionsManager.isRefreshing.collectAsStateWithLifecycle() diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/assets/elements/_Download.Single.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/assets/elements/_Download.Single.kt index 490b6c1a2..0508dd52d 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/assets/elements/_Download.Single.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/assets/elements/_Download.Single.kt @@ -46,6 +46,8 @@ import androidx.compose.material3.Text import androidx.compose.material3.scrollbar import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.State +import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.remember @@ -156,6 +158,16 @@ fun DownloadSingleOperation( } } +@Composable +private fun rememberValidVersions(): State> { + val vers by VersionsManager.versions.collectAsStateWithLifecycle() + return remember(vers) { + derivedStateOf { + vers.filter { it.isValid() } + } + } +} + @Composable private fun DownloadDialog( dependencyProjects: List>, @@ -164,7 +176,7 @@ private fun DownloadDialog( onInstall: (List) -> Unit, onDependencyClicked: (PlatformVersion.PlatformDependency, PlatformClasses) -> Unit ) { - val versions = remember { VersionsManager.versions.filter { it.isValid() } } + val versions by rememberValidVersions() val version by VersionsManager.currentVersion.collectAsStateWithLifecycle() val version0 = version diff --git a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/game/DownloadGameWithAddonScreen.kt b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/game/DownloadGameWithAddonScreen.kt index 93da4ed35..cf4311d58 100644 --- a/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/game/DownloadGameWithAddonScreen.kt +++ b/ZalithLauncher/src/main/java/com/movtery/zalithlauncher/ui/screens/content/download/game/DownloadGameWithAddonScreen.kt @@ -39,7 +39,6 @@ import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.key @@ -56,6 +55,7 @@ import androidx.compose.ui.unit.IntOffset import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.lifecycle.ViewModel +import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewmodel.compose.viewModel import com.movtery.zalithlauncher.R @@ -73,7 +73,6 @@ import com.movtery.zalithlauncher.game.addons.modloader.forgelike.neoforge.NeoFo import com.movtery.zalithlauncher.game.addons.modloader.optifine.OptiFineVersion import com.movtery.zalithlauncher.game.addons.modloader.optifine.OptiFineVersions import com.movtery.zalithlauncher.game.download.game.GameDownloadInfo -import com.movtery.zalithlauncher.game.version.installed.Version import com.movtery.zalithlauncher.game.version.installed.VersionsManager import com.movtery.zalithlauncher.game.version.installed.VersionsManager.isVersionExists import com.movtery.zalithlauncher.ui.base.BaseScreen @@ -641,18 +640,7 @@ private fun ScreenHeader( } } - var versions by remember { mutableStateOf(VersionsManager.versions) } - DisposableEffect(Unit) { - val listener: suspend (List) -> Unit = { versions0 -> - versions = versions0 - } - - VersionsManager.registerListener(listener) - onDispose { - VersionsManager.unregisterListener(listener) - } - } - + val versions by VersionsManager.versions.collectAsStateWithLifecycle() if (versions.isNotEmpty()) { Row { //不使用viewModel存储,防止版本刷新这里状态不同步