package_item_build.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255
  1. #include <Windows.h>
  2. #include <array>
  3. #include <span>
  4. #include "../../../../core/filesystem/path.h"
  5. #include "../../../../core/logging/log.h"
  6. #include "../../../../middleware/content/packages/reader/reader.h"
  7. #include "../../../../middleware/content/packages/tables/definition_index_table.h"
  8. #include "../../../../state/build_data/runtime.h"
  9. #include "../../activity/entity_position_profile_build.h"
  10. #include "../../hash_names/hash_name_build.h"
  11. #include "../../scenarios/scenario_build.h"
  12. #include "../../spawn_sets/spawn_set_build.h"
  13. #include "../../vendors/vendor_build.h"
  14. #include "build.h"
  15. #include "internal.h"
  16. #include "package_socket_plug_build.h"
  17. namespace sunrise::client::content::items::packages {
  18. namespace {
  19. /** @return True when every item and investment-root domain is published. */
  20. [[nodiscard]] bool root_domains_ready() noexcept {
  21. return state::build_data::item_definitions_ready()
  22. && state::build_data::collectible_definitions_ready()
  23. && state::build_data::material_requirement_sets_ready()
  24. && state::build_data::configured_item_details_ready()
  25. && state::build_data::socket_plug_rules_ready()
  26. && state::build_data::inventory_bucket_descriptors_ready()
  27. && state::build_data::socket_entry_lists_ready()
  28. && state::build_data::ability_buckets_ready()
  29. && state::build_data::socket_entry_buckets_ready()
  30. && state::build_data::progression_definitions_ready()
  31. && state::build_data::season_pass_ready()
  32. && state::build_data::repeatable_bounties_ready()
  33. && state::build_data::record_definitions_ready()
  34. && state::build_data::node_definitions_ready()
  35. && state::build_data::sobject_definitions_ready()
  36. && state::build_data::investment_constants_ready() && exotic_catalysts_settled();
  37. }
  38. } // namespace
  39. /** @return True when every domain owned by the package pass is published. */
  40. bool ready() noexcept {
  41. return root_domains_ready() && state::build_data::scenario_layouts_ready()
  42. && state::build_data::spawn_sets_ready() && state::build_data::hash_names_ready()
  43. && state::build_data::vendor_catalog_ready()
  44. && content::activity::entity_position_profiles::ready();
  45. }
  46. /** Publishes the dense item table from the installed packages, once. */
  47. bool build() noexcept {
  48. static Storage storage{};
  49. reader::BlockKeys keys{};
  50. core::path::Buffer directory{};
  51. if (!collect_keys(keys)) {
  52. report(0, "keys");
  53. return false;
  54. }
  55. std::size_t rowCount = 0;
  56. const char* reason = "directory";
  57. if (!package_directory(directory)) {
  58. SecureZeroMemory(&keys, sizeof keys);
  59. report(0, reason);
  60. return false;
  61. }
  62. // The destination layouts and the spawn sets share this pass's directory, keys, and block
  63. // storage. Both are independent of the item table, so a failure here leaves it alone.
  64. {
  65. const reader::Source packageSource{directory.chars.data(), &keys};
  66. (void)content::activity::entity_position_profiles::build(packageSource, storage.scratch);
  67. (void)content::scenarios::build(packageSource, storage.scratch);
  68. (void)content::spawn_sets::build(packageSource, storage.scratch);
  69. (void)content::hash_names::build(packageSource, storage.scratch);
  70. (void)content::vendors::build(packageSource, storage.scratch);
  71. if (ready()) {
  72. SecureZeroMemory(&keys, sizeof keys);
  73. return true;
  74. }
  75. }
  76. if (root_domains_ready()) {
  77. SecureZeroMemory(&keys, sizeof keys);
  78. return ready();
  79. }
  80. reason = "tag";
  81. std::array<std::uint32_t, kContainerCandidates> candidates{};
  82. std::size_t candidateCount = 0;
  83. if (investment_globals_tags(candidates, candidateCount)) {
  84. const reader::Source source{directory.chars.data(), &keys};
  85. tables::Array table{};
  86. bool located = false;
  87. reason = "read";
  88. for (std::size_t candidate = 0; candidate < candidateCount && !located; ++candidate) {
  89. if (!reader::read_tag(
  90. source, storage.scratch, candidates[candidate], storage.container)) {
  91. continue;
  92. }
  93. // Fixed navigation: globals child zero is the investment root, whose slot holds the
  94. // item table, whose array descriptor sits at a fixed offset.
  95. std::uint32_t rootTag = 0;
  96. std::uint32_t rootClass = 0;
  97. std::uint32_t tableTag = 0;
  98. reason = "root";
  99. if (!tables::child_tag(std::span<const std::byte>{storage.container},
  100. tables::kInvestmentRootChild,
  101. rootTag)
  102. || rootTag == 0 || tables::package_of(rootTag) == tables::kAbsentPackageId
  103. || !reader::read_tag(source, storage.scratch, rootTag, storage.child, rootClass)
  104. || rootClass != tables::kInvestmentRootClass) {
  105. continue;
  106. }
  107. // The same root names the bucket and socket-list tables.
  108. storage.root = storage.child;
  109. // Records, nodes, season pass rewards and catalysts all resolve slots through the
  110. // two unlock mapping tables, so they are read once here.
  111. if (!state::build_data::record_definitions_ready()
  112. || !state::build_data::node_definitions_ready()
  113. || !state::build_data::season_pass_ready() || !exotic_catalysts_settled()) {
  114. reason = "unlock_maps";
  115. if (!read_unlock_slot_maps(
  116. source, storage, std::span<const std::byte>{storage.root})) {
  117. continue;
  118. }
  119. }
  120. if (!state::build_data::socket_plug_rules_ready()) {
  121. std::uint32_t plugSetTag = 0;
  122. tables::Array plugSets{};
  123. reason = "plug_sets";
  124. if (!tables::slot_tag(std::span<const std::byte>{storage.root},
  125. tables::kPlugSetTableSlot,
  126. plugSetTag)
  127. || plugSetTag == 0
  128. || !reader::read_tag(source, storage.scratch, plugSetTag, storage.plugSetTable)
  129. || !tables::find_array_at(std::span<const std::byte>{storage.plugSetTable},
  130. tables::kTableArrayDescriptor,
  131. plugSets)) {
  132. continue;
  133. }
  134. }
  135. if (!exotic_catalysts_settled()) {
  136. reason = "catalyst_gates";
  137. if (!read_catalyst_acquisition_gates(source,
  138. storage.scratch,
  139. std::span<const std::byte>{storage.root},
  140. storage.child,
  141. storage.catalystAcquisitionGates)
  142. || !read_catalyst_objective_values(source,
  143. storage.scratch,
  144. std::span<const std::byte>{storage.root},
  145. storage.child,
  146. storage.catalystObjectiveValues)) {
  147. continue;
  148. }
  149. }
  150. reason = "buckets";
  151. if (!build_buckets(source, storage, std::span<const std::byte>{storage.root})) {
  152. continue;
  153. }
  154. (void)build_socket_entry_lists(
  155. source, storage, std::span<const std::byte>{storage.root});
  156. if (!state::build_data::progression_definitions_ready()) {
  157. std::size_t progressionCount = 0;
  158. if (build_progressions(source,
  159. storage.scratch,
  160. std::span<const std::byte>{storage.root},
  161. storage.child,
  162. storage.progressionRows,
  163. progressionCount,
  164. storage.progressionSteps,
  165. storage.progressionStepCount)) {
  166. (void)state::build_data::publish_progression_definitions(
  167. std::span(storage.progressionRows).first(progressionCount),
  168. std::span(storage.progressionSteps).first(storage.progressionStepCount));
  169. }
  170. }
  171. if (!state::build_data::season_pass_ready()
  172. && build_season_pass(source, storage, std::span<const std::byte>{storage.root})) {
  173. (void)state::build_data::publish_season_pass(
  174. std::span(storage.seasonPassRewards).first(storage.seasonPassRewardCount),
  175. std::span(storage.seasonPassPackages).first(storage.seasonPassPackageCount));
  176. }
  177. // Records are read before nodes: a node's lore-book flag and its parent bar come
  178. // from the records it owns, so the record rows must already be in pass storage.
  179. if (!state::build_data::record_definitions_ready()
  180. || !state::build_data::node_definitions_ready()) {
  181. const bool builtRecords =
  182. build_records(source, storage, std::span<const std::byte>{storage.root});
  183. if (builtRecords && !state::build_data::record_definitions_ready()) {
  184. (void)state::build_data::publish_record_definitions(
  185. std::span(storage.recordRows).first(storage.recordCount),
  186. std::span(storage.recordObjectives).first(storage.recordObjectiveCount),
  187. std::span(storage.recordIntervals).first(storage.recordIntervalCount),
  188. std::span(storage.recordRewards).first(storage.recordRewardCount));
  189. }
  190. if (builtRecords && !state::build_data::node_definitions_ready()
  191. && build_nodes(source, storage, std::span<const std::byte>{storage.root})) {
  192. (void)state::build_data::publish_node_definitions(
  193. std::span(storage.nodeRows).first(storage.nodeCount));
  194. }
  195. }
  196. if (!state::build_data::investment_constants_ready()) {
  197. state::build_data::constants::InvestmentConstants extracted{};
  198. if (read_investment_constants(source,
  199. storage.scratch,
  200. std::span<const std::byte>{storage.root},
  201. storage.child,
  202. extracted)) {
  203. (void)state::build_data::publish_investment_constants(extracted);
  204. }
  205. }
  206. reason = "slot";
  207. if (!tables::slot_tag(
  208. std::span<const std::byte>{storage.root}, tables::kItemTableSlot, tableTag)
  209. || tableTag == 0
  210. || !reader::read_tag(source, storage.scratch, tableTag, storage.child)) {
  211. continue;
  212. }
  213. reason = "table";
  214. located = tables::find_array_at(std::span<const std::byte>{storage.child},
  215. tables::kTableArrayDescriptor,
  216. table)
  217. && table.elementClass == tables::kItemIndexTableClass;
  218. }
  219. if (located && build_item_rows(source, storage, table, rowCount, reason)) {
  220. if (!build_material_requirements(
  221. source, storage, std::span<const std::byte>{storage.root}, table.count)) {
  222. reason = "materials";
  223. } else if (!build_collectibles(source,
  224. storage,
  225. std::span<const std::byte>{storage.root},
  226. table.count)) {
  227. reason = "collectibles";
  228. } else if (!state::build_data::repeatable_bounties_ready()
  229. && build_bounties(source, storage, rowCount)) {
  230. (void)state::build_data::publish_repeatable_bounties(
  231. std::span(storage.bountyRows).first(storage.bountyCount));
  232. }
  233. }
  234. }
  235. SecureZeroMemory(&keys, sizeof keys);
  236. const bool complete = ready();
  237. const bool itemDomainsReady = root_domains_ready();
  238. if (complete) {
  239. // Nothing reads a package again until the next boot, so this reader's files go back now.
  240. reader::close_files(storage.scratch);
  241. }
  242. // Scenario, spawn-set, and hash-name extraction advance over later refresh slices and report
  243. // their own progress. Do not mislabel one of those pending domains as the last item substage.
  244. report(itemDomainsReady ? state::build_data::item_definition_count() : 0, reason);
  245. return complete;
  246. }
  247. } // namespace sunrise::client::content::items::packages