For Qt Netplay, added code to sync cheats between server and clients.

This commit is contained in:
harry 2024-04-11 06:58:43 -04:00
parent 6e698433aa
commit c97e2c9ad3
9 changed files with 254 additions and 55 deletions

View File

@ -39,8 +39,8 @@ if ( ${QT} EQUAL 6 )
message( STATUS "GUI Frontend: Qt6")
set( Qt Qt6 )
find_package( Qt6 REQUIRED COMPONENTS Widgets OpenGL OpenGLWidgets)
find_package( Qt6 REQUIRED COMPONENTS Network)
find_package( Qt6 COMPONENTS Help QUIET)
find_package( Qt6 COMPONENTS Network)
find_package( Qt6 COMPONENTS Qml)
find_package( Qt6 COMPONENTS UiTools)
add_definitions( ${Qt6Widgets_DEFINITIONS} ${Qt6Qml_DEFINITIONS} ${Qt6Network_DEFINITIONS} ${Qt6Help_DEFINITIONS} ${Qt6OpenGLWidgets_DEFINITIONS} )

View File

@ -58,6 +58,15 @@ void FCEU_CheatAddRAM(int s, uint32 A, uint8 *p)
CheatRPtrs[AB+x]=p-A;
}
// Cheat change event callback. Called whenever cheat map is changed or recalculated.
static void (*cheatsChangeEventCB)(void*) = nullptr;
static void* cheatsChangeEventUserData = nullptr;
void FCEU_SetCheatChangeEventCallback( void (*func)(void*), void* userData )
{
cheatsChangeEventCB = func;
cheatsChangeEventUserData = userData;
}
CHEATF_SUBFAST SubCheats[256];
uint32 numsubcheats = 0;
@ -132,6 +141,11 @@ void RebuildSubCheats(void)
}
FrozenAddressCount = numsubcheats; //Update the frozen address list
// Notify the system of a change
if (cheatsChangeEventCB != nullptr)
{
cheatsChangeEventCB( cheatsChangeEventUserData );
}
}
void FCEU_PowerCheats()
@ -368,12 +382,15 @@ void FCEU_FlushGameCheats(FILE *override, int nosave)
}
int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type)
int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type, int status, bool rebuild)
{
AddCheatEntry(name, addr, val, compare, 1, type);
AddCheatEntry(name, addr, val, compare, status, type);
savecheats = 1;
RebuildSubCheats();
if (rebuild)
{
RebuildSubCheats();
}
return 1;
}

View File

@ -33,6 +33,8 @@ extern int disableAutoLSCheats;
int FCEU_DisableAllCheats(void);
int FCEU_DeleteAllCheats(void);
void FCEU_SetCheatChangeEventCallback( void (*func)(void*) = nullptr, void* userData = nullptr );
struct CHEATF_SUBFAST
{
uint16 addr;

View File

@ -197,7 +197,7 @@ void FCEU_DispMessage( __FCEU_PRINTF_FORMAT const char *format, int disppos, ...
int FCEUI_DecodePAR(const char *code, int *a, int *v, int *c, int *type);
int FCEUI_DecodeGG(const char *str, int *a, int *v, int *c);
int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type);
int FCEUI_AddCheat(const char *name, uint32 addr, uint8 val, int compare, int type, int status = 1, bool rebuild = true);
int FCEUI_DelCheat(uint32 which);
int FCEUI_ToggleCheat(uint32 which);
int FCEUI_GlobalToggleCheat(int global_enable);

View File

@ -55,6 +55,7 @@
#include "../../movie.h"
#include "../../wave.h"
#include "../../state.h"
#include "../../cheat.h"
#include "../../profiler.h"
#include "../../version.h"
#include "common/os_utils.h"
@ -285,6 +286,19 @@ consoleWin_t::consoleWin_t(QWidget *parent)
}
};
FCEUSS_SetLoadCallback( stateLoadCallback );
// Register Cheat Change Callback
auto cheatChangeCallback = []( void* userData )
{
FCEU_UNUSED(userData);
//printf("Cheats Changed Event!\n");
if (consoleWindow != nullptr)
{
emit consoleWindow->cheatsChanged();
}
};
FCEU_SetCheatChangeEventCallback( cheatChangeCallback, this );
}
consoleWin_t::~consoleWin_t(void)

