Reorganize the whole lobby thing
This commit is contained in:
parent
42ddd61387
commit
858864c9ba
|
@ -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])
|
|
@ -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++
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
|
@ -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("/")
|
||||
}
|
||||
}
|
|
@ -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
41
app/lobby/Hub.scala
Normal 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
37
app/lobby/Lobby.scala
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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
11
app/lobby/messages.scala
Normal 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])
|
|
@ -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
|
||||
|
||||
|
|
16
conf/routes
16
conf/routes
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue