steamworks-ffi-node 0.6.10 → 0.7.1

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.
Files changed (50) hide show
  1. package/README.md +157 -107
  2. package/dist/internal/SteamCloudManager.d.ts +86 -1
  3. package/dist/internal/SteamCloudManager.d.ts.map +1 -1
  4. package/dist/internal/SteamCloudManager.js +142 -0
  5. package/dist/internal/SteamCloudManager.js.map +1 -1
  6. package/dist/internal/SteamInputManager.d.ts +496 -0
  7. package/dist/internal/SteamInputManager.d.ts.map +1 -0
  8. package/dist/internal/SteamInputManager.js +993 -0
  9. package/dist/internal/SteamInputManager.js.map +1 -0
  10. package/dist/internal/SteamLibraryLoader.d.ts +61 -0
  11. package/dist/internal/SteamLibraryLoader.d.ts.map +1 -1
  12. package/dist/internal/SteamLibraryLoader.js +83 -0
  13. package/dist/internal/SteamLibraryLoader.js.map +1 -1
  14. package/dist/internal/SteamScreenshotManager.d.ts +327 -0
  15. package/dist/internal/SteamScreenshotManager.d.ts.map +1 -0
  16. package/dist/internal/SteamScreenshotManager.js +459 -0
  17. package/dist/internal/SteamScreenshotManager.js.map +1 -0
  18. package/dist/internal/SteamWorkshopManager.d.ts +24 -0
  19. package/dist/internal/SteamWorkshopManager.d.ts.map +1 -1
  20. package/dist/internal/SteamWorkshopManager.js +74 -0
  21. package/dist/internal/SteamWorkshopManager.js.map +1 -1
  22. package/dist/internal/callbackTypes/SteamCallbackIds.d.ts +2 -0
  23. package/dist/internal/callbackTypes/SteamCallbackIds.d.ts.map +1 -1
  24. package/dist/internal/callbackTypes/SteamCallbackIds.js +3 -1
  25. package/dist/internal/callbackTypes/SteamCallbackIds.js.map +1 -1
  26. package/dist/internal/callbackTypes/SteamCallbackTypes.d.ts +9 -0
  27. package/dist/internal/callbackTypes/SteamCallbackTypes.d.ts.map +1 -1
  28. package/dist/steam.d.ts +99 -0
  29. package/dist/steam.d.ts.map +1 -1
  30. package/dist/steam.js +9 -0
  31. package/dist/steam.js.map +1 -1
  32. package/dist/types/cloud.d.ts +11 -11
  33. package/dist/types/cloud.d.ts.map +1 -1
  34. package/dist/types/index.d.ts +2 -0
  35. package/dist/types/index.d.ts.map +1 -1
  36. package/dist/types/index.js +4 -0
  37. package/dist/types/index.js.map +1 -1
  38. package/dist/types/input.d.ts +252 -0
  39. package/dist/types/input.d.ts.map +1 -0
  40. package/dist/types/input.js +142 -0
  41. package/dist/types/input.js.map +1 -0
  42. package/dist/types/screenshots.d.ts +63 -0
  43. package/dist/types/screenshots.d.ts.map +1 -0
  44. package/dist/types/screenshots.js +44 -0
  45. package/dist/types/screenshots.js.map +1 -0
  46. package/dist/types/workshop.d.ts +8 -0
  47. package/dist/types/workshop.d.ts.map +1 -1
  48. package/dist/types/workshop.js.map +1 -1
  49. package/docs/README.md +49 -5
  50. package/package.json +10 -2
package/README.md CHANGED
@@ -37,9 +37,13 @@ A TypeScript/JavaScript wrapper for the Steamworks SDK using Koffi FFI, designed
37
37
 
38
38
  > 🎉 **NEW: Overlay API** - 7 functions for complete Steam overlay control! [See Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/OVERLAY_MANAGER.md)
39
39
 
40
- > 🎉 **NEW: Cloud Storage API** - 14 functions for complete Steam Cloud (Remote Storage) integration! [See Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/CLOUD_MANAGER.md)
40
+ > 🎉 **NEW: Cloud Storage API** - 17 functions for complete Steam Cloud (Remote Storage) integration including batch writes! [See Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/CLOUD_MANAGER.md)
41
41
 
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)
42
+ > 🎉 **NEW: Workshop API** - 30 functions for complete Steam Workshop/UGC integration including item deletion! [See Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/WORKSHOP_MANAGER.md)
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
+
46
+ > 🎉 **NEW: Screenshots API** - 9 functions for Steam Screenshots integration! [See Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/SCREENSHOT_MANAGER.md)
43
47
 
44
48
  ## Features
45
49
 
@@ -82,18 +86,33 @@ A TypeScript/JavaScript wrapper for the Steamworks SDK using Koffi FFI, designed
82
86
  - ✅ Open overlay web browser to URLs
