Skip to content

Fix Darkest Dungeon Workshop mod handling#1500

Merged
utkarshdalal merged 2 commits into
utkarshdalal:masterfrom
Nightwalker743:fix/darkest-dungeon-workshop-mods
Jun 2, 2026
Merged

Fix Darkest Dungeon Workshop mod handling#1500
utkarshdalal merged 2 commits into
utkarshdalal:masterfrom
Nightwalker743:fix/darkest-dungeon-workshop-mods

Conversation

@Nightwalker743

@Nightwalker743 Nightwalker743 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Description

This is a requested bug fix from here: https://discord.com/channels/1378308569287622737/1506727093122240632

Fixes Darkest Dungeon workshop mods only partially appearing in-game on GameNative.

The game was being treated as a filesystem-managed mod title because it has a high-confidence mods/ directory, which caused Workshop items to be symlinked instead of exposed through the standard ISteamUGC/gbe_fork Workshop metadata path. This made only a subset of subscribed Workshop mods appear in-game.

This change forces Darkest Dungeon (262060) through the standard Workshop path, prevents generated steam_settings and Workshop symlink trees from being rediscovered as game Steam API locations, excludes generated metadata from Workshop payload sizing, and invalidates stale completed downloads whose usable payload is missing or implausibly incomplete.

Recording

I downloaded a mod that removes the circus icon in the bottom left corner just to show off mods working easily. Image here is before the mod, circus icon in the bottom left:
Screenshot_20260601_222235_GameNative

Video showing the mod working (and other mods enabled and no crashing):
https://github.com/user-attachments/assets/59101681-004d-4731-a68d-44a2023f24d2

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 cubic

Fixes Darkest Dungeon (app 262060) Workshop mods only partially appearing by routing the game through standard Steam Workshop handling and tightening validation/cleanup. All subscribed Workshop mods now load correctly and broken or partial downloads are auto-repaired.

  • Bug Fixes
    • Force 262060 to use the metadata-driven Workshop path via WorkshopOverrideIds (instead of filesystem mod detection).
    • Validate Workshop payload using only real mod files; if empty or implausibly small vs Steam metadata, delete item/partial dirs and re-sync.
    • Exclude steam_settings, hidden files, and preview images from payload scans; compute primary_filename, primary_filesize, and total_files_sizes from filtered files only.
    • Skip DLL discovery in steam_settings, anywhere under /workshop/content/, and symlinks pointing into it to avoid false Steam API locations.
    • Remove generated steam_settings from Workshop content and stale ones in the game tree; keep only dirs next to real Steam DLLs and write the correct steam_appid.txt beside each.

Written for commit 2db27e7. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved detection and automatic re-downloading of corrupted or incomplete workshop content.
    • Safer, symlink-aware removal of invalid or partial mod directories to avoid accidental data loss.
    • Enhanced cleanup of generated Steam settings to prevent stale configuration buildup.
    • More accurate mod metadata and total size calculations by enumerating actual payload files and excluding generated metadata and previews.
  • Chores

    • Added explicit override list for specific games to ensure consistent mod metadata path selection.

@coderabbitai

coderabbitai Bot commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

WorkshopManager adds workshop payload validation, multi-stage steam_settings cleanup, payload-aware metadata generation, and enhanced DLL discovery with path normalization and symlink safety. Items with corrupted/suspicious payloads are now deleted and forced to re-download; mods metadata derives file selection from actual payload files; symlink configuration includes app ID override extension and stale directory cleanup.

Changes

Payload Validation and Metadata Refinement

Layer / File(s) Summary
Imports, constants, and filesystem helper utilities
app/src/main/java/app/gamenative/workshop/WorkshopManager.kt
Adds imports for path handling (LinkOption, Path, Comparator); defines payload validation thresholds (minimum size, suspicious ratio); implements path normalization, payload enumeration, safe non-symlink-following deletion, steam_settings cleanup routines, and DLL discovery path filtering.
Workshop payload validation and sync enforcement
app/src/main/java/app/gamenative/workshop/WorkshopManager.kt
getItemsNeedingSync cleans up generated steam_settings before sync checks, validates payloads via file structure/size analysis, and automatically deletes items with corrupted/suspicious payloads to force re-download.
Mods metadata generation from payload detection
app/src/main/java/app/gamenative/workshop/WorkshopManager.kt
buildModsJson now selects primary filename and computes total file sizes from detected workshop payload files (excluding generated metadata paths) instead of first non-hidden file and naive recursive sum.

Symlink Configuration and DLL Discovery Refinement

