2013-04-08 13:21:03 -06:00
|
|
|
package lila.round
|
|
|
|
|
2017-01-15 05:26:08 -07:00
|
|
|
import chess.{ Status, Color }
|
2013-12-21 05:14:50 -07:00
|
|
|
|
2015-04-21 10:32:11 -06:00
|
|
|
import lila.game.actorApi.{ FinishGame, AbortedBy }
|
2017-07-07 06:59:54 -06:00
|
|
|
import lila.game.{ GameRepo, Game, Pov, RatingDiffs }
|
2014-02-17 02:12:19 -07:00
|
|
|
import lila.i18n.I18nKey.{ Select => SelectI18nKey }
|
2017-01-15 05:26:08 -07:00
|
|
|
import lila.playban.PlaybanApi
|
|
|
|
import lila.user.{ User, UserRepo }
|
2013-04-08 13:21:03 -06:00
|
|
|
|
2013-05-17 19:03:53 -06:00
|
|
|
private[round] final class Finisher(
|
2013-04-08 13:21:03 -06:00
|
|
|
messenger: Messenger,
|
2014-01-13 15:51:49 -07:00
|
|
|
perfsUpdater: PerfsUpdater,
|
2015-04-25 15:06:44 -06:00
|
|
|
playban: PlaybanApi,
|
2016-06-22 06:45:33 -06:00
|
|
|
notifier: RoundNotifier,
|
2014-03-16 14:57:15 -06:00
|
|
|
crosstableApi: lila.game.CrosstableApi,
|
2014-07-27 04:07:13 -06:00
|
|
|
bus: lila.common.Bus,
|
2016-09-07 11:33:24 -06:00
|
|
|
casualOnly: Boolean,
|
2017-02-14 08:34:07 -07:00
|
|
|
getSocketStatus: Game.ID => Fu[actorApi.SocketStatus]
|
|
|
|
) {
|
2013-04-08 13:21:03 -06:00
|
|
|
|
2017-10-22 16:03:47 -06:00
|
|
|
def abort(pov: Pov)(implicit proxy: GameProxy): Fu[Events] = apply(pov.game, _.Aborted, None) >>- {
|
2016-09-07 11:33:24 -06:00
|
|
|
getSocketStatus(pov.gameId) foreach { ss =>
|
|
|
|
playban.abort(pov, ss.colorsOnGame)
|
|
|
|
}
|
2015-04-21 10:32:11 -06:00
|
|
|
bus.publish(AbortedBy(pov), 'abortGame)
|
|
|
|
}
|
|
|
|
|
2016-03-22 07:06:48 -06:00
|
|
|
def rageQuit(game: Game, winner: Option[Color])(implicit proxy: GameProxy): Fu[Events] =
|
2016-07-18 02:22:13 -06:00
|
|
|
apply(game, _.Timeout, winner) >>-
|
|
|
|
winner.?? { color => playban.rageQuit(game, !color) }
|
2015-04-26 05:04:22 -06:00
|
|
|
|
2016-03-28 08:12:28 -06:00
|
|
|
def outOfTime(game: Game)(implicit proxy: GameProxy): Fu[Events] = {
|
|
|
|
import lila.common.PlayApp
|
2017-04-29 08:13:48 -06:00
|
|
|
if (!PlayApp.startedSinceSeconds(60) && (game.movedAt isBefore PlayApp.startedAt)) {
|
2016-03-28 08:12:28 -06:00
|
|
|
logger.info(s"Aborting game last played before JVM boot: ${game.id}")
|
|
|
|
other(game, _.Aborted, none)
|
2017-08-23 17:56:39 -06:00
|
|
|
} else {
|
2016-03-28 08:12:28 -06:00
|
|
|
val winner = Some(!game.player.color) filterNot { color =>
|
|
|
|
game.toChess.board.variant.insufficientWinningMaterial(game.toChess.situation.board, color)
|
|
|
|
}
|
2016-07-18 02:22:13 -06:00
|
|
|
apply(game, _.Outoftime, winner) >>-
|
2017-10-18 13:02:59 -06:00
|
|
|
winner.?? { w => playban.flag(game, !w) }
|
2016-03-28 08:12:28 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-22 16:03:47 -06:00
|
|
|
def noStart(game: Game)(implicit proxy: GameProxy): Fu[Events] =
|
|
|
|
game.playerWhoDidNotMove ?? { culprit =>
|
2017-10-22 17:14:35 -06:00
|
|
|
lila.mon.round.expiration.count()
|
2017-10-22 16:03:47 -06:00
|
|
|
playban.noStart(Pov(game, culprit))
|
2017-10-24 09:09:18 -06:00
|
|
|
if (game.isMandatory) apply(game, _.NoStart, Some(!culprit.color))
|
2017-10-22 16:03:47 -06:00
|
|
|
else apply(game, _.Aborted, None, Some(_.untranslated("Game aborted by server")))
|
|
|
|
}
|
|
|
|
|
2015-04-26 05:04:22 -06:00
|
|
|
def other(
|
|
|
|
game: Game,
|
|
|
|
status: Status.type => Status,
|
2017-10-22 16:03:47 -06:00
|
|
|
winner: Option[Color],
|
2017-02-14 08:34:07 -07:00
|
|
|
message: Option[SelectI18nKey] = None
|
|
|
|
)(implicit proxy: GameProxy): Fu[Events] =
|
2017-06-19 13:39:39 -06:00
|
|
|
apply(game, status, winner, message) >>- playban.other(game, status, winner)
|
2015-04-26 05:04:22 -06:00
|
|
|
|
|
|
|
private def apply(
|
2013-04-08 13:21:03 -06:00
|
|
|
game: Game,
|
2016-03-10 05:25:03 -07:00
|
|
|
makeStatus: Status.type => Status,
|
2013-04-08 13:21:03 -06:00
|
|
|
winner: Option[Color] = None,
|
2017-02-14 08:34:07 -07:00
|
|
|
message: Option[SelectI18nKey] = None
|
|
|
|
)(implicit proxy: GameProxy): Fu[Events] = {
|
2016-03-10 05:25:03 -07:00
|
|
|
val status = makeStatus(Status)
|
|
|
|
val prog = game.finish(status, winner)
|
2016-06-22 06:45:33 -06:00
|
|
|
if (game.nonAi && game.isCorrespondence) Color.all foreach notifier.gameEnd(prog.game)
|
2016-03-10 09:27:55 -07:00
|
|
|
lila.mon.game.finish(status.name)()
|
2014-07-27 04:07:13 -06:00
|
|
|
casualOnly.fold(
|
|
|
|
GameRepo unrate prog.game.id inject prog.game.copy(mode = chess.Mode.Casual),
|
|
|
|
fuccess(prog.game)
|
|
|
|
) flatMap { g =>
|
2016-03-22 07:06:48 -06:00
|
|
|
proxy.save(prog) >>
|
2015-04-27 04:20:07 -06:00
|
|
|
GameRepo.finish(
|
|
|
|
id = g.id,
|
|
|
|
winnerColor = winner,
|
|
|
|
winnerId = winner flatMap (g.player(_).userId),
|
2017-02-14 08:34:07 -07:00
|
|
|
status = prog.game.status
|
|
|
|
) >>
|
2017-01-15 05:26:08 -07:00
|
|
|
UserRepo.pair(
|
|
|
|
g.whitePlayer.userId,
|
2017-02-14 08:34:07 -07:00
|
|
|
g.blackPlayer.userId
|
2017-03-27 07:47:15 -06:00
|
|
|
).zip {
|
2017-08-23 17:56:39 -06:00
|
|
|
// because the game comes from the round GameProxy,
|
|
|
|
// it doesn't have the tvAt field set
|
|
|
|
// so we fetch it from the DB
|
|
|
|
GameRepo hydrateTvAt g
|
|
|
|
} flatMap {
|
|
|
|
case ((whiteO, blackO), g) => {
|
|
|
|
val finish = FinishGame(g, whiteO, blackO)
|
|
|
|
updateCountAndPerfs(finish) map { ratingDiffs =>
|
|
|
|
message foreach { messenger.system(g, _) }
|
|
|
|
GameRepo game g.id foreach { newGame =>
|
|
|
|
bus.publish(finish.copy(game = newGame | g), 'finishGame)
|
|
|
|
}
|
|
|
|
prog.events :+ lila.game.Event.EndData(g, ratingDiffs)
|
2014-07-27 04:07:13 -06:00
|
|
|
}
|
|
|
|
}
|
2017-01-15 05:26:08 -07:00
|
|
|
}
|
2014-07-27 04:07:13 -06:00
|
|
|
}
|
2016-03-28 08:12:28 -06:00
|
|
|
} >>- proxy.invalidate
|
2013-12-21 05:14:50 -07:00
|
|
|
|
2017-07-07 06:59:54 -06:00
|
|
|
private def updateCountAndPerfs(finish: FinishGame): Fu[Option[RatingDiffs]] =
|
2014-04-20 14:26:40 -06:00
|
|
|
(!finish.isVsSelf && !finish.game.aborted) ?? {
|
|
|
|
(finish.white |@| finish.black).tupled ?? {
|
|
|
|
case (white, black) =>
|
2017-07-07 06:59:54 -06:00
|
|
|
crosstableApi.add(finish.game) zip perfsUpdater.save(finish.game, white, black) map {
|
|
|
|
case _ ~ ratingDiffs => ratingDiffs
|
|
|
|
}
|
2014-04-20 14:26:40 -06:00
|
|
|
} zip
|
|
|
|
(finish.white ?? incNbGames(finish.game)) zip
|
2017-07-07 06:59:54 -06:00
|
|
|
(finish.black ?? incNbGames(finish.game)) map {
|
|
|
|
case ratingDiffs ~ _ ~ _ => ratingDiffs
|
|
|
|
}
|
2014-04-20 14:26:40 -06:00
|
|
|
}
|
2013-12-27 06:25:49 -07:00
|
|
|
|
|
|
|
private def incNbGames(game: Game)(user: User): Funit = game.finished ?? {
|
2017-04-27 05:55:08 -06:00
|
|
|
val totalTime = (game.hasClock && user.playTime.isDefined) ?? game.durationSeconds
|
2014-05-08 06:28:36 -06:00
|
|
|
val tvTime = totalTime ifTrue game.metadata.tvAt.isDefined
|
2017-04-15 05:11:31 -06:00
|
|
|
val result =
|
|
|
|
if (game.winnerUserId has user.id) 1
|
|
|
|
else if (game.loserUserId has user.id) -1
|
|
|
|
else 0
|
2013-12-27 06:25:49 -07:00
|
|
|
UserRepo.incNbGames(user.id, game.rated, game.hasAi,
|
2017-04-15 05:11:31 -06:00
|
|
|
result = result,
|
2014-05-08 06:28:36 -06:00
|
|
|
totalTime = totalTime,
|
2016-04-01 10:54:24 -06:00
|
|
|
tvTime = tvTime).void
|
2013-12-21 05:14:50 -07:00
|
|
|
}
|
2013-04-08 13:21:03 -06:00
|
|
|
}
|