@juande_pl/rocketleague-stats-api-client 0.0.0-stage → 1.0.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/LICENSE +21 -0
- package/README.md +176 -2
- package/dist/chunk-IUPCJADJ.js +182 -0
- package/dist/chunk-IUPCJADJ.js.map +1 -0
- package/dist/chunk-IVUQE3LJ.js +726 -0
- package/dist/chunk-IVUQE3LJ.js.map +1 -0
- package/dist/config-3VSG3YBY.js +23 -0
- package/dist/config-3VSG3YBY.js.map +1 -0
- package/dist/index.cjs +1026 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +918 -0
- package/dist/index.d.ts +918 -0
- package/dist/index.js +142 -0
- package/dist/index.js.map +1 -0
- package/dist/node.cjs +1047 -0
- package/dist/node.cjs.map +1 -0
- package/dist/node.d.cts +1 -0
- package/dist/node.d.ts +1 -0
- package/dist/node.js +162 -0
- package/dist/node.js.map +1 -0
- package/package.json +78 -6
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,918 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared structures used across Stats API events.
|
|
3
|
+
*
|
|
4
|
+
* Field visibility markers used in the docs below (mirroring the official documentation):
|
|
5
|
+
* - `CONDITIONAL`: only present when relevant.
|
|
6
|
+
* - `SPECTATOR`: only sent while spectating.
|
|
7
|
+
* - `REPLAY`: only relevant during replays.
|
|
8
|
+
*
|
|
9
|
+
* @see https://www.rocketleague.com/developer/stats-api
|
|
10
|
+
*/
|
|
11
|
+
/** Team index. `0` = Blue, `1` = Orange. */
|
|
12
|
+
type TeamNum = number;
|
|
13
|
+
/** Well-known team indexes. */
|
|
14
|
+
declare const Team: {
|
|
15
|
+
readonly Blue: 0;
|
|
16
|
+
readonly Orange: 1;
|
|
17
|
+
};
|
|
18
|
+
/** Value of `Game.Ball.TeamNum` when the ball has not been touched yet. */
|
|
19
|
+
declare const BALL_UNTOUCHED_TEAM_NUM = 255;
|
|
20
|
+
/** A 3D world position in Unreal Units. */
|
|
21
|
+
interface Vector {
|
|
22
|
+
X: number;
|
|
23
|
+
Y: number;
|
|
24
|
+
Z: number;
|
|
25
|
+
}
|
|
26
|
+
/** Minimal reference to a player, used by most events. */
|
|
27
|
+
interface PlayerRef {
|
|
28
|
+
/** Display name. */
|
|
29
|
+
Name: string;
|
|
30
|
+
/** Spectator shortcut number. */
|
|
31
|
+
Shortcut: number;
|
|
32
|
+
/** Team index (0 = Blue, 1 = Orange). */
|
|
33
|
+
TeamNum: TeamNum;
|
|
34
|
+
}
|
|
35
|
+
/** The last touch of the ball before an event (goal, crossbar hit...). */
|
|
36
|
+
interface BallLastTouch {
|
|
37
|
+
/** The player who made the last touch. */
|
|
38
|
+
Player: PlayerRef;
|
|
39
|
+
/** Speed of the ball resulting from this touch. */
|
|
40
|
+
Speed: number;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Platform identifier in the format `Platform|Uid|Splitscreen`
|
|
44
|
+
* (e.g. `"Steam|123|0"`, `"Epic|456|0"`).
|
|
45
|
+
*/
|
|
46
|
+
type PrimaryId = string;
|
|
47
|
+
/** Full player state, sent in every `UpdateState` tick. */
|
|
48
|
+
interface Player extends PlayerRef {
|
|
49
|
+
/** Platform identifier in the format `Platform|Uid|Splitscreen`. */
|
|
50
|
+
PrimaryId: PrimaryId;
|
|
51
|
+
/** Total match score. */
|
|
52
|
+
Score: number;
|
|
53
|
+
/** Goals scored this match. */
|
|
54
|
+
Goals: number;
|
|
55
|
+
/** Shot attempts this match. */
|
|
56
|
+
Shots: number;
|
|
57
|
+
/** Assists earned this match. */
|
|
58
|
+
Assists: number;
|
|
59
|
+
/** Saves made this match. */
|
|
60
|
+
Saves: number;
|
|
61
|
+
/** Total ball touches. */
|
|
62
|
+
Touches: number;
|
|
63
|
+
/** Touches by the car body (not ball). */
|
|
64
|
+
CarTouches: number;
|
|
65
|
+
/** Demolitions inflicted. */
|
|
66
|
+
Demos: number;
|
|
67
|
+
/**
|
|
68
|
+
* Asset names of the equipped loadout for the player's current team, sorted by product slot.
|
|
69
|
+
* Typed as optional because older game builds did not send it.
|
|
70
|
+
*/
|
|
71
|
+
Loadout?: string[];
|
|
72
|
+
/** `SPECTATOR` True if the player currently has a vehicle. */
|
|
73
|
+
bHasCar?: boolean;
|
|
74
|
+
/** `SPECTATOR` Vehicle speed in Unreal Units/second. */
|
|
75
|
+
Speed?: number;
|
|
76
|
+
/** `SPECTATOR` Boost amount 0–100. */
|
|
77
|
+
Boost?: number;
|
|
78
|
+
/** `SPECTATOR` True if the player is currently boosting. */
|
|
79
|
+
bBoosting?: boolean;
|
|
80
|
+
/** `SPECTATOR` True if at least 3 wheels are touching the world. */
|
|
81
|
+
bOnGround?: boolean;
|
|
82
|
+
/** `SPECTATOR` True if the vehicle is on a wall. */
|
|
83
|
+
bOnWall?: boolean;
|
|
84
|
+
/** `SPECTATOR` True if the player is holding handbrake. */
|
|
85
|
+
bPowersliding?: boolean;
|
|
86
|
+
/** `SPECTATOR` True if the vehicle is currently destroyed. */
|
|
87
|
+
bDemolished?: boolean;
|
|
88
|
+
/** `SPECTATOR` True if the vehicle is at supersonic speed. */
|
|
89
|
+
bSupersonic?: boolean;
|
|
90
|
+
/**
|
|
91
|
+
* `SPECTATOR` Class name of the player's currently available Rumble pickup
|
|
92
|
+
* (e.g. `"SpecialPickup_GrapplingHook_TA"`). Omitted when not playing with rumble powerups.
|
|
93
|
+
*/
|
|
94
|
+
PickupClass?: string;
|
|
95
|
+
/** `CONDITIONAL` The player who demolished this player. Present only when demolished. */
|
|
96
|
+
Attacker?: PlayerRef;
|
|
97
|
+
}
|
|
98
|
+
/** A team entry inside `UpdateState.Game.Teams`. */
|
|
99
|
+
interface GameTeam {
|
|
100
|
+
/** Team name. */
|
|
101
|
+
Name: string;
|
|
102
|
+
/** Team index. */
|
|
103
|
+
TeamNum: TeamNum;
|
|
104
|
+
/** Team goal count. */
|
|
105
|
+
Score: number;
|
|
106
|
+
/** Hex color code (no `#`) for the team's primary color, e.g. `"0000FF"`. */
|
|
107
|
+
ColorPrimary: string;
|
|
108
|
+
/** Hex color code (no `#`) for the team's secondary color. */
|
|
109
|
+
ColorSecondary: string;
|
|
110
|
+
}
|
|
111
|
+
/** Current ball state inside `UpdateState.Game.Ball`. */
|
|
112
|
+
interface GameBall {
|
|
113
|
+
/** Current ball speed in Unreal Units/second. */
|
|
114
|
+
Speed: number;
|
|
115
|
+
/** Index of the last team to touch the ball. `255` if the ball has not been touched. */
|
|
116
|
+
TeamNum: TeamNum;
|
|
117
|
+
}
|
|
118
|
+
/** Match metadata, sent in every `UpdateState` tick. */
|
|
119
|
+
interface Game {
|
|
120
|
+
/** One entry per team, ordered by TeamNum. */
|
|
121
|
+
Teams: GameTeam[];
|
|
122
|
+
/**
|
|
123
|
+
* Playlist Id of the current match.
|
|
124
|
+
* Typed as optional because older game builds did not send it.
|
|
125
|
+
*/
|
|
126
|
+
PlaylistId?: number;
|
|
127
|
+
/** Seconds remaining in the match. */
|
|
128
|
+
TimeSeconds: number;
|
|
129
|
+
/** True if the match is in overtime. */
|
|
130
|
+
bOvertime: boolean;
|
|
131
|
+
/** Current ball state. */
|
|
132
|
+
Ball: GameBall;
|
|
133
|
+
/** True if a goal replay or history replay is active. */
|
|
134
|
+
bReplay: boolean;
|
|
135
|
+
/** True if a team has won. */
|
|
136
|
+
bHasWinner: boolean;
|
|
137
|
+
/** Name of the winning team. Empty string if no winner yet. */
|
|
138
|
+
Winner: string;
|
|
139
|
+
/** Asset name of the current map (e.g. `"Stadium_P"`). */
|
|
140
|
+
Arena: string;
|
|
141
|
+
/** True if the client is currently viewing a specific vehicle. */
|
|
142
|
+
bHasTarget: boolean;
|
|
143
|
+
/**
|
|
144
|
+
* `CONDITIONAL` Player currently being viewed. Members are an empty string or `0`
|
|
145
|
+
* if the player does not have a spectator target.
|
|
146
|
+
*/
|
|
147
|
+
Target?: PlayerRef;
|
|
148
|
+
/** `CONDITIONAL` `REPLAY` Current frame number if a replay is active. */
|
|
149
|
+
Frame?: number;
|
|
150
|
+
/** `CONDITIONAL` `REPLAY` Seconds elapsed since game start if a replay is active. */
|
|
151
|
+
Elapsed?: number;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/** Every event name the Stats API can emit. */
|
|
155
|
+
declare const EVENT_NAMES: readonly ["UpdateState", "BallHit", "BoostPickup", "ClockUpdatedSeconds", "CountdownBegin", "CrossbarHit", "GoalReplayEnd", "GoalReplayStart", "GoalReplayWillEnd", "GoalScored", "MatchCreated", "MatchDestroyed", "MatchEnded", "MatchInitialized", "MatchPaused", "MatchUnpaused", "PlayerJoined", "PlayerLeft", "PodiumStart", "ReplayCreated", "RoundStarted", "StatfeedEvent"];
|
|
156
|
+
/** Union of every event name the Stats API can emit. */
|
|
157
|
+
type EventName = (typeof EVENT_NAMES)[number];
|
|
158
|
+
/** Fields present in every event payload. */
|
|
159
|
+
interface BaseEventData {
|
|
160
|
+
/** Match identifier. Only set for online or LAN matches. */
|
|
161
|
+
MatchGuid?: string;
|
|
162
|
+
}
|
|
163
|
+
/** Sent X times per second, based on the player's `PacketSendRate` setting. */
|
|
164
|
+
interface UpdateStateData extends BaseEventData {
|
|
165
|
+
/** One entry per player in the match. */
|
|
166
|
+
Players: Player[];
|
|
167
|
+
/** Match metadata. */
|
|
168
|
+
Game: Game;
|
|
169
|
+
}
|
|
170
|
+
/** Sent one frame after the ball is hit. */
|
|
171
|
+
interface BallHitData extends BaseEventData {
|
|
172
|
+
/** Players that hit the ball that frame. */
|
|
173
|
+
Players: PlayerRef[];
|
|
174
|
+
/** Ball state at the moment of the hit. */
|
|
175
|
+
Ball: {
|
|
176
|
+
/** Ball speed before the hit (Unreal Units/second). */
|
|
177
|
+
PreHitSpeed: number;
|
|
178
|
+
/** Ball speed after the hit (Unreal Units/second). */
|
|
179
|
+
PostHitSpeed: number;
|
|
180
|
+
/** World position of the ball at impact. */
|
|
181
|
+
Location: Vector;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/** Known boost pickup classes. */
|
|
185
|
+
type BoostType = 'BoostType_Pad' | 'BoostType_Pill';
|
|
186
|
+
/** `SPECTATOR` Sent when a vehicle collects a boost pad or pill. */
|
|
187
|
+
interface BoostPickupData extends BaseEventData {
|
|
188
|
+
/** The player who collected the boost. */
|
|
189
|
+
Player: PlayerRef;
|
|
190
|
+
/** World location of the pickup. */
|
|
191
|
+
Location: Vector;
|
|
192
|
+
/** Amount of boost granted by the pickup. */
|
|
193
|
+
BoostAmount: number;
|
|
194
|
+
/** Pickup class: `BoostType_Pad` (small pad), `BoostType_Pill` (full/big boost). */
|
|
195
|
+
BoostType: BoostType;
|
|
196
|
+
/** True if the pickup occurred during replay playback. */
|
|
197
|
+
bReplay: boolean;
|
|
198
|
+
}
|
|
199
|
+
/** Sent when the in-game clock has changed. */
|
|
200
|
+
interface ClockUpdatedSecondsData extends BaseEventData {
|
|
201
|
+
/** Seconds remaining in the match. */
|
|
202
|
+
TimeSeconds: number;
|
|
203
|
+
/** True if the game is in overtime. */
|
|
204
|
+
bOvertime: boolean;
|
|
205
|
+
}
|
|
206
|
+
/** Sent at the start of each round when the countdown starts. */
|
|
207
|
+
interface CountdownBeginData extends BaseEventData {
|
|
208
|
+
}
|
|
209
|
+
/** Sent when the ball hits a crossbar. */
|
|
210
|
+
interface CrossbarHitData extends BaseEventData {
|
|
211
|
+
/** World position of the ball when the impact occurred. */
|
|
212
|
+
BallLocation: Vector;
|
|
213
|
+
/** Ball speed on impact. */
|
|
214
|
+
BallSpeed: number;
|
|
215
|
+
/** Impact force of the ball relative to the crossbar normal. */
|
|
216
|
+
ImpactForce: number;
|
|
217
|
+
/** The last touch of the ball before the crossbar hit. */
|
|
218
|
+
BallLastTouch: BallLastTouch;
|
|
219
|
+
}
|
|
220
|
+
/** Sent when a goal replay ends. */
|
|
221
|
+
interface GoalReplayEndData extends BaseEventData {
|
|
222
|
+
}
|
|
223
|
+
/** Sent when a goal replay starts. */
|
|
224
|
+
interface GoalReplayStartData extends BaseEventData {
|
|
225
|
+
}
|
|
226
|
+
/** Sent when the ball explodes during a goal replay. Does not fire if the replay is skipped. */
|
|
227
|
+
interface GoalReplayWillEndData extends BaseEventData {
|
|
228
|
+
}
|
|
229
|
+
/** Sent when a goal is scored. */
|
|
230
|
+
interface GoalScoredData extends BaseEventData {
|
|
231
|
+
/** Speed of the ball (Unreal Units/second) when it crossed the goal line. */
|
|
232
|
+
GoalSpeed: number;
|
|
233
|
+
/** Length of the previous round in seconds (not the match clock). */
|
|
234
|
+
GoalTime: number;
|
|
235
|
+
/** World position of the ball when the goal was scored. */
|
|
236
|
+
ImpactLocation: Vector;
|
|
237
|
+
/** The player who scored the goal. */
|
|
238
|
+
Scorer: PlayerRef;
|
|
239
|
+
/** `CONDITIONAL` Present only when an assist was recorded. */
|
|
240
|
+
Assister?: PlayerRef;
|
|
241
|
+
/** The last touch of the ball before the goal. */
|
|
242
|
+
BallLastTouch: BallLastTouch;
|
|
243
|
+
}
|
|
244
|
+
/** Sent when all teams are created and replicated. */
|
|
245
|
+
interface MatchCreatedData extends BaseEventData {
|
|
246
|
+
}
|
|
247
|
+
/** Sent when leaving the game. */
|
|
248
|
+
interface MatchDestroyedData extends BaseEventData {
|
|
249
|
+
}
|
|
250
|
+
/** Sent when the match ends and a winner is chosen. */
|
|
251
|
+
interface MatchEndedData extends BaseEventData {
|
|
252
|
+
/** Team index of the winning team. */
|
|
253
|
+
WinnerTeamNum: TeamNum;
|
|
254
|
+
}
|
|
255
|
+
/** Sent when the first countdown starts. */
|
|
256
|
+
interface MatchInitializedData extends BaseEventData {
|
|
257
|
+
}
|
|
258
|
+
/** Sent when the game is paused by a match admin. */
|
|
259
|
+
interface MatchPausedData extends BaseEventData {
|
|
260
|
+
}
|
|
261
|
+
/** Sent when the game is unpaused by a match admin. */
|
|
262
|
+
interface MatchUnpausedData extends BaseEventData {
|
|
263
|
+
}
|
|
264
|
+
/** Sent when a player is added to the current match. */
|
|
265
|
+
interface PlayerJoinedData extends BaseEventData {
|
|
266
|
+
/** Display name of the player who joined. */
|
|
267
|
+
PlayerName: string;
|
|
268
|
+
/** Platform identifier in the format `Platform|Uid|Splitscreen`. */
|
|
269
|
+
PrimaryId: PrimaryId;
|
|
270
|
+
}
|
|
271
|
+
/** Sent when a player is removed from the current match. */
|
|
272
|
+
interface PlayerLeftData extends BaseEventData {
|
|
273
|
+
/** Display name of the player who left. */
|
|
274
|
+
PlayerName: string;
|
|
275
|
+
/** Platform identifier in the format `Platform|Uid|Splitscreen`. */
|
|
276
|
+
PrimaryId: PrimaryId;
|
|
277
|
+
}
|
|
278
|
+
/** Sent when the game enters the podium state after the match ends. */
|
|
279
|
+
interface PodiumStartData extends BaseEventData {
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Sent when a replay is initialized. Does not pertain to goal replays,
|
|
283
|
+
* only replays loaded via the Match History menu.
|
|
284
|
+
*/
|
|
285
|
+
interface ReplayCreatedData extends BaseEventData {
|
|
286
|
+
/** File name of the replay. */
|
|
287
|
+
FileName: string;
|
|
288
|
+
/** Timestamp the replay was created (e.g. `"2026-06-05 18:42:13"`). */
|
|
289
|
+
Date: string;
|
|
290
|
+
}
|
|
291
|
+
/** Sent when the game enters the active state (after the countdown finishes). */
|
|
292
|
+
interface RoundStartedData extends BaseEventData {
|
|
293
|
+
}
|
|
294
|
+
/**
|
|
295
|
+
* Known `StatfeedEvent.EventName` values. The type stays open (`string & {}`)
|
|
296
|
+
* so new stats added by the game don't break consumers.
|
|
297
|
+
*/
|
|
298
|
+
type StatEventName = 'Demolish' | 'Demolition' | 'Goal' | 'Win' | 'MVP' | 'AerialGoal' | 'BackwardsGoal' | 'BicycleGoal' | 'LongGoal' | 'TurtleGoal' | 'PoolShot' | 'OvertimeGoal' | 'HatTrick' | 'Assist' | 'Playmaker' | 'Save' | 'EpicSave' | 'Savior' | 'Shot' | 'Center' | 'Clear' | 'FirstTouch' | 'BreakoutDamage' | 'BreakoutDamageLarge' | 'LowFive' | 'HighFive' | 'HoopsSwishGoal' | 'BicycleHit' | 'OwnGoal' | 'FlipReset' | (string & {});
|
|
299
|
+
/** Sent when someone earns a stat. */
|
|
300
|
+
interface StatfeedEventData extends BaseEventData {
|
|
301
|
+
/** Asset name of the StatEvent (e.g. `"Demolish"`, `"Save"`). Prefer this over `Type` for logic. */
|
|
302
|
+
EventName: StatEventName;
|
|
303
|
+
/** Localized display label for the stat (e.g. `"Demolition"`). */
|
|
304
|
+
Type: string;
|
|
305
|
+
/** Player who earned the stat. */
|
|
306
|
+
MainTarget: PlayerRef;
|
|
307
|
+
/** `CONDITIONAL` Player involved in the stat (e.g. the demolished player). */
|
|
308
|
+
SecondaryTarget?: PlayerRef;
|
|
309
|
+
}
|
|
310
|
+
/** Maps every event name to its payload type. */
|
|
311
|
+
interface EventDataMap {
|
|
312
|
+
UpdateState: UpdateStateData;
|
|
313
|
+
BallHit: BallHitData;
|
|
314
|
+
BoostPickup: BoostPickupData;
|
|
315
|
+
ClockUpdatedSeconds: ClockUpdatedSecondsData;
|
|
316
|
+
CountdownBegin: CountdownBeginData;
|
|
317
|
+
CrossbarHit: CrossbarHitData;
|
|
318
|
+
GoalReplayEnd: GoalReplayEndData;
|
|
319
|
+
GoalReplayStart: GoalReplayStartData;
|
|
320
|
+
GoalReplayWillEnd: GoalReplayWillEndData;
|
|
321
|
+
GoalScored: GoalScoredData;
|
|
322
|
+
MatchCreated: MatchCreatedData;
|
|
323
|
+
MatchDestroyed: MatchDestroyedData;
|
|
324
|
+
MatchEnded: MatchEndedData;
|
|
325
|
+
MatchInitialized: MatchInitializedData;
|
|
326
|
+
MatchPaused: MatchPausedData;
|
|
327
|
+
MatchUnpaused: MatchUnpausedData;
|
|
328
|
+
PlayerJoined: PlayerJoinedData;
|
|
329
|
+
PlayerLeft: PlayerLeftData;
|
|
330
|
+
PodiumStart: PodiumStartData;
|
|
331
|
+
ReplayCreated: ReplayCreatedData;
|
|
332
|
+
RoundStarted: RoundStartedData;
|
|
333
|
+
StatfeedEvent: StatfeedEventData;
|
|
334
|
+
}
|
|
335
|
+
/** A decoded event message with its name and typed payload. */
|
|
336
|
+
interface StatsApiEventOf<E extends EventName> {
|
|
337
|
+
Event: E;
|
|
338
|
+
Data: EventDataMap[E];
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Discriminated union of every decoded event message.
|
|
342
|
+
* Narrow it by checking `event.Event`:
|
|
343
|
+
*
|
|
344
|
+
* ```ts
|
|
345
|
+
* if (event.Event === 'GoalScored') event.Data.Scorer.Name;
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
type StatsApiEvent = {
|
|
349
|
+
[E in EventName]: StatsApiEventOf<E>;
|
|
350
|
+
}[EventName];
|
|
351
|
+
/**
|
|
352
|
+
* The raw envelope as it arrives over the socket. The game encodes `Data`
|
|
353
|
+
* as a JSON string; some tools forward it as an object, so both are accepted.
|
|
354
|
+
*/
|
|
355
|
+
interface RawEventEnvelope {
|
|
356
|
+
Event: string;
|
|
357
|
+
Data: string | Record<string, unknown>;
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Every command name the Stats API accepts. */
|
|
361
|
+
declare const COMMAND_NAMES: readonly ["ChangePOV", "LoadReplay", "SeekReplay", "SetGameSpeed", "SetHUDVisibility", "SetMatchPaused"];
|
|
362
|
+
/** Union of every command name the Stats API accepts. */
|
|
363
|
+
type CommandName = (typeof COMMAND_NAMES)[number];
|
|
364
|
+
/** Accepted camera perspectives for `ChangePOV`. */
|
|
365
|
+
declare const PERSPECTIVES: readonly ["Fly", "SoftAttach", "HardAttach", "PlayerView", "AutoCam", "Camera_Director"];
|
|
366
|
+
/** Camera mode for `ChangePOV`. */
|
|
367
|
+
type Perspective = (typeof PERSPECTIVES)[number];
|
|
368
|
+
/**
|
|
369
|
+
* What to look at for `ChangePOV`: `"Ball"`, or a player's spectator shortcut
|
|
370
|
+
* as an all-digit string (e.g. `"1"`). Any other value is rejected by the game.
|
|
371
|
+
*/
|
|
372
|
+
type Focus = 'Ball' | `${number}`;
|
|
373
|
+
/**
|
|
374
|
+
* `SPECTATOR` `REPLAY` Changes the spectator/replay viewpoint.
|
|
375
|
+
* At least one of `Focus` or `Perspective` must be supplied; both may be combined.
|
|
376
|
+
*/
|
|
377
|
+
type ChangePOVData = {
|
|
378
|
+
Focus: Focus;
|
|
379
|
+
Perspective?: Perspective;
|
|
380
|
+
} | {
|
|
381
|
+
Focus?: Focus;
|
|
382
|
+
Perspective: Perspective;
|
|
383
|
+
};
|
|
384
|
+
/**
|
|
385
|
+
* Loads and begins playback of a replay by file name or by file path.
|
|
386
|
+
* At least one of `FileName` or `Path` must be supplied. `FileName` takes precedence.
|
|
387
|
+
*/
|
|
388
|
+
type LoadReplayData = {
|
|
389
|
+
FileName: string;
|
|
390
|
+
Path?: string;
|
|
391
|
+
} | {
|
|
392
|
+
FileName?: string;
|
|
393
|
+
Path: string;
|
|
394
|
+
};
|
|
395
|
+
/**
|
|
396
|
+
* `REPLAY` Jumps replay playback to a target frame or time.
|
|
397
|
+
* At least one of `Frame` or `TimeSeconds` must be supplied. `Frame` takes precedence.
|
|
398
|
+
*/
|
|
399
|
+
type SeekReplayData = {
|
|
400
|
+
Frame: number;
|
|
401
|
+
TimeSeconds?: number;
|
|
402
|
+
} | {
|
|
403
|
+
Frame?: number;
|
|
404
|
+
TimeSeconds: number;
|
|
405
|
+
};
|
|
406
|
+
/** `REPLAY` Sets replay playback speed. */
|
|
407
|
+
interface SetGameSpeedData {
|
|
408
|
+
/** Playback multiplier. 1.0 = normal, 0.5 = half speed, 2.0 = double. Must be ≥ 0. */
|
|
409
|
+
Speed: number;
|
|
410
|
+
}
|
|
411
|
+
/** Shows or hides the in-game HUD. */
|
|
412
|
+
interface SetHUDVisibilityData {
|
|
413
|
+
/** `true` shows the HUD; `false` hides it. */
|
|
414
|
+
bVisible: boolean;
|
|
415
|
+
}
|
|
416
|
+
/** Pauses or unpauses the current match or replay. */
|
|
417
|
+
interface SetMatchPausedData {
|
|
418
|
+
/** `true` pauses; `false` resumes. */
|
|
419
|
+
bPaused: boolean;
|
|
420
|
+
}
|
|
421
|
+
/** Maps every command name to its payload type. */
|
|
422
|
+
interface CommandDataMap {
|
|
423
|
+
ChangePOV: ChangePOVData;
|
|
424
|
+
LoadReplay: LoadReplayData;
|
|
425
|
+
SeekReplay: SeekReplayData;
|
|
426
|
+
SetGameSpeed: SetGameSpeedData;
|
|
427
|
+
SetHUDVisibility: SetHUDVisibilityData;
|
|
428
|
+
SetMatchPaused: SetMatchPausedData;
|
|
429
|
+
}
|
|
430
|
+
/** A command envelope with its name and typed payload. */
|
|
431
|
+
interface StatsApiCommandOf<C extends CommandName> {
|
|
432
|
+
Command: C;
|
|
433
|
+
Data: CommandDataMap[C];
|
|
434
|
+
}
|
|
435
|
+
/** Discriminated union of every command envelope. */
|
|
436
|
+
type StatsApiCommand = {
|
|
437
|
+
[C in CommandName]: StatsApiCommandOf<C>;
|
|
438
|
+
}[CommandName];
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Small, dependency-free building blocks for runtime type guards.
|
|
442
|
+
* They are exported so consumers can compose their own guards too.
|
|
443
|
+
*/
|
|
444
|
+
/** A runtime type guard. */
|
|
445
|
+
type Guard<T> = (value: unknown) => value is T;
|
|
446
|
+
/** Extracts the guarded type from a {@link Guard}. */
|
|
447
|
+
type GuardedType<G> = G extends Guard<infer T> ? T : never;
|
|
448
|
+
/** Checks that a value is a non-null, non-array object. */
|
|
449
|
+
declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
450
|
+
declare const isString: Guard<string>;
|
|
451
|
+
/** Checks for a finite number (rejects `NaN` and `Infinity`). */
|
|
452
|
+
declare const isNumber: Guard<number>;
|
|
453
|
+
/** Checks for a finite integer. */
|
|
454
|
+
declare const isInteger: Guard<number>;
|
|
455
|
+
declare const isBoolean: Guard<boolean>;
|
|
456
|
+
/** Accepts `undefined` or a value matching `guard`. */
|
|
457
|
+
declare function optional<T>(guard: Guard<T>): Guard<T | undefined>;
|
|
458
|
+
/** Checks for an array whose items all match `guard`. */
|
|
459
|
+
declare function arrayOf<T>(guard: Guard<T>): Guard<T[]>;
|
|
460
|
+
/** Checks that a value is one of the given literals. */
|
|
461
|
+
declare function oneOf<const T extends readonly (string | number | boolean)[]>(values: T): Guard<T[number]>;
|
|
462
|
+
/** Specification for {@link shape}: one guard per property of `T`, optional ones included. */
|
|
463
|
+
type ShapeSpec<T> = {
|
|
464
|
+
[K in keyof Required<T>]: Guard<T[K]>;
|
|
465
|
+
};
|
|
466
|
+
/**
|
|
467
|
+
* Builds a guard for an object type from one guard per property.
|
|
468
|
+
* Extra properties are allowed so new fields added by the game don't break validation.
|
|
469
|
+
* The spec is checked against `T` at compile time, so a missing property is a type error.
|
|
470
|
+
*/
|
|
471
|
+
declare function shape<T extends object>(spec: ShapeSpec<T>): Guard<T>;
|
|
472
|
+
/** Combines guards with logical AND. */
|
|
473
|
+
declare function allOf<T>(guard: Guard<T>, ...checks: ((value: T) => boolean)[]): Guard<T>;
|
|
474
|
+
|
|
475
|
+
declare const isVector: Guard<Vector>;
|
|
476
|
+
declare const isPlayerRef: Guard<PlayerRef>;
|
|
477
|
+
declare const isBallLastTouch: Guard<BallLastTouch>;
|
|
478
|
+
declare const isPlayer: Guard<Player>;
|
|
479
|
+
declare const isGameTeam: Guard<GameTeam>;
|
|
480
|
+
declare const isGameBall: Guard<GameBall>;
|
|
481
|
+
declare const isGame: Guard<Game>;
|
|
482
|
+
declare const isUpdateStateData: Guard<UpdateStateData>;
|
|
483
|
+
declare const isBallHitData: Guard<BallHitData>;
|
|
484
|
+
declare const isBoostPickupData: Guard<BoostPickupData>;
|
|
485
|
+
declare const isClockUpdatedSecondsData: Guard<ClockUpdatedSecondsData>;
|
|
486
|
+
declare const isCrossbarHitData: Guard<CrossbarHitData>;
|
|
487
|
+
declare const isGoalScoredData: Guard<GoalScoredData>;
|
|
488
|
+
declare const isMatchEndedData: Guard<MatchEndedData>;
|
|
489
|
+
declare const isPlayerJoinedData: Guard<PlayerJoinedData>;
|
|
490
|
+
declare const isPlayerLeftData: Guard<PlayerLeftData>;
|
|
491
|
+
declare const isReplayCreatedData: Guard<ReplayCreatedData>;
|
|
492
|
+
declare const isStatfeedEventData: Guard<StatfeedEventData>;
|
|
493
|
+
declare const isCountdownBeginData: Guard<EventDataMap['CountdownBegin']>;
|
|
494
|
+
declare const isGoalReplayEndData: Guard<EventDataMap['GoalReplayEnd']>;
|
|
495
|
+
declare const isGoalReplayStartData: Guard<EventDataMap['GoalReplayStart']>;
|
|
496
|
+
declare const isGoalReplayWillEndData: Guard<EventDataMap['GoalReplayWillEnd']>;
|
|
497
|
+
declare const isMatchCreatedData: Guard<EventDataMap['MatchCreated']>;
|
|
498
|
+
declare const isMatchDestroyedData: Guard<EventDataMap['MatchDestroyed']>;
|
|
499
|
+
declare const isMatchInitializedData: Guard<EventDataMap['MatchInitialized']>;
|
|
500
|
+
declare const isMatchPausedData: Guard<EventDataMap['MatchPaused']>;
|
|
501
|
+
declare const isMatchUnpausedData: Guard<EventDataMap['MatchUnpaused']>;
|
|
502
|
+
declare const isPodiumStartData: Guard<EventDataMap['PodiumStart']>;
|
|
503
|
+
declare const isRoundStartedData: Guard<EventDataMap['RoundStarted']>;
|
|
504
|
+
/** Payload guard for every event, keyed by event name. */
|
|
505
|
+
declare const eventDataGuards: {
|
|
506
|
+
readonly [E in EventName]: Guard<EventDataMap[E]>;
|
|
507
|
+
};
|
|
508
|
+
/** Checks that a string is a known event name. */
|
|
509
|
+
declare const isEventName: Guard<EventName>;
|
|
510
|
+
/** Checks that `data` is a valid payload for the event `name`. */
|
|
511
|
+
declare function isEventData<E extends EventName>(name: E, data: unknown): data is EventDataMap[E];
|
|
512
|
+
/** Checks the raw envelope shape (`Event` string + `Data` string or object), without validating the payload. */
|
|
513
|
+
declare function isRawEventEnvelope(value: unknown): value is RawEventEnvelope;
|
|
514
|
+
/**
|
|
515
|
+
* Checks that a value is a fully valid, decoded event message (with `Data` already parsed),
|
|
516
|
+
* optionally restricted to a single event name.
|
|
517
|
+
*
|
|
518
|
+
* ```ts
|
|
519
|
+
* if (isStatsApiEvent(msg, 'GoalScored')) msg.Data.Scorer.Name;
|
|
520
|
+
* ```
|
|
521
|
+
*/
|
|
522
|
+
declare function isStatsApiEvent(value: unknown): value is StatsApiEvent;
|
|
523
|
+
declare function isStatsApiEvent<E extends EventName>(value: unknown, name: E): value is StatsApiEventOf<E>;
|
|
524
|
+
|
|
525
|
+
/** Checks for `"Ball"` or an all-digit spectator shortcut string. */
|
|
526
|
+
declare const isFocus: Guard<Focus>;
|
|
527
|
+
declare const isPerspective: Guard<Perspective>;
|
|
528
|
+
declare const isChangePOVData: Guard<ChangePOVData>;
|
|
529
|
+
/** `FileName` / `Path` must be strings; at least one must be non-empty. */
|
|
530
|
+
declare const isLoadReplayData: Guard<LoadReplayData>;
|
|
531
|
+
/** `Frame` must be a non-negative integer, `TimeSeconds` a non-negative number; at least one is required. */
|
|
532
|
+
declare const isSeekReplayData: Guard<SeekReplayData>;
|
|
533
|
+
/** `Speed` must be a finite number ≥ 0. */
|
|
534
|
+
declare const isSetGameSpeedData: Guard<SetGameSpeedData>;
|
|
535
|
+
declare const isSetHUDVisibilityData: Guard<SetHUDVisibilityData>;
|
|
536
|
+
declare const isSetMatchPausedData: Guard<SetMatchPausedData>;
|
|
537
|
+
/** Payload guard for every command, keyed by command name. */
|
|
538
|
+
declare const commandDataGuards: {
|
|
539
|
+
readonly [C in CommandName]: Guard<CommandDataMap[C]>;
|
|
540
|
+
};
|
|
541
|
+
/** Checks that a string is a known command name. */
|
|
542
|
+
declare const isCommandName: Guard<CommandName>;
|
|
543
|
+
/** Checks that `data` is a valid payload for the command `name`. */
|
|
544
|
+
declare function isCommandData<C extends CommandName>(name: C, data: unknown): data is CommandDataMap[C];
|
|
545
|
+
/**
|
|
546
|
+
* Checks that a value is a fully valid command envelope, optionally restricted to a single command name.
|
|
547
|
+
*/
|
|
548
|
+
declare function isStatsApiCommand(value: unknown): value is StatsApiCommand;
|
|
549
|
+
declare function isStatsApiCommand<C extends CommandName>(value: unknown, name: C): value is StatsApiCommandOf<C>;
|
|
550
|
+
|
|
551
|
+
/** Why a message could not be turned into a {@link StatsApiEvent}. */
|
|
552
|
+
type ParseFailureReason =
|
|
553
|
+
/** The message is not valid JSON. */
|
|
554
|
+
'invalid-json'
|
|
555
|
+
/** The message is JSON but not an `{ Event, Data }` envelope. */
|
|
556
|
+
| 'invalid-envelope'
|
|
557
|
+
/** `Data` is a string that is not valid JSON, or does not decode to an object. */
|
|
558
|
+
| 'invalid-data-json'
|
|
559
|
+
/** `Event` is not one of the documented event names. */
|
|
560
|
+
| 'unknown-event'
|
|
561
|
+
/** `Data` does not match the documented shape for that event. */
|
|
562
|
+
| 'invalid-data';
|
|
563
|
+
/** A message that failed to parse or validate. */
|
|
564
|
+
interface ParseFailure {
|
|
565
|
+
ok: false;
|
|
566
|
+
reason: ParseFailureReason;
|
|
567
|
+
/** The original input. */
|
|
568
|
+
raw: unknown;
|
|
569
|
+
/** Event name, when it could be read. */
|
|
570
|
+
event?: string;
|
|
571
|
+
/** Decoded payload, when it could be read. */
|
|
572
|
+
data?: unknown;
|
|
573
|
+
}
|
|
574
|
+
/** A message that was decoded (and validated, unless disabled). */
|
|
575
|
+
interface ParseSuccess {
|
|
576
|
+
ok: true;
|
|
577
|
+
event: StatsApiEvent;
|
|
578
|
+
}
|
|
579
|
+
type ParseResult = ParseSuccess | ParseFailure;
|
|
580
|
+
interface ParseOptions {
|
|
581
|
+
/**
|
|
582
|
+
* Run the payload type guard for the event.
|
|
583
|
+
* When `false`, only the envelope and event name are checked and `Data` is trusted as-is.
|
|
584
|
+
* @default true
|
|
585
|
+
*/
|
|
586
|
+
validate?: boolean;
|
|
587
|
+
}
|
|
588
|
+
/**
|
|
589
|
+
* Decodes a message coming from the Stats API socket.
|
|
590
|
+
*
|
|
591
|
+
* Accepts the raw JSON text, or an already `JSON.parse`d envelope. The game encodes
|
|
592
|
+
* `Data` as a JSON string inside the envelope; it is decoded here. If `Data` is already
|
|
593
|
+
* an object it is used directly.
|
|
594
|
+
*
|
|
595
|
+
* Never throws: check `result.ok`.
|
|
596
|
+
*/
|
|
597
|
+
declare function parseEventMessage(raw: unknown, options?: ParseOptions): ParseResult;
|
|
598
|
+
|
|
599
|
+
/** Base class for every error thrown or emitted by this library. */
|
|
600
|
+
declare class StatsApiError extends Error {
|
|
601
|
+
name: string;
|
|
602
|
+
}
|
|
603
|
+
/** Thrown by `send()` when the socket is not open. */
|
|
604
|
+
declare class NotConnectedError extends StatsApiError {
|
|
605
|
+
name: string;
|
|
606
|
+
constructor(message?: string);
|
|
607
|
+
}
|
|
608
|
+
/** Thrown by `send()` when a command name or payload fails validation. */
|
|
609
|
+
declare class CommandValidationError extends StatsApiError {
|
|
610
|
+
/** The command name that was passed. */
|
|
611
|
+
readonly command: unknown;
|
|
612
|
+
/** The payload that was passed. */
|
|
613
|
+
readonly data: unknown;
|
|
614
|
+
name: string;
|
|
615
|
+
constructor(message: string,
|
|
616
|
+
/** The command name that was passed. */
|
|
617
|
+
command: unknown,
|
|
618
|
+
/** The payload that was passed. */
|
|
619
|
+
data: unknown);
|
|
620
|
+
}
|
|
621
|
+
/** Emitted on `error` when the WebSocket reports a failure. */
|
|
622
|
+
declare class ConnectionError extends StatsApiError {
|
|
623
|
+
name: string;
|
|
624
|
+
}
|
|
625
|
+
/** Emitted on `error` when an event listener throws. */
|
|
626
|
+
declare class ListenerError extends StatsApiError {
|
|
627
|
+
/** The listener event name that was being dispatched. */
|
|
628
|
+
readonly eventName: string;
|
|
629
|
+
name: string;
|
|
630
|
+
constructor(
|
|
631
|
+
/** The listener event name that was being dispatched. */
|
|
632
|
+
eventName: string, cause: unknown);
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
/** Section of the Stats API `.ini` file that holds the exporter settings. */
|
|
636
|
+
declare const STATS_API_SECTION = "TAGame.MatchStatsExporter_TA";
|
|
637
|
+
/** `PacketSendRate` written when the current value is invalid (`<= 0`, missing, or above the maximum). */
|
|
638
|
+
declare const DEFAULT_PACKET_SEND_RATE = 30;
|
|
639
|
+
/** Highest `PacketSendRate` the game accepts. */
|
|
640
|
+
declare const MAX_PACKET_SEND_RATE = 120;
|
|
641
|
+
/** `.ini` file names, in lookup order. `TAStatsAPI.ini` wins over `DefaultStatsAPI.ini`. */
|
|
642
|
+
declare const STATS_API_INI_FILES: readonly ["TAStatsAPI.ini", "DefaultStatsAPI.ini"];
|
|
643
|
+
interface EnsureStatsApiOptions {
|
|
644
|
+
/** Rocket League install directory (the folder containing `TAGame`). Auto-detected on Windows when omitted. */
|
|
645
|
+
installDir?: string;
|
|
646
|
+
/** Exact path of the `.ini` file to check. Overrides `installDir` and auto-detection. */
|
|
647
|
+
iniPath?: string;
|
|
648
|
+
/** `PacketSendRate` written when the current value is `0` or less, missing, or above `120`. Must be 1–120. @default 30 */
|
|
649
|
+
packetSendRate?: number;
|
|
650
|
+
/**
|
|
651
|
+
* Called with a human-readable message when the file was changed (restart required) or could not be written.
|
|
652
|
+
* Pass `false` to stay silent.
|
|
653
|
+
* @default console.warn
|
|
654
|
+
*/
|
|
655
|
+
warn?: ((message: string) => void) | false;
|
|
656
|
+
}
|
|
657
|
+
type EnsureStatsApiResult =
|
|
658
|
+
/** No Stats API `.ini` file was found. */
|
|
659
|
+
{
|
|
660
|
+
status: 'not-found';
|
|
661
|
+
searched: string[];
|
|
662
|
+
}
|
|
663
|
+
/** `PacketSendRate` was already between 1 and 120. */
|
|
664
|
+
| {
|
|
665
|
+
status: 'enabled';
|
|
666
|
+
iniPath: string;
|
|
667
|
+
packetSendRate: number;
|
|
668
|
+
}
|
|
669
|
+
/** `PacketSendRate` was `0` or less, missing, or above 120, and has been set. The game must be restarted. */
|
|
670
|
+
| {
|
|
671
|
+
status: 'updated';
|
|
672
|
+
iniPath: string;
|
|
673
|
+
previous: number | undefined;
|
|
674
|
+
packetSendRate: number;
|
|
675
|
+
restartRequired: true;
|
|
676
|
+
}
|
|
677
|
+
/** The file needed a change but could not be written (e.g. no permission under `Program Files`). */
|
|
678
|
+
| {
|
|
679
|
+
status: 'write-failed';
|
|
680
|
+
iniPath: string;
|
|
681
|
+
previous: number | undefined;
|
|
682
|
+
error: unknown;
|
|
683
|
+
};
|
|
684
|
+
interface PacketSendRateEdit {
|
|
685
|
+
/** Current value, or `undefined` if the key is missing (the game then uses `0`). */
|
|
686
|
+
previous: number | undefined;
|
|
687
|
+
/** `true` when `PacketSendRate` is `0` or less, missing (Stats API disabled), or above {@link MAX_PACKET_SEND_RATE}. */
|
|
688
|
+
needsUpdate: boolean;
|
|
689
|
+
/** The file contents with `PacketSendRate` set to the requested rate. Equals the input when `needsUpdate` is false. */
|
|
690
|
+
content: string;
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Reads `PacketSendRate` from the `[TAGame.MatchStatsExporter_TA]` section of an `.ini` file
|
|
694
|
+
* and, if it is `0` or less (or missing), returns the contents with it set to `rate`.
|
|
695
|
+
* Keeps comments, the other keys, the BOM and the line endings untouched.
|
|
696
|
+
*/
|
|
697
|
+
declare function editPacketSendRate(content: string, rate?: number): PacketSendRateEdit;
|
|
698
|
+
/**
|
|
699
|
+
* Candidate Rocket League install directories (Epic Games Store and Steam, Windows only).
|
|
700
|
+
* Not every returned directory necessarily exists.
|
|
701
|
+
*/
|
|
702
|
+
declare function findRocketLeagueInstallDirs(): Promise<string[]>;
|
|
703
|
+
/** Paths of the Stats API `.ini` files inside an install directory, in lookup order. */
|
|
704
|
+
declare function statsApiIniPaths(installDir: string): string[];
|
|
705
|
+
/** Returns the Stats API `.ini` file in use (`TAStatsAPI.ini`, else `DefaultStatsAPI.ini`), or `undefined`. */
|
|
706
|
+
declare function findStatsApiIni(installDir?: string): Promise<{
|
|
707
|
+
iniPath: string | undefined;
|
|
708
|
+
searched: string[];
|
|
709
|
+
}>;
|
|
710
|
+
/**
|
|
711
|
+
* Looks for the Stats API `.ini` file (`TAStatsAPI.ini` or `DefaultStatsAPI.ini`). If `PacketSendRate`
|
|
712
|
+
* is `0` or less, missing, or above `120`, sets it to `30` (or `options.packetSendRate`) and warns that the game must be restarted,
|
|
713
|
+
* since Rocket League only reads this file at launch.
|
|
714
|
+
*
|
|
715
|
+
* Never throws: every outcome is reported in the returned result.
|
|
716
|
+
*/
|
|
717
|
+
declare function ensureStatsApiEnabled(options?: EnsureStatsApiOptions): Promise<EnsureStatsApiResult>;
|
|
718
|
+
|
|
719
|
+
/**
|
|
720
|
+
* The subset of the standard `WebSocket` API this client relies on.
|
|
721
|
+
* Satisfied by the browser/Node.js (>= 22) global `WebSocket` and by the `ws` package.
|
|
722
|
+
*/
|
|
723
|
+
interface WebSocketLike {
|
|
724
|
+
readonly readyState: number;
|
|
725
|
+
send(data: string): void;
|
|
726
|
+
close(code?: number, reason?: string): void;
|
|
727
|
+
addEventListener(type: 'open', listener: () => void): void;
|
|
728
|
+
addEventListener(type: 'close', listener: (event: {
|
|
729
|
+
code: number;
|
|
730
|
+
reason: string;
|
|
731
|
+
}) => void): void;
|
|
732
|
+
addEventListener(type: 'error', listener: (event: unknown) => void): void;
|
|
733
|
+
addEventListener(type: 'message', listener: (event: {
|
|
734
|
+
data: unknown;
|
|
735
|
+
}) => void): void;
|
|
736
|
+
}
|
|
737
|
+
/** A constructor producing a {@link WebSocketLike}. */
|
|
738
|
+
type WebSocketConstructor = new (url: string) => WebSocketLike;
|
|
739
|
+
interface ReconnectOptions {
|
|
740
|
+
/** Delay before the first reconnection attempt, in ms. @default 1000 */
|
|
741
|
+
initialDelay?: number;
|
|
742
|
+
/** Upper bound for the delay between attempts, in ms. @default 30000 */
|
|
743
|
+
maxDelay?: number;
|
|
744
|
+
/** Multiplier applied to the delay after every failed attempt. @default 2 */
|
|
745
|
+
factor?: number;
|
|
746
|
+
/** Maximum consecutive attempts before giving up. @default Infinity */
|
|
747
|
+
maxAttempts?: number;
|
|
748
|
+
}
|
|
749
|
+
interface StatsApiClientOptions {
|
|
750
|
+
/** Host of the Rocket League client. @default '127.0.0.1' */
|
|
751
|
+
host?: string;
|
|
752
|
+
/** The `WebPort` configured in `TAStatsAPI.ini`. @default 49124 */
|
|
753
|
+
port?: number;
|
|
754
|
+
/** Use `wss://` instead of `ws://`. @default false */
|
|
755
|
+
secure?: boolean;
|
|
756
|
+
/** Full WebSocket URL. Overrides `host`, `port` and `secure` when set. */
|
|
757
|
+
url?: string;
|
|
758
|
+
/**
|
|
759
|
+
* Connect automatically after construction. Set to `false` to call {@link RocketLeagueStatsClient.connect} yourself.
|
|
760
|
+
* The connection starts on the next microtask, so listeners attached right after `new` still receive `connect`.
|
|
761
|
+
* @default true
|
|
762
|
+
*/
|
|
763
|
+
autoConnect?: boolean;
|
|
764
|
+
/**
|
|
765
|
+
* Reconnect automatically when the connection drops or cannot be established
|
|
766
|
+
* (e.g. the game is not running yet). `false` disables it.
|
|
767
|
+
* @default true
|
|
768
|
+
*/
|
|
769
|
+
reconnect?: boolean | ReconnectOptions;
|
|
770
|
+
/**
|
|
771
|
+
* Validate every incoming event with its type guard. Invalid events are not dispatched
|
|
772
|
+
* and are reported on `invalidMessage` instead.
|
|
773
|
+
* @default true
|
|
774
|
+
*/
|
|
775
|
+
validateEvents?: boolean;
|
|
776
|
+
/**
|
|
777
|
+
* WebSocket implementation. Defaults to `globalThis.WebSocket`
|
|
778
|
+
* (browsers, Node.js >= 22). Pass `WebSocket` from the `ws` package for older runtimes.
|
|
779
|
+
*/
|
|
780
|
+
WebSocket?: WebSocketConstructor;
|
|
781
|
+
/**
|
|
782
|
+
* **Node.js only** (ignored in browsers). Check the Stats API `.ini` file (`TAStatsAPI.ini`, else
|
|
783
|
+
* `DefaultStatsAPI.ini`) on construction and, if `PacketSendRate` is `0` or less, missing, or above `120`, set it to `30` and
|
|
784
|
+
* warn that the game must be restarted. Pass options to customize it, or `false` to disable it.
|
|
785
|
+
* @default true
|
|
786
|
+
*/
|
|
787
|
+
ensureStatsApi?: boolean | EnsureStatsApiOptions;
|
|
788
|
+
}
|
|
789
|
+
/** Connection state of the client. */
|
|
790
|
+
type ConnectionState = 'idle' | 'connecting' | 'open' | 'closed';
|
|
791
|
+
interface DisconnectInfo {
|
|
792
|
+
code: number;
|
|
793
|
+
reason: string;
|
|
794
|
+
/** `true` when a reconnection attempt has been scheduled. */
|
|
795
|
+
willReconnect: boolean;
|
|
796
|
+
}
|
|
797
|
+
interface ReconnectInfo {
|
|
798
|
+
/** 1-based attempt number. */
|
|
799
|
+
attempt: number;
|
|
800
|
+
/** Delay before this attempt, in ms. */
|
|
801
|
+
delay: number;
|
|
802
|
+
}
|
|
803
|
+
/** Listener for a specific game event. Receives the typed payload and the whole decoded message. */
|
|
804
|
+
type GameEventListener<E extends EventName> = (data: EventDataMap[E], event: StatsApiEventOf<E>) => void;
|
|
805
|
+
/** Listener receiving every game event as a discriminated union. */
|
|
806
|
+
type AnyEventListener = (event: StatsApiEvent) => void;
|
|
807
|
+
/** Client lifecycle events (lowercase, so they never collide with game events). */
|
|
808
|
+
interface LifecycleListenerMap {
|
|
809
|
+
/** The socket is open. */
|
|
810
|
+
connect: () => void;
|
|
811
|
+
/** The socket closed. */
|
|
812
|
+
disconnect: (info: DisconnectInfo) => void;
|
|
813
|
+
/** A reconnection attempt has been scheduled. */
|
|
814
|
+
reconnecting: (info: ReconnectInfo) => void;
|
|
815
|
+
/** A connection error or a listener error happened. */
|
|
816
|
+
error: (error: Error) => void;
|
|
817
|
+
/** A message could not be decoded, has an unknown event name, or failed validation. */
|
|
818
|
+
invalidMessage: (failure: ParseFailure) => void;
|
|
819
|
+
}
|
|
820
|
+
/** Every listener accepted by {@link RocketLeagueStatsClient.on}. */
|
|
821
|
+
type ClientListenerMap = {
|
|
822
|
+
[E in EventName]: GameEventListener<E>;
|
|
823
|
+
} & LifecycleListenerMap;
|
|
824
|
+
/** Any name accepted by {@link RocketLeagueStatsClient.on}. */
|
|
825
|
+
type ClientEventName = keyof ClientListenerMap;
|
|
826
|
+
/** Call it to remove the listener. */
|
|
827
|
+
type Unsubscribe = () => void;
|
|
828
|
+
/**
|
|
829
|
+
* WebSocket client for the Rocket League Stats API.
|
|
830
|
+
*
|
|
831
|
+
* ```ts
|
|
832
|
+
* const client = new RocketLeagueStatsClient();
|
|
833
|
+
*
|
|
834
|
+
* client.on('GoalScored', (data) => console.log(`${data.Scorer.Name} scored!`));
|
|
835
|
+
* client.onAny((event) => console.log(event.Event));
|
|
836
|
+
* client.send('ChangePOV', { Focus: 'Ball' });
|
|
837
|
+
* ```
|
|
838
|
+
*
|
|
839
|
+
* @see https://www.rocketleague.com/developer/stats-api
|
|
840
|
+
*/
|
|
841
|
+
declare class RocketLeagueStatsClient {
|
|
842
|
+
#private;
|
|
843
|
+
/** The WebSocket URL this client connects to. */
|
|
844
|
+
readonly url: string;
|
|
845
|
+
/**
|
|
846
|
+
* Result of the `.ini` check (see the `ensureStatsApi` option). `undefined` outside Node.js or when
|
|
847
|
+
* `ensureStatsApi: false`. Never rejects.
|
|
848
|
+
*/
|
|
849
|
+
readonly statsApiConfig: Promise<EnsureStatsApiResult> | undefined;
|
|
850
|
+
constructor(options?: StatsApiClientOptions);
|
|
851
|
+
/** Current connection state. */
|
|
852
|
+
get state(): ConnectionState;
|
|
853
|
+
/** `true` when the socket is open and commands can be sent. */
|
|
854
|
+
get isConnected(): boolean;
|
|
855
|
+
/**
|
|
856
|
+
* Opens the connection. Does nothing if already connecting or connected.
|
|
857
|
+
* Called automatically unless `autoConnect: false` was passed.
|
|
858
|
+
*/
|
|
859
|
+
connect(): void;
|
|
860
|
+
/** Closes the connection and stops any pending reconnection. */
|
|
861
|
+
disconnect(code?: number, reason?: string): void;
|
|
862
|
+
/**
|
|
863
|
+
* Decodes, validates and dispatches one raw message. Called for every socket message;
|
|
864
|
+
* exposed so messages from another transport (e.g. the TCP port, a recording) can be fed in.
|
|
865
|
+
*/
|
|
866
|
+
handleMessage(raw: unknown): void;
|
|
867
|
+
/**
|
|
868
|
+
* Listens to a game event (e.g. `'GoalScored'`) or a lifecycle event (e.g. `'connect'`).
|
|
869
|
+
* The listener's arguments are typed from the event name.
|
|
870
|
+
*
|
|
871
|
+
* @returns A function that removes the listener.
|
|
872
|
+
*/
|
|
873
|
+
on<K extends ClientEventName>(name: K, listener: ClientListenerMap[K]): Unsubscribe;
|
|
874
|
+
/** Like {@link on}, but the listener is removed after its first call. */
|
|
875
|
+
once<K extends ClientEventName>(name: K, listener: ClientListenerMap[K]): Unsubscribe;
|
|
876
|
+
/** Removes a listener added with {@link on}. */
|
|
877
|
+
off<K extends ClientEventName>(name: K, listener: ClientListenerMap[K]): void;
|
|
878
|
+
/**
|
|
879
|
+
* Listens to **every** game event. The argument is a discriminated union: narrow it with `event.Event`.
|
|
880
|
+
*
|
|
881
|
+
* ```ts
|
|
882
|
+
* client.onAny((event) => {
|
|
883
|
+
* if (event.Event === 'StatfeedEvent') console.log(event.Data.EventName);
|
|
884
|
+
* });
|
|
885
|
+
* ```
|
|
886
|
+
*
|
|
887
|
+
* @returns A function that removes the listener.
|
|
888
|
+
*/
|
|
889
|
+
onAny(listener: AnyEventListener): Unsubscribe;
|
|
890
|
+
/** Removes a listener added with {@link onAny}. */
|
|
891
|
+
offAny(listener: AnyEventListener): void;
|
|
892
|
+
/** Removes every listener, or every listener of one name. */
|
|
893
|
+
removeAllListeners(name?: ClientEventName): void;
|
|
894
|
+
/**
|
|
895
|
+
* Resolves with the payload of the next occurrence of a game event.
|
|
896
|
+
* Rejects after `timeout` ms if provided.
|
|
897
|
+
*/
|
|
898
|
+
waitFor<E extends EventName>(name: E, options?: {
|
|
899
|
+
timeout?: number;
|
|
900
|
+
}): Promise<EventDataMap[E]>;
|
|
901
|
+
/**
|
|
902
|
+
* Sends a command to the game. The payload is type-checked at compile time and
|
|
903
|
+
* validated with its guard at runtime before being sent.
|
|
904
|
+
*
|
|
905
|
+
* ```ts
|
|
906
|
+
* client.send('ChangePOV', { Focus: '1', Perspective: 'PlayerView' });
|
|
907
|
+
* client.send({ Command: 'SetHUDVisibility', Data: { bVisible: false } });
|
|
908
|
+
* ```
|
|
909
|
+
*
|
|
910
|
+
* @throws {CommandValidationError} If the command name or payload is invalid.
|
|
911
|
+
* @throws {NotConnectedError} If the socket is not open.
|
|
912
|
+
*/
|
|
913
|
+
send<C extends CommandName>(command: C, data: CommandDataMap[C]): void;
|
|
914
|
+
send(command: StatsApiCommand): void;
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
export { BALL_UNTOUCHED_TEAM_NUM, COMMAND_NAMES, CommandValidationError, ConnectionError, DEFAULT_PACKET_SEND_RATE as D, EVENT_NAMES, ListenerError, MAX_PACKET_SEND_RATE as M, NotConnectedError, PERSPECTIVES, RocketLeagueStatsClient, STATS_API_INI_FILES as S, StatsApiError, Team, STATS_API_SECTION as a, allOf, arrayOf, ensureStatsApiEnabled as b, findStatsApiIni as c, commandDataGuards, RocketLeagueStatsClient as default, editPacketSendRate as e, eventDataGuards, findRocketLeagueInstallDirs as f, isBallHitData, isBallLastTouch, isBoolean, isBoostPickupData, isChangePOVData, isClockUpdatedSecondsData, isCommandData, isCommandName, isCountdownBeginData, isCrossbarHitData, isEventData, isEventName, isFocus, isGame, isGameBall, isGameTeam, isGoalReplayEndData, isGoalReplayStartData, isGoalReplayWillEndData, isGoalScoredData, isInteger, isLoadReplayData, isMatchCreatedData, isMatchDestroyedData, isMatchEndedData, isMatchInitializedData, isMatchPausedData, isMatchUnpausedData, isNumber, isPerspective, isPlayer, isPlayerJoinedData, isPlayerLeftData, isPlayerRef, isPodiumStartData, isRawEventEnvelope, isRecord, isReplayCreatedData, isRoundStartedData, isSeekReplayData, isSetGameSpeedData, isSetHUDVisibilityData, isSetMatchPausedData, isStatfeedEventData, isStatsApiCommand, isStatsApiEvent, isString, isUpdateStateData, isVector, oneOf, optional, parseEventMessage, statsApiIniPaths as s, shape };
|
|
918
|
+
export type { AnyEventListener, BallHitData, BallLastTouch, BaseEventData, BoostPickupData, BoostType, ChangePOVData, ClientEventName, ClientListenerMap, ClockUpdatedSecondsData, CommandDataMap, CommandName, ConnectionState, CountdownBeginData, CrossbarHitData, DisconnectInfo, EnsureStatsApiOptions, EnsureStatsApiResult, EventDataMap, EventName, Focus, Game, GameBall, GameEventListener, GameTeam, GoalReplayEndData, GoalReplayStartData, GoalReplayWillEndData, GoalScoredData, Guard, GuardedType, LifecycleListenerMap, LoadReplayData, MatchCreatedData, MatchDestroyedData, MatchEndedData, MatchInitializedData, MatchPausedData, MatchUnpausedData, PacketSendRateEdit as P, ParseFailure, ParseFailureReason, ParseOptions, ParseResult, ParseSuccess, Perspective, Player, PlayerJoinedData, PlayerLeftData, PlayerRef, PodiumStartData, PrimaryId, RawEventEnvelope, ReconnectInfo, ReconnectOptions, ReplayCreatedData, RoundStartedData, SeekReplayData, SetGameSpeedData, SetHUDVisibilityData, SetMatchPausedData, ShapeSpec, StatEventName, StatfeedEventData, StatsApiClientOptions, StatsApiCommand, StatsApiCommandOf, StatsApiEvent, StatsApiEventOf, TeamNum, Unsubscribe, UpdateStateData, Vector, WebSocketConstructor, WebSocketLike };
|