steamworks-ffi-node 0.2.3 → 0.3.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.
@@ -0,0 +1,838 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.SteamStatsManager = void 0;
37
+ const koffi = __importStar(require("koffi"));
38
+ /**
39
+ * SteamStatsManager
40
+ *
41
+ * Manages all Steam user statistics operations including:
42
+ * - User stats (get/set integer and float values)
43
+ * - Average rate stats (for tracking rates over time)
44
+ * - Friend/user stats (view stats of other players)
45
+ * - Global stats (aggregate statistics across all players)
46
+ * - Global stat history (historical data over time)
47
+ *
48
+ * Stats are different from achievements - they are numeric values that can increase/decrease
49
+ * and are used for leaderboards, progress tracking, and game analytics.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * const statsManager = new SteamStatsManager(libraryLoader, apiCore);
54
+ *
55
+ * // Set and get player stats
56
+ * await statsManager.setStatInt('total_kills', 100);
57
+ * const kills = await statsManager.getStatInt('total_kills');
58
+ *
59
+ * // Get global stats
60
+ * await statsManager.requestGlobalStats(7);
61
+ * const globalKills = await statsManager.getGlobalStatInt('total_kills');
62
+ * ```
63
+ */
64
+ class SteamStatsManager {
65
+ /**
66
+ * Creates a new SteamStatsManager instance
67
+ *
68
+ * @param libraryLoader - The Steam library loader for FFI calls
69
+ * @param apiCore - The Steam API core for lifecycle management
70
+ */
71
+ constructor(libraryLoader, apiCore) {
72
+ this.libraryLoader = libraryLoader;
73
+ this.apiCore = apiCore;
74
+ }
75
+ // ========================================
76
+ // User Stats Operations (Get/Set)
77
+ // ========================================
78
+ /**
79
+ * Get an integer stat value for the current user
80
+ *
81
+ * Retrieves a 32-bit integer stat value from Steam. Stats are numeric values
82
+ * that track player progress and can be used for leaderboards and analytics.
83
+ *
84
+ * @param statName - Name of the stat to retrieve (as defined in Steamworks Partner site)
85
+ * @returns The stat value, or null if not found or on error
86
+ *
87
+ * @example
88
+ * ```typescript
89
+ * const totalKills = await statsManager.getStatInt('total_kills');
90
+ * if (totalKills !== null) {
91
+ * console.log(`Total kills: ${totalKills}`);
92
+ * }
93
+ * ```
94
+ *
95
+ * @remarks
96
+ * - Returns null if Steam API is not initialized
97
+ * - Stat names are case-sensitive and must match Steamworks configuration
98
+ * - Use getStatFloat() for decimal values
99
+ *
100
+ * Steamworks SDK Functions:
101
+ * - `SteamAPI_ISteamUserStats_GetStatInt32()` - Get int32 stat value
102
+ */
103
+ async getStatInt(statName) {
104
+ if (!this.apiCore.isInitialized()) {
105
+ console.warn('⚠️ Steam API not initialized');
106
+ return null;
107
+ }
108
+ try {
109
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
110
+ const valueOut = koffi.alloc('int32', 1);
111
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_GetStatInt32(userStatsInterface, statName, valueOut);
112
+ if (success) {
113
+ const value = koffi.decode(valueOut, 'int32');
114
+ console.log(`📊 Got stat "${statName}": ${value}`);
115
+ return value;
116
+ }
117
+ else {
118
+ console.warn(`⚠️ Failed to get stat: ${statName}`);
119
+ return null;
120
+ }
121
+ }
122
+ catch (error) {
123
+ console.error(`❌ Error getting stat "${statName}":`, error.message);
124
+ return null;
125
+ }
126
+ }
127
+ /**
128
+ * Get a float stat value for the current user
129
+ *
130
+ * Retrieves a floating-point stat value from Steam. Use this for stats that
131
+ * require decimal precision (e.g., average accuracy, distance traveled).
132
+ *
133
+ * @param statName - Name of the stat to retrieve (as defined in Steamworks Partner site)
134
+ * @returns The stat value, or null if not found or on error
135
+ *
136
+ * @example
137
+ * ```typescript
138
+ * const accuracy = await statsManager.getStatFloat('shooting_accuracy');
139
+ * if (accuracy !== null) {
140
+ * console.log(`Accuracy: ${(accuracy * 100).toFixed(2)}%`);
141
+ * }
142
+ * ```
143
+ *
144
+ * @remarks
145
+ * - Returns null if Steam API is not initialized
146
+ * - Use getStatInt() for whole number values
147
+ *
148
+ * Steamworks SDK Functions:
149
+ * - `SteamAPI_ISteamUserStats_GetStatFloat()` - Get float stat value
150
+ */
151
+ async getStatFloat(statName) {
152
+ if (!this.apiCore.isInitialized()) {
153
+ console.warn('⚠️ Steam API not initialized');
154
+ return null;
155
+ }
156
+ try {
157
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
158
+ const valueOut = koffi.alloc('float', 1);
159
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_GetStatFloat(userStatsInterface, statName, valueOut);
160
+ if (success) {
161
+ const value = koffi.decode(valueOut, 'float');
162
+ console.log(`📊 Got stat "${statName}": ${value}`);
163
+ return value;
164
+ }
165
+ else {
166
+ console.warn(`⚠️ Failed to get stat: ${statName}`);
167
+ return null;
168
+ }
169
+ }
170
+ catch (error) {
171
+ console.error(`❌ Error getting stat "${statName}":`, error.message);
172
+ return null;
173
+ }
174
+ }
175
+ /**
176
+ * Set an integer stat value for the current user
177
+ *
178
+ * Updates a 32-bit integer stat value and immediately stores it to Steam servers.
179
+ * The new value will be visible in your Steam profile and can trigger achievements.
180
+ *
181
+ * @param statName - Name of the stat to set (as defined in Steamworks Partner site)
182
+ * @param value - Integer value to set
183
+ * @returns true if successful, false otherwise
184
+ *
185
+ * @example
186
+ * ```typescript
187
+ * // Increment kill count
188
+ * const currentKills = await statsManager.getStatInt('total_kills') || 0;
189
+ * const success = await statsManager.setStatInt('total_kills', currentKills + 1);
190
+ * if (success) {
191
+ * console.log('Kill count updated!');
192
+ * }
193
+ * ```
194
+ *
195
+ * @remarks
196
+ * - Automatically calls StoreStats() to save to Steam servers
197
+ * - Runs callbacks to process the store operation
198
+ * - Can trigger stat-based achievements
199
+ * - Use setStatFloat() for decimal values
200
+ *
201
+ * Steamworks SDK Functions:
202
+ * - `SteamAPI_ISteamUserStats_SetStatInt32()` - Set int32 stat value
203
+ * - `SteamAPI_ISteamUserStats_StoreStats()` - Store stats to Steam servers
204
+ * - `SteamAPI_RunCallbacks()` - Process Steam callbacks
205
+ */
206
+ async setStatInt(statName, value) {
207
+ if (!this.apiCore.isInitialized()) {
208
+ console.warn('⚠️ Steam API not initialized');
209
+ return false;
210
+ }
211
+ try {
212
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
213
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_SetStatInt32(userStatsInterface, statName, value);
214
+ if (success) {
215
+ // Store the stats to Steam servers
216
+ const stored = this.libraryLoader.SteamAPI_ISteamUserStats_StoreStats(userStatsInterface);
217
+ if (stored) {
218
+ console.log(`✅ Set stat "${statName}" to ${value}`);
219
+ // Process callbacks
220
+ this.libraryLoader.SteamAPI_RunCallbacks();
221
+ return true;
222
+ }
223
+ else {
224
+ console.warn(`⚠️ Failed to store stat: ${statName}`);
225
+ return false;
226
+ }
227
+ }
228
+ else {
229
+ console.warn(`⚠️ Failed to set stat: ${statName}`);
230
+ return false;
231
+ }
232
+ }
233
+ catch (error) {
234
+ console.error(`❌ Error setting stat "${statName}":`, error.message);
235
+ return false;
236
+ }
237
+ }
238
+ /**
239
+ * Set a float stat value for the current user
240
+ *
241
+ * Updates a floating-point stat value and immediately stores it to Steam servers.
242
+ * Use this for stats requiring decimal precision.
243
+ *
244
+ * @param statName - Name of the stat to set (as defined in Steamworks Partner site)
245
+ * @param value - Float value to set
246
+ * @returns true if successful, false otherwise
247
+ *
248
+ * @example
249
+ * ```typescript
250
+ * // Update accuracy based on hits/shots
251
+ * const hits = 85;
252
+ * const shots = 100;
253
+ * const accuracy = hits / shots; // 0.85
254
+ * await statsManager.setStatFloat('shooting_accuracy', accuracy);
255
+ * ```
256
+ *
257
+ * @remarks
258
+ * - Automatically calls StoreStats() to save to Steam servers
259
+ * - Runs callbacks to process the store operation
260
+ * - Use setStatInt() for whole number values
261
+ *
262
+ * Steamworks SDK Functions:
263
+ * - `SteamAPI_ISteamUserStats_SetStatFloat()` - Set float stat value
264
+ * - `SteamAPI_ISteamUserStats_StoreStats()` - Store stats to Steam servers
265
+ * - `SteamAPI_RunCallbacks()` - Process Steam callbacks
266
+ */
267
+ async setStatFloat(statName, value) {
268
+ if (!this.apiCore.isInitialized()) {
269
+ console.warn('⚠️ Steam API not initialized');
270
+ return false;
271
+ }
272
+ try {
273
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
274
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_SetStatFloat(userStatsInterface, statName, value);
275
+ if (success) {
276
+ // Store the stats to Steam servers
277
+ const stored = this.libraryLoader.SteamAPI_ISteamUserStats_StoreStats(userStatsInterface);
278
+ if (stored) {
279
+ console.log(`✅ Set stat "${statName}" to ${value}`);
280
+ // Process callbacks
281
+ this.libraryLoader.SteamAPI_RunCallbacks();
282
+ return true;
283
+ }
284
+ else {
285
+ console.warn(`⚠️ Failed to store stat: ${statName}`);
286
+ return false;
287
+ }
288
+ }
289
+ else {
290
+ console.warn(`⚠️ Failed to set stat: ${statName}`);
291
+ return false;
292
+ }
293
+ }
294
+ catch (error) {
295
+ console.error(`❌ Error setting stat "${statName}":`, error.message);
296
+ return false;
297
+ }
298
+ }
299
+ /**
300
+ * Update an average rate stat
301
+ *
302
+ * Updates stats that represent rates or averages over time (e.g., "kills per hour",
303
+ * "average speed"). Steam automatically maintains the average calculation.
304
+ *
305
+ * @param statName - Name of the stat to update (as defined in Steamworks Partner site)
306
+ * @param countThisSession - Count/value for this session (e.g., kills this session)
307
+ * @param sessionLength - Length of session in seconds
308
+ * @returns true if successful, false otherwise
309
+ *
310
+ * @example
311
+ * ```typescript
312
+ * // Update kills per hour stat
313
+ * const sessionKills = 25;
314
+ * const sessionSeconds = 1800; // 30 minutes
315
+ * await statsManager.updateAvgRateStat('kills_per_hour', sessionKills, sessionSeconds);
316
+ * // Steam calculates: (25 / 1800) * 3600 = 50 kills/hour
317
+ * ```
318
+ *
319
+ * @remarks
320
+ * - Automatically calls StoreStats() to save to Steam servers
321
+ * - Steam maintains the running average across all sessions
322
+ * - sessionLength should be in seconds
323
+ * - Used for "per hour" or "per game" statistics
324
+ *
325
+ * Steamworks SDK Functions:
326
+ * - `SteamAPI_ISteamUserStats_UpdateAvgRateStat()` - Update average rate stat
327
+ * - `SteamAPI_ISteamUserStats_StoreStats()` - Store stats to Steam servers
328
+ * - `SteamAPI_RunCallbacks()` - Process Steam callbacks
329
+ */
330
+ async updateAvgRateStat(statName, countThisSession, sessionLength) {
331
+ if (!this.apiCore.isInitialized()) {
332
+ console.warn('⚠️ Steam API not initialized');
333
+ return false;
334
+ }
335
+ try {
336
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
337
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_UpdateAvgRateStat(userStatsInterface, statName, countThisSession, sessionLength);
338
+ if (success) {
339
+ // Store the stats to Steam servers
340
+ const stored = this.libraryLoader.SteamAPI_ISteamUserStats_StoreStats(userStatsInterface);
341
+ if (stored) {
342
+ console.log(`✅ Updated avg rate stat "${statName}": ${countThisSession} over ${sessionLength}s`);
343
+ // Process callbacks
344
+ this.libraryLoader.SteamAPI_RunCallbacks();
345
+ return true;
346
+ }
347
+ else {
348
+ console.warn(`⚠️ Failed to store stat: ${statName}`);
349
+ return false;
350
+ }
351
+ }
352
+ else {
353
+ console.warn(`⚠️ Failed to update avg rate stat: ${statName}`);
354
+ return false;
355
+ }
356
+ }
357
+ catch (error) {
358
+ console.error(`❌ Error updating avg rate stat "${statName}":`, error.message);
359
+ return false;
360
+ }
361
+ }
362
+ // ========================================
363
+ // Friend/User Stats Operations
364
+ // ========================================
365
+ /**
366
+ * Request stats for another user (friend)
367
+ *
368
+ * Requests stat data from Steam servers for a specific user. Must be called
369
+ * before getting user stats with getUserStatInt() or getUserStatFloat().
370
+ * This is an asynchronous operation - wait a moment before reading the stats.
371
+ *
372
+ * @param steamId - Steam ID of the user (as string or BigInt)
373
+ * @returns true if request was sent successfully, false otherwise
374
+ *
375
+ * @example
376
+ * ```typescript
377
+ * const friendSteamId = '76561198012345678';
378
+ *
379
+ * // Request the friend's stats
380
+ * const success = await statsManager.requestUserStats(friendSteamId);
381
+ * if (success) {
382
+ * // Wait a moment for Steam to fetch the data
383
+ * await new Promise(resolve => setTimeout(resolve, 100));
384
+ *
385
+ * // Now get the friend's stats
386
+ * const friendKills = await statsManager.getUserStatInt(friendSteamId, 'total_kills');
387
+ * console.log(`Friend has ${friendKills} kills`);
388
+ * }
389
+ * ```
390
+ *
391
+ * @remarks
392
+ * - Must be called before getting user stats
393
+ * - Returns true immediately if request was sent (not when data arrives)
394
+ * - Wait 50-100ms after requesting before reading stats
395
+ * - User's stats must be public for this to work
396
+ *
397
+ * Steamworks SDK Functions:
398
+ * - `SteamAPI_ISteamUserStats_RequestUserStats()` - Request user stats from Steam servers
399
+ */
400
+ async requestUserStats(steamId) {
401
+ if (!this.apiCore.isInitialized()) {
402
+ console.warn('⚠️ Steam API not initialized');
403
+ return false;
404
+ }
405
+ try {
406
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
407
+ const steamIdBigInt = typeof steamId === 'string' ? BigInt(steamId) : steamId;
408
+ const callHandle = this.libraryLoader.SteamAPI_ISteamUserStats_RequestUserStats(userStatsInterface, steamIdBigInt);
409
+ if (callHandle !== BigInt(0)) {
410
+ console.log(`📡 Requested stats for user: ${steamId}`);
411
+ return true;
412
+ }
413
+ else {
414
+ console.warn(`⚠️ Failed to request user stats: ${steamId}`);
415
+ return false;
416
+ }
417
+ }
418
+ catch (error) {
419
+ console.error(`❌ Error requesting user stats:`, error.message);
420
+ return false;
421
+ }
422
+ }
423
+ /**
424
+ * Get an integer stat value for another user (friend)
425
+ *
426
+ * Retrieves a 32-bit integer stat value for a specific user. Must call
427
+ * requestUserStats() first and wait for the data to arrive.
428
+ *
429
+ * @param steamId - Steam ID of the user
430
+ * @param statName - Name of the stat to retrieve
431
+ * @returns The stat value, or null if not found or on error
432
+ *
433
+ * @example
434
+ * ```typescript
435
+ * // Compare your kills to a friend's
436
+ * const friendId = '76561198012345678';
437
+ * await statsManager.requestUserStats(friendId);
438
+ * await new Promise(resolve => setTimeout(resolve, 100));
439
+ *
440
+ * const myKills = await statsManager.getStatInt('total_kills') || 0;
441
+ * const friendKills = await statsManager.getUserStatInt(friendId, 'total_kills') || 0;
442
+ *
443
+ * console.log(`You: ${myKills}, Friend: ${friendKills}`);
444
+ * ```
445
+ *
446
+ * @remarks
447
+ * - Must call requestUserStats() first
448
+ * - Wait 50-100ms after requesting before calling this
449
+ * - Returns null if stats haven't arrived yet or user's stats are private
450
+ *
451
+ * Steamworks SDK Functions:
452
+ * - `SteamAPI_ISteamUserStats_GetUserStatInt32()` - Get user's int32 stat value
453
+ */
454
+ async getUserStatInt(steamId, statName) {
455
+ if (!this.apiCore.isInitialized()) {
456
+ console.warn('⚠️ Steam API not initialized');
457
+ return null;
458
+ }
459
+ try {
460
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
461
+ const steamIdBigInt = typeof steamId === 'string' ? BigInt(steamId) : steamId;
462
+ const valueOut = koffi.alloc('int32', 1);
463
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_GetUserStatInt32(userStatsInterface, steamIdBigInt, statName, valueOut);
464
+ if (success) {
465
+ const value = koffi.decode(valueOut, 'int32');
466
+ console.log(`📊 Got user stat "${statName}" for ${steamId}: ${value}`);
467
+ return value;
468
+ }
469
+ else {
470
+ console.warn(`⚠️ Failed to get user stat: ${statName}`);
471
+ return null;
472
+ }
473
+ }
474
+ catch (error) {
475
+ console.error(`❌ Error getting user stat "${statName}":`, error.message);
476
+ return null;
477
+ }
478
+ }
479
+ /**
480
+ * Get a float stat value for another user (friend)
481
+ *
482
+ * Retrieves a floating-point stat value for a specific user. Must call
483
+ * requestUserStats() first and wait for the data to arrive.
484
+ *
485
+ * @param steamId - Steam ID of the user
486
+ * @param statName - Name of the stat to retrieve
487
+ * @returns The stat value, or null if not found or on error
488
+ *
489
+ * @example
490
+ * ```typescript
491
+ * // Compare accuracy with a friend
492
+ * const friendId = '76561198012345678';
493
+ * await statsManager.requestUserStats(friendId);
494
+ * await new Promise(resolve => setTimeout(resolve, 100));
495
+ *
496
+ * const friendAccuracy = await statsManager.getUserStatFloat(friendId, 'shooting_accuracy');
497
+ * if (friendAccuracy !== null) {
498
+ * console.log(`Friend accuracy: ${(friendAccuracy * 100).toFixed(2)}%`);
499
+ * }
500
+ * ```
501
+ *
502
+ * @remarks
503
+ * - Must call requestUserStats() first
504
+ * - Wait 50-100ms after requesting before calling this
505
+ * - Returns null if stats haven't arrived yet or user's stats are private
506
+ *
507
+ * Steamworks SDK Functions:
508
+ * - `SteamAPI_ISteamUserStats_GetUserStatFloat()` - Get user's float stat value
509
+ */
510
+ async getUserStatFloat(steamId, statName) {
511
+ if (!this.apiCore.isInitialized()) {
512
+ console.warn('⚠️ Steam API not initialized');
513
+ return null;
514
+ }
515
+ try {
516
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
517
+ const steamIdBigInt = typeof steamId === 'string' ? BigInt(steamId) : steamId;
518
+ const valueOut = koffi.alloc('float', 1);
519
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_GetUserStatFloat(userStatsInterface, steamIdBigInt, statName, valueOut);
520
+ if (success) {
521
+ const value = koffi.decode(valueOut, 'float');
522
+ console.log(`📊 Got user stat "${statName}" for ${steamId}: ${value}`);
523
+ return value;
524
+ }
525
+ else {
526
+ console.warn(`⚠️ Failed to get user stat: ${statName}`);
527
+ return null;
528
+ }
529
+ }
530
+ catch (error) {
531
+ console.error(`❌ Error getting user stat "${statName}":`, error.message);
532
+ return null;
533
+ }
534
+ }
535
+ // ========================================
536
+ // Global Stats Operations
537
+ // ========================================
538
+ /**
539
+ * Request global stats data from Steam
540
+ *
541
+ * Requests aggregated statistics across all players from Steam servers.
542
+ * Optionally includes historical data for trend analysis.
543
+ * Must be called before getting global stats.
544
+ *
545
+ * @param historyDays - Number of days of history to retrieve (0-60, default: 0)
546
+ * @returns true if request was sent successfully, false otherwise
547
+ *
548
+ * @example
549
+ * ```typescript
550
+ * // Request current global stats (no history)
551
+ * await statsManager.requestGlobalStats(0);
552
+ *
553
+ * // Request with 7 days of history for trends
554
+ * await statsManager.requestGlobalStats(7);
555
+ * await new Promise(resolve => setTimeout(resolve, 100));
556
+ *
557
+ * const totalKills = await statsManager.getGlobalStatInt('total_kills');
558
+ * console.log(`All players combined: ${totalKills} kills`);
559
+ * ```
560
+ *
561
+ * @remarks
562
+ * - Must be called before getting global stats
563
+ * - Returns true immediately if request was sent (not when data arrives)
564
+ * - Wait 50-100ms after requesting before reading stats
565
+ * - historyDays is automatically clamped to 0-60 range
566
+ * - Historical data allows tracking trends over time
567
+ *
568
+ * Steamworks SDK Functions:
569
+ * - `SteamAPI_ISteamUserStats_RequestGlobalStats()` - Request global stats from Steam servers
570
+ */
571
+ async requestGlobalStats(historyDays = 0) {
572
+ if (!this.apiCore.isInitialized()) {
573
+ console.warn('⚠️ Steam API not initialized');
574
+ return false;
575
+ }
576
+ try {
577
+ // Limit history days to 0-60
578
+ const days = Math.max(0, Math.min(60, historyDays));
579
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
580
+ const callHandle = this.libraryLoader.SteamAPI_ISteamUserStats_RequestGlobalStats(userStatsInterface, days);
581
+ if (callHandle !== BigInt(0)) {
582
+ console.log(`📡 Requested global stats with ${days} days of history`);
583
+ return true;
584
+ }
585
+ else {
586
+ console.warn(`⚠️ Failed to request global stats`);
587
+ return false;
588
+ }
589
+ }
590
+ catch (error) {
591
+ console.error(`❌ Error requesting global stats:`, error.message);
592
+ return false;
593
+ }
594
+ }
595
+ /**
596
+ * Get a global stat value (64-bit integer)
597
+ *
598
+ * Retrieves an aggregated integer stat value across all players.
599
+ * Must call requestGlobalStats() first and wait for the data to arrive.
600
+ *
601
+ * @param statName - Name of the global stat to retrieve
602
+ * @returns The stat value as BigInt, or null if not found or on error
603
+ *
604
+ * @example
605
+ * ```typescript
606
+ * // Get total kills across all players
607
+ * await statsManager.requestGlobalStats();
608
+ * await new Promise(resolve => setTimeout(resolve, 100));
609
+ *
610
+ * const globalKills = await statsManager.getGlobalStatInt('total_kills');
611
+ * if (globalKills !== null) {
612
+ * console.log(`All players have ${globalKills} total kills`);
613
+ * }
614
+ * ```
615
+ *
616
+ * @remarks
617
+ * - Returns BigInt for large numbers (can exceed JavaScript's safe integer range)
618
+ * - Must call requestGlobalStats() first
619
+ * - Wait 50-100ms after requesting before calling this
620
+ * - Useful for tracking game-wide statistics
621
+ *
622
+ * Steamworks SDK Functions:
623
+ * - `SteamAPI_ISteamUserStats_GetGlobalStatInt64()` - Get global int64 stat value
624
+ */
625
+ async getGlobalStatInt(statName) {
626
+ if (!this.apiCore.isInitialized()) {
627
+ console.warn('⚠️ Steam API not initialized');
628
+ return null;
629
+ }
630
+ try {
631
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
632
+ const valueOut = koffi.alloc('int64', 1);
633
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_GetGlobalStatInt64(userStatsInterface, statName, valueOut);
634
+ if (success) {
635
+ const value = koffi.decode(valueOut, 'int64');
636
+ console.log(`🌍 Got global stat "${statName}": ${value}`);
637
+ return value;
638
+ }
639
+ else {
640
+ console.warn(`⚠️ Failed to get global stat: ${statName}`);
641
+ return null;
642
+ }
643
+ }
644
+ catch (error) {
645
+ console.error(`❌ Error getting global stat "${statName}":`, error.message);
646
+ return null;
647
+ }
648
+ }
649
+ /**
650
+ * Get a global stat value (double-precision float)
651
+ *
652
+ * Retrieves an aggregated floating-point stat value across all players.
653
+ * Must call requestGlobalStats() first and wait for the data to arrive.
654
+ * Use this for averages or stats requiring decimal precision.
655
+ *
656
+ * @param statName - Name of the global stat to retrieve
657
+ * @returns The stat value, or null if not found or on error
658
+ *
659
+ * @example
660
+ * ```typescript
661
+ * // Get average accuracy across all players
662
+ * await statsManager.requestGlobalStats();
663
+ * await new Promise(resolve => setTimeout(resolve, 100));
664
+ *
665
+ * const avgAccuracy = await statsManager.getGlobalStatDouble('average_accuracy');
666
+ * if (avgAccuracy !== null) {
667
+ * console.log(`Global average accuracy: ${(avgAccuracy * 100).toFixed(2)}%`);
668
+ * }
669
+ * ```
670
+ *
671
+ * @remarks
672
+ * - Use for stats requiring decimal precision
673
+ * - Must call requestGlobalStats() first
674
+ * - Wait 50-100ms after requesting before calling this
675
+ * - Perfect for calculating game-wide averages
676
+ *
677
+ * Steamworks SDK Functions:
678
+ * - `SteamAPI_ISteamUserStats_GetGlobalStatDouble()` - Get global double stat value
679
+ */
680
+ async getGlobalStatDouble(statName) {
681
+ if (!this.apiCore.isInitialized()) {
682
+ console.warn('⚠️ Steam API not initialized');
683
+ return null;
684
+ }
685
+ try {
686
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
687
+ const valueOut = koffi.alloc('double', 1);
688
+ const success = this.libraryLoader.SteamAPI_ISteamUserStats_GetGlobalStatDouble(userStatsInterface, statName, valueOut);
689
+ if (success) {
690
+ const value = koffi.decode(valueOut, 'double');
691
+ console.log(`🌍 Got global stat "${statName}": ${value}`);
692
+ return value;
693
+ }
694
+ else {
695
+ console.warn(`⚠️ Failed to get global stat: ${statName}`);
696
+ return null;
697
+ }
698
+ }
699
+ catch (error) {
700
+ console.error(`❌ Error getting global stat "${statName}":`, error.message);
701
+ return null;
702
+ }
703
+ }
704
+ /**
705
+ * Get global stat history for an integer stat
706
+ *
707
+ * Retrieves historical daily values for a global stat. Array index [0] is today,
708
+ * [1] is yesterday, etc. Useful for tracking trends and creating graphs.
709
+ *
710
+ * @param statName - Name of the global stat
711
+ * @param days - Number of days of history to retrieve (1-60, default: 7)
712
+ * @returns Array of daily BigInt values, or null if error
713
+ *
714
+ * @example
715
+ * ```typescript
716
+ * // Get 7 days of global kill history
717
+ * await statsManager.requestGlobalStats(7);
718
+ * await new Promise(resolve => setTimeout(resolve, 100));
719
+ *
720
+ * const history = await statsManager.getGlobalStatHistoryInt('total_kills', 7);
721
+ * if (history) {
722
+ * console.log('Kill history (newest to oldest):');
723
+ * history.forEach((kills, index) => {
724
+ * const daysAgo = index === 0 ? 'today' : `${index} days ago`;
725
+ * console.log(` ${daysAgo}: ${kills} kills`);
726
+ * });
727
+ * }
728
+ * ```
729
+ *
730
+ * @remarks
731
+ * - Array index [0] = today, [1] = yesterday, etc.
732
+ * - Must call requestGlobalStats(days) first with same or greater number of days
733
+ * - Returns BigInt values for large numbers
734
+ * - Maximum 60 days of history (automatically clamped)
735
+ * - Perfect for trend analysis and visualizations
736
+ *
737
+ * Steamworks SDK Functions:
738
+ * - `SteamAPI_ISteamUserStats_GetGlobalStatHistoryInt64()` - Get historical int64 values
739
+ */
740
+ async getGlobalStatHistoryInt(statName, days = 7) {
741
+ if (!this.apiCore.isInitialized()) {
742
+ console.warn('⚠️ Steam API not initialized');
743
+ return null;
744
+ }
745
+ try {
746
+ // Limit to 60 days
747
+ const numDays = Math.max(1, Math.min(60, days));
748
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
749
+ const historyOut = koffi.alloc('int64', numDays);
750
+ const elementsReturned = this.libraryLoader.SteamAPI_ISteamUserStats_GetGlobalStatHistoryInt64(userStatsInterface, statName, historyOut, numDays);
751
+ if (elementsReturned > 0) {
752
+ const history = [];
753
+ for (let i = 0; i < elementsReturned; i++) {
754
+ history.push(koffi.decode(historyOut, 'int64', i));
755
+ }
756
+ console.log(`🌍 Got ${elementsReturned} days of history for "${statName}"`);
757
+ return history;
758
+ }
759
+ else {
760
+ console.warn(`⚠️ Failed to get global stat history: ${statName}`);
761
+ return null;
762
+ }
763
+ }
764
+ catch (error) {
765
+ console.error(`❌ Error getting global stat history "${statName}":`, error.message);
766
+ return null;
767
+ }
768
+ }
769
+ /**
770
+ * Get global stat history for a floating-point stat
771
+ *
772
+ * Retrieves historical daily values for a global stat with decimal precision.
773
+ * Array index [0] is today, [1] is yesterday, etc. Ideal for tracking averages
774
+ * and rates over time.
775
+ *
776
+ * @param statName - Name of the global stat
777
+ * @param days - Number of days of history to retrieve (1-60, default: 7)
778
+ * @returns Array of daily float values, or null if error
779
+ *
780
+ * @example
781
+ * ```typescript
782
+ * // Track average accuracy trend over 30 days
783
+ * await statsManager.requestGlobalStats(30);
784
+ * await new Promise(resolve => setTimeout(resolve, 100));
785
+ *
786
+ * const history = await statsManager.getGlobalStatHistoryDouble('average_accuracy', 30);
787
+ * if (history) {
788
+ * console.log('Accuracy trend:');
789
+ * history.forEach((accuracy, index) => {
790
+ * if (index % 7 === 0) { // Weekly intervals
791
+ * console.log(` Week ${index/7}: ${(accuracy * 100).toFixed(2)}%`);
792
+ * }
793
+ * });
794
+ * }
795
+ * ```
796
+ *
797
+ * @remarks
798
+ * - Array index [0] = today, [1] = yesterday, etc.
799
+ * - Must call requestGlobalStats(days) first with same or greater number of days
800
+ * - Returns floating-point values for decimal precision
801
+ * - Maximum 60 days of history (automatically clamped)
802
+ * - Perfect for tracking averages, rates, and percentages over time
803
+ *
804
+ * Steamworks SDK Functions:
805
+ * - `SteamAPI_ISteamUserStats_GetGlobalStatHistoryDouble()` - Get historical double values
806
+ */
807
+ async getGlobalStatHistoryDouble(statName, days = 7) {
808
+ if (!this.apiCore.isInitialized()) {
809
+ console.warn('⚠️ Steam API not initialized');
810
+ return null;
811
+ }
812
+ try {
813
+ // Limit to 60 days
814
+ const numDays = Math.max(1, Math.min(60, days));
815
+ const userStatsInterface = this.libraryLoader.SteamAPI_SteamUserStats_v013();
816
+ const historyOut = koffi.alloc('double', numDays);
817
+ const elementsReturned = this.libraryLoader.SteamAPI_ISteamUserStats_GetGlobalStatHistoryDouble(userStatsInterface, statName, historyOut, numDays);
818
+ if (elementsReturned > 0) {
819
+ const history = [];
820
+ for (let i = 0; i < elementsReturned; i++) {
821
+ history.push(koffi.decode(historyOut, 'double', i));
822
+ }
823
+ console.log(`🌍 Got ${elementsReturned} days of history for "${statName}"`);
824
+ return history;
825
+ }
826
+ else {
827
+ console.warn(`⚠️ Failed to get global stat history: ${statName}`);
828
+ return null;
829
+ }
830
+ }
831
+ catch (error) {
832
+ console.error(`❌ Error getting global stat history "${statName}":`, error.message);
833
+ return null;
834
+ }
835
+ }
836
+ }
837
+ exports.SteamStatsManager = SteamStatsManager;
838
+ //# sourceMappingURL=SteamStatsManager.js.map