Fix: Voice reconnect Endlosschleife verhindern
- Re-Entranz-Guard (isReconnecting) verhindert parallele Handler - Max 3 Reconnect-Versuche bevor fresh join - Exponentieller Backoff (2s, 4s, 6s) zwischen Retries - Ready-State setzt Counter zurueck Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
4c95cce611
commit
0b849b7775
1 changed files with 62 additions and 3 deletions
|
|
@ -556,8 +556,25 @@ function attachVoiceLifecycle(state: GuildAudioState, guild: any) {
|
|||
// Mehrfach-Registrierung verhindern
|
||||
if ((connection as any).__lifecycleAttached) return;
|
||||
try { (connection as any).setMaxListeners?.(0); } catch {}
|
||||
|
||||
// Retry-Tracking um Endlosschleife zu verhindern
|
||||
let reconnectAttempts = 0;
|
||||
const MAX_RECONNECT_ATTEMPTS = 3;
|
||||
let isReconnecting = false;
|
||||
|
||||
connection.on('stateChange', async (oldS: any, newS: any) => {
|
||||
console.log(`${new Date().toISOString()} | VoiceConnection: ${oldS.status} -> ${newS.status}`);
|
||||
|
||||
// Ready zurückgesetzt -> Retry-Counter reset
|
||||
if (newS.status === VoiceConnectionStatus.Ready) {
|
||||
reconnectAttempts = 0;
|
||||
isReconnecting = false;
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-Entranz verhindern: wenn schon ein Reconnect läuft, nicht nochmal starten
|
||||
if (isReconnecting) return;
|
||||
|
||||
try {
|
||||
if (newS.status === VoiceConnectionStatus.Disconnected) {
|
||||
// Versuche, die Verbindung kurzfristig neu auszuhandeln, sonst rejoin
|
||||
|
|
@ -567,7 +584,25 @@ function attachVoiceLifecycle(state: GuildAudioState, guild: any) {
|
|||
entersState(connection, VoiceConnectionStatus.Connecting, 5_000)
|
||||
]);
|
||||
} catch {
|
||||
connection.rejoin({ channelId: state.channelId, selfDeaf: false, selfMute: false });
|
||||
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
||||
reconnectAttempts++;
|
||||
console.warn(`${new Date().toISOString()} | Disconnected rejoin attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}`);
|
||||
connection.rejoin({ channelId: state.channelId, selfDeaf: false, selfMute: false });
|
||||
} else {
|
||||
console.error(`${new Date().toISOString()} | Max reconnect attempts reached from Disconnected, destroying and rejoining fresh`);
|
||||
reconnectAttempts = 0;
|
||||
try { connection.destroy(); } catch {}
|
||||
const newConn = joinVoiceChannel({
|
||||
channelId: state.channelId,
|
||||
guildId: state.guildId,
|
||||
adapterCreator: guild.voiceAdapterCreator as any,
|
||||
selfMute: false,
|
||||
selfDeaf: false
|
||||
});
|
||||
state.connection = newConn;
|
||||
newConn.subscribe(state.player);
|
||||
attachVoiceLifecycle(state, guild);
|
||||
}
|
||||
}
|
||||
} else if (newS.status === VoiceConnectionStatus.Destroyed) {
|
||||
// Komplett neu beitreten
|
||||
|
|
@ -582,14 +617,38 @@ function attachVoiceLifecycle(state: GuildAudioState, guild: any) {
|
|||
newConn.subscribe(state.player);
|
||||
attachVoiceLifecycle(state, guild);
|
||||
} else if (newS.status === VoiceConnectionStatus.Connecting || newS.status === VoiceConnectionStatus.Signalling) {
|
||||
isReconnecting = true;
|
||||
try {
|
||||
await entersState(connection, VoiceConnectionStatus.Ready, 15_000);
|
||||
// Ready wird oben im Handler behandelt
|
||||
} catch (e) {
|
||||
console.warn(`${new Date().toISOString()} | Voice not ready from ${newS.status}, rejoin`, e);
|
||||
connection.rejoin({ channelId: state.channelId, selfDeaf: false, selfMute: false });
|
||||
reconnectAttempts++;
|
||||
if (reconnectAttempts < MAX_RECONNECT_ATTEMPTS) {
|
||||
const backoffMs = reconnectAttempts * 2_000;
|
||||
console.warn(`${new Date().toISOString()} | Voice not ready from ${newS.status}, attempt ${reconnectAttempts}/${MAX_RECONNECT_ATTEMPTS}, backoff ${backoffMs}ms`);
|
||||
await new Promise(r => setTimeout(r, backoffMs));
|
||||
isReconnecting = false;
|
||||
connection.rejoin({ channelId: state.channelId, selfDeaf: false, selfMute: false });
|
||||
} else {
|
||||
console.error(`${new Date().toISOString()} | Max reconnect attempts reached, destroying and rejoining fresh`);
|
||||
reconnectAttempts = 0;
|
||||
isReconnecting = false;
|
||||
try { connection.destroy(); } catch {}
|
||||
const newConn = joinVoiceChannel({
|
||||
channelId: state.channelId,
|
||||
guildId: state.guildId,
|
||||
adapterCreator: guild.voiceAdapterCreator as any,
|
||||
selfMute: false,
|
||||
selfDeaf: false
|
||||
});
|
||||
state.connection = newConn;
|
||||
newConn.subscribe(state.player);
|
||||
attachVoiceLifecycle(state, guild);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
isReconnecting = false;
|
||||
console.error(`${new Date().toISOString()} | Voice lifecycle handler error`, e);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue