steamworks-ffi-node 0.6.9 → 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 +153 -107
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -2
- package/dist/index.js.map +1 -1
- 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 +30 -2
- package/package.json +8 -2
- package/verify-sdk-setup.js +3 -3
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) {
|
|
@@ -139,54 +150,56 @@ function startCallbackPolling(steam: SteamworksSDK, interval: number = 1000) {
|
|
|
139
150
|
}
|
|
140
151
|
|
|
141
152
|
// Initialize Steam connection
|
|
142
|
-
const steam =
|
|
153
|
+
const steam = SteamworksSDK.getInstance();
|
|
143
154
|
const initialized = steam.init({ appId: 480 }); // Your Steam App ID
|
|
144
155
|
|
|
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}`);
|
|
@@ -355,10 +386,17 @@ clearInterval(callbackInterval);
|
|
|
355
386
|
steam.shutdown();
|
|
356
387
|
```
|
|
357
388
|
|
|
358
|
-
### JavaScript
|
|
389
|
+
### JavaScript
|
|
359
390
|
|
|
360
391
|
```javascript
|
|
361
|
-
|
|
392
|
+
// Option 1: ESM Named import
|
|
393
|
+
import { SteamworksSDK } from "steamworks-ffi-node";
|
|
394
|
+
|
|
395
|
+
// Option 2: CommonJs named import (recommended - no .default needed)
|
|
396
|
+
const { SteamworksSDK } = require("steamworks-ffi-node");
|
|
397
|
+
|
|
398
|
+
// Option 3: CommonJs default named import (also works)
|
|
399
|
+
const SteamworksSDK = require("steamworks-ffi-node").default;
|
|
362
400
|
|
|
363
401
|
// Helper to auto-start callback polling
|
|
364
402
|
function startCallbackPolling(steam, interval = 1000) {
|
|
@@ -368,25 +406,25 @@ function startCallbackPolling(steam, interval = 1000) {
|
|
|
368
406
|
}
|
|
369
407
|
|
|
370
408
|
async function example() {
|
|
371
|
-
const steam =
|
|
372
|
-
|
|
409
|
+
const steam = SteamworksSDK.getInstance();
|
|
410
|
+
|
|
373
411
|
if (steam.init({ appId: 480 })) {
|
|
374
412
|
// Start callback polling automatically
|
|
375
413
|
const callbackInterval = startCallbackPolling(steam, 1000);
|
|
376
|
-
|
|
414
|
+
|
|
377
415
|
const achievements = await steam.achievements.getAllAchievements();
|
|
378
416
|
console.log(`Found ${achievements.length} achievements`);
|
|
379
|
-
|
|
417
|
+
|
|
380
418
|
// Unlock first locked achievement
|
|
381
|
-
const locked = achievements.find(a => !a.unlocked);
|
|
419
|
+
const locked = achievements.find((a) => !a.unlocked);
|
|
382
420
|
if (locked) {
|
|
383
421
|
await steam.achievements.unlockAchievement(locked.apiName);
|
|
384
422
|
}
|
|
385
|
-
|
|
423
|
+
|
|
386
424
|
// Cleanup
|
|
387
425
|
clearInterval(callbackInterval);
|
|
388
426
|
}
|
|
389
|
-
|
|
427
|
+
|
|
390
428
|
steam.shutdown();
|
|
391
429
|
}
|
|
392
430
|
|
|
@@ -396,6 +434,7 @@ example();
|
|
|
396
434
|
### Testing with Spacewar
|
|
397
435
|
|
|
398
436
|
For immediate testing, use Spacewar (App ID 480):
|
|
437
|
+
|
|
399
438
|
- Free Steam app for testing Steamworks features
|
|
400
439
|
- Add to Steam library: `steam://install/480` or search "Spacewar" in Steam
|
|
401
440
|
- Launch it once, then you can test with App ID 480
|
|
@@ -407,14 +446,16 @@ Complete documentation for all APIs is available in the [docs folder](https://gi
|
|
|
407
446
|
➡️ **[View Complete Documentation](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/README.md)**
|
|
408
447
|
|
|
409
448
|
### API Guides:
|
|
449
|
+
|
|
410
450
|
- **[Achievement Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/ACHIEVEMENT_MANAGER.md)** - Complete achievement system (20 functions)
|
|
411
451
|
- **[Stats Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/STATS_MANAGER.md)** - User and global statistics (14 functions)
|
|
412
452
|
- **[Leaderboard Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/LEADERBOARD_MANAGER.md)** - Leaderboard operations (7 functions)
|
|
413
453
|
- **[Friends Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/FRIENDS_MANAGER.md)** - Friends and social features (22 functions)
|
|
414
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)
|
|
415
455
|
- **[Overlay Manager](https://github.com/ArtyProf/steamworks-ffi-node/blob/main/docs/OVERLAY_MANAGER.md)** - Steam overlay control (7 functions)
|
|
416
|
-
- **[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)
|
|
417
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_
|
|
418
459
|
|
|
419
460
|
## Steamworks Integration
|
|
420
461
|
|
|
@@ -422,7 +463,7 @@ This library connects directly to the Steam client and Steamworks SDK:
|
|
|
422
463
|
|
|
423
464
|
```javascript
|
|
424
465
|
// Steamworks API
|
|
425
|
-
const steam =
|
|
466
|
+
const steam = SteamworksSDK.getInstance();
|
|
426
467
|
steam.init({ appId: 480 }); // Connects to actual Steam
|
|
427
468
|
|
|
428
469
|
// Live achievements from Steam servers
|
|
@@ -430,11 +471,12 @@ const achievements = await steam.achievements.getAllAchievements();
|
|
|
430
471
|
console.log(achievements); // Achievement data from your Steam app
|
|
431
472
|
|
|
432
473
|
// Permanent achievement unlock in Steam
|
|
433
|
-
await steam.achievements.unlockAchievement(
|
|
474
|
+
await steam.achievements.unlockAchievement("YOUR_ACHIEVEMENT");
|
|
434
475
|
// ^ This shows up in Steam overlay and is saved permanently
|
|
435
476
|
```
|
|
436
477
|
|
|
437
478
|
**What happens when you unlock an achievement:**
|
|
479
|
+
|
|
438
480
|
- ✅ Steam overlay notification appears
|
|
439
481
|
- ✅ Achievement saved to Steam servers permanently
|
|
440
482
|
- ✅ Syncs across all devices
|
|
@@ -447,22 +489,22 @@ For Electron applications, use it in your main process:
|
|
|
447
489
|
|
|
448
490
|
```typescript
|
|
449
491
|
// main.ts
|
|
450
|
-
import { app } from
|
|
451
|
-
import SteamworksSDK from
|
|
492
|
+
import { app } from "electron";
|
|
493
|
+
import SteamworksSDK from "steamworks-ffi-node";
|
|
452
494
|
|
|
453
495
|
app.whenReady().then(() => {
|
|
454
|
-
const steam =
|
|
455
|
-
|
|
496
|
+
const steam = SteamworksSDK.getInstance();
|
|
497
|
+
|
|
456
498
|
if (steam.init({ appId: YOUR_STEAM_APP_ID })) {
|
|
457
|
-
console.log(
|
|
458
|
-
|
|
499
|
+
console.log("Steam initialized in Electron!");
|
|
500
|
+
|
|
459
501
|
// Handle achievement unlocks from renderer process
|
|
460
502
|
// via IPC if needed
|
|
461
503
|
}
|
|
462
504
|
});
|
|
463
505
|
|
|
464
|
-
app.on(
|
|
465
|
-
const steam =
|
|
506
|
+
app.on("before-quit", () => {
|
|
507
|
+
const steam = SteamworksSDK.getInstance();
|
|
466
508
|
steam.shutdown();
|
|
467
509
|
});
|
|
468
510
|
```
|
|
@@ -475,42 +517,46 @@ The library searches for the SDK in standard locations within your Electron app
|
|
|
475
517
|
|
|
476
518
|
## Requirements
|
|
477
519
|
|
|
478
|
-
- **Node.js**: 18+
|
|
520
|
+
- **Node.js**: 18+
|
|
479
521
|
- **Steam Client**: Must be running and logged in
|
|
480
522
|
- **Steam App ID**: Get yours at [Steamworks Partner](https://partner.steamgames.com/)
|
|
481
523
|
- **steam_appid.txt**: Optional - create in your project root OR pass to `steam.init(appId)`
|
|
482
524
|
|
|
483
525
|
### Platform Support
|
|
526
|
+
|
|
484
527
|
- ✅ **Windows**: steam_api64.dll / steam_api.dll
|
|
485
|
-
- ✅ **macOS**: libsteam_api.dylib
|
|
528
|
+
- ✅ **macOS**: libsteam_api.dylib
|
|
486
529
|
- ✅ **Linux**: libsteam_api.so
|
|
487
530
|
|
|
488
531
|
**Steamworks SDK Version**: v1.62 (Latest)
|
|
489
532
|
|
|
490
|
-
|
|
533
|
+
_Note: You must download and install the SDK redistributables separately as described in the Setup section above._
|
|
491
534
|
|
|
492
535
|
## Troubleshooting
|
|
493
536
|
|
|
494
537
|
### "SteamAPI_Init failed"
|
|
538
|
+
|
|
495
539
|
- ❌ Steam client not running → **Solution**: Start Steam and log in
|
|
496
540
|
- ❌ No App ID specified → **Solution**: Create `steam_appid.txt` in project root OR pass App ID to `steam.init(appId)`
|
|
497
541
|
- ❌ Invalid App ID → **Solution**: Use 480 for testing, or your registered App ID
|
|
498
542
|
|
|
499
543
|
### "Cannot find module 'steamworks-ffi-node'"
|
|
544
|
+
|
|
500
545
|
- ❌ Package not installed → **Solution**: Run `npm install steamworks-ffi-node`
|
|
501
546
|
|
|
502
547
|
### Achievement operations not working
|
|
548
|
+
|
|
503
549
|
- ❌ Not initialized → **Solution**: Call `steam.init({ appId })` first
|
|
504
550
|
- ❌ No achievements configured → **Solution**: Configure achievements in Steamworks Partner site
|
|
505
551
|
- ❌ Using Spacewar → **Note**: Spacewar may not have achievements, use your own App ID
|
|
506
552
|
|
|
507
553
|
### Electron-specific issues
|
|
554
|
+
|
|
508
555
|
- ❌ Initialized in renderer → **Solution**: Only initialize in main process
|
|
509
556
|
- ❌ Not cleaning up → **Solution**: Call `shutdown()` in `before-quit` event
|
|
510
557
|
- ❌ "Steamworks SDK library not found" in packaged app → **Solution**: Include SDK redistributables in your build (see Electron Packaging section above)
|
|
511
558
|
- ❌ Native module errors in packaged app → **Solution**: Ensure Steamworks SDK files are properly included in your app bundle
|
|
512
559
|
|
|
513
|
-
|
|
514
560
|
## How to Support This Project
|
|
515
561
|
|
|
516
562
|
You can support the development of this library by wishlisting, subscribing, and purchasing the app **AFK Companion** on Steam:
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,SAAS,CAAC;AAGpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAGvE,cAAc,SAAS,CAAC;AAGxB,eAAe,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,aAAa,MAAM,SAAS,CAAC;AAGpC,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,uBAAuB,EAAE,MAAM,oCAAoC,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,wBAAwB,EAAE,MAAM,qCAAqC,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,gCAAgC,CAAC;AACrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,8BAA8B,CAAC;AACjE,OAAO,EAAE,oBAAoB,EAAE,MAAM,iCAAiC,CAAC;AAGvE,cAAc,SAAS,CAAC;AAGxB,eAAe,aAAa,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -17,8 +17,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
17
17
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
18
|
};
|
|
19
19
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
-
exports.SteamWorkshopManager = exports.SteamCloudManager = exports.SteamOverlayManager = exports.SteamRichPresenceManager = exports.SteamFriendsManager = exports.SteamLeaderboardManager = exports.SteamStatsManager = exports.SteamAchievementManager = void 0;
|
|
20
|
+
exports.SteamworksSDK = exports.SteamWorkshopManager = exports.SteamCloudManager = exports.SteamOverlayManager = exports.SteamRichPresenceManager = exports.SteamFriendsManager = exports.SteamLeaderboardManager = exports.SteamStatsManager = exports.SteamAchievementManager = void 0;
|
|
21
21
|
const steam_1 = __importDefault(require("./steam"));
|
|
22
|
+
exports.SteamworksSDK = steam_1.default;
|
|
22
23
|
// Export manager classes for advanced usage
|
|
23
24
|
var SteamAchievementManager_1 = require("./internal/SteamAchievementManager");
|
|
24
25
|
Object.defineProperty(exports, "SteamAchievementManager", { enumerable: true, get: function () { return SteamAchievementManager_1.SteamAchievementManager; } });
|
|
@@ -38,6 +39,6 @@ var SteamWorkshopManager_1 = require("./internal/SteamWorkshopManager");
|
|
|
38
39
|
Object.defineProperty(exports, "SteamWorkshopManager", { enumerable: true, get: function () { return SteamWorkshopManager_1.SteamWorkshopManager; } });
|
|
39
40
|
// Export all types from organized structure
|
|
40
41
|
__exportStar(require("./types"), exports);
|
|
41
|
-
// Export main Steam class
|
|
42
|
+
// Export main Steam class as both default and named export
|
|
42
43
|
exports.default = steam_1.default;
|
|
43
44
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,oDAAoC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA,oDAAoC;AAiB3B,wBAjBF,eAAa,CAiBE;AAftB,4CAA4C;AAC5C,8EAA6E;AAApE,kIAAA,uBAAuB,OAAA;AAChC,kEAAiE;AAAxD,sHAAA,iBAAiB,OAAA;AAC1B,8EAA6E;AAApE,kIAAA,uBAAuB,OAAA;AAChC,sEAAqE;AAA5D,0HAAA,mBAAmB,OAAA;AAC5B,gFAA+E;AAAtE,oIAAA,wBAAwB,OAAA;AACjC,sEAAqE;AAA5D,0HAAA,mBAAmB,OAAA;AAC5B,kEAAiE;AAAxD,sHAAA,iBAAiB,OAAA;AAC1B,wEAAuE;AAA9D,4HAAA,oBAAoB,OAAA;AAE7B,4CAA4C;AAC5C,0CAAwB;AAExB,2DAA2D;AAC3D,kBAAe,eAAa,CAAC"}
|