Skip to content

Commit

Permalink
Qt netplay fixes for processing input packet data. Don't attempt spaw…
Browse files Browse the repository at this point in the history
…n dialog windows when in read loop, it will cause event loop re-entrancy issues.
  • Loading branch information
thor2016 committed Apr 4, 2024
1 parent 10418f5 commit f71b912
Show file tree
Hide file tree
Showing 3 changed files with 243 additions and 69 deletions.
241 changes: 178 additions & 63 deletions src/drivers/Qt/NetPlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include <QDir>
#include <QMessageBox>
#include <QTemporaryFile>

#include "../../fceu.h"
#include "../../cart.h"
Expand Down Expand Up @@ -380,25 +381,25 @@ int NetPlayServer::sendStateSyncReq( NetPlayClient *client )
{
EMUFILE_MEMORY em;
int compressionLevel = 1;
netPlayMsgHdr hdr(NETPLAY_SYNC_STATE_RESP);
netPlayLoadStateResp resp;

if ( GameInfo == nullptr )
{
return -1;
}
FCEUSS_SaveMS( &em, compressionLevel );

hdr.msgSize += em.size();
resp.hdr.msgSize += em.size();
resp.stateSize = em.size();
resp.opsCrc32 = opsCrc32;

printf("Sending ROM Sync Request\n");
printf("Sending ROM Sync Request: %zu\n", em.size());
FCEUI_SetEmulationPaused(EMULATIONPAUSED_PAUSED);

sendMsg( client, &hdr, sizeof(netPlayMsgHdr), [&hdr]{ hdr.toNetworkByteOrder(); } );
sendMsg( client, &resp, sizeof(netPlayLoadStateResp), [&resp]{ resp.toNetworkByteOrder(); } );
sendMsg( client, em.buf(), em.size() );

client->flushData();
opsCrc32 = 0;
inputClear();

return 0;
}
Expand Down Expand Up @@ -434,6 +435,10 @@ bool NetPlayServer::claimRole(NetPlayClient* client, int _role)
client->role = _role;
}
}
else
{
client->role = NETPLAY_SPECTATOR;
}
return success;
}
//-----------------------------------------------------------------------------
Expand Down Expand Up @@ -712,12 +717,88 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
netPlayLoadRomReq *msg = static_cast<netPlayLoadRomReq*>(msgBuf);
msg->toHostByteOrder();

