For Qt Netplay, added code to sync cheats between server and clients.
This commit is contained in:
parent
6e698433aa
commit
c97e2c9ad3
|
@ -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} )
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,6 +1981,11 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
|
|||
EMUFILE_MEMORY em( stateData, stateDataSize );
|
||||
|
||||
FCEU_WRAPPER_LOCK();
|
||||
|
||||
bool dataValid = romMatch;
|
||||
|
||||
if (dataValid)
|
||||
{
|
||||
serverRequestedStateLoad = true;
|
||||
FCEUSS_LoadFP( &em, SSLOADPARAM_NOBACKUP );
|
||||
serverRequestedStateLoad = false;
|
||||
|
@ -1921,15 +2006,40 @@ void NetPlayClient::clientProcessMessage( void *msgBuf, size_t msgSize )
|
|||
for (int i=0; i<numInputFrames; i++)
|
||||
{
|
||||
NetPlayFrameInput inputFrame;
|
||||
auto ctrlData = msg->ctrlDataBuf();
|
||||
|
||||
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];
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue