build_data_runtime.cpp 8.4 KB

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