Feat/eos overlay utkarsh#1286
Conversation
…r at all Costs working
📝 WalkthroughWalkthroughAdds EOS Overlay support for Epic games: new overlay manager, concurrent overlay downloader, deployment ID resolution, propagates user displayName through tokens/launch args, and integrates overlay installation as a launch dependency. (50 words) Changes
Sequence DiagramsequenceDiagram
participant App as App/GameLauncher
participant EpicSvc as EpicService
participant OverlayMgr as EpicOverlayManager
participant EpicMgr as EpicManager
participant DownloadMgr as EpicDownloadManager
participant WineReg as WineRegistryEditor
App->>EpicSvc: installOverlay(container, forceReinstall)
EpicSvc->>OverlayMgr: installOverlay(context, container, onProgress)
OverlayMgr->>EpicMgr: fetchManifest(namespace, catalogItemId, appName)
EpicMgr-->>OverlayMgr: ManifestResult
OverlayMgr->>DownloadMgr: downloadOverlay(manifestResult, installPath, onProgress)
DownloadMgr->>DownloadMgr: select CDN endpoints & batch chunks
DownloadMgr->>DownloadMgr: download chunks concurrently (batches)
DownloadMgr-->>OverlayMgr: Result<Unit>
OverlayMgr->>WineReg: write HKCU...OverlayPath in user.reg
OverlayMgr-->>EpicSvc: Result<Unit>
EpicSvc-->>App: Result<Unit>
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🤖 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/service/epic/EpicDownloadManager.kt`:
- Around line 403-409: The Cloudflare filter in downloadOverlay currently
hard-fails when all manifestResult.cdnUrls are Cloudflare; instead match
downloadGame/downloadGameWithManifest: compute val usableCdnUrls =
preferredCdnUrls.ifEmpty { manifestResult.cdnUrls }.filter {
!it.baseUrl.startsWith("https://cloudflare.epicgamescdn.com") } and then if
usableCdnUrls.isEmpty() fall back to ranking via
rankCdnUrlsByProbe(manifestResult.cdnUrls, ...) (or simply use
rankCdnUrlsByProbe(usableCdnUrls.ifEmpty { manifestResult.cdnUrls }, ...)) and
proceed with the ranked list rather than returning Result.failure; update the
downloadOverlay flow to use the ranked CDN list for subsequent download steps.
In `@app/src/main/java/app/gamenative/service/epic/EpicManager.kt`:
- Around line 999-1008: fetchDeploymentId currently writes an empty string to
cacheFile on any parse exception which permanently prevents retries; change the
logic in EpicManager.fetchDeploymentId so you only persist a negative cache when
you have high confidence there is no sidecar (e.g., when configStr is blank) and
do not write the empty string when JSONObject(configStr) throws — instead
propagate/return null without updating cache; implement a modest TTL check
(using cacheFile.lastModified()) so cached values older than N days
(configurable) trigger a re-fetch, and keep references to the existing symbols:
fetchDeploymentId, cacheFile.writeText, configStr, and the early-return that
uses takeIf { it.isNotEmpty() } so you update only the cache-writing branch and
add the age-check before using cached values.
In `@app/src/main/java/app/gamenative/service/epic/EpicOverlayManager.kt`:
- Around line 62-80: The docstring is misleading because installOverlay
currently short-circuits solely on isOverlayInstalled (which only checks for
EOSOVH-Win64-Shipping.dll); change the logic to actually verify freshness by
persisting and comparing a manifest fingerprint (e.g.,
manifest.meta.buildVersion or a hash of manifestBytes) next to the installed
files and using that for comparison before returning early. Concretely: when
installing (in installOverlay) write the manifest buildVersion/hash into
overlayDir (e.g., overlayDir/"manifest.meta"), implement a new helper (or extend
isOverlayInstalled) to read and compare the on-disk fingerprint against the
latest manifest; only return Result.success if fingerprint matches (or if
forceReinstall is true); otherwise proceed to download/install and update the
stored fingerprint; alternatively, if you prefer the simpler route, update the
installOverlay docstring to remove the “up-to-date” claim and document that it
only checks presence. Reference symbols: installOverlay, isOverlayInstalled,
overlayDir, manifest.meta/buildVersion (or manifestBytes hash).
- Around line 188-195: The removeRegistryPath function currently calls
WineRegistryEditor.setStringValue(EOS_OVERLAY_REG_KEY, EOS_OVERLAY_REG_VALUE,
"") which leaves an empty value instead of deleting it; change this to call
WineRegistryEditor.removeValue(EOS_OVERLAY_REG_KEY, EOS_OVERLAY_REG_VALUE)
inside the existing use { ... } block so the registry entry is actually removed
(keep the surrounding File lookup, use block, and the Timber.log message
intact).
🪄 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: a2d84fc1-1daa-4dd2-b719-eae9598e9176
📒 Files selected for processing (9)
app/src/main/java/app/gamenative/data/EpicGame.ktapp/src/main/java/app/gamenative/service/epic/EpicAuthManager.ktapp/src/main/java/app/gamenative/service/epic/EpicDownloadManager.ktapp/src/main/java/app/gamenative/service/epic/EpicGameLauncher.ktapp/src/main/java/app/gamenative/service/epic/EpicManager.ktapp/src/main/java/app/gamenative/service/epic/EpicOverlayManager.ktapp/src/main/java/app/gamenative/service/epic/EpicService.ktapp/src/main/java/app/gamenative/utils/LaunchDependencies.ktapp/src/main/java/app/gamenative/utils/launchdependencies/EpicOverlayDependency.kt
| /** | ||
| * Install the EOS overlay into [container]'s Wine prefix. | ||
| * | ||
| * Idempotent: if the overlay is already up-to-date, the function returns | ||
| * success without re-downloading unless [forceReinstall] is true. | ||
| */ | ||
| suspend fun installOverlay( | ||
| context: Context, | ||
| container: Container, | ||
| forceReinstall: Boolean = false, | ||
| onProgress: ((Int, Int) -> Unit)? = null, | ||
| ): Result<Unit> = withContext(Dispatchers.IO) { | ||
| try { | ||
| val overlayDir = overlayDir(container) | ||
|
|
||
| if (!forceReinstall && isOverlayInstalled(container)) { | ||
| Timber.tag("EOSOverlay").i("Overlay already installed at ${overlayDir.absolutePath}, skipping") | ||
| return@withContext Result.success(Unit) | ||
| } |
There was a problem hiding this comment.
Freshness check is missing — docstring "up-to-date" is misleading; overlay updates will never install.
isOverlayInstalled only tests for the presence of EOSOVH-Win64-Shipping.dll, and installOverlay short-circuits on that alone (Lines 77–80). The docstring at Lines 65–66 promises "if the overlay is already up-to-date, the function returns success without re-downloading", but there is no build/version fingerprint anywhere — once the DLL is on disk, the overlay is pinned at whatever version it had at first install, even after Epic ships a new overlay build.
Either tighten the docstring to match reality, or persist the overlay manifest buildId (e.g. manifest.meta?.buildVersion or a hash of manifestBytes) next to the install and compare it before short-circuiting. The latter is what Legendary does.
Also applies to: 129-133
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/app/gamenative/service/epic/EpicOverlayManager.kt` around
lines 62 - 80, The docstring is misleading because installOverlay currently
short-circuits solely on isOverlayInstalled (which only checks for
EOSOVH-Win64-Shipping.dll); change the logic to actually verify freshness by
persisting and comparing a manifest fingerprint (e.g.,
manifest.meta.buildVersion or a hash of manifestBytes) next to the installed
files and using that for comparison before returning early. Concretely: when
installing (in installOverlay) write the manifest buildVersion/hash into
overlayDir (e.g., overlayDir/"manifest.meta"), implement a new helper (or extend
isOverlayInstalled) to read and compare the on-disk fingerprint against the
latest manifest; only return Result.success if fingerprint matches (or if
forceReinstall is true); otherwise proceed to download/install and update the
stored fingerprint; alternatively, if you prefer the simpler route, update the
installOverlay docstring to remove the “up-to-date” claim and document that it
only checks presence. Reference symbols: installOverlay, isOverlayInstalled,
overlayDir, manifest.meta/buildVersion (or manifestBytes hash).
| private fun removeRegistryPath(container: Container) { | ||
| val userRegFile = File(container.rootDir, ".wine/user.reg") | ||
| if (!userRegFile.exists()) return | ||
| WineRegistryEditor(userRegFile).use { editor -> | ||
| editor.setStringValue(EOS_OVERLAY_REG_KEY, EOS_OVERLAY_REG_VALUE, "") | ||
| } | ||
| Timber.tag("EOSOverlay").d("Removed HKCU\\$EOS_OVERLAY_REG_KEY\\$EOS_OVERLAY_REG_VALUE") | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect the public API of WineRegistryEditor
fd -i 'WineRegistryEditor.java' --exec cat {}Repository: utkarshdalal/GameNative
Length of output: 21190
Use removeValue() instead of setStringValue(..., "") to actually delete the registry entry.
setStringValue(..., "") writes an empty value rather than removing it. The WineRegistryEditor API provides removeValue(String key, String name) for proper deletion. Games probing EOS may behave differently when the value exists-but-is-empty versus absent, so use removeValue(EOS_OVERLAY_REG_KEY, EOS_OVERLAY_REG_VALUE) instead.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@app/src/main/java/app/gamenative/service/epic/EpicOverlayManager.kt` around
lines 188 - 195, The removeRegistryPath function currently calls
WineRegistryEditor.setStringValue(EOS_OVERLAY_REG_KEY, EOS_OVERLAY_REG_VALUE,
"") which leaves an empty value instead of deleting it; change this to call
WineRegistryEditor.removeValue(EOS_OVERLAY_REG_KEY, EOS_OVERLAY_REG_VALUE)
inside the existing use { ... } block so the registry entry is actually removed
(keep the surrounding File lookup, use block, and the Timber.log message
intact).
phobos665
left a comment
There was a problem hiding this comment.
Looks sensible. I think it's be great to get in some tests regarding the launch args for the EpicGameLauncher
There was a problem hiding this comment.
3 issues found across 9 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/EpicOverlayDependency.kt">
<violation number="1" location="app/src/main/java/app/gamenative/utils/launchdependencies/EpicOverlayDependency.kt:30">
P3: Move this user-facing loading message into a string resource instead of hardcoding it in code.
(Based on your team's feedback about avoiding hardcoded UI strings.) [FEEDBACK_USED]</violation>
</file>
<file name="app/src/main/java/app/gamenative/service/epic/EpicDownloadManager.kt">
<violation number="1" location="app/src/main/java/app/gamenative/service/epic/EpicDownloadManager.kt:404">
P2: Avoid failing when all CDN URLs are Cloudflare; fall back to the full manifest list like other download paths so overlay downloads still work.</violation>
</file>
<file name="app/src/main/java/app/gamenative/service/epic/EpicManager.kt">
<violation number="1" location="app/src/main/java/app/gamenative/service/epic/EpicManager.kt:1002">
P1: Parse failure is cached as a permanent negative result. When `JSONObject(configStr)` throws, `deploymentId` becomes `null`, and `cacheFile.writeText("")` writes an empty cache. Subsequent calls hit the early-return that reads the empty file and returns `null` without retrying. For games like "Deliver at All Costs" that require `-epicdeploymentid`, a transient parse error permanently breaks launch until the user manually clears `epic/deployment_ids/`. Return early without caching when the failure is a parse exception.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| EpicService.isOverlayInstalled(container) | ||
|
|
||
| override fun getLoadingMessage(context: Context, container: Container, gameSource: GameSource, gameId: Int): String = | ||
| "Downloading EOS overlay" |
There was a problem hiding this comment.
P3: Move this user-facing loading message into a string resource instead of hardcoding it in code.
(Based on your team's feedback about avoiding hardcoded UI strings.)
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At app/src/main/java/app/gamenative/utils/launchdependencies/EpicOverlayDependency.kt, line 30:
<comment>Move this user-facing loading message into a string resource instead of hardcoding it in code.
(Based on your team's feedback about avoiding hardcoded UI strings.) </comment>
<file context>
@@ -0,0 +1,60 @@
+ EpicService.isOverlayInstalled(container)
+
+ override fun getLoadingMessage(context: Context, container: Container, gameSource: GameSource, gameId: Int): String =
+ "Downloading EOS overlay"
+
+ override suspend fun install(
</file context>
* feat(): Silly draft for ideation of EOS. * Added sidecar for EOS, made downloading EOS launch dependency, Deliver at all Costs working * addressed AI comments --------- Co-authored-by: Daniel Joyce <danielalexanderjoyce@gmail.com> Co-authored-by: Phobos665 <5970062+phobos665@users.noreply.github.com> Co-authored-by: Utkarsh Dalal <utkarsh.dalal@toptal.com>
Description
Got EOS overlay working
Recording
https://www.loom.com/share/7d04af23d3104510894b110f99dde820
Type of Change
Checklist
#code-changes, I have discussed this change there and it has been green-lighted. If I do not have access, I have still provided clear context in this PR. If I skip both, I accept that this change may face delays in review, may not be reviewed at all, or may be closed.CONTRIBUTING.md.Summary by CodeRabbit
Summary by cubic
Enables the Epic Online Services (EOS) overlay on Wine containers and passes the EOS deployment ID at launch to improve compatibility with modern Epic titles. Fixes launcher errors like “Failed to connect to the Epic Launcher” and enables in‑game EOS UI where supported.
EpicOverlayManagerto install the EOS overlay into the Wine prefix and set HKCU\SOFTWARE\Epic Games\EOS\OverlayPath.EpicDownloadManager.downloadOverlayto fetch overlay files via manifest chunks.EpicOverlayDependency) so Epic games auto-install it before first run.EpicManager.fetchDeploymentIdto read, cache (30 days), and pass-epicdeploymentidfrom the launcher sidecar.EpicGameTokenand pass it as-epicusernameduring launch.Written for commit 05ae38e. Summary will update on new commits.