diff --git a/BUILD.md b/BUILD.md index ad9eff48..dd5793f7 100644 --- a/BUILD.md +++ b/BUILD.md @@ -42,10 +42,10 @@ Notes: - Other packages may be required on other distributions. When runing the installer, choose "Custom Installation". Then, in addition to the default selection, add the following: -- Select "Desktop" under Qt -> Qt 6.8.x -- Select "Qt Http Server" under Qt -> Qt 6.8.x -> Additional Libraries -- Select "Qt Network Authorization" under Qt -> Qt 6.8.x -> Additional Libraries -- Select "Qt WebSockets" under Qt -> Qt 6.8.x -> Additional Libraries +- Select "Desktop" under Qt -> Qt 6.9.x +- Select "Qt Http Server" under Qt -> Qt 6.9.x -> Additional Libraries +- Select "Qt Network Authorization" under Qt -> Qt 6.9.x -> Additional Libraries +- Select "Qt WebSockets" under Qt -> Qt 6.9.x -> Additional Libraries If you are using a distribution that does not come with OpenSSL 3.x, you'll need to build and install it yourself: - Select "OpenSSL 3.0.x Toolkit" under Qt -> Build Tools diff --git a/CMakeLists.txt b/CMakeLists.txt index f40f2834..e39d67cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -29,7 +29,7 @@ cmake_minimum_required(VERSION 3.29) # version 0.10.5 will still need it, so we can't outright get rid of it. project(acquisition - VERSION 0.13.5 + VERSION 0.13.6 DESCRIPTION "Stash and forum shop management for Path of Exile (TM)" HOMEPAGE_URL "https://github.com/gerwaric/acquisition" LANGUAGES CXX @@ -92,6 +92,7 @@ if(NOT TARGET boost-headers-only) boost-headers-only GIT_REPOSITORY https://github.com/gerwaric/boost-headers-only.git GIT_TAG boost-1.88.0 + GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(boost-headers-only) endif() @@ -101,6 +102,7 @@ if(NOT TARGET json_struct) json_struct GIT_REPOSITORY https://github.com/jorgen/json_struct.git GIT_TAG 1.0.1 + GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(json_struct) endif() @@ -110,6 +112,7 @@ if(NOT TARGET spdlog) spdlog GIT_REPOSITORY https://github.com/gabime/spdlog.git GIT_TAG v1.15.3 + GIT_SHALLOW TRUE ) FetchContent_MakeAvailable(spdlog) endif() diff --git a/cov-build-win.cmd b/cov-build-win.cmd index 1f4da619..44868386 100644 --- a/cov-build-win.cmd +++ b/cov-build-win.cmd @@ -21,7 +21,7 @@ echo ------------------------------------------------------------- setlocal -set "QTBIN=C:\Qt\6.8.1\msvc2022_64\bin" +set "QTBIN=C:\Qt\6.9.1\msvc2022_64\bin" if not exist "%QTBIN%\." ( echo ERROR: QTBIN directory not found: "%QTBIN%" exit /B @@ -39,7 +39,7 @@ if not exist "%VCVARSALL%" ( exit /B ) -set "BUILD_DIR=.\build\Desktop_Qt_6_8_1_MSVC2022_64bit-Release" +set "BUILD_DIR=.\build\Desktop_Qt_6_9_1_MSVC2022_64bit-Release" if not exist "%BUILD_DIR%\." ( echo ERROR: build directory not found: "%BUILD_DIR%" exit /B diff --git a/deps/Crashpad/CMakeLists.txt b/deps/Crashpad/CMakeLists.txt index 9244e00e..1fba7cdf 100644 --- a/deps/Crashpad/CMakeLists.txt +++ b/deps/Crashpad/CMakeLists.txt @@ -26,5 +26,21 @@ target_link_libraries(Crashpad PRIVATE ) # Make sure the crash_handler executable is copied to the build directory. -get_target_property(CrashPadHanderPath CrashpadHandler IMPORTED_LOCATION) -file(COPY ${CrashPadHanderPath} DESTINATION ${CMAKE_BINARY_DIR}) +get_target_property(CrashPadHandlerPath CrashpadHandler IMPORTED_LOCATION) + +# Define the destination path +set(CrashpadHandlerOutput "${CrashpadHandlerDestination}/${CrashpadHandlerExe}") + +# Copy it at build time, not configure time +add_custom_command( + OUTPUT "${CrashpadHandlerOutput}" + COMMAND ${CMAKE_COMMAND} -E copy "${CrashPadHandlerPath}" "${CrashpadHandlerOutput}" + DEPENDS "${CrashPadHandlerPath}" + COMMENT "Copying crashpad_handler to build directory" + VERBATIM +) + +# Add a custom target so the file gets copied during builds +add_custom_target(copy_crashpad_handler ALL + DEPENDS "${CrashpadHandlerOutput}" +) diff --git a/deps/Crashpad/Crashpad-Linux.cmake b/deps/Crashpad/Crashpad-Linux.cmake index 93e5c7ef..20ae8ecc 100644 --- a/deps/Crashpad/Crashpad-Linux.cmake +++ b/deps/Crashpad/Crashpad-Linux.cmake @@ -1,6 +1,9 @@ +set(CrashpadHandlerExe "crashpad_handler") +set(CrashpadHandlerDestination "${CMAKE_BINARY_DIR}") + add_executable(CrashpadHandler IMPORTED) set_target_properties(CrashpadHandler PROPERTIES - IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Bin/Linux/crashpad_handler" + IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Bin/Linux/${CrashpadHandlerExe}" ) function(setup_linux_target target libname) diff --git a/deps/Crashpad/Crashpad-MacOS.cmake b/deps/Crashpad/Crashpad-MacOS.cmake index e6577ce7..b6a8b69b 100644 --- a/deps/Crashpad/Crashpad-MacOS.cmake +++ b/deps/Crashpad/Crashpad-MacOS.cmake @@ -1,31 +1,39 @@ +set(CrashpadHandlerExe "crashpad_handler") +set(CrashpadHandlerDestination "${CMAKE_BINARY_DIR}/acquisition.app/Contents/MacOS") + add_executable(CrashpadHandler IMPORTED) set_target_properties(CrashpadHandler PROPERTIES - IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Bin/MacOS/${CMAKE_SYSTEM_PROCESSOR}/crashpad_handler" + IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Bin/MacOS/${CMAKE_SYSTEM_PROCESSOR}/${CrashpadHandlerExe}" ) function(setup_macos_target target libname) - - # Point to the precompiled libraries. + set(${target}_PREBUILT_LIBS "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Libraries/MacOS/arm64/lib${libname}.a" "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Libraries/MacOS/x86_64/lib${libname}.a" ) - - # Define the output library, which will be multi-architecture. + set(${target}_OUTPUT_LIB "${CMAKE_BINARY_DIR}/crashpad/lib${libname}.a") - - # Use a custom target to cause lipo to combine the two input libraries. - add_custom_target(${target}_MULTIARCH + + add_custom_command( + OUTPUT ${${target}_OUTPUT_LIB} DEPENDS ${${target}_PREBUILT_LIBS} - BYPRODUCTS ${${target}_OUTPUT_LIB} + COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/crashpad COMMAND lipo ${${target}_PREBUILT_LIBS} -create -output ${${target}_OUTPUT_LIB} + COMMENT "Creating universal lib${libname}.a via lipo" + VERBATIM ) - - # Create the target - add_library(${target} SHARED IMPORTED) - - # Point the target to the generated multi-architecture library. - set_target_properties(${target} PROPERTIES IMPORTED_LOCATION ${${target}_OUTPUT_LIB}) + + add_custom_target(${target}_lipo ALL + DEPENDS ${${target}_OUTPUT_LIB} + ) + + add_library(${target} STATIC IMPORTED GLOBAL) + set_target_properties(${target} PROPERTIES + IMPORTED_LOCATION ${${target}_OUTPUT_LIB} + ) + + add_dependencies(${target} ${target}_lipo) endfunction() diff --git a/deps/Crashpad/Crashpad-Windows.cmake b/deps/Crashpad/Crashpad-Windows.cmake index 4a97d378..9dd4435f 100644 --- a/deps/Crashpad/Crashpad-Windows.cmake +++ b/deps/Crashpad/Crashpad-Windows.cmake @@ -1,6 +1,9 @@ +set(CrashpadHandlerExe "crashpad_handler.exe") +set(CrashpadHandlerDestination "${CMAKE_BINARY_DIR}") + add_executable(CrashpadHandler IMPORTED) set_target_properties(CrashpadHandler PROPERTIES - IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Bin/Windows/crashpad_handler.exe" + IMPORTED_LOCATION "${CMAKE_CURRENT_SOURCE_DIR}/Crashpad/Bin/Windows/${CrashpadHandlerExe}" ) function(setup_windows_target target libname) diff --git a/src/datastore/sqlitedatastore.cpp b/src/datastore/sqlitedatastore.cpp index 9fa356e9..84b1ea71 100644 --- a/src/datastore/sqlitedatastore.cpp +++ b/src/datastore/sqlitedatastore.cpp @@ -68,13 +68,12 @@ SqliteDataStore::SqliteDataStore(const QString& filename) SqliteDataStore::~SqliteDataStore() { + // Close and remove each database connection. QMutexLocker locker(&m_mutex); - const auto& connections = m_connection_names; - for (const QString& connection : connections) { + const auto &connections = m_connection_names; + for (const QString &connection : connections) { if (QSqlDatabase::contains(connection)) { - // Close and remove each database connection. - QSqlDatabase db = QSqlDatabase::database(connection); - db.close(); + QSqlDatabase::database(connection).close(); QSqlDatabase::removeDatabase(connection); }; }; diff --git a/src/itemsmanagerworker.cpp b/src/itemsmanagerworker.cpp index a31da1bf..03150ec9 100644 --- a/src/itemsmanagerworker.cpp +++ b/src/itemsmanagerworker.cpp @@ -473,6 +473,91 @@ QNetworkRequest ItemsManagerWorker::MakeOAuthCharacterRequest( return QNetworkRequest(QUrl(url)); } +bool ItemsManagerWorker::IsOAuthTabValid(rapidjson::Value &tab) +{ + // Get the name of the stash tab. + if (!HasString(tab, "name")) { + spdlog::error("The stash tab does not have a name"); + return false; + }; + + // Skip hidden tabs. + if (HasBool(tab, "hidden") && tab["hidden"].GetBool()) { + spdlog::debug("The stash tab is hidden: {}", tab["name"].GetString()); + return false; + }; + + // Get the unique id. + if (!HasString(tab, "id")) { + spdlog::error("The stash tab does not have a unique id: {}", tab["name"].GetString()); + return false; + }; + + // Get the index of this stash tab. + if (!HasInt(tab, "index")) { + spdlog::error("The stash tab does not have an index: {}", tab["name"].GetString()); + return false; + }; + + // Get the type of this stash tab. + if (!HasString(tab, "type")) { + spdlog::error("The stash tab does not have a type: {}", tab["name"].GetString()); + return false; + }; + + return true; +} + +void ItemsManagerWorker::ProcessOAuthTab(rapidjson::Value &tab, + int &count, + rapidjson_allocator &alloc) +{ + // Skip this tab if it doesn't pass sanity checks. + if (!IsOAuthTabValid(tab)) { + return; + }; + + // Get tab info. + const QString tab_name = tab["name"].GetString(); + const int tab_index = tab["index"].GetInt(); + const QString tab_type = tab["type"].GetString(); + + // The unique id for stash tabs returned from the legacy API + // need to be trimmed to 10 characters. + QString tab_id = tab["id"].GetString(); + if (tab_id.size() > 10) { + spdlog::debug("Trimming tab unique id: {}", tab_name); + tab_id = tab_id.first(10); + }; + + if (m_tab_id_index.count(tab_id) == 0) { + // Create this tab. + int r = 0, g = 0, b = 0; + Util::GetTabColor(tab, r, g, b); + ItemLocation location( + tab_index, tab_id, tab_name, ItemLocationType::STASH, tab_type, r, g, b, tab, alloc); + + // Add this tab. + m_tabs.push_back(location); + m_tab_id_index.insert(tab_id); + + // Submit a request for this tab. + if (m_update_tab_contents) { + ++count; + const auto uid = location.get_tab_uniq_id(); + QNetworkRequest request = MakeOAuthStashRequest(m_realm, m_league, uid); + QueueRequest(kOAuthGetStashEndpoint, request, location); + }; + + // Process any children. + if (tab.HasMember("children")) { + for (auto &child : tab["children"]) { + ProcessOAuthTab(child, count, alloc); + }; + }; + }; +} + void ItemsManagerWorker::OnOAuthStashListReceived(QNetworkReply* reply) { spdlog::trace("ItemsManagerWorker::OnOAuthStashListReceived() entered"); @@ -525,70 +610,12 @@ void ItemsManagerWorker::OnOAuthStashListReceived(QNetworkReply* reply) { int tabs_requested = 0; - // Queue stash tab requests. - for (auto& tab : stashes) { - - // Get the name of the stash tab. - if (!HasString(tab, "name")) { - spdlog::error("The stash tab does not have a name"); - continue; - }; - const QString tab_name = tab["name"].GetString(); - - // Skip hidden tabs. - if (HasBool(tab, "hidden") && tab["hidden"].GetBool()) { - spdlog::debug("The stash tab is hidden: {}", tab_name); - continue; - }; - - // Get the unique id. - if (!HasString(tab, "id")) { - spdlog::error("The stash tab does not have a unique id: {}", tab_name); - continue; - } - QString tab_id = tab["id"].GetString(); - if (tab_id.size() > 10) { - // The unique id for stash tabs returned from the legacy API - // need to be trimmed to 10 characters. - spdlog::debug("Trimming tab unique id: {}", tab_name); - tab_id = tab_id.first(10); - }; - - // Skip tabs that are in the index; they are not being refreshed. - if (m_tab_id_index.count(tab_id) > 0) { - spdlog::trace("ItemsManagerWorker::OnOAuthStashListReceived() skipping tab: {}", tab_name); - continue; - }; - - // Get the index of this stash tab. - if (!HasInt(tab, "index")) { - spdlog::error("The stash tab does not have an index: {}", tab_name); - continue; - }; - const int tab_index = tab["index"].GetInt(); - - // Get the type of this stash tab. - if (!HasString(tab, "type")) { - spdlog::error("The stash tab does not have a type: {}", tab_name); - continue; - }; - const QString tab_type = tab["type"].GetString(); + auto &alloc = doc.GetAllocator(); - // Get the stash tab color. - int r = 0, g = 0, b = 0; - Util::GetTabColor(tab, r, g, b); - - // Create and save the tab location object. - ItemLocation location(tab_index, tab_id, tab_name, ItemLocationType::STASH, tab_type, r, g, b, tab, doc.GetAllocator()); - m_tabs.push_back(location); - m_tab_id_index.insert(tab_id); - - // Submit a request for this tab. - if (m_update_tab_contents) { - ++tabs_requested; - QNetworkRequest request = MakeOAuthStashRequest(m_realm, m_league, location.get_tab_uniq_id()); - QueueRequest(kOAuthGetStashEndpoint, request, location); - }; + // Queue stash tab requests. + for (rapidjson::Value &tab : stashes) { + // This will process tabs recursively. + ProcessOAuthTab(tab, tabs_requested, alloc); }; spdlog::debug("Requesting {} out of {} stash tabs", tabs_requested, stashes.Size()); @@ -630,7 +657,7 @@ void ItemsManagerWorker::OnOAuthCharacterListReceived(QNetworkReply* reply) { return; }; - const auto& characters = doc["characters"].GetArray(); + const auto &characters = doc["characters"].GetArray(); int requested_character_count = 0; for (auto& character : characters) { if (!HasString(character, "name")) { @@ -725,6 +752,10 @@ void ItemsManagerWorker::OnOAuthStashReceived(QNetworkReply* reply, const ItemLo ++m_stashes_received; SendStatusUpdate(); + if (HasArray(stash, "children")) { + spdlog::info("HAS_CHILDREN: {}: {}", stash["name"].GetString(), Util::RapidjsonSerialize(stash)); + }; + if ((m_stashes_received == m_stashes_needed) && (m_characters_received == m_characters_needed) && !m_cancel_update) { spdlog::trace("ItemsManagerWorker::OnOAuthStashReceived() finishing update"); FinishUpdate(); @@ -1002,6 +1033,98 @@ void ItemsManagerWorker::FetchItems() { spdlog::debug("Requested {} stashes and {} characters.", m_stashes_needed, m_characters_needed); spdlog::debug("Tab titles: {}", tab_titles); + + // Make sure we cancel the update if there was nothing to do. + // (Discovered this was necessary when trying to refresh a single unique stashtab). + if ((m_stashes_needed == 0) && (m_characters_needed == 0)) { + m_updating = false; + } +} + +bool ItemsManagerWorker::IsLegacyTabValid(rapidjson::Value &tab) +{ + if (!HasString(tab, "n")) { + spdlog::error("Legacy tab does not have name"); + return false; + }; + + if (!HasInt(tab, "i")) { + spdlog::error("Legacy tab does not have an index: {}", tab["n"].GetString()); + return false; + }; + + // Skip hidden tabs. + if (HasBool(tab, "hidden") && tab["hidden"].GetBool()) { + spdlog::debug("The legacy tab is hidden: {}", tab["n"].GetString()); + return false; + }; + + // Skip tabs that are in the index; they are not being refreshed. + if (!HasString(tab, "id")) { + spdlog::error("The legacy tab does not have a unique id: {}", tab["n"].GetString()); + return false; + }; + QString tab_id = tab["id"].GetString(); + if (tab_id.size() > 10) { + // The unique id for stash tabs returned from the legacy API + // need to be trimmed to 10 characters. + spdlog::debug("Trimming legacy tab unique id: {}", tab["n"].GetString()); + tab_id = tab_id.first(10); + }; + + // Get the type of this stash tab. + if (!HasString(tab, "type")) { + spdlog::error("The stash tab does not have a type: {}", tab["n"].GetString()); + return false; + }; + + return true; +} + +void ItemsManagerWorker::ProcessLegacyTab(rapidjson::Value &tab, + int &count, + rapidjson_allocator &alloc) +{ + if (!IsLegacyTabValid(tab)) { + return; + }; + + const QString label = tab["n"].GetString(); + const QString tab_type = tab["type"].GetString(); + const int index = tab["i"].GetInt(); + + // The unique id for stash tabs returned from the legacy API + // need to be trimmed to 10 characters. + QString tab_id = tab["id"].GetString(); + if (tab_id.size() > 10) { + tab_id = tab_id.first(10); + }; + + if (m_tab_id_index.count(tab_id) == 0) { + // Create this tab. + int r = 0, g = 0, b = 0; + Util::GetTabColor(tab, r, g, b); + ItemLocation + location(index, tab_id, label, ItemLocationType::STASH, tab_type, r, g, b, tab, alloc); + + // Add this tab. + m_tabs.push_back(location); + m_tab_id_index.insert(tab_id); + + // Submit a request for this tab. + if (m_update_tab_contents) { + ++count; + QNetworkRequest request = MakeLegacyTabRequest(location.get_tab_id(), true); + QueueRequest(kStashItemsUrl, request, location); + }; + + // Process any children. + if (tab.HasMember("children")) { + for (auto &child : tab["children"]) { + ProcessLegacyTab(child, count, alloc); + }; + }; + }; } void ItemsManagerWorker::OnFirstLegacyTabReceived(QNetworkReply* reply) { @@ -1056,62 +1179,10 @@ void ItemsManagerWorker::OnFirstLegacyTabReceived(QNetworkReply* reply) { }; // Queue stash tab requests. + int count = 0; + auto &alloc = doc.GetAllocator(); for (auto& tab : tabs) { - - if (!HasString(tab, "n")) { - spdlog::error("Legacy tab does not have name"); - continue; - }; - const QString label = tab["n"].GetString(); - - if (!HasInt(tab, "i")) { - spdlog::error("Legacy tab does not have an index: {}", label); - continue; - }; - const int index = tab["i"].GetInt(); - - // Skip hidden tabs. - if (HasBool(tabs[index], "hidden") && tabs[index]["hidden"].GetBool()) { - spdlog::debug("The legacy tab is hidden: {}", label); - continue; - }; - - // Skip tabs that are in the index; they are not being refreshed. - if (!HasString(tab, "id")) { - spdlog::error("The legacy tab does not have a unique id: {}", label); - continue; - }; - QString tab_id = tab["id"].GetString(); - if (tab_id.size() > 10) { - // The unique id for stash tabs returned from the legacy API - // need to be trimmed to 10 characters. - spdlog::debug("Trimming legacy tab unique id: {}", label); - tab_id = tab_id.first(10); - }; - if (m_tab_id_index.count(tab_id) > 0) { - continue; - }; - - // Get the type of this stash tab. - if (!HasString(tab, "type")) { - spdlog::error("The stash tab does not have a type: {}", label); - continue; - }; - const QString tab_type = tab["type"].GetString(); - - // Get the stash tab color; - int r = 0, g = 0, b = 0; - Util::GetTabColor(tab, r, g, b); - - // Create and save the tab location object. - ItemLocation location(index, tab_id, label, ItemLocationType::STASH, tab_type, r, g, b, tab, doc.GetAllocator()); - m_tabs.push_back(location); - m_tab_id_index.insert(tab_id); - - // Submit a request for this tab. - if (m_update_tab_contents) { - QueueRequest(kStashItemsUrl, MakeLegacyTabRequest(location.get_tab_id(), true), location); - }; + ProcessLegacyTab(tab, count, alloc); }; m_has_stash_list = true; diff --git a/src/itemsmanagerworker.h b/src/itemsmanagerworker.h index a5655858..1df89aaa 100644 --- a/src/itemsmanagerworker.h +++ b/src/itemsmanagerworker.h @@ -120,7 +120,13 @@ private slots: bool TabsChanged(rapidjson::Document& doc, QNetworkReply* network_reply, const ItemLocation& location); void FinishUpdate(); - QSettings& m_settings; + bool IsOAuthTabValid(rapidjson::Value &tab); + void ProcessOAuthTab(rapidjson::Value &tab, int &count, rapidjson_allocator &alloc); + + bool IsLegacyTabValid(rapidjson::Value &tab); + void ProcessLegacyTab(rapidjson::Value &tab, int &count, rapidjson_allocator &alloc); + + QSettings &m_settings; QNetworkAccessManager& m_network_manager; RePoE& m_repoe; DataStore& m_datastore;