Merge pull request #10235 from AdmiralCurtiss/netplay-save-sync-boot

Netplay: Fix possible Wii save restore race condition between Netplay and CPU threads on game shutdown by making the Wii Save Sync data part of the BootParameters.
This commit is contained in:
Léo Lam 2021-11-23 16:19:56 +01:00 committed by GitHub
commit ba62019eb5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 259 additions and 113 deletions

View File

@ -549,8 +549,7 @@ static float GetRenderSurfaceScale(JNIEnv* env)
}
static void Run(JNIEnv* env, const std::vector<std::string>& paths, bool riivolution,
const std::optional<std::string>& savestate_path = {},
bool delete_savestate = false)
BootSessionData boot_session_data = BootSessionData())
{
ASSERT(!paths.empty());
__android_log_print(ANDROID_LOG_INFO, DOLPHIN_TAG, "Running : %s", paths[0].c_str());
@ -561,9 +560,8 @@ static void Run(JNIEnv* env, const std::vector<std::string>& paths, bool riivolu
s_have_wm_user_stop = false;
std::unique_ptr<BootParameters> boot = BootParameters::GenerateFromFile(paths, savestate_path);
if (boot)
boot->delete_savestate = delete_savestate;
std::unique_ptr<BootParameters> boot =
BootParameters::GenerateFromFile(paths, std::move(boot_session_data));
if (riivolution && std::holds_alternative<BootParameters::Disc>(boot->parameters))
{
@ -638,8 +636,10 @@ Java_org_dolphinemu_dolphinemu_NativeLibrary_Run___3Ljava_lang_String_2ZLjava_la
JNIEnv* env, jclass, jobjectArray jPaths, jboolean jRiivolution, jstring jSavestate,
jboolean jDeleteSavestate)
{
Run(env, JStringArrayToVector(env, jPaths), jRiivolution, GetJString(env, jSavestate),
jDeleteSavestate);
DeleteSavestateAfterBoot delete_state =
jDeleteSavestate ? DeleteSavestateAfterBoot::Yes : DeleteSavestateAfterBoot::No;
Run(env, JStringArrayToVector(env, jPaths), jRiivolution,
BootSessionData(GetJString(env, jSavestate), delete_state));
}
JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_NativeLibrary_ChangeDisc(JNIEnv* env, jclass,

View File

@ -112,22 +112,77 @@ static std::vector<std::string> ReadM3UFile(const std::string& m3u_path,
return result;
}
BootParameters::BootParameters(Parameters&& parameters_,
const std::optional<std::string>& savestate_path_)
: parameters(std::move(parameters_)), savestate_path(savestate_path_)
BootSessionData::BootSessionData()
{
}
std::unique_ptr<BootParameters>
BootParameters::GenerateFromFile(std::string boot_path,
const std::optional<std::string>& savestate_path)
BootSessionData::BootSessionData(std::optional<std::string> savestate_path,
DeleteSavestateAfterBoot delete_savestate)
: m_savestate_path(std::move(savestate_path)), m_delete_savestate(delete_savestate)
{
return GenerateFromFile(std::vector<std::string>{std::move(boot_path)}, savestate_path);
}
std::unique_ptr<BootParameters>
BootParameters::GenerateFromFile(std::vector<std::string> paths,
const std::optional<std::string>& savestate_path)
BootSessionData::BootSessionData(BootSessionData&& other) = default;
BootSessionData& BootSessionData::operator=(BootSessionData&& other) = default;
BootSessionData::~BootSessionData() = default;
const std::optional<std::string>& BootSessionData::GetSavestatePath() const
{
return m_savestate_path;
}
DeleteSavestateAfterBoot BootSessionData::GetDeleteSavestate() const
{
return m_delete_savestate;
}
void BootSessionData::SetSavestateData(std::optional<std::string> savestate_path,
DeleteSavestateAfterBoot delete_savestate)
{
m_savestate_path = std::move(savestate_path);
m_delete_savestate = delete_savestate;
}
IOS::HLE::FS::FileSystem* BootSessionData::GetWiiSyncFS() const
{
return m_wii_sync_fs.get();
}
const std::vector<u64>& BootSessionData::GetWiiSyncTitles() const
{
return m_wii_sync_titles;
}
void BootSessionData::InvokeWiiSyncCleanup() const
{
if (m_wii_sync_cleanup)
m_wii_sync_cleanup();
}
void BootSessionData::SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs,
std::vector<u64> titles, WiiSyncCleanupFunction cleanup)
{
m_wii_sync_fs = std::move(fs);
m_wii_sync_titles = std::move(titles);
m_wii_sync_cleanup = std::move(cleanup);
}
BootParameters::BootParameters(Parameters&& parameters_, BootSessionData boot_session_data_)
: parameters(std::move(parameters_)), boot_session_data(std::move(boot_session_data_))
{
}
std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(std::string boot_path,
BootSessionData boot_session_data_)
{
return GenerateFromFile(std::vector<std::string>{std::move(boot_path)},
std::move(boot_session_data_));
}
std::unique_ptr<BootParameters> BootParameters::GenerateFromFile(std::vector<std::string> paths,
BootSessionData boot_session_data_)
{
ASSERT(!paths.empty());
@ -176,21 +231,21 @@ BootParameters::GenerateFromFile(std::vector<std::string> paths,
if (disc)
{
return std::make_unique<BootParameters>(Disc{std::move(path), std::move(disc), paths},
savestate_path);
std::move(boot_session_data_));
}
if (extension == ".elf")
{
auto elf_reader = std::make_unique<ElfReader>(path);
return std::make_unique<BootParameters>(Executable{std::move(path), std::move(elf_reader)},
savestate_path);
std::move(boot_session_data_));
}
if (extension == ".dol")
{
auto dol_reader = std::make_unique<DolReader>(path);
return std::make_unique<BootParameters>(Executable{std::move(path), std::move(dol_reader)},
savestate_path);
std::move(boot_session_data_));
}
if (is_drive)
@ -209,13 +264,13 @@ BootParameters::GenerateFromFile(std::vector<std::string> paths,
}
if (extension == ".dff")
return std::make_unique<BootParameters>(DFF{std::move(path)}, savestate_path);
return std::make_unique<BootParameters>(DFF{std::move(path)}, std::move(boot_session_data_));
if (extension == ".wad")
{
std::unique_ptr<DiscIO::VolumeWAD> wad = DiscIO::CreateWAD(std::move(path));
if (wad)
return std::make_unique<BootParameters>(std::move(*wad), savestate_path);
return std::make_unique<BootParameters>(std::move(*wad), std::move(boot_session_data_));
}
if (extension == ".json")
@ -223,7 +278,7 @@ BootParameters::GenerateFromFile(std::vector<std::string> paths,
auto descriptor = DiscIO::ParseGameModDescriptorFile(path);
if (descriptor)
{
auto boot_params = GenerateFromFile(descriptor->base_file, savestate_path);
auto boot_params = GenerateFromFile(descriptor->base_file, std::move(boot_session_data_));
if (!boot_params)
{
PanicAlertFmtT("Could not recognize file {0}", descriptor->base_file);

View File

@ -24,6 +24,11 @@ namespace File
class IOFile;
}
namespace IOS::HLE::FS
{
class FileSystem;
}
struct RegionSetting
{
std::string area;
@ -34,6 +39,46 @@ struct RegionSetting
class BootExecutableReader;
enum class DeleteSavestateAfterBoot : u8
{
No,
Yes
};
class BootSessionData
{
public:
BootSessionData();
BootSessionData(std::optional<std::string> savestate_path,
DeleteSavestateAfterBoot delete_savestate);
BootSessionData(const BootSessionData& other) = delete;
BootSessionData(BootSessionData&& other);
BootSessionData& operator=(const BootSessionData& other) = delete;
BootSessionData& operator=(BootSessionData&& other);
~BootSessionData();
const std::optional<std::string>& GetSavestatePath() const;
DeleteSavestateAfterBoot GetDeleteSavestate() const;
void SetSavestateData(std::optional<std::string> savestate_path,
DeleteSavestateAfterBoot delete_savestate);
using WiiSyncCleanupFunction = std::function<void()>;
IOS::HLE::FS::FileSystem* GetWiiSyncFS() const;
const std::vector<u64>& GetWiiSyncTitles() const;
void InvokeWiiSyncCleanup() const;
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, std::vector<u64> titles,
WiiSyncCleanupFunction cleanup);
private:
std::optional<std::string> m_savestate_path;
DeleteSavestateAfterBoot m_delete_savestate = DeleteSavestateAfterBoot::No;
std::unique_ptr<IOS::HLE::FS::FileSystem> m_wii_sync_fs;
std::vector<u64> m_wii_sync_titles;
WiiSyncCleanupFunction m_wii_sync_cleanup;
};
struct BootParameters
{
struct Disc
@ -70,18 +115,17 @@ struct BootParameters
};
static std::unique_ptr<BootParameters>
GenerateFromFile(std::string boot_path, const std::optional<std::string>& savestate_path = {});
GenerateFromFile(std::string boot_path, BootSessionData boot_session_data_ = BootSessionData());
static std::unique_ptr<BootParameters>
GenerateFromFile(std::vector<std::string> paths,
const std::optional<std::string>& savestate_path = {});
BootSessionData boot_session_data_ = BootSessionData());
using Parameters = std::variant<Disc, Executable, DiscIO::VolumeWAD, NANDTitle, IPL, DFF>;
BootParameters(Parameters&& parameters_, const std::optional<std::string>& savestate_path_ = {});
BootParameters(Parameters&& parameters_, BootSessionData boot_session_data_ = BootSessionData());
Parameters parameters;
std::vector<DiscIO::Riivolution::Patch> riivolution_patches;
std::optional<std::string> savestate_path;
bool delete_savestate = false;
BootSessionData boot_session_data;
};
class CBoot

View File

@ -450,7 +450,7 @@ bool BootCore(std::unique_ptr<BootParameters> boot, const WindowSystemInfo& wsi)
std::make_unique<BootParameters>(
BootParameters::IPL{StartUp.m_region,
std::move(std::get<BootParameters::Disc>(boot->parameters))},
boot->savestate_path),
std::move(boot->boot_session_data)),
wsi);
}
return Core::Init(std::move(boot), wsi);