if (allowClientRomLoadReq)
{
if (client->romLoadData.buf != nullptr)
{
::free(client->romLoadData.buf);
client->romLoadData.buf = nullptr;
}
client->romLoadData.size = 0;
client->romLoadData.fileName.clear();

const uint32_t fileSize = msg->fileSize;
constexpr uint32_t maxRomDataSize = 1024 * 1024;
const char *romData = &static_cast<const char*>(msgBuf)[ sizeof(netPlayLoadRomReq) ];

if ( (fileSize >= 16) && (fileSize < maxRomDataSize) )
{
client->romLoadData.fileName = msg->fileName;
client->romLoadData.size = fileSize;
client->romLoadData.buf = static_cast<char*>(::malloc(fileSize));
::memcpy( client->romLoadData.buf, romData, fileSize );
QTimer::singleShot( 100, this, SLOT(processClientRomLoadRequests(void)) );
}

}
}
break;
case NETPLAY_SYNC_STATE_RESP:
{
netPlayLoadStateResp* msg = static_cast<netPlayLoadStateResp*>(msgBuf);
msg->toHostByteOrder();

FCEU_printf("Sync state request received from client '%s'\n", client->userName.toLocal8Bit().constData());

if (allowClientStateLoadReq)
{
const char *stateData = msg->stateDataBuf();
const uint32_t stateDataSize = msg->stateDataSize();
constexpr uint32_t maxStateDataSize = 1024*1024;

if (client->stateLoadData.buf != nullptr)
{
::free(client->stateLoadData.buf);
client->stateLoadData.buf = nullptr;
}
client->stateLoadData.size = 0;

if ( (stateDataSize >= 16) && (stateDataSize < maxStateDataSize) )
{
client->stateLoadData.size = stateDataSize;
client->stateLoadData.buf = static_cast<char*>(::malloc(stateDataSize));
::memcpy( client->stateLoadData.buf, stateData, stateDataSize );
QTimer::singleShot( 100, this, SLOT(processClientStateLoadRequests(void)) );
}
}
}
break;
case NETPLAY_CLIENT_SYNC_REQ:
{
FCEU_WRAPPER_LOCK();
resyncClient( client );
FCEU_WRAPPER_UNLOCK();
}
break;
default:
printf("Unknown Msg: %08X\n", msgId);
break;
}

}
//-----------------------------------------------------------------------------
void NetPlayServer::processClientRomLoadRequests(void)
{
for (auto& client : clientList )
{
if (client->romLoadData.pending())
{
bool acceptRomLoadReq = false;

if (allowClientRomLoadReq)
{
QString msgBoxTxt = tr("Client '") + client->userName + tr("' has requested to load this ROM:\n\n");
msgBoxTxt += tr(msg->fileName) + tr("\n\nDo you want to load it?");
msgBoxTxt += client->romLoadData.fileName + tr("\n\nDo you want to load it?");
int ans = QMessageBox::question( consoleWindow, tr("Client ROM Load Request"), msgBoxTxt, QMessageBox::Yes | QMessageBox::No );

if (ans == QMessageBox::Yes)
Expand All @@ -729,26 +810,26 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
if (acceptRomLoadReq)
{
FILE *fp;
std::string filepath = QDir::tempPath().toLocal8Bit().constData();
const char *romData = &static_cast<const char*>(msgBuf)[ sizeof(netPlayLoadRomReq) ];
QString filepath = QDir::tempPath();
const char *romData = client->romLoadData.buf;
const size_t romSize = client->romLoadData.size;

filepath.append( "/" );
filepath.append( msg->fileName );

printf("Load ROM Request Received: %s\n", filepath.c_str());
filepath.append( client->romLoadData.fileName );

//printf("Load ROM Request Received: %s\n", filepath.c_str());
//printf("Dumping Temp Rom to: %s\n", filepath.c_str());
fp = ::fopen( filepath.c_str(), "wb");
fp = ::fopen( filepath.toLocal8Bit().constData(), "wb");

if (fp == nullptr)
{
return;
}
::fwrite( romData, 1, msgSize, fp );
::fwrite( romData, 1, romSize, fp );
::fclose(fp);

FCEU_WRAPPER_LOCK();
LoadGame( filepath.c_str(), true, true );
LoadGame( filepath.toLocal8Bit().constData(), true, true );
FCEUI_SetEmulationPaused(EMULATIONPAUSED_PAUSED);
FCEU_WRAPPER_UNLOCK();

Expand All @@ -761,13 +842,24 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
errorMsg.printf("Host is rejected ROMs load request");
sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } );
}

::free(client->romLoadData.buf);
client->romLoadData.buf = nullptr;
client->romLoadData.size = 0;
client->romLoadData.fileName.clear();
}
break;
case NETPLAY_SYNC_STATE_RESP:
}
}
//-----------------------------------------------------------------------------
void NetPlayServer::processClientStateLoadRequests(void)
{
for (auto& client : clientList )
{
if (client->stateLoadData.pending())
{
bool acceptStateLoadReq = false;
EMUFILE_MEMORY em( client->stateLoadData.buf, client->stateLoadData.size );

FCEU_printf("Sync state request received from client '%s'\n", client->userName.toLocal8Bit().constData());
bool acceptStateLoadReq = false;

if (allowClientStateLoadReq)
{
Expand All @@ -783,33 +875,19 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s

if (acceptStateLoadReq)
{
char *stateData = &static_cast<char*>(msgBuf)[ sizeof(netPlayMsgHdr) ];

FCEU_printf("Sync state request accepted\n");

EMUFILE_MEMORY em( stateData, msgSize );

FCEU_WRAPPER_LOCK();
serverRequestedStateLoad = true;
// Clients will be resync'd during this load call.
FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP );
serverRequestedStateLoad = false;
FCEU_WRAPPER_UNLOCK();
}

::free(client->stateLoadData.buf);
client->stateLoadData.buf = nullptr;
client->stateLoadData.size = 0;
}
break;
case NETPLAY_CLIENT_SYNC_REQ:
{
FCEU_WRAPPER_LOCK();
resyncClient( client );
FCEU_WRAPPER_UNLOCK();
}
break;
default:
printf("Unknown Msg: %08X\n", msgId);
break;
}

}
//-----------------------------------------------------------------------------
void NetPlayServer::update(void)
Expand Down Expand Up @@ -1024,6 +1102,21 @@ NetPlayClient::~NetPlayClient(void)
instance = nullptr;
}

if (romLoadData.buf != nullptr)
{
::free(romLoadData.buf);
romLoadData.buf = nullptr;
}
romLoadData.size = 0;
romLoadData.fileName.clear();

if (stateLoadData.buf != nullptr)
{
::free(stateLoadData.buf);
stateLoadData.buf = nullptr;
}
stateLoadData.size = 0;

