Split game and round packages
parent
ed97dc7bdb
commit
141c29dc17
|
@ -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]
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -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))
|
||||
}
|
||||
|
|
|
@ -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._
|
||||
|
|
|
@ -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 ⇒
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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._
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
|
@ -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._
|
|
@ -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) {
|
|
@ -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"
|
|
@ -1,5 +1,5 @@
|
|||
package lila
|
||||
package game
|
||||
package round
|
||||
|
||||
import play.api.libs.json._
|
||||
import scalaz.effects._
|
|
@ -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))
|
|
@ -1,5 +1,5 @@
|
|||
package lila
|
||||
package game
|
||||
package round
|
||||
|
||||
import socket.{ Broom, Close, GetNbMembers, GetUsernames, NbMembers }
|
||||
|
|
@ -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)
|
||||
|
|
@ -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) {
|
|
@ -1,5 +1,5 @@
|
|||
package lila
|
||||
package game
|
||||
package round
|
||||
|
||||
import com.novus.salat.annotations.Key
|
||||
import org.apache.commons.lang3.StringEscapeUtils.escapeXml
|
|
@ -1,5 +1,5 @@
|
|||
package lila
|
||||
package game
|
||||
package round
|
||||
|
||||
import com.novus.salat._
|
||||
import com.novus.salat.dao._
|
|
@ -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))
|
||||
}
|
|
@ -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._
|
|
@ -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
|
||||
}
|
|
@ -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
|
|
@ -2,7 +2,7 @@ package lila
|
|||
|
||||
import ornicar.scalalib._
|
||||
|
||||
package object game {
|
||||
package object round {
|
||||
|
||||
type ValidIOEvents = Valid[scalaz.effects.IO[List[Event]]]
|
||||
}
|
Loading…
Reference in New Issue