tournament wip
This commit is contained in:
parent
e08ed0f447
commit
e811f36dd8
|
@ -48,6 +48,21 @@ object Tournament extends LilaController {
|
|||
}
|
||||
}
|
||||
|
||||
def withdraw(id: String) = Auth { implicit ctx ⇒
|
||||
implicit me ⇒
|
||||
IOptionIORedirect(repo createdById id) { tour ⇒
|
||||
api.withdraw(tour, me) map { _ ⇒ routes.Tournament.show(tour.id) }
|
||||
}
|
||||
}
|
||||
|
||||
def userList(id: String) = Open { implicit ctx ⇒
|
||||
IOptionIOk(repo byId id) { tour ⇒
|
||||
userRepo byIds tour.data.users map { users ⇒
|
||||
html.tournament.userList(users)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
def form = Auth { implicit ctx ⇒
|
||||
me ⇒
|
||||
Ok(html.tournament.form(forms.create))
|
||||
|
|
|
@ -131,6 +131,7 @@ final class Settings(config: Config) {
|
|||
val ActorLobbyHub = "lobby_hub"
|
||||
val ActorMonitorHub = "monitor_hub"
|
||||
val ActorTournamentHubMaster = "tournament_hub_master"
|
||||
val ActorTournamentOrganizer = "tournament_organizer"
|
||||
|
||||
val ModlogCollectionModlog = getString("modlog.collection.modlog")
|
||||
|
||||
|
|
|
@ -41,6 +41,8 @@ final class Hub(
|
|||
notifyVersion("talk", JsString(message.render))
|
||||
}
|
||||
|
||||
case ReloadUserList ⇒ notifyVersion("users", JsNull)
|
||||
|
||||
case GetTournamentVersion(_) ⇒ sender ! history.version
|
||||
|
||||
case Join(uid, user, version) ⇒ {
|
||||
|
|
|
@ -30,6 +30,8 @@ final class HubMaster(
|
|||
|
||||
case msg @ SendTo(_, _) ⇒ hubs.values foreach (_ ! msg)
|
||||
|
||||
case Forward(id, msg) ⇒ hubs.get(id).foreach(_ ! msg)
|
||||
|
||||
case GetHub(id: String) ⇒ sender ! {
|
||||
(hubs get id) | {
|
||||
mkHub(id) ~ { h ⇒ hubs = hubs + (id -> h) }
|
||||
|
|
31
app/tournament/Organizer.scala
Normal file
31
app/tournament/Organizer.scala
Normal file
|
@ -0,0 +1,31 @@
|
|||
package lila
|
||||
package tournament
|
||||
|
||||
import akka.actor._
|
||||
import akka.actor.ReceiveTimeout
|
||||
import akka.util.duration._
|
||||
import akka.util.Timeout
|
||||
import akka.pattern.{ ask, pipe }
|
||||
import akka.dispatch.{ Future, Promise }
|
||||
import play.api.libs.concurrent._
|
||||
import play.api.Play.current
|
||||
|
||||
final class Organizer(
|
||||
api: TournamentApi,
|
||||
repo: TournamentRepo) extends Actor {
|
||||
|
||||
implicit val timeout = Timeout(1 second)
|
||||
implicit val executor = Akka.system.dispatcher
|
||||
|
||||
def receive = {
|
||||
|
||||
case StartTournament => startTournaments.unsafePerformIO
|
||||
}
|
||||
|
||||
def startTournament = for {
|
||||
tours <- repo.created
|
||||
} yield (tours filter (_.readyToStart) map api.start).sequence
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -27,6 +27,10 @@ final class Socket(
|
|||
private val timeoutDuration = 1 second
|
||||
implicit private val timeout = Timeout(timeoutDuration)
|
||||
|
||||
def reloadUserList(tournamentId: String) {
|
||||
hubMaster ! Forward(tournamentId, ReloadUserList)
|
||||
}
|
||||
|
||||
def join(
|
||||
tournamentId: String,
|
||||
version: Option[Int],
|
||||
|
|
|
@ -13,19 +13,22 @@ case class Data(
|
|||
minUsers: Int,
|
||||
createdAt: DateTime,
|
||||
createdBy: String,
|
||||
users: List[String]) {
|
||||
users: List[String])
|
||||
|
||||
sealed trait Tournament {
|
||||
|
||||
val id: String
|
||||
val data: Data
|
||||
def encode: RawTournament
|
||||
|
||||
import data._
|
||||
|
||||
lazy val duration = new Duration(minutes * 60 * 1000)
|
||||
|
||||
def missingUsers = minUsers - users.size
|
||||
|
||||
def contains(username: String) = users contains username
|
||||
}
|
||||
|
||||
sealed trait Tournament {
|
||||
|
||||
def id: String
|
||||
def encode: RawTournament
|
||||
def contains(username: String): Boolean = users contains username
|
||||
def contains(user: User): Boolean = contains(user.id)
|
||||
|
||||
def showClock = "2 + 0"
|
||||
}
|
||||
|
@ -34,12 +37,23 @@ case class Created(
|
|||
id: String,
|
||||
data: Data) extends Tournament {
|
||||
|
||||
import data._
|
||||
|
||||
def readyToStart = users.size >= minUsers
|
||||
|
||||
def encode = RawTournament.created(id, data)
|
||||
|
||||
def join(user: User): Valid[Created] = (data contains user.id).fold(
|
||||
def join(user: User): Valid[Created] = contains(user).fold(
|
||||
!!("User %s is already part of the tournament" format user.id),
|
||||
copy(data = data.copy(users = data.users :+ user.id)).success
|
||||
withUsers(users :+ user.id).success
|
||||
)
|
||||
|
||||
def withdraw(user: User): Valid[Created] = contains(user).fold(
|
||||
withUsers(users filterNot (user.id ==)).success,
|
||||
!!("User %s is not part of the tournament" format user.id)
|
||||
)
|
||||
|
||||
private def withUsers(x: List[String]) = copy(data = data.copy(users = x))
|
||||
}
|
||||
|
||||
case class RawTournament(
|
||||
|
@ -85,7 +99,7 @@ object Tournament {
|
|||
val minuteDefault = 10
|
||||
val minuteChoices = options(minutes, "%d minute{s}")
|
||||
|
||||
val minUsers = 5 to 30 by 5
|
||||
val minUsers = (2 to 4) ++ (5 to 30 by 5)
|
||||
val minUserDefault = 10
|
||||
val minUserChoices = options(minUsers, "%d players{s}")
|
||||
}
|
||||
|
|
|
@ -8,7 +8,8 @@ import scalaz.effects._
|
|||
import user.User
|
||||
|
||||
final class TournamentApi(
|
||||
repo: TournamentRepo) {
|
||||
repo: TournamentRepo,
|
||||
socket: Socket) {
|
||||
|
||||
def createTournament(setup: TournamentSetup, me: User): IO[Created] = {
|
||||
val tournament = Tournament(
|
||||
|
@ -21,7 +22,18 @@ final class TournamentApi(
|
|||
}
|
||||
|
||||
def join(tour: Created, me: User): IO[Unit] = (tour join me).fold(
|
||||
err => putStrLn(err.shows),
|
||||
tour2 ⇒ repo saveIO tour2
|
||||
err ⇒ putStrLn(err.shows),
|
||||
tour2 ⇒ for {
|
||||
_ ← repo saveIO tour2
|
||||
_ ← io(socket reloadUserList tour.id)
|
||||
} yield ()
|
||||
)
|
||||
|
||||
def withdraw(tour: Created, me: User): IO[Unit] = (tour withdraw me).fold(
|
||||
err ⇒ putStrLn(err.shows),
|
||||
tour2 ⇒ for {
|
||||
_ ← repo saveIO tour2
|
||||
_ ← io(socket reloadUserList tour.id)
|
||||
} yield ()
|
||||
)
|
||||
}
|
||||
|
|
|
@ -30,7 +30,8 @@ final class TournamentEnv(
|
|||
collection = mongodb(TournamentCollectionTournament))
|
||||
|
||||
lazy val api = new TournamentApi(
|
||||
repo = repo)
|
||||
repo = repo,
|
||||
socket = socket)
|
||||
|
||||
lazy val roomRepo = new RoomRepo(
|
||||
collection = mongodb(TournamentCollectionRoom)
|
||||
|
@ -55,4 +56,9 @@ final class TournamentEnv(
|
|||
uidTimeout = TournamentUidTimeout,
|
||||
hubTimeout = TournamentHubTimeout
|
||||
)), name = ActorTournamentHubMaster)
|
||||
|
||||
lazy val organizer = Akka.system.actorOf(Props(new HubMaster(
|
||||
repo = repo,
|
||||
api = api
|
||||
)), name = ActorTournamentOrganizer)
|
||||
}
|
||||
|
|
|
@ -33,5 +33,10 @@ case class Talk(u: String, txt: String)
|
|||
case class GetTournamentVersion(tournamentId: String)
|
||||
case class CloseTournament(tournamentId: String)
|
||||
case class GetHub(tournamentId: String)
|
||||
case class Forward(tournamentId: String, msg: Any)
|
||||
case object ReloadUserList
|
||||
case object HubTimeout
|
||||
case object GetNbHubs
|
||||
|
||||
// organizer
|
||||
case object StartTournament
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
@(tour: lila.tournament.Created, roomHtml: Html, version: Int, users: List[User])(implicit ctx: Context)
|
||||
|
||||
@import tour.data
|
||||
@import tour._
|
||||
|
||||
@title = @{ tour.showClock + " tournament" }
|
||||
|
||||
|
@ -12,17 +12,23 @@ title = title) {
|
|||
|
||||
<h1>@title pending</h1>
|
||||
<div>
|
||||
Waiting for @data.missingUsers players
|
||||
Waiting for @missingUsers players
|
||||
</div>
|
||||
<div>
|
||||
Players: @users.map { user =>
|
||||
@userLink(user)
|
||||
}
|
||||
<div class="user_list" data-href="@routes.Tournament.userList(id)">
|
||||
@tournament.userList(users)
|
||||
</div>
|
||||
|
||||
@ctx.me.map { me =>
|
||||
<div>
|
||||
<form action="@routes.Tournament.join(tour.id)" method="POST">
|
||||
<input type="submit" class="submit button" />
|
||||
@if(tour contains me) {
|
||||
<form action="@routes.Tournament.withdraw(tour.id)" method="POST">
|
||||
<input type="submit" class="submit button" value="Withdraw" />
|
||||
</form>
|
||||
} else {
|
||||
<form action="@routes.Tournament.join(tour.id)" method="POST">
|
||||
<input type="submit" class="submit button" value="Join tournament" />
|
||||
</form>
|
||||
}
|
||||
</div>
|
||||
}
|
||||
}
|
||||
|
|
6
app/views/tournament/userList.scala.html
Normal file
6
app/views/tournament/userList.scala.html
Normal file
|
@ -0,0 +1,6 @@
|
|||
@(users: List[User])(implicit ctx: Context)
|
||||
|
||||
Players: @users.map { user =>
|
||||
@userLink(user)
|
||||
}
|
||||
|
|
@ -45,6 +45,8 @@ POST /tournament/new controllers.Tournament.create
|
|||
GET /tournament/$id<[\w\-]{8}> controllers.Tournament.show(id: String)
|
||||
GET /tournament/$id<[\w\-]{8}>/socket controllers.Tournament.websocket(id: String)
|
||||
POST /tournament/$id<[\w\-]{8}>/join controllers.Tournament.join(id: String)
|
||||
POST /tournament/$id<[\w\-]{8}>/withdraw controllers.Tournament.withdraw(id: String)
|
||||
GET /tournament/$id<[\w\-]{8}>/users controllers.Tournament.userList(id: String)
|
||||
|
||||
# Analyse
|
||||
GET /analyse/$gameId<[\w\-]{8}> controllers.Analyse.replay(gameId: String, color: String = "white")
|
||||
|
|
|
@ -9,6 +9,7 @@ $(function() {
|
|||
var $chat = $("div.lichess_chat");
|
||||
var $chatToggle = $chat.find('input.toggle_chat');
|
||||
var chatExists = $chat.length > 0;
|
||||
var $userList = $wrap.find("div.user_list");
|
||||
var socketUrl = $wrap.data("socket-url");
|
||||
|
||||
if (chatExists) {
|
||||
|
@ -50,9 +51,14 @@ $(function() {
|
|||
$('body').trigger('lichess.content_loaded');
|
||||
}
|
||||
|
||||
function reloadUserList() {
|
||||
$userList.load($userList.data("href"));
|
||||
}
|
||||
|
||||
lichess.socket = new $.websocket(lichess.socketUrl + socketUrl, lichess_data.version, $.extend(true, lichess.socketDefaults, {
|
||||
events: {
|
||||
talk: function(e) { if (chatExists) addToChat(e); }
|
||||
talk: function(e) { if (chatExists) addToChat(e); },
|
||||
users: reloadUserList
|
||||
},
|
||||
options: {
|
||||
name: "tournament"
|
||||
|
|
Loading…
Reference in a new issue