Layer / File(s) Summary
Symlink configuration and app ID extension
app/src/main/java/app/gamenative/workshop/WorkshopManager.kt, app/src/main/java/app/gamenative/workshop/WorkshopOverrideIds.kt
configureModSymlinks cleans up generated steam_settings in workshop content; centralized WorkshopOverrideIds.forceStandardAppIds is added and used (includes appId 262060).
DLL discovery with path normalization and safety filtering
app/src/main/java/app/gamenative/workshop/WorkshopManager.kt
Per-DLL steam_settings discovery now computes normalized workshop content paths and realpath comparisons, prunes traversal via shouldSkipDllDiscoveryPath, collects valid steam_settings directories, and constrains stale generated settings cleanup to those valid paths.

Sequence Diagram

sequenceDiagram
  participant SyncFlow as getItemsNeedingSync
  participant Validate as PayloadValidator
  participant Cleanup as CleanupHelpers
  participant Metadata as buildModsJson
  participant Symlink as configureModSymlinks
  participant Discovery as DLLDiscovery

  SyncFlow->>Cleanup: cleanupGeneratedSteamSettingsInWorkshopContent
  SyncFlow->>Validate: invalidWorkshopPayloadReason (per item)
  alt Invalid Payload
    Validate->>Cleanup: deleteDirectoryTreeNoFollow (invalidate)
    Cleanup-->>SyncFlow: Item marked for re-download
  else Valid Payload
    Validate-->>SyncFlow: Item ready for sync
  end

  Metadata->>Validate: getPayloadFiles (detect primary file & sizes)
  Validate-->>Metadata: primary filename + total size

  Symlink->>Cleanup: cleanupGeneratedSteamSettingsInWorkshopContent
  Symlink->>Discovery: Walk gameRootDir with path filtering
  Discovery->>Discovery: shouldSkipDllDiscoveryPath (prune workshop/metadata)
  Discovery->>Cleanup: cleanupStaleGeneratedGameSteamSettings(validPaths)
  Cleanup-->>Symlink: Stale directories removed
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • utkarshdalal

Poem

🐰 I hop through workshop folders bright,
Clearing steam_settings out of sight,
I count payloads, sizes true,
Toss bad mods to start anew,
Symlinks set — the mods dance right.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Fix Darkest Dungeon Workshop mod handling' directly corresponds to the main change: addressing a bug where Darkest Dungeon workshop mods only partially appeared in-game.
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 description is comprehensive and complete, covering all required template sections including detailed description of the bug fix, recording/images of the fix in action, Type of Change selection, and a fully checked Checklist.

✏️ 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.

