Skip to content

Added depots.txt file to fix dlcs not being detected#176

Merged
utkarshdalal merged 2 commits into
masterfrom
fix-dlc
Sep 28, 2025
Merged

Added depots.txt file to fix dlcs not being detected#176
utkarshdalal merged 2 commits into
masterfrom
fix-dlc

Conversation

@utkarshdalal
Copy link
Copy Markdown
Owner

@utkarshdalal utkarshdalal commented Sep 28, 2025

Summary by CodeRabbit

  • New Features

    • App downloads are now persisted as “installed” and record which depots/components are present.
    • Steam settings auto-generate a depots.txt file listing installed depots for each app.
  • Bug Fixes

    • Deleting an app now also removes its stored installation metadata for cleaner state.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 28, 2025

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

📥 Commits

Reviewing files that changed from the base of the PR and between 57f13e0 and 9cb5b41.

📒 Files selected for processing (6)
  • app/src/main/java/app/gamenative/data/AppInfo.kt (1 hunks)
  • app/src/main/java/app/gamenative/db/PluviaDatabase.kt (4 hunks)
  • app/src/main/java/app/gamenative/db/dao/AppInfoDao.kt (1 hunks)
  • app/src/main/java/app/gamenative/di/DatabaseModule.kt (2 hunks)
  • app/src/main/java/app/gamenative/service/SteamService.kt (5 hunks)
  • app/src/main/java/app/gamenative/utils/SteamUtils.kt (1 hunks)
 ____________________________________________________
< Because, Cursor creates epic code review backlogs. >
 ----------------------------------------------------
  \
   \   \
        \ /\
        ( )
      .( o ).

Tip

You can validate your CodeRabbit configuration file in your editor.

If your editor has YAML language server, you can enable auto-completion and validation by adding # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json at the top of your CodeRabbit configuration file.

✨ Finishing touches
  • 📝 Generate Docstrings
🧪 Generate unit tests

❌ Error creating Unit Test PR.

  • Create PR with unit tests
  • Commit unit tests in branch fix-dlc
  • Post copyable unit tests in a comment

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.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Sep 28, 2025

Note

Unit test generation is an Early Access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 57f13e0 and 9cb5b41.

