build_data_runtime.cpp 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #include <Windows.h>
  2. #include <cstdint>
  3. #include <span>
  4. #include <string_view>
  5. #include "../../core/filesystem/path.h"
  6. #include "../content/content_catalog.h"
  7. #include "abilities/ability_bucket_catalog.h"
  8. #include "cache/internal.h"
  9. #include "collectibles/collectible_catalog.h"
  10. #include "constants/investment_constant_catalog.h"
  11. #include "hash_names/hash_name_catalog.h"
  12. #include "inventory/buckets/inventory_bucket_catalog.h"
  13. #include "items/catalysts/exotic_catalyst_catalog.h"
  14. #include "items/details/item_detail_catalog.h"
  15. #include "items/socket_plugs/socket_plug_catalog.h"
  16. #include "material_requirements/material_requirement_catalog.h"
  17. #include "progressions/progression_catalog.h"
  18. #include "runtime.h"
  19. #include "runtime/build_data_catalog_runtime.h"
  20. #include "runtime/domain_markers.h"
  21. #include "runtime/persistence/build_data_persistence.h"
  22. #include "scenarios/scenario_catalog.h"
  23. #include "socket_entry_lists/socket_entry_list_catalog.h"
  24. #include "spawn_sets/spawn_set_catalog.h"
  25. #include "vendors/vendor_catalog.h"
  26. namespace sunrise::state::build_data {
  27. namespace {
  28. /** One cache directory holds the generated build data under the artifact root. */
  29. constexpr std::wstring_view kCacheDirectorySuffix = L"\\cache";
  30. /** One reusable file stores all extracted build mappings. */
  31. constexpr std::wstring_view kCacheFileSuffix = L"\\cache\\build_data.bin";
  32. } // namespace
  33. /** Loads one full cache, or leaves every domain ready for first-boot extraction. */
  34. bool initialize(void* module, std::uint64_t configuredEquipmentHash) noexcept {
  35. runtime::persistence::Context& persistenceState = runtime::persistence::context();
  36. AcquireSRWLockExclusive(&persistenceState.lock);
  37. runtime::persistence::clear_locked(persistenceState);
  38. runtime::clear_catalogs();
  39. if (module == nullptr) {
  40. ReleaseSRWLockExclusive(&persistenceState.lock);
  41. return true;
  42. }
  43. core::path::Buffer moduleDirectory;
  44. if (!core::path::artifact_directory(module, moduleDirectory)) {
  45. ReleaseSRWLockExclusive(&persistenceState.lock);
  46. return false;
  47. }
  48. persistenceState.cacheDirectory = moduleDirectory;
  49. persistenceState.cachePath = moduleDirectory;
  50. if (!core::path::append(persistenceState.cacheDirectory, kCacheDirectorySuffix)
  51. || !core::path::append(persistenceState.cachePath, kCacheFileSuffix)
  52. || !cache::current_build_identity(configuredEquipmentHash,
  53. persistenceState.buildIdentity)) {
  54. runtime::persistence::clear_locked(persistenceState);
  55. ReleaseSRWLockExclusive(&persistenceState.lock);
  56. return false;
  57. }
  58. persistenceState.enabled = true;
  59. cache::records::DomainCounts counts{};
  60. const cache::LoadStatus status =
  61. cache::load(persistenceState.cachePath.chars.data(),
  62. persistenceState.buildIdentity,
  63. runtime::persistence::scratch_domains(persistenceState),
  64. counts);
  65. if (status == cache::LoadStatus::missing) {
  66. runtime::persistence::release_scratch_locked(persistenceState);
  67. ReleaseSRWLockExclusive(&persistenceState.lock);
  68. return true;
  69. }
  70. if (status == cache::LoadStatus::stale) {
  71. // A stale cache is replaced only after every domain is complete.
  72. persistenceState.replaceStaleCache = true;
  73. runtime::persistence::release_scratch_locked(persistenceState);
  74. ReleaseSRWLockExclusive(&persistenceState.lock);
  75. return true;
  76. }
  77. const cache::records::Domains domains =
  78. runtime::persistence::occupied_domains(persistenceState, counts);
  79. bool detailsReplaced = false;
  80. if (domains.itemDetails.empty()) {
  81. items::details::clear();
  82. detailsReplaced = true;
  83. } else {
  84. detailsReplaced = items::details::replace(domains.itemDetails);
  85. }
  86. const constants::InvestmentConstants cachedConstants{
  87. domains.constants.extracted != 0,
  88. domains.constants.lightStatRow,
  89. domains.constants.weaponPowerStatRow,
  90. domains.constants.characterStatRows,
  91. };
  92. if (status != cache::LoadStatus::loaded || !constants::replace(cachedConstants)
  93. || !content::replace(domains.named) || !content::seal() || !items::replace(domains.items)
  94. || !collectibles::replace(domains.collectibles)
  95. || !material_requirements::replace(domains.materialRequirementSets)
  96. || !inventory::buckets::replace(domains.inventoryBuckets)
  97. || !socket_entry_lists::replace(domains.socketEntryLists)
  98. // The per-entry tables are what the subclass selection reads. Without them a cache hit
  99. // makes the lists ready, the package build skips itself, and no ability is picked.
  100. || !socket_entry_lists::replace_entry_tables(domains.socketEntryTables) || !detailsReplaced
  101. || !items::socket_plugs::replace(
  102. domains.socketPlugRules, domains.socketPlugPools, domains.socketPlugMembers)
  103. || !items::catalysts::replace(domains.exoticCatalysts)
  104. || !abilities::replace(domains.abilityBuckets)
  105. || !progressions::replace(domains.progressions)
  106. // The layouts are what activity message 1 reads. Without them a cache hit makes the
  107. // other domains ready, the package build skips itself, and every destination falls back.
  108. || !scenarios::replace(domains.scenarios, domains.rosterGroups)
  109. // An empty catalog is complete, so the spawn-set replace is skipped rather than failed.
  110. || (!domains.spawnStems.empty()
  111. && !spawn_sets::replace(
  112. domains.spawnStems, domains.spawnNameHashes, domains.spawnPoints))
  113. // An empty catalog is complete, so the vendor replace is skipped rather than failed.
  114. || (!domains.vendorIndex.empty()
  115. && !vendors::replace(domains.vendorIndex,
  116. domains.vendorDefinitions,
  117. domains.vendorSaleRows,
  118. domains.vendorInstalledRows))
  119. || !hash_names::replace(domains.hashNames)) {
  120. // No domain remains published when any catalog rejects the cache transaction.
  121. runtime::clear_catalogs();
  122. runtime::persistence::clear_locked(persistenceState);
  123. ReleaseSRWLockExclusive(&persistenceState.lock);
  124. return false;
  125. }
  126. runtime::details::publish();
  127. runtime::named::publish();
  128. runtime::ability_buckets::publish();
  129. runtime::spawn_catalog::publish();
  130. runtime::name_catalog::publish();
  131. persistenceState.persisted = true;
  132. runtime::persistence::release_scratch_locked(persistenceState);
  133. ReleaseSRWLockExclusive(&persistenceState.lock);
  134. return true;
  135. }
  136. /** Clears all generated build mappings and persistence paths. */
  137. void shutdown() noexcept {
  138. runtime::persistence::Context& persistenceState = runtime::persistence::context();
  139. AcquireSRWLockExclusive(&persistenceState.lock);
  140. runtime::clear_catalogs();
  141. runtime::persistence::clear_locked(persistenceState);
  142. ReleaseSRWLockExclusive(&persistenceState.lock);
  143. }
  144. } // namespace sunrise::state::build_data