aqualink 3.0.0 → 3.2.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/autoplay.js +67 -66
- package/build/index.d.ts +171 -134
- package/build/index.js +3 -3
- package/build/structures/Aqua.js +76 -44
- package/build/structures/AquaRecovery.js +48 -13
- package/build/structures/AqualinkEvents.js +2 -0
- package/build/structures/Connection.js +21 -2
- package/build/structures/ConnectionRecovery.js +36 -44
- package/build/structures/Filters.js +3 -1
- package/build/structures/Node.js +266 -124
- package/build/structures/Player.js +146 -66
- package/build/structures/PlayerLifecycle.js +24 -6
- package/build/structures/Queue.js +5 -1
- package/build/structures/Reporting.js +12 -0
- package/build/structures/Rest.js +74 -51
- package/build/structures/Track.js +10 -3
- package/package.json +70 -70
package/build/structures/Node.js
CHANGED
|
@@ -5,6 +5,7 @@ const WebSocketImpl = process.isBun ? globalThis.WebSocket : require('ws')
|
|
|
5
5
|
|
|
6
6
|
const Rest = require('./Rest')
|
|
7
7
|
const { AqualinkEvents } = require('./AqualinkEvents')
|
|
8
|
+
const { emitOperationalError } = require('./Reporting')
|
|
8
9
|
|
|
9
10
|
const privateData = new WeakMap()
|
|
10
11
|
|
|
@@ -36,6 +37,8 @@ const unrefTimer = (t) => {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
const _functions = {
|
|
40
|
+
noop() {},
|
|
41
|
+
|
|
39
42
|
buildWsUrl(host, port, ssl) {
|
|
40
43
|
const needsBrackets = host.includes(':') && !host.startsWith('[')
|
|
41
44
|
return `ws${ssl ? 's' : ''}://${needsBrackets ? `[${host}]` : host}:${port}${WS_PATH}`
|
|
@@ -80,6 +83,16 @@ const _functions = {
|
|
|
80
83
|
|
|
81
84
|
errMsg(err) {
|
|
82
85
|
return err?.message || String(err)
|
|
86
|
+
},
|
|
87
|
+
|
|
88
|
+
statusCode(err) {
|
|
89
|
+
return (
|
|
90
|
+
err?.statusCode ||
|
|
91
|
+
err?.status ||
|
|
92
|
+
err?.response?.statusCode ||
|
|
93
|
+
err?.response?.status ||
|
|
94
|
+
null
|
|
95
|
+
)
|
|
83
96
|
}
|
|
84
97
|
}
|
|
85
98
|
|
|
@@ -128,6 +141,7 @@ class Node {
|
|
|
128
141
|
this.reconnectTimeoutId = null
|
|
129
142
|
this.isDestroyed = false
|
|
130
143
|
this._isConnecting = false
|
|
144
|
+
this._connectPromise = null
|
|
131
145
|
this.isNodelink = false
|
|
132
146
|
|
|
133
147
|
this._wsIsBun = !!process.isBun
|
|
@@ -152,7 +166,7 @@ class Node {
|
|
|
152
166
|
error: this._handleError.bind(this),
|
|
153
167
|
message: this._handleMessage.bind(this),
|
|
154
168
|
close: this._handleClose.bind(this),
|
|
155
|
-
connect: this.connect.
|
|
169
|
+
connect: () => this.connect().catch(() => {})
|
|
156
170
|
}
|
|
157
171
|
})
|
|
158
172
|
}
|
|
@@ -205,7 +219,15 @@ class Node {
|
|
|
205
219
|
this.isNodelink = !!this.info?.isNodelink
|
|
206
220
|
} catch (err) {
|
|
207
221
|
this.info = null
|
|
208
|
-
|
|
222
|
+
if (_functions.statusCode(err) === 404) {
|
|
223
|
+
this._emitDebug(
|
|
224
|
+
'Node info endpoint unavailable (HTTP 404); continuing without remote info'
|
|
225
|
+
)
|
|
226
|
+
} else {
|
|
227
|
+
this._emitError(
|
|
228
|
+
`Failed to fetch node info: ${_functions.errMsg(err)}`
|
|
229
|
+
)
|
|
230
|
+
}
|
|
209
231
|
} finally {
|
|
210
232
|
clearTimeout(timeoutId)
|
|
211
233
|
}
|
|
@@ -289,7 +311,7 @@ class Node {
|
|
|
289
311
|
if (
|
|
290
312
|
code !== Node.WS_CLOSE_NORMAL &&
|
|
291
313
|
code !== 1001 &&
|
|
292
|
-
|
|
314
|
+
isFatal &&
|
|
293
315
|
this.sessionId
|
|
294
316
|
) {
|
|
295
317
|
this._clearSession()
|
|
@@ -375,16 +397,17 @@ class Node {
|
|
|
375
397
|
}
|
|
376
398
|
|
|
377
399
|
connect() {
|
|
378
|
-
if (this.isDestroyed
|
|
400
|
+
if (this.isDestroyed) return Promise.reject(new Error('Node is destroyed'))
|
|
401
|
+
if (this._connectPromise) return this._connectPromise
|
|
379
402
|
|
|
380
403
|
const state = this.ws?.readyState
|
|
381
404
|
if (state === WS_STATES.OPEN) {
|
|
382
405
|
this._emitDebug('WebSocket already connected')
|
|
383
|
-
return
|
|
406
|
+
return Promise.resolve()
|
|
384
407
|
}
|
|
385
408
|
if (state === WS_STATES.CONNECTING || state === WS_STATES.CLOSING) {
|
|
386
409
|
this._emitDebug('WebSocket is connecting/closing; skipping new connect')
|
|
387
|
-
return
|
|
410
|
+
return Promise.reject(new Error('WebSocket is closing'))
|
|
388
411
|
}
|
|
389
412
|
|
|
390
413
|
this._isConnecting = true
|
|
@@ -397,95 +420,165 @@ class Node {
|
|
|
397
420
|
})
|
|
398
421
|
}
|
|
399
422
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
+
let connectPromise
|
|
424
|
+
connectPromise = new Promise((resolve, reject) => {
|
|
425
|
+
let settled = false
|
|
426
|
+
let opened = false
|
|
427
|
+
let timer = null
|
|
428
|
+
const settle = (ok, error) => {
|
|
429
|
+
if (settled) return
|
|
430
|
+
settled = true
|
|
431
|
+
if (timer) clearTimeout(timer)
|
|
432
|
+
queueMicrotask(() => {
|
|
433
|
+
if (this._connectPromise === connectPromise)
|
|
434
|
+
this._connectPromise = null
|
|
435
|
+
})
|
|
436
|
+
this._rejectConnect = null
|
|
437
|
+
ok ? resolve() : reject(error)
|
|
438
|
+
}
|
|
439
|
+
timer = setTimeout(() => {
|
|
440
|
+
const error = new Error(`WebSocket open timeout: ${this.timeout}ms`)
|
|
441
|
+
this._isConnecting = false
|
|
442
|
+
this._cleanup()
|
|
443
|
+
settle(false, error)
|
|
444
|
+
this._scheduleReconnect()
|
|
445
|
+
}, this.timeout)
|
|
446
|
+
unrefTimer(timer)
|
|
447
|
+
this._rejectConnect = (error) => settle(false, error)
|
|
448
|
+
|
|
449
|
+
try {
|
|
450
|
+
const h = this._boundHandlers
|
|
451
|
+
const onOpen = () => {
|
|
452
|
+
opened = true
|
|
453
|
+
h.open()
|
|
454
|
+
settle(true)
|
|
455
|
+
}
|
|
456
|
+
const onError = (error) => {
|
|
457
|
+
h.error(error)
|
|
458
|
+
if (!opened) {
|
|
459
|
+
this._isConnecting = false
|
|
460
|
+
this._cleanup()
|
|
461
|
+
settle(false, error)
|
|
462
|
+
this._scheduleReconnect()
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
const onClose = (code, reason) => {
|
|
466
|
+
if (!opened) {
|
|
467
|
+
settle(
|
|
468
|
+
false,
|
|
469
|
+
new Error(
|
|
470
|
+
`WebSocket closed before open (code ${code}): ${_functions.reasonToString(reason)}`
|
|
471
|
+
)
|
|
472
|
+
)
|
|
473
|
+
}
|
|
474
|
+
h.close(code, reason)
|
|
423
475
|
}
|
|
424
476
|
|
|
425
|
-
|
|
477
|
+
if (this._wsIsBun) {
|
|
478
|
+
const ws = new WebSocketImpl(this.wsUrl, { headers: this._headers })
|
|
479
|
+
ws.binaryType = 'arraybuffer'
|
|
480
|
+
|
|
481
|
+
const offs = []
|
|
482
|
+
const add = (type, fn, once = false) => {
|
|
483
|
+
const wrapped = once
|
|
484
|
+
? (ev) => {
|
|
485
|
+
try {
|
|
486
|
+
ws.removeEventListener(type, wrapped)
|
|
487
|
+
} catch {}
|
|
488
|
+
fn(ev)
|
|
489
|
+
}
|
|
490
|
+
: fn
|
|
491
|
+
ws.addEventListener(type, wrapped)
|
|
492
|
+
offs.push(() => {
|
|
493
|
+
try {
|
|
494
|
+
ws.removeEventListener(type, wrapped)
|
|
495
|
+
} catch {}
|
|
496
|
+
})
|
|
497
|
+
}
|
|
426
498
|
|
|
427
|
-
|
|
428
|
-
'error',
|
|
429
|
-
(event) => {
|
|
430
|
-
const err = event?.error
|
|
431
|
-
h.error(err instanceof Error ? err : new Error('WebSocket error'))
|
|
432
|
-
},
|
|
433
|
-
true
|
|
434
|
-
)
|
|
499
|
+
add('open', onOpen, true)
|
|
435
500
|
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
501
|
+
add(
|
|
502
|
+
'error',
|
|
503
|
+
(event) => {
|
|
504
|
+
const err = event?.error
|
|
505
|
+
onError(err instanceof Error ? err : new Error('WebSocket error'))
|
|
506
|
+
},
|
|
507
|
+
false
|
|
508
|
+
)
|
|
441
509
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
h.
|
|
446
|
-
|
|
447
|
-
? event.code
|
|
448
|
-
: Node.WS_CLOSE_NORMAL,
|
|
449
|
-
typeof event?.reason === 'string' ? event.reason : ''
|
|
450
|
-
)
|
|
451
|
-
},
|
|
452
|
-
true
|
|
453
|
-
)
|
|
510
|
+
add('message', (event) => {
|
|
511
|
+
const data = event?.data
|
|
512
|
+
if (typeof data === 'string') h.message(data, false)
|
|
513
|
+
else h.message(data, true)
|
|
514
|
+
})
|
|
454
515
|
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
516
|
+
add(
|
|
517
|
+
'close',
|
|
518
|
+
(event) => {
|
|
519
|
+
onClose(
|
|
520
|
+
typeof event?.code === 'number'
|
|
521
|
+
? event.code
|
|
522
|
+
: Node.WS_CLOSE_NORMAL,
|
|
523
|
+
typeof event?.reason === 'string' ? event.reason : ''
|
|
524
|
+
)
|
|
525
|
+
},
|
|
526
|
+
true
|
|
527
|
+
)
|
|
461
528
|
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
})
|
|
529
|
+
this._bunCleanup = () => {
|
|
530
|
+
for (let i = 0; i < offs.length; i++) offs[i]()
|
|
531
|
+
}
|
|
532
|
+
this.ws = ws
|
|
533
|
+
return
|
|
534
|
+
}
|
|
469
535
|
|
|
470
|
-
|
|
536
|
+
const ws = new WebSocketImpl(this.wsUrl, {
|
|
537
|
+
headers: this._headers,
|
|
538
|
+
perMessageDeflate: false,
|
|
539
|
+
handshakeTimeout: this.timeout,
|
|
540
|
+
maxPayload: this.maxPayload,
|
|
541
|
+
skipUTF8Validation: this.skipUTF8Validation
|
|
542
|
+
})
|
|
471
543
|
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
544
|
+
ws.once('open', () => {
|
|
545
|
+
onOpen()
|
|
546
|
+
this._wsPingInterval = setInterval(() => {
|
|
547
|
+
if (ws.readyState === WS_STATES.OPEN) ws.ping?.()
|
|
548
|
+
}, 30000)
|
|
549
|
+
this._wsPingInterval.unref?.()
|
|
550
|
+
})
|
|
551
|
+
ws.on('error', onError)
|
|
552
|
+
ws.on('message', h.message)
|
|
553
|
+
ws.once('close', (...args) => {
|
|
554
|
+
if (this._wsPingInterval) {
|
|
555
|
+
clearInterval(this._wsPingInterval)
|
|
556
|
+
this._wsPingInterval = null
|
|
557
|
+
}
|
|
558
|
+
onClose(...args)
|
|
559
|
+
})
|
|
476
560
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
561
|
+
this.ws = ws
|
|
562
|
+
} catch (err) {
|
|
563
|
+
this._isConnecting = false
|
|
564
|
+
this._emitError(`Failed to create WebSocket: ${_functions.errMsg(err)}`)
|
|
565
|
+
settle(false, err)
|
|
566
|
+
this._scheduleReconnect()
|
|
567
|
+
}
|
|
568
|
+
})
|
|
569
|
+
this._connectPromise = connectPromise
|
|
570
|
+
return connectPromise
|
|
483
571
|
}
|
|
484
572
|
|
|
485
573
|
_cleanup() {
|
|
486
574
|
const ws = this.ws
|
|
487
575
|
if (!ws) return
|
|
488
576
|
|
|
577
|
+
if (this._wsPingInterval) {
|
|
578
|
+
clearInterval(this._wsPingInterval)
|
|
579
|
+
this._wsPingInterval = null
|
|
580
|
+
}
|
|
581
|
+
|
|
489
582
|
if (this._wsIsBun) {
|
|
490
583
|
try {
|
|
491
584
|
this._bunCleanup?.()
|
|
@@ -493,6 +586,7 @@ class Node {
|
|
|
493
586
|
this._bunCleanup = null
|
|
494
587
|
} else {
|
|
495
588
|
ws.removeAllListeners?.()
|
|
589
|
+
ws.on?.('error', _functions.noop)
|
|
496
590
|
}
|
|
497
591
|
|
|
498
592
|
try {
|
|
@@ -513,15 +607,17 @@ class Node {
|
|
|
513
607
|
if (this.isDestroyed) return
|
|
514
608
|
|
|
515
609
|
this.isDestroyed = true
|
|
610
|
+
this._rejectConnect?.(new Error('Node destroyed while connecting'))
|
|
516
611
|
this.state = NODE_STATE.IDLE
|
|
517
612
|
this._isConnecting = false
|
|
613
|
+
this._connectPromise = null
|
|
518
614
|
this._clearReconnectTimeout()
|
|
519
615
|
this._cleanup()
|
|
520
616
|
|
|
521
617
|
if (!clean) this.aqua.handleNodeFailover?.(this)
|
|
522
618
|
|
|
523
619
|
this.connected = false
|
|
524
|
-
this.aqua.
|
|
620
|
+
this.aqua._destroyNode?.(this.name)
|
|
525
621
|
this.aqua.emit(AqualinkEvents.NodeDestroy, this)
|
|
526
622
|
|
|
527
623
|
this.rest?.destroy?.()
|
|
@@ -592,8 +688,8 @@ class Node {
|
|
|
592
688
|
}
|
|
593
689
|
|
|
594
690
|
const oldSessionId = this.sessionId
|
|
595
|
-
const
|
|
596
|
-
|
|
691
|
+
const sessionInvalidated = !payload.resumed && !!oldSessionId
|
|
692
|
+
const sessionChanged = sessionInvalidated && oldSessionId !== sessionId
|
|
597
693
|
|
|
598
694
|
this.sessionId = sessionId
|
|
599
695
|
if (this.aqua?.debugTrace) {
|
|
@@ -607,9 +703,9 @@ class Node {
|
|
|
607
703
|
this.rest.setSessionId(sessionId)
|
|
608
704
|
this._headers['Session-Id'] = sessionId
|
|
609
705
|
|
|
610
|
-
if (
|
|
706
|
+
if (sessionInvalidated && this.aqua?.players) {
|
|
611
707
|
this._emitDebug(
|
|
612
|
-
`Session
|
|
708
|
+
`Session invalidated (resumed=${!!payload.resumed}, old=${oldSessionId}, new=${sessionId}), invalidating stale players`
|
|
613
709
|
)
|
|
614
710
|
try {
|
|
615
711
|
await this.aqua._storeBrokenPlayers?.(this)
|
|
@@ -618,6 +714,15 @@ class Node {
|
|
|
618
714
|
`Failed to snapshot stale players before invalidation: ${e?.message || e}`
|
|
619
715
|
)
|
|
620
716
|
}
|
|
717
|
+
for (const [, player] of this.aqua.players) {
|
|
718
|
+
if (player?.nodes === this || player?.nodes?.name === this.name) {
|
|
719
|
+
if (player.connection) {
|
|
720
|
+
player.connection._lastEndpoint = null
|
|
721
|
+
player.connection._stateFlags |= 512
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
|
|
621
726
|
const playersToDestroy = []
|
|
622
727
|
for (const [guildId, player] of this.aqua.players) {
|
|
623
728
|
if (player?.nodes === this || player?.nodes?.name === this.name) {
|
|
@@ -650,7 +755,8 @@ class Node {
|
|
|
650
755
|
|
|
651
756
|
this.aqua.emit(AqualinkEvents.NodeReady, this, {
|
|
652
757
|
resumed: !!payload.resumed,
|
|
653
|
-
sessionChanged
|
|
758
|
+
sessionChanged,
|
|
759
|
+
sessionInvalidated
|
|
654
760
|
})
|
|
655
761
|
|
|
656
762
|
if (this.autoResume) {
|
|
@@ -672,63 +778,99 @@ class Node {
|
|
|
672
778
|
})
|
|
673
779
|
}
|
|
674
780
|
|
|
781
|
+
let resumeSupported = true
|
|
675
782
|
try {
|
|
676
783
|
await this.rest.makeRequest('PATCH', `/v4/sessions/${this.sessionId}`, {
|
|
677
784
|
resuming: true,
|
|
678
785
|
timeout: this.resumeTimeout
|
|
679
786
|
})
|
|
787
|
+
} catch (err) {
|
|
788
|
+
if (_functions.statusCode(err) === 404) {
|
|
789
|
+
resumeSupported = false
|
|
790
|
+
this._emitDebug(
|
|
791
|
+
'Session resume endpoint unavailable (HTTP 404); falling back without server-side session resume'
|
|
792
|
+
)
|
|
793
|
+
} else {
|
|
794
|
+
if (this.aqua?.debugTrace) {
|
|
795
|
+
this.aqua._trace('node.resume.error', {
|
|
796
|
+
node: this.name,
|
|
797
|
+
error: _functions.errMsg(err)
|
|
798
|
+
})
|
|
799
|
+
}
|
|
800
|
+
this._emitError(`Failed to resume session: ${_functions.errMsg(err)}`)
|
|
801
|
+
throw err
|
|
802
|
+
}
|
|
803
|
+
}
|
|
680
804
|
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
805
|
+
if (!resumeSupported) {
|
|
806
|
+
try {
|
|
807
|
+
const existingPlayers = await this.rest.getPlayers()
|
|
808
|
+
if (Array.isArray(existingPlayers) && existingPlayers.length === 0) {
|
|
809
|
+
this._emitDebug(
|
|
810
|
+
'No players found on Lavalink for this session; will rejoin voice only'
|
|
811
|
+
)
|
|
812
|
+
}
|
|
813
|
+
} catch (_) {
|
|
814
|
+
// getPlayers may also 404 — that's fine, we already know session is invalid
|
|
815
|
+
}
|
|
816
|
+
}
|
|
817
|
+
|
|
818
|
+
if (this.aqua?.players) {
|
|
819
|
+
const PLAYER_BATCH_SIZE = 20
|
|
820
|
+
const playersToResume = []
|
|
821
|
+
for (const [guildId, player] of this.aqua.players) {
|
|
822
|
+
if (
|
|
823
|
+
(player?.nodes === this || player?.nodes?.name === this.name) &&
|
|
824
|
+
player.voiceChannel &&
|
|
825
|
+
!player.destroyed
|
|
826
|
+
) {
|
|
827
|
+
playersToResume.push({ guildId, player })
|
|
828
|
+
}
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
for (let i = 0; i < playersToResume.length; i += PLAYER_BATCH_SIZE) {
|
|
832
|
+
const batch = playersToResume.slice(i, i + PLAYER_BATCH_SIZE)
|
|
833
|
+
await Promise.allSettled(
|
|
834
|
+
batch.map(async ({ guildId, player }) => {
|
|
835
|
+
try {
|
|
836
|
+
const recoveryToken = player._claimVoiceRecovery?.(
|
|
837
|
+
resumeSupported
|
|
838
|
+
? 'node_resume_rejoin'
|
|
839
|
+
: 'node_rejoin_after_resume_404'
|
|
840
|
+
)
|
|
841
|
+
this._emitDebug(`Rejoining voice for guild ${guildId} on resume`)
|
|
842
|
+
if (this.aqua?.debugTrace) {
|
|
843
|
+
this.aqua._trace('node.resume.rejoin', {
|
|
844
|
+
node: this.name,
|
|
845
|
+
guildId,
|
|
846
|
+
voiceChannel: player.voiceChannel,
|
|
847
|
+
resumeSupported
|
|
848
|
+
})
|
|
849
|
+
}
|
|
850
|
+
if (player._isVoiceRecoveryActive?.(recoveryToken))
|
|
851
|
+
player.connect({
|
|
852
|
+
voiceChannel: player.voiceChannel,
|
|
853
|
+
deaf: player.deaf,
|
|
854
|
+
mute: player.mute
|
|
855
|
+
})
|
|
705
856
|
} catch (e) {
|
|
706
857
|
this._emitDebug(
|
|
707
858
|
`Failed to rejoin voice for ${guildId}: ${e?.message || e}`
|
|
708
859
|
)
|
|
709
860
|
}
|
|
710
|
-
}
|
|
711
|
-
|
|
861
|
+
})
|
|
862
|
+
)
|
|
712
863
|
}
|
|
864
|
+
}
|
|
713
865
|
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
} catch (err) {
|
|
718
|
-
if (this.aqua?.debugTrace) {
|
|
719
|
-
this.aqua._trace('node.resume.error', {
|
|
720
|
-
node: this.name,
|
|
721
|
-
error: _functions.errMsg(err)
|
|
722
|
-
})
|
|
723
|
-
}
|
|
724
|
-
this._emitError(`Failed to resume session: ${_functions.errMsg(err)}`)
|
|
725
|
-
throw err
|
|
866
|
+
if (this.aqua.loadPlayers && this.aqua.players.size === 0) {
|
|
867
|
+
await this.aqua.loadPlayers()
|
|
726
868
|
}
|
|
727
869
|
}
|
|
728
870
|
|
|
729
871
|
_emitError(error) {
|
|
730
872
|
const errorObj = error instanceof Error ? error : new Error(String(error))
|
|
731
|
-
this.aqua
|
|
873
|
+
emitOperationalError(this.aqua, this, errorObj)
|
|
732
874
|
}
|
|
733
875
|
|
|
734
876
|
_emitDebug(message) {
|