From b98d725bc711d53823d5e520dcec112fbd8e7784 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20Br=C3=BCbach?= Date: Mon, 7 Sep 2026 09:00:52 +0200 Subject: [PATCH] [Client] Bound stale-membership room join heal to one attempt The RespContextError heal (leave + rejoin) previously recurred unconditionally, so a server that kept returning RespContextError for a reason other than stale membership would loop forever. Track room ids that already received a heal and surface the error dialog after one attempt instead of retrying indefinitely. --- .../src/interface/widgets/tabs/tab_server.cpp | 17 ++++++++++++++++- .../src/interface/widgets/tabs/tab_server.h | 3 +++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/cockatrice/src/interface/widgets/tabs/tab_server.cpp b/cockatrice/src/interface/widgets/tabs/tab_server.cpp index 670727f07..2f29e8f35 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_server.cpp +++ b/cockatrice/src/interface/widgets/tabs/tab_server.cpp @@ -229,6 +229,7 @@ void TabServer::joinRoomFinished(const Response &r, switch (r.response_code()) { case Response::RespOk: + healedRoomJoins.remove(roomId); break; case Response::RespNameNotFound: if (setCurrent) { @@ -238,10 +239,24 @@ void TabServer::joinRoomFinished(const Response &r, emit roomJoinFailed(roomId); return; case Response::RespContextError: + if (healedRoomJoins.contains(roomId)) { + // A stale-membership heal was already attempted once; if the server still + // rejects the join there is nothing left to do client-side, so surface it. + if (setCurrent) { + QMessageBox::critical( + this, tr("Error"), + tr("The server thinks you are in the server room but your client is unable to display it. " + "Try restarting your client.")); + } + emit roomJoinFailed(roomId); + return; + } // The server already had us registered in the room even though no tab was open, // usually because two join attempts for the same room overlapped. Leaving and // rejoining makes the server reply with a fresh RespOk so the tab is displayed - // without requiring a client restart. + // without requiring a client restart. This is attempted only once: if the server + // keeps replying with RespContextError we must not loop forever. + healedRoomJoins.insert(roomId); leaveAndRejoinRoom(roomId, setCurrent); return; case Response::RespUserLevelTooLow: diff --git a/cockatrice/src/interface/widgets/tabs/tab_server.h b/cockatrice/src/interface/widgets/tabs/tab_server.h index 595334af0..ab4423130 100644 --- a/cockatrice/src/interface/widgets/tabs/tab_server.h +++ b/cockatrice/src/interface/widgets/tabs/tab_server.h @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -67,6 +68,8 @@ private: bool shouldEmitUpdate = false; /** Room ids with a join command in flight, mapped to whether the tab should be focused once it opens. */ QMap pendingRoomJoins; + /** Room ids for which a stale-membership heal (leave + rejoin) has already been attempted. */ + QSet healedRoomJoins; public: TabServer(TabSupervisor *_tabSupervisor, AbstractClient *_client);