From c29204dcb0fd857ef30036405990bfa53b228eb8 Mon Sep 17 00:00:00 2001 From: Flechazo <2558755403@qq.com> Date: Sun, 4 Oct 2026 12:09:39 +0800 Subject: [PATCH] fix(server): allow restoring the saved arena lobby --- go/internal/server/feature/feature_test.go | 13 +++-- go/internal/server/feature/standalone.go | 56 ++++++++++++--------- go/internal/server/world/field_pack_test.go | 32 ++++++++++++ go/internal/server/world/world.go | 10 ++++ 4 files changed, 81 insertions(+), 30 deletions(-) diff --git a/go/internal/server/feature/feature_test.go b/go/internal/server/feature/feature_test.go index dd63d12..0451bba 100644 --- a/go/internal/server/feature/feature_test.go +++ b/go/internal/server/feature/feature_test.go @@ -58,11 +58,14 @@ func TestEmptyPacketCodesReturnsCopy(t *testing.T) { func TestStandaloneNativeDefaults(t *testing.T) { for path, want := range map[string]int{ - "/EventScheduleInfo": 163, - "/EquipInfo": 34, - "/TodayQuestInfo": 64, - "/UpdateAgeGate": 0, - "/ActiveMap": 0, + "/EventScheduleInfo": 163, + "/EquipInfo": 34, + "/TodayQuestInfo": 64, + "/UpdateAgeGate": 0, + "/ActiveMap": 0, + "/PvpBattleDeckInfo": 87, + "/PvpBattleHistory": 97, + "/PvpBattleOnceRewardInfo": 277, } { code, proto, ok, err := Handle(path, wire.AppendVarint(nil, 1, 55)) if err != nil || !ok || code != want || len(proto) != 0 { diff --git a/go/internal/server/feature/standalone.go b/go/internal/server/feature/standalone.go index 96d25b1..5391eb1 100644 --- a/go/internal/server/feature/standalone.go +++ b/go/internal/server/feature/standalone.go @@ -4,31 +4,37 @@ package feature // features. An empty protobuf means the local account currently has no rows or // no active server schedule; it is not copied from a captured response. var standaloneDefaults = map[string]int{ - "/EventScheduleInfo": 163, - "/LoginEvent": 0, - "/BalanceVersionCheck": 186, - "/ChargeCostInfo": 123, - "/EquipInfo": 34, - "/AchievementInfo": 166, - "/HuntDispatchInfo": 189, - "/EventMissionInfo": 127, - "/MissionInfo": 118, - "/EventRewardHistory": 0, - "/PackEventStoryInfo": 220, - "/PackEventBattleInfo": 214, - "/FishingItemInfo": 459, - "/MonsterHuntDeckInfo": 263, - "/Attendance": 0, - "/AttendanceInfo": 0, - "/PackPreviewInfo": 104, - "/TodayQuestInfo": 64, - "/WaypointInfo": 31, - "/FieldDeckInfo": 273, - "/HuntingGroundInfoList": 387, - "/FriendRecommend": 211, - "/SupporterStatus": 438, - "/SupporterBattleInfo": 439, - "/InnOpen": 109, + "/EventScheduleInfo": 163, + "/LoginEvent": 0, + "/BalanceVersionCheck": 186, + "/ChargeCostInfo": 123, + "/EquipInfo": 34, + "/AchievementInfo": 166, + "/HuntDispatchInfo": 189, + "/EventMissionInfo": 127, + "/MissionInfo": 118, + "/EventRewardHistory": 0, + "/PackEventStoryInfo": 220, + "/PackEventBattleInfo": 214, + "/FishingItemInfo": 459, + "/MonsterHuntDeckInfo": 263, + "/Attendance": 0, + "/AttendanceInfo": 0, + "/PackPreviewInfo": 104, + // No PvP decks, battle history or one-time reward claims have been saved. + // Their repeated-only protobufs encode these account states as empty, and + // the client's callbacks continue the arena-lobby entry sequence. + "/PvpBattleDeckInfo": 87, + "/PvpBattleHistory": 97, + "/PvpBattleOnceRewardInfo": 277, + "/TodayQuestInfo": 64, + "/WaypointInfo": 31, + "/FieldDeckInfo": 273, + "/HuntingGroundInfoList": 387, + "/FriendRecommend": 211, + "/SupporterStatus": 438, + "/SupporterBattleInfo": 439, + "/InnOpen": 109, } // commandSuccess contains idempotent local mutations whose response type is diff --git a/go/internal/server/world/field_pack_test.go b/go/internal/server/world/field_pack_test.go index d1b6e77..f36bed5 100644 --- a/go/internal/server/world/field_pack_test.go +++ b/go/internal/server/world/field_pack_test.go @@ -32,6 +32,38 @@ func TestArenaPositionRestoresAcrossRestartWithoutStoryQuestOrRepurchase(t *test if pack, err := s.LastPlayedPackID(); err != nil || pack != 3001 { t.Fatalf("restored login pack=%d err=%v", pack, err) } + s.fieldPacks[3002] = gamedata.FieldPack{ID: 3002, Type: 3, MapIDs: map[int]bool{30021: true}} + accountInfo := s.accountPackInfo() + arenaRows := 0 + if err := wire.Walk(accountInfo, func(field wire.Field) error { + if field.Number == 1 { + id, _, _ := wire.Varint(field.Value, 1) + if id == 3002 { + t.Fatal("unvisited arena was exposed as purchased") + } + if id == 3001 { + arenaRows++ + buy, _, _ := wire.Varint(field.Value, 8) + if buy != 1 { + t.Fatal("saved arena missing purchased marker") + } + if err := wire.Walk(field.Value, func(inner wire.Field) error { + if inner.Number != 1 && inner.Number != 8 { + t.Fatalf("saved arena fabricated field %d", inner.Number) + } + return nil + }); err != nil { + return err + } + } + } + return nil + }); err != nil { + t.Fatal(err) + } + if arenaRows != 1 { + t.Fatalf("saved arena rows=%d", arenaRows) + } request = wire.AppendVarint(wire.AppendVarint(nil, 1, 1), 2, 3001) code, body, handled, err := s.Handle("/PackInGameInfo", request) if err != nil || !handled || code != 5 { diff --git a/go/internal/server/world/world.go b/go/internal/server/world/world.go index c89c886..11df020 100644 --- a/go/internal/server/world/world.go +++ b/go/internal/server/world/world.go @@ -672,6 +672,16 @@ func (s *Service) packDBInfoRows() [][]byte { } packID = nextPackID } + // A committed arena position is this account's prior-entry marker. Expose + // only that arena as purchased so the client restores its existing lobby + // without treating every independent arena as unlocked or rewarded. + if saved, found := s.state.Position(); found { + if pack, arena := s.fieldPacks[saved.PackID]; arena && pack.MapIDs[saved.Position.MapID] { + current := wire.AppendVarint(nil, 1, uint64(saved.PackID)) + current = wire.AppendVarint(current, 8, 1) + rows = append(rows, current) + } + } return rows }