Skip to content
Merged
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
Original file line number Diff line number Diff line change
Expand Up @@ -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<suspend (List<Version>) -> Unit> = mutableListOf()
private val listeners: MutableList<suspend () -> Unit> = mutableListOf()

/**
* 注册版本列表刷新监听器
*/
fun registerListener(listener: suspend (List<Version>) -> Unit) {
fun registerListener(listener: suspend () -> Unit) {
listeners.add(listener)
}

/**
* 移除版本列表刷新监听器
*/
fun unregisterListener(listener: suspend (List<Version>) -> Unit) {
fun unregisterListener(listener: suspend () -> Unit) {
listeners.remove(listener)
}

/**
* 当前所有的游戏版本
*/
var versions: List<Version> = emptyList()
private set
private val _versions = MutableStateFlow<List<Version>>(emptyList())
/** 当前所有的游戏版本 */
val versions = _versions.asStateFlow()

/**
* 当前的游戏信息
Expand Down Expand Up @@ -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<Version>()
File(getVersionsHome()).listFiles()?.forEach { versionFile ->
Expand All @@ -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 }
}
Expand Down Expand Up @@ -174,18 +172,19 @@ 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)
}
}

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()
}
Expand All @@ -201,18 +200,22 @@ object VersionsManager {
_currentVersion.update { version }
}

fun getVersion(name: String?): Version? {
private fun List<Version>.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 启动器版本标识文件夹
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,7 @@ class GameAssetCleaner(
) { task ->
task.updateProgress(-1f)

val allVersions = VersionsManager.versions.toList()

val allVersions = VersionsManager.versions.value
allVersions.forEach { version ->
ensureActive()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -435,8 +435,8 @@ fun VersionExportScreen(

val cBackToMainScreen by rememberUpdatedState(backToMainScreen)
DisposableEffect(key) {
val listener = object : suspend (List<Version>) -> Unit {
override suspend fun invoke(versions: List<Version>) {
val listener = object : suspend () -> Unit {
override suspend fun invoke() {
cBackToMainScreen()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,8 +207,8 @@ fun VersionSettingsScreen(

val cBackToMainScreen by rememberUpdatedState(backToMainScreen)
DisposableEffect(key) {
val listener = object : suspend (List<Version>) -> Unit {
override suspend fun invoke(versions: List<Version>) {
val listener = object : suspend () -> Unit {
override suspend fun invoke() {
cBackToMainScreen()
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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>(GamePathOperation.None)

private val _versions = MutableStateFlow<List<Version>>(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<Version>,
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()

Expand All @@ -174,7 +133,6 @@ private class VersionsScreenViewModel : ViewModel() {
currentJob = viewModelScope.launch {
mutex.withLock {
this@VersionsScreenViewModel.versionCategory = category
refreshVersions(VersionsManager.versions, false)
}
}
}
Expand All @@ -183,9 +141,7 @@ private class VersionsScreenViewModel : ViewModel() {
* 重新排序当前版本列表
*/
fun resortVersions() {
_versions.update {
it.sortedWith(VersionComparator)
}
resortKey++
}

/** 清理游戏文件操作 */
Expand Down Expand Up @@ -226,22 +182,8 @@ private class VersionsScreenViewModel : ViewModel() {
cleanupOperation = CleanupOperation.None
}

private val listener: suspend (List<Version>) -> Unit = { versions ->
refreshVersions(versions)
}

init {
viewModelScope.launch {
//初始化时刷新一次版本
refreshVersions(VersionsManager.versions)
}

VersionsManager.registerListener(listener)
}

override fun onCleared() {
cancelCleaner()
VersionsManager.unregisterListener(listener)
currentJob?.cancel()
}
}
Expand All @@ -255,6 +197,35 @@ private fun rememberVersionViewModel() : VersionsScreenViewModel {
}
}

@Composable
private fun rememberVersions(
versions: StateFlow<List<Version>>,
viewModel: VersionsScreenViewModel,
): State<List<Version>> {
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,
Expand All @@ -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()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -156,6 +158,16 @@ fun DownloadSingleOperation(
}
}

@Composable
private fun rememberValidVersions(): State<List<Version>> {
val vers by VersionsManager.versions.collectAsStateWithLifecycle()
return remember(vers) {
derivedStateOf {
vers.filter { it.isValid() }
}
}
}

@Composable
private fun DownloadDialog(
dependencyProjects: List<Pair<PlatformVersion.PlatformDependency, PlatformProject>>,
Expand All @@ -164,7 +176,7 @@ private fun DownloadDialog(
onInstall: (List<Version>) -> 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

Expand Down
Loading