Skip to content

Refactored default Proton downloads to launch deps#1031

Merged
utkarshdalal merged 1 commit intoutkarshdalal:masterfrom
unbelievableflavour:default-proton-downloads-refactor
Mar 27, 2026
Merged

Refactored default Proton downloads to launch deps#1031
utkarshdalal merged 1 commit intoutkarshdalal:masterfrom
unbelievableflavour:default-proton-downloads-refactor

Conversation

@unbelievableflavour
Copy link
Copy Markdown
Contributor

@unbelievableflavour unbelievableflavour commented Mar 26, 2026


Summary by cubic

Moved default Proton 9.0 (arm64ec/x86_64) download and extraction into a launch dependency for Bionic containers. This simplifies pre-launch and standardizes behavior; GLIBC containers are unchanged.

  • Refactors
    • Added BionicDefaultProtonDependency for Container.BIONIC when wineVersion is proton-9.0-arm64ec or proton-9.0-x86_64.
    • Registered in LaunchDependencies and removed inline handling from PluviaMain.
    • Checks install via opt/<protonVersion>/bin, downloads via SteamService.downloadFile with progress, extracts the .txz to /opt/<protonVersion>, then deletes the archive.

Written for commit c9f51eb. Summary will update on new commits.

Summary by CodeRabbit

  • Refactor

    • Adjusted launch dependency ordering so BIONIC Proton handling runs earlier; GLIBC pre-launch no longer performs Proton download/extract.
  • New Features

    • BIONIC containers now detect and on-demand install Proton 9.0 (ARM64EC and x86_64) with progress/status updates, archive extraction, and cleanup; installation is skipped if already present.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Mar 26, 2026

📝 Walkthrough

Walkthrough

Removed Proton-specific download/extract from preLaunchApp and introduced a new BionicDefaultProtonDependency that detects, downloads (via SteamService), and extracts Proton archives into ImageFs when running in BIONIC containers with proton-9.0-* wine versions.

Changes

Cohort / File(s) Summary
PluviaMain change
app/src/main/java/app/gamenative/ui/PluviaMain.kt
Removed the GLIBC/Bionic branch in preLaunchApp that previously downloaded and extracted proton-9.0-*.txz archives.
Launch dependencies registration
app/src/main/java/app/gamenative/utils/LaunchDependencies.kt
Prepended BionicDefaultProtonDependency to the launchDependencies list so it is evaluated before GogScriptInterpreterDependency.
New dependency implementation
app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt
Added BionicDefaultProtonDependency implementing LaunchDependency: applies to Container.BIONIC with proton-9.0-* wine versions; checks opt/<wineVersion>/bin for satisfaction; installs by checking SteamService.isFileInstallable, downloading via SteamService.downloadFile(...).await(), extracting with TarCompressorUtils.extract into ImageFs, reporting progress/messages, and cleaning up the archive.

Sequence Diagram(s)

sequenceDiagram
  participant Launcher as LaunchDependencies
  participant Dep as BionicDefaultProtonDependency
  participant Steam as SteamService
  participant FS as ImageFs
  participant Tar as TarCompressorUtils

  Launcher->>Dep: ensureLaunchDependencies(container, gameSource, gameId)
  Dep->>FS: stat `opt/<wineVersion>/bin` (isSatisfied)
  alt not present
    Dep->>Steam: isFileInstallable(proton-9.0-*.txz)?
    alt installable or available remotely
      Dep->>Steam: downloadFile(...).await() (progress callbacks)
      Steam-->>Dep: saved archive path
    end
    Dep->>Tar: extract archive -> FS root `opt/<wineVersion>` (set unknown progress)
    Tar-->>FS: extracted files
    Dep->>FS: delete downloaded archive
  end
  Dep-->>Launcher: dependency satisfied
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • phobos665

Poem

🐰 I hopped through code with nimble paws,
Moved proton chores to orderly laws.
I fetch, I extract, I tidy the FS—
A rabbit's work: tidy, swift, no mess. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: refactoring Proton download logic from inline code in PluviaMain into a dedicated launch dependency component.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 3 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt">