83
87
  - ✅ Open store pages with purchase options
84
88
  - ✅ Show invite dialogs for multiplayer sessions
85
- - **Cloud Storage API**: Complete Steam Cloud (Remote Storage) integration (14 functions)
89
+ - **Cloud Storage API**: Complete Steam Cloud (Remote Storage) integration (17 functions)
86
90
  - ✅ File operations (write, read, delete, check existence)
87
91
  - ✅ File metadata (size, timestamp, persistence status)
88
92
  - ✅ File listing (count, iterate, get all with details)
89
93
  - ✅ Quota management (track storage usage and limits)
90
94
  - ✅ Cloud settings (check/toggle cloud sync for account and app)
91
- - **Workshop API**: Complete Steam Workshop/UGC integration (29 functions)
95
+ - Batch writes (atomic multi-file operations)
96
+ - **Workshop API**: Complete Steam Workshop/UGC integration (30 functions)
92
97
  - ✅ Subscription management (subscribe, unsubscribe, list items)
93
98
  - ✅ Item state & information (download progress, installation info)
94
99
  - ✅ Query operations (text search, browse, filter Workshop content)
95
100
  - ✅ Item creation & update (create, upload, manage your Workshop items)
96
101
  - ✅ Voting & favorites (vote on items, manage favorites)
102
+ - ✅ Item deletion (permanently delete your Workshop items)
103
+ - **Input API**: Complete Steam Input (controller) support (35+ functions) ⚠️ _Tested with virtual gamepad only_
104
+ - ✅ Controller detection (Xbox, PlayStation, Switch, Steam Controller, Steam Deck)
105
+ - ✅ Action sets and layers (menu controls, gameplay controls, etc.)
106
+ - ✅ Digital actions (buttons) and analog actions (sticks/triggers)
107
+ - ✅ Motion data (gyro, accelerometer for supported controllers)
108
+ - ✅ Haptics (vibration, LED control for DualShock/DualSense)
109
+ - **Screenshots API**: Steam Screenshots integration (9 functions)
110
+ - ✅ Screenshot capture (programmatic and user-triggered)
111
+ - ✅ Add existing images to Steam library
112
+ - ✅ Location and user tagging (geotag, tag friends)
113
+ - ✅ Workshop integration (tag published files)
114
+ - ✅ Screenshot hooks (intercept F12 screenshots)
115
+ - ✅ VR screenshot support
97
116
  - **Steamworks Integration**: Direct FFI calls to Steamworks C++ SDK
98
117
  - **Cross-Platform**: Windows, macOS, and Linux support
99
118
  - **Easy Setup**: Simple installation with clear SDK setup guide
@@ -113,23 +132,26 @@ npm install steamworks-ffi-node
113
132
  ### Setup
114
133
 
115
134
  1. **Download Steamworks SDK** (required separately due to licensing):
