bap_activity_link_view.cpp 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. /** Read-only views of the activity link a binding owns, taken under the session lock. */
  2. #include <cstddef>
  3. #include <cstdint>
  4. #include <shared_mutex>
  5. #include "../../state/activity/runtime.h"
  6. #include "encrypted/push/activity/internal.h"
  7. #include "internal.h"
  8. #include "runtime.h"
  9. namespace sunrise::server::bap {
  10. namespace {
  11. /**
  12. * Tests the world-package identity used by the HUD and generated scenario layout.
  13. * Reason, nonce, activity index and descriptor are binding identity, so two selections naming one
  14. * loaded destination may differ in all of them.
  15. */
  16. [[nodiscard]] bool
  17. same_destination_package(const state::activity::destination::DestinationSelection& left,
  18. const state::activity::destination::DestinationSelection& right) noexcept {
  19. return left.packageNameLength != 0 && left.packageNameLength == right.packageNameLength
  20. && left.packageName == right.packageName;
  21. }
  22. /** Resolves the region used by this exact connection's msg-5 builder. */
  23. [[nodiscard]] encrypted::push::activity::EffectiveRegion
  24. selected_region_locked(const Session& session) noexcept {
  25. const auto base = encrypted::push::activity::effective_region(session.activity.session);
  26. return encrypted::push::activity::selected_effective_region(session, base.arrival);
  27. }
  28. /** Copies one selected link's common-root inputs. */
  29. void copy_replication_view(const Session& session, ActivityReplicationView& output) noexcept {
  30. output.binding = session.activity.session;
  31. output.patchEpoch = session.activityPatchEpoch.value;
  32. output.activityClientGeneration = session.activity.bindingGeneration;
  33. output.groupSessionId = session.activity.groupSessionId;
  34. output.memberId = session.activityMemberKey;
  35. output.replicationEpoch = session.activity.replicationEpoch;
  36. }
  37. } // namespace
  38. /** Region index this connection's msg-5 builder selects. */
  39. std::int32_t selected_region_index_locked(const Session& session) noexcept {
  40. return selected_region_locked(session).index;
  41. }
  42. /** Counts matching authenticated links while the caller already owns the BAP lock. */
  43. std::size_t activity_link_count_locked(const state::activity::SessionBinding& binding) noexcept {
  44. std::size_t count = 0;
  45. static_cast<void>(unique_activity_link_locked(binding, count));
  46. return count;
  47. }
  48. /** Counts authenticated BAP links that currently own one exact activity generation. */
  49. std::size_t activity_link_count(const state::activity::SessionBinding& binding) noexcept {
  50. const std::shared_lock lock(session_lock());
  51. const std::size_t count = activity_link_count_locked(binding);
  52. return count;
  53. }
  54. /** Reads the unique ActivityClient and the exact region its msg-5 builder will use. */
  55. bool activity_link_view(const state::activity::SessionBinding& binding,
  56. ActivityLinkView& output) noexcept {
  57. output = {};
  58. const std::shared_lock lock(session_lock());
  59. const Session* const session = unique_activity_link_locked(binding, output.matchingLinks);
  60. if (session != nullptr) {
  61. const auto region = selected_region_locked(*session);
  62. output.activityClientGeneration = session->activity.bindingGeneration;
  63. output.effectiveRegion = region.index;
  64. output.sliceSetIndex =
  65. state::activity::membership::reported_slice_set(session->activity.session.sessionId);
  66. output.arrivalSliceSetIndex = static_cast<std::int32_t>(region.arrival);
  67. output.effectiveRegionReported = region.reported;
  68. output.joined = session->activity.role == ActivityClientRole::publicTarget
  69. || session->activityJoinGeneration == session->activity.bindingGeneration;
  70. output.publicTarget = session->activity.role == ActivityClientRole::publicTarget;
  71. output.rosterReason = session->activityRosterReason;
  72. output.playerKey = encrypted::push::activity::published_player_key(*session);
  73. }
  74. return session != nullptr;
  75. }
  76. /** Selects the exact live ActivityClient for the client's local world slice. */
  77. bool current_activity_link_view(std::int32_t localSliceSet,
  78. CurrentActivityLinkView& output) noexcept {
  79. output = {};
  80. const Session* only = nullptr;
  81. const Session* matched = nullptr;
  82. const Session* coherent = nullptr;
  83. const Session* privateCurrent = nullptr;
  84. bool oneDestination = true;
  85. const std::shared_lock lock(session_lock());
  86. for (const Session& session : sessions()) {
  87. if (session.id == 0 || !session.authenticated
  88. || session.activity.role == ActivityClientRole::none
  89. || session.activity.bindingGeneration == 0
  90. || !state::activity::binding_matches(session.activity.session)) {
  91. continue;
  92. }
  93. only = &session;
  94. ++output.activeLinks;
  95. if (session.activity.role == ActivityClientRole::privateCurrent
  96. && (privateCurrent == nullptr
  97. || session.activity.bindingGeneration
  98. > privateCurrent->activity.bindingGeneration)) {
  99. privateCurrent = &session;
  100. }
  101. if (coherent == nullptr) {
  102. coherent = &session;
  103. } else {
  104. oneDestination = oneDestination
  105. && same_destination_package(coherent->activity.session.destination,
  106. session.activity.session.destination);
  107. // Prefer the persistent private-current link for destination metadata. Public targets
  108. // are disposable region views and can overlap while the client changes bubbles.
  109. if ((session.activity.role == ActivityClientRole::privateCurrent
  110. && coherent->activity.role != ActivityClientRole::privateCurrent)
  111. || (session.activity.role == coherent->activity.role
  112. && session.activity.bindingGeneration > coherent->activity.bindingGeneration)) {
  113. coherent = &session;
  114. }
  115. }
  116. if (localSliceSet >= 0 && selected_region_locked(session).index == localSliceSet) {
  117. ++output.matchingRegions;
  118. // A public region may be represented by several overlapping links. Prefer the stable
  119. // private-current owner; within one role, use the newest binding.
  120. const bool moreSpecific =
  121. matched == nullptr
  122. || (session.activity.role == ActivityClientRole::privateCurrent
  123. && matched->activity.role != ActivityClientRole::privateCurrent)
  124. || (session.activity.role == matched->activity.role
  125. && session.activity.bindingGeneration > matched->activity.bindingGeneration);
  126. if (moreSpecific) {
  127. matched = &session;
  128. }
  129. }
  130. }
  131. const Session* const selected = privateCurrent != nullptr ? privateCurrent
  132. : matched != nullptr ? matched
  133. : output.activeLinks == 1 ? only
  134. : oneDestination ? coherent
  135. : nullptr;
  136. if (selected != nullptr) {
  137. output.binding = selected->activity.session;
  138. output.activityClientGeneration = selected->activity.bindingGeneration;
  139. output.effectiveRegion = selected_region_locked(*selected).index;
  140. output.publicTarget = selected->activity.role == ActivityClientRole::publicTarget;
  141. }
  142. return selected != nullptr;
  143. }
  144. /** Reads the unique ActivityClient whose activity session is the supplied view token. */
  145. bool activity_replication_view_for_session(std::uint64_t activitySessionId,
  146. ActivityReplicationView& output) noexcept {
  147. output = {};
  148. if (activitySessionId == 0) {
  149. return false;
  150. }
  151. const std::shared_lock lock(session_lock());
  152. const Session* selected = nullptr;
  153. std::size_t count = 0;
  154. for (const Session& session : sessions()) {
  155. if (session.id == 0 || !session.authenticated
  156. || session.activity.role == ActivityClientRole::none
  157. || session.activity.bindingGeneration == 0
  158. || session.activity.session.sessionId != activitySessionId
  159. || !state::activity::binding_matches(session.activity.session)
  160. || !session.activityPatchEpoch.seen
  161. || session.activityPatchEpoch.bindingGeneration != session.activity.bindingGeneration) {
  162. continue;
  163. }
  164. selected = &session;
  165. ++count;
  166. }
  167. if (count == 1 && selected != nullptr) {
  168. copy_replication_view(*selected, output);
  169. }
  170. return count == 1;
  171. }
  172. /** Reads the unique ActivityClient bound to one gameplay group session. */
  173. bool activity_replication_view_for_group(std::uint64_t groupSessionId,
  174. ActivityReplicationView& output) noexcept {
  175. output = {};
  176. if (groupSessionId == 0) {
  177. return false;
  178. }
  179. const std::shared_lock lock(session_lock());
  180. const Session* selected = nullptr;
  181. std::size_t count = 0;
  182. for (const Session& session : sessions()) {
  183. if (session.id == 0 || !session.authenticated
  184. || session.activity.role == ActivityClientRole::none
  185. || session.activity.bindingGeneration == 0
  186. || session.activity.groupSessionId != groupSessionId || !session.activityPatchEpoch.seen
  187. || session.activityPatchEpoch.bindingGeneration != session.activity.bindingGeneration) {
  188. continue;
  189. }
  190. selected = &session;
  191. ++count;
  192. }
  193. if (count == 1 && selected != nullptr) {
  194. copy_replication_view(*selected, output);
  195. }
  196. return count == 1;
  197. }
  198. } // namespace sunrise::server::bap