<violation number="1" location="app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt:30">
P2: `File(rootDir, "/opt/$protonVersion")` uses an absolute child path, so the ImageFs root is ignored and this resolves to `/opt/...` on the device root rather than under imagefs. That will make `isSatisfied`/extract checks look in the wrong place. Use a relative child path (no leading slash).</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`:
- Around line 75-88: The extraction result from
TarCompressorUtils.extract(downloaded, outFile) is ignored causing the archive
to be deleted even on failure and leaving partial files; update the block around
outFile/binDir to capture the boolean return of TarCompressorUtils.extract (call
site: TarCompressorUtils.extract(...) and variable downloaded), only call
downloaded.delete() when extraction succeeded, and on failure remove any
partially created outFile (or its binDir) and surface the error by updating
callbacks (e.g., callbacks.setLoadingMessage / setLoadingProgress) and
returning/failing so isSatisfied() cannot incorrectly pass; ensure you reference
outFile, binDir, TarCompressorUtils.extract, downloaded and isSatisfied when
making these checks and cleanup steps.
- Around line 53-73: The unprotected .await() on SteamService.downloadFile can
throw IOException (from SteamService.fetchFileWithFallback) and crash the
install; wrap each coroutineScope { SteamService.downloadFile(...).await() }
invocation in a try-catch that catches IOException (and optionally
CancellationException), call the UI callbacks (e.g., callbacks.setLoadingMessage
and/or a failure callback) with a user-friendly error including e.message,
stop/abort the install flow cleanly (return/throw a controlled error) instead of
letting the raw exception propagate; update the blocks around protonVersion
checks and the downloadFile await calls to implement this handling.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9d370ef3-e821-49cf-aae4-0f2f3a804fbf

📥 Commits

Reviewing files that changed from the base of the PR and between 460dadc and ddc893e.

📒 Files selected for processing (3)
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/java/app/gamenative/utils/LaunchDependencies.kt
  • app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt
💤 Files with no reviewable changes (1)
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt

@unbelievableflavour unbelievableflavour force-pushed the default-proton-downloads-refactor branch 2 times, most recently from d2f09d5 to 848b0be Compare March 26, 2026 15:43
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt (1)

83-85: ⚠️ Potential issue | 🟡 Minor

Partial extraction not cleaned up on failure.

When extraction fails, the code throws an exception but leaves potentially corrupt partial files in outFile. This could cause isSatisfied() to return true on retry if the bin/ directory was partially created, or leave orphaned files consuming storage.

🛡️ Proposed fix to clean up partial extraction
             val success = TarCompressorUtils.extract(
                 TarCompressorUtils.Type.XZ,
                 downloaded,
                 outFile,
             )
             if (!success) {
+                outFile.deleteRecursively()
+                Timber.e("Failed to extract $protonVersion")
                 throw IllegalStateException("Failed to extract $protonVersion from ${downloaded.absolutePath}")
             }
             downloaded.delete()
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`
around lines 83 - 85, The extraction failure path currently throws an
IllegalStateException but leaves partial files behind (outFile /
partially-created bin/), so update the failure branch in the extraction routine
to delete the destination (outFile) and any partially created extraction
directory (e.g., the target that contains bin/) before throwing; specifically,
in the block surrounding the throw for "Failed to extract $protonVersion from
${downloaded.absolutePath}" ensure you close any streams, attempt a recursive
delete of outFile/target directory, log the cleanup attempt, and only then
rethrow the IllegalStateException so subsequent isSatisfied() checks won't be
fooled by leftover partial files.
🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt (1)

69-69: Redundant ImageFs.find(context) call.

imageFs is already retrieved on line 50 but is not reused here. Consider using the existing reference to avoid redundant lookups.

♻️ Proposed fix
-        val outFile = File(ImageFs.find(context).getRootDir(), "opt/$protonVersion")
+        val outFile = File(imageFs.getRootDir(), "opt/$protonVersion")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`
at line 69, The code redundantly calls ImageFs.find(context) when an imageFs
variable was already obtained earlier; update the outFile construction (val
outFile in BionicDefaultProtonDependency.kt) to reuse the existing imageFs
reference (imageFs.getRootDir()) instead of calling ImageFs.find(context) again,
keeping the same "opt/$protonVersion" path and preserving protonVersion and
context usage elsewhere.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`:
- Line 58: The hardcoded loading message in BionicDefaultProtonDependency always
says "Downloading arm64ec Proton" regardless of which archive is being
downloaded; update the callbacks.setLoadingMessage call to compute the message
from the actual archiveName (or derive arch from archiveName) so it displays the
correct variant (e.g., "Downloading <arch> Proton" or include archiveName
directly). Locate the callbacks.setLoadingMessage invocation in
BionicDefaultProtonDependency.kt and replace the literal string with a formatted
message built from the archiveName variable (or a small function that maps
archiveName to a user-friendly architecture label) so the UI reflects the real
download target.

---

