aqualink 2.7.3 → 2.8.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/build/handlers/fetchImage.js +121 -23
- package/build/index.d.ts +169 -10
- package/build/structures/Aqua.js +240 -317
- package/build/structures/Connection.js +20 -24
- package/build/structures/Node.js +113 -88
- package/build/structures/Player.js +155 -169
- package/build/structures/Queue.js +13 -22
- package/build/structures/Rest.js +107 -136
- package/build/structures/Track.js +113 -33
- package/package.json +1 -1
|
@@ -14,20 +14,22 @@ class Connection {
|
|
|
14
14
|
this.endpoint = null;
|
|
15
15
|
this.token = null;
|
|
16
16
|
this.region = null;
|
|
17
|
-
this.selfDeaf = false;
|
|
18
|
-
this.selfMute = false;
|
|
19
17
|
}
|
|
20
18
|
|
|
21
19
|
setServerUpdate(data) {
|
|
22
|
-
if (!data?.endpoint)
|
|
20
|
+
if (!data?.endpoint || !data.token) {
|
|
21
|
+
this.aqua.emit("debug", `[Player ${this.guildId}] Received incomplete server update.`);
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
23
24
|
|
|
24
25
|
const { endpoint, token } = data;
|
|
25
|
-
|
|
26
26
|
const regionMatch = REGION_REGEX.exec(endpoint);
|
|
27
|
-
if (!regionMatch)
|
|
28
|
-
|
|
27
|
+
if (!regionMatch) {
|
|
28
|
+
this.aqua.emit("debug", `[Player ${this.guildId}] Failed to extract region from endpoint: ${endpoint}`);
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
29
31
|
const newRegion = regionMatch[1];
|
|
30
|
-
|
|
32
|
+
|
|
31
33
|
if (this.endpoint === endpoint && this.token === token && this.region === newRegion) {
|
|
32
34
|
return;
|
|
33
35
|
}
|
|
@@ -38,7 +40,7 @@ class Connection {
|
|
|
38
40
|
this.region = newRegion;
|
|
39
41
|
|
|
40
42
|
this.aqua.emit("debug",
|
|
41
|
-
`[Player ${this.guildId}
|
|
43
|
+
`[Player ${this.guildId}] Voice server updated: ${oldRegion ? `Changed from ${oldRegion} to ${newRegion}` : newRegion}`
|
|
42
44
|
);
|
|
43
45
|
|
|
44
46
|
if (this.player.paused) {
|
|
@@ -49,13 +51,12 @@ class Connection {
|
|
|
49
51
|
}
|
|
50
52
|
|
|
51
53
|
setStateUpdate(data) {
|
|
52
|
-
if (!data) {
|
|
53
|
-
this._destroyPlayer();
|
|
54
|
+
if (!data || data.user_id !== this.aqua.clientId) {
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
57
|
|
|
57
|
-
const {
|
|
58
|
-
|
|
58
|
+
const { session_id, channel_id, self_deaf, self_mute } = data;
|
|
59
|
+
|
|
59
60
|
if (!channel_id || !session_id) {
|
|
60
61
|
this._destroyPlayer();
|
|
61
62
|
return;
|
|
@@ -67,27 +68,26 @@ class Connection {
|
|
|
67
68
|
this.player.voiceChannel = channel_id;
|
|
68
69
|
}
|
|
69
70
|
|
|
70
|
-
this.selfDeaf = !!self_deaf;
|
|
71
|
-
this.selfMute = !!self_mute;
|
|
71
|
+
this.player.selfDeaf = !!self_deaf;
|
|
72
|
+
this.player.selfMute = !!self_mute;
|
|
72
73
|
|
|
73
74
|
if (this.sessionId !== session_id) {
|
|
74
75
|
this.sessionId = session_id;
|
|
76
|
+
this.aqua.emit("debug", `[Player ${this.guildId}] Received new session ID.`);
|
|
75
77
|
this._updatePlayerVoiceData();
|
|
76
78
|
}
|
|
77
79
|
}
|
|
78
80
|
|
|
79
81
|
_destroyPlayer() {
|
|
80
|
-
if (this.player) {
|
|
82
|
+
if (this.player && !this.player.destroyed) {
|
|
81
83
|
this.player.destroy();
|
|
82
84
|
this.aqua.emit("playerDestroy", this.player);
|
|
83
85
|
}
|
|
84
86
|
}
|
|
85
87
|
|
|
86
88
|
_updatePlayerVoiceData() {
|
|
87
|
-
if (!this.player)
|
|
88
|
-
|
|
89
|
-
if (!this.sessionId || !this.endpoint || !this.token) {
|
|
90
|
-
this.aqua.emit("debug", `[Player ${this.guildId}] Incomplete voice data, waiting for complete data`);
|
|
89
|
+
if (!this.player || !this.sessionId || !this.endpoint || !this.token) {
|
|
90
|
+
this.aqua.emit("debug", `[Player ${this.guildId}] Incomplete voice data, waiting...`);
|
|
91
91
|
return;
|
|
92
92
|
}
|
|
93
93
|
|
|
@@ -100,10 +100,7 @@ class Connection {
|
|
|
100
100
|
try {
|
|
101
101
|
this.nodes.rest.updatePlayer({
|
|
102
102
|
guildId: this.guildId,
|
|
103
|
-
data: {
|
|
104
|
-
voice: voiceData,
|
|
105
|
-
volume: this.player.volume
|
|
106
|
-
}
|
|
103
|
+
data: { voice: voiceData, volume: this.player.volume },
|
|
107
104
|
});
|
|
108
105
|
} catch (error) {
|
|
109
106
|
this.aqua.emit("apiError", "updatePlayer", {
|
|
@@ -113,7 +110,6 @@ class Connection {
|
|
|
113
110
|
});
|
|
114
111
|
}
|
|
115
112
|
}
|
|
116
|
-
|
|
117
113
|
}
|
|
118
114
|
|
|
119
115
|
module.exports = Connection;
|
package/build/structures/Node.js
CHANGED
|
@@ -2,11 +2,18 @@
|
|
|
2
2
|
const WebSocket = require('ws');
|
|
3
3
|
const Rest = require("./Rest");
|
|
4
4
|
|
|
5
|
+
const LYRICS_OP_REGEX = /^Lyrics/;
|
|
6
|
+
const JSON_VALIDATION_REGEX = /^[\s]*[{\[]/;
|
|
7
|
+
|
|
5
8
|
class Node {
|
|
6
9
|
static BACKOFF_MULTIPLIER = 1.5;
|
|
7
10
|
static MAX_BACKOFF = 60000;
|
|
8
11
|
static WS_OPEN = WebSocket.OPEN;
|
|
9
12
|
static WS_CLOSE_NORMAL = 1000;
|
|
13
|
+
static DEFAULT_RECONNECT_TIMEOUT = 2000;
|
|
14
|
+
static DEFAULT_RESUME_TIMEOUT = 60;
|
|
15
|
+
static JITTER_MAX = 2000;
|
|
16
|
+
static JITTER_FACTOR = 0.2;
|
|
10
17
|
|
|
11
18
|
constructor(aqua, connOptions, options = {}) {
|
|
12
19
|
this.aqua = aqua;
|
|
@@ -21,30 +28,26 @@ class Node {
|
|
|
21
28
|
regions = []
|
|
22
29
|
} = connOptions;
|
|
23
30
|
|
|
24
|
-
this
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
this.sessionId = sessionId;
|
|
30
|
-
this.regions = regions;
|
|
31
|
+
Object.assign(this, {
|
|
32
|
+
host, name, port, password, sessionId, regions,
|
|
33
|
+
secure: !!secure,
|
|
34
|
+
wsUrl: `ws${secure ? "s" : ""}://${host}:${port}/v4/websocket`
|
|
35
|
+
});
|
|
31
36
|
|
|
32
|
-
this.wsUrl = `ws${this.secure ? "s" : ""}://${this.host}:${this.port}/v4/websocket`;
|
|
33
37
|
this.rest = new Rest(aqua, this);
|
|
34
38
|
|
|
35
39
|
const {
|
|
36
|
-
resumeTimeout =
|
|
40
|
+
resumeTimeout = Node.DEFAULT_RESUME_TIMEOUT,
|
|
37
41
|
autoResume = false,
|
|
38
|
-
reconnectTimeout =
|
|
42
|
+
reconnectTimeout = Node.DEFAULT_RECONNECT_TIMEOUT,
|
|
39
43
|
reconnectTries = 3,
|
|
40
44
|
infiniteReconnects = false
|
|
41
45
|
} = options;
|
|
42
46
|
|
|
43
|
-
this
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
this.infiniteReconnects = infiniteReconnects;
|
|
47
|
+
Object.assign(this, {
|
|
48
|
+
resumeTimeout, autoResume, reconnectTimeout,
|
|
49
|
+
reconnectTries, infiniteReconnects
|
|
50
|
+
});
|
|
48
51
|
|
|
49
52
|
this.connected = false;
|
|
50
53
|
this.info = null;
|
|
@@ -53,33 +56,40 @@ class Node {
|
|
|
53
56
|
this.reconnectTimeoutId = null;
|
|
54
57
|
this.isDestroyed = false;
|
|
55
58
|
|
|
56
|
-
this.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
59
|
+
this._boundHandlers = {
|
|
60
|
+
onOpen: () => this._onOpen(),
|
|
61
|
+
onError: (error) => this._onError(error),
|
|
62
|
+
onMessage: (msg) => this._onMessage(msg),
|
|
63
|
+
onClose: (code, reason) => this._onClose(code, reason)
|
|
64
|
+
};
|
|
60
65
|
|
|
61
66
|
this._headers = this._constructHeaders();
|
|
62
|
-
this.
|
|
67
|
+
this._initializeStats();
|
|
63
68
|
}
|
|
64
69
|
|
|
65
|
-
|
|
70
|
+
_initializeStats() {
|
|
66
71
|
this.stats = {
|
|
67
72
|
players: 0,
|
|
68
73
|
playingPlayers: 0,
|
|
69
74
|
uptime: 0,
|
|
70
|
-
memory: {
|
|
71
|
-
|
|
75
|
+
memory: {
|
|
76
|
+
free: 0, used: 0, allocated: 0, reservable: 0,
|
|
77
|
+
freePercentage: 0, usedPercentage: 0
|
|
78
|
+
},
|
|
79
|
+
cpu: {
|
|
80
|
+
cores: 0, systemLoad: 0, lavalinkLoad: 0,
|
|
81
|
+
lavalinkLoadPercentage: 0
|
|
82
|
+
},
|
|
72
83
|
frameStats: { sent: 0, nulled: 0, deficit: 0 },
|
|
73
84
|
ping: 0
|
|
74
85
|
};
|
|
75
86
|
}
|
|
76
87
|
|
|
77
88
|
_constructHeaders() {
|
|
78
|
-
const headers =
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
};
|
|
89
|
+
const headers = Object.create(null);
|
|
90
|
+
headers.Authorization = this.password;
|
|
91
|
+
headers["User-Id"] = this.aqua.clientId;
|
|
92
|
+
headers["Client-Name"] = `Aqua/${this.aqua.version} (https://github.com/ToddyTheNoobDud/AquaLink)`;
|
|
83
93
|
|
|
84
94
|
if (this.sessionId) {
|
|
85
95
|
headers["Session-Id"] = this.sessionId;
|
|
@@ -104,7 +114,7 @@ class Node {
|
|
|
104
114
|
}
|
|
105
115
|
} catch (err) {
|
|
106
116
|
this.info = null;
|
|
107
|
-
this.
|
|
117
|
+
this._emitError(`Failed to fetch node info: ${err.message}`);
|
|
108
118
|
}
|
|
109
119
|
}
|
|
110
120
|
|
|
@@ -113,18 +123,22 @@ class Node {
|
|
|
113
123
|
}
|
|
114
124
|
|
|
115
125
|
_onMessage(msg) {
|
|
126
|
+
if (!JSON_VALIDATION_REGEX.test(msg)) {
|
|
127
|
+
this.aqua.emit("debug", this.name, `Received invalid JSON format: ${msg.slice(0, 100)}...`);
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
116
131
|
let payload;
|
|
117
132
|
try {
|
|
118
133
|
payload = JSON.parse(msg);
|
|
119
134
|
} catch {
|
|
120
|
-
this.aqua.emit("debug", this.name, `
|
|
135
|
+
this.aqua.emit("debug", this.name, `JSON parse failed: ${msg.slice(0, 100)}...`);
|
|
121
136
|
return;
|
|
122
137
|
}
|
|
123
138
|
|
|
124
139
|
const { op, guildId } = payload;
|
|
125
140
|
if (!op) return;
|
|
126
141
|
|
|
127
|
-
|
|
128
142
|
switch (op) {
|
|
129
143
|
case "stats":
|
|
130
144
|
this._updateStats(payload);
|
|
@@ -133,30 +147,32 @@ class Node {
|
|
|
133
147
|
this._handleReadyOp(payload);
|
|
134
148
|
break;
|
|
135
149
|
default:
|
|
136
|
-
|
|
137
|
-
const player = guildId ? this.aqua.players.get(guildId) : null;
|
|
138
|
-
this.aqua.emit(op, player, payload.track || null, payload);
|
|
139
|
-
} else if (guildId) {
|
|
140
|
-
const player = this.aqua.players.get(guildId);
|
|
141
|
-
player?.emit(op, payload);
|
|
142
|
-
}
|
|
150
|
+
this._handleCustomOp(op, guildId, payload);
|
|
143
151
|
break;
|
|
144
152
|
}
|
|
145
153
|
}
|
|
146
154
|
|
|
155
|
+
_handleCustomOp(op, guildId, payload) {
|
|
156
|
+
if (LYRICS_OP_REGEX.test(op)) {
|
|
157
|
+
const player = guildId ? this.aqua.players.get(guildId) : null;
|
|
158
|
+
this.aqua.emit(op, player, payload.track || null, payload);
|
|
159
|
+
} else if (guildId) {
|
|
160
|
+
const player = this.aqua.players.get(guildId);
|
|
161
|
+
if (player) player.emit(op, payload);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
147
165
|
_onClose(code, reason) {
|
|
148
166
|
this.connected = false;
|
|
149
167
|
const reasonStr = reason?.toString() || "No reason provided";
|
|
150
168
|
|
|
151
169
|
this.aqua.emit("nodeDisconnect", this, { code, reason: reasonStr });
|
|
152
|
-
|
|
153
170
|
this.aqua.handleNodeFailover(this);
|
|
154
|
-
|
|
155
|
-
this.scheduleReconnect(code);
|
|
171
|
+
this._scheduleReconnect(code);
|
|
156
172
|
}
|
|
157
173
|
|
|
158
|
-
|
|
159
|
-
this.
|
|
174
|
+
_scheduleReconnect(code) {
|
|
175
|
+
this._clearReconnectTimeout();
|
|
160
176
|
|
|
161
177
|
if (code === Node.WS_CLOSE_NORMAL || this.isDestroyed) {
|
|
162
178
|
this.aqua.emit("debug", this.name, "WebSocket closed normally, not reconnecting");
|
|
@@ -170,12 +186,12 @@ class Node {
|
|
|
170
186
|
}
|
|
171
187
|
|
|
172
188
|
if (this.reconnectAttempted >= this.reconnectTries) {
|
|
173
|
-
this.
|
|
189
|
+
this._emitError(new Error(`Max reconnection attempts reached (${this.reconnectTries})`));
|
|
174
190
|
this.destroy(true);
|
|
175
191
|
return;
|
|
176
192
|
}
|
|
177
193
|
|
|
178
|
-
const backoffTime = this.
|
|
194
|
+
const backoffTime = this._calculateBackoff();
|
|
179
195
|
this.reconnectAttempted++;
|
|
180
196
|
|
|
181
197
|
this.aqua.emit("nodeReconnect", this, {
|
|
@@ -186,13 +202,14 @@ class Node {
|
|
|
186
202
|
this.reconnectTimeoutId = setTimeout(() => this.connect(), backoffTime);
|
|
187
203
|
}
|
|
188
204
|
|
|
189
|
-
|
|
190
|
-
const baseBackoff = this.reconnectTimeout *
|
|
191
|
-
const
|
|
205
|
+
_calculateBackoff() {
|
|
206
|
+
const baseBackoff = this.reconnectTimeout * (Node.BACKOFF_MULTIPLIER ** this.reconnectAttempted);
|
|
207
|
+
const maxJitter = Math.min(Node.JITTER_MAX, baseBackoff * Node.JITTER_FACTOR);
|
|
208
|
+
const jitter = Math.random() * maxJitter;
|
|
192
209
|
return Math.min(baseBackoff + jitter, Node.MAX_BACKOFF);
|
|
193
210
|
}
|
|
194
211
|
|
|
195
|
-
|
|
212
|
+
_clearReconnectTimeout() {
|
|
196
213
|
if (this.reconnectTimeoutId) {
|
|
197
214
|
clearTimeout(this.reconnectTimeoutId);
|
|
198
215
|
this.reconnectTimeoutId = null;
|
|
@@ -202,26 +219,26 @@ class Node {
|
|
|
202
219
|
async connect() {
|
|
203
220
|
if (this.isDestroyed) return;
|
|
204
221
|
|
|
205
|
-
if (this.ws
|
|
222
|
+
if (this.ws?.readyState === Node.WS_OPEN) {
|
|
206
223
|
this.aqua.emit("debug", this.name, "WebSocket already connected");
|
|
207
224
|
return;
|
|
208
225
|
}
|
|
209
226
|
|
|
210
|
-
this.
|
|
227
|
+
this._cleanupExistingConnection();
|
|
211
228
|
|
|
212
229
|
this.ws = new WebSocket(this.wsUrl, {
|
|
213
230
|
headers: this._headers,
|
|
214
231
|
perMessageDeflate: false
|
|
215
232
|
});
|
|
216
233
|
|
|
217
|
-
|
|
218
|
-
this.ws.once("
|
|
219
|
-
this.ws.
|
|
220
|
-
this.ws.
|
|
221
|
-
|
|
234
|
+
const handlers = this._boundHandlers;
|
|
235
|
+
this.ws.once("open", handlers.onOpen);
|
|
236
|
+
this.ws.once("error", handlers.onError);
|
|
237
|
+
this.ws.on("message", handlers.onMessage);
|
|
238
|
+
this.ws.once("close", handlers.onClose);
|
|
222
239
|
}
|
|
223
240
|
|
|
224
|
-
|
|
241
|
+
_cleanupExistingConnection() {
|
|
225
242
|
if (!this.ws) return;
|
|
226
243
|
|
|
227
244
|
this.ws.removeAllListeners();
|
|
@@ -230,7 +247,7 @@ class Node {
|
|
|
230
247
|
try {
|
|
231
248
|
this.ws.close();
|
|
232
249
|
} catch (err) {
|
|
233
|
-
this.
|
|
250
|
+
this._emitError(`Failed to close WebSocket: ${err.message}`);
|
|
234
251
|
}
|
|
235
252
|
}
|
|
236
253
|
|
|
@@ -239,8 +256,8 @@ class Node {
|
|
|
239
256
|
|
|
240
257
|
destroy(clean = false) {
|
|
241
258
|
this.isDestroyed = true;
|
|
242
|
-
this.
|
|
243
|
-
this.
|
|
259
|
+
this._clearReconnectTimeout();
|
|
260
|
+
this._cleanupExistingConnection();
|
|
244
261
|
|
|
245
262
|
if (!clean) {
|
|
246
263
|
this.aqua.handleNodeFailover(this);
|
|
@@ -260,39 +277,45 @@ class Node {
|
|
|
260
277
|
try {
|
|
261
278
|
const newStats = await this.rest.getStats();
|
|
262
279
|
if (newStats && this.stats) {
|
|
263
|
-
this.
|
|
264
|
-
this.stats.playingPlayers = newStats.playingPlayers ?? this.stats.playingPlayers;
|
|
265
|
-
this.stats.uptime = newStats.uptime ?? this.stats.uptime;
|
|
266
|
-
this.stats.ping = newStats.ping ?? this.stats.ping;
|
|
267
|
-
|
|
268
|
-
if (newStats.memory) {
|
|
269
|
-
Object.assign(this.stats.memory, newStats.memory);
|
|
270
|
-
this._calculateMemoryPercentages();
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
if (newStats.cpu) {
|
|
274
|
-
Object.assign(this.stats.cpu, newStats.cpu);
|
|
275
|
-
this._calculateCpuPercentages();
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
if (newStats.frameStats) {
|
|
279
|
-
Object.assign(this.stats.frameStats, newStats.frameStats);
|
|
280
|
-
}
|
|
280
|
+
this._mergeStats(newStats);
|
|
281
281
|
}
|
|
282
282
|
return this.stats;
|
|
283
283
|
} catch (err) {
|
|
284
|
-
this.
|
|
284
|
+
this._emitError(`Failed to fetch node stats: ${err.message}`);
|
|
285
285
|
return this.stats;
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
|
|
289
|
+
_mergeStats(newStats) {
|
|
290
|
+
this.stats.players = newStats.players ?? this.stats.players;
|
|
291
|
+
this.stats.playingPlayers = newStats.playingPlayers ?? this.stats.playingPlayers;
|
|
292
|
+
this.stats.uptime = newStats.uptime ?? this.stats.uptime;
|
|
293
|
+
this.stats.ping = newStats.ping ?? this.stats.ping;
|
|
294
|
+
|
|
295
|
+
if (newStats.memory) {
|
|
296
|
+
Object.assign(this.stats.memory, newStats.memory);
|
|
297
|
+
this._calculateMemoryPercentages();
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
if (newStats.cpu) {
|
|
301
|
+
Object.assign(this.stats.cpu, newStats.cpu);
|
|
302
|
+
this._calculateCpuPercentages();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
if (newStats.frameStats) {
|
|
306
|
+
Object.assign(this.stats.frameStats, newStats.frameStats);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
|
|
289
310
|
_updateStats(payload) {
|
|
290
311
|
if (!payload) return;
|
|
291
312
|
|
|
292
|
-
this.stats
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
313
|
+
Object.assign(this.stats, {
|
|
314
|
+
players: payload.players,
|
|
315
|
+
playingPlayers: payload.playingPlayers,
|
|
316
|
+
uptime: payload.uptime,
|
|
317
|
+
ping: payload.ping
|
|
318
|
+
});
|
|
296
319
|
|
|
297
320
|
if (payload.memory) {
|
|
298
321
|
Object.assign(this.stats.memory, payload.memory);
|
|
@@ -312,8 +335,9 @@ class Node {
|
|
|
312
335
|
_calculateMemoryPercentages() {
|
|
313
336
|
const { memory } = this.stats;
|
|
314
337
|
if (memory.allocated > 0) {
|
|
315
|
-
|
|
316
|
-
memory.
|
|
338
|
+
const allocated = memory.allocated;
|
|
339
|
+
memory.freePercentage = (memory.free / allocated) * 100;
|
|
340
|
+
memory.usedPercentage = (memory.used / allocated) * 100;
|
|
317
341
|
}
|
|
318
342
|
}
|
|
319
343
|
|
|
@@ -326,7 +350,7 @@ class Node {
|
|
|
326
350
|
|
|
327
351
|
_handleReadyOp(payload) {
|
|
328
352
|
if (!payload.sessionId) {
|
|
329
|
-
this.
|
|
353
|
+
this._emitError("Ready payload missing sessionId");
|
|
330
354
|
return;
|
|
331
355
|
}
|
|
332
356
|
|
|
@@ -341,10 +365,11 @@ class Node {
|
|
|
341
365
|
await this.aqua.loadPlayers();
|
|
342
366
|
this.aqua.emit("debug", this.name, "Session resumed successfully");
|
|
343
367
|
} catch (err) {
|
|
344
|
-
this.
|
|
368
|
+
this._emitError(`Failed to resume session: ${err.message}`);
|
|
345
369
|
}
|
|
346
370
|
}
|
|
347
|
-
|
|
371
|
+
|
|
372
|
+
_emitError(error) {
|
|
348
373
|
const errorObj = error instanceof Error ? error : new Error(error);
|
|
349
374
|
console.error(`[Aqua] [${this.name}] Error:`, errorObj);
|
|
350
375
|
this.aqua.emit("error", this, errorObj);
|