-
Notifications
You must be signed in to change notification settings - Fork 0
DLC
Since v0.5.0. steam.dlc is the curated layer over the DLC half of
ISteamApps: what the app has, what the user owns, what is installed, and
installing the rest. It is called dlc because the generated ISteamApps
class already owns steam.apps.
The layer is deliberately small. listDlc is an index loop over a
call with three out-buffers, where getting the name buffer wrong reads garbage,
and install is a two step flow: start the download, then wait for
Steam's DlcInstalled_t. The ownership and install checks are one-line reads
that come along because a DLC gate reads all four in the same breath.
The install, build, beta and launch facts of the running app are on System, not here: this layer answers questions about that app's DLC.
The Apps instance is created lazily and cached on the Steam object. Its
class name is Apps, not Dlc, because it wraps ISteamApps; only the getter
is renamed.
listDlc(): DlcInfo[]The DLC of the running app, one entry per DLC, in Steam's order. Empty when the app has none.
This is a local read against the Steam client, so it needs no round trip.
The list is what Steam knows about the app, not what the user owns. Check
ownership separately with steam.apps.BIsSubscribedApp(appId), and installed
state with steam.apps.BIsDlcInstalled(appId).
import { init } from 'steamwand.js';
const steam = init({ appId: 480 });
for (const dlc of steam.dlc.listDlc()) {
console.log(dlc.appId, dlc.name, steam.dlc.isInstalled(dlc.appId));
}
steam.close();Names are read into a 128-byte buffer, which is what Valve's own samples use.
An index Steam refuses is skipped rather than pushed as a half-filled entry, so
the returned array can be shorter than GetDLCCount().
There is no flag for it. Filter the list yourself:
const owned = steam.dlc.listDlc().filter((d) => d.available && steam.dlc.isOwned(d.appId));available and owned are different questions: available is Steam's flag for
"still on the store and not hidden", which a DLC the user already owns can
lose.
Since v0.6.0.
isOwned(appId: number): boolean
isInstalled(appId: number): booleanisOwned is whether the user has a license, whether or not the files are
anywhere. isInstalled is whether the files are on disk. They are different
questions: a DLC the user just bought is owned long before Steam has finished
downloading it, so a content gate that only checks ownership loads files that
are not there yet.
if (steam.dlc.isOwned(1234) && !steam.dlc.isInstalled(1234)) {
await steam.dlc.install(1234);
}Since v0.6.0.
install(appId: number): Promise<void>Installs a DLC and resolves once Steam reports it installed, so the resolved promise means the files are on disk.
Returns at once when the DLC is already installed. Otherwise Steam queues the
download and confirms with DlcInstalled_t, which this waits for. The download
is Steam's, so this takes as long as the DLC is large; watch it with
downloadProgress.
The user must own the DLC. Steam ignores an install request for a DLC without a license, and no callback follows, so the promise never settles. Check isOwned first.
const [dlc] = steam.dlc.listDlc();
if (dlc && steam.dlc.isOwned(dlc.appId)) await steam.dlc.install(dlc.appId);Since v0.6.0.
uninstall(appId: number): voidUninstalls a DLC and frees its disk space. Steam has no result for this, so it cannot fail from JavaScript, and it returns before the files are gone.
Since v0.6.0.
downloadProgress(appId: number): { bytesDownloaded: bigint; bytesTotal: bigint } | nullHow far a DLC download has come, or null when that DLC is not downloading,
which includes a finished one. Byte counts are 64-bit, so bigint.
const p = steam.dlc.downloadProgress(1234);
if (p) console.log(Number((p.bytesDownloaded * 100n) / p.bytesTotal), '%');Since v0.6.0.
onInstalled(listener: (appId: number) => void): () => voidSubscribes to DLC finishing installation, and returns an unsubscribe function.
It fires for every DLC Steam installs while the app runs, including one the user bought from the store or the overlay, so this is the hook that turns on the content without a restart. install already waits for its own DLC, so this is for the ones the app did not start.
const off = steam.dlc.onInstalled((appId) => console.log('installed', appId));
// later: off();One entry from listDlc.
| Field | Type | Meaning |
|---|---|---|
appId |
number |
App id of the DLC. |
available |
boolean |
True while the DLC is available for purchase or install, so neither hidden nor removed. |
name |
string |
Display name, in the Steam client language. |
App ids are 32-bit, so number and not bigint.
| Shape | When |
|---|---|
| never settles | install was called for a DLC the user does not own. Steam drops the request and sends no callback. |
Nothing here throws. listDlc skips a DLC entry Steam refuses and gives an
empty array for an app with no DLC; if the array is empty and you expected
entries, the app id is wrong or Steam has not finished loading the app's data.
The ownership and install reads answer false rather than failing.
The rest of ISteamApps is either on System or on the raw generated
steam.apps:
On System
- The install and build facts of the running app: installDir, isAppInstalled, buildId.
- The betas: currentBeta, listBetas, setActiveBeta.
- The launch arguments: launchCommandLine and launchQueryParam, onLaunchParameters.
- The licence facts: appOwner and isFamilyShared, timedTrial.
- The language: gameLanguage.
On the raw steam.apps
-
BIsSubscribed,BIsSubscribedFromFreeWeekend,GetEarliestPurchaseUnixTime(appId),BIsVACBanned,BIsCybercafe,BIsLowViolence. -
SetDlcContext(appId), which tells Steam which DLC the app is currently acting as. -
GetInstalledDepots,MarkContentCorrupt,GetFileDetails,GetAvailableGameLanguages. -
SetGamePerformanceSettingandSetGameRenderResolution, the Steam Deck performance hints. -
RequestAppProofOfPurchaseKeyandRequestAllProofOfPurchaseKeys, the CD key retrieval for apps that still have keys.
Flat API explains the calling convention.
Next: System for the client and machine facts, and for everything
ISteamApps says about the running app itself.