NetPlay: Split SyncSaveData() into two parts.

This allows the 'save collection' part to run even if no clients are connected. Fixes issue 13095.
This commit is contained in:
Admiral H. Curtiss 2022-10-30 19:42:16 +01:00
parent 950e1f94dc
commit e9caa09f7b
No known key found for this signature in database
GPG Key ID: F051B4C4044F33FB
2 changed files with 126 additions and 88 deletions

View File

@ -1381,6 +1381,17 @@ bool NetPlayServer::DoAllPlayersHaveHardwareFMA() const
[](const auto& p) { return p.second.has_hardware_fma; }); [](const auto& p) { return p.second.has_hardware_fma; });
} }
struct SaveSyncInfo
{
u8 save_count = 0;
std::shared_ptr<const UICommon::GameFile> game;
bool has_wii_save = false;
std::unique_ptr<IOS::HLE::FS::FileSystem> configured_fs;
std::optional<std::vector<u8>> mii_data;
std::vector<std::pair<u64, WiiSave::StoragePointer>> wii_saves;
std::optional<DiscIO::Riivolution::SavegameRedirect> redirected_save;
};
// called from ---GUI--- thread // called from ---GUI--- thread
bool NetPlayServer::RequestStartGame() bool NetPlayServer::RequestStartGame()
{ {
@ -1389,16 +1400,38 @@ bool NetPlayServer::RequestStartGame()
bool start_now = true; bool start_now = true;
if (m_settings.savedata_load && m_players.size() > 1) if (m_settings.savedata_load)
{ {
start_now = false; auto save_sync_info = CollectSaveSyncInfo();
m_start_pending = true; if (!save_sync_info)
if (!SyncSaveData())
{ {
PanicAlertFmtT("Error synchronizing save data!"); PanicAlertFmtT("Error collecting save data!");
m_start_pending = false; m_start_pending = false;
return false; return false;
} }
if (save_sync_info->has_wii_save)
{
// Set titles for host-side loading in WiiRoot
std::vector<u64> 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 // Check To Send Codes to Clients
@ -1551,72 +1584,108 @@ void NetPlayServer::AbortGameStart()
} }
// called from ---GUI--- thread // called from ---GUI--- thread
bool NetPlayServer::SyncSaveData() std::optional<SaveSyncInfo> NetPlayServer::CollectSaveSyncInfo()
{ {
// We're about to sync saves, so set m_saves_synced to false (waits to start game) SaveSyncInfo sync_info;
m_saves_synced = false;
m_save_data_synced_players = 0;
u8 save_count = 0;
sync_info.save_count = 0;
for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS)
{ {
if (m_settings.exi_device[slot] == ExpansionInterface::EXIDeviceType::MemoryCard || if (m_settings.exi_device[slot] == ExpansionInterface::EXIDeviceType::MemoryCard ||
Config::Get(Config::GetInfoForEXIDevice(slot)) == Config::Get(Config::GetInfoForEXIDevice(slot)) ==
ExpansionInterface::EXIDeviceType::MemoryCardFolder) ExpansionInterface::EXIDeviceType::MemoryCardFolder)
{ {
save_count++; ++sync_info.save_count;
} }
} }
const auto game = m_dialog->FindGameFile(m_selected_game_identifier); sync_info.game = m_dialog->FindGameFile(m_selected_game_identifier);
if (game == nullptr) if (sync_info.game == nullptr)
{ {
PanicAlertFmtT("Selected game doesn't exist in game list!"); PanicAlertFmtT("Selected game doesn't exist in game list!");
return false; return std::nullopt;
} }
bool wii_save = false; sync_info.has_wii_save = false;
if (m_settings.savedata_load && (game->GetPlatform() == DiscIO::Platform::WiiDisc || if (m_settings.savedata_load && (sync_info.game->GetPlatform() == DiscIO::Platform::WiiDisc ||
game->GetPlatform() == DiscIO::Platform::WiiWAD || sync_info.game->GetPlatform() == DiscIO::Platform::WiiWAD ||
game->GetPlatform() == DiscIO::Platform::ELFOrDOL)) sync_info.game->GetPlatform() == DiscIO::Platform::ELFOrDOL))
{ {
wii_save = true; sync_info.has_wii_save = true;
save_count++; ++sync_info.save_count;
}
std::optional<DiscIO::Riivolution::SavegameRedirect> redirected_save; sync_info.configured_fs = IOS::HLE::FS::MakeFileSystem(IOS::HLE::FS::Location::Configured);
if (wii_save && game->GetBlobType() == DiscIO::BlobType::MOD_DESCRIPTOR) if (m_settings.savedata_sync_all_wii)
{
auto boot_params = BootParameters::GenerateFromFile(game->GetFilePath());
if (boot_params)
{ {
redirected_save = IOS::HLE::Kernel ios;
DiscIO::Riivolution::ExtractSavegameRedirect(boot_params->riivolution_patches); 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<u8> 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) for (const auto& config : m_gba_config)
{ {
if (config.enabled && config.has_rom) 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; sf::Packet pac;
pac << MessageID::SyncSaveData; pac << MessageID::SyncSaveData;
pac << SyncSaveDataID::Notify; pac << SyncSaveDataID::Notify;
pac << save_count; pac << sync_info.save_count;
// send this on the chunked data channel to ensure it's sequenced properly // send this on the chunked data channel to ensure it's sequenced properly
SendAsyncToClients(std::move(pac), 0, CHUNKED_DATA_CHANNEL); SendAsyncToClients(std::move(pac), 0, CHUNKED_DATA_CHANNEL);
} }
if (save_count == 0) if (sync_info.save_count == 0)
return true; 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)); const std::string region = Config::GetDirectoryForRegion(Config::ToGameCubeRegion(game_region));
for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS) for (ExpansionInterface::Slot slot : ExpansionInterface::MEMCARD_SLOTS)
@ -1665,7 +1734,7 @@ bool NetPlayServer::SyncSaveData()
if (File::IsDirectory(path)) if (File::IsDirectory(path))
{ {
std::vector<std::string> files = std::vector<std::string> files =
GCMemcardDirectory::GetFileNamesForGameID(path + DIR_SEP, game->GetGameID()); GCMemcardDirectory::GetFileNamesForGameID(path + DIR_SEP, sync_info.game->GetGameID());
pac << static_cast<u8>(files.size()); pac << static_cast<u8>(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<std::pair<u64, WiiSave::StoragePointer>> 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<u64> titles;
sf::Packet pac; sf::Packet pac;
pac << MessageID::SyncSaveData; pac << MessageID::SyncSaveData;
pac << SyncSaveDataID::WiiData; pac << SyncSaveDataID::WiiData;
// Shove the Mii data into the start the packet // 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, pac << true;
Common::GetMiiDatabasePath(), IOS::HLE::FS::Mode::Read);
if (file)
{
pac << true;
std::vector<u8> file_data(file->GetStatus()->size); if (!CompressBufferIntoPacket(*sync_info.mii_data, pac))
if (!file->Read(file_data.data(), file_data.size())) return false;
return false; }
if (!CompressBufferIntoPacket(file_data, pac)) else
return false; {
} pac << false; // no mii data
else
{
pac << false; // no mii data
}
} }
// Carry on with the save files // Carry on with the save files
pac << static_cast<u32>(saves.size()); pac << static_cast<u32>(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}; pac << sf::Uint64{title_id};
titles.push_back(pair.first);
const auto& save = pair.second;
if (save->SaveExists()) if (storage->SaveExists())
{ {
const std::optional<WiiSave::Header> header = save->ReadHeader(); const std::optional<WiiSave::Header> header = storage->ReadHeader();
const std::optional<WiiSave::BkHeader> bk_header = save->ReadBkHeader(); const std::optional<WiiSave::BkHeader> bk_header = storage->ReadBkHeader();
const std::optional<std::vector<WiiSave::Storage::SaveFile>> files = save->ReadFiles(); const std::optional<std::vector<WiiSave::Storage::SaveFile>> files = storage->ReadFiles();
if (!header || !bk_header || !files) if (!header || !bk_header || !files)
return false; return false;
@ -1790,10 +1829,10 @@ bool NetPlayServer::SyncSaveData()
} }
} }
if (redirected_save) if (sync_info.redirected_save)
{ {
pac << true; pac << true;
if (!CompressFolderIntoPacket(redirected_save->m_target_path, pac)) if (!CompressFolderIntoPacket(sync_info.redirected_save->m_target_path, pac))
return false; return false;
} }
else else
@ -1801,10 +1840,6 @@ bool NetPlayServer::SyncSaveData()
pac << false; // no redirected save 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"); SendChunkedToClients(std::move(pac), 1, "Wii Save Synchronization");
} }

View File

@ -8,6 +8,7 @@
#include <map> #include <map>
#include <memory> #include <memory>
#include <mutex> #include <mutex>
#include <optional>
#include <queue> #include <queue>
#include <sstream> #include <sstream>
#include <thread> #include <thread>
@ -28,6 +29,7 @@
namespace NetPlay namespace NetPlay
{ {
class NetPlayUI; class NetPlayUI;
struct SaveSyncInfo;
class NetPlayServer : public TraversalClientClient class NetPlayServer : public TraversalClientClient
{ {
@ -120,7 +122,8 @@ private:
}; };
bool SetupNetSettings(); bool SetupNetSettings();
bool SyncSaveData(); std::optional<SaveSyncInfo> CollectSaveSyncInfo();
bool SyncSaveData(const SaveSyncInfo& sync_info);
bool SyncCodes(); bool SyncCodes();
void CheckSyncAndStartGame(); void CheckSyncAndStartGame();