Lobby hook creation
parent
c8aba03734
commit
81da8c6b52
|
@ -19,49 +19,47 @@ final class Preload(
|
|||
def apply(
|
||||
auth: Boolean,
|
||||
chat: Boolean,
|
||||
myHookId: Option[String]): IO[Response] = for {
|
||||
myHook: Option[Hook]): IO[Response] = for {
|
||||
hooks ← auth.fold(hookRepo.allOpen, hookRepo.allOpenCasual)
|
||||
std = () ⇒ stdResponse(chat, hooks, myHookId)
|
||||
res ← myHookId.fold(
|
||||
id ⇒ hookRepo ownedHook id flatMap { hookResponse(_, std) },
|
||||
std()
|
||||
)
|
||||
std = () ⇒ stdResponse(chat, hooks, myHook)
|
||||
res ← myHook.fold(
|
||||
h ⇒ hookResponse(h, std),
|
||||
std())
|
||||
} yield res
|
||||
|
||||
private def hookResponse(
|
||||
myHook: Option[Hook],
|
||||
std: () ⇒ IO[Response]): IO[Response] = myHook.fold(
|
||||
h ⇒ h.gameId.fold(
|
||||
myHook: Hook,
|
||||
std: () ⇒ IO[Response]): IO[Response] =
|
||||
myHook.gameId.fold(
|
||||
ref ⇒ gameRepo game ref map { game ⇒
|
||||
game.fold(
|
||||
g ⇒ redirect(g fullIdOf g.creatorColor),
|
||||
redirect()
|
||||
)
|
||||
},
|
||||
fisherman shake h flatMap { _ ⇒ std() }
|
||||
),
|
||||
io(redirect())
|
||||
)
|
||||
fisherman shake myHook flatMap { _ ⇒ std() }
|
||||
)
|
||||
|
||||
private def stdResponse(
|
||||
chat: Boolean,
|
||||
hooks: List[Hook],
|
||||
myHookId: Option[String]): IO[Response] = for {
|
||||
myHook: Option[Hook]): IO[Response] = for {
|
||||
messages ← if (chat) messageRepo.recent else io(Nil)
|
||||
entries ← entryRepo.recent
|
||||
} yield Right(Map(
|
||||
"version" -> history.version,
|
||||
"pool" -> renderHooks(hooks, myHookId),
|
||||
"pool" -> renderHooks(hooks, myHook).pp,
|
||||
"chat" -> (messages.reverse map (_.render)),
|
||||
"timeline" -> (entries.reverse map (_.render))
|
||||
))
|
||||
|
||||
private def renderHooks(
|
||||
hooks: List[Hook],
|
||||
myHookId: Option[String]) = hooks map { h ⇒
|
||||
if (myHookId == Some(h.ownerId)) h.render ++ Map("ownerId" -> h.ownerId)
|
||||
else h.render
|
||||
myHook: Option[Hook]) = hooks map { h ⇒
|
||||
myHook.exists(_.id == h.id).fold(
|
||||
h.render ++ Map("ownerId" -> h.ownerId),
|
||||
h.render)
|
||||
}
|
||||
|
||||
private def redirect(url: String = "" ) = Left("/" + url)
|
||||
private def redirect(url: String = "") = Left("/" + url)
|
||||
}
|
||||
|
|
|
@ -110,6 +110,9 @@ trait LilaController
|
|||
def IOk[A](op: IO[A])(implicit writer: Writeable[A], ctype: ContentTypeOf[A]) =
|
||||
Ok(op.unsafePerformIO)
|
||||
|
||||
def IOResult[A](op: IO[Result]) =
|
||||
op.unsafePerformIO
|
||||
|
||||
def IORedirect(op: IO[Call]) = Redirect(op.unsafePerformIO)
|
||||
|
||||
def IOptionOk[A, B](ioa: IO[Option[A]])(op: A ⇒ B)(
|
||||
|
|
|
@ -2,6 +2,7 @@ package controllers
|
|||
|
||||
import lila._
|
||||
import http.Context
|
||||
import lobby.Hook
|
||||
import views._
|
||||
|
||||
import play.api._
|
||||
|
@ -13,22 +14,23 @@ import play.api.libs.iteratee._
|
|||
|
||||
object Lobby extends LilaController {
|
||||
|
||||
private val preloader = env.preloader
|
||||
def preloader = env.preloader
|
||||
def hookRepo = env.lobby.hookRepo
|
||||
|
||||
val home = Open { implicit ctx ⇒
|
||||
renderHome(ctx).fold(identity, Ok(_))
|
||||
renderHome(none).fold(identity, Ok(_))
|
||||
}
|
||||
|
||||
def handleNotFound(req: RequestHeader): Result =
|
||||
handleNotFound(reqToCtx(req))
|
||||
|
||||
def handleNotFound(ctx: Context): Result =
|
||||
renderHome(ctx).fold(identity, NotFound(_))
|
||||
def handleNotFound(implicit ctx: Context): Result =
|
||||
renderHome(none).fold(identity, NotFound(_))
|
||||
|
||||
private def renderHome(implicit ctx: Context) = preloader(
|
||||
private def renderHome(myHook: Option[Hook])(implicit ctx: Context) = preloader(
|
||||
auth = ctx.isAuth,
|
||||
chat = ctx.canSeeChat,
|
||||
myHookId = get("hook")
|
||||
myHook = myHook
|
||||
).unsafePerformIO.bimap(
|
||||
url ⇒ Redirect(url),
|
||||
preload ⇒ html.lobby.home(toJson(preload))
|
||||
|
@ -44,9 +46,15 @@ object Lobby extends LilaController {
|
|||
)
|
||||
}
|
||||
|
||||
def cancel(ownerId: String) = TODO
|
||||
//api.cancel(ownerId).unsafePerformIO
|
||||
//Redirect("/")
|
||||
def hook(ownerId: String) = Open { implicit ctx ⇒
|
||||
hookRepo.ownedHook(ownerId.pp).unsafePerformIO.pp.fold(
|
||||
hook ⇒ renderHome(hook.some).fold(identity, Ok(_)),
|
||||
Redirect(routes.Lobby.home))
|
||||
}
|
||||
|
||||
def cancel(fullId: String) = TODO
|
||||
//api.cancel(ownerId).unsafePerformIO
|
||||
//Redirect("/")
|
||||
//}
|
||||
|
||||
def join(hookId: String) = TODO
|
||||
|
@ -58,10 +66,10 @@ object Lobby extends LilaController {
|
|||
//}
|
||||
|
||||
//def create(hookOwnerId: String) = Action {
|
||||
//IOk(api create hookOwnerId)
|
||||
//IOk(api create hookOwnerId)
|
||||
//}
|
||||
|
||||
//def chatBan(username: String) = Action {
|
||||
//IOk(env.lobby.messenger ban username)
|
||||
//IOk(env.lobby.messenger ban username)
|
||||
//}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,17 @@ object Setup extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
val hookForm = Open { implicit ctx ⇒
|
||||
IOk(forms.hookFilled map { html.setup.hook(_) })
|
||||
}
|
||||
|
||||
val hook = process(forms.hook) { config ⇒
|
||||
implicit ctx ⇒
|
||||
processor hook config map { hook ⇒
|
||||
routes.Lobby.hook(hook.ownerId)
|
||||
}
|
||||
}
|
||||
|
||||
def await(fullId: String) = Open { implicit ctx ⇒
|
||||
IOptionResult(gameRepo pov fullId) { pov ⇒
|
||||
pov.game.started.fold(
|
||||
|
@ -60,12 +71,6 @@ object Setup extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
val hookForm = Open { implicit ctx ⇒
|
||||
IOk(forms.hookFilled map { html.setup.hook(_) })
|
||||
}
|
||||
|
||||
val hook = TODO
|
||||
|
||||
private def process[A](form: Form[A])(op: A ⇒ BodyContext ⇒ IO[Call]) =
|
||||
OpenBody { ctx ⇒
|
||||
implicit val req = ctx.body
|
||||
|
|
|
@ -38,6 +38,7 @@ final class CoreEnv private (application: Application, val settings: Settings) {
|
|||
settings = settings,
|
||||
mongodb = mongodb.apply _,
|
||||
gameRepo = game.gameRepo,
|
||||
fisherman = lobby.fisherman,
|
||||
userRepo = user.userRepo,
|
||||
timelinePush = timeline.push.apply,
|
||||
roundMessenger = round.messenger,
|
||||
|
|
|
@ -3,9 +3,6 @@ package elo
|
|||
|
||||
case class EloRange(min: Int, max: Int) {
|
||||
|
||||
def userMin = (min > EloRange.min) option min
|
||||
def userMax = (max > EloRange.max) option max
|
||||
|
||||
override def toString = "%d-%d".format(min, max)
|
||||
}
|
||||
|
||||
|
@ -27,6 +24,8 @@ object EloRange {
|
|||
|
||||
def orDefault(from: String) = apply(from) | default
|
||||
|
||||
def noneIfDefault(from: String) = apply(from) filter (_ != default)
|
||||
|
||||
def valid(from: String) = apply(from).isDefined
|
||||
|
||||
private def acceptable(v: Int) = v >= min && v <= max
|
||||
|
|
|
@ -2,17 +2,16 @@ package lila
|
|||
package game
|
||||
|
||||
import chess.Clock
|
||||
import user.User
|
||||
|
||||
object Namer {
|
||||
|
||||
val anonPlayerName = "Anonymous"
|
||||
|
||||
def player(player: DbPlayer)(getUsername: String ⇒ String) =
|
||||
player.aiLevel.fold(
|
||||
level ⇒ "A.I. level " + level,
|
||||
(player.userId map getUsername).fold(
|
||||
username ⇒ "%s (%s)".format(username, player.elo getOrElse "?"),
|
||||
anonPlayerName)
|
||||
User.anonymous)
|
||||
)
|
||||
|
||||
def clock(clock: Clock): String = "%d minutes/side + %d seconds/move".format(
|
||||
|
|
|
@ -45,9 +45,4 @@ final class Api(
|
|||
//}
|
||||
//).fold(identity, io(GameNotFound))
|
||||
//} yield result
|
||||
|
||||
def create(hookOwnerId: String): IO[Unit] = for {
|
||||
hook ← hookRepo ownedHook hookOwnerId
|
||||
_ ← hook.fold(fisherman.+, io())
|
||||
} yield ()
|
||||
}
|
||||
|
|
|
@ -10,14 +10,13 @@ final class Fisherman(
|
|||
hookMemo: HookMemo,
|
||||
socket: Socket) {
|
||||
|
||||
// DO delete in db
|
||||
def delete(hook: Hook): IO[Unit] = for {
|
||||
_ ← socket removeHook hook
|
||||
_ ← hookRepo removeId hook.id
|
||||
} yield ()
|
||||
|
||||
// DO NOT insert in db (done on php side)
|
||||
def +(hook: Hook): IO[Unit] = for {
|
||||
def add(hook: Hook): IO[Unit] = for {
|
||||
_ ← io(hookRepo insert hook)
|
||||
_ ← socket addHook hook
|
||||
_ ← shake(hook)
|
||||
} yield ()
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
package lila
|
||||
package lobby
|
||||
|
||||
import chess.{ Variant, Mode }
|
||||
import chess.{ Variant, Mode, Color, Clock }
|
||||
import elo.EloRange
|
||||
import user.User
|
||||
|
||||
import com.novus.salat.annotations.Key
|
||||
import com.mongodb.DBRef
|
||||
import ornicar.scalalib.OrnicarRandom
|
||||
|
||||
case class Hook(
|
||||
@Key("_id") id: String,
|
||||
|
@ -18,18 +20,18 @@ case class Hook(
|
|||
color: String,
|
||||
username: String,
|
||||
elo: Option[Int],
|
||||
`match`: Boolean,
|
||||
eloRange: String,
|
||||
engine: Boolean,
|
||||
game: Option[DBRef]) {
|
||||
`match`: Boolean = false,
|
||||
game: Option[DBRef] = None) {
|
||||
|
||||
def gameId: Option[String] = game map (_.getId.toString)
|
||||
|
||||
def realVariant = Variant orDefault variant
|
||||
def realVariant = Variant orDefault variant
|
||||
|
||||
def realMode = Mode orDefault mode
|
||||
|
||||
lazy val realEloRange = EloRange orDefault eloRange
|
||||
lazy val realEloRange: Option[EloRange] = EloRange noneIfDefault eloRange
|
||||
|
||||
def render = Map(
|
||||
"id" -> id,
|
||||
|
@ -39,11 +41,45 @@ case class Hook(
|
|||
"mode" -> realMode.toString,
|
||||
"color" -> color,
|
||||
"clock" -> clockOrUnlimited,
|
||||
"emin" -> realEloRange.userMin,
|
||||
"emax" -> realEloRange.userMax
|
||||
"emin" -> realEloRange.map(_.min),
|
||||
"emax" -> realEloRange.map(_.max)
|
||||
) +? (engine, "engine" -> true)
|
||||
|
||||
def clockOrUnlimited = ((time filter (_ ⇒ hasClock)) |@| increment apply renderClock _) | "Unlimited"
|
||||
|
||||
def renderClock(time: Int, inc: Int) = "%d + %d".format(time, inc)
|
||||
}
|
||||
|
||||
object Hook {
|
||||
|
||||
val idSize = 8
|
||||
val ownerIdSize = 12
|
||||
|
||||
def apply(
|
||||
variant: Variant,
|
||||
clock: Option[Clock],
|
||||
mode: Mode,
|
||||
color: String,
|
||||
user: Option[User],
|
||||
eloRange: EloRange): Hook = generateId |> { id ⇒
|
||||
new Hook(
|
||||
id = id,
|
||||
ownerId = id + generateOwnerId,
|
||||
variant = variant.id,
|
||||
hasClock = clock.isDefined,
|
||||
time = clock map (_.limit),
|
||||
increment = clock map (_.increment),
|
||||
mode = mode.id,
|
||||
color = color,
|
||||
username = user.fold(_.username, User.anonymous),
|
||||
elo = user map (_.elo),
|
||||
eloRange = eloRange.toString,
|
||||
engine = user.fold(_.engine, false))
|
||||
}
|
||||
|
||||
private def generateId =
|
||||
OrnicarRandom nextAsciiString idSize
|
||||
|
||||
private def generateOwnerId =
|
||||
OrnicarRandom nextAsciiString (ownerIdSize - idSize)
|
||||
}
|
||||
|
|
|
@ -39,8 +39,8 @@ final class Hub(
|
|||
"variant" -> JsString(hook.realVariant.toString),
|
||||
"color" -> JsString(hook.color),
|
||||
"clock" -> JsString(hook.clockOrUnlimited),
|
||||
"emin" -> hook.realEloRange.userMin.fold(JsNumber(_), JsNull),
|
||||
"emax" -> hook.realEloRange.userMax.fold(JsNumber(_), JsNull),
|
||||
"emin" -> hook.realEloRange.fold(range ⇒ JsNumber(range.min), JsNull),
|
||||
"emax" -> hook.realEloRange.fold(range ⇒ JsNumber(range.max), JsNull),
|
||||
"engine" -> JsBoolean(hook.engine))
|
||||
)
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import game.{ DbGame, DbPlayer }
|
|||
case class AiConfig(
|
||||
variant: Variant,
|
||||
level: Int,
|
||||
color: Color) extends Config {
|
||||
color: Color) extends Config with GameGenerator {
|
||||
|
||||
def >> = (variant.id, level, color.name).some
|
||||
|
||||
|
|
|
@ -13,6 +13,9 @@ trait Config {
|
|||
val color: Color
|
||||
|
||||
lazy val creatorColor = color.resolve
|
||||
}
|
||||
|
||||
trait GameGenerator { self: Config ⇒
|
||||
|
||||
def game: DbGame
|
||||
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package lila
|
||||
package setup
|
||||
|
||||
import chess.{ Variant, Mode, Color ⇒ ChessColor }
|
||||
import chess.{ Game, Board, Variant, Mode, Color ⇒ ChessColor }
|
||||
import elo.EloRange
|
||||
import game.{ DbGame, DbPlayer }
|
||||
|
||||
case class FriendConfig(
|
||||
variant: Variant,
|
||||
|
@ -9,10 +11,19 @@ case class FriendConfig(
|
|||
time: Int,
|
||||
increment: Int,
|
||||
mode: Mode,
|
||||
color: Color) extends HumanConfig {
|
||||
color: Color) extends HumanConfig with GameGenerator {
|
||||
|
||||
def >> = (variant.id, clock, time, increment, mode.id, color.name).some
|
||||
|
||||
def game = DbGame(
|
||||
game = Game(board = Board(pieces = variant.pieces)),
|
||||
ai = None,
|
||||
whitePlayer = DbPlayer.white,
|
||||
blackPlayer = DbPlayer.black,
|
||||
creatorColor = creatorColor,
|
||||
mode = mode,
|
||||
variant = variant)
|
||||
|
||||
def encode = RawFriendConfig(
|
||||
v = variant.id,
|
||||
k = clock,
|
||||
|
|
|
@ -3,6 +3,8 @@ package setup
|
|||
|
||||
import chess.{ Variant, Mode, Color ⇒ ChessColor }
|
||||
import elo.EloRange
|
||||
import user.User
|
||||
import lobby.Hook
|
||||
|
||||
case class HookConfig(
|
||||
variant: Variant,
|
||||
|
@ -15,6 +17,14 @@ case class HookConfig(
|
|||
|
||||
def >> = (variant.id, clock, time, increment, mode.id, eloRange.toString, color.name).some
|
||||
|
||||
def hook(user: Option[User]) = Hook(
|
||||
variant = variant,
|
||||
clock = makeClock,
|
||||
mode = mode,
|
||||
color = color.name,
|
||||
user = user,
|
||||
eloRange = eloRange)
|
||||
|
||||
def encode = RawHookConfig(
|
||||
v = variant.id,
|
||||
k = clock,
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
package lila
|
||||
package setup
|
||||
|
||||
import chess.{ Mode, PausedClock, Game, Board }
|
||||
import game.{ DbGame, DbPlayer }
|
||||
import chess.{ Mode, PausedClock }
|
||||
|
||||
trait HumanConfig extends Config {
|
||||
|
||||
|
@ -21,15 +20,6 @@ trait HumanConfig extends Config {
|
|||
def validClock = clock.fold(time + increment > 0, true)
|
||||
|
||||
def makeClock = clock option PausedClock(time * 60, increment)
|
||||
|
||||
def game = DbGame(
|
||||
game = Game(board = Board(pieces = variant.pieces)),
|
||||
ai = None,
|
||||
whitePlayer = DbPlayer.white,
|
||||
blackPlayer = DbPlayer.black,
|
||||
creatorColor = creatorColor,
|
||||
mode = mode,
|
||||
variant = variant)
|
||||
}
|
||||
|
||||
trait BaseHumanConfig extends BaseConfig {
|
||||
|
|
|
@ -6,6 +6,7 @@ import game.{ DbGame, GameRepo, Pov }
|
|||
import user.User
|
||||
import chess.{ Game, Board }
|
||||
import ai.Ai
|
||||
import lobby.{ Hook, Fisherman }
|
||||
|
||||
import com.mongodb.DBRef
|
||||
import scalaz.effects._
|
||||
|
@ -14,6 +15,7 @@ final class Processor(
|
|||
configRepo: UserConfigRepo,
|
||||
friendConfigMemo: FriendConfigMemo,
|
||||
gameRepo: GameRepo,
|
||||
fisherman: Fisherman,
|
||||
timelinePush: DbGame ⇒ IO[Unit],
|
||||
ai: () ⇒ Ai,
|
||||
dbRef: User ⇒ DBRef) {
|
||||
|
@ -55,4 +57,13 @@ final class Processor(
|
|||
_ ← timelinePush(game)
|
||||
_ ← friendConfigMemo.set(pov.game.id, config)
|
||||
} yield pov
|
||||
|
||||
def hook(config: HookConfig)(implicit ctx: Context): IO[Hook] = for {
|
||||
_ ← ctx.me.fold(
|
||||
user ⇒ configRepo.update(user)(_ withHook config),
|
||||
io()
|
||||
)
|
||||
hook = config hook ctx.me
|
||||
_ ← fisherman add hook
|
||||
} yield hook
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package setup
|
|||
|
||||
import core.Settings
|
||||
import game.{ DbGame, GameRepo }
|
||||
import lobby.Fisherman
|
||||
import round.Messenger
|
||||
import ai.Ai
|
||||
import user.{ User, UserRepo }
|
||||
|
@ -15,6 +16,7 @@ final class SetupEnv(
|
|||
settings: Settings,
|
||||
mongodb: String ⇒ MongoCollection,
|
||||
gameRepo: GameRepo,
|
||||
fisherman: Fisherman,
|
||||
userRepo: UserRepo,
|
||||
timelinePush: DbGame ⇒ IO[Unit],
|
||||
roundMessenger: Messenger,
|
||||
|
@ -32,6 +34,7 @@ final class SetupEnv(
|
|||
configRepo = configRepo,
|
||||
friendConfigMemo = friendConfigMemo,
|
||||
gameRepo = gameRepo,
|
||||
fisherman = fisherman,
|
||||
timelinePush = timelinePush,
|
||||
ai = ai,
|
||||
dbRef = dbRef)
|
||||
|
|
|
@ -37,4 +37,6 @@ case class User(
|
|||
object User {
|
||||
|
||||
val STARTING_ELO = 1200
|
||||
|
||||
val anonymous = "Anonymous"
|
||||
}
|
||||
|
|
|
@ -95,8 +95,9 @@ GET /ai controllers.Ai.run
|
|||
|
||||
# Lobby Public API
|
||||
GET / controllers.Lobby.home
|
||||
GET /lobby/cancel/:ownerId controllers.Lobby.cancel(ownerId: String)
|
||||
GET /lobby/join/:hookId controllers.Lobby.join(hookId: String)
|
||||
GET /new/$ownerId<[\w\-]{12}> controllers.Lobby.hook(ownerId: String)
|
||||
GET /new/$ownerId<[\w\-]{12}>/cancel controllers.Lobby.cancel(ownerId: String)
|
||||
GET /new/$hookId<[\w\-]{8}>/join controllers.Lobby.join(hookId: String)
|
||||
GET /lobby/socket controllers.Lobby.socket
|
||||
|
||||
# Lobby Private API
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
User-agent: *
|
||||
Allow: /
|
||||
Disallow: /new
|
||||
Disallow: /setting
|
||||
Disallow: /account
|
1
todo
1
todo
|
@ -19,6 +19,7 @@ user info is expensive - cache it
|
|||
chess960 confirmation http://fr.lichess.org/forum/lichess-feedback/separate-960-lobby?page=1#7
|
||||
use play-navigator router case class MyRegexStr(value: String); implicit val MyRegexStrPathParam: PathParam[MyRegexStr] = new PathParam[MyRegexStr] { def apply(s: MyRegexStr) = s.value}; def unapply(s: String) = val Rx = "(\w+)".r; s match { case Rx(x) => Some(x); case _ => None } }
|
||||
http://codetunes.com/2012/05/09/scala-dsl-tutorial-writing-web-framework-router
|
||||
use POST instead of GET where it makes sense
|
||||
|
||||
next deploy:
|
||||
upgrade jre (jdk?)
|
||||
|
|
Loading…
Reference in New Issue