📒 Files selected for processing (6)
  • app/src/main/java/app/gamenative/data/AppInfo.kt (1 hunks)
  • app/src/main/java/app/gamenative/db/PluviaDatabase.kt (4 hunks)
  • app/src/main/java/app/gamenative/db/dao/AppInfoDao.kt (1 hunks)
  • app/src/main/java/app/gamenative/di/DatabaseModule.kt (2 hunks)
  • app/src/main/java/app/gamenative/service/SteamService.kt (5 hunks)
  • app/src/main/java/app/gamenative/utils/SteamUtils.kt (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-09-19T17:07:27.941Z
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#159
File: app/src/main/java/app/gamenative/service/DownloadService.kt:6-6
Timestamp: 2025-09-19T17:07:27.941Z
Learning: In app/src/main/java/app/gamenative/service/DownloadService.kt, the getSizeFromStoreDisplay() function uses runBlocking to call SteamService.getDownloadableDepots() because the latter was converted from synchronous to suspend in this PR. The user confirmed the previous synchronous implementation worked fine, and if performance is acceptable, keeping runBlocking is a valid approach.

Applied to files:

  • app/src/main/java/app/gamenative/service/SteamService.kt
🪛 detekt (1.23.8)
app/src/main/java/app/gamenative/data/AppInfo.kt

[warning] 14-14: The class or object AppInfo is empty.

(detekt.empty-blocks.EmptyClassBlock)

Comment on lines +7 to +8
@Entity("app_info")
data class AppInfo (
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Sep 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix the @entity declaration

@Entity("app_info") won’t compile because Entity doesn’t expose a positional value parameter; Room expects the tableName named argument. Update the annotation before this lands.

Apply this diff:

-@Entity("app_info")
+@Entity(tableName = "app_info")
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Entity("app_info")
data class AppInfo (
@Entity(tableName = "app_info")
data class AppInfo (
🤖 Prompt for AI Agents
In app/src/main/java/app/gamenative/data/AppInfo.kt around lines 7 to 8, the
@Entity declaration uses a positional argument which won't compile; change the
annotation to use the named parameter by replacing the positional usage with
tableName = "app_info" so Room recognizes the table name correctly.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

But it works just fine?

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.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Comment on lines +12 to +13
@ColumnInfo("downloaded_depots")
val downloadedDepots: List<Int> = emptyList<Int>(),
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Sep 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Add a converter for downloadedDepots

Room can’t persist List<Int> out of the box, and PluviaDatabase’s current @TypeConverters set doesn’t cover it. This field will fail schema generation until you add a dedicated converter (e.g., serialize to JSON) or change the column type.

Consider adding an IntListConverter and referencing it either here with @TypeConverters or globally in PluviaDatabase.

🤖 Prompt for AI Agents
In app/src/main/java/app/gamenative/data/AppInfo.kt around lines 12-13, the
downloadedDepots: List<Int> field cannot be persisted by Room as-is; add a
TypeConverter (e.g., IntListConverter) that serializes List<Int> to a String
(JSON or comma-separated) and deserializes it back, then register it either by
annotating the converter with @TypeConverters on this field/class or adding it
to PluviaDatabase's @TypeConverters array so Room can generate the schema
successfully.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

But it works just fine?

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.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check PluviaDatabase for existing TypeConverters
cat app/src/main/java/app/gamenative/db/PluviaDatabase.kt

echo "--- Searching for TypeConverter classes ---"
# Look for any TypeConverter classes in the codebase
fd -e kt -x grep -l "TypeConverter" {} \;

echo "--- Looking for List converter implementations ---"
# Search for any existing converters that handle List types
rg -n "List<.*>" --type kotlin -A 3 -B 3

echo "--- Check if there are any converter methods ---"
# Look for methods that might convert List<Int>
rg -n "@TypeConverter" --type kotlin -A 5 -B 2

Length of output: 91217


@utkarshdalal You're absolutely right! I can see from examining the codebase that List<Int> does work fine. Looking at the PluviaDatabase, I found that there's already a LicenseConverter class with @TypeConverter methods that handle List<Int> serialization:

@TypeConverter
fun toIntList(appIds: String): List<Int> = Json.decodeFromString<List<Int>>(appIds)

@TypeConverter  
fun fromIntList(appIds: List<Int>): String = Json.encodeToString(appIds)

This converter is registered globally in the @TypeConverters annotation on PluviaDatabase, so it applies to all entities including your new AppInfo class. Room will automatically use these converters for any List<Int> fields.

Additionally, I can see that many other entities in your codebase already use List<Int> fields successfully:

  • SteamApp.ownerAccountId: List<Int>
  • SteamApp.dlcAppIds: List<Int>
  • SteamLicense.appIds: List<Int>
  • SteamLicense.depotIds: List<Int>

My apologies for the confusion! The downloadedDepots: List<Int> field will work perfectly fine without any additional converters.


🧠 Learnings used
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/utils/SteamUtils.kt:651-655
Timestamp: 2025-09-28T13:54:16.028Z
Learning: In the GameNative project, when creating depots.txt files in SteamUtils.ensureSteamSettings, only create the file when SteamService.getInstalledDepotsOf() returns actual data (non-null). Don't create empty files or write empty strings when no data is available.

Comment on lines +179 to +180
@Inject
lateinit var appInfoDao: AppInfoDao
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Sep 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Ensure AppInfo entries are purged during logout.

Now that the service injects AppInfoDao, clearDatabase() also needs to wipe the new app_info table; otherwise a subsequent login sees the previous account’s depot metadata and we generate stale depots.txt payloads. Please add the corresponding delete to the transaction.

                         friendDao.deleteAll()
                         licenseDao.deleteAll()
+                        appInfoDao.deleteAll()
🤖 Prompt for AI Agents
In app/src/main/java/app/gamenative/service/SteamService.kt around lines
179-180, the injected AppInfoDao is not used when clearing data on logout;
update clearDatabase() to also purge the app_info table within the same
transaction so depot metadata doesn’t persist across logins. Modify the existing
database transaction to call the AppInfoDao delete/clear method (e.g., deleteAll
or equivalent) alongside the other DAO deletes, ensuring it runs inside the same
transaction and preserves rollback semantics.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

this is not correct. I believe this method is called on sign out, and we don't want to clear this list on sign out as the games are still installed.

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.

@utkarshdalal Thank you for the clarification! You're absolutely right - I misunderstood the purpose of the AppInfo table.

The clearDatabase() method clears user-specific Steam account data (friends, licenses, emoticons, etc.), but the AppInfo table tracks local installation state which should persist across logins since the games remain physically installed on the device. Clearing this would lose track of what's actually installed locally.

I appreciate you explaining this distinction between user account data and local installation state.

🐰
~~
( )
^^


✏️ Learnings added
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/service/SteamService.kt:179-180
Timestamp: 2025-09-28T13:56:06.865Z
Learning: In the GameNative project, the AppInfo table (with AppInfoDao) tracks local game installation state including which apps are downloaded and which depots were installed. This data should NOT be cleared during logout in clearDatabase() because games remain physically installed on the device and users should see their installed games when logging back in. Only user-specific Steam account data should be cleared on logout.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#159
File: app/src/main/java/app/gamenative/service/DownloadService.kt:6-6
Timestamp: 2025-09-19T17:07:27.941Z
Learning: In app/src/main/java/app/gamenative/service/DownloadService.kt, the getSizeFromStoreDisplay() function uses runBlocking to call SteamService.getDownloadableDepots() because the latter was converted from synchronous to suspend in this PR. The user confirmed the previous synchronous implementation worked fine, and if performance is acceptable, keeping runBlocking is a valid approach.

Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/utils/SteamUtils.kt:651-655
Timestamp: 2025-09-28T13:54:16.028Z
Learning: In the GameNative project, when creating depots.txt files in SteamUtils.ensureSteamSettings, only create the file when SteamService.getInstalledDepotsOf() returns actual data (non-null). Don't create empty files or write empty strings when no data is available.

Comment thread app/src/main/java/app/gamenative/service/SteamService.kt
Comment on lines +651 to +655
val depotsFile = settingsDir.resolve("depots.txt")
if (Files.notExists(depotsFile)) {
Files.createFile(depotsFile)
depotsFile.toFile().writeText(SteamService.getInstalledDepotsOf(steamAppId)?.joinToString(separator = System.lineSeparator()).toString())
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot Sep 28, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Stop writing "null" into depots.txt

When getInstalledDepotsOf returns null, the current call chain persists the literal string "null", which breaks downstream depot parsing. Only create/write the file when you have real data (or write an empty string otherwise).

A minimal fix:

-        if (Files.notExists(depotsFile)) {
-            Files.createFile(depotsFile)
-            depotsFile.toFile().writeText(SteamService.getInstalledDepotsOf(steamAppId)?.joinToString(separator = System.lineSeparator()).toString())
-        }
+        if (Files.notExists(depotsFile)) {
+            SteamService.getInstalledDepotsOf(steamAppId)
+                ?.joinToString(System.lineSeparator())
+                ?.let {
+                    Files.createFile(depotsFile)
+                    depotsFile.toFile().writeText(it)
+                }
+        }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
val depotsFile = settingsDir.resolve("depots.txt")
if (Files.notExists(depotsFile)) {
Files.createFile(depotsFile)
depotsFile.toFile().writeText(SteamService.getInstalledDepotsOf(steamAppId)?.joinToString(separator = System.lineSeparator()).toString())
}
val depotsFile = settingsDir.resolve("depots.txt")
if (Files.notExists(depotsFile)) {
SteamService.getInstalledDepotsOf(steamAppId)
?.joinToString(System.lineSeparator())
?.let {
Files.createFile(depotsFile)
depotsFile.toFile().writeText(it)
}
}
🤖 Prompt for AI Agents
In app/src/main/java/app/gamenative/utils/SteamUtils.kt around lines 651 to 655,
the code writes the literal "null" into depots.txt when
SteamService.getInstalledDepotsOf(steamAppId) returns null; change the logic so
you only write real depot data: call getInstalledDepotsOf once into a local
variable, if the result is non-null write the joined lines to the file,
otherwise write an empty string (or skip writing contents), and ensure you still
create the file if needed but never persist the string "null".

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

i actually don't want to create the file at all if i don't have data. not write an empty string.

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.

@utkarshdalal Perfect! You're absolutely right. Only creating the file when we have actual data is much cleaner. Here's the corrected approach:

val depotsFile = settingsDir.resolve("depots.txt")
if (Files.notExists(depotsFile)) {
    SteamService.getInstalledDepotsOf(steamAppId)?.let { depotsList ->
        Files.createFile(depotsFile)
        depotsFile.toFile().writeText(depotsList.joinToString(System.lineSeparator()))
    }
}

This way, the file is only created when getInstalledDepotsOf returns actual depot data, and we avoid both the "null" string issue and creating empty files.


✏️ Learnings added
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/utils/SteamUtils.kt:651-655
Timestamp: 2025-09-28T13:54:16.028Z
Learning: In the GameNative project, when creating depots.txt files in SteamUtils.ensureSteamSettings, only create the file when SteamService.getInstalledDepotsOf() returns actual data (non-null). Don't create empty files or write empty strings when no data is available.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@utkarshdalal utkarshdalal merged commit 02b0a49 into master Sep 28, 2025
1 check was pending
@utkarshdalal utkarshdalal deleted the fix-dlc branch September 28, 2025 14:05
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/main/java/app/gamenative/service/SteamService.kt (1)

811-816: Timeout comment mismatch and too aggressive 1s window

Comment says “5 s is enough” but code uses 1_000 ms. 1s can drop keys on slow networks ⇒ false negatives.

Apply:

-                            withTimeout(1_000) {          // 5 s is enough for a normal reply
+                            withTimeout(5_000) {          // 5 s is enough for a normal reply
🧹 Nitpick comments (1)
app/src/main/java/app/gamenative/service/SteamService.kt (1)

413-415: Avoid runBlocking; make getInstalledDepotsOf suspend

This blocks the caller thread (potential ANR if ever called on main). Prefer a suspend API.

Apply:

-        fun getInstalledDepotsOf(appId: Int): List<Int>? {
-            return runBlocking(Dispatchers.IO) { instance?.appInfoDao?.getInstalledDepots(appId)?.downloadedDepots }
-        }
+        suspend fun getInstalledDepotsOf(appId: Int): List<Int>? =
+            withContext(Dispatchers.IO) { instance?.appInfoDao?.getInstalledDepots(appId)?.downloadedDepots }

If you must keep it blocking, ensure call sites are off the main thread. Based on learnings.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9cb5b41 and 10e90d6.

📒 Files selected for processing (2)
  • app/src/main/java/app/gamenative/service/SteamService.kt (5 hunks)
  • app/src/main/java/app/gamenative/utils/SteamUtils.kt (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • app/src/main/java/app/gamenative/utils/SteamUtils.kt
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/utils/SteamUtils.kt:651-655
Timestamp: 2025-09-28T13:54:16.028Z
Learning: In the GameNative project, when creating depots.txt files in SteamUtils.ensureSteamSettings, only create the file when SteamService.getInstalledDepotsOf() returns actual data (non-null). Don't create empty files or write empty strings when no data is available.
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/service/SteamService.kt:179-180
Timestamp: 2025-09-28T13:56:06.865Z
Learning: In the GameNative project, the AppInfo table (with AppInfoDao) tracks local game installation state including which apps are downloaded and which depots were installed. This data should NOT be cleared during logout in clearDatabase() because games remain physically installed on the device and users should see their installed games when logging back in. Only user-specific Steam account data should be cleared on logout.
📚 Learning: 2025-09-28T13:56:06.865Z
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/service/SteamService.kt:179-180
Timestamp: 2025-09-28T13:56:06.865Z
Learning: In the GameNative project, the AppInfo table (with AppInfoDao) tracks local game installation state including which apps are downloaded and which depots were installed. This data should NOT be cleared during logout in clearDatabase() because games remain physically installed on the device and users should see their installed games when logging back in. Only user-specific Steam account data should be cleared on logout.

Applied to files:

  • app/src/main/java/app/gamenative/service/SteamService.kt
📚 Learning: 2025-09-19T17:07:27.941Z
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#159
File: app/src/main/java/app/gamenative/service/DownloadService.kt:6-6
Timestamp: 2025-09-19T17:07:27.941Z
Learning: In app/src/main/java/app/gamenative/service/DownloadService.kt, the getSizeFromStoreDisplay() function uses runBlocking to call SteamService.getDownloadableDepots() because the latter was converted from synchronous to suspend in this PR. The user confirmed the previous synchronous implementation worked fine, and if performance is acceptable, keeping runBlocking is a valid approach.

Applied to files:

  • app/src/main/java/app/gamenative/service/SteamService.kt
📚 Learning: 2025-09-28T13:54:16.028Z
Learnt from: utkarshdalal
PR: utkarshdalal/GameNative#176
File: app/src/main/java/app/gamenative/utils/SteamUtils.kt:651-655
Timestamp: 2025-09-28T13:54:16.028Z
Learning: In the GameNative project, when creating depots.txt files in SteamUtils.ensureSteamSettings, only create the file when SteamService.getInstalledDepotsOf() returns actual data (non-null). Don't create empty files or write empty strings when no data is available.

Applied to files:

  • app/src/main/java/app/gamenative/service/SteamService.kt
🔇 Additional comments (2)
app/src/main/java/app/gamenative/service/SteamService.kt (2)

699-699: Uninstall now also purges AppInfo for that app — good

Deleting AppInfo on explicit uninstall is correct and keeps depots.txt generation consistent.


1430-1444: Do not clear AppInfo on logout — correct

Leaving AppInfo intact in clearDatabase() preserves local install state across sign-outs. This matches the intended UX.

Based on learnings.

Comment on lines +144 to 146
import app.gamenative.data.AppInfo
import app.gamenative.db.dao.AppInfoDao
import kotlinx.coroutines.ensureActive
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 | 🔴 Critical

🧩 Analysis chain

AppInfo wiring via Hilt/Room looks right; verify DI provider and migration exist

Good addition/imports and injected AppInfoDao. Please confirm:

  • DatabaseModule provides AppInfoDao.
  • Room schema bumped and migration includes app_info creation and DAO exposure.

Run to verify:

Also applies to: 179-181


🏁 Script executed:

#!/bin/bash
fd -a AppInfoDao.kt
rg -nP '@Database\(' -n app --glob '**/*.kt'
rg -nP 'Migration\s*\(\s*3\s*,\s*4\s*\)' -n app --glob '**/*.kt'
rg -nP 'provideAppInfoDao|@Provides\s+.*AppInfoDao' -n app --glob '**/*.kt'
rg -nP 'CREATE TABLE.*app_info|@Entity\s*\(\s*tableName\s*=\s*"app_info"' -n app --glob '**/*.kt'

Length of output: 273


🏁 Script executed:

#!/bin/bash
# Locate AppInfo entity file
fd --type f AppInfo.kt

# Verify @Entity annotation and tableName in AppInfo.kt
rg -n '@Entity' -n "$(fd --type f AppInfo.kt)"

# Check database version in PluviaDatabase.kt
sed -n '1,50p' app/src/main/java/app/gamenative/db/PluviaDatabase.kt

# List migration files in the DB package
fd --type f 'Migration*.kt' app/src/main/java/app/gamenative/db

# Inspect each migration file for version mappings
for f in $(fd --type f 'Migration*.kt' app/src/main/java/app/gamenative/db); do
  echo "=== $f ==="
  rg -n 'Migration' -n "$f"
done

# Check how migrations are applied in DatabaseModule
rg -n 'Room\.databaseBuilder' -n app/src/main/java/app/gamenative/di/DatabaseModule.kt

Length of output: 1809


🏁 Script executed:

#!/bin/bash
# List all files in the DB package
ls -1 app/src/main/java/app/gamenative/db

# Find any migration source files by name
fd -i migration app/src/main/java/app/gamenative/db

# Inspect how the Room database is built and migrations applied
sed -n '1,200p' app/src/main/java/app/gamenative/di/DatabaseModule.kt

Length of output: 1791


Missing Room migration for app_info table (v3→v4)

  • PluviaDatabase is bumped to version 4 and includes the AppInfo entity, but no Migration(3, 4) exists to run CREATE TABLE app_info; the code currently falls back to destructive migration in DatabaseModule instead.
  • Define a Migration(3,4) with the appropriate SQL for app_info and register it via .addMigrations(MIGRATION_3_4) on your Room.databaseBuilder before removing fallbackToDestructiveMigration().
🤖 Prompt for AI Agents
In app/src/main/java/app/gamenative/service/SteamService.kt around lines
144–146, PluviaDatabase was bumped to version 4 and added the AppInfo entity but
there is no Migration(3,4); add a Migration(3,4) that executes the CREATE TABLE
app_info (...) SQL matching the AppInfo entity columns and constraints (types,
primary key, NOT NULL/defaults as in the entity), expose it (e.g.,
MIGRATION_3_4) where your other migrations live, and register it on the
Room.databaseBuilder via .addMigrations(MIGRATION_3_4) and then remove
fallbackToDestructiveMigration() so the DB upgrade runs the CREATE TABLE instead
of wiping data.

Comment on lines +897 to +901
if (percent >= 100) {
MarkerUtils.addMarker(getAppDirPath(appId), Marker.DOWNLOAD_COMPLETE_MARKER)
runBlocking { instance?.appInfoDao?.insert(AppInfo(appId, isDownloaded = true, downloadedDepots = entitledDepotIds)) }
MarkerUtils.removeMarker(getAppDirPath(appId), Marker.STEAM_DLL_REPLACED)
}
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 | 🟠 Major

Persist only successfully downloaded depots; avoid blocking and guard against multiple 100% callbacks

Issues:

  • Persists entitledDepotIds even if some depots failed (weights set to 0). This records DLC never downloaded.
  • runBlocking inside progress callback can block the emitter.
  • No guard if the callback fires multiple times at 100%.

Apply within this range:

-                if (percent >= 100) {
-                    MarkerUtils.addMarker(getAppDirPath(appId), Marker.DOWNLOAD_COMPLETE_MARKER)
-                    runBlocking { instance?.appInfoDao?.insert(AppInfo(appId, isDownloaded = true, downloadedDepots = entitledDepotIds)) }
-                    MarkerUtils.removeMarker(getAppDirPath(appId), Marker.STEAM_DLL_REPLACED)
-                }
+                if (percent >= 100 && !MarkerUtils.hasMarker(getAppDirPath(appId), Marker.DOWNLOAD_COMPLETE_MARKER)) {
+                    val dir = getAppDirPath(appId)
+                    MarkerUtils.addMarker(dir, Marker.DOWNLOAD_COMPLETE_MARKER)
+                    instance!!.scope.launch {
+                        // persist only what actually finished
+                        appInfoDao.insert(AppInfo(appId, isDownloaded = true, downloadedDepots = succeededDepotIds.toList()))
+                    }
+                    MarkerUtils.removeMarker(dir, Marker.STEAM_DLL_REPLACED)
+                }

Outside the selected range, add tracking of successful depots:

// just before creating DownloadInfo
val succeededDepotIds = java.util.Collections.synchronizedSet(mutableSetOf<Int>())

// inside the per-depot job after `if (success)`:
if (success) {
    di.setProgress(1f, idx)
    succeededDepotIds += depotId
} else {
    Timber.w("Depot $depotId skipped after retries")
    di.setWeight(idx, 0)
    di.setProgress(1f, idx)
}

This aligns SteamUtils.ensureSteamSettings behavior to write depots.txt only with real data. Based on learnings.

🤖 Prompt for AI Agents
In app/src/main/java/app/gamenative/service/SteamService.kt around lines
897-901, change the end-of-download handling so you only persist depots that
actually succeeded, avoid blocking the progress callback with runBlocking, and
guard against multiple 100% callbacks: introduce a thread-safe succeededDepotIds
set outside the shown range (before creating DownloadInfo), add per-depot logic
to add depotId to succeededDepotIds on success and set weight/mark progress on
failure, replace the runBlocking insert with a non-blocking single-shot write
(e.g., use an existing coroutine scope or launch a coroutine) that checks an
AtomicBoolean/flag to ensure the 100% branch runs only once and persists AppInfo
with succeededDepotIds (not entitledDepotIds), and keep the marker operations
as-is after the guarded persistence.

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.

1 participant