Merge pull request #2973 from HeatXD/netplay_dev
Upstream Netplay Changes
This commit is contained in:
commit
0c3f755b80
|
@ -196,8 +196,8 @@ typedef struct {
|
|||
} chat;
|
||||
struct {
|
||||
int nFrameOfDesync;
|
||||
uint16_t ourCheckSum;
|
||||
uint16_t remoteChecksum;
|
||||
uint32_t ourCheckSum;
|
||||
uint32_t remoteChecksum;
|
||||
} desync;
|
||||
} u;
|
||||
} GGPOEvent;
|
||||
|
@ -248,7 +248,7 @@ typedef struct {
|
|||
* free_buffer - Frees a game state allocated in save_game_state. You
|
||||
* should deallocate the memory contained in the buffer.
|
||||
*/
|
||||
void (__cdecl *free_buffer)(void* context, void *buffer);
|
||||
void (__cdecl *free_buffer)(void* context, void *buffer, int frame);
|
||||
|
||||
/*
|
||||
* advance_frame - Called during a rollback. You should advance your game
|
||||
|
|
|
@ -428,21 +428,22 @@ GGPOErrorCode Peer2PeerBackend::SetManualNetworkPolling(bool value)
|
|||
|
||||
GGPOErrorCode
|
||||
Peer2PeerBackend::IncrementFrame(uint16_t checksum1)
|
||||
{
|
||||
{
|
||||
auto currentFrame = _sync.GetFrameCount();
|
||||
_sync.IncrementFrame();
|
||||
uint32 cSum = _sync.GetLastSavedFrame().checksum;
|
||||
char buf[256];
|
||||
uint16_t cSum = checksum1;
|
||||
Log("End of frame (%d)...\n", _sync.GetFrameCount());
|
||||
Log("End of frame (%d)...\n", currentFrame);
|
||||
static int maxDif = 0;
|
||||
if (_pendingCheckSums.count(_sync.GetFrameCount()))
|
||||
if (_pendingCheckSums.count(currentFrame))
|
||||
{
|
||||
auto max = _pendingCheckSums.rbegin()->first;
|
||||
auto diff = max - currentFrame;
|
||||
maxDif = max(maxDif, diff);
|
||||
int oldChecksum = _pendingCheckSums[_sync.GetFrameCount()];
|
||||
_pendingCheckSums[_sync.GetFrameCount()] = cSum;
|
||||
sprintf_s<256>(buf, "Replace local checksum for frame %d: %d with %d, newest frame is %d, max diff %d\n", _sync.GetFrameCount(), oldChecksum, _pendingCheckSums[_sync.GetFrameCount()], max, maxDif);
|
||||
|
||||
int oldChecksum = _pendingCheckSums[currentFrame];
|
||||
_pendingCheckSums[currentFrame] = cSum;
|
||||
sprintf_s<256>(buf, "Replace local checksum for frame %d: %d with %d, newest frame is %d, max diff %d\n",
|
||||
currentFrame, oldChecksum, _pendingCheckSums[currentFrame], max, maxDif);
|
||||
|
||||
if (currentFrame <= _confirmedCheckSumFrame)
|
||||
{
|
||||
|
@ -461,16 +462,13 @@ Peer2PeerBackend::IncrementFrame(uint16_t checksum1)
|
|||
}
|
||||
else
|
||||
{
|
||||
sprintf_s<256>(buf, "Added local checksum for frame %d: %d\n", _sync.GetFrameCount(), cSum);
|
||||
sprintf_s<256>(buf, "Added local checksum for frame %d: %d\n", currentFrame, cSum);
|
||||
//OutputDebugStringA(buf);
|
||||
}
|
||||
|
||||
_pendingCheckSums[_sync.GetFrameCount()]= cSum ;
|
||||
_pendingCheckSums[currentFrame] = cSum;
|
||||
|
||||
|
||||
|
||||
_sync.IncrementFrame();
|
||||
DoPoll();
|
||||
DoPoll();
|
||||
PollSyncEvents();
|
||||
|
||||
return GGPO_OK;
|
||||
|
@ -511,7 +509,7 @@ Peer2PeerBackend::PollUdpProtocolEvents(void)
|
|||
//}
|
||||
}
|
||||
|
||||
void Peer2PeerBackend::CheckRemoteChecksum(int framenumber, uint16 cs)
|
||||
void Peer2PeerBackend::CheckRemoteChecksum(int framenumber, uint32 cs)
|
||||
{
|
||||
if (framenumber <= _sync.MaxPredictionFrames())
|
||||
return;
|
||||
|
@ -522,7 +520,7 @@ void Peer2PeerBackend::CheckRemoteChecksum(int framenumber, uint16 cs)
|
|||
|
||||
int Peer2PeerBackend::HowFarBackForChecksums()const
|
||||
{
|
||||
return 16;
|
||||
return _sync.MaxPredictionFrames() + 2;
|
||||
}/*
|
||||
uint16 Peer2PeerBackend::GetChecksumForConfirmedFrame(int frameNumber) const
|
||||
{
|
||||
|
|
|
@ -82,11 +82,11 @@ protected:
|
|||
int nFrame;
|
||||
int checkSum;;
|
||||
};
|
||||
std::map<int, uint16> _pendingCheckSums;
|
||||
std::map<int, uint16> _confirmedCheckSums;
|
||||
std::map<int, uint32> _pendingCheckSums;
|
||||
std::map<int, uint32> _confirmedCheckSums;
|
||||
|
||||
// uint16 GetChecksumForConfirmedFrame(int frameNumber) const;
|
||||
void CheckRemoteChecksum(int framenumber, uint16 cs);
|
||||
void CheckRemoteChecksum(int framenumber, uint32 cs);
|
||||
int HowFarBackForChecksums()const;
|
||||
int _confirmedCheckSumFrame = -500;
|
||||
void CheckDesync();
|
||||
|
|
|
@ -158,7 +158,7 @@ SyncTestBackend::IncrementFrame(uint16_t cs)
|
|||
RaiseSyncError("Checksum for frame %d does not match saved (%d != %d)", frame, checksum, info.checksum);
|
||||
}
|
||||
printf("Checksum %08d for frame %d matches.\n", checksum, info.frame);
|
||||
_callbacks.free_buffer(_callbacks.context, info.buf);
|
||||
_callbacks.free_buffer(_callbacks.context, info.buf, info.frame);
|
||||
}
|
||||
_last_verified = frame;
|
||||
_rollingback = false;
|
||||
|
|
|
@ -24,7 +24,7 @@ struct GameInput {
|
|||
int frame;
|
||||
int size; /* size in bytes of the entire input for all players */
|
||||
char bits[GAMEINPUT_MAX_BYTES * GAMEINPUT_MAX_PLAYERS];
|
||||
uint16 checksum;
|
||||
uint32 checksum;
|
||||
bool is_null() { return frame == NullFrame; }
|
||||
void init(int frame, char *bits, int size, int offset);
|
||||
void init(int frame, char *bits, int size);
|
||||
|
|
|
@ -67,7 +67,7 @@ struct UdpMsg
|
|||
int ack_frame:31;
|
||||
|
||||
uint16 num_bits;
|
||||
uint16 checksum16;
|
||||
uint32 checksum32;
|
||||
uint8 input_size; // XXX: shouldn't be in every single packet!
|
||||
uint8 bits[MAX_COMPRESSED_BITS]; /* must be last */
|
||||
} input;
|
||||
|
|
|
@ -134,7 +134,7 @@ UdpProtocol::SendPendingOutput()
|
|||
ASSERT(last.frame == -1 || last.frame + 1 == msg->u.input.start_frame);
|
||||
for (j = 0; j < _pending_output.size(); j++) {
|
||||
GameInput ¤t = _pending_output.item(j);
|
||||
msg->u.input.checksum16 = current.checksum;
|
||||
msg->u.input.checksum32 = current.checksum;
|
||||
if (memcmp(current.bits, last.bits, current.size) != 0) {
|
||||
ASSERT((GAMEINPUT_MAX_BYTES * GAMEINPUT_MAX_PLAYERS * 8) < (1 << BITVECTOR_NIBBLE_SIZE));
|
||||
for (i = 0; i < current.size * 8; i++) {
|
||||
|
@ -627,7 +627,7 @@ UdpProtocol::OnInput(UdpMsg *msg, int len)
|
|||
char desc[1024];
|
||||
ASSERT(currentFrame == _last_received_input.frame + 1);
|
||||
_last_received_input.frame = currentFrame;
|
||||
_last_received_input.checksum = msg->u.input.checksum16;
|
||||
_last_received_input.checksum = msg->u.input.checksum32;
|
||||
/*
|
||||
* Send the event to the emualtor
|
||||
*/
|
||||
|
|
|
@ -94,8 +94,8 @@ public:
|
|||
void ApplyToEvents(std::function<void(UdpProtocol::Event&)> cb);
|
||||
void StartPollLoop();
|
||||
void EndPollLoop();
|
||||
std::map<int, uint16> _remoteCheckSums;
|
||||
std::map<int, uint16> _remoteCheckSumsThisFrame;
|
||||
std::map<int, uint32> _remoteCheckSums;
|
||||
std::map<int, uint32> _remoteCheckSumsThisFrame;
|
||||
protected:
|
||||
enum State {
|
||||
Syncing,
|
||||
|
|
|
@ -24,7 +24,7 @@ Sync::~Sync()
|
|||
* structure so we can efficently copy frames via weak references.
|
||||
*/
|
||||
for (int i = 0; i < _savedstate.frames.size(); i++) {
|
||||
_callbacks.free_buffer(_callbacks.context, _savedstate.frames[i].buf);
|
||||
_callbacks.free_buffer(_callbacks.context, _savedstate.frames[i].buf, _savedstate.frames[i].frame);
|
||||
}
|
||||
delete [] _input_queues;
|
||||
_input_queues = NULL;
|
||||
|
@ -204,7 +204,7 @@ Sync::SaveCurrentFrame()
|
|||
*/
|
||||
SavedFrame *state = &_savedstate.frames[_savedstate.head];
|
||||
if (state->buf) {
|
||||
_callbacks.free_buffer(_callbacks.context, state->buf);
|
||||
_callbacks.free_buffer(_callbacks.context, state->buf, state->frame);
|
||||
state->buf = NULL;
|
||||
}
|
||||
state->frame = _framecount;
|
||||
|
|
|
@ -61,7 +61,8 @@ public:
|
|||
bool GetEvent(Event& e);
|
||||
int MaxPredictionFrames() const { return _max_prediction_frames; }
|
||||
protected:
|
||||
friend SyncTestBackend;
|
||||
friend class SyncTestBackend;
|
||||
friend class Peer2PeerBackend;
|
||||
|
||||
struct SavedFrame {
|
||||
byte* buf;
|
||||
|
|
|
@ -7,12 +7,14 @@
|
|||
#include "common/timer.h"
|
||||
#include "digital_controller.h"
|
||||
#include "ggponet.h"
|
||||
#include "host.h"
|
||||
#include "host_settings.h"
|
||||
#include "pad.h"
|
||||
#include "spu.h"
|
||||
#include "system.h"
|
||||
#include <bitset>
|
||||
#include <deque>
|
||||
#include <xxhash.h>
|
||||
Log_SetChannel(Netplay);
|
||||
|
||||
#ifdef _WIN32
|
||||
|
@ -32,7 +34,7 @@ static bool NpAdvFrameCb(void* ctx, int flags);
|
|||
static bool NpSaveFrameCb(void* ctx, unsigned char** buffer, int* len, int* checksum, int frame);
|
||||
static bool NpLoadFrameCb(void* ctx, unsigned char* buffer, int len, int rb_frames, int frame_to_load);
|
||||
static bool NpBeginGameCb(void* ctx, const char* game_name);
|
||||
static void NpFreeBuffCb(void* ctx, void* buffer);
|
||||
static void NpFreeBuffCb(void* ctx, void* buffer, int frame);
|
||||
static bool NpOnEventCb(void* ctx, GGPOEvent* ev);
|
||||
|
||||
static Input ReadLocalInput();
|
||||
|
@ -45,7 +47,7 @@ static void SetSettings();
|
|||
// l = local, r = remote
|
||||
static s32 Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ldelay, u32 pred);
|
||||
|
||||
static void AdvanceFrame(u16 checksum = 0);
|
||||
static void AdvanceFrame();
|
||||
static void RunFrame();
|
||||
|
||||
static s32 CurrentFrame();
|
||||
|
@ -57,6 +59,9 @@ static void InitializeFramePacing();
|
|||
static void HandleTimeSyncEvent(float frame_delta, int update_interval);
|
||||
static void Throttle();
|
||||
|
||||
// Desync Detection
|
||||
static void GenerateChecksumForFrame(int* checksum, int frame, unsigned char* buffer, int buffer_size);
|
||||
static void GenerateDesyncReport(s32 desync_frame);
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
// Variables
|
||||
//////////////////////////////////////////////////////////////////////////
|
||||
|
@ -107,7 +112,7 @@ s32 Netplay::Start(s32 lhandle, u16 lport, std::string& raddr, u16 rport, s32 ld
|
|||
ggpo_start_session(&s_ggpo, &cb, "Duckstation-Netplay", 2, sizeof(Netplay::Input), lport, MAX_ROLLBACK_FRAMES);
|
||||
// result = ggpo_start_synctest(&s_ggpo, &cb, (char*)"asdf", 2, sizeof(Netplay::Input), 1);
|
||||
|
||||
ggpo_set_disconnect_timeout(s_ggpo, 3000);
|
||||
ggpo_set_disconnect_timeout(s_ggpo, 2000);
|
||||
ggpo_set_disconnect_notify_start(s_ggpo, 1000);
|
||||
|
||||
for (int i = 1; i <= 2; i++)
|
||||
|
@ -176,6 +181,11 @@ void Netplay::SetSettings()
|
|||
si.SetIntValue("Main", "RunaheadFrameCount", 0);
|
||||
si.SetBoolValue("Main", "RewindEnable", false);
|
||||
|
||||
// no block linking, it degrades savestate loading performance
|
||||
si.SetBoolValue("CPU", "RecompilerBlockLinking", false);
|
||||
// not sure its needed but enabled for now... TODO
|
||||
si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", true);
|
||||
|
||||
Host::Internal::SetNetplaySettingsLayer(&si);
|
||||
}
|
||||
|
||||
|
@ -200,19 +210,16 @@ void Netplay::UpdateThrottlePeriod()
|
|||
|
||||
void Netplay::HandleTimeSyncEvent(float frame_delta, int update_interval)
|
||||
{
|
||||
// threshold to what is is with correcting for.
|
||||
if (frame_delta <= 1.0f)
|
||||
return;
|
||||
|
||||
float total_time = frame_delta * s_frame_period;
|
||||
// Distribute the frame difference over the next N * 0.8 frames.
|
||||
// Distribute the frame difference over the next N * 0.75 frames.
|
||||
// only part of the interval time is used since we want to come back to normal speed.
|
||||
// otherwise we will keep spiraling into unplayable gameplay.
|
||||
float added_time_per_frame = -(total_time / (static_cast<float>(update_interval) * 0.8f));
|
||||
float total_time = (frame_delta * s_frame_period) / 4;
|
||||
float mun_timesync_frames = update_interval * 0.75f;
|
||||
float added_time_per_frame = -(total_time / mun_timesync_frames);
|
||||
float iterations_per_frame = 1.0f / s_frame_period;
|
||||
|
||||
s_target_speed = (s_frame_period + added_time_per_frame) * iterations_per_frame;
|
||||
s_next_timesync_recovery_frame = CurrentFrame() + static_cast<s32>(std::ceil(static_cast<float>(update_interval) * 0.8f));
|
||||
s_next_timesync_recovery_frame = CurrentFrame() + static_cast<s32>(std::ceil(mun_timesync_frames));
|
||||
|
||||
UpdateThrottlePeriod();
|
||||
|
||||
|
@ -264,15 +271,57 @@ void Netplay::Throttle()
|
|||
}
|
||||
}
|
||||
|
||||
void Netplay::AdvanceFrame(u16 checksum)
|
||||
void Netplay::GenerateChecksumForFrame(int* checksum, int frame, unsigned char* buffer, int buffer_size)
|
||||
{
|
||||
ggpo_advance_frame(s_ggpo, checksum);
|
||||
const u32 sliding_window_size = 4096 * 4; // 4 pages.
|
||||
const u32 num_group_of_pages = buffer_size / sliding_window_size;
|
||||
const u32 start_position = (frame % num_group_of_pages) * sliding_window_size;
|
||||
*checksum = XXH32(buffer + start_position, sliding_window_size, frame);
|
||||
// Log_VerbosePrintf("Netplay Checksum: f:%d wf:%d c:%u", frame, frame % num_group_of_pages, *checksum);
|
||||
}
|
||||
|
||||
void Netplay::GenerateDesyncReport(s32 desync_frame)
|
||||
{
|
||||
std::string path = "\\netplaylogs\\desync_frame_" + std::to_string(desync_frame) + "_p" +
|
||||
std::to_string(s_local_handle) + "_" + System::GetRunningSerial() + "_.txt";
|
||||
std::string filename = EmuFolders::Dumps + path;
|
||||
|
||||
std::unique_ptr<ByteStream> stream =
|
||||
ByteStream::OpenFile(filename.c_str(), BYTESTREAM_OPEN_CREATE | BYTESTREAM_OPEN_WRITE | BYTESTREAM_OPEN_TRUNCATE |
|
||||
BYTESTREAM_OPEN_ATOMIC_UPDATE | BYTESTREAM_OPEN_STREAMED);
|
||||
if (!stream)
|
||||
{
|
||||
Log_VerbosePrint("desync log creation failed to create stream");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ByteStream::WriteBinaryToStream(stream.get(),
|
||||
s_save_buffer_pool.back().get()->state_stream.get()->GetMemoryPointer(),
|
||||
s_save_buffer_pool.back().get()->state_stream.get()->GetMemorySize()))
|
||||
{
|
||||
Log_VerbosePrint("desync log creation failed to write the stream");
|
||||
stream->Discard();
|
||||
return;
|
||||
}
|
||||
/* stream->Write(s_save_buffer_pool.back().get()->state_stream.get()->GetMemoryPointer(),
|
||||
s_save_buffer_pool.back().get()->state_stream.get()->GetMemorySize());*/
|
||||
|
||||
stream->Commit();
|
||||
|
||||
Log_VerbosePrintf("desync log created for frame %d", desync_frame);
|
||||
}
|
||||
|
||||
|
||||
void Netplay::AdvanceFrame()
|
||||
{
|
||||
ggpo_advance_frame(s_ggpo, 0);
|
||||
}
|
||||
|
||||
void Netplay::RunFrame()
|
||||
{
|
||||
// housekeeping
|
||||
ggpo_idle(s_ggpo);
|
||||
// run game
|
||||
bool need_idle = true;
|
||||
auto result = GGPO_OK;
|
||||
int disconnect_flags = 0;
|
||||
Netplay::Input inputs[2] = {};
|
||||
|
@ -291,13 +340,8 @@ void Netplay::RunFrame()
|
|||
// enable again when rolling back done
|
||||
SPU::SetAudioOutputMuted(false);
|
||||
NetplayAdvanceFrame(inputs, disconnect_flags);
|
||||
// coming here means that the system doesnt need to idle anymore
|
||||
need_idle = false;
|
||||
}
|
||||
}
|
||||
// allow ggpo to do housekeeping if needed
|
||||
if (need_idle)
|
||||
ggpo_idle(s_ggpo);
|
||||
}
|
||||
|
||||
s32 Netplay::CurrentFrame()
|
||||
|
@ -372,9 +416,14 @@ void Netplay::StartNetplaySession(s32 local_handle, u16 local_port, std::string&
|
|||
s_game_path = std::move(game_path);
|
||||
// create session
|
||||
int result = Netplay::Start(local_handle, local_port, remote_addr, remote_port, input_delay, MAX_ROLLBACK_FRAMES);
|
||||
// notify that the session failed
|
||||
if (result != GGPO_OK)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to Create Netplay Session! Error: %d", result);
|
||||
else
|
||||
{
|
||||
// Load savestate if available
|
||||
std::string save = EmuFolders::SaveStates + "/netplay/" + System::GetRunningSerial() + ".sav";
|
||||
System::LoadState(save.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -425,11 +474,12 @@ bool Netplay::NpBeginGameCb(void* ctx, const char* game_name)
|
|||
StopNetplaySession();
|
||||
return false;
|
||||
}
|
||||
// Fast Forward to Game Start
|
||||
SPU::SetAudioOutputMuted(true);
|
||||
// Fast Forward to Game Start if needed.
|
||||
while (System::GetInternalFrameNumber() < 2)
|
||||
System::RunFrame();
|
||||
SPU::SetAudioOutputMuted(false);
|
||||
// Set Initial Frame Pacing
|
||||
InitializeFramePacing();
|
||||
return true;
|
||||
}
|
||||
|
@ -446,24 +496,31 @@ bool Netplay::NpAdvFrameCb(void* ctx, int flags)
|
|||
bool Netplay::NpSaveFrameCb(void* ctx, unsigned char** buffer, int* len, int* checksum, int frame)
|
||||
{
|
||||
SaveStateBuffer our_buffer;
|
||||
if (s_save_buffer_pool.empty())
|
||||
// min size is 2 because otherwise the desync logger doesnt have enough time to dump the state.
|
||||
if (s_save_buffer_pool.size() < 2)
|
||||
{
|
||||
our_buffer = std::make_unique<System::MemorySaveState>();
|
||||
}
|
||||
else
|
||||
{
|
||||
our_buffer = std::move(s_save_buffer_pool.back());
|
||||
s_save_buffer_pool.pop_back();
|
||||
our_buffer = std::move(s_save_buffer_pool.front());
|
||||
s_save_buffer_pool.pop_front();
|
||||
}
|
||||
|
||||
if (!System::SaveMemoryState(our_buffer.get()))
|
||||
{
|
||||
s_save_buffer_pool.push_back(std::move(our_buffer));
|
||||
s_save_buffer_pool.push_front(std::move(our_buffer));
|
||||
return false;
|
||||
}
|
||||
|
||||
// desync detection
|
||||
const u32 state_size = our_buffer.get()->state_stream.get()->GetMemorySize();
|
||||
unsigned char* state = reinterpret_cast<unsigned char*>(our_buffer.get()->state_stream.get()->GetMemoryPointer());
|
||||
GenerateChecksumForFrame(checksum, frame, state, state_size);
|
||||
|
||||
*len = sizeof(System::MemorySaveState);
|
||||
*buffer = reinterpret_cast<unsigned char*>(our_buffer.release());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -475,8 +532,9 @@ bool Netplay::NpLoadFrameCb(void* ctx, unsigned char* buffer, int len, int rb_fr
|
|||
return System::LoadMemoryState(*reinterpret_cast<const System::MemorySaveState*>(buffer));
|
||||
}
|
||||
|
||||
void Netplay::NpFreeBuffCb(void* ctx, void* buffer)
|
||||
void Netplay::NpFreeBuffCb(void* ctx, void* buffer, int frame)
|
||||
{
|
||||
// Log_VerbosePrintf("Reuse Buffer: %d", frame);
|
||||
SaveStateBuffer our_buffer(reinterpret_cast<System::MemorySaveState*>(buffer));
|
||||
s_save_buffer_pool.push_back(std::move(our_buffer));
|
||||
}
|
||||
|
@ -484,7 +542,7 @@ void Netplay::NpFreeBuffCb(void* ctx, void* buffer)
|
|||
bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev)
|
||||
{
|
||||
char buff[128];
|
||||
std::string msg;
|
||||
std::string msg, filename;
|
||||
switch (ev->code)
|
||||
{
|
||||
case GGPOEventCode::GGPO_EVENTCODE_CONNECTED_TO_PEER:
|
||||
|
@ -523,10 +581,14 @@ bool Netplay::NpOnEventCb(void* ctx, GGPOEvent* ev)
|
|||
HandleTimeSyncEvent(ev->u.timesync.frames_ahead, ev->u.timesync.timeSyncPeriodInFrames);
|
||||
break;
|
||||
case GGPOEventCode::GGPO_EVENTCODE_DESYNC:
|
||||
sprintf(buff, "Netplay Desync Detected!: Frame: %d, L:%u, R:%u", ev->u.desync.nFrameOfDesync,
|
||||
ev->u.desync.ourCheckSum, ev->u.desync.remoteChecksum);
|
||||
sprintf(buff, "Desync Detected: Current Frame: %d, Desync Frame: %d, Diff: %d, L:%u, R:%u", CurrentFrame(),
|
||||
ev->u.desync.nFrameOfDesync, CurrentFrame() - ev->u.desync.nFrameOfDesync, ev->u.desync.ourCheckSum,
|
||||
ev->u.desync.remoteChecksum);
|
||||
msg = buff;
|
||||
break;
|
||||
GenerateDesyncReport(ev->u.desync.nFrameOfDesync);
|
||||
Host::AddKeyedOSDMessage("Netplay", msg, 5);
|
||||
|
||||
return true;
|
||||
default:
|
||||
sprintf(buff, "Netplay Event Code: %d", ev->code);
|
||||
msg = buff;
|
||||
|
|
|
@ -1752,6 +1752,23 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
|
|||
cpu_overclock_active ?
|
||||
Settings::CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator) :
|
||||
100u);
|
||||
|
||||
// during netplay if a file savestate is loaded set
|
||||
// the overclocks to the same value as the savestate
|
||||
// file savestates are usually only loaded at game start
|
||||
if (Netplay::IsActive() && !is_memory_state && cpu_overclock_active)
|
||||
{
|
||||
g_settings.cpu_overclock_enable = cpu_overclock_active;
|
||||
g_settings.cpu_overclock_numerator = cpu_overclock_numerator;
|
||||
g_settings.cpu_overclock_denominator = cpu_overclock_denominator;
|
||||
|
||||
g_settings.UpdateOverclockActive();
|
||||
|
||||
Host::AddFormattedOSDMessage(10.0f,
|
||||
Host::TranslateString("OSDMessage", "WARNING: CPU overclock was changed to (%u%%) to match netplay savestate."),
|
||||
g_settings.cpu_overclock_enable ? g_settings.GetCPUOverclockPercent() : 100u );
|
||||
}
|
||||
|
||||
UpdateOverclock();
|
||||
}
|
||||
|
||||
|
|
|
@ -1466,7 +1466,7 @@ void EmuThread::run()
|
|||
// main loop
|
||||
while (!m_shutdown_flag)
|
||||
{
|
||||
if (Netplay::IsActive())
|
||||
if (Netplay::IsActive() && System::IsRunning())
|
||||
{
|
||||
Netplay::ExecuteNetplay();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue