steamworks-ffi-node 0.6.10 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +142 -103
- package/dist/internal/SteamInputManager.d.ts +496 -0
- package/dist/internal/SteamInputManager.d.ts.map +1 -0
- package/dist/internal/SteamInputManager.js +993 -0
- package/dist/internal/SteamInputManager.js.map +1 -0
- package/dist/internal/SteamLibraryLoader.d.ts +48 -0
- package/dist/internal/SteamLibraryLoader.d.ts.map +1 -1
- package/dist/internal/SteamLibraryLoader.js +61 -0
- package/dist/internal/SteamLibraryLoader.js.map +1 -1
- package/dist/steam.d.ts +59 -0
- package/dist/steam.d.ts.map +1 -1
- package/dist/steam.js +5 -0
- package/dist/steam.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -1
- package/dist/types/input.d.ts +252 -0
- package/dist/types/input.d.ts.map +1 -0
- package/dist/types/input.js +142 -0
- package/dist/types/input.js.map +1 -0
- package/docs/README.md +29 -1
- package/package.json +8 -2
package/README.md
CHANGED
|
@@ -41,6 +41,8 @@ A TypeScript/JavaScript wrapper for the Steamworks SDK using Koffi FFI, designed
|
|
|
41
41
|
|
|
42
42
|
> 🎉 **NEW: Workshop API** - 29 functions for complete Steam Workshop/UGC integration! [See Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/WORKSHOP_MANAGER.md)
|
|
43
43
|
|
|
44
|
+
> 🎉 **NEW: Input API** - 35+ functions for complete Steam Input (controller) support! [See Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/INPUT_MANAGER.md) ⚠️ Tested with virtual gamepad only
|
|
45
|
+
|
|
44
46
|
## Features
|
|
45
47
|
|
|
46
48
|
- **Core API**: Essential Steam application functions
|
|
@@ -94,6 +96,12 @@ A TypeScript/JavaScript wrapper for the Steamworks SDK using Koffi FFI, designed
|
|
|
94
96
|
- ✅ Query operations (text search, browse, filter Workshop content)
|
|
95
97
|
- ✅ Item creation & update (create, upload, manage your Workshop items)
|
|
96
98
|
- ✅ Voting & favorites (vote on items, manage favorites)
|
|
99
|
+
- **Input API**: Complete Steam Input (controller) support (35+ functions) ⚠️ _Tested with virtual gamepad only_
|
|
100
|
+
- ✅ Controller detection (Xbox, PlayStation, Switch, Steam Controller, Steam Deck)
|
|
101
|
+
- ✅ Action sets and layers (menu controls, gameplay controls, etc.)
|
|
102
|
+
- ✅ Digital actions (buttons) and analog actions (sticks/triggers)
|
|
103
|
+
- ✅ Motion data (gyro, accelerometer for supported controllers)
|
|
104
|
+
- ✅ Haptics (vibration, LED control for DualShock/DualSense)
|
|
97
105
|
- **Steamworks Integration**: Direct FFI calls to Steamworks C++ SDK
|
|
98
106
|
- **Cross-Platform**: Windows, macOS, and Linux support
|
|
99
107
|
- **Easy Setup**: Simple installation with clear SDK setup guide
|
|
@@ -113,23 +121,26 @@ npm install steamworks-ffi-node
|
|
|
113
121
|
### Setup
|
|
114
122
|
|
|
115
123
|
1. **Download Steamworks SDK** (required separately due to licensing):
|
|
124
|
+
|
|
116
125
|
- Visit [Steamworks Partner site](https://partner.steamgames.com/)
|
|
117
126
|
- Download the latest Steamworks SDK
|
|
118
127
|
- Extract and copy `redistributable_bin` folder to your project
|
|
119
128
|
- See [STEAMWORKS_SDK_SETUP.md](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/STEAMWORKS_SDK_SETUP.md) for detailed instructions
|
|
120
129
|
|
|
121
130
|
2. **Create `steam_appid.txt` (optional)** in your project root:
|
|
131
|
+
|
|
122
132
|
```bash
|
|
123
133
|
echo "480" > steam_appid.txt # Use 480 for testing, or your Steam App ID
|
|
124
134
|
```
|
|
125
|
-
|
|
135
|
+
|
|
136
|
+
_Note: You can skip this file and pass the App ID directly to `steam.init(appId)` instead_
|
|
126
137
|
|
|
127
138
|
3. **Make sure Steam is running** and you're logged in
|
|
128
139
|
|
|
129
140
|
### Basic Usage
|
|
130
141
|
|
|
131
142
|
```typescript
|
|
132
|
-
import SteamworksSDK from
|
|
143
|
+
import SteamworksSDK from "steamworks-ffi-node";
|
|
133
144
|
|
|
134
145
|
// Helper to auto-start callback polling
|
|
135
146
|
function startCallbackPolling(steam: SteamworksSDK, interval: number = 1000) {
|
|
@@ -145,48 +156,50 @@ const initialized = steam.init({ appId: 480 }); // Your Steam App ID
|
|
|
145
156
|
if (initialized) {
|
|
146
157
|
// Start callback polling automatically (required for async operations)
|
|
147
158
|
const callbackInterval = startCallbackPolling(steam, 1000);
|
|
148
|
-
|
|
159
|
+
|
|
149
160
|
// Get current Steam language for localization
|
|
150
161
|
const language = steam.getCurrentGameLanguage();
|
|
151
|
-
console.log(
|
|
152
|
-
|
|
162
|
+
console.log("Steam language:", language); // e.g., 'english', 'french', 'german'
|
|
163
|
+
|
|
153
164
|
// Get achievements from Steam servers
|
|
154
165
|
const achievements = await steam.achievements.getAllAchievements();
|
|
155
|
-
console.log(
|
|
156
|
-
|
|
166
|
+
console.log("Steam achievements:", achievements);
|
|
167
|
+
|
|
157
168
|
// Unlock achievement (permanent in Steam!)
|
|
158
|
-
await steam.achievements.unlockAchievement(
|
|
159
|
-
|
|
169
|
+
await steam.achievements.unlockAchievement("ACH_WIN_ONE_GAME");
|
|
170
|
+
|
|
160
171
|
// Check unlock status from Steam
|
|
161
|
-
const isUnlocked = await steam.achievements.isAchievementUnlocked(
|
|
162
|
-
|
|
163
|
-
|
|
172
|
+
const isUnlocked = await steam.achievements.isAchievementUnlocked(
|
|
173
|
+
"ACH_WIN_ONE_GAME"
|
|
174
|
+
);
|
|
175
|
+
console.log("Achievement unlocked:", isUnlocked);
|
|
176
|
+
|
|
164
177
|
// Track user statistics
|
|
165
|
-
const kills = await steam.stats.getStatInt(
|
|
166
|
-
await steam.stats.setStatInt(
|
|
167
|
-
|
|
178
|
+
const kills = (await steam.stats.getStatInt("total_kills")) || 0;
|
|
179
|
+
await steam.stats.setStatInt("total_kills", kills + 1);
|
|
180
|
+
|
|
168
181
|
// Get global statistics
|
|
169
182
|
await steam.stats.requestGlobalStats(7);
|
|
170
|
-
await new Promise(resolve => setTimeout(resolve, 2000));
|
|
183
|
+
await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
171
184
|
steam.runCallbacks();
|
|
172
|
-
const globalKills = await steam.stats.getGlobalStatInt(
|
|
173
|
-
console.log(
|
|
174
|
-
|
|
185
|
+
const globalKills = await steam.stats.getGlobalStatInt("global.total_kills");
|
|
186
|
+
console.log("Total kills worldwide:", globalKills);
|
|
187
|
+
|
|
175
188
|
// Work with leaderboards
|
|
176
189
|
const leaderboard = await steam.leaderboards.findOrCreateLeaderboard(
|
|
177
|
-
|
|
190
|
+
"HighScores",
|
|
178
191
|
1, // Descending (higher is better)
|
|
179
|
-
0
|
|
192
|
+
0 // Numeric display
|
|
180
193
|
);
|
|
181
|
-
|
|
194
|
+
|
|
182
195
|
if (leaderboard) {
|
|
183
196
|
// Upload score
|
|
184
197
|
await steam.leaderboards.uploadLeaderboardScore(
|
|
185
198
|
leaderboard.handle,
|
|
186
199
|
1000,
|
|
187
|
-
1
|
|
200
|
+
1 // Keep best score
|
|
188
201
|
);
|
|
189
|
-
|
|
202
|
+
|
|
190
203
|
// Download top 10 scores
|
|
191
204
|
const topScores = await steam.leaderboards.downloadLeaderboardEntries(
|
|
192
205
|
leaderboard.handle,
|
|
@@ -194,36 +207,38 @@ if (initialized) {
|
|
|
194
207
|
0,
|
|
195
208
|
9
|
|
196
209
|
);
|
|
197
|
-
console.log(
|
|
210
|
+
console.log("Top 10 scores:", topScores);
|
|
198
211
|
}
|
|
199
|
-
|
|
212
|
+
|
|
200
213
|
// Access friends and social features
|
|
201
214
|
const personaName = steam.friends.getPersonaName();
|
|
202
215
|
const friendCount = steam.friends.getFriendCount(4); // All friends
|
|
203
216
|
console.log(`${personaName} has ${friendCount} friends`);
|
|
204
|
-
|
|
217
|
+
|
|
205
218
|
// Get all friends with details
|
|
206
219
|
const allFriends = steam.friends.getAllFriends(4); // All friends
|
|
207
|
-
allFriends.slice(0, 5).forEach(friend => {
|
|
220
|
+
allFriends.slice(0, 5).forEach((friend) => {
|
|
208
221
|
const name = steam.friends.getFriendPersonaName(friend.steamId);
|
|
209
222
|
const state = steam.friends.getFriendPersonaState(friend.steamId);
|
|
210
223
|
const level = steam.friends.getFriendSteamLevel(friend.steamId);
|
|
211
224
|
console.log(`${name}: Level ${level}, Status: ${state}`);
|
|
212
|
-
|
|
225
|
+
|
|
213
226
|
// Get avatar handles
|
|
214
227
|
const smallAvatar = steam.friends.getSmallFriendAvatar(friend.steamId);
|
|
215
228
|
const mediumAvatar = steam.friends.getMediumFriendAvatar(friend.steamId);
|
|
216
229
|
if (smallAvatar > 0) {
|
|
217
|
-
console.log(
|
|
230
|
+
console.log(
|
|
231
|
+
` Avatar handles: small=${smallAvatar}, medium=${mediumAvatar}`
|
|
232
|
+
);
|
|
218
233
|
}
|
|
219
|
-
|
|
234
|
+
|
|
220
235
|
// Check if playing a game
|
|
221
236
|
const gameInfo = steam.friends.getFriendGamePlayed(friend.steamId);
|
|
222
237
|
if (gameInfo) {
|
|
223
238
|
console.log(` Playing: App ${gameInfo.gameId}`);
|
|
224
239
|
}
|
|
225
240
|
});
|
|
226
|
-
|
|
241
|
+
|
|
227
242
|
// Check friend groups (tags)
|
|
228
243
|
const groupCount = steam.friends.getFriendsGroupCount();
|
|
229
244
|
if (groupCount > 0) {
|
|
@@ -232,7 +247,7 @@ if (initialized) {
|
|
|
232
247
|
const members = steam.friends.getFriendsGroupMembersList(groupId);
|
|
233
248
|
console.log(`Group "${groupName}" has ${members.length} members`);
|
|
234
249
|
}
|
|
235
|
-
|
|
250
|
+
|
|
236
251
|
// Check recently played with
|
|
237
252
|
const coplayCount = steam.friends.getCoplayFriendCount();
|
|
238
253
|
if (coplayCount > 0) {
|
|
@@ -241,107 +256,123 @@ if (initialized) {
|
|
|
241
256
|
const coplayTime = steam.friends.getFriendCoplayTime(recentPlayer);
|
|
242
257
|
console.log(`Recently played with ${playerName}`);
|
|
243
258
|
}
|
|
244
|
-
|
|
259
|
+
|
|
245
260
|
// Set rich presence for custom status
|
|
246
|
-
steam.richPresence.setRichPresence(
|
|
247
|
-
steam.richPresence.setRichPresence(
|
|
248
|
-
|
|
261
|
+
steam.richPresence.setRichPresence("status", "In Main Menu");
|
|
262
|
+
steam.richPresence.setRichPresence("connect", "+connect server:27015");
|
|
263
|
+
|
|
249
264
|
// Open Steam overlay
|
|
250
|
-
steam.overlay.activateGameOverlay(
|
|
251
|
-
steam.overlay.activateGameOverlayToWebPage(
|
|
252
|
-
|
|
265
|
+
steam.overlay.activateGameOverlay("Friends"); // Open friends list
|
|
266
|
+
steam.overlay.activateGameOverlayToWebPage("https://example.com/wiki"); // Open wiki
|
|
267
|
+
|
|
253
268
|
// Steam Cloud storage operations
|
|
254
|
-
const saveData = { level: 5, score: 1000, inventory: [
|
|
269
|
+
const saveData = { level: 5, score: 1000, inventory: ["sword", "shield"] };
|
|
255
270
|
const buffer = Buffer.from(JSON.stringify(saveData));
|
|
256
|
-
|
|
271
|
+
|
|
257
272
|
// Write save file to Steam Cloud
|
|
258
|
-
const written = steam.cloud.fileWrite(
|
|
273
|
+
const written = steam.cloud.fileWrite("savegame.json", buffer);
|
|
259
274
|
if (written) {
|
|
260
|
-
console.log(
|
|
275
|
+
console.log("✅ Save uploaded to Steam Cloud");
|
|
261
276
|
}
|
|
262
|
-
|
|
277
|
+
|
|
263
278
|
// Check cloud quota
|
|
264
279
|
const quota = steam.cloud.getQuota();
|
|
265
|
-
console.log(
|
|
266
|
-
|
|
280
|
+
console.log(
|
|
281
|
+
`Cloud storage: ${quota.usedBytes}/${
|
|
282
|
+
quota.totalBytes
|
|
283
|
+
} bytes (${quota.percentUsed.toFixed(2)}%)`
|
|
284
|
+
);
|
|
285
|
+
|
|
267
286
|
// Read save file from Steam Cloud
|
|
268
|
-
if (steam.cloud.fileExists(
|
|
269
|
-
const result = steam.cloud.fileRead(
|
|
287
|
+
if (steam.cloud.fileExists("savegame.json")) {
|
|
288
|
+
const result = steam.cloud.fileRead("savegame.json");
|
|
270
289
|
if (result.success && result.data) {
|
|
271
290
|
const loadedSave = JSON.parse(result.data.toString());
|
|
272
|
-
console.log(
|
|
291
|
+
console.log(
|
|
292
|
+
`Loaded save: Level ${loadedSave.level}, Score ${loadedSave.score}`
|
|
293
|
+
);
|
|
273
294
|
}
|
|
274
295
|
}
|
|
275
|
-
|
|
296
|
+
|
|
276
297
|
// List all cloud files
|
|
277
298
|
const cloudFiles = steam.cloud.getAllFiles();
|
|
278
299
|
console.log(`Steam Cloud contains ${cloudFiles.length} files:`);
|
|
279
|
-
cloudFiles.forEach(file => {
|
|
300
|
+
cloudFiles.forEach((file) => {
|
|
280
301
|
const kb = (file.size / 1024).toFixed(2);
|
|
281
|
-
const status = file.persisted ?
|
|
302
|
+
const status = file.persisted ? "☁️" : "⏳";
|
|
282
303
|
console.log(`${status} ${file.name} - ${kb} KB`);
|
|
283
304
|
});
|
|
284
|
-
|
|
305
|
+
|
|
285
306
|
// Steam Workshop operations
|
|
286
307
|
// Subscribe to a Workshop item
|
|
287
308
|
const subscribeResult = await steam.workshop.subscribeItem(123456789n);
|
|
288
309
|
if (subscribeResult.success) {
|
|
289
|
-
console.log(
|
|
310
|
+
console.log("✅ Subscribed to Workshop item");
|
|
290
311
|
}
|
|
291
|
-
|
|
312
|
+
|
|
292
313
|
// Get all subscribed items
|
|
293
314
|
const subscribedItems = steam.workshop.getSubscribedItems();
|
|
294
315
|
console.log(`Subscribed to ${subscribedItems.length} Workshop items`);
|
|
295
|
-
|
|
316
|
+
|
|
296
317
|
// Query Workshop items with text search
|
|
297
318
|
const query = steam.workshop.createQueryAllUGCRequest(
|
|
298
|
-
11,
|
|
299
|
-
0,
|
|
300
|
-
480,
|
|
301
|
-
480,
|
|
302
|
-
1
|
|
319
|
+
11, // RankedByTextSearch - for text search queries
|
|
320
|
+
0, // Items
|
|
321
|
+
480, // Creator App ID
|
|
322
|
+
480, // Consumer App ID
|
|
323
|
+
1 // Page 1
|
|
303
324
|
);
|
|
304
|
-
|
|
325
|
+
|
|
305
326
|
if (query) {
|
|
306
327
|
// Set search text to filter results
|
|
307
|
-
steam.workshop.setSearchText(query,
|
|
308
|
-
|
|
328
|
+
steam.workshop.setSearchText(query, "map");
|
|
329
|
+
|
|
309
330
|
const queryResult = await steam.workshop.sendQueryUGCRequest(query);
|
|
310
331
|
if (queryResult) {
|
|
311
|
-
console.log(
|
|
312
|
-
|
|
332
|
+
console.log(
|
|
333
|
+
`Found ${queryResult.numResults} Workshop items matching "map"`
|
|
334
|
+
);
|
|
335
|
+
|
|
313
336
|
// Get details for each item
|
|
314
337
|
for (let i = 0; i < queryResult.numResults; i++) {
|
|
315
338
|
const details = steam.workshop.getQueryUGCResult(query, i);
|
|
316
339
|
if (details) {
|
|
317
340
|
console.log(`📦 ${details.title} by ${details.steamIDOwner}`);
|
|
318
|
-
console.log(
|
|
341
|
+
console.log(
|
|
342
|
+
` Score: ${details.score}, Downloads: ${details.numUniqueSubscriptions}`
|
|
343
|
+
);
|
|
319
344
|
}
|
|
320
345
|
}
|
|
321
346
|
}
|
|
322
347
|
steam.workshop.releaseQueryUGCRequest(query);
|
|
323
348
|
}
|
|
324
|
-
|
|
349
|
+
|
|
325
350
|
// Check download progress for subscribed items
|
|
326
|
-
subscribedItems.forEach(itemId => {
|
|
351
|
+
subscribedItems.forEach((itemId) => {
|
|
327
352
|
const state = steam.workshop.getItemState(itemId);
|
|
328
353
|
const stateFlags = [];
|
|
329
|
-
if (state & 1) stateFlags.push(
|
|
330
|
-
if (state & 4) stateFlags.push(
|
|
331
|
-
if (state & 8) stateFlags.push(
|
|
332
|
-
if (state & 16) stateFlags.push(
|
|
333
|
-
|
|
334
|
-
console.log(`Item ${itemId}: ${stateFlags.join(
|
|
335
|
-
|
|
336
|
-
if (state & 16) {
|
|
354
|
+
if (state & 1) stateFlags.push("Subscribed");
|
|
355
|
+
if (state & 4) stateFlags.push("Needs Update");
|
|
356
|
+
if (state & 8) stateFlags.push("Installed");
|
|
357
|
+
if (state & 16) stateFlags.push("Downloading");
|
|
358
|
+
|
|
359
|
+
console.log(`Item ${itemId}: ${stateFlags.join(", ")}`);
|
|
360
|
+
|
|
361
|
+
if (state & 16) {
|
|
362
|
+
// If downloading
|
|
337
363
|
const progress = steam.workshop.getItemDownloadInfo(itemId);
|
|
338
364
|
if (progress) {
|
|
339
|
-
const percent = (progress.downloaded / progress.total * 100).toFixed(
|
|
340
|
-
|
|
365
|
+
const percent = ((progress.downloaded / progress.total) * 100).toFixed(
|
|
366
|
+
1
|
|
367
|
+
);
|
|
368
|
+
console.log(
|
|
369
|
+
` Download: ${percent}% (${progress.downloaded}/${progress.total} bytes)`
|
|
370
|
+
);
|
|
341
371
|
}
|
|
342
372
|
}
|
|
343
|
-
|
|
344
|
-
if (state & 8) {
|
|
373
|
+
|
|
374
|
+
if (state & 8) {
|
|
375
|
+
// If installed
|
|
345
376
|
const info = steam.workshop.getItemInstallInfo(itemId);
|
|
346
377
|
if (info.success) {
|
|
347
378
|
console.log(` Installed at: ${info.folder}`);
|
|
@@ -359,13 +390,13 @@ steam.shutdown();
|
|
|
359
390
|
|
|
360
391
|
```javascript
|
|
361
392
|
// Option 1: ESM Named import
|
|
362
|
-
import { SteamworksSDK } from
|
|
393
|
+
import { SteamworksSDK } from "steamworks-ffi-node";
|
|
363
394
|
|
|
364
395
|
// Option 2: CommonJs named import (recommended - no .default needed)
|
|
365
|
-
const { SteamworksSDK } = require(
|
|
396
|
+
const { SteamworksSDK } = require("steamworks-ffi-node");
|
|
366
397
|
|
|
367
398
|
// Option 3: CommonJs default named import (also works)
|
|
368
|
-
const SteamworksSDK = require(
|
|
399
|
+
const SteamworksSDK = require("steamworks-ffi-node").default;
|
|
369
400
|
|
|
370
401
|
// Helper to auto-start callback polling
|
|
371
402
|
function startCallbackPolling(steam, interval = 1000) {
|
|
@@ -376,24 +407,24 @@ function startCallbackPolling(steam, interval = 1000) {
|
|
|
376
407
|
|
|
377
408
|
async function example() {
|
|
378
409
|
const steam = SteamworksSDK.getInstance();
|
|
379
|
-
|
|
410
|
+
|
|
380
411
|
if (steam.init({ appId: 480 })) {
|
|
381
412
|
// Start callback polling automatically
|
|
382
413
|
const callbackInterval = startCallbackPolling(steam, 1000);
|
|
383
|
-
|
|
414
|
+
|
|
384
415
|
const achievements = await steam.achievements.getAllAchievements();
|
|
385
416
|
console.log(`Found ${achievements.length} achievements`);
|
|
386
|
-
|
|
417
|
+
|
|
387
418
|
// Unlock first locked achievement
|
|
388
|
-
const locked = achievements.find(a => !a.unlocked);
|
|
419
|
+
const locked = achievements.find((a) => !a.unlocked);
|
|
389
420
|
if (locked) {
|
|
390
421
|
await steam.achievements.unlockAchievement(locked.apiName);
|
|
391
422
|
}
|
|
392
|
-
|
|
423
|
+
|
|
393
424
|
// Cleanup
|
|
394
425
|
clearInterval(callbackInterval);
|
|
395
426
|
}
|
|
396
|
-
|
|
427
|
+
|
|
397
428
|
steam.shutdown();
|
|
398
429
|
}
|
|
399
430
|
|
|
@@ -403,6 +434,7 @@ example();
|
|
|
403
434
|
### Testing with Spacewar
|
|
404
435
|
|
|
405
436
|
For immediate testing, use Spacewar (App ID 480):
|
|
437
|
+
|
|
406
438
|
- Free Steam app for testing Steamworks features
|
|
407
439
|
- Add to Steam library: `steam://install/480` or search "Spacewar" in Steam
|
|
408
440
|
- Launch it once, then you can test with App ID 480
|
|
@@ -414,14 +446,16 @@ Complete documentation for all APIs is available in the [docs folder](https://gi
|
|
|
414
446
|
➡️ **[View Complete Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/README.md)**
|
|
415
447
|
|
|
416
448
|
### API Guides:
|
|
449
|
+
|
|
417
450
|
- **[Achievement Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/ACHIEVEMENT_MANAGER.md)** - Complete achievement system (20 functions)
|
|
418
451
|
- **[Stats Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/STATS_MANAGER.md)** - User and global statistics (14 functions)
|
|
419
452
|
- **[Leaderboard Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/LEADERBOARD_MANAGER.md)** - Leaderboard operations (7 functions)
|
|
420
453
|
- **[Friends Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/FRIENDS_MANAGER.md)** - Friends and social features (22 functions)
|
|
421
454
|
- **[Rich Presence Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/RICH_PRESENCE_MANAGER.md)** - Custom status display and join functionality (6 functions)
|
|
422
455
|
- **[Overlay Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/OVERLAY_MANAGER.md)** - Steam overlay control (7 functions)
|
|
423
|
-
- **[Cloud Storage Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/
|
|
456
|
+
- **[Cloud Storage Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/CLOUD_MANAGER.md)** - Steam Cloud file operations (14 functions)
|
|
424
457
|
- **[Workshop Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/WORKSHOP_MANAGER.md)** - Steam Workshop/UGC operations (29 functions)
|
|
458
|
+
- **[Input Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/INPUT_MANAGER.md)** - Steam Input controller support (35+ functions) ⚠️ _Virtual gamepad testing only_
|
|
425
459
|
|
|
426
460
|
## Steamworks Integration
|
|
427
461
|
|
|
@@ -437,11 +471,12 @@ const achievements = await steam.achievements.getAllAchievements();
|
|
|
437
471
|
console.log(achievements); // Achievement data from your Steam app
|
|
438
472
|
|
|
439
473
|
// Permanent achievement unlock in Steam
|
|
440
|
-
await steam.achievements.unlockAchievement(
|
|
474
|
+
await steam.achievements.unlockAchievement("YOUR_ACHIEVEMENT");
|
|
441
475
|
// ^ This shows up in Steam overlay and is saved permanently
|
|
442
476
|
```
|
|
443
477
|
|
|
444
478
|
**What happens when you unlock an achievement:**
|
|
479
|
+
|
|
445
480
|
- ✅ Steam overlay notification appears
|
|
446
481
|
- ✅ Achievement saved to Steam servers permanently
|
|
447
482
|
- ✅ Syncs across all devices
|
|
@@ -454,21 +489,21 @@ For Electron applications, use it in your main process:
|
|
|
454
489
|
|
|
455
490
|
```typescript
|
|
456
491
|
// main.ts
|
|
457
|
-
import { app } from
|
|
458
|
-
import SteamworksSDK from
|
|
492
|
+
import { app } from "electron";
|
|
493
|
+
import SteamworksSDK from "steamworks-ffi-node";
|
|
459
494
|
|
|
460
495
|
app.whenReady().then(() => {
|
|
461
496
|
const steam = SteamworksSDK.getInstance();
|
|
462
|
-
|
|
497
|
+
|
|
463
498
|
if (steam.init({ appId: YOUR_STEAM_APP_ID })) {
|
|
464
|
-
console.log(
|
|
465
|
-
|
|
499
|
+
console.log("Steam initialized in Electron!");
|
|
500
|
+
|
|
466
501
|
// Handle achievement unlocks from renderer process
|
|
467
502
|
// via IPC if needed
|
|
468
503
|
}
|
|
469
504
|
});
|
|
470
505
|
|
|
471
|
-
app.on(
|
|
506
|
+
app.on("before-quit", () => {
|
|
472
507
|
const steam = SteamworksSDK.getInstance();
|
|
473
508
|
steam.shutdown();
|
|
474
509
|
});
|
|
@@ -482,42 +517,46 @@ The library searches for the SDK in standard locations within your Electron app
|
|
|
482
517
|
|
|
483
518
|
## Requirements
|
|
484
519
|
|
|
485
|
-
- **Node.js**: 18+
|
|
520
|
+
- **Node.js**: 18+
|
|
486
521
|
- **Steam Client**: Must be running and logged in
|
|
487
522
|
- **Steam App ID**: Get yours at [Steamworks Partner](https://partner.steamgames.com/)
|
|
488
523
|
- **steam_appid.txt**: Optional - create in your project root OR pass to `steam.init(appId)`
|
|
489
524
|
|
|
490
525
|
### Platform Support
|
|
526
|
+
|
|
491
527
|
- ✅ **Windows**: steam_api64.dll / steam_api.dll
|
|
492
|
-
- ✅ **macOS**: libsteam_api.dylib
|
|
528
|
+
- ✅ **macOS**: libsteam_api.dylib
|
|
493
529
|
- ✅ **Linux**: libsteam_api.so
|
|
494
530
|
|
|
495
531
|
**Steamworks SDK Version**: v1.62 (Latest)
|
|
496
532
|
|
|
497
|
-
|
|
533
|
+
_Note: You must download and install the SDK redistributables separately as described in the Setup section above._
|
|
498
534
|
|
|
499
535
|
## Troubleshooting
|
|
500
536
|
|
|
501
537
|
### "SteamAPI_Init failed"
|
|
538
|
+
|
|
502
539
|
- ❌ Steam client not running → **Solution**: Start Steam and log in
|
|
503
540
|
- ❌ No App ID specified → **Solution**: Create `steam_appid.txt` in project root OR pass App ID to `steam.init(appId)`
|
|
504
541
|
- ❌ Invalid App ID → **Solution**: Use 480 for testing, or your registered App ID
|
|
505
542
|
|
|
506
543
|
### "Cannot find module 'steamworks-ffi-node'"
|
|
544
|
+
|
|
507
545
|
- ❌ Package not installed → **Solution**: Run `npm install steamworks-ffi-node`
|
|
508
546
|
|
|
509
547
|
### Achievement operations not working
|
|
548
|
+
|
|
510
549
|
- ❌ Not initialized → **Solution**: Call `steam.init({ appId })` first
|
|
511
550
|
- ❌ No achievements configured → **Solution**: Configure achievements in Steamworks Partner site
|
|
512
551
|
- ❌ Using Spacewar → **Note**: Spacewar may not have achievements, use your own App ID
|
|
513
552
|
|
|
514
553
|
### Electron-specific issues
|
|
554
|
+
|
|
515
555
|
- ❌ Initialized in renderer → **Solution**: Only initialize in main process
|
|
516
556
|
- ❌ Not cleaning up → **Solution**: Call `shutdown()` in `before-quit` event
|
|
517
557
|
- ❌ "Steamworks SDK library not found" in packaged app → **Solution**: Include SDK redistributables in your build (see Electron Packaging section above)
|
|
518
558
|
- ❌ Native module errors in packaged app → **Solution**: Ensure Steamworks SDK files are properly included in your app bundle
|
|
519
559
|
|
|
520
|
-
|
|
521
560
|
## How to Support This Project
|
|
522
561
|
|
|
523
562
|
You can support the development of this library by wishlisting, subscribing, and purchasing the app **AFK Companion** on Steam:
|