package_item_rows.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256
  1. #include <array>
  2. #include <span>
  3. #include "../../../../state/build_data/items/catalysts/exotic_catalyst_builder.h"
  4. #include "../../../../state/build_data/items/details/item_detail_catalog.h"
  5. #include "../../../../state/build_data/runtime.h"
  6. #include "internal.h"
  7. #include "package_socket_plug_build.h"
  8. namespace sunrise::client::content::items::packages {
  9. namespace {
  10. namespace build_details = state::build_data::items::details;
  11. namespace build_items = state::build_data::items;
  12. /** @return True when one extracted detail can join the currently published numeric domains. */
  13. [[nodiscard]] bool publishable_detail(const build_details::Definition& detail) noexcept {
  14. const std::size_t itemCount = state::build_data::item_definition_count();
  15. const std::size_t socketListCount = state::build_data::socket_entry_list_count();
  16. build_items::Definition item{};
  17. if (!build_details::valid(std::span<const build_details::Definition>{&detail, 1})
  18. || detail.definitionIndex >= itemCount || detail.socketEntryListIndex >= socketListCount
  19. || !build_items::find_index(detail.definitionIndex, item)
  20. || item.bucketId != detail.bucketId) {
  21. return false;
  22. }
  23. for (const std::uint16_t plugIndex : detail.initialPlugIndices) {
  24. if (plugIndex != build_details::kUnavailableItemIndex && plugIndex >= itemCount) {
  25. return false;
  26. }
  27. }
  28. return true;
  29. }
  30. } // namespace
  31. /** Walks the located item index table, then publishes every domain that depends on it. */
  32. bool build_item_rows(const reader::Source& source,
  33. Storage& storage,
  34. const tables::Array& table,
  35. std::size_t& rowCount,
  36. const char*& reason) noexcept {
  37. const bool needDefinitions = !state::build_data::item_definitions_ready();
  38. const bool needDetails = !state::build_data::configured_item_details_ready();
  39. const bool needSocketPlugs = !state::build_data::socket_plug_rules_ready();
  40. const bool needCatalysts = !state::build_data::exotic_catalysts_ready();
  41. const bool needBuckets = !state::build_data::inventory_bucket_descriptors_ready();
  42. const bool retainDetails = needDetails || needCatalysts;
  43. const bool needSocketRows = needSocketPlugs || needCatalysts;
  44. const bool needDetailRows = needDetails || needSocketRows;
  45. // Bucket equipment slots are derived from this same complete item walk, so a partial retry
  46. // must still revisit the table even when definitions and detail domains already published.
  47. const bool needRows = needDefinitions || needDetailRows || needBuckets;
  48. bool published = !needRows;
  49. if (retainDetails && storage.details.size() != kDetailCapacity) {
  50. storage.details.assign(kDetailCapacity, build_details::Definition{});
  51. }
  52. if (needCatalysts) {
  53. storage.catalystCompletionConditions.assign(
  54. static_cast<std::size_t>(table.count),
  55. state::build_data::items::catalysts::CompletionCondition{});
  56. for (std::size_t item = 0; item < storage.catalystCompletionConditions.size(); ++item) {
  57. storage.catalystCompletionConditions[item].itemDefinitionIndex =
  58. static_cast<std::uint16_t>(item);
  59. }
  60. }
  61. const bool detailStorageReady = !retainDetails || storage.details.size() == kDetailCapacity;
  62. const std::span<const std::byte> container{storage.child};
  63. reason = "rows";
  64. // The detail closure is gathered during this one walk. Collections can name any installed
  65. // item row, including profile-owned shaders and modifications, so retain every readable row.
  66. // The fixed request bitset bounds this to the installed 16-bit item-table domain.
  67. storage.detailRequests.reset();
  68. storage.specialPlugCategories.fill(0);
  69. std::size_t detailCount = 0;
  70. // One malformed entry is omitted on its own so the rest of the table still publishes. An
  71. // entry can fail either at its index row or at the definition the row points to, and neither
  72. // says anything about the entries that follow it.
  73. std::uint64_t index = 0;
  74. for (; needRows && index < table.count && rowCount < storage.rows.size(); ++index) {
  75. tables::IndexRow row{};
  76. if (!tables::index_row(container, table, index, row)) {
  77. continue;
  78. }
  79. tables::items::Row item{};
  80. item.definitionHash = row.definitionHash;
  81. item.definitionIndex = static_cast<std::uint16_t>(index);
  82. if (!reader::read_tag(source, storage.scratch, row.targetTag, storage.definition)
  83. || !tables::items::read_definition(std::span<const std::byte>{storage.definition},
  84. item)) {
  85. continue;
  86. }
  87. const std::uint32_t plugCategoryHash =
  88. corrected_plug_category(item.definitionHash, item.plugCategoryHash);
  89. storage.rows[rowCount++] =
  90. state::build_data::items::Definition{item.definitionHash,
  91. item.definitionIndex,
  92. item.bucketId,
  93. item.insertionMaterialRequirementSetIndex,
  94. item.enabledMaterialRequirementSetIndex,
  95. item.tier,
  96. plugCategoryHash,
  97. item.rollSetIndex,
  98. item.linkedPlugIndex};
  99. if (needSocketRows) {
  100. storage.specialPlugCategories[item.definitionIndex] =
  101. special_plug_category(plugCategoryHash);
  102. }
  103. if (needDetailRows) {
  104. request(item.definitionIndex, storage.detailRequests);
  105. append_initial_plugs(item, table.count, storage.detailRequests);
  106. }
  107. }
  108. bool requestsFit = true;
  109. if (needRows) {
  110. // Every walked entry either published a row or was skipped, so the count of one
  111. // follows from the other rather than being tracked alongside them.
  112. report_row_count(index, rowCount, index - rowCount, index < table.count);
  113. requestsFit = publish_buckets(storage)
  114. && (!needDetailRows
  115. || materialize_requests(
  116. storage.detailRequests, storage.requestedDetailIndices, detailCount));
  117. published = rowCount != 0 && requestsFit && detailStorageReady;
  118. }
  119. if (published && needDefinitions) {
  120. published =
  121. state::build_data::publish_item_definitions(std::span(storage.rows).first(rowCount));
  122. }
  123. if (!published) {
  124. reason = !detailStorageReady ? "detail_storage"
  125. : !requestsFit ? "detail_capacity"
  126. : "publish";
  127. }
  128. SocketPlugBuild socketPlugBuild;
  129. const bool socketStorageReady =
  130. !needSocketRows
  131. || socketPlugBuild.prepare(storage.specialPlugCategories,
  132. std::span(storage.rows).first(rowCount));
  133. if (published && !socketStorageReady) {
  134. published = false;
  135. reason = "socket_storage";
  136. }
  137. // Every readable installed row is found through the table this pass just published. One
  138. // malformed row is omitted independently so unrelated Collections categories stay usable.
  139. if (published && needDetailRows) {
  140. reason = "details";
  141. const DetailSource detailSource{
  142. &source, &storage.scratch, container, table, &storage.definition};
  143. std::size_t builtDetailCount = 0;
  144. for (std::size_t slot = 0; slot < detailCount; ++slot) {
  145. build_details::Definition detail{};
  146. tables::items::Row item{};
  147. if (!build_detail(detailSource, storage.requestedDetailIndices[slot], detail, item)
  148. || !publishable_detail(detail)) {
  149. report_detail_failure(slot, storage.requestedDetailIndices[slot]);
  150. continue;
  151. }
  152. if (retainDetails) {
  153. storage.details[builtDetailCount++] = detail;
  154. }
  155. if (needCatalysts
  156. && detail.definitionIndex < storage.catalystCompletionConditions.size()) {
  157. read_catalyst_completion_condition(
  158. std::span<const std::byte>{storage.definition},
  159. detail.definitionIndex,
  160. storage.catalystCompletionConditions[detail.definitionIndex]);
  161. }
  162. if (needSocketRows) {
  163. (void)socketPlugBuild.append(item,
  164. std::span<const std::byte>{storage.definition},
  165. std::span<const std::byte>{storage.plugSetTable},
  166. table.count);
  167. }
  168. }
  169. if (needDetails) {
  170. published = state::build_data::publish_configured_item_details(
  171. std::span<build_details::Definition>{storage.details}.first(builtDetailCount));
  172. report_detail_count(detailCount, builtDetailCount);
  173. }
  174. std::array<state::build_data::items::catalysts::Definition,
  175. state::build_data::items::catalysts::kDefinitionCapacity>
  176. catalystRows{};
  177. std::size_t catalystCount = 0;
  178. state::build_data::items::catalysts::Report catalystReport{};
  179. const state::build_data::items::catalysts::Source catalystSource{
  180. {},
  181. std::span(storage.rows).first(rowCount),
  182. std::span(storage.details).first(builtDetailCount),
  183. socketPlugBuild.rules(),
  184. socketPlugBuild.pools(),
  185. socketPlugBuild.members(),
  186. storage.catalystCompletionConditions,
  187. storage.catalystAcquisitionGates,
  188. storage.catalystObjectiveValues,
  189. };
  190. bool catalystBuilt = false;
  191. if (published && needCatalysts) {
  192. reason = "exotic_catalysts";
  193. catalystBuilt = state::build_data::derive_exotic_catalysts(
  194. catalystSource, catalystRows, catalystCount, catalystReport);
  195. report_catalyst_catalog(catalystReport, catalystBuilt);
  196. published = catalystBuilt;
  197. }
  198. if (published && needSocketPlugs) {
  199. const std::size_t rules = socketPlugBuild.rule_count();
  200. const std::size_t pools = socketPlugBuild.pool_count();
  201. const std::size_t members = socketPlugBuild.member_count();
  202. const std::size_t skipped = socketPlugBuild.skipped();
  203. reason = "socket_plugs";
  204. published = socketPlugBuild.publish();
  205. if (published) {
  206. report_socket_plug_count(rules, pools, members, skipped);
  207. }
  208. }
  209. if (published && needCatalysts && catalystBuilt) {
  210. reason = "exotic_catalysts";
  211. published = state::build_data::publish_exotic_catalysts(
  212. catalystSource, std::span(catalystRows).first(catalystCount));
  213. }
  214. }
  215. // Ability buckets read the socket entry list table again and depend on the detail domain, so
  216. // they run last. The entry-bucket table never joins the on-disk cache, so a warm boot still
  217. // has to run this once to fill it in for the session.
  218. if (published
  219. && (!state::build_data::ability_buckets_ready()
  220. || !state::build_data::socket_entry_buckets_ready())) {
  221. reason = "abilities";
  222. std::size_t abilityCount = 0;
  223. std::size_t entryBucketCount = 0;
  224. const bool built = build_character_abilities(source,
  225. storage.scratch,
  226. std::span<const std::byte>{storage.root},
  227. storage.abilityTable,
  228. storage.definition,
  229. storage.abilityPool,
  230. storage.abilityRows,
  231. abilityCount,
  232. storage.entryBucketRows,
  233. entryBucketCount);
  234. published = built
  235. && state::build_data::publish_ability_buckets(
  236. std::span(storage.abilityRows).first(abilityCount))
  237. && state::build_data::publish_socket_entry_buckets(
  238. std::span(storage.entryBucketRows).first(entryBucketCount));
  239. if (built) {
  240. report_ability_count(abilityCount);
  241. }
  242. }
  243. return published && state::build_data::item_definitions_ready()
  244. && state::build_data::configured_item_details_ready()
  245. && state::build_data::socket_plug_rules_ready()
  246. && state::build_data::exotic_catalysts_ready()
  247. && state::build_data::ability_buckets_ready();
  248. }
  249. } // namespace sunrise::client::content::items::packages