View File

@ -479,8 +479,10 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
Keyboard::LoadConfig();
}
const std::optional<std::string> savestate_path = boot->savestate_path;
const bool delete_savestate = boot->delete_savestate;
BootSessionData boot_session_data = std::move(boot->boot_session_data);
const std::optional<std::string>& savestate_path = boot_session_data.GetSavestatePath();
const bool delete_savestate =
boot_session_data.GetDeleteSavestate() == DeleteSavestateAfterBoot::Yes;
// Load and Init Wiimotes - only if we are booting in Wii mode
bool init_wiimotes = false;
@ -615,9 +617,12 @@ static void EmuThread(std::unique_ptr<BootParameters> boot, WindowSystemInfo wsi
// Initialise Wii filesystem contents.
// This is done here after Boot and not in BootManager to ensure that we operate
// with the correct title context since save copying requires title directories to exist.
Common::ScopeGuard wiifs_guard{&Core::CleanUpWiiFileSystemContents};
Common::ScopeGuard wiifs_guard{[&boot_session_data] {
Core::CleanUpWiiFileSystemContents(boot_session_data);
boot_session_data.InvokeWiiSyncCleanup();
}};
if (SConfig::GetInstance().bWii)
Core::InitializeWiiFileSystemContents(savegame_redirect);
Core::InitializeWiiFileSystemContents(savegame_redirect, boot_session_data);
else
wiifs_guard.Dismiss();

View File

@ -33,6 +33,7 @@
#include "Common/Version.h"
#include "Core/ActionReplay.h"
#include "Core/Boot/Boot.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/NetplaySettings.h"
#include "Core/Config/SessionSettings.h"
@ -75,8 +76,6 @@ using namespace WiimoteCommon;
static std::mutex crit_netplay_client;
static NetPlayClient* netplay_client = nullptr;
static std::unique_ptr<IOS::HLE::FS::FileSystem> s_wii_sync_fs;
static std::vector<u64> s_wii_sync_titles;
static bool s_si_poll_batching = false;
// called from ---GUI--- thread
@ -1191,7 +1190,7 @@ void NetPlayClient::OnSyncSaveDataWii(sf::Packet& packet)
}
}
SetWiiSyncData(std::move(temp_fs), titles);
SetWiiSyncData(std::move(temp_fs), std::move(titles));
SyncSaveDataResponse(true);
}
@ -1721,7 +1720,14 @@ bool NetPlayClient::StartGame(const std::string& path)
}
// boot game
m_dialog->BootGame(path);
auto boot_session_data = std::make_unique<BootSessionData>();
boot_session_data->SetWiiSyncData(std::move(m_wii_sync_fs), std::move(m_wii_sync_titles), [] {
// on emulation end clean up the Wii save sync directory -- see OnSyncSaveDataWii()
const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP;
if (File::Exists(path))
File::DeleteDirRecursively(path);
});
m_dialog->BootGame(path, std::move(boot_session_data));
UpdateDevices();
@ -2251,8 +2257,6 @@ bool NetPlayClient::StopGame()
// stop game
m_dialog->StopGame();
ClearWiiSyncData();
return true;
}
@ -2496,6 +2500,13 @@ void NetPlayClient::AdjustPadBufferSize(const unsigned int size)
m_dialog->OnPadBufferChanged(size);
}
void NetPlayClient::SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs,
std::vector<u64> titles)
{
m_wii_sync_fs = std::move(fs);
m_wii_sync_titles = std::move(titles);
}
SyncIdentifier NetPlayClient::GetSDCardIdentifier()
{
return SyncIdentifier{{}, "sd", {}, {}, {}, {}};
@ -2538,33 +2549,6 @@ const NetSettings& GetNetSettings()
return netplay_client->GetNetSettings();
}
IOS::HLE::FS::FileSystem* GetWiiSyncFS()
{
return s_wii_sync_fs.get();
}
const std::vector<u64>& GetWiiSyncTitles()
{
return s_wii_sync_titles;
}
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, const std::vector<u64>& titles)
{
s_wii_sync_fs = std::move(fs);
s_wii_sync_titles.insert(s_wii_sync_titles.end(), titles.begin(), titles.end());
}
void ClearWiiSyncData()
{
// We're just assuming it will always be here because it is
const std::string path = File::GetUserPath(D_USER_IDX) + "Wii" GC_MEMCARD_NETPLAY DIR_SEP;
if (File::Exists(path))
File::DeleteDirRecursively(path);
s_wii_sync_fs.reset();
s_wii_sync_titles.clear();
}
void SetSIPollBatching(bool state)
{
s_si_poll_batching = state;

View File

@ -23,6 +23,13 @@
#include "Core/SyncIdentifier.h"
#include "InputCommon/GCPadStatus.h"
class BootSessionData;
namespace IOS::HLE::FS
{
class FileSystem;
}
namespace UICommon
{
class GameFile;
@ -34,7 +41,8 @@ class NetPlayUI
{
public:
virtual ~NetPlayUI() {}
virtual void BootGame(const std::string& filename) = 0;
virtual void BootGame(const std::string& filename,
std::unique_ptr<BootSessionData> boot_session_data) = 0;
virtual void StopGame() = 0;
virtual bool IsHosting() const = 0;
@ -77,6 +85,8 @@ public:
const std::vector<int>& players) = 0;
virtual void HideChunkedProgressDialog() = 0;
virtual void SetChunkedProgress(int pid, u64 progress) = 0;
virtual void SetHostWiiSyncTitles(std::vector<u64> titles) = 0;
};
class Player
@ -147,6 +157,8 @@ public:
void AdjustPadBufferSize(unsigned int size);
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, std::vector<u64> titles);
static SyncIdentifier GetSDCardIdentifier();
protected:
@ -313,6 +325,9 @@ private:
u64 m_initial_rtc = 0;
u32 m_timebase_frame = 0;
std::unique_ptr<IOS::HLE::FS::FileSystem> m_wii_sync_fs;
std::vector<u64> m_wii_sync_titles;
};
void NetPlay_Enable(NetPlayClient* const np);

