account_state.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. #include "account_state.h"
  2. #include <algorithm>
  3. #include <array>
  4. #include <cmath>
  5. namespace sunrise::state::account {
  6. namespace {
  7. /** Enough fixed storage for every account, character, profile-stack, and character-item key. */
  8. inline constexpr std::size_t kIdentityCapacity =
  9. 1 + inventory::kProfileItemCapacity
  10. + kCharacterCapacity * (1 + inventory::kEquipmentSlotCount + inventory::kCharacterItemCapacity);
  11. /** @return True when a profile row carries no authored or runtime-owned value. */
  12. [[nodiscard]] bool empty_profile_item(const inventory::ProfileItem& item) noexcept {
  13. return item.instanceSoid == 0 && item.definitionHash == 0 && item.quantity == 0
  14. && item.mutationSerial == 0;
  15. }
  16. /** Adds one nonzero globally unique key to a bounded identity set. */
  17. [[nodiscard]] bool append_identity(std::array<std::uint64_t, kIdentityCapacity>& identities,
  18. std::size_t& count,
  19. std::uint64_t soid) noexcept {
  20. if (soid == 0 || count >= identities.size()) {
  21. return false;
  22. }
  23. const auto end = identities.cbegin() + static_cast<std::ptrdiff_t>(count);
  24. if (std::find(identities.cbegin(), end, soid) != end) {
  25. return false;
  26. }
  27. identities[count++] = soid;
  28. return true;
  29. }
  30. /** Checks the complete authored/runtime structure without consulting installed build data. */
  31. [[nodiscard]] bool valid_impl(const AccountState& state) noexcept {
  32. if (state.profileItemCount > state.profileItems.size()
  33. || state.characterCount > state.characters.size()) {
  34. return false;
  35. }
  36. if (state.primarySoid == 0) {
  37. if (state.profileItemCount != 0 || state.characterCount != 0 || state.settings.configured
  38. || state.settings.keyBindings.configured) {
  39. return false;
  40. }
  41. return std::all_of(
  42. state.profileItems.cbegin(), state.profileItems.cend(), empty_profile_item);
  43. }
  44. if (!settings::valid(state.settings)) {
  45. return false;
  46. }
  47. std::array<std::uint64_t, kIdentityCapacity> identities{};
  48. std::size_t identityCount = 0;
  49. if (!append_identity(identities, identityCount, state.primarySoid)) {
  50. return false;
  51. }
  52. for (std::size_t index = 0; index < state.profileItems.size(); ++index) {
  53. const inventory::ProfileItem& item = state.profileItems[index];
  54. if (index >= state.profileItemCount) {
  55. if (!empty_profile_item(item)) {
  56. return false;
  57. }
  58. continue;
  59. }
  60. if (item.definitionHash == inventory::kNoDefinitionHash || item.quantity <= 0
  61. || item.mutationSerial < 0
  62. || (item.instanceSoid != 0
  63. && !append_identity(identities, identityCount, item.instanceSoid))) {
  64. return false;
  65. }
  66. }
  67. bool selected = false;
  68. for (std::size_t index = 0; index < state.characterCount; ++index) {
  69. const CharacterState& character = state.characters[index];
  70. if (!append_identity(identities, identityCount, character.soid)
  71. || (character.selected && selected) || character.race > CharacterRace::exo
  72. || character.gender > CharacterGender::female
  73. || character.characterClass > CharacterClass::warlock
  74. || !std::isfinite(character.appearanceValue) || !inventory::valid(character.equipment)
  75. || !inventory::valid(character.inventory)) {
  76. return false;
  77. }
  78. selected = selected || character.selected;
  79. for (const std::optional<inventory::Item>& item : character.equipment.slots) {
  80. if (item.has_value()
  81. && !append_identity(identities, identityCount, item->instanceSoid)) {
  82. return false;
  83. }
  84. }
  85. for (std::size_t itemIndex = 0; itemIndex < character.inventory.count; ++itemIndex) {
  86. if (!append_identity(identities,
  87. identityCount,
  88. character.inventory.values[itemIndex].instanceSoid)) {
  89. return false;
  90. }
  91. }
  92. }
  93. return true;
  94. }
  95. } // namespace
  96. /**
  97. * Checks bounded ids and whole settings for every nonzero account.
  98. * @param state Account State to check.
  99. * @return True for empty State, or a whole account with unique ids.
  100. */
  101. bool valid(const AccountState& state) noexcept {
  102. return valid_impl(state);
  103. }
  104. /** Checks settings-authored State before runtime-only profile stack identities are seeded. */
  105. bool valid_authored(const AccountState& state) noexcept {
  106. return valid_impl(state);
  107. }
  108. /**
  109. * Finds the selected character without storing a second account-level key.
  110. * @param state Account snapshot read under the lock.
  111. * @return The selected character's nonzero SOID, or zero when none is selected.
  112. */
  113. std::uint64_t selected_character_soid(const AccountState& state) noexcept {
  114. const std::size_t count = (std::min)(state.characterCount, state.characters.size());
  115. for (std::size_t index = 0; index < count; ++index) {
  116. if (state.characters[index].selected) {
  117. return state.characters[index].soid;
  118. }
  119. }
  120. return 0;
  121. }
  122. } // namespace sunrise::state::account