Split game and round packages

pull/1/merge
Thibault Duplessis 2012-05-14 22:36:32 +02:00
parent ed97dc7bdb
commit 141c29dc17
29 changed files with 347 additions and 306 deletions

View File

@ -2,9 +2,9 @@ package lila
import game._
import user._
import game.{ GameRepo, RoomRepo }
import game.GameRepo
import chess.{ Color, White, Black }
import game.{ IsConnectedOnGame, GetGameVersion }
import round.{ IsConnectedOnGame, GetGameVersion, RoomRepo, Progress, Event }
import analyse.GameInfo
import scalaz.effects._
@ -19,8 +19,8 @@ import play.api.Play.current
final class AppApi(
userRepo: UserRepo,
gameRepo: GameRepo,
gameSocket: game.Socket,
messenger: Messenger,
roundSocket: round.Socket,
messenger: round.Messenger,
starter: lobby.Starter,
eloUpdater: EloUpdater,
gameInfo: DbGame IO[GameInfo]) {
@ -61,10 +61,10 @@ final class AppApi(
pov for {
p1 starter.start(pov.game, entryData)
p2 messenger.systemMessages(p1.game, messages) map { evts
p1 + RedirectOwnerEvent(!pov.color, url) ++ evts
p1 + Event.RedirectOwner(!pov.color, url) ++ evts
}
_ gameRepo save p2
_ gameSocket send p2
_ roundSocket send p2
} yield success(),
io(GameNotFound)
)
@ -76,7 +76,7 @@ final class AppApi(
g1 for {
progress starter.start(g1, entryData)
_ gameRepo save progress
_ gameSocket send progress
_ roundSocket send progress
} yield success(Unit),
io { !!("No such game") }
)
@ -96,20 +96,20 @@ final class AppApi(
result (newGameOption |@| g1Option).apply(
(newGame, g1) {
val progress = Progress(g1, List(
RedirectOwnerEvent(White, whiteRedirect),
RedirectOwnerEvent(Black, blackRedirect),
Event.RedirectOwner(White, whiteRedirect),
Event.RedirectOwner(Black, blackRedirect),
// tell spectators to reload the table
ReloadTableEvent(White),
ReloadTableEvent(Black)))
Event.ReloadTable(White),
Event.ReloadTable(Black)))
for {
_ gameRepo save progress
_ gameSocket send progress
_ roundSocket send progress
newProgress starter.start(newGame, entryData)
newProgress2 messenger.systemMessages(
newProgress.game, messageString
) map newProgress.++
_ gameRepo save newProgress2
_ gameSocket send newProgress2
_ roundSocket send newProgress2
} yield success()
}
).fold(identity, io(GameNotFound))
@ -121,10 +121,10 @@ final class AppApi(
g1Option gameRepo game gameId
result g1Option.fold(
g1 {
val progress = Progress(g1, Color.all map ReloadTableEvent)
val progress = Progress(g1, Color.all map Event.ReloadTable)
for {
_ gameRepo save progress
_ gameSocket send progress
_ roundSocket send progress
} yield success()
},
io(GameNotFound)
@ -140,7 +140,7 @@ final class AppApi(
def isConnected(gameId: String, colorName: String): Future[Boolean] =
Color(colorName).fold(
c gameSocket.hubMaster ? IsConnectedOnGame(gameId, c) mapTo manifest[Boolean],
c roundSocket.hubMaster ? IsConnectedOnGame(gameId, c) mapTo manifest[Boolean],
Promise successful false)
def adjust(username: String): IO[Unit] = for {
@ -157,5 +157,5 @@ final class AppApi(
} yield ()
private def futureVersion(gameId: String): Future[Int] =
gameSocket.hubMaster ? GetGameVersion(gameId) mapTo manifest[Int]
roundSocket.hubMaster ? GetGameVersion(gameId) mapTo manifest[Int]
}

View File

@ -1,7 +1,9 @@
package lila
package command
import game._
import game.GameRepo
import round.Finisher
import scalaz.effects._
final class GameFinish(gameRepo: GameRepo, finisher: Finisher) {

View File

@ -4,7 +4,8 @@ import lila._
import http.Context
import DataForm._
import chess.Color
import game.{ Event, DbGame }
import game.DbGame
import round.Event
import play.api._
import mvc._
@ -18,7 +19,7 @@ import scalaz.effects._
object App extends LilaController {
private val hand = env.game.hand
private val hand = env.round.hand
def socket = WebSocket.async[JsValue] { implicit req
implicit val ctx = Context(req, None)
@ -30,7 +31,7 @@ object App extends LilaController {
def gameSocket(gameId: String, color: String) =
WebSocket.async[JsValue] { implicit req
implicit val ctx = Context(req, None)
env.game.socket.join(
env.round.socket.join(
uidOption = get("uid"),
username = get("username"),
gameId = gameId,
@ -77,5 +78,5 @@ object App extends LilaController {
}
private def performEvents(fullId: String)(events: List[Event]): IO[Unit] =
env.game.socket.send(DbGame takeGameId fullId, events)
env.round.socket.send(DbGame takeGameId fullId, events)
}

View File

@ -30,8 +30,8 @@ final class CoreEnv private(application: Application, settings: Settings) {
mongodb = mongodb.apply _,
userRepo = user.userRepo,
gameRepo = game.gameRepo,
gameSocket = game.socket,
gameMessenger = game.messenger,
roundSocket = round.socket,
roundMessenger = round.messenger,
entryRepo = timeline.entryRepo,
ai = ai.ai)
@ -47,9 +47,14 @@ final class CoreEnv private(application: Application, settings: Settings) {
settings = settings)
lazy val game = new lila.game.GameEnv(
settings = settings,
mongodb = mongodb.apply _)
lazy val round = new lila.round.RoundEnv(
app = app,
settings = settings,
mongodb = mongodb.apply _,
gameRepo = game.gameRepo,
userRepo = user.userRepo,
eloUpdater = user.eloUpdater,
ai = ai.ai)
@ -67,8 +72,8 @@ final class CoreEnv private(application: Application, settings: Settings) {
lazy val appApi = new AppApi(
userRepo = user.userRepo,
gameRepo = game.gameRepo,
gameSocket = game.socket,
messenger = game.messenger,
roundSocket = round.socket,
messenger = round.messenger,
starter = lobby.starter,
eloUpdater = user.eloUpdater,
gameInfo = analyse.gameInfo)
@ -88,7 +93,7 @@ final class CoreEnv private(application: Application, settings: Settings) {
lazy val gameFinishCommand = new command.GameFinish(
gameRepo = game.gameRepo,
finisher = game.finisher)
finisher = round.finisher)
lazy val gameCleanNextCommand = new command.GameCleanNext(gameRepo = game.gameRepo)
}

View File

@ -23,7 +23,7 @@ object Cron {
implicit val executor = Akka.system.dispatcher
unsafe(5 seconds) {
(env.site.hub :: env.lobby.hub :: env.game.hubMaster :: Nil) foreach { actor
(env.site.hub :: env.lobby.hub :: env.round.hubMaster :: Nil) foreach { actor
actor ! socket.Broom
}
}
@ -79,7 +79,7 @@ object Cron {
env.ai.remoteAi.diagnose.unsafePerformIO
lazy val hubs: List[ActorRef] =
List(env.site.hub, env.lobby.hub, env.game.hubMaster)
List(env.site.hub, env.lobby.hub, env.round.hubMaster)
def message(freq: Duration)(to: (ActorRef, Any)) {
Akka.system.scheduler.schedule(freq, freq.randomize(), to._1, to._2)

View File

@ -1,11 +1,13 @@
package lila
package game
import round.{ Event, Progress }
import chess.{ History ChessHistory, Board, Move, Pos, Game, Clock, Status, Color, Piece, Variant }
import Color._
import chess.format.{ PgnReader, Fen }
import chess.Pos.piotr
import chess.Role.forsyth
import org.joda.time.DateTime
case class DbGame(
@ -94,7 +96,7 @@ case class DbGame(
val events =
Event.possibleMoves(game.situation, White) ::
Event.possibleMoves(game.situation, Black) ::
StateEvent(game.situation.color, game.turns) ::
Event.State(game.situation.color, game.turns) ::
(Event fromMove move) :::
(Event fromSituation game.situation)
@ -133,12 +135,12 @@ case class DbGame(
)
val finalEvents = events :::
updated.clock.fold(c List(ClockEvent(c)), Nil) ::: {
updated.clock.fold(c List(Event.Clock(c)), Nil) ::: {
(updated.playable && (
abortable != updated.abortable || (Color.all exists { color
playerCanOfferDraw(color) != updated.playerCanOfferDraw(color)
})
)).fold(Color.all map ReloadTableEvent, Nil)
)).fold(Color.all map Event.ReloadTable, Nil)
}
Progress(this, updated, finalEvents)
@ -224,7 +226,7 @@ case class DbGame(
blackPlayer = blackPlayer finish (winner == Some(Black)),
clock = clock map (_.stop)
),
List(EndEvent())
List(Event.End())
)
def rated = isRated

View File

@ -1,173 +0,0 @@
package lila
package game
import play.api.libs.json._
import chess._
import Pos.{ piotr, allPiotrs }
sealed trait Event {
def typ: String
def data: JsValue
def only: Option[Color] = None
def owner: Boolean = false
}
sealed trait EmptyEvent extends Event {
def data = JsNull
}
object Event {
def fromMove(move: Move): List[Event] = MoveEvent(move) :: List(
if (move.enpassant) move.capture map EnpassantEvent.apply else None,
move.promotion map { PromotionEvent(_, move.dest) },
move.castle map {
case (king, rook) CastlingEvent(king, rook, move.color)
}
).flatten
def fromSituation(situation: Situation): List[Event] = List(
if (situation.check) situation.kingPos map CheckEvent.apply else None,
if (situation.threefoldRepetition) Some(ThreefoldEvent()) else None,
Some(PremoveEvent(situation.color))
).flatten
def possibleMoves(situation: Situation, color: Color): Event =
PossibleMovesEvent(
color,
if (color == situation.color) situation.destinations else Map.empty
)
}
case class StartEvent() extends EmptyEvent {
def typ = "start"
}
case class MoveEvent(orig: Pos, dest: Pos, color: Color) extends Event {
def typ = "move"
def data = JsObject(Seq(
"type" -> JsString("move"),
"from" -> JsString(orig.key),
"to" -> JsString(dest.key),
"color" -> JsString(color.name)
))
}
object MoveEvent {
def apply(move: Move): MoveEvent =
MoveEvent(move.orig, move.dest, move.piece.color)
}
case class PossibleMovesEvent(
color: Color,
moves: Map[Pos, List[Pos]]) extends Event {
def typ = "possible_moves"
def data =
if (moves.isEmpty) JsNull
else JsObject(moves map {
case (o, d) o.key -> JsString(d map (_.key) mkString)
} toList)
override def only = Some(color)
}
case class EnpassantEvent(killed: Pos) extends Event {
def typ = "enpassant"
def data = JsString(killed.key)
}
case class CastlingEvent(king: (Pos, Pos), rook: (Pos, Pos), color: Color) extends Event {
def typ = "castling"
def data = JsObject(Seq(
"king" -> jsArray(king._1.key, king._2.key),
"rook" -> jsArray(rook._1.key, rook._2.key),
"color" -> JsString(color.name)
))
def jsArray(a: String, b: String) = JsArray(List(JsString(a), JsString(b)))
}
sealed trait RedirectEvent extends Event {
def url: String
def typ = "redirect"
def data = JsString(url)
}
case class RedirectOwnerEvent(color: Color, url: String) extends RedirectEvent {
override def only = Some(color)
override def owner = true
}
case class ReloadEvent() extends EmptyEvent {
def typ = "reload"
}
case class PromotionEvent(role: PromotableRole, pos: Pos) extends Event {
def typ = "promotion"
def data = JsObject(Seq(
"key" -> JsString(pos.key),
"pieceClass" -> JsString(role.toString.toLowerCase)
))
}
case class CheckEvent(pos: Pos) extends Event {
def typ = "check"
def data = JsString(pos.key)
}
case class MessageEvent(author: String, message: String) extends Event {
def typ = "message"
def data = JsString(Room render (author, message))
override def owner = true
}
case class EndEvent() extends EmptyEvent {
def typ = "end"
}
case class ThreefoldEvent() extends EmptyEvent {
def typ = "threefold_repetition"
}
case class ReloadTableEvent(color: Color) extends Event {
def typ = "reload_table"
def data = JsNull
override def only = Some(color)
}
case class PremoveEvent(color: Color) extends EmptyEvent {
def typ = "premove"
override def only = Some(color)
}
case class ClockEvent(white: Float, black: Float) extends Event {
def typ = "clock"
def data = JsObject(Seq(
"white" -> JsNumber(white),
"black" -> JsNumber(black)
))
}
object ClockEvent {
def apply(clock: Clock): ClockEvent = ClockEvent(
clock remainingTime White,
clock remainingTime Black)
}
case class StateEvent(color: Color, turns: Int) extends Event {
def typ = "state"
def data = JsObject(Seq(
"color" -> JsString(color.name),
"turns" -> JsNumber(turns)
))
}
case class CrowdEvent(
white: Boolean,
black: Boolean,
watchers: Int) extends Event {
def typ = "crowd"
def data = JsObject(Seq(
"white" -> JsBoolean(white),
"black" -> JsBoolean(black),
"watchers" -> JsNumber(watchers)
))
def incWatchers = copy(watchers = watchers + 1)
}

View File

@ -3,71 +3,13 @@ package game
import com.mongodb.casbah.MongoCollection
import akka.actor._
import play.api.libs.concurrent._
import play.api.Application
import play.api.i18n.Lang
import play.api.i18n.MessagesPlugin
import user.{ UserRepo, EloUpdater }
import ai.Ai
import core.Settings
final class GameEnv(
app: Application,
settings: Settings,
mongodb: String MongoCollection,
userRepo: UserRepo,
eloUpdater: EloUpdater,
ai: () => Ai) {
mongodb: String MongoCollection) {
implicit val ctx = app
import settings._
lazy val history = () new History(timeout = GameMessageLifetime)
lazy val hubMaster = Akka.system.actorOf(Props(new HubMaster(
makeHistory = history,
uidTimeout = GameUidTimeout,
hubTimeout = GameHubTimeout,
playerTimeout = GamePlayerTimeout
)), name = ActorGameHubMaster)
lazy val socket = new Socket(
getGame = gameRepo.game,
hand = hand,
hubMaster = hubMaster,
messenger = messenger)
lazy val hand = new Hand(
gameRepo = gameRepo,
messenger = messenger,
ai = ai,
finisher = finisher,
takeback = takeback,
hubMaster = hubMaster,
moretimeSeconds = MoretimeSeconds)
lazy val finisher = new Finisher(
userRepo = userRepo,
gameRepo = gameRepo,
messenger = messenger,
eloUpdater = eloUpdater,
eloCalculator = eloCalculator,
finisherLock = finisherLock)
lazy val eloCalculator = new chess.EloCalculator
lazy val finisherLock = new FinisherLock(timeout = FinisherLockTimeout)
lazy val takeback = new Takeback(
gameRepo = gameRepo,
messenger = messenger)
lazy val messenger = new Messenger(roomRepo = roomRepo)
lazy val gameRepo = new GameRepo(mongodb(MongoCollectionGame))
lazy val roomRepo = new RoomRepo(mongodb(MongoCollectionRoom))
}

View File

@ -5,6 +5,7 @@ import DbGame._
import chess.{ Color, Variant, Status }
import chess.format.Forsyth
import round.Progress
import com.novus.salat._
import com.novus.salat.dao._

View File

@ -1,7 +1,8 @@
package lila
package lobby
import game.{ GameRepo, Socket GameSocket, Messenger GameMessenger }
import game.GameRepo
import round.{ Socket RoundSocket, Messenger RoundMessenger }
import chess.Color
import scalaz.effects._
@ -10,8 +11,8 @@ final class Api(
hookRepo: HookRepo,
fisherman: Fisherman,
gameRepo: GameRepo,
gameSocket: GameSocket,
gameMessenger: GameMessenger,
roundSocket: RoundSocket,
roundMessenger: RoundMessenger,
starter: Starter) {
def cancel(ownerId: String): IO[Unit] = for {
@ -32,9 +33,9 @@ final class Api(
(color, game) {
for {
p1 starter.start(game, entryData)
p2 gameMessenger.systemMessages(game, messageString) map p1.++
p2 roundMessenger.systemMessages(game, messageString) map p1.++
_ gameRepo save p2
_ gameSocket send p2
_ roundSocket send p2
_ hook.fold(h fisherman.bite(h, p2.game), io())
_ myHookOwnerId.fold(
ownerId hookRepo ownedHook ownerId flatMap { myHook

View File

@ -11,7 +11,8 @@ import play.api.i18n.Lang
import play.api.i18n.MessagesPlugin
import user.UserRepo
import game.{ GameRepo, Socket GameSocket, Messenger GameMessenger }
import game.GameRepo
import round.{ Socket RoundSocket, Messenger RoundMessenger }
import timeline.EntryRepo
import ai.Ai
import core.Settings
@ -22,8 +23,8 @@ final class LobbyEnv(
mongodb: String MongoCollection,
userRepo: UserRepo,
gameRepo: GameRepo,
gameSocket: GameSocket,
gameMessenger: GameMessenger,
roundSocket: RoundSocket,
roundMessenger: RoundMessenger,
entryRepo: EntryRepo,
ai: () Ai) {
@ -71,8 +72,8 @@ final class LobbyEnv(
hookRepo = hookRepo,
fisherman = fisherman,
gameRepo = gameRepo,
gameSocket = gameSocket,
gameMessenger = gameMessenger,
roundSocket = roundSocket,
roundMessenger = roundMessenger,
starter = starter)
lazy val hookRepo = new HookRepo(mongodb(MongoCollectionHook))

View File

@ -2,7 +2,8 @@ package lila
package lobby
import timeline.{ EntryRepo, Entry }
import game.{ GameRepo, DbGame, Progress }
import game.{ GameRepo, DbGame }
import round.{ Progress }
import ai.Ai
import scalaz.effects._

View File

@ -2,7 +2,7 @@ package lila
package report
import socket.GetNbMembers
import game.GetNbHubs
import round.GetNbHubs
import akka.actor._
import akka.pattern.{ ask, pipe }
@ -55,8 +55,8 @@ final class Reporting extends Actor {
Future.sequence(List(
(env.site.hub ? GetNbMembers).mapTo[Int],
(env.lobby.hub ? GetNbMembers).mapTo[Int],
(env.game.hubMaster ? GetNbHubs).mapTo[Int],
(env.game.hubMaster ? GetNbMembers).mapTo[Int],
(env.round.hubMaster ? GetNbHubs).mapTo[Int],
(env.round.hubMaster ? GetNbMembers).mapTo[Int],
Future(env.game.gameRepo.countAll.unsafePerformIO),
Future(env.game.gameRepo.countPlaying.unsafePerformIO)
)) onSuccess {

View File

@ -0,0 +1,174 @@
package lila
package round
import play.api.libs.json._
import chess.{ PromotableRole, Pos, Color, Situation, Move ChessMove, Clock ChessClock }
import Pos.{ piotr, allPiotrs }
sealed trait Event {
def typ: String
def data: JsValue
def only: Option[Color] = None
def owner: Boolean = false
}
object Event {
def fromMove(move: ChessMove): List[Event] = Move(move) :: List(
if (move.enpassant) move.capture map Event.Enpassant.apply else None,
move.promotion map { Promotion(_, move.dest) },
move.castle map {
case (king, rook) Castling(king, rook, move.color)
}
).flatten
def fromSituation(situation: Situation): List[Event] = List(
if (situation.check) situation.kingPos map Check.apply else None,
if (situation.threefoldRepetition) Some(Threefold()) else None,
Some(Premove(situation.color))
).flatten
def possibleMoves(situation: Situation, color: Color): Event =
PossibleMoves(
color,
if (color == situation.color) situation.destinations else Map.empty
)
sealed trait Empty extends Event {
def data = JsNull
}
case class Start() extends Empty {
def typ = "start"
}
case class Move(orig: Pos, dest: Pos, color: Color) extends Event {
def typ = "move"
def data = JsObject(Seq(
"type" -> JsString("move"),
"from" -> JsString(orig.key),
"to" -> JsString(dest.key),
"color" -> JsString(color.name)
))
}
object Move {
def apply(move: ChessMove): Move =
Move(move.orig, move.dest, move.piece.color)
}
case class PossibleMoves(
color: Color,
moves: Map[Pos, List[Pos]]) extends Event {
def typ = "possible_moves"
def data =
if (moves.isEmpty) JsNull
else JsObject(moves map {
case (o, d) o.key -> JsString(d map (_.key) mkString)
} toList)
override def only = Some(color)
}
case class Enpassant(killed: Pos) extends Event {
def typ = "enpassant"
def data = JsString(killed.key)
}
case class Castling(king: (Pos, Pos), rook: (Pos, Pos), color: Color) extends Event {
def typ = "castling"
def data = JsObject(Seq(
"king" -> jsArray(king._1.key, king._2.key),
"rook" -> jsArray(rook._1.key, rook._2.key),
"color" -> JsString(color.name)
))
def jsArray(a: String, b: String) = JsArray(List(JsString(a), JsString(b)))
}
sealed trait Redirect extends Event {
def url: String
def typ = "redirect"
def data = JsString(url)
}
case class RedirectOwner(color: Color, url: String) extends Redirect {
override def only = Some(color)
override def owner = true
}
case class Reload() extends Empty {
def typ = "reload"
}
case class Promotion(role: PromotableRole, pos: Pos) extends Event {
def typ = "promotion"
def data = JsObject(Seq(
"key" -> JsString(pos.key),
"pieceClass" -> JsString(role.toString.toLowerCase)
))
}
case class Check(pos: Pos) extends Event {
def typ = "check"
def data = JsString(pos.key)
}
case class Message(author: String, message: String) extends Event {
def typ = "message"
def data = JsString(Room render (author, message))
override def owner = true
}
case class End() extends Empty {
def typ = "end"
}
case class Threefold() extends Empty {
def typ = "threefold_repetition"
}
case class ReloadTable(color: Color) extends Event {
def typ = "reload_table"
def data = JsNull
override def only = Some(color)
}
case class Premove(color: Color) extends Empty {
def typ = "premove"
override def only = Some(color)
}
case class Clock(white: Float, black: Float) extends Event {
def typ = "clock"
def data = JsObject(Seq(
"white" -> JsNumber(white),
"black" -> JsNumber(black)
))
}
object Clock {
def apply(clock: ChessClock): Clock = Clock(
clock remainingTime Color.White,
clock remainingTime Color.Black)
}
case class State(color: Color, turns: Int) extends Event {
def typ = "state"
def data = JsObject(Seq(
"color" -> JsString(color.name),
"turns" -> JsNumber(turns)
))
}
case class Crowd(
white: Boolean,
black: Boolean,
watchers: Int) extends Event {
def typ = "crowd"
def data = JsObject(Seq(
"white" -> JsBoolean(white),
"black" -> JsBoolean(black),
"watchers" -> JsNumber(watchers)
))
def incWatchers = copy(watchers = watchers + 1)
}
}

View File

@ -1,6 +1,7 @@
package lila
package game
package round
import game.{ GameRepo, DbGame, Pov }
import user.{ UserRepo, EloUpdater }
import chess.{ EloCalculator, Status, Color }
import Status._

View File

@ -1,7 +1,9 @@
package lila
package game
package round
import game.DbGame
import memo.BooleanExpiryMemo
import scalaz.effects._
final class FinisherLock(timeout: Int) extends BooleanExpiryMemo(timeout) {

View File

@ -1,7 +1,8 @@
package lila
package game
package round
import ai.Ai
import game.{ GameRepo, Pov, PovRef }
import chess.Role
import chess.Pos.posAt
@ -66,7 +67,7 @@ final class Hand(
(povOption toValid "No such game" flatMap { pov
implicit val timeout = Timeout(100 millis)
Await.result(
hubMaster ? game.IsGone(pov.game.id, !pov.color) map {
hubMaster ? round.IsGone(pov.game.id, !pov.color) map {
case true finisher resignForce pov
case _ !!("Opponent is not gone")
},
@ -88,7 +89,7 @@ final class Hand(
else success {
for {
p1 messenger.systemMessages(g1, "Draw offer sent") map { es
Progress(g1, ReloadTableEvent(!color) :: es)
Progress(g1, Event.ReloadTable(!color) :: es)
}
p2 = p1 map { g g.updatePlayer(color, _ offerDraw g.turns) }
_ gameRepo save p2
@ -103,7 +104,7 @@ final class Hand(
if (pov.player.isOfferingDraw) success {
for {
p1 messenger.systemMessages(g1, "Draw offer canceled") map { es
Progress(g1, ReloadTableEvent(!color) :: es)
Progress(g1, Event.ReloadTable(!color) :: es)
}
p2 = p1 map { g g.updatePlayer(color, _.removeDrawOffer) }
_ gameRepo save p2
@ -117,7 +118,7 @@ final class Hand(
if (g1.player(!color).isOfferingDraw) success {
for {
p1 messenger.systemMessages(g1, "Draw offer declined") map { es
Progress(g1, ReloadTableEvent(!color) :: es)
Progress(g1, Event.ReloadTable(!color) :: es)
}
p2 = p1 map { g g.updatePlayer(!color, _.removeDrawOffer) }
_ gameRepo save p2
@ -146,7 +147,7 @@ final class Hand(
takeback(pov.game, fen).sequence
else for {
p1 messenger.systemMessages(g1, "Takeback proposition sent") map { es
Progress(g1, ReloadTableEvent(!color) :: es)
Progress(g1, Event.ReloadTable(!color) :: es)
}
p2 = p1 map { g g.updatePlayer(color, _.proposeTakeback) }
_ gameRepo save p2
@ -163,7 +164,7 @@ final class Hand(
if (pov.player.isProposingTakeback) success {
for {
p1 messenger.systemMessages(g1, "Takeback proposition canceled") map { es
Progress(g1, ReloadTableEvent(!color) :: es)
Progress(g1, Event.ReloadTable(!color) :: es)
}
p2 = p1 map { g g.updatePlayer(color, _.removeTakebackProposition) }
_ gameRepo save p2
@ -177,7 +178,7 @@ final class Hand(
if (g1.player(!color).isProposingTakeback) success {
for {
p1 messenger.systemMessages(g1, "Takeback proposition declined") map { es
Progress(g1, ReloadTableEvent(!color) :: es)
Progress(g1, Event.ReloadTable(!color) :: es)
}
p2 = p1 map { g g.updatePlayer(!color, _.removeTakebackProposition) }
_ gameRepo save p2
@ -195,7 +196,7 @@ final class Hand(
events messenger.systemMessage(
progress.game, "%s + %d seconds".format(color, moretimeSeconds)
)
progress2 = progress ++ (ClockEvent(newClock) :: events)
progress2 = progress ++ (Event.Clock(newClock) :: events)
_ gameRepo save progress2
} yield progress2.events
} toValid "cannot add moretime"

View File

@ -1,5 +1,5 @@
package lila
package game
package round
import play.api.libs.json._
import scalaz.effects._

View File

@ -1,8 +1,9 @@
package lila
package game
package round
import socket._
import chess.{ Color, White, Black }
import game.PovRef
import akka.actor._
import akka.util.duration._
@ -81,7 +82,7 @@ final class Hub(
}
}
def crowdEvent = CrowdEvent(
def crowdEvent = Event.Crowd(
white = ownerOf(White).isDefined,
black = ownerOf(Black).isDefined,
watchers = members.values count (_.watcher))

View File

@ -1,5 +1,5 @@
package lila
package game
package round
import socket.{ Broom, Close, GetNbMembers, GetUsernames, NbMembers }

View File

@ -1,7 +1,10 @@
package lila
package game
package round
import game.{ DbGame, PovRef }
import chess.Color
import Event.Message
import scalaz.effects._
final class Messenger(roomRepo: RoomRepo) {
@ -11,7 +14,7 @@ final class Messenger(roomRepo: RoomRepo) {
message: String): IO[List[Event]] =
if (message.size <= 140 && message.nonEmpty)
roomRepo.addMessage(ref.gameId, ref.color.name, message) map { _
List(MessageEvent(ref.color.name, message))
List(Message(ref.color.name, message))
}
else io(Nil)
@ -19,7 +22,7 @@ final class Messenger(roomRepo: RoomRepo) {
if (game.invited.isHuman) {
val messages = (encodedMessages split '$').toList
roomRepo.addSystemMessages(game.id, messages) map { _
messages map { MessageEvent("system", _) }
messages map { Message("system", _) }
}
}
else io(Nil)
@ -27,7 +30,7 @@ final class Messenger(roomRepo: RoomRepo) {
def systemMessage(game: DbGame, message: String): IO[List[Event]] =
if (game.invited.isHuman)
roomRepo.addSystemMessage(game.id, message) map { _
List(MessageEvent("system", message))
List(Message("system", message))
}
else io(Nil)

View File

@ -1,5 +1,7 @@
package lila
package game
package round
import game.DbGame
// events are kept in insertion/addition order
case class Progress(origin: DbGame, game: DbGame, events: List[Event] = Nil) {

View File

@ -1,5 +1,5 @@
package lila
package game
package round
import com.novus.salat.annotations.Key
import org.apache.commons.lang3.StringEscapeUtils.escapeXml

View File

@ -1,5 +1,5 @@
package lila
package game
package round
import com.novus.salat._
import com.novus.salat.dao._

View File

@ -0,0 +1,71 @@
package lila
package round
import com.mongodb.casbah.MongoCollection
import akka.actor._
import play.api.libs.concurrent._
import play.api.Application
import game.{ GameRepo }
import user.{ UserRepo, EloUpdater }
import ai.Ai
import core.Settings
final class RoundEnv(
app: Application,
settings: Settings,
mongodb: String MongoCollection,
gameRepo: GameRepo,
userRepo: UserRepo,
eloUpdater: EloUpdater,
ai: () => Ai) {
implicit val ctx = app
import settings._
lazy val history = () new History(timeout = GameMessageLifetime)
lazy val hubMaster = Akka.system.actorOf(Props(new HubMaster(
makeHistory = history,
uidTimeout = GameUidTimeout,
hubTimeout = GameHubTimeout,
playerTimeout = GamePlayerTimeout
)), name = ActorGameHubMaster)
lazy val socket = new Socket(
getGame = gameRepo.game,
hand = hand,
hubMaster = hubMaster,
messenger = messenger)
lazy val hand = new Hand(
gameRepo = gameRepo,
messenger = messenger,
ai = ai,
finisher = finisher,
takeback = takeback,
hubMaster = hubMaster,
moretimeSeconds = MoretimeSeconds)
lazy val finisher = new Finisher(
userRepo = userRepo,
gameRepo = gameRepo,
messenger = messenger,
eloUpdater = eloUpdater,
eloCalculator = eloCalculator,
finisherLock = finisherLock)
lazy val eloCalculator = new chess.EloCalculator
lazy val finisherLock = new FinisherLock(timeout = FinisherLockTimeout)
lazy val takeback = new Takeback(
gameRepo = gameRepo,
messenger = messenger)
lazy val messenger = new Messenger(roomRepo = roomRepo)
lazy val roomRepo = new RoomRepo(mongodb(MongoCollectionRoom))
}

View File

@ -1,5 +1,5 @@
package lila
package game
package round
import akka.actor._
import akka.pattern.ask
@ -13,6 +13,7 @@ import play.api.Play.current
import scalaz.effects._
import game.{ DbGame, PovRef }
import chess.Color
import socket.{ Util, Ping, Quit }
import implicits.RichJs._

View File

@ -1,6 +1,7 @@
package lila
package game
package round
import game.{ GameRepo, DbGame }
import scalaz.effects._
final class Takeback(
@ -24,7 +25,7 @@ final class Takeback(
private def save(p1: Progress): IO[List[Event]] = for {
_ messenger.systemMessage(p1.game, "Takeback proposition accepted")
p2 = p1 + ReloadEvent()
p2 = p1 + Event.Reload()
_ gameRepo save p2
} yield p2.events
}

View File

@ -1,8 +1,9 @@
package lila
package game
package round
import chess.Color
import socket.SocketMember
import game.PovRef
import akka.actor.ActorRef
import scalaz.effects.IO

View File

@ -2,7 +2,7 @@ package lila
import ornicar.scalalib._
package object game {
package object round {
type ValidIOEvents = Valid[scalaz.effects.IO[List[Event]]]
}