فهرست منبع

feat(inventory): add atomic item mutation transactions

Add prepare, preview, and commit transactions for Collections acquisition, profile-stack acquisition, dismantling, generic equip and unequip, socket changes, and item-state toggles.

Every mutation validates ownership, native bucket placement, socket compatibility, serial/version overflow, full before-images, and canonical after-images before publishing State. Failed or stale operations leave the account untouched.

Material requirements are resolved from extracted build data and charged against profile stacks in the same transaction, so item creation and currency deductions cannot diverge.
Thomas Shields 3 هفته پیش
والد
کامیت
83521885fe
2فایلهای تغییر یافته به همراه1961 افزوده شده و 0 حذف شده
  1. 344 0
      Sunrise/src/state/runtime/runtime.h
  2. 1617 0
      Sunrise/src/state/runtime/state_account_runtime.cpp

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

@@ -1,5 +1,7 @@
 #pragma once
 
+#include <array>
+#include <cstddef>
 #include <cstdint>
 #include <span>
 
@@ -7,6 +9,164 @@
 
 namespace sunrise::state {
 
+/**
+ * Assigns runtime SOIDs only to installed profile mod/shader rows which are socket action sources.
+ * Currency, material, and consumable profile rows remain canonically non-instanced.
+ */
+[[nodiscard]] bool ensure_profile_item_identities() noexcept;
+
+/** Direction of one checked character equipment mutation. */
+enum class EquipmentMutationKind : std::uint8_t {
+    none,
+    equip,
+    unequip,
+};
+
+/** Prepared character-inventory mutation kept private until its response and update both fit. */
+struct PendingEquipmentSwap {
+    /** Exact prepare-time character view used as the commit staleness guard. */
+    CharacterState beforeCharacter{};
+    /** Canonical after-image, including every row-change mutation generation. */
+    CharacterState afterCharacter{};
+    std::uint64_t characterSoid{};
+    std::uint64_t requestedInstanceSoid{};
+    std::uint64_t previousInstanceSoid{};
+    std::size_t characterIndex{};
+    std::size_t equipmentSlotIndex{};
+    std::size_t inventoryIndex{};
+    std::size_t movedItemCount{};
+    std::uint8_t nativeEquipmentSlot{};
+    EquipmentMutationKind kind{};
+    bool prepared{};
+};
+
+/** Prepared selected-character inventory insertion kept private until its reply and push fit. */
+struct PendingItemAcquisition {
+    CharacterState beforeCharacter{};
+    CharacterState afterCharacter{};
+    /** Exact profile material view observed before and after charging the native requirement set.
+     */
+    std::array<account::inventory::ProfileItem, account::inventory::kProfileItemCapacity>
+        beforeProfileItems{};
+    std::array<account::inventory::ProfileItem, account::inventory::kProfileItemCapacity>
+        afterProfileItems{};
+    std::uint64_t accountSoid{};
+    std::uint64_t characterSoid{};
+    std::uint64_t acquiredInstanceSoid{};
+    std::uint32_t acquiredDefinitionHash{};
+    std::uint32_t materialRequirementSetHash{};
+    std::uint32_t expectedNextInventorySerial{};
+    std::size_t characterIndex{};
+    std::size_t expectedInventoryCount{};
+    std::size_t expectedProfileItemCount{};
+    std::size_t afterProfileItemCount{};
+    std::size_t inventoryIndex{};
+    std::uint16_t collectibleIndex{};
+    std::uint16_t inventoryRow{};
+    std::uint8_t equipmentSlot{};
+    std::uint8_t materialRequirementCount{};
+    bool profileChanged{};
+    bool prepared{};
+};
+
+/** Prepared account-profile stack insertion kept private until its reply and account upsert fit. */
+struct PendingProfileItemAcquisition {
+    /** Exact profile inventory observed while preparing the mutation. */
+    std::array<account::inventory::ProfileItem, account::inventory::kProfileItemCapacity>
+        beforeItems{};
+    /** Canonical profile inventory after incrementing or appending one stack. */
+    std::array<account::inventory::ProfileItem, account::inventory::kProfileItemCapacity>
+        afterItems{};
+    std::uint64_t accountSoid{};
+    /** Stable profile-row source identity, preserved for increments and allocated for appends. */
+    std::uint64_t acquiredInstanceSoid{};
+    std::uint32_t acquiredDefinitionHash{};
+    std::uint32_t materialRequirementSetHash{};
+    std::size_t expectedItemCount{};
+    std::size_t afterItemCount{};
+    std::size_t profileIndex{};
+    std::int32_t previousQuantity{};
+    std::int32_t acquiredQuantity{};
+    std::int32_t previousMutationSerial{};
+    std::int32_t acquiredMutationSerial{};
+    std::uint16_t collectibleIndex{};
+    std::uint8_t bucketId{};
+    std::uint8_t materialRequirementCount{};
+    /** True only for installed profile mod/shader rows materialized as Family-4 residents. */
+    bool actionSource{};
+    bool appended{};
+    bool prepared{};
+};
+
+/** Prepared selected-character inventory removal kept private until its reply and push fit. */
+struct PendingItemDismantle {
+    /** Exact prepare-time character view used as the commit staleness guard. */
+    CharacterState beforeCharacter{};
+    /** Canonical dense inventory after-image, including row-change mutation generations. */
+    CharacterState afterCharacter{};
+    account::inventory::Item dismantledItem{};
+    std::uint64_t characterSoid{};
+    std::uint64_t dismantledInstanceSoid{};
+    std::size_t characterIndex{};
+    std::size_t expectedInventoryCount{};
+    std::size_t inventoryIndex{};
+    std::size_t movedInventoryItemCount{};
+    std::uint16_t inventoryRow{};
+    std::uint8_t equipmentSlot{};
+    bool prepared{};
+};
+
+/** Prepared ordinary-socket selection for one selected-character item instance. */
+struct PendingSocketPlug {
+    /** Exact prepare-time character view used as the commit staleness guard. */
+    CharacterState beforeCharacter{};
+    /** Canonical after-image. Only the target item's authored socket block differs. */
+    CharacterState afterCharacter{};
+    /** Exact account-wide material balances observed before applying the installed cost set. */
+    std::array<account::inventory::ProfileItem, account::inventory::kProfileItemCapacity>
+        beforeProfileItems{};
+    /** Canonical material balances after every consuming row in the installed cost set. */
+    std::array<account::inventory::ProfileItem, account::inventory::kProfileItemCapacity>
+        afterProfileItems{};
+    std::uint64_t accountSoid{};
+    std::uint64_t characterSoid{};
+    std::uint64_t targetInstanceSoid{};
+    std::uint32_t targetDefinitionHash{};
+    std::uint32_t plugDefinitionHash{};
+    std::uint32_t materialRequirementSetHash{};
+    std::size_t characterIndex{};
+    std::size_t expectedProfileItemCount{};
+    std::size_t afterProfileItemCount{};
+    /** Equipment semantic index or dense inventory index, selected by `targetEquipped`. */
+    std::size_t itemIndex{};
+    std::uint16_t targetDefinitionIndex{};
+    std::uint16_t plugDefinitionIndex{};
+    std::uint16_t materialRequirementSetIndex{0xFFFFU};
+    std::uint8_t socketLane{};
+    std::uint8_t targetBucketId{};
+    std::uint8_t plugBucketId{};
+    std::uint8_t materialRequirementCount{};
+    bool profileChanged{};
+    bool targetEquipped{};
+    bool prepared{};
+};
+
+/** Prepared accumulated item-state change for one selected-character item instance. */
+struct PendingItemState {
+    CharacterState beforeCharacter{};
+    CharacterState afterCharacter{};
+    std::uint64_t characterSoid{};
+    std::uint64_t targetInstanceSoid{};
+    std::size_t characterIndex{};
+    /** Equipment semantic index or dense inventory index, selected by `targetEquipped`. */
+    std::size_t itemIndex{};
+    std::uint16_t targetDefinitionIndex{};
+    std::uint32_t beforeFlags{};
+    std::uint32_t afterFlags{};
+    bool targetEquipped{};
+    bool prepared{};
+};
+
 /**
  * Loads cached build data and generates secrets with Sunrise's authored activity defaults.
  * @param module Loaded Sunrise module, or null to disable disk persistence.
@@ -56,6 +216,190 @@ void shutdown() noexcept;
  */
 [[nodiscard]] bool set_selected_character(std::uint64_t characterSoid, bool& changed) noexcept;
 
+/**
+ * Prepares an equip operation for one unequipped instance on the selected character.
+ * An occupied slot is swapped; an empty semantic slot receives the requested item directly.
+ *
+ * @param requestedInstanceSoid Unequipped item instance selected by the Client.
+ * @param mutation
+ * Gets the checked after-image without changing account State.
+ * @return True when the instance is
+ * owned, unequipped, and maps to one supported native equipment slot.
+ */
+[[nodiscard]] bool prepare_equipment_swap(std::uint64_t requestedInstanceSoid,
+                                          PendingEquipmentSwap& mutation) noexcept;
+
+/**
+ * Prepares an unequip operation for one equipped selected-character instance.
+ * The item is inserted before existing inventory items in its native bucket so their published
+ * rows remain stable. Native slots without a proven semantic State mapping are rejected.
+ *
+ * @param requestedInstanceSoid Equipped item instance selected by the Client.
+ * @param mutation Gets the checked after-image without changing account State.
+ * @return True when the instance is equipped and the dense character inventory has room.
+ */
+[[nodiscard]] bool prepare_equipment_unequip(std::uint64_t requestedInstanceSoid,
+                                             PendingEquipmentSwap& mutation) noexcept;
+
+/**
+ * Commits a prepared equipment mutation only while the full captured character still matches.
+ *
+ * @param mutation Prepared mutation, always cleared before this function returns.
+ * @return True
+ * when the equip or unequip commits atomically and leaves the whole account valid.
+ */
+[[nodiscard]] bool commit_equipment_swap(PendingEquipmentSwap& mutation) noexcept;
+
+/**
+ * Prepares one installed equippable definition as a new selected-character inventory instance.
+
+ * * Native-default sockets, a unique runtime SOID, and the selected character's current item level
+
+ * * are used. Full loadout resolution is the authoritative bucket-capacity check.
+ *
+ * @param
+ * definitionHash Installed item definition requested by the Client.
+ * @param mutation Gets a
+ * checked after-image without changing account State.
+ * @return True when the item and every
+ * existing loadout row resolve with one free native row.
+ */
+[[nodiscard]] bool prepare_item_acquisition(std::uint16_t collectibleIndex,
+                                            std::uint32_t definitionHash,
+                                            PendingItemAcquisition& mutation) noexcept;
+
+/** Builds the exact full-account after-image while a prepared item pull remains current. */
+[[nodiscard]] bool preview_item_acquisition(const PendingItemAcquisition& mutation,
+                                            AccountState& after) noexcept;
+
+/**
+ * Commits a prepared inventory insertion only while its selected character, existing loadout,
+
+ * * and next inventory serial still match the prepare-time view.
+ *
+ * @param mutation Prepared
+ * mutation, always cleared before this function returns.
+ * @return True when the insertion commits
+ * atomically and leaves the whole account valid.
+ */
+[[nodiscard]] bool commit_item_acquisition(PendingItemAcquisition& mutation) noexcept;
+
+/**
+ * Prepares one installed profile-owned stackable definition for a Collections pull.
+ *
+ * An existing non-full stack is incremented. Otherwise a new dense State entry is appended only
+ * when the installed profile bucket still owns a free native row.
+ *
+ * @param definitionHash Installed stackable definition requested by the Client.
+ * @param mutation Gets the checked profile before/after images without changing account State.
+ * @return True when the definition belongs to the main profile array and one unit fits.
+ */
+[[nodiscard]] bool
+prepare_profile_item_acquisition(std::uint16_t collectibleIndex,
+                                 std::uint32_t definitionHash,
+                                 PendingProfileItemAcquisition& mutation) noexcept;
+
+/**
+ * Materializes a prepared profile acquisition over the current account only while its complete
+ * profile-inventory view is unchanged. This is the account object encoded before commit.
+ *
+ * @param mutation Prepared mutation that remains owned by the transaction.
+ * @param after Gets the exact full-account after-image used by the Family-4 upsert.
+ * @return True when the mutation is whole and its prepare-time profile remains current.
+ */
+[[nodiscard]] bool preview_profile_item_acquisition(const PendingProfileItemAcquisition& mutation,
+                                                    AccountState& after) noexcept;
+
+/**
+ * Commits a prepared profile stack insertion only while its prepare-time profile remains current.
+ *
+ * @param mutation Prepared mutation, always cleared before this function returns.
+ * @return True when the stack update commits atomically and leaves the whole account valid.
+ */
+[[nodiscard]] bool
+commit_profile_item_acquisition(PendingProfileItemAcquisition& mutation) noexcept;
+
+/**
+ * Prepares removal of one unequipped instance from the selected character.
+ *
+ * The authored inventory prefix is compacted. Any surviving item whose installed native row
+ * changes receives a fresh mutation generation. Equipped items are never accepted.
+ *
+ * @param instanceSoid Unequipped item-instance key selected by the Client.
+ * @param mutation Gets checked before/after images without changing account State.
+ * @return True when the instance is uniquely owned by the selected character and both loadouts
+ * resolve completely.
+ */
+[[nodiscard]] bool prepare_item_dismantle(std::uint64_t instanceSoid,
+                                          PendingItemDismantle& mutation) noexcept;
+
+/**
+ * Commits a prepared inventory removal only while the complete prepare-time character view is
+ * unchanged.
+ *
+ * @param mutation Prepared mutation, always cleared before this function returns.
+ * @return True when the removal commits atomically and leaves the whole account valid.
+ */
+[[nodiscard]] bool commit_item_dismantle(PendingItemDismantle& mutation) noexcept;
+
+/**
+ * Prepares one exact opcode-903 ordinary-socket selection on a selected-character item.
+ *
+ * The target may be equipped or unequipped. Native defaults are first materialized into a complete
+ * authored socket block, then only the requested lane changes. Item identity, native row,
+ * quantity, level, and mutation generation remain byte-for-byte stable.
+ *
+ * @param targetInstanceSoid Selected-character item-instance key named by the Client.
+ * @param socketLane Zero-based ordinary socket lane.
+ * @param plugDefinitionIndex Installed plug-definition row selected by the Client.
+ * @param mutation Gets the checked before/after images without changing account State.
+ * @return True when ownership, item detail, lane, plug compatibility, and both loadouts validate.
+ */
+[[nodiscard]] bool prepare_socket_plug(std::uint64_t targetInstanceSoid,
+                                       std::uint8_t socketLane,
+                                       std::uint16_t plugDefinitionIndex,
+                                       PendingSocketPlug& mutation) noexcept;
+
+/**
+ * Prepares one ordinary-socket selection for an exact character-screen item selector.
+ *
+ * Opcode 1901 carries four times the item instance's low 62-bit identity. The resolved selected-
+ * character instance is passed through the same checked transition as an instance-addressed
+ * action, so acquired and unequipped items do not depend on a coincidental menu-row ordinal.
+ *
+ * @param itemSelector Encoded character-item identity selector carried by opcode 1901.
+ * @param requestedSocketLane Native socket action lane; the installed compatibility relation
+ * resolves the target's exact physical lane.
+ * @param plugDefinitionIndex Installed plug-definition row selected by the Client.
+ * @param mutation Gets the checked before/after images without changing account State.
+ * @return True when the location has one matching item, the plug resolves to exactly the
+ * requested compatible ordinary socket lane, and the socket transition is valid.
+ */
+[[nodiscard]] bool prepare_character_selector_socket_plug(std::uint64_t itemSelector,
+                                                          std::uint8_t requestedSocketLane,
+                                                          std::uint16_t plugDefinitionIndex,
+                                                          PendingSocketPlug& mutation) noexcept;
+
+/** Produces the complete uncommitted account after-image for a prepared socket transaction. */
+[[nodiscard]] bool preview_socket_plug(const PendingSocketPlug& mutation,
+                                       AccountState& after) noexcept;
+
+/**
+ * Commits a prepared socket selection only while the complete prepare-time character is unchanged.
+ * @param mutation Prepared mutation, always cleared before this function returns.
+ * @return True when the exact canonical transition commits atomically.
+ */
+[[nodiscard]] bool commit_socket_plug(PendingSocketPlug& mutation) noexcept;
+
+/** Prepares one complete native item-state value for an owned selected-character instance. */
+[[nodiscard]] bool prepare_item_state(std::uint64_t targetInstanceSoid,
+                                      std::uint16_t targetDefinitionIndex,
+                                      std::uint32_t flags,
+                                      PendingItemState& mutation) noexcept;
+
+/** Commits one prepared item-state change behind an exact full-character staleness guard. */
+[[nodiscard]] bool commit_item_state(PendingItemState& mutation) noexcept;
+
 /** @return A copy of the active account state, read under the lock. */
 [[nodiscard]] AccountState account_snapshot() noexcept;
 

تفاوت فایلی نمایش داده نمی شود زیرا این فایل بسیار بزرگ است
+ 1617 - 0
Sunrise/src/state/runtime/state_account_runtime.cpp


برخی فایل ها در این مقایسه diff نمایش داده نمی شوند زیرا تعداد فایل ها بسیار زیاد است