package_season_pass_build.cpp 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. #include <algorithm>
  2. #include <limits>
  3. #include "../../../../core/logging/log.h"
  4. #include "../../../../middleware/content/packages/tables/field_reader.h"
  5. #include "../../../../state/build_data/season_pass/season_pass_catalog.h"
  6. #include "../../../../state/progression/season_pass_reward_catalog.h"
  7. #include "internal.h"
  8. #include "package_reward_build.h"
  9. namespace sunrise::client::content::items::packages {
  10. namespace {
  11. namespace domain = state::build_data::season_pass;
  12. /**
  13. * Reads one pass row: item, rank, quantity and claim flag, then its sockets and conditions.
  14. * @return False when any field is unreadable or out of range.
  15. */
  16. bool read_season_reward(Storage& storage,
  17. std::span<const std::byte> progressionTable,
  18. const tables::Array& itemRows,
  19. std::size_t at,
  20. domain::Reward& reward) noexcept {
  21. std::uint32_t rank = 0;
  22. std::uint32_t itemIndex = 0;
  23. std::uint32_t claimSlot = 0;
  24. if (!tables::read(progressionTable, at + tables::kProgressionRewardRankOffset, rank)
  25. || !tables::read(
  26. progressionTable, at + tables::kProgressionRewardItemIndexOffset, itemIndex)
  27. || !tables::read(
  28. progressionTable, at + tables::kProgressionRewardQuantityOffset, reward.quantity)
  29. || !tables::read(
  30. progressionTable, at + tables::kProgressionRewardClaimSlotOffset, claimSlot)) {
  31. return false;
  32. }
  33. // Native fields are 32-bit, the stored row keeps the narrower native index widths.
  34. tables::IndexRow entry{};
  35. if (rank > (std::numeric_limits<std::uint8_t>::max)()
  36. || itemIndex > (std::numeric_limits<std::uint16_t>::max)()
  37. || claimSlot > (std::numeric_limits<std::uint16_t>::max)()
  38. || !tables::index_row(storage.itemIndexTable, itemRows, itemIndex, entry)) {
  39. return false;
  40. }
  41. reward.itemHash = entry.definitionHash;
  42. reward.itemIndex = static_cast<std::uint16_t>(itemIndex);
  43. reward.requiredRank = static_cast<std::uint8_t>(rank);
  44. // A reward with no claim flag carries slot 0, any other slot must map to the account bank.
  45. if (claimSlot != 0) {
  46. reward.claimFlagIndex =
  47. bank_index(storage.slotMaps.accountFlag, static_cast<std::int32_t>(claimSlot));
  48. if (reward.claimFlagIndex == domain::kUnavailableFlagIndex) {
  49. return false;
  50. }
  51. }
  52. std::size_t socketCount = 0;
  53. std::size_t conditionCount = 0;
  54. if (!read_reward_sockets(progressionTable,
  55. at + tables::kProgressionRewardSocketsOffset,
  56. reward.sockets,
  57. socketCount)
  58. || !storage.rewardBuild.conditions.read_list(
  59. progressionTable,
  60. at + tables::kProgressionRewardConditionsOffset,
  61. reward.condition,
  62. conditionCount)) {
  63. return false;
  64. }
  65. reward.socketCount = static_cast<std::uint8_t>(socketCount);
  66. reward.conditionCount = static_cast<std::uint8_t>(conditionCount);
  67. return domain::valid(std::span(&reward, 1))
  68. && std::all_of(reward.sockets.begin(),
  69. reward.sockets.begin() + reward.socketCount,
  70. [&itemRows](const state::build_data::rewards::SocketOverride& socket) {
  71. return state::build_data::rewards::valid_socket(socket,
  72. itemRows.count);
  73. });
  74. }
  75. } // namespace
  76. /**
  77. * Reads the season pass reward list in native order, which the opcode-2400 claim indexes.
  78. * A row that fails to read is kept as an unavailable row so later claim indices stay aligned.
  79. * @param source Installed package source.
  80. * @param storage Pass storage receiving the reward rows.
  81. * @param root Investment root bytes.
  82. * @return True when the reward list read and produced at least one row.
  83. */
  84. bool build_season_pass(const reader::Source& source,
  85. Storage& storage,
  86. std::span<const std::byte> root) noexcept {
  87. storage.seasonPassRewardCount = 0;
  88. std::uint32_t itemTableTag = 0;
  89. tables::Array itemRows{};
  90. if (!tables::slot_tag(root, tables::kItemTableSlot, itemTableTag) || itemTableTag == 0
  91. || !reader::read_tag(source, storage.scratch, itemTableTag, storage.itemIndexTable)
  92. || !tables::find_array_at(std::span<const std::byte>{storage.itemIndexTable},
  93. tables::kTableArrayDescriptor,
  94. itemRows)
  95. || itemRows.elementClass != tables::kItemIndexTableClass) {
  96. return false;
  97. }
  98. std::uint32_t tableTag = 0;
  99. tables::Array progressions{};
  100. if (!tables::slot_tag(root, tables::kProgressionTableSlot, tableTag) || tableTag == 0
  101. || !reader::read_tag(source, storage.scratch, tableTag, storage.progressionTable)
  102. || !tables::find_array_at(std::span<const std::byte>{storage.progressionTable},
  103. tables::kTableArrayDescriptor,
  104. progressions)
  105. || progressions.elementClass != tables::kProgressionTableClass
  106. || progressions.count <= state::progression::season_pass::kProgressionDefinitionIndex) {
  107. return false;
  108. }
  109. const std::span<const std::byte> progressionTable{storage.progressionTable};
  110. const std::size_t passAt = progressions.dataOffset
  111. + state::progression::season_pass::kProgressionDefinitionIndex
  112. * tables::kProgressionRowStride;
  113. tables::Array rewards{};
  114. if (!tables::read_array(progressionTable,
  115. passAt + tables::kProgressionRewardField,
  116. tables::kProgressionRewardRowClass,
  117. tables::kProgressionRewardStride,
  118. rewards)
  119. || rewards.count == 0 || rewards.count > domain::kRewardCapacity) {
  120. return false;
  121. }
  122. std::size_t skipped = 0;
  123. for (std::uint64_t row = 0; row < rewards.count; ++row) {
  124. const std::size_t at =
  125. rewards.dataOffset + static_cast<std::size_t>(row) * tables::kProgressionRewardStride;
  126. auto& reward = storage.seasonPassRewards[row];
  127. reward = {};
  128. if (!read_season_reward(storage, progressionTable, itemRows, at, reward)) {
  129. reward = {};
  130. ++skipped;
  131. }
  132. }
  133. if (skipped != 0) {
  134. core::log::writef(core::log::Channel::client,
  135. core::log::Level::warn,
  136. "ev=pkg stage=season_pass skipped=%zu",
  137. skipped);
  138. }
  139. storage.seasonPassRewardCount = static_cast<std::size_t>(rewards.count);
  140. return storage.seasonPassRewardCount != 0;
  141. }
  142. } // namespace sunrise::client::content::items::packages