1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
|
package player
// Regression: LoadCharacter must sanitize the inventory/bank slot maps. A
// hand-edited or corrupt save with out-of-range keys, nil entries, or
// non-positive quantities used to leave items no gameplay loop could reach
// while still consuming capacity, and could drive FreeSlots negative.
import (
"os"
"testing"
)
func TestLoadCharacterSanitizesSlotMaps(t *testing.T) {
s := testAccountStore(t)
content := `name: Corrupt
room_id: 1
inventory:
0:
item_id: rope
quantity: 2
3: null
5:
item_id: ghost
quantity: 0
7:
item_id: negative
quantity: -4
99:
item_id: out_of_range
quantity: 1
bank:
0:
item_id: coins
quantity: 100
30:
item_id: far_slot
quantity: 1
-2:
item_id: bogus
quantity: 3
4: null
6:
item_id: empty
quantity: 0
`
if err := os.WriteFile(s.CharPath("Corrupt"), []byte(content), 0600); err != nil {
t.Fatal(err)
}
p, err := s.LoadCharacter("Corrupt")
if err != nil {
t.Fatal(err)
}
if len(p.Inventory) != 1 {
t.Fatalf("inventory = %v, want only the valid slot 0", p.Inventory)
}
slot := p.Inventory[0]
if slot == nil || slot.ItemID != "rope" || slot.Quantity != 2 {
t.Errorf("valid slot 0 changed: %+v", slot)
}
if got := p.FreeSlots(); got != 27 {
t.Errorf("FreeSlots() = %d, want 27", got)
}
if got := p.FreeSlots(); got < 0 {
t.Errorf("FreeSlots() went negative: %d", got)
}
if len(p.Bank) != 2 {
t.Fatalf("bank = %v, want only slots 0 and 30", p.Bank)
}
if bs := p.Bank[0]; bs == nil || bs.ItemID != "coins" || bs.Quantity != 100 {
t.Errorf("valid bank slot 0 changed: %+v", bs)
}
// Bank slots past the inventory's 28 are valid: the bank grows unbounded.
if bs := p.Bank[30]; bs == nil || bs.ItemID != "far_slot" {
t.Errorf("valid bank slot 30 dropped: %+v", bs)
}
if _, ok := p.Bank[-2]; ok {
t.Error("negative bank slot survived sanitization")
}
}
func TestSaveLoadKeepsValidSlots(t *testing.T) {
s := testAccountStore(t)
p := New("Alice")
p.SetInvSlot(0, &InventorySlot{ItemID: "rope", Quantity: 2, Quality: 3, MaxQuality: 5})
p.SetInvSlot(27, &InventorySlot{ItemID: "torch", Quantity: 1})
p.SetBankSlot(0, &InventorySlot{ItemID: "coins", Quantity: 100})
p.SetBankSlot(30, &InventorySlot{ItemID: "spade", Quantity: 1})
if err := s.SaveCharacter(p); err != nil {
t.Fatal(err)
}
loaded, err := s.LoadCharacter("Alice")
if err != nil {
t.Fatal(err)
}
if len(loaded.Inventory) != 2 || len(loaded.Bank) != 2 {
t.Fatalf("slots lost on a round trip: inventory=%v bank=%v", loaded.Inventory, loaded.Bank)
}
if got := loaded.Inventory[0]; got == nil || got.ItemID != "rope" || got.Quantity != 2 || got.Quality != 3 || got.MaxQuality != 5 {
t.Errorf("inventory slot 0 changed: %+v", got)
}
if got := loaded.Inventory[27]; got == nil || got.ItemID != "torch" {
t.Errorf("inventory slot 27 changed: %+v", got)
}
if got := loaded.Bank[30]; got == nil || got.ItemID != "spade" {
t.Errorf("bank slot 30 changed: %+v", got)
}
if loaded.FreeSlots() != 26 {
t.Errorf("FreeSlots() = %d, want 26", loaded.FreeSlots())
}
}
|