From aa08b44343c5d455b015f2e5ac4cd9ea7d0e348e Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:06:10 +0100 Subject: [PATCH 1/9] new changes for gxs & gxstunnel --- RetroChessPlugin.cpp | 6 ++ RetroChessPlugin.h | 4 ++ gui/RetroChessChatWidgetHolder.cpp | 40 ++++++----- gui/chess.cpp | 87 +++++++++++++++++++++-- gui/chess.h | 4 ++ gui/tile.cpp | 6 +- interface/rsRetroChess.h | 9 +++ services/p3RetroChess.cc | 107 ++++++++++++++++++++++++++++- services/p3RetroChess.h | 36 +++++++++- services/rsRetroChessItems.h | 1 + 10 files changed, 271 insertions(+), 29 deletions(-) diff --git a/RetroChessPlugin.cpp b/RetroChessPlugin.cpp index 5bd9144..a2f3765 100644 --- a/RetroChessPlugin.cpp +++ b/RetroChessPlugin.cpp @@ -32,6 +32,7 @@ #include "gui/RetroChessChatWidgetHolder.h" #include +#include #include #include @@ -90,6 +91,7 @@ RetroChessPlugin::RetroChessPlugin() void RetroChessPlugin::setInterfaces(RsPlugInInterfaces &interfaces) { mPeers = interfaces.mPeers; + mGxsTunnels = interfaces.mGxsTunnels; } /*ConfigPage *RetroChessPlugin::qt_config_page() const @@ -145,6 +147,10 @@ p3Service *RetroChessPlugin::p3_service() const void RetroChessPlugin::setPlugInHandler(RsPluginHandler *pgHandler) { mPlugInHandler = pgHandler; + + if (mGxsTunnels) { + mGxsTunnels->registerClientService(RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, mGxsTunnelClient); + } } QIcon *RetroChessPlugin::qt_icon() const diff --git a/RetroChessPlugin.h b/RetroChessPlugin.h index c2e6fee..ed05ad7 100644 --- a/RetroChessPlugin.h +++ b/RetroChessPlugin.h @@ -26,6 +26,7 @@ /*libretroshare"*/ #include +#include #include "gui/NEMainpage.h" @@ -76,6 +77,9 @@ class RetroChessPlugin: public RsPlugin mutable QIcon *mIcon; mutable MainPage* mainpage ; + RsGxsTunnelService *mGxsTunnels; + RsGxsTunnelClientService *mGxsTunnelClient; + RetroChessNotify *mRetroChessNotify ; RetroChessGUIHandler *mRetroChessGUIHandler ; }; diff --git a/gui/RetroChessChatWidgetHolder.cpp b/gui/RetroChessChatWidgetHolder.cpp index 29e7b8c..2cc1166 100644 --- a/gui/RetroChessChatWidgetHolder.cpp +++ b/gui/RetroChessChatWidgetHolder.cpp @@ -30,6 +30,7 @@ #include "RetroChessChatWidgetHolder.h" +#include #include #include @@ -41,7 +42,6 @@ RetroChessChatWidgetHolder::RetroChessChatWidgetHolder(ChatWidget *chatWidget, R QIcon icon ; icon.addPixmap(QPixmap(IMAGE_RetroChess)) ; - playChessButton = new QToolButton ; playChessButton->setIcon(icon) ; playChessButton->setToolTip(tr("Invite Friend to Chess")); @@ -111,29 +111,37 @@ void RetroChessChatWidgetHolder::chessnotify(RsPeerId from_peer_id) void RetroChessChatWidgetHolder::chessPressed() { - RsPeerId peer_id = mChatWidget->getChatId().toPeerId();//TODO support GXSID - if (rsRetroChess->hasInviteFrom(peer_id)) - { + ChatId chatId = mChatWidget->getChatId(); + if (chatId.isDistantChatId()) { + rsRetroChess->sendGxsInvite(chatId.toGxsId()); + } else { + RsPeerId peer_id = chatId.toPeerId(); + + if (rsRetroChess->hasInviteFrom(peer_id)){ + rsRetroChess->acceptedInvite(peer_id); + mRetroChessNotify->notifyChessStart(peer_id); + return; + } - rsRetroChess->acceptedInvite(peer_id); - mRetroChessNotify->notifyChessStart(peer_id); - return; + rsRetroChess->sendInvite(peer_id); - } - rsRetroChess->sendInvite(peer_id); - - QString peerName = QString::fromUtf8(rsPeers->getPeerName(peer_id).c_str()); - mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime() + QString peerName = QString::fromUtf8(rsPeers->getPeerName(peer_id).c_str()); + mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime() , tr("You're now inviting %1 to play Chess").arg(peerName), ChatWidget::MSGTYPE_SYSTEM); + } } void RetroChessChatWidgetHolder::chessStart() { - RsPeerId peer_id = mChatWidget->getChatId().toPeerId();//TODO support GXSID - - rsRetroChess->acceptedInvite(peer_id); - mRetroChessNotify->notifyChessStart(peer_id); + ChatId chatId = mChatWidget->getChatId(); + if (chatId.isDistantChatId()) { + rsRetroChess->acceptedInviteGxs(chatId.toDistantChatId()); + } else { + RsPeerId peer_id = chatId.toPeerId(); + rsRetroChess->acceptedInvite(peer_id); + mRetroChessNotify->notifyChessStart(peer_id); + } return; } diff --git a/gui/chess.cpp b/gui/chess.cpp index 68277e0..5ab2312 100644 --- a/gui/chess.cpp +++ b/gui/chess.cpp @@ -26,10 +26,69 @@ #include "gui/common/AvatarDefs.h" #include "../services/p3RetroChess.h" +// NEW: Constructor for Distant GXS Identity +RetroChessWindow::RetroChessWindow(const RsGxsId &gxsId, int player, QWidget *parent) : + QWidget(parent), + m_ui(new Ui::RetroChessWindow), + mGxsId(gxsId), + mIsGxs(true) +{ + m_ui->setupUi(this); + mPeerId = gxsId.toStdString(); // Use string representation for internal tracking + + m_ui->m_player1_result->hide(); + m_ui->m_player2_result->hide(); + m_ui->m_status_bar->hide(); + + m_flag_finished = 0; // set as unfinish + + //tile = { { NULL } }; + count=0; + turn=1; // white first + max=0; + texp = new int[60]; + + setGeometry(0,0,1370,700); + + // Resolve our own primary GXS ID + std::list ownIds; + rsIdentity->getOwnIds(ownIds); + RsGxsId myGxsId = ownIds.empty() ? RsGxsId() : ownIds.front(); + + if (player) { // local player as black + // Note: For GXS we track identities rather than PeerIds + player_str = " (1)"; + m_localplayer_turn = 0; + + RsIdentityDetails d1, d2; + rsIdentity->getIdDetails(myGxsId, d1); + rsIdentity->getIdDetails(gxsId, d2); + p1name = d1.mNickname; + p2name = d2.mNickname; + } else { // local player as white + player_str = " (2)"; + m_localplayer_turn = 1; + + RsIdentityDetails d1, d2; + rsIdentity->getIdDetails(gxsId, d1); + rsIdentity->getIdDetails(myGxsId, d2); + p1name = d1.mNickname; + p2name = d2.mNickname; + } + + QString title = QString::fromUtf8(p2name.c_str()) + " Playing Chess against " + QString::fromUtf8(p1name.c_str()) + player_str; + + setWindowTitle(title); + initAccessories(); + playerTurnNotice(); + initChessBoard(); +} + RetroChessWindow::RetroChessWindow(std::string peerid, int player, QWidget *parent) : QWidget(parent), m_ui( new Ui::RetroChessWindow() ), - mPeerId(peerid) + mPeerId(peerid), + mIsGxs(false) //ui(new Ui::RetroChessWindow) { m_ui->setupUi( this ); @@ -125,7 +184,11 @@ void RetroChessWindow::initAccessories() void RetroChessWindow::closeEvent(QCloseEvent *event) { // send leave message - rsRetroChess->player_leave(this->mPeerId); + if (mIsGxs) { + rsRetroChess->player_leave_gxs(this->mGxsId); + } else { + rsRetroChess->player_leave(mPeerId); + } QWidget::closeEvent(event); } @@ -998,10 +1061,22 @@ int RetroChessWindow::resultJudge() void RetroChessWindow::showPlayerLeaveMsg() { - std::string player_name = rsPeers->getPeerName( RsPeerId(mPeerId )); - QString status_msg(player_name.c_str()); - status_msg += " has left"; - m_ui->m_status_bar->setText( status_msg ); + QString name; + if (mIsGxs) { + // Resolve GXS nickname + RsIdentityDetails details; + if (rsIdentity->getIdDetails(mGxsId, details)) { + name = QString::fromUtf8(details.mNickname.c_str()); + } else { + name = tr("Distant Friend"); + } + } else { + // Resolve Peer name + name = QString::fromStdString(rsPeers->getPeerName(RsPeerId(mPeerId))); + } + + QString status_msg = name + tr(" has left"); + m_ui->m_status_bar->setText(status_msg); m_ui->m_status_bar->setVisible(true); } diff --git a/gui/chess.h b/gui/chess.h index 6e30272..5576813 100644 --- a/gui/chess.h +++ b/gui/chess.h @@ -27,6 +27,7 @@ #include #include "retroshare/rspeers.h" +#include "retroshare/rsidentity.h" #include @@ -55,9 +56,12 @@ class RetroChessWindow : public QWidget public: std::string mPeerId; explicit RetroChessWindow(std::string peerid, int player = 0, QWidget *parent = 0); + explicit RetroChessWindow(const RsGxsId &gxsId, int player = 0, QWidget *parent = 0); ~RetroChessWindow(); int currentplayer; int myid; + RsGxsId mGxsId; // Store GXS identity if using a tunnel + bool mIsGxs; //from global diff --git a/gui/tile.cpp b/gui/tile.cpp index 681af84..dd0bd16 100644 --- a/gui/tile.cpp +++ b/gui/tile.cpp @@ -48,7 +48,11 @@ void Tile::mousePressEvent(QMouseEvent *event) if((chess_window_p)->m_localplayer_turn == (chess_window_p)->turn) { validate( ++(chess_window_p)->count ); - rsRetroChess->chess_click(peer_id, this->row,this->col,(chess_window_p)->count); + if ((chess_window_p)->mIsGxs) { + rsRetroChess->chess_click_gxs((chess_window_p)->mGxsId, this->row,this->col, (chess_window_p)->count); + } else { + rsRetroChess->chess_click((chess_window_p)->mPeerId, this->row,this->col, (chess_window_p)->count); + } } // not local player's turn } diff --git a/interface/rsRetroChess.h b/interface/rsRetroChess.h index 3e715d7..10ac9df 100644 --- a/interface/rsRetroChess.h +++ b/interface/rsRetroChess.h @@ -53,6 +53,15 @@ class RsRetroChess virtual void acceptedInvite(RsPeerId peerID) = 0; virtual void gotInvite(RsPeerId peerID) = 0; virtual void sendInvite(RsPeerId peerID) = 0; + + // New GXSID & Tunneling methods + virtual void chess_click_gxs(const RsGxsId &gxs_id, int col, int row, int count) = 0; + virtual void player_leave_gxs(const RsGxsId &gxs_id) = 0; + virtual void requestGxsTunnel(const RsGxsId &gxsId) = 0; + virtual void sendGxsInvite(const RsGxsId &gxsId) = 0; + virtual void addChessFriend(const RsGxsId &gxsId) = 0; + virtual void acceptedInviteGxs(const RsGxsId &gxsId) = 0; + }; diff --git a/services/p3RetroChess.cc b/services/p3RetroChess.cc index 912ec51..385468f 100644 --- a/services/p3RetroChess.cc +++ b/services/p3RetroChess.cc @@ -25,6 +25,7 @@ #include "pqi/p3linkmgr.h" #include #include +#include "retroshare/rsmsgs.h" #include // for std::istringstream @@ -111,10 +112,11 @@ int p3RetroChess::tick() #ifdef DEBUG_RetroChess std::cerr << "ticking p3RetroChess" << std::endl; #endif + // Call your GXS polling logic + handleGxsTick(); - //processIncoming(); - //sendPackets(); - + // Call the base class tick if necessary, or return 0 + // Returning 0 tells the core this service is idle for this slice return 0; } @@ -417,3 +419,102 @@ RsSerialiser *p3RetroChess::setupSerialiser() return rsSerialiser ; } + +void p3RetroChess::chess_click_gxs(const RsGxsId &gxs_id, int col, int row, int count) +{ + if (mActiveTunnels.find(gxs_id) == mActiveTunnels.end()) { + // Tunnel not ready, try to re-open + sendGxsInvite(gxs_id); + return; + } + + RsGxsTunnelId tunnel_id = mActiveTunnels[gxs_id]; + + // Create a data item for the move + RsRetroChessDataItem *item = new RsRetroChessDataItem(); + item->m_msg = QString("%1,%2,%3").arg(col).arg(row).arg(count).toStdString(); + + // Send raw data through the secured tunnel + mGxsTunnel->sendData(tunnel_id, item); +} + +void p3RetroChess::requestGxsTunnel(const RsGxsId &gxsId) +{ + // Implementation: This triggers the async tunnel request + sendGxsInvite(gxsId); +} + +void p3RetroChess::sendGxsInvite(const RsGxsId &to_gxs_id) +{ + RsGxsId from_gxs_id; + std::list ownIds; + rsIdentity->getOwnIds(ownIds); + if (ownIds.empty()) return; + from_gxs_id = ownIds.front(); + + RsGxsTunnelId tunnel_id; + uint32_t error_code; + + // Open a tunnel using mGxsTunnel (Async Request) + if (mGxsTunnel->requestSecuredTunnel( + to_gxs_id, from_gxs_id, tunnel_id, + RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, error_code)) + { + mPendingTunnels[to_gxs_id] = tunnel_id; + std::cout << "Chess Tunnel requested. Pending ID: " << tunnel_id << std::endl; + } +} + +void p3RetroChess::handleGxsTick() +{ + // Periodically check status of pending tunnels + auto it = mPendingTunnels.begin(); + while (it != mPendingTunnels.end()) { + RsGxsTunnelInfo tinfo; + if (mGxsTunnel->getTunnelInfo(it->second, tinfo)) { + if (tinfo.status == RS_GXS_TUNNEL_STATUS_CONNECTED) { + // 1.c - Established! Move to active and notify UI + mActiveTunnels[it->first] = it->second; + mRetroChessNotify->notifyGxsTunnelReady(it->first); + it = mPendingTunnels.erase(it); + continue; + } else if (tinfo.status == RS_GXS_TUNNEL_STATUS_CLOSED || tinfo.status == RS_GXS_TUNNEL_STATUS_FAILED) { + it = mPendingTunnels.erase(it); + continue; + } + } + ++it; + } +} + +// Update this signature to include gxs_id and am_I_client_side +void p3RetroChess::handleRawData(const RsGxsId& gxs_id, + const RsGxsTunnelId& tunnel_id, + bool am_I_client_side, + const uint8_t *data, + uint32_t data_size) +{ + // Use the de-serialization constructor + uint32_t temp_size = data_size; + RsRetroChessDataItem item((void*)data, temp_size); + + QString qMsg = QString::fromStdString(item.m_msg); + QStringList parts = qMsg.split(","); + + if (parts.size() == 3) { + int col = parts[0].toInt(); + int row = parts[1].toInt(); + int count = parts[2].toInt(); + + // Use the gxs_id provided by the tunnel to notify the UI + mRetroChessNotify->notifyChessMoveGxs(gxs_id, col, row, count); + } +} + +void p3RetroChess::player_leave_gxs(const RsGxsId &gxs_id) { + // Logic to close tunnel + if(mActiveTunnels.count(gxs_id)) { + mGxsTunnel->closeExistingTunnel(mActiveTunnels[gxs_id], RETRO_CHESS_GXS_TUNNEL_SERVICE_ID); + mActiveTunnels.erase(gxs_id); + } +} \ No newline at end of file diff --git a/services/p3RetroChess.h b/services/p3RetroChess.h index 2ff1387..3174e82 100644 --- a/services/p3RetroChess.h +++ b/services/p3RetroChess.h @@ -30,12 +30,16 @@ #include "serialiser/rstlvbase.h" #include "rsitems/rsconfigitems.h" #include "plugins/rspqiservice.h" +#include "retroshare/rsidentity.h" +#include + #include class p3LinkMgr; class RetroChessNotify ; - +// Use a valid 16-bit Service ID (below 0xFFFF) +#define RETRO_CHESS_GXS_TUNNEL_SERVICE_ID 0xC4E5 //!The RS VoIP Test service. /** @@ -61,7 +65,7 @@ class p3RetroChess: public RsPQIService, public RsRetroChess * : notifyCustomState, notifyChatStatus, notifyPeerHasNewAvatar * @see NotifyBase */ - virtual int tick(); + virtual int tick() override;; virtual int status(); virtual bool recvItem(RsItem *item); @@ -101,13 +105,39 @@ class p3RetroChess: public RsPQIService, public RsRetroChess void gotInvite(RsPeerId peerID); void acceptedInvite(RsPeerId peerID); void sendInvite(RsPeerId peerID); -private: + void player_leave_gxs(const RsGxsId &gxs_id); + void addChessFriend(const RsGxsId &gxsId); + void sendGxsInvite(const RsGxsId &toGxsId); + void acceptedInviteGxs(const RsGxsId &gxsId); + void chess_click_gxs(const RsGxsId &gxs_id, int col, int row, int count); + virtual void requestGxsTunnel(const RsGxsId &gxsId) override; + + // Async tunnel management + void handleGxsTick(); // Called periodically by the core + + virtual uint32_t getGxsTunnelServiceId() const override { + return RETRO_CHESS_GXS_TUNNEL_SERVICE_ID; + } + + // Fix handleRawData signature + void handleRawData(const RsGxsId& gxs_id, const RsGxsTunnelId& tunnel_id, bool am_I_client_side, const uint8_t *data, uint32_t data_size); + +private: + // Helper to find which friend sent the data based on the tunnel ID + RsGxsId findGxsIdByTunnel(const RsGxsTunnelId& tunnel_id); std::set invitesTo; std::set invitesFrom; + void handleData(RsRetroChessDataItem*) ; + // Tracks GXS IDs that we are currently trying to connect to + std::map mPendingTunnels; + // Tracks established tunnels ready for data + std::map mActiveTunnels; + + RsGxsTunnelService *mGxsTunnels; RsMutex mRetroChessMtx; //RsPeerId mPeerID; diff --git a/services/rsRetroChessItems.h b/services/rsRetroChessItems.h index 1e4a563..6166419 100644 --- a/services/rsRetroChessItems.h +++ b/services/rsRetroChessItems.h @@ -98,6 +98,7 @@ class RsRetroChessDataItem: public RsRetroChessItem uint32_t flags ; uint32_t data_size ; std::string m_msg; + RsGxsId m_gxsId; // Optional: track origin GXS ID in the item }; From 064508acc5e74b3d5d1abc0c026c073757d49e1c Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Sun, 11 Jan 2026 00:48:10 +0100 Subject: [PATCH 2/9] update gxs notify & tunnel check --- RetroChessPlugin.cpp | 1 + gui/RetroChessNotify.cpp | 10 ++++++++++ gui/RetroChessNotify.h | 3 +++ 3 files changed, 14 insertions(+) diff --git a/RetroChessPlugin.cpp b/RetroChessPlugin.cpp index a2f3765..6f17215 100644 --- a/RetroChessPlugin.cpp +++ b/RetroChessPlugin.cpp @@ -130,6 +130,7 @@ ChatWidgetHolder *RetroChessPlugin::qt_get_chat_widget_holder(ChatWidget *chatWi case ChatWidget::CHATTYPE_UNKNOWN: case ChatWidget::CHATTYPE_LOBBY: case ChatWidget::CHATTYPE_DISTANT: + return new RetroChessChatWidgetHolder(chatWidget, mRetroChessNotify); break; } diff --git a/gui/RetroChessNotify.cpp b/gui/RetroChessNotify.cpp index 8b2fb5a..74ed28d 100644 --- a/gui/RetroChessNotify.cpp +++ b/gui/RetroChessNotify.cpp @@ -51,3 +51,13 @@ void RetroChessNotify::notifyChessInvite(const RsPeerId &peer_id) emit chessInvited(peer_id) ; } + +void RetroChessNotify::notifyChessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count) +{ + emit chessMoveGxs(gxs_id, col, row, count); +} + +void RetroChessNotify::notifyGxsTunnelReady(const RsGxsId &gxs_id) +{ + emit gxsTunnelReady(gxs_id); +} diff --git a/gui/RetroChessNotify.h b/gui/RetroChessNotify.h index 4fdd0fa..5bb0d5a 100644 --- a/gui/RetroChessNotify.h +++ b/gui/RetroChessNotify.h @@ -47,6 +47,9 @@ class RetroChessNotify : public QObject void chessStart(const RsPeerId &peer_id) ; void chessInvited(const RsPeerId &peer_id) ; + void chessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); + void gxsTunnelReady(const RsGxsId &gxs_id); + public slots: }; From be315434879a8eef8967c6ffadd4fc5b7b917453 Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Sun, 11 Jan 2026 00:49:08 +0100 Subject: [PATCH 3/9] changes for handlerawdata --- services/p3RetroChess.cc | 39 ++++++++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 5 deletions(-) diff --git a/services/p3RetroChess.cc b/services/p3RetroChess.cc index 385468f..956128c 100644 --- a/services/p3RetroChess.cc +++ b/services/p3RetroChess.cc @@ -440,8 +440,13 @@ void p3RetroChess::chess_click_gxs(const RsGxsId &gxs_id, int col, int row, int void p3RetroChess::requestGxsTunnel(const RsGxsId &gxsId) { - // Implementation: This triggers the async tunnel request - sendGxsInvite(gxsId); + // Check if we already have a tunnel + if (mActiveTunnels.count(gxsId)) { + mRetroChessNotify->notifyGxsTunnelReady(gxsId); + return; + } + // Otherwise, start the async tunnel request + this->sendGxsInvite(gxsId); } void p3RetroChess::sendGxsInvite(const RsGxsId &to_gxs_id) @@ -494,10 +499,25 @@ void p3RetroChess::handleRawData(const RsGxsId& gxs_id, const uint8_t *data, uint32_t data_size) { - // Use the de-serialization constructor + // Identify who sent the data using the Tunnel ID helper + // This ensures we have a record of this tunnel in our mActiveTunnels map + RsGxsId sender_id = findGxsIdByTunnel(tunnel_id); + + // Fallback: If the map lookup fails but the API provided a valid gxs_id, use that + if (sender_id.isNull() && !gxs_id.isNull()) { + sender_id = gxs_id; + } + + if (sender_id.isNull()) { + std::cerr << "p3RetroChess::handleRawData: Received data from unknown tunnel " << tunnel_id << std::endl; + return; + } + + // Deserialize the raw bytes into the Chess Data Item uint32_t temp_size = data_size; RsRetroChessDataItem item((void*)data, temp_size); + // Parse the move protocol (Format: "col,row,count") QString qMsg = QString::fromStdString(item.m_msg); QStringList parts = qMsg.split(","); @@ -506,8 +526,8 @@ void p3RetroChess::handleRawData(const RsGxsId& gxs_id, int row = parts[1].toInt(); int count = parts[2].toInt(); - // Use the gxs_id provided by the tunnel to notify the UI - mRetroChessNotify->notifyChessMoveGxs(gxs_id, col, row, count); + // Notify the GUI with the resolved GXS Identity + mRetroChessNotify->notifyChessMoveGxs(sender_id, col, row, count); } } @@ -517,4 +537,13 @@ void p3RetroChess::player_leave_gxs(const RsGxsId &gxs_id) { mGxsTunnel->closeExistingTunnel(mActiveTunnels[gxs_id], RETRO_CHESS_GXS_TUNNEL_SERVICE_ID); mActiveTunnels.erase(gxs_id); } +} + +RsGxsId p3RetroChess::findGxsIdByTunnel(const RsGxsTunnelId& tunnel_id) +{ + std::map::iterator it; + for (it = mActiveTunnels.begin(); it != mActiveTunnels.end(); ++it) { + if (it->second == tunnel_id) return it->first; + } + return RsGxsId(); } \ No newline at end of file From a1a2e2a6cd0e837e612d44edf32a69a2be80c342 Mon Sep 17 00:00:00 2001 From: jolavillette Date: Sun, 11 Jan 2026 09:29:11 +0100 Subject: [PATCH 4/9] fix compilation of gxschess-v2 branch --- RetroChessPlugin.cpp | 22 +++++++----- RetroChessPlugin.h | 3 +- gui/RetroChessChatWidgetHolder.cpp | 4 +-- gui/RetroChessNotify.h | 5 +++ gui/chess.cpp | 7 ++++ services/p3RetroChess.cc | 58 +++++++++++++++++++----------- services/p3RetroChess.h | 9 +++-- services/rsRetroChessItems.h | 2 +- 8 files changed, 74 insertions(+), 36 deletions(-) diff --git a/RetroChessPlugin.cpp b/RetroChessPlugin.cpp index 6f17215..967ba79 100644 --- a/RetroChessPlugin.cpp +++ b/RetroChessPlugin.cpp @@ -139,19 +139,23 @@ ChatWidgetHolder *RetroChessPlugin::qt_get_chat_widget_holder(ChatWidget *chatWi p3Service *RetroChessPlugin::p3_service() const { - if(mRetroChess == NULL) - rsRetroChess = mRetroChess = new p3RetroChess(mPlugInHandler,mRetroChessNotify) ; // , 3600 * 24 * 30 * 6); // 6 Months - - return mRetroChess ; + if(mRetroChess == NULL) + { + // Create the service + rsRetroChess = mRetroChess = new p3RetroChess(mPlugInHandler, mRetroChessNotify); + + // Register it for GXS Tunnels immediately if the interface is available + if (mGxsTunnels) { + mGxsTunnels->registerClientService(RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, mRetroChess); + } + } + return mRetroChess; } void RetroChessPlugin::setPlugInHandler(RsPluginHandler *pgHandler) { - mPlugInHandler = pgHandler; - - if (mGxsTunnels) { - mGxsTunnels->registerClientService(RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, mGxsTunnelClient); - } + mPlugInHandler = pgHandler; + // No need to register here if done in p3_service } QIcon *RetroChessPlugin::qt_icon() const diff --git a/RetroChessPlugin.h b/RetroChessPlugin.h index ed05ad7..0b0c0a1 100644 --- a/RetroChessPlugin.h +++ b/RetroChessPlugin.h @@ -78,8 +78,7 @@ class RetroChessPlugin: public RsPlugin mutable MainPage* mainpage ; RsGxsTunnelService *mGxsTunnels; - RsGxsTunnelClientService *mGxsTunnelClient; - + RsGxsTunnelService::RsGxsTunnelClientService *mGxsTunnelClient; RetroChessNotify *mRetroChessNotify ; RetroChessGUIHandler *mRetroChessGUIHandler ; }; diff --git a/gui/RetroChessChatWidgetHolder.cpp b/gui/RetroChessChatWidgetHolder.cpp index 2cc1166..cad590c 100644 --- a/gui/RetroChessChatWidgetHolder.cpp +++ b/gui/RetroChessChatWidgetHolder.cpp @@ -113,7 +113,7 @@ void RetroChessChatWidgetHolder::chessPressed() { ChatId chatId = mChatWidget->getChatId(); if (chatId.isDistantChatId()) { - rsRetroChess->sendGxsInvite(chatId.toGxsId()); + rsRetroChess->sendGxsInvite(RsGxsId(chatId.toDistantChatId().toStdString())); } else { RsPeerId peer_id = chatId.toPeerId(); @@ -136,7 +136,7 @@ void RetroChessChatWidgetHolder::chessStart() { ChatId chatId = mChatWidget->getChatId(); if (chatId.isDistantChatId()) { - rsRetroChess->acceptedInviteGxs(chatId.toDistantChatId()); + rsRetroChess->acceptedInviteGxs(RsGxsId(chatId.toDistantChatId().toStdString())); } else { RsPeerId peer_id = chatId.toPeerId(); rsRetroChess->acceptedInvite(peer_id); diff --git a/gui/RetroChessNotify.h b/gui/RetroChessNotify.h index 5bb0d5a..e2f70a8 100644 --- a/gui/RetroChessNotify.h +++ b/gui/RetroChessNotify.h @@ -40,6 +40,11 @@ class RetroChessNotify : public QObject void notifyReceivedMsg(const RsPeerId &peer_id, QString str) ; void notifyChessStart(const RsPeerId &peer_id) ; void notifyChessInvite(const RsPeerId &peer_id) ; + /** Notify the UI of a chess move received via a GXS tunnel */ + void notifyChessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); + + /** Notify the UI that a GXS tunnel is now ready for use */ + void notifyGxsTunnelReady(const RsGxsId &gxs_id); signals: void NeMsgArrived(const RsPeerId &peer_id, QString str) ; // emitted when the peer gets a msg diff --git a/gui/chess.cpp b/gui/chess.cpp index 5ab2312..e606cba 100644 --- a/gui/chess.cpp +++ b/gui/chess.cpp @@ -33,6 +33,13 @@ RetroChessWindow::RetroChessWindow(const RsGxsId &gxsId, int player, QWidget *pa mGxsId(gxsId), mIsGxs(true) { + QString player_str; + if (player == 1) { + player_str = " (1)"; + } else if (player == 2) { + player_str = " (2)"; + } + m_ui->setupUi(this); mPeerId = gxsId.toStdString(); // Use string representation for internal tracking diff --git a/services/p3RetroChess.cc b/services/p3RetroChess.cc index 956128c..e042378 100644 --- a/services/p3RetroChess.cc +++ b/services/p3RetroChess.cc @@ -25,7 +25,7 @@ #include "pqi/p3linkmgr.h" #include #include -#include "retroshare/rsmsgs.h" +//#include "retroshare/rsmsgs.h" #include // for std::istringstream @@ -247,8 +247,6 @@ void p3RetroChess::msg_all(std::string msg) // mServiceControl->getPeersConnected(getServiceInfo().mServiceType, onlineIds); rsPeers->getOnlineList(onlineIds); - double ts = getCurrentTS(); - #ifdef DEBUG_RetroChess std::cerr << "p3RetroChess::msg_all() @ts: " << ts; std::cerr << std::endl; @@ -274,9 +272,6 @@ void p3RetroChess::broadcast_paint(int x, int y) // mServiceControl->getPeersConnected(getServiceInfo().mServiceType, onlineIds); rsPeers->getOnlineList(onlineIds); - double ts = getCurrentTS(); - - std::cout << "READY TO PAINT: " << onlineIds.size() << "\n"; /* prepare packets */ std::list::iterator it; @@ -435,14 +430,14 @@ void p3RetroChess::chess_click_gxs(const RsGxsId &gxs_id, int col, int row, int item->m_msg = QString("%1,%2,%3").arg(col).arg(row).arg(count).toStdString(); // Send raw data through the secured tunnel - mGxsTunnel->sendData(tunnel_id, item); + mGxsTunnels->sendData(tunnel_id, RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, (const uint8_t*)item->m_msg.c_str(), item->m_msg.size()); } void p3RetroChess::requestGxsTunnel(const RsGxsId &gxsId) { // Check if we already have a tunnel if (mActiveTunnels.count(gxsId)) { - mRetroChessNotify->notifyGxsTunnelReady(gxsId); + mNotify->notifyGxsTunnelReady(gxsId); return; } // Otherwise, start the async tunnel request @@ -461,7 +456,7 @@ void p3RetroChess::sendGxsInvite(const RsGxsId &to_gxs_id) uint32_t error_code; // Open a tunnel using mGxsTunnel (Async Request) - if (mGxsTunnel->requestSecuredTunnel( + if (mGxsTunnels->requestSecuredTunnel( to_gxs_id, from_gxs_id, tunnel_id, RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, error_code)) { @@ -472,23 +467,25 @@ void p3RetroChess::sendGxsInvite(const RsGxsId &to_gxs_id) void p3RetroChess::handleGxsTick() { - // Periodically check status of pending tunnels auto it = mPendingTunnels.begin(); while (it != mPendingTunnels.end()) { - RsGxsTunnelInfo tinfo; - if (mGxsTunnel->getTunnelInfo(it->second, tinfo)) { - if (tinfo.status == RS_GXS_TUNNEL_STATUS_CONNECTED) { - // 1.c - Established! Move to active and notify UI + RsGxsTunnelService::GxsTunnelInfo tinfo; + if (mGxsTunnels->getTunnelInfo(it->second, tinfo)) { + // Check if the tunnel is "Connected" (CAN_TALK) + if (tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_CAN_TALK) { mActiveTunnels[it->first] = it->second; - mRetroChessNotify->notifyGxsTunnelReady(it->first); + mNotify->notifyGxsTunnelReady(it->first); it = mPendingTunnels.erase(it); continue; - } else if (tinfo.status == RS_GXS_TUNNEL_STATUS_CLOSED || tinfo.status == RS_GXS_TUNNEL_STATUS_FAILED) { + } + // Check for "Closed/Failed" status + else if (tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED || + tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_TUNNEL_DN) { it = mPendingTunnels.erase(it); continue; } } - ++it; + ++it; } } @@ -527,14 +524,14 @@ void p3RetroChess::handleRawData(const RsGxsId& gxs_id, int count = parts[2].toInt(); // Notify the GUI with the resolved GXS Identity - mRetroChessNotify->notifyChessMoveGxs(sender_id, col, row, count); + mNotify->notifyChessMoveGxs(sender_id, col, row, count); } } void p3RetroChess::player_leave_gxs(const RsGxsId &gxs_id) { // Logic to close tunnel if(mActiveTunnels.count(gxs_id)) { - mGxsTunnel->closeExistingTunnel(mActiveTunnels[gxs_id], RETRO_CHESS_GXS_TUNNEL_SERVICE_ID); + mGxsTunnels->closeExistingTunnel(mActiveTunnels[gxs_id], RETRO_CHESS_GXS_TUNNEL_SERVICE_ID); mActiveTunnels.erase(gxs_id); } } @@ -546,4 +543,25 @@ RsGxsId p3RetroChess::findGxsIdByTunnel(const RsGxsTunnelId& tunnel_id) if (it->second == tunnel_id) return it->first; } return RsGxsId(); -} \ No newline at end of file +} + +// services/p3RetroChess.cc + +void p3RetroChess::notifyTunnelStatus(const RsGxsTunnelId& /*tunnel_id*/, uint32_t /*tunnel_status*/) +{ +} + +void p3RetroChess::receiveData(const RsGxsTunnelId& id, unsigned char *data, uint32_t data_size) +{ + this->handleRawData(RsGxsId(), id, false, (const uint8_t*)data, data_size); +} + +void p3RetroChess::connectToGxsTunnelService(RsGxsTunnelService *tunnel_service) +{ + mGxsTunnels = tunnel_service; +} + +bool p3RetroChess::acceptDataFromPeer(const RsGxsId& /*gxs_id*/, const RsGxsTunnelId& /*tunnel_id*/, bool /*am_I_client_side*/) +{ + return true; +} diff --git a/services/p3RetroChess.h b/services/p3RetroChess.h index 3174e82..56dd940 100644 --- a/services/p3RetroChess.h +++ b/services/p3RetroChess.h @@ -47,7 +47,7 @@ class RetroChessNotify ; * This is only used to test Latency for the moment. */ -class p3RetroChess: public RsPQIService, public RsRetroChess +class p3RetroChess: public RsPQIService, public RsRetroChess, public RsGxsTunnelService::RsGxsTunnelClientService // Maybe we inherit from these later - but not needed for now. //, public p3Config, public pqiMonitor { @@ -116,13 +116,18 @@ class p3RetroChess: public RsPQIService, public RsRetroChess // Async tunnel management void handleGxsTick(); // Called periodically by the core - virtual uint32_t getGxsTunnelServiceId() const override { + virtual uint32_t getGxsTunnelServiceId() const { return RETRO_CHESS_GXS_TUNNEL_SERVICE_ID; } // Fix handleRawData signature void handleRawData(const RsGxsId& gxs_id, const RsGxsTunnelId& tunnel_id, bool am_I_client_side, const uint8_t *data, uint32_t data_size); + virtual void notifyTunnelStatus(const RsGxsTunnelId& tunnel_id, uint32_t tunnel_status) override; + virtual void receiveData(const RsGxsTunnelId& id, unsigned char *data, uint32_t data_size) override; + virtual void connectToGxsTunnelService(RsGxsTunnelService *tunnel_service) override; + virtual bool acceptDataFromPeer(const RsGxsId& gxs_id, const RsGxsTunnelId& tunnel_id, bool am_I_client_side) override; + private: // Helper to find which friend sent the data based on the tunnel ID RsGxsId findGxsIdByTunnel(const RsGxsTunnelId& tunnel_id); diff --git a/services/rsRetroChessItems.h b/services/rsRetroChessItems.h index 6166419..623e992 100644 --- a/services/rsRetroChessItems.h +++ b/services/rsRetroChessItems.h @@ -55,7 +55,7 @@ /**************************************************************************/ -const uint16_t RS_SERVICE_TYPE_RetroChess_PLUGIN = 0xc4e55; +const uint16_t RS_SERVICE_TYPE_RetroChess_PLUGIN = 0xc4e5; const uint8_t RS_PKT_SUBTYPE_RetroChess_DATA = 0x01; From da76f46e3046ce2f47fb3c22aa50c853515a4349 Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Sun, 11 Jan 2026 11:01:02 +0100 Subject: [PATCH 5/9] Fixed compile --- interface/rsRetroChess.h | 2 +- services/p3RetroChess.cc | 13 +++++++++++++ services/p3RetroChess.h | 2 +- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/interface/rsRetroChess.h b/interface/rsRetroChess.h index 10ac9df..861029c 100644 --- a/interface/rsRetroChess.h +++ b/interface/rsRetroChess.h @@ -59,7 +59,7 @@ class RsRetroChess virtual void player_leave_gxs(const RsGxsId &gxs_id) = 0; virtual void requestGxsTunnel(const RsGxsId &gxsId) = 0; virtual void sendGxsInvite(const RsGxsId &gxsId) = 0; - virtual void addChessFriend(const RsGxsId &gxsId) = 0; + //virtual void addChessFriend(const RsGxsId &gxsId) = 0; virtual void acceptedInviteGxs(const RsGxsId &gxsId) = 0; }; diff --git a/services/p3RetroChess.cc b/services/p3RetroChess.cc index e042378..ecf021e 100644 --- a/services/p3RetroChess.cc +++ b/services/p3RetroChess.cc @@ -465,6 +465,19 @@ void p3RetroChess::sendGxsInvite(const RsGxsId &to_gxs_id) } } +void p3RetroChess::acceptedInviteGxs(const RsGxsId &gxsId) +{ + std::cout << "p3RetroChess: Accepted GXS invite from " << gxsId << std::endl; + + // If we don't have an active tunnel yet, request one now + if (mActiveTunnels.find(gxsId) == mActiveTunnels.end()) { + requestGxsTunnel(gxsId); + } else { + // If tunnel is already ready, tell the GUI to start the game + mNotify->notifyGxsTunnelReady(gxsId); + } +} + void p3RetroChess::handleGxsTick() { auto it = mPendingTunnels.begin(); diff --git a/services/p3RetroChess.h b/services/p3RetroChess.h index 56dd940..58fd7a0 100644 --- a/services/p3RetroChess.h +++ b/services/p3RetroChess.h @@ -107,7 +107,7 @@ class p3RetroChess: public RsPQIService, public RsRetroChess, public RsGxsTunnel void sendInvite(RsPeerId peerID); void player_leave_gxs(const RsGxsId &gxs_id); - void addChessFriend(const RsGxsId &gxsId); + //void addChessFriend(const RsGxsId &gxsId); void sendGxsInvite(const RsGxsId &toGxsId); void acceptedInviteGxs(const RsGxsId &gxsId); void chess_click_gxs(const RsGxsId &gxs_id, int col, int row, int count); From 14c042594cc5f78b2d832571a169902c58b2cd63 Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Sun, 11 Jan 2026 14:58:57 +0100 Subject: [PATCH 6/9] Added some notifys --- gui/NEMainpage.cpp | 8 +++ gui/NEMainpage.h | 1 + gui/RetroChessChatWidgetHolder.cpp | 93 +++++++++++++++++++++++++++++- gui/RetroChessChatWidgetHolder.h | 2 + gui/RetroChessNotify.cpp | 5 ++ gui/RetroChessNotify.h | 4 ++ 6 files changed, 110 insertions(+), 3 deletions(-) diff --git a/gui/NEMainpage.cpp b/gui/NEMainpage.cpp index 51f8a38..a0386dc 100644 --- a/gui/NEMainpage.cpp +++ b/gui/NEMainpage.cpp @@ -50,6 +50,7 @@ NEMainpage::NEMainpage(QWidget *parent, RetroChessNotify *notify) : connect(mNotify, SIGNAL(NeMsgArrived(RsPeerId,QString)), this, SLOT(NeMsgArrived(RsPeerId,QString))); connect(mNotify, SIGNAL(chessStart(RsPeerId)), this, SLOT(chessStart(RsPeerId))); + connect(mNotify, SIGNAL(chessStartGxs(RsGxsId)), this, SLOT(showChessWindowGxs(RsGxsId))); connect(ui->friendSelectionWidget, SIGNAL(itemSelectionChanged()), this, SLOT(friendSelectionChanged())); // enable/disable the invite button @@ -178,6 +179,13 @@ void NEMainpage::create_chess_window(std::string peer_id, int player_id) ui->active_games->addItem(QString::fromStdString(peer_id)); } +void NEMainpage::showChessWindowGxs(const RsGxsId &gxs_id) +{ + // Open the window with the GXS constructor + RetroChessWindow *win = new RetroChessWindow(gxs_id, 0); + win->show(); +} + // enable the invite button when selected a friend void NEMainpage::enable_inviteButton() { diff --git a/gui/NEMainpage.h b/gui/NEMainpage.h index cf6d72f..8dce420 100644 --- a/gui/NEMainpage.h +++ b/gui/NEMainpage.h @@ -58,6 +58,7 @@ private slots: void friendSelectionChanged(); void NeMsgArrived(const RsPeerId &peer_id, QString str); void chessStart(const RsPeerId &peer_id); + void showChessWindowGxs(const RsGxsId &gxs_id); void on_broadcastButton_clicked(); diff --git a/gui/RetroChessChatWidgetHolder.cpp b/gui/RetroChessChatWidgetHolder.cpp index cad590c..970a210 100644 --- a/gui/RetroChessChatWidgetHolder.cpp +++ b/gui/RetroChessChatWidgetHolder.cpp @@ -51,6 +51,10 @@ RetroChessChatWidgetHolder::RetroChessChatWidgetHolder(ChatWidget *chatWidget, R mChatWidget->addChatBarWidget(playChessButton); connect(playChessButton, SIGNAL(clicked()), this, SLOT(chessPressed())); connect(notify, SIGNAL(chessInvited(RsPeerId)), this, SLOT(chessnotify(RsPeerId))); + connect(notify, SIGNAL(chessInvitedGxs(RsGxsId)), this, SLOT(chessnotifyGxs(RsGxsId))); + // When the p3RetroChess service detects the tunnel is CONNECTED, + // it calls notifyGxsTunnelReady, which emits this signal: + connect(notify, SIGNAL(gxsTunnelReady(RsGxsId)), this, SLOT(handleGxsTunnelReady(RsGxsId))); } @@ -109,11 +113,74 @@ void RetroChessChatWidgetHolder::chessnotify(RsPeerId from_peer_id) } } +void RetroChessChatWidgetHolder::chessnotifyGxs(const RsGxsId &from_gxs_id) +{ + ChatId chatId = mChatWidget->getChatId(); + + // Check if the current chat window matches the GXS ID of the person who invited us + if (!chatId.isDistantChatId()) { + return; + } + + // You need a way to check if an invite exists. + // If you haven't implemented hasInviteFromGxs, you can use the active tunnel status. + if (true) // In GXS, the receipt of the signal itself implies an invite + { + if (mChatWidget) + { + // Get the name from the GXS Identity system + std::string identityName; + RsGxsId nameId = from_gxs_id; + // Attempt to get a readable name for the GXS ID + // If rsIdentity doesn't have a direct getName, we use a placeholder or the ID string + QString buttonName = QString::fromStdString(from_gxs_id.toStdString()).left(8); + + // Clear old buttons for this chat to avoid duplicates + button_map::iterator it = buttonMapTakeChess.begin(); + while (it != buttonMapTakeChess.end()) + { + it = buttonMapTakeChess.erase(it); + } + + // Add the system message and the "Accept" button to the chat history + mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime(), + tr("%1 is inviting you to start Chess via GXS. Accept?").arg(buttonName), + ChatWidget::MSGTYPE_SYSTEM); + + RSButtonOnText *button = mChatWidget->getNewButtonOnTextBrowser(tr("Accept GXS Invite")); + button->setToolTip(tr("Accept Chess Invitation")); + + // Apply your specific green styling + button->setStyleSheet(QString("border: 1px solid #199909;") + .append("font-size: 12pt; color: white;") + .append("min-width: 128px; min-height: 24px;") + .append("border-radius: 6px;") + .append("background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.67, " + "stop: 0 #22c70d, stop: 1 #116a06);")); + + button->updateImage(); + + // Connect to the logic that starts the game/tunnel + connect(button, SIGNAL(clicked()), this, SLOT(chessStart())); + connect(button, SIGNAL(mouseEnter()), this, SLOT(botMouseEnter())); + connect(button, SIGNAL(mouseLeave()), this, SLOT(botMouseLeave())); + + buttonMapTakeChess.insert(buttonName, button); + } + } +} + void RetroChessChatWidgetHolder::chessPressed() { ChatId chatId = mChatWidget->getChatId(); + QString peerName; if (chatId.isDistantChatId()) { rsRetroChess->sendGxsInvite(RsGxsId(chatId.toDistantChatId().toStdString())); + + mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), + QDateTime::currentDateTime(), + tr("Requesting secure game tunnel..."), + ChatWidget::MSGTYPE_SYSTEM); } else { RsPeerId peer_id = chatId.toPeerId(); @@ -125,10 +192,10 @@ void RetroChessChatWidgetHolder::chessPressed() rsRetroChess->sendInvite(peer_id); - QString peerName = QString::fromUtf8(rsPeers->getPeerName(peer_id).c_str()); - mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime() - , tr("You're now inviting %1 to play Chess").arg(peerName), ChatWidget::MSGTYPE_SYSTEM); + peerName = QString::fromUtf8(rsPeers->getPeerName(peer_id).c_str()); } + mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime() + , tr("You're now inviting %1 to play Chess").arg(peerName), ChatWidget::MSGTYPE_SYSTEM); } @@ -137,6 +204,15 @@ void RetroChessChatWidgetHolder::chessStart() ChatId chatId = mChatWidget->getChatId(); if (chatId.isDistantChatId()) { rsRetroChess->acceptedInviteGxs(RsGxsId(chatId.toDistantChatId().toStdString())); + + // We wait for the service to signal handleGxsTunnelReady. + mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), + QDateTime::currentDateTime(), + tr("Establishing secure GXS tunnel..."), + ChatWidget::MSGTYPE_SYSTEM); + + // Notify the UI to open the Chess window for this GXS ID + mRetroChessNotify->notifyChessStartGxs(RsGxsId(chatId.toDistantChatId().toStdString())); } else { RsPeerId peer_id = chatId.toPeerId(); rsRetroChess->acceptedInvite(peer_id); @@ -145,6 +221,17 @@ void RetroChessChatWidgetHolder::chessStart() return; } +void RetroChessChatWidgetHolder::handleGxsTunnelReady(const RsGxsId &gxs_id) +{ + ChatId chatId = mChatWidget->getChatId(); + if (chatId.isDistantChatId()) { + // Now the tunnel is safe to use. Open the window. + mRetroChessNotify->notifyChessStartGxs(gxs_id); + + if (playChessButton) playChessButton->hide(); + } +} + void RetroChessChatWidgetHolder::botMouseEnter() { RSButtonOnText *source = qobject_cast(QObject::sender()); diff --git a/gui/RetroChessChatWidgetHolder.h b/gui/RetroChessChatWidgetHolder.h index 958ba88..0909c46 100644 --- a/gui/RetroChessChatWidgetHolder.h +++ b/gui/RetroChessChatWidgetHolder.h @@ -39,6 +39,8 @@ public slots: void chessStart(); void chessnotify(RsPeerId from_peer_id); + void chessnotifyGxs(const RsGxsId &from_gxs_id); + void handleGxsTunnelReady(const RsGxsId &gxs_id); private slots: void botMouseEnter(); diff --git a/gui/RetroChessNotify.cpp b/gui/RetroChessNotify.cpp index 74ed28d..0770945 100644 --- a/gui/RetroChessNotify.cpp +++ b/gui/RetroChessNotify.cpp @@ -61,3 +61,8 @@ void RetroChessNotify::notifyGxsTunnelReady(const RsGxsId &gxs_id) { emit gxsTunnelReady(gxs_id); } + +void RetroChessNotify::notifyChessStartGxs(const RsGxsId &gxs_id) +{ + emit chessStartGxs(gxs_id); +} diff --git a/gui/RetroChessNotify.h b/gui/RetroChessNotify.h index e2f70a8..bf32d18 100644 --- a/gui/RetroChessNotify.h +++ b/gui/RetroChessNotify.h @@ -46,6 +46,8 @@ class RetroChessNotify : public QObject /** Notify the UI that a GXS tunnel is now ready for use */ void notifyGxsTunnelReady(const RsGxsId &gxs_id); + void notifyChessStartGxs(const RsGxsId &gxs_id); + signals: void NeMsgArrived(const RsPeerId &peer_id, QString str) ; // emitted when the peer gets a msg @@ -55,6 +57,8 @@ class RetroChessNotify : public QObject void chessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); void gxsTunnelReady(const RsGxsId &gxs_id); + void chessStartGxs(const RsGxsId &gxs_id); + public slots: }; From d8333397bb376bf4baa11cef17628ddd4dd58e72 Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Sun, 26 Apr 2026 17:09:05 +0200 Subject: [PATCH 7/9] trying fix issues --- gui/NEMainpage.cpp | 28 +++++++++++++++-- gui/NEMainpage.h | 4 ++- gui/RetroChessChatWidgetHolder.cpp | 44 +++++++++++++++++++------- gui/chess.cpp | 50 +++++++++++++++++++++--------- 4 files changed, 96 insertions(+), 30 deletions(-) diff --git a/gui/NEMainpage.cpp b/gui/NEMainpage.cpp index a0386dc..15d0157 100644 --- a/gui/NEMainpage.cpp +++ b/gui/NEMainpage.cpp @@ -50,7 +50,8 @@ NEMainpage::NEMainpage(QWidget *parent, RetroChessNotify *notify) : connect(mNotify, SIGNAL(NeMsgArrived(RsPeerId,QString)), this, SLOT(NeMsgArrived(RsPeerId,QString))); connect(mNotify, SIGNAL(chessStart(RsPeerId)), this, SLOT(chessStart(RsPeerId))); - connect(mNotify, SIGNAL(chessStartGxs(RsGxsId)), this, SLOT(showChessWindowGxs(RsGxsId))); + connect(mNotify, SIGNAL(chessStartGxs(RsGxsId)), this, SLOT(chessStartGxs(RsGxsId))); + connect(mNotify, SIGNAL(chessMoveGxs(RsGxsId,int,int,int)), this, SLOT(chessMoveGxs(RsGxsId,int,int,int))); connect(ui->friendSelectionWidget, SIGNAL(itemSelectionChanged()), this, SLOT(friendSelectionChanged())); // enable/disable the invite button @@ -79,6 +80,22 @@ void NEMainpage::chessStart(const RsPeerId &peer_id) create_chess_window(peer_id.toStdString(), 0); } +void NEMainpage::chessStartGxs(const RsGxsId &gxs_id) +{ + create_chess_window_gxs(gxs_id, 0); +} + +void NEMainpage::chessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count) +{ + std::string key = gxs_id.toStdString(); + if (activeGames.find(key) != activeGames.end()) { + RetroChessWindow* rcw = activeGames.value(key); + rcw->validate_tile(row, col, count); + } else { + std::cerr << "RetroChess: Received GXS move but no active game for " << key << std::endl; + } +} + // decode received message here void NEMainpage::NeMsgArrived(const RsPeerId &peer_id, QString str) { @@ -179,11 +196,16 @@ void NEMainpage::create_chess_window(std::string peer_id, int player_id) ui->active_games->addItem(QString::fromStdString(peer_id)); } -void NEMainpage::showChessWindowGxs(const RsGxsId &gxs_id) +void NEMainpage::create_chess_window_gxs(const RsGxsId &gxs_id, int player_id) { // Open the window with the GXS constructor - RetroChessWindow *win = new RetroChessWindow(gxs_id, 0); + RetroChessWindow *win = new RetroChessWindow(gxs_id, player_id); win->show(); + + // Track the game so GXS moves can be routed to it + std::string key = gxs_id.toStdString(); + activeGames.insert(key, win); + ui->active_games->addItem(QString::fromStdString(key)); } // enable the invite button when selected a friend diff --git a/gui/NEMainpage.h b/gui/NEMainpage.h index 8dce420..1a3dbe9 100644 --- a/gui/NEMainpage.h +++ b/gui/NEMainpage.h @@ -58,7 +58,8 @@ private slots: void friendSelectionChanged(); void NeMsgArrived(const RsPeerId &peer_id, QString str); void chessStart(const RsPeerId &peer_id); - void showChessWindowGxs(const RsGxsId &gxs_id); + void chessStartGxs(const RsGxsId &gxs_id); + void chessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); void on_broadcastButton_clicked(); @@ -79,6 +80,7 @@ private slots: QMap activeGames; void create_chess_window(std::string peer_id, int player_id); + void create_chess_window_gxs(const RsGxsId &gxs_id, int player_id); }; #endif // NEMAINPAGE_H diff --git a/gui/RetroChessChatWidgetHolder.cpp b/gui/RetroChessChatWidgetHolder.cpp index 970a210..ccf95c0 100644 --- a/gui/RetroChessChatWidgetHolder.cpp +++ b/gui/RetroChessChatWidgetHolder.cpp @@ -27,6 +27,7 @@ #include "interface/rsRetroChess.h" #include "gui/chat/ChatWidget.h" +#include #include "RetroChessChatWidgetHolder.h" @@ -175,12 +176,22 @@ void RetroChessChatWidgetHolder::chessPressed() ChatId chatId = mChatWidget->getChatId(); QString peerName; if (chatId.isDistantChatId()) { - rsRetroChess->sendGxsInvite(RsGxsId(chatId.toDistantChatId().toStdString())); + // Resolve the real remote GXS ID from the distant chat tunnel + DistantChatPeerInfo dcpinfo; + if (!rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo)) { + std::cerr << "RetroChess: Failed to resolve distant chat status" << std::endl; + return; + } + RsGxsId remoteGxsId = dcpinfo.to_id; + + rsRetroChess->sendGxsInvite(remoteGxsId); + peerName = QString::fromStdString(remoteGxsId.toStdString()).left(8); mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), QDateTime::currentDateTime(), tr("Requesting secure game tunnel..."), ChatWidget::MSGTYPE_SYSTEM); + return; } else { RsPeerId peer_id = chatId.toPeerId(); @@ -203,16 +214,22 @@ void RetroChessChatWidgetHolder::chessStart() { ChatId chatId = mChatWidget->getChatId(); if (chatId.isDistantChatId()) { - rsRetroChess->acceptedInviteGxs(RsGxsId(chatId.toDistantChatId().toStdString())); + // Resolve the real remote GXS ID from the distant chat tunnel + DistantChatPeerInfo dcpinfo; + if (!rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo)) { + std::cerr << "RetroChess: Failed to resolve distant chat status" << std::endl; + return; + } + RsGxsId remoteGxsId = dcpinfo.to_id; - // We wait for the service to signal handleGxsTunnelReady. - mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), + rsRetroChess->acceptedInviteGxs(remoteGxsId); + + // Do NOT open the chess window here — wait for handleGxsTunnelReady() + // to fire when the tunnel is actually established. + mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), QDateTime::currentDateTime(), tr("Establishing secure GXS tunnel..."), ChatWidget::MSGTYPE_SYSTEM); - - // Notify the UI to open the Chess window for this GXS ID - mRetroChessNotify->notifyChessStartGxs(RsGxsId(chatId.toDistantChatId().toStdString())); } else { RsPeerId peer_id = chatId.toPeerId(); rsRetroChess->acceptedInvite(peer_id); @@ -225,10 +242,15 @@ void RetroChessChatWidgetHolder::handleGxsTunnelReady(const RsGxsId &gxs_id) { ChatId chatId = mChatWidget->getChatId(); if (chatId.isDistantChatId()) { - // Now the tunnel is safe to use. Open the window. - mRetroChessNotify->notifyChessStartGxs(gxs_id); - - if (playChessButton) playChessButton->hide(); + // Verify this tunnel is for the current chat + DistantChatPeerInfo dcpinfo; + if (rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo)) { + if (dcpinfo.to_id == gxs_id) { + // Now the tunnel is safe to use. Open the window. + mRetroChessNotify->notifyChessStartGxs(gxs_id); + if (playChessButton) playChessButton->hide(); + } + } } } diff --git a/gui/chess.cpp b/gui/chess.cpp index e606cba..87be0c5 100644 --- a/gui/chess.cpp +++ b/gui/chess.cpp @@ -67,20 +67,33 @@ RetroChessWindow::RetroChessWindow(const RsGxsId &gxsId, int player, QWidget *pa player_str = " (1)"; m_localplayer_turn = 0; + // Use non-blocking lookup with fallback for unknown identities RsIdentityDetails d1, d2; - rsIdentity->getIdDetails(myGxsId, d1); - rsIdentity->getIdDetails(gxsId, d2); - p1name = d1.mNickname; - p2name = d2.mNickname; + if (rsIdentity->getIdDetails(myGxsId, d1)) { + p1name = d1.mNickname; + } else { + p1name = myGxsId.toStdString().substr(0, 8) + "..."; + } + if (rsIdentity->getIdDetails(gxsId, d2)) { + p2name = d2.mNickname; + } else { + p2name = gxsId.toStdString().substr(0, 8) + "..."; + } } else { // local player as white player_str = " (2)"; m_localplayer_turn = 1; RsIdentityDetails d1, d2; - rsIdentity->getIdDetails(gxsId, d1); - rsIdentity->getIdDetails(myGxsId, d2); - p1name = d1.mNickname; - p2name = d2.mNickname; + if (rsIdentity->getIdDetails(gxsId, d1)) { + p1name = d1.mNickname; + } else { + p1name = gxsId.toStdString().substr(0, 8) + "..."; + } + if (rsIdentity->getIdDetails(myGxsId, d2)) { + p2name = d2.mNickname; + } else { + p2name = myGxsId.toStdString().substr(0, 8) + "..."; + } } QString title = QString::fromUtf8(p2name.c_str()) + " Playing Chess against " + QString::fromUtf8(p1name.c_str()) + player_str; @@ -177,13 +190,20 @@ void RetroChessWindow::initAccessories() m_ui->m_player1_name->setText( p1name.c_str() ); m_ui->m_player2_name->setText( p2name.c_str() ); - QPixmap p1avatar; - AvatarDefs::getAvatarFromSslId(p1id, p1avatar); - m_ui->m_player1_avatar->setPixmap(p1avatar);//QPixmap(":/images/profile.png")); - - QPixmap p2avatar; - AvatarDefs::getAvatarFromSslId(p2id, p2avatar); - m_ui->m_player2_avatar->setPixmap(p2avatar);//QPixmap(":/images/profile.png")); + if (!mIsGxs) { + // SSL peer avatar loading (only valid for direct peer connections) + QPixmap p1avatar; + AvatarDefs::getAvatarFromSslId(p1id, p1avatar); + m_ui->m_player1_avatar->setPixmap(p1avatar); + + QPixmap p2avatar; + AvatarDefs::getAvatarFromSslId(p2id, p2avatar); + m_ui->m_player2_avatar->setPixmap(p2avatar); + } else { + // GXS mode: p1id/p2id are not set, use default avatars + m_ui->m_player1_avatar->setPixmap(QPixmap(":/images/profile.png")); + m_ui->m_player2_avatar->setPixmap(QPixmap(":/images/profile.png")); + } //m_ui->m_move_record->setStyleSheet("QLabel {background-color: white;}"); } From b25fa9e508be6361b142fd1ef923110d4a643494 Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Sun, 3 May 2026 15:53:17 +0200 Subject: [PATCH 8/9] Fix Chess invites --- RetroChessPlugin.cpp | 19 ++- RetroChessPlugin.h | 4 + gui/NEMainpage.cpp | 2 +- gui/RetroChessChatWidgetHolder.cpp | 146 +++++++++-------- gui/RetroChessNotify.cpp | 5 + gui/RetroChessNotify.h | 4 + gui/chess.cpp | 4 +- interface/rsRetroChess.h | 4 + services/p3RetroChess.cc | 255 +++++++++++++++++++++++------ services/p3RetroChess.h | 13 ++ 10 files changed, 331 insertions(+), 125 deletions(-) diff --git a/RetroChessPlugin.cpp b/RetroChessPlugin.cpp index 967ba79..c2f881e 100644 --- a/RetroChessPlugin.cpp +++ b/RetroChessPlugin.cpp @@ -78,12 +78,16 @@ void RetroChessPlugin::getPluginVersion(int& major, int& minor, int& build, int& RetroChessPlugin::RetroChessPlugin() { qRegisterMetaType("RsPeerId"); + qRegisterMetaType("RsGxsId"); mainpage = NULL ; mRetroChess = NULL ; mPlugInHandler = NULL; mPeers = NULL; config_page = NULL ; mIcon = NULL ; + mGxsTunnels = NULL; + mIdentity = NULL; + mChats = NULL; mRetroChessNotify = new RetroChessNotify; } @@ -92,6 +96,13 @@ void RetroChessPlugin::setInterfaces(RsPlugInInterfaces &interfaces) { mPeers = interfaces.mPeers; mGxsTunnels = interfaces.mGxsTunnels; + mIdentity = interfaces.mIdentity; + mChats = interfaces.mChats; + + if(mRetroChess) + { + mRetroChess->connectToGxsTunnelService(mGxsTunnels); + } } /*ConfigPage *RetroChessPlugin::qt_config_page() const @@ -127,10 +138,10 @@ ChatWidgetHolder *RetroChessPlugin::qt_get_chat_widget_holder(ChatWidget *chatWi { case ChatWidget::CHATTYPE_PRIVATE: return new RetroChessChatWidgetHolder(chatWidget, mRetroChessNotify); - case ChatWidget::CHATTYPE_UNKNOWN: - case ChatWidget::CHATTYPE_LOBBY: case ChatWidget::CHATTYPE_DISTANT: return new RetroChessChatWidgetHolder(chatWidget, mRetroChessNotify); + case ChatWidget::CHATTYPE_UNKNOWN: + case ChatWidget::CHATTYPE_LOBBY: break; } @@ -143,10 +154,10 @@ p3Service *RetroChessPlugin::p3_service() const { // Create the service rsRetroChess = mRetroChess = new p3RetroChess(mPlugInHandler, mRetroChessNotify); - + // Register it for GXS Tunnels immediately if the interface is available if (mGxsTunnels) { - mGxsTunnels->registerClientService(RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, mRetroChess); + mRetroChess->connectToGxsTunnelService(mGxsTunnels); } } return mRetroChess; diff --git a/RetroChessPlugin.h b/RetroChessPlugin.h index 0b0c0a1..99cad73 100644 --- a/RetroChessPlugin.h +++ b/RetroChessPlugin.h @@ -27,6 +27,8 @@ /*libretroshare"*/ #include #include +#include +#include #include "gui/NEMainpage.h" @@ -78,6 +80,8 @@ class RetroChessPlugin: public RsPlugin mutable MainPage* mainpage ; RsGxsTunnelService *mGxsTunnels; + RsIdentity *mIdentity; + RsChats *mChats; RsGxsTunnelService::RsGxsTunnelClientService *mGxsTunnelClient; RetroChessNotify *mRetroChessNotify ; RetroChessGUIHandler *mRetroChessGUIHandler ; diff --git a/gui/NEMainpage.cpp b/gui/NEMainpage.cpp index 15d0157..0d6c979 100644 --- a/gui/NEMainpage.cpp +++ b/gui/NEMainpage.cpp @@ -77,7 +77,7 @@ NEMainpage::~NEMainpage() void NEMainpage::chessStart(const RsPeerId &peer_id) { - create_chess_window(peer_id.toStdString(), 0); + create_chess_window(peer_id.toStdString(), 1); } void NEMainpage::chessStartGxs(const RsGxsId &gxs_id) diff --git a/gui/RetroChessChatWidgetHolder.cpp b/gui/RetroChessChatWidgetHolder.cpp index ccf95c0..5114c1d 100644 --- a/gui/RetroChessChatWidgetHolder.cpp +++ b/gui/RetroChessChatWidgetHolder.cpp @@ -117,80 +117,85 @@ void RetroChessChatWidgetHolder::chessnotify(RsPeerId from_peer_id) void RetroChessChatWidgetHolder::chessnotifyGxs(const RsGxsId &from_gxs_id) { ChatId chatId = mChatWidget->getChatId(); - - // Check if the current chat window matches the GXS ID of the person who invited us + + // Only handle distant (GXS) chats if (!chatId.isDistantChatId()) { - return; + return; } - // You need a way to check if an invite exists. - // If you haven't implemented hasInviteFromGxs, you can use the active tunnel status. - if (true) // In GXS, the receipt of the signal itself implies an invite - { - if (mChatWidget) - { - // Get the name from the GXS Identity system - std::string identityName; - RsGxsId nameId = from_gxs_id; - // Attempt to get a readable name for the GXS ID - // If rsIdentity doesn't have a direct getName, we use a placeholder or the ID string - QString buttonName = QString::fromStdString(from_gxs_id.toStdString()).left(8); - - // Clear old buttons for this chat to avoid duplicates - button_map::iterator it = buttonMapTakeChess.begin(); - while (it != buttonMapTakeChess.end()) - { - it = buttonMapTakeChess.erase(it); - } + // Verify the invite is specifically for THIS chat window. + // Each distant chat window has a unique tunnel — check the remote GXS ID matches. + DistantChatPeerInfo dcpinfo; + if (!rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo)) { + return; // Can't identify this window's remote peer + } + if (dcpinfo.to_id != from_gxs_id) { + return; // This invite is for a different chat window + } - // Add the system message and the "Accept" button to the chat history - mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime(), - tr("%1 is inviting you to start Chess via GXS. Accept?").arg(buttonName), - ChatWidget::MSGTYPE_SYSTEM); - - RSButtonOnText *button = mChatWidget->getNewButtonOnTextBrowser(tr("Accept GXS Invite")); - button->setToolTip(tr("Accept Chess Invitation")); - - // Apply your specific green styling - button->setStyleSheet(QString("border: 1px solid #199909;") - .append("font-size: 12pt; color: white;") - .append("min-width: 128px; min-height: 24px;") - .append("border-radius: 6px;") - .append("background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.67, " - "stop: 0 #22c70d, stop: 1 #116a06);")); - - button->updateImage(); - - // Connect to the logic that starts the game/tunnel - connect(button, SIGNAL(clicked()), this, SLOT(chessStart())); - connect(button, SIGNAL(mouseEnter()), this, SLOT(botMouseEnter())); - connect(button, SIGNAL(mouseLeave()), this, SLOT(botMouseLeave())); - - buttonMapTakeChess.insert(buttonName, button); - } + if (!mChatWidget) { + return; } + + // Get a display name for the button + QString buttonName = QString::fromStdString(from_gxs_id.toStdString()).left(8); + + // Clear any old accept buttons to avoid duplicates + button_map::iterator it = buttonMapTakeChess.begin(); + while (it != buttonMapTakeChess.end()) { + it = buttonMapTakeChess.erase(it); + } + + // Add the system message and the "Accept" button to the chat history + mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime(), + tr("%1 is inviting you to start Chess via GXS. Accept?").arg(buttonName), + ChatWidget::MSGTYPE_SYSTEM); + + RSButtonOnText *button = mChatWidget->getNewButtonOnTextBrowser(tr("Accept GXS Invite")); + button->setToolTip(tr("Accept Chess Invitation")); + + button->setStyleSheet(QString("border: 1px solid #199909;") + .append("font-size: 12pt; color: white;") + .append("min-width: 128px; min-height: 24px;") + .append("border-radius: 6px;") + .append("background-color: qlineargradient(x1: 0, y1: 0, x2: 0, y2: 0.67, " + "stop: 0 #22c70d, stop: 1 #116a06);")); + + button->updateImage(); + + connect(button, SIGNAL(clicked()), this, SLOT(chessStart())); + connect(button, SIGNAL(mouseEnter()), this, SLOT(botMouseEnter())); + connect(button, SIGNAL(mouseLeave()), this, SLOT(botMouseLeave())); + + buttonMapTakeChess.insert(buttonName, button); } + void RetroChessChatWidgetHolder::chessPressed() { ChatId chatId = mChatWidget->getChatId(); QString peerName; if (chatId.isDistantChatId()) { - // Resolve the real remote GXS ID from the distant chat tunnel + // sendInvite_chat() handles everything: + // - if the GXS tunnel is ready: requests tunnel + queues invite immediately + // - if not ready yet: stores chatId and retries automatically on each tick() + rsRetroChess->sendInvite_chat(chatId); + DistantChatPeerInfo dcpinfo; - if (!rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo)) { - std::cerr << "RetroChess: Failed to resolve distant chat status" << std::endl; - return; + if (rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo) + && !dcpinfo.to_id.isNull()) { + peerName = QString::fromStdString(dcpinfo.to_id.toStdString()).left(8); + mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), + QDateTime::currentDateTime(), + tr("Chess invite queued for %1 — will send when connection is ready.").arg(peerName), + ChatWidget::MSGTYPE_SYSTEM); + } else { + // Tunnel not up yet — service will retry automatically + mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), + QDateTime::currentDateTime(), + tr("Chess invite queued — connecting to friend, will send automatically..."), + ChatWidget::MSGTYPE_SYSTEM); } - RsGxsId remoteGxsId = dcpinfo.to_id; - - rsRetroChess->sendGxsInvite(remoteGxsId); - - peerName = QString::fromStdString(remoteGxsId.toStdString()).left(8); - mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), - QDateTime::currentDateTime(), - tr("Requesting secure game tunnel..."), - ChatWidget::MSGTYPE_SYSTEM); return; } else { RsPeerId peer_id = chatId.toPeerId(); @@ -205,16 +210,18 @@ void RetroChessChatWidgetHolder::chessPressed() peerName = QString::fromUtf8(rsPeers->getPeerName(peer_id).c_str()); } - mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime() - , tr("You're now inviting %1 to play Chess").arg(peerName), ChatWidget::MSGTYPE_SYSTEM); - + mChatWidget->addChatMsg(true, tr("Chess Status"), QDateTime::currentDateTime(), QDateTime::currentDateTime() + , tr("You're now inviting %1 to play Chess").arg(peerName), ChatWidget::MSGTYPE_SYSTEM); } + void RetroChessChatWidgetHolder::chessStart() { ChatId chatId = mChatWidget->getChatId(); if (chatId.isDistantChatId()) { - // Resolve the real remote GXS ID from the distant chat tunnel + // Remote side accepting an invite: + // The tunnel is already open (the invite arrived over it). + // Call acceptedInviteGxs() which will send chess_accept over the existing tunnel. DistantChatPeerInfo dcpinfo; if (!rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo)) { std::cerr << "RetroChess: Failed to resolve distant chat status" << std::endl; @@ -224,12 +231,10 @@ void RetroChessChatWidgetHolder::chessStart() rsRetroChess->acceptedInviteGxs(remoteGxsId); - // Do NOT open the chess window here — wait for handleGxsTunnelReady() - // to fire when the tunnel is actually established. - mChatWidget->addChatMsg(true, tr("RetroChess"), QDateTime::currentDateTime(), - QDateTime::currentDateTime(), - tr("Establishing secure GXS tunnel..."), - ChatWidget::MSGTYPE_SYSTEM); + // Open the chess window immediately — we're the server side, tunnel is already up + mRetroChessNotify->notifyChessStartGxs(remoteGxsId); + if (playChessButton) playChessButton->hide(); + } else { RsPeerId peer_id = chatId.toPeerId(); rsRetroChess->acceptedInvite(peer_id); @@ -238,6 +243,7 @@ void RetroChessChatWidgetHolder::chessStart() return; } + void RetroChessChatWidgetHolder::handleGxsTunnelReady(const RsGxsId &gxs_id) { ChatId chatId = mChatWidget->getChatId(); diff --git a/gui/RetroChessNotify.cpp b/gui/RetroChessNotify.cpp index 0770945..0989d42 100644 --- a/gui/RetroChessNotify.cpp +++ b/gui/RetroChessNotify.cpp @@ -52,6 +52,11 @@ void RetroChessNotify::notifyChessInvite(const RsPeerId &peer_id) } +void RetroChessNotify::notifyChessInviteGxs(const RsGxsId &gxs_id) +{ + emit chessInvitedGxs(gxs_id); +} + void RetroChessNotify::notifyChessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count) { emit chessMoveGxs(gxs_id, col, row, count); diff --git a/gui/RetroChessNotify.h b/gui/RetroChessNotify.h index bf32d18..1112386 100644 --- a/gui/RetroChessNotify.h +++ b/gui/RetroChessNotify.h @@ -43,6 +43,9 @@ class RetroChessNotify : public QObject /** Notify the UI of a chess move received via a GXS tunnel */ void notifyChessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); + /** Notify the UI that a GXS chess invite was received */ + void notifyChessInviteGxs(const RsGxsId &gxs_id); + /** Notify the UI that a GXS tunnel is now ready for use */ void notifyGxsTunnelReady(const RsGxsId &gxs_id); @@ -57,6 +60,7 @@ class RetroChessNotify : public QObject void chessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); void gxsTunnelReady(const RsGxsId &gxs_id); + void chessInvitedGxs(const RsGxsId &gxs_id); void chessStartGxs(const RsGxsId &gxs_id); public slots: diff --git a/gui/chess.cpp b/gui/chess.cpp index 87be0c5..f1fedaa 100644 --- a/gui/chess.cpp +++ b/gui/chess.cpp @@ -1111,7 +1111,7 @@ void RetroChessWindow::playerTurnNotice() { if( this->turn ) { - m_ui->m_player1_result->setText("Waiting for opponent"); + m_ui->m_player1_result->setText("Waiting"); m_ui->m_player1_result->setStyleSheet("QLabel {font: 14pt; color: gray;}"); m_ui->m_player2_result->setText("Turn"); @@ -1125,7 +1125,7 @@ void RetroChessWindow::playerTurnNotice() m_ui->m_player1_result->setText("Turn"); m_ui->m_player1_result->setStyleSheet("QLabel {font: 14pt;color: green;}"); - m_ui->m_player2_result->setText("Waiting for opponent"); + m_ui->m_player2_result->setText("Waiting"); m_ui->m_player2_result->setStyleSheet("QLabel {font: 14pt;color: gray; text:bold;}"); m_ui->m_player1_result->setVisible(true); diff --git a/interface/rsRetroChess.h b/interface/rsRetroChess.h index 861029c..baa1eed 100644 --- a/interface/rsRetroChess.h +++ b/interface/rsRetroChess.h @@ -27,6 +27,7 @@ #include #include #include +#include // for ChatId #include #include @@ -62,6 +63,9 @@ class RsRetroChess //virtual void addChessFriend(const RsGxsId &gxsId) = 0; virtual void acceptedInviteGxs(const RsGxsId &gxsId) = 0; + // Send invite via an *existing* distant chat tunnel (the right approach for distant chat) + virtual void sendInvite_chat(const ChatId &chatId) = 0; + }; diff --git a/services/p3RetroChess.cc b/services/p3RetroChess.cc index ecf021e..623630a 100644 --- a/services/p3RetroChess.cc +++ b/services/p3RetroChess.cc @@ -35,6 +35,7 @@ #include #include "gui/RetroChessNotify.h" +#include //#define DEBUG_RetroChess 1 @@ -83,7 +84,7 @@ static double convert64bitsToTs(uint64_t bits) } p3RetroChess::p3RetroChess(RsPluginHandler *handler,RetroChessNotify *notifier) - : RsPQIService(RS_SERVICE_TYPE_RetroChess_PLUGIN,0,handler), mRetroChessMtx("p3RetroChess"), mServiceControl(handler->getServiceControl()), mNotify(notifier) + : RsPQIService(RS_SERVICE_TYPE_RetroChess_PLUGIN,0,handler), mRetroChessMtx("p3RetroChess"), mServiceControl(handler->getServiceControl()), mNotify(notifier), mGxsTunnels(NULL) { addSerialType(new RsRetroChessSerialiser()); @@ -112,11 +113,8 @@ int p3RetroChess::tick() #ifdef DEBUG_RetroChess std::cerr << "ticking p3RetroChess" << std::endl; #endif - // Call your GXS polling logic handleGxsTick(); - - // Call the base class tick if necessary, or return 0 - // Returning 0 tells the core this service is idle for this slice + retryPendingDistantChatInvites(); return 0; } @@ -467,77 +465,215 @@ void p3RetroChess::sendGxsInvite(const RsGxsId &to_gxs_id) void p3RetroChess::acceptedInviteGxs(const RsGxsId &gxsId) { - std::cout << "p3RetroChess: Accepted GXS invite from " << gxsId << std::endl; - - // If we don't have an active tunnel yet, request one now - if (mActiveTunnels.find(gxsId) == mActiveTunnels.end()) { + std::cout << "Chess: acceptedInviteGxs from " << gxsId << std::endl; + + RsStackMutex stack(mRetroChessMtx); + auto it = mActiveTunnels.find(gxsId); + if (it != mActiveTunnels.end()) { + // Tunnel already active (server side — invite arrived over it). + // Send chess_accept immediately. + std::string accept = "{\"type\":\"chess_accept\"}"; + std::cout << "Chess: Sending chess_accept over tunnel " << it->second << std::endl; + mGxsTunnels->sendData(it->second, RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, + (const uint8_t*)accept.c_str(), accept.size()); + } else { + // Client side: we initiated the invite but tunnel isn't in mActiveTunnels yet. + // Queue accept for when tunnel becomes CAN_TALK. + mPendingGxsInvites[gxsId] = "{\"type\":\"chess_accept\"}"; requestGxsTunnel(gxsId); + } +} + + +void p3RetroChess::sendInvite_chat(const ChatId &chatId) +{ + // For a peer (non-GXS) chat: use the legacy sendInvite path + if (chatId.isPeerId()) { + sendInvite(chatId.toPeerId()); + return; + } + + if (!chatId.isDistantChatId()) { + std::cerr << "Chess: sendInvite_chat: unknown ChatId type" << std::endl; + return; + } + + DistantChatPeerInfo info; + if (!rsChats->getDistantChatStatus(chatId.toDistantChatId(), info) + || info.to_id.isNull() || info.own_id.isNull()) + { + // Distant chat tunnel not established yet (no messages exchanged). + // Queue this chatId and retry every tick() until the tunnel is ready. + std::cout << "Chess: sendInvite_chat: distant chat not ready yet, queuing retry for " + << chatId.toStdString() << std::endl; + RsStackMutex stack(mRetroChessMtx); + // Only add once; don't reset timestamp on duplicate clicks + if (mPendingDistantChatInvites.find(chatId.toDistantChatId()) == mPendingDistantChatInvites.end()) { + mPendingDistantChatInvites[chatId.toDistantChatId()] = time(NULL); + } + return; + } + + doSendInviteOverGxs(info.to_id, info.own_id); +} + +void p3RetroChess::doSendInviteOverGxs(const RsGxsId &toId, const RsGxsId &ownId) +{ + std::cout << "Chess: doSendInviteOverGxs: to=" << toId << " from=" << ownId << std::endl; + + if (!mGxsTunnels) { + std::cerr << "Chess: doSendInviteOverGxs: mGxsTunnels is NULL!" << std::endl; + return; + } + + { + RsStackMutex stack(mRetroChessMtx); + mPendingTunnels.erase(toId); // clear any stale pending entry + } + + RsGxsTunnelId tunnelId; + uint32_t error_code = 0; + if (mGxsTunnels->requestSecuredTunnel(toId, ownId, tunnelId, + RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, error_code)) + { + RsStackMutex stack(mRetroChessMtx); + mPendingTunnels[toId] = tunnelId; + mPendingGxsInvites[toId] = "{\"type\":\"chess_invite\"}"; + std::cout << "Chess: Tunnel requested (id=" << tunnelId << "), invite queued for " << toId << std::endl; } else { - // If tunnel is already ready, tell the GUI to start the game - mNotify->notifyGxsTunnelReady(gxsId); + std::cerr << "Chess: doSendInviteOverGxs: requestSecuredTunnel failed, error=" << error_code << std::endl; } } +void p3RetroChess::retryPendingDistantChatInvites() +{ + std::map pending; + { + RsStackMutex stack(mRetroChessMtx); + pending = mPendingDistantChatInvites; + } + + if (pending.empty()) return; + + time_t now = time(NULL); + for (auto it = pending.begin(); it != pending.end(); ++it) { + // Throttle: only retry every 2 seconds + if (now - it->second < 2) continue; + + DistantChatPeerInfo info; + if (rsChats->getDistantChatStatus(it->first, info) + && !info.to_id.isNull() && !info.own_id.isNull()) + { + std::cout << "Chess: Retry succeeded for distant chat " + << it->first << " -> GXS " << info.to_id << std::endl; + { + RsStackMutex stack(mRetroChessMtx); + mPendingDistantChatInvites.erase(it->first); + } + // Now we have valid GXS IDs — send the invite + doSendInviteOverGxs(info.to_id, info.own_id); + } else { + // Still not ready — update timestamp so we wait another 2 seconds + RsStackMutex stack(mRetroChessMtx); + mPendingDistantChatInvites[it->first] = now; + } + } +} + + void p3RetroChess::handleGxsTick() { auto it = mPendingTunnels.begin(); while (it != mPendingTunnels.end()) { - RsGxsTunnelService::GxsTunnelInfo tinfo; + RsGxsTunnelService::GxsTunnelInfo tinfo; if (mGxsTunnels->getTunnelInfo(it->second, tinfo)) { // Check if the tunnel is "Connected" (CAN_TALK) if (tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_CAN_TALK) { - mActiveTunnels[it->first] = it->second; - mNotify->notifyGxsTunnelReady(it->first); + RsGxsId gxsId = it->first; + RsGxsTunnelId tunnelId = it->second; + mActiveTunnels[gxsId] = tunnelId; it = mPendingTunnels.erase(it); + + // Flush any queued invite for this peer + auto inviteIt = mPendingGxsInvites.find(gxsId); + if (inviteIt != mPendingGxsInvites.end()) { + std::string invite = inviteIt->second; + mPendingGxsInvites.erase(inviteIt); + std::cout << "Chess: Tunnel ready, flushing queued invite to " << gxsId << std::endl; + mGxsTunnels->sendData(tunnelId, RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, + (const uint8_t*)invite.c_str(), invite.size()); + } + + mNotify->notifyGxsTunnelReady(gxsId); continue; - } + } // Check for "Closed/Failed" status - else if (tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED || - tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_TUNNEL_DN) { + else if (tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED || + tinfo.tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_TUNNEL_DN) { + mPendingGxsInvites.erase(it->first); // discard queued invite it = mPendingTunnels.erase(it); continue; } } - ++it; + ++it; } } -// Update this signature to include gxs_id and am_I_client_side -void p3RetroChess::handleRawData(const RsGxsId& gxs_id, - const RsGxsTunnelId& tunnel_id, - bool am_I_client_side, - const uint8_t *data, + +void p3RetroChess::handleRawData(const RsGxsId& gxs_id, + const RsGxsTunnelId& tunnel_id, + bool /*am_I_client_side*/, + const uint8_t *data, uint32_t data_size) { - // Identify who sent the data using the Tunnel ID helper - // This ensures we have a record of this tunnel in our mActiveTunnels map - RsGxsId sender_id = findGxsIdByTunnel(tunnel_id); - - // Fallback: If the map lookup fails but the API provided a valid gxs_id, use that - if (sender_id.isNull() && !gxs_id.isNull()) { - sender_id = gxs_id; + // Resolve sender: first try the acceptDataFromPeer-populated map, then the passed gxs_id + RsGxsId sender_id; + { + RsStackMutex stack(mRetroChessMtx); + auto it = mTunnelToGxsIdMap.find(tunnel_id); + if (it != mTunnelToGxsIdMap.end()) + sender_id = it->second; } + if (sender_id.isNull() && !gxs_id.isNull()) + sender_id = gxs_id; if (sender_id.isNull()) { - std::cerr << "p3RetroChess::handleRawData: Received data from unknown tunnel " << tunnel_id << std::endl; + std::cerr << "Chess::handleRawData: unknown tunnel " << tunnel_id << std::endl; return; } - // Deserialize the raw bytes into the Chess Data Item - uint32_t temp_size = data_size; - RsRetroChessDataItem item((void*)data, temp_size); - - // Parse the move protocol (Format: "col,row,count") - QString qMsg = QString::fromStdString(item.m_msg); - QStringList parts = qMsg.split(","); - - if (parts.size() == 3) { - int col = parts[0].toInt(); - int row = parts[1].toInt(); - int count = parts[2].toInt(); - - // Notify the GUI with the resolved GXS Identity - mNotify->notifyChessMoveGxs(sender_id, col, row, count); + // All messages are JSON + std::string msg((const char*)data, data_size); + std::cout << "Chess::handleRawData: received from " << sender_id << ": " << msg << std::endl; + + QJsonDocument jsondoc = QJsonDocument::fromJson(QByteArray::fromStdString(msg)); + QVariantMap map = jsondoc.toVariant().toMap(); + QString type = map.value("type").toString(); + + if (type == "chess_invite") { + std::cout << "Chess: Received invite from GXS " << sender_id << std::endl; + { + RsStackMutex stack(mRetroChessMtx); + // Remember this tunnel is active for the sender (server-side) + mActiveTunnels[sender_id] = tunnel_id; + } + mNotify->notifyChessInviteGxs(sender_id); + + } else if (type == "chess_accept") { + std::cout << "Chess: Received accept from GXS " << sender_id << std::endl; + mNotify->notifyGxsTunnelReady(sender_id); // reuse: means game can start + + } else { + // Chess move: format "col,row,count" + QStringList parts = QString::fromStdString(msg).split(","); + if (parts.size() == 3) { + int col = parts[0].toInt(); + int row = parts[1].toInt(); + int count = parts[2].toInt(); + mNotify->notifyChessMoveGxs(sender_id, col, row, count); + } else { + std::cerr << "Chess: Unknown message type '" << type.toStdString() << "' ignored" << std::endl; + } } } @@ -566,15 +702,38 @@ void p3RetroChess::notifyTunnelStatus(const RsGxsTunnelId& /*tunnel_id*/, uint32 void p3RetroChess::receiveData(const RsGxsTunnelId& id, unsigned char *data, uint32_t data_size) { - this->handleRawData(RsGxsId(), id, false, (const uint8_t*)data, data_size); + // Look up the GXS ID that was stored in acceptDataFromPeer() — don't pass empty one + RsGxsId sender_gxs_id; + { + RsStackMutex stack(mRetroChessMtx); + auto it = mTunnelToGxsIdMap.find(id); + if (it != mTunnelToGxsIdMap.end()) + sender_gxs_id = it->second; + } + handleRawData(sender_gxs_id, id, false, (const uint8_t*)data, data_size); + free(data); // RS tunnel service transfers ownership } void p3RetroChess::connectToGxsTunnelService(RsGxsTunnelService *tunnel_service) { mGxsTunnels = tunnel_service; + if (mGxsTunnels) { + std::cout << "Chess: Registering GXS Tunnel client (Service ID: " << RETRO_CHESS_GXS_TUNNEL_SERVICE_ID << ")" << std::endl; + if (!mGxsTunnels->registerClientService(RETRO_CHESS_GXS_TUNNEL_SERVICE_ID, this)) { + std::cerr << "Chess: FAILED to register GXS Tunnel client!" << std::endl; + } + } } -bool p3RetroChess::acceptDataFromPeer(const RsGxsId& /*gxs_id*/, const RsGxsTunnelId& /*tunnel_id*/, bool /*am_I_client_side*/) +bool p3RetroChess::acceptDataFromPeer(const RsGxsId& gxs_id, const RsGxsTunnelId& tunnel_id, bool am_I_client_side) { + std::cout << "Chess: acceptDataFromPeer: gxs=" << gxs_id + << " tunnel=" << tunnel_id + << " side=" << (am_I_client_side ? "client" : "server") << std::endl; + { + RsStackMutex stack(mRetroChessMtx); + // Store the mapping so receiveData / handleRawData can identify the sender + mTunnelToGxsIdMap[tunnel_id] = gxs_id; + } return true; } diff --git a/services/p3RetroChess.h b/services/p3RetroChess.h index 58fd7a0..e524a63 100644 --- a/services/p3RetroChess.h +++ b/services/p3RetroChess.h @@ -32,6 +32,7 @@ #include "plugins/rspqiservice.h" #include "retroshare/rsidentity.h" #include +#include #include @@ -113,8 +114,13 @@ class p3RetroChess: public RsPQIService, public RsRetroChess, public RsGxsTunnel void chess_click_gxs(const RsGxsId &gxs_id, int col, int row, int count); virtual void requestGxsTunnel(const RsGxsId &gxsId) override; + // Send invite via existing distant chat tunnel (correct approach) + virtual void sendInvite_chat(const ChatId &chatId) override; + // Async tunnel management void handleGxsTick(); // Called periodically by the core + void retryPendingDistantChatInvites(); // Retry invites queued before the tunnel was ready + void doSendInviteOverGxs(const RsGxsId &toId, const RsGxsId &ownId); // Actually request tunnel + queue invite virtual uint32_t getGxsTunnelServiceId() const { return RETRO_CHESS_GXS_TUNNEL_SERVICE_ID; @@ -141,6 +147,13 @@ class p3RetroChess: public RsPQIService, public RsRetroChess, public RsGxsTunnel std::map mPendingTunnels; // Tracks established tunnels ready for data std::map mActiveTunnels; + // Pending invite messages to send once a tunnel becomes CAN_TALK + std::map mPendingGxsInvites; + // Maps tunnel ID → remote GXS ID (populated by acceptDataFromPeer, server-side) + std::map mTunnelToGxsIdMap; + // DistantChatIds for which sendInvite_chat() was called but getDistantChatStatus() + // failed (tunnel not established yet). Retried every tick() until it succeeds. + std::map mPendingDistantChatInvites; RsGxsTunnelService *mGxsTunnels; RsMutex mRetroChessMtx; From d6fad1ebedb8d4b9d8bffee7712103dae214d1c6 Mon Sep 17 00:00:00 2001 From: defnax <9952056+defnax@users.noreply.github.com> Date: Sun, 3 May 2026 17:07:49 +0200 Subject: [PATCH 9/9] Fix load gxs avatars Added check for tunnel closed --- gui/RetroChessChatWidgetHolder.cpp | 29 ++++++++++++++++++++++++++ gui/RetroChessChatWidgetHolder.h | 1 + gui/RetroChessNotify.cpp | 5 +++++ gui/RetroChessNotify.h | 4 ++++ gui/chess.cpp | 33 +++++++++++++++++++++++++++--- services/p3RetroChess.cc | 25 +++++++++++++++++++++- 6 files changed, 93 insertions(+), 4 deletions(-) diff --git a/gui/RetroChessChatWidgetHolder.cpp b/gui/RetroChessChatWidgetHolder.cpp index 5114c1d..c334f93 100644 --- a/gui/RetroChessChatWidgetHolder.cpp +++ b/gui/RetroChessChatWidgetHolder.cpp @@ -56,6 +56,7 @@ RetroChessChatWidgetHolder::RetroChessChatWidgetHolder(ChatWidget *chatWidget, R // When the p3RetroChess service detects the tunnel is CONNECTED, // it calls notifyGxsTunnelReady, which emits this signal: connect(notify, SIGNAL(gxsTunnelReady(RsGxsId)), this, SLOT(handleGxsTunnelReady(RsGxsId))); + connect(notify, SIGNAL(gxsTunnelClosed(RsGxsId)), this, SLOT(handleGxsTunnelClosed(RsGxsId))); } @@ -260,6 +261,34 @@ void RetroChessChatWidgetHolder::handleGxsTunnelReady(const RsGxsId &gxs_id) } } +void RetroChessChatWidgetHolder::handleGxsTunnelClosed(const RsGxsId &gxs_id) +{ + ChatId chatId = mChatWidget->getChatId(); + if (!chatId.isDistantChatId()) + return; + + // Check that this closure is for the remote peer of THIS chat window + DistantChatPeerInfo dcpinfo; + if (rsChats->getDistantChatStatus(chatId.toDistantChatId(), dcpinfo)) { + if (dcpinfo.to_id != gxs_id) + return; // Belongs to a different chat window + } + + // Restore the button so the user can start a new game + if (playChessButton) { + playChessButton->show(); + } + + if (mChatWidget) { + mChatWidget->addChatMsg(true, tr("RetroChess"), + QDateTime::currentDateTime(), + QDateTime::currentDateTime(), + tr("Connection to chess partner was lost. You can invite them again."), + ChatWidget::MSGTYPE_SYSTEM); + } +} + + void RetroChessChatWidgetHolder::botMouseEnter() { RSButtonOnText *source = qobject_cast(QObject::sender()); diff --git a/gui/RetroChessChatWidgetHolder.h b/gui/RetroChessChatWidgetHolder.h index 0909c46..db2f3ab 100644 --- a/gui/RetroChessChatWidgetHolder.h +++ b/gui/RetroChessChatWidgetHolder.h @@ -41,6 +41,7 @@ public slots: void chessnotifyGxs(const RsGxsId &from_gxs_id); void handleGxsTunnelReady(const RsGxsId &gxs_id); + void handleGxsTunnelClosed(const RsGxsId &gxs_id); private slots: void botMouseEnter(); diff --git a/gui/RetroChessNotify.cpp b/gui/RetroChessNotify.cpp index 0989d42..a3b9ae1 100644 --- a/gui/RetroChessNotify.cpp +++ b/gui/RetroChessNotify.cpp @@ -67,6 +67,11 @@ void RetroChessNotify::notifyGxsTunnelReady(const RsGxsId &gxs_id) emit gxsTunnelReady(gxs_id); } +void RetroChessNotify::notifyGxsTunnelClosed(const RsGxsId &gxs_id) +{ + emit gxsTunnelClosed(gxs_id); +} + void RetroChessNotify::notifyChessStartGxs(const RsGxsId &gxs_id) { emit chessStartGxs(gxs_id); diff --git a/gui/RetroChessNotify.h b/gui/RetroChessNotify.h index 1112386..636a0f7 100644 --- a/gui/RetroChessNotify.h +++ b/gui/RetroChessNotify.h @@ -49,6 +49,9 @@ class RetroChessNotify : public QObject /** Notify the UI that a GXS tunnel is now ready for use */ void notifyGxsTunnelReady(const RsGxsId &gxs_id); + /** Notify the UI that a GXS tunnel has been closed/lost */ + void notifyGxsTunnelClosed(const RsGxsId &gxs_id); + void notifyChessStartGxs(const RsGxsId &gxs_id); signals: @@ -59,6 +62,7 @@ class RetroChessNotify : public QObject void chessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); void gxsTunnelReady(const RsGxsId &gxs_id); + void gxsTunnelClosed(const RsGxsId &gxs_id); void chessInvitedGxs(const RsGxsId &gxs_id); void chessStartGxs(const RsGxsId &gxs_id); diff --git a/gui/chess.cpp b/gui/chess.cpp index f1fedaa..e8ef76d 100644 --- a/gui/chess.cpp +++ b/gui/chess.cpp @@ -200,9 +200,36 @@ void RetroChessWindow::initAccessories() AvatarDefs::getAvatarFromSslId(p2id, p2avatar); m_ui->m_player2_avatar->setPixmap(p2avatar); } else { - // GXS mode: p1id/p2id are not set, use default avatars - m_ui->m_player1_avatar->setPixmap(QPixmap(":/images/profile.png")); - m_ui->m_player2_avatar->setPixmap(QPixmap(":/images/profile.png")); + // GXS mode: retrieve avatars via the GXS identity service. + // Determine which slot is "us" and which is the remote peer, + // mirroring the same player/role logic used in the constructor. + std::list ownIds; + rsIdentity->getOwnIds(ownIds); + RsGxsId myGxsId = ownIds.empty() ? RsGxsId() : ownIds.front(); + + QPixmap p1avatar, p2avatar; + // p1 is always the identity shown in the player-1 slot (set in constructor) + // p2 is the identity shown in the player-2 slot + // The remote peer is mGxsId; our own is myGxsId. + // Which slot each maps to depends on the player role (set by the constructor). + RsGxsId slot1Id, slot2Id; + if (m_localplayer_turn == 0) { + // We are black (player 1 slot = us, player 2 slot = remote) + slot1Id = myGxsId; + slot2Id = mGxsId; + } else { + // We are white (player 1 slot = remote, player 2 slot = us) + slot1Id = mGxsId; + slot2Id = myGxsId; + } + + if (!slot1Id.isNull()) + AvatarDefs::getAvatarFromGxsId(slot1Id, p1avatar); + if (!slot2Id.isNull()) + AvatarDefs::getAvatarFromGxsId(slot2Id, p2avatar); + + m_ui->m_player1_avatar->setPixmap(p1avatar); + m_ui->m_player2_avatar->setPixmap(p2avatar); } //m_ui->m_move_record->setStyleSheet("QLabel {background-color: white;}"); diff --git a/services/p3RetroChess.cc b/services/p3RetroChess.cc index 623630a..73107fb 100644 --- a/services/p3RetroChess.cc +++ b/services/p3RetroChess.cc @@ -696,8 +696,31 @@ RsGxsId p3RetroChess::findGxsIdByTunnel(const RsGxsTunnelId& tunnel_id) // services/p3RetroChess.cc -void p3RetroChess::notifyTunnelStatus(const RsGxsTunnelId& /*tunnel_id*/, uint32_t /*tunnel_status*/) +void p3RetroChess::notifyTunnelStatus(const RsGxsTunnelId& tunnel_id, uint32_t tunnel_status) { + // React to tunnel being closed or going down + if (tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_REMOTELY_CLOSED || + tunnel_status == RsGxsTunnelService::RS_GXS_TUNNEL_STATUS_TUNNEL_DN) + { + RsGxsId gxs_id; + { + RsStackMutex stack(mRetroChessMtx); + // Search active tunnels for the closed tunnel + for (auto it = mActiveTunnels.begin(); it != mActiveTunnels.end(); ++it) { + if (it->second == tunnel_id) { + gxs_id = it->first; + mActiveTunnels.erase(it); + break; + } + } + // Also clean up mapping + mTunnelToGxsIdMap.erase(tunnel_id); + } + if (!gxs_id.isNull()) { + std::cout << "Chess: Tunnel closed for GXS " << gxs_id << std::endl; + mNotify->notifyGxsTunnelClosed(gxs_id); + } + } } void p3RetroChess::receiveData(const RsGxsTunnelId& id, unsigned char *data, uint32_t data_size)