diff --git a/go/internal/server/mail/mail.go b/go/internal/server/mail/mail.go index 21a9e92..77b7c53 100644 --- a/go/internal/server/mail/mail.go +++ b/go/internal/server/mail/mail.go @@ -46,6 +46,7 @@ var currencyRewardTypes = map[uint64]bool{ 3: true, // free jewelry 4: true, // gold 12: true, // catalyst / talent elixir + 20: true, // mileage / golden thread } // MailDBInfo is the persisted shape used by the client. A mail either has a diff --git a/go/internal/server/mail/mail_test.go b/go/internal/server/mail/mail_test.go index d981e16..c31a9ba 100644 --- a/go/internal/server/mail/mail_test.go +++ b/go/internal/server/mail/mail_test.go @@ -160,17 +160,17 @@ func TestMailOpenGrantsNonResourceItemDBInfoType(t *testing.T) { } } -func TestMailOpenGrantsCatalystCurrencyAndPersists(t *testing.T) { +func TestMailOpenGrantsCatalystAndMileageCurrenciesAndPersists(t *testing.T) { seed := &Starter{Version: "2.35.10", MailCount: 2, MaxMailID: 14, Mails: []MailDBInfo{{ MailID: 14, MailType: 2, ExpiresAt: 100, SentAt: 10, - RewardTypes: []uint64{12}, RewardIDs: []uint64{0}, RewardCounts: []uint64{250}, + RewardTypes: []uint64{12, 20}, RewardIDs: []uint64{0, 0}, RewardCounts: []uint64{250, 300}, }}} storage := stateio.NewMemory() inv, err := player.OpenInventory(storage, &player.Starter{Version: "2.35.10"}) if err != nil { t.Fatal(err) } - wallet, err := player.OpenWallet(storage, player.Currency{Catalyst: 10}) + wallet, err := player.OpenWallet(storage, player.Currency{Catalyst: 10, Mileage: 20}) if err != nil { t.Fatal(err) } @@ -184,35 +184,46 @@ func TestMailOpenGrantsCatalystCurrencyAndPersists(t *testing.T) { if err != nil { t.Fatal(err) } - if wallet.Snapshot().Catalyst != 260 || len(inv.All()) != 0 { + if wallet.Snapshot().Catalyst != 260 || wallet.Snapshot().Mileage != 320 || len(inv.All()) != 0 { t.Fatalf("wallet=%+v items=%+v", wallet.Snapshot(), inv.All()) } bundle, found, _ := wire.Bytes(response, 1) if !found { t.Fatal("reward bundle missing") } - item, found, _ := wire.Bytes(bundle, 1) - if !found { - t.Fatal("currency ItemDBInfo missing") + gotCurrencies := map[uint64]uint64{} + if err := wire.Walk(bundle, func(field wire.Field) error { + if field.Number != 1 { + return nil + } + typ, _, err := wire.Varint(field.Value, 3) + if err != nil { + return err + } + count, _, err := wire.Varint(field.Value, 4) + if err != nil { + return err + } + gotCurrencies[typ] = count + return nil + }); err != nil { + t.Fatal(err) } - if typ, _, _ := wire.Varint(item, 3); typ != 12 { - t.Fatalf("currency type=%d", typ) - } - if count, _, _ := wire.Varint(item, 4); count != 250 { - t.Fatalf("currency count=%d", count) + if gotCurrencies[12] != 250 || gotCurrencies[20] != 300 { + t.Fatalf("currency rewards=%v", gotCurrencies) } if _, _, _, err := service.Handle("/MailOpen", request); err != nil { t.Fatal(err) } - if wallet.Snapshot().Catalyst != 260 { - t.Fatalf("replay catalyst=%d", wallet.Snapshot().Catalyst) + if wallet.Snapshot().Catalyst != 260 || wallet.Snapshot().Mileage != 320 { + t.Fatalf("replay wallet=%+v", wallet.Snapshot()) } reopened, err := player.OpenWallet(storage, player.Currency{}) if err != nil { t.Fatal(err) } - if reopened.Snapshot().Catalyst != 260 { - t.Fatalf("persisted catalyst=%d", reopened.Snapshot().Catalyst) + if reopened.Snapshot().Catalyst != 260 || reopened.Snapshot().Mileage != 320 { + t.Fatalf("persisted wallet=%+v", reopened.Snapshot()) } } diff --git a/go/internal/server/player/wallet.go b/go/internal/server/player/wallet.go index 84196ff..6b90d34 100644 --- a/go/internal/server/player/wallet.go +++ b/go/internal/server/player/wallet.go @@ -328,6 +328,11 @@ func (s *Wallet) GrantQuestOnce(identity string, rewards []gamedata.Reward) (Cur return Currency{}, errors.New("player: catalyst overflow") } next.Catalyst += reward.Count + case 20: + if math.MaxUint64-next.Mileage < reward.Count { + return Currency{}, errors.New("player: mileage overflow") + } + next.Mileage += reward.Count } } next.Granted[identity] = true diff --git a/tools/python/dev_mail_grant.py b/tools/python/dev_mail_grant.py index cff8464..f27ff7b 100644 --- a/tools/python/dev_mail_grant.py +++ b/tools/python/dev_mail_grant.py @@ -54,7 +54,7 @@ ITEM_SOURCES = ( # and mail-claim path are implemented by this server are offered. Their mail # reward ID is zero; names still come from the current GameData rather than # being embedded here. -MAIL_CURRENCY_TYPES = frozenset({3, 4, 12}) +MAIL_CURRENCY_TYPES = frozenset({3, 4, 12, 20}) def _varint(value: Any) -> int: diff --git a/tools/python/tests/test_offline_tools.py b/tools/python/tests/test_offline_tools.py index 2d16575..33476b2 100644 --- a/tools/python/tests/test_offline_tools.py +++ b/tools/python/tests/test_offline_tools.py @@ -196,17 +196,18 @@ class DevelopmentMailGrantToolTests(unittest.TestCase): connection = sqlite3.connect(":memory:") connection.execute("CREATE TABLE NameTextTable (id INTEGER, ProtoBuf BLOB)") connection.execute("CREATE TABLE CurrencyTable (id INTEGER, ProtoBuf BLOB)") - for text_id, value in ((264, "天赋神药"), (999, "未支持货币")): + for text_id, value in ((264, "天赋神药"), (681, "金线"), (999, "未支持货币")): encoded = value.encode("utf-8") connection.execute( "INSERT INTO NameTextTable VALUES (?, ?)", (text_id, proto_field(2, text_id) + import_seed.encode_field(4, 2, encoded)), ) connection.execute("INSERT INTO CurrencyTable VALUES (?, ?)", (12, proto_field(3, 12) + proto_field(5, 264))) + connection.execute("INSERT INTO CurrencyTable VALUES (?, ?)", (20, proto_field(3, 20) + proto_field(5, 681))) connection.execute("INSERT INTO CurrencyTable VALUES (?, ?)", (99, proto_field(3, 99) + proto_field(5, 999))) currencies = dev_mail_grant._mail_currencies(connection) - self.assertEqual([(item["element_type"], item["id"], item["name"]) for item in currencies], [(12, 0, "天赋神药")]) + self.assertEqual([(item["element_type"], item["id"], item["name"]) for item in currencies], [(12, 0, "天赋神药"), (20, 0, "金线")]) connection.close() def test_internal_lost_resource_is_not_mail_safe(self):