round app compiles

pull/83/head
Thibault Duplessis 2013-05-07 20:44:26 -03:00
parent d2cd87f940
commit f9bcb6afbf
43 changed files with 502 additions and 484 deletions

View File

@ -25,6 +25,9 @@ private[controllers] trait LilaController
protected implicit val LilaPlainResultZero = new Zero[PlainResult] {
val zero = Results.NotFound
}
protected implicit val LilaHtmlZero = new Zero[Html] {
val zero = Html("")
}
protected implicit final class LilaPimpedResult(result: Result) {
def fuccess = scala.concurrent.Future successful result
@ -99,15 +102,16 @@ private[controllers] trait LilaController
// protected def NoEngine[A <: Result](a: A)(implicit ctx: Context): Result =
// ctx.me.fold(false)(_.engine).fold(Forbidden(views.html.site.noEngine()), a)
protected def JsonOk[A: Writes](fua: Fu[A]) = fua map { a =>
protected def JsonOk[A: Writes](fua: Fu[A]) = fua map { a
Ok(Json toJson a) as JSON
}
// protected def JsFuk(js: Fu[String], headers: (String, String)*) =
// JsOk(js.unsafePerformFu, headers: _*)
protected def JsonOptionOk[A: Writes](fua: Fu[Option[A]])(implicit ctx: Context) = fua flatMap {
_.fold(notFound(ctx))(a fuccess(Ok(Json toJson a) as JSON))
}
// protected def JsOk(js: String, headers: (String, String)*) =
// Ok(js) as JAVASCRIPT withHeaders (headers: _*)
protected def JsOk(fua: Fu[String], headers: (String, String)*) =
fua map { a Ok(a) as JAVASCRIPT withHeaders (headers: _*) }
// protected def ValidOk(valid: Valid[Unit]): Result = valid.fold(
// e BadRequest(e.shows),

View File

@ -1,11 +1,14 @@
package controllers
import lila.api._
import lila.user.Context
import play.api.mvc._, Results._
import lila.app._
import views._
object Notification extends LilaController {
def remove(id: String) = TODO
private def api = Env.notification.api
def remove(id: String) = Auth { implicit ctx
me
Ok(api.remove(me.id, id)).fuccess
}
}

View File

@ -1,13 +1,171 @@
package controllers
import lila.api._
import lila.user.Context
import lila.app._
import views._
import lila.user.{ Context, UserRepo }
import lila.game.{ Pov, GameRepo, Game GameModel }
import lila.round.{ RoomRepo, Room }
import lila.round.actorApi.GetGameVersion
import lila.tournament.{ Tournament Tourney }
import makeTimeout.large
import play.api.mvc._, Results._
import akka.pattern.ask
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.iteratee._
import play.api.templates.Html
object Round extends LilaController {
object Round extends LilaController with TheftPrevention with RoundEventPerformer {
def watcher(gameId: String, color: String) = TODO
private def env = Env.round
// private def rematcher = Env.setup.rematcher
private def bookmarkApi = Env.bookmark.api
private def analyser = Env.analyse.analyser
// private def tournamentRepo = Env.tournament.repo
private def gameJs = Env.game.gameJs
def player(fullId: String) = TODO
def websocketWatcher(gameId: String, color: String) = WebSocket.async[JsValue] { req
reqToCtx(req) flatMap { implicit ctx
env.socketHandler.joinWatcher(
gameId,
color,
~getInt("version"),
~get("sri"),
ctx)
}
}
def websocketPlayer(fullId: String) = WebSocket.async[JsValue] { req
reqToCtx(req) flatMap { implicit ctx
env.socketHandler.joinPlayer(
fullId,
~getInt("version"),
~get("sri"),
~get("tk2"),
ctx)
}
}
def signedJs(gameId: String) = Open { implicit ctx
JsOk(GameRepo token gameId map gameJs.sign, CACHE_CONTROL -> "max-age=3600")
}
def player(fullId: String) = Open { implicit ctx
OptionFuResult(GameRepo pov fullId) { pov
pov.game.started.fold(
for {
// TODO zip that shit
roomHtml pov.game.hasChat optionFu {
RoomRepo room pov.gameId map { room
html.round.room(html.round.roomInner(room.decodedMessages), false)
}
}
v version(pov.gameId)
bookmarkers bookmarkApi userIdsByGame pov.game
engine pov.opponent.userId.zmap(UserRepo.isEngine)
analysed analyser has pov.gameId
tour = none[Tourney]
// TODO tournamentRepo byId pov.game.tournamentId
} yield PreventTheft(pov) {
Ok(html.round.player(
pov,
v,
engine,
roomHtml,
bookmarkers,
analysed,
tour = tour))
},
Ok("TODO").fuccess
// TODO Redirect(routes.Setup.await(fullId)).fuccess
)
}
}
def watcher(gameId: String, color: String) = Open { implicit ctx
OptionFuResult(GameRepo.pov(gameId, color)) { pov
pov.game.started.fold(watch _, join _)(pov)
}
}
private def join(pov: Pov)(implicit ctx: Context): Fu[Result] =
GameRepo initialFen pov.gameId zip version(pov.gameId) map {
case (fen, version) Ok("TODO") // TODO
// Ok(html.setup.join(
// pov, version, env.setup.friendConfigMemo get pov.game.id, fen
// ))
}
private def watch(pov: Pov)(implicit ctx: Context): Fu[Result] =
bookmarkApi userIdsByGame pov.game zip
version(pov.gameId) zip
(RoomRepo room pov.gameId map { room
html.round.room(html.round.roomInner(room.decodedMessages), true)
}) zip
(analyser has pov.gameId) zip
fuccess(none[Tourney]) map {
// TODO (tournamentRepo byId pov.game.tournamentId) map {
case ((((bookmarkers, v), roomHtml), analysed), tour)
Ok(html.round.watcher(
pov, v, roomHtml, bookmarkers, analysed, tour))
}
private def hand = env.hand
def abort(fullId: String) = performAndRedirect(fullId, hand.abort)
def resign(fullId: String) = performAndRedirect(fullId, hand.resign)
def resignForce(fullId: String) = performAndRedirect(fullId, hand.resignForce)
def drawClaim(fullId: String) = performAndRedirect(fullId, hand.drawClaim)
def drawAccept(fullId: String) = performAndRedirect(fullId, hand.drawAccept)
def drawOffer(fullId: String) = performAndRedirect(fullId, hand.drawOffer)
def drawCancel(fullId: String) = performAndRedirect(fullId, hand.drawCancel)
def drawDecline(fullId: String) = performAndRedirect(fullId, hand.drawDecline)
def rematch(fullId: String) = TODO
// Action {
// rematcher offerOrAccept fullId flatMap { validResult
// validResult.fold(
// err putFailures(err) map { _
// Redirect(routes.Round.player(fullId))
// }, {
// case (nextFullId, events) performEvents(fullId)(events) map { _
// Redirect(routes.Round.player(nextFullId))
// }
// }
// )
// }
// }
def rematchCancel(fullId: String) = performAndRedirect(fullId, hand.rematchCancel)
def rematchDecline(fullId: String) = performAndRedirect(fullId, hand.rematchDecline)
def takebackAccept(fullId: String) = performAndRedirect(fullId, hand.takebackAccept)
def takebackOffer(fullId: String) = performAndRedirect(fullId, hand.takebackOffer)
def takebackCancel(fullId: String) = performAndRedirect(fullId, hand.takebackCancel)
def takebackDecline(fullId: String) = performAndRedirect(fullId, hand.takebackDecline)
def tableWatcher(gameId: String, color: String) = Open { implicit ctx
OptionOk(GameRepo.pov(gameId, color)) { html.round.table.watch(_) }
}
def tablePlayer(fullId: String) = Open { implicit ctx
OptionFuOk(GameRepo pov fullId) { pov
fuccess(none[Tourney]) map { tour
// TODO tournamentRepo byId pov.game.tournamentId map { tour
pov.game.playable.fold(
html.round.table.playing(pov),
html.round.table.end(pov, tour))
}
}
}
def players(gameId: String) = Open { implicit ctx
import templating.Environment.playerLink
JsonOptionOk(GameRepo game gameId map2 { (game: GameModel)
(game.players collect {
case player if player.isHuman player.color.name -> playerLink(player).body
} toMap) ++ ctx.me.zmap(me Map("me" -> me.usernameWithElo))
})
}
private def version(gameId: String): Fu[Int] =
env.socketHub ? GetGameVersion(gameId) mapTo manifest[Int]
}

View File

@ -0,0 +1,32 @@
package controllers
import lila.app._
import lila.game.Event
import lila.round.actorApi.GameEvents
import lila.game.Game.takeGameId
import play.api.mvc._, Results._
trait RoundEventPerformer {
protected type FuValidEvents = Fu[Valid[List[Event]]]
protected def performAndRedirect(fullId: String, op: String FuValidEvents) =
Action {
perform(fullId, op)
Redirect(routes.Round.player(fullId))
}
protected def perform(fullId: String, op: String FuValidEvents) {
op(fullId) foreach { validEvents
validEvents.fold(
err logwarn("[round] fail to perform on game %s\n%s".format(fullId, err)),
performEvents(fullId)
)
}
}
protected def performEvents(fullId: String)(events: List[Event]) {
Env.round.socketHub ! GameEvents(takeGameId(fullId), events)
}
}

View File

@ -4,14 +4,12 @@ import lila.app._
import views._
import play.api.mvc._
import scalaz.effects._
object Bookmark extends LilaController {
private def api = env.bookmark.api
private def gameRepo = env.game.gameRepo
private def api = Env.bookmark.api
def toggle(gameId: String) = Auth { implicit ctx
me IOk(api.toggle(gameId, me.id))
me api.toggle(gameId, me.id)
}
}

View File

@ -2,18 +2,18 @@ package controllers
import lila.app._
import views._
import http.Context
import game.{ DbGame, Pov }
import security.Granter
import lila.user.Context
import lila.game.Pov
import lila.security.Granter
import play.api.mvc._
import play.api.mvc.Results.Redirect
trait TheftPrevention {
private[controllers] trait TheftPrevention {
protected def PreventTheft(pov: Pov)(ok: Result)(implicit ctx: Context): Result =
isTheft(pov).fold(Redirect(routes.Round.watcher(pov.gameId, pov.color.name)), ok)
protected def isTheft(pov: Pov)(implicit ctx: Context) =
pov.player.userId != ctx.userId && !Granter.superAdmin(ctx.me)
pov.player.userId != ctx.userId && !(ctx.me zmap Granter.superAdmin)
}

View File

@ -0,0 +1,172 @@
package controllers
import lila.app._
import views._
import lila.game.Pov
// import lila.tournament.{ Created, Started, Finished }
import lila.tournament.{ Tournament Tourney }
import lila.user.Context
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import play.api.templates.Html
object Tournament extends LilaController {
// private def repo = env.tournament.repo
// private def forms = env.tournament.forms
// private def api = env.tournament.api
// private def socket = env.tournament.socket
// private def messenger = env.tournament.messenger
// private def userRepo = env.user.userRepo
// private def gameRepo = env.game.gameRepo
val home = TODO
// Open { implicit ctx
// Async {
// futureTournaments zip userRepo.sortedByToints(10).toFuture map {
// case (((created, started), finished), leaderboard)
// Ok(html.tournament.home(created, started, finished, leaderboard))
// }
// }
// }
// val faq = Open { implicit ctx Ok(html.tournament.faqPage()) }
// val homeReload = Open { implicit ctx
// Async {
// futureTournaments map {
// case ((created, started), finished)
// Ok(html.tournament.homeInner(created, started, finished))
// }
// }
// }
// private def futureTournaments =
// repo.created.toFuture zip repo.started.toFuture zip repo.finished(20).toFuture
def show(id: String) = TODO
// Open { implicit ctx
// IOResult(for {
// t repo byId id
// res t match {
// case Some(tour: Created) showCreated(tour) map { Ok(_) }
// case Some(tour: Started) showStarted(tour) map { Ok(_) }
// case Some(tour: Finished) showFinished(tour) map { Ok(_) }
// case _ io(NotFound(html.tournament.notFound()))
// }
// } yield res)
// }
// private def showCreated(tour: Created)(implicit ctx: Context) = for {
// roomHtml messenger render tour
// } yield html.tournament.show.created(
// tour = tour,
// roomHtml = Html(roomHtml),
// version = version(tour.id))
// private def showStarted(tour: Started)(implicit ctx: Context) = for {
// roomHtml messenger render tour
// games gameRepo games (tour recentGameIds 4)
// pov tour.userCurrentPov(ctx.me).fold(io(none[Pov]))(gameRepo.pov)
// } yield html.tournament.show.started(
// tour = tour,
// roomHtml = Html(roomHtml),
// version = version(tour.id),
// games = games,
// pov = pov)
// private def showFinished(tour: Finished)(implicit ctx: Context) = for {
// roomHtml messenger render tour
// games gameRepo games (tour recentGameIds 4)
// } yield html.tournament.show.finished(
// tour = tour,
// roomHtml = Html(roomHtml),
// version = version(tour.id),
// games = games)
// def join(id: String) = AuthBody { implicit ctx
// implicit me
// NoEngine {
// IOptionIORedirect(repo createdById id) { tour
// api.join(tour, me).fold(
// err putStrLn(err.shows) map (_ routes.Tournament.home()),
// res res map (_ routes.Tournament.show(tour.id))
// )
// }
// }
// }
// def withdraw(id: String) = Auth { implicit ctx
// implicit me
// IOptionIORedirect(repo byId id) { tour
// api.withdraw(tour, me.id) inject routes.Tournament.show(tour.id)
// }
// }
// def earlyStart(id: String) = Auth { implicit ctx
// implicit me
// IOptionIORedirect(repo.createdByIdAndCreator(id, me.id)) { tour
// ~api.earlyStart(tour) inject routes.Tournament.show(tour.id)
// }
// }
// def reload(id: String) = Open { implicit ctx
// IOptionIOk(repo byId id) {
// case tour: Created reloadCreated(tour)
// case tour: Started reloadStarted(tour)
// case tour: Finished reloadFinished(tour)
// }
// }
// private def reloadCreated(tour: Created)(implicit ctx: Context) = io {
// val inner = html.tournament.show.createdInner(tour)
// html.tournament.show.inner(none)(inner)
// }
// private def reloadStarted(tour: Started)(implicit ctx: Context) = for {
// games gameRepo games (tour recentGameIds 4)
// pov tour.userCurrentPov(ctx.me).fold(io(none[Pov]))(gameRepo.pov)
// } yield {
// val pairings = html.tournament.pairings(tour)
// val inner = html.tournament.show.startedInner(tour, games, pov)
// html.tournament.show.inner(pairings.some)(inner)
// }
// private def reloadFinished(tour: Finished)(implicit ctx: Context) =
// gameRepo games (tour recentGameIds 4) map { games
// val pairings = html.tournament.pairings(tour)
// val inner = html.tournament.show.finishedInner(tour, games)
// html.tournament.show.inner(pairings.some)(inner)
// }
// def form = Auth { implicit ctx
// me
// NoEngine {
// Ok(html.tournament.form(forms.create, forms))
// }
// }
// def create = AuthBody { implicit ctx
// implicit me
// NoEngine {
// IOResult {
// implicit val req = ctx.body
// forms.create.bindFromRequest.fold(
// err io(BadRequest(html.tournament.form(err, forms))),
// setup api.createTournament(setup, me) map { tour
// Redirect(routes.Tournament.show(tour.id))
// })
// }
// }
// }
// def websocket(id: String) = WebSocket.async[JsValue] { req
// implicit val ctx = reqToCtx(req)
// socket.join(id, getInt("version"), get("sri"), ctx.me).unsafePerformIO
// }
// private def version(tournamentId: String): Int = socket blockingVersion tournamentId
}

View File

@ -10,6 +10,10 @@ import scala.math.{ min, max, round }
trait RoundHelper {
def moretimeSeconds = roundEnv.moretimeSeconds
def gameAnimationDelay = roundEnv.animationDelay
def roundPlayerJsData(pov: Pov, version: Int) = {
import pov._
Json.obj(

View File

@ -1,4 +1,4 @@
@(game: DbGame)(implicit ctx: Context)
@(game: Game)(implicit ctx: Context)
@if(isGranted(Permission.ViewBlurs)) {
@game.players.map { p =>

View File

@ -1,4 +1,4 @@
@(game: DbGame)(implicit ctx: Context)
@(game: Game)(implicit ctx: Context)
<a class="continue_from_here" title="@trans.continueFromHere()">@game.fenString</a>
<span class="opponent_choice">

View File

@ -1,4 +1,4 @@
@(pov: Pov, version: Int, engine: Boolean, roomHtml: Option[Html], bookmarkers: List[String], analysed: Boolean, tour: Option[lila.app.tournament.Tournament])(implicit ctx: Context)
@(pov: Pov, version: Int, engine: Boolean, roomHtml: Option[Html], bookmarkers: List[String], analysed: Boolean, tour: Option[lila.tournament.Tournament])(implicit ctx: Context)
@import pov._
@ -21,8 +21,8 @@ signedJs = routes.Round.signedJs(pov.gameId).toString.some) {
data-table-url="@routes.Round.tablePlayer(fullId)"
data-players-url="@routes.Round.players(gameId)">
<div class="lichess_board_wrap">
@widget.connection()
<div class="lichess_board with_marks">@Html(ui.Board.render(pov))</div>
@site.connection()
<div class="lichess_board with_marks">@Html(lila.app.ui.Board.render(pov))</div>
<div id="premove">@trans.premoveEnabledClickAnywhereToCancel()</div>
</div>
<div class="lichess_ground">
@ -61,10 +61,14 @@ signedJs = routes.Round.signedJs(pov.gameId).toString.some) {
<a href="@routes.Analyse.replay(gameId, color.name)">@trans.replayAndAnalyse()</a>
@if(game.pgnImport.isEmpty) {
<br />
@* TODO
<a href="@routes.Analyse.stats(gameId)">@trans.viewGameStats()</a>
*@
}
<br />
@* TODO
<a class="view_pgn_toggle" href="@routes.Analyse.pgn(game.id)">View PGN</a>
*@
</nav>
}
}

View File

@ -0,0 +1,5 @@
@(inner: Html, spectator: Boolean)(implicit ctx: Context)
@base.chatRoom(
title = spectator.fold(trans.spectatorRoom, trans.chatRoom).str(),
cssClass = spectator.fold("spectator", "kings"))(inner)

View File

@ -0,0 +1,7 @@
@(messages: List[(String, String)])(implicit ctx: Context)
@messages.map {
case (author, text) => {
<li class="@author@{ (author == "system").??(" trans_me") }">@text</li>
}
}

View File

@ -1,4 +1,4 @@
@(pov: Pov, tour: Option[lila.app.tournament.Tournament])(implicit ctx: Context)
@(pov: Pov, tour: Option[lila.tournament.Tournament])(implicit ctx: Context)
@import pov._

View File

@ -1,6 +1,6 @@
@(game: DbGame)(implicit ctx: Context)
@(game: Game)(implicit ctx: Context)
@winner(player: DbPlayer) = {
@winner(player: Player) = {
<div class="lichess_player @player.color">
<div class="lichess_piece king @player.color"></div>
<p>

View File

@ -22,7 +22,9 @@
@game.next.map { next =>
<a class="button" href="@routes.Round.watcher(next, opponent.color.name)">@trans.viewRematch()</a>
}
@* TODO
@game.tournamentId.map { tourId =>
<a class="button" href="@routes.Tournament.show(tourId)">@trans.viewTournament()</a>
}
*@
</div>

View File

@ -1,4 +1,4 @@
@(player: DbPlayer, engine: Boolean = false)(implicit ctx: Context)
@(player: Player, engine: Boolean = false)(implicit ctx: Context)
@ai(level: Int) = {
<div class="username connected">

View File

@ -1,4 +1,4 @@
@(pov: Pov, version: Int, roomHtml: Html, bookmarkers: List[String], analysed: Boolean, tour: Option[lila.app.tournament.Tournament])(implicit ctx: Context)
@(pov: Pov, version: Int, roomHtml: Html, bookmarkers: List[String], analysed: Boolean, tour: Option[lila.tournament.Tournament])(implicit ctx: Context)
@import pov._
@ -20,8 +20,8 @@ underchat = underchat.some) {
data-table-url="@routes.Round.tableWatcher(gameId, color.name)"
data-players-url="@routes.Round.players(gameId)">
<div class="lichess_board_wrap">
@widget.connection()
<div class="lichess_board with_marks">@Html(ui.Board.render(pov))</div>
@site.connection()
<div class="lichess_board with_marks">@Html(lila.app.ui.Board.render(pov))</div>
<div id="dont_touch">@trans.youAreViewingThisGameAsASpectator()</div>
</div>
<div class="lichess_ground">
@ -59,10 +59,14 @@ underchat = underchat.some) {
<a href="@routes.Round.watcher(gameId, (!color).name)">@trans.flipBoard()</a>
@if(game.pgnImport.isEmpty) {
<br />
@* TODO
<a href="@routes.Analyse.stats(gameId)">@trans.viewGameStats()</a>
*@
}
<br />
@* TODO
<a class="view_pgn_toggle" href="@routes.Analyse.pgn(game.id)">View PGN</a>
*@
</nav>
}
}

View File

@ -38,34 +38,34 @@ GET /games/search controllers.Search.form(page: Int ?= 1)
GET /$gameId<[\w\-]{8}> controllers.Round.watcher(gameId: String, color: String = "white")
GET /$gameId<[\w\-]{8}>/$color<white|black> controllers.Round.watcher(gameId: String, color: String)
GET /$fullId<[\w\-]{12}> controllers.Round.player(fullId: String)
# GET /$gameId<[\w\-]{8}>/$color<white|black>/socket controllers.Round.websocketWatcher(gameId: String, color: String)
# GET /$gameId<[\w\-]{8}>/s.js controllers.Round.signedJs(gameId: String)
# GET /$fullId<[\w\-]{12}>/socket controllers.Round.websocketPlayer(fullId: String)
# GET /$fullId<[\w\-]{12}>/abort controllers.Round.abort(fullId: String)
# 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}>/rematch controllers.Round.rematch(fullId: String)
# GET /$fullId<[\w\-]{12}>/rematch/cancel controllers.Round.rematchCancel(fullId: String)
# GET /$fullId<[\w\-]{12}>/rematch/decline controllers.Round.rematchDecline(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)
#
GET /$gameId<[\w\-]{8}>/$color<white|black>/socket controllers.Round.websocketWatcher(gameId: String, color: String)
GET /$gameId<[\w\-]{8}>/s.js controllers.Round.signedJs(gameId: String)
GET /$fullId<[\w\-]{12}>/socket controllers.Round.websocketPlayer(fullId: String)
GET /$fullId<[\w\-]{12}>/abort controllers.Round.abort(fullId: String)
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}>/rematch controllers.Round.rematch(fullId: String)
GET /$fullId<[\w\-]{12}>/rematch/cancel controllers.Round.rematchCancel(fullId: String)
GET /$fullId<[\w\-]{12}>/rematch/decline controllers.Round.rematchDecline(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)
# Tournament
# GET /tournament controllers.Tournament.home
# GET /tournament/reload controllers.Tournament.homeReload
# GET /tournament/new controllers.Tournament.form
# POST /tournament/new controllers.Tournament.create
# GET /tournament/$id<[\w\-]{8}> controllers.Tournament.show(id: String)
GET /tournament/$id<[\w\-]{8}> controllers.Tournament.show(id: String)
# GET /tournament/$id<[\w\-]{8}>/socket controllers.Tournament.websocket(id: String)
# POST /tournament/$id<[\w\-]{8}>/join controllers.Tournament.join(id: String)
# POST /tournament/$id<[\w\-]{8}>/withdraw controllers.Tournament.withdraw(id: String)
@ -152,7 +152,7 @@ GET /wiki/:slug controllers.Wiki.show(slug: String)
# GET /lobby/log controllers.Lobby.log
#
# Bookmark
# POST /bookmark/$gameId<[\w\-]{8}> controllers.Bookmark.toggle(gameId: String)
POST /bookmark/$gameId<[\w\-]{8}> controllers.Bookmark.toggle(gameId: String)
# Forum
GET /forum controllers.ForumCateg.index

View File

@ -127,6 +127,12 @@ trait WithPlay extends Zeros { self: PackageObject ⇒
def doUnless(cond: Boolean): Fu[A] = doIf(!cond)
}
implicit final class LilaPimpedBooleanForFuture(b: Boolean) {
def optionFu[A](v: Fu[A]): Fu[Option[A]] =
if (b) v map (_.some) else fuccess(none)
}
object makeTimeout {
import akka.util.Timeout

View File

@ -393,35 +393,35 @@ object Game {
def takeGameId(fullId: String) = fullId take gameIdSize
def make(
game: ChessGame,
whitePlayer: Player,
blackPlayer: Player,
ai: Option[(Color, Int)],
creatorColor: Color,
mode: Mode,
variant: Variant,
source: Source,
pgnImport: Option[PgnImport]): Game = Game(
id = IdGenerator.game,
token = IdGenerator.token,
whitePlayer = whitePlayer withEncodedPieces game.allPieces,
blackPlayer = blackPlayer withEncodedPieces game.allPieces,
status = Status.Created,
turns = game.turns,
clock = game.clock,
lastMove = None,
check = None,
creatorColor = creatorColor,
positionHashes = "",
castles = "KQkq",
mode = mode,
variant = variant,
lastMoveTime = None,
metadata = Metadata(
source = source,
pgnImport = pgnImport).some,
createdAt = DateTime.now)
def make(
game: ChessGame,
whitePlayer: Player,
blackPlayer: Player,
ai: Option[(Color, Int)],
creatorColor: Color,
mode: Mode,
variant: Variant,
source: Source,
pgnImport: Option[PgnImport]): Game = Game(
id = IdGenerator.game,
token = IdGenerator.token,
whitePlayer = whitePlayer withEncodedPieces game.allPieces,
blackPlayer = blackPlayer withEncodedPieces game.allPieces,
status = Status.Created,
turns = game.turns,
clock = game.clock,
lastMove = None,
check = None,
creatorColor = creatorColor,
positionHashes = "",
castles = "KQkq",
mode = mode,
variant = variant,
lastMoveTime = None,
metadata = Metadata(
source = source,
pgnImport = pgnImport).some,
createdAt = DateTime.now)
import lila.db.Tube
import play.api.libs.json._

View File

@ -1,6 +1,6 @@
package lila.game
final class GameJs(path: String, useCache: Boolean) {
private[game] final class GameJs(path: String, useCache: Boolean) {
def unsigned: String = useCache.fold(cached, readFromSource)

View File

@ -22,6 +22,8 @@ object GameRepo {
import Game._
import Game.ShortFields._
def game(gameId: ID): Fu[Option[Game]] = $find byId gameId
def player(gameId: ID, color: Color): Fu[Option[Player]] =
$find byId gameId map2 { (game: Game) game player color }

View File

@ -8,7 +8,7 @@ import play.api.templates.Html
import akka.actor.ActorRef
import akka.pattern.ask
final class Api(socketHub: ActorRef, renderer: ActorRef) {
private[notification] final class Api(socketHub: ActorRef, renderer: ActorRef) {
private val repo = mutable.Map[String, List[Notification]]()
import makeTimeout.large

View File

@ -70,7 +70,8 @@ final class Env(
lazy val messenger = new Messenger(i18nKeys)
val animationDelay = AnimationDelay
def animationDelay = AnimationDelay
def moretimeSeconds = Moretime.toSeconds
{
import scala.concurrent.duration._

View File

@ -65,15 +65,6 @@ final class Messenger(i18nKeys: I18nKeys) {
}
}
// def render(game: Game): Fu[Option[String]] =
// game.hasChat.fold(render(game.id) map (_.some), io(None))
// def render(roomId: String): Fu[String] =
// roomRepo room roomId map (_.render)
// def renderWatcher(game: Game): Fu[String] =
// watcherRoomRepo room game.id map (_.render)
private def cleanupText(text: String) = {
val cleanedUp = text.trim.replace(""""""", "'")
(cleanedUp.size <= 140 && cleanedUp.nonEmpty) option cleanedUp

View File

@ -2,8 +2,7 @@ package lila.round
case class Room(id: String, messages: List[String]) {
// def render: String =
// messages map ((Room.render _) compose Room.decode) mkString ""
def decodedMessages = messages map Room.decode
def rematchCopy(id: String, nb: Int) = copy(
id = id,

View File

@ -3,7 +3,7 @@ package lila.round
import lila.db.api._
import tube.roomTube
private[round] object RoomRepo {
object RoomRepo {
def room(id: String): Fu[Room] = $find byId id map (_ | Room(id, Nil))

View File

@ -37,7 +37,7 @@ private[round] final class Socket(
}
withMember(uid) { member
history ? GetEventsSince(v) foreach {
case MaybeEvents(events) events.fold(resync(member))(batch(member, _))
case maybeEvents maybeEvents.fold(resync(member))(batch(member, _))
}
}
}

View File

@ -60,7 +60,6 @@ case class GameEvents(gameId: String, events: List[Event])
case class GetGameVersion(gameId: String)
case object GetVersion
case class GetEventsSince(version: Int)
case class MaybeEvents(events: Option[List[VersionedEvent]])
case class AddEvent(event: Event)
case object ClockSync
case class IsConnectedOnGame(gameId: String, color: Color)

View File

@ -1,3 +1,6 @@
package lila.tournament
case class Tournament(id: String)
case class Tournament(id: String) {
def isRunning = false
}

View File

@ -1,14 +0,0 @@
package controllers
import lila.app._
import views._
object Notification extends LilaController {
private def api = env.notificationApi
def remove(id: String) = Auth { implicit ctx
me
Ok(api.remove(me.id, id))
}
}

View File

@ -1,153 +0,0 @@
package controllers
import lila.app._
import views._
import http.Context
import game.{ DbGame, Pov }
import round.Event
import socket.Util.connectionFail
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.iteratee._
import play.api.templates.Html
import scalaz.effects._
object Round extends LilaController with TheftPrevention with RoundEventPerformer {
private def gameRepo = env.game.gameRepo
private def socket = env.round.socket
private def hand = env.round.hand
private def messenger = env.round.messenger
private def rematcher = env.setup.rematcher
private def bookmarkApi = env.bookmark.api
private def userRepo = env.user.userRepo
private def analyser = env.analyse.analyser
private def tournamentRepo = env.tournament.repo
private def gameJs = env.game.gameJs
def websocketWatcher(gameId: String, color: String) = WebSocket.async[JsValue] { req
implicit val ctx = reqToCtx(req)
socket.joinWatcher(
gameId,
color,
getInt("version"),
get("sri"),
ctx).unsafePerformIO
}
def websocketPlayer(fullId: String) = WebSocket.async[JsValue] { req
implicit val ctx = reqToCtx(req)
socket.joinPlayer(
fullId,
getInt("version"),
get("sri"),
get("tk2"),
ctx).unsafePerformIO
}
def signedJs(gameId: String) = Open { implicit ctx
JsIOk(gameRepo token gameId map gameJs.sign, CACHE_CONTROL -> "max-age=3600")
}
def player(fullId: String) = Open { implicit ctx
IOptionIOResult(gameRepo pov fullId) { pov
pov.game.started.fold(
for {
roomHtml messenger render pov.game
bookmarkers bookmarkApi userIdsByGame pov.game
engine pov.opponent.userId.fold(io(false))(userRepo.isEngine)
analysed analyser has pov.gameId
tour tournamentRepo byId pov.game.tournamentId
} yield PreventTheft(pov) {
Ok(html.round.player(
pov,
version(pov.gameId),
engine,
roomHtml map Html.apply,
bookmarkers,
analysed,
tour = tour))
},
io(Redirect(routes.Setup.await(fullId)))
)
}
}
def watcher(gameId: String, color: String) = Open { implicit ctx
IOptionIOResult(gameRepo.pov(gameId, color)) { pov
pov.game.started.fold(watch _, join _)(pov)
}
}
private def join(pov: Pov)(implicit ctx: Context): IO[Result] =
gameRepo initialFen pov.gameId map { initialFen
Ok(html.setup.join(
pov, version(pov.gameId), env.setup.friendConfigMemo get pov.game.id, initialFen
))
}
private def watch(pov: Pov)(implicit ctx: Context): IO[Result] = for {
bookmarkers bookmarkApi userIdsByGame pov.game
roomHtml messenger renderWatcher pov.game
analysed analyser has pov.gameId
tour tournamentRepo byId pov.game.tournamentId
} yield Ok(html.round.watcher(pov, version(pov.gameId), Html(roomHtml), bookmarkers, analysed, tour))
def abort(fullId: String) = performAndRedirect(fullId, hand.abort)
def resign(fullId: String) = performAndRedirect(fullId, hand.resign)
def resignForce(fullId: String) = performAndRedirect(fullId, hand.resignForce)
def drawClaim(fullId: String) = performAndRedirect(fullId, hand.drawClaim)
def drawAccept(fullId: String) = performAndRedirect(fullId, hand.drawAccept)
def drawOffer(fullId: String) = performAndRedirect(fullId, hand.drawOffer)
def drawCancel(fullId: String) = performAndRedirect(fullId, hand.drawCancel)
def drawDecline(fullId: String) = performAndRedirect(fullId, hand.drawDecline)
def rematch(fullId: String) = Action {
rematcher offerOrAccept fullId flatMap { validResult
validResult.fold(
err putFailures(err) map { _
Redirect(routes.Round.player(fullId))
}, {
case (nextFullId, events) performEvents(fullId)(events) map { _
Redirect(routes.Round.player(nextFullId))
}
}
)
} unsafePerformIO
}
def rematchCancel(fullId: String) = performAndRedirect(fullId, hand.rematchCancel)
def rematchDecline(fullId: String) = performAndRedirect(fullId, hand.rematchDecline)
def takebackAccept(fullId: String) = performAndRedirect(fullId, hand.takebackAccept)
def takebackOffer(fullId: String) = performAndRedirect(fullId, hand.takebackOffer)
def takebackCancel(fullId: String) = performAndRedirect(fullId, hand.takebackCancel)
def takebackDecline(fullId: String) = performAndRedirect(fullId, hand.takebackDecline)
def tableWatcher(gameId: String, color: String) = Open { implicit ctx
IOptionOk(gameRepo.pov(gameId, color)) { html.round.table.watch(_) }
}
def tablePlayer(fullId: String) = Open { implicit ctx
IOptionIOk(gameRepo pov fullId) { pov
tournamentRepo byId pov.game.tournamentId map { tour
pov.game.playable.fold(
html.round.table.playing(pov),
html.round.table.end(pov, tour))
}
}
}
def players(gameId: String) = Open { implicit ctx
import templating.Environment.playerLink
JsonIOk(gameRepo game gameId map { gameOption
~(gameOption map { game
(game.players collect {
case player if player.isHuman player.color.name -> playerLink(player).body
} toMap) ++ ~ctx.me.map(me Map("me" -> me.usernameWithElo))
})
})
}
private def version(gameId: String): Int = socket blockingVersion gameId
}

View File

@ -1,32 +0,0 @@
package controllers
import lila.app._
import core.CoreEnv
import round.Event
import game.{ DbGame, Pov }
import play.api.mvc._
import play.api.mvc.Results._
import scalaz.effects._
trait RoundEventPerformer {
protected def env: CoreEnv
protected type IOValidEvents = IO[Valid[List[Event]]]
protected def performAndRedirect(fullId: String, op: String IOValidEvents) =
Action {
perform(fullId, op).unsafePerformIO
Redirect(routes.Round.player(fullId))
}
protected def perform(fullId: String, op: String IOValidEvents): IO[Unit] =
op(fullId) flatMap { validEvents
validEvents.fold(_ => io(), performEvents(fullId))
}
protected def performEvents(fullId: String)(events: List[Event]): IO[Unit] = io {
env.round.socket.send(DbGame takeGameId fullId, events)
}
}

View File

@ -1,172 +0,0 @@
package controllers
import lila.app._
import views._
import game.Pov
import tournament.{ Created, Started, Finished }
import http.Context
import lila.common.Futuristic.ioToFuture
import scalaz.effects._
import play.api.mvc._
import play.api.libs.json._
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import play.api.templates.Html
import play.api.libs.concurrent.Execution.Implicits._
object Tournament extends LilaController {
private def repo = env.tournament.repo
private def forms = env.tournament.forms
private def api = env.tournament.api
private def socket = env.tournament.socket
private def messenger = env.tournament.messenger
private def userRepo = env.user.userRepo
private def gameRepo = env.game.gameRepo
val home = Open { implicit ctx
Async {
futureTournaments zip userRepo.sortedByToints(10).toFuture map {
case (((created, started), finished), leaderboard)
Ok(html.tournament.home(created, started, finished, leaderboard))
}
}
}
val faq = Open { implicit ctx Ok(html.tournament.faqPage()) }
val homeReload = Open { implicit ctx
Async {
futureTournaments map {
case ((created, started), finished)
Ok(html.tournament.homeInner(created, started, finished))
}
}
}
private def futureTournaments =
repo.created.toFuture zip repo.started.toFuture zip repo.finished(20).toFuture
def show(id: String) = Open { implicit ctx
IOResult(for {
t repo byId id
res t match {
case Some(tour: Created) showCreated(tour) map { Ok(_) }
case Some(tour: Started) showStarted(tour) map { Ok(_) }
case Some(tour: Finished) showFinished(tour) map { Ok(_) }
case _ io(NotFound(html.tournament.notFound()))
}
} yield res)
}
private def showCreated(tour: Created)(implicit ctx: Context) = for {
roomHtml messenger render tour
} yield html.tournament.show.created(
tour = tour,
roomHtml = Html(roomHtml),
version = version(tour.id))
private def showStarted(tour: Started)(implicit ctx: Context) = for {
roomHtml messenger render tour
games gameRepo games (tour recentGameIds 4)
pov tour.userCurrentPov(ctx.me).fold(io(none[Pov]))(gameRepo.pov)
} yield html.tournament.show.started(
tour = tour,
roomHtml = Html(roomHtml),
version = version(tour.id),
games = games,
pov = pov)
private def showFinished(tour: Finished)(implicit ctx: Context) = for {
roomHtml messenger render tour
games gameRepo games (tour recentGameIds 4)
} yield html.tournament.show.finished(
tour = tour,
roomHtml = Html(roomHtml),
version = version(tour.id),
games = games)
def join(id: String) = AuthBody { implicit ctx
implicit me
NoEngine {
IOptionIORedirect(repo createdById id) { tour
api.join(tour, me).fold(
err putStrLn(err.shows) map (_ routes.Tournament.home()),
res res map (_ routes.Tournament.show(tour.id))
)
}
}
}
def withdraw(id: String) = Auth { implicit ctx
implicit me
IOptionIORedirect(repo byId id) { tour
api.withdraw(tour, me.id) inject routes.Tournament.show(tour.id)
}
}
def earlyStart(id: String) = Auth { implicit ctx
implicit me
IOptionIORedirect(repo.createdByIdAndCreator(id, me.id)) { tour
~api.earlyStart(tour) inject routes.Tournament.show(tour.id)
}
}
def reload(id: String) = Open { implicit ctx
IOptionIOk(repo byId id) {
case tour: Created reloadCreated(tour)
case tour: Started reloadStarted(tour)
case tour: Finished reloadFinished(tour)
}
}
private def reloadCreated(tour: Created)(implicit ctx: Context) = io {
val inner = html.tournament.show.createdInner(tour)
html.tournament.show.inner(none)(inner)
}
private def reloadStarted(tour: Started)(implicit ctx: Context) = for {
games gameRepo games (tour recentGameIds 4)
pov tour.userCurrentPov(ctx.me).fold(io(none[Pov]))(gameRepo.pov)
} yield {
val pairings = html.tournament.pairings(tour)
val inner = html.tournament.show.startedInner(tour, games, pov)
html.tournament.show.inner(pairings.some)(inner)
}
private def reloadFinished(tour: Finished)(implicit ctx: Context) =
gameRepo games (tour recentGameIds 4) map { games
val pairings = html.tournament.pairings(tour)
val inner = html.tournament.show.finishedInner(tour, games)
html.tournament.show.inner(pairings.some)(inner)
}
def form = Auth { implicit ctx
me
NoEngine {
Ok(html.tournament.form(forms.create, forms))
}
}
def create = AuthBody { implicit ctx
implicit me
NoEngine {
IOResult {
implicit val req = ctx.body
forms.create.bindFromRequest.fold(
err io(BadRequest(html.tournament.form(err, forms))),
setup api.createTournament(setup, me) map { tour
Redirect(routes.Tournament.show(tour.id))
})
}
}
}
def websocket(id: String) = WebSocket.async[JsValue] { req
implicit val ctx = reqToCtx(req)
socket.join(id, getInt("version"), get("sri"), ctx.me).unsafePerformIO
}
private def version(tournamentId: String): Int = socket blockingVersion tournamentId
}

View File

@ -1,5 +0,0 @@
@(roomHtml: Html, spectator: Boolean)(implicit ctx: Context)
@base.chatRoom(
title = spectator.fold(trans.spectatorRoom, trans.chatRoom).str(),
cssClass = spectator.fold("spectator", "kings"))(roomHtml)