259 lines
8.9 KiB
Go
259 lines
8.9 KiB
Go
package battle
|
|
|
|
import (
|
|
"fmt"
|
|
"testing"
|
|
|
|
"bd2server/internal/server/gamedata"
|
|
"bd2server/internal/server/player"
|
|
"bd2server/internal/server/stateio"
|
|
"bd2server/internal/server/wire"
|
|
)
|
|
|
|
func request(seq uint64) []byte { return wire.AppendVarint(nil, 1, seq) }
|
|
|
|
func TestQuestBattleSelectsAndRetainsDifficultyDeck(t *testing.T) {
|
|
s := NewService("design", "version", nil, func() (int, error) { return 1, nil })
|
|
s.AttachCurrentDifficulty(func() (uint64, error) { return 2, nil })
|
|
s.loadDifficultyDeck = func(_, _ string, pack int, deck, difficulty uint64) (uint64, error) {
|
|
if pack != 1 || deck != 1 || difficulty != 2 {
|
|
t.Fatalf("selection %d/%d/%d", pack, deck, difficulty)
|
|
}
|
|
return 200001, nil
|
|
}
|
|
s.loadPhases = func(_, _ string, pack int, monster, deck uint64) ([]gamedata.BattlePhase, error) {
|
|
if pack != 1 || deck != 200001 {
|
|
t.Fatalf("phase selection %d/%d", pack, deck)
|
|
}
|
|
return nil, nil
|
|
}
|
|
enter := wire.AppendVarint(wire.AppendVarint(wire.AppendVarint(request(1), 3, 1), 4, 1), 5, 1)
|
|
_, response, _, err := s.Handle("/BattleEnter", enter)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
deck, _, _ := wire.Varint(response, 2)
|
|
if deck != 200001 {
|
|
t.Fatalf("deck %d", deck)
|
|
}
|
|
start := wire.AppendBytes(wire.AppendVarint(request(2), 2, 200001), 5, wire.AppendVarint(nil, 2, 101))
|
|
if _, _, _, err := s.Handle("/BattleStart", start); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
_, response, _, err = s.Handle("/BattleRetry", wire.AppendVarint(request(3), 2, 200001))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
deck, _, _ = wire.Varint(response, 3)
|
|
if deck != 200001 {
|
|
t.Fatalf("retry deck %d", deck)
|
|
}
|
|
}
|
|
|
|
func TestLocalBattleLifecycle(t *testing.T) {
|
|
s := &Service{}
|
|
enter := request(1)
|
|
enter = wire.AppendVarint(enter, 4, 1)
|
|
enter = wire.AppendVarint(enter, 5, 1)
|
|
code, response, ok, err := s.Handle("/BattleEnter", enter)
|
|
if err != nil || !ok || code != 52 {
|
|
t.Fatalf("enter: %d %v %v", code, ok, err)
|
|
}
|
|
engine, found, _ := wire.Varint(response, 6)
|
|
if !found || engine != 1 {
|
|
t.Fatalf("engine=%d/%v", engine, found)
|
|
}
|
|
red := wire.AppendVarint(nil, 1, 1)
|
|
blue := wire.AppendVarint(nil, 1, 6)
|
|
start := request(2)
|
|
start = wire.AppendVarint(start, 2, 1)
|
|
start = wire.AppendBytes(start, 4, red)
|
|
start = wire.AppendBytes(start, 5, blue)
|
|
code, response, _, err = s.Handle("/BattleStart", start)
|
|
if err != nil || code != 14 {
|
|
t.Fatalf("start: %d %v", code, err)
|
|
}
|
|
if _, found, _ := wire.Bytes(response, 1); !found {
|
|
t.Fatal("red state not echoed")
|
|
}
|
|
if _, found, _ := wire.Bytes(response, 2); !found {
|
|
t.Fatal("blue state not echoed")
|
|
}
|
|
code, response, _, err = s.Handle("/BattleVerifyState", request(3))
|
|
state, found, _ := wire.Varint(response, 1)
|
|
if err != nil || code != 142 || !found || state != 3 {
|
|
t.Fatalf("verify: code=%d state=%d/%v err=%v", code, state, found, err)
|
|
}
|
|
end := request(4)
|
|
end = wire.AppendVarint(end, 2, 2)
|
|
end = wire.AppendBytes(end, 3, blue)
|
|
code, response, _, err = s.Handle("/BattleEnd", end)
|
|
result, found, _ := wire.Varint(response, 1)
|
|
if err != nil || code != 15 || !found || result != 2 {
|
|
t.Fatalf("end: code=%d result=%d/%v err=%v", code, result, found, err)
|
|
}
|
|
}
|
|
|
|
func TestBattleOrdering(t *testing.T) {
|
|
s := &Service{}
|
|
start := request(1)
|
|
start = wire.AppendVarint(start, 2, 1)
|
|
if _, _, _, err := s.Handle("/BattleStart", start); err == nil {
|
|
t.Fatal("start without enter accepted")
|
|
}
|
|
}
|
|
|
|
func TestBattleRetryRestoresFirstSubmittedBlueTeam(t *testing.T) {
|
|
s := &Service{}
|
|
enter := wire.AppendVarint(wire.AppendVarint(request(1), 4, 4), 5, 1)
|
|
if _, _, _, err := s.Handle("/BattleEnter", enter); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first := wire.AppendVarint(wire.AppendVarint(nil, 2, 101), 4, 513)
|
|
second := wire.AppendVarint(wire.AppendVarint(nil, 2, 102), 4, 200)
|
|
start := wire.AppendVarint(request(2), 2, 77)
|
|
start = wire.AppendBytes(start, 5, first)
|
|
start = wire.AppendBytes(start, 5, second)
|
|
if _, _, _, err := s.Handle("/BattleStart", start); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
retry := wire.AppendVarint(request(3), 2, 77)
|
|
code, response, handled, err := s.Handle("/BattleRetry", retry)
|
|
if err != nil || !handled || code != 58 {
|
|
t.Fatalf("retry code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
index, found, err := wire.Varint(response, 3)
|
|
if err != nil || !found || index != 77 {
|
|
t.Fatalf("retry battle index=%d found=%v err=%v", index, found, err)
|
|
}
|
|
var restored [][]byte
|
|
if err := wire.Walk(response, func(field wire.Field) error {
|
|
if field.Number == 2 {
|
|
restored = append(restored, field.Value)
|
|
}
|
|
return nil
|
|
}); err != nil || len(restored) != 2 || string(restored[0]) != string(first) || string(restored[1]) != string(second) {
|
|
t.Fatalf("retry team=%x err=%v", restored, err)
|
|
}
|
|
}
|
|
|
|
func TestBattleEnterUsesSamePictorialSnapshotAsAllCharRefresh(t *testing.T) {
|
|
s := &Service{}
|
|
s.AttachPictorialBuffs(func() ([]gamedata.PictorialBuffStat, error) {
|
|
return []gamedata.PictorialBuffStat{{StatType: 2, Value: .0175}, {StatType: 4, Value: .01}}, nil
|
|
})
|
|
enter := wire.AppendVarint(wire.AppendVarint(request(9), 4, 1), 5, 1)
|
|
code, response, handled, err := s.Handle("/BattleEnter", enter)
|
|
if err != nil || !handled || code != 52 {
|
|
t.Fatalf("battle entry code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
var count int
|
|
if err := wire.Walk(response, func(field wire.Field) error {
|
|
if field.Number == 4 {
|
|
count++
|
|
stat, found, err := wire.Varint(field.Value, 1)
|
|
if err != nil || !found || (stat != 2 && stat != 4) {
|
|
t.Fatalf("invalid battle buff stat=%d found=%v err=%v", stat, found, err)
|
|
}
|
|
}
|
|
return nil
|
|
}); err != nil || count != 2 {
|
|
t.Fatalf("battle buffs count=%d err=%v", count, err)
|
|
}
|
|
}
|
|
|
|
func TestBattleVictoryLocksPackAtEnterForRewardsAndIdentity(t *testing.T) {
|
|
storage := stateio.NewMemory()
|
|
inventory, err := player.OpenInventory(storage, &player.Starter{Version: "2.35.10"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
currentPack := 22
|
|
s := NewService("test-root", "test-version", inventory, func() (int, error) {
|
|
return currentPack, nil
|
|
})
|
|
var loadedPack int
|
|
var loadedDeck uint64
|
|
s.loadPhases = func(string, string, int, uint64, uint64) ([]gamedata.BattlePhase, error) { return nil, nil }
|
|
s.loadRewards = func(_, _ string, packID int, deckID uint64) ([]gamedata.BattleReward, error) {
|
|
loadedPack, loadedDeck = packID, deckID
|
|
return []gamedata.BattleReward{{Type: 8, ID: 8, Count: 3}}, nil
|
|
}
|
|
|
|
enter := wire.AppendVarint(request(1), 3, 7)
|
|
enter = wire.AppendVarint(enter, 4, 9)
|
|
enter = wire.AppendVarint(enter, 5, 1)
|
|
if _, _, _, err := s.Handle("/BattleEnter", enter); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
// A later world transition must not change the identity of an in-flight
|
|
// battle; the pack is captured at BattleEnter.
|
|
currentPack = 21
|
|
end := wire.AppendVarint(request(2), 2, 1)
|
|
if _, _, _, err := s.Handle("/BattleEnd", end); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if loadedPack != 22 || loadedDeck != 9 {
|
|
t.Fatalf("reward lookup pack/deck=%d/%d, want 22/9", loadedPack, loadedDeck)
|
|
}
|
|
if got := inventory.GrantedItems("pack22:monster7:deck9"); len(got) != 1 || got[0].ID != 8 || got[0].Count != 3 {
|
|
t.Fatalf("pack22 reward grant=%+v", got)
|
|
}
|
|
if got := inventory.GrantedItems("pack21:monster7:deck9"); len(got) != 0 {
|
|
t.Fatalf("reward leaked into pack21 identity: %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestNewGameSessionDiscardsUnfinishedBattleWithoutRewards(t *testing.T) {
|
|
s := NewService("", "", nil, nil)
|
|
s.BeginSession("session-a")
|
|
enter := wire.AppendVarint(request(1), 4, 10)
|
|
enter = wire.AppendVarint(enter, 5, 1)
|
|
if _, _, _, err := s.Handle("/BattleEnter", enter); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
start := wire.AppendVarint(request(2), 2, 99)
|
|
start = wire.AppendBytes(start, 5, wire.AppendVarint(nil, 1, 123))
|
|
if _, _, _, err := s.Handle("/BattleStart", start); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s.BeginSession("session-b")
|
|
end := wire.AppendVarint(request(3), 2, 1)
|
|
if _, _, _, err := s.Handle("/BattleEnd", end); err == nil {
|
|
t.Fatal("new session resumed unfinished battle")
|
|
}
|
|
}
|
|
|
|
func TestBattleStateIsIsolatedPerGameSession(t *testing.T) {
|
|
s := NewService("", "", nil, nil)
|
|
enter := wire.AppendVarint(request(1), 4, 10)
|
|
enter = wire.AppendVarint(enter, 5, 1)
|
|
s.BeginSession("session-a")
|
|
if _, _, _, err := s.Handle("/BattleEnter", enter); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s.BeginSession("session-b")
|
|
end := wire.AppendVarint(request(2), 2, 1)
|
|
if _, _, _, err := s.Handle("/BattleEnd", end); err == nil {
|
|
t.Fatal("session-b observed session-a battle")
|
|
}
|
|
s.BeginSession("session-a")
|
|
if _, _, _, err := s.Handle("/BattleEnd", end); err != nil {
|
|
t.Fatalf("session-a lost its own battle: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestRepeatedCurrentSessionSelectionDoesNotEvictOtherBattles(t *testing.T) {
|
|
service := NewService("", "", nil, nil)
|
|
for index := range 1024 {
|
|
service.BeginSession(fmt.Sprintf("session-%d", index))
|
|
}
|
|
service.BeginSession("session-1023")
|
|
if len(service.states) != 1024 {
|
|
t.Fatalf("reselecting current session changed cache size: %d", len(service.states))
|
|
}
|
|
if service.states["session-1023"] == nil {
|
|
t.Fatal("reselecting current session evicted its battle state")
|
|
}
|
|
}
|