Skip to content

Commit

Permalink
Added netplay client frame throttling functionality to keep it in ste…
Browse files Browse the repository at this point in the history
…p with server.
  • Loading branch information
thor2016 committed Mar 4, 2024
1 parent 798c5a1 commit 72d1a8e
Show file tree
Hide file tree
Showing 4 changed files with 172 additions and 20 deletions.
108 changes: 90 additions & 18 deletions src/drivers/Qt/NetPlay.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,14 @@ struct NetPlayFrameDataHist_t
int getLast( NetPlayFrameData& out )
{
int i;
if (bufHead == 0)
const int head = bufHead;
if (head == 0)
{
i = numFrames - 1;
}
else
{
i = bufHead - 1;
i = head - 1;
}
out = data[i];

Expand All @@ -87,17 +88,18 @@ struct NetPlayFrameDataHist_t
int find( uint32_t frame, NetPlayFrameData& out )
{
int i, retval = -1;
const int head = bufHead;

if (bufHead == 0)
if (head == 0)
{
i = numFrames - 1;
}
else
{
i = bufHead - 1;
i = head - 1;
}

while (i != bufHead)
while (i != head)
{
if (data[i].frameNum == frame)
{
Expand Down Expand Up @@ -173,6 +175,10 @@ NetPlayServer::NetPlayServer(QObject *parent)

connect(consoleWindow, SIGNAL(romLoaded(void)), this, SLOT(onRomLoad(void)));
connect(consoleWindow, SIGNAL(nesResetOccurred(void)), this, SLOT(onNesReset(void)));

FCEU_WRAPPER_LOCK();
inputFrameCount = static_cast<uint32_t>(currFrameCounter);
FCEU_WRAPPER_UNLOCK();
}


Expand Down Expand Up @@ -417,6 +423,10 @@ void NetPlayServer::onRomLoad()
{
//printf("New ROM Loaded!\n");
FCEU_WRAPPER_LOCK();

inputClear();
inputFrameCount = static_cast<uint32_t>(currFrameCounter);

// New ROM has been loaded by server, signal clients to load and sync
for (auto& client : clientList )
{
Expand All @@ -430,6 +440,10 @@ void NetPlayServer::onNesReset()
{
//printf("New ROM Loaded!\n");
FCEU_WRAPPER_LOCK();

inputClear();
inputFrameCount = static_cast<uint32_t>(currFrameCounter);

// NES Reset has occurred on server, signal clients sync
for (auto& client : clientList )
{
Expand Down Expand Up @@ -663,7 +677,7 @@ void NetPlayServer::update(void)
const uint32_t maxLead = maxLeadFrames;
const uint32_t currFrame = static_cast<uint32_t>(currFrameCounter);
const uint32_t leadFrame = currFrame + maxLead;
const uint32_t lastFrame = inputFrameBack();
//const uint32_t lastFrame = inputFrameBack();
uint32_t lagFrame = 0;
uint8_t localGP[4] = { 0 };
uint8_t gpData[4] = { 0 };
Expand Down Expand Up @@ -735,7 +749,7 @@ void NetPlayServer::update(void)
}
}

hostRdyFrame = ( (currFrame > lastFrame) || (lastFrame == 0) );
hostRdyFrame = (currFrame >= inputFrameCount);

shouldRunFrame = (clientMinFrame != 0xFFFFFFFF) &&
(clientMinFrame >= lagFrame ) &&
Expand All @@ -752,6 +766,7 @@ void NetPlayServer::update(void)
clientWaitCounter = 0;
}

//printf("Host Frame: Run:%u Input:%u Last:%u\n", currFrame, inputFrameCount, lastFrame);
//printf("Client Frame: Min:%u Max:%u\n", clientMinFrame, clientMaxFrame);

if (shouldRunFrame)
Expand All @@ -760,7 +775,7 @@ void NetPlayServer::update(void)
NetPlayFrameInput inputFrame;
netPlayRunFrameReq runFrameReq;

inputFrame.frameCounter = static_cast<uint32_t>(currFrameCounter) + 1;
inputFrame.frameCounter = ++inputFrameCount;
inputFrame.ctrl[0] = gpData[0];
inputFrame.ctrl[1] = gpData[1];
inputFrame.ctrl[2] = gpData[2];
Expand All @@ -772,6 +787,13 @@ void NetPlayServer::update(void)
runFrameReq.ctrlState[2] = inputFrame.ctrl[2];
runFrameReq.ctrlState[3] = inputFrame.ctrl[3];

uint32_t catchUpThreshold = maxLead;
if (catchUpThreshold < 3)
{
catchUpThreshold = 3;
}
runFrameReq.catchUpThreshold = catchUpThreshold;

pushBackInput( inputFrame );

runFrameReq.toNetworkByteOrder();
Expand Down Expand Up @@ -1109,15 +1131,21 @@ int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgB
{
bool readReq;
netPlayMsgHdr *hdr;
const int netPlayMsgHdrSize = sizeof(netPlayMsgHdr);
constexpr int netPlayMsgHdrSize = sizeof(netPlayMsgHdr);
FCEU::timeStampRecord ts;

ts.readNew();
int bytesAvailable = sock->bytesAvailable();
readReq = bytesAvailable > 0;

readReq = sock->bytesAvailable() > 0;
//printf("Read Bytes Available: %lu %i\n", ts.toMilliSeconds(), bytesAvailable);

while (readReq)
{
if (recvMsgBytesLeft > 0)
{
readReq = (sock->bytesAvailable() >= recvMsgBytesLeft);
bytesAvailable = sock->bytesAvailable();
readReq = (bytesAvailable >= recvMsgBytesLeft);

if (readReq)
{
Expand All @@ -1135,16 +1163,18 @@ int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgB

if (recvMsgBytesLeft > 0)
{
readReq = (sock->bytesAvailable() >= recvMsgBytesLeft);
bytesAvailable = sock->bytesAvailable();
readReq = (bytesAvailable >= recvMsgBytesLeft);
}
else
{
msgCallback( userData, recvMsgBuf, recvMsgSize );
readReq = (sock->bytesAvailable() > 0);
bytesAvailable = sock->bytesAvailable();
readReq = (bytesAvailable > 0);
}
}
}
else if (sock->bytesAvailable() >= netPlayMsgHdrSize)
else if (bytesAvailable >= netPlayMsgHdrSize)
{
sock->read( recvMsgBuf, netPlayMsgHdrSize );

Expand All @@ -1166,7 +1196,8 @@ int NetPlayClient::readMessages( void (*msgCallback)( void *userData, void *msgB
{
msgCallback( userData, recvMsgBuf, recvMsgSize );
}
readReq = (sock->bytesAvailable() >= recvMsgSize);
bytesAvailable = sock->bytesAvailable();
readReq = (bytesAvailable >= recvMsgSize);
}
else
{
Expand Down Expand Up @@ -1269,10 +1300,20 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
inputFrame.ctrl[2] = msg->ctrlState[2];
inputFrame.ctrl[3] = msg->ctrlState[3];

if (inputFrame.frameCounter > inputFrameBack())
catchUpThreshold = msg->catchUpThreshold;

uint32_t lastInputFrame = inputFrameBack();
uint32_t currFrame = static_cast<uint32_t>(currFrameCounter);

if (inputFrame.frameCounter > lastInputFrame)
{
pushBackInput( inputFrame );
}
else
{
printf("Drop Frame: LastRun:%u LastInput:%u NewInput:%u\n", currFrame, lastInputFrame, inputFrame.frameCounter);
}
//printf("Run Frame: LastRun:%u LastInput:%u NewInput:%u\n", currFrame, lastInputFrame, inputFrame.frameCounter);
}
break;
case NETPLAY_PING_REQ:
Expand Down Expand Up @@ -1302,6 +1343,8 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent)
QVBoxLayout *mainLayout;
QHBoxLayout *hbox;
QGridLayout *grid;
QGroupBox *networkGroupBox;
QGroupBox *settingsGroupBox;
QPushButton *cancelButton, *startButton;
QLabel *lbl;

Expand All @@ -1310,8 +1353,18 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent)
setWindowTitle("NetPlay Host Game");

mainLayout = new QVBoxLayout();
networkGroupBox = new QGroupBox(tr("Network Setup"));
settingsGroupBox = new QGroupBox(tr("Server Settings"));
hbox = new QHBoxLayout();
grid = new QGridLayout();

mainLayout->addLayout(hbox);
hbox->addWidget(networkGroupBox);
hbox->addWidget(settingsGroupBox);

// Network Settings
networkGroupBox->setLayout(grid);

lbl = new QLabel( tr("Server Name:") );
grid->addWidget( lbl, 0, 0 );

Expand Down Expand Up @@ -1350,7 +1403,22 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent)
passwordEntry = new QLineEdit();
grid->addWidget( passwordEntry, 4, 1 );

mainLayout->addLayout(grid);
// Misc Settings
grid = new QGridLayout();
settingsGroupBox->setLayout(grid);

lbl = new QLabel( tr("Max Frame Lead:") );
frameLeadSpinBox = new QSpinBox();
frameLeadSpinBox->setRange(5,60);
frameLeadSpinBox->setValue(30);
grid->addWidget( lbl, 0, 0, 1, 1 );
grid->addWidget( frameLeadSpinBox, 0, 1, 1, 1 );

allowClientRomReqCBox = new QCheckBox(tr("Allow Client ROM Load Requests"));
grid->addWidget( allowClientRomReqCBox, 1, 0, 1, 2 );

allowClientStateReqCBox = new QCheckBox(tr("Allow Client State Load Requests"));
grid->addWidget( allowClientStateReqCBox, 2, 0, 1, 2 );

startButton = new QPushButton( tr("Start") );
startButton->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton));
Expand Down Expand Up @@ -1406,6 +1474,9 @@ void NetPlayHostDialog::onStartClicked(void)
server->setRole( playerRoleBox->currentData().toInt() );
server->sessionName = sessionNameEntry->text();
server->sessionPasswd = passwordEntry->text();
server->setMaxLeadFrames( frameLeadSpinBox->value() );
server->setAllowClientRomLoadRequest( allowClientRomReqCBox->isChecked() );
server->setAllowClientStateLoadRequest( allowClientStateReqCBox->isChecked() );

bool listenSucceeded = server->listen( QHostAddress::Any, netPort );

Expand Down Expand Up @@ -1503,6 +1574,7 @@ NetPlayJoinDialog::NetPlayJoinDialog(QWidget *parent)

passwordEntry = new QLineEdit();
passwordEntry->setMaxLength(64);
passwordEntry->setEnabled(false);
grid->addWidget( passwordEntry, 4, 1 );

mainLayout->addLayout(grid);
Expand Down Expand Up @@ -1944,7 +2016,7 @@ bool NetPlaySkipWait(void)

if (client)
{
skip = client->inputAvailable() > 1;
skip = client->inputAvailableCount() > client->catchUpThreshold;
}
return skip;
}
Expand Down
17 changes: 16 additions & 1 deletion src/drivers/Qt/NetPlay.h
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ class NetPlayServer : public QTcpServer

uint32_t getMaxLeadFrames(){ return maxLeadFrames; }
void setMaxLeadFrames(uint32_t value){ maxLeadFrames = value; }
void setAllowClientRomLoadRequest(bool value){ allowClientRomLoadReq = value; }
void setAllowClientStateLoadRequest(bool value){ allowClientStateLoadReq = value; }

void serverProcessMessage( NetPlayClient *client, void *msgBuf, size_t msgSize );

Expand All @@ -161,7 +163,9 @@ class NetPlayServer : public QTcpServer
uint32_t cycleCounter = 0;
uint32_t maxLeadFrames = 10u;
uint32_t clientWaitCounter = 0;
uint32_t inputFrameCount = 0;
bool allowClientRomLoadReq = true;
bool allowClientStateLoadReq = true;

public:
signals:
Expand Down Expand Up @@ -203,12 +207,18 @@ class NetPlayClient : public QObject
int readMessages( void (*msgCallback)( void *userData, void *msgBuf, size_t msgSize ), void *userData );
void clientProcessMessage( void *msgBuf, size_t msgSize );

size_t inputAvailable(void)
bool inputAvailable(void)
{
FCEU::autoScopedLock alock(inputMtx);
return !input.empty();
};

size_t inputAvailableCount(void)
{
FCEU::autoScopedLock alock(inputMtx);
return input.size();
};

void pushBackInput( NetPlayFrameInput &in )
{
FCEU::autoScopedLock alock(inputMtx);
Expand Down Expand Up @@ -264,6 +274,8 @@ class NetPlayClient : public QObject
bool syncOk = false;
unsigned int currentFrame = 0;
unsigned int readyFrame = 0;
unsigned int catchUpThreshold = 10;
unsigned int tailTarget = 3;
uint8_t gpData[4];

private:
Expand Down Expand Up @@ -319,6 +331,9 @@ class NetPlayHostDialog : public QDialog
QComboBox *playerRoleBox;
QLineEdit *passwordEntry;
QCheckBox *passwordRequiredCBox;
QSpinBox *frameLeadSpinBox;
QCheckBox *allowClientRomReqCBox;
QCheckBox *allowClientStateReqCBox;

static NetPlayHostDialog* instance;

Expand Down
3 changes: 2 additions & 1 deletion src/drivers/Qt/NetPlayMsgDef.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,9 +252,10 @@ struct netPlayRunFrameReq
uint32_t flags;
uint32_t frameNum;
uint8_t ctrlState[4];
uint8_t catchUpThreshold;

netPlayRunFrameReq(void)
: hdr(NETPLAY_RUN_FRAME_REQ, sizeof(netPlayRunFrameReq)), flags(0), frameNum(0)
: hdr(NETPLAY_RUN_FRAME_REQ, sizeof(netPlayRunFrameReq)), flags(0), frameNum(0), catchUpThreshold(10)
{
memset( ctrlState, 0, sizeof(ctrlState) );
}
Expand Down
Loading

0 comments on commit 72d1a8e

Please sign in to comment.