diff --git a/src/drivers/Qt/ConsoleWindow.cpp b/src/drivers/Qt/ConsoleWindow.cpp index 59ff4c4d..67d9b3e8 100644 --- a/src/drivers/Qt/ConsoleWindow.cpp +++ b/src/drivers/Qt/ConsoleWindow.cpp @@ -1670,6 +1670,7 @@ void consoleWin_t::createMainMenu(void) //act->setShortcut( QKeySequence(tr("Shift+F7"))); act->setStatusTip(tr("Host Game Window")); connect(act, SIGNAL(triggered()), this, SLOT(openNetPlayHostWindow(void)) ); + netPlayHostAct = act; netPlayMenu->addAction(act); @@ -1678,10 +1679,20 @@ void consoleWin_t::createMainMenu(void) //act->setShortcut( QKeySequence(tr("Shift+F7"))); act->setStatusTip(tr("Join Game Window")); connect(act, SIGNAL(triggered()), this, SLOT(openNetPlayJoinWindow(void)) ); + netPlayJoinAct = act; netPlayMenu->addAction(act); - netPlayMenu->setEnabled(false); + // NetPlay -> End Game / Disconnect + act = new QAction(tr("&Disconnect/End Game"), this); + //act->setShortcut( QKeySequence(tr("Shift+F7"))); + act->setStatusTip(tr("Disconnect Netplay Game")); + connect(act, SIGNAL(triggered()), this, SLOT(closeNetPlaySession(void)) ); + netPlayDiscAct = act; + + netPlayMenu->addAction(act); + + //netPlayMenu->setEnabled(false); //----------------------------------------------------------------------- // Tools @@ -3159,6 +3170,11 @@ void consoleWin_t::openNetPlayJoinWindow(void) win->show(); } +void consoleWin_t::closeNetPlaySession(void) +{ + NetPlayCloseSession(); +} + void consoleWin_t::openAviRiffViewer(void) { AviRiffViewerDialog *win; @@ -4717,6 +4733,12 @@ void consoleWin_t::updatePeriodic(void) recAsWavAct->setEnabled( FCEU_IsValidUI( FCEUI_RECORDMOVIE ) && !FCEUI_WaveRecordRunning() ); stopWavAct->setEnabled( FCEUI_WaveRecordRunning() ); tasEditorAct->setEnabled( FCEU_IsValidUI(FCEUI_TASEDITOR) ); + + bool netPlayactv = NetPlayActive(); + + netPlayHostAct->setEnabled( !netPlayactv ); + netPlayJoinAct->setEnabled( !netPlayactv ); + netPlayDiscAct->setEnabled( netPlayactv ); } if ( errorMsgValid ) diff --git a/src/drivers/Qt/ConsoleWindow.h b/src/drivers/Qt/ConsoleWindow.h index d7ac9119..23206dcf 100644 --- a/src/drivers/Qt/ConsoleWindow.h +++ b/src/drivers/Qt/ConsoleWindow.h @@ -260,6 +260,9 @@ class consoleWin_t : public QMainWindow QAction *recAsWavAct; QAction *stopWavAct; QAction *tasEditorAct; + QAction *netPlayHostAct; + QAction *netPlayJoinAct; + QAction *netPlayDiscAct; //QAction *aviHudAct; //QAction *aviMsgAct; @@ -354,6 +357,7 @@ class consoleWin_t : public QMainWindow void openPaletteEditorWin(void); void openNetPlayHostWindow(void); void openNetPlayJoinWindow(void); + void closeNetPlaySession(void); void openAviRiffViewer(void); void openTimingStatWin(void); void openMovieOptWin(void); diff --git a/src/drivers/Qt/NetPlay.cpp b/src/drivers/Qt/NetPlay.cpp index b2dfeb0f..2794cba1 100644 --- a/src/drivers/Qt/NetPlay.cpp +++ b/src/drivers/Qt/NetPlay.cpp @@ -69,6 +69,17 @@ int NetPlayServer::Create(QObject *parent) return 0; } +int NetPlayServer::Destroy() +{ + NetPlayServer* server = NetPlayServer::GetInstance(); + if (server != nullptr) + { + delete server; + server = nullptr; + } + return 0; +} + void NetPlayServer::newConnectionRdy(void) { printf("New Connection Ready!\n"); @@ -82,7 +93,7 @@ void NetPlayServer::processPendingConnections(void) newSock = nextPendingConnection(); - while (newSock) + while (newSock != nullptr) { NetPlayClient *client = new NetPlayClient(this); @@ -100,47 +111,20 @@ void NetPlayServer::processPendingConnections(void) } } -bool NetPlayServer::removeClient(NetPlayClient *client, bool markForDelete) -{ - bool removed = false; - std::list ::iterator it; - - it = clientList.begin(); - - while (it != clientList.end()) - { - if (client == *it) - { - if (markForDelete) - { - client->deleteLater(); - } - - it = clientList.erase(it); - } - else - { - it++; - } - } - - for (int i=0; i<4; i++) - { - if (clientPlayer[i] == client) - { - clientPlayer[i] = nullptr; - } - } - return removed; -} - int NetPlayServer::closeAllConnections(void) { std::list ::iterator it; + for (int i=0; i<4; i++) + { + clientPlayer[i] = nullptr; + } + for (it = clientList.begin(); it != clientList.end(); it++) { - delete *it; + auto* client = *it; + + delete client; } clientList.clear(); @@ -298,31 +282,31 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s if ( claimRole(client, msg->playerId) ) { + client->userName = msg->userName; sendRomLoadReq( client ); sendStateSyncReq( client ); client->state = 1; + FCEU_DispMessage("%s Joined",0, client->userName.toLocal8Bit().constData()); } else { netPlayErrorMsg<128> errorMsg; + errorMsg.setDisconnectFlag(); errorMsg.printf("Player %i role is not available", msg->playerId+1); sendMsg( client, &errorMsg, errorMsg.hdr.msgSize ); - //client->disconnect(); + client->flushData(); } } break; - case NETPLAY_RUN_FRAME_RESP: - { - netPlayRunFrameResp *msg = static_cast(msgBuf); - - client->currentFrame = msg->frameRun; - } - break; case NETPLAY_CLIENT_STATE: { netPlayClientState *msg = static_cast(msgBuf); client->currentFrame = msg->frameRun; + client->gpData[0] = msg->ctrlState[0]; + client->gpData[1] = msg->ctrlState[1]; + client->gpData[2] = msg->ctrlState[2]; + client->gpData[3] = msg->ctrlState[3]; } break; default: @@ -342,26 +326,65 @@ void NetPlayServer::update(void) const uint32_t leadFrame = currFrame + maxLead; const uint32_t lastFrame = inputFrameBack(); uint32_t lagFrame = 0; + uint8_t localGP[4] = { 0 }; + uint8_t gpData[4] = { 0 }; if (currFrame > maxLead) { lagFrame = currFrame - maxLead; } + uint32_t ctlrData = GetGamepadPressedImmediate(); + localGP[0] = (ctlrData ) & 0x000000ff; + localGP[1] = (ctlrData >> 8) & 0x000000ff; + localGP[2] = (ctlrData >> 16) & 0x000000ff; + localGP[3] = (ctlrData >> 24) & 0x000000ff; + + if ( (role >= NETPLAY_PLAYER1) && (role <= NETPLAY_PLAYER4) ) + { + gpData[role] = localGP[role]; + } + // Input Processing - for (auto it = clientList.begin(); it != clientList.end(); it++) + for (auto it = clientList.begin(); it != clientList.end(); ) { NetPlayClient *client = *it; client->readMessages( serverMessageCallback, client ); - if (client->currentFrame < clientMinFrame) + if (client->isAuthenticated()) { - clientMinFrame = client->currentFrame; + if (client->currentFrame < clientMinFrame) + { + clientMinFrame = client->currentFrame; + } + if (client->currentFrame > clientMaxFrame) + { + clientMaxFrame = client->currentFrame; + } + + if (client->isPlayerRole()) + { + gpData[client->role] = client->gpData[client->role]; + } } - if (client->currentFrame > clientMaxFrame) + + + if (client->shouldDestroy()) { - clientMaxFrame = client->currentFrame; + it = clientList.erase(it); + for (int i=0; i<4; i++) + { + if (client == clientPlayer[i]) + { + clientPlayer[i] = nullptr; + } + } + delete client; + } + else + { + it++; } } @@ -378,13 +401,11 @@ void NetPlayServer::update(void) NetPlayFrameInput inputFrame; netPlayRunFrameReq runFrameReq; - uint32_t ctlrData = GetGamepadPressedImmediate(); - inputFrame.frameCounter = static_cast(currFrameCounter) + 1; - inputFrame.ctrl[0] = (ctlrData ) & 0x000000ff; - inputFrame.ctrl[1] = (ctlrData >> 8) & 0x000000ff; - inputFrame.ctrl[2] = (ctlrData >> 16) & 0x000000ff; - inputFrame.ctrl[3] = (ctlrData >> 24) & 0x000000ff; + inputFrame.ctrl[0] = gpData[0]; + inputFrame.ctrl[1] = gpData[1]; + inputFrame.ctrl[2] = gpData[2]; + inputFrame.ctrl[3] = gpData[3]; runFrameReq.frameNum = inputFrame.frameCounter; runFrameReq.ctrlState[0] = inputFrame.ctrl[0]; @@ -438,6 +459,7 @@ NetPlayClient::~NetPlayClient(void) if (sock != nullptr) { + sock->close(); delete sock; sock = nullptr; } if (recvMsgBuf) @@ -460,10 +482,43 @@ int NetPlayClient::Create(QObject *parent) } return 0; } -//----------------------------------------------------------------------------- -void NetPlayClient::disconnect() + +int NetPlayClient::Destroy() { - sock->close(); + NetPlayClient* client = NetPlayClient::GetInstance(); + if (client != nullptr) + { + delete client; + client = nullptr; + } + return 0; +} +//----------------------------------------------------------------------------- +void NetPlayClient::forceDisconnect() +{ + disconnectPending = true; + needsDestroy = true; +} +//----------------------------------------------------------------------------- +bool NetPlayClient::isAuthenticated() +{ + return state > 0; +} +//----------------------------------------------------------------------------- +bool NetPlayClient::isPlayerRole() +{ + return (role >= NETPLAY_PLAYER1) && (role <= NETPLAY_PLAYER4); +} +//----------------------------------------------------------------------------- +bool NetPlayClient::flushData() +{ + bool success = false; + + if (sock != nullptr) + { + success = sock->flush(); + } + return success; } //----------------------------------------------------------------------------- void NetPlayClient::setSocket(QTcpSocket *s) @@ -486,6 +541,7 @@ int NetPlayClient::createSocket(void) connect(sock, SIGNAL(connected(void)) , this, SLOT(onConnect(void))); connect(sock, SIGNAL(disconnected(void)), this, SLOT(onDisconnect(void))); + connect(sock, SIGNAL(errorOccurred(QAbstractSocket::SocketError)), this, SLOT(onSocketError(QAbstractSocket::SocketError))); return 0; } @@ -512,6 +568,8 @@ int NetPlayClient::connectToHost( const QString host, int port ) void NetPlayClient::onConnect(void) { printf("Client Connected!!!\n"); + FCEU_DispMessage("Joined Host",0); + connected = true; } //----------------------------------------------------------------------------- void NetPlayClient::onDisconnect(void) @@ -522,11 +580,23 @@ void NetPlayClient::onDisconnect(void) if (server) { - if (server->removeClient(this)) - { - deleteLater(); - } + FCEU_DispMessage("%s Disconnected",0, userName.toLocal8Bit().constData()); } + else + { + FCEU_DispMessage("Host Disconnected",0); + } + needsDestroy = true; +} +//----------------------------------------------------------------------------- +void NetPlayClient::onSocketError(QAbstractSocket::SocketError error) +{ + FCEU_DispMessage("Socket Error",0); + + QString errorMsg = sock->errorString(); + printf("Error: %s\n", errorMsg.toLocal8Bit().constData()); + + FCEU_DispMessage("%s", 0, errorMsg.toLocal8Bit().constData()); } //----------------------------------------------------------------------------- static void clientMessageCallback( void *userData, void *msgBuf, size_t msgSize ) @@ -540,20 +610,22 @@ void NetPlayClient::update(void) { readMessages( clientMessageCallback, this ); - uint32_t ctlrData = GetGamepadPressedImmediate(); - uint32_t currFrame = static_cast(currFrameCounter); + if (connected) + { + uint32_t ctlrData = GetGamepadPressedImmediate(); + uint32_t currFrame = static_cast(currFrameCounter); - netPlayClientState statusMsg; - statusMsg.flags = 0; - statusMsg.frameRdy = inputFrameBack(); - statusMsg.frameRun = currFrame; - statusMsg.ctrlState[0] = (ctlrData ) & 0x000000ff; - statusMsg.ctrlState[1] = (ctlrData >> 8) & 0x000000ff; - statusMsg.ctrlState[2] = (ctlrData >> 16) & 0x000000ff; - statusMsg.ctrlState[3] = (ctlrData >> 24) & 0x000000ff; - - sock->write( reinterpret_cast(&statusMsg), sizeof(statusMsg) ); + netPlayClientState statusMsg; + statusMsg.flags = 0; + statusMsg.frameRdy = inputFrameBack(); + statusMsg.frameRun = currFrame; + statusMsg.ctrlState[0] = (ctlrData ) & 0x000000ff; + statusMsg.ctrlState[1] = (ctlrData >> 8) & 0x000000ff; + statusMsg.ctrlState[2] = (ctlrData >> 16) & 0x000000ff; + statusMsg.ctrlState[3] = (ctlrData >> 24) & 0x000000ff; + sock->write( reinterpret_cast(&statusMsg), sizeof(statusMsg) ); + } } //----------------------------------------------------------------------------- int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgBuf, size_t msgSize ), void *userData ) @@ -633,11 +705,24 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize ) switch (hdr->msgId) { + case NETPLAY_ERROR_MSG: + { + auto *msg = static_cast*>(msgBuf); + printf("Error: 0x%X %s\n", msg->code, msg->getBuffer()); + + if (msg->isDisconnectFlagSet()) + { + sock->disconnectFromHost(); + } + FCEU_DispMessage("Host connect failed",0); + } + break; case NETPLAY_AUTH_REQ: { netPlayAuthResp msg; msg.playerId = role; - strncpy( msg.pswd, "TODO: Dummy Password", sizeof(msg.pswd) ); + strncpy( msg.userName, userName.toLocal8Bit().constData(), sizeof(msg.userName)); + strncpy( msg.pswd, password.toLocal8Bit().constData(), sizeof(msg.pswd) ); sock->write( (const char*)&msg, sizeof(netPlayAuthResp) ); } @@ -682,8 +767,6 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize ) NetPlayFrameInput inputFrame; netPlayRunFrameReq *msg = static_cast(msgBuf); - uint32_t currFrame = static_cast(currFrameCounter); - inputFrame.frameCounter = msg->frameNum; inputFrame.ctrl[0] = msg->ctrlState[0]; inputFrame.ctrl[1] = msg->ctrlState[1]; @@ -694,17 +777,6 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize ) { pushBackInput( inputFrame ); } - - netPlayRunFrameResp resp; - resp.flags = msg->flags; - resp.frameNum = msg->frameNum; - resp.frameRun = currFrame; - resp.ctrlState[0] = msg->ctrlState[0]; - resp.ctrlState[1] = msg->ctrlState[1]; - resp.ctrlState[2] = msg->ctrlState[2]; - resp.ctrlState[3] = msg->ctrlState[3]; - - sock->write( reinterpret_cast(&resp), sizeof(resp) ); } break; default: @@ -828,7 +900,7 @@ NetPlayJoinDialog::NetPlayJoinDialog(QWidget *parent) QPushButton *cancelButton, *startButton; QLabel *lbl; - setWindowTitle("NetPlay Host Game"); + setWindowTitle("NetPlay Join Game"); mainLayout = new QVBoxLayout(); grid = new QGridLayout(); @@ -860,6 +932,26 @@ NetPlayJoinDialog::NetPlayJoinDialog(QWidget *parent) playerRoleBox->setCurrentIndex(2); grid->addWidget( playerRoleBox, 2, 1 ); + lbl = new QLabel( tr("User:") ); + grid->addWidget( lbl, 3, 0 ); + + QString name = qgetenv("USER"); + if (name.isEmpty()) + { + name = qgetenv("USERNAME"); + } + userNameEntry = new QLineEdit(); + userNameEntry->setMaxLength(63); + userNameEntry->setText(name); + grid->addWidget( userNameEntry, 3, 1 ); + + lbl = new QLabel( tr("Password:") ); + grid->addWidget( lbl, 4, 0 ); + + passwordEntry = new QLineEdit(); + passwordEntry->setMaxLength(64); + grid->addWidget( passwordEntry, 4, 1 ); + mainLayout->addLayout(grid); startButton = new QPushButton( tr("Join") ); @@ -912,10 +1004,12 @@ void NetPlayJoinDialog::onJoinClicked(void) client = NetPlayClient::GetInstance(); client->role = playerRoleBox->currentData().toInt(); + client->userName = userNameEntry->text(); + client->password = passwordEntry->text(); if (client->connectToHost( hostEntry->text(), portEntry->value() )) { - printf("Failed to connect to Host\n"); + FCEU_DispMessage("Host connect failed",0); } //printf("Close Window\n"); @@ -930,6 +1024,11 @@ bool NetPlayActive(void) return (NetPlayClient::GetInstance() != nullptr) || (NetPlayServer::GetInstance() != nullptr); } //---------------------------------------------------------------------------- +bool isNetPlayHost(void) +{ + return (NetPlayServer::GetInstance() != nullptr); +} +//---------------------------------------------------------------------------- void NetPlayPeriodicUpdate(void) { NetPlayClient *client = NetPlayClient::GetInstance(); @@ -1007,3 +1106,9 @@ void NetPlayReadInputFrame(uint8_t* joy) joy[3] = netPlayInputFrame.ctrl[3]; } //---------------------------------------------------------------------------- +void NetPlayCloseSession(void) +{ + NetPlayClient::Destroy(); + NetPlayServer::Destroy(); +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/NetPlay.h b/src/drivers/Qt/NetPlay.h index 5d9714ef..8ff86e3b 100644 --- a/src/drivers/Qt/NetPlay.h +++ b/src/drivers/Qt/NetPlay.h @@ -70,8 +70,7 @@ class NetPlayServer : public QTcpServer static NetPlayServer *GetInstance(void){ return instance; }; static int Create(QObject *parent = 0); - - bool removeClient(NetPlayClient *client, bool markForDelete = false); + static int Destroy(); int closeAllConnections(void); @@ -148,11 +147,14 @@ class NetPlayClient : public QObject static NetPlayClient *GetInstance(void){ return instance; }; static int Create(QObject *parent = 0); + static int Destroy(); int connectToHost( const QString host, int port ); bool isConnected(void); - void disconnect(); + bool disconnectRequested(){ return disconnectPending; } + void forceDisconnect(); + bool flushData(); void setSocket(QTcpSocket *s); QTcpSocket* getSocket(void){ return sock; }; @@ -197,23 +199,31 @@ class NetPlayClient : public QObject return frame; } + bool isAuthenticated(); + bool isPlayerRole(); + bool shouldDestroy(){ return needsDestroy; } QString userName; - int role; - int state; - unsigned int currentFrame; + QString password; + int role = -1; + int state = 0; + unsigned int currentFrame = 0; + uint8_t gpData[4]; private: int createSocket(void); static NetPlayClient *instance; - QTcpSocket *sock; - int recvMsgId; - int recvMsgSize; - int recvMsgBytesLeft; - int recvMsgByteIndex; - char *recvMsgBuf; + QTcpSocket *sock = nullptr; + int recvMsgId = 0; + int recvMsgSize = 0; + int recvMsgBytesLeft = 0; + int recvMsgByteIndex = 0; + char *recvMsgBuf = nullptr; + bool disconnectPending = false; + bool needsDestroy = false; + bool connected = false; std::list input; FCEU::mutex inputMtx; @@ -223,6 +233,7 @@ class NetPlayClient : public QObject public slots: void onConnect(void); void onDisconnect(void); + void onSocketError(QAbstractSocket::SocketError); }; @@ -261,6 +272,8 @@ protected: QLineEdit *hostEntry; QSpinBox *portEntry; QComboBox *playerRoleBox; + QLineEdit *userNameEntry; + QLineEdit *passwordEntry; public slots: void closeWindow(void); @@ -269,7 +282,9 @@ public slots: }; bool NetPlayActive(void); +bool isNetPlayHost(void); void NetPlayPeriodicUpdate(void); bool NetPlaySkipWait(void); int NetPlayFrameWait(void); void NetPlayReadInputFrame(uint8_t* joy); +void NetPlayCloseSession(void); diff --git a/src/drivers/Qt/NetPlayMsgDef.h b/src/drivers/Qt/NetPlayMsgDef.h index dcec13e7..e32e8a4a 100644 --- a/src/drivers/Qt/NetPlayMsgDef.h +++ b/src/drivers/Qt/NetPlayMsgDef.h @@ -14,7 +14,6 @@ enum netPlayMsgType NETPLAY_LOAD_ROM_REQ, NETPLAY_SYNC_STATE, NETPLAY_RUN_FRAME_REQ, - NETPLAY_RUN_FRAME_RESP, NETPLAY_CLIENT_STATE, NETPLAY_ERROR_MSG, }; @@ -61,7 +60,8 @@ struct netPlayAuthResp netPlayMsgHdr hdr; char playerId; - char pswd[128]; + char userName[64]; + char pswd[72]; netPlayAuthResp(void) : hdr(NETPLAY_AUTH_RESP, sizeof(netPlayAuthResp)), playerId(NETPLAY_SPECTATOR) @@ -75,14 +75,29 @@ struct netPlayErrorMsg { netPlayMsgHdr hdr; + unsigned short code; + unsigned short flags; char data[N]; + static const uint32_t DISCONNECT_FLAG = 0x00000001; + netPlayErrorMsg(void) - : hdr(NETPLAY_ERROR_MSG, sizeof(netPlayErrorMsg)) + : hdr(NETPLAY_ERROR_MSG, sizeof(netPlayErrorMsg)), code(0), flags(0) { + memset(data, 0, N); } - char *getMsgBuffer() + void setDisconnectFlag() + { + flags |= DISCONNECT_FLAG; + } + + bool isDisconnectFlagSet() + { + return (flags & DISCONNECT_FLAG) ? true : false; + } + + const char *getBuffer() { return &data[0]; } @@ -95,7 +110,7 @@ struct netPlayErrorMsg retval = ::vsnprintf(data, sizeof(data), format, args); va_end(args); - hdr.msgSize = sizeof(netPlayMsgHdr) + strlen(data); + hdr.msgSize = sizeof(netPlayErrorMsg) - N + strlen(data) + 1; return retval; } @@ -131,22 +146,6 @@ struct netPlayRunFrameReq } }; -struct netPlayRunFrameResp -{ - netPlayMsgHdr hdr; - - uint32_t flags; - uint32_t frameNum; - uint32_t frameRun; - uint8_t ctrlState[4]; - - netPlayRunFrameResp(void) - : hdr(NETPLAY_RUN_FRAME_RESP, sizeof(netPlayRunFrameResp)), flags(0), frameNum(0), frameRun(0) - { - memset( ctrlState, 0, sizeof(ctrlState) ); - } -}; - struct netPlayClientState { netPlayMsgHdr hdr;