state_account_identity_runtime.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. /** Instance identity helpers: generated SOIDs, ownership tests, and loadout row lookups. */
  2. #include <Windows.h>
  3. #include <algorithm>
  4. #include <array>
  5. #include <cstddef>
  6. #include <cstdint>
  7. #include <cstdio>
  8. #include <limits>
  9. #include <string_view>
  10. #include <utility>
  11. #include "../../core/logging/log.h"
  12. #include "../../middleware/datagen/family4/loadout/loadout_resolver.h"
  13. #include "../build_data/runtime.h"
  14. #include "runtime.h"
  15. #include "state.h"
  16. #include "state_account_transaction_helpers.h"
  17. #include "storage/internal.h"
  18. namespace sunrise::state {
  19. namespace runtime::detail {
  20. namespace authored_inventory = account::inventory;
  21. namespace item_details = build_data::items::details;
  22. namespace inventory_buckets = build_data::inventory::buckets;
  23. namespace family4_loadout = middleware::datagen::family4::loadout;
  24. /** First SOID reserved for item instances created by this local runtime. */
  25. constexpr std::uint64_t kFirstGeneratedItemSoid = 0x4000000000000001ULL;
  26. /** Returns one character-owned instance's definition hash for bounded transaction diagnostics. */
  27. [[nodiscard]] std::uint32_t character_item_definition_hash(const CharacterState& character,
  28. std::uint64_t instanceSoid) noexcept {
  29. for (const auto& item : character.equipment.slots) {
  30. if (item.has_value() && item->instanceSoid == instanceSoid) {
  31. return item->definitionHash;
  32. }
  33. }
  34. for (std::size_t index = 0; index < character.inventory.count; ++index) {
  35. if (character.inventory.values[index].instanceSoid == instanceSoid) {
  36. return character.inventory.values[index].definitionHash;
  37. }
  38. }
  39. return 0;
  40. }
  41. /** @return True when any account, character, profile-stack, or character-item key owns one SOID. */
  42. [[nodiscard]] bool account_owns_soid(const AccountState& account, std::uint64_t soid) noexcept {
  43. if (soid == 0 || account.primarySoid == soid) {
  44. return true;
  45. }
  46. for (std::size_t index = 0; index < account.profileItemCount; ++index) {
  47. if (account.profileItems[index].instanceSoid == soid) {
  48. return true;
  49. }
  50. }
  51. for (std::size_t characterIndex = 0; characterIndex < account.characterCount;
  52. ++characterIndex) {
  53. const CharacterState& character = account.characters[characterIndex];
  54. if (character.soid == soid) {
  55. return true;
  56. }
  57. for (const auto& item : character.equipment.slots) {
  58. if (item.has_value() && item->instanceSoid == soid) {
  59. return true;
  60. }
  61. }
  62. for (std::size_t index = 0; index < character.inventory.count; ++index) {
  63. if (character.inventory.values[index].instanceSoid == soid) {
  64. return true;
  65. }
  66. }
  67. }
  68. return false;
  69. }
  70. /** Finds a fresh deterministic item-instance SOID without sharing any other identity key. */
  71. [[nodiscard]] bool next_item_instance_soid(const AccountState& account,
  72. std::uint64_t& output) noexcept {
  73. std::uint64_t candidate = kFirstGeneratedItemSoid;
  74. for (std::size_t characterIndex = 0; characterIndex < account.characterCount;
  75. ++characterIndex) {
  76. const CharacterState& character = account.characters[characterIndex];
  77. for (const auto& item : character.equipment.slots) {
  78. if (!item.has_value() || item->instanceSoid < candidate) {
  79. continue;
  80. }
  81. if (item->instanceSoid == (std::numeric_limits<std::uint64_t>::max)()) {
  82. return false;
  83. }
  84. candidate = item->instanceSoid + 1U;
  85. }
  86. for (std::size_t index = 0; index < character.inventory.count; ++index) {
  87. const std::uint64_t instanceSoid = character.inventory.values[index].instanceSoid;
  88. if (instanceSoid < candidate) {
  89. continue;
  90. }
  91. if (instanceSoid == (std::numeric_limits<std::uint64_t>::max)()) {
  92. return false;
  93. }
  94. candidate = instanceSoid + 1U;
  95. }
  96. }
  97. while (account_owns_soid(account, candidate)) {
  98. if (candidate == (std::numeric_limits<std::uint64_t>::max)()) {
  99. return false;
  100. }
  101. ++candidate;
  102. }
  103. output = candidate;
  104. return output != 0;
  105. }
  106. /** Finds a collision-free SOID for one newly appended profile stack. */
  107. [[nodiscard]] bool next_profile_item_instance_soid(const AccountState& account,
  108. std::uint64_t& output) noexcept {
  109. std::uint64_t candidate = authored_inventory::kFirstProfileItemInstanceSoid;
  110. while (account_owns_soid(account, candidate)) {
  111. if (candidate == (std::numeric_limits<std::uint64_t>::max)()) {
  112. return false;
  113. }
  114. ++candidate;
  115. }
  116. output = candidate;
  117. return output != 0;
  118. }
  119. /** @return True when an account or character identity already owns the candidate object key. */
  120. [[nodiscard]] bool identity_uses_soid(const AccountState& account, std::uint64_t soid) noexcept {
  121. if (soid == 0 || account.primarySoid == soid) {
  122. return true;
  123. }
  124. for (std::size_t index = 0; index < account.characterCount; ++index) {
  125. if (account.characters[index].soid == soid) {
  126. return true;
  127. }
  128. }
  129. return false;
  130. }
  131. /** Uses the character's strongest existing item as the neutral local Collections pull level. */
  132. [[nodiscard]] std::int32_t acquisition_level(const CharacterState& character) noexcept {
  133. std::int32_t level = 0;
  134. for (const auto& item : character.equipment.slots) {
  135. if (item.has_value()) {
  136. level = (std::max)(level, item->level);
  137. }
  138. }
  139. for (std::size_t index = 0; index < character.inventory.count; ++index) {
  140. level = (std::max)(level, character.inventory.values[index].level);
  141. }
  142. return level;
  143. }
  144. /** Finds the one resolved unequipped row created by an acquisition candidate. */
  145. [[nodiscard]] bool find_acquired_row(const family4_loadout::ResolvedLoadout& loadout,
  146. std::uint64_t instanceSoid,
  147. std::uint16_t& inventoryRow,
  148. std::uint8_t& equipmentSlot) noexcept {
  149. bool found = false;
  150. for (std::size_t index = 0; index < loadout.itemCount; ++index) {
  151. const family4_loadout::ResolvedItem& item = loadout.items[index];
  152. if (item.instance.instanceSoid != instanceSoid) {
  153. continue;
  154. }
  155. if (found || item.equipped) {
  156. return false;
  157. }
  158. found = true;
  159. inventoryRow = item.inventoryRow;
  160. equipmentSlot = item.equipmentSlot;
  161. }
  162. return found;
  163. }
  164. /** Finds the unique resolved unequipped position for one instance. */
  165. [[nodiscard]] bool find_unequipped_row(const family4_loadout::ResolvedLoadout& loadout,
  166. std::uint64_t instanceSoid,
  167. std::uint16_t& inventoryRow,
  168. std::uint8_t& equipmentSlot) noexcept {
  169. bool found = false;
  170. for (std::size_t index = 0; index < loadout.itemCount; ++index) {
  171. const family4_loadout::ResolvedItem& item = loadout.items[index];
  172. if (item.instance.instanceSoid != instanceSoid) {
  173. continue;
  174. }
  175. if (found || item.equipped) {
  176. return false;
  177. }
  178. found = true;
  179. inventoryRow = item.inventoryRow;
  180. equipmentSlot = item.equipmentSlot;
  181. }
  182. return found;
  183. }
  184. /** @return True when the resolved loadout still carries an instance with this key. */
  185. [[nodiscard]] bool loadout_contains(const family4_loadout::ResolvedLoadout& loadout,
  186. std::uint64_t instanceSoid) noexcept {
  187. for (std::size_t index = 0; index < loadout.itemCount; ++index) {
  188. if (loadout.items[index].instance.instanceSoid == instanceSoid) {
  189. return true;
  190. }
  191. }
  192. return false;
  193. }
  194. } // namespace runtime::detail
  195. } // namespace sunrise::state