@ovencord/ws 2.0.2 → 2.0.4
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/package.json +1 -1
- package/src/strategies/sharding/IShardingStrategy.ts +4 -0
- package/src/strategies/sharding/SimpleShardingStrategy.ts +10 -0
- package/src/strategies/sharding/WorkerShardingStrategy.ts +5 -0
- package/src/utils/constants.ts +9 -9
- package/src/ws/WebSocketManager.ts +27 -1
- package/src/ws/WebSocketShard.ts +13 -1
package/package.json
CHANGED
|
@@ -27,4 +27,8 @@ export interface IShardingStrategy {
|
|
|
27
27
|
* Spawns all the shards
|
|
28
28
|
*/
|
|
29
29
|
spawn(shardIds: number[]): Awaitable<void>;
|
|
30
|
+
/**
|
|
31
|
+
* Gets all the shards managed by this strategy
|
|
32
|
+
*/
|
|
33
|
+
getShards(): Awaitable<Collection<number, { ping?: number; status: WebSocketShardStatus }>>;
|
|
30
34
|
}
|
|
@@ -81,4 +81,14 @@ export class SimpleShardingStrategy implements IShardingStrategy {
|
|
|
81
81
|
public async fetchStatus() {
|
|
82
82
|
return this.shards.mapValues((shard) => shard.status);
|
|
83
83
|
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* {@inheritDoc IShardingStrategy.getShards}
|
|
87
|
+
*/
|
|
88
|
+
public async getShards() {
|
|
89
|
+
return this.shards.mapValues((shard) => ({
|
|
90
|
+
ping: shard.ping,
|
|
91
|
+
status: shard.status,
|
|
92
|
+
}));
|
|
93
|
+
}
|
|
84
94
|
}
|
|
@@ -22,6 +22,11 @@ export class WorkerShardingStrategy implements IShardingStrategy {
|
|
|
22
22
|
this.options = options;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
public async getShards(): Promise<Collection<number, { ping?: number; status: WebSocketShardStatus; }>> {
|
|
26
|
+
const statuses = await this.fetchStatus();
|
|
27
|
+
return statuses.mapValues((status) => ({ status }));
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
|
|
26
31
|
public async spawn(shardIds: number[]) {
|
|
27
32
|
for (const shardId of shardIds) {
|
package/src/utils/constants.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { Collection } from '@ovencord/collection';
|
|
2
2
|
import { lazy } from '@ovencord/util';
|
|
3
|
-
import { APIVersion, GatewayOpcodes } from 'discord-api-types/v10';
|
|
3
|
+
import { APIVersion, GatewayOpcodes, type GatewayPresenceUpdateData } from 'discord-api-types/v10';
|
|
4
4
|
import { SimpleShardingStrategy } from '../strategies/sharding/SimpleShardingStrategy.js';
|
|
5
5
|
import { SimpleIdentifyThrottler } from '../throttling/SimpleIdentifyThrottler.js';
|
|
6
|
-
import type { SessionInfo, OptionalWebSocketManagerOptions, WebSocketManager } from '../ws/WebSocketManager.js';
|
|
6
|
+
import type { SessionInfo, OptionalWebSocketManagerOptions, WebSocketManager, ShardRange } from '../ws/WebSocketManager.js';
|
|
7
7
|
import type { SendRateLimitState } from '../ws/WebSocketShard.js';
|
|
8
8
|
|
|
9
9
|
/**
|
|
@@ -40,11 +40,11 @@ export const DefaultWebSocketManagerOptions = {
|
|
|
40
40
|
const info = await manager.fetchGatewayInformation();
|
|
41
41
|
return new SimpleIdentifyThrottler(info.session_start_limit.max_concurrency);
|
|
42
42
|
},
|
|
43
|
-
buildStrategy: (manager) => new SimpleShardingStrategy(manager),
|
|
44
|
-
shardCount: null,
|
|
45
|
-
shardIds: null,
|
|
46
|
-
largeThreshold: null,
|
|
47
|
-
initialPresence: null,
|
|
43
|
+
buildStrategy: (manager: WebSocketManager) => new SimpleShardingStrategy(manager),
|
|
44
|
+
shardCount: null as number | null,
|
|
45
|
+
shardIds: null as number[] | ShardRange | null,
|
|
46
|
+
largeThreshold: null as number | null,
|
|
47
|
+
initialPresence: null as GatewayPresenceUpdateData | null,
|
|
48
48
|
identifyProperties: {
|
|
49
49
|
browser: DefaultDeviceProperty,
|
|
50
50
|
device: DefaultDeviceProperty,
|
|
@@ -52,9 +52,9 @@ export const DefaultWebSocketManagerOptions = {
|
|
|
52
52
|
},
|
|
53
53
|
version: APIVersion,
|
|
54
54
|
encoding: Encoding.JSON,
|
|
55
|
-
compression: null,
|
|
55
|
+
compression: null as CompressionMethod | null,
|
|
56
56
|
useIdentifyCompression: false,
|
|
57
|
-
retrieveSessionInfo(shardId) {
|
|
57
|
+
retrieveSessionInfo(shardId: number) {
|
|
58
58
|
const store = getDefaultSessionStore();
|
|
59
59
|
return store.get(shardId) ?? null;
|
|
60
60
|
},
|
|
@@ -13,7 +13,7 @@ import type {
|
|
|
13
13
|
import type { IShardingStrategy } from '../strategies/sharding/IShardingStrategy.js';
|
|
14
14
|
import type { IIdentifyThrottler } from '../throttling/IIdentifyThrottler.js';
|
|
15
15
|
import { DefaultWebSocketManagerOptions, type CompressionMethod, type Encoding } from '../utils/constants.js';
|
|
16
|
-
import type
|
|
16
|
+
import { WebSocketShardStatus, type WebSocketShardDestroyOptions, type WebSocketShardEvents } from './WebSocketShard.js';
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Represents a range of shard ids
|
|
@@ -264,6 +264,32 @@ export class WebSocketManager extends AsyncEventEmitter<ManagerShardEventsMap> i
|
|
|
264
264
|
return this.#token;
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
/**
|
|
268
|
+
* Gets the average ping of all active shards
|
|
269
|
+
*
|
|
270
|
+
* @returns The average ping in milliseconds, or -1 if no shards are ready
|
|
271
|
+
*/
|
|
272
|
+
public get ping(): number {
|
|
273
|
+
return -1; // Will be calculated asynchronously
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
/**
|
|
277
|
+
* Gets the average ping of all active shards asynchronously
|
|
278
|
+
*
|
|
279
|
+
* @returns The average ping in milliseconds, or -1 if no shards are ready
|
|
280
|
+
*/
|
|
281
|
+
public async getPing(): Promise<number> {
|
|
282
|
+
const shards = await this.strategy.getShards();
|
|
283
|
+
const activeShards = [...shards.values()].filter(
|
|
284
|
+
(shard) => shard.status === WebSocketShardStatus.Ready && shard.ping !== undefined && shard.ping >= 0
|
|
285
|
+
);
|
|
286
|
+
|
|
287
|
+
if (activeShards.length === 0) return -1;
|
|
288
|
+
|
|
289
|
+
const totalPing = activeShards.reduce((sum, shard) => sum + (shard.ping ?? 0), 0);
|
|
290
|
+
return Math.round(totalPing / activeShards.length);
|
|
291
|
+
}
|
|
292
|
+
|
|
267
293
|
public constructor(options: CreateWebSocketManagerOptions) {
|
|
268
294
|
if (typeof options.fetchGatewayInformation !== 'function') {
|
|
269
295
|
throw new TypeError('fetchGatewayInformation is required');
|
package/src/ws/WebSocketShard.ts
CHANGED
|
@@ -98,6 +98,13 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
|
|
98
98
|
|
|
99
99
|
private lastHeartbeatAt = -1;
|
|
100
100
|
|
|
101
|
+
public lastPingTimestamp = -1;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The latency of the last heartbeat in milliseconds
|
|
105
|
+
*/
|
|
106
|
+
public ping = -1;
|
|
107
|
+
|
|
101
108
|
// Indicates whether the shard has already resolved its original connect() call
|
|
102
109
|
private initialConnectResolved = false;
|
|
103
110
|
|
|
@@ -510,6 +517,8 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
|
|
510
517
|
|
|
511
518
|
const session = await this.strategy.retrieveSessionInfo(this.id);
|
|
512
519
|
|
|
520
|
+
this.lastPingTimestamp = Number(Bun.nanoseconds());
|
|
521
|
+
|
|
513
522
|
await this.send({
|
|
514
523
|
op: GatewayOpcodes.Heartbeat,
|
|
515
524
|
|
|
@@ -689,10 +698,13 @@ export class WebSocketShard extends AsyncEventEmitter<WebSocketShardEventsMap> {
|
|
|
689
698
|
this.isAck = true;
|
|
690
699
|
|
|
691
700
|
const ackAt = Date.now();
|
|
701
|
+
const latency = Math.round((Number(Bun.nanoseconds()) - this.lastPingTimestamp) / 1_000_000);
|
|
702
|
+
this.ping = latency;
|
|
703
|
+
|
|
692
704
|
this.emit(WebSocketShardEvents.HeartbeatComplete, {
|
|
693
705
|
ackAt,
|
|
694
706
|
heartbeatAt: this.lastHeartbeatAt,
|
|
695
|
-
latency
|
|
707
|
+
latency,
|
|
696
708
|
});
|
|
697
709
|
|
|
698
710
|
break;
|