@juande_pl/rocketleague-stats-api-client 1.0.0 → 1.1.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 +204 -177
- package/dist/{chunk-IVUQE3LJ.js → chunk-5ZC7WJPK.js} +51 -1
- package/dist/chunk-5ZC7WJPK.js.map +1 -0
- package/dist/index.cjs +58 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +45 -2
- package/dist/index.d.ts +45 -2
- package/dist/index.js +15 -3
- package/dist/node.cjs +56 -0
- package/dist/node.cjs.map +1 -1
- package/dist/node.d.cts +1 -1
- package/dist/node.d.ts +1 -1
- package/dist/node.js +14 -2
- package/package.json +1 -1
- package/dist/chunk-IVUQE3LJ.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -596,6 +596,33 @@ interface ParseOptions {
|
|
|
596
596
|
*/
|
|
597
597
|
declare function parseEventMessage(raw: unknown, options?: ParseOptions): ParseResult;
|
|
598
598
|
|
|
599
|
+
/** Compares two players like an `Array.prototype.sort` callback. */
|
|
600
|
+
type PlayerComparator<T extends PlayerRef = Player> = (a: T, b: T) => number;
|
|
601
|
+
/** Sort direction. */
|
|
602
|
+
type SortOrder = 'asc' | 'desc';
|
|
603
|
+
/** Numeric `Player` fields that can be used with {@link byStat}. */
|
|
604
|
+
type PlayerStat = {
|
|
605
|
+
[K in keyof Player]-?: NonNullable<Player[K]> extends number ? K : never;
|
|
606
|
+
}[keyof Player];
|
|
607
|
+
/** Orders by team: Blue (0) first, or Orange (1) first with `'desc'`. */
|
|
608
|
+
declare function byTeam<T extends PlayerRef>(order?: SortOrder): PlayerComparator<T>;
|
|
609
|
+
/** Orders by spectator shortcut. */
|
|
610
|
+
declare function byShortcut<T extends PlayerRef>(order?: SortOrder): PlayerComparator<T>;
|
|
611
|
+
/** Orders by display name (case-insensitive, locale-aware). */
|
|
612
|
+
declare function byName<T extends PlayerRef>(order?: SortOrder): PlayerComparator<T>;
|
|
613
|
+
/** Orders by a numeric stat (`Score`, `Goals`, `Boost`…). Highest first by default. Missing values go last. */
|
|
614
|
+
declare function byStat(stat: PlayerStat, order?: SortOrder): PlayerComparator;
|
|
615
|
+
/**
|
|
616
|
+
* Returns a new array sorted by the given comparators: the first one decides, the next ones break ties.
|
|
617
|
+
* The sort is stable, so players that compare equal keep their original order. The input is not modified.
|
|
618
|
+
*
|
|
619
|
+
* ```ts
|
|
620
|
+
* sortPlayers(data.Players, byTeam(), byStat('Score')); // Blue then Orange, best score first
|
|
621
|
+
* sortPlayers(data.Players, (a, b) => a.Demos - b.Demos); // any custom comparator
|
|
622
|
+
* ```
|
|
623
|
+
*/
|
|
624
|
+
declare function sortPlayers<T extends PlayerRef>(players: readonly T[], ...comparators: PlayerComparator<T>[]): T[];
|
|
625
|
+
|
|
599
626
|
/** Base class for every error thrown or emitted by this library. */
|
|
600
627
|
declare class StatsApiError extends Error {
|
|
601
628
|
name: string;
|
|
@@ -773,6 +800,17 @@ interface StatsApiClientOptions {
|
|
|
773
800
|
* @default true
|
|
774
801
|
*/
|
|
775
802
|
validateEvents?: boolean;
|
|
803
|
+
/**
|
|
804
|
+
* Drop the fake `GoalScored` event the game sends when a replay ends
|
|
805
|
+
* (its `Scorer` is `{ Name: '', Shortcut: 0, TeamNum: 0 }`). See {@link isReplayEndGoal}.
|
|
806
|
+
* @default true
|
|
807
|
+
*/
|
|
808
|
+
ignoreReplayEndGoal?: boolean;
|
|
809
|
+
/**
|
|
810
|
+
* Sort `UpdateState.Players` before listeners receive it. Pass one comparator, or several to break ties
|
|
811
|
+
* (e.g. `[byTeam(), byStat('Score')]`). When omitted, players keep the order sent by the game.
|
|
812
|
+
*/
|
|
813
|
+
sortPlayers?: PlayerComparator | PlayerComparator[];
|
|
776
814
|
/**
|
|
777
815
|
* WebSocket implementation. Defaults to `globalThis.WebSocket`
|
|
778
816
|
* (browsers, Node.js >= 22). Pass `WebSocket` from the `ws` package for older runtimes.
|
|
@@ -786,6 +824,11 @@ interface StatsApiClientOptions {
|
|
|
786
824
|
*/
|
|
787
825
|
ensureStatsApi?: boolean | EnsureStatsApiOptions;
|
|
788
826
|
}
|
|
827
|
+
/**
|
|
828
|
+
* `true` for the fake `GoalScored` event the game sends when a replay ends:
|
|
829
|
+
* its `Scorer` is `{ Name: '', Shortcut: 0, TeamNum: 0 }`.
|
|
830
|
+
*/
|
|
831
|
+
declare function isReplayEndGoal(event: StatsApiEvent): boolean;
|
|
789
832
|
/** Connection state of the client. */
|
|
790
833
|
type ConnectionState = 'idle' | 'connecting' | 'open' | 'closed';
|
|
791
834
|
interface DisconnectInfo {
|
|
@@ -914,5 +957,5 @@ declare class RocketLeagueStatsClient {
|
|
|
914
957
|
send(command: StatsApiCommand): void;
|
|
915
958
|
}
|
|
916
959
|
|
|
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 };
|
|
960
|
+
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, byName, byShortcut, byStat, byTeam, 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, isReplayEndGoal, isRoundStartedData, isSeekReplayData, isSetGameSpeedData, isSetHUDVisibilityData, isSetMatchPausedData, isStatfeedEventData, isStatsApiCommand, isStatsApiEvent, isString, isUpdateStateData, isVector, oneOf, optional, parseEventMessage, statsApiIniPaths as s, shape, sortPlayers };
|
|
961
|
+
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, PlayerComparator, PlayerJoinedData, PlayerLeftData, PlayerRef, PlayerStat, PodiumStartData, PrimaryId, RawEventEnvelope, ReconnectInfo, ReconnectOptions, ReplayCreatedData, RoundStartedData, SeekReplayData, SetGameSpeedData, SetHUDVisibilityData, SetMatchPausedData, ShapeSpec, SortOrder, StatEventName, StatfeedEventData, StatsApiClientOptions, StatsApiCommand, StatsApiCommandOf, StatsApiEvent, StatsApiEventOf, TeamNum, Unsubscribe, UpdateStateData, Vector, WebSocketConstructor, WebSocketLike };
|
package/dist/index.js
CHANGED
|
@@ -12,6 +12,10 @@ import {
|
|
|
12
12
|
Team,
|
|
13
13
|
allOf,
|
|
14
14
|
arrayOf,
|
|
15
|
+
byName,
|
|
16
|
+
byShortcut,
|
|
17
|
+
byStat,
|
|
18
|
+
byTeam,
|
|
15
19
|
commandDataGuards,
|
|
16
20
|
eventDataGuards,
|
|
17
21
|
isBallHitData,
|
|
@@ -52,6 +56,7 @@ import {
|
|
|
52
56
|
isRawEventEnvelope,
|
|
53
57
|
isRecord,
|
|
54
58
|
isReplayCreatedData,
|
|
59
|
+
isReplayEndGoal,
|
|
55
60
|
isRoundStartedData,
|
|
56
61
|
isSeekReplayData,
|
|
57
62
|
isSetGameSpeedData,
|
|
@@ -66,8 +71,9 @@ import {
|
|
|
66
71
|
oneOf,
|
|
67
72
|
optional,
|
|
68
73
|
parseEventMessage,
|
|
69
|
-
shape
|
|
70
|
-
|
|
74
|
+
shape,
|
|
75
|
+
sortPlayers
|
|
76
|
+
} from "./chunk-5ZC7WJPK.js";
|
|
71
77
|
export {
|
|
72
78
|
BALL_UNTOUCHED_TEAM_NUM,
|
|
73
79
|
COMMAND_NAMES,
|
|
@@ -82,6 +88,10 @@ export {
|
|
|
82
88
|
Team,
|
|
83
89
|
allOf,
|
|
84
90
|
arrayOf,
|
|
91
|
+
byName,
|
|
92
|
+
byShortcut,
|
|
93
|
+
byStat,
|
|
94
|
+
byTeam,
|
|
85
95
|
commandDataGuards,
|
|
86
96
|
RocketLeagueStatsClient as default,
|
|
87
97
|
eventDataGuards,
|
|
@@ -123,6 +133,7 @@ export {
|
|
|
123
133
|
isRawEventEnvelope,
|
|
124
134
|
isRecord,
|
|
125
135
|
isReplayCreatedData,
|
|
136
|
+
isReplayEndGoal,
|
|
126
137
|
isRoundStartedData,
|
|
127
138
|
isSeekReplayData,
|
|
128
139
|
isSetGameSpeedData,
|
|
@@ -137,6 +148,7 @@ export {
|
|
|
137
148
|
oneOf,
|
|
138
149
|
optional,
|
|
139
150
|
parseEventMessage,
|
|
140
|
-
shape
|
|
151
|
+
shape,
|
|
152
|
+
sortPlayers
|
|
141
153
|
};
|
|
142
154
|
//# sourceMappingURL=index.js.map
|
package/dist/node.cjs
CHANGED
|
@@ -243,6 +243,10 @@ __export(node_exports, {
|
|
|
243
243
|
Team: () => Team,
|
|
244
244
|
allOf: () => allOf,
|
|
245
245
|
arrayOf: () => arrayOf,
|
|
246
|
+
byName: () => byName,
|
|
247
|
+
byShortcut: () => byShortcut,
|
|
248
|
+
byStat: () => byStat,
|
|
249
|
+
byTeam: () => byTeam,
|
|
246
250
|
commandDataGuards: () => commandDataGuards,
|
|
247
251
|
default: () => RocketLeagueStatsClient,
|
|
248
252
|
editPacketSendRate: () => editPacketSendRate,
|
|
@@ -288,6 +292,7 @@ __export(node_exports, {
|
|
|
288
292
|
isRawEventEnvelope: () => isRawEventEnvelope,
|
|
289
293
|
isRecord: () => isRecord,
|
|
290
294
|
isReplayCreatedData: () => isReplayCreatedData,
|
|
295
|
+
isReplayEndGoal: () => isReplayEndGoal,
|
|
291
296
|
isRoundStartedData: () => isRoundStartedData,
|
|
292
297
|
isSeekReplayData: () => isSeekReplayData,
|
|
293
298
|
isSetGameSpeedData: () => isSetGameSpeedData,
|
|
@@ -303,6 +308,7 @@ __export(node_exports, {
|
|
|
303
308
|
optional: () => optional,
|
|
304
309
|
parseEventMessage: () => parseEventMessage,
|
|
305
310
|
shape: () => shape,
|
|
311
|
+
sortPlayers: () => sortPlayers,
|
|
306
312
|
statsApiIniPaths: () => statsApiIniPaths
|
|
307
313
|
});
|
|
308
314
|
module.exports = __toCommonJS(node_exports);
|
|
@@ -642,6 +648,37 @@ function parseEventMessage(raw, options = {}) {
|
|
|
642
648
|
return { ok: true, event: { Event: event, Data: data } };
|
|
643
649
|
}
|
|
644
650
|
|
|
651
|
+
// src/sort.ts
|
|
652
|
+
var direction = (order) => order === "asc" ? 1 : -1;
|
|
653
|
+
function byTeam(order = "asc") {
|
|
654
|
+
return (a, b) => (a.TeamNum - b.TeamNum) * direction(order);
|
|
655
|
+
}
|
|
656
|
+
function byShortcut(order = "asc") {
|
|
657
|
+
return (a, b) => (a.Shortcut - b.Shortcut) * direction(order);
|
|
658
|
+
}
|
|
659
|
+
function byName(order = "asc") {
|
|
660
|
+
return (a, b) => a.Name.localeCompare(b.Name, void 0, { sensitivity: "base" }) * direction(order);
|
|
661
|
+
}
|
|
662
|
+
function byStat(stat, order = "desc") {
|
|
663
|
+
return (a, b) => {
|
|
664
|
+
const x = a[stat];
|
|
665
|
+
const y = b[stat];
|
|
666
|
+
if (x === void 0 || y === void 0) return x === y ? 0 : x === void 0 ? 1 : -1;
|
|
667
|
+
return (x - y) * direction(order);
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
function sortPlayers(players, ...comparators) {
|
|
671
|
+
const copy = [...players];
|
|
672
|
+
if (comparators.length === 0) return copy;
|
|
673
|
+
return copy.sort((a, b) => {
|
|
674
|
+
for (const compare of comparators) {
|
|
675
|
+
const result = compare(a, b);
|
|
676
|
+
if (result !== 0) return result;
|
|
677
|
+
}
|
|
678
|
+
return 0;
|
|
679
|
+
});
|
|
680
|
+
}
|
|
681
|
+
|
|
645
682
|
// src/errors.ts
|
|
646
683
|
var StatsApiError = class extends Error {
|
|
647
684
|
name = "StatsApiError";
|
|
@@ -676,6 +713,11 @@ var ListenerError = class extends StatsApiError {
|
|
|
676
713
|
|
|
677
714
|
// src/client.ts
|
|
678
715
|
var WS_OPEN = 1;
|
|
716
|
+
function isReplayEndGoal(event) {
|
|
717
|
+
if (event.Event !== "GoalScored") return false;
|
|
718
|
+
const { Name, Shortcut, TeamNum } = event.Data.Scorer;
|
|
719
|
+
return Name === "" && Shortcut === 0 && TeamNum === 0;
|
|
720
|
+
}
|
|
679
721
|
function isNodeRuntime() {
|
|
680
722
|
const proc = globalThis.process;
|
|
681
723
|
return typeof proc?.versions?.node === "string";
|
|
@@ -691,6 +733,8 @@ var RocketLeagueStatsClient = class {
|
|
|
691
733
|
#autoConnect;
|
|
692
734
|
#reconnect;
|
|
693
735
|
#validateEvents;
|
|
736
|
+
#ignoreReplayEndGoal;
|
|
737
|
+
#playerComparators;
|
|
694
738
|
#WebSocket;
|
|
695
739
|
#socket = null;
|
|
696
740
|
#state = "idle";
|
|
@@ -704,6 +748,8 @@ var RocketLeagueStatsClient = class {
|
|
|
704
748
|
this.url = options.url ?? `${protocol}://${options.host ?? "127.0.0.1"}:${options.port ?? 49124}`;
|
|
705
749
|
this.#autoConnect = options.autoConnect ?? true;
|
|
706
750
|
this.#validateEvents = options.validateEvents ?? true;
|
|
751
|
+
this.#ignoreReplayEndGoal = options.ignoreReplayEndGoal ?? true;
|
|
752
|
+
this.#playerComparators = options.sortPlayers === void 0 ? [] : [options.sortPlayers].flat();
|
|
707
753
|
this.#WebSocket = options.WebSocket ?? globalThis.WebSocket;
|
|
708
754
|
const reconnect = options.reconnect ?? true;
|
|
709
755
|
this.#reconnect = reconnect === false ? null : {
|
|
@@ -842,6 +888,10 @@ var RocketLeagueStatsClient = class {
|
|
|
842
888
|
return;
|
|
843
889
|
}
|
|
844
890
|
const { event } = result;
|
|
891
|
+
if (this.#ignoreReplayEndGoal && isReplayEndGoal(event)) return;
|
|
892
|
+
if (this.#playerComparators.length > 0 && event.Event === "UpdateState" && Array.isArray(event.Data.Players)) {
|
|
893
|
+
event.Data.Players = sortPlayers(event.Data.Players, ...this.#playerComparators);
|
|
894
|
+
}
|
|
845
895
|
this.#dispatch(event.Event, [event.Data, event]);
|
|
846
896
|
for (const listener of [...this.#anyListeners]) {
|
|
847
897
|
this.#invoke("*", listener, [event]);
|
|
@@ -983,6 +1033,10 @@ init_config();
|
|
|
983
1033
|
Team,
|
|
984
1034
|
allOf,
|
|
985
1035
|
arrayOf,
|
|
1036
|
+
byName,
|
|
1037
|
+
byShortcut,
|
|
1038
|
+
byStat,
|
|
1039
|
+
byTeam,
|
|
986
1040
|
commandDataGuards,
|
|
987
1041
|
editPacketSendRate,
|
|
988
1042
|
ensureStatsApiEnabled,
|
|
@@ -1027,6 +1081,7 @@ init_config();
|
|
|
1027
1081
|
isRawEventEnvelope,
|
|
1028
1082
|
isRecord,
|
|
1029
1083
|
isReplayCreatedData,
|
|
1084
|
+
isReplayEndGoal,
|
|
1030
1085
|
isRoundStartedData,
|
|
1031
1086
|
isSeekReplayData,
|
|
1032
1087
|
isSetGameSpeedData,
|
|
@@ -1042,6 +1097,7 @@ init_config();
|
|
|
1042
1097
|
optional,
|
|
1043
1098
|
parseEventMessage,
|
|
1044
1099
|
shape,
|
|
1100
|
+
sortPlayers,
|
|
1045
1101
|
statsApiIniPaths
|
|
1046
1102
|
});
|
|
1047
1103
|
//# sourceMappingURL=node.cjs.map
|