do all takeback negociation in websockets
parent
407c4b621a
commit
9de59615d2
|
@ -7,7 +7,9 @@ import lila.game.{ Pov, PlayerRef, GameRepo, Game ⇒ GameModel }
|
|||
import lila.round.{ RoomRepo, WatcherRoomRepo }
|
||||
import lila.round.actorApi.round._
|
||||
import lila.socket.actorApi.{ Forward, GetVersion }
|
||||
import lila.hub.actorApi.map.{ Tell, Ask }
|
||||
import lila.tournament.{ TournamentRepo, Tournament ⇒ Tourney }
|
||||
import makeTimeout.large
|
||||
|
||||
import akka.pattern.ask
|
||||
import play.api.mvc._
|
||||
|
@ -15,7 +17,7 @@ import play.api.libs.json._
|
|||
import play.api.libs.iteratee._
|
||||
import play.api.templates.Html
|
||||
|
||||
object Round extends LilaController with TheftPrevention with RoundEventPerformer {
|
||||
object Round extends LilaController with TheftPrevention {
|
||||
|
||||
private def env = Env.round
|
||||
private def bookmarkApi = Env.bookmark.api
|
||||
|
@ -96,16 +98,6 @@ object Round extends LilaController with TheftPrevention with RoundEventPerforme
|
|||
def abort(fullId: String) = performAndRedirect(fullId, Abort(_))
|
||||
def resign(fullId: String) = performAndRedirect(fullId, Resign(_))
|
||||
def resignForce(fullId: String) = performAndRedirect(fullId, ResignForce(_))
|
||||
def drawClaim(fullId: String) = performAndRedirect(fullId, DrawClaim(_))
|
||||
def drawAccept(fullId: String) = performAndRedirect(fullId, DrawAccept(_))
|
||||
def drawOffer(fullId: String) = performAndRedirect(fullId, DrawOffer(_))
|
||||
def drawCancel(fullId: String) = performAndRedirect(fullId, DrawCancel(_))
|
||||
def drawDecline(fullId: String) = performAndRedirect(fullId, DrawDecline(_))
|
||||
|
||||
def takebackAccept(fullId: String) = performAndRedirect(fullId, TakebackAccept(_))
|
||||
def takebackOffer(fullId: String) = performAndRedirect(fullId, TakebackOffer(_))
|
||||
def takebackCancel(fullId: String) = performAndRedirect(fullId, TakebackCancel(_))
|
||||
def takebackDecline(fullId: String) = performAndRedirect(fullId, TakebackDecline(_))
|
||||
|
||||
def tableWatcher(gameId: String, color: String) = Open { implicit ctx ⇒
|
||||
OptionOk(GameRepo.pov(gameId, color)) { html.round.table.watch(_) }
|
||||
|
@ -129,4 +121,18 @@ object Round extends LilaController with TheftPrevention with RoundEventPerforme
|
|||
} toMap) ++ ctx.me.??(me ⇒ Map("me" -> me.usernameWithElo))
|
||||
})
|
||||
}
|
||||
|
||||
protected def performAndRedirect(fullId: String, makeMessage: String ⇒ Any) = Action {
|
||||
Async {
|
||||
perform(fullId, makeMessage) inject Redirect(routes.Round.player(fullId))
|
||||
}
|
||||
}
|
||||
|
||||
protected def perform(fullId: String, makeMessage: String ⇒ Any): Funit = {
|
||||
Env.round.roundMap ! Tell(
|
||||
GameModel takeGameId fullId,
|
||||
makeMessage(GameModel takePlayerId(fullId))
|
||||
)
|
||||
Env.round.roundMap ? Ask(GameModel takeGameId fullId, Await) void
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
package controllers
|
||||
|
||||
import lila.app._
|
||||
import lila.game.Event
|
||||
import lila.socket.actorApi.Forward
|
||||
import lila.hub.actorApi.map.{ Tell, Ask }
|
||||
import lila.round.actorApi.round.Await
|
||||
import lila.game.Game.{ takeGameId, takePlayerId }
|
||||
import makeTimeout.large
|
||||
|
||||
import akka.pattern.ask
|
||||
import play.api.mvc._, Results._
|
||||
|
||||
trait RoundEventPerformer {
|
||||
|
||||
protected def performAndRedirect(fullId: String, makeMessage: String ⇒ Any) = Action {
|
||||
Async {
|
||||
perform(fullId, makeMessage) inject Redirect(routes.Round.player(fullId))
|
||||
}
|
||||
}
|
||||
|
||||
protected def perform(fullId: String, makeMessage: String ⇒ Any): Funit = {
|
||||
Env.round.roundMap ! Tell(
|
||||
takeGameId(fullId),
|
||||
makeMessage(takePlayerId(fullId))
|
||||
)
|
||||
Env.round.roundMap ? Ask(takeGameId(fullId), Await) void
|
||||
}
|
||||
}
|
|
@ -8,7 +8,7 @@ import views._
|
|||
import play.api.mvc.{ Result, Call }
|
||||
import play.api.data.Form
|
||||
|
||||
object Setup extends LilaController with TheftPrevention with RoundEventPerformer {
|
||||
object Setup extends LilaController with TheftPrevention {
|
||||
|
||||
private def env = Env.setup
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
<a class="offer_draw" href="@routes.Round.drawOffer(fullId)">@trans.offerDraw()</a>
|
||||
}
|
||||
@if(game.playerCanProposeTakeback(color)) {
|
||||
<a class="propose_takeback" title="@trans.proposeATakeback()"href="@routes.Round.takebackOffer(fullId)">@trans.takeback()</a>
|
||||
<a class="propose_takeback socket-link" data-msg="takeback-yes" title="@trans.proposeATakeback()">@trans.takeback()</a>
|
||||
}
|
||||
}
|
||||
</div>
|
||||
|
@ -53,14 +53,14 @@
|
|||
@if(player.isProposingTakeback) {
|
||||
<div class="proposed_takeback">
|
||||
@trans.takebackPropositionSent().
|
||||
<a href="@routes.Round.takebackCancel(fullId)">@trans.cancel()</a>
|
||||
<a class="socket-link" data-msg="takeback-no">@trans.cancel()</a>
|
||||
</div>
|
||||
} else {
|
||||
@if(opponent.isProposingTakeback) {
|
||||
<div class="offered_draw">
|
||||
@trans.yourOpponentProposesATakeback().<br />
|
||||
<a href="@routes.Round.takebackAccept(fullId)">@trans.accept()</a>
|
||||
<a href="@routes.Round.takebackDecline(fullId)">@trans.decline()</a>
|
||||
<a class="socket-link" data-msg="takeback-yes">@trans.accept()</a>
|
||||
<a class="socket-link" data-msg="takeback-no">@trans.decline()</a>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
|
@ -47,14 +47,6 @@ GET /$fullId<[\w\-]{12}>/abort controllers.Round.abort(ful
|
|||
GET /$fullId<[\w\-]{12}>/resign controllers.Round.resign(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/resign/force controllers.Round.resignForce(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/draw/claim controllers.Round.drawClaim(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/draw/accept controllers.Round.drawAccept(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/draw/offer controllers.Round.drawOffer(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/draw/cancel controllers.Round.drawCancel(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/draw/decline controllers.Round.drawDecline(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/takeback/accept controllers.Round.takebackAccept(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/takeback/offer controllers.Round.takebackOffer(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/takeback/cancel controllers.Round.takebackCancel(fullId: String)
|
||||
GET /$fullId<[\w\-]{12}>/takeback/decline controllers.Round.takebackDecline(fullId: String)
|
||||
GET /$gameId<[\w\-]{8}>/$color<white|black>/table controllers.Round.tableWatcher(gameId: String, color: String)
|
||||
GET /$fullId<[\w\-]{12}>/table controllers.Round.tablePlayer(fullId: String)
|
||||
GET /$gameId<[\w\-]{8}>/players controllers.Round.players(gameId: String)
|
||||
|
|
|
@ -144,6 +144,11 @@ object Event {
|
|||
def typ = "reload_table"
|
||||
def data = JsNull
|
||||
}
|
||||
case object ReloadTablesOwner extends Event {
|
||||
def typ = "reload_table"
|
||||
def data = JsNull
|
||||
override def owner = true
|
||||
}
|
||||
|
||||
case class Premove(color: Color) extends Empty {
|
||||
def typ = "premove"
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
package lila.round
|
||||
|
||||
import chess.{ Game ⇒ ChessGame, Board, Clock, Variant, Color ⇒ ChessColor }
|
||||
import ChessColor.{ White, Black }
|
||||
import lila.game.{ GameRepo, Game, Event, Progress, Pov, PlayerRef, Namer, Source }
|
||||
import lila.user.User
|
||||
import makeTimeout.short
|
||||
|
||||
import lila.game.tube.gameTube
|
||||
import lila.user.tube.userTube
|
||||
import lila.db.api._
|
||||
|
||||
import akka.pattern.ask
|
||||
|
||||
private[round] final class Drawer(
|
||||
messenger: Messenger,
|
||||
finisher: Finisher) {
|
||||
|
||||
def yes(pov: Pov): Fu[Events] = pov match {
|
||||
case pov if pov.opponent.isOfferingDraw ⇒
|
||||
finisher(pov.game, _.Draw, None, Some(_.drawOfferAccepted))
|
||||
case Pov(g1, color) if (g1 playerCanOfferDraw color) ⇒ for {
|
||||
p1 ← messenger.systemMessage(g1, _.drawOfferSent) map { es ⇒
|
||||
Progress(g1, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _ offerDraw g.turns) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case _ ⇒ fufail("[drawer] invalid yes " + pov)
|
||||
}
|
||||
|
||||
def no(pov: Pov): Fu[Events] = pov match {
|
||||
case Pov(g1, color) if pov.player.isOfferingDraw ⇒ for {
|
||||
p1 ← messenger.systemMessage(g1, _.drawOfferCanceled) map { es ⇒
|
||||
Progress(g1, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.removeDrawOffer) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case Pov(g1, color) if pov.opponent.isOfferingDraw ⇒ for {
|
||||
p1 ← messenger.systemMessage(g1, _.drawOfferDeclined) map { es ⇒
|
||||
Progress(g1, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(!color, _.removeDrawOffer) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case _ ⇒ fufail("[drawer] invalid no " + pov)
|
||||
}
|
||||
|
||||
def claim(pov: Pov): Fu[Events] =
|
||||
(pov.game.playable &&
|
||||
pov.game.player.color == pov.color &&
|
||||
pov.game.toChessHistory.threefoldRepetition
|
||||
) ?? finisher(pov.game, _.Draw)
|
||||
|
||||
def force(game: Game): Fu[Events] = finisher(game, _.Draw, None, None)
|
||||
}
|
|
@ -43,10 +43,11 @@ final class Env(
|
|||
new Round(
|
||||
gameId = id,
|
||||
messenger = messenger,
|
||||
takeback = takeback,
|
||||
takebacker = takebacker,
|
||||
ai = ai,
|
||||
finisher = finisher,
|
||||
rematcher = rematcher,
|
||||
drawer = drawer,
|
||||
notifyMove = notifyMove,
|
||||
socketHub = socketHub,
|
||||
moretimeDuration = Moretime)
|
||||
|
@ -79,6 +80,10 @@ final class Env(
|
|||
router = hub.actor.router,
|
||||
timeline = hub.actor.timeline)
|
||||
|
||||
private lazy val drawer = new Drawer(
|
||||
messenger = messenger,
|
||||
finisher = finisher)
|
||||
|
||||
lazy val meddler = new Meddler(
|
||||
roundMap = roundMap,
|
||||
socketHub = socketHub)
|
||||
|
@ -109,7 +114,8 @@ final class Env(
|
|||
|
||||
private lazy val hijack = new Hijack(HijackTimeout)
|
||||
|
||||
private lazy val takeback = new Takeback(messenger)
|
||||
private lazy val takebacker = new Takebacker(
|
||||
messenger = messenger)
|
||||
|
||||
private def notifyMove(gameId: String, fen: String, lastMove: Option[String]) {
|
||||
hub.socket.hub ! lila.socket.actorApi.Fen(gameId, fen, lastMove)
|
||||
|
|
|
@ -40,6 +40,7 @@ private[round] final class Finisher(
|
|||
((g.status >= Status.Mate) ?? incNbGames(g, Black)) >>-
|
||||
(indexer ! lila.game.actorApi.InsertGame(g)) >>-
|
||||
(tournamentOrganizer ! FinishGame(g.id))
|
||||
// TODO send redirection events
|
||||
} yield p2.events
|
||||
|
||||
private def incNbGames(game: Game, color: Color): Funit =
|
||||
|
|
|
@ -20,28 +20,30 @@ private[round] final class Rematcher(
|
|||
timeline: lila.hub.ActorLazyRef) {
|
||||
|
||||
def yes(pov: Pov): Fu[Events] = pov match {
|
||||
case Pov(game, color) ⇒ (game playerCanRematch color) ??
|
||||
case Pov(game, color) if (game playerCanRematch color) ⇒
|
||||
game.opponent(color).isOfferingRematch.fold(
|
||||
game.next.fold(rematchJoin(pov))(rematchExists(pov)),
|
||||
rematchCreate(pov)
|
||||
)
|
||||
case _ ⇒ fufail("[rematcher] invalid yes " + pov)
|
||||
}
|
||||
|
||||
def no(pov: Pov): Fu[Events] = pov match {
|
||||
case Pov(g1, color) if (pov.player.isOfferingRematch) ⇒ for {
|
||||
case Pov(g1, color) if pov.player.isOfferingRematch ⇒ for {
|
||||
p1 ← messenger.systemMessage(g1, _.rematchOfferCanceled) map { es ⇒
|
||||
Progress(g1, Event.ReloadTables :: es)
|
||||
Progress(g1, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.removeRematchOffer) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case Pov(g1, color) if (g1.player(!color).isOfferingRematch) ⇒ for {
|
||||
case Pov(g1, color) if pov.opponent.isOfferingRematch ⇒ for {
|
||||
p1 ← messenger.systemMessage(g1, _.rematchOfferDeclined) map { es ⇒
|
||||
Progress(g1, Event.ReloadTables :: es)
|
||||
Progress(g1, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(!color, _.removeRematchOffer) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case _ ⇒ fufail("[rematcher] invalid no " + pov)
|
||||
}
|
||||
|
||||
private def rematchExists(pov: Pov)(nextId: String): Fu[Events] =
|
||||
|
@ -63,7 +65,7 @@ private[round] final class Rematcher(
|
|||
|
||||
private def rematchCreate(pov: Pov): Fu[Events] = for {
|
||||
p1 ← messenger.systemMessage(pov.game, _.rematchOfferSent) map { es ⇒
|
||||
Progress(pov.game, Event.ReloadTables :: es)
|
||||
Progress(pov.game, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(pov.color, _ offerRematch) }
|
||||
_ ← GameRepo save p2
|
||||
|
|
|
@ -17,10 +17,11 @@ import akka.pattern.{ ask, pipe }
|
|||
private[round] final class Round(
|
||||
gameId: String,
|
||||
messenger: Messenger,
|
||||
takeback: Takeback,
|
||||
takebacker: Takebacker,
|
||||
ai: Ai,
|
||||
finisher: Finisher,
|
||||
rematcher: Rematcher,
|
||||
drawer: Drawer,
|
||||
notifyMove: (String, String, Option[String]) ⇒ Unit,
|
||||
socketHub: ActorRef,
|
||||
moretimeDuration: Duration) extends Actor {
|
||||
|
@ -114,106 +115,16 @@ private[round] final class Round(
|
|||
}
|
||||
}
|
||||
|
||||
case DrawClaim(playerId) ⇒ publishing(playerId) { pov ⇒
|
||||
(pov.game.playable &&
|
||||
pov.game.player.color == pov.color &&
|
||||
pov.game.toChessHistory.threefoldRepetition
|
||||
) ?? finisher(pov.game, _.Draw)
|
||||
}
|
||||
case DrawYes(playerRef) ⇒ publishing(playerRef)(drawer.yes)
|
||||
case DrawNo(playerRef) ⇒ publishing(playerRef)(drawer.no)
|
||||
case DrawClaim(playerId) ⇒ publishing(playerId)(drawer.claim)
|
||||
case DrawForce ⇒ publishing(drawer force _)
|
||||
|
||||
case DrawAccept(playerId) ⇒ publishing(playerId) { pov ⇒
|
||||
pov.opponent.isOfferingDraw ?? finisher(pov.game, _.Draw, None, Some(_.drawOfferAccepted))
|
||||
}
|
||||
case RematchYes(playerRef) ⇒ publishing(playerRef)(rematcher.yes)
|
||||
case RematchNo(playerRef) ⇒ publishing(playerRef)(rematcher.no)
|
||||
|
||||
case DrawForce ⇒ publishing { game ⇒
|
||||
finisher(game, _.Draw, None, None)
|
||||
}
|
||||
|
||||
case DrawOffer(playerId) ⇒ publishing(playerId) {
|
||||
case pov @ Pov(g1, color) ⇒ (g1 playerCanOfferDraw color) ?? {
|
||||
if (g1.player(!color).isOfferingDraw)
|
||||
finisher(pov.game, _.Draw, None, Some(_.drawOfferAccepted))
|
||||
else for {
|
||||
p1 ← messenger.systemMessage(g1, _.drawOfferSent) map { es ⇒
|
||||
Progress(g1, Event.ReloadTable(!color) :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _ offerDraw g.turns) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
}
|
||||
}
|
||||
|
||||
case DrawCancel(playerRef) ⇒ publishing(playerRef) {
|
||||
case pov @ Pov(g1, color) ⇒ (pov.player.isOfferingDraw) ?? (for {
|
||||
p1 ← messenger.systemMessage(g1, _.drawOfferCanceled) map { es ⇒
|
||||
Progress(g1, Event.ReloadTable(!color) :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.removeDrawOffer) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events)
|
||||
}
|
||||
|
||||
case DrawDecline(playerRef) ⇒ publishing(playerRef) {
|
||||
case pov @ Pov(g1, color) ⇒ (g1.player(!color).isOfferingDraw) ?? (for {
|
||||
p1 ← messenger.systemMessage(g1, _.drawOfferDeclined) map { es ⇒
|
||||
Progress(g1, Event.ReloadTable(!color) :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(!color, _.removeDrawOffer) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events)
|
||||
}
|
||||
|
||||
case RematchYes(playerRef) ⇒ publishing(playerRef)(rematcher.yes)
|
||||
case RematchNo(playerRef) ⇒ publishing(playerRef)(rematcher.no)
|
||||
|
||||
case TakebackAccept(playerRef) ⇒ publishing(playerRef) { pov ⇒
|
||||
(pov.opponent.isProposingTakeback && pov.game.nonTournament) ?? (for {
|
||||
fen ← GameRepo initialFen pov.game.id
|
||||
pgn ← PgnRepo get pov.game.id
|
||||
res ← takeback(pov.game, pgn, fen)
|
||||
} yield res)
|
||||
}
|
||||
|
||||
case TakebackOffer(playerRef) ⇒ publishing(playerRef) {
|
||||
case pov @ Pov(g1, color) ⇒
|
||||
(g1.playable && g1.bothPlayersHaveMoved && g1.nonTournament) ?? (for {
|
||||
fen ← GameRepo initialFen pov.game.id
|
||||
pgn ← PgnRepo get pov.game.id
|
||||
result ← if (g1.player(!color).isAi)
|
||||
takeback.double(pov.game, pgn, fen)
|
||||
else if (g1.player(!color).isProposingTakeback)
|
||||
takeback(pov.game, pgn, fen)
|
||||
else for {
|
||||
p1 ← messenger.systemMessage(g1, _.takebackPropositionSent) map { es ⇒
|
||||
Progress(g1, Event.ReloadTable(!color) :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.proposeTakeback) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
} yield result)
|
||||
}
|
||||
|
||||
case TakebackCancel(playerRef) ⇒ publishing(playerRef) {
|
||||
case pov @ Pov(g1, color) ⇒
|
||||
(pov.player.isProposingTakeback) ?? (for {
|
||||
p1 ← messenger.systemMessage(g1, _.takebackPropositionCanceled) map { es ⇒
|
||||
Progress(g1, Event.ReloadTable(!color) :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.removeTakebackProposition) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events)
|
||||
}
|
||||
|
||||
case TakebackDecline(playerRef) ⇒ publishing(playerRef) {
|
||||
case pov @ Pov(g1, color) ⇒
|
||||
(g1.player(!color).isProposingTakeback) ?? (for {
|
||||
p1 ← messenger.systemMessage(g1, _.takebackPropositionDeclined) map { es ⇒
|
||||
Progress(g1, Event.ReloadTable(!color) :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(!color, _.removeTakebackProposition) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events)
|
||||
}
|
||||
case TakebackYes(playerRef) ⇒ publishing(playerRef)(takebacker.yes)
|
||||
case TakebackNo(playerRef) ⇒ publishing(playerRef)(takebacker.no)
|
||||
|
||||
case Moretime(playerRef) ⇒ publishing(playerRef) { pov ⇒
|
||||
pov.game.clock.filter(_ ⇒ pov.game.moretimeable) ?? { clock ⇒
|
||||
|
@ -226,7 +137,7 @@ private[round] final class Round(
|
|||
GameRepo save progress2 inject progress2.events
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private def publish(events: List[Event]) {
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
package lila.round
|
||||
|
||||
import lila.game.{ GameRepo, Game, Rewind, Event, Progress }
|
||||
import lila.db.api._
|
||||
|
||||
private[round] final class Takeback(messenger: Messenger) {
|
||||
|
||||
def apply(game: Game, pgn: String, initialFen: Option[String]): Fu[Events] =
|
||||
(Rewind(game, pgn, initialFen) map {
|
||||
case (progress, newPgn) ⇒ savePgn(game.id, newPgn) >> save(progress)
|
||||
}) ||| fail(game)
|
||||
|
||||
def double(game: Game, pgn: String, initialFen: Option[String]): Fu[Events] = {
|
||||
for {
|
||||
first ← Rewind(game, pgn, initialFen)
|
||||
(prog1, pgn1) = first
|
||||
second ← Rewind(prog1.game, pgn1, initialFen) map {
|
||||
case (progress, newPgn) ⇒ (prog1 withGame progress.game, newPgn)
|
||||
}
|
||||
(prog2, pgn2) = second
|
||||
} yield savePgn(game.id, pgn2) >> save(prog2)
|
||||
} ||| fail(game)
|
||||
|
||||
def fail[A](game: Game)(err: Failures) =
|
||||
fufail[A]("Takeback %s".format(game.id) <:: err)
|
||||
|
||||
private def savePgn(gameId: String, pgn: String): Funit = {
|
||||
import lila.game.tube.pgnTube
|
||||
$update.field(gameId, "p", pgn, upsert = true)
|
||||
}
|
||||
|
||||
private def save(p1: Progress): Fu[Events] = {
|
||||
val p2 = p1 + Event.Reload
|
||||
messenger.systemMessage(p1.game, _.takebackPropositionAccepted) >>
|
||||
(GameRepo save p2) inject p2.events
|
||||
}
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package lila.round
|
||||
|
||||
import lila.game.{ GameRepo, Game, PgnRepo, Pov, Rewind, Event, Progress }
|
||||
import lila.db.api._
|
||||
|
||||
private[round] final class Takebacker(messenger: Messenger) {
|
||||
|
||||
def yes(pov: Pov): Fu[Events] = pov match {
|
||||
case Pov(game, _) if pov.opponent.isProposingTakeback ⇒ single(game)
|
||||
case Pov(game, _) if pov.opponent.isAi ⇒ double(game)
|
||||
case Pov(game, color) if (game playerCanProposeTakeback color) ⇒ for {
|
||||
p1 ← messenger.systemMessage(game, _.takebackPropositionSent) map { es ⇒
|
||||
Progress(game, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.proposeTakeback) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case _ ⇒ fufail("[takebacker] invalid yes " + pov)
|
||||
}
|
||||
def no(pov: Pov): Fu[Events] = pov match {
|
||||
case Pov(game, color) if pov.player.isProposingTakeback ⇒ for {
|
||||
p1 ← messenger.systemMessage(game, _.takebackPropositionCanceled) map { es ⇒
|
||||
Progress(game, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(color, _.removeTakebackProposition) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case Pov(game, color) if pov.opponent.isProposingTakeback ⇒ for {
|
||||
p1 ← messenger.systemMessage(game, _.takebackPropositionDeclined) map { es ⇒
|
||||
Progress(game, Event.ReloadTablesOwner :: es)
|
||||
}
|
||||
p2 = p1 map { g ⇒ g.updatePlayer(!color, _.removeTakebackProposition) }
|
||||
_ ← GameRepo save p2
|
||||
} yield p2.events
|
||||
case _ ⇒ fufail("[takebacker] invalid no " + pov)
|
||||
}
|
||||
|
||||
private def extras(gameId: String): Fu[(Option[String], String)] =
|
||||
(GameRepo initialFen gameId) zip (PgnRepo get gameId)
|
||||
|
||||
private def single(game: Game): Fu[Events] = extras(game.id) flatMap {
|
||||
case (fen, pgn) ⇒ Rewind(game, pgn, fen).future flatMap {
|
||||
case (progress, newPgn) ⇒ PgnRepo.save(game.id, newPgn) >> save(progress)
|
||||
}
|
||||
}
|
||||
|
||||
private def double(game: Game): Fu[Events] = extras(game.id) flatMap {
|
||||
case (fen, pgn) ⇒ for {
|
||||
first ← Rewind(game, pgn, fen).future
|
||||
(prog1, pgn1) = first
|
||||
second ← Rewind(prog1.game, pgn1, fen).future map {
|
||||
case (progress, newPgn) ⇒ (prog1 withGame progress.game, newPgn)
|
||||
}
|
||||
(prog2, pgn2) = second
|
||||
_ ← PgnRepo.save(game.id, pgn2)
|
||||
events ← save(prog2)
|
||||
} yield events
|
||||
}
|
||||
|
||||
private def save(p1: Progress): Fu[Events] = {
|
||||
val p2 = p1 + Event.Reload
|
||||
messenger.systemMessage(p1.game, _.takebackPropositionAccepted) >>
|
||||
(GameRepo save p2) inject p2.events
|
||||
}
|
||||
}
|
|
@ -84,17 +84,13 @@ package round {
|
|||
case class ResignColor(color: Color)
|
||||
case class ResignForce(playerId: String)
|
||||
case class DrawClaim(playerId: String)
|
||||
case class DrawAccept(playerId: String)
|
||||
case class DrawOffer(playerId: String)
|
||||
case class DrawCancel(playerId: String)
|
||||
case class DrawDecline(playerId: String)
|
||||
case class DrawYes(playerId: String)
|
||||
case class DrawNo(playerId: String)
|
||||
case object DrawForce
|
||||
case class RematchYes(playerId: String)
|
||||
case class RematchNo(playerId: String)
|
||||
case class TakebackAccept(playerId: String)
|
||||
case class TakebackOffer(playerId: String)
|
||||
case class TakebackCancel(playerId: String)
|
||||
case class TakebackDecline(playerId: String)
|
||||
case class TakebackYes(playerId: String)
|
||||
case class TakebackNo(playerId: String)
|
||||
case class Moretime(playerId: String)
|
||||
case object Outoftime
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue