more progress on pools

pull/87/head
Thibault Duplessis 2014-06-09 17:33:41 +02:00
parent aa158fcc63
commit a1680d2221
12 changed files with 96 additions and 33 deletions

View File

@ -56,6 +56,24 @@ trait UserHelper { self: I18nHelper with StringHelper =>
}
}
def lightUserLink(
user: LightUser,
cssClass: Option[String] = None,
withOnline: Boolean = true,
withTitle: Boolean = true,
truncate: Option[Int] = None,
params: String = ""): Html = Html {
userIdNameLink(
userId = user.id,
username = user.name,
title = user.title,
cssClass = cssClass,
withOnline = withOnline,
withTitle = withTitle,
truncate = truncate,
params = params)
}
def userIdLink(
userId: String,
cssClass: Option[String]): Html = userIdLink(userId.some, cssClass)

View File

@ -5,18 +5,18 @@ case (p, it) => {
<div class="pool_@(it + 1)">
<h2>@p.setup.name</h2>
<div class="body">
@defining(4) { displayedUsers =>
<table class="users">
@defining(4) { displayedPlayers =>
<table class="players">
<tbody>
@p.sortedUsers.take(displayedUsers).map { user =>
@p.playersAround(ctx.me, displayedPlayers).map { player =>
<tr>
<td>@userLink(user, withRating = false)</td>
<td>@p.setup.glickoLens(user).intRating</td>
<td>@lightUserLink(player.user)</td>
<td>@player.rating</td>
</tr>
}
@defining(p.nbUsers - displayedUsers) { moreUsers =>
@if(moreUsers > 0) {
<tr><td colspan=2>And 15 more</td></tr>
@defining(p.nbPlayers - displayedPlayers) { morePlayers =>
@if(morePlayers > 0) {
<tr><td colspan=2>And @morePlayers more</td></tr>
}
}
</tbody>

View File

@ -4,20 +4,20 @@
<table class="slist standing">
<thead>
<tr>
<th class="large">@trans.standing() (@p.nbUsers)</th>
<th class="large">@trans.standing() (@p.nbPlayers)</th>
<th></th>
</tr>
</thead>
<tbody>
@p.rankedUsers.map {
case (user, rank) => {
<tr @if(ctx is user) { class="me" }>
@p.rankedPlayers.map {
case (player, rank) => {
<tr @if(ctx.userId ?? (player.user.id==)) { class="me" }>
<td class="name">
<span class="rank">@rank</span>
@userLink(user, withRating = false)
@lightUserLink(player.user)
</td>
<td class="rating">
@p.setup.glickoLens(user).intRating
@player.rating
</td>
</tr>
}

View File

@ -48,6 +48,7 @@ final class Env(
history = new History(ttl = HistoryMessageTtl),
uidTimeout = UidTimeout,
lightUser = lightUser,
isOnline = isOnline,
renderer = hub.actor.renderer)
}), name = SocketName)

View File

@ -0,0 +1,6 @@
package lila.pool
case class Player(user: lila.common.LightUser, rating: Int) {
def is(p: Player) = user.id == p.user.id
}

View File

@ -1,28 +1,43 @@
package lila.pool
import lila.common.LightUser
import lila.user.User
case class Pool(
setup: PoolSetup,
users: List[User]) {
players: List[Player]) {
lazy val sortedUsers = users.sortBy(u => -setup.glickoLens(u).intRating)
lazy val sortedPlayers = players.sortBy(-_.rating)
lazy val rankedUsers = sortedUsers.zipWithIndex map {
case (user, rank) => user -> (rank + 1)
lazy val rankedPlayers = sortedPlayers.zipWithIndex map {
case (player, rank) => player -> (rank + 1)
}
lazy val nbUsers = users.size
lazy val nbPlayers = players.size
def contains(u: User) = users contains u
def contains(userId: String): Boolean = players exists (_.user.id == userId)
def contains(u: User): Boolean = contains(u.id)
def contains(p: Player): Boolean = contains(p.user.id)
def withUser(u: User) = copy(users = u :: users).distinctUsers
def withPlayer(p: Player) = copy(players = p :: players).distinctPlayers
def withUser(u: User) = withPlayer(Player(
LightUser(u.id, u.username, u.title),
setup.glickoLens(u).intRating
))
private def distinctUsers = copy(
users = users.map { u =>
u.id -> u
def filterPlayers(cond: Player => Boolean) = copy(players = players filter cond)
private def distinctPlayers = copy(
players = players.map { p =>
p.user.id -> p
}.toMap.values.toList
)
def withoutUser(u: User) = copy(users = users filter (_.id != u.id))
def withoutPlayer(p: Player) = copy(players = players filterNot (_ is p))
def withoutUser(u: User) = copy(players = players filterNot (_.user.id == u.id))
def playersAround(uo: Option[User], nb: Int) = uo.fold(sortedPlayers take nb) { u =>
val rating = setup.glickoLens(u).intRating
players.sortBy(p => math.abs(rating - p.rating)) take nb sortBy (-_.rating)
}
}

View File

@ -8,18 +8,26 @@ import play.api.libs.json._
import actorApi._
import lila.common.LightUser
import lila.hub.actorApi.SendTos
import lila.socket.actorApi.{ Connected => _, _ }
import lila.socket.{ SocketActor, History, Historical }
import lila.hub.actorApi.SendTos
import lila.user.User
private[pool] final class PoolActor(
setup: PoolSetup,
val history: History,
lightUser: String => Option[LightUser],
isOnline: String => Boolean,
renderer: ActorSelection,
uidTimeout: Duration) extends SocketActor[Member](uidTimeout) with Historical[Member] {
private var pool = Pool(setup, Nil)
lila.user.UserRepo randomDudes scala.util.Random.nextInt(500) foreach { users =>
pool = users.foldLeft(pool)(_ withUser _)
}
// last time each user waved to the pool
private val wavers = new lila.memo.ExpireSetMemo(10 seconds)
def receiveSpecific = {
@ -27,12 +35,23 @@ private[pool] final class PoolActor(
case Enter(user) =>
pool = pool withUser user
wavers put user.id
sender ! true
case Wave(user) =>
wavers put user.id
case Leave(user) =>
pool = pool withoutUser user
sender ! true
case Broom =>
broom
pool = pool filterPlayers { p =>
wavers get p.user.id
}
pool.players map (_.user.id) filter isOnline foreach wavers.put
case GetVersion => sender ! history.version
// case StartGame(game) => game.players foreach { player =>
@ -49,7 +68,7 @@ private[pool] final class PoolActor(
import makeTimeout.short
renderer ? RemindPool(pool) foreach {
case html: play.twirl.api.Html =>
val event = SendTos(pool.users.map(_.id).toSet, Json.obj(
val event = SendTos(pool.players.map(_.user.id).toSet, Json.obj(
"t" -> "poolReminder",
"d" -> Json.obj(
"id" -> pool.setup.id,
@ -58,7 +77,7 @@ private[pool] final class PoolActor(
context.system.lilaBus.publish(event, 'users)
}
case Reload => notifyReload
case Reload => notifyReload
case PingVersion(uid, v) =>
ping(uid)
@ -66,8 +85,6 @@ private[pool] final class PoolActor(
history.since(v).fold(resync(m))(_ foreach sendMessage(m))
}
case Broom => broom
case lila.chat.actorApi.ChatLine(_, line) => line match {
case line: lila.chat.UserLine =>
notifyVersionTrollable("message", lila.chat.Line toJson line, troll = line.troll)

View File

@ -30,6 +30,7 @@ private[pool] case object GetPool
private[pool] case object Reload
private[pool] case class Enter(user: User)
private[pool] case class Leave(user: User)
private[pool] case class Wave(user: User)
private[pool] case object Pairing
private[pool] case object CheckLeaders
case class RemindPool(pool: Pool)

View File

@ -72,6 +72,8 @@ trait UserRepo {
def usernameById(id: ID) = $primitive.one($select(id), F.username)(_.asOpt[String])
def randomDudes(nb: Int) = $find($query(stableGoodLadSelect) sort BSONDocument("count.games" -> -1) skip scala.util.Random.nextInt(5000), nb)
def rank(user: User) = $count(enabledSelect ++ Json.obj(F.rating -> $gt(Glicko.default.rating))) map (1+)
def orderByGameCount(u1: String, u2: String): Fu[Option[(String, String)]] =

View File

@ -14,7 +14,7 @@ object ApplicationBuild extends Build {
resolvers ++= Dependencies.Resolvers.commons,
scalacOptions := compilerOptions,
incOptions := incOptions.value.withNameHashing(true),
offline := false,
offline := true,
libraryDependencies ++= Seq(
scalaz, scalalib, hasher, config, apache, scalaTime,
csv, jgit, actuarius, elastic4s, findbugs, RM,

View File

@ -219,6 +219,7 @@ body.dark div.content_box .loader:before,
body.dark div.content_box .loader:after {
background-color: #2b2b2b;
}
body.dark #pool_list h2,
body.dark #timeline,
body.dark #timeline > .entry {
border-color: #2b2b2b;
@ -416,6 +417,10 @@ body.dark div.user_show div.user-infos.scroll-shadow-hard {
background-size: 100% 20px, 100% 20px, 100% 10px, 100% 10px;
background-attachment: local, local, scroll, scroll;
}
body.dark #pool_list > div {
background: rgba(0, 0, 0, 0.4);
box-shadow: 0 0 2px rgba(255,255,255,0.2);
}
body.dark::-webkit-input-placeholder {
color: #666;
}

View File

@ -14,6 +14,4 @@
width: 246px;
max-height: 510px;
overflow-y: hidden;
border: 1px solid #ccc;
padding: 3px;
}