scenario_roster_publish.cpp 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. #include <algorithm>
  2. #include <cstddef>
  3. #include "../../../middleware/content/packages/tables/roster_intersection.h"
  4. #include "internal.h"
  5. namespace sunrise::client::content::scenarios {
  6. namespace {
  7. namespace tables = middleware::content::packages::tables;
  8. /**
  9. * Orders the safe groups the way the destination publishes them.
  10. * A group that binds the player or reports the lifetime comes first, then one reached through the
  11. * destination's own registry array, then the lower key.
  12. * @return True when left publishes before right.
  13. */
  14. [[nodiscard]] bool publishes_first(const Candidate& left, const Candidate& right) noexcept {
  15. const bool leftFilled = left.bindsPlayer || left.reportsLifetime;
  16. const bool rightFilled = right.bindsPlayer || right.reportsLifetime;
  17. if (leftFilled != rightFilled) {
  18. return leftFilled;
  19. }
  20. if (left.primaryRegistry != right.primaryRegistry) {
  21. return left.primaryRegistry;
  22. }
  23. return left.key < right.key;
  24. }
  25. /**
  26. * Writes the top-level half: the candidates whose key is in every slice set.
  27. * @param walk Accumulator for one destination.
  28. * @param row Destination row receiving its group indices.
  29. */
  30. void publish_top_level(Walk& walk, layouts::Definition& row) noexcept {
  31. std::array<std::uint32_t, tables::kRosterKeyCapacity> safe{};
  32. std::size_t safeCount = 0;
  33. if (!tables::safe_roster_keys(walk.intersection, safe, safeCount) || safeCount == 0) {
  34. return;
  35. }
  36. std::array<Candidate, tables::kRosterKeyCapacity> kept{};
  37. std::size_t keptCount = 0;
  38. for (std::size_t index = 0; index < walk.candidateCount; ++index) {
  39. const Candidate& candidate = walk.candidates[index];
  40. const auto last = safe.begin() + static_cast<std::ptrdiff_t>(safeCount);
  41. const bool keep = std::find(safe.begin(), last, candidate.key) != last;
  42. if (keep && keptCount < kept.size()) {
  43. kept[keptCount++] = candidate;
  44. }
  45. }
  46. std::sort(kept.begin(), kept.begin() + static_cast<std::ptrdiff_t>(keptCount), publishes_first);
  47. // A roster missing either filled type seeds nothing the spawn gate reads, so publish none.
  48. bool binds = false;
  49. bool reports = false;
  50. for (std::size_t index = 0; index < keptCount; ++index) {
  51. binds = binds || kept[index].bindsPlayer;
  52. reports = reports || kept[index].reportsLifetime;
  53. }
  54. if (!binds || !reports) {
  55. return;
  56. }
  57. const std::size_t published = (std::min)(keptCount, layouts::kDestinationGroupCapacity);
  58. for (std::size_t index = 0; index < published; ++index) {
  59. row.rosterGroups[index] = kept[index].group;
  60. }
  61. row.rosterGroupCount = static_cast<std::uint8_t>(published);
  62. }
  63. /**
  64. * Writes the per-bubble half: the candidates whose key is in some slice sets and not all.
  65. * @param walk Accumulator for one destination.
  66. * @param row Destination row receiving its per-bubble group indices and their bubbles.
  67. */
  68. void publish_per_bubble(Walk& walk, layouts::Definition& row) noexcept {
  69. std::array<std::uint32_t, tables::kRosterKeyCapacity> keys{};
  70. std::array<std::uint64_t, tables::kRosterKeyCapacity> masks{};
  71. std::size_t partialCount = 0;
  72. if (!tables::partial_roster_keys(walk.intersection, keys, masks, partialCount)
  73. || partialCount == 0) {
  74. return;
  75. }
  76. std::size_t published = 0;
  77. for (std::size_t index = 0;
  78. index < walk.candidateCount && published < layouts::kDestinationBubbleGroupCapacity;
  79. ++index) {
  80. const Candidate& candidate = walk.candidates[index];
  81. for (std::size_t partial = 0; partial < partialCount; ++partial) {
  82. if (keys[partial] != candidate.key) {
  83. continue;
  84. }
  85. row.bubbleGroups[published] = candidate.group;
  86. row.bubbleGroupMasks[published] = masks[partial];
  87. ++published;
  88. break;
  89. }
  90. }
  91. row.bubbleGroupCount = static_cast<std::uint8_t>(published);
  92. }
  93. } // namespace
  94. /** Splits the candidates between the destination row's two lists. */
  95. void publish_groups(Walk& walk, layouts::Definition& row) noexcept {
  96. row.rosterGroupCount = 0;
  97. row.rosterGroups = {};
  98. row.bubbleGroupCount = 0;
  99. row.bubbleGroups = {};
  100. row.bubbleGroupMasks = {};
  101. publish_top_level(walk, row);
  102. // The per-bubble half is independent of the top-level one: its keys register through the
  103. // delta's own field 1, and a destination may reach one half and not the other.
  104. publish_per_bubble(walk, row);
  105. }
  106. } // namespace sunrise::client::content::scenarios