135
+
116
136
  - Visit [Steamworks Partner site](https://partner.steamgames.com/)
117
137
  - Download the latest Steamworks SDK
118
138
  - Extract and copy `redistributable_bin` folder to your project
119
139
  - See [STEAMWORKS_SDK_SETUP.md](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/STEAMWORKS_SDK_SETUP.md) for detailed instructions
120
140
 
121
141
  2. **Create `steam_appid.txt` (optional)** in your project root:
142
+
122
143
  ```bash
123
144
  echo "480" > steam_appid.txt # Use 480 for testing, or your Steam App ID
124
145
  ```
125
- *Note: You can skip this file and pass the App ID directly to `steam.init(appId)` instead*
146
+
147
+ _Note: You can skip this file and pass the App ID directly to `steam.init(appId)` instead_
126
148
 
127
149
  3. **Make sure Steam is running** and you're logged in
128
150
 
129
151
  ### Basic Usage
130
152
 
131
153
  ```typescript
132
- import SteamworksSDK from 'steamworks-ffi-node';
154
+ import SteamworksSDK from "steamworks-ffi-node";
133
155
 
134
156
  // Helper to auto-start callback polling
135
157
  function startCallbackPolling(steam: SteamworksSDK, interval: number = 1000) {
@@ -145,48 +167,50 @@ const initialized = steam.init({ appId: 480 }); // Your Steam App ID
145
167
  if (initialized) {
146
168
  // Start callback polling automatically (required for async operations)
147
169
  const callbackInterval = startCallbackPolling(steam, 1000);
148
-
170
+
149
171
  // Get current Steam language for localization
150
172
  const language = steam.getCurrentGameLanguage();
151
- console.log('Steam language:', language); // e.g., 'english', 'french', 'german'
152
-
173
+ console.log("Steam language:", language); // e.g., 'english', 'french', 'german'
174
+
153
175
  // Get achievements from Steam servers
154
176
  const achievements = await steam.achievements.getAllAchievements();
155
- console.log('Steam achievements:', achievements);
156
-
177
+ console.log("Steam achievements:", achievements);
178
+
157
179
  // Unlock achievement (permanent in Steam!)
158
- await steam.achievements.unlockAchievement('ACH_WIN_ONE_GAME');
159
-
180
+ await steam.achievements.unlockAchievement("ACH_WIN_ONE_GAME");
181
+
160
182
  // Check unlock status from Steam
161
- const isUnlocked = await steam.achievements.isAchievementUnlocked('ACH_WIN_ONE_GAME');
162
- console.log('Achievement unlocked:', isUnlocked);
163
-
183
+ const isUnlocked = await steam.achievements.isAchievementUnlocked(
184
+ "ACH_WIN_ONE_GAME"
185
+ );
186
+ console.log("Achievement unlocked:", isUnlocked);
187
+
164
188
  // Track user statistics
165
- const kills = await steam.stats.getStatInt('total_kills') || 0;
166
- await steam.stats.setStatInt('total_kills', kills + 1);
167
-
189
+ const kills = (await steam.stats.getStatInt("total_kills")) || 0;
190
+ await steam.stats.setStatInt("total_kills", kills + 1);
191
+
168
192
  // Get global statistics
169
193
  await steam.stats.requestGlobalStats(7);
170
- await new Promise(resolve => setTimeout(resolve, 2000));
194
+ await new Promise((resolve) => setTimeout(resolve, 2000));
171
195
  steam.runCallbacks();
172
- const globalKills = await steam.stats.getGlobalStatInt('global.total_kills');
173
- console.log('Total kills worldwide:', globalKills);
174
-
196
+ const globalKills = await steam.stats.getGlobalStatInt("global.total_kills");
197
+ console.log("Total kills worldwide:", globalKills);
198
+
175
199
  // Work with leaderboards
176
200
  const leaderboard = await steam.leaderboards.findOrCreateLeaderboard(
177
- 'HighScores',
201
+ "HighScores",
178
202
  1, // Descending (higher is better)
179
- 0 // Numeric display
203
+ 0 // Numeric display
180
204
  );
181
-
205
+
182
206
  if (leaderboard) {
183
207
  // Upload score
184
208
  await steam.leaderboards.uploadLeaderboardScore(
185
209
  leaderboard.handle,
186
210
  1000,
187
- 1 // Keep best score
211
+ 1 // Keep best score
188
212
  );
189
-
213
+
190
214
  // Download top 10 scores
191
215
  const topScores = await steam.leaderboards.downloadLeaderboardEntries(
192
216
  leaderboard.handle,
@@ -194,36 +218,38 @@ if (initialized) {
194
218
  0,
195
219
  9
196
220
  );
197
- console.log('Top 10 scores:', topScores);
221
+ console.log("Top 10 scores:", topScores);
198
222
  }
199
-
223
+
200
224
  // Access friends and social features
201
225
  const personaName = steam.friends.getPersonaName();
202
226
  const friendCount = steam.friends.getFriendCount(4); // All friends
203
227
  console.log(`${personaName} has ${friendCount} friends`);
204
-
228
+
205
229
  // Get all friends with details
206
230
  const allFriends = steam.friends.getAllFriends(4); // All friends
207
- allFriends.slice(0, 5).forEach(friend => {
231
+ allFriends.slice(0, 5).forEach((friend) => {
208
232
  const name = steam.friends.getFriendPersonaName(friend.steamId);
209
233
  const state = steam.friends.getFriendPersonaState(friend.steamId);
210
234
  const level = steam.friends.getFriendSteamLevel(friend.steamId);
211
235
  console.log(`${name}: Level ${level}, Status: ${state}`);
212
-
236
+
213
237
  // Get avatar handles
214
238
  const smallAvatar = steam.friends.getSmallFriendAvatar(friend.steamId);
215
239
  const mediumAvatar = steam.friends.getMediumFriendAvatar(friend.steamId);
216
240
  if (smallAvatar > 0) {
217
- console.log(` Avatar handles: small=${smallAvatar}, medium=${mediumAvatar}`);
241
+ console.log(
242
+ ` Avatar handles: small=${smallAvatar}, medium=${mediumAvatar}`
243
+ );
218
244
  }
219
-
245
+
220
246
  // Check if playing a game
221
247
  const gameInfo = steam.friends.getFriendGamePlayed(friend.steamId);
222
248
  if (gameInfo) {
223
249
  console.log(` Playing: App ${gameInfo.gameId}`);
224
250
  }
225
251
  });
226
-
252
+
227
253
  // Check friend groups (tags)
228
254
  const groupCount = steam.friends.getFriendsGroupCount();
229
255
  if (groupCount > 0) {
@@ -232,7 +258,7 @@ if (initialized) {
232
258
  const members = steam.friends.getFriendsGroupMembersList(groupId);
233
259
  console.log(`Group "${groupName}" has ${members.length} members`);
234
260
  }
235
-
261
+
236
262
  // Check recently played with
237
263
  const coplayCount = steam.friends.getCoplayFriendCount();
238
264
  if (coplayCount > 0) {
@@ -241,107 +267,123 @@ if (initialized) {
241
267
  const coplayTime = steam.friends.getFriendCoplayTime(recentPlayer);
242
268
  console.log(`Recently played with ${playerName}`);
243
269
  }
244
-
270
+
245
271
  // Set rich presence for custom status
246
- steam.richPresence.setRichPresence('status', 'In Main Menu');
247
- steam.richPresence.setRichPresence('connect', '+connect server:27015');
248
-
272
+ steam.richPresence.setRichPresence("status", "In Main Menu");
273
+ steam.richPresence.setRichPresence("connect", "+connect server:27015");
274
+
249
275
  // Open Steam overlay
250
- steam.overlay.activateGameOverlay('Friends'); // Open friends list
251
- steam.overlay.activateGameOverlayToWebPage('https://example.com/wiki'); // Open wiki
252
-
276
+ steam.overlay.activateGameOverlay("Friends"); // Open friends list
277
+ steam.overlay.activateGameOverlayToWebPage("https://example.com/wiki"); // Open wiki
278
+
253
279
  // Steam Cloud storage operations
254
- const saveData = { level: 5, score: 1000, inventory: ['sword', 'shield'] };
280
+ const saveData = { level: 5, score: 1000, inventory: ["sword", "shield"] };
255
281
  const buffer = Buffer.from(JSON.stringify(saveData));
256
-
282
+
257
283
  // Write save file to Steam Cloud
258
- const written = steam.cloud.fileWrite('savegame.json', buffer);
284
+ const written = steam.cloud.fileWrite("savegame.json", buffer);
259
285
  if (written) {
260
- console.log('✅ Save uploaded to Steam Cloud');
286
+ console.log("✅ Save uploaded to Steam Cloud");
261
287
  }
262
-
288
+
263
289
  // Check cloud quota
264
290
  const quota = steam.cloud.getQuota();
265
- console.log(`Cloud storage: ${quota.usedBytes}/${quota.totalBytes} bytes (${quota.percentUsed.toFixed(2)}%)`);
266
-
291
+ console.log(
292
+ `Cloud storage: ${quota.usedBytes}/${
293
+ quota.totalBytes
294
+ } bytes (${quota.percentUsed.toFixed(2)}%)`
295
+ );
296
+
267
297
  // Read save file from Steam Cloud
268
- if (steam.cloud.fileExists('savegame.json')) {
269
- const result = steam.cloud.fileRead('savegame.json');
298
+ if (steam.cloud.fileExists("savegame.json")) {
299
+ const result = steam.cloud.fileRead("savegame.json");
270
300
  if (result.success && result.data) {
271
301
  const loadedSave = JSON.parse(result.data.toString());
272
- console.log(`Loaded save: Level ${loadedSave.level}, Score ${loadedSave.score}`);
302
+ console.log(
303
+ `Loaded save: Level ${loadedSave.level}, Score ${loadedSave.score}`
304
+ );
273
305
  }
274
306
  }
275
-
307
+
276
308
  // List all cloud files
277
309
  const cloudFiles = steam.cloud.getAllFiles();
278
310
  console.log(`Steam Cloud contains ${cloudFiles.length} files:`);
279
- cloudFiles.forEach(file => {
311
+ cloudFiles.forEach((file) => {
280
312
  const kb = (file.size / 1024).toFixed(2);
281
- const status = file.persisted ? '☁️' : '';
313
+ const status = file.persisted ? "☁️" : "";
282
314
  console.log(`${status} ${file.name} - ${kb} KB`);
283
315
  });
284
-
316
+
285
317
  // Steam Workshop operations
286
318
  // Subscribe to a Workshop item
287
319
  const subscribeResult = await steam.workshop.subscribeItem(123456789n);
288
320
  if (subscribeResult.success) {
289
- console.log('✅ Subscribed to Workshop item');
321
+ console.log("✅ Subscribed to Workshop item");
290
322
  }
291
-
323
+
292
324
  // Get all subscribed items
293
325
  const subscribedItems = steam.workshop.getSubscribedItems();
294
326
  console.log(`Subscribed to ${subscribedItems.length} Workshop items`);
295
-
327
+
296
328
  // Query Workshop items with text search
297
329
  const query = steam.workshop.createQueryAllUGCRequest(
298
- 11, // RankedByTextSearch - for text search queries
299
- 0, // Items
300
- 480, // Creator App ID
301
- 480, // Consumer App ID
302
- 1 // Page 1
330
+ 11, // RankedByTextSearch - for text search queries
331
+ 0, // Items
332
+ 480, // Creator App ID
333
+ 480, // Consumer App ID
334
+ 1 // Page 1
303
335
  );
304
-
336
+
305
337
  if (query) {
306
338
  // Set search text to filter results
307
- steam.workshop.setSearchText(query, 'map');
308
-
339
+ steam.workshop.setSearchText(query, "map");
340
+
309
341
  const queryResult = await steam.workshop.sendQueryUGCRequest(query);
310
342
  if (queryResult) {
311
- console.log(`Found ${queryResult.numResults} Workshop items matching "map"`);
312
-
343
+ console.log(
344
+ `Found ${queryResult.numResults} Workshop items matching "map"`
345
+ );
346
+
313
347
  // Get details for each item
314
348
  for (let i = 0; i < queryResult.numResults; i++) {
315
349
  const details = steam.workshop.getQueryUGCResult(query, i);
316
350
  if (details) {
317
351
  console.log(`📦 ${details.title} by ${details.steamIDOwner}`);
318
- console.log(` Score: ${details.score}, Downloads: ${details.numUniqueSubscriptions}`);
352
+ console.log(
353
+ ` Score: ${details.score}, Downloads: ${details.numUniqueSubscriptions}`
354
+ );
319
355
  }
320
356
  }
321
357
  }
322
358
  steam.workshop.releaseQueryUGCRequest(query);
323
359
  }
324
-
360
+
325
361
  // Check download progress for subscribed items
326
- subscribedItems.forEach(itemId => {
362
+ subscribedItems.forEach((itemId) => {
327
363
  const state = steam.workshop.getItemState(itemId);
328
364
  const stateFlags = [];
329
- if (state & 1) stateFlags.push('Subscribed');
330
- if (state & 4) stateFlags.push('Needs Update');
331
- if (state & 8) stateFlags.push('Installed');
332
- if (state & 16) stateFlags.push('Downloading');
333
-
334
- console.log(`Item ${itemId}: ${stateFlags.join(', ')}`);
335
-
336
- if (state & 16) { // If downloading
365
+ if (state & 1) stateFlags.push("Subscribed");
366
+ if (state & 4) stateFlags.push("Needs Update");
367
+ if (state & 8) stateFlags.push("Installed");
368
+ if (state & 16) stateFlags.push("Downloading");
369
+
370
+ console.log(`Item ${itemId}: ${stateFlags.join(", ")}`);
371
+
372
+ if (state & 16) {
373
+ // If downloading
337
374
  const progress = steam.workshop.getItemDownloadInfo(itemId);
338
375
  if (progress) {
339
- const percent = (progress.downloaded / progress.total * 100).toFixed(1);
340
- console.log(` Download: ${percent}% (${progress.downloaded}/${progress.total} bytes)`);
376
+ const percent = ((progress.downloaded / progress.total) * 100).toFixed(
377
+ 1
378
+ );
379
+ console.log(
380
+ ` Download: ${percent}% (${progress.downloaded}/${progress.total} bytes)`
381
+ );
341
382
  }
342
383
  }
343
-
344
- if (state & 8) { // If installed
384
+
385
+ if (state & 8) {
386
+ // If installed
345
387
  const info = steam.workshop.getItemInstallInfo(itemId);
346
388
  if (info.success) {
347
389
  console.log(` Installed at: ${info.folder}`);
@@ -359,13 +401,13 @@ steam.shutdown();
359
401
 
360
402
  ```javascript
361
403
  // Option 1: ESM Named import
362
- import { SteamworksSDK } from 'steamworks-ffi-node';
404
+ import { SteamworksSDK } from "steamworks-ffi-node";
363
405
 
364
406
  // Option 2: CommonJs named import (recommended - no .default needed)
365
- const { SteamworksSDK } = require('steamworks-ffi-node');
407
+ const { SteamworksSDK } = require("steamworks-ffi-node");
366
408
 
367
409
  // Option 3: CommonJs default named import (also works)
368
- const SteamworksSDK = require('steamworks-ffi-node').default;
410
+ const SteamworksSDK = require("steamworks-ffi-node").default;
369
411
 
370
412
  // Helper to auto-start callback polling
371
413
  function startCallbackPolling(steam, interval = 1000) {
@@ -376,24 +418,24 @@ function startCallbackPolling(steam, interval = 1000) {
376
418
 
377
419
  async function example() {
378
420
  const steam = SteamworksSDK.getInstance();
379
-
421
+
380
422
  if (steam.init({ appId: 480 })) {
381
423
  // Start callback polling automatically
382
424
  const callbackInterval = startCallbackPolling(steam, 1000);
383
-
425
+
384
426
  const achievements = await steam.achievements.getAllAchievements();
385
427
  console.log(`Found ${achievements.length} achievements`);
386
-
428
+
387
429
  // Unlock first locked achievement
388
- const locked = achievements.find(a => !a.unlocked);
430
+ const locked = achievements.find((a) => !a.unlocked);
389
431
  if (locked) {
390
432
  await steam.achievements.unlockAchievement(locked.apiName);
391
433
  }
392
-
434
+
393
435
  // Cleanup
394
436
  clearInterval(callbackInterval);
395
437
  }
396
-
438
+
397
439
  steam.shutdown();
398
440
  }
399
441
 
@@ -403,6 +445,7 @@ example();
403
445
  ### Testing with Spacewar
404
446
 
405
447
  For immediate testing, use Spacewar (App ID 480):
448
+
406
449
  - Free Steam app for testing Steamworks features
407
450
  - Add to Steam library: `steam://install/480` or search "Spacewar" in Steam
408
451
  - Launch it once, then you can test with App ID 480
@@ -414,14 +457,16 @@ Complete documentation for all APIs is available in the [docs folder](https://gi
414
457
  ➡️ **[View Complete Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/README.md)**
415
458
 
416
459
  ### API Guides:
460
+
417
461
  - **[Achievement Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/ACHIEVEMENT_MANAGER.md)** - Complete achievement system (20 functions)
418
462
  - **[Stats Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/STATS_MANAGER.md)** - User and global statistics (14 functions)
419
463
  - **[Leaderboard Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/LEADERBOARD_MANAGER.md)** - Leaderboard operations (7 functions)
420
464
  - **[Friends Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/FRIENDS_MANAGER.md)** - Friends and social features (22 functions)
421
465
  - **[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
466
  - **[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/CLOUD_STORAGE_MANAGER.md)** - Steam Cloud file operations (14 functions)
467
+ - **[Cloud Storage Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/CLOUD_MANAGER.md)** - Steam Cloud file operations (14 functions)
424
468
  - **[Workshop Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/WORKSHOP_MANAGER.md)** - Steam Workshop/UGC operations (29 functions)
469
+ - **[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
470
 
426
471
  ## Steamworks Integration
427
472
 
@@ -437,11 +482,12 @@ const achievements = await steam.achievements.getAllAchievements();
437
482
  console.log(achievements); // Achievement data from your Steam app
438
483
 
439
484
  // Permanent achievement unlock in Steam
440
- await steam.achievements.unlockAchievement('YOUR_ACHIEVEMENT');
485
+ await steam.achievements.unlockAchievement("YOUR_ACHIEVEMENT");
441
486
  // ^ This shows up in Steam overlay and is saved permanently
442
487
  ```
443
488
 
444
489
  **What happens when you unlock an achievement:**
490
+
445
491
  - ✅ Steam overlay notification appears
446
492
  - ✅ Achievement saved to Steam servers permanently
447
493
  - ✅ Syncs across all devices
@@ -454,21 +500,21 @@ For Electron applications, use it in your main process:
454
500
 
455
501
  ```typescript
456
502
  // main.ts
457
- import { app } from 'electron';
458
- import SteamworksSDK from 'steamworks-ffi-node';
503
+ import { app } from "electron";
504
+ import SteamworksSDK from "steamworks-ffi-node";
459
505
 
460
506
  app.whenReady().then(() => {
461
507
  const steam = SteamworksSDK.getInstance();
462
-
508
+
463
509
  if (steam.init({ appId: YOUR_STEAM_APP_ID })) {
464
- console.log('Steam initialized in Electron!');
465
-
510
+ console.log("Steam initialized in Electron!");
511
+
466
512
  // Handle achievement unlocks from renderer process
467
513
  // via IPC if needed
468
514
  }
469
515
  });
470
516
 
471
- app.on('before-quit', () => {
517
+ app.on("before-quit", () => {
472
518
  const steam = SteamworksSDK.getInstance();
473
519
  steam.shutdown();
474
520
  });
@@ -482,42 +528,46 @@ The library searches for the SDK in standard locations within your Electron app
482
528
 
483
529
  ## Requirements
484
530
 
485
- - **Node.js**: 18+
531
+ - **Node.js**: 18+
486
532
  - **Steam Client**: Must be running and logged in
487
533
  - **Steam App ID**: Get yours at [Steamworks Partner](https://partner.steamgames.com/)
488
534
  - **steam_appid.txt**: Optional - create in your project root OR pass to `steam.init(appId)`
489
535
 
490
536
  ### Platform Support
537
+
491
538
  - ✅ **Windows**: steam_api64.dll / steam_api.dll
492
- - ✅ **macOS**: libsteam_api.dylib
539
+ - ✅ **macOS**: libsteam_api.dylib
493
540
  - ✅ **Linux**: libsteam_api.so
494
541
 
495
542
  **Steamworks SDK Version**: v1.62 (Latest)
496
543
 
497
- *Note: You must download and install the SDK redistributables separately as described in the Setup section above.*
544
+ _Note: You must download and install the SDK redistributables separately as described in the Setup section above._
498
545
 
499
546
  ## Troubleshooting
500
547
 
501
548
  ### "SteamAPI_Init failed"
549
+
502
550
  - ❌ Steam client not running → **Solution**: Start Steam and log in
503
551
  - ❌ No App ID specified → **Solution**: Create `steam_appid.txt` in project root OR pass App ID to `steam.init(appId)`
504
552
  - ❌ Invalid App ID → **Solution**: Use 480 for testing, or your registered App ID
505
553
 
506
554
  ### "Cannot find module 'steamworks-ffi-node'"
555
+
507
556
  - ❌ Package not installed → **Solution**: Run `npm install steamworks-ffi-node`
508
557
 
509
558
  ### Achievement operations not working
559
+
510
560
  - ❌ Not initialized → **Solution**: Call `steam.init({ appId })` first
511
561
  - ❌ No achievements configured → **Solution**: Configure achievements in Steamworks Partner site
512
562
  - ❌ Using Spacewar → **Note**: Spacewar may not have achievements, use your own App ID
513
563
 
514
564
  ### Electron-specific issues
565
+
515
566
  - ❌ Initialized in renderer → **Solution**: Only initialize in main process
516
567
  - ❌ Not cleaning up → **Solution**: Call `shutdown()` in `before-quit` event
517
568
  - ❌ "Steamworks SDK library not found" in packaged app → **Solution**: Include SDK redistributables in your build (see Electron Packaging section above)
518
569
  - ❌ Native module errors in packaged app → **Solution**: Ensure Steamworks SDK files are properly included in your app bundle
519
570
 
520
-
521
571
  ## How to Support This Project
522
572
 
523
573
  You can support the development of this library by wishlisting, subscribing, and purchasing the app **AFK Companion** on Steam:
@@ -1,6 +1,6 @@
1
1
  import { SteamLibraryLoader } from './SteamLibraryLoader';
2
2
  import { SteamAPICore } from './SteamAPICore';
3
- import { CloudFileInfo, CloudQuota, CloudFileReadResult } from '../types';
3
+ import { CloudFileInfo, CloudQuota, CloudFileReadResult, CloudBatchWriteResult } from '../types';
4
4
  /**
5
5
  * Manager for Steam Cloud / Remote Storage operations
6
6
  *
@@ -315,5 +315,90 @@ export declare class SteamCloudManager {
315
315
  * ```
316
316
  */
317
317
  setCloudEnabledForApp(enabled: boolean): void;
318
+ /**
319
+ * Begins a batch of file write operations
320
+ *
321
+ * @returns True if the batch was started successfully, false otherwise
322
+ *
323
+ * @remarks
324
+ * Use this when you need to update multiple files atomically, such as a game save
325
+ * that consists of multiple files. Steam will treat all writes between
326
+ * BeginFileWriteBatch() and EndFileWriteBatch() as a single atomic operation.
327
+ *
328
+ * If a batch is started but not ended (e.g., due to a crash), Steam will
329
+ * rollback the changes when the user next plays the game.
330
+ *
331
+ * @example
332
+ * ```typescript
333
+ * // Write multiple save files atomically
334
+ * const success = steam.cloud.beginFileWriteBatch();
335
+ * if (success) {
336
+ * steam.cloud.fileWrite('save_meta.json', metaBuffer);
337
+ * steam.cloud.fileWrite('save_world.bin', worldBuffer);
338
+ * steam.cloud.fileWrite('save_inventory.json', inventoryBuffer);
339
+ * steam.cloud.endFileWriteBatch();
340
+ * }
341
+ * ```
342
+ *
343
+ * @see {@link endFileWriteBatch}
344
+ */
345
+ beginFileWriteBatch(): boolean;
346
+ /**
347
+ * Ends a batch of file write operations
348
+ *
349
+ * @returns True if the batch was ended successfully, false otherwise
350
+ *
351
+ * @remarks
352
+ * Call this after all files in the batch have been written. If this returns
353
+ * true, all the files written since BeginFileWriteBatch() will be committed
354
+ * atomically.
355
+ *
356
+ * @example
357
+ * ```typescript
358
+ * // Complete a batch write
359
+ * steam.cloud.beginFileWriteBatch();
360
+ * steam.cloud.fileWrite('file1.dat', data1);
361
+ * steam.cloud.fileWrite('file2.dat', data2);
362
+ * const committed = steam.cloud.endFileWriteBatch();
363
+ * if (committed) {
364
+ * console.log('All files saved atomically');
365
+ * }
366
+ * ```
367
+ *
368
+ * @see {@link beginFileWriteBatch}
369
+ */
370
+ endFileWriteBatch(): boolean;
371
+ /**
372
+ * Writes multiple files atomically as a batch operation
373
+ *
374
+ * @param files - Array of objects containing filename and data pairs
375
+ * @returns Object with success status and individual file results
376
+ *
377
+ * @remarks
378
+ * This is a convenience method that wraps BeginFileWriteBatch(), multiple
379
+ * fileWrite() calls, and EndFileWriteBatch() into a single operation.
380
+ *
381
+ * If any file fails to write, the entire batch is considered failed and
382
+ * Steam may rollback the changes.
383
+ *
384
+ * @example
385
+ * ```typescript
386
+ * const result = steam.cloud.writeFilesBatch([
387
+ * { filename: 'save_meta.json', data: Buffer.from(JSON.stringify(meta)) },
388
+ * { filename: 'save_world.bin', data: worldBuffer },
389
+ * { filename: 'save_inventory.json', data: Buffer.from(JSON.stringify(inventory)) }
390
+ * ]);
391
+ *
392
+ * if (result.success) {
393
+ * console.log(`All ${result.filesWritten} files saved`);
394
+ * } else {
395
+ * console.error('Batch write failed:', result.failedFiles);
396
+ * }
397
+ * ```
398
+ */
399
+ writeFilesBatch(files: Array<{
400
+ filename: string;
401
+ data: Buffer;
402
+ }>): CloudBatchWriteResult;
318
403
  }
319
404
  //# sourceMappingURL=SteamCloudManager.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SteamCloudManager.d.ts","sourceRoot":"","sources":["../../src/internal/SteamCloudManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,UAAU,EAEV,mBAAmB,EAGpB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,qBAAa,iBAAiB;IAC5B,kDAAkD;IAClD,OAAO,CAAC,aAAa,CAAqB;IAE1C,gEAAgE;IAChE,OAAO,CAAC,OAAO,CAAe;IAE9B;;;;;OAKG;gBACS,aAAa,EAAE,kBAAkB,EAAE,OAAO,EAAE,YAAY;IAKpE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IA2BlD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB;IAiD/C;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAkBrC;;;;;;;;;;;;;;;;;;OAkBG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAkBrC;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAkBrC;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAkB1C;;;;;;;;;;OAUG;IACH,YAAY,IAAI,MAAM;IAkBtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IA0CxE;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,IAAI,aAAa,EAAE;IAuB9B;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAkBxC;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,IAAI,UAAU;IA2CtB;;;;;;;;;;;;;;;OAeG;IACH,wBAAwB,IAAI,OAAO;IAkBnC;;;;;;;;;;;;;;;OAeG;IACH,oBAAoB,IAAI,OAAO;IAkB/B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;CAgB9C"}
1
+ {"version":3,"file":"SteamCloudManager.d.ts","sourceRoot":"","sources":["../../src/internal/SteamCloudManager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EACL,aAAa,EACb,UAAU,EACV,mBAAmB,EACnB,qBAAqB,EACtB,MAAM,UAAU,CAAC;AAElB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoDG;AACH,qBAAa,iBAAiB;IAC5B,kDAAkD;IAClD,OAAO,CAAC,aAAa,CAAqB;IAE1C,gEAAgE;IAChE,OAAO,CAAC,OAAO,CAAe;IAE9B;;;;;OAKG;gBACS,aAAa,EAAE,kBAAkB,EAAE,OAAO,EAAE,YAAY;IAKpE;;;;;;;;;;;;;;;;;;;;;OAqBG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO;IA2BlD;;;;;;;;;;;;;;;;;;;OAmBG;IACH,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB;IAiD/C;;;;;;;;;;;;OAYG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAkBrC;;;;;;;;;;;;;;;;;;OAkBG;IACH,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAkBrC;;;;;;;;;;;OAWG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAkBrC;;;;;;;;;;;;OAYG;IACH,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM;IAkB1C;;;;;;;;;;OAUG;IACH,YAAY,IAAI,MAAM;IAkBtB;;;;;;;;;;;;;;;;;;;OAmBG;IACH,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI;IA0CxE;;;;;;;;;;;;;;;;OAgBG;IACH,WAAW,IAAI,aAAa,EAAE;IAuB9B;;;;;;;;;;;;;;;;;;OAkBG;IACH,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAkBxC;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,IAAI,UAAU;IA2CtB;;;;;;;;;;;;;;;OAeG;IACH,wBAAwB,IAAI,OAAO;IAkBnC;;;;;;;;;;;;;;;OAeG;IACH,oBAAoB,IAAI,OAAO;IAkB/B;;;;;;;;;;;;;;;OAeG;IACH,qBAAqB,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAqB7C;;;;;;;;;;;;;;;;;;;;;;;;;;OA0BG;IACH,mBAAmB,IAAI,OAAO;IAoB9B;;;;;;;;;;;;;;;;;;;;;;;OAuBG;IACH,iBAAiB,IAAI,OAAO;IAoB5B;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2BG;IACH,eAAe,CAAC,KAAK,EAAE,KAAK,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,qBAAqB;CA2BzF"}