View File

@ -257,10 +257,6 @@ bool IsNetPlayRunning();
// Precondition: A netplay client instance must be present. In other words,
// IsNetPlayRunning() must be true before calling this.
const NetSettings& GetNetSettings();
IOS::HLE::FS::FileSystem* GetWiiSyncFS();
const std::vector<u64>& GetWiiSyncTitles();
void SetWiiSyncData(std::unique_ptr<IOS::HLE::FS::FileSystem> fs, const std::vector<u64>& titles);
void ClearWiiSyncData();
void SetSIPollBatching(bool state);
void SendPowerButtonEvent();
bool IsSyncingAllWiiSaves();

View File

@ -1819,7 +1819,7 @@ bool NetPlayServer::SyncSaveData()
}
// Set titles for host-side loading in WiiRoot
SetWiiSyncData(nullptr, titles);
m_dialog->SetHostWiiSyncTitles(std::move(titles));
SendChunkedToClients(std::move(pac), 1, "Wii Save Synchronization");
}

View File

@ -17,6 +17,7 @@
#include "Common/Logging/Log.h"
#include "Common/NandPaths.h"
#include "Common/StringUtil.h"
#include "Core/Boot/Boot.h"
#include "Core/CommonTitles.h"
#include "Core/Config/SessionSettings.h"
#include "Core/ConfigManager.h"
@ -114,7 +115,8 @@ static bool CopyNandFile(FS::FileSystem* source_fs, const std::string& source_fi
return true;
}
static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs)
static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs,
const BootSessionData& boot_session_data)
{
const u64 title_id = SConfig::GetInstance().GetTitleID();
const auto configured_fs = FS::MakeFileSystem(FS::Location::Configured);
@ -136,8 +138,8 @@ static void InitializeDeterministicWiiSaves(FS::FileSystem* session_fs)
(Movie::IsMovieActive() && !Movie::IsStartingFromClearSave()))
{
// Copy the current user's save to the Blank NAND
auto* sync_fs = NetPlay::GetWiiSyncFS();
auto& sync_titles = NetPlay::GetWiiSyncTitles();
auto* sync_fs = boot_session_data.GetWiiSyncFS();
auto& sync_titles = boot_session_data.GetWiiSyncTitles();
if (sync_fs)
{
for (const u64 title : sync_titles)
@ -298,7 +300,8 @@ static bool CopySysmenuFilesToFS(FS::FileSystem* fs, const std::string& host_sou
}
void InitializeWiiFileSystemContents(
std::optional<DiscIO::Riivolution::SavegameRedirect> save_redirect)
std::optional<DiscIO::Riivolution::SavegameRedirect> save_redirect,
const BootSessionData& boot_session_data)
{
const auto fs = IOS::HLE::GetIOS()->GetFS();
@ -315,7 +318,7 @@ void InitializeWiiFileSystemContents(
SysConf sysconf{fs};
sysconf.Save();
InitializeDeterministicWiiSaves(fs.get());
InitializeDeterministicWiiSaves(fs.get(), boot_session_data);
}
else if (save_redirect)
{
@ -336,10 +339,10 @@ void InitializeWiiFileSystemContents(
}
}
void CleanUpWiiFileSystemContents()
void CleanUpWiiFileSystemContents(const BootSessionData& boot_session_data)
{
if (!WiiRootIsTemporary() || !Config::Get(Config::SESSION_SAVE_DATA_WRITABLE) ||
NetPlay::GetWiiSyncFS())
boot_session_data.GetWiiSyncFS())
{
return;
}

View File

@ -8,6 +8,8 @@
#include "DiscIO/RiivolutionPatcher.h"
class BootSessionData;
namespace IOS::HLE::FS
{
struct NandRedirect;
@ -32,8 +34,9 @@ void RestoreWiiSettings(RestoreReason reason);
// Initialize or clean up the filesystem contents.
void InitializeWiiFileSystemContents(
std::optional<DiscIO::Riivolution::SavegameRedirect> save_redirect);
void CleanUpWiiFileSystemContents();
std::optional<DiscIO::Riivolution::SavegameRedirect> save_redirect,
const BootSessionData& boot_session_data);
void CleanUpWiiFileSystemContents(const BootSessionData& boot_session_data);
const std::vector<IOS::HLE::FS::NandRedirect>& GetActiveNandRedirects();
} // namespace Core

View File

@ -187,7 +187,8 @@ int main(int argc, char* argv[])
const std::list<std::string> paths_list = options.all("exec");
const std::vector<std::string> paths{std::make_move_iterator(std::begin(paths_list)),
std::make_move_iterator(std::end(paths_list))};
boot = BootParameters::GenerateFromFile(paths, save_state_path);
boot = BootParameters::GenerateFromFile(
paths, BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
game_specified = true;
}
else if (options.is_set("nand_title"))
@ -204,7 +205,8 @@ int main(int argc, char* argv[])
}
else if (args.size())
{
boot = BootParameters::GenerateFromFile(args.front(), save_state_path);
boot = BootParameters::GenerateFromFile(
args.front(), BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
args.erase(args.begin());
game_specified = true;
}

View File

@ -197,7 +197,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
const std::list<std::string> paths_list = options.all("exec");
const std::vector<std::string> paths{std::make_move_iterator(std::begin(paths_list)),
std::make_move_iterator(std::end(paths_list))};
boot = BootParameters::GenerateFromFile(paths, save_state_path);
boot = BootParameters::GenerateFromFile(
paths, BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
game_specified = true;
}
else if (options.is_set("nand_title"))
@ -216,7 +217,8 @@ int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PWSTR pCmdLine
}
else if (!args.empty())
{
boot = BootParameters::GenerateFromFile(args.front(), save_state_path);
boot = BootParameters::GenerateFromFile(
args.front(), BootSessionData(save_state_path, DeleteSavestateAfterBoot::No));
game_specified = true;
}

