Skip to content

fix: don't let expired free weekend licenses shadow owned games#1148

Open
kiequoo wants to merge 4 commits intoutkarshdalal:masterfrom
kiequoo:fix/free-weekend-library-filter
Open

fix: don't let expired free weekend licenses shadow owned games#1148
kiequoo wants to merge 4 commits intoutkarshdalal:masterfrom
kiequoo:fix/free-weekend-library-filter

Conversation

@kiequoo
Copy link
Copy Markdown
Contributor

@kiequoo kiequoo commented Apr 8, 2026

Summary

I played Dying Light during a Steam free weekend, then later bought it. After purchasing, the game wasn't showing up in GameNative's library at all.

This is related to but distinct from the expired-license filtering work in #945, #982, and #985. Those PRs addressed expired free weekend games appearing in the library. This fixes the inverse: a game you genuinely own disappearing because the expired free weekend license shadows the real purchase.

Root cause: During PICS package processing, each package unconditionally overwrites the packageId on its associated SteamApp rows. If the expired free weekend package happened to be processed after the real purchase package, it clobbered packageId with the expired license's ID — making the game invisible to the owned apps query (which, post-#985, correctly filters out expired packages).

Fix: Before iterating packages, load the set of all expired package IDs. When updating an app's packageId, skip the write if the incoming package is expired but the app's current packageId is not — a real purchase already won, don't let an expired license shadow it.

  • SteamLicenseDao: new query to fetch expired package IDs (license_flags & 8 != 0)
  • SteamService: guard around packageId update — expired packages can only overwrite other expired packages

Summary by cubic

Fixes cases where expired or temporary Steam licenses could hide games you own. PICS updates now choose a stable packageId so durable purchases and free-to-play titles remain visible.

  • Bug Fixes
    • Deterministic package selection: prioritize licenses with metadata, non-expired, durable, then newest; includes AutoGrant handling and package-ID tie-breaks to stop temporary/expired licenses from overwriting purchases.
    • Preloaded license data and chunked app lookups with SQLITE_MAX_VARS to pick the best package per app and avoid overwrite-order issues.

Written for commit 40f2e0b. Summary will update on new commits.

Summary by CodeRabbit

  • Bug Fixes

    • Prevented temporary/expiring licenses from incorrectly overwriting permanent purchases.
  • New Features

    • Improved package-selection so the most appropriate package is chosen when multiple licenses exist.
    • Added bulk app fetching with safe batching to handle large requests.
  • Tests

    • Added comprehensive unit tests validating deterministic package selection across license scenarios.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 1d5ea5f2-c0d3-41ff-b749-635dbd53731f

📥 Commits

Reviewing files that changed from the base of the PR and between 7c4b197 and 40f2e0b.

📒 Files selected for processing (5)
  • app/src/main/java/app/gamenative/db/dao/DbConstants.kt
  • app/src/main/java/app/gamenative/db/dao/SteamAppDao.kt
  • app/src/main/java/app/gamenative/db/dao/SteamLicenseDao.kt
  • app/src/main/java/app/gamenative/service/SteamService.kt
  • app/src/test/java/app/gamenative/service/SteamPackageSelectionTest.kt
💤 Files with no reviewable changes (1)
  • app/src/main/java/app/gamenative/db/dao/SteamLicenseDao.kt
✅ Files skipped from review due to trivial changes (1)
  • app/src/main/java/app/gamenative/db/dao/DbConstants.kt

📝 Walkthrough

Walkthrough

Removed a local SQLITE_MAX_VARS constant and introduced a shared DbConstants value; added batching to SteamAppDao.findApps; implemented deterministic package-selection and conditional app updates in SteamService; added unit tests for package-selection logic and adjusted DAO usages referencing SQLITE_MAX_VARS.

Changes

