lila/modules/relay/src/main/RelayRound.scala

153 lines
4.2 KiB
Scala

package lila.relay
import org.joda.time.DateTime
import lila.study.{ Chapter, Study }
import lila.user.User
case class RelayRound(
_id: RelayRound.Id,
tourId: RelayTour.Id,
name: String,
sync: RelayRound.Sync,
/* When it's planned to start */
startsAt: Option[DateTime],
/* When it actually starts */
startedAt: Option[DateTime],
/* at least it *looks* finished... but maybe it's not
* sync.nextAt is used for actually synchronising */
finished: Boolean,
createdAt: DateTime
) {
def id = _id
def studyId = Study.Id(id.value)
lazy val slug = {
val s = lila.common.String slugify name
if (s.isEmpty) "-" else s
}
def finish =
copy(
finished = true,
sync = sync.pause
)
def resume =
copy(
finished = false,
sync = sync.play
)
def ensureStarted =
copy(
startedAt = startedAt orElse DateTime.now.some
)
def hasStarted = startedAt.isDefined
def hasStartedEarly = hasStarted && startsAt.exists(_ isAfter DateTime.now)
def shouldHaveStarted = hasStarted || startsAt.exists(_ isBefore DateTime.now)
def shouldGiveUp =
!hasStarted && (startsAt match {
case Some(at) => at.isBefore(DateTime.now minusHours 3)
case None => createdAt.isBefore(DateTime.now minusDays 1)
})
def withSync(f: RelayRound.Sync => RelayRound.Sync) = copy(sync = f(sync))
def withTour(tour: RelayTour) = RelayRound.WithTour(this, tour)
override def toString = s"""relay #$id "$name" $sync"""
}
object RelayRound {
case class Id(value: String) extends AnyVal with StringValue
def makeId = Id(lila.common.ThreadLocalRandom nextString 8)
case class Sync(
upstream: Option[Sync.Upstream], // if empty, needs a client to push PGN
until: Option[DateTime], // sync until then; resets on move
nextAt: Option[DateTime], // when to run next sync
delay: Option[Int], // override time between two sync (rare)
log: SyncLog
) {
def hasUpstream = upstream.isDefined
def renew =
if (hasUpstream) copy(until = DateTime.now.plusHours(1).some)
else pause
def ongoing = until ?? DateTime.now.isBefore
def play =
if (hasUpstream) renew.copy(nextAt = nextAt orElse DateTime.now.plusSeconds(3).some)
else pause
def pause =
copy(
nextAt = none,
until = none
)
def seconds: Option[Int] =
until map { u =>
(u.getSeconds - nowSeconds).toInt
} filter (0 <)
def playing = nextAt.isDefined
def paused = !playing
def addLog(event: SyncLog.Event) = copy(log = log add event)
def clearLog = copy(log = SyncLog.empty)
override def toString = upstream.toString
}
object Sync {
sealed trait Upstream {
def asUrl: Option[UpstreamUrl] = this match {
case url: UpstreamUrl => url.some
case _ => none
}
def local = asUrl.fold(true)(_.isLocal)
}
case class UpstreamUrl(url: String) extends Upstream {
def isLocal = url.contains("://127.0.0.1") || url.contains("://localhost")
def withRound =
url.split(" ", 2) match {
case Array(u, round) => UpstreamUrl.WithRound(u, round.toIntOption)
case _ => UpstreamUrl.WithRound(url, none)
}
}
object UpstreamUrl {
case class WithRound(url: String, round: Option[Int])
val LccRegex = """.*view\.livechesscloud\.com/#?([0-9a-f\-]+)""".r
}
case class UpstreamIds(ids: List[lila.game.Game.ID]) extends Upstream
}
trait AndTour {
val round: RelayRound
val tour: RelayTour
def fullName = s"${tour.name}${round.name}"
def path: String =
s"/broadcast/${tour.slug}/${if (round.slug == tour.slug) "-" else round.slug}/${round.id}"
def path(chapterId: Chapter.Id): String = s"$path/$chapterId"
}
case class WithTour(round: RelayRound, tour: RelayTour) extends AndTour {
def withStudy(study: Study) = WithTourAndStudy(round, tour, study)
}
case class WithTourAndStudy(relay: RelayRound, tour: RelayTour, study: Study) {
def path = WithTour(relay, tour).path
def fullName = WithTour(relay, tour).fullName
}
}