Automatically pause stallers in arenas

arena-stallers
ProgramFOX 2019-08-24 23:34:40 +02:00
parent 0535133cbf
commit cb277db10e
5 changed files with 38 additions and 12 deletions

View File

@ -87,7 +87,9 @@ final class PlaybanApi(
seconds = nowSeconds - game.movedAt.getSeconds
limit <- unreasonableTime
if seconds >= limit
} yield save(Outcome.Sitting, userId, roughWinEstimate(game, flaggerColor)) >>- feedback.sitting(Pov(game, flaggerColor))
} yield save(Outcome.Sitting, userId, roughWinEstimate(game, flaggerColor)) >>-
feedback.sitting(Pov(game, flaggerColor)) >>-
bus.publish(SittingDetected(game, userId), 'playban)
// flagged after waiting a short time;
// but the previous move used a long time.
@ -98,7 +100,9 @@ final class PlaybanApi(
lastMovetime <- movetimes.lastOption
limit <- unreasonableTime
if lastMovetime.toSeconds >= limit
} yield save(Outcome.SitMoving, userId, roughWinEstimate(game, flaggerColor)) >>- feedback.sitting(Pov(game, flaggerColor))
} yield save(Outcome.SitMoving, userId, roughWinEstimate(game, flaggerColor)) >>-
feedback.sitting(Pov(game, flaggerColor)) >>-
bus.publish(SittingDetected(game, userId), 'playban)
sandbag(game, flaggerColor) flatMap { isSandbag =>
IfBlameable(game) {

View File

@ -3,6 +3,8 @@ package lila.playban
import org.joda.time.DateTime
import play.api.libs.json._
import lila.game.Game
case class UserRecord(
_id: String,
o: Option[List[Outcome]],
@ -117,3 +119,5 @@ object Outcome {
def apply(id: Int): Option[Outcome] = byId get id
}
case class SittingDetected(game: Game, userId: String)

View File

@ -15,6 +15,8 @@ private[tournament] final class ApiActor(
case FinishGame(game, _, _) => api finishGame game
case lila.playban.SittingDetected(game, player) => api.sittingDetected(game, player)
case lila.hub.actorApi.mod.MarkCheater(userId, true) =>
leaderboard.getAndDeleteRecent(userId, DateTime.now minusDays 3) foreach {
api.ejectLame(userId, _)

View File

@ -29,7 +29,8 @@ final class Env(
trophyApi: lila.user.TrophyApi,
notifyApi: lila.notify.NotifyApi,
scheduler: lila.common.Scheduler,
startedSinceSeconds: Int => Boolean
startedSinceSeconds: Int => Boolean,
playbanApi: lila.playban.PlaybanApi
) {
private val startsAtMillis = nowMillis
@ -110,7 +111,8 @@ final class Env(
duelStore = duelStore,
pause = pause,
lightUserApi = lightUserApi,
proxyGame = proxyGame
proxyGame = proxyGame,
playbanApi = playbanApi
)
lazy val crudApi = new crud.CrudApi
@ -225,6 +227,7 @@ object Env {
trophyApi = lila.user.Env.current.trophyApi,
notifyApi = lila.notify.Env.current.api,
scheduler = lila.common.PlayApp.scheduler,
startedSinceSeconds = lila.common.PlayApp.startedSinceSeconds
startedSinceSeconds = lila.common.PlayApp.startedSinceSeconds,
playbanApi = lila.playban.Env.current.api
)
}

View File

@ -41,7 +41,8 @@ final class TournamentApi(
pause: Pause,
asyncCache: lila.memo.AsyncCache.Builder,
lightUserApi: lila.user.LightUserApi,
proxyGame: Game.ID => Fu[Option[Game]]
proxyGame: Game.ID => Fu[Option[Game]],
playbanApi: lila.playban.PlaybanApi
) {
private val bus = system.lilaBus
@ -247,21 +248,24 @@ final class TournamentApi(
TournamentRepo tourIdsToWithdrawWhenEntering tourId foreach {
PlayerRepo.filterExists(_, userId) foreach {
_ foreach {
withdraw(_, userId, isPause = false)
withdraw(_, userId, isPause = false, isStalling = false)
}
}
}
def selfPause(tourId: Tournament.ID, userId: User.ID): Unit =
withdraw(tourId, userId, isPause = true)
withdraw(tourId, userId, isPause = true, isStalling = false)
private def withdraw(tourId: Tournament.ID, userId: User.ID, isPause: Boolean): Unit = {
private def stallPause(tourId: Tournament.ID, userId: User.ID): Unit =
withdraw(tourId, userId, isPause = false, isStalling = true)
private def withdraw(tourId: Tournament.ID, userId: User.ID, isPause: Boolean, isStalling: Boolean): Unit = {
Sequencing(tourId)(TournamentRepo.enterableById) {
case tour if tour.isCreated =>
PlayerRepo.remove(tour.id, userId) >> updateNbPlayers(tour.id) >>- socketReload(tour.id) >>- publish()
case tour if tour.isStarted => for {
_ <- PlayerRepo.withdraw(tour.id, userId)
pausable <- isPause ?? cached.ranking(tour).map { _ get userId exists (7>) }
pausable <- if (isPause) cached.ranking(tour).map { _ get userId exists (7>) } else fuccess(isStalling)
} yield {
if (pausable) pause.add(userId, tour)
socketReload(tour.id)
@ -275,7 +279,7 @@ final class TournamentApi(
TournamentRepo.nonEmptyEnterableIds foreach {
PlayerRepo.filterExists(_, user.id) foreach {
_ foreach {
withdraw(_, user.id, isPause = false)
withdraw(_, user.id, isPause = false, isStalling = false)
}
}
}
@ -311,6 +315,15 @@ final class TournamentApi(
}
}
def sittingDetected(game: Game, player: User.ID): Unit =
game.tournamentId foreach { tourId =>
playbanApi.sitAndDcCounter(player) map { counter =>
if (counter <= -5) {
stallPause(tourId, player)
}
}
}
private def updatePlayer(
tour: Tournament,
finishing: Option[Game] // if set, update the player performance. Leave to none to just recompute the sheet.
@ -349,7 +362,7 @@ final class TournamentApi(
if game.status == chess.Status.NoStart
player <- game.playerWhoDidNotMove
userId <- player.userId
} withdraw(tourId, userId, isPause = false)
} withdraw(tourId, userId, isPause = false, isStalling = false)
def pausePlaybanned(userId: User.ID) =
TournamentRepo.startedIds flatMap {