Przeglądaj źródła

Clean up seasonal progression flows

Millie 1 tydzień temu
rodzic
commit
0a1766e061

+ 4 - 5
Sunrise/src/middleware/datagen/character_record/appearance/character_appearance_stats.cpp

@@ -10,9 +10,7 @@ namespace sunrise::middleware::datagen::character_record::appearance {
 namespace {
 
 namespace constants = state::build_data::constants;
-
-/** Season 11's visible artifact; its sole declared stat is the conditional Power bonus. */
-constexpr std::uint32_t kSeedOfSilverWingsHash = 0x613A3DA6U;
+namespace seasonal = state::progression::seasonal_experience;
 
 /**
  * Sums one definition's declared contribution to a single stat row.
@@ -156,12 +154,13 @@ bool apply_stats(const family4::loadout::ResolvedInstances& instances,
         details::Definition detail{};
         Equipped equipped{};
         if (!resolve_equipped(instances.items[index], detail, equipped)
-            || detail.definitionHash != kSeedOfSilverWingsHash || detail.statCount == 0
+            || detail.definitionHash != seasonal::kSeedOfSilverWingsHash
+            || detail.statCount == 0
             || detail.stats.front().row == details::kEmptyStatRow) {
             continue;
         }
         append(detail.stats.front().row,
-               state::progression::seasonal_experience::artifact_power_bonus(),
+               seasonal::artifact_power_bonus(),
                appearance.characterStats,
                written);
         break;

+ 11 - 12
Sunrise/src/middleware/datagen/family4/account/account_encoder.cpp

@@ -20,6 +20,8 @@
 namespace sunrise::middleware::datagen::family4::account {
 namespace {
 
+namespace seasonal = state::progression::seasonal_experience;
+
 /** Every bit set is the native empty biased 16-bit definition index. */
 constexpr std::uint16_t kEmptyDefinitionIndex = (std::numeric_limits<std::uint16_t>::max)();
 /** Signed 32-bit maximum keeps publicity deadlines beyond a normal session clock. */
@@ -95,7 +97,7 @@ bool encode(const state::AccountState& state,
     object.profileUnlockFlags = unlocks.profileFlags;
     object.objectiveValues = unlocks.objectiveValues;
     // Season claims map to account flags; pending claims overlay the same response.
-    if (!state::progression::seasonal_experience::apply_reward_claims(object.acquiredFlags)) {
+    if (!seasonal::apply_reward_claims(object.acquiredFlags)) {
         return false;
     }
     if (pendingSeasonReward.has_value()) {
@@ -127,25 +129,22 @@ bool encode(const state::AccountState& state,
                                object.progressions)) {
         return false;
     }
-    const std::int32_t earnedExperience = state::progression::seasonal_experience::earned();
-    constexpr std::int32_t experiencePerRank = 100'000;
-    constexpr std::int32_t maximumPassExperience = 9'900'000;
+    const std::int32_t earnedExperience = seasonal::earned();
     for (std::size_t slot = 0; slot < object.progressions.size(); ++slot) {
         progression::layout::Entry& entry = object.progressions[slot];
         std::int32_t projectedExperience = earnedExperience;
         if (entry.definitionIndex == state::progression::season_pass::kProgressionDefinitionIndex) {
-            projectedExperience = (std::min)(earnedExperience, maximumPassExperience);
+            projectedExperience = (std::min)(earnedExperience, seasonal::kMaximumPassExperience);
         } else if (entry.definitionIndex
                    == state::progression::season_pass::kHudProgressionDefinitionIndex) {
-            projectedExperience = earnedExperience < maximumPassExperience
-                                      ? earnedExperience % experiencePerRank
-                                      : earnedExperience - maximumPassExperience;
+            projectedExperience =
+                earnedExperience < seasonal::kMaximumPassExperience
+                    ? earnedExperience % seasonal::kExperiencePerRank
+                    : earnedExperience - seasonal::kMaximumPassExperience;
         } else if (entry.definitionIndex
-                       != state::progression::seasonal_experience::
-                           kArtifactPowerProgressionDefinitionIndex
+                       != seasonal::kArtifactPowerProgressionDefinitionIndex
                    && entry.definitionIndex
-                          != state::progression::seasonal_experience::
-                              kArtifactUnlockProgressionDefinitionIndex) {
+                          != seasonal::kArtifactUnlockProgressionDefinitionIndex) {
             continue;
         }
         entry.values[0] = (std::max)(entry.values[0], projectedExperience);

+ 0 - 1
Sunrise/src/server/bap/bap_route.cpp

@@ -305,7 +305,6 @@ bool arm_seasonal_experience_presentation(std::int32_t amount) noexcept {
         if (!state::progression::seasonal_experience::grant(amount)) {
             return false;
         }
-        (void)state::refresh_artifact_progression();
         peer.pendingSeasonalExperienceAmount += amount;
         peer.pendingSeasonalExperienceFailures = 0;
         return true;

+ 0 - 1
Sunrise/src/server/bap/encrypted/activity_message/receipts/activity_message_receipts.cpp

@@ -461,7 +461,6 @@ resolve_egg_context(const client::player::position::Snapshot& player,
         const bool queued = bap::arm_seasonal_experience_presentation(kBaseExperienceReward);
         if (!queued) {
             (void)state::progression::seasonal_experience::grant(kBaseExperienceReward);
-            (void)state::refresh_artifact_progression();
         }
     }
     return outcome;

+ 55 - 76
Sunrise/src/server/bap/encrypted/push/snapshot/family4_selection_move.cpp

@@ -208,6 +208,40 @@ bool prepare_selection_move(Scratch& scratch,
     return true;
 }
 
+/** Compresses and commits one selected-character-only Family-4 increment. */
+[[nodiscard]] bool finish_character_upsert(Scratch& scratch,
+                                           const Reservation& reservation,
+                                           std::span<std::byte> characterBytes,
+                                           const queuez::EquipmentSwap& update,
+                                           const char* objectFailure,
+                                           const char* commitFailure,
+                                           Prepared& prepared) noexcept {
+    Prepared staged{};
+    staged.rawClearSize =
+        (std::max)(reservation.rawClearSize,
+                   reservation.rawWriteOffset + family4_datagen::character::layout::kObjectSize);
+    std::size_t compressedExtent = reservation.compressedWriteOffset;
+    if (!append_object(scratch,
+                       characterBytes,
+                       update.characterDefinitionId,
+                       update.characterSoid,
+                       staged.objects.front(),
+                       compressedExtent)) {
+        return report_failure(objectFailure);
+    }
+    staged.compressedClearSize = (std::max)(reservation.compressedClearSize, compressedExtent);
+    staged.family = middleware::queuez::Family{kAccountFamilyType,
+                                                update.after.family4RootSoid,
+                                                update.after.family4Version,
+                                                0,
+                                                std::span(staged.objects).first(1)};
+    if (!commit(staged, prepared)) {
+        clear_after(scratch, reservation);
+        return report_failure(commitFailure);
+    }
+    return true;
+}
+
 /** Builds a single-character Family-4 upsert from an uncommitted equipment after-image. */
 bool prepare_equipment_swap(
     Scratch& scratch,
@@ -279,32 +313,13 @@ bool prepare_equipment_swap(
             return report_failure("equip_moved_inventory_row");
         }
     }
-    Prepared staged{};
-    staged.rawClearSize =
-        (std::max)(reservation.rawClearSize,
-                   reservation.rawWriteOffset + family4_datagen::character::layout::kObjectSize);
-    std::size_t compressedExtent = reservation.compressedWriteOffset;
-    if (!append_object(scratch,
-                       characterBytes,
-                       swap.characterDefinitionId,
-                       swap.characterSoid,
-                       staged.objects.front(),
-                       compressedExtent)) {
-        return report_failure("equip_character_object");
-    }
-    staged.compressedClearSize = (std::max)(reservation.compressedClearSize, compressedExtent);
-    staged.family = middleware::queuez::Family{
-        kAccountFamilyType,
-        swap.after.family4RootSoid,
-        swap.after.family4Version,
-        0,
-        std::span(staged.objects).first(1),
-    };
-    if (!commit(staged, prepared)) {
-        clear_after(scratch, reservation);
-        return report_failure("equip_commit");
-    }
-    return true;
+    return finish_character_upsert(scratch,
+                                   reservation,
+                                   characterBytes,
+                                   swap,
+                                   "equip_character_object",
+                                   "equip_commit",
+                                   prepared);
 }
 
 /** Builds a single-character Family-4 upsert from an uncommitted item-state after-image. */
@@ -368,32 +383,13 @@ bool prepare_item_state(
         return report_failure("item_state_presentation");
     }
 
-    Prepared staged{};
-    staged.rawClearSize =
-        (std::max)(reservation.rawClearSize,
-                   reservation.rawWriteOffset + family4_datagen::character::layout::kObjectSize);
-    std::size_t compressedExtent = reservation.compressedWriteOffset;
-    if (!append_object(scratch,
-                       characterBytes,
-                       update.characterDefinitionId,
-                       update.characterSoid,
-                       staged.objects.front(),
-                       compressedExtent)) {
-        return report_failure("item_state_character_object");
-    }
-    staged.compressedClearSize = (std::max)(reservation.compressedClearSize, compressedExtent);
-    staged.family = middleware::queuez::Family{
-        kAccountFamilyType,
-        update.after.family4RootSoid,
-        update.after.family4Version,
-        0,
-        std::span(staged.objects).first(1),
-    };
-    if (!commit(staged, prepared)) {
-        clear_after(scratch, reservation);
-        return report_failure("item_state_commit");
-    }
-    return true;
+    return finish_character_upsert(scratch,
+                                   reservation,
+                                   characterBytes,
+                                   update,
+                                   "item_state_character_object",
+                                   "item_state_commit",
+                                   prepared);
 }
 
 /** Builds a single-character Family-4 upsert from an uncommitted artifact mask. */
@@ -446,30 +442,13 @@ bool prepare_artifact_purchase(
         return report_failure("artifact_projection");
     }
 
-    Prepared staged{};
-    staged.rawClearSize =
-        (std::max)(reservation.rawClearSize,
-                   reservation.rawWriteOffset + family4_datagen::character::layout::kObjectSize);
-    std::size_t compressedExtent = reservation.compressedWriteOffset;
-    if (!append_object(scratch,
-                       characterBytes,
-                       update.characterDefinitionId,
-                       update.characterSoid,
-                       staged.objects.front(),
-                       compressedExtent)) {
-        return report_failure("artifact_character_object");
-    }
-    staged.compressedClearSize = (std::max)(reservation.compressedClearSize, compressedExtent);
-    staged.family = middleware::queuez::Family{kAccountFamilyType,
-                                                update.after.family4RootSoid,
-                                                update.after.family4Version,
-                                                0,
-                                                std::span(staged.objects).first(1)};
-    if (!commit(staged, prepared)) {
-        clear_after(scratch, reservation);
-        return report_failure("artifact_commit");
-    }
-    return true;
+    return finish_character_upsert(scratch,
+                                   reservation,
+                                   characterBytes,
+                                   update,
+                                   "artifact_character_object",
+                                   "artifact_commit",
+                                   prepared);
 }
 
 /** Builds an incremental reset image without re-announcing every resident item. */

+ 20 - 20
Sunrise/src/server/bap/encrypted/queuez/queuez_deferred_push.cpp

@@ -40,6 +40,19 @@ void fail_seasonal_experience_presentation(Session& session) noexcept {
                      "ev=season_xp stage=deferred_presentation result=drop reason=retry_limit");
 }
 
+[[nodiscard]] const state::CharacterState*
+selected_character(const state::AccountState& account) noexcept {
+    if (!state::account::valid(account)) {
+        return nullptr;
+    }
+    for (std::size_t index = 0; index < account.characterCount; ++index) {
+        if (account.characters[index].selected) {
+            return &account.characters[index];
+        }
+    }
+    return nullptr;
+}
+
 /** Publishes and commits one character-inventory world reward. */
 [[nodiscard]] bool consume_world_item_acquisition(const WorldRewardRequest& request,
                                                   Session& session,
@@ -421,20 +434,14 @@ void fail_seasonal_experience_presentation(Session& session) noexcept {
         return false;
     }
     const state::AccountState account = state::account_snapshot();
-    std::size_t selected = account.characterCount;
-    for (std::size_t index = 0; index < account.characterCount; ++index) {
-        if (account.characters[index].selected) {
-            selected = index;
-            break;
-        }
-    }
-    if (!state::account::valid(account) || selected >= account.characterCount) {
+    const state::CharacterState* selected = selected_character(account);
+    if (selected == nullptr) {
         return false;
     }
     state::PendingArtifactPurchase refresh{};
     refresh.accountSoid = account.primarySoid;
-    refresh.characterSoid = account.characters[selected].soid;
-    refresh.characterIndex = selected;
+    refresh.characterSoid = selected->soid;
+    refresh.characterIndex = static_cast<std::size_t>(selected - account.characters.data());
     refresh.beforeMask = state::progression::seasonal_experience::artifact_mod_mask();
     refresh.afterMask = refresh.beforeMask;
     refresh.prepared = true;
@@ -480,14 +487,8 @@ void fail_seasonal_experience_presentation(Session& session) noexcept {
         return false;
     }
     const state::AccountState account = state::account_snapshot();
-    std::size_t selected = account.characterCount;
-    for (std::size_t index = 0; index < account.characterCount; ++index) {
-        if (account.characters[index].selected) {
-            selected = index;
-            break;
-        }
-    }
-    if (!state::account::valid(account) || selected >= account.characterCount) {
+    const state::CharacterState* selected = selected_character(account);
+    if (selected == nullptr) {
         return false;
     }
     const std::uint64_t instanceSoid =
@@ -496,8 +497,7 @@ void fail_seasonal_experience_presentation(Session& session) noexcept {
     auto nextSendNonce = session.sendNonce;
     std::size_t framedSize = 0;
     touchesScratch = true;
-    if (!queuez::stage_equipment_swap(
-            session.queuez, account.characters[selected].soid, update)
+    if (!queuez::stage_equipment_swap(session.queuez, selected->soid, update)
         || !push::append_artifact_item_refresh_notification(scratch,
                                                             update,
                                                             instanceSoid,

+ 25 - 24
Sunrise/src/server/web_service/web_service_actions.cpp

@@ -81,6 +81,23 @@ prepare_destination_resource_bundle(state::PendingSeasonPassReward& grant) noexc
 }
 
 /** Chooses one installed weapon or selected-class armour item from an auto-decrypting engram. */
+template <std::size_t Size>
+[[nodiscard]] std::span<const std::uint32_t>
+class_armour_pool(state::CharacterClass characterClass,
+                  const std::array<std::uint32_t, Size>& titan,
+                  const std::array<std::uint32_t, Size>& hunter,
+                  const std::array<std::uint32_t, Size>& warlock) noexcept {
+    switch (characterClass) {
+    case state::CharacterClass::hunter:
+        return hunter;
+    case state::CharacterClass::warlock:
+        return warlock;
+    case state::CharacterClass::titan:
+    default:
+        return titan;
+    }
+}
+
 [[nodiscard]] bool choose_engram_reward(std::uint32_t engramHash,
                                         std::uint16_t& itemIndex) noexcept {
     namespace pass = state::progression::season_pass;
@@ -100,32 +117,16 @@ prepare_destination_resource_bundle(state::PendingSeasonPassReward& grant) noexc
 
     if (engramHash == pass::kLegendaryEngramHash) {
         weapons = pass::kLegendaryEngramWeapons;
-        switch (character->characterClass) {
-        case state::CharacterClass::hunter:
-            armour = pass::kLegendaryHunterArmour;
-            break;
-        case state::CharacterClass::warlock:
-            armour = pass::kLegendaryWarlockArmour;
-            break;
-        case state::CharacterClass::titan:
-        default:
-            armour = pass::kLegendaryTitanArmour;
-            break;
-        }
+        armour = class_armour_pool(character->characterClass,
+                                   pass::kLegendaryTitanArmour,
+                                   pass::kLegendaryHunterArmour,
+                                   pass::kLegendaryWarlockArmour);
     } else if (engramHash == pass::kExoticEngramHash) {
         weapons = pass::kExoticEngramWeapons;
-        switch (character->characterClass) {
-        case state::CharacterClass::hunter:
-            armour = pass::kExoticHunterArmour;
-            break;
-        case state::CharacterClass::warlock:
-            armour = pass::kExoticWarlockArmour;
-            break;
-        case state::CharacterClass::titan:
-        default:
-            armour = pass::kExoticTitanArmour;
-            break;
-        }
+        armour = class_armour_pool(character->characterClass,
+                                   pass::kExoticTitanArmour,
+                                   pass::kExoticHunterArmour,
+                                   pass::kExoticWarlockArmour);
     } else {
         return false;
     }

+ 4 - 2
Sunrise/src/server/web_service/web_service_runtime.cpp

@@ -21,6 +21,7 @@
 #include "../../middleware/web_service/messages/opcode903.h"
 #include "../../middleware/web_service/web_service_envelope.h"
 #include "../../state/account/account_state.h"
+#include "../../state/progression/seasonal_experience.h"
 #include "../../state/runtime/runtime.h"
 #include "opcode_routes.h"
 #include "web_service_actions.h"
@@ -57,7 +58,6 @@ constexpr std::size_t kEchoLineCapacity = 64;
 constexpr std::int32_t kPurchaseRefusedCode = 1;
 /** Season of Arrivals artifact vendor row in the installed build's vendor index. */
 constexpr std::int16_t kArtifactVendorIndex = 430;
-constexpr std::uint16_t kArtifactSaleCount = 26;
 constexpr std::int32_t kArtifactResetGlimmerCost = 20'000;
 
 /**
@@ -126,7 +126,9 @@ constexpr std::int32_t kArtifactResetGlimmerCost = 20'000;
     purchase_codec::Request purchase{};
     if (!purchase_codec::parse_request(message, purchase)
         || purchase.vendorIndex != kArtifactVendorIndex || purchase.saleIndex < 0
-        || purchase.saleIndex >= static_cast<std::int16_t>(kArtifactSaleCount)) {
+        || purchase.saleIndex
+               >= static_cast<std::int16_t>(
+                   state::progression::seasonal_experience::kArtifactSaleCount)) {
         return false;
     }
     const auto saleIndex = static_cast<std::uint16_t>(purchase.saleIndex);

+ 2 - 4
Sunrise/src/state/progression/seasonal_experience.cpp

@@ -28,8 +28,6 @@ constexpr std::size_t kRewardClaimByteCount = (kRewardCount + 7U) / 8U;
 constexpr std::size_t kV1DocumentSize = kV1Magic.size() + sizeof(std::int32_t);
 constexpr std::size_t kV2DocumentSize = kV1DocumentSize + kRewardClaimByteCount;
 constexpr std::size_t kDocumentSize = kV2DocumentSize + sizeof(std::uint32_t);
-constexpr std::int32_t kExperiencePerRank = 100'000;
-constexpr std::uint16_t kMaximumRank = 100;
 constexpr std::int64_t kFirstArtifactPowerCost = 55'000;
 constexpr std::int64_t kArtifactPowerCostStep = 110'000;
 constexpr std::uint16_t kArtifactPowerBonusSlot = 602;
@@ -47,11 +45,11 @@ constexpr std::array<std::int32_t, 12> kArtifactPointCosts{0,
                                                            600'000,
                                                            790'000,
                                                            900'000};
-constexpr std::array<std::uint16_t, 26> kArtifactModFlags{
+constexpr std::array<std::uint16_t, kArtifactSaleCount> kArtifactModFlags{
     1428, 1429, 1430, 1431, 1432, 0,    1393, 1394, 1395, 1396, 1397, 1388, 1389,
     1390, 1391, 1392, 1398, 1399, 1400, 1401, 1402, 1403, 1404, 1405, 1406, 1407};
 /** Character acquired-flag mapping rows for each artifact vendor row. */
-constexpr std::array<std::uint16_t, 26> kArtifactModCharacterRows{
+constexpr std::array<std::uint16_t, kArtifactSaleCount> kArtifactModCharacterRows{
     164, 165, 166, 167, 168, 0,   149, 150, 151, 152, 153, 144, 145,
     146, 147, 148, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163};
 /** Character objective mapping row whose destination is artifact value slot 604. */

+ 6 - 0
Sunrise/src/state/progression/seasonal_experience.h

@@ -12,6 +12,12 @@ namespace sunrise::state::progression::seasonal_experience {
 
 inline constexpr std::uint16_t kArtifactPowerProgressionDefinitionIndex = 38;
 inline constexpr std::uint16_t kArtifactUnlockProgressionDefinitionIndex = 39;
+inline constexpr std::uint16_t kArtifactSaleCount = 26;
+inline constexpr std::int32_t kExperiencePerRank = 100'000;
+inline constexpr std::uint16_t kMaximumRank = 100;
+inline constexpr std::int32_t kMaximumPassExperience =
+    (static_cast<std::int32_t>(kMaximumRank) - 1) * kExperiencePerRank;
+inline constexpr std::uint32_t kSeedOfSilverWingsHash = 0x613A3DA6U;
 
 /** Resolves persistent storage and restores earned seasonal XP. */
 [[nodiscard]] bool initialize(void* module) noexcept;

+ 0 - 3
Sunrise/src/state/runtime/runtime.h

@@ -601,9 +601,6 @@ commit_profile_item_acquisition(PendingProfileItemAcquisition& mutation) noexcep
 /** @return A copy of the evaluated content state, read under the lock. */
 [[nodiscard]] InvestmentState investment_snapshot() noexcept;
 
-/** Refreshes artifact XP-derived global values after seasonal XP changes. */
-[[nodiscard]] bool refresh_artifact_progression() noexcept;
-
 /** Prepares one artifact purchase without changing persistent state. */
 [[nodiscard]] bool prepare_artifact_mod_unlock(std::uint16_t saleIndex,
                                                PendingArtifactPurchase& mutation) noexcept;

+ 78 - 73
Sunrise/src/state/runtime/state_runtime.cpp

@@ -41,6 +41,72 @@ constexpr std::uint64_t kGlobalFamily5Soid =
 constexpr std::uint16_t kActiveSeasonValueSlot = 607;
 /** One-based season number carried by the Season of Arrivals definition. */
 constexpr std::int32_t kSeasonOfArrivalsNumber = 11;
+constexpr std::uint32_t kGlimmerHash = 3159615086U;
+constexpr std::array<std::uint32_t, 25> kArtifactModHashes{
+    715026181U,  715026182U,  715026183U,  715026176U,  715026177U,
+    3213968582U, 3213968581U, 3213968580U, 3213968579U, 3213968578U,
+    3465659109U, 3465659110U, 3465659111U, 3465659104U, 3465659105U,
+    3175764264U, 3175764267U, 3175764266U, 3175764269U, 3175764268U,
+    4186620519U, 4186620516U, 4186620517U, 4186620514U, 4186620515U};
+
+[[nodiscard]] bool is_artifact_mod(std::uint32_t hash) noexcept {
+    return std::find(kArtifactModHashes.begin(), kArtifactModHashes.end(), hash)
+           != kArtifactModHashes.end();
+}
+
+[[nodiscard]] bool same_profile_item(const account::inventory::ProfileItem& left,
+                                     const account::inventory::ProfileItem& right) noexcept {
+    return left.instanceSoid == right.instanceSoid
+           && left.definitionHash == right.definitionHash && left.quantity == right.quantity
+           && left.mutationSerial == right.mutationSerial;
+}
+
+/** Restores artifact-mod sockets to their manifest-declared initial plugs. */
+[[nodiscard]] bool clear_artifact_sockets(account::inventory::Item& item) noexcept {
+    if (item.sockets.policy != account::inventory::SocketPolicy::authored) {
+        return true;
+    }
+    build_data::items::Definition base{};
+    build_data::items::details::Definition detail{};
+    if (!build_data::find_item_definition_hash(item.definitionHash, base)
+        || !build_data::find_configured_item_detail(base.definitionIndex, detail)
+        || detail.definitionIndex != base.definitionIndex
+        || item.sockets.plugCount != detail.ordinarySocketCount) {
+        return false;
+    }
+    for (std::size_t lane = 0; lane < item.sockets.plugCount; ++lane) {
+        const auto& plug = item.sockets.plugs[lane];
+        if (!plug.has_value() || !is_artifact_mod(*plug)) {
+            continue;
+        }
+        const std::uint16_t initial = detail.initialPlugIndices[lane];
+        if (initial == build_data::items::details::kUnavailableItemIndex) {
+            item.sockets.plugs[lane].reset();
+            continue;
+        }
+        build_data::items::Definition replacement{};
+        if (!build_data::find_item_definition_index(initial, replacement)) {
+            return false;
+        }
+        item.sockets.plugs[lane] = replacement.definitionHash;
+    }
+    return true;
+}
+
+[[nodiscard]] bool record_changed_item(const account::inventory::Item& prior,
+                                       const account::inventory::Item& current,
+                                       ArtifactResetResult& result) noexcept {
+    if (prior.sockets.policy == current.sockets.policy
+        && prior.sockets.plugCount == current.sockets.plugCount
+        && prior.sockets.plugs == current.sockets.plugs) {
+        return true;
+    }
+    if (result.instanceCount >= result.instanceSoids.size()) {
+        return false;
+    }
+    result.instanceSoids[result.instanceCount++] = current.instanceSoid;
+    return true;
+}
 
 /**
  * Makes one process-owned global value authoritative without disturbing authored overrides.
@@ -319,11 +385,6 @@ InvestmentState investment_snapshot() noexcept {
     return snapshot;
 }
 
-bool refresh_artifact_progression() noexcept {
-    // Artifact progress is projected into each Family-4 account image at encode time.
-    return true;
-}
-
 bool prepare_artifact_mod_unlock(std::uint16_t saleIndex,
                                  PendingArtifactPurchase& mutation) noexcept {
     mutation = {};
@@ -370,13 +431,6 @@ bool commit_artifact_mod_unlock(PendingArtifactPurchase& mutation) noexcept {
 
 bool reset_artifact(std::int32_t glimmerCost, ArtifactResetResult& result) noexcept {
     result = {};
-    constexpr std::uint32_t kGlimmerHash = 3159615086U;
-    constexpr std::array<std::uint32_t, 25> kArtifactModHashes{
-        715026181U,  715026182U,  715026183U,  715026176U,  715026177U,
-        3213968582U, 3213968581U, 3213968580U, 3213968579U, 3213968578U,
-        3465659109U, 3465659110U, 3465659111U, 3465659104U, 3465659105U,
-        3175764264U, 3175764267U, 3175764266U, 3175764269U, 3175764268U,
-        4186620519U, 4186620516U, 4186620517U, 4186620514U, 4186620515U};
     if (glimmerCost <= 0) {
         return false;
     }
@@ -398,10 +452,7 @@ bool reset_artifact(std::int32_t glimmerCost, ArtifactResetResult& result) noexc
             item.quantity -= spent;
             remainingCost -= spent;
         }
-        const bool artifactMod =
-            std::find(kArtifactModHashes.begin(), kArtifactModHashes.end(), item.definitionHash)
-            != kArtifactModHashes.end();
-        if (item.quantity > 0 && !artifactMod) {
+        if (item.quantity > 0 && !is_artifact_mod(item.definitionHash)) {
             compacted[compactedCount++] = item;
         }
     }
@@ -416,16 +467,11 @@ bool reset_artifact(std::int32_t glimmerCost, ArtifactResetResult& result) noexc
     for (std::size_t index = 0; index < before.profileItemCount; ++index) {
         serial = (std::max)(serial, before.profileItems[index].mutationSerial);
     }
-    const auto same = [](const auto& left, const auto& right) noexcept {
-        return left.instanceSoid == right.instanceSoid
-               && left.definitionHash == right.definitionHash && left.quantity == right.quantity
-               && left.mutationSerial == right.mutationSerial;
-    };
     std::size_t changedRows = 0;
     for (std::size_t index = 0; index < compactedCount; ++index) {
         changedRows += static_cast<std::size_t>(index >= before.profileItemCount
-                                                || !same(compacted[index],
-                                                         before.profileItems[index]));
+                                                || !same_profile_item(compacted[index],
+                                                                      before.profileItems[index]));
     }
     if (changedRows > static_cast<std::size_t>((std::numeric_limits<std::int32_t>::max)()
                                                - serial)) {
@@ -433,7 +479,7 @@ bool reset_artifact(std::int32_t glimmerCost, ArtifactResetResult& result) noexc
     }
     for (std::size_t index = 0; index < compactedCount; ++index) {
         if (index >= before.profileItemCount
-            || !same(compacted[index], before.profileItems[index])) {
+            || !same_profile_item(compacted[index], before.profileItems[index])) {
             compacted[index].mutationSerial = ++serial;
         }
     }
@@ -441,48 +487,16 @@ bool reset_artifact(std::int32_t glimmerCost, ArtifactResetResult& result) noexc
     AccountState candidate = before;
     candidate.profileItems = compacted;
     candidate.profileItemCount = compactedCount;
-    const auto clearArtifactSockets = [&](account::inventory::Item& item) noexcept {
-        if (item.sockets.policy != account::inventory::SocketPolicy::authored) {
-            return true;
-        }
-        build_data::items::Definition base{};
-        build_data::items::details::Definition detail{};
-        if (!build_data::find_item_definition_hash(item.definitionHash, base)
-            || !build_data::find_configured_item_detail(base.definitionIndex, detail)
-            || detail.definitionIndex != base.definitionIndex
-            || item.sockets.plugCount != detail.ordinarySocketCount) {
-            return false;
-        }
-        for (std::size_t lane = 0; lane < item.sockets.plugCount; ++lane) {
-            const auto& plug = item.sockets.plugs[lane];
-            if (!plug.has_value()
-                || std::find(kArtifactModHashes.begin(), kArtifactModHashes.end(), *plug)
-                       == kArtifactModHashes.end()) {
-                continue;
-            }
-            const std::uint16_t initial = detail.initialPlugIndices[lane];
-            if (initial == build_data::items::details::kUnavailableItemIndex) {
-                item.sockets.plugs[lane].reset();
-                continue;
-            }
-            build_data::items::Definition replacement{};
-            if (!build_data::find_item_definition_index(initial, replacement)) {
-                return false;
-            }
-            item.sockets.plugs[lane] = replacement.definitionHash;
-        }
-        return true;
-    };
     for (std::size_t characterIndex = 0; characterIndex < candidate.characterCount;
          ++characterIndex) {
         auto& character = candidate.characters[characterIndex];
         for (auto& item : character.equipment.slots) {
-            if (item.has_value() && !clearArtifactSockets(*item)) {
+            if (item.has_value() && !clear_artifact_sockets(*item)) {
                 return false;
             }
         }
         for (std::size_t itemIndex = 0; itemIndex < character.inventory.count; ++itemIndex) {
-            if (!clearArtifactSockets(character.inventory.values[itemIndex])) {
+            if (!clear_artifact_sockets(character.inventory.values[itemIndex])) {
                 return false;
             }
         }
@@ -493,29 +507,20 @@ bool reset_artifact(std::int32_t glimmerCost, ArtifactResetResult& result) noexc
         if (!before.characters[characterIndex].selected) {
             continue;
         }
-        const auto recordChanged = [&changed](const account::inventory::Item& prior,
-                                              const account::inventory::Item& current) noexcept {
-            if (prior.sockets.policy == current.sockets.policy
-                && prior.sockets.plugCount == current.sockets.plugCount
-                && prior.sockets.plugs == current.sockets.plugs) {
-                return true;
-            }
-            if (changed.instanceCount >= changed.instanceSoids.size()) {
-                return false;
-            }
-            changed.instanceSoids[changed.instanceCount++] = current.instanceSoid;
-            return true;
-        };
         const auto& prior = before.characters[characterIndex];
         const auto& current = candidate.characters[characterIndex];
         for (std::size_t slot = 0; slot < current.equipment.slots.size(); ++slot) {
             if (current.equipment.slots[slot].has_value()
-                && !recordChanged(*prior.equipment.slots[slot], *current.equipment.slots[slot])) {
+                && (!prior.equipment.slots[slot].has_value()
+                    || !record_changed_item(*prior.equipment.slots[slot],
+                                            *current.equipment.slots[slot],
+                                            changed))) {
                 return false;
             }
         }
         for (std::size_t index = 0; index < current.inventory.count; ++index) {
-            if (!recordChanged(prior.inventory.values[index], current.inventory.values[index])) {
+            if (!record_changed_item(
+                    prior.inventory.values[index], current.inventory.values[index], changed)) {
                 return false;
             }
         }