Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Automatically pause stallers in arenas #5455

Merged
merged 3 commits into from
Aug 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
15 changes: 13 additions & 2 deletions modules/playban/src/main/PlaybanApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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)) >>-
propagateSitting(game, userId)

// flagged after waiting a short time;
// but the previous move used a long time.
Expand All @@ -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)) >>-
propagateSitting(game, userId)

sandbag(game, flaggerColor) flatMap { isSandbag =>
IfBlameable(game) {
Expand All @@ -109,6 +113,13 @@ final class PlaybanApi(
}
}

def propagateSitting(game: Game, userId: String) =
sitAndDcCounter(userId) map { counter =>
if (counter <= -5) {
bus.publish(SittingDetected(game, userId), 'playban)
}
}

def other(game: Game, status: Status.type => Status, winner: Option[Color]): Funit =
winner.?? { w => sandbag(game, !w) } flatMap { isSandbag =>
IfBlameable(game) {
Expand Down
4 changes: 4 additions & 0 deletions modules/playban/src/main/model.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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]],
Expand Down Expand Up @@ -117,3 +119,5 @@ object Outcome {

def apply(id: Int): Option[Outcome] = byId get id
}

case class SittingDetected(game: Game, userId: String)
2 changes: 2 additions & 0 deletions modules/tournament/src/main/ApiActor.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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, _)
Expand Down
20 changes: 14 additions & 6 deletions modules/tournament/src/main/TournamentApi.scala
Original file line number Diff line number Diff line change
Expand Up @@ -247,21 +247,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)
Expand All @@ -275,7 +278,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)
}
}
}
Expand Down Expand Up @@ -311,6 +314,11 @@ final class TournamentApi(
}
}

def sittingDetected(game: Game, player: User.ID): Unit =
game.tournamentId foreach { tourId =>
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.
Expand Down Expand Up @@ -349,7 +357,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 {
Expand Down