]> git.lizzy.rs Git - mt.git/blob - inv.go
rudp: fix errors returned by Peer.Recv other than net.ErrClosed when the Peer is...
[mt.git] / inv.go
1 package mt
2
3 import (
4         "fmt"
5         "io"
6         "reflect"
7 )
8
9 type Inv []NamedInvList
10
11 type NamedInvList struct {
12         Name string
13         InvList
14 }
15
16 func (inv Inv) List(name string) *NamedInvList {
17         for i, l := range inv {
18                 if l.Name == name {
19                         return &inv[i]
20                 }
21         }
22         return nil
23 }
24
25 func (i Inv) Serialize(w io.Writer) error {
26         for _, l := range i {
27                 if _, err := fmt.Fprintln(w, "List", l.Name, len(l.Stacks)); err != nil {
28                         return err
29                 }
30                 if err := l.Serialize(w); err != nil {
31                         return err
32                 }
33         }
34         _, err := fmt.Fprintln(w, "EndInventory")
35         return err
36 }
37
38 func (i *Inv) Deserialize(r io.Reader) (err error) {
39         s := new(sentinal)
40         defer s.recover(&err)
41
42         old := *i
43         *i = nil
44
45         for {
46                 if err := readCmdLn(r, map[string]interface{}{
47                         "List": func(name string, size int) {
48                                 l := old.List(name)
49                                 if l == nil {
50                                         l = &NamedInvList{Name: name}
51                                 }
52
53                                 if err := l.Deserialize(r); err != nil {
54                                         s.ret(fmt.Errorf("List %s %d: %w", name, size, err))
55                                 }
56                                 if len(l.Stacks) != size {
57                                         s.ret(fmt.Errorf("List %s %d: contains %d stacks", name, size, len(l.Stacks)))
58                                 }
59
60                                 *i = append(*i, *l)
61                         },
62                         "KeepList": func(name string) {
63                                 l := old.List(name)
64                                 if l == nil {
65                                         s.ret(fmt.Errorf("KeepList %s: list does not exist", name))
66                                 }
67
68                                 *i = append(*i, *l)
69                         },
70                         "EndInventory": func() {
71                                 s.ret(nil)
72                         },
73                 }); err != nil {
74                         if err == io.EOF {
75                                 s.ret(io.ErrUnexpectedEOF)
76                         }
77                         s.ret(err)
78                 }
79         }
80 }
81
82 type InvList struct {
83         Width  int
84         Stacks []Stack
85 }
86
87 func (l InvList) Serialize(w io.Writer) error {
88         if _, err := fmt.Fprintln(w, "Width", l.Width); err != nil {
89                 return err
90         }
91         for _, i := range l.Stacks {
92                 if i.Count > 0 {
93                         if _, err := fmt.Fprintln(w, "Item", i); err != nil {
94                                 return err
95                         }
96                 } else {
97                         if _, err := fmt.Fprintln(w, "Empty"); err != nil {
98                                 return err
99                         }
100                 }
101         }
102         _, err := fmt.Fprintln(w, "EndInventoryList")
103         return err
104 }
105
106 func (i *InvList) Deserialize(r io.Reader) (err error) {
107         s := new(sentinal)
108         defer s.recover(&err)
109
110         if _, err := fmt.Fscanf(r, "Width %d\n", &i.Width); err != nil {
111                 s.ret(err)
112         }
113
114         i.Stacks = i.Stacks[:0]
115
116         for {
117                 if err := readCmdLn(r, map[string]interface{}{
118                         "Empty": func() {
119                                 i.Stacks = append(i.Stacks, Stack{})
120                         },
121                         "Item": func(stk Stack) {
122                                 i.Stacks = append(i.Stacks, stk)
123                         },
124                         "Keep": func() {
125                                 if len(i.Stacks) < cap(i.Stacks) {
126                                         i.Stacks = i.Stacks[:len(i.Stacks)+1]
127                                 } else {
128                                         i.Stacks = append(i.Stacks, Stack{})
129                                 }
130                         },
131                         "EndInventoryList": func() {
132                                 s.ret(nil)
133                         },
134                 }); err != nil {
135                         if err == io.EOF {
136                                 s.ret(io.ErrUnexpectedEOF)
137                         }
138                         s.ret(err)
139                 }
140         }
141 }
142
143 func readCmdLn(r io.Reader, cmds map[string]interface{}) error {
144         if _, ok := r.(io.RuneScanner); !ok {
145                 r = &readRune{Reader: r, peekRune: -1}
146         }
147
148         var cmd string
149         if _, err := fmt.Fscan(r, &cmd); err != nil {
150                 return err
151         }
152
153         f, ok := cmds[cmd]
154         if !ok {
155                 return fmt.Errorf("unsupported line type: %+q", cmd)
156         }
157
158         t := reflect.TypeOf(f)
159
160         a := make([]interface{}, t.NumIn())
161         for i := range a {
162                 a[i] = reflect.New(t.In(i)).Interface()
163         }
164         fmt.Fscanln(r, a...)
165
166         args := make([]reflect.Value, t.NumIn())
167         for i := range args {
168                 args[i] = reflect.ValueOf(a[i]).Elem()
169         }
170         reflect.ValueOf(f).Call(args)
171
172         return nil
173 }
174
175 type sentinal struct {
176         err error
177 }
178
179 func (s *sentinal) ret(err error) {
180         s.err = err
181         panic(s)
182 }
183
184 func (s *sentinal) recover(p *error) {
185         if r := recover(); r == s {
186                 *p = s.err
187         } else {
188                 panic(r)
189         }
190 }