Parcourir la source

Merge pull request #154 from gagefulwood/fix/social-roster-power

Show Power and Triumph score on the fireteam roster card
stan il y a 4 jours
Parent
commit
bfe60e8ec4

+ 46 - 5
Sunrise/src/server/bap/encrypted/push/snapshot/social_roster_snapshot.cpp

@@ -8,25 +8,32 @@
 #include <array>
 #include <cstdio>
 #include <cstring>
+#include <limits>
 #include <span>
 
 #include "../../../../../core/logging/log.h"
 #include "../../../../../middleware/datagen/definitions.h"
 #include "../../../../../state/account/inventory/inventory_state.h"
 #include "../../../../../state/build_data/items/item_catalog.h"
+#include "../../../../../state/equipment/light/resolution/configured_equipment_light_resolver.h"
 #include "../../../../../state/runtime/runtime.h"
+#include "../../../../../state/unlocks/unlocks_records.h"
 #include "internal.h"
 #include "snapshot_storage.h"
 
 namespace sunrise::server::bap::encrypted::push::snapshot {
 namespace {
 
-/** One line carries the soid, the object count and the encoded size. */
+/** Capacity for one social-roster status line. */
 constexpr std::size_t kReportCapacity = 160;
 
+/** Family-two member byte offset for the signed Power field. */
+constexpr std::size_t kPowerOffset = 20;
 /** Where the member record carries the emblem the panel row reads. */
 constexpr std::size_t kEmblemDefinitionOffset = 36;
 constexpr std::size_t kEmblemVariantOffset = 38;
+/** Family-two member byte offset for the account Triumph score field. */
+constexpr std::size_t kTriumphScoreOffset = 44;
 
 /**
  * Absent definition index. The variant field always takes it, because a real value there indexes
@@ -66,6 +73,23 @@ constexpr std::uint16_t kEmptyDefinitionIndex = 0xFFFFU;
     return false;
 }
 
+/**
+ * Computes the selected character's Power, the same value the banner and character card show.
+ * @param account Account snapshot, already read under the lock by the caller.
+ * @param power Receives the equipment light plus the artifact bonus, or 0 on failure.
+ * @return False when nothing is selected or an equipped item cannot be resolved.
+ */
+[[nodiscard]] bool selected_character_power(const state::AccountState& account,
+                                            std::int32_t& power) noexcept {
+    power = 0;
+    for (std::size_t index = 0; index < account.characterCount; ++index) {
+        if (account.characters[index].selected) {
+            return state::equipment::light::resolution::character_light(account, index, power);
+        }
+    }
+    return false;
+}
+
 } // namespace
 
 /** Builds the family-two snapshot carrying the social roster directory and member record. */
@@ -93,6 +117,16 @@ bool prepare_social_roster(Scratch& scratch,
     if (!selected_emblem_definition_index(account, emblem, emblemHash)) {
         emblem = kEmptyDefinitionIndex;
     }
+    // Preserve the zero wire fallback when Power resolution fails.
+    std::int32_t power = 0;
+    if (!selected_character_power(account, power)) {
+        power = 0;
+    }
+    // The client reads the score as a signed 32-bit value; the store never publishes it negative.
+    const std::uint32_t storedScore = state::unlocks::records::score();
+    const std::int32_t triumphScore = static_cast<std::int32_t>(
+        (std::min)(storedScore,
+                   static_cast<std::uint32_t>((std::numeric_limits<std::int32_t>::max)())));
 
     Prepared staged{};
     std::size_t objectCount = 0;
@@ -102,10 +136,12 @@ bool prepare_social_roster(Scratch& scratch,
     /**
      * Writes one object and stages it.
      * Both lookups match on the first qword, so the directory leads with the account soid and
-     * repeats it at +8 as the link the member record leads with. Only the member has the emblem.
+     * repeats it at +8 as the link the member record leads with. Only the member has the Power,
+     * the emblem and the Triumph score.
      */
     const auto emit = [&](std::size_t size, std::uint32_t id, bool directory) noexcept {
-        if (objectCount >= staged.objects.size() || size < kEmblemVariantOffset + sizeof emblem) {
+        if (objectCount >= staged.objects.size()
+            || size < kTriumphScoreOffset + sizeof triumphScore) {
             return false;
         }
         const auto body = destination.subspan(rawUsed, size);
@@ -117,10 +153,12 @@ bool prepare_social_roster(Scratch& scratch,
                         sizeof account.primarySoid);
         } else {
             std::memcpy(body.data(), &account.primarySoid, sizeof account.primarySoid);
+            std::memcpy(body.data() + kPowerOffset, &power, sizeof power);
             std::memcpy(body.data() + kEmblemDefinitionOffset, &emblem, sizeof emblem);
             std::memcpy(body.data() + kEmblemVariantOffset,
                         &kEmptyDefinitionIndex,
                         sizeof kEmptyDefinitionIndex);
+            std::memcpy(body.data() + kTriumphScoreOffset, &triumphScore, sizeof triumphScore);
         }
         std::size_t compressedSize = 0;
         if (!compress_object(scratch,
@@ -169,12 +207,15 @@ bool prepare_social_roster(Scratch& scratch,
     const int written = std::snprintf(line.data(),
                                       line.size(),
                                       "ev=queuez stage=social_roster result=ok soid=0x%016llX"
-                                      " objects=%zu bytes=%zu emblem=%u hash=0x%08X",
+                                      " objects=%zu bytes=%zu emblem=%u hash=0x%08X power=%d"
+                                      " score=%d",
                                       static_cast<unsigned long long>(account.primarySoid),
                                       objectCount,
                                       rawUsed,
                                       static_cast<unsigned>(emblem),
-                                      static_cast<unsigned>(emblemHash));
+                                      static_cast<unsigned>(emblemHash),
+                                      static_cast<int>(power),
+                                      static_cast<int>(triumphScore));
     if (written > 0) {
         core::log::write(core::log::Channel::server,
                          core::log::Level::info,