Stuff. Lot of.
This commit is contained in:
parent
da9d5e79f0
commit
b3ea12c381
|
@ -15,13 +15,13 @@ final class AppXhr(
|
|||
aliveMemo: AliveMemo,
|
||||
moretimeSeconds: Int) {
|
||||
|
||||
type IOValid = IO[Valid[Unit]]
|
||||
type IOValidEvents = IO[Valid[List[Event]]]
|
||||
|
||||
def play(
|
||||
fullId: String,
|
||||
povRef: PovRef,
|
||||
origString: String,
|
||||
destString: String,
|
||||
promString: Option[String] = None): List[Event] = fromPov(fullId) {
|
||||
promString: Option[String] = None): IOValidEvents = fromPov(povRef) {
|
||||
case Pov(g1, color) ⇒ (for {
|
||||
g2 ← (g1.playable).fold(success(g1), failure("Game not playable" wrapNel))
|
||||
orig ← posAt(origString) toValid "Wrong orig " + origString
|
||||
|
@ -51,19 +51,19 @@ final class AppXhr(
|
|||
)
|
||||
}
|
||||
|
||||
def abort(fullId: String): IOValid = attempt(fullId, finisher.abort)
|
||||
def abort(fullId: String): IOValidEvents = attempt(fullId, finisher.abort)
|
||||
|
||||
def resign(fullId: String): IOValid = attempt(fullId, finisher.resign)
|
||||
def resign(fullId: String): IOValidEvents = attempt(fullId, finisher.resign)
|
||||
|
||||
def forceResign(fullId: String): IOValid = attempt(fullId, finisher.forceResign)
|
||||
def forceResign(fullId: String): IOValidEvents = attempt(fullId, finisher.forceResign)
|
||||
|
||||
def outoftime(fullId: String): IOValid = attempt(fullId, finisher outoftime _.game)
|
||||
def outoftime(fullId: String): IOValidEvents = attempt(fullId, finisher outoftime _.game)
|
||||
|
||||
def drawClaim(fullId: String): IOValid = attempt(fullId, finisher.drawClaim)
|
||||
def drawClaim(fullId: String): IOValidEvents = attempt(fullId, finisher.drawClaim)
|
||||
|
||||
def drawAccept(fullId: String): IOValid = attempt(fullId, finisher.drawAccept)
|
||||
def drawAccept(fullId: String): IOValidEvents = attempt(fullId, finisher.drawAccept)
|
||||
|
||||
def drawOffer(fullId: String): IO[Valid[List[Event]]] = attempt(fullId, {
|
||||
def drawOffer(fullId: String): IOValidEvents = attempt(fullId, {
|
||||
case pov @ Pov(g1, color) ⇒
|
||||
if (g1 playerCanOfferDraw color) {
|
||||
if (g1.player(!color).isOfferingDraw) finisher drawAccept pov
|
||||
|
@ -132,5 +132,8 @@ final class AppXhr(
|
|||
private def fromPov[A](fullId: String)(op: Pov ⇒ IO[A]): IO[A] =
|
||||
gameRepo pov fullId flatMap op
|
||||
|
||||
private def fromPov[A](ref: PovRef)(op: Pov ⇒ IO[A]): IO[A] =
|
||||
gameRepo pov ref flatMap op
|
||||
|
||||
private def !!(msg: String) = failure(msg.wrapNel)
|
||||
}
|
||||
|
|
|
@ -16,32 +16,32 @@ final class Finisher(
|
|||
eloCalculator: EloCalculator,
|
||||
finisherLock: FinisherLock) {
|
||||
|
||||
type ValidIO = Valid[IO[Unit]]
|
||||
type ValidIOEvents = Valid[IO[List[Event]]]
|
||||
|
||||
def abort(pov: Pov): ValidIO =
|
||||
def abort(pov: Pov): ValidIOEvents =
|
||||
if (pov.game.abortable) finish(pov.game, Aborted)
|
||||
else !!("game is not abortable")
|
||||
|
||||
def resign(pov: Pov): ValidIO =
|
||||
def resign(pov: Pov): ValidIOEvents =
|
||||
if (pov.game.resignable) finish(pov.game, Resign, Some(!pov.color))
|
||||
else !!("game is not resignable")
|
||||
|
||||
def forceResign(pov: Pov): ValidIO =
|
||||
def forceResign(pov: Pov): ValidIOEvents =
|
||||
if (pov.game.playable && aliveMemo.inactive(pov.game.id, !pov.color))
|
||||
finish(pov.game, Timeout, Some(pov.color))
|
||||
else !!("game is not force-resignable")
|
||||
|
||||
def drawClaim(pov: Pov): ValidIO = pov match {
|
||||
def drawClaim(pov: Pov): ValidIOEvents = pov match {
|
||||
case Pov(game, color) if game.playable && game.player.color == color && game.toChessHistory.threefoldRepetition ⇒ finish(game, Draw)
|
||||
case Pov(game, color) ⇒ !!("game is not threefold repetition")
|
||||
}
|
||||
|
||||
def drawAccept(pov: Pov): ValidIO =
|
||||
def drawAccept(pov: Pov): ValidIOEvents =
|
||||
if (pov.opponent.isOfferingDraw)
|
||||
finish(pov.game, Draw, None, Some("Draw offer accepted"))
|
||||
else !!("opponent is not proposing a draw")
|
||||
|
||||
def outoftime(game: DbGame): ValidIO =
|
||||
def outoftime(game: DbGame): ValidIOEvents =
|
||||
game.outoftimePlayer some { player ⇒
|
||||
finish(game, Outoftime,
|
||||
Some(!player.color) filter game.toChess.board.hasEnoughMaterialToMate)
|
||||
|
@ -49,14 +49,17 @@ final class Finisher(
|
|||
|
||||
def outoftimes(games: List[DbGame]): List[IO[Unit]] =
|
||||
games map { g ⇒
|
||||
outoftime(g).fold(msgs ⇒ putStrLn(g.id + " " + (msgs.list mkString "\n")), identity)
|
||||
outoftime(g).fold(
|
||||
msgs ⇒ putStrLn(g.id + " " + (msgs.list mkString "\n")),
|
||||
_ map (_ ⇒ Unit) // events are lost
|
||||
): IO[Unit]
|
||||
}
|
||||
|
||||
def moveFinish(game: DbGame, color: Color): IO[List[Event]] =
|
||||
(game.status match {
|
||||
case Mate ⇒ finish(game, Mate, Some(color))
|
||||
case status @ (Stalemate | Draw) ⇒ finish(game, status)
|
||||
case _ ⇒ success(io(Nil))
|
||||
case _ ⇒ success(io(Nil)): ValidIOEvents
|
||||
}) | io(Nil)
|
||||
|
||||
private def finish(
|
||||
|
|
|
@ -8,12 +8,11 @@ import scalaz.effects._
|
|||
final class Messenger(roomRepo: RoomRepo) {
|
||||
|
||||
def playerMessage(
|
||||
gameId: String,
|
||||
color: Color,
|
||||
ref: PovRef,
|
||||
message: String): IO[List[Event]] =
|
||||
if (message.size <= 140 && message.nonEmpty)
|
||||
roomRepo.addMessage(gameId, color.name, message) map { _ ⇒
|
||||
List(MessageEvent(color.name, message))
|
||||
roomRepo.addMessage(ref.gameId, ref.color.name, message) map { _ ⇒
|
||||
List(MessageEvent(ref.color.name, message))
|
||||
}
|
||||
else io(Nil)
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@ final class SystemEnv(config: Config) {
|
|||
|
||||
lazy val gameSocket = new game.Socket(
|
||||
gameRepo = gameRepo,
|
||||
xhr = appXhr,
|
||||
hubMemo = gameHubMemo,
|
||||
messenger = messenger)
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package controllers
|
|||
|
||||
import DataForm._
|
||||
import chess.Color
|
||||
import model.{ Event, DbGame }
|
||||
|
||||
import play.api._
|
||||
import mvc._
|
||||
|
@ -11,7 +12,7 @@ import Play.current
|
|||
import libs.json._
|
||||
import libs.iteratee._
|
||||
|
||||
import scalaz.effects.IO
|
||||
import scalaz.effects._
|
||||
|
||||
object AppXhrC extends LilaController {
|
||||
|
||||
|
@ -28,23 +29,25 @@ object AppXhrC extends LilaController {
|
|||
username = get("username")).unsafePerformIO
|
||||
}
|
||||
|
||||
def outoftime(fullId: String) = Action { ValidIOk(xhr outoftime fullId) }
|
||||
def outoftime(fullId: String) = Action {
|
||||
IOk(perform(fullId, xhr.outoftime))
|
||||
}
|
||||
|
||||
def abort(fullId: String) = validAndRedirect(fullId, xhr.abort)
|
||||
def abort(fullId: String) = performAndRedirect(fullId, xhr.abort)
|
||||
|
||||
def resign(fullId: String) = validAndRedirect(fullId, xhr.resign)
|
||||
def resign(fullId: String) = performAndRedirect(fullId, xhr.resign)
|
||||
|
||||
def forceResign(fullId: String) = validAndRedirect(fullId, xhr.forceResign)
|
||||
def forceResign(fullId: String) = performAndRedirect(fullId, xhr.forceResign)
|
||||
|
||||
def drawClaim(fullId: String) = validAndRedirect(fullId, xhr.drawClaim)
|
||||
def drawClaim(fullId: String) = performAndRedirect(fullId, xhr.drawClaim)
|
||||
|
||||
def drawAccept(fullId: String) = validAndRedirect(fullId, xhr.drawAccept)
|
||||
def drawAccept(fullId: String) = performAndRedirect(fullId, xhr.drawAccept)
|
||||
|
||||
def drawOffer(fullId: String) = validAndRedirect(fullId, xhr.drawOffer)
|
||||
def drawOffer(fullId: String) = performAndRedirect(fullId, xhr.drawOffer)
|
||||
|
||||
def drawCancel(fullId: String) = validAndRedirect(fullId, xhr.drawCancel)
|
||||
def drawCancel(fullId: String) = performAndRedirect(fullId, xhr.drawCancel)
|
||||
|
||||
def drawDecline(fullId: String) = validAndRedirect(fullId, xhr.drawDecline)
|
||||
def drawDecline(fullId: String) = performAndRedirect(fullId, xhr.drawDecline)
|
||||
|
||||
def moretime(fullId: String) = Action {
|
||||
(xhr moretime fullId).unsafePerformIO.fold(
|
||||
|
@ -57,8 +60,19 @@ object AppXhrC extends LilaController {
|
|||
|
||||
def nbGames = Action { Ok(env.gameRepo.countPlaying.unsafePerformIO) }
|
||||
|
||||
private def validAndRedirect(fullId: String, f: String ⇒ IO[Valid[Unit]]) =
|
||||
Action {
|
||||
ValidIORedir(f(fullId), fullId)
|
||||
type IOValidEvents = IO[Valid[List[Event]]]
|
||||
|
||||
private def perform(fullId: String, op: String ⇒ IOValidEvents): IO[Unit] =
|
||||
op(fullId) flatMap { res ⇒
|
||||
res.fold(
|
||||
failures ⇒ putStrLn(failures.list mkString "\n"),
|
||||
events ⇒ env.gameSocket.send(DbGame takeGameId fullId, events)
|
||||
)
|
||||
}
|
||||
|
||||
private def performAndRedirect(fullId: String, op: String ⇒ IOValidEvents) =
|
||||
Action {
|
||||
perform(fullId, op).unsafePerformIO
|
||||
Redirect("/" + fullId)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,15 +28,6 @@ trait LilaController extends Controller with ContentTypes with RequestGetter {
|
|||
_ ⇒ Ok("ok")
|
||||
)
|
||||
|
||||
def ValidIORedir(op: IO[Valid[Unit]], url: ⇒ String) =
|
||||
op.unsafePerformIO.fold(
|
||||
failures ⇒ {
|
||||
println(failures.list mkString "\n")
|
||||
Redirect("/" + url)
|
||||
},
|
||||
_ ⇒ Redirect("/" + url)
|
||||
)
|
||||
|
||||
def FormValidIOk[A](form: Form[A])(op: A ⇒ IO[Unit])(implicit request: Request[_]) =
|
||||
form.bindFromRequest.fold(
|
||||
form ⇒ BadRequest(form.errors mkString "\n"),
|
||||
|
|
|
@ -30,6 +30,8 @@ class GameRepo(collection: MongoCollection)
|
|||
else findOneByID(gameId) flatMap decode
|
||||
}
|
||||
|
||||
def pov(ref: PovRef): IO[Pov] = pov(ref.gameId, ref.color)
|
||||
|
||||
def pov(gameId: String, color: Color): IO[Pov] =
|
||||
game(gameId) map { g ⇒ Pov(g, g player color) }
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ import scalaz.effects._
|
|||
|
||||
import chess.Color
|
||||
import db.GameRepo
|
||||
import model.{ DbGame, Pov, Progress }
|
||||
import model.{ DbGame, Pov, PovRef, Progress, Event }
|
||||
|
||||
final class Socket(
|
||||
gameRepo: GameRepo,
|
||||
|
@ -24,26 +24,38 @@ final class Socket(
|
|||
|
||||
implicit val timeout = Timeout(1 second)
|
||||
|
||||
def send(progress: Progress): IO[Unit] = io {
|
||||
(hubMemo get progress.game.id) ! Events(progress.events)
|
||||
implicit def richJsObject(js: JsObject) = new {
|
||||
def str(key: String): Option[String] = js.value get key map (_.as[String])
|
||||
def obj(key: String): Option[JsObject] = js.value get key map (_.as[JsObject])
|
||||
}
|
||||
|
||||
def send(progress: Progress): IO[Unit] =
|
||||
send(progress.game.id, progress.events)
|
||||
|
||||
def send(gameId: String, events: List[Event]): IO[Unit] = io {
|
||||
(hubMemo get gameId) ! Events(events)
|
||||
}
|
||||
|
||||
def listener(
|
||||
hub: ActorRef,
|
||||
member: Member,
|
||||
gameId: String): JsValue ⇒ Unit = member match {
|
||||
povRef: PovRef): JsValue ⇒ Unit = member match {
|
||||
case Watcher(_, _, _) ⇒ (_: JsValue) ⇒ Unit
|
||||
case Owner(_, _, _) ⇒ (e: JsValue) ⇒ (e \ "t").as[String] match {
|
||||
case Owner(_, color, _) ⇒ (e: JsValue) ⇒ (e \ "t").as[String] match {
|
||||
case "talk" ⇒ (e \ "d").as[String] |> { txt ⇒
|
||||
hub ! Events(
|
||||
messenger.playerMessage(gameId, member.color, txt).unsafePerformIO
|
||||
messenger.playerMessage(povRef, txt).unsafePerformIO
|
||||
)
|
||||
}
|
||||
case "move" ⇒ {
|
||||
val orig = (e \ "d" \ "from").as[String]
|
||||
val dest = (e \ "d" \ "to").as[String]
|
||||
xhr.play(fullId, move._1, move._2, move._3).unsafePerformIO
|
||||
}
|
||||
case "move" ⇒ for {
|
||||
d ← e.as[JsObject] obj "d"
|
||||
orig ← d str "from"
|
||||
dest ← d str "to"
|
||||
promotion = d str "promotion"
|
||||
} xhr.play(povRef, orig, dest, promotion).unsafePerformIO.fold(
|
||||
error ⇒ println(error.list mkString "\n"),
|
||||
events ⇒ send(povRef.gameId, events)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -68,7 +80,7 @@ final class Socket(
|
|||
)).asPromise map {
|
||||
case Connected(member) ⇒ (
|
||||
Iteratee.foreach[JsValue](
|
||||
listener(hub, member, gameId)
|
||||
listener(hub, member, PovRef(gameId, member.color))
|
||||
) mapDone { _ ⇒
|
||||
hub ! Quit(uid)
|
||||
},
|
||||
|
|
|
@ -11,6 +11,8 @@ case class Pov(game: DbGame, color: Color) {
|
|||
|
||||
def isPlayerFullId(fullId: Option[String]): Boolean =
|
||||
fullId some { game.isPlayerFullId(player, _) } none false
|
||||
|
||||
def ref = PovRef(game.id, color)
|
||||
}
|
||||
|
||||
object Pov {
|
||||
|
@ -20,3 +22,5 @@ object Pov {
|
|||
def apply(game: DbGame, playerId: String): Option[Pov] =
|
||||
game player playerId map { p ⇒ new Pov(game, p.color) }
|
||||
}
|
||||
|
||||
case class PovRef(gameId: String, color: Color)
|
||||
|
|
|
@ -23,6 +23,8 @@ package object lila
|
|||
|
||||
object Tick // standard actor tick
|
||||
|
||||
type ValidIOEvents = Valid[scalaz.effects.IO[List[model.Event]]]
|
||||
|
||||
// custom salat context
|
||||
implicit val ctx = new Context {
|
||||
val name = "Lila System Context"
|
||||
|
|
Loading…
Reference in a new issue