View File

@ -332,6 +332,7 @@ class consoleWin_t : public QMainWindow
void stateLoaded(void);
void nesResetOccurred(void);
void pauseToggled(bool state);
void cheatsChanged(void);
public slots:
void openDebugWindow(void);

View File

@ -25,6 +25,7 @@
#include "../../fceu.h"
#include "../../cart.h"
#include "../../cheat.h"
#include "../../state.h"
#include "../../movie.h"
#include "../../debug.h"
@ -182,6 +183,7 @@ NetPlayServer::NetPlayServer(QObject *parent)
connect(consoleWindow, SIGNAL(romUnload(void)), this, SLOT(onRomUnload(void)));
connect(consoleWindow, SIGNAL(stateLoaded(void)), this, SLOT(onStateLoad(void)));
connect(consoleWindow, SIGNAL(nesResetOccurred(void)), this, SLOT(onNesReset(void)));
connect(consoleWindow, SIGNAL(cheatsChanged(void)), this, SLOT(onCheatsChanged(void)));
connect(consoleWindow, SIGNAL(pauseToggled(bool)), this, SLOT(onPauseToggled(bool)));
FCEU_WRAPPER_LOCK();
@ -396,12 +398,43 @@ int NetPlayServer::sendRomLoadReq( NetPlayClient *client )
return 0;
}
//-----------------------------------------------------------------------------
struct NetPlayServerCheatQuery
{
int numLoaded = 0;
netPlayLoadStateResp::CheatData data[netPlayLoadStateResp::MaxCheats];
};
static int serverActiveCheatListCB(const char *name, uint32 a, uint8 v, int c, int s, int type, void *data)
{
NetPlayServerCheatQuery* query = static_cast<NetPlayServerCheatQuery*>(data);
const int i = query->numLoaded;
if (i < netPlayLoadStateResp::MaxCheats)
{
auto& cheat = query->data[i];
cheat.addr = a;
cheat.val = v;
cheat.cmp = c;
cheat.type = type;
cheat.stat = s;
Strlcpy( cheat.name, name, sizeof(cheat.name));
query->numLoaded++;
}
return 1;
}
//-----------------------------------------------------------------------------
int NetPlayServer::sendStateSyncReq( NetPlayClient *client )
{
EMUFILE_MEMORY em;
int compressionLevel = 1;
int numCtrlFrames = 0, numCheats = 0, compressionLevel = 1;
static constexpr size_t maxBytesPerWrite = 32 * 1024;
netPlayLoadStateResp resp;
netPlayLoadStateResp::CtrlData ctrlData[netPlayLoadStateResp::MaxCtrlFrames];
NetPlayServerCheatQuery cheatQuery;
if ( GameInfo == nullptr )
{
@ -409,9 +442,9 @@ int NetPlayServer::sendStateSyncReq( NetPlayClient *client )
}
FCEUSS_SaveMS( &em, compressionLevel );
resp.hdr.msgSize += em.size();
resp.stateSize = em.size();
resp.opsCrc32 = opsCrc32;
resp.romCrc32 = romCrc32;
NetPlayFrameData lastFrameData;
netPlayFrameData.getLast( lastFrameData );
@ -428,20 +461,34 @@ int NetPlayServer::sendStateSyncReq( NetPlayClient *client )
{
if (i < netPlayLoadStateResp::MaxCtrlFrames)
{
resp.ctrlData[i].frameNum = inputFrame.frameCounter;
resp.ctrlData[i].ctrlState[0] = inputFrame.ctrl[0];
resp.ctrlData[i].ctrlState[1] = inputFrame.ctrl[1];
resp.ctrlData[i].ctrlState[2] = inputFrame.ctrl[2];
resp.ctrlData[i].ctrlState[3] = inputFrame.ctrl[3];
ctrlData[i].frameNum = netPlayByteSwap(inputFrame.frameCounter);
ctrlData[i].ctrlState[0] = inputFrame.ctrl[0];
ctrlData[i].ctrlState[1] = inputFrame.ctrl[1];
ctrlData[i].ctrlState[2] = inputFrame.ctrl[2];
ctrlData[i].ctrlState[3] = inputFrame.ctrl[3];
i++;
}
}
resp.numCtrlFrames = i;
resp.numCtrlFrames = numCtrlFrames = i;
}
FCEUI_ListCheats(::serverActiveCheatListCB, (void *)&cheatQuery);
resp.numCheats = numCheats = cheatQuery.numLoaded;
resp.calcTotalSize();
printf("Sending ROM Sync Request: %zu\n", em.size());
sendMsg( client, &resp, sizeof(netPlayLoadStateResp), [&resp]{ resp.toNetworkByteOrder(); } );
if (numCtrlFrames > 0)
{
sendMsg( client, ctrlData, numCtrlFrames * sizeof(netPlayLoadStateResp::CtrlData) );
}
if (numCheats > 0)
{
sendMsg( client, &cheatQuery.data, numCheats * sizeof(netPlayLoadStateResp::CheatData) );
}
//sendMsg( client, em.buf(), em.size() );
const unsigned char* bufPtr = em.buf();
@ -583,6 +630,7 @@ void NetPlayServer::onRomLoad()
//-----------------------------------------------------------------------------
void NetPlayServer::onRomUnload()
{
//printf("ROM UnLoaded!\n");
netPlayMsgHdr unloadMsg(NETPLAY_UNLOAD_ROM_REQ);
romCrc32 = 0;
@ -642,6 +690,32 @@ void NetPlayServer::onNesReset()
FCEU_WRAPPER_UNLOCK();
}
//-----------------------------------------------------------------------------
void NetPlayServer::onCheatsChanged()
{
//printf("NES Cheats Event!\n");
if (romCrc32 == 0)
{
return;
}
FCEU_WRAPPER_LOCK();
opsCrc32 = 0;
netPlayFrameData.reset();
inputClear();
inputFrameCount = static_cast<uint32_t>(currFrameCounter);
sendPauseAll();
// NES Reset has occurred on server, signal clients sync
for (auto& client : clientList )
{
//sendRomLoadReq( client );
sendStateSyncReq( client );
}
FCEU_WRAPPER_UNLOCK();
}
//-----------------------------------------------------------------------------
void NetPlayServer::onPauseToggled( bool isPaused )
{
if (isPaused)
@ -1568,6 +1642,11 @@ int NetPlayClient::requestStateLoad(EMUFILE *is)
{
printf("Read Error\n");
}
if (currCartInfo != nullptr)
{
resp.romCrc32 = romCrc32;
}
printf("Sending Client ROM Sync Request: %u\n", resp.stateSize);
resp.toNetworkByteOrder();
@ -1893,6 +1972,7 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
netPlayLoadStateResp* msg = static_cast<netPlayLoadStateResp*>(msgBuf);
msg->toHostByteOrder();
const bool romMatch = (msg->romCrc32 = romCrc32);
char *stateData = msg->stateDataBuf();
const uint32_t stateDataSize = msg->stateDataSize();
@ -1901,34 +1981,64 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
EMUFILE_MEMORY em( stateData, stateDataSize );
FCEU_WRAPPER_LOCK();
serverRequestedStateLoad = true;
FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP );
serverRequestedStateLoad = false;
opsCrc32 = msg->opsCrc32;
netPlayFrameData.reset();
bool dataValid = romMatch;
NetPlayFrameData data;
data.frameNum = msg->lastFrame.num;
data.opsCrc32 = msg->lastFrame.opsCrc32;
data.ramCrc32 = msg->lastFrame.ramCrc32;
netPlayFrameData.push( data );
inputClear();
const int numInputFrames = msg->numCtrlFrames;
for (int i=0; i<numInputFrames; i++)
if (dataValid)
{
NetPlayFrameInput inputFrame;
serverRequestedStateLoad = true;
FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP );
serverRequestedStateLoad = false;
inputFrame.frameCounter = msg->ctrlData[i].frameNum;
inputFrame.ctrl[0] = msg->ctrlData[i].ctrlState[0];
inputFrame.ctrl[1] = msg->ctrlData[i].ctrlState[1];
inputFrame.ctrl[2] = msg->ctrlData[i].ctrlState[2];
inputFrame.ctrl[3] = msg->ctrlData[i].ctrlState[3];
opsCrc32 = msg->opsCrc32;
netPlayFrameData.reset();
pushBackInput( inputFrame );
NetPlayFrameData data;
data.frameNum = msg->lastFrame.num;
data.opsCrc32 = msg->lastFrame.opsCrc32;
data.ramCrc32 = msg->lastFrame.ramCrc32;
netPlayFrameData.push( data );
inputClear();
const int numInputFrames = msg->numCtrlFrames;
for (int i=0; i<numInputFrames; i++)
{
NetPlayFrameInput inputFrame;
auto ctrlData = msg->ctrlDataBuf();
ctrlData[i].toHostByteOrder();
inputFrame.frameCounter = ctrlData[i].frameNum;
inputFrame.ctrl[0] = ctrlData[i].ctrlState[0];
inputFrame.ctrl[1] = ctrlData[i].ctrlState[1];
inputFrame.ctrl[2] = ctrlData[i].ctrlState[2];
inputFrame.ctrl[3] = ctrlData[i].ctrlState[3];
pushBackInput( inputFrame );
}
const int numCheats = msg->numCheats;
if (numCheats > 0)
{
const int lastCheatIdx = numCheats - 1;
FCEU_FlushGameCheats(0, 1);
for (int i=0; i<numCheats; i++)
{
auto cheatBuf = msg->cheatDataBuf();
auto& cheatData = cheatBuf[i];
// Set cheat rebuild flag on last item.
bool lastItem = (i == lastCheatIdx);
FCEUI_AddCheat( cheatData.name, cheatData.addr, cheatData.val, cheatData.cmp, cheatData.type, cheatData.stat, lastItem );
}
}
else
{
FCEU_DeleteAllCheats();
}
}
FCEU_WRAPPER_UNLOCK();
@ -3009,10 +3119,11 @@ uint64_t netPlayByteSwap(uint64_t in)
//----------------------------------------------------------------------------
uint32_t netPlayCalcRamChkSum()
{
constexpr int ramSize = 0x800;
uint32_t crc = 0;
uint8_t ram[256];
uint8_t ram[ramSize];
for (int i=0; i<256; i++)
for (int i=0; i<ramSize; i++)
{
ram[i] = GetMem(i);
}

View File

@ -190,6 +190,7 @@ class NetPlayServer : public QTcpServer
void onStateLoad(void);
void onNesReset(void);
void onPauseToggled(bool);
void onCheatsChanged(void);
void processClientRomLoadRequests(void);
void processClientStateLoadRequests(void);
};

