send empty frames for ping; keep bot player alive in the game socket

This commit is contained in:
Thibault Duplessis 2018-04-17 18:03:07 +02:00
parent c1b409a489
commit e273ad4232
7 changed files with 45 additions and 15 deletions

View file

@ -14,7 +14,7 @@ object Bot extends LilaController {
WithMyBotGame(id, me) { pov =>
RequireHttp11(req) {
lila.game.GameRepo.withInitialFen(pov.game) map { wf =>
Ok.chunked(Env.bot.gameStateStream(wf))
Ok.chunked(Env.bot.gameStateStream(me, wf, pov.color))
}
}
}

View file

@ -22,7 +22,7 @@ final class EventStream(
var stream: Option[ActorRef] = None
val enumerator = Concurrent.unicast[JsObject](
val enumerator = Concurrent.unicast[Option[JsObject]](
onStart = channel => {
val actor = system.actorOf(Props(new Actor {
@ -34,9 +34,11 @@ final class EventStream(
case SetOnline =>
setOnline(me.id)
context.system.scheduler.scheduleOnce(6 second, self, SetOnline)
// gotta send a message to check if the client has disconnected
channel push Json.obj("up" -> true)
context.system.scheduler.scheduleOnce(6 second) {
// gotta send a message to check if the client has disconnected
channel push None
self ! SetOnline
}
case UserStartGame(userId, game) if userId == me.id => pushGameStart(game)
@ -46,12 +48,12 @@ final class EventStream(
def pushGameStart(game: Game) = channel push Json.obj(
"type" -> "gameStart",
"game" -> Json.obj("id" -> game.id)
)
).some
def pushChallenge(c: lila.challenge.Challenge) = channel push Json.obj(
"type" -> "challenge",
"challenge" -> challengeJsonView(none)(c)
)
).some
}))
system.lilaBus.subscribe(actor, Symbol(s"userStartGame:${me.id}"), 'challenge)
stream = actor.some
@ -59,6 +61,6 @@ final class EventStream(
onComplete = onComplete(stream, system)
)
enumerator &> stringify
enumerator &> stringifyOrEmpty
}
}

View file

@ -5,12 +5,17 @@ import akka.actor._
final class Env(
system: ActorSystem,
hub: lila.hub.Env,
onlineUserIds: lila.memo.ExpireSetMemo,
lightUserApi: lila.user.LightUserApi
) {
lazy val jsonView = new BotJsonView(lightUserApi)
lazy val gameStateStream = new GameStateStream(system, jsonView)
lazy val gameStateStream = new GameStateStream(
system,
jsonView,
hub.socket.round
)
lazy val player = new BotPlayer(
roundMap = hub.actor.roundMap
@ -22,6 +27,7 @@ object Env {
lazy val current: Env = "bot" boot new Env(
system = lila.common.PlayApp.system,
hub = lila.hub.Env.current,
onlineUserIds = lila.user.Env.current.onlineUserIdMemo,
lightUserApi = lila.user.Env.current.lightUserApi
)
}

View file

@ -1,5 +1,6 @@
package lila.bot
import scala.concurrent.duration._
import akka.actor._
import play.api.libs.iteratee._
import play.api.libs.json._
@ -9,39 +10,51 @@ import chess.format.FEN
import lila.game.actorApi.{ FinishGame, AbortedBy }
import lila.game.{ Game, GameRepo }
import lila.hub.actorApi.round.MoveEvent
import lila.hub.actorApi.map.Tell
import lila.socket.actorApi.BotPing
import lila.user.User
final class GameStateStream(
system: ActorSystem,
jsonView: BotJsonView
jsonView: BotJsonView,
roundSocketHub: ActorSelection
) {
import lila.common.HttpStream._
def apply(init: Game.WithInitialFen): Enumerator[String] = {
def apply(me: User, init: Game.WithInitialFen, as: chess.Color): Enumerator[String] = {
val id = init.game.id
var stream: Option[ActorRef] = None
val enumerator = Concurrent.unicast[JsObject](
val enumerator = Concurrent.unicast[Option[JsObject]](
onStart = channel => {
val actor = system.actorOf(Props(new Actor {
jsonView gameFull init foreach { json =>
// prepend the full game JSON at the start of the stream
channel push json
channel push json.some
// close stream if game is over
if (init.game.finished) channel.eofAndEnd()
}
self ! SetOnline
def receive = {
case g: Game if g.id == id => pushState(g)
case FinishGame(g, _, _) if g.id == id => terminate
case AbortedBy(pov) if pov.gameId == id => terminate
case SetOnline =>
roundSocketHub ! Tell(init.game.id, BotPing(as))
context.system.scheduler.scheduleOnce(6 second) {
// gotta send a message to check if the client has disconnected
channel push None
self ! SetOnline
}
}
def pushState(g: Game) = jsonView gameState Game.WithInitialFen(g, init.fen) map channel.push
def pushState(g: Game) = jsonView gameState Game.WithInitialFen(g, init.fen) map some map channel.push
def terminate = channel.eofAndEnd()
}))
system.lilaBus.subscribe(actor, Symbol(s"moveGame:$id"), 'finishGame, 'abortGame)
@ -50,6 +63,6 @@ final class GameStateStream(
onComplete = onComplete(stream, system)
)
enumerator &> stringify
enumerator &> stringifyOrEmpty
}
}

View file

@ -11,6 +11,12 @@ object HttpStream {
Json.stringify(js) + "\n"
}
val stringifyOrEmpty =
Enumeratee.map[Option[JsObject]].apply[String] {
case None => "\n"
case Some(js) => Json.stringify(js) + "\n"
}
def onComplete(stream: Option[ActorRef], system: ActorSystem) =
stream foreach { actor =>
system.lilaBus.unsubscribe(actor)

View file

@ -141,6 +141,8 @@ private[round] final class Socket(
(history getEventsSince v).fold(resyncNow(member))(batch(member, _))
}
case BotPing(color) => playerDo(color.pp("bot ping"), _.ping)
case Bye(color) => playerDo(color, _.setBye)
case Broom =>

View file

@ -11,6 +11,7 @@ case class Connected[M <: SocketMember](
)
case class Sync(uid: String, friends: List[String])
case class Ping(uid: String, version: Option[Int], lagCentis: Option[Centis])
case class BotPing(color: chess.Color)
object Ping {
def apply(uid: Socket.Uid, o: JsObject): Ping =