build_data_runtime.cpp 6.4 KB

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