Skip to content

Commit

Permalink
fix: Always showing update available on first install
Browse files Browse the repository at this point in the history
refactor: Clean up download functions, separate cache checking logic from download logic
fix: Fix issues with download cache when two instances reference the same URLs by caching per instance
feat: Check for instance updates before launch from the main screen
feat: Add updates available dialog when clicking play button, remove the install change dialog (This simplifies the UX a lot, there's confusion between what install/update would mean.) Plan to add revert update button in the future.
fix: Don't use transparency on window, issues seem to be fixed on Windows 11. Hope to fix some issues on older Windows 10 devices.
chore: Remove onboarding screen until it gets updated
  • Loading branch information
0ffz committed Apr 3, 2024
1 parent f4d9dc3 commit 4a382f2
Show file tree
Hide file tree
Showing 19 changed files with 238 additions and 223 deletions.
2 changes: 1 addition & 1 deletion gradle.properties
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
group=com.mineinabyss
version=2.0.0-alpha.15
version=2.0.0-alpha.16
idofrontVersion=0.22.3
1 change: 0 additions & 1 deletion src/main/kotlin/com/mineinabyss/launchy/Main.kt
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ fun main() {
icon = icon,
onCloseRequest = onClose,
undecorated = true,
transparent = OS.get() == OS.WINDOWS, // Windows 11 shows a white bar on the bottom without this
) {
window.minimumSize = Dimension(600, 400)
val topBarState = remember { TopBarState(onClose, windowState, this) }
Expand Down
15 changes: 6 additions & 9 deletions src/main/kotlin/com/mineinabyss/launchy/data/Dirs.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.mineinabyss.launchy.data

import com.mineinabyss.launchy.logic.urlToFileName
import com.mineinabyss.launchy.data.config.GameInstance
import com.mineinabyss.launchy.util.OS
import java.util.*
import kotlin.io.path.*
Expand All @@ -26,17 +26,14 @@ object Dirs {
OS.LINUX -> home / ".config"
} / "mineinabyss"

val cacheDir = config / "cache"
val imageCache = cacheDir / "images"
fun cacheDir(instance: GameInstance) = instance.configDir / "cache"

val jdks = mineinabyss / ".jdks"
val imageCache = config / "cache" / "images"

val tmp = config / ".tmp"
val jdks = mineinabyss / ".jdks"

val accounts = config / "accounts"

fun tmpCloudInstance(url: String) = tmp / "cloudInstances" / "${urlToFileName(url)}.yml"

fun avatar(uuid: UUID) = imageCache / "avatar-$uuid"

val configFile = config / "mia-launcher.yml"
Expand All @@ -50,15 +47,15 @@ object Dirs {
fun createDirs() {
config.createDirectories()
mineinabyss.createDirectories()
tmp.createDirectories()
modpackConfigsDir.createDirectories()
jdks.createDirectories()
cacheDir.createDirectories()
imageCache.createDirectories()
}

fun createConfigFiles() {
if (configFile.notExists())
configFile.createFile().writeText("{}")
}

fun createTempCloudInstanceFile() = createTempFile(prefix = "cloudInstance", suffix = ".yml")
}
34 changes: 21 additions & 13 deletions src/main/kotlin/com/mineinabyss/launchy/data/config/GameInstance.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,10 @@ class GameInstance(
val downloadsDir: Path = minecraftDir / "launchyDownloads"
val userConfigFile = (configDir / "config.yml")

val updateCheckerScope = CoroutineScope(Dispatchers.IO)

var updatesAvailable by mutableStateOf(false)
var enabled: Boolean by mutableStateOf(true)

suspend fun createModpackState(state: LaunchyState): GameInstanceState? {
suspend fun createModpackState(state: LaunchyState, awaitUpdatesCheck: Boolean = false): GameInstanceState? {
val userConfig = InstanceUserConfig.load(userConfigFile).getOrNull() ?: InstanceUserConfig()

val modpack = state.runTask("loadingModpack ${config.name}", InProgressTask("Loading modpack ${config.name}")) {
Expand All @@ -51,13 +49,12 @@ class GameInstance(
return null
}
}

val cloudUrl = config.cloudInstanceURL
if (cloudUrl != null) AppDispatchers.IO.launch {
val updates = Downloader.checkUpdates(cloudUrl)
if (updates.result != UpdateResult.UpToDate) {
updatesAvailable = true
}
if (cloudUrl != null) {
AppDispatchers.IO.launch {
val result = Downloader.checkUpdates(this@GameInstance, cloudUrl)
if (result !is UpdateResult.UpToDate) updatesAvailable = true
}.also { if(awaitUpdatesCheck) it.join() }
}
return GameInstanceState(this, modpack, userConfig)
}
Expand All @@ -67,13 +64,24 @@ class GameInstance(
userMods
}

data class CloudInstanceWithHeaders(
val config: GameInstanceConfig,
val url: String,
val headers: Downloader.ModifyHeaders,
)

companion object {
fun create(state: LaunchyState, config: GameInstanceConfig) {
val instanceDir = Dirs.modpackConfigDir(config.name)
fun createCloudInstance(state: LaunchyState, cloud: CloudInstanceWithHeaders) {
val instanceDir = Dirs.modpackConfigDir(cloud.config.name)
instanceDir.createDirectories()

Formats.yaml.encodeToStream(config, (instanceDir / "instance.yml").outputStream())
state.gameInstances += GameInstance(instanceDir)
Formats.yaml.encodeToStream(
cloud.config.copy(cloudInstanceURL = cloud.url),
(instanceDir / "instance.yml").outputStream()
)
val instance = GameInstance(instanceDir)
Downloader.saveHeaders(instance, cloud.url, cloud.headers)
state.gameInstances += instance
}

fun readAll(rootDir: Path): List<GameInstance> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,15 @@ data class GameInstanceConfig(

private suspend fun loadBackground() {
runCatching {
Downloader.download(backgroundURL, backgroundPath, override = false)
Downloader.download(backgroundURL, backgroundPath, Downloader.Options(overwrite = false))
val painter = BitmapPainter(loadImageBitmap(backgroundPath.inputStream()))
cachedBackground.value = painter
}.onFailure { it.printStackTrace() }
}

private suspend fun loadLogo() {
runCatching {
Downloader.download(logoURL, logoPath, override = false)
Downloader.download(logoURL, logoPath, Downloader.Options(overwrite = false))
val painter = BitmapPainter(loadImageBitmap(logoPath.inputStream()))
cachedLogo.value = painter
}.onFailure { it.printStackTrace() }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ data class PlayerProfile(
avatar.also {
if (it.value != null) return@also
downloadScope.launch {
Downloader.downloadAvatar(uuid)
Downloader.downloadAvatar(uuid, Downloader.Options(overwrite = false))
it.value = BitmapPainter(
loadImageBitmap(Dirs.avatar(uuid).inputStream()),
filterQuality = FilterQuality.None
Expand Down
3 changes: 0 additions & 3 deletions src/main/kotlin/com/mineinabyss/launchy/data/modpacks/Mod.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,6 @@ data class Mod(

val downloadUrl: Url = Url(info.url)

val config = Dirs.tmp / "${info.name}-config.zip"


fun compatibleWith(other: Mod) =
other.info.name !in info.incompatibleWith && info.name !in other.info.incompatibleWith
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@ package com.mineinabyss.launchy.data.modpacks.source

import com.mineinabyss.launchy.data.config.GameInstance
import com.mineinabyss.launchy.data.modpacks.Modpack
import com.mineinabyss.launchy.logic.AppDispatchers
import com.mineinabyss.launchy.logic.Downloader
import com.mineinabyss.launchy.logic.UpdateResult
import kotlinx.coroutines.launch
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
import kotlin.io.path.notExists

@Serializable
sealed class PackSource {
abstract suspend fun loadInstance(instance: GameInstance): Result<Modpack>

abstract suspend fun updateInstance(instance: GameInstance): Result<GameInstance>

@Serializable
@SerialName("localFile")
class LocalFile(val type: PackType) : PackSource() {
Expand All @@ -30,16 +36,13 @@ sealed class PackSource {
class DownloadFromURL(val url: String, val type: PackType) : PackSource() {
override suspend fun loadInstance(instance: GameInstance): Result<Modpack> {
val downloadTo = type.getFilePath(instance.configDir)
if (!type.isDownloaded(instance.configDir))
Downloader.download(url, downloadTo, whenChanged = {
type.afterDownload(instance.configDir)
})
else {
instance.updateCheckerScope.launch {
val updates = Downloader.checkUpdates(url)
if (updates.result != UpdateResult.UpToDate) {
instance.updatesAvailable = true
}
if (downloadTo.notExists()) {
Downloader.download(url, downloadTo, options = Downloader.Options(saveModifyHeadersFor = instance))
type.afterDownload(instance.configDir)
} else {
AppDispatchers.IO.launch {
val result = Downloader.checkUpdates(instance, url)
if (result !is UpdateResult.UpToDate) instance.updatesAvailable = true
}
}
return LocalFile(type).loadInstance(instance)
Expand All @@ -48,14 +51,10 @@ sealed class PackSource {
override suspend fun updateInstance(instance: GameInstance): Result<GameInstance> {
return runCatching {
val downloadTo = type.getFilePath(instance.configDir)
Downloader.download(url, downloadTo)
Downloader.download(url, downloadTo, options = Downloader.Options(saveModifyHeadersFor = instance))
type.afterDownload(instance.configDir)
GameInstance(instance.configDir)
}
}
}

abstract suspend fun loadInstance(instance: GameInstance): Result<Modpack>

abstract suspend fun updateInstance(instance: GameInstance): Result<GameInstance>
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ import kotlin.io.path.*
enum class PackType {
Launchy, Modrinth;

fun isDownloaded(configDir: Path) = getFilePath(configDir).isRegularFile()

fun getFilePath(configDir: Path): Path {
val ext = when (this) {
Launchy -> "yml"
Expand Down
12 changes: 9 additions & 3 deletions src/main/kotlin/com/mineinabyss/launchy/logic/Auth.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,21 @@ object Auth {
onVerificationRequired: (VerificationRequired) -> Unit,
onAuthenticate: (FullJavaSession) -> Unit,
) {
val testAuth = MinecraftAuth.builder()
.withClientId("00000000402b5328")
.withScope("service::user.auth.xboxlive.com::MBI_SSL")
.deviceCode()
.withDeviceToken("Win32")
.sisuTitleAuthentication("https://multiplayer.minecraft.net/")
.buildMinecraftJavaProfileStep(true);
val httpClient = MinecraftAuth.createHttpClient()
val previousSession = state.currentProfile?.let { SessionStorage.load(it.uuid) }
if (previousSession != null) {
val refreshedSession = MinecraftAuth.JAVA_DEVICE_CODE_LOGIN.refresh(httpClient, previousSession)
val refreshedSession = testAuth.refresh(httpClient, previousSession)
onAuthenticate(refreshedSession)
return
}

val javaSession = MinecraftAuth.JAVA_DEVICE_CODE_LOGIN.getFromInput(
val javaSession = testAuth.getFromInput(
httpClient,
MsaDeviceCodeCallback { msaDeviceCode: MsaDeviceCode ->
onVerificationRequired(
Expand Down
Loading

0 comments on commit 4a382f2

Please sign in to comment.