View File

@ -244,8 +244,13 @@ MainWindow::MainWindow(std::unique_ptr<BootParameters> boot_parameters,
if (!movie_path.empty())
{
if (Movie::PlayInput(movie_path, &m_pending_boot->savestate_path))
std::optional<std::string> savestate_path;
if (Movie::PlayInput(movie_path, &savestate_path))
{
m_pending_boot->boot_session_data.SetSavestateData(std::move(savestate_path),
DeleteSavestateAfterBoot::No);
emit RecordingStatusChanged(true);
}
}
}
@ -768,14 +773,16 @@ void MainWindow::Play(const std::optional<std::string>& savestate_path)
std::shared_ptr<const UICommon::GameFile> selection = m_game_list->GetSelectedGame();
if (selection)
{
StartGame(selection->GetFilePath(), ScanForSecondDisc::Yes, savestate_path);
StartGame(selection->GetFilePath(), ScanForSecondDisc::Yes,
std::make_unique<BootSessionData>(savestate_path, DeleteSavestateAfterBoot::No));
}
else
{
const QString default_path = QString::fromStdString(Config::Get(Config::MAIN_DEFAULT_ISO));
if (!default_path.isEmpty() && QFile::exists(default_path))
{
StartGame(default_path, ScanForSecondDisc::Yes, savestate_path);
StartGame(default_path, ScanForSecondDisc::Yes,
std::make_unique<BootSessionData>(savestate_path, DeleteSavestateAfterBoot::No));
}
else
{
@ -978,7 +985,7 @@ void MainWindow::ScreenShot()
}
void MainWindow::ScanForSecondDiscAndStartGame(const UICommon::GameFile& game,
const std::optional<std::string>& savestate_path)
std::unique_ptr<BootSessionData> boot_session_data)
{
auto second_game = m_game_list->FindSecondDisc(game);
@ -986,35 +993,37 @@ void MainWindow::ScanForSecondDiscAndStartGame(const UICommon::GameFile& game,
if (second_game != nullptr)
paths.push_back(second_game->GetFilePath());
StartGame(paths, savestate_path);
StartGame(paths, std::move(boot_session_data));
}
void MainWindow::StartGame(const QString& path, ScanForSecondDisc scan,
const std::optional<std::string>& savestate_path)
std::unique_ptr<BootSessionData> boot_session_data)
{
StartGame(path.toStdString(), scan, savestate_path);
StartGame(path.toStdString(), scan, std::move(boot_session_data));
}
void MainWindow::StartGame(const std::string& path, ScanForSecondDisc scan,
const std::optional<std::string>& savestate_path)
std::unique_ptr<BootSessionData> boot_session_data)
{
if (scan == ScanForSecondDisc::Yes)
{
std::shared_ptr<const UICommon::GameFile> game = m_game_list->FindGame(path);
if (game != nullptr)
{
ScanForSecondDiscAndStartGame(*game, savestate_path);
ScanForSecondDiscAndStartGame(*game, std::move(boot_session_data));
return;
}
}
StartGame(BootParameters::GenerateFromFile(path, savestate_path));
StartGame(BootParameters::GenerateFromFile(
path, boot_session_data ? std::move(*boot_session_data) : BootSessionData()));
}
void MainWindow::StartGame(const std::vector<std::string>& paths,
const std::optional<std::string>& savestate_path)
std::unique_ptr<BootSessionData> boot_session_data)
{
StartGame(BootParameters::GenerateFromFile(paths, savestate_path));
StartGame(BootParameters::GenerateFromFile(
paths, boot_session_data ? std::move(*boot_session_data) : BootSessionData()));
}
void MainWindow::StartGame(std::unique_ptr<BootParameters>&& parameters)
@ -1363,13 +1372,15 @@ void MainWindow::NetPlayInit()
{
const auto& game_list_model = m_game_list->GetGameListModel();
m_netplay_setup_dialog = new NetPlaySetupDialog(game_list_model, this);
m_netplay_dialog = new NetPlayDialog(game_list_model);
m_netplay_dialog = new NetPlayDialog(
game_list_model,
[this](const std::string& path, std::unique_ptr<BootSessionData> boot_session_data) {
StartGame(path, ScanForSecondDisc::Yes, std::move(boot_session_data));
});
#ifdef USE_DISCORD_PRESENCE
m_netplay_discord = new DiscordHandler(this);
#endif
connect(m_netplay_dialog, &NetPlayDialog::Boot, this,
[this](const QString& path) { StartGame(path, ScanForSecondDisc::Yes); });
connect(m_netplay_dialog, &NetPlayDialog::Stop, this, &MainWindow::ForceStop);
connect(m_netplay_dialog, &NetPlayDialog::rejected, this, &MainWindow::NetPlayQuit);
connect(m_netplay_setup_dialog, &NetPlaySetupDialog::Join, this, &MainWindow::NetPlayJoin);
@ -1818,8 +1829,7 @@ void MainWindow::ShowRiivolutionBootWidget(const UICommon::GameFile& game)
std::vector<std::string> paths = {game.GetFilePath()};
if (second_game != nullptr)
paths.push_back(second_game->GetFilePath());
std::unique_ptr<BootParameters> boot_params =
BootParameters::GenerateFromFile(paths, std::nullopt);
std::unique_ptr<BootParameters> boot_params = BootParameters::GenerateFromFile(paths);
if (!boot_params)
return;
if (!std::holds_alternative<BootParameters::Disc>(boot_params->parameters))

View File

@ -15,6 +15,7 @@ class QStackedWidget;
class QString;
class BreakpointWidget;
class BootSessionData;
struct BootParameters;
class CheatsManager;
class CodeWidget;
@ -132,13 +133,13 @@ private:
};
void ScanForSecondDiscAndStartGame(const UICommon::GameFile& game,
const std::optional<std::string>& savestate_path = {});
std::unique_ptr<BootSessionData> boot_session_data = nullptr);
void StartGame(const QString& path, ScanForSecondDisc scan,
const std::optional<std::string>& savestate_path = {});
std::unique_ptr<BootSessionData> boot_session_data = nullptr);
void StartGame(const std::string& path, ScanForSecondDisc scan,
const std::optional<std::string>& savestate_path = {});
std::unique_ptr<BootSessionData> boot_session_data = nullptr);
void StartGame(const std::vector<std::string>& paths,
const std::optional<std::string>& savestate_path = {});
std::unique_ptr<BootSessionData> boot_session_data = nullptr);
void StartGame(std::unique_ptr<BootParameters>&& parameters);
void ShowRenderWidget();
void HideRenderWidget(bool reinit = true, bool is_exit = false);

