feat(entitlement): pivot to paid app ownership
Check Steam ownership of the paid app with BIsSubscribedApp; keep Debug fake ownership out of Release. Ref: https://partner.steamgames.com/doc/api/ISteamApps#BIsSubscribedApp
This commit is contained in:
+30
-29
@@ -10,9 +10,9 @@ storefront, support or promo work. Cause of death for every retired path:
|
||||
|
||||
- **Free tier — every feature, every scene, every overlay**, free forever, no account, no
|
||||
watermark, nothing nagged inside the output. It is not a trial.
|
||||
- **Pro is a one-time Steam purchase: a DLC on the free app.**
|
||||
**$79 introductory → $99** on a stated date before launch. No subscription, no lifetime SKU, no
|
||||
third-party checkout.
|
||||
- **Pro is the paid Steam app; the free Demo is associated with it.** The paid app is **$79 during
|
||||
the announced launch period, then $99** on the announced date. No subscription or third-party
|
||||
checkout.
|
||||
- **The only delta is output quality.** Features are never gated; a payment buys pixels.
|
||||
- **No watermark, ever**, in either edition. The branding flash is retired as a paid delta and
|
||||
stays off (`ShowBrandFlash`, default `false`).
|
||||
@@ -49,25 +49,28 @@ The cap is a pixel budget plus a frame rate, not the word "720p":
|
||||
- **The greyed menu entry is the sales surface.** It stays clickable, says what it costs, opens
|
||||
checkout, and is labelled ("720p30 · free tier") — never silently missing, because silence
|
||||
reads as broken.
|
||||
- **Activation is live**, not restart-gated: activating enables the high tiers and a lapse
|
||||
(cancel, revoke, sign-out, channel switch) greys them again on the next refresh.
|
||||
- **Ownership is checked at app startup** and cached in `IsAppOwned` for that run. Purchases and
|
||||
refunds are reflected on the next app start; there is no subscription lapse or sign-out path.
|
||||
- **Bitrate follows the profile** — a capped user is not offered a 1080p bitrate.
|
||||
|
||||
## Entitlement — one source
|
||||
|
||||
**Pro is `steamDlcOwned`, and nothing else.** It resolves to the `OutputProfile`; it never touches a
|
||||
feature. Fail closed: anything unresolved is the free tier.
|
||||
**Paid-app ownership is the one entitlement.** Steam sells one paid app; its associated free demo is
|
||||
the purchase funnel. Both use the same code. The demo checks whether the current Steam account owns
|
||||
the paid app, then exposes the higher output tiers. The app-wide `IsAppOwned` value is the binary
|
||||
ownership result used by later feature checks.
|
||||
|
||||
- **Steam owns the SKU.** `BIsDlcInstalled(dlcAppId)` after `SteamAPI.Init`, behind
|
||||
`IEntitlement` (`Services/Entitlement/`). It is a local check against Steam's own state — no
|
||||
server, no key, no lease, no account, no email.
|
||||
- **Fail closed, four ways:** no DLC app id configured, `SteamAPI.Init()` false (no client), a
|
||||
- **Steam owns the purchase.** The demo calls `BIsSubscribedApp(paidAppId)` after `SteamAPI.Init`,
|
||||
behind `IEntitlement` (`Services/Entitlement/`). It is a local check against Steam's own state —
|
||||
no server, key, lease, or LlamaCasty account.
|
||||
- **Fail closed:** no paid app ID configured, `SteamAPI.Init()` false (no client), a
|
||||
thrown native load (missing `steam_api64.dll`), and "not owned" all resolve to the free tier.
|
||||
None of them may crash the app or grant Pro.
|
||||
- **Steam down or not running** → free profile, with a line explaining why, never a refusal to
|
||||
start. A Steam build launched outside Steam is the common case for people who pin shortcuts.
|
||||
- **No licence key exists**, so nothing to paste, email, lose or reclaim. A refunded DLC simply
|
||||
stops reporting ownership on the next start.
|
||||
- **Development builds fake ownership as true.** `FakeEntitlement` is excluded from Release builds.
|
||||
- **No licence key exists**, so nothing to paste, email, lose or reclaim. A refunded purchase stops
|
||||
reporting ownership on the next Steam check.
|
||||
- **The upgrade path is the Steam store overlay** (`ActivateGameOverlayToStore`) from the greyed
|
||||
tier menu — Valve's own surface, so the purchase stays inside the Steam Wallet rules. Never a
|
||||
checkout of ours, never an external payment page.
|
||||
@@ -80,20 +83,20 @@ re-derived.
|
||||
|
||||
## Channels — Steam only
|
||||
|
||||
**Steam is the distributor.** One free app on the store, the paid edition as a Steam **DLC**, and
|
||||
no other vector: no direct download, no winget, no Scoop/Chocolatey, no Microsoft Store. The store
|
||||
page is the single authoritative source for the download, the stats and the reviews.
|
||||
**Steam is the distributor.** One paid app and its associated free Demo; no direct download,
|
||||
GitHub binary, winget, Scoop/Chocolatey, or Microsoft Store. The store page is the single
|
||||
authoritative source for the download, stats, and reviews.
|
||||
|
||||
- **Pro = Steam DLC, one-time.** Bought on the **store page**; the app only reads ownership
|
||||
(`ISteamApps::BIsDlcInstalled`). No purchase flow, no Wallet code, nothing to reconcile.
|
||||
- **The paid app is one-time purchase.** The demo's App ID is separate, but it shares the code and
|
||||
checks ownership of the paid app (`ISteamApps::BIsSubscribedApp`). No in-app checkout or license
|
||||
server.
|
||||
- **The website sends people to Steam.** It publishes the model and links the store page; it takes
|
||||
no payment, collects no email, and hosts no binaries. One shop, one price, one receipt.
|
||||
- **No in-app checkout of ours, ever.** Steamworks requires in-app purchases to use the Steam
|
||||
Wallet, so the only purchase surface is Steam's own store overlay.
|
||||
- **Boot shape:** the app ships `steam_api64.dll` beside the exe and calls `SteamAPI.Init`. That
|
||||
is a functional dependency, not the rejected "native helper DLL for IP protection" — Steam
|
||||
ownership cannot be checked without it. Launched without Steam running, the app must fall back to
|
||||
the **free profile** and say so, never refuse to start.
|
||||
- **Boot shape:** the app ships `steam_api64.dll` beside the exe and calls `SteamAPI.Init`. Launched
|
||||
without Steam running, the app falls back to the **free profile** and says so; it never refuses to
|
||||
start.
|
||||
- **Updates are Steam's.** The depot push is the update; the app keeps no updater and no version
|
||||
banner (the old "no self-updater" rule stays, for the same reason: it works differently).
|
||||
- **Portable mode is no longer a distributable.** `portable.txt` remains as a feature for anyone who
|
||||
@@ -135,8 +138,8 @@ repo for bugs that survive the friction of needing an account.
|
||||
- **The issue tracker is a filter, deliberately.** Bugs go to the public releases repo on GitHub,
|
||||
which requires an account; Steam Discussions stay pinned and unread; Announcements carry known
|
||||
issues and release notes. Steam refunds are Valve's own (2 h / 14 days) and cost us nothing.
|
||||
- **Nobody is locked out over a licence.** A billing hiccup drops the app to the free profile with
|
||||
a banner and a portal link — never a blocked stream, never a modal.
|
||||
- **Nobody is locked out of streaming.** If Steam ownership is unavailable or not owned, the app
|
||||
remains usable at the free output profile; no modal or account portal.
|
||||
|
||||
## The introductory price — the only promotion
|
||||
|
||||
@@ -176,15 +179,13 @@ pointless and a leak that is traceable — not an unbreakable binary.
|
||||
|
||||
- **The free tier is the real defence.** If the only delta is pixels above 921,600, a cracked
|
||||
build buys almost nothing and forfeits updates, support and the publisher's signature.
|
||||
- **Steam DRM is optional and not needed.** The DLC check is already the entitlement; adding
|
||||
- **Steam DRM is optional and not needed.** The app ownership check is the entitlement; adding
|
||||
Valve's DRM wrapper buys nothing a determined crack respects, and it can break an exe on a
|
||||
machine that never runs Steam.
|
||||
- **The distribution is not a handout.** Steam deliveries go through Valve's client and account, so
|
||||
there is no public zip for a mirror site to repackage, and no hash pin for us to maintain.
|
||||
- **Steam is the licence state** — a refunded DLC stops reporting ownership the next time Steam is
|
||||
asked, and the app drops to the free profile on the next start. Nothing to revoke by hand.
|
||||
- **The Steam purchase is in About** — a leaked build traces back to a Steam account, which Valve
|
||||
can action; we hold nothing to trace.
|
||||
- **Steam is the ownership state** — a refunded purchase stops reporting ownership on the next
|
||||
check. The app holds no customer account or license key.
|
||||
|
||||
⛔ **Rejected, do not re-propose:** a native helper DLL for "IP protection", obfuscation, hardware
|
||||
locking, remote code that downloads and executes, and gating asset downloads as an auth check.
|
||||
|
||||
Reference in New Issue
Block a user