Cohort / File(s) Summary
DB constants
app/src/main/java/app/gamenative/db/dao/DbConstants.kt
Added shared const val SQLITE_MAX_VARS = 999 for chunked SQLite queries.
License DAO
app/src/main/java/app/gamenative/db/dao/SteamLicenseDao.kt
Removed local SQLITE_MAX_VARS; existing batching logic now depends on DbConstants.SQLITE_MAX_VARS (constant moved out).
App DAO (batch fetch)
app/src/main/java/app/gamenative/db/dao/SteamAppDao.kt
Added _findApps(appIds: List<Int>) and @Transaction findApps(appIds: List<Int>) with chunking by SQLITE_MAX_VARS.
Service: package selection & PICS processing
app/src/main/java/app/gamenative/service/SteamService.kt
Added selectPreferredPackageId and reworked PICS package processing to batch-load apps/licenses, choose preferred package IDs, avoid overwriting better package_ids, and enqueue processed app IDs per package batch.
Tests
app/src/test/java/app/gamenative/service/SteamPackageSelectionTest.kt
New test suite exercising selectPreferredPackageId across multiple license scenarios (durable vs temporary, expired, autogrant, tiebreakers).

Sequence Diagram(s)

sequenceDiagram
    participant PICS as PICS Callback
    participant SteamSvc as SteamService
    participant DB as Database (DAOs)

    PICS->>SteamSvc: package callback (packageId, apps, depots)
    SteamSvc->>DB: batch load apps for package appIds
    SteamSvc->>DB: batch load licenses for packageIds
    SteamSvc->>SteamSvc: selectPreferredPackageId(existingPkg, incomingPkg, licensesByPackage)
    SteamSvc->>DB: update or insert SteamApp rows (conditionally using selected packageId)
    SteamSvc->>DB: enqueue processed app IDs
    DB-->>SteamSvc: ack
    SteamSvc-->>PICS: processing complete
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • phobos665

Poem

🐰 I hopped through rows of code tonight,
Moved constants, tests, and logic tight.
I pick the best package, keep the true,
And batch up apps with hops anew —
Tiny rabbit, big delight! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 9.52% 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
Title check ✅ Passed The title clearly and specifically summarizes the main fix: preventing expired free weekend licenses from shadowing owned games in the library.
Description check ✅ Passed The description provides comprehensive context on the bug, root cause, and fix, though the required checklist items are not explicitly marked as complete.

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

No issues found across 2 files

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

🤖 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/SteamService.kt`:
- Around line 3849-3866: The current update logic only skips updating when the
incoming package is expired, which still allows a temporary free-weekend package
to overwrite a valid purchased package; instead compute a stable precedence
between the existing SteamApp.packageId and the incoming pkg.id before calling
appDao.update: use licenseDao.getExpiredPackageIds() plus whatever
expiry/start/purchase metadata is available on picsCallback.packages[pkg.id] (or
a lookup via licenseDao) to determine which package is higher-priority (e.g.,
prefer non-expired over expired; if both non-expired prefer the package with the
later expiry or explicit purchase timestamp; if tied use deterministic
tie-breaker like package id), and only call
appDao.update(steamApp.copy(packageId = selectedPkgId)) when selectedPkgId !=
steamApp.packageId; reference picsCallback.packages,
licenseDao.getExpiredPackageIds(), appDao.findApp(), appDao.update(), and
SteamApp.packageId when making the change.
🪄 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: 313034e5-f5a4-43fa-ab24-30021b59dfd0

📥 Commits

Reviewing files that changed from the base of the PR and between 55c0796 and 7c4b197.

📒 Files selected for processing (2)
  • app/src/main/java/app/gamenative/db/dao/SteamLicenseDao.kt
  • app/src/main/java/app/gamenative/service/SteamService.kt

Comment thread app/src/main/java/app/gamenative/service/SteamService.kt Outdated
Comment thread app/src/main/java/app/gamenative/service/SteamService.kt Outdated
kiequoo added 4 commits April 15, 2026 21:32
When a user played a free weekend and later purchased the game, both
packages contain the same app ID. PICS processing was overwriting
package_id unconditionally, so whichever package was processed last
won — if the expired free weekend package landed last, the owned-apps
query excluded the game because it only checked that one package_id.

Pre-load expired package IDs once per PICS transaction batch and skip
updating package_id when the incoming package is expired but the stored
one is already valid.
Adds two tests to SteamPackageSelectionTest verifying that a game whose
only license is AutoGrant (package 0) still gets its packageId set, and
that a durable paid purchase correctly outranks AutoGrant when both are
present.
Removes the coupling where SteamAppDao depended on a constant defined
in SteamLicenseDao's file.
@kiequoo kiequoo force-pushed the fix/free-weekend-library-filter branch from 7c4b197 to 40f2e0b Compare April 15, 2026 19:38
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