Added app version check between netplay host and clients to ensure interface compatibility.

This commit is contained in:
harry 2024-03-23 19:01:33 -04:00
parent cc234ae04b
commit cc61b7b5ab
4 changed files with 96 additions and 19 deletions

View File

@ -443,6 +443,9 @@ void NetPlayServer::onRomLoad()
//printf("New ROM Loaded!\n"); //printf("New ROM Loaded!\n");
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
opsCrc32 = 0;
netPlayFrameData.reset();
inputClear(); inputClear();
inputFrameCount = static_cast<uint32_t>(currFrameCounter); inputFrameCount = static_cast<uint32_t>(currFrameCounter);
@ -460,13 +463,16 @@ void NetPlayServer::onStateLoad()
//printf("New State Loaded!\n"); //printf("New State Loaded!\n");
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
opsCrc32 = 0;
netPlayFrameData.reset();
inputClear(); inputClear();
inputFrameCount = static_cast<uint32_t>(currFrameCounter); inputFrameCount = static_cast<uint32_t>(currFrameCounter);
// New State has been loaded by server, signal clients to load and sync // New State has been loaded by server, signal clients to load and sync
for (auto& client : clientList ) for (auto& client : clientList )
{ {
//sendRomLoadReq( client ); sendRomLoadReq( client );
sendStateSyncReq( client ); sendStateSyncReq( client );
} }
FCEU_WRAPPER_UNLOCK(); FCEU_WRAPPER_UNLOCK();
@ -477,12 +483,16 @@ void NetPlayServer::onNesReset()
//printf("NES Reset Event!\n"); //printf("NES Reset Event!\n");
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
opsCrc32 = 0;
netPlayFrameData.reset();
inputClear(); inputClear();
inputFrameCount = static_cast<uint32_t>(currFrameCounter); inputFrameCount = static_cast<uint32_t>(currFrameCounter);
// NES Reset has occurred on server, signal clients sync // NES Reset has occurred on server, signal clients sync
for (auto& client : clientList ) for (auto& client : clientList )
{ {
sendRomLoadReq( client );
sendStateSyncReq( client ); sendStateSyncReq( client );
} }
FCEU_WRAPPER_UNLOCK(); FCEU_WRAPPER_UNLOCK();
@ -530,7 +540,27 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
msg->toHostByteOrder(); msg->toHostByteOrder();
printf("Authorize: Player: %i Passwd: %s\n", msg->playerId, msg->pswd); printf("Authorize: Player: %i Passwd: %s\n", msg->playerId, msg->pswd);
if (sessionPasswd.isEmpty()) bool version_chk_ok = true;
if (enforceAppVersionCheck)
{
version_chk_ok = (msg->appVersionMajor == FCEU_VERSION_MAJOR) &&
(msg->appVersionMinor == FCEU_VERSION_MINOR) &&
(msg->appVersionPatch == FCEU_VERSION_PATCH);
}
if (!version_chk_ok)
{
netPlayTextMsg<128> errorMsg(NETPLAY_ERROR_MSG);
errorMsg.setFlag(netPlayTextMsgFlags::DISCONNECT);
errorMsg.setFlag(netPlayTextMsgFlags::ERROR);
errorMsg.printf("Client/Host Version Mismatch:\nHost version is %i.%i.%i\nClient version is %i.%i.%i",
FCEU_VERSION_MAJOR, FCEU_VERSION_MINOR, FCEU_VERSION_PATCH,
msg->appVersionMajor, msg->appVersionMinor, msg->appVersionPatch);
sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } );
client->flushData();
}
else if (sessionPasswd.isEmpty())
{ {
authentication_passed = true; authentication_passed = true;
} }
@ -542,6 +572,7 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
{ {
netPlayTextMsg<128> errorMsg(NETPLAY_ERROR_MSG); netPlayTextMsg<128> errorMsg(NETPLAY_ERROR_MSG);
errorMsg.setFlag(netPlayTextMsgFlags::DISCONNECT); errorMsg.setFlag(netPlayTextMsgFlags::DISCONNECT);
errorMsg.setFlag(netPlayTextMsgFlags::ERROR);
errorMsg.printf("Invalid Password"); errorMsg.printf("Invalid Password");
sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } ); sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } );
client->flushData(); client->flushData();
@ -564,6 +595,7 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
{ {
netPlayTextMsg<128> errorMsg(NETPLAY_ERROR_MSG); netPlayTextMsg<128> errorMsg(NETPLAY_ERROR_MSG);
errorMsg.setFlag(netPlayTextMsgFlags::DISCONNECT); errorMsg.setFlag(netPlayTextMsgFlags::DISCONNECT);
errorMsg.setFlag(netPlayTextMsgFlags::ERROR);
errorMsg.printf("Player %i role is not available", msg->playerId+1); errorMsg.printf("Player %i role is not available", msg->playerId+1);
sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } ); sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } );
client->flushData(); client->flushData();
@ -693,6 +725,7 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
else else
{ {
netPlayTextMsg<128> errorMsg(NETPLAY_ERROR_MSG); netPlayTextMsg<128> errorMsg(NETPLAY_ERROR_MSG);
errorMsg.setFlag(netPlayTextMsgFlags::WARNING);
errorMsg.printf("Host is rejected ROMs load request"); errorMsg.printf("Host is rejected ROMs load request");
sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } ); sendMsg( client, &errorMsg, errorMsg.hdr.msgSize, [&errorMsg]{ errorMsg.toNetworkByteOrder(); } );
} }
@ -726,14 +759,10 @@ void NetPlayServer::serverProcessMessage( NetPlayClient *client, void *msgBuf, s
FCEU_WRAPPER_LOCK(); FCEU_WRAPPER_LOCK();
serverRequestedStateLoad = true; serverRequestedStateLoad = true;
// Clients will be resync'd during this load call.
FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP ); FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP );
serverRequestedStateLoad = false; serverRequestedStateLoad = false;
FCEU_WRAPPER_UNLOCK(); FCEU_WRAPPER_UNLOCK();
opsCrc32 = 0;
netPlayFrameData.reset();
inputClear();
resyncAllClients();
} }
} }
break; break;
@ -1347,11 +1376,28 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
switch (msgId) switch (msgId)
{ {
case NETPLAY_INFO_MSG:
{
auto *msg = static_cast<netPlayTextMsg<256>*>(msgBuf);
msg->toHostByteOrder();
FCEU_printf("NetPlay Info: %s\n", msg->getBuffer());
if (msg->isFlagSet(netPlayTextMsgFlags::DISCONNECT))
{
sock->disconnectFromHost();
}
FCEU_DispMessage("NetPlay Errors... check message log",0);
}
break;
case NETPLAY_ERROR_MSG: case NETPLAY_ERROR_MSG:
{ {
auto *msg = static_cast<netPlayTextMsg<256>*>(msgBuf); auto *msg = static_cast<netPlayTextMsg<256>*>(msgBuf);
msg->toHostByteOrder(); msg->toHostByteOrder();
FCEU_printf("NetPlay Error: 0x%X %s\n", msg->code, msg->getBuffer()); FCEU_printf("NetPlay Error: %s\n", msg->getBuffer());
QString msgBoxTxt = tr("Host has replied with an error:\n\n");
msgBoxTxt += tr(msg->getBuffer());
QMessageBox::critical( consoleWindow, tr("NetPlay Error"), msgBoxTxt, QMessageBox::Ok );
if (msg->isFlagSet(netPlayTextMsgFlags::DISCONNECT)) if (msg->isFlagSet(netPlayTextMsgFlags::DISCONNECT))
{ {
@ -1547,21 +1593,28 @@ NetPlayHostDialog::NetPlayHostDialog(QWidget *parent)
grid->addWidget( lbl, 0, 0, 1, 1 ); grid->addWidget( lbl, 0, 0, 1, 1 );
grid->addWidget( frameLeadSpinBox, 0, 1, 1, 1 ); grid->addWidget( frameLeadSpinBox, 0, 1, 1, 1 );
bool enforceAppVersionChk = false;
enforceAppVersionChkCBox = new QCheckBox(tr("Enforce Client Versions Match"));
grid->addWidget( enforceAppVersionChkCBox, 1, 0, 1, 2 );
g_config->getOption("SDL.NetPlayHostEnforceAppVersionChk", &enforceAppVersionChk);
enforceAppVersionChkCBox->setChecked(enforceAppVersionChk);
bool romLoadReqEna = false; bool romLoadReqEna = false;
allowClientRomReqCBox = new QCheckBox(tr("Allow Client ROM Load Requests")); allowClientRomReqCBox = new QCheckBox(tr("Allow Client ROM Load Requests"));
grid->addWidget( allowClientRomReqCBox, 1, 0, 1, 2 ); grid->addWidget( allowClientRomReqCBox, 2, 0, 1, 2 );
g_config->getOption("SDL.NetPlayHostAllowClientRomLoadReq", &romLoadReqEna); g_config->getOption("SDL.NetPlayHostAllowClientRomLoadReq", &romLoadReqEna);
allowClientRomReqCBox->setChecked(romLoadReqEna); allowClientRomReqCBox->setChecked(romLoadReqEna);
bool stateLoadReqEna = false; bool stateLoadReqEna = false;
allowClientStateReqCBox = new QCheckBox(tr("Allow Client State Load Requests")); allowClientStateReqCBox = new QCheckBox(tr("Allow Client State Load Requests"));
grid->addWidget( allowClientStateReqCBox, 2, 0, 1, 2 ); grid->addWidget( allowClientStateReqCBox, 3, 0, 1, 2 );
g_config->getOption("SDL.NetPlayHostAllowClientStateLoadReq", &stateLoadReqEna); g_config->getOption("SDL.NetPlayHostAllowClientStateLoadReq", &stateLoadReqEna);
allowClientStateReqCBox->setChecked(stateLoadReqEna); allowClientStateReqCBox->setChecked(stateLoadReqEna);
connect(passwordRequiredCBox, SIGNAL(stateChanged(int)), this, SLOT(passwordRequiredChanged(int))); connect(passwordRequiredCBox, SIGNAL(stateChanged(int)), this, SLOT(passwordRequiredChanged(int)));
connect(allowClientRomReqCBox, SIGNAL(stateChanged(int)), this, SLOT(allowClientRomReqChanged(int))); connect(allowClientRomReqCBox, SIGNAL(stateChanged(int)), this, SLOT(allowClientRomReqChanged(int)));
connect(allowClientStateReqCBox, SIGNAL(stateChanged(int)), this, SLOT(allowClientStateReqChanged(int))); connect(allowClientStateReqCBox, SIGNAL(stateChanged(int)), this, SLOT(allowClientStateReqChanged(int)));
connect(enforceAppVersionChkCBox, SIGNAL(stateChanged(int)), this, SLOT(enforceAppVersionChkChanged(int)));
startButton = new QPushButton( tr("Start") ); startButton = new QPushButton( tr("Start") );
startButton->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton)); startButton->setIcon(style()->standardIcon(QStyle::SP_DialogApplyButton));
@ -1609,11 +1662,19 @@ void NetPlayHostDialog::passwordRequiredChanged(int state)
void NetPlayHostDialog::allowClientRomReqChanged(int state) void NetPlayHostDialog::allowClientRomReqChanged(int state)
{ {
g_config->setOption("SDL.NetPlayHostAllowClientRomLoadReq", state != Qt::Unchecked); g_config->setOption("SDL.NetPlayHostAllowClientRomLoadReq", state != Qt::Unchecked);
g_config->save();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void NetPlayHostDialog::allowClientStateReqChanged(int state) void NetPlayHostDialog::allowClientStateReqChanged(int state)
{ {
g_config->setOption("SDL.NetPlayHostAllowClientStateLoadReq", state != Qt::Unchecked); g_config->setOption("SDL.NetPlayHostAllowClientStateLoadReq", state != Qt::Unchecked);
g_config->save();
}
//-----------------------------------------------------------------------------
void NetPlayHostDialog::enforceAppVersionChkChanged(int state)
{
g_config->setOption("SDL.NetPlayHostEnforceAppVersionChk", state != Qt::Unchecked);
g_config->save();
} }
//----------------------------------------------------------------------------- //-----------------------------------------------------------------------------
void NetPlayHostDialog::onStartClicked(void) void NetPlayHostDialog::onStartClicked(void)
@ -1632,6 +1693,7 @@ void NetPlayHostDialog::onStartClicked(void)
server->setRole( playerRoleBox->currentData().toInt() ); server->setRole( playerRoleBox->currentData().toInt() );
server->sessionName = sessionNameEntry->text(); server->sessionName = sessionNameEntry->text();
server->setMaxLeadFrames( frameLeadSpinBox->value() ); server->setMaxLeadFrames( frameLeadSpinBox->value() );
server->setEnforceAppVersionCheck( enforceAppVersionChkCBox->isChecked() );
server->setAllowClientRomLoadRequest( allowClientRomReqCBox->isChecked() ); server->setAllowClientRomLoadRequest( allowClientRomReqCBox->isChecked() );
server->setAllowClientStateLoadRequest( allowClientStateReqCBox->isChecked() ); server->setAllowClientStateLoadRequest( allowClientStateReqCBox->isChecked() );

View File

@ -139,6 +139,7 @@ class NetPlayServer : public QTcpServer
uint32_t getMaxLeadFrames(){ return maxLeadFrames; } uint32_t getMaxLeadFrames(){ return maxLeadFrames; }
void setMaxLeadFrames(uint32_t value){ maxLeadFrames = value; } void setMaxLeadFrames(uint32_t value){ maxLeadFrames = value; }
void setEnforceAppVersionCheck(bool value){ enforceAppVersionCheck = value; }
void setAllowClientRomLoadRequest(bool value){ allowClientRomLoadReq = value; } void setAllowClientRomLoadRequest(bool value){ allowClientRomLoadReq = value; }
void setAllowClientStateLoadRequest(bool value){ allowClientStateLoadReq = value; } void setAllowClientStateLoadRequest(bool value){ allowClientStateLoadReq = value; }
@ -164,6 +165,7 @@ class NetPlayServer : public QTcpServer
uint32_t maxLeadFrames = 10u; uint32_t maxLeadFrames = 10u;
uint32_t clientWaitCounter = 0; uint32_t clientWaitCounter = 0;
uint32_t inputFrameCount = 0; uint32_t inputFrameCount = 0;
bool enforceAppVersionCheck = true;
bool allowClientRomLoadReq = false; bool allowClientRomLoadReq = false;
bool allowClientStateLoadReq = false; bool allowClientStateLoadReq = false;
@ -334,6 +336,7 @@ protected:
QLineEdit *passwordEntry; QLineEdit *passwordEntry;
QCheckBox *passwordRequiredCBox; QCheckBox *passwordRequiredCBox;
QSpinBox *frameLeadSpinBox; QSpinBox *frameLeadSpinBox;
QCheckBox *enforceAppVersionChkCBox;
QCheckBox *allowClientRomReqCBox; QCheckBox *allowClientRomReqCBox;
QCheckBox *allowClientStateReqCBox; QCheckBox *allowClientStateReqCBox;
@ -345,6 +348,7 @@ public slots:
void passwordRequiredChanged(int state); void passwordRequiredChanged(int state);
void allowClientRomReqChanged(int state); void allowClientRomReqChanged(int state);
void allowClientStateReqChanged(int state); void allowClientStateReqChanged(int state);
void enforceAppVersionChkChanged(int state);
}; };
class NetPlayJoinDialog : public QDialog class NetPlayJoinDialog : public QDialog

View File

@ -4,6 +4,7 @@
#include <string.h> #include <string.h>
#include <stdint.h> #include <stdint.h>
#include "version.h"
// Network Byte Swap Functions // Network Byte Swap Functions
uint16_t netPlayByteSwap(uint16_t); uint16_t netPlayByteSwap(uint16_t);
@ -92,12 +93,17 @@ struct netPlayAuthResp
{ {
netPlayMsgHdr hdr; netPlayMsgHdr hdr;
uint16_t appVersionMajor;
uint16_t appVersionMinor;
uint32_t appVersionPatch;
char playerId; char playerId;
char userName[64]; char userName[64];
char pswd[72]; char pswd[72];
netPlayAuthResp(void) netPlayAuthResp(void)
: hdr(NETPLAY_AUTH_RESP, sizeof(netPlayAuthResp)), playerId(NETPLAY_SPECTATOR) : hdr(NETPLAY_AUTH_RESP, sizeof(netPlayAuthResp)),
appVersionMajor(FCEU_VERSION_MAJOR), appVersionMinor(FCEU_VERSION_MINOR), appVersionPatch(FCEU_VERSION_PATCH),
playerId(NETPLAY_SPECTATOR)
{ {
memset(pswd, 0, sizeof(pswd)); memset(pswd, 0, sizeof(pswd));
} }
@ -105,19 +111,26 @@ struct netPlayAuthResp
void toHostByteOrder() void toHostByteOrder()
{ {
hdr.toHostByteOrder(); hdr.toHostByteOrder();
appVersionMajor = netPlayByteSwap(appVersionMajor);
appVersionMinor = netPlayByteSwap(appVersionMinor);
appVersionPatch = netPlayByteSwap(appVersionPatch);
} }
void toNetworkByteOrder() void toNetworkByteOrder()
{ {
hdr.toNetworkByteOrder(); hdr.toNetworkByteOrder();
appVersionMajor = netPlayByteSwap(appVersionMajor);
appVersionMinor = netPlayByteSwap(appVersionMinor);
appVersionPatch = netPlayByteSwap(appVersionPatch);
} }
}; };
struct netPlayTextMsgFlags struct netPlayTextMsgFlags
{ {
static const uint32_t DISCONNECT = 0x00000001; static const uint32_t DISCONNECT = 0x00000001;
static const uint32_t WARNING = 0x00000002; static const uint32_t ERROR = 0x00000002;
static const uint32_t INFO = 0x00000004; static const uint32_t WARNING = 0x00000004;
static const uint32_t INFO = 0x00000008;
}; };
template <size_t N=8> template <size_t N=8>
@ -125,12 +138,11 @@ struct netPlayTextMsg
{ {
netPlayMsgHdr hdr; netPlayMsgHdr hdr;
unsigned short code; uint32_t flags;
unsigned short flags; uint16_t dataSize;
unsigned short dataSize;
netPlayTextMsg(int type) netPlayTextMsg(int type)
: hdr(type, sizeof(netPlayTextMsg)), code(0), flags(0), dataSize(0) : hdr(type, sizeof(netPlayTextMsg)), flags(0), dataSize(0)
{ {
hdr.msgSize = sizeof(*this) - N + 1; hdr.msgSize = sizeof(*this) - N + 1;
memset(data, 0, N); memset(data, 0, N);
@ -202,7 +214,6 @@ struct netPlayTextMsg
void toHostByteOrder() void toHostByteOrder()
{ {
hdr.toHostByteOrder(); hdr.toHostByteOrder();
code = netPlayByteSwap(code);
flags = netPlayByteSwap(flags); flags = netPlayByteSwap(flags);
dataSize = netPlayByteSwap(dataSize); dataSize = netPlayByteSwap(dataSize);
} }
@ -210,7 +221,6 @@ struct netPlayTextMsg
void toNetworkByteOrder() void toNetworkByteOrder()
{ {
hdr.toNetworkByteOrder(); hdr.toNetworkByteOrder();
code = netPlayByteSwap(code);
flags = netPlayByteSwap(flags); flags = netPlayByteSwap(flags);
dataSize = netPlayByteSwap(dataSize); dataSize = netPlayByteSwap(dataSize);
} }

View File

@ -635,6 +635,7 @@ InitConfig()
config->addOption("players", "SDL.NetworkPlayers", 1); config->addOption("players", "SDL.NetworkPlayers", 1);
config->addOption("SDL.NetPlayHostAllowClientRomLoadReq", 0); config->addOption("SDL.NetPlayHostAllowClientRomLoadReq", 0);
config->addOption("SDL.NetPlayHostAllowClientStateLoadReq", 0); config->addOption("SDL.NetPlayHostAllowClientStateLoadReq", 0);
config->addOption("SDL.NetPlayHostEnforceAppVersionChk", 1);
// input configuration options // input configuration options
config->addOption("input1", "SDL.Input.0", "GamePad.0"); config->addOption("input1", "SDL.Input.0", "GamePad.0");