extend playban to ragequitters

pull/450/head
Thibault Duplessis 2015-04-26 13:04:22 +02:00
parent e4c4c8cf7f
commit b3e9941ffb
8 changed files with 46 additions and 30 deletions

View File

@ -7,8 +7,10 @@
<p>You'll be able to play again <strong>@secondsFromNow(ban.remainingSeconds)</strong>.</p>
<h2>Why?</h2>
<p>Either you have <strong>aborted too many games</strong>,<br />
or you have <strong>not played</strong> the games you started.
or you have <strong>not played</strong> the games you started,<br />
or you <strong>quit games without resigning</strong>.
</p>
<br />
<p>Once you start a game, you <strong>must</strong> play it!</p>
<br />
<p>To avoid starting unwanted games,<br />

View File

@ -27,16 +27,19 @@ final class PlaybanApi(coll: Coll) {
private def blameable(game: Game) = game.source == Some(Source.Lobby) && game.hasClock
def abort(pov: Pov): Funit = blameable(pov.game) ?? {
if (pov.game olderThan 45) pov.game.playerWhoDidNotMove map { Blame(_, Outcome.NoPlay) }
else if (pov.game olderThan 15) none
else pov.player.some map { Blame(_, Outcome.Abort) }
} ?? {
case Blame(player, outcome) => player.userId.??(save(outcome))
}
val blame =
if (pov.game olderThan 45) pov.game.playerWhoDidNotMove map { Blame(_, Outcome.NoPlay) }
else if (pov.game olderThan 15) none
else pov.player.some map { Blame(_, Outcome.Abort) }
def rageQuit(game: Game): Funit = blameable(game) ?? {
game.loser.flatMap(_.userId) ?? save(Outcome.RageQuit)
}
blame match {
case None => pov.game.userIds.map(save(Outcome.Good)).sequenceFu.void
case Some(Blame(player, outcome)) => player.userId.??(save(outcome))
}
def goodFinish(game: Game): Funit = blameable(game) ?? {
game.userIds.map(save(Outcome.Good)).sequenceFu.void
}
def currentBan(userId: String): Fu[Option[TempBan]] = coll.find(

View File

@ -60,6 +60,7 @@ object Outcome {
case object Good extends Outcome(0, "Nothing unusual")
case object Abort extends Outcome(1, "Aborts the game")
case object NoPlay extends Outcome(2, "Won't play a move")
case object RageQuit extends Outcome(3, "Quit without resigning")
val all = List(Good, Abort, NoPlay)

View File

@ -26,9 +26,9 @@ private[round] final class Drawer(
def yes(pov: Pov): Fu[Events] = pov match {
case pov if pov.game.toChessHistory.threefoldRepetition =>
finisher(pov.game, _.Draw)
finisher.other(pov.game, _.Draw)
case pov if pov.opponent.isOfferingDraw =>
finisher(pov.game, _.Draw, None, Some(_.drawOfferAccepted))
finisher.other(pov.game, _.Draw, None, Some(_.drawOfferAccepted))
case Pov(g, color) if (g playerCanOfferDraw color) => GameRepo save {
messenger.system(g, color.fold(_.whiteOffersDraw, _.blackOffersDraw))
Progress(g) map { g => g.updatePlayer(color, _ offerDraw g.turns) }
@ -49,7 +49,7 @@ private[round] final class Drawer(
}
def claim(pov: Pov): Fu[Events] =
(pov.game.playable && pov.game.toChessHistory.threefoldRepetition) ?? finisher(pov.game, _.Draw)
(pov.game.playable && pov.game.toChessHistory.threefoldRepetition) ?? finisher.other(pov.game, _.Draw)
def force(game: Game): Fu[Events] = finisher(game, _.Draw, None, None)
def force(game: Game): Fu[Events] = finisher.other(game, _.Draw, None, None)
}

View File

@ -28,7 +28,19 @@ private[round] final class Finisher(
bus.publish(AbortedBy(pov), 'abortGame)
}
def apply(
def rageQuit(game: Game, winner: Option[Color]): Fu[Events] =
apply(game, _.Timeout, winner) addEffect { _ =>
playban.rageQuit(game)
}
def other(
game: Game,
status: Status.type => Status,
winner: Option[Color] = None,
message: Option[SelectI18nKey] = None): Fu[Events] =
apply(game, status, winner, message) >>- playban.goodFinish(game)
private def apply(
game: Game,
status: Status.type => Status,
winner: Option[Color] = None,

View File

@ -76,12 +76,10 @@ private[round] final class Player(
private def moveFinish(game: Game, color: Color): Fu[Events] = {
lazy val winner = game.toChess.situation.winner
game.status match {
case Status.Mate => finisher(game, _.Mate, winner)
case Status.VariantEnd => finisher(game, _.VariantEnd, winner)
case status@(Status.Stalemate | Status.Draw) => finisher(game, _ => status)
case Status.Mate => finisher.other(game, _.Mate, winner)
case Status.VariantEnd => finisher.other(game, _.VariantEnd, winner)
case status@(Status.Stalemate | Status.Draw) => finisher.other(game, _ => status)
case _ => fuccess(Nil)
}
}

View File

@ -56,11 +56,11 @@ private[round] final class Round(
}
case Resign(playerId) => handle(playerId) { pov =>
pov.game.resignable ?? finisher(pov.game, _.Resign, Some(!pov.color))
pov.game.resignable ?? finisher.other(pov.game, _.Resign, Some(!pov.color))
}
case ResignColor(color) => handle(color) { pov =>
pov.game.resignable ?? finisher(pov.game, _.Resign, Some(!pov.color))
pov.game.resignable ?? finisher.other(pov.game, _.Resign, Some(!pov.color))
}
case GoBerserk(color) => handle(color) { pov =>
@ -74,20 +74,20 @@ private[round] final class Round(
case ResignForce(playerId) => handle(playerId) { pov =>
(pov.game.resignable && !pov.game.hasAi && pov.game.hasClock) ?? {
socketHub ? Ask(pov.gameId, IsGone(!pov.color)) flatMap {
case true => finisher(pov.game, _.Timeout, Some(pov.color))
case true => finisher.rageQuit(pov.game, Some(pov.color))
case _ => fuccess(List(Event.Reload))
}
}
}
case NoStartColor(color) => handle(color) { pov =>
finisher(pov.game, _.NoStart, Some(!pov.color))
finisher.other(pov.game, _.NoStart, Some(!pov.color))
}
case DrawForce(playerId) => handle(playerId) { pov =>
(pov.game.drawable && !pov.game.hasAi && pov.game.hasClock) ?? {
socketHub ? Ask(pov.gameId, IsGone(!pov.color)) flatMap {
case true => finisher(pov.game, _.Timeout, None)
case true => finisher.rageQuit(pov.game, None)
case _ => fuccess(List(Event.Reload))
}
}
@ -103,8 +103,8 @@ private[round] final class Round(
case Abandon => fuccess {
GameRepo game gameId foreach { gameOption =>
gameOption filter (_.abandoned) foreach { game =>
if (game.abortable) finisher(game, _.Aborted)
else finisher(game, _.Resign, Some(!game.player.color))
if (game.abortable) finisher.other(game, _.Aborted)
else finisher.other(game, _.Resign, Some(!game.player.color))
self ! PoisonPill
}
}
@ -116,7 +116,7 @@ private[round] final class Round(
case DrawForce => handle(drawer force _)
case Cheat(color) => handle { game =>
(game.playable && !game.imported) ?? {
finisher(game, _.Cheat, Some(!color))
finisher.other(game, _.Cheat, Some(!color))
}
}
@ -172,12 +172,12 @@ private[round] final class Round(
case AbortForMaintenance => handle { game =>
messenger.system(game, (_.untranslated("Game aborted for server maintenance")))
messenger.system(game, (_.untranslated("Sorry for the inconvenience!")))
game.playable ?? finisher(game, _.Aborted)
game.playable ?? finisher.other(game, _.Aborted)
}
}
private def outOfTime(game: Game)(p: lila.game.Player) =
finisher(game, _.Outoftime, Some(!p.color) filterNot { color =>
finisher.other(game, _.Outoftime, Some(!p.color) filterNot { color =>
game.toChess.board.variant.drawsOnInsufficientMaterial &&
chess.InsufficientMatingMaterial(game.toChess.board, color)
})

View File

@ -592,7 +592,7 @@ body.fpmenu > div.content {
display: block;
margin-bottom: 3px;
color: #555;
opacity: 0.6;
opacity: 0.7;
transition: opacity 0.13s;
}
#site_title:hover {