From 1327e44f32c0b97ec0de79e2817402a5237ad7cd Mon Sep 17 00:00:00 2001 From: Flechazo <2558755403@qq.com> Date: Sun, 4 Oct 2026 18:09:23 +0800 Subject: [PATCH] server: keep chest claims independent of quest difficulty --- go/internal/server/progress/chests.go | 18 +++++++++--------- go/internal/server/world/field_objects.go | 12 ++++++------ go/internal/server/world/field_objects_test.go | 9 +++++++-- go/internal/server/world/pack_detail_test.go | 2 +- 4 files changed, 23 insertions(+), 18 deletions(-) diff --git a/go/internal/server/progress/chests.go b/go/internal/server/progress/chests.go index 72bc0ab..e287d21 100644 --- a/go/internal/server/progress/chests.go +++ b/go/internal/server/progress/chests.go @@ -8,20 +8,20 @@ import ( "strings" ) -func fieldRewardKey(pack, difficulty, id int) string { - return fmt.Sprintf("%d:%d:%d", pack, difficulty, id) +func fieldRewardKey(pack, id int) string { + return fmt.Sprintf("%d:%d", pack, id) } // FieldRewardOpened reads persisted entries on every call; request rollback // therefore does not leave an opened-object cache behind. -func (s *Store) FieldRewardOpened(pack, difficulty, id int, period string) (bool, error) { +func (s *Store) FieldRewardOpened(pack, id int, period string) (bool, error) { s.mu.Lock() defer s.mu.Unlock() store, err := s.fieldRewardEntries() if err != nil { return false, err } - raw, found, err := store.LoadEntry("progress", "field_rewards", fieldRewardKey(pack, difficulty, id)) + raw, found, err := store.LoadEntry("progress", "field_rewards", fieldRewardKey(pack, id)) return found && string(raw) == period, err } func (s *Store) fieldRewardEntries() (stateio.EntryStore, error) { @@ -34,19 +34,19 @@ func (s *Store) fieldRewardEntries() (stateio.EntryStore, error) { } return store, nil } -func (s *Store) MarkFieldRewardOpened(pack, difficulty, id int, period string) error { +func (s *Store) MarkFieldRewardOpened(pack, id int, period string) error { s.mu.Lock() defer s.mu.Unlock() - if pack <= 0 || difficulty < 0 || difficulty > 4 || id <= 0 { + if pack <= 0 || id <= 0 { return fmt.Errorf("progress: invalid field reward identity") } store, err := s.fieldRewardEntries() if err != nil { return err } - return store.PutEntry("progress", "field_rewards", fieldRewardKey(pack, difficulty, id), []byte(period)) + return store.PutEntry("progress", "field_rewards", fieldRewardKey(pack, id), []byte(period)) } -func (s *Store) OpenedFieldRewards(pack, difficulty int) ([]int, error) { +func (s *Store) OpenedFieldRewards(pack int) ([]int, error) { s.mu.Lock() defer s.mu.Unlock() store, err := s.fieldRewardEntries() @@ -57,7 +57,7 @@ func (s *Store) OpenedFieldRewards(pack, difficulty int) ([]int, error) { if err != nil { return nil, err } - prefix := fmt.Sprintf("%d:%d:", pack, difficulty) + prefix := fmt.Sprintf("%d:", pack) var ids []int for key, raw := range entries { if len(raw) == 0 { diff --git a/go/internal/server/world/field_objects.go b/go/internal/server/world/field_objects.go index 617ff19..eb88208 100644 --- a/go/internal/server/world/field_objects.go +++ b/go/internal/server/world/field_objects.go @@ -44,7 +44,7 @@ func (s *Service) fieldObjectDesign(pack int) (gamedata.FieldObjectDesign, error return design, nil } func (s *Service) openedFieldObjects(pack int) ([]int, error) { - ids, err := s.state.OpenedFieldRewards(pack, s.questDifficulty(pack)) + ids, err := s.state.OpenedFieldRewards(pack) if err != nil { return nil, err } @@ -65,7 +65,7 @@ func (s *Service) openedFieldObjects(pack int) ([]int, error) { if e != nil { continue } - opened, e := s.state.FieldRewardOpened(pack, s.questDifficulty(pack), id, period) + opened, e := s.state.FieldRewardOpened(pack, id, period) if e != nil { return nil, e } @@ -134,12 +134,12 @@ func (s *Service) openFieldObject(pack, group, id int) ([]byte, error) { if obj.Type < 1 || obj.Type > 3 || obj.BuffID != 0 || obj.MonsterID != 0 || obj.QuestID != 0 || len(obj.Rewards) == 0 { return nil, fmt.Errorf("%w: unsupported field object reward graph/reset", ErrInvalidRequest) } - difficulty := s.questDifficulty(pack) + period, err := s.fieldObjectPeriod(obj) if err != nil { return nil, err } - opened, err := s.state.FieldRewardOpened(pack, difficulty, id, period) + opened, err := s.state.FieldRewardOpened(pack, id, period) if err != nil { return nil, err } @@ -171,7 +171,7 @@ func (s *Service) openFieldObject(pack, group, id int) ([]byte, error) { return nil, fmt.Errorf("%w: unsupported field reward type %d", ErrInvalidRequest, r.Type) } } - identity := fmt.Sprintf("field-reward:%d:%d:%d:%s", pack, difficulty, id, period) + identity := fmt.Sprintf("field-reward:%d:%d:%s", pack, id, period) if _, err = s.wallet.GrantQuestOnce(identity, rewards); err != nil { return nil, err } @@ -179,7 +179,7 @@ func (s *Service) openFieldObject(pack, group, id int) ([]byte, error) { if err != nil { return nil, err } - if err = s.state.MarkFieldRewardOpened(pack, difficulty, id, period); err != nil { + if err = s.state.MarkFieldRewardOpened(pack, id, period); err != nil { return nil, err } var bundle []byte diff --git a/go/internal/server/world/field_objects_test.go b/go/internal/server/world/field_objects_test.go index 57c79b0..813807f 100644 --- a/go/internal/server/world/field_objects_test.go +++ b/go/internal/server/world/field_objects_test.go @@ -79,7 +79,7 @@ func TestFieldChestAtomicAwardRollback(t *testing.T) { if err != nil { t.Fatal(err) } - opened, err := s.state.FieldRewardOpened(21, 0, 1001, "once") + opened, err := s.state.FieldRewardOpened(21, 1001, "once") if err != nil || opened || s.wallet.Snapshot().FreeJewelry != 100 || len(s.inventory.All()) != 0 { t.Fatalf("partial state after rollback opened=%v wallet=%+v err=%v", opened, s.wallet.Snapshot(), err) } @@ -129,7 +129,7 @@ func TestFieldChestAtomicAwardRollback(t *testing.T) { if err != nil { t.Fatal(err) } - opened, err = s.state.FieldRewardOpened(21, 0, 1002, "once") + opened, err = s.state.FieldRewardOpened(21, 1002, "once") if err != nil || opened || len(s.inventory.All()) != 0 { t.Fatalf("inventory partially committed after rollback opened=%v items=%v err=%v", opened, s.inventory.All(), err) } @@ -170,6 +170,11 @@ func TestFieldChestDeltaAndPersistentRepeat(t *testing.T) { if err != nil { t.Fatal(err) } + // Quest selection difficulty is independent of the pack's opened chests. + // Switching it must neither hide the chest nor create a second grant. + if err = s.state.SelectQuest(21, progress.QuestSelection{QuestID: 1, Difficulty: 2}); err != nil { + t.Fatal(err) + } req = wire.AppendVarint(req, 1, 999) _, res, _, err = s.handleFieldObjectReward(req) if err != nil { diff --git a/go/internal/server/world/pack_detail_test.go b/go/internal/server/world/pack_detail_test.go index a7bb868..7e649f7 100644 --- a/go/internal/server/world/pack_detail_test.go +++ b/go/internal/server/world/pack_detail_test.go @@ -16,7 +16,7 @@ func TestCollectedFieldObjectsAppearInDetailAndSummary(t *testing.T) { 1002: {ID: 1002, GroupID: 102, Type: 1, ResetType: 1}, }}}) for _, id := range []int{1001, 1002} { - if err := s.state.MarkFieldRewardOpened(21, 0, id, "once"); err != nil { + if err := s.state.MarkFieldRewardOpened(21, id, "once"); err != nil { t.Fatal(err) } }