Reorganize the whole lobby thing

This commit is contained in:
Thibault Duplessis 2012-04-05 18:53:04 +02:00
parent 42ddd61387
commit 858864c9ba
14 changed files with 146 additions and 148 deletions

View file

@ -1,73 +0,0 @@
package lila
import akka.actor._
import akka.util.duration._
import akka.util.Timeout
import akka.pattern.ask
import play.api._
import play.api.libs.json._
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import play.api.Play.current
import scalaz.effects._
object Lobby {
implicit val timeout = Timeout(1 second)
lazy val instance = Akka.system.actorOf(Props(new Lobby(
env = Global.env
)))
def join(uid: String): Promise[(Iteratee[JsValue, _], Enumerator[JsValue])] =
(instance ? Join(uid)).asPromise.map {
case Connected(enumerator)
val iteratee = Iteratee.foreach[JsValue] { event
(event \ "t").as[String] match {
case "talk" instance ! Talk(
(event \ "data" \ "txt").as[String],
(event \ "data" \ "u").as[String]
)
}
}.mapDone { _ instance ! Quit(uid) }
(iteratee, enumerator)
}
}
final class Lobby private (env: SystemEnv) extends Actor {
private var members = Map.empty[String, PushEnumerator[JsValue]]
def receive = {
case Join(uid) {
// Create an Enumerator to write to this socket
val channel = Enumerator.imperative[JsValue]()
members = members + (uid -> channel)
sender ! Connected(channel)
}
case Talk(txt, u) env.messageRepo.add(txt, u).foreach { save
save flatMap { message
notifyAll("talk", Seq(
"txt" -> JsString(message.text),
"u" -> JsString(message.username)
))
} unsafePerformIO
}
case Quit(uid) { members = members - uid }
}
def notifyAll(t: String, data: Seq[(String, JsValue)]): IO[Unit] = io {
val msg = JsObject(Seq("t" -> JsString(t), "d" -> JsObject(data)))
members.foreach { case (_, channel) channel.push(msg) }
}
}
case class Join(uid: String)
case class Quit(uid: String)
case class Talk(txt: String, u: String)
case class Connected(enumerator: Enumerator[JsValue])

View file

@ -1,22 +0,0 @@
package lila
import model._
import memo._
import db._
import scalaz.effects._
import scala.annotation.tailrec
import scala.math.max
final class LobbyXhr(
hookRepo: HookRepo,
lobbyMemo: LobbyMemo,
hookMemo: HookMemo) {
def cancel(ownerId: String): IO[Unit] = for {
_ hookRepo removeOwnerId ownerId
_ hookMemo remove ownerId
_ versionInc
} yield ()
def versionInc: IO[Int] = lobbyMemo++
}

View file

