Implement lobby hook matching

This commit is contained in:
Thibault Duplessis 2012-05-26 01:32:37 +02:00
parent 2ad61ad857
commit 97b92844b3
19 changed files with 185 additions and 68 deletions

View file

@ -48,7 +48,7 @@ final class Preload(
entries entryRepo.recent
} yield Right(Map(
"version" -> history.version,
"pool" -> renderHooks(hooks, myHook).pp,
"pool" -> renderHooks(hooks, myHook),
"chat" -> (messages.reverse map (_.render)),
"timeline" -> (entries.reverse map (_.render))
))

View file

@ -18,6 +18,7 @@ object Lobby extends LilaController {
def preloader = env.preloader
def hookRepo = env.lobby.hookRepo
def fisherman = env.lobby.fisherman
def joiner = env.setup.hookJoiner
val home = Open { implicit ctx
renderHome(none).fold(identity, Ok(_))
@ -35,7 +36,7 @@ object Lobby extends LilaController {
myHook = myHook
).unsafePerformIO.bimap(
url Redirect(url),
preload html.lobby.home(toJson(preload))
preload html.lobby.home(toJson(preload), myHook)
)
def socket = WebSocket.async[JsValue] { implicit req
@ -49,11 +50,22 @@ object Lobby extends LilaController {
}
def hook(ownerId: String) = Open { implicit ctx
hookRepo.ownedHook(ownerId.pp).unsafePerformIO.pp.fold(
hookRepo.ownedHook(ownerId).unsafePerformIO.fold(
hook renderHome(hook.some).fold(identity, Ok(_)),
Redirect(routes.Lobby.home))
}
def join(hookId: String) = Open { implicit ctx
IORedirect {
val myHookId = get("cancel")
joiner(hookId, myHookId)(ctx.me) map { result
result.fold(
_ myHookId.fold(routes.Lobby.hook(_), routes.Lobby.home),
pov routes.Round.player(pov.fullId))
}
}
}
def cancel(ownerId: String) = Open { implicit ctx
IORedirect {
for {
@ -63,8 +75,6 @@ object Lobby extends LilaController {
}
}
def join(hookId: String) = TODO
//def join(gameId: String, color: String) = Action { implicit req
//FormValidIOk[LobbyJoinData](lobbyJoinForm)(join
//api.join(gameId, color, join._1, join._2, join._3, join._4)

View file

@ -20,7 +20,7 @@ object Round extends LilaController {
private val hand = env.round.hand
private val messenger = env.round.messenger
private val rematcher = env.setup.rematcher
private val joiner = env.setup.joiner
private val joiner = env.setup.friendJoiner
def websocketWatcher(gameId: String, color: String) = WebSocket.async[JsValue] { req
implicit val ctx = reqToCtx(req)

View file

@ -38,12 +38,13 @@ final class CoreEnv private (application: Application, val settings: Settings) {
settings = settings,
mongodb = mongodb.apply _,
gameRepo = game.gameRepo,
hookRepo = lobby.hookRepo,
fisherman = lobby.fisherman,
userRepo = user.userRepo,
timelinePush = timeline.push.apply,
roundMessenger = round.messenger,
ai = ai.ai,
dbRef = user.userRepo.dbRef)
userDbRef = user.userRepo.dbRef)
lazy val timeline = new lila.timeline.TimelineEnv(
settings = settings,

View file

@ -28,9 +28,9 @@ object Cron {
}
}
message(1 seconds) {
env.monitor.reporting -> monitor.Update(env)
}
//message(1 seconds) {
//env.monitor.reporting -> monitor.Update(env)
//}
message(1 second) {
env.lobby.hub -> lobby.WithHooks(env.lobby.hookMemo.putAll)
@ -52,28 +52,28 @@ object Cron {
env.lobby.hookRepo.cleanupOld
}
unsafe(3 seconds) {
Future.traverse(hubs) { hub
hub ? socket.GetUsernames mapTo manifest[Iterable[String]]
} map (_.flatten) onSuccess {
case xs (env.user.usernameMemo putAll xs).unsafePerformIO
}
}
//unsafe(3 seconds) {
//Future.traverse(hubs) { hub
//hub ? socket.GetUsernames mapTo manifest[Iterable[String]]
//} map (_.flatten) onSuccess {
//case xs (env.user.usernameMemo putAll xs).unsafePerformIO
//}
//}
effect(4.1 hours) {
env.game.gameRepo.cleanupUnplayed flatMap { _
env.gameCleanNextCommand.apply
}
}
//effect(4.1 hours) {
//env.game.gameRepo.cleanupUnplayed flatMap { _
//env.gameCleanNextCommand.apply
//}
//}
effect(1 hour) {
env.gameFinishCommand.apply
}
//effect(1 hour) {
//env.gameFinishCommand.apply
//}
effect(1 minute) {
env.ai.remoteAi.diagnose
}
env.ai.remoteAi.diagnose.unsafePerformIO
//effect(1 minute) {
//env.ai.remoteAi.diagnose
//}
//env.ai.remoteAi.diagnose.unsafePerformIO
lazy val hubs: List[ActorRef] =
List(env.site.hub, env.lobby.hub, env.round.hubMaster)

View file

@ -15,11 +15,12 @@ object Global extends GlobalSettings {
coreEnv = CoreEnv(app)
//if (env.ai.isServer) println("Running as AI server")
//else core.Cron start env
if (env.ai.isServer) println("Running as AI server")
else core.Cron start env
}
override def onRouteRequest(req: RequestHeader): Option[Handler] = {
println(req)
env.monitor.rpsProvider.countRequest()
env.i18n.requestHandler(req) orElse super.onRouteRequest(req)
}

View file

@ -3,6 +3,8 @@ package elo
case class EloRange(min: Int, max: Int) {
def contains(elo: Int) = elo >= min && elo <= max
override def toString = "%d-%d".format(min, max)
}
@ -11,7 +13,8 @@ object EloRange {
val min = 800
val max = 2200
val default = EloRange(min, max)
val broad = EloRange(min, max)
val default = broad
// ^\d{3,4}\-\d{3,4}$
def apply(from: String): Option[EloRange] = for {
@ -28,5 +31,5 @@ object EloRange {
def valid(from: String) = apply(from).isDefined
private def acceptable(v: Int) = v >= min && v <= max
private def acceptable(elo: Int) = broad contains elo
}

View file

@ -14,7 +14,7 @@ final class I18nRequestHandler(pool: I18nPool) {
else pool.domainLang(req).isDefined.fold(
None,
Action {
Redirect(redirectUrl(req).pp)
Redirect(redirectUrl(req))
} some
)

View file

@ -24,6 +24,7 @@ final class Fisherman(
def bite(hook: Hook, game: DbGame): IO[Unit] = for {
_ socket removeHook hook
_ socket.biteHook(hook, game)
_ hookRepo.setGame(hook, game)
} yield ()
// mark the hook as active, once

View file

@ -1,7 +1,8 @@
package lila
package lobby
import chess.{ Variant, Mode, Color, Clock }
import chess.{ Variant, Mode, Clock }
import setup.Color
import elo.EloRange
import user.User
@ -18,6 +19,7 @@ case class Hook(
increment: Option[Int],
mode: Int,
color: String,
userId: Option[String],
username: String,
elo: Option[Int],
eloRange: String,
@ -25,6 +27,8 @@ case class Hook(
`match`: Boolean = false,
game: Option[DBRef] = None) {
def realColor = Color orDefault color
def gameId: Option[String] = game map (_.getId.toString)
def realVariant = Variant orDefault variant
@ -71,6 +75,7 @@ object Hook {
increment = clock map (_.increment),
mode = mode.id,
color = color,
userId = user map (_.idString),
username = user.fold(_.username, User.anonymous),
elo = user map (_.elo),
eloRange = eloRange.toString,

View file

@ -1,6 +1,8 @@
package lila
package lobby
import game.DbGame
import com.novus.salat._
import com.novus.salat.dao._
import com.mongodb.casbah.MongoCollection
@ -33,8 +35,12 @@ class HookRepo(collection: MongoCollection)
find(query) sort DBObject("createdAt" -> 1) toList
}
def setGame(hook: Hook, game: DbGame) = io {
update(idSelector(hook), $set("match" -> true) ++ $set("gameId" -> game.id))
}
def removeId(id: String): IO[Unit] = io {
remove(DBObject("_id" -> id))
remove(idSelector(id))
}
def removeOwnerId(ownerId: String): IO[Unit] = io {
@ -50,4 +56,7 @@ class HookRepo(collection: MongoCollection)
def cleanupOld: IO[Unit] = io {
remove("createdAt" $lt (DateTime.now - 1.hour))
}
private def idSelector(id: String): DBObject = DBObject("_id" -> id)
private def idSelector(hook: Hook): DBObject = idSelector(hook.id)
}

View file

@ -1,19 +1,18 @@
package lila
package lobby
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 scalaz.effects._
import com.mongodb.casbah.MongoCollection
import com.mongodb.DBRef
import user.UserRepo
import game.GameRepo
import user.{ User, UserRepo }
import game.{ GameRepo, DbGame }
import round.{ Socket RoundSocket, Messenger RoundMessenger }
import ai.Ai
import core.Settings
final class LobbyEnv(
@ -50,14 +49,6 @@ final class LobbyEnv(
collection = mongodb(MongoCollectionMessage),
max = LobbyMessageMax)
//lazy val api = new Api(
//hookRepo = hookRepo,
//fisherman = fisherman,
//gameRepo = gameRepo,
//roundSocket = roundSocket,
//roundMessenger = roundMessenger,
//starter = starter)
lazy val hookRepo = new HookRepo(mongodb(MongoCollectionHook))
lazy val hookMemo = new HookMemo(timeout = MemoHookTimeout)

View file

@ -27,11 +27,13 @@ object Color {
def apply(name: String): Option[Color] = all find (_.name == name)
def orDefault(name: String) = apply(name) | default
val all = List(White, Black, Random)
val names = all map (_.name)
val choices = names zip names
val default = White
val default = Random
}

View file

@ -1,7 +1,7 @@
package lila
package setup
import chess.{ Game, Board, Variant, Mode, Color ChessColor }
import chess.{ Game, Board, Variant, Mode, PausedClock, Color ChessColor }
import elo.EloRange
import game.{ DbGame, DbPlayer }
@ -16,7 +16,13 @@ case class FriendConfig(
def >> = (variant.id, clock, time, increment, mode.id, color.name).some
def game = DbGame(
game = Game(board = Board(pieces = variant.pieces)),
game = Game(
board = Board(pieces = variant.pieces),
clock = clock option PausedClock(
limit = time,
increment = increment
)
),
ai = None,
whitePlayer = DbPlayer.white,
blackPlayer = DbPlayer.black,

View file

@ -10,19 +10,19 @@ import controllers.routes
import com.mongodb.DBRef
import scalaz.effects._
final class Joiner(
final class FriendJoiner(
gameRepo: GameRepo,
messenger: Messenger,
timelinePush: DbGame IO[Unit],
dbRef: User DBRef) {
userDbRef: User DBRef) {
def apply(game: DbGame, user: Option[User]): Valid[IO[(Pov, List[Event])]] =
game.notStarted option {
val color = game.invitedColor
for {
p1 user.fold(
u gameRepo.setUser(game.id, color, dbRef(u), u.elo) map { _
Progress(game, game.updatePlayer(color, _.withUser(u, dbRef(u))))
u gameRepo.setUser(game.id, color, userDbRef(u), u.elo) map { _
Progress(game, game.updatePlayer(color, _.withUser(u, userDbRef(u))))
},
io(Progress(game)))
p2 = p1 withGame game.start

View file

@ -0,0 +1,78 @@
package lila
package setup
import lobby.{ HookRepo, Hook, Fisherman }
import user.{ User, UserRepo }
import chess.{ Game, Board, Variant, Mode, PausedClock, Color ChessColor }
import game.{ GameRepo, DbGame, DbPlayer, Pov }
import round.{ Messenger, Progress }
import scalaz.effects._
import com.mongodb.DBRef
final class HookJoiner(
hookRepo: HookRepo,
fisherman: Fisherman,
gameRepo: GameRepo,
userRepo: UserRepo,
userDbRef: User DBRef,
timelinePush: DbGame IO[Unit],
messenger: Messenger) {
def apply(hookId: String, myHookId: Option[String])(me: Option[User]): IO[Valid[Pov]] =
for {
hookOption hookRepo hook hookId
myHookOption myHookId.fold(hookRepo.ownedHook, io(none))
result hookOption.fold(
hook canJoin(hook, me).fold(
join(hook, myHookOption)(me) map success,
io(!!("Can not join hook"))
),
io(!!("No such hook"))
)
} yield result
private def join(hook: Hook, myHook: Option[Hook])(me: Option[User]): IO[Pov] = for {
_ myHook.fold(fisherman.delete, io())
ownerOption hook.userId.fold(userRepo.user, io(none))
game = blame(
_.invitedColor, me,
blame(_.creatorColor, ownerOption, makeGame(hook))
).start
_ gameRepo insert game
_ game.variant.standard.fold(io(), gameRepo saveInitialFen game)
_ timelinePush(game)
// messenges are not sent to the game socket
// as nobody is there to see them yet
_ messenger init game
_ fisherman.bite(hook, game)
} yield Pov(game, game.invitedColor)
def blame(color: DbGame ChessColor, userOption: Option[User], game: DbGame) =
userOption.fold(
user game.updatePlayer(color(game), _.withUser(user, userDbRef(user))),
game)
def makeGame(hook: Hook) = DbGame(
game = Game(
board = Board(pieces = hook.realVariant.pieces),
clock = hook.hasClock.fold(
hook.time |@| hook.increment apply { (limit, inc)
PausedClock(limit = limit, increment = inc)
},
none)
),
ai = None,
whitePlayer = DbPlayer.white,
blackPlayer = DbPlayer.black,
creatorColor = hook.realColor.resolve,
mode = hook.realMode,
variant = hook.realVariant)
private def canJoin(hook: Hook, me: Option[User]) =
hook.realMode.fold(
true,
me exists { u hook.realEloRange.fold(_ contains u.elo, true) }
)
}

View file

@ -18,7 +18,7 @@ final class Processor(
fisherman: Fisherman,
timelinePush: DbGame IO[Unit],
ai: () Ai,
dbRef: User DBRef) {
userDbRef: User DBRef) {
def ai(config: AiConfig)(implicit ctx: Context): IO[Pov] = for {
_ ctx.me.fold(
@ -27,7 +27,7 @@ final class Processor(
)
pov = config.pov
game = ctx.me.fold(
user pov.game.updatePlayer(pov.color, _.withUser(user, dbRef(user))),
user pov.game.updatePlayer(pov.color, _.withUser(user, userDbRef(user))),
pov.game)
_ gameRepo insert game
_ game.variant.standard.fold(io(), gameRepo saveInitialFen game)
@ -50,7 +50,7 @@ final class Processor(
)
pov = config.pov
game = ctx.me.fold(
user pov.game.updatePlayer(pov.color, _.withUser(user, dbRef(user))),
user pov.game.updatePlayer(pov.color, _.withUser(user, userDbRef(user))),
pov.game)
_ gameRepo insert game
_ game.variant.standard.fold(io(), gameRepo saveInitialFen game)

View file

@ -3,7 +3,7 @@ package setup
import core.Settings
import game.{ DbGame, GameRepo }
import lobby.Fisherman
import lobby.{ HookRepo, Fisherman }
import round.Messenger
import ai.Ai
import user.{ User, UserRepo }
@ -16,12 +16,13 @@ final class SetupEnv(
settings: Settings,
mongodb: String MongoCollection,
gameRepo: GameRepo,
hookRepo: HookRepo,
fisherman: Fisherman,
userRepo: UserRepo,
timelinePush: DbGame IO[Unit],
roundMessenger: Messenger,
ai: () Ai,
dbRef: User DBRef) {
userDbRef: User DBRef) {
import settings._
@ -37,7 +38,7 @@ final class SetupEnv(
fisherman = fisherman,
timelinePush = timelinePush,
ai = ai,
dbRef = dbRef)
userDbRef = userDbRef)
lazy val friendConfigMemo = new FriendConfigMemo(
ttl = SetupFriendConfigMemoTtl)
@ -48,9 +49,18 @@ final class SetupEnv(
messenger = roundMessenger,
timelinePush = timelinePush)
lazy val joiner = new Joiner(
lazy val friendJoiner = new FriendJoiner(
gameRepo = gameRepo,
messenger = roundMessenger,
timelinePush = timelinePush,
dbRef = dbRef)
userDbRef = userDbRef)
lazy val hookJoiner = new HookJoiner(
hookRepo = hookRepo,
fisherman = fisherman,
gameRepo = gameRepo,
userRepo = userRepo,
userDbRef = userDbRef,
timelinePush = timelinePush,
messenger = roundMessenger)
}

View file

@ -1,4 +1,4 @@
@(preload: String, hookId: Option[String] = None)(implicit ctx: Context)
@(preload: String, myHook: Option[lila.lobby.Hook])(implicit ctx: Context)
@chat = {
@ctx.me.map { m =>
@ -25,9 +25,9 @@
@widget.connection()
<div class="hooks_wrap">
<div class="hooks"
data-my-hook="@hookId"
data-my-hook="@myHook.map(_.ownerId)"
data-cancel-url="@routes.Lobby.cancel("000000000000")"
data-join-url="@routes.Lobby.join("000000000000")"
data-join-url="@routes.Lobby.join("00000000")"
>
<table></table>
<textarea class="hooks_preload" style="display: none">@Html(preload)</textarea>