diff --git a/RetroChessPlugin.cpp b/RetroChessPlugin.cpp index 5bd9144..c2f881e 100644 --- a/RetroChessPlugin.cpp +++ b/RetroChessPlugin.cpp @@ -32,6 +32,7 @@ #include "gui/RetroChessChatWidgetHolder.h" #include +#include #include #include @@ -77,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; } @@ -90,6 +95,14 @@ RetroChessPlugin::RetroChessPlugin() 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 @@ -125,9 +138,10 @@ ChatWidgetHolder *RetroChessPlugin::qt_get_chat_widget_holder(ChatWidget *chatWi { case ChatWidget::CHATTYPE_PRIVATE: return new RetroChessChatWidgetHolder(chatWidget, mRetroChessNotify); + case ChatWidget::CHATTYPE_DISTANT: + return new RetroChessChatWidgetHolder(chatWidget, mRetroChessNotify); case ChatWidget::CHATTYPE_UNKNOWN: case ChatWidget::CHATTYPE_LOBBY: - case ChatWidget::CHATTYPE_DISTANT: break; } @@ -136,15 +150,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) { + mRetroChess->connectToGxsTunnelService(mGxsTunnels); + } + } + return mRetroChess; } void RetroChessPlugin::setPlugInHandler(RsPluginHandler *pgHandler) { - mPlugInHandler = pgHandler; + 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 c2e6fee..99cad73 100644 --- a/RetroChessPlugin.h +++ b/RetroChessPlugin.h @@ -26,6 +26,9 @@ /*libretroshare"*/ #include +#include +#include +#include #include "gui/NEMainpage.h" @@ -76,6 +79,10 @@ class RetroChessPlugin: public RsPlugin mutable QIcon *mIcon; 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 51f8a38..0d6c979 100644 --- a/gui/NEMainpage.cpp +++ b/gui/NEMainpage.cpp @@ -50,6 +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(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 @@ -75,7 +77,23 @@ 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) +{ + 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 @@ -178,6 +196,18 @@ void NEMainpage::create_chess_window(std::string peer_id, int player_id) ui->active_games->addItem(QString::fromStdString(peer_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, 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 void NEMainpage::enable_inviteButton() { diff --git a/gui/NEMainpage.h b/gui/NEMainpage.h index cf6d72f..1a3dbe9 100644 --- a/gui/NEMainpage.h +++ b/gui/NEMainpage.h @@ -58,6 +58,8 @@ private slots: void friendSelectionChanged(); void NeMsgArrived(const RsPeerId &peer_id, QString str); void chessStart(const RsPeerId &peer_id); + void chessStartGxs(const RsGxsId &gxs_id); + void chessMoveGxs(const RsGxsId &gxs_id, int col, int row, int count); void on_broadcastButton_clicked(); @@ -78,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 29e7b8c..c334f93 100644 --- a/gui/RetroChessChatWidgetHolder.cpp +++ b/gui/RetroChessChatWidgetHolder.cpp @@ -27,9 +27,11 @@ #include "interface/rsRetroChess.h" #include "gui/chat/ChatWidget.h" +#include #include "RetroChessChatWidgetHolder.h" +#include #include #include @@ -41,7 +43,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")); @@ -51,6 +52,11 @@ 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))); + connect(notify, SIGNAL(gxsTunnelClosed(RsGxsId)), this, SLOT(handleGxsTunnelClosed(RsGxsId))); } @@ -109,34 +115,180 @@ void RetroChessChatWidgetHolder::chessnotify(RsPeerId from_peer_id) } } -void RetroChessChatWidgetHolder::chessPressed() +void RetroChessChatWidgetHolder::chessnotifyGxs(const RsGxsId &from_gxs_id) { - RsPeerId peer_id = mChatWidget->getChatId().toPeerId();//TODO support GXSID - if (rsRetroChess->hasInviteFrom(peer_id)) - { + ChatId chatId = mChatWidget->getChatId(); + + // Only handle distant (GXS) chats + if (!chatId.isDistantChatId()) { + return; + } + + // 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 + } + + 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); +} - rsRetroChess->acceptedInvite(peer_id); - mRetroChessNotify->notifyChessStart(peer_id); + +void RetroChessChatWidgetHolder::chessPressed() +{ + ChatId chatId = mChatWidget->getChatId(); + QString peerName; + if (chatId.isDistantChatId()) { + // 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) + && !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); + } return; + } else { + RsPeerId peer_id = chatId.toPeerId(); - } - rsRetroChess->sendInvite(peer_id); + if (rsRetroChess->hasInviteFrom(peer_id)){ + rsRetroChess->acceptedInvite(peer_id); + mRetroChessNotify->notifyChessStart(peer_id); + return; + } + + rsRetroChess->sendInvite(peer_id); - QString peerName = QString::fromUtf8(rsPeers->getPeerName(peer_id).c_str()); + 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 + ChatId chatId = mChatWidget->getChatId(); + if (chatId.isDistantChatId()) { + // 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; + return; + } + RsGxsId remoteGxsId = dcpinfo.to_id; + + rsRetroChess->acceptedInviteGxs(remoteGxsId); - rsRetroChess->acceptedInvite(peer_id); - mRetroChessNotify->notifyChessStart(peer_id); + // 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); + mRetroChessNotify->notifyChessStart(peer_id); + } return; } + +void RetroChessChatWidgetHolder::handleGxsTunnelReady(const RsGxsId &gxs_id) +{ + ChatId chatId = mChatWidget->getChatId(); + if (chatId.isDistantChatId()) { + // 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(); + } + } + } +} + +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 958ba88..db2f3ab 100644 --- a/gui/RetroChessChatWidgetHolder.h +++ b/gui/RetroChessChatWidgetHolder.h @@ -39,6 +39,9 @@ public slots: void chessStart(); void chessnotify(RsPeerId from_peer_id); + 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 8b2fb5a..a3b9ae1 100644 --- a/gui/RetroChessNotify.cpp +++ b/gui/RetroChessNotify.cpp @@ -51,3 +51,28 @@ void RetroChessNotify::notifyChessInvite(const RsPeerId &peer_id) emit chessInvited(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); +} + +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 4fdd0fa..636a0f7 100644 --- a/gui/RetroChessNotify.h +++ b/gui/RetroChessNotify.h @@ -40,6 +40,19 @@ 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 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); + + /** Notify the UI that a GXS tunnel has been closed/lost */ + void notifyGxsTunnelClosed(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 @@ -47,6 +60,13 @@ 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); + void gxsTunnelClosed(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 68277e0..e8ef76d 100644 --- a/gui/chess.cpp +++ b/gui/chess.cpp @@ -26,10 +26,89 @@ #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) +{ + 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 + + 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; + + // Use non-blocking lookup with fallback for unknown identities + RsIdentityDetails d1, d2; + 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; + 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; + + 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 ); @@ -111,13 +190,47 @@ 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")); + 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: 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); - QPixmap p2avatar; - AvatarDefs::getAvatarFromSslId(p2id, p2avatar); - m_ui->m_player2_avatar->setPixmap(p2avatar);//QPixmap(":/images/profile.png")); + m_ui->m_player1_avatar->setPixmap(p1avatar); + m_ui->m_player2_avatar->setPixmap(p2avatar); + } //m_ui->m_move_record->setStyleSheet("QLabel {background-color: white;}"); } @@ -125,7 +238,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 +1115,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); } @@ -1009,7 +1138,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"); @@ -1023,7 +1152,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/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..baa1eed 100644 --- a/interface/rsRetroChess.h +++ b/interface/rsRetroChess.h @@ -27,6 +27,7 @@ #include #include #include +#include // for ChatId #include #include @@ -53,6 +54,18 @@ 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; + + // 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 912ec51..73107fb 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 @@ -34,6 +35,7 @@ #include #include "gui/RetroChessNotify.h" +#include //#define DEBUG_RetroChess 1 @@ -82,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()); @@ -111,10 +113,8 @@ int p3RetroChess::tick() #ifdef DEBUG_RetroChess std::cerr << "ticking p3RetroChess" << std::endl; #endif - - //processIncoming(); - //sendPackets(); - + handleGxsTick(); + retryPendingDistantChatInvites(); return 0; } @@ -245,8 +245,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; @@ -272,9 +270,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; @@ -417,3 +412,351 @@ 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 + 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)) { + mNotify->notifyGxsTunnelReady(gxsId); + return; + } + // Otherwise, start the async tunnel request + this->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 (mGxsTunnels->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::acceptedInviteGxs(const RsGxsId &gxsId) +{ + 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 { + 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; + 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) { + 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) { + mPendingGxsInvites.erase(it->first); // discard queued invite + it = mPendingTunnels.erase(it); + continue; + } + } + ++it; + } +} + + +void p3RetroChess::handleRawData(const RsGxsId& gxs_id, + const RsGxsTunnelId& tunnel_id, + bool /*am_I_client_side*/, + const uint8_t *data, + uint32_t data_size) +{ + // 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 << "Chess::handleRawData: unknown tunnel " << tunnel_id << std::endl; + return; + } + + // 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; + } + } +} + +void p3RetroChess::player_leave_gxs(const RsGxsId &gxs_id) { + // Logic to close tunnel + if(mActiveTunnels.count(gxs_id)) { + mGxsTunnels->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(); +} + +// services/p3RetroChess.cc + +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) +{ + // 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) +{ + 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 2ff1387..e524a63 100644 --- a/services/p3RetroChess.h +++ b/services/p3RetroChess.h @@ -30,12 +30,17 @@ #include "serialiser/rstlvbase.h" #include "rsitems/rsconfigitems.h" #include "plugins/rspqiservice.h" +#include "retroshare/rsidentity.h" +#include +#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. /** @@ -43,7 +48,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 { @@ -61,7 +66,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 +106,56 @@ 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; + + // 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; + } + + // 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); 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; + // 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; //RsPeerId mPeerID; diff --git a/services/rsRetroChessItems.h b/services/rsRetroChessItems.h index 1e4a563..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; @@ -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 };