@ -6,14 +6,17 @@ import db.{ GameRepo, EntryRepo }
import scalaz.effects._
final class Starter(
val gameRepo: GameRepo,
entryRepo: EntryRepo,
val versionMemo: VersionMemo,
ai: Ai) extends IOTools {
val gameRepo: GameRepo,
entryRepo: EntryRepo,
val versionMemo: VersionMemo,
lobbySocket: lobby.Lobby,
ai: Ai) extends IOTools {
def start(game: DbGame, entryData: String): IO[DbGame] = for {
_ if (game.variant == Standard) io() else gameRepo saveInitialFen game
_ Entry(game, entryData).fold(entryRepo.add, io())
_ Entry(game, entryData).fold(
entry entryRepo add entry flatMap { _ lobbySocket addEntry entry },
io())
g2 if (game.player.isHuman) io(game) else for {
aiResult ai(game) map (_.err)
(newChessGame, move) = aiResult

View file

@ -3,6 +3,10 @@ package lila
import com.mongodb.casbah.MongoConnection
import com.mongodb.{ Mongo, MongoOptions, ServerAddress MongoServer }
import com.typesafe.config._
import akka.actor._
import play.api.libs.concurrent._
import play.api.Play.current
import chess.EloCalculator
import db._
@ -12,6 +16,10 @@ import command._
final class SystemEnv(config: Config) {
lazy val lobbyHub = Akka.system.actorOf(Props(new lobby.Hub(this)))
lazy val lobbySocket = new lobby.Lobby(lobbyHub)
lazy val appXhr = new AppXhr(
gameRepo = gameRepo,
messenger = messenger,
@ -35,12 +43,7 @@ final class SystemEnv(config: Config) {
duration = getMilliseconds("sync.duration"),
sleep = getMilliseconds("sync.sleep"))
lazy val lobbyXhr = new LobbyXhr(
hookRepo = hookRepo,
lobbyMemo = lobbyMemo,
hookMemo = hookMemo)
lazy val lobbyApi = new LobbyApi(
lazy val lobbyApi = new lobby.Api(
hookRepo = hookRepo,
gameRepo = gameRepo,
messenger = messenger,
@ -50,7 +53,7 @@ final class SystemEnv(config: Config) {
aliveMemo = aliveMemo,
hookMemo = hookMemo)
lazy val lobbyPreloader = new LobbyPreloader(
lazy val lobbyPreloader = new lobby.Preload(
hookRepo = hookRepo,
gameRepo = gameRepo,
messageRepo = messageRepo,
@ -81,6 +84,7 @@ final class SystemEnv(config: Config) {
gameRepo = gameRepo,
entryRepo = entryRepo,
ai = ai,
lobbySocket = lobbySocket,
versionMemo = versionMemo)
lazy val ai: Ai = craftyAi

View file

@ -1,16 +1,32 @@
package lila
package controllers
import lila.DataForm._
import lila.http._
import DataForm._
import play.api._
import mvc._
object LobbyApiC extends LilaController {
import play.api.libs.concurrent.Akka
import play.api.Play.current
import play.api.libs.json._
import play.api.libs.iteratee._
object LobbyC extends LilaController {
private val api = env.lobbyApi
private val preloader = env.lobbyPreloader
def socket(uid: String) = WebSocket.async[JsValue] { request
env.lobbySocket.join(uid)
}
def cancel(ownerId: String) = Action {
api.cancel(ownerId).unsafePerformIO
Redirect("/")
}
def preload = Action { implicit request
JsonIOk(preloader(
auth = getIntOr("auth", 0) == 1,

View file

@ -1,28 +0,0 @@
package lila
package controllers
import lila.http._
import DataForm._
import play.api._
import mvc._
import play.api.libs.concurrent.Akka
import play.api.Play.current
import play.api.libs.json._
import play.api.libs.iteratee._
object LobbyXhrC extends LilaController {
private val xhr = env.lobbyXhr
def socket(uid: String) = WebSocket.async[JsValue] { request
Lobby.join(uid)
}
def cancel(ownerId: String) = Action {
xhr.cancel(ownerId).unsafePerformIO
Redirect("/")
}
}

View file

@ -1,11 +1,12 @@
package lila
package lobby
import model._
import memo._
import db._
import scalaz.effects._
final class LobbyApi(
final class Api(
hookRepo: HookRepo,
val gameRepo: GameRepo,
messenger: Messenger,
@ -15,6 +16,12 @@ final class LobbyApi(
aliveMemo: AliveMemo,
hookMemo: HookMemo) extends IOTools {
def cancel(ownerId: String): IO[Unit] = for {
_ hookRepo removeOwnerId ownerId
_ hookMemo remove ownerId
_ versionInc
} yield ()
def join(
gameId: String,
colorName: String,

41
app/lobby/Hub.scala Normal file
View file

@ -0,0 +1,41 @@
package lila
package lobby
import akka.actor._
import play.api.libs.json._
import play.api.libs.iteratee._
final class Hub(env: SystemEnv) extends Actor {
private var members = Map.empty[String, PushEnumerator[JsValue]]
def receive = {
case Join(uid) {
// Create an Enumerator to write to this socket
val channel = Enumerator.imperative[JsValue]()
members = members + (uid -> channel)
sender ! Connected(channel)
}
case Talk(txt, u) env.messageRepo.add(txt, u).foreach { save
val message = save.unsafePerformIO
notifyAll("talk", Seq(
"txt" -> JsString(message.text),
"u" -> JsString(message.username)
))
}
case Entry(entry) notifyAll("entry", Seq(
"html" -> JsString(entry.render)
))
case Quit(uid) { members = members - uid }
}
def notifyAll(t: String, data: Seq[(String, JsValue)]) {
val msg = JsObject(Seq("t" -> JsString(t), "d" -> JsObject(data)))
members.foreach { case (_, channel) channel.push(msg) }
}
}

37
app/lobby/Lobby.scala Normal file
View file

@ -0,0 +1,37 @@
package lila
package lobby
import akka.actor._
import akka.pattern.ask
import akka.util.duration._
import akka.util.Timeout
import play.api._
import play.api.libs.json._
import play.api.libs.iteratee._
import play.api.libs.concurrent._
import scalaz.effects._
final class Lobby(hub: ActorRef) {
implicit val timeout = Timeout(1 second)
def join(uid: String): Promise[(Iteratee[JsValue, _], Enumerator[JsValue])] =
(hub ? Join(uid)).asPromise.map {
case Connected(enumerator)
val iteratee = Iteratee.foreach[JsValue] { event
(event \ "t").as[String] match {
case "talk" hub ! Talk(
(event \ "data" \ "txt").as[String],
(event \ "data" \ "u").as[String]
)
}
}.mapDone { _ hub ! Quit(uid) }
(iteratee, enumerator)
}
def addEntry(entry: model.Entry): IO[Unit] = io {
hub ! Entry(entry)
}
}

View file

@ -1,11 +1,12 @@
package lila
package lobby
import model._
import memo._
import db._
import scalaz.effects._
final class LobbyPreloader(
final class Preload(
hookRepo: HookRepo,
gameRepo: GameRepo,
messageRepo: MessageRepo,

11
app/lobby/messages.scala Normal file
View file

@ -0,0 +1,11 @@
package lila
package lobby
import play.api.libs.iteratee.Enumerator
import play.api.libs.json.JsValue
case class Entry(entry: model.Entry)
case class Join(uid: String)
case class Quit(uid: String)
case class Talk(txt: String, u: String)
case class Connected(enumerator: Enumerator[JsValue])

View file

@ -56,7 +56,7 @@ application.langs="en"
# ~~~~~
# Define the Global object class for this application.
# Default to Global in the root package.
global="lila.http.Global"
global="lila.Global"
evolutionplugin=disabled

View file

@ -1,4 +1,4 @@
# App XHR
# App Public API
GET /ping lila.controllers.AppXhrC.ping
GET /sync/:gameId/:color/:version lila.controllers.AppXhrC.syncPublic(gameId: String, color: String, version: Int)
GET /sync/:gameId/:color/:version/:fullId lila.controllers.AppXhrC.sync(gameId: String, color: String, version: Int, fullId: String)
@ -29,15 +29,15 @@ GET /api/player-version/:gameId/:color lila.controllers.AppApiC.playerVersion(
GET /api/nb-players lila.controllers.AppXhrC.nbPlayers
POST /api/outoftime/:fullId lila.controllers.AppXhrC.outoftime(fullId: String)
# Lobby XHR
GET /lobby/cancel/:ownerId lila.controllers.LobbyXhrC.cancel(ownerId: String)
GET /lobby/socket/:uid lila.controllers.LobbyXhrC.socket(uid: String)
# Lobby Public API
GET /lobby/cancel/:ownerId lila.controllers.LobbyC.cancel(ownerId: String)
GET /lobby/socket/:uid lila.controllers.LobbyC.socket(uid: String)
# Lobby Private API
POST /api/lobby/join/:gameId/:color lila.controllers.LobbyApiC.join(gameId: String, color: String)
GET /api/lobby/preload lila.controllers.LobbyApiC.preload
POST /api/lobby/create/:hookOwnerId lila.controllers.LobbyApiC.create(hookOwnerId: String)
POST /api/lobby/alive/:hookOwnerId lila.controllers.LobbyApiC.alive(hookOwnerId: String)
POST /api/lobby/join/:gameId/:color lila.controllers.LobbyC.join(gameId: String, color: String)
GET /api/lobby/preload lila.controllers.LobbyC.preload
POST /api/lobby/create/:hookOwnerId lila.controllers.LobbyC.create(hookOwnerId: String)
POST /api/lobby/alive/:hookOwnerId lila.controllers.LobbyC.alive(hookOwnerId: String)
# Useless, but play2 needs it
GET /assets/*file controllers.Assets.at(path="/public", file)

1
todo
View file

@ -5,3 +5,4 @@ ping nbm
http://en.lichess.org/@/noworkingmen elo graph missing
need ability to cancel rematch
compress player.ps notation
move ai to marty