Duplicate comments:
In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`:
- Around line 83-85: The extraction failure path currently throws an
IllegalStateException but leaves partial files behind (outFile /
partially-created bin/), so update the failure branch in the extraction routine
to delete the destination (outFile) and any partially created extraction
directory (e.g., the target that contains bin/) before throwing; specifically,
in the block surrounding the throw for "Failed to extract $protonVersion from
${downloaded.absolutePath}" ensure you close any streams, attempt a recursive
delete of outFile/target directory, log the cleanup attempt, and only then
rethrow the IllegalStateException so subsequent isSatisfied() checks won't be
fooled by leftover partial files.

---

Nitpick comments:
In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`:
- Line 69: The code redundantly calls ImageFs.find(context) when an imageFs
variable was already obtained earlier; update the outFile construction (val
outFile in BionicDefaultProtonDependency.kt) to reuse the existing imageFs
reference (imageFs.getRootDir()) instead of calling ImageFs.find(context) again,
keeping the same "opt/$protonVersion" path and preserving protonVersion and
context usage elsewhere.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: af893e95-f292-4953-b572-ac30efef6bf5

📥 Commits

Reviewing files that changed from the base of the PR and between d2f09d5 and 848b0be.

📒 Files selected for processing (3)
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/java/app/gamenative/utils/LaunchDependencies.kt
  • app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt
💤 Files with no reviewable changes (1)
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/app/gamenative/utils/LaunchDependencies.kt

@unbelievableflavour unbelievableflavour force-pushed the default-proton-downloads-refactor branch from 848b0be to c9f51eb Compare March 26, 2026 15:52
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt (1)

77-86: ⚠️ Potential issue | 🟠 Major

Handle extraction failure with cleanup to avoid false “satisfied” state.

If extraction fails, Line 84 throws, but partial files in outFile remain. Since isSatisfied() (Line 27-31) only checks for bin/, a partial extract can make future runs skip reinstall incorrectly.

🧹 Proposed failure cleanup
             withContext(Dispatchers.IO) {
                 val success = TarCompressorUtils.extract(
                     TarCompressorUtils.Type.XZ,
                     downloaded,
                     outFile,
                 )
                 if (!success) {
+                    outFile.deleteRecursively()
+                    downloaded.delete()
                     throw IllegalStateException("Failed to extract $protonVersion from ${downloaded.absolutePath}")
                 }
                 downloaded.delete()
             }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`
around lines 77 - 86, The extraction failure currently throws an
IllegalStateException but leaves partial files in outFile which can make
BionicDefaultProtonDependency.isSatisfied() return true later; modify the
extraction block inside BionicDefaultProtonDependency so that if
TarCompressorUtils.extract(...) returns false (or an exception is thrown) you
perform cleanup of partial extraction artifacts (e.g., delete outFile or its
extracted children) and still rethrow the error (or throw the
IllegalStateException) afterward; ensure this covers both the boolean-false path
and any exceptions from TarCompressorUtils.extract so partial contents are
removed before signaling failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`:
- Around line 17-18: The KDoc at the top of BionicDefaultProtonDependency (the
comment describing extraction destination) is out of sync with the
implementation: the code writes Proton to opt/<wineVersion> (see usage of
wineVersion and the write/extract logic), but the KDoc still says
imagefs_shared/proton; update the KDoc to state that Proton is downloaded and
extracted to opt/<wineVersion> (or to whichever exact opt path the
implementation uses) and adjust the wording to match the conditional behavior
for BIONIC and proton-9.0-arm64ec/proton-9.0-x86_64.

---

Duplicate comments:
In
`@app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt`:
- Around line 77-86: The extraction failure currently throws an
IllegalStateException but leaves partial files in outFile which can make
BionicDefaultProtonDependency.isSatisfied() return true later; modify the
extraction block inside BionicDefaultProtonDependency so that if
TarCompressorUtils.extract(...) returns false (or an exception is thrown) you
perform cleanup of partial extraction artifacts (e.g., delete outFile or its
extracted children) and still rethrow the error (or throw the
IllegalStateException) afterward; ensure this covers both the boolean-false path
and any exceptions from TarCompressorUtils.extract so partial contents are
removed before signaling failure.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d09bb27a-b526-474b-8281-c70668699e2c

📥 Commits

Reviewing files that changed from the base of the PR and between 848b0be and c9f51eb.

📒 Files selected for processing (3)
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt
  • app/src/main/java/app/gamenative/utils/LaunchDependencies.kt
  • app/src/main/java/app/gamenative/utils/launchdependencies/BionicDefaultProtonDependency.kt
💤 Files with no reviewable changes (1)
  • app/src/main/java/app/gamenative/ui/PluviaMain.kt

@utkarshdalal utkarshdalal merged commit 86331e1 into utkarshdalal:master Mar 27, 2026
2 checks passed
@unbelievableflavour unbelievableflavour deleted the default-proton-downloads-refactor branch March 27, 2026 06:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants