Skip to content

Feat/eos overlay utkarsh#1286

Merged
utkarshdalal merged 6 commits intomasterfrom
feat/eos-overlay-utkarsh
Apr 24, 2026
Merged

Feat/eos overlay utkarsh#1286
utkarshdalal merged 6 commits intomasterfrom
feat/eos-overlay-utkarsh

Conversation

@utkarshdalal
Copy link
Copy Markdown
Owner

@utkarshdalal utkarshdalal commented Apr 24, 2026

Description

Got EOS overlay working

Recording

https://www.loom.com/share/7d04af23d3104510894b110f99dde820

Type of Change

  • Bug fix
  • Performance / stability improvement
  • Compatibility improvements
  • Other (requires prior approval)

Checklist

  • If I have access to #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.
  • This change aligns with the current project scope (core functionality, stability, or performance). If not, it has been explicitly approved beforehand.
  • I have attached a recording of the change.
  • I have read and agree to the contribution guidelines in CONTRIBUTING.md.

Summary by CodeRabbit

  • New Features
    • Epic EOS Overlay: install, remove, and status checks for Wine containers.
    • Overlay downloads now report progress during install.
    • Game launches use players' Epic display names when available.
    • Resolver for Epic EOS deployment IDs to improve launch parameters.
    • Overlay installation is evaluated before launch but won't block game start if it fails (logged instead).

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.

  • New Features
    • Add EpicOverlayManager to install the EOS overlay into the Wine prefix and set HKCU\SOFTWARE\Epic Games\EOS\OverlayPath.
    • Add EpicDownloadManager.downloadOverlay to fetch overlay files via manifest chunks.
    • Integrate overlay as a launch dependency (EpicOverlayDependency) so Epic games auto-install it before first run.
    • Add EpicManager.fetchDeploymentId to read, cache (30 days), and pass -epicdeploymentid from the launcher sidecar.
    • Include user display name in EpicGameToken and pass it as -epicusername during launch.

Written for commit 05ae38e. Summary will update on new commits.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 24, 2026

📝 Walkthrough

Walkthrough

Adds 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

Cohort / File(s) Summary
Token & Auth
app/src/main/java/app/gamenative/data/EpicGame.kt, app/src/main/java/app/gamenative/service/epic/EpicAuthManager.kt
Added displayName to EpicGameToken and propagate stored credential displayName into getGameLaunchToken() results.
Launch & Deployment
app/src/main/java/app/gamenative/service/epic/EpicGameLauncher.kt, app/src/main/java/app/gamenative/service/epic/EpicManager.kt
Derive -epicusername from token displayName (fallback "EpicUser"); add fetchDeploymentId() to query/cache EOS deploymentId from Epic assets endpoint.
Overlay Downloading
app/src/main/java/app/gamenative/service/epic/EpicDownloadManager.kt
New downloadOverlay(...) to parse overlay manifest, probe/filter CDN URLs, download chunks in concurrent batches, assemble files, report progress, and fail-fast with cache cleanup on errors.
Overlay Management
app/src/main/java/app/gamenative/service/epic/EpicOverlayManager.kt, app/src/main/java/app/gamenative/service/epic/EpicService.kt
New EpicOverlayManager service for install/remove/isInstalled (idempotent, writes Wine registry); EpicService injected with overlay manager and companion helpers to call install/remove/isInstalled.
Launch Dependencies
app/src/main/java/app/gamenative/utils/LaunchDependencies.kt, app/src/main/java/app/gamenative/utils/launchdependencies/EpicOverlayDependency.kt
Add EpicOverlayDependency to default launch dependencies; dependency checks EpicService.isOverlayInstalled() and attempts install (logs but does not block launch on failure).

Sequence Diagram

sequenceDiagram
    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>
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Epic Token Launcher #494: Overlaps changes to EpicGameToken, EpicAuthManager, and EpicGameLauncher (displayName propagation and launch flow).
  • 0.7.2 prerelease fixes #523: Related edits to EpicDownloadManager and EpicService affecting download/install lifecycle.

Suggested reviewers

  • phobos665

Poem

🐰 I hopped through manifests and CDN streams,

assembling overlays from tiny dreams,
a registry nibble, a deployment clue,
display names stitched — the launcher grew,
hoppity hop, install complete — woo! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is vague and uninformative—'Feat/eos overlay utkarsh' uses a branch name format rather than a clear, descriptive summary of the main change. Rename the title to concisely describe the feature, e.g., 'Add EOS overlay support for Epic Games' or 'Enable Epic Online Services overlay in Wine containers.'
✅ Passed checks (4 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 80.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description check ✅ Passed The pull request provides a clear, concise description with a recording, appropriate type categorization, and confirms all checklist requirements.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/eos-overlay-utkarsh

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

@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: 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

📥 Commits

Reviewing files that changed from the base of the PR and between 2620dc7 and 90cac8e.

📒 Files selected for processing (9)
  • app/src/main/java/app/gamenative/data/EpicGame.kt
  • app/src/main/java/app/gamenative/service/epic/EpicAuthManager.kt
  • app/src/main/java/app/gamenative/service/epic/EpicDownloadManager.kt
  • app/src/main/java/app/gamenative/service/epic/EpicGameLauncher.kt
  • app/src/main/java/app/gamenative/service/epic/EpicManager.kt
  • app/src/main/java/app/gamenative/service/epic/EpicOverlayManager.kt
  • app/src/main/java/app/gamenative/service/epic/EpicService.kt
  • app/src/main/java/app/gamenative/utils/LaunchDependencies.kt
  • app/src/main/java/app/gamenative/utils/launchdependencies/EpicOverlayDependency.kt

Comment thread app/src/main/java/app/gamenative/service/epic/EpicDownloadManager.kt Outdated
Comment thread app/src/main/java/app/gamenative/service/epic/EpicManager.kt
Comment on lines +62 to +80
/**
* 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)
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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).

Comment on lines +188 to +195
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")
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

🧩 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).

Copy link
Copy Markdown
Contributor

@phobos665 phobos665 left a comment

Choose a reason for hiding this comment

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

Looks sensible. I think it's be great to get in some tests regarding the launch args for the EpicGameLauncher

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.

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.

Comment thread app/src/main/java/app/gamenative/service/epic/EpicManager.kt
Comment thread app/src/main/java/app/gamenative/service/epic/EpicDownloadManager.kt Outdated
EpicService.isOverlayInstalled(container)

override fun getLoadingMessage(context: Context, container: Container, gameSource: GameSource, gameId: Int): String =
"Downloading EOS overlay"
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot Apr 24, 2026

Choose a reason for hiding this comment

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

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.)

View Feedback

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>
Fix with Cubic

@utkarshdalal utkarshdalal merged commit cbea7f7 into master Apr 24, 2026
3 checks passed
@utkarshdalal utkarshdalal deleted the feat/eos-overlay-utkarsh branch April 24, 2026 12:25
ben-pearson pushed a commit to ben-pearson/GameNative that referenced this pull request Apr 27, 2026
* 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>
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