290 lines
9.2 KiB
Scala
290 lines
9.2 KiB
Scala
package lila.round
|
|
|
|
import akka.actor._
|
|
import akka.pattern.{ ask, pipe }
|
|
import com.typesafe.config.Config
|
|
import scala.concurrent.duration._
|
|
|
|
import actorApi.{ GetSocketStatus, SocketStatus }
|
|
|
|
import lila.game.{ Game, GameRepo, Pov }
|
|
import lila.hub.actorApi.map.{ Tell, Exists, Ask }
|
|
import lila.hub.actorApi.round.{ Abort, Resign, FishnetPlay }
|
|
import lila.hub.actorApi.{ HasUserId, DeployPost }
|
|
|
|
final class Env(
|
|
config: Config,
|
|
system: ActorSystem,
|
|
db: lila.db.Env,
|
|
hub: lila.hub.Env,
|
|
fishnetPlayer: lila.fishnet.Player,
|
|
aiPerfApi: lila.fishnet.AiPerfApi,
|
|
crosstableApi: lila.game.CrosstableApi,
|
|
playban: lila.playban.PlaybanApi,
|
|
lightUser: lila.common.LightUser.Getter,
|
|
userJsonView: lila.user.JsonView,
|
|
rankingApi: lila.user.RankingApi,
|
|
notifyApi: lila.notify.NotifyApi,
|
|
uciMemo: lila.game.UciMemo,
|
|
rematch960Cache: lila.memo.ExpireSetMemo,
|
|
onStart: String => Unit,
|
|
divider: lila.game.Divider,
|
|
prefApi: lila.pref.PrefApi,
|
|
historyApi: lila.history.HistoryApi,
|
|
evalCache: lila.evalCache.EvalCacheApi,
|
|
evalCacheHandler: lila.evalCache.EvalCacheSocketHandler,
|
|
isBotSync: lila.common.LightUser.IsBotSync
|
|
) {
|
|
|
|
private val settings = new {
|
|
val UidTimeout = config duration "uid.timeout"
|
|
val PlayerDisconnectTimeout = config duration "player.disconnect.timeout"
|
|
val PlayerRagequitTimeout = config duration "player.ragequit.timeout"
|
|
val AnimationDuration = config duration "animation.duration"
|
|
val MoretimeDuration = config duration "moretime"
|
|
val SocketName = config getString "socket.name"
|
|
val SocketTimeout = config duration "socket.timeout"
|
|
val NetDomain = config getString "net.domain"
|
|
val ActiveTtl = config duration "active.ttl"
|
|
val CollectionNote = config getString "collection.note"
|
|
val CollectionHistory = config getString "collection.history"
|
|
val CollectionForecast = config getString "collection.forecast"
|
|
val CollectionAlarm = config getString "collection.alarm"
|
|
val ChannelMoveTime = config getString "channel.move_time.name "
|
|
}
|
|
import settings._
|
|
|
|
private val bus = system.lilaBus
|
|
|
|
private val moveTimeChannel = system.actorOf(Props(classOf[lila.socket.Channel]), name = ChannelMoveTime)
|
|
|
|
lazy val eventHistory = History(db(CollectionHistory)) _
|
|
|
|
private lazy val roundDependencies = Round.Dependencies(
|
|
messenger = messenger,
|
|
takebacker = takebacker,
|
|
finisher = finisher,
|
|
rematcher = rematcher,
|
|
player = player,
|
|
drawer = drawer,
|
|
forecastApi = forecastApi,
|
|
socketHub = socketHub,
|
|
scheduler = system.scheduler,
|
|
moretimeDuration = MoretimeDuration
|
|
)
|
|
val roundMap = new lila.hub.DuctMap[Round](
|
|
mkDuct = id => new Round(
|
|
dependencies = roundDependencies,
|
|
gameId = id
|
|
),
|
|
accessTimeout = ActiveTtl
|
|
)
|
|
|
|
bus.subscribeFun('roundMapTell, 'deploy) {
|
|
case Tell(id, msg) => roundMap.tell(id, msg)
|
|
case DeployPost => roundMap.tellAll(DeployPost)
|
|
}
|
|
|
|
private var nbRounds = 0
|
|
def count = nbRounds
|
|
|
|
system.scheduler.schedule(5 seconds, 2 seconds) {
|
|
nbRounds = roundMap.size
|
|
bus.publish(lila.hub.actorApi.round.NbRounds(nbRounds), 'nbRounds)
|
|
}
|
|
|
|
def roundProxyGame(gameId: Game.ID): Fu[Option[Game]] =
|
|
roundMap.getOrMake(gameId).game.mon(_.round.proxyGameWatcherTime) addEffect { g =>
|
|
if (!g.isDefined) roundMap kill gameId
|
|
lila.mon.round.proxyGameWatcherCount(g.isDefined.toString)()
|
|
}
|
|
|
|
private var historyPersistenceEnabled = false
|
|
|
|
private val socketHub = new lila.hub.ActorMapNew(
|
|
mkActor = id => new Socket(
|
|
gameId = id,
|
|
history = eventHistory(id, historyPersistenceEnabled),
|
|
lightUser = lightUser,
|
|
uidTimeout = UidTimeout,
|
|
disconnectTimeout = PlayerDisconnectTimeout,
|
|
ragequitTimeout = PlayerRagequitTimeout,
|
|
simulActor = hub.actor.simul
|
|
),
|
|
accessTimeout = SocketTimeout,
|
|
name = "round.socket",
|
|
system = system
|
|
)
|
|
|
|
private val socketHubActor = system.actorOf(Props(new Actor {
|
|
def receive = {
|
|
case msg @ lila.chat.actorApi.ChatLine(id, line) => socketHub.tell(id.value take 8, msg)
|
|
case msg: lila.game.actorApi.StartGame => socketHub.tell(msg.game.id, msg)
|
|
case _: lila.hub.actorApi.Deploy =>
|
|
logger.warn("Enable history persistence")
|
|
historyPersistenceEnabled = true
|
|
// if the deploy didn't go through, cancel persistence
|
|
system.scheduler.scheduleOnce(10.minutes) {
|
|
logger.warn("Disabling round history persistence!")
|
|
historyPersistenceEnabled = false
|
|
}
|
|
case Tell(id, msg) => socketHub.tell(id, msg)
|
|
case Exists(id) => sender ! socketHub.exists(id)
|
|
case Ask(id, msg) =>
|
|
import makeTimeout.short
|
|
socketHub.getOrMake(id) ? msg pipeTo sender
|
|
}
|
|
}), name = SocketName)
|
|
bus.subscribe(socketHubActor, 'tvSelect, 'startGame, 'deploy)
|
|
|
|
lazy val selfReport = new SelfReport(roundMap)
|
|
|
|
lazy val recentTvGames = new {
|
|
val fast = new lila.memo.ExpireSetMemo(7 minutes)
|
|
val slow = new lila.memo.ExpireSetMemo(2 hours)
|
|
def get(gameId: Game.ID) = fast.get(gameId) || slow.get(gameId)
|
|
def put(game: Game) = {
|
|
GameRepo.setTv(game.id)
|
|
(if (game.speed <= chess.Speed.Bullet) fast else slow) put game.id
|
|
}
|
|
}
|
|
|
|
lazy val socketHandler = new SocketHandler(
|
|
hub = hub,
|
|
roundMap = roundMap,
|
|
socketHub = socketHub,
|
|
messenger = messenger,
|
|
evalCacheHandler = evalCacheHandler,
|
|
selfReport = selfReport,
|
|
bus = bus,
|
|
isRecentTv = recentTvGames get _
|
|
)
|
|
|
|
lazy val perfsUpdater = new PerfsUpdater(historyApi, rankingApi)
|
|
|
|
lazy val forecastApi: ForecastApi = new ForecastApi(
|
|
coll = db(CollectionForecast),
|
|
roundMap = roundMap
|
|
)
|
|
|
|
private lazy val notifier = new RoundNotifier(
|
|
timeline = hub.actor.timeline,
|
|
isUserPresent = isUserPresent,
|
|
notifyApi = notifyApi
|
|
)
|
|
|
|
private lazy val finisher = new Finisher(
|
|
messenger = messenger,
|
|
perfsUpdater = perfsUpdater,
|
|
crosstableApi = crosstableApi,
|
|
notifier = notifier,
|
|
playban = playban,
|
|
bus = bus,
|
|
getSocketStatus = getSocketStatus,
|
|
isRecentTv = recentTvGames get _
|
|
)
|
|
|
|
private lazy val rematcher = new Rematcher(
|
|
messenger = messenger,
|
|
onStart = onStart,
|
|
rematch960Cache = rematch960Cache,
|
|
bus = bus
|
|
)
|
|
|
|
private lazy val player: Player = new Player(
|
|
fishnetPlayer = fishnetPlayer,
|
|
bus = bus,
|
|
finisher = finisher,
|
|
uciMemo = uciMemo
|
|
)
|
|
|
|
private lazy val drawer = new Drawer(
|
|
prefApi = prefApi,
|
|
messenger = messenger,
|
|
finisher = finisher,
|
|
isBotSync = isBotSync,
|
|
bus = bus
|
|
)
|
|
|
|
lazy val messenger = new Messenger(
|
|
chat = hub.actor.chat
|
|
)
|
|
|
|
def getSocketStatus(gameId: Game.ID): Fu[SocketStatus] =
|
|
socketHub.ask[SocketStatus](gameId, GetSocketStatus)
|
|
|
|
private def isUserPresent(game: Game, userId: lila.user.User.ID): Fu[Boolean] =
|
|
socketHub.ask[Boolean](game.id, HasUserId(userId))
|
|
|
|
lazy val jsonView = new JsonView(
|
|
noteApi = noteApi,
|
|
userJsonView = userJsonView,
|
|
getSocketStatus = getSocketStatus,
|
|
canTakeback = takebacker.isAllowedByPrefs,
|
|
divider = divider,
|
|
evalCache = evalCache,
|
|
baseAnimationDuration = AnimationDuration,
|
|
moretimeSeconds = MoretimeDuration.toSeconds.toInt
|
|
)
|
|
|
|
lazy val noteApi = new NoteApi(db(CollectionNote))
|
|
|
|
MoveMonitor.start(system, moveTimeChannel)
|
|
|
|
system.actorOf(
|
|
Props(new Titivate(roundMap, hub.actor.bookmark, hub.actor.chat)),
|
|
name = "titivate"
|
|
)
|
|
|
|
bus.subscribe(system.actorOf(
|
|
Props(new CorresAlarm(db(CollectionAlarm), socketHub)),
|
|
name = "corres-alarm"
|
|
), 'moveEventCorres, 'finishGame)
|
|
|
|
lazy val takebacker = new Takebacker(
|
|
messenger = messenger,
|
|
uciMemo = uciMemo,
|
|
prefApi = prefApi,
|
|
bus = bus
|
|
)
|
|
|
|
val tvBroadcast = system.actorOf(Props(classOf[TvBroadcast]))
|
|
bus.subscribe(tvBroadcast, 'moveEvent, 'changeFeaturedGame)
|
|
|
|
def checkOutoftime(game: Game): Unit = {
|
|
if (game.playable && game.started && !game.isUnlimited)
|
|
roundMap.tell(game.id, actorApi.round.QuietFlag)
|
|
}
|
|
|
|
def resign(pov: Pov): Unit =
|
|
if (pov.game.abortable) roundMap.tell(pov.gameId, Abort(pov.playerId))
|
|
else if (pov.game.resignable) roundMap.tell(pov.gameId, Resign(pov.playerId))
|
|
}
|
|
|
|
object Env {
|
|
|
|
lazy val current = "round" boot new Env(
|
|
config = lila.common.PlayApp loadConfig "round",
|
|
system = lila.common.PlayApp.system,
|
|
db = lila.db.Env.current,
|
|
hub = lila.hub.Env.current,
|
|
fishnetPlayer = lila.fishnet.Env.current.player,
|
|
aiPerfApi = lila.fishnet.Env.current.aiPerfApi,
|
|
crosstableApi = lila.game.Env.current.crosstableApi,
|
|
playban = lila.playban.Env.current.api,
|
|
lightUser = lila.user.Env.current.lightUser,
|
|
userJsonView = lila.user.Env.current.jsonView,
|
|
rankingApi = lila.user.Env.current.rankingApi,
|
|
notifyApi = lila.notify.Env.current.api,
|
|
uciMemo = lila.game.Env.current.uciMemo,
|
|
rematch960Cache = lila.game.Env.current.cached.rematch960,
|
|
onStart = lila.game.Env.current.onStart,
|
|
divider = lila.game.Env.current.divider,
|
|
prefApi = lila.pref.Env.current.api,
|
|
historyApi = lila.history.Env.current.api,
|
|
evalCache = lila.evalCache.Env.current.api,
|
|
evalCacheHandler = lila.evalCache.Env.current.socketHandler,
|
|
isBotSync = lila.user.Env.current.lightUserApi.isBotSync
|
|
)
|
|
}
|