View File

@ -31,6 +31,7 @@
#include "Common/Logging/Log.h"
#include "Common/TraversalClient.h"
#include "Core/Boot/Boot.h"
#include "Core/Config/GraphicsSettings.h"
#include "Core/Config/MainSettings.h"
#include "Core/Config/NetplaySettings.h"
@ -39,6 +40,7 @@
#ifdef HAS_LIBMGBA
#include "Core/HW/GBACore.h"
#endif
#include "Core/IOS/FS/FileSystem.h"
#include "Core/NetPlayServer.h"
#include "Core/SyncIdentifier.h"
@ -62,8 +64,10 @@
#include "VideoCommon/RenderBase.h"
#include "VideoCommon/VideoConfig.h"
NetPlayDialog::NetPlayDialog(const GameListModel& game_list_model, QWidget* parent)
: QDialog(parent), m_game_list_model(game_list_model)
NetPlayDialog::NetPlayDialog(const GameListModel& game_list_model,
StartGameCallback start_game_callback, QWidget* parent)
: QDialog(parent), m_game_list_model(game_list_model),
m_start_game_callback(std::move(start_game_callback))
{
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
@ -682,10 +686,11 @@ void NetPlayDialog::UpdateGUI()
// NetPlayUI methods
void NetPlayDialog::BootGame(const std::string& filename)
void NetPlayDialog::BootGame(const std::string& filename,
std::unique_ptr<BootSessionData> boot_session_data)
{
m_got_stop_request = false;
emit Boot(QString::fromStdString(filename));
m_start_game_callback(filename, std::move(boot_session_data));
}
void NetPlayDialog::StopGame()
@ -1173,3 +1178,10 @@ void NetPlayDialog::SetChunkedProgress(const int pid, const u64 progress)
m_chunked_progress_dialog->SetProgress(pid, progress);
});
}
void NetPlayDialog::SetHostWiiSyncTitles(std::vector<u64> titles)
{
auto client = Settings::Instance().GetNetPlayClient();
if (client)
client->SetWiiSyncData(nullptr, std::move(titles));
}

