Skip to content

Ask once whether to register a download on the ModDB listing - #220

Open
Pixnop wants to merge 3 commits into
devfrom
feat/moddb-visibility-prompt
Open

Ask once whether to register a download on the ModDB listing#220
Pixnop wants to merge 3 commits into
devfrom
feat/moddb-visibility-prompt

Conversation

@Pixnop

@Pixnop Pixnop commented Aug 22, 2026

Copy link
Copy Markdown
Contributor

RiftLauncher has a ModDB listing whose download counter is most of what decides whether anyone browsing that site ever sees the launcher. The listing holds no build. It holds a 315 byte text archive pointing at the GitHub releases page, and the binaries stay on GitHub exactly as they are.

This adds one prompt, shown once, asking whether the launcher may request that little archive so the listing registers a download. It does not download the launcher from ModDB and it does not touch where updates come from.

The measurements are in issue #219: requesting the CDN URL leaves the counter alone, requesting the site's own download endpoint increments it. That endpoint is the only door that counts. In practice the bounded network layer follows no redirect, so the archive's bytes never even transfer.

Kept honest by construction, not by wording

Three answers drawn and treated as equals: count me in, no thanks, already did it. Whichever is pressed gets written to the config and the question never returns, on any later launch, whatever the answer was. Only the yes makes a request, exactly one, and only after a click. Nothing runs on a timer or at startup on its own.

Closing the dialog without pressing anything writes nothing. The question comes back next launch rather than counting as a yes or a no, because silence is neither. No default focus, no colour or size pushing one button over the others, and no download count displayed anywhere that would turn a question into a scoreboard.

Implementation

The request happens in the main process through the existing bounded network layer, behind the same trusted-sender gate as its neighbours. The listing's current file id is resolved from the ModDB API first, since a new upload mints a new id and a stale one would count towards an entry nobody is looking at, and only then is the counting endpoint touched, capped at 256 KB. Nothing is written to disk.

If the API is unreachable, the listing carries no readable release, or the request fails, the whole thing is dropped silently. The answer is still recorded so the player is never asked twice, and no error reaches them, because this is a courtesy they offered rather than a task they are waiting on.

Both allowlist rules the two calls use were already there, so no new host or path is opened. Widening API_URL_RULES to cover the download endpoint would have been the shorter route and was refused on purpose: a feature about a counter is the last place to hand the renderer a way to reach that counter.

Tests

The prompt appears only when the question is unanswered. Each of the three answers is recorded, only the yes fetches, and the dismissal writes nothing. The file id is resolved before the counting endpoint is hit. A failure at either step still records the answer silently. Config normalization covers an unknown stored value falling back to unasked. Every one of those was checked against a deliberate mutation of the code it guards.

The launcher is listed on ModDB as mod 11016, and that listing's download
count is most of what decides whether anyone browsing the site finds it. The
listing holds a 315-byte archive pointing at the GitHub releases page, not a
build.

This adds a prompt shown once, on startup, offering to request that archive so
the listing registers one download. Three answers, all equal: count me in, no
thanks, already did it. Whichever is pressed is stored in the config and the
question never comes back. Only the first one makes a request, exactly one,
and only after a click. Closing the dialog without answering stores nothing,
so the question survives to the next launch rather than being read as consent
or as a refusal.

The request goes through the main process: the current file id is resolved
from the ModDB API first, since a new upload mints a new one, and only then is
the counting endpoint touched, capped at 256 KB. A failure anywhere along the
way is logged at debug and dropped, with nothing shown to the player.

Refs #219
@Pixnop
Pixnop requested a review from Zaldaryon August 22, 2026 16:47
@Pixnop

Pixnop commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Adjusted the dialog copy in one respect, because the implementation turned out more honest than the sentence describing it. The bounded network layer follows no redirect, and the counter increments before the site issues its 302, so the archive's bytes never actually transfer. The copy said the launcher fetches the archive and throws it away, which promises a download that does not happen. It now says the launcher asks ModDB once for the listing's archive, that this registers as one download, and that the launcher does not keep it. Same meaning for the reader, no claim the code does not back.

190 renderer tests still pass with the new string.

@Pixnop Pixnop changed the title feat(moddb): ask once whether to fetch the listing archive Ask once whether to register a download on the ModDB listing Aug 22, 2026
@Pixnop

Pixnop commented Aug 22, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up commit on a log line, found by reading a real run's debug log rather than the tests. Accepting the prompt printed 'Attempted to redirect, but redirect policy was error', because the counting endpoint answers with a 302 and the bounded network layer refuses to follow redirects, so the generic catch treated the successful outcome as a failure. Anyone debugging something unrelated would have followed that trail for nothing.

That branch now says what happened: the endpoint answered with its redirect, which is the counted outcome, not followed on purpose. Everything else about the behaviour is unchanged, the request is still one attempt and still silent to the user.

Reading Electron's message is the only signal available for that distinction, so the comment says as much: if the wording ever changes upstream, the request behaves identically and only this line falls back to the generic branch. The new test pins the counted-outcome line and fails when the branch is removed, checked by mutation. The log had to be mocked module-wide rather than spied, since freshHandlers resets the module registry between tests and a spy would watch an instance the handler no longer imports.

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