Allow forfeiting match after more than ten minutes of inactivity
Resolves #15.
This commit is contained in:
parent
89f20dffb9
commit
3607efee41
5 changed files with 123 additions and 11 deletions
59
game.go
59
game.go
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"fmt"
|
||||
"log"
|
||||
"math"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
|
@ -47,6 +48,10 @@ type Game struct {
|
|||
|
||||
Reroll bool // Used in acey-deucey.
|
||||
|
||||
partialTurn int8
|
||||
partialTime time.Time
|
||||
partialHandled bool
|
||||
|
||||
boardStates [][]int8 // One board state for each move to allow undoing a move.
|
||||
enteredStates [][2]bool // Player 1 entered state and Player 2 entered state for each move.
|
||||
|
||||
|
@ -96,6 +101,10 @@ func (g *Game) Copy(shallow bool) *Game {
|
|||
DoubleOffered: g.DoubleOffered,
|
||||
|
||||
Reroll: g.Reroll,
|
||||
|
||||
partialTurn: g.partialTurn,
|
||||
partialTime: g.partialTime,
|
||||
partialHandled: g.partialHandled,
|
||||
}
|
||||
copy(newGame.Board, g.Board)
|
||||
copy(newGame.Moves, g.Moves)
|
||||
|
@ -108,6 +117,50 @@ func (g *Game) Copy(shallow bool) *Game {
|
|||
return newGame
|
||||
}
|
||||
|
||||
func (g *Game) PartialTurn() int8 {
|
||||
return g.partialTurn
|
||||
}
|
||||
|
||||
func (g *Game) PartialTime() int {
|
||||
var delta time.Duration
|
||||
if g.partialTime.IsZero() {
|
||||
delta = time.Since(g.Started)
|
||||
} else {
|
||||
delta = time.Since(g.partialTime)
|
||||
}
|
||||
if delta <= 30*time.Second {
|
||||
return 0
|
||||
}
|
||||
return int(math.Floor(delta.Seconds()))
|
||||
}
|
||||
|
||||
func (g *Game) PartialHandled() bool {
|
||||
return g.partialHandled
|
||||
}
|
||||
|
||||
func (g *Game) SetPartialHandled(handled bool) {
|
||||
g.partialHandled = handled
|
||||
}
|
||||
|
||||
func (g *Game) NextPartialTurn(player int8) {
|
||||
if g.Started.IsZero() || g.Winner != 0 {
|
||||
return
|
||||
}
|
||||
|
||||
delta := g.PartialTime()
|
||||
if delta > 0 {
|
||||
switch g.partialTurn {
|
||||
case 1:
|
||||
g.Player1.Inactive += delta
|
||||
case 2:
|
||||
g.Player2.Inactive += delta
|
||||
}
|
||||
}
|
||||
|
||||
g.partialTurn = player
|
||||
g.partialTime = time.Now()
|
||||
}
|
||||
|
||||
func (g *Game) NextTurn(reroll bool) {
|
||||
if g.Winner != 0 {
|
||||
return
|
||||
|
@ -121,6 +174,8 @@ func (g *Game) NextTurn(reroll bool) {
|
|||
g.Turn = nextTurn
|
||||
}
|
||||
|
||||
g.NextPartialTurn(g.Turn)
|
||||
|
||||
g.Roll1, g.Roll2, g.Roll3 = 0, 0, 0
|
||||
g.Moves = g.Moves[:0]
|
||||
g.boardStates = g.boardStates[:0]
|
||||
|
@ -128,6 +183,8 @@ func (g *Game) NextTurn(reroll bool) {
|
|||
}
|
||||
|
||||
func (g *Game) Reset() {
|
||||
g.Player1.Inactive = 0
|
||||
g.Player2.Inactive = 0
|
||||
if g.Variant != VariantBackgammon {
|
||||
g.Player1.Entered = false
|
||||
g.Player2.Entered = false
|
||||
|
@ -144,6 +201,8 @@ func (g *Game) Reset() {
|
|||
g.Reroll = false
|
||||
g.boardStates = nil
|
||||
g.enteredStates = nil
|
||||
g.partialTurn = 0
|
||||
g.partialTime = time.Time{}
|
||||
}
|
||||
|
||||
func (g *Game) turnPlayer() Player {
|
||||
|
|
|
@ -23,6 +23,7 @@ type serverGame struct {
|
|||
allowed2 []byte
|
||||
account1 int
|
||||
account2 int
|
||||
inactive int8
|
||||
forefeit int8
|
||||
rematch int8
|
||||
rejoin1 bool
|
||||
|
@ -130,6 +131,7 @@ func (g *serverGame) playForcedMoves() bool {
|
|||
return true
|
||||
}
|
||||
}
|
||||
g.NextPartialTurn(g.Turn)
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
|
@ -23,6 +23,8 @@ import (
|
|||
|
||||
const clientTimeout = 40 * time.Second
|
||||
|
||||
const inactiveLimit = 600 // 10 minutes.
|
||||
|
||||
var allowDebugCommands bool
|
||||
|
||||
var (
|
||||
|
@ -134,7 +136,7 @@ func NewServer(tz string, dataSource string, mailServer string, passwordSalt str
|
|||
go s.handleNewGameIDs()
|
||||
go s.handleNewClientIDs()
|
||||
go s.handleCommands()
|
||||
go s.handleTerminatedGames()
|
||||
go s.handleGames()
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -242,20 +244,62 @@ func (s *server) removeClient(c *serverClient) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *server) handleTerminatedGames() {
|
||||
func (s *server) handleGames() {
|
||||
t := time.NewTicker(time.Minute)
|
||||
for range t.C {
|
||||
s.gamesLock.Lock()
|
||||
|
||||
i := 0
|
||||
for _, g := range s.games {
|
||||
if !g.PartialHandled() && g.Player1.Rating != 0 && g.Player2.Rating != 0 {
|
||||
partialTurn := g.PartialTurn()
|
||||
if partialTurn != 0 {
|
||||
total := g.PartialTime()
|
||||
switch partialTurn {
|
||||
case 1:
|
||||
total += g.Player1.Inactive
|
||||
case 2:
|
||||
total += g.Player2.Inactive
|
||||
}
|
||||
if total >= inactiveLimit {
|
||||
g.inactive = partialTurn
|
||||
g.SetPartialHandled(true)
|
||||
if !g.terminated() {
|
||||
var player *serverClient
|
||||
var opponent *serverClient
|
||||
switch partialTurn {
|
||||
case 1:
|
||||
player = g.client1
|
||||
opponent = g.client2
|
||||
case 2:
|
||||
player = g.client2
|
||||
opponent = g.client1
|
||||
}
|
||||
if player != nil {
|
||||
player.sendNotice("You have been inactive for more than ten minutes. If your opponent leaves the match they will receive a win.")
|
||||
}
|
||||
if opponent != nil {
|
||||
opponent.sendNotice("Your opponent has been inactive for more than ten minutes. You may continue playing or leave the match at any time and receive a win.")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !g.terminated() {
|
||||
s.games[i] = g
|
||||
i++
|
||||
} else if g.forefeit != 0 && g.Winner == 0 {
|
||||
g.Winner = 1
|
||||
if g.forefeit == 1 {
|
||||
g.Winner = 2
|
||||
} else if g.Winner == 0 && (g.inactive != 0 || g.forefeit != 0) {
|
||||
if g.inactive != 0 {
|
||||
g.Winner = 1
|
||||
if g.inactive == 1 {
|
||||
g.Winner = 2
|
||||
}
|
||||
} else {
|
||||
g.Winner = 1
|
||||
if g.forefeit == 1 {
|
||||
g.Winner = 2
|
||||
}
|
||||
}
|
||||
err := recordMatchResult(g, matchTypeCasual)
|
||||
if err != nil {
|
||||
|
|
|
@ -519,6 +519,7 @@ COMMANDS:
|
|||
}
|
||||
|
||||
clientGame.DoubleOffered = true
|
||||
clientGame.NextPartialTurn(opponent.playerNumber)
|
||||
|
||||
cmd.client.sendNotice(fmt.Sprintf("Double offered to opponent (%d points).", clientGame.DoubleValue*2))
|
||||
clientGame.opponent(cmd.client).sendNotice(fmt.Sprintf("%s offers a double (%d points).", cmd.client.name, clientGame.DoubleValue*2))
|
||||
|
@ -552,6 +553,8 @@ COMMANDS:
|
|||
continue
|
||||
}
|
||||
|
||||
clientGame.NextPartialTurn(opponent.playerNumber)
|
||||
|
||||
cmd.client.sendNotice("Declined double offer")
|
||||
clientGame.opponent(cmd.client).sendNotice(fmt.Sprintf("%s declined double offer.", cmd.client.name))
|
||||
|
||||
|
@ -734,6 +737,8 @@ COMMANDS:
|
|||
}
|
||||
}
|
||||
|
||||
clientGame.NextPartialTurn(clientGame.Turn)
|
||||
|
||||
forcedMove := clientGame.playForcedMoves()
|
||||
if forcedMove && len(clientGame.LegalMoves(false)) == 0 {
|
||||
chooseRoll := clientGame.Variant == bgammon.VariantAceyDeucey && ((clientGame.Roll1 == 1 && clientGame.Roll2 == 2) || (clientGame.Roll1 == 2 && clientGame.Roll2 == 1)) && len(clientGame.Moves) == 2
|
||||
|
@ -898,6 +903,7 @@ COMMANDS:
|
|||
clientGame.DoubleOffered = false
|
||||
clientGame.DoubleValue = clientGame.DoubleValue * 2
|
||||
clientGame.DoublePlayer = cmd.client.playerNumber
|
||||
clientGame.NextPartialTurn(opponent.playerNumber)
|
||||
|
||||
cmd.client.sendNotice("Accepted double.")
|
||||
opponent.sendNotice(fmt.Sprintf("%s accepted double.", cmd.client.name))
|
||||
|
|
11
player.go
11
player.go
|
@ -1,11 +1,12 @@
|
|||
package bgammon
|
||||
|
||||
type Player struct {
|
||||
Number int8 // 1 black, 2 white
|
||||
Name string
|
||||
Rating int
|
||||
Points int8
|
||||
Entered bool // Whether all checkers have entered the board. (Acey-deucey)
|
||||
Number int8 // 1 black, 2 white
|
||||
Name string
|
||||
Rating int
|
||||
Points int8
|
||||
Entered bool // Whether all checkers have entered the board. (Acey-deucey)
|
||||
Inactive int // Inactive time. (Seconds)
|
||||
}
|
||||
|
||||
func NewPlayer(number int8) Player {
|
||||
|
|
Loading…
Reference in a new issue