752 lines
28 KiB
Go
752 lines
28 KiB
Go
package player
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"path/filepath"
|
|
"testing"
|
|
|
|
"bd2server/internal/server/gamedata"
|
|
"bd2server/internal/server/wire"
|
|
)
|
|
|
|
func TestEquipmentUpgradeAndSequenceUseGameDataCosts(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wallet, err := OpenWallet(testStore(filepath.Join(dir, "wallet.json")), Currency{Gold: 1000})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store, err := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
design := &gamedata.EquipmentUpgradeDesign{
|
|
MaxLevel: map[uint64]uint64{943035: 3}, Group: map[uint64]uint64{943035: 954},
|
|
RankGroup: map[uint64]uint64{943035: 904}, RankRatio: map[[2]uint64][]float64{{904, 1}: {1, 0, 0, 0}},
|
|
Levels: map[[2]uint64]gamedata.EquipmentUpgradeLevel{
|
|
{954, 0}: {Level: 0, Costs: []gamedata.PromotionCost{{Type: 4, Count: 100}}, SuccessRatio: 1},
|
|
{954, 1}: {Level: 1, Costs: []gamedata.PromotionCost{{Type: 4, Count: 200}}, SuccessRatio: 1},
|
|
{954, 2}: {Level: 2, Costs: []gamedata.PromotionCost{{Type: 4, Count: 300}}, SuccessRatio: 1},
|
|
},
|
|
}
|
|
items, err := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := store.AttachUpgrade(design, wallet, items); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, err := store.GrantOnce("upgrade", 943035)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 1), 2, entry.InvenIndex)
|
|
request = wire.AppendBytes(request, 3, ItemWire(Item{Type: 4, Count: 100}))
|
|
code, response, handled, err := store.Handle("/EquipUpgrade", request)
|
|
if err != nil || !handled || code != 37 {
|
|
t.Fatalf("single upgrade code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
encoded, found, err := wire.Bytes(response, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("single upgrade equipment: %v", err)
|
|
}
|
|
base, _, _ := wire.Bytes(encoded, 5)
|
|
if level, _, _ := wire.Varint(base, 2); level != 1 {
|
|
t.Fatalf("single upgraded level=%d", level)
|
|
}
|
|
sequence := wire.AppendVarint(wire.AppendVarint(nil, 1, 2), 2, entry.InvenIndex)
|
|
sequence = wire.AppendVarint(sequence, 3, 9)
|
|
sequence = wire.AppendVarint(sequence, 6, 3)
|
|
code, response, handled, err = store.Handle("/EquipSequenceUpgrade", sequence)
|
|
if err != nil || !handled || code != 176 {
|
|
t.Fatalf("sequence upgrade code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
if result, _, _ := wire.Varint(response, 3); result != equipUpgradeStopMaxLevel {
|
|
t.Fatalf("sequence result=%d", result)
|
|
}
|
|
if attempts, _, _ := wire.Varint(response, 4); attempts != 2 {
|
|
t.Fatalf("sequence attempts=%d", attempts)
|
|
}
|
|
if used, _, _ := wire.Varint(response, 7); used != 500 {
|
|
t.Fatalf("sequence used gold=%d", used)
|
|
}
|
|
if wallet.Snapshot().Gold != 400 {
|
|
t.Fatalf("wallet gold=%d", wallet.Snapshot().Gold)
|
|
}
|
|
restored, err := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := restored.All(); len(got) != 1 || got[0].Level != 3 || got[0].UpgradeAttempts != 3 {
|
|
t.Fatalf("persisted upgrade=%+v", got)
|
|
}
|
|
if rank := restored.All()[0].Rank; len(rank) != 3 || rank[0] != 1 || rank[1] != 0 || rank[2] != 0 {
|
|
t.Fatalf("+3 should roll only the first rank: %v", rank)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentSmeltingImprovesByTotalScoreAndReplaysWithoutSecondCharge(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wallet, err := OpenWallet(testStore(filepath.Join(dir, "wallet.json")), Currency{Gold: 1000, EquipMileageExchangeGage: 990})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
inventory, err := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
materials, err := inventory.GrantOnce("refine-material", []gamedata.BattleReward{{Type: 8, ID: 10, Count: 100}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store, err := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
design := smeltingTestDesign([][]float64{{1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}})
|
|
if err := store.AttachSmelting(design, wallet, inventory); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store.BeginSession("smelting-test")
|
|
entry, err := store.GrantOnce("refinable", 943035)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store.owned.Equipment[0].Level = 9
|
|
store.owned.Equipment[0].Rank = []uint64{4, 1, 1} // 6 -> candidate 1+4+4=9.
|
|
if err := store.commitLocked(cloneEquipmentSnapshot(store.owned), "test setup"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 77), 2, entry.InvenIndex)
|
|
request = wire.AppendBytes(request, 3, ItemWire(Item{Type: 4, Count: 80}))
|
|
material := materials[0]
|
|
material.Count = 30
|
|
request = wire.AppendBytes(request, 3, ItemWire(material))
|
|
code, response, handled, err := store.Handle("/EquipSmelting", request)
|
|
if err != nil || !handled || code != 105 {
|
|
t.Fatalf("smelting code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
if result, found, _ := wire.Varint(response, 3); found || result != 0 {
|
|
t.Fatalf("successful smelting result=%d found=%v", result, found)
|
|
}
|
|
if got := store.All()[0].Rank; len(got) != 3 || got[0] != 1 || got[1] != 4 || got[2] != 4 {
|
|
t.Fatalf("smelting rank=%v", got)
|
|
}
|
|
if gauge, _, _ := wire.Varint(response, 5); gauge != 20 {
|
|
t.Fatalf("smelting gauge=%d", gauge)
|
|
}
|
|
bundle, found, err := wire.Bytes(response, 6)
|
|
if err != nil || !found {
|
|
t.Fatalf("smelting mileage bundle: %v", err)
|
|
}
|
|
reward, found, err := wire.Bytes(bundle, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("smelting mileage item: %v", err)
|
|
}
|
|
if typ, _, _ := wire.Varint(reward, 3); typ != 68 {
|
|
t.Fatalf("smelting mileage type=%d", typ)
|
|
}
|
|
if count, _, _ := wire.Varint(reward, 4); count != 1 {
|
|
t.Fatalf("smelting mileage count=%d", count)
|
|
}
|
|
if currency := wallet.Snapshot(); currency.Gold != 920 || currency.EquipMileage != 1 || currency.EquipMileageExchangeGage != 20 {
|
|
t.Fatalf("smelting wallet=%+v", currency)
|
|
}
|
|
if _, replay, _, err := store.Handle("/EquipSmelting", request); err != nil || string(replay) != string(response) {
|
|
t.Fatalf("smelting replay differs err=%v", err)
|
|
}
|
|
if currency := wallet.Snapshot(); currency.Gold != 920 || currency.EquipMileage != 1 || currency.EquipMileageExchangeGage != 20 {
|
|
t.Fatalf("smelting replay charged again: %+v", currency)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentSmeltingFailureConsumesAndReturnsCandidateGrades(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wallet, _ := OpenWallet(testStore(filepath.Join(dir, "wallet.json")), Currency{Gold: 1000})
|
|
inventory, _ := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
materials, _ := inventory.GrantOnce("refine-material", []gamedata.BattleReward{{Type: 8, ID: 10, Count: 30}})
|
|
store, _ := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err := store.AttachSmelting(smeltingTestDesign([][]float64{{1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}}), wallet, inventory); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, _ := store.GrantOnce("refinable", 943035)
|
|
store.owned.Equipment[0].Level = 9
|
|
store.owned.Equipment[0].Rank = []uint64{4, 4, 4}
|
|
if err := store.commitLocked(cloneEquipmentSnapshot(store.owned), "test setup"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 1), 2, entry.InvenIndex)
|
|
request = wire.AppendBytes(request, 3, ItemWire(Item{Type: 4, Count: 80}))
|
|
request = wire.AppendBytes(request, 3, ItemWire(materials[0]))
|
|
_, response, _, err := store.Handle("/EquipSmelting", request)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if result, _, _ := wire.Varint(response, 3); result != equipUpgradeFail {
|
|
t.Fatalf("smelting failure result=%d", result)
|
|
}
|
|
var grades []uint64
|
|
if err := wire.Walk(response, func(field wire.Field) error {
|
|
if field.Number == 4 {
|
|
value, _ := binary.Uvarint(field.Value)
|
|
grades = append(grades, value)
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(grades) != 3 || grades[0] != 1 || grades[1] != 1 || grades[2] != 1 {
|
|
t.Fatalf("failure candidate grades=%v", grades)
|
|
}
|
|
if got := store.All()[0].Rank; got[0] != 4 || got[1] != 4 || got[2] != 4 {
|
|
t.Fatalf("failure overwrote ranks=%v", got)
|
|
}
|
|
if currency := wallet.Snapshot(); currency.Gold != 920 || currency.EquipMileageExchangeGage != 30 {
|
|
t.Fatalf("failure did not consume: %+v", currency)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentSequenceSmeltingRepeatsAndStopsAtTargetScore(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wallet, _ := OpenWallet(testStore(filepath.Join(dir, "wallet.json")), Currency{Gold: 1000})
|
|
inventory, _ := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
_, _ = inventory.GrantOnce("refine-material", []gamedata.BattleReward{{Type: 8, ID: 10, Count: 300}})
|
|
store, _ := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err := store.AttachSmelting(smeltingTestDesign([][]float64{{1, 0, 0, 0}, {0, 0, 0, 1}, {0, 0, 0, 1}}), wallet, inventory); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, _ := store.GrantOnce("refinable", 943035)
|
|
store.owned.Equipment[0].Level = 9
|
|
store.owned.Equipment[0].Rank = []uint64{4, 1, 1}
|
|
if err := store.commitLocked(cloneEquipmentSnapshot(store.owned), "test setup"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 2), 2, entry.InvenIndex)
|
|
request = wire.AppendVarint(request, 3, 100)
|
|
request = wire.AppendVarint(request, 4, 9)
|
|
code, response, handled, err := store.Handle("/EquipSequenceSmelting", request)
|
|
if err != nil || !handled || code != 177 {
|
|
t.Fatalf("sequence smelting code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
if result, _, _ := wire.Varint(response, 3); result != equipUpgradeStopTargetLevel {
|
|
t.Fatalf("sequence smelting result=%d", result)
|
|
}
|
|
if attempts, _, _ := wire.Varint(response, 4); attempts != 1 {
|
|
t.Fatalf("sequence smelting attempts=%d", attempts)
|
|
}
|
|
if successes, _, _ := wire.Varint(response, 9); successes != 1 {
|
|
t.Fatalf("sequence smelting successes=%d", successes)
|
|
}
|
|
if currency := wallet.Snapshot(); currency.Gold != 920 || currency.EquipMileageExchangeGage != 30 {
|
|
t.Fatalf("sequence smelting wallet=%+v", currency)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentSequenceSmeltingPacketLimitIsNotTerminal(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wallet, _ := OpenWallet(testStore(filepath.Join(dir, "wallet.json")), Currency{Gold: 1000})
|
|
inventory, _ := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
_, _ = inventory.GrantOnce("refine-material", []gamedata.BattleReward{{Type: 8, ID: 10, Count: 300}})
|
|
store, _ := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err := store.AttachSmelting(smeltingTestDesign([][]float64{{1, 0, 0, 0}, {1, 0, 0, 0}, {1, 0, 0, 0}}), wallet, inventory); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, _ := store.GrantOnce("refinable", 943035)
|
|
store.owned.Equipment[0].Level = 9
|
|
store.owned.Equipment[0].Rank = []uint64{1, 1, 1}
|
|
if err := store.commitLocked(cloneEquipmentSnapshot(store.owned), "test setup"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 3), 2, entry.InvenIndex)
|
|
request = wire.AppendVarint(request, 3, 2)
|
|
request = wire.AppendVarint(request, 4, 12)
|
|
_, response, _, err := store.Handle("/EquipSequenceSmelting", request)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if result, _, _ := wire.Varint(response, 3); result != equipUpgradeSuccess {
|
|
t.Fatalf("packet exhaustion result=%d want non-terminal success", result)
|
|
}
|
|
if attempts, _, _ := wire.Varint(response, 4); attempts != 2 {
|
|
t.Fatalf("sequence smelting attempts=%d", attempts)
|
|
}
|
|
}
|
|
|
|
func smeltingTestDesign(ratios [][]float64) *gamedata.EquipmentSmeltingDesign {
|
|
design := &gamedata.EquipmentSmeltingDesign{
|
|
Equipment: map[uint64]gamedata.EquipmentSmeltingItem{943035: {Grade: 4, RankGroup: 904, MaxLevel: 9}},
|
|
Ranks: map[[2]uint64]gamedata.EquipmentSmeltingRank{},
|
|
Grades: map[uint64][]gamedata.PromotionCost{4: {{Type: 4, Count: 80}, {Type: 8, ID: 10, Count: 30}}},
|
|
Mileage: gamedata.EquipmentSmeltingMileage{UseType: 8, UseID: 10, UseCount: 1000, RewardType: 68, RewardCount: 1},
|
|
MaxStreak: 5000,
|
|
}
|
|
for i := 0; i < 3; i++ {
|
|
design.Ranks[[2]uint64{904, uint64(i + 1)}] = gamedata.EquipmentSmeltingRank{Values: []uint64{1, 2, 3, 4}, GrowthPoint: []uint64{1, 2, 3, 4}, Ratio: ratios[i]}
|
|
}
|
|
return design
|
|
}
|
|
|
|
func TestEquipmentUpgradeFailureConsumesGoldWithoutLevel(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wallet, err := OpenWallet(testStore(filepath.Join(dir, "wallet.json")), Currency{Gold: 200})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store, err := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
design := &gamedata.EquipmentUpgradeDesign{MaxLevel: map[uint64]uint64{1: 1}, Group: map[uint64]uint64{1: 2}, Levels: map[[2]uint64]gamedata.EquipmentUpgradeLevel{{2, 0}: {Costs: []gamedata.PromotionCost{{Type: 4, Count: 50}}, SuccessRatio: 0}}}
|
|
items, err := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := store.AttachUpgrade(design, wallet, items); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, err := store.GrantOnce("failure", 1)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 1), 2, entry.InvenIndex)
|
|
request = wire.AppendBytes(request, 3, ItemWire(Item{Type: 4, Count: 50}))
|
|
_, response, _, err := store.Handle("/EquipUpgrade", request)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if result, _, _ := wire.Varint(response, 2); result != equipUpgradeFail {
|
|
t.Fatalf("failure result=%d", result)
|
|
}
|
|
if got := store.All()[0]; got.Level != 0 || got.UpgradeAttempts != 1 {
|
|
t.Fatalf("failed upgrade state=%+v", got)
|
|
}
|
|
if wallet.Snapshot().Gold != 150 {
|
|
t.Fatalf("failure gold=%d", wallet.Snapshot().Gold)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentUpgradeConsumesResourceStacksAndSequenceStopsWhenEmpty(t *testing.T) {
|
|
dir := t.TempDir()
|
|
wallet, err := OpenWallet(testStore(filepath.Join(dir, "wallet.json")), Currency{Gold: 1})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
inventory, err := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
resources, err := inventory.GrantOnce("upgrade-items", []gamedata.BattleReward{{Type: 8, ID: 3001, Count: 2}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
store, err := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
design := &gamedata.EquipmentUpgradeDesign{
|
|
MaxLevel: map[uint64]uint64{10: 3}, Group: map[uint64]uint64{10: 20},
|
|
RankGroup: map[uint64]uint64{10: 30}, RankRatio: map[[2]uint64][]float64{{30, 1}: {1, 0, 0, 0}},
|
|
Levels: map[[2]uint64]gamedata.EquipmentUpgradeLevel{
|
|
{20, 0}: {Costs: []gamedata.PromotionCost{{Type: 8, ID: 3001, Count: 1}}, SuccessRatio: 1},
|
|
{20, 1}: {Costs: []gamedata.PromotionCost{{Type: 8, ID: 3001, Count: 1}}, SuccessRatio: 1},
|
|
{20, 2}: {Costs: []gamedata.PromotionCost{{Type: 8, ID: 3001, Count: 1}}, SuccessRatio: 1},
|
|
},
|
|
}
|
|
if err := store.AttachUpgrade(design, wallet, inventory); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
equipment, err := store.GrantOnce("resource-upgrade", 10)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first := resources[0]
|
|
first.Count = 1
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 1), 2, equipment.InvenIndex)
|
|
request = wire.AppendBytes(request, 3, ItemWire(first))
|
|
if _, _, _, err := store.Handle("/EquipUpgrade", request); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
sequence := wire.AppendVarint(wire.AppendVarint(nil, 1, 2), 2, equipment.InvenIndex)
|
|
sequence = wire.AppendVarint(sequence, 3, 10)
|
|
sequence = wire.AppendVarint(sequence, 6, 3)
|
|
_, response, _, err := store.Handle("/EquipSequenceUpgrade", sequence)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if result, _, _ := wire.Varint(response, 3); result != equipUpgradeStopNotEnough {
|
|
t.Fatalf("resource sequence result=%d", result)
|
|
}
|
|
var lack []byte
|
|
if err := wire.Walk(response, func(field wire.Field) error {
|
|
if field.Number == 6 {
|
|
lack = field.Value
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if itemType, _, _ := wire.Varint(lack, 3); itemType != 8 {
|
|
t.Fatalf("missing resource type=%d", itemType)
|
|
}
|
|
if attempts, _, _ := wire.Varint(response, 4); attempts != 1 {
|
|
t.Fatalf("resource sequence attempts=%d", attempts)
|
|
}
|
|
if got := store.All()[0]; got.Level != 2 {
|
|
t.Fatalf("resource upgraded level=%d", got.Level)
|
|
}
|
|
if err := inventory.CanConsume([]Item{first}); err == nil {
|
|
t.Fatal("resource stack was not exhausted")
|
|
}
|
|
}
|
|
|
|
func TestEquipmentCustomMarkSetDeletePersistsAndReturnsInInfo(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "equipment.json")
|
|
store, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, err := store.GrantOnce("marked", 943035)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
set := wire.AppendVarint(wire.AppendVarint(nil, 1, 1), 2, entry.InvenIndex)
|
|
set = wire.AppendBytes(set, 3, []byte("~15|5"))
|
|
code, response, handled, err := store.Handle("/EquipMarkSet", set)
|
|
if err != nil || !handled || code != 396 || len(response) != 0 {
|
|
t.Fatalf("mark set code=%d handled=%v response=%x err=%v", code, handled, response, err)
|
|
}
|
|
restored, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
infoRequest := wire.AppendVarint(nil, 1, 2)
|
|
_, info, _, err := restored.Handle("/EquipInfo", infoRequest)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
equipment, found, err := wire.Bytes(info, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("marked equipment missing: %v", err)
|
|
}
|
|
mark, found, err := wire.Bytes(equipment, 8)
|
|
if err != nil || !found || string(mark) != "~15|5" {
|
|
t.Fatalf("mark=%q found=%v err=%v", mark, found, err)
|
|
}
|
|
deleteRequest := wire.AppendVarint(wire.AppendVarint(nil, 1, 3), 2, entry.InvenIndex)
|
|
code, response, handled, err = restored.Handle("/EquipMarkDelete", deleteRequest)
|
|
if err != nil || !handled || code != 397 || len(response) != 0 {
|
|
t.Fatalf("mark delete code=%d handled=%v response=%x err=%v", code, handled, response, err)
|
|
}
|
|
cleared, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := cleared.All(); len(got) != 1 || got[0].Mark != "" {
|
|
t.Fatalf("cleared equipment=%+v", got)
|
|
}
|
|
for _, invalid := range []string{"", "~0|0", "~16|0", "~1|6", "abc|0", "x", "_|0"} {
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 4), 2, entry.InvenIndex)
|
|
if invalid != "" {
|
|
request = wire.AppendBytes(request, 3, []byte(invalid))
|
|
}
|
|
if _, _, _, err := cleared.Handle("/EquipMarkSet", request); err == nil {
|
|
t.Fatalf("invalid mark %q accepted", invalid)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestEquipmentLockAndProtoDefaultUnlockPersist(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "equipment.json")
|
|
store, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, err := store.GrantOnce("lockable", 943035)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
lock := wire.AppendVarint(wire.AppendVarint(wire.AppendVarint(nil, 1, 1), 2, entry.InvenIndex), 3, 1)
|
|
code, response, handled, err := store.Handle("/EquipLock", lock)
|
|
if err != nil || !handled || code != 38 || len(response) != 0 {
|
|
t.Fatalf("lock code=%d handled=%v response=%x err=%v", code, handled, response, err)
|
|
}
|
|
locked, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := locked.All(); len(got) != 1 || got[0].LockFlag != 1 {
|
|
t.Fatalf("locked equipment=%+v", got)
|
|
}
|
|
unlock := wire.AppendVarint(wire.AppendVarint(nil, 1, 2), 2, entry.InvenIndex)
|
|
code, response, handled, err = locked.Handle("/EquipLock", unlock)
|
|
if err != nil || !handled || code != 38 || len(response) != 0 {
|
|
t.Fatalf("unlock code=%d handled=%v response=%x err=%v", code, handled, response, err)
|
|
}
|
|
unlocked, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := unlocked.All(); len(got) != 1 || got[0].LockFlag != 0 {
|
|
t.Fatalf("unlocked equipment=%+v", got)
|
|
}
|
|
invalid := wire.AppendVarint(wire.AppendVarint(wire.AppendVarint(nil, 1, 3), 2, entry.InvenIndex), 3, 2)
|
|
if _, _, _, err := unlocked.Handle("/EquipLock", invalid); err == nil {
|
|
t.Fatal("invalid lock flag accepted")
|
|
}
|
|
}
|
|
|
|
func TestEquipmentGrantPersistsAndIsIdempotent(t *testing.T) {
|
|
path := filepath.Join(t.TempDir(), "equipment.json")
|
|
store, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
first, err := store.GrantOnce("pack21:quest28:equip10010", 10010)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
again, err := store.GrantOnce("pack21:quest28:equip10010", 10010)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if first.InvenIndex != again.InvenIndex || first.ID != again.ID || first.InvenIndex == 0 || first.ID != 10010 {
|
|
t.Fatalf("grants: %+v %+v", first, again)
|
|
}
|
|
restored, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(nil, 1, 1)
|
|
code, response, ok, err := restored.Handle("/EquipInfo", request)
|
|
if err != nil || !ok || code != 34 {
|
|
t.Fatalf("handle: %d %v %v", code, ok, err)
|
|
}
|
|
encoded, found, err := wire.Bytes(response, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("equipment missing: %v", err)
|
|
}
|
|
index, _, _ := wire.Varint(encoded, 1)
|
|
base, _, _ := wire.Bytes(encoded, 5)
|
|
id, _, _ := wire.Varint(base, 1)
|
|
if index != first.InvenIndex || id != 10010 {
|
|
t.Fatalf("wire index=%d id=%d", index, id)
|
|
}
|
|
var ranks []uint64
|
|
if err := wire.Walk(base, func(field wire.Field) error {
|
|
if field.Number == 6 {
|
|
value, _ := binary.Uvarint(field.Value)
|
|
ranks = append(ranks, value)
|
|
}
|
|
return nil
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(ranks) != 3 || ranks[0] != 0 || ranks[1] != 0 || ranks[2] != 0 {
|
|
t.Fatalf("initial rank slots=%v, want three explicit zeroes", ranks)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentUsePersistsCharacterBinding(t *testing.T) {
|
|
dir := t.TempDir()
|
|
starter := &Starter{Version: "2.35.10"}
|
|
items, err := OpenInventory(testStore(filepath.Join(dir, "items.json")), starter)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
characters, err := OpenCharacterStore(testStore(filepath.Join(dir, "characters.json")),
|
|
[]Character{{InvenIndex: 535607162, ID: 350, Level: 20}}, items, "", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
equipment, err := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := equipment.AttachCharacters(characters); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, err := equipment.GrantOnce("quest28", 10010)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(nil, 1, 1)
|
|
request = wire.AppendVarint(request, 2, entry.InvenIndex)
|
|
request = wire.AppendVarint(request, 3, 535607162)
|
|
code, response, ok, err := equipment.Handle("/EquipUse", request)
|
|
if err != nil || !ok || code != 35 {
|
|
t.Fatalf("use: code=%d ok=%v err=%v", code, ok, err)
|
|
}
|
|
character, found, err := wire.Bytes(response, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("response character: %v", err)
|
|
}
|
|
index, _, _ := wire.Varint(character, 1)
|
|
if index != 535607162 {
|
|
t.Fatalf("character index=%d", index)
|
|
}
|
|
restored, err := OpenEquipmentInventory(testStore(filepath.Join(dir, "equipment.json")))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
infoRequest := wire.AppendVarint(nil, 1, 2)
|
|
_, info, _, err := restored.Handle("/EquipInfo", infoRequest)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
encoded, found, err := wire.Bytes(info, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("restored equipment: %v", err)
|
|
}
|
|
useChar, found, err := wire.Varint(encoded, 2)
|
|
if err != nil || !found || useChar != 535607162 {
|
|
t.Fatalf("useChar=%d found=%v err=%v", useChar, found, err)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentClearPersistsUnboundEquipmentAndReturnsCharacter(t *testing.T) {
|
|
dir := t.TempDir()
|
|
items, err := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
characters, err := OpenCharacterStore(testStore(filepath.Join(dir, "characters.json")),
|
|
[]Character{{InvenIndex: 535607162, ID: 350, Level: 20}}, items, "", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(dir, "equipment.json")
|
|
equipment, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := equipment.AttachCharacters(characters); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
entry, err := equipment.GrantOnce("equipped", 943035)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
equipment.owned.Equipment[0].UseChar = 535607162
|
|
equipment.owned.Equipment[0].LockFlag = 1 // Lock prevents disposal, not unequipping.
|
|
if err := equipment.commitLocked(cloneEquipmentSnapshot(equipment.owned), "test setup"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 9), 2, entry.InvenIndex)
|
|
request = wire.AppendVarint(request, 3, 535607162)
|
|
code, response, handled, err := equipment.Handle("/EquipClear", request)
|
|
if err != nil || !handled || code != 36 {
|
|
t.Fatalf("clear code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
character, found, err := wire.Bytes(response, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("missing clear character: %v", err)
|
|
}
|
|
if index, _, _ := wire.Varint(character, 1); index != 535607162 {
|
|
t.Fatalf("clear character index=%d", index)
|
|
}
|
|
restored, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := restored.All(); len(got) != 1 || got[0].UseChar != 0 || got[0].LockFlag != 1 {
|
|
t.Fatalf("cleared equipment=%+v", got)
|
|
}
|
|
if _, _, _, err := equipment.Handle("/EquipClear", request); err == nil {
|
|
t.Fatal("already-cleared equipment accepted")
|
|
}
|
|
wrongCharacter := wire.AppendVarint(wire.AppendVarint(nil, 1, 10), 2, entry.InvenIndex)
|
|
wrongCharacter = wire.AppendVarint(wrongCharacter, 3, 999)
|
|
if _, _, _, err := equipment.Handle("/EquipClear", wrongCharacter); err == nil {
|
|
t.Fatal("unknown character accepted")
|
|
}
|
|
}
|
|
|
|
func TestEquipmentChangeReplacesOnlyMatchingGameDataSlot(t *testing.T) {
|
|
dir := t.TempDir()
|
|
items, err := OpenInventory(testStore(filepath.Join(dir, "items.json")), &Starter{Version: "2.35.10"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
characters, err := OpenCharacterStore(testStore(filepath.Join(dir, "characters.json")),
|
|
[]Character{{InvenIndex: 535607162, ID: 350, Level: 20}}, items, "", "")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(dir, "equipment.json")
|
|
equipment, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := equipment.AttachCharacters(characters); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := equipment.AttachSlots(map[uint64]uint64{10010: 0, 943009: 0, 943619: 4}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
old, err := equipment.GrantOnce("old", 10010)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
replacement, err := equipment.GrantOnce("replacement", 943009)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
otherSlot, err := equipment.GrantOnce("other-slot", 943619)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
old.UseChar = 535607162
|
|
otherSlot.UseChar = 535607162
|
|
equipment.owned.Equipment[0] = old
|
|
equipment.owned.Equipment[2] = otherSlot
|
|
if err := equipment.commitLocked(cloneEquipmentSnapshot(equipment.owned), "test setup"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
request := wire.AppendVarint(wire.AppendVarint(nil, 1, 7), 2, replacement.InvenIndex)
|
|
request = wire.AppendVarint(request, 3, 535607162)
|
|
code, response, handled, err := equipment.Handle("/EquipChange", request)
|
|
if err != nil || !handled || code != 45 {
|
|
t.Fatalf("change code=%d handled=%v err=%v", code, handled, err)
|
|
}
|
|
character, found, err := wire.Bytes(response, 1)
|
|
if err != nil || !found {
|
|
t.Fatalf("missing response character: %v", err)
|
|
}
|
|
if index, _, _ := wire.Varint(character, 1); index != 535607162 {
|
|
t.Fatalf("response character=%d", index)
|
|
}
|
|
restored, err := OpenEquipmentInventory(testStore(path))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got := restored.All()
|
|
if got[0].UseChar != 0 || got[1].UseChar != 535607162 || got[2].UseChar != 535607162 {
|
|
t.Fatalf("changed equipment=%+v", got)
|
|
}
|
|
}
|
|
|
|
func TestEquipmentWirePrivateObjectPresenceAndExplicitOption(t *testing.T) {
|
|
for _, private := range []*EquipmentOption{nil, {GroupID: 301, ID: 2}} {
|
|
entry := Equipment{ID: 943035, InvenIndex: 1, Rank: []uint64{1, 2, 3}, PrivateOption: private}
|
|
// EquipInfo and equipment reward bundles share this serializer with all
|
|
// enhancement/refinement responses.
|
|
encoded := EquipmentWire(entry)
|
|
base, _, _ := wire.Bytes(encoded, 5)
|
|
option, present, err := wire.Bytes(base, 5)
|
|
if err != nil || !present {
|
|
t.Fatal("PrivateOption was omitted", err)
|
|
}
|
|
group, _, _ := wire.Varint(option, 1)
|
|
id, _, _ := wire.Varint(option, 2)
|
|
if private == nil {
|
|
if len(option) != 0 || group != 0 || id != 0 {
|
|
t.Fatal("empty fallback object malformed")
|
|
}
|
|
} else if group != private.GroupID || id != private.ID {
|
|
t.Fatal("explicit private option altered")
|
|
}
|
|
if entry.PrivateOption != private {
|
|
t.Fatal("wire generation mutated saved ownership")
|
|
}
|
|
}
|
|
}
|