Netplay: Match peer settings to host

This commit is contained in:
Stenzek 2023-05-16 21:58:06 +10:00
parent 4177560a6c
commit 7ba2d5c94e
5 changed files with 189 additions and 92 deletions

View File

@ -10,6 +10,7 @@
#include "common/timer.h" #include "common/timer.h"
#include "digital_controller.h" #include "digital_controller.h"
#include "host.h" #include "host.h"
#include "host_display.h"
#include "host_settings.h" #include "host_settings.h"
#include "netplay_packets.h" #include "netplay_packets.h"
#include "pad.h" #include "pad.h"
@ -62,7 +63,8 @@ static GGPOErrorCode AddLocalInput(Netplay::Input input);
static GGPOErrorCode SyncInput(Input inputs[2], int* disconnect_flags); static GGPOErrorCode SyncInput(Input inputs[2], int* disconnect_flags);
static void SetInputs(Input inputs[2]); static void SetInputs(Input inputs[2]);
static void SetSettings(); static void SetSettings(const ConnectResponseMessage* msg);
static void FillSettings(ConnectResponseMessage* msg);
static bool CreateDummySystem(); static bool CreateDummySystem();
static void CloseSessionWithError(const std::string_view& message); static void CloseSessionWithError(const std::string_view& message);
@ -198,6 +200,8 @@ struct PacketWrapper
ALWAYS_INLINE const T* operator->() const { return reinterpret_cast<const T*>(pkt->data); } ALWAYS_INLINE const T* operator->() const { return reinterpret_cast<const T*>(pkt->data); }
ALWAYS_INLINE T* operator->() { return reinterpret_cast<T*>(pkt->data); } ALWAYS_INLINE T* operator->() { return reinterpret_cast<T*>(pkt->data); }
ALWAYS_INLINE const T* operator&() const { return reinterpret_cast<const T*>(pkt->data); }
ALWAYS_INLINE T* operator&() { return reinterpret_cast<T*>(pkt->data); }
}; };
template<typename T> template<typename T>
static PacketWrapper<T> NewWrappedPacket(u32 size = sizeof(T), u32 flags = 0) static PacketWrapper<T> NewWrappedPacket(u32 size = sizeof(T), u32 flags = 0)
@ -300,24 +304,30 @@ bool Netplay::Start(bool is_hosting, std::string nickname, const std::string& re
} }
// Need a system if we're hosting. // Need a system if we're hosting.
if (!System::IsValid()) if (is_hosting)
{ {
if (is_hosting) if (!System::IsValid())
{ {
Log_ErrorPrintf("Can't host a netplay session without a valid VM"); Log_ErrorPrintf("Can't host a netplay session without a valid VM");
return false; return false;
} }
else if (!is_hosting && !CreateDummySystem()) }
else
{
// We shouldn't have a system, toss it if we do.
if (System::IsValid())
System::ShutdownSystem(false);
// But we need the display to show the connecting screen.
if (!Host::AcquireHostDisplay(Settings::GetRenderAPIForRenderer(g_settings.gpu_renderer)))
{ {
Log_ErrorPrintf("Failed to create VM for joining session"); Log_ErrorPrintf("Failed to get host display for netplay");
return false; return false;
} }
} }
s_state = SessionState::Initializing; s_state = SessionState::Initializing;
SetSettings();
if (!InitializeEnet()) if (!InitializeEnet())
{ {
Log_ErrorPrintf("Failed to initialize Enet."); Log_ErrorPrintf("Failed to initialize Enet.");
@ -348,9 +358,11 @@ bool Netplay::Start(bool is_hosting, std::string nickname, const std::string& re
s_player_id = 0; s_player_id = 0;
s_num_players = 1; s_num_players = 1;
s_reset_players = 1; s_reset_players = 1;
s_peers[s_player_id].nickname = s_local_nickname;
CreateGGPOSession(); CreateGGPOSession();
s_state = SessionState::Running; s_state = SessionState::Running;
Log_InfoPrintf("Netplay session started as host on port %d.", port); Log_InfoPrintf("Netplay session started as host on port %d.", port);
SetSettings(nullptr);
System::SetState(System::State::Paused); System::SetState(System::State::Paused);
return true; return true;
} }
@ -378,7 +390,6 @@ bool Netplay::Start(bool is_hosting, std::string nickname, const std::string& re
s_state = SessionState::Connecting; s_state = SessionState::Connecting;
s_reset_start_time.Reset(); s_reset_start_time.Reset();
s_last_host_connection_attempt.Reset(); s_last_host_connection_attempt.Reset();
System::SetState(System::State::Paused);
return true; return true;
} }
@ -496,6 +507,9 @@ void Netplay::RequestCloseSession(CloseSessionMessage::Reason reason)
Host::DisplayLoadingScreen("Closing session"); Host::DisplayLoadingScreen("Closing session");
Host::PumpMessagesOnCPUThread(); Host::PumpMessagesOnCPUThread();
} }
// toss host display, if we were still connecting, this'll be up
Host::ReleaseHostDisplay();
} }
bool Netplay::InitializeEnet() bool Netplay::InitializeEnet()
@ -839,12 +853,12 @@ void Netplay::HandlePeerConnectionAsHost(ENetPeer* peer)
sizeof(ConnectResponseMessage) + static_cast<u32>(game_serial.length()) + static_cast<u32>(game_title.length())); sizeof(ConnectResponseMessage) + static_cast<u32>(game_serial.length()) + static_cast<u32>(game_title.length()));
pkt->num_players = s_num_players; pkt->num_players = s_num_players;
pkt->max_players = MAX_PLAYERS; pkt->max_players = MAX_PLAYERS;
pkt->console_region = System::GetRegion();
pkt->game_title_length = static_cast<u32>(game_title.length()); pkt->game_title_length = static_cast<u32>(game_title.length());
pkt->game_serial_length = static_cast<u32>(game_serial.length()); pkt->game_serial_length = static_cast<u32>(game_serial.length());
pkt->game_hash = System::GetGameHash(); pkt->game_hash = System::GetGameHash();
pkt->bios_hash = System::GetBIOSHash(); pkt->bios_hash = System::GetBIOSHash();
pkt->was_fast_booted = System::WasFastBooted(); FillSettings(&pkt);
std::memcpy(pkt.pkt->data + sizeof(ConnectResponseMessage), game_serial.c_str(), pkt->game_serial_length); std::memcpy(pkt.pkt->data + sizeof(ConnectResponseMessage), game_serial.c_str(), pkt->game_serial_length);
std::memcpy(pkt.pkt->data + sizeof(ConnectResponseMessage) + pkt->game_serial_length, game_title.c_str(), std::memcpy(pkt.pkt->data + sizeof(ConnectResponseMessage) + pkt->game_serial_length, game_title.c_str(),
pkt->game_title_length); pkt->game_title_length);
@ -874,14 +888,14 @@ void Netplay::HandleConnectResponseMessage(s32 player_id, const ENetPacket* pkt)
} }
Log_InfoPrintf("Received session details from host: "); Log_InfoPrintf("Received session details from host: ");
Log_InfoPrintf(" Console Region: %s", Settings::GetConsoleRegionDisplayName(msg->console_region)); Log_InfoPrintf(" Console Region: %s", Settings::GetConsoleRegionDisplayName(msg->settings.console_region));
Log_InfoPrintf(" BIOS Hash: %s%s", msg->bios_hash.ToString().c_str(), msg->was_fast_booted ? " (fast booted)" : ""); Log_InfoPrintf(" BIOS Hash: %s%s", msg->bios_hash.ToString().c_str(), msg->settings.was_fast_booted ? " (fast booted)" : "");
Log_InfoPrintf(" Game Serial: %.*s", msg->game_serial_length, msg->GetGameSerial().data()); Log_InfoPrintf(" Game Serial: %.*s", msg->game_serial_length, msg->GetGameSerial().data());
Log_InfoPrintf(" Game Title: %.*s", msg->game_title_length, msg->GetGameTitle().data()); Log_InfoPrintf(" Game Title: %.*s", msg->game_title_length, msg->GetGameTitle().data());
Log_InfoPrintf(" Game Hash: %" PRIX64, msg->game_hash); Log_InfoPrintf(" Game Hash: %" PRIX64, msg->game_hash);
// Find a matching BIOS. // Find a matching BIOS.
const std::string bios_path = BIOS::FindBIOSPathWithHash(EmuFolders::Bios.c_str(), msg->bios_hash); std::string bios_path = BIOS::FindBIOSPathWithHash(EmuFolders::Bios.c_str(), msg->bios_hash);
if (bios_path.empty()) if (bios_path.empty())
{ {
CloseSessionWithError(fmt::format( CloseSessionWithError(fmt::format(
@ -891,8 +905,14 @@ void Netplay::HandleConnectResponseMessage(s32 player_id, const ENetPacket* pkt)
} }
// Find the matching game. // Find the matching game.
const GameList::Entry* entry = GameList::GetEntryBySerialAndHash(msg->GetGameSerial(), msg->game_hash); std::string game_path;
if (!entry) {
auto lock = GameList::GetLock();
const GameList::Entry* entry = GameList::GetEntryBySerialAndHash(msg->GetGameSerial(), msg->game_hash);
if (entry)
game_path = entry->path;
}
if (game_path.empty())
{ {
CloseSessionWithError(fmt::format( CloseSessionWithError(fmt::format(
Host::TranslateString("Netplay", "Cannot join session: Unable to find game \"{}\".\nSerial: {}\nHash: {}") Host::TranslateString("Netplay", "Cannot join session: Unable to find game \"{}\".\nSerial: {}\nHash: {}")
@ -902,12 +922,19 @@ void Netplay::HandleConnectResponseMessage(s32 player_id, const ENetPacket* pkt)
} }
Log_InfoPrintf("Found matching BIOS: %s", bios_path.c_str()); Log_InfoPrintf("Found matching BIOS: %s", bios_path.c_str());
Log_InfoPrintf("Found matching game: %s", entry->path.c_str()); Log_InfoPrintf("Found matching game: %s", game_path.c_str());
// Reboot created system with host details. // Apply settings from host.
if (!System::ReinitializeSystem(msg->console_region, bios_path.c_str(), entry->path.c_str(), msg->was_fast_booted)) SetSettings(msg);
// Create system with host details.
Assert(!System::IsValid());
SystemBootParameters params;
params.filename = std::move(game_path);
params.override_bios = std::move(bios_path);
if (!System::BootSystem(std::move(params)))
{ {
CloseSessionWithError(Host::TranslateStdString("Netplay", "Cannot join session: Failed to reinitialize system.")); CloseSessionWithError(Host::TranslateStdString("Netplay", "Cannot join session: Failed to boot system."));
return; return;
} }
@ -1512,7 +1539,7 @@ void Netplay::HandleChatMessage(s32 player_id, const ENetPacket* pkt)
// Settings Overlay // Settings Overlay
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
void Netplay::SetSettings() void Netplay::SetSettings(const ConnectResponseMessage* msg)
{ {
MemorySettingsInterface& si = s_settings_overlay; MemorySettingsInterface& si = s_settings_overlay;
@ -1537,26 +1564,106 @@ void Netplay::SetSettings()
// si.SetStringValue("CPU", "ExecutionMode", "Interpreter"); // si.SetStringValue("CPU", "ExecutionMode", "Interpreter");
// BIOS patching must be the same.
si.SetBoolValue("BIOS", "PatchTTYEnable", false);
si.SetBoolValue("BIOS", "PatchFastBoot", true);
si.SetBoolValue("CDROM", "LoadImagePatches", false);
// No runahead or rewind, that'd be a disaster. // No runahead or rewind, that'd be a disaster.
si.SetIntValue("Main", "RunaheadFrameCount", 0); si.SetIntValue("Main", "RunaheadFrameCount", 0);
si.SetBoolValue("Main", "RewindEnable", false); si.SetBoolValue("Main", "RewindEnable", false);
// no block linking, it degrades savestate loading performance // no block linking, it degrades savestate loading performance
si.SetBoolValue("CPU", "RecompilerBlockLinking", false); si.SetBoolValue("CPU", "RecompilerBlockLinking", false);
// not sure its needed but enabled for now... TODO
// Turn off fastmem, it can affect determinism depending on when it was loaded.
si.SetBoolValue("CPU", "FastmemMode", Settings::GetCPUFastmemModeName(CPUFastmemMode::Disabled));
// SW renderer for readbacks ensures differences in host GPU don't affect downloads.
si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", true); si.SetBoolValue("GPU", "UseSoftwareRendererForReadbacks", true);
// TODO: PGXP should be the same as the host, as should overclock etc. // No cheats.. yet. Need to serialize them, and that has security risks.
si.SetBoolValue("Main", "AutoLoadCheats", false);
// No PCDRV or texture replacements, they require local files.
si.SetBoolValue("PCDrv", "Enabled", false);
si.SetBoolValue("TextureReplacements", "EnableVRAMWriteReplacements", false);
si.SetBoolValue("CDROM", "LoadImagePatches", false);
// Disable achievements for now, we might be able to support them later though.
si.SetBoolValue("Cheevos", "Enabled", false);
// Settings from host.
#define SELECT_SETTING(field) (msg ? msg->settings.field : g_settings.field)
si.SetStringValue("Console", "Region",
Settings::GetConsoleRegionName(msg ? msg->settings.console_region : System::GetRegion()));
si.SetStringValue("CPU", "ExecutionMode", Settings::GetCPUExecutionModeName(SELECT_SETTING(cpu_execution_mode)));
si.SetBoolValue("CPU", "OverclockEnable", SELECT_SETTING(cpu_overclock_enable));
si.SetIntValue("CPU", "OverclockNumerator", SELECT_SETTING(cpu_overclock_numerator));
si.SetIntValue("CPU", "OverclockDenominator", SELECT_SETTING(cpu_overclock_denominator));
si.SetBoolValue("CPU", "RecompilerMemoryExceptions", SELECT_SETTING(cpu_recompiler_memory_exceptions));
si.SetBoolValue("CPU", "RecompilerICache", SELECT_SETTING(cpu_recompiler_icache));
si.SetBoolValue("GPU", "DisableInterlacing", SELECT_SETTING(gpu_disable_interlacing));
si.SetBoolValue("GPU", "ForceNTSCTimings", SELECT_SETTING(gpu_force_ntsc_timings));
si.SetBoolValue("GPU", "WidescreenHack", SELECT_SETTING(gpu_widescreen_hack));
si.SetBoolValue("GPU", "PGXPEnable", SELECT_SETTING(gpu_pgxp_enable));
si.SetBoolValue("GPU", "PGXPCulling", SELECT_SETTING(gpu_pgxp_culling));
si.SetBoolValue("GPU", "PGXPCPU", SELECT_SETTING(gpu_pgxp_cpu));
si.SetBoolValue("GPU", "PGXPPreserveProjFP", SELECT_SETTING(gpu_pgxp_preserve_proj_fp));
si.SetBoolValue("CDROM", "RegionCheck", SELECT_SETTING(cdrom_region_check));
si.SetBoolValue("Main", "DisableAllEnhancements", SELECT_SETTING(disable_all_enhancements));
si.SetBoolValue("Hacks", "UseOldMDECRoutines", SELECT_SETTING(use_old_mdec_routines));
si.SetBoolValue("BIOS", "PatchTTYEnable", SELECT_SETTING(bios_patch_tty_enable));
si.SetBoolValue("BIOS", "PatchFastBoot", msg ? msg->settings.was_fast_booted : System::WasFastBooted());
si.SetBoolValue("Console", "Enable8MBRAM", SELECT_SETTING(enable_8mb_ram));
si.SetStringValue(
"Display", "AspectRatio",
Settings::GetDisplayAspectRatioName(msg ? msg->settings.display_aspect_ratio :
(g_settings.display_aspect_ratio == DisplayAspectRatio::MatchWindow ?
DisplayAspectRatio::Auto :
g_settings.display_aspect_ratio)));
si.SetIntValue("Display", "CustomAspectRatioNumerator", SELECT_SETTING(display_aspect_ratio_custom_numerator));
si.GetIntValue("Display", "CustomAspectRatioDenominator", SELECT_SETTING(display_aspect_ratio_custom_denominator));
si.SetStringValue("ControllerPorts", "MultitapMode", Settings::GetMultitapModeName(SELECT_SETTING(multitap_mode)));
si.SetIntValue("Hacks", "DMAMaxSliceTicks", SELECT_SETTING(dma_max_slice_ticks));
si.SetIntValue("Hacks", "DMAHaltTicks", SELECT_SETTING(dma_halt_ticks));
si.SetIntValue("Hacks", "GPUFIFOSize", SELECT_SETTING(gpu_fifo_size));
si.SetIntValue("Hacks", "GPUMaxRunAhead", SELECT_SETTING(gpu_max_run_ahead));
#undef SELECT_SETTING
Host::Internal::SetNetplaySettingsLayer(&si); Host::Internal::SetNetplaySettingsLayer(&si);
System::ApplySettings(false); System::ApplySettings(false);
} }
void Netplay::FillSettings(ConnectResponseMessage* msg)
{
#define FILL_SETTING(field) msg->settings.field = g_settings.field
msg->settings.console_region = System::GetRegion();
FILL_SETTING(cpu_execution_mode);
FILL_SETTING(cpu_overclock_enable);
FILL_SETTING(cpu_overclock_numerator);
FILL_SETTING(cpu_overclock_denominator);
FILL_SETTING(cpu_recompiler_memory_exceptions);
FILL_SETTING(cpu_recompiler_icache);
FILL_SETTING(gpu_disable_interlacing);
FILL_SETTING(gpu_force_ntsc_timings);
FILL_SETTING(gpu_widescreen_hack);
FILL_SETTING(gpu_pgxp_enable);
FILL_SETTING(gpu_pgxp_culling);
FILL_SETTING(gpu_pgxp_cpu);
FILL_SETTING(gpu_pgxp_preserve_proj_fp);
FILL_SETTING(cdrom_region_check);
FILL_SETTING(disable_all_enhancements);
FILL_SETTING(use_old_mdec_routines);
FILL_SETTING(bios_patch_tty_enable);
msg->settings.was_fast_booted = System::WasFastBooted();
FILL_SETTING(enable_8mb_ram);
FILL_SETTING(display_aspect_ratio);
FILL_SETTING(display_aspect_ratio_custom_numerator);
FILL_SETTING(display_aspect_ratio_custom_denominator);
FILL_SETTING(multitap_mode);
FILL_SETTING(dma_max_slice_ticks);
FILL_SETTING(dma_halt_ticks);
FILL_SETTING(gpu_fifo_size);
FILL_SETTING(gpu_max_run_ahead);
#undef FILL_SETTING
}
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
// Frame Pacing // Frame Pacing
////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////
@ -1801,7 +1908,8 @@ void Netplay::NetplayAdvanceFrame(Netplay::Input inputs[], int disconnect_flags)
void Netplay::ExecuteNetplay() void Netplay::ExecuteNetplay()
{ {
// TODO: Fix this hackery to get out of the CPU loop... // TODO: Fix this hackery to get out of the CPU loop...
System::SetState(System::State::Running); if (System::IsValid())
System::SetState(System::State::Running);
while (s_state != SessionState::Inactive) while (s_state != SessionState::Inactive)
{ {

View File

@ -64,14 +64,50 @@ struct ConnectResponseMessage
u64 game_hash; u64 game_hash;
u32 game_serial_length; u32 game_serial_length;
u32 game_title_length; u32 game_title_length;
ConsoleRegion console_region;
BIOS::Hash bios_hash; BIOS::Hash bios_hash;
bool was_fast_booted;
struct
{
ConsoleRegion console_region;
CPUExecutionMode cpu_execution_mode;
u32 cpu_overclock_numerator;
u32 cpu_overclock_denominator;
bool cpu_overclock_enable;
bool cpu_recompiler_memory_exceptions;
bool cpu_recompiler_icache;
bool gpu_disable_interlacing;
bool gpu_force_ntsc_timings;
bool gpu_widescreen_hack;
bool gpu_pgxp_enable;
bool gpu_pgxp_culling;
bool gpu_pgxp_cpu;
bool gpu_pgxp_preserve_proj_fp;
bool cdrom_region_check;
bool disable_all_enhancements;
bool use_old_mdec_routines;
bool bios_patch_tty_enable;
bool was_fast_booted;
bool enable_8mb_ram;
DisplayAspectRatio display_aspect_ratio;
u16 display_aspect_ratio_custom_numerator;
u16 display_aspect_ratio_custom_denominator;
MultitapMode multitap_mode;
TickCount dma_max_slice_ticks;
TickCount dma_halt_ticks;
u32 gpu_fifo_size;
TickCount gpu_max_run_ahead;
} settings;
// <char> * game_serial_length + game_title_length follows // <char> * game_serial_length + game_title_length follows
// TODO: Include the settings overlays required to match the host config. // TODO: Include the settings overlays required to match the host config.
bool Validate() const { return static_cast<unsigned>(console_region) < static_cast<unsigned>(ConsoleRegion::Count); } bool Validate() const
{
return (static_cast<unsigned>(settings.console_region) < static_cast<unsigned>(ConsoleRegion::Count) &&
static_cast<unsigned>(settings.cpu_execution_mode) < static_cast<unsigned>(CPUExecutionMode::Count) &&
static_cast<unsigned>(settings.display_aspect_ratio) < static_cast<unsigned>(DisplayAspectRatio::Count) &&
static_cast<unsigned>(settings.multitap_mode) < static_cast<unsigned>(MultitapMode::Count));
}
std::string_view GetGameSerial() const std::string_view GetGameSerial() const
{ {

View File

@ -87,7 +87,7 @@ static bool ReadExecutableFromImage(ISOReader& iso, std::string* out_executable_
static void StallCPU(TickCount ticks); static void StallCPU(TickCount ticks);
static bool LoadBIOS(); static bool LoadBIOS(const std::string& override_bios_path);
static void InternalReset(); static void InternalReset();
static void ClearRunningGame(); static void ClearRunningGame();
static void DestroySystem(); static void DestroySystem();
@ -1251,7 +1251,7 @@ bool System::BootSystem(SystemBootParameters parameters)
#endif #endif
// Load BIOS image. // Load BIOS image.
if (!LoadBIOS()) if (!LoadBIOS(parameters.override_bios))
{ {
s_state = State::Shutdown; s_state = State::Shutdown;
ClearRunningGame(); ClearRunningGame();
@ -1364,44 +1364,6 @@ bool System::BootSystem(SystemBootParameters parameters)
return true; return true;
} }
bool System::ReinitializeSystem(ConsoleRegion region, const char* bios_path, const char* media_path, bool fast_boot)
{
std::optional<BIOS::Image> bios_image = FileSystem::ReadBinaryFile(bios_path);
if (!bios_image.has_value())
{
Log_ErrorPrintf("Failed to read replacement BIOS at '%s'", bios_path);
return false;
}
if (!InsertMedia(media_path))
{
Log_ErrorPrintf("Failed to insert media at '%s'", media_path);
return false;
}
// Replace the BIOS.
s_bios_hash = BIOS::GetImageHash(bios_image.value());
s_bios_image_info = BIOS::GetInfoForImage(bios_image.value(), s_bios_hash);
if (s_bios_image_info)
Log_InfoPrintf("Replacing BIOS: %s", s_bios_image_info->description);
else
Log_WarningPrintf("Replacing with an unknown BIOS: %s", s_bios_hash.ToString().c_str());
std::memcpy(Bus::g_bios, bios_image->data(), Bus::BIOS_SIZE);
if (s_bios_image_info && s_bios_image_info->patch_compatible)
BIOS::PatchBIOSEnableTTY(Bus::g_bios, Bus::BIOS_SIZE);
s_was_fast_booted = false;
if (s_bios_image_info && s_bios_image_info->patch_compatible && fast_boot)
{
BIOS::PatchBIOSFastBoot(Bus::g_bios, Bus::BIOS_SIZE);
s_was_fast_booted = true;
}
return true;
}
bool System::Initialize(bool force_software_renderer) bool System::Initialize(bool force_software_renderer)
{ {
g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK); g_ticks_per_second = ScaleTicksToOverclock(MASTER_CLOCK);
@ -1843,22 +1805,6 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
Settings::CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator) : Settings::CPUOverclockFractionToPercent(cpu_overclock_numerator, cpu_overclock_denominator) :
100u); 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(); UpdateOverclock();
} }
@ -1892,9 +1838,10 @@ bool System::DoState(StateWrapper& sw, GPUTexture** host_texture, bool update_di
return !sw.HasError(); return !sw.HasError();
} }
bool System::LoadBIOS() bool System::LoadBIOS(const std::string& override_bios_path)
{ {
std::optional<BIOS::Image> bios_image(BIOS::GetBIOSImage(s_region)); std::optional<BIOS::Image> bios_image(
override_bios_path.empty() ? BIOS::GetBIOSImage(s_region) : FileSystem::ReadBinaryFile(override_bios_path.c_str()));
if (!bios_image.has_value()) if (!bios_image.has_value())
{ {
Host::ReportFormattedErrorAsync("Error", Host::TranslateString("System", "Failed to load %s BIOS."), Host::ReportFormattedErrorAsync("Error", Host::TranslateString("System", "Failed to load %s BIOS."),

View File

@ -39,6 +39,7 @@ struct SystemBootParameters
std::string filename; std::string filename;
std::string save_state; std::string save_state;
std::string override_exe; std::string override_exe;
std::string override_bios;
std::optional<bool> override_fast_boot; std::optional<bool> override_fast_boot;
std::optional<bool> override_fullscreen; std::optional<bool> override_fullscreen;
std::optional<bool> override_start_paused; std::optional<bool> override_start_paused;
@ -223,7 +224,6 @@ void ApplySettings(bool display_osd_messages);
bool ReloadGameSettings(bool display_osd_messages); bool ReloadGameSettings(bool display_osd_messages);
bool BootSystem(SystemBootParameters parameters); bool BootSystem(SystemBootParameters parameters);
bool ReinitializeSystem(ConsoleRegion region, const char* bios_path, const char* media_path, bool fast_boot);
void PauseSystem(bool paused); void PauseSystem(bool paused);
void ResetSystem(); void ResetSystem();

View File

@ -408,7 +408,9 @@ void EmuThread::applySettings(bool display_osd_messages /* = false */)
} }
System::ApplySettings(display_osd_messages); System::ApplySettings(display_osd_messages);
if (!FullscreenUI::IsInitialized() && System::IsPaused()) if (!FullscreenUI::IsInitialized() && !System::IsValid())
setInitialState(std::nullopt);
else if (!FullscreenUI::IsInitialized() && System::IsPaused())
redrawDisplayWindow(); redrawDisplayWindow();
} }
@ -1104,6 +1106,9 @@ void EmuThread::joinNetplaySession(const QString& nickname, const QString& hostn
errorReported(tr("Netplay Error"), tr("Failed to join netplay session. The log may contain more information.")); errorReported(tr("Netplay Error"), tr("Failed to join netplay session. The log may contain more information."));
return; return;
} }
// Exit the event loop, we'll take it from here.
g_emu_thread->wakeThread();
} }
void EmuThread::runOnEmuThread(std::function<void()> callback) void EmuThread::runOnEmuThread(std::function<void()> callback)
@ -1451,11 +1456,12 @@ void EmuThread::run()
// bind buttons/axises // bind buttons/axises
createBackgroundControllerPollTimer(); createBackgroundControllerPollTimer();
startBackgroundControllerPollTimer(); startBackgroundControllerPollTimer();
setInitialState(std::nullopt);
// main loop // main loop
while (!m_shutdown_flag) while (!m_shutdown_flag)
{ {
if (Netplay::IsActive() && System::IsValid()) if (Netplay::IsActive())
{ {
Netplay::ExecuteNetplay(); Netplay::ExecuteNetplay();
} }