-
Notifications
You must be signed in to change notification settings - Fork 3
Workshop Manager
Complete reference for all Workshop/UGC (User Generated Content) functionality in Steamworks FFI.
The SteamWorkshopManager provides comprehensive access to the Steam Workshop API, enabling you to browse, subscribe to, create, and manage user-generated content. The Workshop system allows players to share mods, maps, items, and other creative content with the community.
| Category | Functions | Description |
|---|---|---|
| Subscription Management | 4 | Subscribe, unsubscribe, and list Workshop items |
| Item State & Information | 4 | Get item state, installation info, and download progress |
| Query Operations | 12 | Search and browse Workshop content |
| Item Creation & Update | 12 | Create and update your own Workshop items |
| Voting & Favorites | 4 | Vote on items and manage favorites |
| Item Deletion | 1 | Delete Workshop items you created |
| Content Descriptors | 1 | Mature content labeling for Workshop items |
Total: 38 Functions
Functions for subscribing to and managing Workshop items.
Subscribe to a Workshop item. The item will be automatically downloaded and installed.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SubscribeItem()- Subscribe to item
Parameters:
-
publishedFileId: PublishedFileId- The Workshop item ID (BigInt)
Returns: Promise<boolean> - true if subscription successful, false otherwise
Example:
import SteamworksSDK from 'steamworks-ffi-node';
const steam = SteamworksSDK.getInstance();
steam.init({ appId: 480 });
// Subscribe to a Workshop item
const itemId = BigInt('123456789');
const success = await steam.workshop.subscribeItem(itemId);
if (success) {
console.log('Successfully subscribed!');
// Check if it's downloading
const state = steam.workshop.getItemState(itemId);
if (state & EItemState.Downloading) {
console.log('Item is downloading...');
}
}
steam.shutdown();Notes:
- Item downloads automatically after subscription
- Use
getItemDownloadInfo()to track download progress - Use
getItemInstallInfo()to get installation location - Waits up to 5 seconds for Steam server response
Unsubscribe from a Workshop item. The item will be uninstalled when the game quits.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_UnsubscribeItem()- Unsubscribe from item
Parameters:
-
publishedFileId: PublishedFileId- The Workshop item ID (BigInt)
Returns: Promise<boolean> - true if unsubscription successful, false otherwise
Example:
const itemId = BigInt('123456789');
const success = await steam.workshop.unsubscribeItem(itemId);
if (success) {
console.log('Successfully unsubscribed');
console.log('Item will be uninstalled when game quits');
}Notes:
- Item files remain until application restarts
- Item is marked for deletion but not immediately removed
- Waits up to 5 seconds for Steam server response
Get the total number of subscribed Workshop items.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetNumSubscribedItems()- Get subscription count
Returns: number - Number of subscribed items
Example:
const count = steam.workshop.getNumSubscribedItems();
console.log(`Subscribed to ${count} Workshop items`);Get all subscribed Workshop item IDs.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetSubscribedItems()- Get subscribed item IDs
Returns: PublishedFileId[] - Array of Workshop item IDs
Example:
const items = steam.workshop.getSubscribedItems();
console.log(`You have ${items.length} subscribed items:`);
items.forEach(itemId => {
console.log(`Item ID: ${itemId}`);
// Get installation info for each
const info = steam.workshop.getItemInstallInfo(itemId);
if (info) {
console.log(` Location: ${info.folder}`);
console.log(` Size: ${(Number(info.sizeOnDisk) / 1024 / 1024).toFixed(2)} MB`);
}
});Functions for checking Workshop item status and getting detailed information.
Get the state flags for a Workshop item.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetItemState()- Get item state flags
Parameters:
-
publishedFileId: PublishedFileId- The Workshop item ID
Returns: number - Bit flags indicating item state (combination of EItemState values)
Type:
enum EItemState {
None = 0,
Subscribed = 1,
LegacyItem = 2,
Installed = 4,
NeedsUpdate = 8,
Downloading = 16,
DownloadPending = 32
}Example:
import { EItemState } from 'steamworks-ffi-node';
const itemId = BigInt('123456789');
const state = steam.workshop.getItemState(itemId);
if (state & EItemState.Subscribed) {
console.log('✓ Subscribed');
}
if (state & EItemState.Installed) {
console.log('✓ Installed');
}
if (state & EItemState.Downloading) {
console.log('⬇ Downloading...');
}
if (state & EItemState.NeedsUpdate) {
console.log('🔄 Update available');
}
if (state & EItemState.DownloadPending) {
console.log('⏳ Download pending');
}Notes:
- Multiple flags can be set simultaneously using bitwise OR
- Check flags using bitwise AND (
&) -
Nonemeans item is not subscribed
Get installation information for a Workshop item.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetItemInstallInfo()- Get installation details
Parameters:
-
publishedFileId: PublishedFileId- The Workshop item ID
Returns: ItemInstallInfo | null - Installation info or null if not installed
Type:
interface ItemInstallInfo {
sizeOnDisk: bigint; // Size in bytes
folder: string; // Installation folder path
timestamp: number; // Unix timestamp of installation
}Example:
const itemId = BigInt('123456789');
const info = steam.workshop.getItemInstallInfo(itemId);
if (info) {
console.log(`Installed at: ${info.folder}`);
console.log(`Size: ${(Number(info.sizeOnDisk) / 1024 / 1024).toFixed(2)} MB`);
console.log(`Installed: ${new Date(info.timestamp * 1000).toLocaleString()}`);
// Load the mod files
const fs = require('fs');
const files = fs.readdirSync(info.folder);
console.log('Files:', files);
} else {
console.log('Item not installed');
}Notes:
- Only returns data if item has
EItemState.Installedflag - Folder path is the location where item files are stored
- Typical locations:
-
Windows:
C:\Program Files (x86)\Steam\steamapps\workshop\content\<appid>\<publishedfileid> -
macOS:
~/Library/Application Support/Steam/steamapps/workshop/content/<appid>/<publishedfileid> -
Linux:
~/.local/share/Steam/steamapps/workshop/content/<appid>/<publishedfileid>
-
Windows:
Get download progress for a Workshop item.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetItemDownloadInfo()- Get download progress
Parameters:
-
publishedFileId: PublishedFileId- The Workshop item ID
Returns: ItemDownloadInfo | null - Download info or null if not downloading
Type:
interface ItemDownloadInfo {
bytesDownloaded: bigint; // Bytes downloaded so far
bytesTotal: bigint; // Total bytes to download
percentComplete: number; // Percentage complete (0-100)
}Example:
const itemId = BigInt('123456789');
// Start monitoring download
const interval = setInterval(() => {
steam.runCallbacks(); // Process Steam callbacks
const progress = steam.workshop.getItemDownloadInfo(itemId);
if (progress) {
const mb = Number(progress.bytesDownloaded) / 1024 / 1024;
const totalMb = Number(progress.bytesTotal) / 1024 / 1024;
console.log(`Downloading: ${progress.percentComplete.toFixed(1)}%`);
console.log(`${mb.toFixed(2)} MB / ${totalMb.toFixed(2)} MB`);
} else {
console.log('Download complete!');
clearInterval(interval);
}
}, 1000);Notes:
- Only returns data if item has
EItemState.Downloadingflag - Returns null when download is complete
- Call
runCallbacks()regularly to update progress
Force download of a Workshop item.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_DownloadItem()- Initiate download
Parameters:
-
publishedFileId: PublishedFileId- The Workshop item ID -
highPriority?: boolean- If true, prioritizes this download (default: false)
Returns: boolean - true if download started, false otherwise
Example:
const itemId = BigInt('123456789');
// Force download with high priority
const started = steam.workshop.downloadItem(itemId, true);
if (started) {
console.log('Download started');
// Monitor progress
const checkProgress = setInterval(() => {
steam.runCallbacks();
const progress = steam.workshop.getItemDownloadInfo(itemId);
if (progress) {
console.log(`Progress: ${progress.percentComplete.toFixed(1)}%`);
} else {
console.log('Download complete!');
clearInterval(checkProgress);
}
}, 500);
}Notes:
- High priority suspends other downloads
- Useful for forcing re-download of already installed items
- If not subscribed, item will be cached temporarily
Functions for searching and browsing Workshop content.
createQueryUserUGCRequest(accountId, listType, matchingType, sortOrder, creatorAppId, consumerAppId, page)
Create a query for a specific user's Workshop items.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_CreateQueryUserUGCRequest()- Create user query
Parameters:
-
accountId: number- Steam account ID (32-bit, lower part of SteamID) -
listType: EUserUGCList- Type of list to query -
matchingType: EUGCMatchingUGCType- Type of UGC to match -
sortOrder: EUserUGCListSortOrder- How to sort results -
creatorAppId: number- App that created the items -
consumerAppId: number- App that will consume the items -
page: number- Page number (1-based)
Returns: UGCQueryHandle - Query handle for use with sendQueryUGCRequest()
Types:
enum EUserUGCList {
Published = 0, // Items user has published
VotedOn = 1, // Items user has voted on
VotedUp = 2, // Items user voted up
VotedDown = 3, // Items user voted down
Favorited = 5, // Items user favorited
Subscribed = 6, // Items user is subscribed to
UsedOrPlayed = 7, // Items user has used
Followed = 8 // Items user follows
}
enum EUserUGCListSortOrder {
CreationOrderDesc = 0, // Newest first
CreationOrderAsc = 1, // Oldest first
TitleAsc = 2, // A-Z
LastUpdatedDesc = 3, // Recently updated first
SubscriptionDateDesc = 4, // Recently subscribed first
VoteScoreDesc = 5, // Highest rated first
ForModeration = 6 // For moderation tools
}
enum EUGCMatchingUGCType {
Items = 0, // Standard Workshop items
ItemsMtx = 1, // Microtransaction items
ItemsReadyToUse = 2, // Items ready to use
Collections = 3, // Collections
Artwork = 4, // Artwork
Videos = 5, // Videos
Screenshots = 6, // Screenshots
AllGuides = 7, // All guides
WebGuides = 8, // Web guides
IntegratedGuides = 9, // Integrated guides
UsableInGame = 10, // Usable in game
ControllerBindings = 11, // Controller configs
GameManagedItems = 12 // Game-managed items
}Example:
import { EUserUGCList, EUserUGCListSortOrder, EUGCMatchingUGCType } from 'steamworks-ffi-node';
// Query current user's published items
const accountId = 12345678; // From user's SteamID
const query = steam.workshop.createQueryUserUGCRequest(
accountId,
EUserUGCList.Published,
EUGCMatchingUGCType.Items,
EUserUGCListSortOrder.CreationOrderDesc,
480, // App ID
480,
1 // First page
);
// Send query and get results
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
console.log(`Found ${result.numResults} items`);
}Create a query for all Workshop items with various sorting options.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_CreateQueryAllUGCRequestPage()- Create general query
Parameters:
-
queryType: EUGCQuery- Type of query/sorting -
matchingType: EUGCMatchingUGCType- Type of UGC to match -
creatorAppId: number- App that created the items -
consumerAppId: number- App that will consume the items -
page: number- Page number (1-based)
Returns: UGCQueryHandle - Query handle for use with sendQueryUGCRequest()
Type:
enum EUGCQuery {
RankedByVote = 0, // Most popular
RankedByPublicationDate = 1, // Newest
AcceptedForGameRankedByAcceptanceDate = 2,
RankedByTrend = 3, // Trending
FavoritedByFriendsRankedByPublicationDate = 4,
CreatedByFriendsRankedByPublicationDate = 5,
RankedByNumTimesReported = 6,
CreatedByFollowedUsersRankedByPublicationDate = 7,
NotYetRated = 8,
RankedByTotalVotesAsc = 9,
RankedByVotesUp = 10,
RankedByTextSearch = 11, // Text search
RankedByTotalUniqueSubscriptions = 12,
RankedByPlaytimeTrend = 13,
RankedByTotalPlaytime = 14,
RankedByAveragePlaytimeTrend = 15,
RankedByLifetimeAveragePlaytime = 16,
RankedByPlaytimeSessionsTrend = 17,
RankedByLifetimePlaytimeSessions = 18,
RankedByLastUpdatedDate = 19 // Recently updated
}Example:
import { EUGCQuery, EUGCMatchingUGCType } from 'steamworks-ffi-node';
// Query most popular Workshop items
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, // Spacewar
480,
1 // First page
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
console.log(`Found ${result.totalResults} total items`);
console.log(`Showing ${result.numResults} results`);
// Get individual items
for (let i = 0; i < result.numResults; i++) {
const item = steam.workshop.getQueryUGCResult(query, i);
if (item) {
console.log(`${i + 1}. ${item.title}`);
console.log(` Votes: 👍 ${item.votesUp} | Score: ${item.score.toFixed(2)}`);
}
}
// Clean up
steam.workshop.releaseQueryUGCRequest(query);
}Send a UGC query to Steam and wait for results.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SendQueryUGCRequest()- Send query
Parameters:
-
queryHandle: UGCQueryHandle- Query handle from create query functions
Returns: Promise<{ numResults: number; totalResults: number; cachedData: boolean } | null>
Example:
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByPublicationDate,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
console.log(`Page contains ${result.numResults} items`);
console.log(`Total available: ${result.totalResults} items`);
console.log(`Cached: ${result.cachedData ? 'Yes' : 'No'}`);
// Process results...
// Always release when done
steam.workshop.releaseQueryUGCRequest(query);
} else {
console.error('Query failed');
}Notes:
- Waits up to 60 seconds for Steam server response
- After processing results, always call
releaseQueryUGCRequest() - Use
getQueryUGCResult()to retrieve individual items - Query returns up to 50 items per page
Set the search text for a UGC query to filter by text in the title or description.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetSearchText()- Set search text filter
Parameters:
-
queryHandle: UGCQueryHandle- Query handle to configure -
searchText: string- Text to search for in item titles and descriptions
Returns: boolean - true if successful
Example:
// Create a text search query
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByTextSearch, // Use text search query type
EUGCMatchingUGCType.Items,
480, 480, 1
);
// Set the search text
steam.workshop.setSearchText(query, "dragon");
// Now send the query
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
console.log(`Found ${result.numResults} items matching "dragon"`);
for (let i = 0; i < result.numResults; i++) {
const item = steam.workshop.getQueryUGCResult(query, i);
if (item) {
console.log(`${item.title} - ${item.description}`);
}
}
steam.workshop.releaseQueryUGCRequest(query);
}Notes:
- Call this BEFORE
sendQueryUGCRequest() - Works best with query type
EUGCQuery.RankedByTextSearch(11) - Searches in both item titles and descriptions
- Case-insensitive text matching
- Returns items ranked by search relevance
Enable returning playtime statistics for UGC query results.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetReturnPlaytimeStats()- Enable statistics
Parameters:
-
queryHandle: UGCQueryHandle- Query handle to configure -
days: number- Number of days of playtime stats to return (0 = lifetime)
Returns: boolean - true if successful
Example:
// Create query
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
// Enable lifetime playtime statistics
steam.workshop.setReturnPlaytimeStats(query, 0);
// Now send the query
const result = await steam.workshop.sendQueryUGCRequest(query);
// Statistics will be available in query resultsNotes:
- Call this BEFORE
sendQueryUGCRequest() - Enables additional statistics in WorkshopItem results
- Use 0 for lifetime stats, or specific number of days
- Allows tracking item popularity by playtime
Get a single result from a completed UGC query.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetQueryUGCResult()- Get query result
Parameters:
-
queryHandle: UGCQueryHandle- Query handle from completed query -
index: number- Index of result (0-based)
Returns: WorkshopItem | null - Workshop item details or null
Type:
interface WorkshopItem {
publishedFileId: bigint; // Unique item ID
result: number; // EResult code
fileType: number; // EWorkshopFileType
creatorAppID: number; // App that created it
consumerAppID: number; // App that uses it
title: string; // Item title
description: string; // Item description
steamIDOwner: bigint; // Creator's Steam ID
timeCreated: number; // Unix timestamp
timeUpdated: number; // Unix timestamp
timeAddedToUserList: number; // Unix timestamp
visibility: number; // ERemoteStoragePublishedFileVisibility
banned: boolean; // Is banned
acceptedForUse: boolean; // Accepted for use
tagsTruncated: boolean; // Tags were truncated
tags: string[]; // Item tags
file: bigint; // File handle
previewFile: bigint; // Preview file handle
fileName: string; // File name
fileSize: number; // File size in bytes
previewFileSize: number; // Preview size in bytes
url: string; // Workshop URL
votesUp: number; // Upvotes (subscriptions)
score: number; // Score (0-1)
numChildren: number; // Number of children
totalFilesSize: bigint; // Total size
}Example:
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
console.log('Top Workshop Items:');
for (let i = 0; i < result.numResults; i++) {
const item = steam.workshop.getQueryUGCResult(query, i);
if (item) {
console.log(`\n${i + 1}. ${item.title}`);
console.log(` ID: ${item.publishedFileId}`);
console.log(` By: ${item.steamIDOwner}`);
console.log(` Votes: 👍 ${item.votesUp} | Score: ${item.score.toFixed(2)}`);
console.log(` Size: ${(item.fileSize / 1024 / 1024).toFixed(2)} MB`);
console.log(` Tags: ${item.tags.join(', ')}`);
console.log(` URL: ${item.url}`);
console.log(` Created: ${new Date(item.timeCreated * 1000).toLocaleString()}`);
console.log(` Updated: ${new Date(item.timeUpdated * 1000).toLocaleString()}`);
// Subscribe to item
// await steam.workshop.subscribeItem(item.publishedFileId);
}
}
steam.workshop.releaseQueryUGCRequest(query);
}Get the number of tags for a specific query result.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetQueryUGCNumTags()- Get tag count
Parameters:
-
queryHandle: UGCQueryHandle- Query handle from completed query -
index: number- Index of result (0-based)
Returns: number - Number of tags, or 0 if failed
Example:
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
for (let i = 0; i < result.numResults; i++) {
const numTags = steam.workshop.getQueryUGCNumTags(query, i);
const item = steam.workshop.getQueryUGCResult(query, i);
if (item) {
console.log(`${item.title} has ${numTags} tags`);
console.log(`Tags: ${item.tags.join(', ')}`);
}
}
steam.workshop.releaseQueryUGCRequest(query);
}Notes:
- Returns 0 if index is out of range
- Tags are also available in the
tagsarray of WorkshopItem - Useful for validation before processing tags
Get a specific tag by index for a query result.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetQueryUGCTag()- Get tag by index
Parameters:
-
queryHandle: UGCQueryHandle- Query handle from completed query -
index: number- Index of result (0-based) -
tagIndex: number- Index of tag (0-based)
Returns: string | null - Tag string, or null if failed
Example:
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
for (let i = 0; i < result.numResults; i++) {
const item = steam.workshop.getQueryUGCResult(query, i);
const numTags = steam.workshop.getQueryUGCNumTags(query, i);
if (item) {
console.log(`${item.title} tags:`);
for (let t = 0; t < numTags; t++) {
const tag = steam.workshop.getQueryUGCTag(query, i, t);
console.log(` - ${tag}`);
}
}
}
steam.workshop.releaseQueryUGCRequest(query);
}Notes:
- Use
getQueryUGCNumTags()first to get the tag count - Returns null if tagIndex is out of range
- Tags are also available in the
tagsarray of WorkshopItem
Get the preview image URL for a specific query result.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetQueryUGCPreviewURL()- Get preview URL
Parameters:
-
queryHandle: UGCQueryHandle- Query handle from completed query -
index: number- Index of result (0-based)
Returns: string | null - Preview image URL, or null if failed
Example:
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
for (let i = 0; i < result.numResults; i++) {
const item = steam.workshop.getQueryUGCResult(query, i);
const previewURL = steam.workshop.getQueryUGCPreviewURL(query, i);
if (item && previewURL) {
console.log(`${item.title}`);
console.log(`Preview: ${previewURL}`);
// Download preview image for your UI
// await downloadImage(previewURL, `./previews/${item.publishedFileId}.jpg`);
}
}
steam.workshop.releaseQueryUGCRequest(query);
}Notes:
- Returns null if no preview available or index out of range
- URL is hosted on Steam's CDN
- Image is typically 512x512 or 256x256
- Useful for displaying item previews in custom UI
Get the metadata string for a specific query result.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetQueryUGCMetadata()- Get metadata
Parameters:
-
queryHandle: UGCQueryHandle- Query handle from completed query -
index: number- Index of result (0-based)
Returns: string | null - Metadata string, or null if none
Example:
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
for (let i = 0; i < result.numResults; i++) {
const item = steam.workshop.getQueryUGCResult(query, i);
const metadata = steam.workshop.getQueryUGCMetadata(query, i);
if (item) {
console.log(`\n${item.title}`);
if (metadata) {
// Parse JSON metadata (if your mod uses it)
try {
const data = JSON.parse(metadata);
console.log(`Version: ${data.version}`);
console.log(`Author: ${data.author}`);
console.log(`Compatible: ${data.gameVersion}`);
} catch {
console.log(`Metadata: ${metadata}`);
}
} else {
console.log('No metadata available');
}
}
}
steam.workshop.releaseQueryUGCRequest(query);
}Notes:
- Returns null if no metadata or index out of range
- Max size: 5000 characters
- Can store JSON, XML, or any text format
- Set via
SetItemMetadata()during item update - Useful for version info, compatibility data, mod settings
Release a query handle and free associated memory.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_ReleaseQueryUGCRequest()- Release query
Parameters:
-
queryHandle: UGCQueryHandle- Query handle to release
Returns: boolean - true if released successfully
Example:
const query = steam.workshop.createQueryAllUGCRequest(/*...*/);
const result = await steam.workshop.sendQueryUGCRequest(query);
// Process results...
// Always release when done
steam.workshop.releaseQueryUGCRequest(query);Notes:
- Always call this after processing query results
- Prevents memory leaks
- Required for every successful query
Get the content descriptors (mature content labels) attached to a specific query result.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetQueryUGCContentDescriptors()- Get content descriptors for a result
Parameters:
-
queryHandle: UGCQueryHandle- Query handle from a completed query -
index: number- Index of result (0-based)
Returns: EUGCContentDescriptorID[] - Array of content descriptor IDs (empty if none)
Type:
enum EUGCContentDescriptorID {
NudityOrSexualContent = 1,
FrequentViolenceOrGore = 2,
AdultOnlySexualContent = 3,
GratuitousSexualContent = 4,
AnyMatureContent = 5,
}Example:
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (result) {
for (let i = 0; i < result.numResults; i++) {
const descriptors = steam.workshop.getQueryUGCContentDescriptors(query, i);
if (descriptors.length > 0) {
console.log(`Item ${i} has mature content labels:`, descriptors);
if (descriptors.includes(EUGCContentDescriptorID.FrequentViolenceOrGore)) {
console.log(' - Contains frequent violence or gore');
}
}
}
steam.workshop.releaseQueryUGCRequest(query);
}Notes:
- Returns an empty array if the item has no content descriptors
- Use in combination with
getUserContentDescriptorPreferences()to filter items
Functions for creating and updating your own Workshop items.
Create a new Workshop item and wait for the result.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_CreateItem()- Create Workshop item
Parameters:
-
consumerAppId: number- App ID that will consume this item -
fileType: EWorkshopFileType- Type of Workshop file
Returns: Promise<PublishedFileId | null> - New item ID or null if failed
Type:
enum EWorkshopFileType {
Community = 0, // Standard community item
Microtransaction = 1, // Microtransaction item
Collection = 2, // Collection of items
Art = 3, // Artwork
Video = 4, // Video
Screenshot = 5, // Screenshot
Game = 6, // Managed by game
Software = 7, // Software
Concept = 8, // Concept
WebGuide = 9, // Web guide
IntegratedGuide = 10, // Integrated guide
Merch = 11, // Merchandise
ControllerBinding = 12, // Controller binding
SteamworksAccessInvite = 13,
SteamVideo = 14, // Steam video
GameManagedItem = 15 // Game-managed item
}Example:
import { EWorkshopFileType } from 'steamworks-ffi-node';
// Create a new Workshop item
const itemId = await steam.workshop.createItem(
480, // Spacewar
EWorkshopFileType.Community
);
if (itemId) {
console.log(`Created Workshop item: ${itemId}`);
console.log(`URL: https://steamcommunity.com/sharedfiles/filedetails/?id=${itemId}`);
// Now update it with content
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
steam.workshop.setItemTitle(updateHandle, 'My Awesome Mod');
steam.workshop.setItemDescription(updateHandle, 'A cool new mod!');
await steam.workshop.submitItemUpdate(updateHandle, 'Initial release');
} else {
console.error('Failed to create item');
}Notes:
- User must accept Workshop Legal Agreement first
- Check console for legal agreement URL if creation fails
- Item is created but has no content until updated
- Waits up to 60 seconds for Steam server response
Start an update for a Workshop item.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_StartItemUpdate()- Begin item update
Parameters:
-
consumerAppId: number- App ID -
publishedFileId: PublishedFileId- Workshop item ID to update
Returns: UGCUpdateHandle - Update handle for use with set* functions
Example:
const itemId = BigInt('123456789');
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
// Now use set* functions to modify properties
steam.workshop.setItemTitle(updateHandle, 'Updated Title');
steam.workshop.setItemDescription(updateHandle, 'Updated description');
// Submit changes
await steam.workshop.submitItemUpdate(updateHandle, 'Updated description');Set the title of a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetItemTitle()- Set title
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
title: string- New title (max 129 characters)
Returns: boolean - true if set successfully
Example:
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
const success = steam.workshop.setItemTitle(updateHandle, 'My Epic Mod v2.0');
if (success) {
console.log('Title updated');
}Set the description of a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetItemDescription()- Set description
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
description: string- New description (max 8000 characters)
Returns: boolean - true if set successfully
Example:
const description = `
This mod adds:
- New weapons
- New maps
- Bug fixes
Installation:
1. Subscribe to this item
2. Launch the game
3. Enable the mod in settings
Changelog:
v2.0 - Added new weapons
v1.0 - Initial release
`;
steam.workshop.setItemDescription(updateHandle, description);Set the visibility of a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetItemVisibility()- Set visibility
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
visibility: ERemoteStoragePublishedFileVisibility- Visibility setting
Returns: boolean - true if set successfully
Type:
enum ERemoteStoragePublishedFileVisibility {
Public = 0, // Everyone can see
FriendsOnly = 1, // Only friends can see
Private = 2, // Only you can see
Unlisted = 3 // Anyone with link can see
}Example:
import { ERemoteStoragePublishedFileVisibility } from 'steamworks-ffi-node';
// Set to public for everyone to see
steam.workshop.setItemVisibility(
updateHandle,
ERemoteStoragePublishedFileVisibility.Public
);
// Set to private for testing
steam.workshop.setItemVisibility(
updateHandle,
ERemoteStoragePublishedFileVisibility.Private
);Set the tags for a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetItemTags()- Set item tags
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
tags: string[]- Array of tag strings (replaces all existing tags) -
allowAdminTags?: boolean- Allow admin-only tags (default:false)
Returns: boolean - true if set successfully
Example:
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
// Set tags for the item
const success = steam.workshop.setItemTags(updateHandle, ['multiplayer', 'map', 'competitive']);
if (success) {
console.log('Tags updated');
}
// Clear all tags
steam.workshop.setItemTags(updateHandle, []);Notes:
- Tags replace any previously set tags — always pass the full desired list
- Tag strings longer than 255 characters are truncated by Steam
- An empty array clears all tags from the item
- Call
submitItemUpdate()after setting tags to apply the changes
Set the content folder for a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetItemContent()- Set content folder
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
contentFolder: string- Path to folder containing item files
Returns: boolean - true if set successfully
Example:
const contentPath = '/path/to/my/mod/files';
const success = steam.workshop.setItemContent(updateHandle, contentPath);
if (success) {
console.log('Content folder set');
console.log('All files in folder will be uploaded');
}Notes:
- All files in the folder will be uploaded
- Folder structure is preserved
- Use relative paths in your mod code
- Maximum 1GB per item
Set the preview image for a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetItemPreview()- Set preview image
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
previewFile: string- Path to preview image file
Returns: boolean - true if set successfully
Example:
const previewPath = '/path/to/preview.jpg';
const success = steam.workshop.setItemPreview(updateHandle, previewPath);
if (success) {
console.log('Preview image set');
}Notes:
- Image must be under 1MB
- Supported formats: JPG, PNG, GIF
- Recommended size: 512x512 or 256x256
- Shown in Workshop browser
Add a mature content descriptor to a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_AddContentDescriptor()- Add content descriptor
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
descriptor: EUGCContentDescriptorID- Content descriptor to add
Returns: boolean - true if added successfully
Example:
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
steam.workshop.addContentDescriptor(
updateHandle,
EUGCContentDescriptorID.FrequentViolenceOrGore
);
await steam.workshop.submitItemUpdate(updateHandle, 'Added mature content label');Notes:
- Call
submitItemUpdate()to apply the change - Multiple descriptors can be added per item
- See
EUGCContentDescriptorIDfor all available values
Remove a mature content descriptor from a Workshop item being updated.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_RemoveContentDescriptor()- Remove content descriptor
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
descriptor: EUGCContentDescriptorID- Content descriptor to remove
Returns: boolean - true if removed successfully
Example:
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
steam.workshop.removeContentDescriptor(
updateHandle,
EUGCContentDescriptorID.FrequentViolenceOrGore
);
await steam.workshop.submitItemUpdate(updateHandle, 'Removed mature content label');Notes:
- Call
submitItemUpdate()to apply the change - Has no effect if the descriptor was not previously set
Submit an item update to Steam Workshop and wait for completion.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SubmitItemUpdate()- Submit update
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate() -
changeNote: string- Description of changes (shown in update history)
Returns: Promise<boolean> - true if update submitted successfully
Example:
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
// Set all properties
steam.workshop.setItemTitle(updateHandle, 'My Mod v2.0');
steam.workshop.setItemDescription(updateHandle, 'Updated with new features!');
steam.workshop.setItemContent(updateHandle, '/path/to/mod/files');
steam.workshop.setItemPreview(updateHandle, '/path/to/preview.jpg');
// Submit with changelog
const success = await steam.workshop.submitItemUpdate(
updateHandle,
'v2.0 - Added new weapons, fixed bugs, improved performance'
);
if (success) {
console.log('Workshop item updated successfully!');
// Track upload progress
const checkProgress = setInterval(() => {
steam.runCallbacks();
const progress = steam.workshop.getItemUpdateProgress(updateHandle);
console.log(`Status: ${EItemUpdateStatus[progress.status]}`);
console.log(`Progress: ${progress.percentComplete.toFixed(1)}%`);
if (progress.status === EItemUpdateStatus.Invalid) {
console.log('Upload complete!');
clearInterval(checkProgress);
}
}, 1000);
}Notes:
- Waits up to 30 seconds for initial submission
- Use
getItemUpdateProgress()to track upload - Change note is visible to subscribers
- Large files may take time to upload
Get the progress of an item update submission.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetItemUpdateProgress()- Get progress
Parameters:
-
updateHandle: UGCUpdateHandle- Handle fromstartItemUpdate()
Returns: ItemUpdateProgress - Update progress info
Type:
interface ItemUpdateProgress {
status: EItemUpdateStatus; // Current status
bytesProcessed: bigint; // Bytes uploaded
bytesTotal: bigint; // Total bytes
percentComplete: number; // Percentage (0-100)
}
enum EItemUpdateStatus {
Invalid = 0, // No update in progress
PreparingConfig = 1, // Preparing configuration
PreparingContent = 2, // Preparing content files
UploadingContent = 3, // Uploading to Steam
UploadingPreviewFile = 4, // Uploading preview
CommittingChanges = 5 // Committing to database
}Example:
import { EItemUpdateStatus } from 'steamworks-ffi-node';
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
// ... set properties ...
await steam.workshop.submitItemUpdate(updateHandle, 'Changelog');
// Monitor upload progress
const monitor = setInterval(() => {
steam.runCallbacks();
const progress = steam.workshop.getItemUpdateProgress(updateHandle);
const statusName = EItemUpdateStatus[progress.status];
const percent = progress.percentComplete.toFixed(1);
const mb = Number(progress.bytesProcessed) / 1024 / 1024;
const totalMb = Number(progress.bytesTotal) / 1024 / 1024;
console.log(`[${statusName}] ${percent}% - ${mb.toFixed(2)}/${totalMb.toFixed(2)} MB`);
if (progress.status === EItemUpdateStatus.Invalid) {
console.log('Upload finished!');
clearInterval(monitor);
}
}, 1000);Functions for voting on Workshop items and managing favorites.
Vote on a Workshop item.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_SetUserItemVote()- Set vote
Parameters:
-
publishedFileId: PublishedFileId- Workshop item ID -
voteUp: boolean-trueto vote up,falseto vote down
Returns: Promise<boolean> - true if vote registered successfully
Example:
const itemId = BigInt('123456789');
// Vote up
const success = await steam.workshop.setUserItemVote(itemId, true);
if (success) {
console.log('Voted up!');
}
// Vote down
await steam.workshop.setUserItemVote(itemId, false);Notes:
- Can change vote at any time
- Waits up to 5 seconds for Steam server response
Get the user's vote on a Workshop item.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetUserItemVote()- Get vote status
Parameters:
-
publishedFileId: PublishedFileId- Workshop item ID
Returns: Promise<{ votedUp: boolean; votedDown: boolean; voteSkipped: boolean } | null>
Example:
const itemId = BigInt('123456789');
const vote = await steam.workshop.getUserItemVote(itemId);
if (vote) {
if (vote.votedUp) {
console.log('You voted up 👍');
} else if (vote.votedDown) {
console.log('You voted down 👎');
} else if (vote.voteSkipped) {
console.log('You skipped voting');
} else {
console.log('You have not voted');
}
}Notes:
- Returns current vote status
- Waits up to 5 seconds for Steam server response
Add a Workshop item to the user's favorites.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_AddItemToFavorites()- Add to favorites
Parameters:
-
appId: number- App ID -
publishedFileId: PublishedFileId- Workshop item ID
Returns: Promise<boolean> - true if added successfully
Example:
const itemId = BigInt('123456789');
const success = await steam.workshop.addItemToFavorites(480, itemId);
if (success) {
console.log('Added to favorites! ⭐');
}Notes:
- Favorites are per-app
- Waits up to 5 seconds for Steam server response
Remove a Workshop item from the user's favorites.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_RemoveItemFromFavorites()- Remove from favorites
Parameters:
-
appId: number- App ID -
publishedFileId: PublishedFileId- Workshop item ID
Returns: Promise<boolean> - true if removed successfully
Example:
const itemId = BigInt('123456789');
const success = await steam.workshop.removeItemFromFavorites(480, itemId);
if (success) {
console.log('Removed from favorites');
}Notes:
- Waits up to 5 seconds for Steam server response
Functions for permanently deleting Workshop items you created.
Permanently delete a Workshop item that you created. This action cannot be undone.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_DeleteItem()- Delete Workshop item
Parameters:
-
publishedFileId: PublishedFileId- Workshop item ID to delete
Returns: Promise<boolean> - true if deleted successfully, false otherwise
Example:
const itemId = BigInt('123456789');
const success = await steam.workshop.deleteItem(itemId);
if (success) {
console.log('Workshop item deleted permanently');
} else {
console.log('Failed to delete - you may not be the owner');
}Notes:
- You can only delete items that you created
- This action is permanent and cannot be undone
- All subscribers will lose access to the item
- Waits up to 10 seconds for Steam server response
Functions for managing and reading mature content labels on Workshop items.
Gets the mature content descriptor preferences configured by the current user in their Steam account settings.
Steamworks SDK Functions:
-
SteamAPI_ISteamUGC_GetUserContentDescriptorPreferences()- Get user's content preferences
Parameters: None
Returns: EUGCContentDescriptorID[] - Array of descriptor IDs the user has enabled (empty if none)
Type:
enum EUGCContentDescriptorID {
NudityOrSexualContent = 1,
FrequentViolenceOrGore = 2,
AdultOnlySexualContent = 3,
GratuitousSexualContent = 4,
AnyMatureContent = 5,
}Example:
const prefs = steam.workshop.getUserContentDescriptorPreferences();
if (prefs.length === 0) {
console.log('User has no mature content enabled');
} else {
console.log('User content preferences:');
for (const pref of prefs) {
switch (pref) {
case EUGCContentDescriptorID.NudityOrSexualContent:
console.log(' - Nudity or sexual content');
break;
case EUGCContentDescriptorID.FrequentViolenceOrGore:
console.log(' - Frequent violence or gore');
break;
case EUGCContentDescriptorID.AdultOnlySexualContent:
console.log(' - Adult-only sexual content');
break;
case EUGCContentDescriptorID.GratuitousSexualContent:
console.log(' - Gratuitous sexual content');
break;
case EUGCContentDescriptorID.AnyMatureContent:
console.log(' - Any mature content');
break;
}
}
}Notes:
- Reflects the user's Steam account privacy/content settings
- Use to decide whether to display mature Workshop items
- Combine with
getQueryUGCContentDescriptors()to filter query results
Before using Workshop, configure it in the Steamworks Partner portal.
Steps:
- Log in to Steamworks Partner
- Select your app
- Go to Workshop → Configuration
- Enable Workshop for your app
- Configure:
- Workshop Type: Community or Microtransaction
- Allowed File Types: What types of content are allowed
- Max File Size: Maximum size per item (up to 1GB)
- Preview Image Requirements
- Content Folder Structure
- Set up Workshop tags and categories
- Publish changes
Important:
- Workshop must be enabled before items can be created
- Test with Spacewar (AppID 480) first
- Review Workshop Guidelines
Complete type definitions for Workshop functionality.
// Enums
enum EWorkshopFileType {
Community = 0,
Microtransaction = 1,
Collection = 2,
Art = 3,
Video = 4,
Screenshot = 5,
Game = 6,
Software = 7,
Concept = 8,
WebGuide = 9,
IntegratedGuide = 10,
Merch = 11,
ControllerBinding = 12,
SteamworksAccessInvite = 13,
SteamVideo = 14,
GameManagedItem = 15
}
enum EItemState {
None = 0,
Subscribed = 1,
LegacyItem = 2,
Installed = 4,
NeedsUpdate = 8,
Downloading = 16,
DownloadPending = 32
}
enum ERemoteStoragePublishedFileVisibility {
Public = 0,
FriendsOnly = 1,
Private = 2,
Unlisted = 3
}
enum EUGCQuery {
RankedByVote = 0,
RankedByPublicationDate = 1,
AcceptedForGameRankedByAcceptanceDate = 2,
RankedByTrend = 3,
FavoritedByFriendsRankedByPublicationDate = 4,
CreatedByFriendsRankedByPublicationDate = 5,
RankedByNumTimesReported = 6,
CreatedByFollowedUsersRankedByPublicationDate = 7,
NotYetRated = 8,
RankedByTotalVotesAsc = 9,
RankedByVotesUp = 10,
RankedByTextSearch = 11,
RankedByTotalUniqueSubscriptions = 12,
RankedByPlaytimeTrend = 13,
RankedByTotalPlaytime = 14,
RankedByAveragePlaytimeTrend = 15,
RankedByLifetimeAveragePlaytime = 16,
RankedByPlaytimeSessionsTrend = 17,
RankedByLifetimePlaytimeSessions = 18,
RankedByLastUpdatedDate = 19
}
enum EUGCMatchingUGCType {
Items = 0,
ItemsMtx = 1,
ItemsReadyToUse = 2,
Collections = 3,
Artwork = 4,
Videos = 5,
Screenshots = 6,
AllGuides = 7,
WebGuides = 8,
IntegratedGuides = 9,
UsableInGame = 10,
ControllerBindings = 11,
GameManagedItems = 12
}
enum EItemUpdateStatus {
Invalid = 0,
PreparingConfig = 1,
PreparingContent = 2,
UploadingContent = 3,
UploadingPreviewFile = 4,
CommittingChanges = 5
}
enum EUGCContentDescriptorID {
NudityOrSexualContent = 1,
FrequentViolenceOrGore = 2,
AdultOnlySexualContent = 3,
GratuitousSexualContent = 4,
AnyMatureContent = 5,
}
// Interfaces
interface ItemInstallInfo {
sizeOnDisk: bigint;
folder: string;
timestamp: number;
}
interface ItemDownloadInfo {
bytesDownloaded: bigint;
bytesTotal: bigint;
percentComplete: number;
}
interface ItemUpdateProgress {
status: EItemUpdateStatus;
bytesProcessed: bigint;
bytesTotal: bigint;
percentComplete: number;
}
interface WorkshopItem {
publishedFileId: bigint;
result: number;
fileType: number;
creatorAppID: number;
consumerAppID: number;
title: string;
description: string;
steamIDOwner: bigint;
timeCreated: number;
timeUpdated: number;
timeAddedToUserList: number;
visibility: number;
banned: boolean;
acceptedForUse: boolean;
tagsTruncated: boolean;
tags: string[];
file: bigint;
previewFile: bigint;
fileName: string;
fileSize: number;
previewFileSize: number;
url: string;
votesUp: number;
score: number;
numChildren: number;
totalFilesSize: bigint;
}
// Type Aliases
type PublishedFileId = bigint;
type UGCQueryHandle = bigint;
type UGCUpdateHandle = bigint;import SteamworksSDK, { EItemState } from 'steamworks-ffi-node';
const steam = SteamworksSDK.getInstance();
async function subscribeAndDownload() {
steam.init({ appId: 480 });
const itemId = BigInt('123456789');
// Subscribe
console.log('Subscribing to item...');
const subscribed = await steam.workshop.subscribeItem(itemId);
if (!subscribed) {
console.error('Failed to subscribe');
return;
}
console.log('✓ Subscribed successfully');
// Monitor download
const monitor = setInterval(() => {
steam.runCallbacks();
const state = steam.workshop.getItemState(itemId);
if (state & EItemState.Downloading) {
const progress = steam.workshop.getItemDownloadInfo(itemId);
if (progress) {
console.log(`Downloading: ${progress.percentComplete.toFixed(1)}%`);
}
} else if (state & EItemState.Installed) {
console.log('✓ Download complete and installed!');
const info = steam.workshop.getItemInstallInfo(itemId);
if (info) {
console.log(`Location: ${info.folder}`);
console.log(`Size: ${(Number(info.sizeOnDisk) / 1024 / 1024).toFixed(2)} MB`);
}
clearInterval(monitor);
}
}, 500);
steam.shutdown();
}
subscribeAndDownload();import SteamworksSDK, {
EUGCQuery,
EUGCMatchingUGCType
} from 'steamworks-ffi-node';
const steam = SteamworksSDK.getInstance();
async function browsePopularMods() {
steam.init({ appId: 480 });
// Query top 50 popular items
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480,
480,
1 // Page 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
if (!result) {
console.error('Query failed');
return;
}
console.log(`\nTop ${result.numResults} Most Popular Mods:`);
console.log('='.repeat(60));
for (let i = 0; i < result.numResults; i++) {
const item = steam.workshop.getQueryUGCResult(query, i);
if (item) {
console.log(`\n${i + 1}. ${item.title}`);
console.log(` ID: ${item.publishedFileId}`);
console.log(` Rating: 👍 ${item.votesUp} | Score: ${item.score.toFixed(2)}`);
console.log(` Size: ${(item.fileSize / 1024 / 1024).toFixed(2)} MB`);
console.log(` Tags: ${item.tags.join(', ')}`);
console.log(` URL: ${item.url}`);
// Check if subscribed
const state = steam.workshop.getItemState(item.publishedFileId);
if (state & EItemState.Subscribed) {
console.log(` Status: ✓ Subscribed`);
}
}
}
steam.workshop.releaseQueryUGCRequest(query);
steam.shutdown();
}
browsePopularMods();import SteamworksSDK, {
EWorkshopFileType,
ERemoteStoragePublishedFileVisibility,
EItemUpdateStatus
} from 'steamworks-ffi-node';
import * as fs from 'fs';
import * as path from 'path';
const steam = SteamworksSDK.getInstance();
async function publishMod() {
steam.init({ appId: 480 });
// Step 1: Create the Workshop item
console.log('Creating Workshop item...');
const itemId = await steam.workshop.createItem(
480,
EWorkshopFileType.Community
);
if (!itemId) {
console.error('Failed to create item');
return;
}
console.log(`✓ Created item: ${itemId}`);
console.log(` URL: https://steamcommunity.com/sharedfiles/filedetails/?id=${itemId}`);
// Step 2: Prepare content
const contentDir = '/tmp/my-mod';
const previewPath = '/tmp/my-mod-preview.jpg';
// Create mod files
if (!fs.existsSync(contentDir)) {
fs.mkdirSync(contentDir, { recursive: true });
}
fs.writeFileSync(
path.join(contentDir, 'mod.json'),
JSON.stringify({
name: 'My Awesome Mod',
version: '1.0',
author: 'Me'
})
);
// Step 3: Update item with content
console.log('\nUpdating item...');
const updateHandle = steam.workshop.startItemUpdate(480, itemId);
steam.workshop.setItemTitle(updateHandle, 'My Awesome Mod');
steam.workshop.setItemDescription(updateHandle, `
# My Awesome Mod
This mod adds amazing features!
## Features
- Cool feature 1
- Awesome feature 2
- Amazing feature 3
## Installation
1. Subscribe to this mod
2. Launch the game
3. Enjoy!
## Changelog
v1.0 - Initial release
`);
steam.workshop.setItemVisibility(
updateHandle,
ERemoteStoragePublishedFileVisibility.Public
);
steam.workshop.setItemContent(updateHandle, contentDir);
steam.workshop.setItemPreview(updateHandle, previewPath);
// Step 4: Submit update
console.log('Submitting update...');
const submitted = await steam.workshop.submitItemUpdate(
updateHandle,
'v1.0 - Initial release'
);
if (!submitted) {
console.error('Failed to submit update');
return;
}
console.log('✓ Update submitted');
// Step 5: Monitor upload progress
console.log('\nUploading...');
const monitor = setInterval(() => {
steam.runCallbacks();
const progress = steam.workshop.getItemUpdateProgress(updateHandle);
const statusName = EItemUpdateStatus[progress.status];
const percent = progress.percentComplete.toFixed(1);
console.log(`[${statusName}] ${percent}%`);
if (progress.status === EItemUpdateStatus.Invalid) {
console.log('\n✓ Upload complete!');
console.log(`View your mod: https://steamcommunity.com/sharedfiles/filedetails/?id=${itemId}`);
clearInterval(monitor);
}
}, 1000);
steam.shutdown();
}
publishMod();import SteamworksSDK, { EItemState } from 'steamworks-ffi-node';
const steam = SteamworksSDK.getInstance();
function listSubscribedMods() {
steam.init({ appId: 480 });
const items = steam.workshop.getSubscribedItems();
console.log(`\nYou have ${items.length} subscribed Workshop items:\n`);
items.forEach((itemId, index) => {
const state = steam.workshop.getItemState(itemId);
const info = steam.workshop.getItemInstallInfo(itemId);
console.log(`${index + 1}. Item ${itemId}`);
// Status
const status = [];
if (state & EItemState.Subscribed) status.push('Subscribed');
if (state & EItemState.Installed) status.push('Installed');
if (state & EItemState.Downloading) status.push('Downloading');
if (state & EItemState.NeedsUpdate) status.push('Needs Update');
console.log(` Status: ${status.join(', ')}`);
// Installation info
if (info) {
console.log(` Location: ${info.folder}`);
console.log(` Size: ${(Number(info.sizeOnDisk) / 1024 / 1024).toFixed(2)} MB`);
console.log(` Installed: ${new Date(info.timestamp * 1000).toLocaleString()}`);
}
// Download progress
if (state & EItemState.Downloading) {
const progress = steam.workshop.getItemDownloadInfo(itemId);
if (progress) {
console.log(` Progress: ${progress.percentComplete.toFixed(1)}%`);
}
}
console.log();
});
steam.shutdown();
}
listSubscribedMods();// ✅ Good: Always release queries
const query = steam.workshop.createQueryAllUGCRequest(/*...*/);
const result = await steam.workshop.sendQueryUGCRequest(query);
// ... process results ...
steam.workshop.releaseQueryUGCRequest(query);
// ❌ Bad: Memory leak
const query = steam.workshop.createQueryAllUGCRequest(/*...*/);
await steam.workshop.sendQueryUGCRequest(query);
// Forgot to release!// ✅ Good: Wait for operations to complete
const success = await steam.workshop.subscribeItem(itemId);
if (success) {
console.log('Subscribed');
}
// ❌ Bad: Not waiting
steam.workshop.subscribeItem(itemId); // Promise ignored// ✅ Good: Process callbacks for download progress
const interval = setInterval(() => {
steam.runCallbacks(); // Process Steam callbacks
const progress = steam.workshop.getItemDownloadInfo(itemId);
// ...
}, 500);
// ❌ Bad: Not calling runCallbacks()
const interval = setInterval(() => {
const progress = steam.workshop.getItemDownloadInfo(itemId);
// Won't update without runCallbacks()
}, 500);// ✅ Good: Check state first
const state = steam.workshop.getItemState(itemId);
if (state & EItemState.Installed) {
const info = steam.workshop.getItemInstallInfo(itemId);
// Use installed files
}
// ❌ Bad: Assume item is installed
const info = steam.workshop.getItemInstallInfo(itemId);
// Might be null!// ✅ Good: Private during development
steam.workshop.setItemVisibility(
updateHandle,
ERemoteStoragePublishedFileVisibility.Private
);
// When ready for release
steam.workshop.setItemVisibility(
updateHandle,
ERemoteStoragePublishedFileVisibility.Public
);Problem: createItem() fails with legal agreement message
Solution:
- Visit the URL shown in the console
- Accept the Workshop Legal Agreement
- Try creating the item again
const itemId = await steam.workshop.createItem(480, EWorkshopFileType.Community);
if (!itemId) {
console.log('Check console for Workshop Legal Agreement URL');
}Problem: Subscribed but item doesn't download
Solutions:
- Check item state for download status
- Force download manually
- Ensure enough disk space
await steam.workshop.subscribeItem(itemId);
// Force download
steam.workshop.downloadItem(itemId, true);Problem: sendQueryUGCRequest() returns 0 results
Solutions:
- Check if Workshop items exist for your app
- Try different query types
- Test with Spacewar (AppID 480) first
// Try ranked by publication date
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByPublicationDate,
EUGCMatchingUGCType.Items,
480, 480, 1
);Problem: submitItemUpdate() takes too long
Solutions:
- Check internet connection
- Reduce content size
- Check Steam server status
- Monitor upload progress
const submitted = await steam.workshop.submitItemUpdate(updateHandle, 'Update');
if (submitted) {
// Monitor progress
const interval = setInterval(() => {
steam.runCallbacks();
const progress = steam.workshop.getItemUpdateProgress(updateHandle);
console.log(`${EItemUpdateStatus[progress.status]}: ${progress.percentComplete}%`);
}, 1000);
}// Test Workshop without a published game
const steam = SteamworksSDK.getInstance();
steam.init({ appId: 480 }); // Spacewar
// Browse existing Workshop items
const query = steam.workshop.createQueryAllUGCRequest(
EUGCQuery.RankedByVote,
EUGCMatchingUGCType.Items,
480, 480, 1
);
const result = await steam.workshop.sendQueryUGCRequest(query);
// ... test subscribing, voting, etc. ...
steam.workshop.releaseQueryUGCRequest(query);
steam.shutdown();- Steam Workshop Documentation
- Workshop Implementation Guide
- ISteamUGC API Reference
- Workshop Best Practices
Questions or Issues? Visit the GitHub Issues page.