From 0986cfd7a008eb78cacdb01c1544408f4e26f1fa Mon Sep 17 00:00:00 2001 From: igor725 Date: Tue, 7 Jul 2026 18:07:48 +0300 Subject: [PATCH 1/3] Fix shard reconnection issue --- include/dpp/discordclient.h | 4 +--- src/dpp/cluster.cpp | 40 +++++++++++++++++-------------------- src/dpp/discordclient.cpp | 6 +++--- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/include/dpp/discordclient.h b/include/dpp/discordclient.h index fbe97c41c1..ad8f31c650 100644 --- a/include/dpp/discordclient.h +++ b/include/dpp/discordclient.h @@ -558,10 +558,8 @@ class DPP_EXPORT discord_client : public websocket_client * other object, along with the seq number. * * @param old Previous connection to resume from - * @param sequence Sequence number of previous session - * @param session_id Session ID of previous session */ - explicit discord_client(discord_client& old, uint64_t sequence, const std::string& session_id); + explicit discord_client(discord_client& old); /** * @brief Destroy the discord client object diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index 33360a8279..4b416a49aa 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -231,46 +231,42 @@ void cluster::start(start_type return_after) { if (now >= shard_reconnect_time) { /* This shard needs to be reconnected */ reconnections.erase(reconnect); - discord_client* old = nullptr; + discord_client * cl_old = nullptr; + discord_client * cl_new = nullptr; { std::shared_lock lk(shards_mutex); - old = shards[shard_id]; + cl_old = shards[shard_id]; } - /* These values must be copied to the new connection - * to attempt to resume it - */ - auto seq_no = old->last_seq; - auto session_id = old->sessionid; log(ll_info, "Reconnecting shard " + std::to_string(shard_id)); /* Make a new resumed connection based off the old one */ try { - std::unique_lock lk(shards_mutex); - if (shards[shard_id] != nullptr) { + if (cl_old != nullptr) { log(ll_trace, "Attempting resume..."); - shards[shard_id] = nullptr; - shards[shard_id] = new discord_client(*old, seq_no, session_id); + cl_new = new discord_client(*cl_old); } else { log(ll_trace, "Attempting full reconnection..."); - shards[shard_id] = nullptr; - shards[shard_id] = new discord_client(this, shard_id, numshards, token, intents, compressed, ws_mode); + cl_new = new discord_client(this, shard_id, numshards, token, intents, compressed, ws_mode); } - /* Delete the old one */ - log(ll_trace, "Attempting to delete old connection..."); - delete old; - old = nullptr; /* Set up the new shard's IO events */ log(ll_trace, "Running new connection..."); - shards[shard_id]->run(); + cl_new->run(); } catch (const std::exception& e) { std::unique_lock lk(shards_mutex); log(ll_info, "Exception when reconnecting shard " + std::to_string(shard_id) + ": " + std::string(e.what())); - delete shards[shard_id]; - delete old; - old = nullptr; - shards[shard_id] = nullptr; add_reconnect(shard_id); } + /* Delete the old one */ + if (cl_old != nullptr) { + log(ll_trace, "Deleting old connection..."); + std::unique_lock lk(shards_mutex); + shards[shard_id] = nullptr; + delete cl_old; + cl_old = nullptr; + } + log(ll_trace, "Installing new connection..."); + std::unique_lock lk(shards_mutex); + shards[shard_id] = cl_new; /* It is not possible to reconnect another shard within the same 5-second window, * due to discords strict rate limiting on shard connections, so we bail out here * and only try another reconnect in the next timer interval. Do not try and make diff --git a/src/dpp/discordclient.cpp b/src/dpp/discordclient.cpp index ece9a01eae..0059966fab 100644 --- a/src/dpp/discordclient.cpp +++ b/src/dpp/discordclient.cpp @@ -51,7 +51,7 @@ constexpr int LARGE_THRESHOLD = 250; /** * @brief Resume constructor for websocket client */ -discord_client::discord_client(discord_client &old, uint64_t sequence, const std::string& session_id) +discord_client::discord_client(discord_client &old) : websocket_client(old.owner, old.resume_gateway_url, "443", old.compressed ? (old.protocol == ws_json ? PATH_COMPRESSED_JSON : PATH_COMPRESSED_ETF) : (old.protocol == ws_json ? PATH_UNCOMPRESSED_JSON : PATH_UNCOMPRESSED_ETF)), compressed(old.compressed), zlib(nullptr), @@ -63,10 +63,10 @@ discord_client::discord_client(discord_client &old, uint64_t sequence, const std last_heartbeat(time(nullptr)), shard_id(old.shard_id), max_shards(old.max_shards), - last_seq(sequence), + last_seq(old.last_seq), token(old.token), intents(old.intents), - sessionid(session_id), + sessionid(old.sessionid), resumes(old.resumes), reconnects(old.reconnects), websocket_ping(old.websocket_ping), From d258f5355beed4b98741842f81d372d90e5b4ee1 Mon Sep 17 00:00:00 2001 From: igor725 Date: Sun, 12 Jul 2026 14:46:08 +0300 Subject: [PATCH 2/3] Add missed logic --- src/dpp/cluster.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index cb25da6234..2db51b9334 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -256,12 +256,13 @@ void cluster::start(start_type return_after) { std::unique_lock lk(shards_mutex); log(ll_info, "Exception when reconnecting shard " + std::to_string(shard_id) + ": " + std::string(e.what())); add_reconnect(shard_id); + shards[shard_id] = std::move(cl_old); + return; } /* Delete the old one */ if (cl_old != nullptr) { log(ll_trace, "Deleting old connection..."); std::unique_lock lk(shards_mutex); - shards[shard_id] = nullptr; cl_old = nullptr; } log(ll_trace, "Installing new connection..."); From 9949adfc83178c3c15b889098668b89e16d1378a Mon Sep 17 00:00:00 2001 From: igor725 Date: Tue, 21 Jul 2026 11:21:41 +0300 Subject: [PATCH 3/3] Restore resume logic --- src/dpp/cluster.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/dpp/cluster.cpp b/src/dpp/cluster.cpp index 2db51b9334..4576d1df17 100644 --- a/src/dpp/cluster.cpp +++ b/src/dpp/cluster.cpp @@ -240,10 +240,12 @@ void cluster::start(start_type return_after) { } log(ll_info, "Reconnecting shard " + std::to_string(shard_id)); /* Make a new resumed connection based off the old one */ + bool resume = false; try { if (cl_old != nullptr) { log(ll_trace, "Attempting resume..."); cl_new = std::make_unique(*cl_old); + resume = true; } else { log(ll_trace, "Attempting full reconnection..."); cl_new = std::make_unique(this, shard_id, numshards, token, intents, compressed, ws_mode); @@ -268,6 +270,14 @@ void cluster::start(start_type return_after) { log(ll_trace, "Installing new connection..."); std::unique_lock lk(shards_mutex); shards[shard_id] = std::move(cl_new); + if (resume) { + /* Start connecting only after the old client is destroyed */ + shards[shard_id]->start_connecting(); + std::unique_lock lock(shards[shard_id]->voice_mutex); + for (auto &c : shards[shard_id]->connecting_voice_channels) { + c.second->connect(); + } + } /* It is not possible to reconnect another shard within the same 5-second window, * due to discords strict rate limiting on shard connections, so we bail out here * and only try another reconnect in the next timer interval. Do not try and make