From 3e8632a2d2b73ec1dfee043e18cc4a2c289c1eff Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Thu, 1 Jan 2026 18:34:56 +0800 Subject: [PATCH 1/7] Adds debug check for chunk cancellation log Prevents unnecessary logging of chunk processing job cancellation in non-debug mode. This change reduces log clutter by only logging the cancellation of the chunk processing job when the debug flag is enabled. --- .../in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt index 547a3e3b..01634e85 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt @@ -1199,7 +1199,7 @@ class DepotDownloader @JvmOverloads constructor( // Cancel the continuous flow job since no more chunks will be added chunkProcessingJob?.cancel() - logger?.debug("Canceled chunk processing job for depot ${depot.depotId}") + if (debug) logger?.debug("Canceled chunk processing job for depot ${depot.depotId}") } } From 7e9b5799684ca492689403394abf4b6b74f0d139 Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Thu, 1 Jan 2026 14:22:07 +0800 Subject: [PATCH 2/7] Fixes race condition during download completion Moves the `finishDepotDownload` call to after all depots have been processed. This resolves a race condition where the download completion logic could be triggered before all files were fully downloaded, leading to incomplete or corrupted downloads. --- .../javasteam/depotdownloader/DepotDownloader.kt | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt index 01634e85..a3169eec 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt @@ -244,7 +244,9 @@ class DepotDownloader @JvmOverloads constructor( steam3!!.packageTokens[license.packageID] = license.accessToken } } + } + fun startDownloading() { // Launch the processing loop scope.launch { processItems() @@ -952,6 +954,8 @@ class DepotDownloader @JvmOverloads constructor( "(${downloadCounter.totalBytesUncompressed} bytes uncompressed) from ${depots.size} depots" ) } + + finishDepotDownload(mainAppId) } private suspend fun processDepotManifestAndFiles( @@ -1244,10 +1248,6 @@ class DepotDownloader @JvmOverloads constructor( } if (debug) logger?.debug("Depot ${depot.depotId} - Downloaded ${depotCounter.depotBytesCompressed} bytes (${depotCounter.depotBytesUncompressed} bytes uncompressed)") - - if (isLastDepot) { - finishDepotDownload(mainAppId) - } } private suspend fun downloadSteam3DepotFile( From fc01e15e799d7a3e045c84d7a92d4938b5fa654f Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Thu, 1 Jan 2026 17:33:01 +0800 Subject: [PATCH 3/7] Fixes race condition in download completion Ensures that the onDownloadCompleted event is notified on the main thread to prevent potential race conditions when updating the UI or other thread-sensitive components. Also, handles the case where the AppItem is not found gracefully. --- .../javasteam/depotdownloader/DepotDownloader.kt | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt index a3169eec..1c67167c 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt @@ -1600,7 +1600,18 @@ class DepotDownloader @JvmOverloads constructor( private suspend fun finishDepotDownload(mainAppId: Int) { val appItem = processingItemsMap[mainAppId] if (appItem != null) { + if (debug) { + logger?.debug("Notifying onDownloadCompleted") + } notifyListeners { it.onDownloadCompleted(appItem) } + } else { + if (debug) { + logger?.error("AppItem not found, cannot notify onDownloadCompleted") + } + } + + if (debug) { + logger?.debug("Complete the completionFuture.") } completionFuture.complete(null) @@ -1619,7 +1630,7 @@ class DepotDownloader @JvmOverloads constructor( } private fun notifyListeners(action: (IDownloadListener) -> Unit) { - scope.launch(Dispatchers.IO) { + scope.launch(Dispatchers.Main) { listeners.forEach { listener -> action(listener) } } } From 20a6e122874044d93e8135b52a670fcce1aa3875 Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Thu, 1 Jan 2026 19:17:03 +0800 Subject: [PATCH 4/7] Fixes potential race condition Moves the listener notification to an IO dispatcher to avoid potential race conditions when listeners perform long-running operations. --- .../in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt index 1c67167c..441870f5 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt @@ -1630,7 +1630,7 @@ class DepotDownloader @JvmOverloads constructor( } private fun notifyListeners(action: (IDownloadListener) -> Unit) { - scope.launch(Dispatchers.Main) { + scope.launch(Dispatchers.IO) { listeners.forEach { listener -> action(listener) } } } From 4b9739b28a971f5e2c47362e1a76a7eb6e00f6ad Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Fri, 2 Jan 2026 08:17:45 +0800 Subject: [PATCH 5/7] Update SampleDownloadApp for changed download flow --- .../javasteamsamples/_023_downloadapp/SampleDownloadApp.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java b/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java index 1305d187..a91655b6 100644 --- a/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java +++ b/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java @@ -306,6 +306,9 @@ private void downloadApp() { // Once all items in queue are done, 'completion' will signal that everything had finished. depotDownloader.finishAdding(); + // Start downloading. + depotDownloader.startDownloading(); + // Block until we're done downloading. // Note: If you did not call `finishAdding()` before awaiting, depotDownloader will be expecting // more items to be added to queue. It may look like a hang. You could call `close()` to finish too. From 312968daddbdf27a6094a02944fd921e5a1db7ab Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Fri, 2 Jan 2026 09:18:05 +0800 Subject: [PATCH 6/7] Adds auto-start option to DepotDownloader Adds an option to control whether the DepotDownloader automatically starts downloading items after initialization. This allows for more control over the download process, especially in scenarios where additional configuration or setup is required before starting the download. Removes the manual call to startDownloading() in the example application as it's now handled automatically by default. --- .../javasteam/depotdownloader/DepotDownloader.kt | 8 ++++++++ .../_023_downloadapp/SampleDownloadApp.java | 3 --- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt index 441870f5..bb1d5dc9 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt @@ -106,6 +106,7 @@ import kotlin.text.toLongOrNull * @param maxFileWrites Number of concurrent files being written. Default: 1 * @param androidEmulation Forces "Windows" as the default OS filter. Used when running Android games in PC emulators that expect Windows builds. * @param parentJob Parent job for the downloader. If provided, the downloader will be cancelled when the parent job is cancelled. + * @param autoStartDownload Whether to start downloading automatically. If false, you must call [startDownloading] manually. * * @author Oxters * @author Lossy @@ -123,6 +124,7 @@ class DepotDownloader @JvmOverloads constructor( private var maxFileWrites: Int = 1, private val androidEmulation: Boolean = false, private val parentJob: Job? = null, + private val autoStartDownload: Boolean = true, ) : Closeable { companion object { @@ -244,6 +246,12 @@ class DepotDownloader @JvmOverloads constructor( steam3!!.packageTokens[license.packageID] = license.accessToken } } + + if (autoStartDownload) { + scope.launch { + processItems() + } + } } fun startDownloading() { diff --git a/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java b/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java index a91655b6..1305d187 100644 --- a/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java +++ b/javasteam-samples/src/main/java/in/dragonbra/javasteamsamples/_023_downloadapp/SampleDownloadApp.java @@ -306,9 +306,6 @@ private void downloadApp() { // Once all items in queue are done, 'completion' will signal that everything had finished. depotDownloader.finishAdding(); - // Start downloading. - depotDownloader.startDownloading(); - // Block until we're done downloading. // Note: If you did not call `finishAdding()` before awaiting, depotDownloader will be expecting // more items to be added to queue. It may look like a hang. You could call `close()` to finish too. From 50ba53ac52a1e8017cb5554799f10dfb51bd25fb Mon Sep 17 00:00:00 2001 From: Joshua Tam <297250+joshuatam@users.noreply.github.com> Date: Fri, 2 Jan 2026 10:28:02 +0800 Subject: [PATCH 7/7] use startDownloading() on Init() --- .../in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt index bb1d5dc9..58b5f4a5 100644 --- a/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt +++ b/javasteam-depotdownloader/src/main/kotlin/in/dragonbra/javasteam/depotdownloader/DepotDownloader.kt @@ -248,9 +248,7 @@ class DepotDownloader @JvmOverloads constructor( } if (autoStartDownload) { - scope.launch { - processItems() - } + startDownloading() } }