Skip to content

Commit

Permalink
feat: add config download update support
Browse files Browse the repository at this point in the history
  • Loading branch information
DevScyu committed Nov 11, 2022
1 parent 0eac8dc commit b0ef346
Show file tree
Hide file tree
Showing 6 changed files with 94 additions and 50 deletions.
1 change: 1 addition & 0 deletions src/main/kotlin/com/mineinabyss/launchy/data/Config.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ data class Config(
val toggledMods: Set<ModName> = setOf(),
val toggledConfigs: Set<ModName> = setOf(),
val downloads: Map<ModName, DownloadURL> = mapOf(),
val configs: Map<ModName, ConfigURL> = mapOf(),
val seenGroups: Set<GroupName> = setOf(),
val installedFabricVersion: String? = null,
val downloadUpdates: Boolean = true,
Expand Down
4 changes: 3 additions & 1 deletion src/main/kotlin/com/mineinabyss/launchy/data/Dirs.kt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ object Dirs {
OS.LINUX -> Path(System.getProperty("user.home")) / ".wynntils"
}
val mods = wynntils / "mods"
val configZip = wynntils / "configs.zip"
val tmp = wynntils / ".tmp"

val config = when (OS.get()) {
OS.WINDOWS -> Path(System.getenv("APPDATA"))
Expand All @@ -31,6 +31,8 @@ object Dirs {
fun createDirs() {
config.createDirectories()
wynntils.createDirectories()
mods.createDirectories()
tmp.createDirectories()
}

fun createConfigFiles() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ package com.mineinabyss.launchy.data
typealias ModName = String
typealias GroupName = String
typealias DownloadURL = String
typealias ConfigURL = String
31 changes: 21 additions & 10 deletions src/main/kotlin/com/mineinabyss/launchy/logic/Downloader.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,27 @@ object Downloader {
writeTo: Path,
onProgressUpdate: (progress: Progress) -> Unit = {},
) {
val startTime = System.currentTimeMillis()
val response = httpClient.get<HttpStatement>(url) {
onDownload { bytesSentTotal, contentLength ->
onProgressUpdate(Progress(bytesSentTotal, contentLength, timeElapsed = System.currentTimeMillis() - startTime))
}
}.receive<ByteArray>()
writeTo.parent.createDirectories()
if (!writeTo.exists())
writeTo.createFile()
writeTo.writeBytes(response)
try {
val startTime = System.currentTimeMillis()
val response = httpClient.get<HttpStatement>(url) {
onDownload { bytesSentTotal, contentLength ->
onProgressUpdate(
Progress(
bytesSentTotal,
contentLength,
timeElapsed = System.currentTimeMillis() - startTime
)
)
}
}.receive<ByteArray>()
writeTo.parent.createDirectories()
if (!writeTo.exists())
writeTo.createFile()
writeTo.writeBytes(response)
} catch (e: Exception) {
e.printStackTrace()
throw e
}
}
}

Expand Down
99 changes: 60 additions & 39 deletions src/main/kotlin/com/mineinabyss/launchy/logic/LaunchyState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.util.*
import java.util.concurrent.CancellationException
import kotlin.io.path.deleteIfExists
import kotlin.io.path.div
import kotlin.io.path.exists
Expand Down Expand Up @@ -42,8 +43,8 @@ class LaunchyState(
)
}

val downloadConfigURLs = mutableStateMapOf<Mod, DownloadURL>().apply {
putAll(config.downloads
val downloadConfigURLs = mutableStateMapOf<Mod, ConfigURL>().apply {
putAll(config.configs
.mapNotNull { it.key.toMod()?.to(it.value) }
.toMap()
)
Expand All @@ -53,16 +54,23 @@ class LaunchyState(

var notPresentDownloads by mutableStateOf(setOf<Mod>())
private set

init {
updateNotPresent()
}

val upToDate: Set<Mod> by derivedStateOf {
(downloadURLs - notPresentDownloads).filter { (mod, url) -> mod.url == url }.keys
val upToDateMods by derivedStateOf {
enabledMods.filter { it in downloadURLs && downloadURLs[it] == it.url }
}

val queuedDownloads by derivedStateOf { enabledMods - upToDate }
val upToDateConfigs by derivedStateOf {
enabledMods.filter { it in downloadConfigURLs && downloadConfigURLs[it] == it.configUrl }
}

val enabledModsWithConfig by derivedStateOf {
enabledMods.filter { it.configUrl != "" }
}

val queuedDownloads by derivedStateOf { (enabledMods - upToDateMods) + (enabledModsWithConfig - upToDateConfigs) }
val queuedUpdates by derivedStateOf { queuedDownloads.filter { it.isDownloaded }.toSet() }
val queuedInstalls by derivedStateOf { queuedDownloads - queuedUpdates }
private var _deleted by mutableStateOf(0)
Expand All @@ -71,12 +79,7 @@ class LaunchyState(
disabledMods.filter { it.isDownloaded }.also { if (it.isEmpty()) updateNotPresent() }
}

var notPresentConfigDownloads by mutableStateOf(setOf<Mod>())
private set

init {
configUpdateNotPresent()
}

val enabledConfigs: MutableSet<Mod> = mutableStateSetOf<Mod>().apply {
addAll(config.toggledConfigs.mapNotNull { it.toMod() })
Expand Down Expand Up @@ -187,41 +190,61 @@ class LaunchyState(

suspend fun download(mod: Mod) {
runCatching {
println("Starting download of ${mod.name}")
downloading[mod] = Progress(0, 0, 0) // set progress to 0
Downloader.download(url = mod.url, writeTo = mod.file) progress@{
downloading[mod] = it
if (mod !in upToDateMods) {
try {
println("Starting download of ${mod.name}")
downloading[mod] = Progress(0, 0, 0) // set progress to 0
Downloader.download(url = mod.url, writeTo = mod.file) progress@{
downloading[mod] = it
}
downloadURLs[mod] = mod.url
save()
println("Successfully downloaded ${mod.name}")
} catch (ex: CancellationException) {
throw ex // Must let the CancellationException propagate
} catch (e: Exception) {
println("Failed to download ${mod.name}")
e.printStackTrace()
failedDownloads += mod
} finally {
println("Finished download of ${mod.name}")
downloading -= mod
}
}
downloadURLs[mod] = mod.url
save()

if (mod.configUrl.isNotBlank() && (mod in enabledConfigs)) {
Downloader.download(url = mod.configUrl, writeTo = Dirs.configZip) progress@{
downloadingConfigs[mod] = it
if (mod.configUrl.isNotBlank() && (mod in enabledConfigs) && mod !in upToDateConfigs) {
try {
println("Starting download of ${mod.name} config")
downloadingConfigs[mod] = Progress(0, 0, 0) // set progress to 0
Downloader.download(url = mod.configUrl, writeTo = mod.config) progress@{
downloadingConfigs[mod] = it
}
downloadConfigURLs[mod] = mod.configUrl
unzip(mod.config.toFile(), Dirs.wynntils.toString())
mod.config.toFile().delete()
save()
println("Successfully downloaded ${mod.name} config")
} catch (ex: CancellationException) {
throw ex // Must let the CancellationException propagate
} catch (e: Exception) {
println("Failed to download ${mod.name} config")
failedDownloads += mod
e.printStackTrace()
} finally {
println("Finished download of ${mod.name} config")
downloadingConfigs -= mod
}
downloadConfigURLs[mod] = mod.configUrl
unzip((Dirs.configZip).toFile(), Dirs.wynntils.toString())
(Dirs.configZip).toFile().delete()
}
}.onFailure {
downloading -= mod
downloadingConfigs -= mod
failedDownloads += mod
setModEnabled(mod, false)

println("Failed to download ${mod.name}")
it.printStackTrace()
if (it !is CancellationException) {
it.printStackTrace()
}
// Badge {
// Text("Failed to download ${mod.name}: ${it.localizedMessage}!"/*, "OK"*/)
// }
// scaffoldState.snackbarHostState.showSnackbar(
// "Failed to download ${mod.name}: ${it.localizedMessage}!", "OK"
// )
}.onSuccess {
val downloadInfo = downloading[mod]!!
println("Finished downloading ${mod.name} in ${downloadInfo.timeElapsed}ms, ${downloadInfo.bytesDownloaded.toFloat() / 1024}kb")
downloading -= mod
downloadingConfigs -= mod
}
}

Expand All @@ -233,6 +256,7 @@ class LaunchyState(
toggledMods = enabledMods.mapTo(mutableSetOf()) { it.name },
toggledConfigs = enabledConfigs.mapTo(mutableSetOf()) { it.name } + enabledMods.filter { it.forceConfigDownload }.mapTo(mutableSetOf()) { it.name },
downloads = downloadURLs.mapKeys { it.key.name },
configs = downloadConfigURLs.mapKeys { it.key.name },
seenGroups = versions.groups.map { it.name }.toSet(),
installedFabricVersion = installedFabricVersion,
handledImportOptions = handledImportOptions,
Expand All @@ -244,16 +268,13 @@ class LaunchyState(
fun GroupName.toGroup(): Group? = versions.nameToGroup[this]

val Mod.file get() = Dirs.mods / "${name}.jar"
val Mod.config get() = Dirs.tmp / "${name}-config.zip"
val Mod.isDownloaded get() = file.exists()

private fun updateNotPresent(): Set<Mod> {
return downloadURLs.filter { !it.key.isDownloaded }.keys.also { notPresentDownloads = it }
}

private fun configUpdateNotPresent(): Set<Mod> {
return downloadConfigURLs.filter { !it.key.isDownloaded }.keys.also { notPresentConfigDownloads = it }
}

fun launch() {
TODO()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,14 @@ fun InfoBar(modifier: Modifier = Modifier) {
)
}

if (state.failedDownloads.isNotEmpty()) {
// Show failed downloads
Text(
text = "Failed downloads: ${state.failedDownloads.size}",
style = MaterialTheme.typography.bodySmall,
)
}

// var path by remember { mutableStateOf("") }
// Button(onClick = {
// path = FileDialog(ComposeWindow()).apply {
Expand Down

0 comments on commit b0ef346

Please sign in to comment.