diff --git a/app/Env.scala b/app/Env.scala
index 983e89be8f..3469288d4d 100644
--- a/app/Env.scala
+++ b/app/Env.scala
@@ -29,6 +29,7 @@ final class Env(
bookmarkApi = Env.bookmark.api,
eloCalculator = Env.round.eloCalculator,
relationApi = Env.relation.api,
+ gameCached = Env.game.cached,
postApi = Env.forum.postApi,
getRank = Env.user.ranking.get) _
diff --git a/app/Global.scala b/app/Global.scala
index e6928191e4..13c9108921 100644
--- a/app/Global.scala
+++ b/app/Global.scala
@@ -18,9 +18,8 @@ object Global extends GlobalSettings {
else Action(NotFound("I am an AI server")).some
}
else {
- // if (!Env.api.isProd) println(req)
Env.monitor.reporting ! AddRequest
- Env.security.wiretap(req)
+ // Env.security.wiretap(req)
Env.security.firewall.requestHandler(req).await orElse
Env.i18n.requestHandler(req) orElse
super.onRouteRequest(req)
diff --git a/app/controllers/Setup.scala b/app/controllers/Setup.scala
index 421cb24f2e..ec11ed4932 100644
--- a/app/controllers/Setup.scala
+++ b/app/controllers/Setup.scala
@@ -63,7 +63,7 @@ object Setup extends LilaController with TheftPrevention {
def hook(uid: String) = OpenBody { implicit ctx ⇒
implicit val req = ctx.body
env.forms.hook(ctx).bindFromRequest.value ?? { config ⇒
- env.processor.hook(config, uid, lila.common.HTTPRequest sid req)
+ env.processor.hook(config, uid, lila.common.HTTPRequest sid req, ctx.me)
}
}
diff --git a/app/controllers/Tv.scala b/app/controllers/Tv.scala
index f120b22006..82fcaf1c5b 100644
--- a/app/controllers/Tv.scala
+++ b/app/controllers/Tv.scala
@@ -36,7 +36,7 @@ object Tv extends LilaController {
private def confrontation(game: GameModel): Fu[Option[Confrontation]] = ~{
(game.creator.userId |@| game.invited.userId) apply {
case (id1, id2) ⇒ (UserRepo byId id1) zip (UserRepo byId id2) flatMap {
- case (Some(user1), Some(user2)) ⇒ GameRepo.confrontation(user1, user2) map (_.some)
+ case (Some(user1), Some(user2)) ⇒ Env.game.cached.confrontation(user1, user2) map (_.some)
case _ ⇒ fuccess(none)
}
}
diff --git a/app/mashup/UserInfo.scala b/app/mashup/UserInfo.scala
index 6b8af6c953..26e072ab26 100644
--- a/app/mashup/UserInfo.scala
+++ b/app/mashup/UserInfo.scala
@@ -36,16 +36,17 @@ object UserInfo {
bookmarkApi: BookmarkApi,
eloCalculator: EloCalculator,
relationApi: RelationApi,
+ gameCached: lila.game.Cached,
postApi: PostApi,
getRank: String ⇒ Fu[Option[Int]])(user: User, ctx: Context): Fu[UserInfo] =
(getRank(user.id) flatMap {
_ ?? { rank ⇒ countUsers() map { nb ⇒ (rank -> nb).some } }
}) zip
((ctx is user) ?? {
- GameRepo count (_ notFinished user.id) map (_.some)
+ gameCached nbPlaying user.id map (_.some)
}) zip
(ctx.me.filter(user!=) ?? { me ⇒
- GameRepo.confrontation(me, user) map (_.some filterNot (_.empty))
+ gameCached.confrontation(me, user) map (_.some filterNot (_.empty))
}) zip
(bookmarkApi countByUser user) zip
EloChart(user) zip
diff --git a/app/views/user/confrontation.scala.html b/app/views/user/confrontation.scala.html
index 6b4aa2c93b..8de55e1dda 100644
--- a/app/views/user/confrontation.scala.html
+++ b/app/views/user/confrontation.scala.html
@@ -1,7 +1,7 @@
@(c: lila.user.Confrontation)(implicit ctx: Context)
-
@userLink(c.user1, withElo = false, withOnline = false) vs @userLink(c.user2, withElo = false, withOnline = false)
+
@userIdLink(c.user1.some, withOnline = false) vs @userIdLink(c.user2.some, withOnline = false)
@trans.nbWins("
"+c.wins+""),
@trans.nbDraws("
"+c.draws+""),
@trans.nbLosses("
"+c.losses+"")
diff --git a/conf/messages.bs b/conf/messages.bs
index ce5aed6e35..1c5784ffbd 100644
--- a/conf/messages.bs
+++ b/conf/messages.bs
@@ -1,87 +1,100 @@
-playWithAFriend=Igraj s prijateljem
+playWithAFriend=Igraj protiv prijatelja
inviteAFriendToPlayWithYou=Pozovi prijatelja
playWithTheMachine=Igraj protiv računara
challengeTheArtificialIntelligence=Izazovi vještačku inteligenciju
-toInviteSomeoneToPlayGiveThisUrl=Kako bi pozvao nekoga, pošalji slijedeći link:
-gameOver=Kraj igre
-waitingForOpponent=Čekaj protivnika
-waiting=Čekamo
-yourTurn=Ti si na potezu
-aiNameLevelAiLevel=%s Nivo %s
-level=Nivo
-toggleTheChat=Aktiviraj/deaktiviraj chat
-toggleSound=Uključi/Isključi zvuk
-chat=Chat
-resign=Predaj
+toInviteSomeoneToPlayGiveThisUrl=Za poziv u igru, posalji ovu adresu
+gameOver=Igra je završila
+waitingForOpponent=Čekanje protivnika
+waiting=% na potezu
+yourTurn=Vi ste na potezu
+aiNameLevelAiLevel=%s Razina %s
+level=Razina
+toggleTheChat=Uključi/isključi dopisivanje
+toggleSound=Uključi/ isključi zvuk
+chat=Dopisivanje
+resign=Predati igru
checkmate=Šah-mat
stalemate=Pat
white=Bijeli
black=Crni
-createAGame=Kreiraj partiju
-noGameAvailableRightNowCreateOne=Trenutno nema raspoložive partije, kreiraj je!
-whiteIsVictorious=Bijeli pobjeđuje
-blackIsVictorious=Crni pobjeđuje
-playWithTheSameOpponentAgain=Igraj opet sa istim protivnikom
+createAGame=Stvoriti igru
+noGameAvailableRightNowCreateOne=Nema dostupnih igara
+whiteIsVictorious=Bijeli je pobjednik
+blackIsVictorious=Crni je pobjednik
+playWithTheSameOpponentAgain=Igrati ponovo sa istim protivnikom
newOpponent=Novi protivnik
-playWithAnotherOpponent=Igraj protiv nekog drugog
-yourOpponentWantsToPlayANewGameWithYou=Tvoj protivnik želi opet ograti s tobom
-joinTheGame=Uključi se u igru
+playWithAnotherOpponent=Igrati sa drugim protivnikom
+yourOpponentWantsToPlayANewGameWithYou=Vaš protivnik želi novu igru
+joinTheGame=Pridruži se igri
whitePlays=Bijeli je na potezu
blackPlays=Crni je na potezu
-theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim=Protivnički igrač je napustio igru. Možete tražiti da preda partiju ili čekati.
-makeYourOpponentResign=Tražite da preda igru.
-forceResignation=Tražite da preda igru.
-talkInChat=Chat
-theFirstPersonToComeOnThisUrlWillPlayWithYou=Prva osoba koja iskoristi ovaj link će igrati s vama
-whiteCreatesTheGame=Bijeli kreira igru
-blackCreatesTheGame=Crni kreira igru
-whiteJoinsTheGame=Bijeli se pridružio
-blackJoinsTheGame=Crni se pridružio
+theOtherPlayerHasLeftTheGameYouCanForceResignationOrWaitForHim=Vaš protivnik je napustio igru. Možete ga prisiliti na predaju ili čekati.
+makeYourOpponentResign=Tražite da protivnik preda igru
+forceResignation=Prisilite protivnika na predaju
+talkInChat=Dopisivati se
+theFirstPersonToComeOnThisUrlWillPlayWithYou=Prva osoba koja se pojavi, na ovoj adresi,će igrati protiv vas
+whiteCreatesTheGame=Bijeli stvara igru
+blackCreatesTheGame=Crni stvara igru
+whiteJoinsTheGame=Bijeli se pridružuje igri
+blackJoinsTheGame=Crni se pridružuje igri
whiteResigned=Bijeli je predao
blackResigned=Crni je predao
whiteLeftTheGame=Bijeli je napustio igru
blackLeftTheGame=Crni je napustio igru
-shareThisUrlToLetSpectatorsSeeTheGame=Podijeli ovaj link kako bi posmatrači mogli vidjeti igru
-youAreViewingThisGameAsASpectator=Gledate ovu igru kao promatrač
-replayAndAnalyse=Repriza i analiza
-viewGameStats=Pogledaj statistiku igre
-flipBoard=Zamijeni strane
-threefoldRepetition=Pozicija tri puta ponovljena
-claimADraw=Zatraži neriješeno
-offerDraw=Ponudi remi
-draw=neriješeno
-nbConnectedPlayers=%s spojenih igrača
-talkAboutChessAndDiscussLichessFeaturesInTheForum=Razgovaraj o šahu i raspravljaj lichess značajke na forumu
-seeTheGamesBeingPlayedInRealTime=Pogledaj trenutne igre
+shareThisUrlToLetSpectatorsSeeTheGame=Podijeliti ovaj link kako bi drugi mogli vidjeti igru
+youAreViewingThisGameAsASpectator=Ovdje ste kao promatrač
+replayAndAnalyse=Ponovi i analiziraj
+computerAnalysisInProgress=Računarska analiza u tijeku
+theComputerAnalysisYouRequestedIsNowAvailable=Računarska analiza koju ste trazili je sada dostupna
+theComputerAnalysisHasFailed=Raćunarska analiza nije uspjela
+viewTheComputerAnalysis=Pogledati računarsku analizu
+requestAComputerAnalysis=Zahtijevaj računarsku analizu
+blunders=Zabuna
+mistakes=Greški
+inaccuracies=Nepravilnosti
+viewGameStats=Pogledati statistiku igre
+flipBoard=Okrenuti ploču
+threefoldRepetition=Trostruko ponavljanje
+claimADraw=Zahtijevati remi
+offerDraw=Ponuditi remi
+draw=Remi
+nbConnectedPlayers=%s prisutnih igrača
+talkAboutChessAndDiscussLichessFeaturesInTheForum=Razgovarati o šahu i diskutovati o lichess svojstvima na forumu
+seeTheGamesBeingPlayedInRealTime=Pogledati trenutne igre
gamesBeingPlayedRightNow=Trenutne igre
-viewAllNbGames=Pogledaj sve %s igre
-viewNbCheckmates=Pogledaj %s matova
-nbBookmarks=%s Favoriti
+viewAllNbGames=%s igre
+viewNbCheckmates=%s Šah - matovi
+nbBookmarks=%s Označeno
nbPopularGames=%s Popularne Igre
nbAnalysedGames=%s Analizirane Igre
-bookmarkedByNbPlayers=Dodano u favorite od strane %s igraca
-viewInFullSize=Pogledaj u punoj veličini
+bookmarkedByNbPlayers=Igra je označena od strane %s protivnika
+viewInFullSize=Pogledati u punoj veličini
logOut=Odjava
signIn=Prijava
-signUp=Registracija
-people=Ljudi
+newToLichess=Novi u lichess-u?
+youNeedAnAccountToDoThat=Trebate račun da bi to napravili
+signUp=Registrovati se
+people=Prijatelji
games=Igre
forum=Forum
+xPostedInForumY=%s objavio u forumu %s
chessPlayers=Igrači
-minutesPerSide=Minuta po strani
+minutesPerSide=Minuta po igraču
variant=Varijanta
-timeControl=Upravljanje vremenom
+timeControl=Vremenska kontrola
+time=Vrijeme
start=Početak
username=Korisničko ime
password=Lozinka
-haveAnAccount=Imate li račun?
+haveAnAccount=Već imate raćun?
allYouNeedIsAUsernameAndAPassword=Sve što trebate je korisničko ime i lozinka.
-learnMoreAboutLichess=Nauči više o Lichess-u
-rank=Rank
-gamesPlayed=Igara odigrano
-declineInvitation=Odbi pozivnicu
-cancel=Odgodi
-timeOut=Vrijeme za partiju isteklo
+learnMoreAboutLichess=Naučiti više o Lichess-u
+rank=Poredak
+gamesPlayed=Broj odigranih igara
+nbGamesWithYou=%s odigranih igara protiv vas
+declineInvitation=Odbiti pozivnicu
+cancel=Otkazati
+timeOut=Vrijeme je isteklo
drawOfferSent=Ponuda za remi poslana
drawOfferDeclined=Punuda za remi odbijena
drawOfferAccepted=Ponuda za remi prihvaćena
@@ -90,6 +103,7 @@ yourOpponentOffersADraw=Protivnik nudi remi
accept=Prihvati
decline=Odbij
playingRightNow=Upravo igra
+finished=Završeno
abortGame=Prekini partiju
gameAborted=Partija prekinuta
standard=Standardan
@@ -111,6 +125,10 @@ chatRoom=Razgovaranje
spectatorRoom=Soba za posmatrače
composeMessage=Skladaj poruku
sentMessages=Poslane pruke
+noNewMessages=Nema novih poruka
+subject=Subjekt
+recipient=Primalac
+send=Pošalji
incrementInSeconds=Dodatnih sekundi po potezu
freeOnlineChess=Besplatni internet šah
spectators=Posmatrača
@@ -135,8 +153,10 @@ takebackPropositionCanceled=Prijedlog za povlačenje poteza otkazan
yourOpponentProposesATakeback=Vaš protivnik predlaže da povučete potez
bookmarkThisGame=Dodaj ovu igru u favorite
toggleBackground=Izmijeni boju pozadine
+search=Traži
advancedSearch=Napredna pretraga
tournament=Turnir
+tournaments=Turniri
tournamentPoints=Bodovi na turniru
viewTournament=Pogledaj turnir
freeOnlineChessGamePlayChessNowInACleanInterfaceNoRegistrationNoAdsNoPluginRequiredPlayChessWithComputerFriendsOrRandomOpponents=Besplatni internet šah. Igrajte šah sa čistim interface-om. Registracija nije neophodna, nema reklama, ne zahtijeva browser plugins. Igrajte protiv računara, prijatelja ili slučajnih protivnika.
diff --git a/modules/bookmark/src/main/BookmarkRepo.scala b/modules/bookmark/src/main/BookmarkRepo.scala
index fadb3d87ec..74f6c19b03 100644
--- a/modules/bookmark/src/main/BookmarkRepo.scala
+++ b/modules/bookmark/src/main/BookmarkRepo.scala
@@ -10,9 +10,6 @@ import tube.bookmarkTube
case class Bookmark(game: lila.game.Game, user: lila.user.User)
-// db.bookmark.ensureIndex({g:1})
-// db.bookmark.ensureIndex({u:1})
-// db.bookmark.ensureIndex({d: -1})
private[bookmark] object BookmarkRepo {
def toggle(gameId: String, userId: String): Fu[Boolean] =
diff --git a/modules/game/src/main/Cached.scala b/modules/game/src/main/Cached.scala
index dbabb96eff..f7a2bb09d1 100644
--- a/modules/game/src/main/Cached.scala
+++ b/modules/game/src/main/Cached.scala
@@ -6,14 +6,29 @@ import play.api.libs.json.JsObject
import lila.db.api.$count
import lila.memo.AsyncCache
+import lila.user.{ User, Confrontation }
import tube.gameTube
-private[game] final class Cached(ttl: Duration) {
+final class Cached(ttl: Duration) {
def nbGames: Fu[Int] = count(Query.all)
def nbMates: Fu[Int] = count(Query.mate)
def nbPopular: Fu[Int] = count(Query.popular)
def nbImported: Fu[Int] = count(Query.imported)
+ def nbPlaying(userId: String): Fu[Int] = count(Query notFinished userId)
+
+ def confrontation(user1: User, user2: User): Fu[Confrontation] = {
+ def idGame(user: User) = (user.id, user.count.game)
+ confrontationCache {
+ (user1 < user2).fold(
+ idGame(user1) -> idGame(user2),
+ idGame(user2) -> idGame(user1))
+ }
+ }
+
+ private val confrontationCache =
+ AsyncCache(GameRepo.confrontation, timeToLive = 1.minute)
+
private val count = AsyncCache((o: JsObject) ⇒ $count(o), timeToLive = ttl)
}
diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala
index d0bf8152de..49e3725530 100644
--- a/modules/game/src/main/GameRepo.scala
+++ b/modules/game/src/main/GameRepo.scala
@@ -226,32 +226,37 @@ object GameRepo {
_ sort Query.sortCreated skip (Random nextInt 1000)
)
- // user1 wins, draws, losses
- def confrontation(user1: User, user2: User): Fu[Confrontation] = {
- import reactivemongo.bson._
- import reactivemongo.core.commands._
- val userIds = List(user1, user2).sortBy(_.count.game).map(_.id)
- val command = Aggregate(gameTube.coll.name, Seq(
- Match(BSONDocument(
- "uids" -> BSONDocument("$all" -> userIds),
- "s" -> BSONDocument("$gte" -> chess.Status.Mate.id)
- )),
- GroupField("wid")("nb" -> SumValue(1))
- ))
- gameTube.coll.db.command(command) map { stream ⇒
- val res = (stream.toList map { obj ⇒
- toJSON(obj).asOpt[JsObject] flatMap { o ⇒
- o int "nb" map { nb ⇒
- ~(o str "_id") -> nb
+ // gets 2 users (id, nbGames)
+ // returns user1 wins, draws, losses
+ // the 2 userIds SHOULD be sorted by game count desc
+ // this method is cached in lila.game.Cached
+ private[game] def confrontation(users: ((String, Int), (String, Int))): Fu[Confrontation] = users match {
+ case (user1, user2) ⇒ {
+ import reactivemongo.bson._
+ import reactivemongo.core.commands._
+ val userIds = List(user1, user2).sortBy(_._2).map(_._1)
+ val command = Aggregate(gameTube.coll.name, Seq(
+ Match(BSONDocument(
+ "uids" -> BSONDocument("$all" -> userIds),
+ "s" -> BSONDocument("$gte" -> chess.Status.Mate.id)
+ )),
+ GroupField("wid")("nb" -> SumValue(1))
+ ))
+ gameTube.coll.db.command(command) map { stream ⇒
+ val res = (stream.toList map { obj ⇒
+ toJSON(obj).asOpt[JsObject] flatMap { o ⇒
+ o int "nb" map { nb ⇒
+ ~(o str "_id") -> nb
+ }
}
- }
- }).flatten.toMap
- Confrontation(
- user1, user2,
- ~(res get user1.id),
- ~(res get ""),
- ~(res get user2.id)
- )
+ }).flatten.toMap
+ Confrontation(
+ user1._1, user2._1,
+ ~(res get user1._1),
+ ~(res get ""),
+ ~(res get user2._1)
+ )
+ }
}
}
}
diff --git a/modules/game/src/main/PgnDump.scala b/modules/game/src/main/PgnDump.scala
index ed088400e9..11ae80a573 100644
--- a/modules/game/src/main/PgnDump.scala
+++ b/modules/game/src/main/PgnDump.scala
@@ -28,7 +28,7 @@ final class PgnDump(
player(game.blackPlayer, blackUser),
game.id)
- private val dateFormat = DateTimeFormat forPattern "yyyy-MM-dd";
+ private val dateFormat = DateTimeFormat forPattern "yyyy.MM.dd";
private def elo(p: Player) = p.elo.fold("?")(_.toString)
diff --git a/modules/lobby/src/main/Biter.scala b/modules/lobby/src/main/Biter.scala
index d47a70be8b..6d4fbce7b3 100644
--- a/modules/lobby/src/main/Biter.scala
+++ b/modules/lobby/src/main/Biter.scala
@@ -49,13 +49,13 @@ private[lobby] final class Biter(
source = lila.game.Source.Lobby,
pgnImport = None)
- private def canJoin(hook: Hook, userOption: Option[User]) =
+ def canJoin(hook: Hook, user: Option[User]) =
hook.open &&
hook.realMode.casual.fold(
- userOption.isDefined || hook.allowAnon,
- userOption ?? { u ⇒ hook.realEloRange.fold(true)(_ contains u.elo) }
+ user.isDefined || hook.allowAnon,
+ user ?? { u ⇒ hook.realEloRange.fold(true)(_ contains u.elo) }
) && !{
- userOption ?? { u ⇒
+ user ?? { u ⇒
hook.userId ?? { blocks(_, u.id).await }
}
}
diff --git a/modules/lobby/src/main/Color.scala b/modules/lobby/src/main/Color.scala
index 72510945f8..01df21d4d9 100644
--- a/modules/lobby/src/main/Color.scala
+++ b/modules/lobby/src/main/Color.scala
@@ -5,6 +5,8 @@ import scala.util.Random.nextBoolean
sealed abstract class Color(val name: String) {
def resolve: chess.Color
+
+ def compatibleWith(c: Color) = (c == this).fold(c == Color.Random, true)
}
object Color {
diff --git a/modules/lobby/src/main/Hook.scala b/modules/lobby/src/main/Hook.scala
index 6137f1da16..e01dc7893a 100644
--- a/modules/lobby/src/main/Hook.scala
+++ b/modules/lobby/src/main/Hook.scala
@@ -1,10 +1,10 @@
package lila.lobby
+import chess.{ Variant, Mode, Clock }
import org.joda.time.DateTime
import ornicar.scalalib.Random
import play.api.libs.json._
-import chess.{ Variant, Mode, Clock }
import lila.common.EloRange
import lila.user.User
@@ -19,11 +19,8 @@ case class Hook(
mode: Int,
allowAnon: Boolean,
color: String,
- userId: Option[String],
- username: String,
- elo: Option[Int],
+ user: Option[User],
eloRange: String,
- engine: Boolean,
gameId: Option[String] = None,
createdAt: DateTime) {
@@ -36,8 +33,23 @@ case class Hook(
def realMode = Mode orDefault mode
+ def memberOnly = !allowAnon
+
+ def compatibleWith(h: Hook) =
+ compatibilityProperties == h.compatibilityProperties &&
+ (realColor compatibleWith h.realColor) &&
+ (memberOnly || h.memberOnly).fold(isMember && h.isMember, true)
+
+ private def compatibilityProperties = (variant, time, increment, mode)
+
lazy val realEloRange: Option[EloRange] = EloRange noneIfDefault eloRange
+ def userId = user map (_.id)
+ def isMember = user.nonEmpty
+ def username = user.fold(User.anonymous)(_.username)
+ def elo = user map (_.elo)
+ def engine = user ?? (_.engine)
+
def render: JsObject = Json.obj(
"id" -> id,
"uid" -> uid,
@@ -82,11 +94,8 @@ object Hook {
mode = mode.id,
allowAnon = allowAnon || user.isEmpty,
color = color,
- userId = user map (_.id),
- username = user.fold(User.anonymous)(_.username),
+ user = user,
sid = sid,
- elo = user map (_.elo),
eloRange = eloRange.toString,
- engine = user.??(_.engine),
createdAt = DateTime.now)
}
diff --git a/modules/lobby/src/main/HookRepo.scala b/modules/lobby/src/main/HookRepo.scala
index 7d1f1f6ef6..4f2cc62c8f 100644
--- a/modules/lobby/src/main/HookRepo.scala
+++ b/modules/lobby/src/main/HookRepo.scala
@@ -7,6 +7,8 @@ object HookRepo {
private var hooks = Vector[Hook]()
+ def findCompatible(hook: Hook): List[Hook] = allOpen filter (_ compatibleWith hook)
+
def list = hooks.toList
def byId(id: String) = hooks find (_.id == id)
diff --git a/modules/lobby/src/main/Lobby.scala b/modules/lobby/src/main/Lobby.scala
index 859ec4ef75..8d4f3b5241 100644
--- a/modules/lobby/src/main/Lobby.scala
+++ b/modules/lobby/src/main/Lobby.scala
@@ -5,11 +5,12 @@ import scala.concurrent.duration._
import actorApi._
import akka.actor._
import akka.pattern.{ ask, pipe }
+import makeTimeout.short
+
import lila.db.api._
import lila.hub.actorApi.GetUids
import lila.memo.ExpireSetMemo
import lila.socket.actorApi.Broom
-import makeTimeout.short
private[lobby] final class Lobby(
biter: Biter,
@@ -19,11 +20,16 @@ private[lobby] final class Lobby(
case GetOpen ⇒ sender ! HookRepo.allOpen
- case msg @ AddHook(hook) ⇒ {
+ case msg @ AddHook(hook, user) ⇒ {
HookRepo byUid hook.uid foreach remove
hook.sid ?? { sid ⇒ HookRepo bySid sid foreach remove }
- HookRepo save hook
- socket ! msg
+ HookRepo findCompatible hook find { biter.canJoin(_, user) } match {
+ case Some(h) ⇒ self ! BiteHook(h.id, hook.uid, user map (_.id))
+ case None ⇒ {
+ HookRepo save hook
+ socket ! msg
+ }
+ }
}
case CancelHook(uid) ⇒ {
diff --git a/modules/lobby/src/main/Socket.scala b/modules/lobby/src/main/Socket.scala
index 7e289cb13d..aa08f84d14 100644
--- a/modules/lobby/src/main/Socket.scala
+++ b/modules/lobby/src/main/Socket.scala
@@ -5,16 +5,17 @@ import scala.concurrent.duration._
import actorApi._
import akka.actor._
import akka.pattern.ask
+import makeTimeout.short
+import play.api.libs.iteratee._
+import play.api.libs.json._
+import play.api.templates.Html
+
import lila.game.actorApi._
import lila.hub.actorApi.lobby._
import lila.hub.actorApi.router.{ Homepage, Player }
import lila.hub.actorApi.timeline._
import lila.socket.actorApi.{ Connected ⇒ _, _ }
import lila.socket.{ SocketActor, History, Historical }
-import makeTimeout.short
-import play.api.libs.iteratee._
-import play.api.libs.json._
-import play.api.templates.Html
private[lobby] final class Socket(
val history: History,
@@ -43,7 +44,7 @@ private[lobby] final class Socket(
case ReloadTimeline(user) ⇒ sendTo(user, makeMessage("reload_timeline", JsNull))
- case AddHook(hook) ⇒ notifyVersion("hook_add", hook.render)
+ case AddHook(hook, _) ⇒ notifyVersion("hook_add", hook.render)
case RemoveHook(hookId) ⇒ notifyVersion("hook_remove", hookId)
diff --git a/modules/lobby/src/main/actorApi.scala b/modules/lobby/src/main/actorApi.scala
index e046c925d8..d6717f0540 100644
--- a/modules/lobby/src/main/actorApi.scala
+++ b/modules/lobby/src/main/actorApi.scala
@@ -20,7 +20,7 @@ object Member {
case class Connected(enumerator: JsEnumerator, member: Member)
case class WithHooks(op: Iterable[String] ⇒ Unit)
-case class AddHook(hook: Hook)
+case class AddHook(hook: Hook, user: Option[User])
case class RemoveHook(hookId: String)
case class CancelHook(uid: String)
case class BiteHook(hookId: String, uid: String, userId: Option[String])
diff --git a/modules/setup/src/main/Processor.scala b/modules/setup/src/main/Processor.scala
index 0e0a21bad0..dce014b58f 100644
--- a/modules/setup/src/main/Processor.scala
+++ b/modules/setup/src/main/Processor.scala
@@ -1,9 +1,10 @@
package lila.setup
import akka.pattern.ask
+import chess.{ Game ⇒ ChessGame, Board, Color ⇒ ChessColor }
+import makeTimeout.short
import play.api.libs.json.{ Json, JsObject }
-import chess.{ Game ⇒ ChessGame, Board, Color ⇒ ChessColor }
import lila.ai.Ai
import lila.db.api._
import lila.game.tube.gameTube
@@ -13,7 +14,6 @@ import lila.i18n.I18nDomain
import lila.lobby.actorApi.AddHook
import lila.lobby.Hook
import lila.user.{ User, Context }
-import makeTimeout.short
import tube.{ userConfigTube, anonConfigTube }
private[setup] final class Processor(
@@ -50,12 +50,14 @@ private[setup] final class Processor(
friendConfigMemo.set(pov.game.id, config) inject pov
}
- def hook(config: HookConfig, uid: String, sid: Option[String])(implicit ctx: Context): Funit = {
- val hook = config.hook(uid, ctx.me, sid)
+ def hook(
+ config: HookConfig,
+ uid: String,
+ sid: Option[String],
+ user: Option[User])(implicit ctx: Context): Funit =
saveConfig(_ withHook config) >>- {
- lobby ! AddHook(hook)
+ lobby ! AddHook(config.hook(uid, ctx.me, sid), user)
}
- }
def api(implicit ctx: Context): Fu[JsObject] = {
val config = ApiConfig
diff --git a/modules/team/src/main/MemberRepo.scala b/modules/team/src/main/MemberRepo.scala
index 72794931e9..9e68b2f938 100644
--- a/modules/team/src/main/MemberRepo.scala
+++ b/modules/team/src/main/MemberRepo.scala
@@ -6,9 +6,6 @@ import reactivemongo.api._
import lila.db.api._
import tube.memberTube
-// db.team_member.ensureIndex({team:1})
-// db.team_member.ensureIndex({user:1})
-// db.team_member.ensureIndex({date: -1})
object MemberRepo {
type ID = String
diff --git a/modules/team/src/main/RequestRepo.scala b/modules/team/src/main/RequestRepo.scala
index 6d7f8002dc..a60025a584 100644
--- a/modules/team/src/main/RequestRepo.scala
+++ b/modules/team/src/main/RequestRepo.scala
@@ -6,8 +6,6 @@ import reactivemongo.api._
import lila.db.api._
import tube.requestTube
-// db.team_request.ensureIndex({team:1})
-// db.team_request.ensureIndex({date: -1})
object RequestRepo {
type ID = String
diff --git a/modules/user/src/main/Confrontation.scala b/modules/user/src/main/Confrontation.scala
index 6c991eb4f7..99baef2407 100644
--- a/modules/user/src/main/Confrontation.scala
+++ b/modules/user/src/main/Confrontation.scala
@@ -1,8 +1,8 @@
package lila.user
case class Confrontation(
- user1: User,
- user2: User,
+ user1: String,
+ user2: String,
wins: Int,
draws: Int,
losses: Int) {
diff --git a/project/Dependencies.scala b/project/Dependencies.scala
index 6113929065..8970fa504a 100644
--- a/project/Dependencies.scala
+++ b/project/Dependencies.scala
@@ -43,15 +43,15 @@ object Dependencies {
val jodaTime = "joda-time" % "joda-time" % "2.2"
val jodaConvert = "org.joda" % "joda-convert" % "1.3.1"
val scalastic = "scalastic" %% "scalastic" % "0.90.0-thib"
- val reactivemongo = "org.reactivemongo" %% "reactivemongo" % "0.10.0"
- val playReactivemongo = "org.reactivemongo" %% "play2-reactivemongo" % "0.10-THIB"
+ val reactivemongo = "org.reactivemongo" %% "reactivemongo" % "0.10.5-THIB"
+ val playReactivemongo = "org.reactivemongo" %% "play2-reactivemongo" % "0.10.5-THIB"
object play {
- val version = "2.2.0-RC1"
+ val version = "2.2.0-RC2"
val api = "com.typesafe.play" %% "play" % version
val test = "com.typesafe.play" %% "play-test" % version
}
object akka {
- val version = "2.1.0"
+ val version = "2.2.0"
val agent = "com.typesafe.akka" %% "akka-agent" % version
}
object spray {
diff --git a/public/javascripts/deps.min.js b/public/javascripts/deps.min.js
index 816fbb951f..e0431addc2 100644
--- a/public/javascripts/deps.min.js
+++ b/public/javascripts/deps.min.js
@@ -20,5 +20,5 @@
// jquery.timeago 1.2.0 https://github.com/rmm5t/jquery-timeago
(function(d){"function"===typeof define&&define.amd?define(["jquery"],d):d(jQuery)})(function(d){function l(){var a;a=d(this);if(!a.data("timeago")){a.data("timeago",{datetime:e.datetime(a)});var b=d.trim(a.text());e.settings.localeTitle?a.attr("title",a.data("timeago").datetime.toLocaleString()):0
a&&(e=c.prefixFromNow,f=c.suffixFromNow);var h=Math.abs(a)/1E3,g=h/60,m=g/60,k=m/24,l=k/365,h=45>h&&b(c.seconds,Math.round(h))||90>h&&b(c.minute,1)||45>g&&b(c.minutes,Math.round(g))||90>g&&b(c.hour,1)||24>m&&b(c.hours,Math.round(m))||42>m&&b(c.day,1)||30>k&&b(c.days,Math.round(k))||45>k&&b(c.month,1)||365>k&&b(c.months,Math.round(k/30))||1.5>l&&b(c.year,1)||b(c.years,Math.round(l)),g=c.wordSeparator||"";void 0===c.wordSeparator&&(g=" ");return d.trim([e,h,f].join(g))},parse:function(a){a=d.trim(a);a=a.replace(/\.\d+/,"");a=a.replace(/-/,"/").replace(/-/,"/");a=a.replace(/T/," ").replace(/Z/," UTC");a=a.replace(/([\+\-]\d\d)\:?(\d\d)/," $1$2");return new Date(a)},datetime:function(a){a=e.isTime(a)?d(a).attr("datetime"):d(a).attr("title");return e.parse(a)},isTime:function(a){return"time"===d(a).get(0).tagName.toLowerCase()}});var n={init:function(){var a=d.proxy(l,this);a();var b=e.settings;0a)return l.idle=!1,clearTimeout(e.idleTimer.tId),d.enabled&&(e.idleTimer.tId=setTimeout(o,d.timeout)),void 0;var m=e.Event(e.data(i,"idleTimer",l.idle?"idle":"active")+".idleTimer");e(i).trigger(m)},m=function(e){var t=e.data("idleTimerObj")||{};t.enabled=!1,clearTimeout(t.tId),e.off(".idleTimer")};if(a.olddate=a.olddate||+new Date,"number"==typeof t)d.timeout=t;else{if("destroy"===t)return m(l),this;if("getElapsedTime"===t)return+new Date-a.olddate}l.on(e.trim((d.events+" ").split(" ").join(".idleTimer ")),function(){var t=e.data(this,"idleTimerObj");clearTimeout(t.tId),t.enabled&&(t.idle&&o(this),t.tId=setTimeout(o,t.timeout))}),a.idle=d.idle,a.enabled=d.enabled,a.timeout=d.timeout,d.startImmediately&&(a.tId=setTimeout(o,a.timeout)),l.data("idleTimer","active"),l.data("idleTimerObj",a)},e.fn.idleTimer=function(t,i){return i||(i={}),this[0]&&e.idleTimer(t,this[0],i),this}})(jQuery);
+// Idle Timer - v0.9.3 - 2013-08-04 https://github.com/mikesherov/jquery-idletimer
+(function(e){e.idleTimer=function(t,i,d){d=e.extend({startImmediately:!0,idle:!1,enabled:!0,timeout:3e4,events:"mousemove keydown DOMMouseScroll mousewheel mousedown touchstart touchmove"},d),i=i||document;var l=e(i),a=l.data("idleTimerObj")||{},o=function(t){"number"==typeof t&&(t=void 0);var l=e.data(t||i,"idleTimerObj");l.idle=!l.idle;var a=+new Date-l.olddate;if(l.olddate=+new Date,l.idle&&d.timeout>a)return l.idle=!1,clearTimeout(e.idleTimer.tId),d.enabled&&(e.idleTimer.tId=setTimeout(o,d.timeout)),void 0;var m=e.Event(e.data(i,"idleTimer",l.idle?"idle":"active")+".idleTimer");e(i).trigger(m)},m=function(e){var t=e.data("idleTimerObj")||{};t.enabled=!1,clearTimeout(t.tId),e.off(".idleTimer")};if(a.olddate=a.olddate||+new Date,"number"==typeof t)d.timeout=t;else{if("destroy"===t)return m(l),this;if("getElapsedTime"===t)return+new Date-a.olddate}l.on(e.trim((d.events+" ").split(" ").join(".idleTimer ")),function(){var t=e.data(this,"idleTimerObj");clearTimeout(t.tId),t.enabled&&(t.idle&&o(this),t.tId=setTimeout(o,t.timeout))}),a.idle=d.idle,a.enabled=d.enabled,a.timeout=d.timeout,d.startImmediately&&(a.tId=setTimeout(o,a.timeout)),l.data("idleTimer","active"),l.data("idleTimerObj",a)},e.fn.idleTimer=function(t,i){return i||(i={}),this[0]?e.idleTimer(t,this[0],i):this}})(jQuery);
diff --git a/todo b/todo
index f5003a990e..8f498c88c9 100644
--- a/todo
+++ b/todo
@@ -42,7 +42,6 @@ the forum search user:mephostophilis returns no result
players world map
account closed accounts in team counts
IE10 no sound toggle http://en.lichess.org/forum/lichess-feedback/notification-of-game-creation#3
-show friend games in homepage board
replace plot with watch plot when game starts
team search is broken
liquid UI for large screens http://fr.lichess.org/forum/lichess-feedback/hi-res#5
@@ -61,5 +60,6 @@ separate games in spectator chat
real board editor save
clickable mistakes list in analysis
FEN/editor castle/enpassant https://github.com/ornicar/lila/issues/42 white/black to play
-stupid team forum slug http://ru.lichess.org/forum/team-4epa250h
user notes, shared among friends
+detect cheat using lichess AI
+fix AI protocol