feat(all): add verified gacha flows and native client studio
This commit is contained in:
@@ -2,7 +2,7 @@ package feature
|
||||
|
||||
import "bd2server/internal/server/wire"
|
||||
|
||||
// initialResponses are locally constructed, typed defaults for the 2.34.13
|
||||
// initialResponses are locally constructed, typed defaults for the
|
||||
// new-player account. These do not reuse recorded response bytes. Stateful
|
||||
// entities (inventory, characters, quests, decks) are deliberately excluded.
|
||||
func initialResponse(path string) (int, []byte, bool) {
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
package feature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"bd2server/internal/server/wire"
|
||||
)
|
||||
|
||||
func TestBootstrapProtocolDefaults(t *testing.T) {
|
||||
for _, test := range []struct {
|
||||
path string
|
||||
code int
|
||||
proto []byte
|
||||
}{
|
||||
{"/PackInfo", 4, []byte{0x28, 3}},
|
||||
{"/RecipeInfo", 46, []byte{0x12, 1, 101}},
|
||||
{"/CashMailInfo", 140, []byte{0x18, 0}},
|
||||
{"/AvatarInfo", 467, []byte{0x0a, 0}},
|
||||
{"/GuildRaidSeasonReward", 310, []byte{0x0a, 0}},
|
||||
{"/DeckInfo", 8, []byte{0x12, 4, 0, 0, 0, 0}},
|
||||
{"/RootSortIdInfo", 367, []byte{0x0a, 7, 0x08, 2, 0x10, 0x8c, 1, 0x18, 1}},
|
||||
} {
|
||||
t.Run(test.path, func(t *testing.T) {
|
||||
code, proto, handled, err := Handle(test.path, wire.AppendVarint(nil, 1, 42))
|
||||
if err != nil || !handled || code != test.code || !bytes.Equal(proto, test.proto) {
|
||||
t.Fatalf("code=%d proto=%x handled=%v err=%v", code, proto, handled, err)
|
||||
}
|
||||
if len(proto) != 0 {
|
||||
proto[0] ^= 0xff
|
||||
}
|
||||
_, again, _, err := Handle(test.path, wire.AppendVarint(nil, 1, 43))
|
||||
if err != nil || !bytes.Equal(again, test.proto) {
|
||||
t.Fatalf("response mutation escaped into defaults: proto=%x err=%v", again, err)
|
||||
}
|
||||
if _, _, handled, err := Handle(test.path, nil); !handled || err == nil {
|
||||
t.Fatal("bootstrap endpoint accepted missing sequence")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -1,50 +0,0 @@
|
||||
package feature
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"bd2server/internal/server/fixture"
|
||||
"bd2server/internal/server/wire"
|
||||
)
|
||||
|
||||
// TestCaptureCompatibility is an optional development-time protocol audit.
|
||||
// Normal server execution never opens the capture.
|
||||
func TestCaptureCompatibility(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)
|
||||
}
|
||||
key, err := set.SessionKey()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
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 _, _, handled, err := Handle(item.Path, wire.AppendVarint(nil, 1, 1)); err != nil || !handled {
|
||||
t.Logf("batch=%d pending domain endpoint=%s", batch, item.Path)
|
||||
}
|
||||
if _, known := EmptyPacketCodes()[item.Path]; known {
|
||||
if len(decoded[item.Path]) != 0 {
|
||||
t.Errorf("%s captured protobuf was not empty", item.Path)
|
||||
}
|
||||
}
|
||||
if _, _, known := initialResponse(item.Path); known {
|
||||
code, generated, handled, err := Handle(item.Path, wire.AppendVarint(nil, 1, 1))
|
||||
if err != nil || !handled || code != item.Response.PacketCode || !bytes.Equal(generated, decoded[item.Path]) {
|
||||
t.Errorf("%s native response differs from observed 2.34.13 new account: code=%d vs %d proto=%x vs %x err=%v", item.Path, code, item.Response.PacketCode, generated, decoded[item.Path], err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,7 +15,7 @@ import (
|
||||
// ErrInvalidRequest means a known endpoint was sent a malformed request.
|
||||
var ErrInvalidRequest = errors.New("feature: invalid protobuf request")
|
||||
|
||||
// emptyResponses is the stateless subset of the 2.34.13 new-player batch whose
|
||||
// emptyResponses is the stateless subset of the new-player batch whose
|
||||
// successful response protobuf has zero bytes. Stateful routes leave this map
|
||||
// as soon as their owning domain persists them. Packet codes are protocol
|
||||
// values, not ordinals derived from the request or response order.
|
||||
|
||||
Reference in New Issue
Block a user