fix user TV race condition - closes #4614
If the game finishes between page load and websocket connection, the spectator can remain stuck on the finished game. Now checking if a new game is available on websocket connection.
This commit is contained in:
parent
bd1817392a
commit
0f9c6003e4
|
@ -237,7 +237,7 @@ private[controllers] trait LilaController
|
|||
}
|
||||
|
||||
protected def NoCurrentGame(a: => Fu[Result])(implicit ctx: Context): Fu[Result] =
|
||||
ctx.me.??(mashup.Preload.currentGame(Env.user.lightUserSync)) flatMap {
|
||||
ctx.me.??(mashup.Preload.currentGameMyTurn(Env.user.lightUserSync)) flatMap {
|
||||
_.fold(a) { current =>
|
||||
negotiate(
|
||||
html = Lobby.renderHome(Results.Forbidden),
|
||||
|
|
|
@ -21,12 +21,18 @@ object Round extends LilaController with TheftPrevention {
|
|||
proxyPov(gameId, color) flatMap {
|
||||
_ ?? { pov =>
|
||||
getSocketUid("sri") ?? { uid =>
|
||||
val userTv = get("userTv") map { userId =>
|
||||
lila.round.actorApi.UserTv(
|
||||
userId,
|
||||
pov.game.finishedOrAborted ?? GameRepo.lastPlayedPlaying(userId).map(_.isDefined)
|
||||
)
|
||||
}
|
||||
env.socketHandler.watcher(
|
||||
pov = pov,
|
||||
uid = uid,
|
||||
user = ctx.me,
|
||||
ip = ctx.ip,
|
||||
userTv = get("userTv"),
|
||||
userTv = userTv,
|
||||
version = getSocketVersion
|
||||
) map some
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ final class Preload(
|
|||
liveStreams().dmap(_.autoFeatured.withTitles(lightUserApi)) zip
|
||||
(ctx.userId ?? getPlayban) flatMap {
|
||||
case (data, povs) ~ posts ~ tours ~ events ~ simuls ~ feat ~ entries ~ lead ~ tWinners ~ puzzle ~ streams ~ playban =>
|
||||
val currentGame = ctx.me ?? Preload.currentGame(povs, lightUserApi.sync) _
|
||||
val currentGame = ctx.me ?? Preload.currentGameMyTurn(povs, lightUserApi.sync) _
|
||||
lightUserApi.preloadMany {
|
||||
tWinners.map(_.userId) :::
|
||||
posts.flatMap(_.userId) :::
|
||||
|
@ -63,12 +63,12 @@ object Preload {
|
|||
|
||||
case class CurrentGame(pov: Pov, json: JsObject, opponent: String)
|
||||
|
||||
def currentGame(lightUser: lila.common.LightUser.GetterSync)(user: User): Fu[Option[CurrentGame]] =
|
||||
def currentGameMyTurn(lightUser: lila.common.LightUser.GetterSync)(user: User): Fu[Option[CurrentGame]] =
|
||||
GameRepo.playingRealtimeNoAi(user, 10) map {
|
||||
currentGame(_, lightUser)(user)
|
||||
currentGameMyTurn(_, lightUser)(user)
|
||||
}
|
||||
|
||||
def currentGame(povs: List[Pov], lightUser: lila.common.LightUser.GetterSync)(user: User): Option[CurrentGame] =
|
||||
private def currentGameMyTurn(povs: List[Pov], lightUser: lila.common.LightUser.GetterSync)(user: User): Option[CurrentGame] =
|
||||
povs.collectFirst {
|
||||
case pov if pov.game.nonAi && pov.game.hasClock && pov.isMyTurn =>
|
||||
val opponent = lila.game.Namer.playerText(pov.opponent)(lightUser)
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
package lila.common
|
||||
|
||||
import scala.concurrent.duration._
|
||||
import akka.actor.ActorSystem
|
||||
import play.api.libs.iteratee._
|
||||
import scala.concurrent.duration._
|
||||
|
||||
object Iteratee {
|
||||
|
||||
|
@ -15,4 +15,10 @@ object Iteratee {
|
|||
def prepend[A](elements: Seq[A], enumerator: Enumerator[A]): Enumerator[A] =
|
||||
if (elements.isEmpty) enumerator
|
||||
else Enumerator(elements: _*) >>> enumerator
|
||||
|
||||
// Avoid running `andThen` on empty elements, just for perf
|
||||
def prependFu[A](elements: Fu[Seq[A]], enumerator: Enumerator[A]): Enumerator[A] =
|
||||
Enumerator flatten {
|
||||
elements map { prepend(_, enumerator) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -170,11 +170,13 @@ object GameRepo {
|
|||
|
||||
// gets last recently played move game in progress
|
||||
def lastPlayedPlaying(user: User): Fu[Option[Pov]] =
|
||||
coll.find(Query recentlyPlaying user.id)
|
||||
lastPlayedPlaying(user.id).map { _ flatMap { Pov(_, user) } }
|
||||
|
||||
def lastPlayedPlaying(userId: User.ID): Fu[Option[Game]] =
|
||||
coll.find(Query recentlyPlaying userId)
|
||||
.sort(Query.sortMovedAtNoIndex)
|
||||
.cursor[Game](readPreference = ReadPreference.secondaryPreferred)
|
||||
.uno
|
||||
.map { _ flatMap { Pov(_, user) } }
|
||||
|
||||
def allPlaying(userId: User.ID): Fu[List[Pov]] =
|
||||
coll.find(Query nowPlaying userId).list[Game]()
|
||||
|
|
|
@ -185,11 +185,18 @@ private[round] final class Socket(
|
|||
|
||||
case Join(uid, user, color, playerId, ip, onTv, version) =>
|
||||
val (enumerator, channel) = Concurrent.broadcast[JsValue]
|
||||
val member = Member(channel, user, color, playerId, ip, onTv)
|
||||
val member = Member(channel, user, color, playerId, ip, onTv.map(_.userId))
|
||||
addMember(uid, member)
|
||||
notifyCrowd
|
||||
if (playerId.isDefined) playerDo(color, _.ping)
|
||||
if (member.userTv.isDefined) buscriptions.tv
|
||||
val reloadTvEvent = onTv ?? {
|
||||
case UserTv(_, reload) => reload map {
|
||||
case true => makeMessage("resync").some
|
||||
case false =>
|
||||
buscriptions.tv // reload buscriptions
|
||||
none
|
||||
}
|
||||
}
|
||||
val events = version.fold(history.getRecentEvents(5).some) {
|
||||
history.getEventsSince
|
||||
}
|
||||
|
@ -198,11 +205,13 @@ private[round] final class Socket(
|
|||
batchMsgs(member, _)
|
||||
} map { m => Enumerator(m: JsValue) }
|
||||
|
||||
sender ! Connected(
|
||||
initialMsgs.fold(enumerator) { _ >>> enumerator },
|
||||
member
|
||||
val fullEnumerator = lila.common.Iteratee.prependFu(
|
||||
reloadTvEvent.map(_.toList),
|
||||
initialMsgs.fold(enumerator) { _ >>> enumerator }
|
||||
)
|
||||
|
||||
sender ! Connected(fullEnumerator, member)
|
||||
|
||||
case Nil =>
|
||||
case eventList: EventList => notify(eventList.events)
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ private[round] final class SocketHandler(
|
|||
uid: Uid,
|
||||
user: Option[User],
|
||||
ip: IpAddress,
|
||||
userTv: Option[User.ID],
|
||||
userTv: Option[UserTv],
|
||||
version: Option[SocketVersion]
|
||||
): Fu[JsSocketHandler] = join(pov, none, uid, user, ip, userTv, version)
|
||||
|
||||
|
@ -144,7 +144,7 @@ private[round] final class SocketHandler(
|
|||
uid: Uid,
|
||||
user: Option[User],
|
||||
ip: IpAddress,
|
||||
userTv: Option[User.ID],
|
||||
userTv: Option[UserTv],
|
||||
version: Option[SocketVersion]
|
||||
): Fu[JsSocketHandler] = {
|
||||
val join = Join(
|
||||
|
|
|
@ -7,13 +7,12 @@ import chess.format.Uci
|
|||
import chess.{ MoveMetrics, Color }
|
||||
|
||||
import lila.common.IpAddress
|
||||
import lila.game.Event
|
||||
import lila.socket.Socket.Uid
|
||||
import lila.socket.SocketMember
|
||||
import lila.user.User
|
||||
import lila.socket.Socket.SocketVersion
|
||||
|
||||
case class EventList(events: List[Event])
|
||||
case class EventList(events: List[lila.game.Event])
|
||||
|
||||
sealed trait Member extends SocketMember {
|
||||
|
||||
|
@ -77,9 +76,10 @@ case class Join(
|
|||
color: Color,
|
||||
playerId: Option[String],
|
||||
ip: IpAddress,
|
||||
userTv: Option[User.ID],
|
||||
userTv: Option[UserTv],
|
||||
version: Option[SocketVersion]
|
||||
)
|
||||
case class UserTv(userId: User.ID, reload: Fu[Boolean])
|
||||
case class Connected(enumerator: JsEnumerator, member: Member)
|
||||
case class Bye(color: Color)
|
||||
case class IsGone(color: Color)
|
||||
|
|
Loading…
Reference in a new issue