1 package server
2
3 import (
4 "bufio"
5 "bytes"
6 "fmt"
7 "log"
8 "time"
9
10 "code.rocket9labs.com/tslocum/bgammon"
11 )
12
13 type serverGame struct {
14 id int
15 created int64
16 active int64
17 name []byte
18 password []byte
19 client1 *serverClient
20 client2 *serverClient
21 spectators []*serverClient
22 allowed1 []byte
23 allowed2 []byte
24 account1 int
25 account2 int
26 inactive int8
27 forefeit int8
28 rematch int8
29 rejoin1 bool
30 rejoin2 bool
31 replay [][]byte
32 *bgammon.Game
33 }
34
35 func newServerGame(id int, variant int8) *serverGame {
36 now := time.Now().Unix()
37 return &serverGame{
38 id: id,
39 created: now,
40 active: now,
41 Game: bgammon.NewGame(variant),
42 }
43 }
44
45 func (g *serverGame) playForcedMoves() bool {
46 if g.Winner != 0 || len(g.Moves) != 0 || g.client1 == nil || g.client2 == nil {
47 return false
48 }
49 rolls := g.DiceRolls()
50 if len(rolls) == 0 {
51 return false
52 }
53 var playerName string
54 switch g.Turn {
55 case 1:
56 if !g.client1.autoplay {
57 return false
58 }
59 playerName = g.Player1.Name
60 case 2:
61 if !g.client2.autoplay {
62 return false
63 }
64 playerName = g.Player2.Name
65 case 0:
66 return false
67 }
68 tb, ok := g.TabulaBoard()
69 if !ok {
70 return false
71 }
72 allMoves, allBoards := tb.Available(g.Turn)
73 if len(allMoves) == 0 {
74 return false
75 }
76 for i := range allBoards {
77 if i == 0 {
78 continue
79 } else if allBoards[i] != allBoards[0] {
80 return false
81 }
82 }
83 var forcedMoves [][2]int8
84 for _, m1 := range allMoves[0] {
85 if m1[0] == 0 && m1[1] == 0 {
86 break
87 }
88 forcedMoves = append(forcedMoves, m1)
89 }
90 if len(forcedMoves) == 0 {
91 return false
92 }
93 gc := g.Copy(true)
94 for _, move := range forcedMoves {
95 if gc.Winner != 0 {
96 break
97 } else if gc.HaveDiceRoll(move[0], move[1]) == 0 {
98 return false
99 }
100 ok, _ := gc.AddMoves([][]int8{{move[0], move[1]}}, false)
101 if !ok {
102 log.Printf("ERROR: failed to play forced move during validation %v: %v %v (%v) (%v) (%v)", move, forcedMoves, gc.DiceRolls(), gc, gc.Board, allMoves)
103 return false
104 }
105 }
106 g.eachClient(func(client *serverClient) {
107 g.sendBoard(client, true)
108 })
109 for _, move := range forcedMoves {
110 if g.HaveDiceRoll(move[0], move[1]) == 0 {
111 break
112 }
113 ok, _ := g.AddMoves([][]int8{{move[0], move[1]}}, false)
114 if !ok {
115 log.Printf("ERROR: failed to play forced move %v: %v %v (%v) (%v) (%v)", move, forcedMoves, g.DiceRolls(), g.Game, g.Board, allMoves)
116 g.eachClient(func(client *serverClient) {
117 g.sendBoard(client, false)
118 })
119 return false
120 }
121 g.eachClient(func(client *serverClient) {
122 ev := &bgammon.EventMoved{
123 Moves: bgammon.FlipMoves([][]int8{{move[0], move[1]}}, client.playerNumber, g.Variant),
124 }
125 ev.Player = playerName
126 client.sendEvent(ev)
127 })
128 if g.handleWin() {
129 return true
130 }
131 }
132 g.NextPartialTurn(g.Turn)
133 return true
134 }
135
136 func (g *serverGame) roll(player int8) bool {
137 if g.client1 == nil || g.client2 == nil || g.Winner != 0 {
138 return false
139 }
140
141 if g.Turn == 0 {
142 if player == 1 {
143 if g.Roll1 != 0 {
144 return false
145 }
146 g.Roll1 = int8(RandInt(6) + 1)
147 } else {
148 if g.Roll2 != 0 {
149 return false
150 }
151 g.Roll2 = int8(RandInt(6) + 1)
152 }
153
154
155 if g.allowed1 == nil {
156 g.allowed1, g.allowed2 = g.client1.name, g.client2.name
157 }
158
159
160 if g.Started.IsZero() && g.Roll1 != 0 && g.Roll2 != 0 {
161 g.Started = time.Now()
162 if g.client1.account != nil {
163 g.account1 = g.client1.account.id
164 }
165 if g.client2.account != nil {
166 g.account2 = g.client2.account.id
167 }
168 }
169 return true
170 } else if player != g.Turn || g.Roll1 != 0 || g.Roll2 != 0 {
171 return false
172 }
173
174 g.Roll1 = int8(RandInt(6) + 1)
175 g.Roll2 = int8(RandInt(6) + 1)
176 if g.Variant == bgammon.VariantTabula {
177 g.Roll3 = int8(RandInt(6) + 1)
178 }
179
180 return true
181 }
182
183 func (g *serverGame) sendBoard(client *serverClient, forcedMove bool) {
184 if client.json {
185 ev := &bgammon.EventBoard{
186 GameState: bgammon.GameState{
187 Game: g.Game,
188 PlayerNumber: client.playerNumber,
189 Available: g.LegalMoves(false),
190 Forced: forcedMove,
191 Spectating: g.client1 != client && g.client2 != client,
192 },
193 }
194
195
196 if client.playerNumber == 2 {
197 ev.GameState.Game = ev.GameState.Copy(true)
198
199 ev.GameState.PlayerNumber = 1
200 ev.GameState.Player1, ev.GameState.Player2 = ev.GameState.Player2, ev.GameState.Player1
201 ev.GameState.Player1.Number = 1
202 ev.GameState.Player2.Number = 2
203
204 switch ev.GameState.Turn {
205 case 1:
206 ev.GameState.Turn = 2
207 case 2:
208 ev.GameState.Turn = 1
209 }
210
211 switch ev.GameState.DoublePlayer {
212 case 1:
213 ev.GameState.DoublePlayer = 2
214 case 2:
215 ev.GameState.DoublePlayer = 1
216 }
217
218 switch ev.GameState.Winner {
219 case 1:
220 ev.GameState.Winner = 2
221 case 2:
222 ev.GameState.Winner = 1
223 }
224
225 if ev.GameState.Roll1 == 0 || ev.GameState.Roll2 == 0 {
226 ev.GameState.Roll1, ev.GameState.Roll2 = ev.GameState.Roll2, ev.GameState.Roll1
227 }
228
229
230 if g.Variant == bgammon.VariantTabula {
231 for space := int8(1); space <= 24; space++ {
232 ev.Board[space] = g.Game.Board[space] * -1
233 }
234 } else {
235 for space := int8(1); space <= 24; space++ {
236 ev.Board[space] = g.Game.Board[bgammon.FlipSpace(space, client.playerNumber, g.Variant)] * -1
237 }
238 }
239 ev.Board[bgammon.SpaceHomePlayer], ev.Board[bgammon.SpaceHomeOpponent] = ev.Board[bgammon.SpaceHomeOpponent]*-1, ev.Board[bgammon.SpaceHomePlayer]*-1
240 ev.Board[bgammon.SpaceBarPlayer], ev.Board[bgammon.SpaceBarOpponent] = ev.Board[bgammon.SpaceBarOpponent]*-1, ev.Board[bgammon.SpaceBarPlayer]*-1
241 ev.Moves = bgammon.FlipMoves(g.Game.Moves, client.playerNumber, g.Variant)
242 ev.GameState.Available = g.LegalMoves(false)
243 for i := range ev.GameState.Available {
244 ev.GameState.Available[i][0], ev.GameState.Available[i][1] = bgammon.FlipSpace(ev.GameState.Available[i][0], client.playerNumber, g.Variant), bgammon.FlipSpace(ev.GameState.Available[i][1], client.playerNumber, g.Variant)
245 }
246 }
247
248
249 bgammon.SortMoves(ev.Available)
250
251 client.sendEvent(ev)
252 return
253 }
254
255 scanner := bufio.NewScanner(bytes.NewReader(g.BoardState(client.playerNumber, false)))
256 for scanner.Scan() {
257 client.sendNotice(string(scanner.Bytes()))
258 }
259 }
260
261 func (g *serverGame) playerCount() int8 {
262 var c int8
263 if g.client1 != nil {
264 c++
265 }
266 if g.client2 != nil {
267 c++
268 }
269 return c
270 }
271
272 func (g *serverGame) eachClient(f func(client *serverClient)) {
273 if g.client1 != nil {
274 f(g.client1)
275 }
276 if g.client2 != nil {
277 f(g.client2)
278 }
279 for _, spectator := range g.spectators {
280 f(spectator)
281 }
282 }
283
284 func (g *serverGame) addClient(client *serverClient) (spectator bool) {
285 if g.allowed1 != nil && !bytes.Equal(client.name, g.allowed1) && !bytes.Equal(client.name, g.allowed2) {
286 spectator = true
287 } else if g.client1 != nil && g.client2 != nil {
288 spectator = true
289 }
290 if spectator {
291 for _, spec := range g.spectators {
292 if spec == client {
293 return true
294 }
295 }
296 client.playerNumber = 1
297 g.spectators = append(g.spectators, client)
298 ev := &bgammon.EventJoined{
299 GameID: g.id,
300 PlayerNumber: 1,
301 }
302 ev.Player = string(client.name)
303 client.sendEvent(ev)
304 g.sendBoard(client, false)
305 return spectator
306 }
307
308 var playerNumber int8
309 defer func() {
310 ev := &bgammon.EventJoined{
311 GameID: g.id,
312 PlayerNumber: 1,
313 }
314 ev.Player = string(client.name)
315 client.sendEvent(ev)
316 g.sendBoard(client, false)
317
318 if playerNumber == 0 {
319 return
320 }
321
322 opponent := g.opponent(client)
323 if opponent != nil {
324 ev := &bgammon.EventJoined{
325 GameID: g.id,
326 PlayerNumber: 2,
327 }
328 ev.Player = string(client.name)
329 opponent.sendEvent(ev)
330 g.sendBoard(opponent, false)
331 }
332
333 {
334 ev := &bgammon.EventJoined{
335 GameID: g.id,
336 PlayerNumber: client.playerNumber,
337 }
338 ev.Player = string(client.name)
339 for _, spectator := range g.spectators {
340 spectator.sendEvent(ev)
341 g.sendBoard(spectator, false)
342 }
343 }
344
345 if playerNumber == 1 {
346 g.rejoin1 = true
347 } else {
348 g.rejoin2 = true
349 }
350
351 if g.forefeit == playerNumber {
352 g.forefeit = 0
353 }
354 }()
355 var rating int
356 if client.account != nil {
357 rating = client.account.casual.getRating(g.Variant, g.Points > 1) / 100
358 }
359 switch {
360 case g.client1 != nil:
361 g.client2 = client
362 g.Player2.Name = string(client.name)
363 g.Player2.Rating = rating
364 client.playerNumber = 2
365 playerNumber = 2
366 case g.client2 != nil:
367 g.client1 = client
368 g.Player1.Name = string(client.name)
369 g.Player1.Rating = rating
370 client.playerNumber = 1
371 playerNumber = 1
372 default:
373 if RandInt(2) == 0 {
374 g.client1 = client
375 g.Player1.Name = string(client.name)
376 g.Player1.Rating = rating
377 client.playerNumber = 1
378 playerNumber = 1
379 } else {
380 g.client2 = client
381 g.Player2.Name = string(client.name)
382 g.Player2.Rating = rating
383 client.playerNumber = 2
384 playerNumber = 2
385 }
386 }
387 return spectator
388 }
389
390 func (g *serverGame) removeClient(client *serverClient) {
391 var playerNumber int
392 defer func() {
393 if playerNumber == 0 {
394 return
395 }
396
397 ev := &bgammon.EventLeft{}
398 ev.Player = string(client.name)
399
400 client.sendEvent(ev)
401 if !client.json {
402 g.sendBoard(client, false)
403 }
404
405 var opponent *serverClient
406 if playerNumber == 1 && g.client2 != nil {
407 opponent = g.client2
408 } else if playerNumber == 2 && g.client1 != nil {
409 opponent = g.client1
410 }
411 if opponent != nil {
412 opponent.sendEvent(ev)
413 if !opponent.json {
414 g.sendBoard(opponent, false)
415 }
416 }
417
418 for _, spectator := range g.spectators {
419 spectator.sendEvent(ev)
420 if !spectator.json {
421 g.sendBoard(spectator, false)
422 }
423 }
424
425 if playerNumber == 1 && g.client2 != nil {
426 g.forefeit = 1
427 } else if playerNumber == 2 && g.client1 != nil {
428 g.forefeit = 2
429 }
430
431 client.playerNumber = 0
432 }()
433 switch {
434 case g.client1 == client:
435 g.client1 = nil
436 g.Player1.Name = ""
437 g.Player1.Rating = 0
438 playerNumber = 1
439 case g.client2 == client:
440 g.client2 = nil
441 g.Player2.Name = ""
442 g.Player2.Rating = 0
443 playerNumber = 2
444 default:
445 for i, spectator := range g.spectators {
446 if spectator == client {
447 g.spectators = append(g.spectators[:i], g.spectators[i+1:]...)
448
449 ev := &bgammon.EventLeft{}
450 ev.Player = string(client.name)
451
452 client.sendEvent(ev)
453 if !client.json {
454 g.sendBoard(client, false)
455 }
456
457 client.playerNumber = 0
458 return
459 }
460 }
461 return
462 }
463 }
464
465 func (g *serverGame) opponent(client *serverClient) *serverClient {
466 if g.client1 == client {
467 return g.client2
468 } else if g.client2 == client {
469 return g.client1
470 }
471 return nil
472 }
473
474 func (g *serverGame) listing(playerName []byte) *bgammon.GameListing {
475 if g.terminated() {
476 return nil
477 }
478
479 var playerCount int8
480 if len(g.allowed1) != 0 && (len(playerName) == 0 || (!bytes.Equal(g.allowed1, playerName) && !bytes.Equal(g.allowed2, playerName))) {
481 playerCount = 2
482 } else {
483 playerCount = g.playerCount()
484 }
485
486 var rating int
487 if g.client1 != nil && g.client1.account != nil {
488 rating = g.client1.account.casual.getRating(g.Variant, g.Points > 1)
489 }
490 if g.client2 != nil && g.client2.account != nil {
491 r := g.client2.account.casual.getRating(g.Variant, g.Points > 1)
492 if r > rating {
493 rating = r
494 }
495 }
496
497 name := string(g.name)
498 switch g.Variant {
499 case bgammon.VariantAceyDeucey:
500 name = "(Acey-deucey) " + name
501 case bgammon.VariantTabula:
502 name = "(Tabula) " + name
503 }
504
505 return &bgammon.GameListing{
506 ID: g.id,
507 Points: g.Points,
508 Password: len(g.password) != 0,
509 Players: playerCount,
510 Rating: rating / 100,
511 Name: name,
512 }
513 }
514
515 func (g *serverGame) recordEvent() {
516 r1, r2, r3 := g.Roll1, g.Roll2, g.Roll3
517 if r2 > r1 {
518 r1, r2 = r2, r1
519 }
520 if r3 > r1 {
521 r1, r3 = r3, r1
522 }
523 if r3 > r2 {
524 r2, r3 = r3, r2
525 }
526 var movesFormatted []byte
527 if len(g.Moves) != 0 {
528 movesFormatted = append([]byte(" "), bgammon.FormatMoves(g.Moves)...)
529 }
530 line := []byte(fmt.Sprintf("%d r %d-%d", g.Turn, r1, r2))
531 if r3 > 0 {
532 line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
533 }
534 line = append(line, movesFormatted...)
535 g.replay = append(g.replay, line)
536 }
537
538 func (g *serverGame) nextTurn(reroll bool) {
539 g.Game.NextTurn(reroll)
540 if reroll {
541 return
542 }
543
544
545 if g.Winner == 0 {
546 gameState := &bgammon.GameState{
547 Game: g.Game,
548 PlayerNumber: g.Turn,
549 Available: g.LegalMoves(false),
550 }
551 if !gameState.MayDouble() {
552 if !g.roll(g.Turn) {
553 g.eachClient(func(client *serverClient) {
554 client.Terminate("Server error")
555 })
556 return
557 }
558 ev := &bgammon.EventRolled{
559 Roll1: g.Roll1,
560 Roll2: g.Roll2,
561 Roll3: g.Roll3,
562 }
563 if g.Turn == 1 {
564 ev.Player = gameState.Player1.Name
565 } else {
566 ev.Player = gameState.Player2.Name
567 }
568 g.eachClient(func(client *serverClient) {
569 client.sendEvent(ev)
570 })
571
572
573 forcedMove := g.playForcedMoves()
574 if forcedMove && len(g.LegalMoves(false)) == 0 {
575 chooseRoll := g.Variant == bgammon.VariantAceyDeucey && ((g.Roll1 == 1 && g.Roll2 == 2) || (g.Roll1 == 2 && g.Roll2 == 1)) && len(g.Moves) == 2
576 if g.Variant != bgammon.VariantAceyDeucey || !chooseRoll {
577 g.recordEvent()
578 g.nextTurn(false)
579 return
580 }
581 }
582 }
583 }
584
585 g.eachClient(func(client *serverClient) {
586 g.sendBoard(client, false)
587 })
588 }
589
590 func (g *serverGame) handleWin() bool {
591 if g.Winner == 0 {
592 return false
593 }
594 var opponent int8 = 1
595 opponentHome := bgammon.SpaceHomePlayer
596 opponentEntered := g.Player1.Entered
597 playerBar := bgammon.SpaceBarPlayer
598 if g.Winner == 1 {
599 opponent = 2
600 opponentHome = bgammon.SpaceHomeOpponent
601 opponentEntered = g.Player2.Entered
602 playerBar = bgammon.SpaceBarOpponent
603 }
604
605 backgammon := bgammon.PlayerCheckers(g.Board[playerBar], opponent) != 0
606 if !backgammon {
607 homeStart, homeEnd := bgammon.HomeRange(g.Winner, g.Variant)
608 bgammon.IterateSpaces(homeStart, homeEnd, g.Variant, func(space int8, spaceCount int8) {
609 if bgammon.PlayerCheckers(g.Board[space], opponent) != 0 {
610 backgammon = true
611 }
612 })
613 }
614
615 var winPoints int8
616 switch g.Variant {
617 case bgammon.VariantAceyDeucey:
618 for space := int8(0); space < bgammon.BoardSpaces; space++ {
619 if (space == bgammon.SpaceHomePlayer || space == bgammon.SpaceHomeOpponent) && opponentEntered {
620 continue
621 }
622 winPoints += bgammon.PlayerCheckers(g.Board[space], opponent)
623 }
624 case bgammon.VariantTabula:
625 winPoints = 1
626 default:
627 if backgammon {
628 winPoints = 3
629 } else if g.Board[opponentHome] == 0 {
630 winPoints = 2
631 } else {
632 winPoints = 1
633 }
634 }
635
636 g.replay = append([][]byte{[]byte(fmt.Sprintf("i %d %s %s %d %d %d %d %d %d", g.Started.Unix(), g.Player1.Name, g.Player2.Name, g.Points, g.Player1.Points, g.Player2.Points, g.Winner, winPoints, g.Variant))}, g.replay...)
637
638 r1, r2, r3 := g.Roll1, g.Roll2, g.Roll3
639 if r2 > r1 {
640 r1, r2 = r2, r1
641 }
642 if r3 > r1 {
643 r1, r3 = r3, r1
644 }
645 if r3 > r2 {
646 r2, r3 = r3, r2
647 }
648 var movesFormatted []byte
649 if len(g.Moves) != 0 {
650 movesFormatted = append([]byte(" "), bgammon.FormatMoves(g.Moves)...)
651 }
652 line := []byte(fmt.Sprintf("%d r %d-%d", g.Turn, r1, r2))
653 if r3 > 0 {
654 line = append(line, []byte(fmt.Sprintf("-%d", r3))...)
655 }
656 line = append(line, movesFormatted...)
657 g.replay = append(g.replay, line)
658
659 winEvent := &bgammon.EventWin{
660 Points: winPoints * g.DoubleValue,
661 }
662 var reset bool
663 if g.Winner == 1 {
664 winEvent.Player = g.Player1.Name
665 g.Player1.Points = g.Player1.Points + winPoints*g.DoubleValue
666 if g.Player1.Points < g.Points {
667 reset = true
668 } else {
669 g.Ended = time.Now()
670 }
671 } else {
672 winEvent.Player = g.Player2.Name
673 g.Player2.Points = g.Player2.Points + winPoints*g.DoubleValue
674 if g.Player2.Points < g.Points {
675 reset = true
676 } else {
677 g.Ended = time.Now()
678 }
679 }
680
681 winType := winPoints
682 if g.Variant != bgammon.VariantBackgammon {
683 winType = 1
684 }
685 err := recordGameResult(g, winType, g.replay)
686 if err != nil {
687 log.Fatalf("failed to record game result: %s", err)
688 }
689
690 if !reset {
691 err := recordMatchResult(g, matchTypeCasual)
692 if err != nil {
693 log.Fatalf("failed to record match result: %s", err)
694 }
695 } else {
696 g.Reset()
697 g.replay = g.replay[:0]
698 }
699 g.eachClient(func(client *serverClient) {
700 client.sendEvent(winEvent)
701 })
702
703 if g.client1 != nil && g.client1.account != nil {
704 g.Player1.Rating = g.client1.account.casual.getRating(g.Variant, g.Points > 1) / 100
705 }
706 if g.client2 != nil && g.client2.account != nil {
707 g.Player2.Rating = g.client2.account.casual.getRating(g.Variant, g.Points > 1) / 100
708 }
709 g.eachClient(func(client *serverClient) {
710 g.sendBoard(client, false)
711 })
712 return true
713 }
714
715 func (g *serverGame) terminated() bool {
716 return g.client1 == nil && g.client2 == nil
717 }
718
View as plain text