diff --git a/src/ModMan/Context/FastFileContext.cpp b/src/ModMan/Context/FastFileContext.cpp index 2a5f8112..1027b488 100644 --- a/src/ModMan/Context/FastFileContext.cpp +++ b/src/ModMan/Context/FastFileContext.cpp @@ -10,7 +10,7 @@ namespace fs = std::filesystem; namespace { - constexpr double MIN_PROGRESS_TO_REPORT = 0.005; + constexpr double MIN_PROGRESS_TO_REPORT = 0.5; class LoadingEventProgressReporter : public ProgressCallback { @@ -23,7 +23,7 @@ namespace void OnProgress(const size_t current, const size_t total) override { - const double percentage = static_cast(current) / static_cast(total); + const double percentage = static_cast(current) / static_cast(total) * 100.0; if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT) { @@ -38,38 +38,53 @@ namespace }; } // namespace +LoadedZone::LoadedZone(std::unique_ptr zone, std::string filePath) + : m_zone(std::move(zone)), + m_file_path(std::move(filePath)) +{ +} + void FastFileContext::Destroy() { // Unload all zones m_loaded_zones.clear(); } -result::Expected FastFileContext::LoadFastFile(const std::string& path) +result::Expected FastFileContext::LoadFastFile(const std::string& path) { auto zone = ZoneLoading::LoadZone(path, std::make_unique(fs::path(path).filename().replace_extension().string())); if (!zone) return result::Unexpected(std::move(zone.error())); - auto* result = m_loaded_zones.emplace_back(std::move(*zone)).get(); + auto loadedZone = std::make_unique(std::move(*zone), path); - ui::NotifyZoneLoaded(result->m_name, path); + LoadedZone* result; + { + std::lock_guard lock(m_zone_lock); + result = m_loaded_zones.emplace_back(std::move(loadedZone)).get(); + } + + ui::NotifyZoneLoaded(*result); return result; } result::Expected FastFileContext::UnloadZone(const std::string& zoneName) { - const auto existingZone = std::ranges::find_if(m_loaded_zones, - [&zoneName](const std::unique_ptr& zone) - { - return zone->m_name == zoneName; - }); - - if (existingZone != m_loaded_zones.end()) { - m_loaded_zones.erase(existingZone); - ui::NotifyZoneUnloaded(zoneName); - return NoResult(); + std::lock_guard lock(m_zone_lock); + const auto existingZone = std::ranges::find_if(m_loaded_zones, + [&zoneName](const std::unique_ptr& loadedZone) + { + return loadedZone->m_zone->m_name == zoneName; + }); + + if (existingZone != m_loaded_zones.end()) + { + m_loaded_zones.erase(existingZone); + ui::NotifyZoneUnloaded(zoneName); + return NoResult(); + } } return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); diff --git a/src/ModMan/Context/FastFileContext.h b/src/ModMan/Context/FastFileContext.h index a3f87a42..e769e34f 100644 --- a/src/ModMan/Context/FastFileContext.h +++ b/src/ModMan/Context/FastFileContext.h @@ -4,15 +4,26 @@ #include "Zone/Zone.h" #include +#include #include +class LoadedZone +{ +public: + std::unique_ptr m_zone; + std::string m_file_path; + + LoadedZone(std::unique_ptr zone, std::string filePath); +}; + class FastFileContext { public: void Destroy(); - result::Expected LoadFastFile(const std::string& path); + result::Expected LoadFastFile(const std::string& path); result::Expected UnloadZone(const std::string& zoneName); - std::vector> m_loaded_zones; + std::vector> m_loaded_zones; + std::shared_mutex m_zone_lock; }; diff --git a/src/ModMan/Web/Binds/UnlinkingBinds.cpp b/src/ModMan/Web/Binds/UnlinkingBinds.cpp index 82d9d9b2..ba3f7a58 100644 --- a/src/ModMan/Web/Binds/UnlinkingBinds.cpp +++ b/src/ModMan/Web/Binds/UnlinkingBinds.cpp @@ -23,7 +23,7 @@ namespace NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnlinkProgressDto, zoneName, percentage); - constexpr double MIN_PROGRESS_TO_REPORT = 0.005; + constexpr double MIN_PROGRESS_TO_REPORT = 0.5; class UnlinkingEventProgressReporter : public ProgressCallback { @@ -36,7 +36,7 @@ namespace void OnProgress(const size_t current, const size_t total) override { - const double percentage = static_cast(current) / static_cast(total); + const double percentage = static_cast(current) / static_cast(total) * 100.0; if (percentage - m_last_progress >= MIN_PROGRESS_TO_REPORT) { @@ -54,17 +54,17 @@ namespace { const auto& context = ModManContext::Get().m_fast_file; const auto existingZone = std::ranges::find_if(context.m_loaded_zones, - [&zoneName](const std::unique_ptr& zone) + [&zoneName](const std::unique_ptr& loadedZone) { - return zone->m_name == zoneName; + return loadedZone->m_zone->m_name == zoneName; }); if (existingZone == context.m_loaded_zones.end()) return result::Unexpected(std::format("No zone with name {} loaded", zoneName)); - const auto& zone = *existingZone->get(); + const auto& loadedZone = *existingZone->get(); - const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game_id); + const auto* objWriter = IObjWriter::GetObjWriterForGame(loadedZone.m_zone->m_game_id); const auto outputFolderPath = fs::path(utils::GetExecutablePath()).parent_path() / "zone_dump" / zoneName; const auto outputFolderPathStr = outputFolderPath.string(); @@ -72,7 +72,7 @@ namespace OutputPathFilesystem outputFolderOutputPath(outputFolderPath); SearchPaths searchPaths; AssetDumpingContext dumpingContext( - zone, outputFolderPathStr, outputFolderOutputPath, searchPaths, std::make_unique(zoneName)); + *loadedZone.m_zone, outputFolderPathStr, outputFolderOutputPath, searchPaths, std::make_unique(zoneName)); objWriter->DumpZone(dumpingContext); return NoResult(); diff --git a/src/ModMan/Web/Binds/ZoneBinds.cpp b/src/ModMan/Web/Binds/ZoneBinds.cpp index fd538376..01b2fd70 100644 --- a/src/ModMan/Web/Binds/ZoneBinds.cpp +++ b/src/ModMan/Web/Binds/ZoneBinds.cpp @@ -7,6 +7,15 @@ namespace { + class ZoneDto + { + public: + std::string name; + std::string filePath; + }; + + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneDto, name, filePath); + class ZoneLoadProgressDto { public: @@ -19,11 +28,10 @@ namespace class ZoneLoadedDto { public: - std::string zoneName; - std::string filePath; + ZoneDto zone; }; - NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zoneName, filePath); + NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneLoadedDto, zone); class ZoneUnloadedDto { @@ -33,6 +41,33 @@ namespace NLOHMANN_DEFINE_TYPE_EXTENSION(ZoneUnloadedDto, zoneName); + ZoneDto CreateZoneDto(const LoadedZone& loadedZone) + { + return ZoneDto{ + .name = loadedZone.m_zone->m_name, + .filePath = loadedZone.m_file_path, + }; + } + + std::vector GetLoadedZones() + { + auto& context = ModManContext::Get().m_fast_file; + + std::vector result; + + { + std::shared_lock lock(context.m_zone_lock); + result.reserve(context.m_loaded_zones.size()); + + for (const auto& loadedZone : context.m_loaded_zones) + { + result.emplace_back(CreateZoneDto(*loadedZone)); + } + } + + return result; + } + void LoadFastFile(webview::webview& wv, std::string id, std::string path) // NOLINT(performance-unnecessary-value-param) Copy is made for thread safety { ModManContext::Get().m_db_thread.Dispatch( @@ -45,10 +80,9 @@ namespace ui::PromiseResolve(wv, id, ZoneLoadedDto{ - .zoneName = maybeZone.value()->m_name, - .filePath = path, + .zone = CreateZoneDto(*maybeZone.value()), }); - con::debug("Loaded zone \"{}\"", maybeZone.value()->m_name); + con::debug("Loaded zone \"{}\"", maybeZone.value()->m_zone->m_name); } else { @@ -89,11 +123,10 @@ namespace ui Notify(*ModManContext::Get().m_main_webview, "zoneLoadProgress", dto); } - void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath) + void NotifyZoneLoaded(const LoadedZone& loadedZone) { const ZoneLoadedDto dto{ - .zoneName = std::move(zoneName), - .filePath = std::move(fastFilePath), + .zone = CreateZoneDto(loadedZone), }; Notify(*ModManContext::Get().m_main_webview, "zoneLoaded", dto); } @@ -108,6 +141,13 @@ namespace ui void RegisterZoneBinds(webview::webview& wv) { + BindRetOnly>(wv, + "getZones", + [] + { + return GetLoadedZones(); + }); + BindAsync(wv, "loadFastFile", [&wv](const std::string& id, std::string path) diff --git a/src/ModMan/Web/Binds/ZoneBinds.h b/src/ModMan/Web/Binds/ZoneBinds.h index 142d5e5b..ee2efda8 100644 --- a/src/ModMan/Web/Binds/ZoneBinds.h +++ b/src/ModMan/Web/Binds/ZoneBinds.h @@ -1,11 +1,12 @@ #pragma once +#include "Context/FastFileContext.h" #include "Web/WebViewLib.h" namespace ui { void NotifyZoneLoadProgress(std::string zoneName, double percentage); - void NotifyZoneLoaded(std::string zoneName, std::string fastFilePath); + void NotifyZoneLoaded(const LoadedZone& loadedZone); void NotifyZoneUnloaded(std::string zoneName); void RegisterZoneBinds(webview::webview& wv); diff --git a/src/ModManUi/src/App.vue b/src/ModManUi/src/App.vue index d79530fc..8e4f6e0f 100644 --- a/src/ModManUi/src/App.vue +++ b/src/ModManUi/src/App.vue @@ -1,80 +1,21 @@ @@ -113,6 +36,7 @@ webviewAddEventListener("zoneUnlinkProgress", (dto) => { display: flex; justify-content: center; column-gap: 0.5em; + margin-top: 1em; } .zone-list { diff --git a/src/ModManUi/src/components/ZoneSelector.vue b/src/ModManUi/src/components/ZoneSelector.vue index bc098574..2e4ab4b0 100644 --- a/src/ModManUi/src/components/ZoneSelector.vue +++ b/src/ModManUi/src/components/ZoneSelector.vue @@ -1,17 +1,46 @@