11-inventory-storage
暂无简介
- License 许可
- MIT
- 最近更新
- 2026-04-12
- Stars
- ★ 0
- Forks
- 0
02 资源文件 3
- 参考文档ERROR_CATALOG.md16.8 KB
- 参考文档FALSE_POSITIVES.md14.4 KB
- 参考文档README.md7.8 KB
03 SKILL.md 正文
11-inventory-storage
Status: ACTIVE AppliesTo: v10
InventoryStorage는 InventoryManager의 live runtime 데이터 컨테이너이다. 통화 잔고 / 장비(AbilityItemEquip) / 카드(AbilityItemCard) / 재료(AbilityItemMaterial) / 영웅(AbilityItemHero)을 통합 관리한다.
InventoryStorage는 InventoryManager가 소유하며 Devian.Samples.MobilePackage asmdef에 속한다.
외부 시스템의 public boundary는 InventoryManager이고, persistence boundary는 InventorySnapshot이다.
1. InventoryStorage
코드는 실제 InventoryStorage.cs 참조. sealed class이다.
CurrencyBalances— 내부Dictionary<CURRENCY_TYPE, long>기반 잔고 상태Equipments—Dictionary<string, AbilityItemEquip>(itemUid → 장비)Cards—Dictionary<string, AbilityItemCard>(item_id → 카드)Materials—Dictionary<string, AbilityItemMaterial>(item_id → 재료)Heroes—Dictionary<string, AbilityItemHero>(item_id → 영웅)Rentals—Dictionary<string, long>(item_id → expiresAtClientUtcMs)Passes—Dictionary<string, bool>(item_id → owned)GetEquip/AddEquip/RemoveEquip— 장비 CRUD (key=itemUid)GetEquipsByItemId— itemId로 인스턴스 목록 조회GetCard/AddCard— 카드 CRUDGetMaterial/AddMaterial— 재료 CRUDGetHero/AddHero— 영웅 CRUDSetRental(id, expiresAtClientUtcMs)/GetRentalExpiry(id)/HasActiveRental(id)/GetRentalRemainingMs(id)/RemoveRental(id)— 렌탈 CRUDSetPass(id, owned)/HasPass(id)/RemovePass(id)— 시즌패스 CRUDTreasureCurrent—InventoryTreasureCurrent(Exp/Level, sealed POCO)TreasureCounts—Dictionary<ITEM_GRADE_TYPE, int>(grade별 보유 chest count)GetTreasureCount(gradeType)/AddTreasure(gradeType, amount)/SetTreasureCount(gradeType, count)— treasure count CRUDAddTreasureExp(amount)— delegates toTreasureCurrent.ExpResetTreasure(level, exp)— delegates toTreasureCurrent.Reset(...)CopyFrom(source)— validated temp state를 live storage에 복사- Pass 변경 알림 publish는
InventoryManager가 담당한다 (trigger 직접 노출 금지). - 초기 보상 지급 트리거는
InventoryStorage가 아니라RewardManager.FirstInitAsync()에서 처리한다 (FirstRewardSettingssource). — 삭제됨. 21-savedata-system/43-savedata-json-codec의ToJson()SaveDataJsonCodec으로 이전.— 삭제됨. 21-savedata-system/43-savedata-json-codec의FromJson(string json)SaveDataJsonCodec으로 이전.
장비 장착
장비 장착/해제의 저장 로직은 AbilityItemHero._SetEquip/_RemoveEquip이 담당한다.
-
이 경로는 hero loadout metadata와 equip owner metadata만 갱신한다.
-
이미 다른 hero에 장착된 equip 이동은
InventoryStorage.Equip()가 target hero의 장착 가능 여부를 먼저 확인한 뒤 기존 owner를 정리하고 위임하는 경로만 사용한다. -
equip stat 계산은
AbilityUnitHero._Equip/_Unequip에서 수행한다. InventoryStorage는 hero/equip 조회 + 위임하는 편의 메서드를 제공한다. -
RemoveEquip은 장착 상태면 hero 슬롯 맵까지 함께 정리한 뒤 제거한다. -
Equip(heroId, equipSlot, equipUid)/Unequip(heroId, equipSlot)편의 메서드를 제공한다. -
slot key는
EQUIP_SLOT_TYPE이며NONE은 유효한 장착 slot이 아니다. -
SaveData inventory JSON의
heroes[*].equipskey도EQUIP_SLOT_TYPEenum name 문자열을 사용한다. 숫자 slot key serialize는 금지한다. -
two-handed main 장착에 따른
HAND_SUB자동 해제는 최종적으로AbilityItemHero._SetEquip()규칙에 따른다.
2. InventoryManager 관계
| 타입 | 책임 |
|---|---|
InventoryManager |
InventoryStorage 소유, 타입별 Apply/Revoke/Query API 제공 |
InventoryStorage |
CurrencyBalances (Dictionary<CURRENCY_TYPE, long>), Equipments (itemUid → AbilityItemEquip), Cards (item_id → AbilityItemCard), Materials (item_id → AbilityItemMaterial), Heroes (item_id → AbilityItemHero), SelectedHeroId (string), Rentals (item_id → expiresAtClientUtcMs), Passes (item_id → owned), TreasureCurrent (InventoryTreasureCurrent), TreasureCounts (ITEM_GRADE_TYPE → int) |
AbilityItemEquip |
OwnerUnitId/OwnerSlotType(별도 필드) + 능력치(UNIT_STAT_TYPE 기반) 관리 |
AbilityItemCard |
수량(UNIT_STAT_TYPE.ITEM_AMOUNT) + 능력치(UNIT_STAT_TYPE 기반) 관리 |
AbilityItemMaterial |
수량(UNIT_STAT_TYPE.ITEM_AMOUNT) + 능력치(UNIT_STAT_TYPE 기반) 관리 |
AbilityItemHero |
수량(UNIT_STAT_TYPE.ITEM_AMOUNT) + 영웅 장비 슬롯(Dict<EQUIP_SLOT_TYPE, AbilityItemEquip>) 관리 |
-
InventoryManager가InventoryStorage를 소유한다 (싱글톤 등록 안 함). -
장비는
itemUid(GUID)를 pk로 관리한다. 같은item_id에 여러 인스턴스가 존재할 수 있다. -
ApplyCard(item_id, amount)/AddCardAmount(item_id, delta)—_storage.Cards에 AbilityItemCard를 추가하고AddAmount(delta)로 수량을 증가시킨다. 음수 delta는 허용하지 않는다. -
ApplyMaterial(item_id, amount)/AddMaterialAmount(item_id, delta)—_storage.Materials에 AbilityItemMaterial을 추가하고AddAmount(delta)로 수량을 증가시킨다. 음수 delta는 허용하지 않는다. -
ApplyEquip(item_id)— 새itemUid(GUID)로 AbilityItemEquip을 생성하여_storage.Equipments에 추가한다. -
ApplyHero(item_id, amount)/AddHeroAmount(item_id, delta)—_storage.Heroes에 AbilityItemHero를 추가하고AddAmount(delta)로 수량을 증가시킨다. 음수 delta는 허용하지 않는다. -
SelectedHeroId— 현재 선택된 owned hero의 itemId raw state. 존재하지 않는 hero가 제거되면 함께 비운다. -
UpgradeEquipItem(baseItemUid, materialItemUid0, materialItemUid1)— manager boundary에서 equip upgrade recipe를 검증하고 source 3개를 제거한 뒤 upgraded equip 1개를 추가한다. storage는 개별RemoveEquip/AddEquip만 수행하고, manager는 생성된 upgradedAbilityItemEquipruntime을 반환한다. -
ApplyRental(item_id, durationMs)—_storage.SetRental(id, max(currentExpiry, now)+duration)로 로컬 만료 시각을 설정/연장한다. -
SetPassOwnership(item_id)—_storage.SetPass(id, true)로 소유권을 설정한다. -
ApplyTreasure(gradeType, amount)—_storage.AddTreasure(gradeType, amount)로 chest count를 누적한다. -
publish는
InventoryStorage가 아니라InventoryManager가 담당한다.PASS_OWNERSHIP_CHANGEDCURRENCY_CHANGEDITEM_EQUIP_CHANGEDITEM_CARD_CHANGEDITEM_MATERIAL_CHANGEDITEM_HERO_CHANGEDITEM_EQUIP_LIST_CHANGEDITEM_CARD_LIST_CHANGEDITEM_MATERIAL_LIST_CHANGEDITEM_HERO_LIST_CHANGEDRENTAL_CHANGEDTREASURE_STATE_CHANGEDINVENTORY_SNAPSHOT_CHANGED
-
InventoryManager가 public query boundary를 제공한다. -
외부 시스템은
EquippedItems/UnequippedItems/UnownedEquipItems/GetEquipments/GetCards/GetMaterials/GetHeroes/GetRentals/GetPasses/GetRentalRemainingMs/HasPass/HasActiveRental같은 manager helper를 사용한다. -
신규 코드에서
InventoryStorage직접 조회/변경은 금지한다. -
load/import/clear는 temp
InventorySnapshot을 만든 뒤InventoryManager.ReplaceState/ClearState로 반영한다. -
카드/재료/영웅은 일반 revoke/apply 경로에서 amount가 0이 되면 storage에서 제거한다.
-
단, card/hero level up 결과로 amount가 0이 된 runtime은 level/equip 상태 보존을 위해 storage/snapshot에 남을 수 있다.
-
selected hero raw state는
InventoryStorage/InventorySnapshot/SaveData inventory JSON(selectedHeroItemId)에 저장한다.SelectedHeroruntime은 manager 파생 view다. -
장착/미장착 장비 목록은
InventoryStorage가 아니라InventoryManager가_storage.Equipments로부터 재구성하는 파생 runtime view다. -
미보유 장비 목록은
InventoryStorage에 저장하지 않는다.InventoryManager가TB_ITEM_EQUIP.GetAll()과 owned equipitem_id집합을 비교해서 만드는 파생 table-row view다.
3. Implementation Location (3-path mirror)
파일 위치 (MobilePackage 샘플)
MobilePackage/Runtime/Inventory/
├── InventoryManager.cs (10-inventory-manager)
├── InventoryStorage.cs
├── InventorySnapshot.cs (save/load DTO)
├── InventoryStaminaController.cs (14-inventory-stamina-controller)
└── InventoryMessageTrigger.cs (16-inventory-message-trigger)
3-path mirror (정책):
- UPM (정본):
framework-cs/upm/com.devian.foundation/Samples~/MobilePackage/Runtime/Inventory/ - Packages (sync):
framework-cs/apps/UnityExample/Packages/com.devian.foundation/Samples~/MobilePackage/Runtime/Inventory/ - Assets/Samples (import):
framework-cs/apps/UnityExample/Assets/Samples/Devian Foundation/{version}/MobilePackage/Runtime/Inventory/
NOTE: ItemData 클래스는 AbilityItemEquip에 통합되어 삭제되었다. BagItems는 Equipments로 리네임되었다.
4. asmdef
Devian.Samples.MobilePackage.asmdef에 포함된 참조:
Devian.Domain.Game(UNIT_STAT_TYPE — AbilityItemEquip → AbilityBase 경유)
5. namespace
namespace Devian
(Samples 정책: 단일 namespace Devian)
6. Hard Rules
- InventoryStorage는 sealed POCO 클래스이다 (MonoBehaviour 금지).
- 장비는
itemUid(GUID)를 pk로 관리한다. 같은item_id에 여러 인스턴스가 존재할 수 있다. - currency 잔고는
InventoryStorage내부 딕셔너리와 helper(GetCurrencyAmount/TryAddCurrency)로 직접 관리한다. Equipmentskey =itemUid(string). value =AbilityItemEquip.Cardskey =item_id(string). value =AbilityItemCard.Materialskey =item_id(string). value =AbilityItemMaterial.Heroeskey =item_id(string). value =AbilityItemHero.Rentalskey =item_id(string key). value =longexpiresAtClientUtcMs.Passeskey =item_id(string key). value =boolowned.TreasureCurrent=InventoryTreasureCurrent(sealed POCO, Exp/Level).TreasureCountskey =ITEM_GRADE_TYPE(NONE 제외). value =int(보유 chest count).- treasure count와 exp는 음수 불가 (0 이하 clamp).
- treasure current 상태는 grade별 분리 없이 단일 Exp/Level만 사용한다.
- 장비 장착/해제의 핵심 로직은
AbilityItemHero가 담당한다. InventoryStorage는 편의 메서드(Equip/Unequip)로 위임한다. - InventoryStorage는 InventoryManager가 소유한다 (싱글톤 등록 안 함).
- "Sample" 접두사 금지 (정책).
7. JSON 직렬화 — SaveDataJsonCodec으로 이전됨
변경:
ToJson()/FromJson()메서드는 삭제되었다. 직렬화 책임은 21-savedata-system/43-savedata-json-codec의 **SaveDataJsonCodec**가 담당한다.
InventoryStorage는 live runtime helper를 제공한다.
SaveDataJsonCodec은 live storage를 직접 직렬화하지 않고 InventorySnapshot을 사용한다.
InventoryManager.CreateSnapshot()이 live storage를 snapshot으로 투영하고, InventoryManager.ReplaceState()가 snapshot을 validate/build 후 live storage로 적용한다.
JSON 스키마: 03-ssot 참조.
SaveDataJsonCodec 설계: 21-savedata-system/43-savedata-json-codec 참조.
8. Related
- 12-game-ability — AbilityBase, AbilityItemEquip, AbilityItemCard, AbilityItemMaterial, AbilityItemHero, AbilityUnitHero
- 13-game-stat-type — UNIT_STAT_TYPE enum
- 10-inventory-manager — InventoryManager (InventoryStorage 소유자, 수량 SSOT)
- 12-inventory-wallet — Currency State Flattening (legacy wallet wrapper 제거)
- 03-ssot — Inventory State/Apply Rules
- 00-overview — Inventory System 개요
- 21-savedata-system/43-savedata-json-codec — SaveData JSON 직렬화 담당