From e9caa09f7b28803d270e25fe2cf106bc1206cfac Mon Sep 17 00:00:00 2001 From: "Admiral H. Curtiss" Date: Sun, 30 Oct 2022 19:42:16 +0100 Subject: [PATCH] NetPlay: Split SyncSaveData() into two parts. This allows the 'save collection' part to run even if no clients are connected. Fixes issue 13095. --- Source/Core/Core/NetPlayServer.cpp | 209 +++++++++++++++++------------ Source/Core/Core/NetPlayServer.h | 5 +- 2 files changed, 126 insertions(+), 88 deletions(-) diff --git a/Source/Core/Core/NetPlayServer.cpp b/Source/Core/Core/NetPlayServer.cpp index 1c4b46216f..18076ada29 100644 --- a/Source/Core/Core/NetPlayServer.cpp +++ b/Source/Core/Core/NetPlayServer.cpp @@ -1381,6 +1381,17 @@ bool NetPlayServer::DoAllPlayersHaveHardwareFMA() const [](const auto& p) { return p.second.has_hardware_fma; }); } +struct SaveSyncInfo +{ + u8 save_count = 0; + std::shared_ptr game; + bool has_wii_save = false; + std::unique_ptr configured_fs; + std::optional> mii_data; + std::vector> wii_saves; + std::optional redirected_save; +}; + // called from ---GUI--- thread bool NetPlayServer::RequestStartGame() { @@ -1389,16 +1400,38 @@ bool NetPlayServer::RequestStartGame() bool start_now = true; - if (m_settings.savedata_load && m_players.size() > 1) + if (m_settings.savedata_load) { - start_now = false; - m_start_pending = true; - if (!SyncSaveData()) + auto save_sync_info = CollectSaveSyncInfo(); + if (!save_sync_info) { - PanicAlertFmtT("Error synchronizing save data!"); + PanicAlertFmtT("Error collecting save data!"); m_start_pending = false; return false; } + + if (save_sync_info->has_wii_save) + { + // Set titles for host-side loading in WiiRoot + std::vector titles; + for (const auto& [title_id, storage] : save_sync_info->wii_saves) + titles.push_back(title_id); + m_dialog->SetHostWiiSyncData( + std::move(titles), + save_sync_info->redirected_save ? save_sync_info->redirected_save->m_target_path : ""); + } + + if (m_players.size() > 1) + { + start_now = false; + m_start_pending = true; + if (!SyncSaveData(*save_sync_info)) + { + PanicAlertFmtT("Error synchronizing save data!"); + m_start_pending = false; + return false; + } + } } // Check To Send Codes to Clients @@ -1551,72 +1584,108 @@ void NetPlayServer::AbortGameStart() } // called from ---GUI--- thread -bool NetPlayServer::SyncSaveData() +std::optional NetPlayServer::CollectSaveSyncInfo() { - // We're about to sync saves, so set m_saves_synced to false (waits to start game) - m_saves_synced = false; - - m_save_data_synced_players = 0; - - u8 save_count = 0; + SaveSyncInfo sync_info; + sync_info.save_count = 0; for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) { if (m_settings.exi_device[slot] == ExpansionInterface::EXIDeviceType::MemoryCard || Config::Get(Config::GetInfoForEXIDevice(slot)) == ExpansionInterface::EXIDeviceType::MemoryCardFolder) { - save_count++; + ++sync_info.save_count; } } - const auto game = m_dialog->FindGameFile(m_selected_game_identifier); - if (game == nullptr) + sync_info.game = m_dialog->FindGameFile(m_selected_game_identifier); + if (sync_info.game == nullptr) { PanicAlertFmtT("Selected game doesn't exist in game list!"); - return false; + return std::nullopt; } - bool wii_save = false; - if (m_settings.savedata_load && (game->GetPlatform() == DiscIO::Platform::WiiDisc || - game->GetPlatform() == DiscIO::Platform::WiiWAD || - game->GetPlatform() == DiscIO::Platform::ELFOrDOL)) + sync_info.has_wii_save = false; + if (m_settings.savedata_load && (sync_info.game->GetPlatform() == DiscIO::Platform::WiiDisc || + sync_info.game->GetPlatform() == DiscIO::Platform::WiiWAD || + sync_info.game->GetPlatform() == DiscIO::Platform::ELFOrDOL)) { - wii_save = true; - save_count++; - } + sync_info.has_wii_save = true; + ++sync_info.save_count; - std::optional redirected_save; - if (wii_save && game->GetBlobType() == DiscIO::BlobType::MOD_DESCRIPTOR) - { - auto boot_params = BootParameters::GenerateFromFile(game->GetFilePath()); - if (boot_params) + sync_info.configured_fs = IOS::HLE::FS::MakeFileSystem(IOS::HLE::FS::Location::Configured); + if (m_settings.savedata_sync_all_wii) { - redirected_save = - DiscIO::Riivolution::ExtractSavegameRedirect(boot_params->riivolution_patches); + IOS::HLE::Kernel ios; + for (const u64 title : ios.GetES()->GetInstalledTitles()) + { + auto save = WiiSave::MakeNandStorage(sync_info.configured_fs.get(), title); + sync_info.wii_saves.emplace_back(title, std::move(save)); + } + } + else if (sync_info.game->GetPlatform() == DiscIO::Platform::WiiDisc || + sync_info.game->GetPlatform() == DiscIO::Platform::WiiWAD) + { + auto save = + WiiSave::MakeNandStorage(sync_info.configured_fs.get(), sync_info.game->GetTitleID()); + sync_info.wii_saves.emplace_back(sync_info.game->GetTitleID(), std::move(save)); + } + + { + auto file = sync_info.configured_fs->OpenFile( + IOS::PID_KERNEL, IOS::PID_KERNEL, Common::GetMiiDatabasePath(), IOS::HLE::FS::Mode::Read); + if (file) + { + std::vector file_data(file->GetStatus()->size); + if (!file->Read(file_data.data(), file_data.size())) + return std::nullopt; + sync_info.mii_data = std::move(file_data); + } + } + + if (sync_info.game->GetBlobType() == DiscIO::BlobType::MOD_DESCRIPTOR) + { + auto boot_params = BootParameters::GenerateFromFile(sync_info.game->GetFilePath()); + if (boot_params) + { + sync_info.redirected_save = + DiscIO::Riivolution::ExtractSavegameRedirect(boot_params->riivolution_patches); + } } } for (const auto& config : m_gba_config) { if (config.enabled && config.has_rom) - save_count++; + ++sync_info.save_count; } + return sync_info; +} + +// called from ---GUI--- thread +bool NetPlayServer::SyncSaveData(const SaveSyncInfo& sync_info) +{ + // We're about to sync saves, so set m_saves_synced to false (waits to start game) + m_saves_synced = false; + + m_save_data_synced_players = 0; + { sf::Packet pac; pac << MessageID::SyncSaveData; pac << SyncSaveDataID::Notify; - pac << save_count; + pac << sync_info.save_count; // send this on the chunked data channel to ensure it's sequenced properly SendAsyncToClients(std::move(pac), 0, CHUNKED_DATA_CHANNEL); } - if (save_count == 0) + if (sync_info.save_count == 0) return true; - const auto game_region = game->GetRegion(); + const auto game_region = sync_info.game->GetRegion(); const std::string region = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(game_region)); for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) @@ -1665,7 +1734,7 @@ bool NetPlayServer::SyncSaveData() if (File::IsDirectory(path)) { std::vector files = - GCMemcardDirectory::GetFileNamesForGameID(path + DIR_SEP, game->GetGameID()); + GCMemcardDirectory::GetFileNamesForGameID(path + DIR_SEP, sync_info.game->GetGameID()); pac << static_cast(files.size()); @@ -1686,67 +1755,37 @@ bool NetPlayServer::SyncSaveData() } } - if (wii_save) + if (sync_info.has_wii_save) { - const auto configured_fs = IOS::HLE::FS::MakeFileSystem(IOS::HLE::FS::Location::Configured); - - std::vector> saves; - if (m_settings.savedata_sync_all_wii) - { - IOS::HLE::Kernel ios; - for (const u64 title : ios.GetES()->GetInstalledTitles()) - { - auto save = WiiSave::MakeNandStorage(configured_fs.get(), title); - saves.push_back(std::make_pair(title, std::move(save))); - } - } - else if (game->GetPlatform() == DiscIO::Platform::WiiDisc || - game->GetPlatform() == DiscIO::Platform::WiiWAD) - { - auto save = WiiSave::MakeNandStorage(configured_fs.get(), game->GetTitleID()); - saves.push_back(std::make_pair(game->GetTitleID(), std::move(save))); - } - - std::vector titles; - sf::Packet pac; pac << MessageID::SyncSaveData; pac << SyncSaveDataID::WiiData; // Shove the Mii data into the start the packet + if (sync_info.mii_data) { - auto file = configured_fs->OpenFile(IOS::PID_KERNEL, IOS::PID_KERNEL, - Common::GetMiiDatabasePath(), IOS::HLE::FS::Mode::Read); - if (file) - { - pac << true; + pac << true; - std::vector file_data(file->GetStatus()->size); - if (!file->Read(file_data.data(), file_data.size())) - return false; - if (!CompressBufferIntoPacket(file_data, pac)) - return false; - } - else - { - pac << false; // no mii data - } + if (!CompressBufferIntoPacket(*sync_info.mii_data, pac)) + return false; + } + else + { + pac << false; // no mii data } // Carry on with the save files - pac << static_cast(saves.size()); + pac << static_cast(sync_info.wii_saves.size()); - for (const auto& pair : saves) + for (const auto& [title_id, storage] : sync_info.wii_saves) { - pac << sf::Uint64{pair.first}; - titles.push_back(pair.first); - const auto& save = pair.second; + pac << sf::Uint64{title_id}; - if (save->SaveExists()) + if (storage->SaveExists()) { - const std::optional header = save->ReadHeader(); - const std::optional bk_header = save->ReadBkHeader(); - const std::optional> files = save->ReadFiles(); + const std::optional header = storage->ReadHeader(); + const std::optional bk_header = storage->ReadBkHeader(); + const std::optional> files = storage->ReadFiles(); if (!header || !bk_header || !files) return false; @@ -1790,10 +1829,10 @@ bool NetPlayServer::SyncSaveData() } } - if (redirected_save) + if (sync_info.redirected_save) { pac << true; - if (!CompressFolderIntoPacket(redirected_save->m_target_path, pac)) + if (!CompressFolderIntoPacket(sync_info.redirected_save->m_target_path, pac)) return false; } else @@ -1801,10 +1840,6 @@ bool NetPlayServer::SyncSaveData() pac << false; // no redirected save } - // Set titles for host-side loading in WiiRoot - m_dialog->SetHostWiiSyncData(std::move(titles), - redirected_save ? redirected_save->m_target_path : ""); - SendChunkedToClients(std::move(pac), 1, "Wii Save Synchronization"); } diff --git a/Source/Core/Core/NetPlayServer.h b/Source/Core/Core/NetPlayServer.h index 89f5bf82b7..223e97e457 100644 --- a/Source/Core/Core/NetPlayServer.h +++ b/Source/Core/Core/NetPlayServer.h @@ -8,6 +8,7 @@ #include #include #include +#include #include #include #include @@ -28,6 +29,7 @@ namespace NetPlay { class NetPlayUI; +struct SaveSyncInfo; class NetPlayServer : public TraversalClientClient { @@ -120,7 +122,8 @@ private: }; bool SetupNetSettings(); - bool SyncSaveData(); + std::optional CollectSaveSyncInfo(); + bool SyncSaveData(const SaveSyncInfo& sync_info); bool SyncCodes(); void CheckSyncAndStartGame();