View File

@ -262,7 +262,10 @@ struct netPlayLoadStateResp
netPlayMsgHdr hdr;
uint32_t stateSize;
uint32_t numCtrlFrames;
uint32_t numCheats;
uint32_t opsCrc32;
uint32_t romCrc32;
struct {
uint32_t num = 0;
@ -270,57 +273,107 @@ struct netPlayLoadStateResp
uint32_t ramCrc32 = 0;
} lastFrame;
uint32_t numCtrlFrames;
static constexpr int MaxCtrlFrames = 10;
static constexpr int MaxCheats = 64;
struct {
struct CtrlData
{
uint32_t frameNum = 0;
uint8_t ctrlState[4] = {0};
} ctrlData[MaxCtrlFrames];
void toHostByteOrder()
{
frameNum = netPlayByteSwap(frameNum);
}
void toNetworkByteOrder()
{
frameNum = netPlayByteSwap(frameNum);
}
};
struct CheatData
{
uint16_t addr = 0;
uint8_t val = 0;
int8_t cmp = -1; /* -1 for no compare. */
int8_t type = 0; /* 0 for replace, 1 for substitute(GG). */
int8_t stat = 0;
char name[64] = {0};
void toHostByteOrder()
{
addr = netPlayByteSwap(addr);
}
void toNetworkByteOrder()
{
addr = netPlayByteSwap(addr);
}
};
netPlayLoadStateResp(void)
: hdr(NETPLAY_SYNC_STATE_RESP, sizeof(netPlayLoadStateResp)),
stateSize(0), opsCrc32(0), numCtrlFrames(0)
stateSize(0), numCtrlFrames(0), numCheats(0), opsCrc32(0), romCrc32(0)
{
}
size_t calcTotalSize()
{
size_t size = sizeof(netPlayLoadStateResp) +
(numCtrlFrames * sizeof(CtrlData) ) +
(numCheats * sizeof(CheatData)) +
stateSize;
hdr.msgSize = size;
return size;
}
void toHostByteOrder()
{
hdr.toHostByteOrder();
stateSize = netPlayByteSwap(stateSize);
numCtrlFrames = netPlayByteSwap(numCtrlFrames);
numCheats = netPlayByteSwap(numCheats);
opsCrc32 = netPlayByteSwap(opsCrc32);
romCrc32 = netPlayByteSwap(romCrc32);
lastFrame.num = netPlayByteSwap(lastFrame.num);
lastFrame.opsCrc32 = netPlayByteSwap(lastFrame.opsCrc32);
lastFrame.ramCrc32 = netPlayByteSwap(lastFrame.ramCrc32);
numCtrlFrames = netPlayByteSwap(numCtrlFrames);
for (int i=0; i<MaxCtrlFrames; i++)
{
ctrlData[i].frameNum = netPlayByteSwap(ctrlData[i].frameNum);
}
}
void toNetworkByteOrder()
{
hdr.toNetworkByteOrder();
stateSize = netPlayByteSwap(stateSize);
numCtrlFrames = netPlayByteSwap(numCtrlFrames);
numCheats = netPlayByteSwap(numCheats);
opsCrc32 = netPlayByteSwap(opsCrc32);
romCrc32 = netPlayByteSwap(romCrc32);
lastFrame.num = netPlayByteSwap(lastFrame.num);
lastFrame.opsCrc32 = netPlayByteSwap(lastFrame.opsCrc32);
lastFrame.ramCrc32 = netPlayByteSwap(lastFrame.ramCrc32);
numCtrlFrames = netPlayByteSwap(numCtrlFrames);
}
for (int i=0; i<MaxCtrlFrames; i++)
{
ctrlData[i].frameNum = netPlayByteSwap(ctrlData[i].frameNum);
}
CtrlData* ctrlDataBuf()
{
uintptr_t buf = ((uintptr_t)this) + sizeof(netPlayLoadStateResp);
return (CtrlData*)buf;
}
CheatData* cheatDataBuf()
{
uintptr_t buf = ((uintptr_t)this) + sizeof(netPlayLoadStateResp) +
(numCtrlFrames * sizeof(CtrlData));
return (CheatData*)buf;
}
char* stateDataBuf()
{
uintptr_t buf = ((uintptr_t)this) + sizeof(netPlayLoadStateResp);
uintptr_t buf = ((uintptr_t)this) + sizeof(netPlayLoadStateResp) +
(numCtrlFrames * sizeof(CtrlData)) + (numCheats * sizeof(CheatData));
return (char*)buf;
}