feat(all): add verified gacha flows and native client studio

This commit is contained in:
2026-09-30 23:49:00 +08:00
parent addc394cae
commit bf4f259bad
66 changed files with 2387 additions and 2351 deletions
+28 -38
View File
@@ -5,54 +5,44 @@ import (
"path/filepath"
"testing"
"bd2server/internal/server/fixture"
"bd2server/internal/server/wire"
)
func TestSeedByteCompatibility(t *testing.T) {
root := filepath.Join("..", "..", "..", "..", "data", "capture", "2.34.13", "20260920-003254")
set, err := fixture.Load(root)
if err != nil {
t.Skipf("optional capture unavailable: %v", err)
func TestSeedProtocolEncodingAndRoundTrip(t *testing.T) {
seed := &Seed{Version: StateVersion(), Responses: map[string]Response{
"/CashShopInfo": {PacketCode: 60, Fields: []Field{
{Number: 1, Type: 2, Fields: []Field{{Number: 1, Type: 0, Varint: 150}, {Number: 2, Type: 0, Varint: 7}}},
{Number: 2, Type: 1, Fixed64: 0x0807060504030201},
{Number: 3, Type: 5, Fixed32: 0x04030201},
{Number: 4, Type: 2, Bytes: []byte("shop")},
{Number: 5, Type: 2},
}},
}}
path := filepath.Join(t.TempDir(), "readonly.json")
if err := seed.Write(path); err != nil {
t.Fatal(err)
}
key, err := set.SessionKey()
loaded, err := Load(path)
if err != nil {
t.Fatal(err)
}
seed, err := Load(filepath.Join("..", "..", "..", "seed", "v2_35_10", "readonly.json"))
if err != nil {
t.Fatal(err)
want := []byte{0x0a, 5, 0x08, 0x96, 1, 0x10, 7, 0x11, 1, 2, 3, 4, 5, 6, 7, 8, 0x1d, 1, 2, 3, 4, 0x22, 4, 's', 'h', 'o', 'p', 0x2a, 0}
code, got, handled, err := loaded.Handle("/CashShopInfo", wire.AppendVarint(nil, 1, 999))
if err != nil || !handled || code != 60 || !bytes.Equal(got, want) {
t.Fatalf("code=%d proto=%x handled=%v err=%v", code, got, handled, err)
}
want := map[string]struct {
PacketCode int
Proto []byte
}{}
for batch := 1; batch <= 2; batch++ {
body, err := set.BatchResponse(batch)
if err != nil {
t.Fatal(err)
}
items, decoded, err := fixture.DecodeBatch(body, []byte(key))
if err != nil {
t.Fatal(err)
}
for _, item := range items {
if _, ok := seed.Responses[item.Path]; ok {
want[item.Path] = struct {
PacketCode int
Proto []byte
}{item.Response.PacketCode, decoded[item.Path]}
}
got[0] = 0
_, again, _, err := loaded.Handle("/CashShopInfo", wire.AppendVarint(nil, 1, 1000))
if err != nil || !bytes.Equal(again, want) {
t.Fatal("encoded response aliases seed data")
}
for _, request := range [][]byte{nil, {0x08, 0}, {0x08, 0x80}} {
if _, _, handled, err := loaded.Handle("/CashShopInfo", request); !handled || err == nil {
t.Fatal("invalid request accepted")
}
}
if len(want) != len(seed.Responses) {
t.Fatalf("capture has %d seed responses, want %d", len(want), len(seed.Responses))
}
for path, expected := range want {
code, got, ok, err := seed.Handle(path, wire.AppendVarint(nil, 1, 999))
if err != nil || !ok || code != expected.PacketCode || !bytes.Equal(got, expected.Proto) {
t.Errorf("%s: code=%d want=%d equal=%v err=%v", path, code, expected.PacketCode, bytes.Equal(got, expected.Proto), err)
}
if _, _, handled, err := loaded.Handle("/Unknown", nil); handled || err != nil {
t.Fatal("unknown endpoint accepted")
}
}