if (sock != nullptr)
{
sock->close();
Expand Down Expand Up @@ -1261,10 +1354,12 @@ int NetPlayClient::requestStateLoad(EMUFILE *is)
{
size_t dataSize;
char *dataBuf;
netPlayMsgHdr hdr(NETPLAY_SYNC_STATE_RESP);
static constexpr size_t maxBytesPerWrite = 16 * 1024;
netPlayLoadStateResp resp;

dataSize = is->size();
hdr.msgSize += dataSize;
resp.hdr.msgSize += dataSize;
resp.stateSize = dataSize;

if (dataSize == 0)
{
Expand All @@ -1284,11 +1379,26 @@ int NetPlayClient::requestStateLoad(EMUFILE *is)
{
printf("Read Error\n");
}
printf("Sending Client ROM Sync Request\n");
printf("Sending Client ROM Sync Request: %u\n", resp.stateSize);

hdr.toNetworkByteOrder();
sock->write( reinterpret_cast<const char*>(&hdr), sizeof(netPlayMsgHdr));
sock->write( reinterpret_cast<const char*>(dataBuf), dataSize );
resp.toNetworkByteOrder();
sock->write( reinterpret_cast<const char*>(&resp), sizeof(netPlayLoadStateResp));

const char* bufPtr = dataBuf;
while (dataSize > 0)
{
size_t bytesToWrite = dataSize;

if (bytesToWrite > maxBytesPerWrite)
{
bytesToWrite = maxBytesPerWrite;
}
sock->write( bufPtr, bytesToWrite );

bufPtr += bytesToWrite;
dataSize -= bytesToWrite;
}
sock->flush();

::free(dataBuf);

Expand Down Expand Up @@ -1377,6 +1487,13 @@ void NetPlayClient::update(void)
//-----------------------------------------------------------------------------
int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgBuf, size_t msgSize ), void *userData )
{
if (readMessageProcessing)
{
printf("Read Message is Processing in callstack, don't allow re-entrantancy.\n");
return 0;
}
readMessageProcessing = true;

if (sock)
{
bool readReq;
Expand Down Expand Up @@ -1461,6 +1578,7 @@ int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgB
}
}
}
readMessageProcessing = false;

return 0;
}
Expand Down Expand Up @@ -1517,29 +1635,22 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
break;
case NETPLAY_LOAD_ROM_REQ:
{
FILE *fp;
std::string filepath = QDir::tempPath().toLocal8Bit().constData();
QTemporaryFile tmpFile;
netPlayLoadRomReq *msg = static_cast<netPlayLoadRomReq*>(msgBuf);
msg->toHostByteOrder();
const char *romData = &static_cast<const char*>(msgBuf)[ sizeof(netPlayLoadRomReq) ];

filepath.append( "/" );
filepath.append( msg->fileName );
FCEU_printf("Load ROM Request Received: %s\n", msg->fileName);

FCEU_printf("Load ROM Request Received: %s\n", filepath.c_str());

//printf("Dumping Temp Rom to: %s\n", filepath.c_str());
fp = ::fopen( filepath.c_str(), "wb");

if (fp == nullptr)
{
return;
}
::fwrite( romData, 1, msgSize, fp );
::fclose(fp);
tmpFile.setFileTemplate(QString("tmpRomXXXXXX.nes"));
tmpFile.open();
QString filepath = tmpFile.fileName();
printf("Dumping Temp Rom to: %s\n", tmpFile.fileName().toLocal8Bit().constData());
tmpFile.write( romData, msgSize );
tmpFile.close();

FCEU_WRAPPER_LOCK();
LoadGame( filepath.c_str(), true, true );
LoadGame( filepath.toLocal8Bit().constData(), true, true );
FCEUI_SetEmulationPaused(EMULATIONPAUSED_PAUSED);
FCEU_WRAPPER_UNLOCK();
}
Expand All @@ -1553,19 +1664,23 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
break;
case NETPLAY_SYNC_STATE_RESP:
{
char *stateData = &static_cast<char*>(msgBuf)[ sizeof(netPlayMsgHdr) ];
netPlayLoadStateResp* msg = static_cast<netPlayLoadStateResp*>(msgBuf);
msg->toHostByteOrder();

char *stateData = msg->stateDataBuf();
const uint32_t stateDataSize = msg->stateDataSize();

FCEU_printf("Sync state Request Received\n");
FCEU_printf("Sync state Request Received: %u\n", stateDataSize);

EMUFILE_MEMORY em( stateData, msgSize );
EMUFILE_MEMORY em( stateData, stateDataSize );

FCEU_WRAPPER_LOCK();
serverRequestedStateLoad = true;
FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP );
serverRequestedStateLoad = false;
FCEU_WRAPPER_UNLOCK();

opsCrc32 = 0;
opsCrc32 = msg->opsCrc32;
netPlayFrameData.reset();
inputClear();
}
Expand Down
Loading

0 comments on commit f71b912

Please sign in to comment.