View File

@ -3,6 +3,10 @@
#pragma once
#include <functional>
#include <memory>
#include <string>
#include <QDialog>
#include <QMenuBar>
@ -11,6 +15,7 @@
#include "DolphinQt/GameList/GameListModel.h"
#include "VideoCommon/OnScreenDisplay.h"
class BootSessionData;
class ChunkedProgressDialog;
class MD5Dialog;
class PadMappingDialog;
@ -30,14 +35,19 @@ class NetPlayDialog : public QDialog, public NetPlay::NetPlayUI
{
Q_OBJECT
public:
explicit NetPlayDialog(const GameListModel& game_list_model, QWidget* parent = nullptr);
using StartGameCallback = std::function<void(const std::string& path,
std::unique_ptr<BootSessionData> boot_session_data)>;
explicit NetPlayDialog(const GameListModel& game_list_model,
StartGameCallback start_game_callback, QWidget* parent = nullptr);
~NetPlayDialog();
void show(std::string nickname, bool use_traversal);
void reject() override;
// NetPlayUI methods
void BootGame(const std::string& filename) override;
void BootGame(const std::string& filename,
std::unique_ptr<BootSessionData> boot_session_data) override;
void StopGame() override;
bool IsHosting() const override;
@ -84,8 +94,10 @@ public:
const std::vector<int>& players) override;
void HideChunkedProgressDialog() override;
void SetChunkedProgress(int pid, u64 progress) override;
void SetHostWiiSyncTitles(std::vector<u64> titles) override;
signals:
void Boot(const QString& filename);
void Stop();
private:
@ -162,4 +174,6 @@ private:
int m_player_count = 0;
int m_old_player_count = 0;
bool m_host_input_authority = false;
StartGameCallback m_start_game_callback;
};