diff --git a/common/build.gradle.kts b/common/build.gradle.kts index 8bf77b8..8d4e465 100644 --- a/common/build.gradle.kts +++ b/common/build.gradle.kts @@ -31,11 +31,14 @@ kotlin { val generateBuildConfig = tasks.register("generateBuildConfig") { + // To allow overriding the current project version + val projectVersion: String? by project + val buildConfigDirectory = project.layout.buildDirectory.dir("generated") classFullyQualifiedName.set("generated.BuildConfig") generatedOutputDirectory.set(buildConfigDirectory) - fieldsToGenerate.put("PROJECT_VERSION", libs.versions.project.get()) + fieldsToGenerate.put("PROJECT_VERSION", projectVersion ?: libs.versions.project.get()) } sourceSets.main.configure { diff --git a/sync-script/src/main/kotlin/services/updater/JarAutoUpdater.kt b/sync-script/src/main/kotlin/services/updater/JarAutoUpdater.kt index b0032bd..b85b9eb 100644 --- a/sync-script/src/main/kotlin/services/updater/JarAutoUpdater.kt +++ b/sync-script/src/main/kotlin/services/updater/JarAutoUpdater.kt @@ -3,12 +3,15 @@ package services.updater import constants.ProjectInfoConstants import constants.SyncScriptDotMinecraftFiles import generated.BuildConfig +import gui.dialogs.LoadingIndicatorDialog import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.Request import utils.FileDownloader import utils.HttpService import utils.SystemInfoProvider +import utils.buildHtml +import utils.convertBytesToReadableMegabytesAsString import utils.createFileWithParentDirectoriesOrTerminate import utils.deleteExistingOrTerminate import utils.executeAsync @@ -38,10 +41,34 @@ object JarAutoUpdater { } val latestJarFileDownloadUrl = ProjectInfoConstants.LATEST_SYNC_SCRIPT_JAR_FILE_URL println("\uD83D\uDD3D Downloading the new JAR file from: $latestJarFileDownloadUrl") + + LoadingIndicatorDialog.instance?.updateComponentProperties( + title = "Updating...", + infoText = + "Sending HTTP request...", + progress = 0, + detailsText = + "Initiating network request for the update.", + ) FileDownloader( downloadUrl = ProjectInfoConstants.LATEST_SYNC_SCRIPT_JAR_FILE_URL, targetFilePath = newJarFile, - progressListener = null, + progressListener = { downloadedBytes, downloadedProgress, bytesToDownload -> + LoadingIndicatorDialog.instance?.updateComponentProperties( + title = "Updating...", + infoText = + buildHtml { + text("Downloading ") + boldText(ProjectInfoConstants.DISPLAY_NAME) + text(" Update") + }.buildBodyAsText(), + progress = downloadedProgress.toInt(), + // TODO: This is duplicated twice + detailsText = + "${downloadedBytes.convertBytesToReadableMegabytesAsString()} MB /" + + " ${bytesToDownload.convertBytesToReadableMegabytesAsString()} MB", + ) + }, ).downloadFile() Result.success(newJarFile) } catch (e: Exception) { @@ -77,6 +104,12 @@ object JarAutoUpdater { } private suspend fun shouldUpdate(): Boolean { + LoadingIndicatorDialog.instance?.updateComponentProperties( + title = "Checking for Update...", + infoText = "Checking for a new update...", + progress = 0, + detailsText = "Determining if a new version is available.", + ) val latestProjectVersionString = getLatestProjectVersion().getOrElse { println("❌ We couldn't get the latest project version: ${it.message}") @@ -126,6 +159,8 @@ object JarAutoUpdater { return } + LoadingIndicatorDialog.instance?.isVisible = true + val shouldUpdate = shouldUpdate() if (!shouldUpdate) { return @@ -164,20 +199,40 @@ object JarAutoUpdater { } OperatingSystem.Windows -> { - // On Windows, we can't rename, delete or modify the current running JAR file due to file locking + // On Windows, we can't rename, delete or modify the current running JAR file due to file locking. + // Will create a batch script, execute it in a different windows process + // and close the application immediately; the batch script expects + // the application to be closed after a short delay. + // The batch script will handle the update process + val updateBatScriptFile = SyncScriptDotMinecraftFiles.SyncScriptData.Temp.path .resolve("update.bat") withContext(Dispatchers.IO) { updateBatScriptFile.createFileWithParentDirectoriesOrTerminate() } + val secondsToWait = 1 + + val windowTitle = "Update Complete" + val message = "${ProjectInfoConstants.DISPLAY_NAME} has been updated. Relaunch to use the new version." + + val messageVbsFilePath = + SyncScriptDotMinecraftFiles.SyncScriptData.Temp.path + .resolve("updateMessage.vbs") + updateBatScriptFile.writeText( """ @echo off - echo Waiting for 2 seconds to ensure application closure... - timeout /t 2 > nul + + echo Waiting for $secondsToWait second to ensure application closure... + timeout /t $secondsToWait > nul del "${currentRunningJarFilePath.absolutePathString()}" move "${newJarFilePath.absolutePathString()}" "${currentRunningJarFilePath.absolutePathString()}" + + echo MsgBox "$message", 64, "$windowTitle" > "${messageVbsFilePath.absolutePathString()}" + cscript //nologo "${messageVbsFilePath.absolutePathString()}" + del "${messageVbsFilePath.absolutePathString()}" + exit """.trimIndent(), )