diff --git a/src/drivers/Qt/NetPlay.cpp b/src/drivers/Qt/NetPlay.cpp index e98db8eb..b9c0bc47 100644 --- a/src/drivers/Qt/NetPlay.cpp +++ b/src/drivers/Qt/NetPlay.cpp @@ -42,6 +42,7 @@ //----------------------------------------------------------------------------- static uint32_t opsCrc32 = 0; static void *traceRegistrationHandle = nullptr; +static bool serverRequestedStateLoad = false; struct NetPlayFrameData { @@ -678,6 +679,46 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } ); } } + break; + case NETPLAY_SYNC_STATE_RESP: + { + bool acceptStateLoadReq = false; + + FCEU_printf("Sync state request received from client '%s'\n", client->userName.toLocal8Bit().constData()); + + if (allowClientStateLoadReq) + { + QString msgBoxTxt = tr("Client '") + client->userName + tr("' has requested to load a new state:\n"); + msgBoxTxt += tr("\nDo you want to load it?"); + int ans = QMessageBox::question( consoleWindow, tr("Client State Load Request"), msgBoxTxt, QMessageBox::Yes | QMessageBox::No ); + + if (ans == QMessageBox::Yes) + { + acceptStateLoadReq = true; + } + } + + if (acceptStateLoadReq) + { + char *stateData = &static_cast(msgBuf)[ sizeof(netPlayMsgHdr) ]; + + FCEU_printf("Sync state request accepted\n"); + + EMUFILE_MEMORY em( stateData, msgSize ); + + FCEU_WRAPPER_LOCK(); + serverRequestedStateLoad = true; + FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP ); + serverRequestedStateLoad = false; + FCEU_WRAPPER_UNLOCK(); + + opsCrc32 = 0; + netPlayFrameData.reset(); + inputClear(); + resyncAllClients(); + } + } + break; default: printf("Unknown Msg: %08X\n", msgId); break; @@ -1088,6 +1129,44 @@ int NetPlayClient::requestRomLoad( const char *romPath ) return 0; } //----------------------------------------------------------------------------- +int NetPlayClient::requestStateLoad(EMUFILE *is) +{ + size_t dataSize; + char *dataBuf; + netPlayMsgHdr hdr(NETPLAY_SYNC_STATE_RESP); + + dataSize = is->size(); + hdr.msgSize += dataSize; + + if (dataSize == 0) + { + return -1; + } + + dataBuf = static_cast(::malloc(dataSize)); + + if (dataBuf == nullptr) + { + return -1; + } + is->fseek( 0, SEEK_SET ); + size_t readResult = is->fread( dataBuf, dataSize ); + + if (readResult != dataSize ) + { + printf("Read Error\n"); + } + printf("Sending Client ROM Sync Request\n"); + + hdr.toNetworkByteOrder(); + sock->write( reinterpret_cast(&hdr), sizeof(netPlayMsgHdr)); + sock->write( reinterpret_cast(dataBuf), dataSize ); + + ::free(dataBuf); + + return 0; +} +//----------------------------------------------------------------------------- void NetPlayClient::recordPingResult( uint64_t delay_ms ) { pingNumSamples++; @@ -1313,7 +1392,9 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize ) EMUFILE_MEMORY em( stateData, msgSize ); FCEU_WRAPPER_LOCK(); + serverRequestedStateLoad = true; FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP ); + serverRequestedStateLoad = false; FCEU_WRAPPER_UNLOCK(); opsCrc32 = 0; @@ -1447,11 +1528,17 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent) grid->addWidget( lbl, 0, 0, 1, 1 ); grid->addWidget( frameLeadSpinBox, 0, 1, 1, 1 ); + bool romLoadReqEna = false; allowClientRomReqCBox = new QCheckBox(tr("Allow Client ROM Load Requests")); grid->addWidget( allowClientRomReqCBox, 1, 0, 1, 2 ); + g_config->getOption("SDL.NetPlayHostAllowClientRomLoadReq", &romLoadReqEna); + allowClientRomReqCBox->setChecked(romLoadReqEna); + bool stateLoadReqEna = false; allowClientStateReqCBox = new QCheckBox(tr("Allow Client State Load Requests")); grid->addWidget( allowClientStateReqCBox, 2, 0, 1, 2 ); + g_config->getOption("SDL.NetPlayHostAllowClientStateLoadReq", &stateLoadReqEna); + allowClientStateReqCBox->setChecked(stateLoadReqEna); startButton = new QPushButton( tr("Start") ); startButton->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton)); @@ -1511,6 +1598,9 @@ void NetPlayHostDialog::onStartClicked(void) server->setAllowClientRomLoadRequest( allowClientRomReqCBox->isChecked() ); server->setAllowClientStateLoadRequest( allowClientStateReqCBox->isChecked() ); + g_config->setOption("SDL.NetPlayHostAllowClientRomLoadReq", allowClientRomReqCBox->isChecked() ); + g_config->setOption("SDL.NetPlayHostAllowClientStateLoadReq", allowClientStateReqCBox->isChecked() ); + bool listenSucceeded = server->listen( QHostAddress::Any, netPort ); if (listenSucceeded) @@ -2217,3 +2307,18 @@ void NetPlayOnFrameBegin() //printf("Frame: %u Ops:%08X Ram:%08X\n", data.frameNum, data.opsCrc32, data.ramCrc32 ); } //---------------------------------------------------------------------------- +bool NetPlayStateLoadReq(EMUFILE* is) +{ + auto* client = NetPlayClient::GetInstance(); + + bool shouldLoad = (client == nullptr) || serverRequestedStateLoad; + + printf("NetPlay Load State: %i\n", shouldLoad); + + if ( (client != nullptr) && !serverRequestedStateLoad) + { // Send state to host + client->requestStateLoad(is); + } + return !shouldLoad; +} +//---------------------------------------------------------------------------- diff --git a/src/drivers/Qt/NetPlay.h b/src/drivers/Qt/NetPlay.h index 75756c4c..fcee9458 100644 --- a/src/drivers/Qt/NetPlay.h +++ b/src/drivers/Qt/NetPlay.h @@ -164,8 +164,8 @@ class NetPlayServer : public QTcpServer uint32_t maxLeadFrames = 10u; uint32_t clientWaitCounter = 0; uint32_t inputFrameCount = 0; - bool allowClientRomLoadReq = true; - bool allowClientStateLoadReq = true; + bool allowClientRomLoadReq = false; + bool allowClientStateLoadReq = false; public: signals: @@ -198,6 +198,7 @@ class NetPlayClient : public QObject void forceDisconnect(); bool flushData(); int requestRomLoad( const char *romPath ); + int requestStateLoad(EMUFILE* is); QTcpSocket* createSocket(void); void setSocket(QTcpSocket *s); @@ -426,6 +427,7 @@ int NetPlayFrameWait(void); void NetPlayOnFrameBegin(void); void NetPlayReadInputFrame(uint8_t* joy); void NetPlayCloseSession(void); +bool NetPlayStateLoadReq(EMUFILE* is); void NetPlayTraceInstruction(uint8_t *opcode, int size); void openNetPlayHostDialog(QWidget* parent = nullptr); void openNetPlayJoinDialog(QWidget* parent = nullptr); diff --git a/src/drivers/Qt/config.cpp b/src/drivers/Qt/config.cpp index e31cf378..601e2e89 100644 --- a/src/drivers/Qt/config.cpp +++ b/src/drivers/Qt/config.cpp @@ -633,6 +633,8 @@ InitConfig() config->addOption('k', "netkey", "SDL.NetworkGameKey", ""); config->addOption("port", "SDL.NetworkPort", NetPlayServer::DefaultPort); config->addOption("players", "SDL.NetworkPlayers", 1); + config->addOption("SDL.NetPlayHostAllowClientRomLoadReq", 0); + config->addOption("SDL.NetPlayHostAllowClientStateLoadReq", 0); // input configuration options config->addOption("input1", "SDL.Input.0", "GamePad.0"); diff --git a/src/state.cpp b/src/state.cpp index 734a58c2..b5905741 100644 --- a/src/state.cpp +++ b/src/state.cpp @@ -639,6 +639,11 @@ int FCEUSS_LoadFP_old(EMUFILE* is, ENUM_SSLOADPARAMS params) return(x); } +#ifdef __QT_DRIVER__ +// Qt Driver NetPlay state load handler. This is to control state loading, +// only hosts can load states and clients can request loads. +bool NetPlayStateLoadReq(EMUFILE* is); +#endif bool FCEUSS_LoadFP(EMUFILE* is, ENUM_SSLOADPARAMS params) { @@ -665,6 +670,13 @@ bool FCEUSS_LoadFP(EMUFILE* is, ENUM_SSLOADPARAMS params) return ret; } +#ifdef __QT_DRIVER__ + if ( NetPlayStateLoadReq(is) ) + { + return false; + } +#endif + size_t totalsize = FCEU_de32lsb(header + 4); int stateversion = FCEU_de32lsb(header + 8); uint32_t comprlen = FCEU_de32lsb(header + 12); @@ -687,7 +699,8 @@ bool FCEUSS_LoadFP(EMUFILE* is, ENUM_SSLOADPARAMS params) int error = uncompress(memory_savestate.buf(), &uncomprlen, &compressed_buf[0], comprlen); if(error != Z_OK || uncomprlen != totalsize) return false; // we dont need to restore the backup here because we havent messed with the emulator state yet - } else + } + else { // the savestate is not compressed: just read from is to memory_savestate.vec is->fread(memory_savestate.buf(), totalsize); @@ -710,7 +723,8 @@ bool FCEUSS_LoadFP(EMUFILE* is, ENUM_SSLOADPARAMS params) FCEUPPU_LoadState(stateversion); FCEUSND_LoadState(stateversion); x=FCEUMOV_PostLoad(); - } else if (backup) + } + else if (backup) { msBackupSavestate.fseek(0,SEEK_SET); FCEUSS_LoadFP(&msBackupSavestate,SSLOADPARAM_NOBACKUP);