@Nightwalker743
Nightwalker743 force-pushed the fix/darkest-dungeon-workshop-mods branch 2 times, most recently from 0487b59 to 47e25a2 Compare June 1, 2026 03:51
@Nightwalker743
Nightwalker743 marked this pull request as ready for review June 1, 2026 03:57

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@app/src/main/java/app/gamenative/workshop/WorkshopManager.kt`:
- Around line 1513-1515: The current payload detection in isWorkshopPayloadFile
(which uses pathKey(file) and hasSteamSettingsSegment) incorrectly treats
preview.* files as mod payload; update isWorkshopPayloadFile to also exclude
files whose names match the generated preview pattern (e.g., names starting with
"preview." or matching "preview.*") so preview images are not counted as
payload, and then ensure any logic that sets primary_filename (the code that
derives primary_filename from the recursive payload set) computes it from the
filtered payload list returned by isWorkshopPayloadFile rather than from the
unfiltered file list so primary_filename can never point to a preview artifact.
🪄 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: a7d89f16-1a03-4da4-bff6-b5ec4f11776b

📥 Commits

Reviewing files that changed from the base of the PR and between 2bea2aa and 47e25a2.

📒 Files selected for processing (1)
  • app/src/main/java/app/gamenative/workshop/WorkshopManager.kt

Comment thread app/src/main/java/app/gamenative/workshop/WorkshopManager.kt

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

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.

1 issue found across 1 file

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread app/src/main/java/app/gamenative/workshop/WorkshopManager.kt Outdated
@Nightwalker743
Nightwalker743 force-pushed the fix/darkest-dungeon-workshop-mods branch from 47e25a2 to e06b895 Compare June 1, 2026 04:17
Comment on lines +1498 to +1502
private fun pathKey(path: Path): String =
path.normalize().toAbsolutePath().toString()
.replace('\\', '/')
.trimEnd('/')
.lowercase()

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.

Might be worth pulling or re-using some of these from fileUtils.kt

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good thought but I checked FileUtils and didn’t see a clean direct reuse. These helpers are pretty specific to the Workshop cleanup path, especially the steam_settings/workshop-content filtering and no-follow delete behavior.

The only reusable bits would maybe be path normalization / same-child checks, but since they’re only used here right now I kept them local for now.

What are your thoughts?

// _metadata format that doesn't work when symlinked into mods/.
// Force ISteamUGC path regardless of detection.
val forceStandardAppIds = setOf(211820,1468810) // Starbound,TaleofImmortal
val forceStandardAppIds = setOf(211820, 1468810, 262060) // Starbound, Tale of Immortal, Darkest Dungeon

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.

I think it's worth us pulling these out into a different file like a workshopOverrideIds.kt where we can more easily track this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Good idea, latest commit adds a WorkshopOverrideIds.kt as requested :)

@phobos665

Copy link
Copy Markdown
Contributor

A few suggestions, otherwise looks pretty good.

Have you also tested that the changes are backwards compatible with other titles?

Would also be good for a quick recording of the change that allows DD to work

@Nightwalker743
Nightwalker743 force-pushed the fix/darkest-dungeon-workshop-mods branch from e06b895 to 1e38ab6 Compare June 2, 2026 00:58

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/workshop/WorkshopOverrideIds.kt (1)

4-7: 💤 Low value

Consider enhancing the documentation with rationale.

The KDoc explains what the override does but could benefit from explaining why these specific games need this treatment. Consider adding a note about the common characteristic they share (e.g., "Games with a mods/ directory that shouldn't trigger filesystem-based mod detection").

This would help future maintainers understand when to add new App IDs to this set.

📝 Suggested documentation enhancement
 /**
  * Games that should use the standard ISteamUGC/mods.json path even when
  * mod directory detection finds a high-confidence filesystem mod folder.
+ *
+ * These games have a mods/ directory in their installation but rely on
+ * Workshop metadata rather than direct filesystem access for mod loading.
  */
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@app/src/main/java/app/gamenative/workshop/WorkshopOverrideIds.kt` around
lines 4 - 7, The KDoc on WorkshopOverrideIds should be expanded to include the
rationale for the override: explain that the listed App IDs (in
WorkshopOverrideIds) are games that have a mods/ (or similar) directory which
would otherwise trigger high-confidence filesystem-based mod detection, but
should instead use the standard ISteamUGC/mods.json path; add a short note
describing this common characteristic and guidance for when to add new App IDs
(e.g., "add when a game has a mods/ folder that should not be treated as a
filesystem mod folder").
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@app/src/main/java/app/gamenative/workshop/WorkshopOverrideIds.kt`:
- Around line 4-7: The KDoc on WorkshopOverrideIds should be expanded to include
the rationale for the override: explain that the listed App IDs (in
WorkshopOverrideIds) are games that have a mods/ (or similar) directory which
would otherwise trigger high-confidence filesystem-based mod detection, but
should instead use the standard ISteamUGC/mods.json path; add a short note
describing this common characteristic and guidance for when to add new App IDs
(e.g., "add when a game has a mods/ folder that should not be treated as a
filesystem mod folder").

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 93865dad-199a-4aa3-8f69-72ede32cfaa0

📥 Commits

Reviewing files that changed from the base of the PR and between e06b895 and 1e38ab6.

📒 Files selected for processing (2)
  • app/src/main/java/app/gamenative/workshop/WorkshopManager.kt
  • app/src/main/java/app/gamenative/workshop/WorkshopOverrideIds.kt
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/app/gamenative/workshop/WorkshopManager.kt

@Nightwalker743

Nightwalker743 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

A few suggestions, otherwise looks pretty good.

Have you also tested that the changes are backwards compatible with other titles?

Would also be good for a quick recording of the change that allows DD to work

I've tested a handful of games already and they still work. There shouldn't be any regressions since if another game had broken generated steam_settings it likely already didn't work anyway. Although I wouldn't be completely surprised if there's some weird edge case where a game could have been “working” despite stale generated files, and this PR may now clean them.

Video added as well.

Added comments to clarify the purpose of forceStandardAppIds.
@utkarshdalal
utkarshdalal merged commit 077a113 into utkarshdalal:master Jun 2, 2026
3 checks passed
@utkarshdalal

Copy link
Copy Markdown
Owner

Thank you, merged. Can you get the nightly to the person who brought it up @Nightwalker743? I'm flying for the next 24 hours

@Nightwalker743

Copy link
Copy Markdown
Contributor Author

@utkarshdalal I've sent the nightly over to them! Thanks, have a safe flight!

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.

3 participants