1 package server
2
3 import (
4 "bytes"
5 "encoding/json"
6 "fmt"
7 "log"
8 "strconv"
9 "time"
10
11 "code.rocket9labs.com/tslocum/bgammon"
12 )
13
14 type clientRating struct {
15 backgammonSingle int
16 backgammonMulti int
17 aceySingle int
18 aceyMulti int
19 tabulaSingle int
20 tabulaMulti int
21 }
22
23 func (r *clientRating) getRating(variant int8, multiPoint bool) int {
24 switch variant {
25 case bgammon.VariantBackgammon:
26 if !multiPoint {
27 return r.backgammonSingle
28 }
29 return r.backgammonMulti
30 case bgammon.VariantAceyDeucey:
31 if !multiPoint {
32 return r.aceySingle
33 }
34 return r.aceyMulti
35 case bgammon.VariantTabula:
36 if !multiPoint {
37 return r.tabulaSingle
38 }
39 return r.tabulaMulti
40 default:
41 log.Panicf("unknown variant: %d", variant)
42 return 0
43 }
44 }
45
46 func (r *clientRating) setRating(variant int8, multiPoint bool, rating int) {
47 switch variant {
48 case bgammon.VariantBackgammon:
49 if !multiPoint {
50 r.backgammonSingle = rating
51 return
52 }
53 r.backgammonMulti = rating
54 case bgammon.VariantAceyDeucey:
55 if !multiPoint {
56 r.aceySingle = rating
57 return
58 }
59 r.aceyMulti = rating
60 case bgammon.VariantTabula:
61 if !multiPoint {
62 r.tabulaSingle = rating
63 }
64 r.tabulaMulti = rating
65 default:
66 log.Panicf("unknown variant: %d", variant)
67 }
68 }
69
70 type serverClient struct {
71 id int
72 json bool
73 name []byte
74 account *account
75 accountID int
76 connected int64
77 active int64
78 lastPing int64
79 commands chan []byte
80 autoplay bool
81 playerNumber int8
82 terminating bool
83 bgammon.Client
84 }
85
86 func (c *serverClient) sendEvent(e interface{}) {
87
88 if c.json {
89 switch ev := e.(type) {
90 case *bgammon.EventWelcome:
91 ev.Type = bgammon.EventTypeWelcome
92 case *bgammon.EventHelp:
93 ev.Type = bgammon.EventTypeHelp
94 case *bgammon.EventPing:
95 ev.Type = bgammon.EventTypePing
96 case *bgammon.EventNotice:
97 ev.Type = bgammon.EventTypeNotice
98 case *bgammon.EventSay:
99 ev.Type = bgammon.EventTypeSay
100 case *bgammon.EventList:
101 ev.Type = bgammon.EventTypeList
102 case *bgammon.EventJoined:
103 ev.Type = bgammon.EventTypeJoined
104 case *bgammon.EventFailedJoin:
105 ev.Type = bgammon.EventTypeFailedJoin
106 case *bgammon.EventLeft:
107 ev.Type = bgammon.EventTypeLeft
108 case *bgammon.EventFailedLeave:
109 ev.Type = bgammon.EventTypeFailedLeave
110 case *bgammon.EventBoard:
111 ev.Type = bgammon.EventTypeBoard
112 case *bgammon.EventRolled:
113 ev.Type = bgammon.EventTypeRolled
114 case *bgammon.EventFailedRoll:
115 ev.Type = bgammon.EventTypeFailedRoll
116 case *bgammon.EventMoved:
117 ev.Type = bgammon.EventTypeMoved
118 case *bgammon.EventFailedMove:
119 ev.Type = bgammon.EventTypeFailedMove
120 case *bgammon.EventFailedOk:
121 ev.Type = bgammon.EventTypeFailedOk
122 case *bgammon.EventWin:
123 ev.Type = bgammon.EventTypeWin
124 case *bgammon.EventSettings:
125 ev.Type = bgammon.EventTypeSettings
126 case *bgammon.EventReplay:
127 ev.Type = bgammon.EventTypeReplay
128 case *bgammon.EventHistory:
129 ev.Type = bgammon.EventTypeHistory
130 default:
131 log.Panicf("unknown event type %+v", ev)
132 }
133
134 buf, err := json.Marshal(e)
135 if err != nil {
136 panic(err)
137 }
138 c.Write(buf)
139 return
140 }
141
142
143 switch ev := e.(type) {
144 case *bgammon.EventWelcome:
145 c.Write([]byte(fmt.Sprintf("welcome %s there are %d clients playing %d matches.", ev.PlayerName, ev.Clients, ev.Games)))
146 case *bgammon.EventHelp:
147 c.Write([]byte("helpstart Help text:"))
148 c.Write([]byte(fmt.Sprintf("help %s", ev.Message)))
149 c.Write([]byte("helpend End of help text."))
150 case *bgammon.EventPing:
151 c.Write([]byte(fmt.Sprintf("ping %s", ev.Message)))
152 case *bgammon.EventNotice:
153 c.Write([]byte(fmt.Sprintf("notice %s", ev.Message)))
154 case *bgammon.EventSay:
155 c.Write([]byte(fmt.Sprintf("say %s %s", ev.Player, ev.Message)))
156 case *bgammon.EventList:
157 c.Write([]byte("liststart Matches list:"))
158 for _, g := range ev.Games {
159 password := 0
160 if g.Password {
161 password = 1
162 }
163 name := "(No name)"
164 if g.Name != "" {
165 name = g.Name
166 }
167 c.Write([]byte(fmt.Sprintf("game %d %d %d %d %s", g.ID, password, g.Points, g.Players, name)))
168 }
169 c.Write([]byte("listend End of matches list."))
170 case *bgammon.EventJoined:
171 c.Write([]byte(fmt.Sprintf("joined %d %d %s", ev.GameID, ev.PlayerNumber, ev.Player)))
172 case *bgammon.EventFailedJoin:
173 c.Write([]byte(fmt.Sprintf("failedjoin %s", ev.Reason)))
174 case *bgammon.EventLeft:
175 c.Write([]byte(fmt.Sprintf("left %s", ev.Player)))
176 case *bgammon.EventRolled:
177 msg := []byte(fmt.Sprintf("rolled %s %d %d", ev.Player, ev.Roll1, ev.Roll2))
178 if ev.Roll3 != 0 {
179 msg = append(msg, []byte(fmt.Sprintf(" %d", ev.Roll3))...)
180 }
181 c.Write(msg)
182 case *bgammon.EventFailedRoll:
183 c.Write([]byte(fmt.Sprintf("failedroll %s", ev.Reason)))
184 case *bgammon.EventMoved:
185 c.Write([]byte(fmt.Sprintf("moved %s %s", ev.Player, bgammon.FormatAndFlipMoves(ev.Moves, c.playerNumber, bgammon.VariantBackgammon))))
186 case *bgammon.EventFailedMove:
187 c.Write([]byte(fmt.Sprintf("failedmove %d/%d %s", ev.From, ev.To, ev.Reason)))
188 case *bgammon.EventFailedOk:
189 c.Write([]byte(fmt.Sprintf("failedok %s", ev.Reason)))
190 case *bgammon.EventWin:
191 if ev.Points != 0 {
192 c.Write([]byte(fmt.Sprintf("win %s wins %d points!", ev.Player, ev.Points)))
193 } else {
194 c.Write([]byte(fmt.Sprintf("win %s wins!", ev.Player)))
195 }
196 default:
197 log.Printf("warning: skipped sending unknown event to non-json client: %+v", ev)
198 }
199 }
200
201 func (c *serverClient) sendNotice(message string) {
202 c.sendEvent(&bgammon.EventNotice{
203 Message: message,
204 })
205 }
206
207 func (c *serverClient) label() string {
208 if len(c.name) > 0 {
209 return string(c.name)
210 }
211 return strconv.Itoa(c.id)
212 }
213
214 func (c *serverClient) Terminate(reason string) {
215 if c.Terminated() || c.terminating {
216 return
217 }
218 c.terminating = true
219
220 var extra string
221 if reason != "" {
222 extra = ": " + reason
223 }
224 c.sendNotice("Connection terminated" + extra)
225
226 go func() {
227 time.Sleep(time.Second)
228 c.Client.Terminate(reason)
229 }()
230 }
231
232 func logClientRead(msg []byte) {
233 msgLower := bytes.ToLower(msg)
234 loginJSON := bytes.HasPrefix(msgLower, []byte("loginjson ")) || bytes.HasPrefix(msgLower, []byte("lj "))
235 if bytes.HasPrefix(msgLower, []byte("login ")) || bytes.HasPrefix(msgLower, []byte("l ")) || loginJSON {
236 split := bytes.Split(msg, []byte(" "))
237 var clientName []byte
238 var username []byte
239 var password []byte
240 l := len(split)
241 if l > 1 {
242 if loginJSON {
243 clientName = split[1]
244 } else {
245 username = split[1]
246 }
247 if l > 2 {
248 if loginJSON {
249 username = split[2]
250 } else {
251 password = []byte("*******")
252 }
253 if l > 3 {
254 if loginJSON {
255 password = []byte("*******")
256 }
257 }
258 }
259 }
260 if len(clientName) == 0 {
261 clientName = []byte("unspecified")
262 }
263 log.Printf("<- %s %s %s %s", split[0], clientName, username, password)
264 } else if !bytes.HasPrefix(msgLower, []byte("list")) && !bytes.HasPrefix(msgLower, []byte("ls")) && !bytes.HasPrefix(msgLower, []byte("pong")) {
265 log.Printf("<- %s", msg)
266 }
267 }
268
View as plain text