Bladeren bron

fix(investment): stop unsupported catalog retries

y9522 2 weken geleden
bovenliggende
commit
07201027ed

+ 0 - 1
Sunrise/src/client/content/items/packages/package_item_build.cpp

@@ -23,7 +23,6 @@
 #include "../../spawn_sets/spawn_set_build.h"
 #include "build.h"
 #include "internal.h"
-#include "package_socket_plug_build.h"
 
 namespace sunrise::client::content::items::packages {
 namespace {

+ 0 - 1
Sunrise/src/state/build_data/cache/records/format.h

@@ -11,7 +11,6 @@
 #include "../../constants/definition.h"
 #include "../../definition.h"
 #include "../../hash_names/definition.h"
-#include "../../items/catalysts/definition.h"
 #include "../../items/details/definition.h"
 #include "../../items/item_catalog.h"
 #include "../../items/socket_plugs/definition.h"

+ 0 - 1
Sunrise/src/state/build_data/runtime.h

@@ -7,7 +7,6 @@
 #include <span>
 #include <string_view>
 
-#include "../investment/investment.h"
 #include "abilities/definition.h"
 #include "collectibles/collectible_catalog.h"
 #include "constants/definition.h"

+ 24 - 9
Sunrise/src/state/build_data/runtime/persistence/build_data_persistence.cpp

@@ -105,15 +105,20 @@ Context& context() noexcept {
     return g_context;
 }
 
-/** @return True when every extracted domain is complete in State. */
-bool all_domains_ready() noexcept {
+/** @return True when every required extracted domain is complete in State. */
+[[nodiscard]] static bool required_domains_ready() noexcept {
     constants::InvestmentConstants published{};
     return runtime::named::ready() && item_definitions_ready() && configured_item_details_ready()
            && collectible_definitions_ready() && socket_plug_rules_ready()
-           && exotic_catalysts_ready() && material_requirement_sets_ready()
-           && inventory_bucket_descriptors_ready() && socket_entry_lists_ready()
-           && ability_buckets_ready() && progression_definitions_ready() && scenario_layouts_ready()
-           && spawn_sets_ready() && hash_names_ready() && constants::find(published);
+           && material_requirement_sets_ready() && inventory_bucket_descriptors_ready()
+           && socket_entry_lists_ready() && ability_buckets_ready()
+           && progression_definitions_ready() && scenario_layouts_ready() && spawn_sets_ready()
+           && hash_names_ready() && constants::find(published);
+}
+
+/** @return True when every extracted domain is complete in State. */
+bool all_domains_ready() noexcept {
+    return required_domains_ready() && exotic_catalysts_ready();
 }
 
 /** Gives mutable views over every fixed snapshot buffer. */
@@ -348,12 +353,22 @@ bool persist_if_complete_locked(Context& state) noexcept {
 
 namespace sunrise::state::build_data {
 
-/** @return True only when every domain is ready and any needed cache write works. */
+/** @return True when required domains are ready and any safe cache write works. */
 bool persist() noexcept {
     runtime::persistence::Context& state = runtime::persistence::context();
     AcquireSRWLockExclusive(&state.lock);
-    const bool result = runtime::persistence::all_domains_ready()
-                        && runtime::persistence::persist_if_complete_locked(state);
+    const bool requiredReady = runtime::persistence::required_domains_ready();
+    bool result = false;
+    if (requiredReady && !exotic_catalysts_ready()) {
+        // Catalyst facts are build-pinned. A rejected build must not keep the refresh worker
+        // active or put an incomplete catalog on disk. Other extracted domains stay usable.
+        runtime::persistence::release_scratch_locked(state);
+        state.enabled = false;
+        state.replaceStaleCache = false;
+        result = true;
+    } else if (requiredReady) {
+        result = runtime::persistence::persist_if_complete_locked(state);
+    }
     ReleaseSRWLockExclusive(&state.lock);
     return result;
 }

+ 6 - 0
tests/catalyst_regression_tests.cpp

@@ -203,6 +203,12 @@ void test_structural_lifecycles() noexcept {
            "stored catalog matches a fresh structural derivation");
 
     auto altered = fixture.output;
+    altered[0].completedPlugDefinitionIndex = Fixture::kLegacyEffect;
+    expect(!catalysts::matches_derived(
+               fixture.source(), fixture.facts(), std::span(altered).first(count)),
+           "catalog rejects a completed plug outside its socket pool");
+
+    altered = fixture.output;
     altered[0].completedPlugDefinitionIndex = Fixture::kLegacyProgress;
     expect(!catalysts::matches_derived(
                fixture.source(), fixture.facts(), std::span(altered).first(count)),

+ 50 - 0
tests/settings_regression_tests.cpp

@@ -1,5 +1,6 @@
 #include <Windows.h>
 
+#include <array>
 #include <crtdbg.h>
 #include <cstdint>
 #include <cstdio>
@@ -115,6 +116,54 @@ void test_optional_catalyst_policy() noexcept {
            "non-boolean catalyst policy is rejected");
 }
 
+void test_settings_file_round_trip() noexcept {
+    constexpr std::string_view document = R"({"version":8,"complete_exotic_catalysts":false})";
+    std::array<char, MAX_PATH + 1> directory{};
+    std::array<char, MAX_PATH + 1> path{};
+    const DWORD directoryLength =
+        GetTempPathA(static_cast<DWORD>(directory.size()), directory.data());
+    if (directoryLength == 0 || directoryLength >= directory.size()
+        || GetTempFileNameA(directory.data(), "sun", 0, path.data()) == 0) {
+        expect(false, "settings round trip creates a temporary file");
+        return;
+    }
+
+    HANDLE file = CreateFileA(
+        path.data(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_TEMPORARY, nullptr);
+    DWORD written = 0;
+    const bool saved =
+        file != INVALID_HANDLE_VALUE
+        && WriteFile(file, document.data(), static_cast<DWORD>(document.size()), &written, nullptr)
+        && written == document.size();
+    if (file != INVALID_HANDLE_VALUE) {
+        CloseHandle(file);
+    }
+
+    std::array<char, 128> reloaded{};
+    file = CreateFileA(path.data(),
+                       GENERIC_READ,
+                       FILE_SHARE_READ,
+                       nullptr,
+                       OPEN_EXISTING,
+                       FILE_ATTRIBUTE_NORMAL,
+                       nullptr);
+    DWORD read = 0;
+    const bool loaded =
+        file != INVALID_HANDLE_VALUE
+        && ReadFile(file, reloaded.data(), static_cast<DWORD>(reloaded.size()), &read, nullptr)
+        && read == document.size();
+    if (file != INVALID_HANDLE_VALUE) {
+        CloseHandle(file);
+    }
+    DeleteFileA(path.data());
+
+    bool enabled = true;
+    expect(saved && loaded
+               && parse_catalyst_policy(std::string_view{reloaded.data(), read}, enabled)
+               && !enabled,
+           "settings file round trip preserves the catalyst policy");
+}
+
 } // namespace
 
 void test_exotic_catalysts() noexcept;
@@ -128,6 +177,7 @@ int main() {
 #endif
     test_item_state_contract();
     test_optional_catalyst_policy();
+    test_settings_file_round_trip();
     test_opcode406_item_state();
     test_exotic_catalysts();
     test_resolved_catalyst_output();