From cc0aa2f5318400b6acdf8c027f2b30bc125cf932 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sat, 6 Nov 2021 09:25:26 +0100 Subject: [PATCH 001/144] default to dark theme Only applies to anonymous players, and accounts created after this change. --- modules/insight/src/main/Share.scala | 20 +++++++++----------- modules/pref/src/main/Pref.scala | 15 +++++++++++++-- modules/pref/src/main/PrefApi.scala | 18 +++++++++--------- modules/round/src/main/Drawer.scala | 6 +++--- 4 files changed, 34 insertions(+), 25 deletions(-) diff --git a/modules/insight/src/main/Share.scala b/modules/insight/src/main/Share.scala index 1bbf212fde..dd19566b27 100644 --- a/modules/insight/src/main/Share.scala +++ b/modules/insight/src/main/Share.scala @@ -9,20 +9,18 @@ final class Share( relationApi: lila.relation.RelationApi )(implicit ec: scala.concurrent.ExecutionContext) { - def getPrefId(insighted: User) = prefApi.getPrefById(insighted.id) dmap (_.insightShare) + def getPrefId(insighted: User) = prefApi.getPref(insighted.id, _.insightShare) def grant(insighted: User, to: Option[User]): Fu[Boolean] = if (to ?? Granter(_.SeeInsight)) fuTrue else - prefApi.getPrefById(insighted.id) flatMap { pref => - pref.insightShare match { - case _ if to.contains(insighted) => fuTrue - case Pref.InsightShare.EVERYBODY => fuTrue - case Pref.InsightShare.FRIENDS => - to ?? { t => - relationApi.fetchAreFriends(insighted.id, t.id) - } - case Pref.InsightShare.NOBODY => fuFalse - } + getPrefId(insighted) flatMap { + case _ if to.contains(insighted) => fuTrue + case Pref.InsightShare.EVERYBODY => fuTrue + case Pref.InsightShare.FRIENDS => + to ?? { t => + relationApi.fetchAreFriends(insighted.id, t.id) + } + case Pref.InsightShare.NOBODY => fuFalse } } diff --git a/modules/pref/src/main/Pref.scala b/modules/pref/src/main/Pref.scala index 108f709ead..bc4ef91564 100644 --- a/modules/pref/src/main/Pref.scala +++ b/modules/pref/src/main/Pref.scala @@ -1,5 +1,9 @@ package lila.pref +import org.joda.time.DateTime + +import lila.user.User + case class Pref( _id: String, // user id bg: Int, @@ -413,11 +417,18 @@ object Pref { object Zen extends BooleanPref {} object Ratings extends BooleanPref {} - def create(id: String) = default.copy(_id = id) + val darkByDefaultSince = new DateTime(2021, 11, 7, 8, 0) + + def create(id: User.ID) = default.copy(_id = id) + + def create(user: User) = default.copy( + _id = user.id, + bg = if (user.createdAt isAfter darkByDefaultSince) Bg.DARK else Bg.LIGHT + ) lazy val default = Pref( _id = "", - bg = Bg.LIGHT, + bg = Bg.DARK, bgImg = none, is3d = false, theme = Theme.default.name, diff --git a/modules/pref/src/main/PrefApi.scala b/modules/pref/src/main/PrefApi.scala index 3aee2d168c..b029cd4311 100644 --- a/modules/pref/src/main/PrefApi.scala +++ b/modules/pref/src/main/PrefApi.scala @@ -37,13 +37,16 @@ final class PrefApi( .void >>- { cache invalidate user.id } } >>- { cache invalidate user.id } - def getPrefById(id: User.ID): Fu[Pref] = cache get id dmap (_ getOrElse Pref.create(id)) - val getPref = getPrefById _ - def getPref(user: User): Fu[Pref] = getPref(user.id) - def getPref(user: Option[User]): Fu[Pref] = user.fold(fuccess(Pref.default))(getPref) + def getPrefById(id: User.ID): Fu[Option[Pref]] = cache get id - def getPref[A](user: User, pref: Pref => A): Fu[A] = getPref(user) dmap pref - def getPref[A](userId: User.ID, pref: Pref => A): Fu[A] = getPref(userId) dmap pref + def getPref(user: User): Fu[Pref] = cache get user.id dmap { + _ getOrElse Pref.create(user) + } + + def getPref[A](user: User, pref: Pref => A): Fu[A] = getPref(user) dmap pref + + def getPref[A](userId: User.ID, pref: Pref => A): Fu[A] = + getPrefById(userId).dmap(p => pref(p | Pref.default)) def getPref(user: User, req: RequestHeader): Fu[Pref] = getPref(user) dmap RequestPref.queryParamOverride(req) @@ -81,9 +84,6 @@ final class PrefApi( def setPref(user: User, change: Pref => Pref): Funit = getPref(user) map change flatMap setPref - def setPref(userId: User.ID, change: Pref => Pref): Funit = - getPref(userId) map change flatMap setPref - def setPrefString(user: User, name: String, value: String): Funit = getPref(user) map { _.set(name, value) } orFail s"Bad pref ${user.id} $name -> $value" flatMap setPref diff --git a/modules/round/src/main/Drawer.scala b/modules/round/src/main/Drawer.scala index 5ff6135c7f..09bda554e9 100644 --- a/modules/round/src/main/Drawer.scala +++ b/modules/round/src/main/Drawer.scala @@ -22,9 +22,9 @@ final private[round] class Drawer( import Pref.PrefZero if (game.playerHasOfferedDrawRecently(pov.color)) fuccess(pov.some) else - pov.player.userId ?? prefApi.getPref map { pref => - pref.autoThreefold == Pref.AutoThreefold.ALWAYS || { - pref.autoThreefold == Pref.AutoThreefold.TIME && + pov.player.userId ?? { uid => prefApi.getPref(uid, _.autoThreefold) } map { autoThreefold => + autoThreefold == Pref.AutoThreefold.ALWAYS || { + autoThreefold == Pref.AutoThreefold.TIME && game.clock ?? { _.remainingTime(pov.color) < Centis.ofSeconds(30) } } || pov.player.userId.exists(isBotSync) } map (_ option pov) From ccb172b519ed3166d2470cac7029807afe91ac12 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sun, 7 Nov 2021 17:50:37 +0100 Subject: [PATCH 002/144] increase explorer sampling rates --- .../explorer/src/main/ExplorerIndexer.scala | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/modules/explorer/src/main/ExplorerIndexer.scala b/modules/explorer/src/main/ExplorerIndexer.scala index 722d4951e4..576027ceff 100644 --- a/modules/explorer/src/main/ExplorerIndexer.scala +++ b/modules/explorer/src/main/ExplorerIndexer.scala @@ -71,21 +71,22 @@ final private class ExplorerIndexer( case Correspondence | Classical => 1.00f case Rapid if rating >= 2200 => 1.00f - case Rapid if rating >= 2000 => 0.50f - case Rapid if rating >= 1800 => 0.28f - case Rapid if rating >= 1600 => 0.24f + case Rapid if rating >= 2000 => 0.83f + case Rapid if rating >= 1800 => 0.46f + case Rapid if rating >= 1600 => 0.39f case Rapid => 0.02f case Blitz if rating >= 2500 => 1.00f - case Blitz if rating >= 2200 => 0.24f - case Blitz if rating >= 2000 => 0.11f - case Blitz if rating >= 1600 => 0.08f + case Blitz if rating >= 2200 => 0.38f + case Blitz if rating >= 2000 => 0.18f + case Blitz if rating >= 1600 => 0.13f case Blitz => 0.02f case Bullet if rating >= 2500 => 1.00f - case Bullet if rating >= 2200 => 0.30f - case Bullet if rating >= 2000 => 0.17f - case Bullet if rating >= 1600 => 0.11f + case Bullet if rating >= 2200 => 0.48f + case Bullet if rating >= 2000 => 0.27f + case Bullet if rating >= 1800 => 0.19f + case Bullet if rating >= 1600 => 0.18f case Bullet => 0.02f case UltraBullet => 1.00f From 46e6819bdc6f8c20f504f0160e31b046ced3355c Mon Sep 17 00:00:00 2001 From: Sharad Date: Sun, 7 Nov 2021 19:00:07 -0500 Subject: [PATCH 003/144] Add right margin to user activity --- ui/site/css/user/_activity.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/site/css/user/_activity.scss b/ui/site/css/user/_activity.scss index 2a109e37b2..3687142925 100644 --- a/ui/site/css/user/_activity.scss +++ b/ui/site/css/user/_activity.scss @@ -5,6 +5,10 @@ $c-contours: mix($c-brag, $c-shade, 80%); font-size: 1.2em; margin-left: 2rem; padding-top: 1rem; + + @include breakpoint($mq-not-small) { + margin-right: 2em; + } } section h2 { From 4256bbecd0f5bf1b3abcb665faaf80413b30cdc3 Mon Sep 17 00:00:00 2001 From: Sharad Date: Sun, 7 Nov 2021 19:57:11 -0500 Subject: [PATCH 004/144] Fix move being unselected in study --- ui/analyse/src/ctrl.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/analyse/src/ctrl.ts b/ui/analyse/src/ctrl.ts index 5026dfb458..465e9bf492 100644 --- a/ui/analyse/src/ctrl.ts +++ b/ui/analyse/src/ctrl.ts @@ -389,7 +389,6 @@ export default class AnalyseCtrl { userJump = (path: Tree.Path): void => { this.autoplay.stop(); - this.withCg(cg => cg.selectSquare(null)); if (this.practice) { const prev = this.path; this.practice.preUserJump(prev, path); From 26ccf278fa59380e05328e2616930d86135e9014 Mon Sep 17 00:00:00 2001 From: Albert Ford Date: Sat, 6 Nov 2021 04:49:23 -0700 Subject: [PATCH 005/144] Colorize the logo spinner and add animation --- app/templating/Environment.scala | 2 +- ui/common/css/component/_loader.scss | 56 +++++++++++++++++++--------- ui/common/src/spinner.ts | 11 +----- ui/site/src/component/spinner.ts | 2 +- 4 files changed, 41 insertions(+), 30 deletions(-) diff --git a/app/templating/Environment.scala b/app/templating/Environment.scala index 1dec94f4ca..1e9fdc4420 100644 --- a/app/templating/Environment.scala +++ b/app/templating/Environment.scala @@ -51,6 +51,6 @@ object Environment ) val spinner: Frag = raw( - """
""" + """
""" ) } diff --git a/ui/common/css/component/_loader.scss b/ui/common/css/component/_loader.scss index 2a69703be5..47180e7964 100644 --- a/ui/common/css/component/_loader.scss +++ b/ui/common/css/component/_loader.scss @@ -1,34 +1,49 @@ /* circular loader */ +$len1: 33.96; +$len2: 42.6; +$len3: 104.31; +$total_len: $len1 + $len2 + $len3; + @keyframes mask1 { 0% { - stroke-dashoffset: 1; + stroke-dashoffset: $total_len; } - 17.86% { - stroke-dashoffset: 0; + 100% { + stroke-dashoffset: -$total_len; } } @keyframes mask2 { 0% { - stroke-dashoffset: 1; + stroke-dashoffset: $total_len + $len1; } - 17.86% { - stroke-dashoffset: 1; - } - 31.43% { - stroke-dashoffset: 0; + 100% { + stroke-dashoffset: -$total_len + $len1; } } @keyframes mask3 { 0% { - stroke-dashoffset: 1; + stroke-dashoffset: $total_len + $len1 + $len2; } - 31.43% { - stroke-dashoffset: 1; + 100% { + stroke-dashoffset: -$total_len + $len1 + $len2; } - 85% { - stroke-dashoffset: 0; +} + +@keyframes spinner-color { + 0%, + 100% { + stroke: $c-primary; + } + 25% { + stroke: $c-error; + } + 50% { + stroke: $c-secondary; + } + 75% { + stroke: $c-brag; } } @@ -37,14 +52,19 @@ height: 70px; margin: auto; - path:nth-child(1) { - animation: mask1 2s cubic-bezier(0.417, 0.086, 0.741, 0.452) infinite; + path { + animation: mask1 2.75s cubic-bezier(.49,.67,.45,.29) infinite; } path:nth-child(2) { - animation: mask2 2s cubic-bezier(0.333, 0.317, 0.621, 0.661) infinite; + animation-name: mask2; } path:nth-child(3) { - animation: mask3 2s cubic-bezier(0, 0, 0.431, 1) infinite; + animation-name: mask3; + } + + g { + animation: spinner-color 11s steps(1) infinite; + stroke-dasharray: $total_len; } .white & path { diff --git a/ui/common/src/spinner.ts b/ui/common/src/spinner.ts index 90c7b71eef..413b7802b6 100644 --- a/ui/common/src/spinner.ts +++ b/ui/common/src/spinner.ts @@ -29,18 +29,9 @@ export default function (): VNode { attrs: { mask: 'url(#mask)', fill: 'none', - stroke: '#888', - 'stroke-dasharray': 1, }, }, - pathAttrs.map(attrs => { - return h('path', { - attrs: { - pathLength: 1, // cannot be inherited from parent group - ...attrs, - }, - }); - }) + pathAttrs.map(attrs => h('path', { attrs })) ), ]), ] diff --git a/ui/site/src/component/spinner.ts b/ui/site/src/component/spinner.ts index 83da995a81..708f40f581 100644 --- a/ui/site/src/component/spinner.ts +++ b/ui/site/src/component/spinner.ts @@ -1,4 +1,4 @@ const spinner = - '
'; + '
'; export default spinner; From 366db2657ad0093a475eab01fcaa5f7bc3644cbe Mon Sep 17 00:00:00 2001 From: Albert Ford Date: Mon, 8 Nov 2021 08:03:51 -0800 Subject: [PATCH 006/144] Format css --- ui/common/css/component/_loader.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/common/css/component/_loader.scss b/ui/common/css/component/_loader.scss index 47180e7964..62734c3949 100644 --- a/ui/common/css/component/_loader.scss +++ b/ui/common/css/component/_loader.scss @@ -53,7 +53,7 @@ $total_len: $len1 + $len2 + $len3; margin: auto; path { - animation: mask1 2.75s cubic-bezier(.49,.67,.45,.29) infinite; + animation: mask1 2.75s cubic-bezier(0.49, 0.67, 0.45, 0.29) infinite; } path:nth-child(2) { animation-name: mask2; From 2a44758c45623dd9f2acf7a6af56d8e84a37ae55 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 7 Nov 2021 09:21:29 +0100 Subject: [PATCH 007/144] {master} counter player biases in daily puzzle selection --- modules/common/src/main/Random.scala | 5 +++++ modules/puzzle/src/main/DailyPuzzle.scala | 15 ++++++++++++++- modules/puzzle/src/main/Puzzle.scala | 2 ++ 3 files changed, 21 insertions(+), 1 deletion(-) diff --git a/modules/common/src/main/Random.scala b/modules/common/src/main/Random.scala index 3fe0a0e128..fd6fffb48a 100644 --- a/modules/common/src/main/Random.scala +++ b/modules/common/src/main/Random.scala @@ -38,6 +38,11 @@ abstract class Random { vec.nonEmpty ?? { vec lift nextInt(vec.size) } + + // odds(1) = 100% true + // odds(2) = 50% true + // odds(3) = 33% true + def odds(n: Int): Boolean = nextInt(n) == 0 } object ThreadLocalRandom extends Random { diff --git a/modules/puzzle/src/main/DailyPuzzle.scala b/modules/puzzle/src/main/DailyPuzzle.scala index 8a237a3a3d..5a14cbc5d9 100644 --- a/modules/puzzle/src/main/DailyPuzzle.scala +++ b/modules/puzzle/src/main/DailyPuzzle.scala @@ -7,6 +7,7 @@ import scala.concurrent.duration._ import lila.db.dsl._ import lila.memo.CacheApi._ +import lila.common.ThreadLocalRandom final private[puzzle] class DailyPuzzle( colls: PuzzleColls, @@ -26,7 +27,7 @@ final private[puzzle] class DailyPuzzle( def get: Fu[Option[DailyPuzzle.WithHtml]] = cache.getUnit private def find: Fu[Option[DailyPuzzle.WithHtml]] = - (findCurrent orElse findNew) recover { case e: Exception => + (findCurrent orElse findNewBiased()) recover { case e: Exception => logger.error("find daily", e) none } flatMap { _ ?? makeDaily } @@ -48,6 +49,18 @@ final private[puzzle] class DailyPuzzle( .one[Puzzle] } + private def findNewBiased(tries: Int = 0): Fu[Option[Puzzle]] = { + def tryAgainMaybe = (tries < 5) ?? findNewBiased(tries + 1) + import lila.common.ThreadLocalRandom.odds + import PuzzleTheme._ + findNew flatMap { + case None => tryAgainMaybe + case Some(p) if p.hasTheme(anastasiaMate) && !odds(3) => tryAgainMaybe dmap (_ orElse p.some) + case Some(p) if p.hasTheme(arabianMate) && odds(2) => tryAgainMaybe dmap (_ orElse p.some) + case p => fuccess(p) + } + } + private def findNew: Fu[Option[Puzzle]] = colls .path { diff --git a/modules/puzzle/src/main/Puzzle.scala b/modules/puzzle/src/main/Puzzle.scala index c806e91e1c..4e1555b193 100644 --- a/modules/puzzle/src/main/Puzzle.scala +++ b/modules/puzzle/src/main/Puzzle.scala @@ -29,6 +29,8 @@ case class Puzzle( } err s"Can't apply puzzle $id first move" def color = fen.color.fold[chess.Color](chess.White)(!_) + + def hasTheme(theme: PuzzleTheme) = themes(theme.key) } object Puzzle { From 51605e46f190a26e80fbd15ae301e84843f39a2f Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 7 Nov 2021 09:21:56 +0100 Subject: [PATCH 008/144] {master} log team closure --- modules/team/src/main/TeamApi.scala | 1 + 1 file changed, 1 insertion(+) diff --git a/modules/team/src/main/TeamApi.scala b/modules/team/src/main/TeamApi.scala index dfdb861434..0b0c7682ea 100644 --- a/modules/team/src/main/TeamApi.scala +++ b/modules/team/src/main/TeamApi.scala @@ -288,6 +288,7 @@ final class TeamApi( lila.security.Granter(_.ManageTeam)(by) || team.createdBy == by.id || (team.leaders(by.id) && !team.leaders(team.createdBy)) ) { + logger.info(s"toggleEnabled ${team.id}: ${!team.enabled} by @${by.id}") if (team.enabled) teamRepo.disable(team).void >> memberRepo.userIdsByTeam(team.id).map { _ foreach cached.invalidateTeamIds } >> From 79ed95d366d74a10df4c61d3556430c46118adac Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 8 Nov 2021 12:08:19 +0100 Subject: [PATCH 009/144] {master} fix PGN export of masters explorer games --- modules/game/src/main/PgnDump.scala | 6 ++++-- modules/game/src/main/Player.scala | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/modules/game/src/main/PgnDump.scala b/modules/game/src/main/PgnDump.scala index 6c51281333..60218cf8dd 100644 --- a/modules/game/src/main/PgnDump.scala +++ b/modules/game/src/main/PgnDump.scala @@ -57,10 +57,12 @@ final class PgnDump( private def gameLightUsers(game: Game): Fu[(Option[LightUser], Option[LightUser])] = (game.whitePlayer.userId ?? lightUserApi.async) zip (game.blackPlayer.userId ?? lightUserApi.async) - private def rating(p: Player) = p.rating.fold("?")(_.toString) + private def rating(p: Player) = p.rating.orElse(p.nameSplit.flatMap(_._2)).fold("?")(_.toString) def player(p: Player, u: Option[LightUser]) = - p.aiLevel.fold(u.fold(p.name | lila.user.User.anonymous)(_.name))("lichess AI level " + _) + p.aiLevel.fold(u.fold(p.nameSplit.map(_._1).orElse(p.name) | lila.user.User.anonymous)(_.name))( + "lichess AI level " + _ + ) private val customStartPosition: Set[chess.variant.Variant] = Set(chess.variant.Chess960, chess.variant.FromPosition, chess.variant.Horde, chess.variant.RacingKings) diff --git a/modules/game/src/main/Player.scala b/modules/game/src/main/Player.scala index cf3eed3302..1d21d22530 100644 --- a/modules/game/src/main/Player.scala +++ b/modules/game/src/main/Player.scala @@ -60,8 +60,8 @@ case class Player( def nameSplit: Option[(String, Option[Int])] = name map { - case Player.nameSplitRegex(n, r) => n -> r.toIntOption - case n => n -> none + case Player.nameSplitRegex(n, r) => n.trim -> r.toIntOption + case n => n -> none } def before(other: Player) = From 8e3cfc3a6dc160345f333a834f01443deb6eb753 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 14:50:00 +0100 Subject: [PATCH 010/144] Never use analysis quota for official broadcasts --- modules/relay/src/main/RelaySync.scala | 3 ++- modules/study/src/main/ServerEval.scala | 9 +++++---- modules/study/src/main/StudyApi.scala | 9 +++++++-- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/modules/relay/src/main/RelaySync.scala b/modules/relay/src/main/RelaySync.scala index f6c28bd2ad..0bfceb5e6b 100644 --- a/modules/relay/src/main/RelaySync.scala +++ b/modules/relay/src/main/RelaySync.scala @@ -155,7 +155,8 @@ final private class RelaySync( (tour.official && chapter.root.mainline.sizeIs > 10) ?? studyApi.analysisRequest( studyId = study.id, chapterId = chapter.id, - userId = study.ownerId + userId = study.ownerId, + unlimited = true ) } >>- { multiboard.invalidate(study.id) diff --git a/modules/study/src/main/ServerEval.scala b/modules/study/src/main/ServerEval.scala index f20f30f3ca..885c07ba39 100644 --- a/modules/study/src/main/ServerEval.scala +++ b/modules/study/src/main/ServerEval.scala @@ -22,14 +22,15 @@ object ServerEval { private val onceEvery = lila.memo.OnceEvery(5 minutes) - def apply(study: Study, chapter: Chapter, userId: User.ID): Funit = + def apply(study: Study, chapter: Chapter, userId: User.ID, unlimited: Boolean = false): Funit = chapter.serverEval.fold(true) { eval => !eval.done && onceEvery(chapter.id.value) } ?? { val unlimitedFu = - fuccess(userId == User.lichessId) >>| userRepo - .byId(userId) - .map(_.exists(Granter(_.Relay))) + fuccess(unlimited) >>| + fuccess(userId == User.lichessId) >>| userRepo + .byId(userId) + .map(_.exists(Granter(_.Relay))) unlimitedFu flatMap { unlimited => chapterRepo.startServerEval(chapter) >>- { fishnet ! StudyChapterRequest( diff --git a/modules/study/src/main/StudyApi.scala b/modules/study/src/main/StudyApi.scala index 9e069100ce..df05b134c2 100644 --- a/modules/study/src/main/StudyApi.scala +++ b/modules/study/src/main/StudyApi.scala @@ -841,10 +841,15 @@ final class StudyApi( } } - def analysisRequest(studyId: Study.Id, chapterId: Chapter.Id, userId: User.ID): Funit = + def analysisRequest( + studyId: Study.Id, + chapterId: Chapter.Id, + userId: User.ID, + unlimited: Boolean = false + ): Funit = sequenceStudyWithChapter(studyId, chapterId) { case Study.WithChapter(study, chapter) => Contribute(userId, study) { - serverEvalRequester(study, chapter, userId) + serverEvalRequester(study, chapter, userId, unlimited) } } From c1b639dde0c9320bbaa4445606bb68db74a0aac0 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 15:19:50 +0100 Subject: [PATCH 011/144] Hide rating of correspondence seeks --- ui/lobby/src/view/correspondence.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/lobby/src/view/correspondence.ts b/ui/lobby/src/view/correspondence.ts index 8f8ba3dadf..5c93845cb8 100644 --- a/ui/lobby/src/view/correspondence.ts +++ b/ui/lobby/src/view/correspondence.ts @@ -28,7 +28,7 @@ function renderSeek(ctrl: LobbyController, seek: Seek): VNode { seek.username ) : 'Anonymous', - seek.rating + (seek.provisional ? '?' : ''), + seek.rating && ctrl.opts.showRatings ? seek.rating + (seek.provisional ? '?' : '') : '', seek.days ? ctrl.trans.plural('nbDays', seek.days) : '∞', h('span', [ h('span.varicon', { From 2e9ad93333962d655c5562049f57ce3aace86aa4 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 16:53:41 +0100 Subject: [PATCH 012/144] Send reminder to zulip on name close preset --- app/controllers/Mod.scala | 1 + modules/irc/src/main/IrcApi.scala | 3 +++ modules/irc/src/main/ZulipClient.scala | 1 + modules/mod/src/main/Presets.scala | 6 +++++- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/app/controllers/Mod.scala b/app/controllers/Mod.scala index 171a7adbe8..ad60b5556d 100644 --- a/app/controllers/Mod.scala +++ b/app/controllers/Mod.scala @@ -108,6 +108,7 @@ final class Mod( suspect <- modApi.setTroll(me, prev, prev.user.marks.troll) _ <- env.msg.api.systemPost(suspect.user.id, preset.text) _ <- env.mod.logApi.modMessage(me.id, suspect.user.id, preset.name) + _ <- preset.isNameClose ?? env.irc.api.nameClosePreset(username) } yield (inquiry, suspect).some } } diff --git a/modules/irc/src/main/IrcApi.scala b/modules/irc/src/main/IrcApi.scala index d12ac2a037..e91ef0f740 100644 --- a/modules/irc/src/main/IrcApi.scala +++ b/modules/irc/src/main/IrcApi.scala @@ -132,6 +132,9 @@ final class IrcApi( } } + def nameClosePreset(username: String): Funit = + zulip(_.mod.commsPublic, "/" + username)("@**remind** here in 48h to close this account") + def stop(): Funit = zulip(_.general, "lila")("Lichess is restarting.") def publishEvent(event: Event): Funit = event match { diff --git a/modules/irc/src/main/ZulipClient.scala b/modules/irc/src/main/ZulipClient.scala index fbef967183..d15d6f1406 100644 --- a/modules/irc/src/main/ZulipClient.scala +++ b/modules/irc/src/main/ZulipClient.scala @@ -80,6 +80,7 @@ private object ZulipClient { val log = "mod-log" val adminLog = "mod-admin-log" val adminGeneral = "mod-admin-general" + val commsPublic = "mod-comms-public" val commsPrivate = "mod-comms-private" val hunterCheat = "mod-hunter-cheat" val adminAppeal = "mod-admin-appeal" diff --git a/modules/mod/src/main/Presets.scala b/modules/mod/src/main/Presets.scala index caf76ea09c..b76f03f7b0 100644 --- a/modules/mod/src/main/Presets.scala +++ b/modules/mod/src/main/Presets.scala @@ -50,11 +50,15 @@ case class ModPresets(value: List[ModPreset]) { value.find(_.text.filter(_.isLetter) == clean) } } -case class ModPreset(name: String, text: String, permissions: Set[Permission]) +case class ModPreset(name: String, text: String, permissions: Set[Permission]) { + + def isNameClose = name contains ModPresets.nameClosePresetName +} object ModPresets { val groups = List("PM", "appeal") + val nameClosePresetName = "Account closure for name in 48h" private[mod] object setting { From 2836a215f131fa9e3e9b7a7f35fca21bb5fbecca Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 16:53:51 +0100 Subject: [PATCH 013/144] Send chat panic zulip notification to pub comms stream as well --- modules/irc/src/main/IrcApi.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/irc/src/main/IrcApi.scala b/modules/irc/src/main/IrcApi.scala index e91ef0f740..fc847383d5 100644 --- a/modules/irc/src/main/IrcApi.scala +++ b/modules/irc/src/main/IrcApi.scala @@ -96,10 +96,10 @@ final class IrcApi( // def printBan(mod: Holder, print: String, userIds: List[User.ID]): Funit = // logMod(mod.id, "footprints", s"Ban print $print of ${userIds} users: ${userIds map linkifyUsers}") - def chatPanic(mod: Holder, v: Boolean): Funit = - zulip(_.mod.log, "chat panic")( - s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("/mod/chat-panic", " Chat Panic")}" - ) + def chatPanic(mod: Holder, v: Boolean): Funit = { + val msg = s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("/mod/chat-panic", " Chat Panic")}" + zulip(_.mod.log, "chat panic")(msg) >> zulip(_.mod.commsPublic, "main")(msg) + } def garbageCollector(msg: String): Funit = zulip(_.mod.adminLog, "garbage collector")(markdown linkifyUsers msg) From 7a769bc6643d1cfb68c310f173764fb8b33f104f Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 17:21:07 +0100 Subject: [PATCH 014/144] Fix "delete imported game" button style --- ui/analyse/css/_action-menu.scss | 5 ----- 1 file changed, 5 deletions(-) diff --git a/ui/analyse/css/_action-menu.scss b/ui/analyse/css/_action-menu.scss index 6ceb904b4f..c1945231b3 100644 --- a/ui/analyse/css/_action-menu.scss +++ b/ui/analyse/css/_action-menu.scss @@ -82,11 +82,6 @@ text-align: right; margin-top: 6px; - .button { - display: inline-block; - padding: 0 8px; - } - .button::before { font-size: 1.2em; } From b6a3d9fc15c338f17a3b51ab44d0beb64164d79e Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 17:21:28 +0100 Subject: [PATCH 015/144] Avoid redirecting to study when admin joining broadcast --- ui/analyse/src/study/studyMembers.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ui/analyse/src/study/studyMembers.ts b/ui/analyse/src/study/studyMembers.ts index b53d8a9fb3..5733b68850 100644 --- a/ui/analyse/src/study/studyMembers.ts +++ b/ui/analyse/src/study/studyMembers.ts @@ -1,4 +1,5 @@ import { prop, Prop } from 'common'; +import { textRaw as xhrTextRaw } from 'common/xhr'; import { bind, onInsert, dataIcon } from 'common/snabbdom'; import { h, VNode } from 'snabbdom'; import { AnalyseSocketSend } from '../socket'; @@ -310,17 +311,15 @@ export function view(ctrl: StudyCtrl): VNode { 'form.admin', { key: ':admin', - attrs: { - method: 'post', - action: `/study/${ctrl.data.id}/admin`, - }, + hook: onInsert(el => + el.addEventListener('submit', () => + xhrTextRaw(`/study/${ctrl.data.id}/admin`, { method: 'post' }).then(() => location.reload()) + ) + ), }, [ h( 'button.button.button-red.button-thin', - { - attrs: { type: 'submit' }, - }, 'Enter as admin' ), ] From 002df6f7f0b5b0ea299195eb2110b85430d4de4f Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 17:36:34 +0100 Subject: [PATCH 016/144] CSS: Improve simul styling --- ui/simul/css/_show.scss | 7 ++++++- ui/simul/src/view/created.ts | 6 +++--- ui/simul/src/view/main.ts | 12 +++++++----- ui/simul/src/view/util.ts | 2 +- 4 files changed, 17 insertions(+), 10 deletions(-) diff --git a/ui/simul/css/_show.scss b/ui/simul/css/_show.scss index d904128896..c376ab5d69 100644 --- a/ui/simul/css/_show.scss +++ b/ui/simul/css/_show.scss @@ -10,11 +10,16 @@ $box-padding: var(--box-padding); @import 'started'; .simul { + @include breakpoint($mq-xx-small) { + .box__top { + flex-wrap: nowrap; + } + } + h1 { font-size: 2.1em; .author { - margin-left: 0.7em; font-size: 0.7em; } } diff --git a/ui/simul/src/view/created.ts b/ui/simul/src/view/created.ts index 7d5976ad3e..5439976e50 100644 --- a/ui/simul/src/view/created.ts +++ b/ui/simul/src/view/created.ts @@ -1,12 +1,12 @@ -import { h, VNode } from 'snabbdom'; -import { bind } from 'common/snabbdom'; +import { h } from 'snabbdom'; +import { bind, MaybeVNode } from 'common/snabbdom'; import SimulCtrl from '../ctrl'; import { Applicant } from '../interfaces'; import xhr from '../xhr'; import * as util from './util'; import modal from 'common/modal'; -export default function (showText: (ctrl: SimulCtrl) => VNode) { +export default function (showText: (ctrl: SimulCtrl) => MaybeVNode) { return (ctrl: SimulCtrl) => { const candidates = ctrl.candidates().sort(byName), accepted = ctrl.accepted().sort(byName), diff --git a/ui/simul/src/view/main.ts b/ui/simul/src/view/main.ts index b26227f2fd..eb390ca55a 100644 --- a/ui/simul/src/view/main.ts +++ b/ui/simul/src/view/main.ts @@ -46,11 +46,13 @@ export default function (ctrl: SimulCtrl) { } const showText = (ctrl: SimulCtrl) => - h('div.simul-text', [ - h('p', { - hook: richHTML(ctrl.data.text), - }), - ]); + ctrl.data.text.length > 0 + ? h('div.simul-text', [ + h('p', { + hook: richHTML(ctrl.data.text), + }), + ]) + : undefined; const started = (ctrl: SimulCtrl) => [util.title(ctrl), showText(ctrl), results(ctrl), pairings(ctrl)]; diff --git a/ui/simul/src/view/util.ts b/ui/simul/src/view/util.ts index 5dff403a04..442da300c5 100644 --- a/ui/simul/src/view/util.ts +++ b/ui/simul/src/view/util.ts @@ -24,4 +24,4 @@ export function player(p: Player, ctrl: SimulCtrl) { const userName = (u: LightUser) => (u.title ? [h('span.utitle', u.title), ' ' + u.name] : [u.name]); export const title = (ctrl: SimulCtrl) => - h('h1', [ctrl.data.fullName, h('span.author', ctrl.trans.vdom('by', player(ctrl.data.host, ctrl)))]); + h('h1', [ctrl.data.fullName, h('br'), h('span.author', ctrl.trans.vdom('by', player(ctrl.data.host, ctrl)))]); From 07abf0f5263288808b5ea850769000adc57c96ba Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 17:38:06 +0100 Subject: [PATCH 017/144] Fix: Engine toggle tooltip looks like I but is L --- ui/ceval/src/view.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/ceval/src/view.ts b/ui/ceval/src/view.ts index 9abcde3f77..f2b54d6d72 100644 --- a/ui/ceval/src/view.ts +++ b/ui/ceval/src/view.ts @@ -232,7 +232,7 @@ export function renderCeval(ctrl: ParentCtrl): VNode | undefined { : h( 'div.switch', { - attrs: { title: trans.noarg('toggleLocalEvaluation') + ' (l)' }, + attrs: { title: trans.noarg('toggleLocalEvaluation') + ' (L)' }, }, [ h('input#analyse-toggle-ceval.cmn-toggle.cmn-toggle--subtle', { From c4aece719785ba435f364709c4e938eaac2c3e29 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 9 Nov 2021 18:09:39 +0100 Subject: [PATCH 018/144] Prettier --- ui/analyse/src/study/studyMembers.ts | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ui/analyse/src/study/studyMembers.ts b/ui/analyse/src/study/studyMembers.ts index 5733b68850..a5e5edf2ba 100644 --- a/ui/analyse/src/study/studyMembers.ts +++ b/ui/analyse/src/study/studyMembers.ts @@ -317,12 +317,7 @@ export function view(ctrl: StudyCtrl): VNode { ) ), }, - [ - h( - 'button.button.button-red.button-thin', - 'Enter as admin' - ), - ] + [h('button.button.button-red.button-thin', 'Enter as admin')] ) : null, ] From 50cf322e25d2048416a708d20eb221d57dc2a8c9 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 10 Nov 2021 08:30:40 +0100 Subject: [PATCH 019/144] Explorer: Separate since/until config by db --- ui/analyse/src/explorer/explorerConfig.ts | 36 ++++++++++++++++------- ui/analyse/src/explorer/explorerXhr.ts | 9 +++--- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/ui/analyse/src/explorer/explorerConfig.ts b/ui/analyse/src/explorer/explorerConfig.ts index c3f947e2fb..833443eb19 100644 --- a/ui/analyse/src/explorer/explorerConfig.ts +++ b/ui/analyse/src/explorer/explorerConfig.ts @@ -18,6 +18,13 @@ const allRatings = [1600, 1800, 2000, 2200, 2500]; const minYear = 1952; type Month = string; +type ByDbSetting = { + since: StoredProp; + until: StoredProp; +}; +type ByDbSettings = { + [key in ExplorerDb]: ByDbSetting; +}; export interface ExplorerConfigData { open: Prop; @@ -25,14 +32,14 @@ export interface ExplorerConfigData { rating: StoredJsonProp; speed: StoredJsonProp; mode: StoredJsonProp; - since: StoredProp; - until: StoredProp; + byDbData: ByDbSettings; playerName: { open: Prop; value: StoredProp; previous: StoredJsonProp; }; color: Prop; + byDb(): ByDbSetting; } export class ExplorerConfigCtrl { @@ -43,20 +50,29 @@ export class ExplorerConfigCtrl { constructor(readonly root: AnalyseCtrl, readonly variant: VariantKey, readonly onClose: () => void) { this.myName = document.body.dataset['user']; if (variant === 'standard') this.allDbs.unshift('masters'); + const byDbData = {} as ByDbSettings; + for (const db of this.allDbs) { + byDbData[db] = { + since: storedProp('explorer.since-2.' + db, ''), + until: storedProp('explorer.until-2.' + db, ''), + }; + } this.data = { open: prop(false), db: storedProp('explorer.db.' + variant, this.allDbs[0]), rating: storedJsonProp('explorer.rating', () => allRatings), speed: storedJsonProp('explorer.speed', () => allSpeeds), mode: storedJsonProp('explorer.mode', () => allModes), - since: storedProp('explorer.since-2', ''), - until: storedProp('explorer.until-2', ''), + byDbData, playerName: { open: prop(false), value: storedProp('explorer.player.name', document.body.dataset['user'] || ''), previous: storedJsonProp('explorer.player.name.previous', () => []), }, color: prop('white'), + byDb() { + return this.byDbData[this.db() as ExplorerDb] || this.byDbData.lichess; + }, }; } @@ -91,8 +107,8 @@ export class ExplorerConfigCtrl { }; fullHouse = () => - this.data.since() <= `${minYear}-01` && - (!this.data.until() || new Date().toISOString().slice(0, 7) <= this.data.until()) && + this.data.byDb().since() <= `${minYear}-01` && + (!this.data.byDb().until() || new Date().toISOString().slice(0, 7) <= this.data.byDb().until()) && (this.data.db() === 'masters' || this.data.speed().length == allSpeeds.length) && (this.data.db() !== 'lichess' || this.data.rating().length == allRatings.length) && (this.data.db() !== 'player' || this.data.mode().length == allModes.length); @@ -155,8 +171,8 @@ const playerDb = (ctrl: ExplorerConfigCtrl) => { const masterDb = (ctrl: ExplorerConfigCtrl) => h('div', [ h('section.date', [ - h('label', ['Since', yearInput(ctrl.data.since, () => '', ctrl.root.redraw)]), - h('label', ['Until', yearInput(ctrl.data.until, ctrl.data.since, ctrl.root.redraw)]), + h('label', ['Since', yearInput(ctrl.data.byDb().since, () => '', ctrl.root.redraw)]), + h('label', ['Until', yearInput(ctrl.data.byDb().until, ctrl.data.byDb().since, ctrl.root.redraw)]), ]), ]); @@ -258,8 +274,8 @@ const yearInput = (prop: StoredProp, after: () => Month, redraw: Redraw) const monthSection = (ctrl: ExplorerConfigCtrl) => h('section.date', [ - h('label', ['Since', monthInput(ctrl.data.since, () => '', ctrl.root.redraw)]), - h('label', ['Until', monthInput(ctrl.data.until, ctrl.data.since, ctrl.root.redraw)]), + h('label', ['Since', monthInput(ctrl.data.byDb().since, () => '', ctrl.root.redraw)]), + h('label', ['Until', monthInput(ctrl.data.byDb().until, ctrl.data.byDb().since, ctrl.root.redraw)]), ]); const playerModal = (ctrl: ExplorerConfigCtrl) => { diff --git a/ui/analyse/src/explorer/explorerXhr.ts b/ui/analyse/src/explorer/explorerXhr.ts index fb6c821c66..8054ce80e1 100644 --- a/ui/analyse/src/explorer/explorerXhr.ts +++ b/ui/analyse/src/explorer/explorerXhr.ts @@ -20,17 +20,18 @@ export async function opening( processData: (data: ExplorerData) => void ): Promise { const conf = opts.config; + const confByDb = conf.byDb(); const url = new URL(`/${opts.db}`, opts.endpoint); const params = url.searchParams; params.set('variant', opts.variant || 'standard'); params.set('fen', opts.rootFen); params.set('play', opts.play.join(',')); if (opts.db === 'masters') { - if (conf.since()) params.set('since', conf.since().split('-')[0]); - if (conf.until()) params.set('until', conf.until().split('-')[0]); + if (confByDb.since()) params.set('since', confByDb.since().split('-')[0]); + if (confByDb.until()) params.set('until', confByDb.until().split('-')[0]); } else { - if (conf.since()) params.set('since', conf.since()); - if (conf.until()) params.set('until', conf.until()); + if (confByDb.since()) params.set('since', confByDb.since()); + if (confByDb.until()) params.set('until', confByDb.until()); params.set('speeds', conf.speed().join(',')); } if (opts.db === 'lichess') { From 8bac0632ba4511c0c883ed6bab167a78d1610799 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 10 Nov 2021 10:28:45 +0100 Subject: [PATCH 020/144] team kick: Allow pasting members and existing values --- ui/site/src/teamAdmin.ts | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/ui/site/src/teamAdmin.ts b/ui/site/src/teamAdmin.ts index c6e83d22c9..0e4970023b 100644 --- a/ui/site/src/teamAdmin.ts +++ b/ui/site/src/teamAdmin.ts @@ -12,12 +12,17 @@ lichess.load.then(() => { }); function initTagify(input: HTMLInputElement, maxTags: number) { - const team = input.dataset['rel']; + const team = input.dataset.rel; const tagify = new Tagify(input, { pattern: /.{3,}/, maxTags, - enforceWhitelist: true, - whitelist: input.value.trim().split(/\s*,\s*/), + whitelist: [], + hooks: { + beforePaste: (_, data) => { + data.tagify.settings.enforceWhitelist = false; + return Promise.resolve(undefined); + }, + }, }); const doFetch: (term: string) => Promise = debounce( (term: string) => xhr.json(xhr.url('/player/autocomplete', { term, names: 1, team })), @@ -26,11 +31,12 @@ function initTagify(input: HTMLInputElement, maxTags: number) { tagify.on('input', e => { const term = e.detail.value.trim(); if (term.length < 3) return; - tagify.settings.whitelist!.length = 0; // reset the whitelist + tagify.whitelist = []; + tagify.settings.enforceWhitelist = true; // show loading animation and hide the suggestions dropdown tagify.loading(true).dropdown.hide.call(tagify); doFetch(term).then((list: string[]) => { - tagify.settings.whitelist!.splice(0, list.length, ...list); // update whitelist Array in-place + tagify.whitelist = list; tagify.loading(false).dropdown.show.call(tagify, term); // render the suggestions dropdown }); }); From 0e8b09e4f7f7e67e4b6d4226a0a4856538dbe135 Mon Sep 17 00:00:00 2001 From: kraktus Date: Wed, 10 Nov 2021 10:48:31 +0100 Subject: [PATCH 021/144] Fix source string Cancelled takes two `l` in british english, also makes it consistent with other source strings --- translation/source/challenge.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/translation/source/challenge.xml b/translation/source/challenge.xml index dedbdae844..097a4fce7f 100644 --- a/translation/source/challenge.xml +++ b/translation/source/challenge.xml @@ -4,7 +4,7 @@ Challenge to a game Challenge declined Challenge accepted! - Challenge canceled. + Challenge cancelled. Please register to send challenges. You cannot challenge %s. %s does not accept challenges. From 0479d710220b7af6a4664b6d7abd2718c66bc069 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 10 Nov 2021 12:15:30 +0100 Subject: [PATCH 022/144] Don't sort bots twice --- app/views/user/bots.scala | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/views/user/bots.scala b/app/views/user/bots.scala index aa0bfe078e..6813a82a34 100644 --- a/app/views/user/bots.scala +++ b/app/views/user/bots.scala @@ -50,9 +50,7 @@ object bots { private def botTable(users: List[User])(implicit ctx: Context) = table(cls := "slist slist-pad")( tbody( - users.sortBy { u => - (if (u.isVerified) -1 else 1, -u.playTime.??(_.total)) - } map { u => + users map { u => tr( td(userLink(u)), u.profile From 90d361757794707636ecc2375989eaa6043661a8 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Wed, 10 Nov 2021 22:32:19 +0100 Subject: [PATCH 023/144] Update google-auth-library-oauth2-http to 1.3.0 --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 7905ab1361..e419d714ef 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -13,7 +13,7 @@ object Dependencies { val maxmind = "com.sanoma.cda" %% "maxmind-geoip2-scala" % "1.3.1-THIB" val prismic = "io.prismic" %% "scala-kit" % "1.2.19-THIB213" val scaffeine = "com.github.blemale" %% "scaffeine" % "5.1.1" % "compile" - val googleOAuth = "com.google.auth" % "google-auth-library-oauth2-http" % "1.2.2" + val googleOAuth = "com.google.auth" % "google-auth-library-oauth2-http" % "1.3.0" val scalaUri = "io.lemonlabs" %% "scala-uri" % "3.6.0" val scalatags = "com.lihaoyi" %% "scalatags" % "0.10.0" val lettuce = "io.lettuce" % "lettuce-core" % "6.1.5.RELEASE" From 836271329f75fc09e3e212b6954265e96f76e3be Mon Sep 17 00:00:00 2001 From: Sharad Date: Thu, 11 Nov 2021 01:07:03 -0500 Subject: [PATCH 024/144] Skip unselect square only in study --- ui/analyse/src/ctrl.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/analyse/src/ctrl.ts b/ui/analyse/src/ctrl.ts index 465e9bf492..71841e8f79 100644 --- a/ui/analyse/src/ctrl.ts +++ b/ui/analyse/src/ctrl.ts @@ -389,6 +389,7 @@ export default class AnalyseCtrl { userJump = (path: Tree.Path): void => { this.autoplay.stop(); + if (!this.study) this.withCg(cg => cg.selectSquare(null)); if (this.practice) { const prev = this.path; this.practice.preUserJump(prev, path); From 2cfcc927646e0bfe5c54face2ab78e3e79218734 Mon Sep 17 00:00:00 2001 From: Sharad Date: Thu, 11 Nov 2021 01:35:49 -0500 Subject: [PATCH 025/144] use gamebookplay() --- ui/analyse/src/ctrl.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/analyse/src/ctrl.ts b/ui/analyse/src/ctrl.ts index 71841e8f79..7dcb8386c6 100644 --- a/ui/analyse/src/ctrl.ts +++ b/ui/analyse/src/ctrl.ts @@ -389,7 +389,7 @@ export default class AnalyseCtrl { userJump = (path: Tree.Path): void => { this.autoplay.stop(); - if (!this.study) this.withCg(cg => cg.selectSquare(null)); + if (!this.gamebookPlay()) this.withCg(cg => cg.selectSquare(null)); if (this.practice) { const prev = this.path; this.practice.preUserJump(prev, path); From 6e7a4ba40db6dbd9195897b8d7aa31cb633cf27d Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Thu, 11 Nov 2021 11:49:17 +0100 Subject: [PATCH 026/144] scalafmt --- modules/irc/src/main/IrcApi.scala | 3 ++- modules/mod/src/main/Presets.scala | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/modules/irc/src/main/IrcApi.scala b/modules/irc/src/main/IrcApi.scala index fc847383d5..d84eccebed 100644 --- a/modules/irc/src/main/IrcApi.scala +++ b/modules/irc/src/main/IrcApi.scala @@ -97,7 +97,8 @@ final class IrcApi( // logMod(mod.id, "footprints", s"Ban print $print of ${userIds} users: ${userIds map linkifyUsers}") def chatPanic(mod: Holder, v: Boolean): Funit = { - val msg = s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("/mod/chat-panic", " Chat Panic")}" + val msg = + s":stop: ${markdown.modLink(mod.user)} ${if (v) "enabled" else "disabled"} ${markdown.lichessLink("/mod/chat-panic", " Chat Panic")}" zulip(_.mod.log, "chat panic")(msg) >> zulip(_.mod.commsPublic, "main")(msg) } diff --git a/modules/mod/src/main/Presets.scala b/modules/mod/src/main/Presets.scala index b76f03f7b0..1fba16ac90 100644 --- a/modules/mod/src/main/Presets.scala +++ b/modules/mod/src/main/Presets.scala @@ -57,7 +57,7 @@ case class ModPreset(name: String, text: String, permissions: Set[Permission]) { object ModPresets { - val groups = List("PM", "appeal") + val groups = List("PM", "appeal") val nameClosePresetName = "Account closure for name in 48h" private[mod] object setting { From f34edfd1de7d8dd3ca19e15e48c4f0568f04bcd2 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Thu, 11 Nov 2021 11:48:17 +0100 Subject: [PATCH 027/144] Don't adjust colorIt when aborting fixed color lobby games --- modules/game/src/main/GameRepo.scala | 4 ++++ modules/lobby/src/main/AbortListener.scala | 6 +++++- modules/lobby/src/main/Biter.scala | 10 +++++++++- modules/user/src/main/UserRepo.scala | 2 +- 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/modules/game/src/main/GameRepo.scala b/modules/game/src/main/GameRepo.scala index 292b74eb1c..ddc2f98bca 100644 --- a/modules/game/src/main/GameRepo.scala +++ b/modules/game/src/main/GameRepo.scala @@ -1,5 +1,7 @@ package lila.game +import scala.concurrent.duration._ + import chess.format.{ FEN, Forsyth } import chess.{ Color, Status } import org.joda.time.DateTime @@ -19,6 +21,8 @@ final class GameRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont import Game.{ ID, BSONFields => F } import Player.holdAlertBSONHandler + val fixedColorLobbyCache = new lila.memo.ExpireSetMemo(2 hours) + def game(gameId: ID): Fu[Option[Game]] = coll.byId[Game](gameId) def gameFromSecondary(gameId: ID): Fu[Option[Game]] = coll.secondaryPreferred.byId[Game](gameId) diff --git a/modules/lobby/src/main/AbortListener.scala b/modules/lobby/src/main/AbortListener.scala index 54f521d59b..548cbf936b 100644 --- a/modules/lobby/src/main/AbortListener.scala +++ b/modules/lobby/src/main/AbortListener.scala @@ -4,6 +4,7 @@ import lila.game.{ Pov, Source } final private class AbortListener( userRepo: lila.user.UserRepo, + gameRepo: lila.game.GameRepo, seekApi: SeekApi, lobbyActor: LobbySyncActor )(implicit ec: scala.concurrent.ExecutionContext) { @@ -14,7 +15,10 @@ final private class AbortListener( lobbyActor.registerAbortedGame(pov.game) private def cancelColorIncrement(pov: Pov): Unit = - if (pov.game.source.exists(s => s == Source.Lobby || s == Source.Pool)) pov.game.userIds match { + if ( + pov.game.source + .exists(s => s == Source.Lobby || s == Source.Pool) && !gameRepo.fixedColorLobbyCache.get(pov.game.id) + ) pov.game.userIds match { case List(u1, u2) => userRepo.incColor(u1, -1) userRepo.incColor(u2, 1) diff --git a/modules/lobby/src/main/Biter.scala b/modules/lobby/src/main/Biter.scala index ce7c7b6038..ed1b755d45 100644 --- a/modules/lobby/src/main/Biter.scala +++ b/modules/lobby/src/main/Biter.scala @@ -36,6 +36,7 @@ final private class Biter( _ <- gameRepo insertDenormalized game } yield { lila.mon.lobby.hook.join.increment() + rememberIfFixedColor(hook.realColor, game) JoinHook(sri, hook, game, creatorColor) } @@ -50,7 +51,14 @@ final private class Biter( blackUser = creatorColor.fold(user.some, owner.some) ).withUniqueId _ <- gameRepo insertDenormalized game - } yield JoinSeek(user.id, seek, game, creatorColor) + } yield { + rememberIfFixedColor(seek.realColor, game) + JoinSeek(user.id, seek, game, creatorColor) + } + + private def rememberIfFixedColor(color: Color, game: Game) = + if (color != Color.Random) + gameRepo.fixedColorLobbyCache put game.id private def assignCreatorColor( creatorUser: Option[User], diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala index 060610ed32..f6fe9374d1 100644 --- a/modules/user/src/main/UserRepo.scala +++ b/modules/user/src/main/UserRepo.scala @@ -173,7 +173,7 @@ final class UserRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont coll .update(ordered = false, WriteConcern.Unacknowledged) .one( - $id(userId) ++ (value < 0).??($doc(F.colorIt $gt -3)), + $id(userId) ++ (if (value < 0) $doc(F.colorIt $gt -3) else $doc(F.colorIt $lt 5)), $inc(F.colorIt -> value) ) .unit From 431c01a4d928b1741a6a3befa7d5f6037565417a Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 11 Nov 2021 08:44:53 +0100 Subject: [PATCH 028/144] {master} more round volatile system messages --- modules/round/src/main/Drawer.scala | 2 +- modules/round/src/main/Finisher.scala | 10 +++++----- modules/round/src/main/Messenger.scala | 12 ++++++++++++ modules/round/src/main/Rematcher.scala | 8 ++++---- modules/round/src/main/RoundAsyncActor.scala | 2 +- modules/round/src/main/Takebacker.scala | 6 +++--- 6 files changed, 26 insertions(+), 14 deletions(-) diff --git a/modules/round/src/main/Drawer.scala b/modules/round/src/main/Drawer.scala index 5ff6135c7f..170216f0cc 100644 --- a/modules/round/src/main/Drawer.scala +++ b/modules/round/src/main/Drawer.scala @@ -37,7 +37,7 @@ final private[round] class Drawer( case pov if pov.game.history.threefoldRepetition => finisher.other(pov.game, _.Draw, None) case pov if pov.opponent.isOfferingDraw => - finisher.other(pov.game, _.Draw, None, Some(trans.drawOfferAccepted.txt())) + finisher.other(pov.game, _.Draw, None, Messenger.Persistent(trans.drawOfferAccepted.txt()).some) case Pov(g, color) if g playerCanOfferDraw color => val progress = Progress(g) map { _ offerDraw color } messenger.system(g, color.fold(trans.whiteOffersDraw, trans.blackOffersDraw).txt()) diff --git a/modules/round/src/main/Finisher.scala b/modules/round/src/main/Finisher.scala index af35ddbca0..d9f338797c 100644 --- a/modules/round/src/main/Finisher.scala +++ b/modules/round/src/main/Finisher.scala @@ -48,7 +48,7 @@ final private class Finisher( other(game, _.Aborted, none) } else if (game.player(!game.player.color).isOfferingDraw) { - apply(game, _.Draw, None, Some(trans.drawOfferAccepted.txt())) + apply(game, _.Draw, None, Messenger.Persistent(trans.drawOfferAccepted.txt()).some) } else { val winner = Some(!game.player.color) ifFalse game.situation.opponentHasInsufficientMaterial apply(game, _.Outoftime, winner) >>- @@ -62,14 +62,14 @@ final private class Finisher( lila.mon.round.expiration.count.increment() playban.noStart(Pov(game, culprit)) if (game.isMandatory) apply(game, _.NoStart, Some(!culprit.color)) - else apply(game, _.Aborted, None, Some("Game aborted by server")) + else apply(game, _.Aborted, None, Messenger.Persistent("Game aborted by server").some) } def other( game: Game, status: Status.type => Status, winner: Option[Color], - message: Option[String] = None + message: Option[Messenger.SystemMessage] = None )(implicit proxy: GameProxy): Fu[Events] = apply(game, status, winner, message) >>- playban.other(game, status, winner).unit @@ -108,7 +108,7 @@ final private class Finisher( game: Game, makeStatus: Status.type => Status, winnerC: Option[Color], - message: Option[String] = None + message: Option[Messenger.SystemMessage] = None )(implicit proxy: GameProxy): Fu[Events] = { val status = makeStatus(Status) val prog = game.finish(status, winnerC) @@ -139,7 +139,7 @@ final private class Finisher( .flatMap { case (whiteO, blackO) => val finish = FinishGame(g, whiteO, blackO) updateCountAndPerfs(finish) map { ratingDiffs => - message foreach { messenger.system(g, _) } + message foreach { messenger(g, _) } gameRepo game g.id foreach { newGame => newGame foreach proxy.setFinishedGame val newFinish = finish.copy(game = newGame | g) diff --git a/modules/round/src/main/Messenger.scala b/modules/round/src/main/Messenger.scala index fd7f587dbb..60e11fa5ce 100644 --- a/modules/round/src/main/Messenger.scala +++ b/modules/round/src/main/Messenger.scala @@ -13,6 +13,11 @@ final class Messenger(api: ChatApi) { def volatile(game: Game, message: String): Unit = system(persistent = false)(game, message) + def apply(game: Game, message: Messenger.SystemMessage): Unit = message match { + case Messenger.Persistent(msg) => system(persistent = true)(game, msg) + case Messenger.Volatile(msg) => system(persistent = false)(game, msg) + } + def system(persistent: Boolean)(game: Game, message: String): Unit = if (game.nonAi) { api.userChat.volatile(watcherId(Chat.Id(game.id)), message, _.Round) if (persistent) api.userChat.system(Chat.Id(game.id), message, _.Round) @@ -58,3 +63,10 @@ final class Messenger(api: ChatApi) { private def watcherId(chatId: Chat.Id) = Chat.Id(s"$chatId/w") private def watcherId(gameId: Game.Id) = Chat.Id(s"$gameId/w") } + +private object Messenger { + + sealed trait SystemMessage { val msg: String } + case class Persistent(msg: String) extends SystemMessage + case class Volatile(msg: String) extends SystemMessage +} diff --git a/modules/round/src/main/Rematcher.scala b/modules/round/src/main/Rematcher.scala index 50605f5754..0f36554b99 100644 --- a/modules/round/src/main/Rematcher.scala +++ b/modules/round/src/main/Rematcher.scala @@ -56,10 +56,10 @@ final private class Rematcher( } def no(pov: Pov): Fu[Events] = { - if (isOffering(pov)) messenger.system(pov.game, trans.rematchOfferCanceled.txt()) + if (isOffering(pov)) messenger.volatile(pov.game, trans.rematchOfferCanceled.txt()) else if (isOffering(!pov)) { declined put pov.fullId - messenger.system(pov.game, trans.rematchOfferDeclined.txt()) + messenger.volatile(pov.game, trans.rematchOfferDeclined.txt()) } offers invalidate pov.game.id fuccess(List(Event.RematchOffer(by = none))) @@ -80,7 +80,7 @@ final private class Rematcher( _ = if (pov.game.variant == Chess960 && !chess960.get(pov.gameId)) chess960.put(nextGame.id) _ <- gameRepo insertDenormalized nextGame } yield { - messenger.system(pov.game, trans.rematchOfferAccepted.txt()) + messenger.volatile(pov.game, trans.rematchOfferAccepted.txt()) onStart(nextGame.id) redirectEvents(nextGame) } @@ -88,7 +88,7 @@ final private class Rematcher( } private def rematchCreate(pov: Pov): Events = { - messenger.system(pov.game, trans.rematchOfferSent.txt()) + messenger.volatile(pov.game, trans.rematchOfferSent.txt()) pov.opponent.userId foreach { forId => Bus.publish(lila.hub.actorApi.round.RematchOffer(pov.gameId), s"rematchFor:$forId") } diff --git a/modules/round/src/main/RoundAsyncActor.scala b/modules/round/src/main/RoundAsyncActor.scala index 324c2bd166..f2e36f57b9 100644 --- a/modules/round/src/main/RoundAsyncActor.scala +++ b/modules/round/src/main/RoundAsyncActor.scala @@ -403,7 +403,7 @@ final private[round] class RoundAsyncActor( case WsBoot => handle { game => game.playable ?? { - messenger.system(game, "Lichess has been updated! Sorry for the inconvenience.") + messenger.volatile(game, "Lichess has been updated! Sorry for the inconvenience.") val progress = moretimer.give(game, Color.all, 20 seconds) proxy save progress inject progress.events } diff --git a/modules/round/src/main/Takebacker.scala b/modules/round/src/main/Takebacker.scala index ec4441b96b..26cbf444c9 100644 --- a/modules/round/src/main/Takebacker.scala +++ b/modules/round/src/main/Takebacker.scala @@ -35,7 +35,7 @@ final private class Takebacker( double(game) >>- publishTakeback(pov) dmap (_ -> situation) case Pov(game, color) if (game playerCanProposeTakeback color) && situation.offerable => { - messenger.system(game, trans.takebackPropositionSent.txt()) + messenger.volatile(game, trans.takebackPropositionSent.txt()) val progress = Progress(game) map { g => g.updatePlayer(color, _ proposeTakeback g.turns) } @@ -50,7 +50,7 @@ final private class Takebacker( def no(situation: TakebackSituation)(pov: Pov)(implicit proxy: GameProxy): Fu[(Events, TakebackSituation)] = pov match { case Pov(game, color) if pov.player.isProposingTakeback => - messenger.system(game, trans.takebackPropositionCanceled.txt()) + messenger.volatile(game, trans.takebackPropositionCanceled.txt()) val progress = Progress(game) map { g => g.updatePlayer(color, _.removeTakebackProposition) } @@ -58,7 +58,7 @@ final private class Takebacker( publishTakebackOffer(progress.game) inject List(Event.TakebackOffers(white = false, black = false)) -> situation.decline case Pov(game, color) if pov.opponent.isProposingTakeback => - messenger.system(game, trans.takebackPropositionDeclined.txt()) + messenger.volatile(game, trans.takebackPropositionDeclined.txt()) val progress = Progress(game) map { g => g.updatePlayer(!color, _.removeTakebackProposition) } From 69f1143c9d1f83eddfb2bc2f7ebe7ce4ba87351b Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 12 Nov 2021 07:50:07 +0100 Subject: [PATCH 029/144] New Crowdin updates (#10080) * New translations: emails.xml (Sanskrit) * New translations: puzzleTheme.xml (English, United States) * New translations: learn.xml (Odia) * New translations: site.xml (Odia) * New translations: learn.xml (Odia) * New translations: site.xml (Odia) * New translations: emails.xml (Odia) * New translations: class.xml (Odia) * New translations: contact.xml (Odia) * New translations: site.xml (Odia) * New translations: study.xml (Odia) * New translations: patron.xml (Odia) * New translations: team.xml (Odia) * New translations: faq.xml (Odia) * New translations: ublog.xml (Odia) * New translations: site.xml (Odia) * New translations: team.xml (Odia) * New translations: site.xml (Odia) * New translations: contact.xml (Odia) * New translations: site.xml (Odia) * New translations: activity.xml (Odia) * New translations: faq.xml (Persian) * New translations: tourname.xml (English, United States) * New translations: tourname.xml (English, United States) * New translations: puzzleTheme.xml (Persian) * New translations: study.xml (Indonesian) * New translations: study.xml (Indonesian) * New translations: site.xml (Indonesian) * New translations: study.xml (Hebrew) * New translations: site.xml (Hindi) * New translations: site.xml (Hindi) * New translations: study.xml (Hindi) * New translations: activity.xml (Hindi) * New translations: site.xml (Hindi) * New translations: settings.xml (Hindi) * New translations: puzzle.xml (Hindi) * New translations: storm.xml (Chinese Simplified) * New translations: challenge.xml (Russian) * New translations: challenge.xml (French) --- translation/dest/activity/hi-IN.xml | 1 + translation/dest/activity/or-IN.xml | 4 +++ translation/dest/challenge/fr-FR.xml | 2 +- translation/dest/challenge/ru-RU.xml | 2 +- translation/dest/class/or-IN.xml | 16 +++++------ translation/dest/contact/or-IN.xml | 5 ++-- translation/dest/emails/or-IN.xml | 4 +-- translation/dest/emails/sa-IN.xml | 4 ++- translation/dest/faq/fa-IR.xml | 4 +++ translation/dest/faq/or-IN.xml | 2 +- translation/dest/learn/or-IN.xml | 8 +++--- translation/dest/patron/or-IN.xml | 8 +++--- translation/dest/puzzle/hi-IN.xml | 4 +++ translation/dest/puzzleTheme/en-US.xml | 6 ++-- translation/dest/puzzleTheme/fa-IR.xml | 1 + translation/dest/settings/hi-IN.xml | 2 +- translation/dest/site/hi-IN.xml | 11 +++++++- translation/dest/site/id-ID.xml | 24 ++++++++-------- translation/dest/site/or-IN.xml | 38 +++++++++++++++----------- translation/dest/storm/zh-CN.xml | 2 ++ translation/dest/study/he-IL.xml | 2 ++ translation/dest/study/hi-IN.xml | 7 +++++ translation/dest/study/id-ID.xml | 2 ++ translation/dest/study/or-IN.xml | 6 ++-- translation/dest/team/or-IN.xml | 4 +-- translation/dest/tourname/en-US.xml | 6 ++-- translation/dest/ublog/or-IN.xml | 6 ++-- 27 files changed, 113 insertions(+), 68 deletions(-) diff --git a/translation/dest/activity/hi-IN.xml b/translation/dest/activity/hi-IN.xml index 0f749db7cc..f931121801 100644 --- a/translation/dest/activity/hi-IN.xml +++ b/translation/dest/activity/hi-IN.xml @@ -62,6 +62,7 @@ %4$s में %3$s खेल के साथ %1$s (टॉप %2$s%%) रैंक किया गया %4$s में %3$s खेल के साथ %1$s (टॉप %2$s%%) रैंक किया गया + #%1$s स्थान %2$s मे lichess.org पर साइन किया गया %s टीम में शामिल हुए diff --git a/translation/dest/activity/or-IN.xml b/translation/dest/activity/or-IN.xml index ab4d2aab72..bf0382980f 100644 --- a/translation/dest/activity/or-IN.xml +++ b/translation/dest/activity/or-IN.xml @@ -1,4 +1,8 @@ କାର୍ଯ୍ୟକଳାପ + + ଜଣେ %2$s ଭାବରେ %1$s ମାସ ପାଇଁ lichess.org କୁ ସମର୍ଥନ କଲେ + ଜଣେ %2$s ଭାବରେ %1$s ମାସ ପାଇଁ lichess.org କୁ ସମର୍ଥନ କଲେ + diff --git a/translation/dest/challenge/fr-FR.xml b/translation/dest/challenge/fr-FR.xml index f6149aee69..5089227697 100644 --- a/translation/dest/challenge/fr-FR.xml +++ b/translation/dest/challenge/fr-FR.xml @@ -4,7 +4,7 @@ Défier ce joueur Défi refusé Défi accepté! - Défi annulé + Défi annulé. Veuillez vous inscrire pour envoyer des défis. Vous ne pouvez pas défier %s. %s n’accepte pas les défis. diff --git a/translation/dest/challenge/ru-RU.xml b/translation/dest/challenge/ru-RU.xml index fc18631961..5428910381 100644 --- a/translation/dest/challenge/ru-RU.xml +++ b/translation/dest/challenge/ru-RU.xml @@ -13,7 +13,7 @@ %s принимает вызовы только от друзей. Я не принимаю вызовы прямо сейчас. Я не принимаю вызовы прямо сейчас, пожалуйста, вызовите меня позже. - Такой контроль времени слишком быстрый для меня, пожалуйста, вызовите меня на игру с контролем времени побольше. + Такой контроль слишком быстрый для меня, пожалуйста, вызовите меня с контролем времени побольше. Такой контроль слишком медленный для меня, пожалуйста, вызовите меня с контролем времени поменьше. Я не принимаю вызовы с таким контролем времени. Вызовите меня на рейтинговую игру, пожалуйста. diff --git a/translation/dest/class/or-IN.xml b/translation/dest/class/or-IN.xml index 6cfd607b6d..1ad571dfcc 100644 --- a/translation/dest/class/or-IN.xml +++ b/translation/dest/class/or-IN.xml @@ -1,16 +1,16 @@ - ଛାତ୍ରମାନଙ୍କ ପାଇଁ ଶୀଘ୍ର ସୁରକ୍ଷିତ ଉପଭୋକ୍ତା ନାମ ଏବଂ ପାସ୍ଓ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ - ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର - ଏବେ ପାସ୍ଓ୍ୱାର୍ଡ଼ କପି କିମ୍ୱା ଲେଖିବାକୁ ନିଶ୍ଚିତ କରନ୍ତୁ। ଆପଣ ଏହାକୁ ପୁନର୍ବାର ଦେଖିବାକୁ ସମର୍ଥ ହେବେ ନାହିଁ! - ପାସ୍ଓ୍ୱାର୍ଡ଼: %s - ଛାତ୍ରଙ୍କ ପାଇଁ ଏକ ନୂତନ ପାସ୍ଓ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ + ଛାତ୍ରମାନଙ୍କ ପାଇଁ ଶୀଘ୍ର ସୁରକ୍ଷିତ ଉପଭୋକ୍ତା ନାମ ଏବଂ ପାସ୍‍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ + ପାସ୍‍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର + ଏବେ ପାସ୍‍ୱାର୍ଡ଼ କପି କିମ୍ୱା ଲେଖିବାକୁ ନିଶ୍ଚିତ କରନ୍ତୁ। ଆପଣ ଏହାକୁ ପୁନର୍ବାର ଦେଖିବାକୁ ସମର୍ଥ ହେବେ ନାହିଁ! + ପାସ୍‍ୱାର୍ଡ଼: %s + ଛାତ୍ରଙ୍କ ପାଇଁ ଏକ ନୂତନ ପାସ୍‍ୱାର୍ଡ଼ ସୃଷ୍ଟି କରନ୍ତୁ ଛାତ୍ର: %1$s ଉପଭୋକ୍ତା ନାମ: %2$s -ପାସ୍ଓ୍ୱାର୍ଡ଼: %3$s - କୌଣସି ଇମେଲ୍ ଠିକଣା ଆବଶ୍ୟକ ନାହିଁ। ଏକ ପାସ୍ଓ୍ୱାର୍ଡ଼ ସୃଷ୍ଟି ହେବ, ଏବଂ ଆପଣଙ୍କୁ ଏହାକୁ ଛାତ୍ରଙ୍କ ନିକଟକୁ ପଠାଇବାକୁ ପଡ଼ିବ, ଯାହା ଫଳରେ ସେମାନେ ଲଗ୍ ଇନ୍ କରିପାରିବେ। +ପାସ୍‍ୱାର୍ଡ଼: %3$s + କୌଣସି ଇମେଲ୍ ଠିକଣା ଆବଶ୍ୟକ ନାହିଁ। ଏକ ପାସ୍‍ୱାର୍ଡ଼ ସୃଷ୍ଟି ହେବ, ଏବଂ ଆପଣଙ୍କୁ ଏହାକୁ ଛାତ୍ରଙ୍କ ନିକଟକୁ ପଠାଇବାକୁ ପଡ଼ିବ, ଯାହା ଫଳରେ ସେମାନେ ଲଗ୍ ଇନ୍ କରିପାରିବେ। ପରିଚାଳିତ ଏହି ଛାତ୍ର ଆକାଉଣ୍ଟ୍ ଟି ପରିଚାଳିତ ଅଟେ ପରିଚାଳିତରୁ ସ୍ୱୟଂଶାସିତକୁ ଅପଗ୍ରେଡ୍ କର - ଏକ ସ୍ନାତକ ଆକାଉଣ୍ଟ ପୁନର୍ବାର ପରିଚାଳନା କରାଯାଇପାରିବ ନାହିଁ। ଛାତ୍ର ଶିଶୁ ମୋଡ୍ ଟୋଗଲ୍ କରିବାକୁ ଏବଂ ନିଜେ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ସମର୍ଥ ହେବେ। + ଏକ ସ୍ନାତକ ଆକାଉଣ୍ଟ ପୁନର୍ବାର ପରିଚାଳନା କରାଯାଇପାରିବ ନାହିଁ। ଛାତ୍ର ଶିଶୁ ମୋଡ୍ ଟୋଗଲ୍ କରିବାକୁ ଏବଂ ନିଜେ ପାସ୍‍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ସମର୍ଥ ହେବେ। diff --git a/translation/dest/contact/or-IN.xml b/translation/dest/contact/or-IN.xml index 0eb3c534d2..de64c50c1a 100644 --- a/translation/dest/contact/or-IN.xml +++ b/translation/dest/contact/or-IN.xml @@ -1,7 +1,8 @@ - ମୁଁ ମୋର ପାସ୍ଓ୍ୱାର୍ଡ଼ ଭୁଲିଗଲି - ଆପଣଙ୍କ ଦ୍ୱିତୀୟ ଫ୍ୟାକ୍ଟର ଅପସାରଣ କରିବାକୁ ଏକ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ + ମୁଁ ମୋର ପାସ୍‍ୱାର୍ଡ଼ ଭୁଲିଗଲି + ଆପଣଙ୍କ ଦ୍ୱିତୀୟ ଫ୍ୟାକ୍ଟର ଅପସାରଣ କରିବାକୁ ଏକ ପାସ୍‍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ ସମ୍ପୂର୍ଣ୍ଣ କରନ୍ତୁ ଏକ ଖାତା ବନ୍ଦ କରିବାକୁ ଆମକୁ ଇମେଲ୍ ମାଧ୍ୟମରେ କୁହନ୍ତୁ ନାହିଁ, ଆମେ ତାହା କରିବୁ ନାହିଁ। ଏକ ଖାତା ପୁନଃଖୋଲିବାକୁ ଆମକୁ ଇମେଲ୍ ମାଧ୍ୟମରେ କୁହନ୍ତୁ ନାହିଁ, ଆମେ ତାହା କରିବୁ ନାହିଁ। + ଫୋରମର Lichess Feedback ବିଭାଗରେ diff --git a/translation/dest/emails/or-IN.xml b/translation/dest/emails/or-IN.xml index 7c3d8ec4c9..fcfc30b490 100644 --- a/translation/dest/emails/or-IN.xml +++ b/translation/dest/emails/or-IN.xml @@ -1,7 +1,7 @@ ଆପଣଙ୍କର lichess.org ଖାତା,%s ନିଶ୍ଚିତ କରନ୍ତୁ | - ଆପଣଙ୍କ lichess.org ପାସ୍ଓ୍ୱାର୍ଡ଼ କୁ ପୁନଃସେଟ୍ କରନ୍ତୁ, %s - ଆପଣଙ୍କ ଆକାଉଣ୍ଟ ପାଇଁ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଆମେ ଏକ ଅନୁରୋଧ ପାଇଲୁ। + ଆପଣଙ୍କ lichess.org ପାସ୍‍ୱାର୍ଡ଼ କୁ ପୁନଃସେଟ୍ କରନ୍ତୁ, %s + ଆପଣଙ୍କ ଆକାଉଣ୍ଟ ପାଇଁ ପାସ୍‍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଆମେ ଏକ ଅନୁରୋଧ ପାଇଲୁ। ଯଦି ଆପଣ ଏହି ଅନୁରୋଧ କରିଛନ୍ତି, ନିମ୍ନ ଲିଙ୍କ୍ କ୍ଲିକ୍ କରନ୍ତୁ। ଯଦି ନୁହେଁ, ତେବେ ଆପଣ ଏହି ଇମେଲ୍ ଅଣଦେଖା କରିପାରିବେ। diff --git a/translation/dest/emails/sa-IN.xml b/translation/dest/emails/sa-IN.xml index 3ea04e700d..5a0f646cdf 100644 --- a/translation/dest/emails/sa-IN.xml +++ b/translation/dest/emails/sa-IN.xml @@ -1,2 +1,4 @@ - + + क्रीडकस्य नामः %s + diff --git a/translation/dest/faq/fa-IR.xml b/translation/dest/faq/fa-IR.xml index 110b0f053a..775f517af4 100644 --- a/translation/dest/faq/fa-IR.xml +++ b/translation/dest/faq/fa-IR.xml @@ -26,5 +26,9 @@ نام کاربری من چه می‌تواند باشد؟ دستورالعمل‌ها آیا می‌توانم نام کاربری خود را تغییر دهم؟ + بازیکنان با قدرت یکسان + چگونه وقتی بازی می کنیم ریتینگ ها را پنهان کنیم؟ + تنظیمات نمایش + من یک بازی را بخاطر قطعی اینترنت باختم. آیا می توانم ریتینگ از دست رفته ام را بازگردانی کنم؟ چگونه... diff --git a/translation/dest/faq/or-IN.xml b/translation/dest/faq/or-IN.xml index acbf89c93d..03505f7fde 100644 --- a/translation/dest/faq/or-IN.xml +++ b/translation/dest/faq/or-IN.xml @@ -1,5 +1,5 @@ - ହଁ। Lichess ପ୍ରକୃତରେ ଅନ୍ୟ ଖୋଲା-ଉତ୍ସ ସାଇଟଗୁଡ଼ିକୁ ପ୍ରେରଣା ଦେଇଛି ଯାହା ଆମର %1$s, %2$s, କିମ୍ୱା %3$s ବ୍ୟବହାର କରନ୍ତି। + ହଁ। Lichess ପ୍ରକୃତରେ ଅନ୍ୟ ଖୋଲା-ଉତ୍ସ ସାଇଟଗୁଡ଼ିକୁ ପ୍ରେରଣା ଦେଇଛି ଯାହା ଆମର %1$s, %2$s, କିମ୍ୱା %3$s ବ୍ୟବହାର କରନ୍ତି। ପ୍ରଦର୍ଶନ ଅଗ୍ରାଧିକାର diff --git a/translation/dest/learn/or-IN.xml b/translation/dest/learn/or-IN.xml index 503721090f..3dea0673f7 100644 --- a/translation/dest/learn/or-IN.xml +++ b/translation/dest/learn/or-IN.xml @@ -5,21 +5,21 @@ ରାଣୀ ରାଣୀ = ଡଙ୍ଗା + ହାତୀ ରଜା - ଘୋଡ଼ା + ଘୋଡ଼ା ସୈନିକ ବିକଶିତ - ଉଚ୍ଚ ଗତିଶୀଳତା ଥିବା ଗୋଟିଗୁଡ଼ିକର ଅଧିକ ମୂଲ୍ୟ ଅଛି! + ଉଚ୍ଚ ଗତିଶୀଳତା ଥିବା ଗୋଟିଗୁଡ଼ିକର ଅଧିକ ମୂଲ୍ୟ ଅଛି! ରାଣୀ = 9 ଡଙ୍ଗା = 5 ହାତୀ = 3 -ଘୋଡ଼ା = 3 +ଘୋଡ଼ା = 3 ସୈନିକ = 1 ରାଜା ଅମୂଲ୍ୟ ଅଟେ! ଏହାକୁ ହରାଇବାର ଅର୍ଥ ହେଉଛି ଖେଳ ହାରିବା। ଅଭିନନ୍ଦନ! ଆପଣ ମୋହରା ର ମୂଲ୍ୟ ଜାଣନ୍ତି! ରାଣୀ = 9 ଡଙ୍ଗା = 5 ହାତୀ = 3 -ଘୋଡ଼ା = 3 +ଘୋଡ଼ା = 3 ସୈନିକ = 1 ପ୍ରହେଳିକା ଆପଣଙ୍କ କୌଶଳ ଅଭ୍ୟାସ କରନ୍ତୁ diff --git a/translation/dest/patron/or-IN.xml b/translation/dest/patron/or-IN.xml index dcd51f23e9..aff9b6604c 100644 --- a/translation/dest/patron/or-IN.xml +++ b/translation/dest/patron/or-IN.xml @@ -1,7 +1,7 @@ ଦାନ କରନ୍ତୁ - %s ଭାବରେ ଦାନ କରନ୍ତୁ + %s ଭାବରେ ଦାନ କରନ୍ତୁ Lichess Patron ମାଗଣା ଖାତା ଏକ Lichess Patron ହୁଅନ୍ତୁ @@ -17,9 +17,9 @@ ମାସିକ ଅନ୍ୟ ଦୟାକରି %s ରେ ଏକ ରାଶି ପ୍ରବେଶ କରନ୍ତୁ - କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼ + କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼ PayPal - ଟଙ୍କା କୁଆଡ଼େ ଯାଏ? + ଟଙ୍କା କୁଆଡ଼େ ଯାଏ? ସର୍ବପ୍ରଥମେ, ଶକ୍ତିଶାଳୀ ସର୍ଭର। ତା\'ପରେ ଆମେ ଏକ ପୂର୍ଣ୍ଣକାଳୀନ ବିକାଶକାରୀ: %sଙ୍କୁ, Lichess ର ପ୍ରତିଷ୍ଠାତା, ଦେୟ ଦେଇଥାଉ। Lichess ସମର୍ଥନ ସହ ଯୋଗାଯୋଗ କରନ୍ତୁ @@ -35,7 +35,7 @@ ଆପଣଙ୍କ ସମର୍ଥନ ପ୍ରତ୍ୟାହାର କରନ୍ତୁ %1$s ବା %2$s Lichess ଆପଣଙ୍କ ପାଇଁ କେତେ ମୂଲ୍ୟବାନ ତାହା ସ୍ଥିର କରନ୍ତୁ: - ଆପଣଙ୍କ କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼ ପ୍ରତ୍ୟାହାର କରନ୍ତୁ ଏବଂ ଦେୟ ବନ୍ଦ କରନ୍ତୁ: + ଆପଣଙ୍କ କ୍ରେଡ଼ିଟ୍ କାର୍ଡ଼ ପ୍ରତ୍ୟାହାର କରନ୍ତୁ ଏବଂ ଦେୟ ବନ୍ଦ କରନ୍ତୁ: ଆଉ Lichess କୁ ସମର୍ଥନ କରନ୍ତି ନାହିଁ ତାରିଖ ରାଶି diff --git a/translation/dest/puzzle/hi-IN.xml b/translation/dest/puzzle/hi-IN.xml index 882cad68ff..4fe2b415d1 100644 --- a/translation/dest/puzzle/hi-IN.xml +++ b/translation/dest/puzzle/hi-IN.xml @@ -13,6 +13,7 @@ विशेष चाल क्या आपको यह पहेली पसंद आई? अगले एक को लोड करने के लिए वोट दें! + आपकी पज़ल रेटिङ नही बदलेगी।पज़ल एक स्पर्धा नही है रेटिङ आपके अनुकुल पज़ल चुनने मे मदद करती है। सफेद रंग के लिए सबसे अच्छी चाल का पता लगाएं। काले रंग के लिए सबसे अच्छी चाल का पता लगाएं। वैयक्तिकृत पहेलियाँ प्राप्त करने के लिए: @@ -62,4 +63,7 @@ अपने कॉम्बो को संरक्षित करने के लिए इस कदम को छोड़ दें। सम्पूर्ण पारी मे केवल एक बार काम करता है । श्रंखला बनाए रखें । नई श्रंखला + मेरे खेलों से + %s के खेलो की पज़ल​| + पज़ल खोजे diff --git a/translation/dest/puzzleTheme/en-US.xml b/translation/dest/puzzleTheme/en-US.xml index 307363b078..8b6e2382ec 100644 --- a/translation/dest/puzzleTheme/en-US.xml +++ b/translation/dest/puzzleTheme/en-US.xml @@ -51,7 +51,7 @@ A move where the moved piece attacks two opponent pieces at once. Hanging piece A tactic involving an opponent piece being undefended or insufficiently defended and free to capture. - Hook mate + Rook mate Checkmate with a rook, knight, and pawn along with one enemy pawn to limit the enemy king\'s escape. Interference Moving a piece between two opponent pieces to leave one or both opponent pieces undefended, such as a knight on a defended square between two rooks. @@ -107,7 +107,7 @@ A motif involving a high value piece being attacked, moving out the way, and allowing a lower value piece behind it to be captured or attacked, the inverse of a pin. Smothered mate A checkmate delivered by a knight in which the mated king is unable to move because it is surrounded (or smothered) by its own pieces. - Super GM games + Super GrandMaster games Puzzles from games played by the best players in the world. Trapped piece A piece is unable to escape capture as it has limited moves. @@ -121,7 +121,7 @@ The opponent is limited in the moves they can make, and all moves worsen their position. Healthy mix A bit of everything. You don\'t know what to expect, so you remain ready for anything! Just like in real games. - Player games + Player Games Lookup puzzles generated from your games, or from another player\'s games. These puzzles are in the public domain, and can be downloaded from %s. diff --git a/translation/dest/puzzleTheme/fa-IR.xml b/translation/dest/puzzleTheme/fa-IR.xml index 5c861b6793..f891024bb1 100644 --- a/translation/dest/puzzleTheme/fa-IR.xml +++ b/translation/dest/puzzleTheme/fa-IR.xml @@ -28,6 +28,7 @@ چنگال حرکتی که در آن مهره ای که حرکت می کند دو مهره حریف را به صورت همزمان مورد حمله قرار می دهد. مهره بی دفاع + تاکتیکی که در آن مهره های حریف برای گرفتن، بدون دفاع یا با دفاع ناکافی است. معمای طولانی بازی‌های اساتید بازیهای استاد در مقابل استاد diff --git a/translation/dest/settings/hi-IN.xml b/translation/dest/settings/hi-IN.xml index e007e57345..5ca28787e5 100644 --- a/translation/dest/settings/hi-IN.xml +++ b/translation/dest/settings/hi-IN.xml @@ -3,7 +3,7 @@ व्यवस्था (सेटिंग्स) खातें को बंद करें समापन निश्चित है। वहां से कोई वापसी नहीं है। क्या आपने सुनिश्चित कर लिया है? - आपको एक ही नाम से एक नया खाता खोलने की अनुमति नहीं दी जाएगी, भले ही मामला अलग हो। + आपको एक ही नाम से एक नया खाता खोलने की अनुमति नहीं दी जाएगी, भले ही अक्षर बड़े-छोटे हो। मैंने अपना मन बदल लिया, मेरे खाता को बंद न करें क्या आप वाकई अपना खाता बंद करना चाहते हैं? अपना खाता बंद करना एक स्थायी निर्णय है। आप कभी भी लॉग इन नहीं कर पाएंगे। यह ख़ाता बंद है| diff --git a/translation/dest/site/hi-IN.xml b/translation/dest/site/hi-IN.xml index 79ba584588..0061f6e301 100644 --- a/translation/dest/site/hi-IN.xml +++ b/translation/dest/site/hi-IN.xml @@ -13,7 +13,7 @@ चैट को टोग्ल करें बातचीत हार मान लें - शाहमात + शह और मात गतिरोध (स्टेलमेट ) सफेद काला @@ -94,6 +94,8 @@ %s ओपनिंग एक्सप्लोरर जीत 50-चालन नियम से रोका गया हार 50-चाल नियम द्वारा बचाया + जीत अथ्वा 50 चाल नियम पुरानी गलती के कारण​। + हार​ अथ्वा 50 चाल नियम पुरानी गलती के कारण​। सब तैयार! PGN आयात करें हटाएँ @@ -561,6 +563,8 @@ यह पहले से ही आपका ईमेल पता है कम से कम %s वर्ण का शब्द होना चाहिए। कम से कम %s वर्ण का शब्द होना चाहिए। + कम से कम %s + %s से ज़्यादा नही। अगर स्तर %s ऊपर निचे हो तब केवल मौजूदा वार्तालाप केवल दोस्त @@ -854,6 +858,7 @@ समकालिक खेल का विवरण क्या आप प्रतिभागियों को कुछ बताना चाहते हैं? %s अत्याधुनिक स्वरूपण के लिए उपलब्ध है। + खेल अथवा अध्ययन का URL paste करे एमबेड करने के लिये| आपके अपने क्षेत्रीय समयानुसार प्रतियोगिता वार्ता वार्ता निषेध @@ -874,5 +879,9 @@ खाते से संबंधित ईमेल पता हमने आपको लिंक के साथ एक ईमेल भेजा है। प्रतियोगिता प्रवेश कोड + प्रतिक्षा कीजीए। + %s के साथ आपका खेल समाप्त नही हुआ है। + खेल रद्द करें + हार मान ले जब तक यह गेम समाप्त नहीं हो जाता तब तक आप एक नया गेम शुरू नहीं कर सकते। diff --git a/translation/dest/site/id-ID.xml b/translation/dest/site/id-ID.xml index 467521531a..6bbf41a333 100644 --- a/translation/dest/site/id-ID.xml +++ b/translation/dest/site/id-ID.xml @@ -152,7 +152,7 @@ Pemain Teman Diskusi - Hari ini + Hari ini Kemarin Menit untuk tiap pihak Variasi permainan @@ -402,9 +402,9 @@ Pembuka populer Posisi akhir permainan Posisi awal Catur960: %s - Posisi Awal + Posisi Awal Bersihkan papan - Panggil posisi + Panggil posisi Pribadi Lapor %s ke moderator Kelengkapan profil %s @@ -423,7 +423,7 @@ Sebelumnya di Lichess TV Pemain online Pemain aktif - Awas, permainan ini pake rating namun tanpa pake waktu! + Awas, permainan ini pake rating namun tanpa pake waktu! Berhasil %s pertandingan sedang berlangsung @@ -491,7 +491,7 @@ Pada catur klasik Selalu Tidak pernah - %1$s berkompetisi dalam %2$s + %1$s berkompetisi dalam %2$s Berhasil Kalah %1$s melawan %2$s dalam permainan %3$s @@ -524,10 +524,10 @@ Alamat e-mail ini tidak dapat diterma. Pastikan kembali e-mail Anda, lalu coba lagi. Alamat e-mail tidak valid atau sudah dipakai Ini sudah menjadi alamat e-mail Anda - Panjang minimum adalah %s - Panjang maksimum adalah %s - Harus lebih besar atau sama dengan %s - Harus kurang atau sama dengan %s + Panjang minimum adalah %s + Panjang maksimum adalah %s + Harus lebih besar atau sama dengan %s + Harus kurang atau sama dengan %s Jika ratingnya ± %s Khusus percakapan yang ada Hanya teman @@ -611,7 +611,7 @@ Unduh mentah Unduh permainan yang telah dimasukkan Napak tilas - Anda juga bisa menggunakan tombol gulung untuk bergerak dalam permainan + Anda juga bisa menggunakan scroll untuk bergerak dalam permainan. Letakkan mouse diatas variasi komputer untuk meninjau hal tersebut. Tekan shift+klik atau klik-kanan untuk menggambar lingkaran dan panah di papan. Memperbolehkan pemain lain mengirim pesan untuk Anda @@ -647,7 +647,7 @@ %1$s tuan rumah %2$s %1$s bergabung %2$s %1$s suka %2$s - Pencarian lawan dengan cepat + Pencarian cepat Ruang Lobi Tanpa Nama Skor Anda: %s @@ -770,7 +770,7 @@ Anda belum bisa mengirim pos di forum ini. Mainkan beberapa permainan! Berlangganan Berhenti berlangganan - membahas anda di \"%1$s\". + membahas anda di \"%1$s\". %1$s membahas anda di \"%2$s\". mengundang anda ke \"%1$s\". %1$s mengundang ke \"%2$s\". diff --git a/translation/dest/site/or-IN.xml b/translation/dest/site/or-IN.xml index f06c354397..7d7e006231 100644 --- a/translation/dest/site/or-IN.xml +++ b/translation/dest/site/or-IN.xml @@ -21,8 +21,8 @@ ଏକ ଖେଳ ସୃଷ୍ଟି କରନ୍ତୁ ଧଳା ବିଜୟୀ ଅଟେ କଳା ବିଜୟୀ ଅଟେ - ଆପଣ ଧଳା ଖଣ୍ଡଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି - ଆପଣ କଳା ଖଣ୍ଡଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି + ଆପଣ ଧଳା ଗୋଟିଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି + ଆପଣ କଳା ଗୋଟିଗୁଡ଼ିକ ଖେଳୁଛନ୍ତି ଏହା ଆପଣଙ୍କର ପାଳି! କେନ୍ଦ୍ରରେ ରାଜା | ତିନି ଚେକ୍ @@ -33,7 +33,11 @@ ଖେଳରେ ଯୋଗ ଦିଅନ୍ତୁ ଧଳାର ପାଳି କଳାର ପାଳି - ତୁମର ପ୍ରତିଦ୍ୱନ୍ଦୀ ଖେଳ ଛାଡିଥିଲେ | ଆପଣ ବିଜୟ ଦାବି କରିପାରିବେ, ଖେଳକୁ ଡ୍ର ବୋଲି କହିପାରିବେ କିମ୍ବା ଅପେକ୍ଷା କରିପାରିବେ | + + ଆପଣଙ୍କ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱୀ ଖେଳ ଛାଡ଼ିଛନ୍ତି। ଆପଣ %s ସେକେଣ୍ଡରେ ବିଜୟ ଦାବି କରିପାରିବେ। + ଆପଣଙ୍କ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱୀ ଖେଳ ଛାଡ଼ିଛନ୍ତି। ଆପଣ %s ସେକେଣ୍ଡରେ ବିଜୟ ଦାବି କରିପାରିବେ। + + ଆପଣଙ୍କ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱୀ ଖେଳ ଛାଡ଼ିଛନ୍ତି। ଆପଣ ବିଜୟ ଦାବି କରିପାରିବେ, ଖେଳକୁ ଡ୍ର ବୋଲି କହିପାରିବେ କିମ୍ବା ଅପେକ୍ଷା କରିପାରିବେ। ବିଜୟ ଦାବି କରନ୍ତୁ କଲ୍ ଡ୍ର ଦୟାକରି ଚାଟରେ ଭଲ ହୁଅନ୍ତୁ! @@ -142,17 +146,17 @@ ଉପଭୋକ୍ତାଙ୍କ ନାମ ଉପଭୋକ୍ତା ନାମ କିମ୍ୱା ଇମେଲ୍ ଉପଭୋକ୍ତା ନାମ ପରିବର୍ତ୍ତନ କରନ୍ତୁ - ପାସ୍ଓ୍ୱାର୍ଡ଼ - ପାସ୍ଓ୍ୱାର୍ଡ଼ ପରିବର୍ତ୍ତନ କରନ୍ତୁ + ପାସ୍‍ୱାର୍ଡ଼ + ପାସ୍‍ୱାର୍ଡ଼ ବଦଳାନ୍ତୁ ଇମେଲ୍ ପରିବର୍ତ୍ତନ କରନ୍ତୁ ଇମେଲ୍ - ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର - ପାସ୍ଓ୍ୱାର୍ଡ଼ ଭୁଲିଯାଇଛନ୍ତି କି? + ପାସ୍‍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କର + ପାସ୍‍ୱାର୍ଡ଼ ଭୁଲିଯାଇଛନ୍ତି କି? %sଟିଏ ପ୍ରହେଳିକା %sଟି ପ୍ରହେଳିକା - ଖେଳାଯାଇଥିବା ଖେଳଗୁଡ଼ିକ + ଖେଳାଯାଇଥିବା ଖେଳଗୁଡ଼ିକ ବର୍ତ୍ତମାନ ଖେଳୁଛନ୍ତି ଏହି ଆକାଉଣ୍ଟ Lichess ସେବା ସର୍ତ୍ତାବଳୀର ଉଲ୍ଲଂଘନ କରିଛି @@ -196,16 +200,17 @@ ଉପଭୋକ୍ତା କାରଣ ମାମଲା କ\'ଣ? - ଅବୈଧ ଉପଭୋକ୍ତାଙ୍କ ନାମ କିମ୍ୱା ପାସ୍ଓ୍ୱାର୍ଡ଼ - ଭୁଲ୍ ପାସ୍ଓ୍ୱାର୍ଡ଼ - ଏବେକାର ପାସ୍ଓ୍ୱାର୍ଡ଼ - ନୁତନ ପାସ୍ଓ୍ୱାର୍ଡ଼ - ନୂତନ ପାସ୍ଓ୍ୱାର୍ଡ଼ (ପୁନଃ) - ନୂତନ ପାସ୍ଓ୍ୱାର୍ଡ଼ଗୁଡ଼ିକ ମେଳ ଖାଉ ନାହିଁ + ଅବୈଧ ଉପଭୋକ୍ତାଙ୍କ ନାମ କିମ୍ୱା ପାସ୍‍ୱାର୍ଡ଼ + ଭୁଲ୍ ପାସ୍‍ୱାର୍ଡ଼ + ଏବେକାର ପାସ୍‍ୱାର୍ଡ଼ + ନୂଆ ପାସ୍‍ୱାର୍ଡ଼ + ନୂଆ ପାସ୍‍ୱାର୍ଡ଼ (ପୁନଃ) + ନୂତନ ପାସ୍‍ୱାର୍ଡ଼ଗୁଡ଼ିକ ମେଳ ଖାଉ ନାହିଁ ଗୋପନୀୟତା ଗୋପନୀୟତା ନୀତି ଅନ୍ୟ ଖେଳାଳିମାନଙ୍କୁ ଆପଣଙ୍କୁ ଅଧ୍ୟୟନ ପାଇଁ ଆମନ୍ତ୍ରଣ କରିବାକୁ ଦିଅନ୍ତୁ ଧ୍ଵନି + %1$s %2$sରେ ପ୍ରତିଦ୍ୱନ୍ଦ୍ୱିତା କରନ୍ତି ବିଜୟ ସମୟରେଖା ଆରମ୍ଭ: @@ -223,7 +228,7 @@ କେବଳ ବନ୍ଧୁଗଣ ସେବାର ସର୍ତ୍ତାବଳୀ ସୃଷ୍ଟି କରନ୍ତୁ - ଆମେ %s କୁ ଏକ ଇମେଲ୍ ପଠାଇଛୁ। ଆପଣଙ୍କ ପାସ୍ଓ୍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଇମେଲରେ ଥିବା ଲିଙ୍କକୁ କ୍ଲିକ୍ କରନ୍ତୁ। + ଆମେ %s କୁ ଏକ ଇମେଲ୍ ପଠାଇଛୁ। ଆପଣଙ୍କ ପାସ୍‍ୱାର୍ଡ଼ ପୁନଃସେଟ୍ କରିବାକୁ ଇମେଲରେ ଥିବା ଲିଙ୍କକୁ କ୍ଲିକ୍ କରନ୍ତୁ। ଆମର %s ବିଷୟରେ ପଢ଼ନ୍ତୁ। ସୁରକ୍ଷା ସମାଧାନ ଦେଖନ୍ତୁ @@ -242,7 +247,7 @@ କ୍ଷମା କରନ୍ତୁ :( କାହିଁକି? ଆମେ ସମସ୍ତଙ୍କ ପାଇଁ ଏକ ସୁଖଦ ଚେସ୍ ଅଭିଜ୍ଞତା ପ୍ରଦାନ କରିବାକୁ ଲକ୍ଷ୍ୟ ରଖିଛୁ। - ଏହାକୁ କିପରି ଏଡ଼ାଇ ହେବ? + ଏହାକୁ କିପରି ଏଡ଼ାଇ ହେବ? ପଢ଼ିବା ପାଇଁ ଧନ୍ୟବାଦ! ମୁଁ ସହମତ ଯେ ମୁଁ ସମସ୍ତ Lichess ନୀତି ଅନୁସରଣ କରିବି। ସମ୍ପାଦନା @@ -250,6 +255,7 @@ ଚେସ୍ କୌଶଳ ପ୍ରଶିକ୍ଷକ ସଦସ୍ୟତା ନିଅନ୍ତୁ ସଦସ୍ୟତା ରଦ୍ଦ କରନ୍ତୁ + ଆପଣଙ୍କୁ \"%1$s\"ରେ ଉଲ୍ଲେଖ କରିଛନ୍ତି। ନୂଆ ବିଚାରାଧୀନ ସମୀକ୍ଷା ଏହାକୁ ଗ୍ରନ୍ଥନ କରିବା ପାଇଁ ଏକ ଖେଳ URL କିମ୍ବା ଏକ ଅଧ୍ୟାୟ URL ପେଷ୍ଟ କରନ୍ତୁ। କେବଳ ଦଳର ସଦସ୍ୟଗଣ diff --git a/translation/dest/storm/zh-CN.xml b/translation/dest/storm/zh-CN.xml index f9e12874f0..746eb67784 100644 --- a/translation/dest/storm/zh-CN.xml +++ b/translation/dest/storm/zh-CN.xml @@ -54,4 +54,6 @@ 本月 全部时间 点击重新加载 + 本次冲刺已超时! + 本次冲刺已经在另一个标签页中打开! diff --git a/translation/dest/study/he-IL.xml b/translation/dest/study/he-IL.xml index 6ab068ebb2..1dae4d9c9b 100644 --- a/translation/dest/study/he-IL.xml +++ b/translation/dest/study/he-IL.xml @@ -143,6 +143,7 @@ מסע מפוקפק המסע היחיד כפאי + עמדה מאוזנת יתרון קל ללבן יתרון קל לשחור לבן יותר טוב @@ -152,4 +153,5 @@ חידוש פיתוח התקפה + פרק הבא diff --git a/translation/dest/study/hi-IN.xml b/translation/dest/study/hi-IN.xml index 6247aba8c1..fe86d896f8 100644 --- a/translation/dest/study/hi-IN.xml +++ b/translation/dest/study/hi-IN.xml @@ -15,6 +15,7 @@ जोड़ा गया (सबसे पुराना) हाल ही में अद्यतित सबसे लोकप्रिय + वर्णक्रमानुसार एक नया अध्याय जोड़ें %s अध्याय @@ -40,6 +41,7 @@ अब आप एक दर्शक हैं PGN टैग लाइक + नापसन्द करे नया टैग इस स्थिति पर टिप्पणी करें इस चाल पर टिप्पणी करें @@ -126,4 +128,9 @@ क्या इस पड़ाई से सम्बन्धित बातों को मिटा देना चाहिए? इससे पीछे जाने का कोई रास्ता शेष नहीं है! अध्याय को मिटा दे आप इसको खा से पड़ना चाहते है + अच्छी चाल! + गलती + अद्भुत चाल​। + भयंकर गलती + दिलचस्प चाल​ | diff --git a/translation/dest/study/id-ID.xml b/translation/dest/study/id-ID.xml index 60a28050dc..780f2759b1 100644 --- a/translation/dest/study/id-ID.xml +++ b/translation/dest/study/id-ID.xml @@ -38,6 +38,7 @@ Sekarang Anda adalah penonton Tagar PGN Suka + Batal Suka Tagar baru Komentar di posisi ini Komentari langkah ini @@ -122,6 +123,7 @@ Bersihkan obrolan Hapus riwayat obrolan studi? Ini tidak akan dapat mengulangkan kembali! Hapus studi + Hapus seluruh studi? Tidak dapat kembal lagi! Tuliskan nama studi untuk konfirmasi: %s Dimana Anda ingin mempelajarinya? Langkah bagus Kesalahan diff --git a/translation/dest/study/or-IN.xml b/translation/dest/study/or-IN.xml index 76e15360d6..0ea8093111 100644 --- a/translation/dest/study/or-IN.xml +++ b/translation/dest/study/or-IN.xml @@ -9,17 +9,17 @@ %sଟି ଅଧ୍ୟାୟ ଅଧ୍ୟୟନକୁ ଆମନ୍ତ୍ରଣ କରନ୍ତୁ - ଅଧ୍ୟୟନ ଛାଡ଼ି ଦିଅନ୍ତୁ + ଅଧ୍ୟୟନ ଛାଡ଼ି ଦିଅନ୍ତୁ PGN ଟ୍ୟାଗ୍ ପସନ୍ଦ କରନ୍ତୁ - ପସନ୍ଦ ହଟାନ୍ତୁ + ପସନ୍ଦ ହଟାନ୍ତୁ ଅଂଶୀଦାର ଓ ରପ୍ତାନି ଅଧ୍ୟୟନର PGN ଅଧ୍ୟାୟର PGN ଅଧ୍ୟୟନ URL ଆପଣଙ୍କ ୱେବସାଇଟ୍ ରେ ଗ୍ରନ୍ଥନ କରନ୍ତୁ ଗ୍ରନ୍ଥନ ବିଷୟରେ ଅଧିକ ପଢ଼ନ୍ତୁ - କେବଳ ସାର୍ବଜନୀନ ଅଧ୍ୟୟନଗୁଡ଼ିକ ଗ୍ରଥିତ ହୋଇପାରିବ! + କେବଳ ସାର୍ବଜନୀନ ଅଧ୍ୟୟନଗୁଡ଼ିକ ଗ୍ରଥିତ ହୋଇପାରିବ! ଖୋଲନ୍ତୁ ଅଧ୍ୟୟନ ମିଳିଲା ନାହିଁ ଅଧ୍ୟାୟ ସମ୍ପାଦନା diff --git a/translation/dest/team/or-IN.xml b/translation/dest/team/or-IN.xml index ffed1d7fc6..3a1526b68e 100644 --- a/translation/dest/team/or-IN.xml +++ b/translation/dest/team/or-IN.xml @@ -1,7 +1,7 @@ Team - ଦଳଗୁଡ଼ିକ + ଦଳଗୁଡ଼ିକ ସମସ୍ତ ଦଳ ନୂତନ ଦଳ ମୋର ଦଳଗୁଡିକ @@ -17,7 +17,7 @@ ଆପଣ କାହାକୁ ଦଳରୁ ବାହାର କରିବାକୁ ଚାହୁଁଛନ୍ତି? ଆପଣଙ୍କର ଯୋଗଦାନ ଅନୁରୋଧ, ଦଳର ନେତାଙ୍କ ଦ୍ୱାରା ସମୀକ୍ଷା କରାଯିବ ଆପଣଙ୍କର ଯୋଗଦାନ ଅନୁରୋଧ, ଦଳର ନେତାଙ୍କ ଦ୍ୱାରା ସମୀକ୍ଷା କରାଯାଉଛି - ଦଳ ବାର୍ତ୍ତାଗୁଡ଼ିକୁ ସଦସ୍ୟ ହୁଅନ୍ତୁ + ଦଳ ବାର୍ତ୍ତାର ସଦସ୍ୟ ହୁଅନ୍ତୁ ଦଳ ଯୁଦ୍ଧ ଏକାଧିକ ଦଳର ଯୁଦ୍ଧରେ, ପ୍ରତ୍ୟେକ ଖେଳାଳି ନିଜ ଦଳ ପାଇଁ ପଏଣ୍ଟ ସ୍କୋର କରନ୍ତି ଦଳ ଟୁର୍ନାମେଣ୍ଟ diff --git a/translation/dest/tourname/en-US.xml b/translation/dest/tourname/en-US.xml index 78db850b6f..962c23c52d 100644 --- a/translation/dest/tourname/en-US.xml +++ b/translation/dest/tourname/en-US.xml @@ -1,7 +1,7 @@ - Hourly Rapid Arena - Hourly Rapid + Hourly Rapid Arena + Hourly Rapid Arena Hourly %s Arena Hourly %s Daily Rapid Arena @@ -14,7 +14,7 @@ Eastern Rapid Eastern Classical Arena Eastern Classical - Eastern %s Arena + Elite %s Arena Eastern %s Weekly Rapid Arena Weekly Rapid diff --git a/translation/dest/ublog/or-IN.xml b/translation/dest/ublog/or-IN.xml index 0878279911..dedf91670c 100644 --- a/translation/dest/ublog/or-IN.xml +++ b/translation/dest/ublog/or-IN.xml @@ -2,13 +2,13 @@ ପ୍ରକାଶିତ ହୋଇଛି ଆପଣଙ୍କ ବ୍ଲଗ୍ ରେ ପ୍ରକାଶନ କରନ୍ତୁ - ଯଦି ଯାଞ୍ଚ କରାଯାଏ, ତେବେ ପୋଷ୍ଟଟି ଆପଣଙ୍କ ବ୍ଲଗରେ ତାଲିକାଭୁକ୍ତ ହେବ। ଯଦି ନୁହେଁ, ତେବେ ଏହା ଆପଣଙ୍କ ଡ୍ରାଫ୍ଟ ପୋଷ୍ଟଗୁଡ଼ିକରେ ବ୍ୟକ୍ତିଗତ ରହିବ + ଯଦି ଯାଞ୍ଚ କରାଯାଏ, ତେବେ ପୋଷ୍ଟଟି ଆପଣଙ୍କ ବ୍ଲଗରେ ତାଲିକାଭୁକ୍ତ ହେବ। ଯଦି ନୁହେଁ, ତେବେ ଏହା ଆପଣଙ୍କ ଡ୍ରାଫ୍ଟ ପୋଷ୍ଟଗୁଡ଼ିକରେ ବ୍ୟକ୍ତିଗତ ରହିବ ଏକ ବ୍ଲଗ୍ ପୋଷ୍ଟ ପ୍ରକାଶିତ ହୋଇଛି %sଟି ବ୍ଲଗ୍ ପୋଷ୍ଟ ପ୍ରକାଶିତ ହୋଇଛି %1$s %2$s ପ୍ରକାଶ କରିଛନ୍ତି ଏହି ପୋଷ୍ଟ ପ୍ରକାଶିତ ହୋଇଛି - ପ୍ରତିଛବି ବିକଳ୍ପ ପାଠ୍ୟ - ପ୍ରତିଛବି କ୍ରେଡିଟ୍ + ପ୍ରତିଛବି ବିକଳ୍ପ ପାଠ୍ୟ + ପ୍ରତିଛବି କ୍ରେଡିଟ୍ From 3fcb39312f69015704cc8016914046c7eb23d862 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 12 Nov 2021 07:56:51 +0100 Subject: [PATCH 030/144] Revert "disable wikibooks in studies until serverside CSP is here - REVERT ME" This reverts commit baa0d8c337271acbbb91380a9a6b063b8a30a95d. --- ui/analyse/src/study/commentForm.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/analyse/src/study/commentForm.ts b/ui/analyse/src/study/commentForm.ts index 6bd6eb8412..40323cda9a 100644 --- a/ui/analyse/src/study/commentForm.ts +++ b/ui/analyse/src/study/commentForm.ts @@ -107,7 +107,7 @@ export function view(root: AnalyseCtrl): VNode { return h( 'div.study__comments', { - // hook: onInsert(() => root.enableWiki(true)), + hook: onInsert(() => root.enableWiki(true)), }, [ currentComments(root, !study.members.canContribute()), @@ -126,7 +126,7 @@ export function view(root: AnalyseCtrl): VNode { }, }), ]), - h('div.analyse__wiki.study__wiki.empty'), + h('div.analyse__wiki.study__wiki'), ] ); } From a180f13143e95c6f996ecc05f37dcf9e95815300 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 12 Nov 2021 09:15:40 +0100 Subject: [PATCH 031/144] loader CSS: fix bad merge and colour switch --- ui/common/css/component/_loader.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/ui/common/css/component/_loader.scss b/ui/common/css/component/_loader.scss index 36640c50de..2756c90e47 100644 --- a/ui/common/css/component/_loader.scss +++ b/ui/common/css/component/_loader.scss @@ -83,10 +83,6 @@ $total_len: $len1 + $len2 + $len3; stroke-dasharray: $total_len; } - g { - animation: spinner-color 8s steps(1) infinite; - } - .white & path { stroke: #fff; } From 9bd52e46f95bbf530c2f0a600a89c743721481f6 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 12 Nov 2021 09:19:41 +0100 Subject: [PATCH 032/144] fix fishnet opening book monitoring --- modules/common/src/main/mon.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/common/src/main/mon.scala b/modules/common/src/main/mon.scala index f5e1cb6a28..75f5e732dd 100644 --- a/modules/common/src/main/mon.scala +++ b/modules/common/src/main/mon.scala @@ -648,7 +648,7 @@ object mon { def move(level: Int) = counter("fishnet.move.time").withTag("level", level) def openingBook(level: Int, variant: String, ply: Int, hit: Boolean) = timer("fishnet.opening.hit").withTags( - Map("level" -> level, "variant" -> variant, "ply" -> ply, "hit" -> hit) + Map("level" -> level.toLong, "variant" -> variant, "ply" -> ply.toLong, "hit" -> hit) ) } object study { From ed32245760c691199085b21992f69b6be194ec4b Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 11:30:56 +0100 Subject: [PATCH 033/144] analysis/wiki: Only request up to move 15 (30 plies) and when path length is <256 (otherwise the wikibooks API rejects the request) --- ui/analyse/src/wiki.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index 822a2d279a..250db2bf42 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -32,7 +32,7 @@ export default function wikiTheory(): WikiTheory { async (nodes: Tree.Node[]) => { const pathParts = nodes.slice(1).map(n => `${plyPrefix(n)}${n.san}`); const path = pathParts.join('/'); - if (!path) show(''); + if (pathParts.length > 30 || !path || path.length > 255) show(''); else if (cache.has(path)) show(cache.get(path)!); else if ( Array.from({ length: pathParts.length }, (_, i) => -i - 1) From 3036f084716cda37f4a395c418edbb8178fcfdb8 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 11:31:50 +0100 Subject: [PATCH 034/144] analysis/wiki: Handle invalid wiki requests --- ui/analyse/src/wiki.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index 250db2bf42..d18d00b9b0 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -50,7 +50,9 @@ export default function wikiTheory(): WikiTheory { }; if (res.ok) { const json = await res.json(); - if (json.query.pages[0].missing) saveAndShow(''); + const page = json.query.pages[0]; + if (page.missing) saveAndShow(''); + else if (page.invalid) show('invalid request: ' + page.invalidreason); else saveAndShow(transform(json.query.pages[0].extract, title)); } else saveAndShow(''); } catch (err) { From 8aa6e5220aac95ad699e4c579a36b08788239b47 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 11:39:03 +0100 Subject: [PATCH 035/144] analysis/wiki: Better handle unexpected API responses --- ui/analyse/src/wiki.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index d18d00b9b0..bb3bc1a0c6 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -53,7 +53,8 @@ export default function wikiTheory(): WikiTheory { const page = json.query.pages[0]; if (page.missing) saveAndShow(''); else if (page.invalid) show('invalid request: ' + page.invalidreason); - else saveAndShow(transform(json.query.pages[0].extract, title)); + else if (!page.extract) show('error: unexpected API response:
' + JSON.stringify(page) + '
'); + else saveAndShow(transform(page.extract, title)); } else saveAndShow(''); } catch (err) { show('error: ' + err); From 416498ab1b3277f579d6a18ae165c5e18df660df Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 13:00:03 +0100 Subject: [PATCH 036/144] Bring sit-resigning limit in line with flagging --- modules/playban/src/main/PlaybanApi.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/playban/src/main/PlaybanApi.scala b/modules/playban/src/main/PlaybanApi.scala index b19242a9a9..97dcac80d7 100644 --- a/modules/playban/src/main/PlaybanApi.scala +++ b/modules/playban/src/main/PlaybanApi.scala @@ -132,7 +132,7 @@ final class PlaybanApi( Status.Resign.is(status) } .map { c => - (c.estimateTotalSeconds / 10) atLeast 15 atMost (3 * 60) + (c.estimateTotalSeconds / 10) atLeast 30 atMost (3 * 60) } .exists(_ < nowSeconds - game.movedAt.getSeconds) .option { From afee9072e41cfe23abbd82ad6d40ae273457ba14 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 13:43:47 +0100 Subject: [PATCH 037/144] ublog: Translate like button and show unlike when already liked --- app/views/ublog/post.scala | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/app/views/ublog/post.scala b/app/views/ublog/post.scala index 416dd7fb6b..f1fe289043 100644 --- a/app/views/ublog/post.scala +++ b/app/views/ublog/post.scala @@ -104,9 +104,7 @@ object post { div(cls := "ublog-post__footer")( (ctx.isAuth && !ctx.is(user)) option div(cls := "ublog-post__actions")( - likeButton(post, liked)(ctx)(cls := "ublog-post__like--big button button-big button-red")( - span(cls := "button-label")("Like this post") - ), + likeButton(post, liked), followButton(user, post, followed) ), h2(a(href := routes.Ublog.index(user.username))(trans.ublog.moreBlogPostsBy(user.username))), @@ -122,15 +120,21 @@ object post { dataIcon := "" )(trans.edit()) - private def likeButton(post: UblogPost, liked: Boolean)(implicit ctx: Context) = button( - tpe := "button", - cls := List( - "ublog-post__like is" -> true, - "ublog-post__like--liked" -> liked - ), - dataRel := post.id.value, - title := trans.study.like.txt() - )(span(cls := "ublog-post__like__nb")(post.likes.value.localize)) + private def likeButton(post: UblogPost, liked: Boolean)(implicit ctx: Context) = { + val text = if (liked) trans.study.unlike.txt() else trans.study.like.txt() + button( + tpe := "button", + cls := List( + "ublog-post__like is ublog-post__like--big button button-big button-red" -> true, + "ublog-post__like--liked" -> liked + ), + dataRel := post.id.value, + title := text + )( + span(cls := "ublog-post__like__nb")(post.likes.value.localize), + span(cls := "button-label")(text) + ) + } private def followButton(user: User, post: UblogPost, followed: Boolean)(implicit ctx: Context) = div( From 39f6a94d7548ceb62ab9b50c0557250d761b7fef Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 21:45:15 +0100 Subject: [PATCH 038/144] Don't redirect study admin endpoint on xhr --- app/controllers/Study.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/controllers/Study.scala b/app/controllers/Study.scala index 4907fff8d9..969608de1c 100644 --- a/app/controllers/Study.scala +++ b/app/controllers/Study.scala @@ -336,7 +336,8 @@ final class Study( def admin(id: String) = Secure(_.StudyAdmin) { _ => me => - env.study.api.adminInvite(id, me) inject Redirect(routes.Study.show(id)) + env.study.api.adminInvite(id, me) inject (if (HTTPRequest isXhr ctx.req) NoConent + else Redirect(routes.Study.show(id))) } def embed(id: String, chapterId: String) = From 4d291d10e5ec999c8105f9eab6465230b9899bc6 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 22:12:55 +0100 Subject: [PATCH 039/144] Fix study admin endpoint typo --- app/controllers/Study.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/Study.scala b/app/controllers/Study.scala index 969608de1c..cf5e21e83d 100644 --- a/app/controllers/Study.scala +++ b/app/controllers/Study.scala @@ -335,8 +335,8 @@ final class Study( } def admin(id: String) = - Secure(_.StudyAdmin) { _ => me => - env.study.api.adminInvite(id, me) inject (if (HTTPRequest isXhr ctx.req) NoConent + Secure(_.StudyAdmin) { ctx => me => + env.study.api.adminInvite(id, me) inject (if (HTTPRequest isXhr ctx.req) NoContent else Redirect(routes.Study.show(id))) } From fc81966d2d128e6050861f68613f83033eaf82dc Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 12 Nov 2021 22:29:58 +0100 Subject: [PATCH 040/144] preventDefault study admin form --- ui/analyse/src/study/studyMembers.ts | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/ui/analyse/src/study/studyMembers.ts b/ui/analyse/src/study/studyMembers.ts index a5e5edf2ba..6f23bb6751 100644 --- a/ui/analyse/src/study/studyMembers.ts +++ b/ui/analyse/src/study/studyMembers.ts @@ -1,6 +1,6 @@ import { prop, Prop } from 'common'; import { textRaw as xhrTextRaw } from 'common/xhr'; -import { bind, onInsert, dataIcon } from 'common/snabbdom'; +import { bind, onInsert, dataIcon, bindNonPassive } from 'common/snabbdom'; import { h, VNode } from 'snabbdom'; import { AnalyseSocketSend } from '../socket'; import { iconTag, scrollTo, titleNameToId } from '../util'; @@ -311,11 +311,10 @@ export function view(ctrl: StudyCtrl): VNode { 'form.admin', { key: ':admin', - hook: onInsert(el => - el.addEventListener('submit', () => - xhrTextRaw(`/study/${ctrl.data.id}/admin`, { method: 'post' }).then(() => location.reload()) - ) - ), + hook: bindNonPassive('submit', () => { + xhrTextRaw(`/study/${ctrl.data.id}/admin`, { method: 'post' }).then(() => location.reload()); + return false; + }), }, [h('button.button.button-red.button-thin', 'Enter as admin')] ) From d44e6958b8804f7edc1386a2b47fa5431d52d0de Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 13 Nov 2021 22:13:07 +0100 Subject: [PATCH 041/144] Always set colorIt when it's undefined (for new players) --- modules/user/src/main/UserRepo.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala index f6fe9374d1..f5409ad0b4 100644 --- a/modules/user/src/main/UserRepo.scala +++ b/modules/user/src/main/UserRepo.scala @@ -173,7 +173,8 @@ final class UserRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont coll .update(ordered = false, WriteConcern.Unacknowledged) .one( - $id(userId) ++ (if (value < 0) $doc(F.colorIt $gt -3) else $doc(F.colorIt $lt 5)), + // limit to -3 <= colorIt <= 5 but set when undefined + $id(userId) ++ $not(if (value < 0) $doc(F.colorIt $lte -3) else F.colorIt $gte 5), $inc(F.colorIt -> value) ) .unit From e0b09cde3731fdbf63e9383a605b3c08bdf5c066 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 13 Nov 2021 22:20:22 +0100 Subject: [PATCH 042/144] Define $not operator in mongo dsl --- modules/db/src/main/dsl.scala | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/modules/db/src/main/dsl.scala b/modules/db/src/main/dsl.scala index ab3933eabc..36525d0190 100644 --- a/modules/db/src/main/dsl.scala +++ b/modules/db/src/main/dsl.scala @@ -63,6 +63,10 @@ trait dsl { def $nor(expressions: Bdoc*): Bdoc = { $doc("$nor" -> expressions) } + + def $not(expression: Bdoc): Bdoc = { + $doc("$not" -> expression) + } // End of Top Level Logical Operators //**********************************************************************************************// From f46dc7536f9e3672035ef60f5ddfd9179878dd95 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 13 Nov 2021 22:27:38 +0100 Subject: [PATCH 043/144] remove unused LogicalOperators from dsl --- modules/db/src/main/dsl.scala | 8 -------- 1 file changed, 8 deletions(-) diff --git a/modules/db/src/main/dsl.scala b/modules/db/src/main/dsl.scala index 36525d0190..210453e6f6 100644 --- a/modules/db/src/main/dsl.scala +++ b/modules/db/src/main/dsl.scala @@ -295,13 +295,6 @@ trait dsl { } - trait LogicalOperators { self: ElementBuilder => - def $not(f: String => Expression[Bdoc]): SimpleExpression[Bdoc] = { - val expression = f(field) - SimpleExpression(field, $doc("$not" -> expression.value)) - } - } - trait ElementOperators { self: ElementBuilder => def $exists(v: Boolean): SimpleExpression[Bdoc] = { SimpleExpression(field, $doc("$exists" -> v)) @@ -385,7 +378,6 @@ trait dsl { with ComparisonOperators with ElementOperators with EvaluationOperators - with LogicalOperators with ArrayOperators implicit def toBSONDocument[V: BSONWriter](expression: Expression[V]): Bdoc = From 384cc4493ef11fcb2edf479e340124d1293cbdb9 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 13 Nov 2021 22:37:08 +0100 Subject: [PATCH 044/144] Actually use $not correctly --- modules/user/src/main/UserRepo.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala index f5409ad0b4..fa80426d6c 100644 --- a/modules/user/src/main/UserRepo.scala +++ b/modules/user/src/main/UserRepo.scala @@ -174,7 +174,7 @@ final class UserRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont .update(ordered = false, WriteConcern.Unacknowledged) .one( // limit to -3 <= colorIt <= 5 but set when undefined - $id(userId) ++ $not(if (value < 0) $doc(F.colorIt $lte -3) else F.colorIt $gte 5), + $id(userId) ++ $doc(F.colorIt -> $not(if (value < 0) $lte(-3) else $gte(5)))), $inc(F.colorIt -> value) ) .unit From 049a30e536da816e1e7e6d08776a3f400e3e7da4 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 13 Nov 2021 22:57:26 +0100 Subject: [PATCH 045/144] Remove extra parans --- modules/user/src/main/UserRepo.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/user/src/main/UserRepo.scala b/modules/user/src/main/UserRepo.scala index fa80426d6c..b0156c2415 100644 --- a/modules/user/src/main/UserRepo.scala +++ b/modules/user/src/main/UserRepo.scala @@ -174,7 +174,7 @@ final class UserRepo(val coll: Coll)(implicit ec: scala.concurrent.ExecutionCont .update(ordered = false, WriteConcern.Unacknowledged) .one( // limit to -3 <= colorIt <= 5 but set when undefined - $id(userId) ++ $doc(F.colorIt -> $not(if (value < 0) $lte(-3) else $gte(5)))), + $id(userId) ++ $doc(F.colorIt -> $not(if (value < 0) $lte(-3) else $gte(5))), $inc(F.colorIt -> value) ) .unit From e343d23e7e1c33f14a8d7a86954f9d0c79c6c048 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 13 Nov 2021 23:56:34 +0100 Subject: [PATCH 046/144] avoid script-src unsafe-inline almost everywhere --- app/views/oAuth/authorize.scala | 3 ++- modules/common/src/main/ContentSecurityPolicy.scala | 8 +++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/app/views/oAuth/authorize.scala b/app/views/oAuth/authorize.scala index 2608f17e6c..028a04d9d8 100644 --- a/app/views/oAuth/authorize.scala +++ b/app/views/oAuth/authorize.scala @@ -17,7 +17,8 @@ object authorize { moreJs = embedJsUnsafe( // ensure maximum browser compatibility """setTimeout(function(){var el=document.getElementById('oauth-authorize');el.removeAttribute('disabled');el.setAttribute('class','button')}, 2000);""" - ) + ), + csp = defaultCsp.withLegacyCompatibility.some ) { main(cls := "oauth box box-pad")( div(cls := "oauth__top")( diff --git a/modules/common/src/main/ContentSecurityPolicy.scala b/modules/common/src/main/ContentSecurityPolicy.scala index 7677142576..666b18aa07 100644 --- a/modules/common/src/main/ContentSecurityPolicy.scala +++ b/modules/common/src/main/ContentSecurityPolicy.scala @@ -11,11 +11,9 @@ case class ContentSecurityPolicy( baseUri: List[String] ) { - def withNonce(nonce: Nonce) = copy(scriptSrc = - nonce.scriptSrc :: - "'unsafe-inline'" :: // ignored by browsers supporting nonce - scriptSrc - ) + def withNonce(nonce: Nonce) = copy(scriptSrc = nonce.scriptSrc :: scriptSrc) + + def withLegacyCompatibility = copy(scriptSrc = "'unsafe-inline'" :: scriptSrc) def withWebAssembly = copy( From ca28dd450a3c744f8414b0a18f08ffa9778cbafb Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sun, 14 Nov 2021 16:37:37 +0100 Subject: [PATCH 047/144] Always send rating in hook form - fixes #10115 --- app/views/setup/forms.scala | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/app/views/setup/forms.scala b/app/views/setup/forms.scala index 47eb144611..eca03be81a 100644 --- a/app/views/setup/forms.scala +++ b/app/views/setup/forms.scala @@ -167,14 +167,24 @@ object forms { div(cls := "ratings")( form3.hidden("rating", "?"), lila.rating.PerfType.nonPuzzle.map { perfType => - div(cls := perfType.key)( - trans.perfRatingX( - raw(s"""${ctx.pref.showRatings ?? me - .perfs(perfType.key) - .map(_.intRating.toString) - .getOrElse("?")} ${perfType.trans}""") + { + val rating = me + .perfs(perfType.key) + .map(_.intRating.toString) + .getOrElse("?") + div(cls := perfType.key)( + if (ctx.pref.showRatings) + trans.perfRatingX( + raw(s"""${rating} ${perfType.trans}""") + ) + else + frag( + i(dataIcon := perfType.iconChar), + strong(cls := "none")(rating), // To calculate rating range in JS + perfType.trans + ) ) - ) + } } ) } From 6f71649b6b25554633ab846ff07a2ad28401de3e Mon Sep 17 00:00:00 2001 From: Albert Ford Date: Sun, 14 Nov 2021 07:57:32 -0800 Subject: [PATCH 048/144] Use full form of stroke-dasharray for Safari --- ui/common/css/component/_loader.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/common/css/component/_loader.scss b/ui/common/css/component/_loader.scss index 2756c90e47..dd2a3f6d73 100644 --- a/ui/common/css/component/_loader.scss +++ b/ui/common/css/component/_loader.scss @@ -80,7 +80,7 @@ $total_len: $len1 + $len2 + $len3; g { animation: spinner-color 11s steps(1) infinite; - stroke-dasharray: $total_len; + stroke-dasharray: $total_len $total_len; } .white & path { From 7118148f4dfc7478033032c0716c1a32965bbb62 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 14 Nov 2021 19:39:43 +0100 Subject: [PATCH 049/144] Fix download page date range for Safari close https://github.com/ornicar/lila/issues/10065 --- ui/site/src/userGamesDownload.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ui/site/src/userGamesDownload.ts b/ui/site/src/userGamesDownload.ts index cdc4d04efd..81b93b16a6 100644 --- a/ui/site/src/userGamesDownload.ts +++ b/ui/site/src/userGamesDownload.ts @@ -9,8 +9,8 @@ function generateSearchParams(): string { }); ['since', 'until'].forEach(name => { - const minTimestamp = 1356998400070; - const date = $(`#dl-date-${name}`).val() as string; + const minTimestamp = 1356998400070; // 01/01/2013, 01:00:00 + const date = ($(`#dl-date-${name}`).val() as string).replace(/-/g, '/'); // for Safari https://stackoverflow.com/a/4310986/11955835 const time = $(`#dl-time-${name}`).val() as string; if (date.length == 10) { // the 00:00:00 is necessary for the time to be interpreted in the local timezone From eb58eddcae388d899f09a47bacf3aa42e0ac3b51 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sun, 14 Nov 2021 19:57:53 +0100 Subject: [PATCH 050/144] Fix ublog like buttons --- app/views/ublog/post.scala | 18 ++++++++++++------ ui/site/src/ublog.ts | 5 ++++- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/app/views/ublog/post.scala b/app/views/ublog/post.scala index f1fe289043..5c9a34c362 100644 --- a/app/views/ublog/post.scala +++ b/app/views/ublog/post.scala @@ -73,7 +73,7 @@ object post { post.lived map { live => span(cls := "ublog-post__meta__date")(semanticDate(live.at)) }, - likeButton(post, liked)(ctx)(cls := "ublog-post__like--mini button-link"), + likeButton(post, liked, showText = false), span(cls := "ublog-post__views")( trans.ublog.nbViews.plural(post.views.value, strong(post.views.value.localize)) ), @@ -104,7 +104,7 @@ object post { div(cls := "ublog-post__footer")( (ctx.isAuth && !ctx.is(user)) option div(cls := "ublog-post__actions")( - likeButton(post, liked), + likeButton(post, liked, showText = true), followButton(user, post, followed) ), h2(a(href := routes.Ublog.index(user.username))(trans.ublog.moreBlogPostsBy(user.username))), @@ -120,19 +120,25 @@ object post { dataIcon := "" )(trans.edit()) - private def likeButton(post: UblogPost, liked: Boolean)(implicit ctx: Context) = { + private def likeButton(post: UblogPost, liked: Boolean, showText: Boolean)(implicit ctx: Context) = { val text = if (liked) trans.study.unlike.txt() else trans.study.like.txt() button( tpe := "button", cls := List( - "ublog-post__like is ublog-post__like--big button button-big button-red" -> true, - "ublog-post__like--liked" -> liked + "ublog-post__like is" -> true, + "ublog-post__like--liked" -> liked, + "ublog-post__like--big button button-big button-red" -> showText, + "ublog-post__like--mini button-link" -> !showText ), dataRel := post.id.value, title := text )( span(cls := "ublog-post__like__nb")(post.likes.value.localize), - span(cls := "button-label")(text) + showText option span( + cls := "button-label", + attr("data-i18n-like") := trans.study.like.txt(), + attr("data-i18n-unlike") := trans.study.unlike.txt() + )(text) ) } diff --git a/ui/site/src/ublog.ts b/ui/site/src/ublog.ts index 6107c680be..13bcabcad4 100644 --- a/ui/site/src/ublog.ts +++ b/ui/site/src/ublog.ts @@ -14,8 +14,11 @@ lichess.load.then(() => { method: 'post', }) .then(likes => { + const label = $('.ublog-post__like .button-label'); + const newText = label.data(`i18n-${liked ? 'unlike' : 'like'}`); + label.text(newText); + $('.ublog-post__like').toggleClass(likeClass, liked).attr('title', newText); $('.ublog-post__like__nb').text(likes); - $('.ublog-post__like').toggleClass(likeClass, liked); }); }) ); From f1fcdc5c622ff248a536a48e783404fab3ccd082 Mon Sep 17 00:00:00 2001 From: Konstantinos07 Date: Sun, 14 Nov 2021 21:07:48 +0200 Subject: [PATCH 051/144] restrict irwin dashboard to hunters only --- app/controllers/Irwin.scala | 2 +- app/views/mod/menu.scala | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/controllers/Irwin.scala b/app/controllers/Irwin.scala index 1cdf029257..74851ceb76 100644 --- a/app/controllers/Irwin.scala +++ b/app/controllers/Irwin.scala @@ -7,7 +7,7 @@ final class Irwin(env: Env) extends LilaController(env) { import lila.irwin.JSONHandlers.reportReader def dashboard = - Secure(_.SeeReport) { implicit ctx => _ => + Secure(_.MarkEngine) { implicit ctx => _ => env.irwin.api.dashboard map { d => Ok(views.html.irwin.dashboard(d)) } diff --git a/app/views/mod/menu.scala b/app/views/mod/menu.scala index ba5217e2a8..c1e072a63c 100644 --- a/app/views/mod/menu.scala +++ b/app/views/mod/menu.scala @@ -30,7 +30,7 @@ object menu { a(cls := active.active("tour"), href := routes.TournamentCrud.index(1))("Tournaments"), isGranted(_.ManageEvent) option a(cls := active.active("event"), href := routes.Event.manager)("Events"), - isGranted(_.SeeReport) option + isGranted(_.MarkEngine) option a(cls := active.active("irwin"), href := routes.Irwin.dashboard)("Irwin dashboard"), isGranted(_.Shadowban) option a(cls := active.active("panic"), href := routes.Mod.chatPanic)( From 26101352faf27c3c8c711dd760e40601e2f682ce Mon Sep 17 00:00:00 2001 From: kraktus Date: Sun, 14 Nov 2021 21:56:24 +0100 Subject: [PATCH 052/144] Fix reports visibility in `all` queue for hunters close https://github.com/lichess-org/tavern/issues/139 --- modules/report/src/main/Reason.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/report/src/main/Reason.scala b/modules/report/src/main/Reason.scala index 7fbb648e3e..0341b8a786 100644 --- a/modules/report/src/main/Reason.scala +++ b/modules/report/src/main/Reason.scala @@ -49,10 +49,10 @@ object Reason { def isGrantedFor(mod: Holder)(reason: Reason) = { import lila.security.Granter reason match { - case Cheat => Granter.is(_.MarkEngine)(mod) - case AltPrint | CheatPrint => Granter.is(_.Admin)(mod) - case Comm => Granter.is(_.Shadowban)(mod) - case Boost | Playbans | Other => Granter.is(_.MarkBooster)(mod) + case Cheat => Granter.is(_.MarkEngine)(mod) + case AltPrint | CheatPrint | Playbans | Other => Granter.is(_.Admin)(mod) + case Comm => Granter.is(_.Shadowban)(mod) + case Boost => Granter.is(_.MarkBooster)(mod) } } } From 13dbacd997303a3e675175216b3caf89436b0d44 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Mon, 15 Nov 2021 17:42:52 +0100 Subject: [PATCH 053/144] Use DOMContentLoaded instead of window.onload --- app/views/base/layout.scala | 2 +- ui/@types/lichess/index.d.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index 6c97690679..42bcb638c4 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -149,7 +149,7 @@ object layout { def lichessJsObject(nonce: Nonce)(implicit lang: Lang) = embedJsUnsafe( - s"""lichess={load:new Promise(r=>{window.onload=r}),quantity:${lila.i18n + s"""lichess={load:new Promise(r=>{document.addEventListener("DOMContentLoaded",r)}),quantity:${lila.i18n .JsQuantity(lang)}};$timeagoLocaleScript""", nonce ) diff --git a/ui/@types/lichess/index.d.ts b/ui/@types/lichess/index.d.ts index 1520451db0..ac083af572 100644 --- a/ui/@types/lichess/index.d.ts +++ b/ui/@types/lichess/index.d.ts @@ -1,5 +1,5 @@ interface Lichess { - load: Promise; // window.onload promise + load: Promise; // document.onload promise info: any; requestIdleCallback(f: () => void, timeout?: number): void; sri: string; From d4f87c1575ddcee460fc6535b503bd11800f5a00 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 16 Nov 2021 11:44:01 +0100 Subject: [PATCH 054/144] fix nextInt(0) and add chance for fishnet to ignore book --- modules/fishnet/src/main/FishnetOpeningBook.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index e7add7e163..fd8fee4761 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -56,7 +56,8 @@ object FishnetOpeningBook { case class Response(moves: List[Move]) { def randomPonderedMove: Option[Move] = { val sum = moves.map(_.nb).sum - val rng = ThreadLocalRandom nextInt sum + val novelty = 1 + val rng = ThreadLocalRandom.nextInt(sum + novelty) moves .foldLeft((none[Move], 0)) { case ((found, it), next) => val nextIt = it + next.nb From 1108c4b84324b6cb1a7b4315ea215ef44860e9a8 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 17 Nov 2021 08:05:55 +0100 Subject: [PATCH 055/144] Don't unload soundSet-independent sounds on soundSet change (#10126) * Don't unload soundSet-independent sounds on soundSet change * Fix lint * Fix lint 2 --- ui/@types/lichess/index.d.ts | 2 +- ui/analyse/src/study/practice/studyPracticeCtrl.ts | 4 ++-- ui/learn/src/sound.ts | 2 +- ui/puz/src/util.ts | 2 +- ui/puzzle/src/ctrl.ts | 2 +- ui/site/src/component/sound.ts | 13 +++++++------ 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/ui/@types/lichess/index.d.ts b/ui/@types/lichess/index.d.ts index 1520451db0..bbd823e117 100644 --- a/ui/@types/lichess/index.d.ts +++ b/ui/@types/lichess/index.d.ts @@ -102,7 +102,7 @@ interface UserCompleteOpts { } interface SoundI { - loadOggOrMp3(name: string, path: string): void; + loadOggOrMp3(name: string, path: string, noSoundSet?: boolean): void; loadStandard(name: string, soundSet?: string): void; play(name: string, volume?: number): void; playOnce(name: string): void; diff --git a/ui/analyse/src/study/practice/studyPracticeCtrl.ts b/ui/analyse/src/study/practice/studyPracticeCtrl.ts index 5e1b20c0c1..669170312e 100644 --- a/ui/analyse/src/study/practice/studyPracticeCtrl.ts +++ b/ui/analyse/src/study/practice/studyPracticeCtrl.ts @@ -15,8 +15,8 @@ export default function (root: AnalyseCtrl, studyData: StudyData, data: StudyPra analysisUrl = prop(''), autoNext = storedProp('practice-auto-next', true); - lichess.sound.loadOggOrMp3('practiceSuccess', `${lichess.sound.baseUrl}/other/energy3`); - lichess.sound.loadOggOrMp3('practiceFailure', `${lichess.sound.baseUrl}/other/failure2`); + lichess.sound.loadOggOrMp3('practiceSuccess', `${lichess.sound.baseUrl}/other/energy3`, true); + lichess.sound.loadOggOrMp3('practiceFailure', `${lichess.sound.baseUrl}/other/failure2`, true); function onLoad() { root.showAutoShapes = readOnlyProp(true); diff --git a/ui/learn/src/sound.ts b/ui/learn/src/sound.ts index 5c5f63ad90..d15dd1dbe2 100644 --- a/ui/learn/src/sound.ts +++ b/ui/learn/src/sound.ts @@ -1,5 +1,5 @@ const make = (file: string, volume?: number) => { - lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`); + lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`, true); return () => lichess.sound.play(file, volume); }; diff --git a/ui/puz/src/util.ts b/ui/puz/src/util.ts index 5cb0770194..8db9dac4eb 100644 --- a/ui/puz/src/util.ts +++ b/ui/puz/src/util.ts @@ -10,7 +10,7 @@ export const uciToLastMove = (uci: string | undefined): [Key, Key] | undefined = export const puzzlePov = (puzzle: Puzzle) => opposite(parseFen(puzzle.fen).unwrap().turn); export const loadSound = (file: string, volume?: number, delay?: number) => { - setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`), delay || 1000); + setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`, true), delay || 1000); return () => lichess.sound.play(file, volume); }; diff --git a/ui/puzzle/src/ctrl.ts b/ui/puzzle/src/ctrl.ts index abb9b53948..cae4fc41c7 100644 --- a/ui/puzzle/src/ctrl.ts +++ b/ui/puzzle/src/ctrl.ts @@ -50,7 +50,7 @@ export default function (opts: PuzzleOpts, redraw: Redraw): Controller { const throttleSound = (name: string) => throttle(100, () => lichess.sound.play(name)); const loadSound = (file: string, volume?: number, delay?: number) => { - setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`), delay || 1000); + setTimeout(() => lichess.sound.loadOggOrMp3(file, `${lichess.sound.baseUrl}/${file}`, true), delay || 1000); return () => lichess.sound.play(file, volume); }; const sound = { diff --git a/ui/site/src/component/sound.ts b/ui/site/src/component/sound.ts index 7ed9d7cdf0..07793ebef1 100644 --- a/ui/site/src/component/sound.ts +++ b/ui/site/src/component/sound.ts @@ -18,7 +18,8 @@ type Name = string; type Path = string; const sound: SoundI = new (class { - sounds = new Map(); // The loaded sounds and their instances + soundSetSounds = new Map(); // The loaded sounds and their instances + standaloneSounds = new Map(); // Sounds that are independent of the sound set soundSet = $('body').data('sound-set'); speechStorage = storage.makeBoolean('speech.enabled'); volumeStorage = storage.make('sound-volume'); @@ -30,8 +31,8 @@ const sound: SoundI = new (class { if (this.soundSet == 'music') setTimeout(this.publish, 500); } - loadOggOrMp3 = (name: Name, path: Path) => - this.sounds.set( + loadOggOrMp3 = (name: Name, path: Path, noSoundSet = false) => + (noSoundSet ? this.standaloneSounds : this.soundSetSounds).set( name, new Howl({ src: ['ogg', 'mp3'].map(ext => `${path}.${ext}`), @@ -49,10 +50,10 @@ const sound: SoundI = new (class { } private getOrLoadSound = (name: string, set: string): Howl => { - let s = this.sounds.get(name); + let s = this.soundSetSounds.get(name) ?? this.standaloneSounds.get(name); if (!s) { this.loadStandard(name, set); - s = this.sounds.get(name)!; + s = this.soundSetSounds.get(name)!; } return s; }; @@ -115,7 +116,7 @@ const sound: SoundI = new (class { changeSet = (s: string) => { this.soundSet = s; - this.sounds.clear(); + this.soundSetSounds.clear(); this.publish(); }; From 0b39bccc1fd2db9bcf231a88fd4d2a860424fd25 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 17 Nov 2021 08:48:10 +0100 Subject: [PATCH 056/144] improve fishnet opening book monitoring --- modules/common/src/main/mon.scala | 11 +++++++++-- .../fishnet/src/main/FishnetOpeningBook.scala | 17 ++++++++++++----- modules/fishnet/src/main/FishnetPlayer.scala | 8 +++----- 3 files changed, 24 insertions(+), 12 deletions(-) diff --git a/modules/common/src/main/mon.scala b/modules/common/src/main/mon.scala index 75f5e732dd..407db66086 100644 --- a/modules/common/src/main/mon.scala +++ b/modules/common/src/main/mon.scala @@ -646,9 +646,15 @@ object mon { def request(hit: Boolean) = counter("fishnet.http.acquire").withTag("hit", hit) } def move(level: Int) = counter("fishnet.move.time").withTag("level", level) - def openingBook(level: Int, variant: String, ply: Int, hit: Boolean) = + def openingBook(level: Int, variant: String, ply: Int, hit: Boolean, success: Boolean) = timer("fishnet.opening.hit").withTags( - Map("level" -> level.toLong, "variant" -> variant, "ply" -> ply.toLong, "hit" -> hit) + Map( + "level" -> level.toLong, + "variant" -> variant, + "ply" -> ply.toLong, + "hit" -> hitTag(hit), + "success" -> successTag(success) + ) ) } object study { @@ -727,6 +733,7 @@ object mon { ) private def successTag(success: Boolean) = if (success) "success" else "failure" + private def hitTag(hit: Boolean) = if (hit) "hit" else "miss" private def apiTag(api: Option[ApiVersion]) = api.fold("-")(_.toString) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index fd8fee4761..81a2f538e2 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -13,6 +13,7 @@ import lila.common.Json.uciReader import lila.common.ThreadLocalRandom import lila.game.Game import lila.memo.SettingStore +import scala.util.{ Failure, Success } final private class FishnetOpeningBook( ws: StandaloneWSClient, @@ -42,10 +43,16 @@ final private class FishnetOpeningBook( move <- data.randomPonderedMove } yield move.uci } - .monValue(uci => + .monTry { res => _.fishnet - .openingBook(level = level, variant = game.variant.key, ply = game.turns, hit = uci.isDefined) - ) + .openingBook( + level = level, + variant = game.variant.key, + ply = game.turns, + hit = res.toOption.exists(_.isDefined), + success = res.isSuccess + ) + } } } @@ -55,9 +62,9 @@ object FishnetOpeningBook { case class Response(moves: List[Move]) { def randomPonderedMove: Option[Move] = { - val sum = moves.map(_.nb).sum + val sum = moves.map(_.nb).sum val novelty = 1 - val rng = ThreadLocalRandom.nextInt(sum + novelty) + val rng = ThreadLocalRandom.nextInt(sum + novelty) moves .foldLeft((none[Move], 0)) { case ((found, it), next) => val nextIt = it + next.nb diff --git a/modules/fishnet/src/main/FishnetPlayer.scala b/modules/fishnet/src/main/FishnetPlayer.scala index f37c0fc3fd..348ba36f73 100644 --- a/modules/fishnet/src/main/FishnetPlayer.scala +++ b/modules/fishnet/src/main/FishnetPlayer.scala @@ -1,15 +1,13 @@ package lila.fishnet +import chess.format.Uci +import chess.{ Black, Clock, White } import scala.concurrent.duration._ -import chess.{ Black, Clock, White } - -import lila.common.{ Future, ThreadLocalRandom } +import lila.common.{ Bus, Future, ThreadLocalRandom } import lila.game.{ Game, GameRepo, UciMemo } -import lila.common.Bus import lila.hub.actorApi.map.Tell import lila.hub.actorApi.round.FishnetPlay -import chess.format.Uci final class FishnetPlayer( redis: FishnetRedis, From 0be124f644eda3f2b7edc66f44a51e09a7db7a27 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 17 Nov 2021 10:10:39 +0100 Subject: [PATCH 057/144] New Crowdin updates (#10109) * New translations: study.xml (Luxembourgish) * New translations: site.xml (Luxembourgish) * New translations: challenge.xml (Nepali) * New translations: challenge.xml (Nepali) * New translations: emails.xml (Nepali) * New translations: activity.xml (Nepali) * New translations: activity.xml (Nepali) * New translations: coordinates.xml (Nepali) * New translations: emails.xml (Latin) * New translations: patron.xml (Belarusian) * New translations: study.xml (Belarusian) * New translations: emails.xml (Azerbaijani) * New translations: emails.xml (Azerbaijani) * New translations: swiss.xml (Hebrew) * New translations: site.xml (Nepali) * New translations: site.xml (Nepali) * New translations: site.xml (Nepali) * New translations: site.xml (Nepali) * New translations: site.xml (Nepali) * New translations: site.xml (Nepali) * New translations: site.xml (Nepali) * New translations: site.xml (Russian) * New translations: site.xml (Malayalam) * New translations: site.xml (Malayalam) * New translations: site.xml (Malayalam) * New translations: site.xml (Malayalam) * New translations: site.xml (Russian) * New translations: challenge.xml (Afrikaans) * New translations: challenge.xml (Afrikaans) * New translations: challenge.xml (Afrikaans) * New translations: site.xml (Afrikaans) * New translations: site.xml (Finnish) --- translation/dest/activity/ne-NP.xml | 7 ++++++- translation/dest/challenge/af-ZA.xml | 25 ++++++++++++++++++++++++- translation/dest/challenge/ne-NP.xml | 22 +++++++++++++++++++++- translation/dest/coordinates/ne-NP.xml | 10 +++++----- translation/dest/emails/az-AZ.xml | 4 ++-- translation/dest/emails/la-LA.xml | 2 +- translation/dest/emails/ne-NP.xml | 18 +++++++++--------- translation/dest/patron/be-BY.xml | 2 +- translation/dest/site/af-ZA.xml | 14 +++++++++----- translation/dest/site/fi-FI.xml | 4 ++-- translation/dest/site/lb-LU.xml | 22 +++++++++++----------- translation/dest/site/ml-IN.xml | 4 ++++ translation/dest/site/ne-NP.xml | 19 ++++++++++++++----- translation/dest/study/be-BY.xml | 3 ++- translation/dest/study/lb-LU.xml | 6 +++--- translation/dest/swiss/he-IL.xml | 2 ++ 16 files changed, 116 insertions(+), 48 deletions(-) diff --git a/translation/dest/activity/ne-NP.xml b/translation/dest/activity/ne-NP.xml index df052afd29..82d4b3ed15 100644 --- a/translation/dest/activity/ne-NP.xml +++ b/translation/dest/activity/ne-NP.xml @@ -1,7 +1,7 @@ क्रियाकलाप - लाइभ स्ट्रोम आयोजना गर्नुभयो + लाइभ स्ट्रीम आयोजना गर्नुभयो lichess.org लाई %1$s महिनासम्म %2$s भई समर्थन गर्नुभयो lichess.org लाई %1$s महिनासम्म %2$s भई समर्थन गर्नुभयो @@ -62,6 +62,11 @@ %4$s मा %3$s वटा खेल खेली #%1$s औ स्थान (माथिल्लो %2$s%%) %4$s मा %3$s वटा खेलहरु खेली #%1$s औ स्थान (माथिल्लो %2$s%%) + + %sवटा स्वीस् प्रतियोगितामा सहभागी हुनुभयो। + %sवटा स्वीस् प्रतियोगिताहरूमा सहभागी हुनुभयो + + %2$s मा #%1$s स्थान प्राप्त lichess.org मा भर्ना हुनुभयो %s वटा टोलीमा सम्मिलित हुनुभयो diff --git a/translation/dest/challenge/af-ZA.xml b/translation/dest/challenge/af-ZA.xml index 3ea04e700d..2071078edc 100644 --- a/translation/dest/challenge/af-ZA.xml +++ b/translation/dest/challenge/af-ZA.xml @@ -1,2 +1,25 @@ - + + Uitdagings + Daag uit tot \'n spel + Uitdaging afgewys + Uitdaging aanvaar! + Uitdaging gekanselleer. + Registreer om uitdagings te stuur. + Jy kan nie %s uitdaag nie. + %s aanvaar nie uitdagings nie. + Jou %1$s gradering is te ver van %2$s. + Kan nie uitdaag nie weens voorlopige %s gradering. + %s aanvaar net uitdagings van vriende. + Ek aanvaar nie uitdagings op die oomblik nie. + Dit is \'n ongeleë tyd, vra asseblief weer later. + Die tydskontrole is te vinnig vir my, daag my weer uit met \'n stadiger tydskontrole. + Die tydskontrole is te stadig vir my, daag my weer uit met \'n vinniger tydskontrole. + Ek aanvaar nie uitdagings met hierdie tydskontrole nie. + Stuur eerder vir my \'n gegradeerde uitdaging asseblief. + Stuur eerder vir my \'n vriendskaplike uitdaging asseblief. + Ek aanvaar nie variant-uitdagings op die oomblik nie. + Ek is nie bereid om hierdie variant op die oomblik te speel nie. + Ek aanvaar nie uitdagings deur bots nie. + Ek aanvaar slegs uitdagings deur bots. + diff --git a/translation/dest/challenge/ne-NP.xml b/translation/dest/challenge/ne-NP.xml index 3ea04e700d..1879556dfa 100644 --- a/translation/dest/challenge/ne-NP.xml +++ b/translation/dest/challenge/ne-NP.xml @@ -1,2 +1,22 @@ - + + चुनौतीहरू + खेलको लागि चुनौति दिनुहोस् + चुनौती अस्वीकृत + चुनौती स्वीकृत! + चुनौती रद्द। + चुनौति दिनका लागि दर्ता गर्नुहोस्। + तपाईँ %sलाई चुनौति दिन सक्नुहुन्न। + %sले चुनौती स्वीकार गर्नुभएन। + तपाईँको %1$s रेटिङ्ग %2$s भन्दा धेरै टाढा छ। + अस्थायी %s रेटिङका कारण खेलको लागि आमन्त्रण गर्न असम्भव छ। + %sले मित्रहरूको आमन्त्रण मात्र स्वीकार गर्नुहुन्छ। + म यो समय चुनौति स्वीकार गरिरहेको छैँन। + मेरा लागि यो उपयुक्त समय होइन, कृपया केही समय पछि सोध्नुहोला। + यो टाइम कन्ट्रोल मेरा लागि धेरै नै छिटो भयो, कृपया ढिलो खेलका लागि चुनौती दिनुहोस्। + यो टाइम कन्ट्रोल मेरा लागि धेरै नै ढिलो भयो, कृपया छिटो खेलका लागि चुनौती दिनुहोस्। + म यो टाइम कन्ट्रोलको चुनौति स्वीकार गरिरहेको छैँन। + कृपया मलाई सरेटिङ चुनौति पठाउनुहोस्। + कृपया मलाई अरेटिङ चुनौति पठाउनुहोस्। + म बटहरूको आमन्त्रण मात्र स्वीकार गरिरहेको छु। + diff --git a/translation/dest/coordinates/ne-NP.xml b/translation/dest/coordinates/ne-NP.xml index 3fcc435995..ca996ecd45 100644 --- a/translation/dest/coordinates/ne-NP.xml +++ b/translation/dest/coordinates/ne-NP.xml @@ -1,12 +1,12 @@ - को‍-अर्डिनेटहरु - को-अर्डिनेटको अभ्यास + समकक्षहरु + समकक्षको अभ्यास सेतो लिएर औसत अंक: %s कालो लिएर औसत अंक: %s - चेस बोर्ड को को-अर्डिनेटहरूको को ज्ञान हुनु धेरै महत्वपूर्ण छ: - प्राय सबै चेस कोर्स र अभ्यासहरुमा एल्जेब्रिक नोटेशन प्रयोग हुन्छ। - यसले तपाइलाई आफ्नो चेस खेल्ने मित्रहरु संग कुरा गर्न सहज हुन्छ, किनकि तपाईहरु दुवै \"चेस को भाषा\" बुझ्नुहुन्छ। + चेस बोर्डका समकक्षहरूको को ज्ञान हुनु धेरै महत्वपूर्ण छ: + प्राय सबै चेस कोर्स र अभ्यासहरूमा विजीय सङ्केत प्रयोग हुन्छ। + यसले तपाइलाईँ आफ्ना चेस खेल्ने मित्रहरू संग कुरा गर्न सहज हुन्छ, किनकि तपाईहरू दुवै \"चेस को भाषा\" बुझ्नुहुन्छ। तपाइले खेलहरु प्रभावकारी रुपमा विश्लेषण गर्न सक्नुहुन्छ किनकि तपाइलाई कोठाको नामहरु खोजिरहनु पर्दैन। कोठाको नाम बोर्डमा आउछ अनि तपाइले सहि कोठामा क्लिक गर्नुपर्ने हुन्छ| तपाइसंग तपाइले सकेजति कोठा चिन्न ३० सेकेन्ड हुनेछ! अभ्यास सुरु गर्नुहोस् diff --git a/translation/dest/emails/az-AZ.xml b/translation/dest/emails/az-AZ.xml index fa7366b9d0..e594603ee5 100644 --- a/translation/dest/emails/az-AZ.xml +++ b/translation/dest/emails/az-AZ.xml @@ -5,11 +5,11 @@ \"Lichess\"-də qeydiyyatdan keçməmisinizsə, bu mesaja əhəmiyyət verməyə bilərsiniz. Lichess.org şifrənizi sıfırlayın, %s Hesabınızın şifrəsini sıfırlamaq üçün bir sorğu aldıq. - Əgər bu sorğunu siz etmisinizsə, aşağıdakı linkə klikləyin. Əks halda, bu e-poçta əhəmiyyət verməyin. + Əgər bu sorğunu siz etmisinizsə, aşağıdakı linkə klikləyin. Siz etməmisinizsə, bu e-poçta əhəmiyyət verməyin. Yeni e-poçt ünvanınızı təsdiqləyin, %s E-poçt ünvanınızın dəyişdirilməsi üçün sorğu göndərdiniz. Bu e-poçt sizin üçün əlçatandırsa, zəhmət olmasa aşağıdakı bağlantıya klikləyin: - \"Lichess.org\"-a xoş gəldiniz, %s + Lichess.org\'a xoş gəldiniz, %s Hesabınız https://lichess.org üzərindən uğurla yaradıldı. Profil səhifəniz: %1$s. Profilinizi %2$s üzərindən özəlləşdirə bilərsiniz. diff --git a/translation/dest/emails/la-LA.xml b/translation/dest/emails/la-LA.xml index fe55e1df2e..70d9dc2055 100644 --- a/translation/dest/emails/la-LA.xml +++ b/translation/dest/emails/la-LA.xml @@ -1,5 +1,5 @@ Signum vestrum lichess.org repone, %s - Salve lichess.org, %s + Bene advenisti ad lichess.org, %s diff --git a/translation/dest/emails/ne-NP.xml b/translation/dest/emails/ne-NP.xml index bbdaf293b2..08609051a8 100644 --- a/translation/dest/emails/ne-NP.xml +++ b/translation/dest/emails/ne-NP.xml @@ -1,21 +1,21 @@ - %s, कृपया तपाइको lichess.org खाता पुष्टी गर्नुहोस्। + %s, कृपया तपाईँको lichess.org खाता पुष्टी गर्नुहोस्। तपाईंको Lichess खाता सुचारु गर्न यो लिंकमा क्लिक गर्नुहोस्: - यदि तपाईले Lichessमा दर्ता गर्नुभएको छैन भने यो सन्देशलाई नडराइकन बेवास्ता गर्नुहोस्। + यदि तपाईँले Lichessमा दर्ता गर्नुभएको छैन भने यो सन्देशलाई बेवास्ता गर्नुहोस्। %s, तपाईंको lichess.org को पासवर्ड परिवर्तन गर्नुहोस्। हामीले तपाईंको खाताको पासवर्ड फेर्ने अनुरोध प्राप्त गरेका छौं। - यदि तपाईले यो अनुरोध गर्नुभएको हो भने तलको लिंकमा क्लिक गर्नुहोस्। अन्यथा यो इमेललाइ बेवास्ता गर्नुहोस्। - %s, नयाँ इमेल पुष्टी गर्नुहोस्। - तपाईले इमेल ठेगाना परिवर्तन गर्न अनुरोध गर्नुभएको छ। + यदि तपाईँले यो अनुरोध गर्नुभएको हो भने तलको लिङ्कमा क्लिक गर्नुहोस्। अन्यथा यो इमेललाई बेवास्ता गर्नुहोस्। + %s, नयाँ इमेल पुष्टि गर्नुहोस्। + तपाईँले इमेल परिवर्तन गर्न अनुरोध गर्नुभएको छ। यो इमेलमा तपाईंको पहुँच छ भन्ने यकिन गर्न तलको लिंकमा क्लिक गर्नुहोस्: %s, lichess.org मा तपाईलाई स्वागत् छ। - तपाई https://lichess.org मा खाता दर्ता गर्न सफल हुनुभएको छ। + तपाईँ https://lichess.org मा खाता दर्ता गर्न सफल हुनुभएको छ। -तपाईंको प्रोफाइल पृष्ठ यहाँ छ: %1$s। यसलाई %2$s मा तपाईलाई अनुकुल पर्नेगरी परिवर्तन गर्न सक्नुहुनेछ। +तपाईंको प्रोफाइल पृष्ठ यहाँ छ: %1$s। यसलाई %2$s मा तपाईँलाई अनुकुल पर्नेगरी परिवर्तन गर्न सक्नुहुनेछ। -रमाइलो गर्नुहोस्। तपाईंको गोटीहरु विपक्षी राजा सम्म पुगुन्। - %s, lichess.org मा लग इन गरौं +रमाइलो गर्नुहोस्। तपाईंका गोटीहरू विपक्षी राजा सम्म पुगुन्। + %s, lichess.org मा लग इन गर्नुहोस्। (क्लिक भएन? ब्राउजरमा पेष्ट गरेर हेर्नुहोस्!) यो तपाईंको %s को प्रयोग संग सम्बन्धित इमेल हो। कृपया हामीसँग सम्पर्क गर्न %s को प्रयोग गर्नुहोस्। diff --git a/translation/dest/patron/be-BY.xml b/translation/dest/patron/be-BY.xml index 3119ff4d6c..dee03b4a47 100644 --- a/translation/dest/patron/be-BY.xml +++ b/translation/dest/patron/be-BY.xml @@ -58,7 +58,7 @@ Спонсар Lichess на працягу %s месяцаў Вы ахвяруеце lichess.org %s кожны месяц. - Дзякуй вілікі за дапамогу! Вы неверагодны! + Дзякуй вялікі за дапамогу! Вы неверагодны! Бягучы статус Наступны плацёж %2$s вы заплаціце %1$s. diff --git a/translation/dest/site/af-ZA.xml b/translation/dest/site/af-ZA.xml index 2543ffb7eb..b99993bd71 100644 --- a/translation/dest/site/af-ZA.xml +++ b/translation/dest/site/af-ZA.xml @@ -48,9 +48,11 @@ Wit het die spel verlaat Swart het die spel verlaat WitHetniebeweegnie + Swart het nie geskuif nie Versoek \'n rekenaar analise Rekenaar analise Rekenaar analise beskikbaar + Rekenaaranalise gedeaktiveer Ontledingsbord Diepte %s Die gebruik van bediener analise @@ -85,9 +87,11 @@ Mat in %s half-skuif Mat in %s half-skuiwe
+ DTZ50\" met afronding, gebaseer op die hoeveelheid half-skuiwe tot die volgende vat of pionskuif Geen spel gevind Voeg dalk meer wedstryde van die voorkeure kieslys by? Opening ontdekker + Opening/eindspel ontdekker %s opening ontdekker Oorwinning gekelder deur 50-skuifreël Verloor voorkom deur 50-skuifreël @@ -142,7 +146,7 @@ Spelers Vriende Gesprekke - Vandag + Vandag Gister Minute per kant Variant @@ -411,9 +415,9 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Bord redigeerder Pak die bord Gewilde openinge - Begin posisie + Begin posisie Maak bord skoon - Laai posisie + Laai posisie Privaat Raporteer %s aan moderators Profiel voltooiing: %s @@ -431,7 +435,7 @@ rekenaar analise, kletskamer en deelbare URL te kry.
Voorigekeer op Lichess TV Aanlyn spelers Aktiewe spelers - Pasop, dié spel is gegradeer, maar sonder \'n klok! + Pasop, dié spel is gegradeer, maar sonder \'n klok! Sukses %s spel aan die gang @@ -497,7 +501,7 @@ rekenaar analise, kletskamer en deelbare URL te kry. Op stadig spelle Altyd Nooit - %1$s kompeteer in %2$s + %1$s kompeteer in %2$s Oorwinning Nederlaag %1$s vs %2$s in %3$s diff --git a/translation/dest/site/fi-FI.xml b/translation/dest/site/fi-FI.xml index aeab410e62..b27448cb0d 100644 --- a/translation/dest/site/fi-FI.xml +++ b/translation/dest/site/fi-FI.xml @@ -794,8 +794,8 @@ Klassinen Järjettömän nopeat pelit: alle 30 sekuntia Hyvin nopeat pelit: alle 3 minuuttia - Nopeat pelit: 3-8 minuuttia - Pikapelit: 8-25 minuuttia + Pikapelit: 8-25 minuuttia + Nopeat pelit: 8-25 minuuttia Klassiset pelit: 25 minuuttia tai enemmän Kirjeshakkipelit: yksi tai useampi päivä per siirto Taktiikkaharjoittelu diff --git a/translation/dest/site/lb-LU.xml b/translation/dest/site/lb-LU.xml index 490d405d06..5cd52f8746 100644 --- a/translation/dest/site/lb-LU.xml +++ b/translation/dest/site/lb-LU.xml @@ -163,7 +163,7 @@ Spiller Frënn Konversatiounen - Haut + Haut Gëschter Minutten pro Säit Variant @@ -435,9 +435,9 @@ Beléift Eröffnungen Endspill Positiounen Chess960 Start Positioun: %s - Start Positioun + Start Positioun Briet opraumen - Positioun lueden + Positioun lueden Privat %s den Moderatoren mellen Profil vollstänneg zu: %s @@ -456,7 +456,7 @@ Virdrun op Lichess TV Online Spiller Aktiv Spiller - Pass op, d\'Partie ass gewäert mee huet keng Auer! + Pass op, d\'Partie ass gewäert mee huet keng Auer! Succès %s Partie amgaang @@ -525,7 +525,7 @@ An luesen Partien Ëmmer Nie - %1$s hëlt deel bei %2$s + %1$s hëlt deel bei %2$s Victoire Defaite %1$s vs %2$s an %3$s @@ -550,7 +550,7 @@ Auer Géigner Léieren - Léieren + Etüden Üben Gemeinschaft Tools @@ -560,10 +560,10 @@ Dës Email Adress ass net akzeptabel. Iwwerpréif se wannechgelift an probéier nach eng Kéier. Email Adress ongëlteg oder schon benotzt Dat ass schon deng Email Adress - Mindestens %s Zeechen - Maximal %s Zeechen - Muss mindestens %s sinn - Däerf maximal %s sinn + Mindestens %s Zeechen + Maximal %s Zeechen + Muss mindestens %s sinn + Däerf maximal %s sinn Falls Wäertung ± %s Just lafend Konversatiounen Just Frënn @@ -812,7 +812,7 @@ Du kanns nach net am Forum schreiwen. Spill puer Partien! Abonéieren Desabonéiren - huet dech an \"%1$s\" erwähnt. + huet dech an \"%1$s\" erwähnt. %1$s huet dech an \"%2$s\" erwähnt. huet dech zu \"%1$s\" invitéiert. %1$s huet dech zu \"%2$s\" invitéiert. diff --git a/translation/dest/site/ml-IN.xml b/translation/dest/site/ml-IN.xml index bc290c49bc..c614944ce5 100644 --- a/translation/dest/site/ml-IN.xml +++ b/translation/dest/site/ml-IN.xml @@ -24,6 +24,7 @@ നിങ്ങൾ വെള്ള കരുകൾ വെച്ച് കളിക്കുന്നു നിങ്ങൾ കറുപ്പ് കരുകൾ വെച്ച് കളിക്കുന്നു നിങ്ങളുടെ കളി! + കള്ളത്തരം കണ്ടെത്തി കിംഗ് ഇൻ ദി സെന്റർ ഗെയിം മൂനു ചെക്ക് ഗെയിം ഓട്ടം അവസാനിച്ചിരിക്കുന്നു @@ -89,6 +90,7 @@ ഒരു ഗെയിമും കണ്ടെത്താൻ സാധിച്ചില്ല പരിഗണന മെനുവിൽ നിന്ന് കൂടുതൽ ഗെയിമുകൾ ഉൾപ്പെടുത്താണോ? എക്സ്പ്ലോററും ടേബിൾ ബേസും തുറക്കാം + ഓപ്പണിങ്ങ്/എൻഡ്ഗെയിം എക്‌സ്‌പ്ലോറർ %s ഓപ്പണിങ് എക്സ്പ്ലോറർ 50 നീക്കങ്ങളുടെ നിയമം പ്രകാരം ജയം തടയപ്പെട്ടു 50 നീക്കങ്ങളുടെ നിയമം പ്രകാരം തോൽവി തടയപ്പെട്ടു @@ -428,6 +430,7 @@ കളിക്കളം സജ്ജമാക്കുക പ്രചാരത്തിലുള്ള ഓപ്പണിങ്ങുകൾ ഏൻഡ് ഗെയിം പൊസിഷനുകൾ + ചതുരംഗം960 ആരംഭ സ്ഥാനം: %s പ്രാരംഭ സ്ഥാനം പലക വ്യക്തമാക്കുക സ്ഥാനത്തു നിന്ന് @@ -550,6 +553,7 @@ കയറ്റം ഈ ഭാഗം ആവശ്യമാണ് ഈ ഇമെയിൽ അഡ്രസ് സാധുവല്ല + ഈ ഇമെയിൽ വിലാസം സ്വീകാര്യമല്ല. പരിശോധിച്ചുറപ്പാക്കി വീണ്ടും ശ്രമിക്കുക. ± %s ക്രമത്തിലാണോ നിലവിലുള്ള സംഭാഷങ്ങൾ മാത്രം സുഹൃത്തുക്കൾക്കു മാത്രം diff --git a/translation/dest/site/ne-NP.xml b/translation/dest/site/ne-NP.xml index 0441e58ca8..dbaa0d6f7e 100644 --- a/translation/dest/site/ne-NP.xml +++ b/translation/dest/site/ne-NP.xml @@ -24,6 +24,7 @@ तपाईं सेतो खेल्नुहुनेछ तपाईं कालो खेल्नुहुनेछ तपाईंको पालो! + बेइमानी पत्ता पर्यो राजा बिचमा तिनवटा चेकहरु दौड सकियो @@ -49,6 +50,7 @@ कम्प्युटर विश्लेषण आग्रह गर्नुहोस् कम्प्युटर विश्लेषण कम्प्युटर विश्लेषण उपलब्ध छ + कम्प्युटर विश्लेषण बन्द छ विश्लेषण बोर्ड गहिराइ %s सर्भरको विश्लेषण प्रयोग गरिदैं छ @@ -140,7 +142,7 @@ खेलाडीहरू साथीहरू वार्तालाप - आज + आज हिजो एक पक्षको मिनट परिमार्जन @@ -257,6 +259,7 @@ %s सेकेण्ड दिनु %s सेकेण्ड दिनु + यस खाताले Lichess Terms of Service को उल्लङ्घन गरेको छ शुरुका चाल अन्वेषक & टेबलबेस चाल फिर्ता चाल फिर्ता लिने प्रस्ताव गरौं @@ -405,9 +408,9 @@ बोर्ड सम्पादक बोर्ड सेट गरौं लोकप्रिय सुरुवाती चालहरु - शुरुवाती अवस्था + शुरुवाती अवस्था बोर्ड खालि गरौं - पोजिसन लोड गरौं + पोजिसन लोड गरौं नीजि %s माथी उजुरी गरौं प्रोफाइल समापन: %s @@ -419,13 +422,14 @@ शुभनाम थर जीवनी + देश अथवा झण्डा धन्यवाद! सामाजिक सञ्जाल लिङ्क पङ्तिमा नोटेशन लीचेस टिभीमा यसअघि अनलाइन खेलाडीहरु कृयाशील खेलाडीहरु - सावधान, खेल रेटिङवाला हो तर कुनै घडी छैन! + सावधान, खेल रेटिङवाला हो तर कुनै घडी छैन! सफलता %s खेल जारी @@ -475,6 +479,7 @@ घडीको शुरु समय हरेक चालमा थप हुने समय गोपनियता + गोप्यता नीति अरु खेलाडीलाई पछ्याउन दिने अरु खेलाडी लाई खेलको निम्तो दिन दिने अरु खेलाडीलाई पाठ मा आमन्त्रण गर्न दिने @@ -488,7 +493,7 @@ ढिलो खेलहरुमा सधैँ नाइँ - %1$s %2$s मा भाग लिनुहुन्छ + %1$s %2$s मा भाग लिनुहुन्छ विजय हार %1$s बनाम %2$s: %3$s मा @@ -754,5 +759,9 @@ प्रायः सोधिने प्रश्नहरूमा चोरी वा खराब ब्यहोराको उजुरी गर्नुपरेमा %1$s रिपोर्ट फारम प्रयोग गर्नुहोस् + मद्दतका लागि अनुरोध गर्न, %1$s + सम्पर्क पेजबाट प्रयास गर्नुहोस् + यो विषय सङ्गृहीत छ र यसलाई जवाफ दिन सकिँदैन। + %1$s टोली समय सक्किन लाग्यो! diff --git a/translation/dest/study/be-BY.xml b/translation/dest/study/be-BY.xml index 1824412e56..f6de5fa8b2 100644 --- a/translation/dest/study/be-BY.xml +++ b/translation/dest/study/be-BY.xml @@ -46,7 +46,8 @@ Вы цяпер рэдактар Вы цяпер глядач Тэгі PGN - Падабайка + Упадабаць + Разпадабаць Новы тэг Каментаваць пазіцыю Каментаваць гэты ход diff --git a/translation/dest/study/lb-LU.xml b/translation/dest/study/lb-LU.xml index 864d99f137..fb24d23a3c 100644 --- a/translation/dest/study/lb-LU.xml +++ b/translation/dest/study/lb-LU.xml @@ -41,7 +41,7 @@ Du bass elo en Zuschauer PGN Tags Gefällt mir - Gefällt mer net méi + Gefällt mer net méi Néien Tag Kommentéier des Positioun Kommentéier dësen Zuch @@ -59,11 +59,11 @@ Lescht Säit Deelen & exportéieren Klonen - PGN studéieren + Etüden PGN All Partien eroflueden Kapitel PGN Partie eroflueden - Etüd URL + Etüden URL Aktuellt Kapitel URL Zum Anbetten an een Forum oder Blog afügen Mat Startpositioun ufänken diff --git a/translation/dest/swiss/he-IL.xml b/translation/dest/swiss/he-IL.xml index b6ae168d59..0c19e83700 100644 --- a/translation/dest/swiss/he-IL.xml +++ b/translation/dest/swiss/he-IL.xml @@ -11,6 +11,8 @@ בטורניר שוויצרי, כל השחקנים משחקים אותו מספר של משחקים, ויכולים לשחק אחד נגד השני פעם אחת בלבד. זאת יכולה להיות אפשרות טובה למועדונים וטורנירים רשמיים. האם טורנירים שוויצריים יחליפו את טורנירי הזירות? לא. הם מאפיינים משלימים. + משך הטורניר + מספר משחקים כל יריב זמין עם דירוג דומה מהיר: אין צורך לחכות לשאר השחקנים איטי: צריך לחכות לסיום המשחקים של כל השחקנים From 61eb1b5533fb727f7cbaeb602118ca32d094f878 Mon Sep 17 00:00:00 2001 From: kraktus Date: Wed, 17 Nov 2021 17:43:31 +0100 Subject: [PATCH 058/144] Fix patron gift when the receiver is already patron. Previously nothing was changed for the receiver. --- modules/plan/src/main/PlanApi.scala | 37 ++++++++++++++--------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/modules/plan/src/main/PlanApi.scala b/modules/plan/src/main/PlanApi.scala index 6ea1f74425..152a94aa69 100644 --- a/modules/plan/src/main/PlanApi.scala +++ b/modules/plan/src/main/PlanApi.scala @@ -282,25 +282,24 @@ final class PlanApi( .void >> setDbUserPlanOnCharge(user, levelUp = false) def gift(from: User, to: User, money: Money): Funit = - !to.isPatron ?? { - for { - isLifetime <- pricingApi isLifetime money - _ <- patronColl.update - .one( - $id(to.id), - $set( - "lastLevelUp" -> DateTime.now, - "lifetime" -> isLifetime, - "free" -> Patron.Free(DateTime.now, by = from.id.some), - "expiresAt" -> (!isLifetime option DateTime.now.plusMonths(1)) - ), - upsert = true - ) - newTo = to.mapPlan(_.incMonths) - _ <- setDbUserPlan(newTo) - } yield { - notifier.onGift(from, newTo, isLifetime) - } + for { + toPatronOpt <- userPatron(to) + isLifetime <- fuccess(~toPatronOpt.map(_.isLifetime)) >>| (pricingApi isLifetime money) + _ <- patronColl.update + .one( + $id(to.id), + $set( + "lastLevelUp" -> DateTime.now, + "lifetime" -> isLifetime, + "free" -> Patron.Free(DateTime.now, by = from.id.some), + "expiresAt" -> (!isLifetime option DateTime.now.plusMonths(1)) + ), + upsert = true + ) + newTo = to.mapPlan(p => if (~toPatronOpt.map(_.canLevelUp)) p.incMonths else p.enable) + _ <- setDbUserPlan(newTo) + } yield { + notifier.onGift(from, newTo, isLifetime) } def recentGiftFrom(from: User): Fu[Option[Patron]] = From 78400f6a1ca04a4a3c9431648feb5ff55785f5d3 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Thu, 18 Nov 2021 02:06:36 +0100 Subject: [PATCH 059/144] Correct comment in @types/lichess/index.d.ts --- ui/@types/lichess/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/@types/lichess/index.d.ts b/ui/@types/lichess/index.d.ts index 78c0c4a673..382eeca0e2 100644 --- a/ui/@types/lichess/index.d.ts +++ b/ui/@types/lichess/index.d.ts @@ -1,5 +1,5 @@ interface Lichess { - load: Promise; // document.onload promise + load: Promise; // DOMContentLoaded promise info: any; requestIdleCallback(f: () => void, timeout?: number): void; sri: string; From 1095edbf13428740b6747e96487cbf3b1464ca9c Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 18 Nov 2021 07:44:35 +0100 Subject: [PATCH 060/144] New translations: site.xml (Chinese Simplified) (#10132) --- translation/dest/site/zh-CN.xml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/translation/dest/site/zh-CN.xml b/translation/dest/site/zh-CN.xml index 9bf07f6add..d41bac5564 100644 --- a/translation/dest/site/zh-CN.xml +++ b/translation/dest/site/zh-CN.xml @@ -92,6 +92,7 @@ %s开局浏览器 因50回合规则未赢棋 因50回合规则未输棋 + 由于先前的错误而损失或打平局(rule50) 好了! 导入PGN文件 删除 @@ -151,7 +152,7 @@ 棋手 棋友 讨论组 - 今天 + 今天 昨天 各方限时(分钟) 变体 @@ -401,9 +402,9 @@ 常见开局 残局局面 Chess960 起始局面: %s - 起始局面 + 起始局面 清空棋盘 - 载入局面 + 载入局面 私人锦标赛 举报%s 资料完成度:%s @@ -422,7 +423,7 @@ 过去的 Lichess 直播 在线棋手 活跃棋手 - 注意,此棋局是排位赛,但是不计时! + 注意,此棋局是排位赛,但是不计时! 解题成功 %s 场对局进行中 @@ -490,7 +491,7 @@ 在慢棋时 总是 从不 - %1$s参加%2$s + %1$s参加%2$s 获胜 负败 %1$s,与 %2$s 进行的 %3$s @@ -769,7 +770,7 @@ 您还不能在论坛发表意见。再下几局棋吧! 订阅 取消订阅 - 在 “%1$s” 中提到了你。 + 在 “%1$s” 中提到了你。 %1$s 在 \"%2$s\" 中提到了你。 邀请你加入\"%1$s\"。 %1$s 邀请你加入 \"%2$s\"。 From 00711203f692843e4264cefd2699a61862a882df Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 18 Nov 2021 11:00:02 +0100 Subject: [PATCH 061/144] add /api/bot/game/{gameId}/chat - closes #10135 --- app/controllers/PlayApi.scala | 19 +++++++++++++------ conf/routes | 1 + 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/app/controllers/PlayApi.scala b/app/controllers/PlayApi.scala index 7caf2360ba..d4429af035 100644 --- a/app/controllers/PlayApi.scala +++ b/app/controllers/PlayApi.scala @@ -122,15 +122,22 @@ final class PlayApi( def boardCommandGet(cmd: String) = ScopedBody(_.Board.Play) { implicit req => me => cmd.split('/') match { - case Array("game", id, "chat") => - WithPovAsBoard(id, me) { pov => - env.chat.api.userChat.find(lila.chat.Chat.Id(pov.game.id)) map - lila.chat.JsonView.boardApi map JsonOk - } - case _ => notFoundJson("No such command") + case Array("game", id, "chat") => WithPovAsBoard(id, me)(getChat) + case _ => notFoundJson("No such command") } } + def botCommandGet(cmd: String) = + ScopedBody(_.Bot.Play) { implicit req => me => + cmd.split('/') match { + case Array("game", id, "chat") => WithPovAsBot(id, me)(getChat) + case _ => notFoundJson("No such command") + } + } + + private def getChat(pov: Pov) = + env.chat.api.userChat.find(lila.chat.Chat.Id(pov.game.id)) map lila.chat.JsonView.boardApi map JsonOk + // utils private def toResult(f: Funit): Fu[Result] = catchClientError(f inject jsonOkResult) diff --git a/conf/routes b/conf/routes index 4d5236e407..3f72505716 100644 --- a/conf/routes +++ b/conf/routes @@ -708,6 +708,7 @@ GET /api/user/:name/games controllers.Api.userGames(name: String) GET /api/bot/game/stream/:id controllers.PlayApi.botGameStream(id: String) POST /api/bot/game/:id/move/:uci controllers.PlayApi.botMove(id: String, uci: String, offeringDraw: Option[Boolean] ?= None) POST /api/bot/*cmd controllers.PlayApi.botCommand(cmd: String) +GET /api/bot/*cmd controllers.PlayApi.botCommandGet(cmd: String) GET /player/bots controllers.PlayApi.botOnline GET /api/bot/online controllers.PlayApi.botOnlineApi From f9bf2dcd5308fbc7c082f70d53ce860ae999825b Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 18 Nov 2021 11:42:11 +0100 Subject: [PATCH 062/144] stop hitting the opening explorer after the AI games goes out of book --- .../fishnet/src/main/FishnetOpeningBook.scala | 71 ++++++++++--------- 1 file changed, 39 insertions(+), 32 deletions(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index 81a2f538e2..c86dcb1030 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -8,6 +8,7 @@ import play.api.libs.json._ import play.api.libs.ws.JsonBodyReadables._ import play.api.libs.ws.StandaloneWSClient import scala.concurrent.ExecutionContext +import scala.concurrent.duration._ import lila.common.Json.uciReader import lila.common.ThreadLocalRandom @@ -22,38 +23,44 @@ final private class FishnetOpeningBook( import FishnetOpeningBook._ - def apply(game: Game, level: Int): Fu[Option[Uci]] = (game.turns < depth.get()) ?? { - ws.url(endpoint) - .withQueryStringParameters( - "variant" -> game.variant.key, - "fen" -> Forsyth.>>(game.chess).value, - "topGames" -> "0", - "recentGames" -> "0", - "ratings" -> (~levelRatings.get(level)).mkString(","), - "speeds" -> (~openingSpeeds.get(game.speed)).map(_.key).mkString(",") - ) - .get() - .map { - case res if res.status != 200 => - logger.warn(s"opening book ${game.id} ${level} ${res.status} ${res.body}") - none - case res => - for { - data <- res.body[JsValue].validate[Response](responseReader).asOpt - move <- data.randomPonderedMove - } yield move.uci - } - .monTry { res => - _.fishnet - .openingBook( - level = level, - variant = game.variant.key, - ply = game.turns, - hit = res.toOption.exists(_.isDefined), - success = res.isSuccess - ) - } - } + private val outOfBook = new lila.memo.ExpireSetMemo(10 minutes) + + def apply(game: Game, level: Int): Fu[Option[Uci]] = + (game.turns < depth.get() && !outOfBook.get(game.id)) ?? { + ws.url(endpoint) + .withQueryStringParameters( + "variant" -> game.variant.key, + "fen" -> Forsyth.>>(game.chess).value, + "topGames" -> "0", + "recentGames" -> "0", + "ratings" -> (~levelRatings.get(level)).mkString(","), + "speeds" -> (~openingSpeeds.get(game.speed)).map(_.key).mkString(",") + ) + .get() + .map { + case res if res.status != 200 => + logger.warn(s"opening book ${game.id} ${level} ${res.status} ${res.body}") + none + case res => + for { + data <- res.body[JsValue].validate[Response](responseReader).asOpt + move <- data.randomPonderedMove + } yield move.uci + } + .addEffect { uci => + if (uci.isEmpty) outOfBook.put(game.id) + } + .monTry { res => + _.fishnet + .openingBook( + level = level, + variant = game.variant.key, + ply = game.turns, + hit = res.toOption.exists(_.isDefined), + success = res.isSuccess + ) + } + } } object FishnetOpeningBook { From 3fca7343e2f30b0c92a3290a86a028de5a6eb014 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 18 Nov 2021 11:48:32 +0100 Subject: [PATCH 063/144] fix AI play opening novelty doesn't mean it's out of book --- modules/fishnet/src/main/FishnetOpeningBook.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index c86dcb1030..172af35134 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -45,10 +45,10 @@ final private class FishnetOpeningBook( for { data <- res.body[JsValue].validate[Response](responseReader).asOpt move <- data.randomPonderedMove - } yield move.uci - } - .addEffect { uci => - if (uci.isEmpty) outOfBook.put(game.id) + } yield { + if (data.moves.isEmpty) outOfBook.put(game.id) + move.uci + } } .monTry { res => _.fishnet From 830fedd11a92c4f6de1e41aee00f2fd3a32c401a Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 18 Nov 2021 15:05:28 +0100 Subject: [PATCH 064/144] prevent markdown stackoverflow --- modules/common/src/main/Markdown.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/common/src/main/Markdown.scala b/modules/common/src/main/Markdown.scala index b837917388..6384ca4a5a 100644 --- a/modules/common/src/main/Markdown.scala +++ b/modules/common/src/main/Markdown.scala @@ -60,11 +60,14 @@ final class Markdown( private def mentionsToLinks(markdown: Text): Text = RawHtml.atUsernameRegex.replaceAllIn(markdown, "[$1](/@/$1)") + private val tooManyUnderscoreRegex = """(_{4,})""".r + private def preventStackOverflow(text: String) = tooManyUnderscoreRegex.replaceAllIn(text, "_" * 3) + def apply(key: Key)(text: Text): Html = Chronometer .sync { try { - addLinkAttributes(renderer.render(parser.parse(mentionsToLinks(text)))) + addLinkAttributes(renderer.render(parser.parse(mentionsToLinks(preventStackOverflow(text))))) } catch { case e: StackOverflowError => logger.branch(key).error("StackOverflowError", e) From 0e49e0e67975bdcd588c66afcfcc6af5eb74af4c Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Thu, 18 Nov 2021 18:44:23 +0100 Subject: [PATCH 065/144] Update tagging to 2.3.2 --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index e419d714ef..4f5be86ade 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -38,7 +38,7 @@ object Dependencies { val version = "2.4.2" val macros = "com.softwaremill.macwire" %% "macros" % version % "provided" val util = "com.softwaremill.macwire" %% "util" % version % "provided" - val tagging = "com.softwaremill.common" %% "tagging" % "2.3.1" + val tagging = "com.softwaremill.common" %% "tagging" % "2.3.2" def bundle = Seq(macros, util, tagging) } From e206b2e8adcbbdc98d3902c4ee423e4645e3e0d7 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Fri, 19 Nov 2021 05:30:42 +0100 Subject: [PATCH 066/144] Update sbt-scalafmt to 2.4.4 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 9795a8de6c..3832063891 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,5 +3,5 @@ resolvers += Resolver.url( url("https://raw.githubusercontent.com/ornicar/lila-maven/master") )(Resolver.ivyStylePatterns) addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.8-lila_1.8") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.3") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.4") addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.11") From dbac4b9c1a865bada3372886881660c71e3d06b6 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 19 Nov 2021 08:42:40 +0100 Subject: [PATCH 067/144] use empty prismic repo by default to avoid lichess clones copying our blog and content --- conf/base.conf | 4 +--- modules/blog/src/main/BlogApi.scala | 10 ++++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/conf/base.conf b/conf/base.conf index 5f82fb52be..0c988e8cca 100644 --- a/conf/base.conf +++ b/conf/base.conf @@ -108,9 +108,7 @@ pagerDuty { serviceId = "" apiKey = "" } -prismic { - api_url = "https://lichess.cdn.prismic.io/api" -} +prismic.api_url = "https://lichess-clone.cdn.prismic.io/api" blog { prismic = ${prismic} collection = blog diff --git a/modules/blog/src/main/BlogApi.scala b/modules/blog/src/main/BlogApi.scala index 6fbade9a40..e53163eb03 100644 --- a/modules/blog/src/main/BlogApi.scala +++ b/modules/blog/src/main/BlogApi.scala @@ -6,6 +6,7 @@ import play.api.libs.ws.StandaloneWSClient import lila.common.config.MaxPerPage import lila.common.paginator._ +import scala.util.Try final class BlogApi( config: BlogConfig @@ -20,7 +21,7 @@ final class BlogApi( page: Int, maxPerPage: MaxPerPage, ref: Option[String] - ): Fu[Option[Paginator[Document]]] = + ): Fu[Option[Paginator[Document]]] = Try { api .forms(collection) .ref(ref | api.master.ref) @@ -30,6 +31,9 @@ final class BlogApi( .submit() .fold(_ => none, some) .dmap2 { PrismicPaginator(_, page, maxPerPage) } + } recover { case _: NoSuchElementException => + fuccess(none) + } get def recent( prismic: BlogApi.Context, @@ -94,9 +98,7 @@ final class BlogApi( } getOrElse reqRef } - private val prismicBuilder = new Prismic - - def prismicApi = prismicBuilder.get(config.apiUrl) + def prismicApi = (new Prismic).get(config.apiUrl) } object BlogApi { From 2b71a6b81f11a8e11a57287250dcd1887ca31d9a Mon Sep 17 00:00:00 2001 From: Enrique Betancourt Date: Fri, 19 Nov 2021 02:12:12 -0600 Subject: [PATCH 068/144] replace +/#/! characters on PGN title --- ui/analyse/src/wiki.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index bb3bc1a0c6..9a4310d0f9 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -31,8 +31,9 @@ export default function wikiTheory(): WikiTheory { return debounce( async (nodes: Tree.Node[]) => { const pathParts = nodes.slice(1).map(n => `${plyPrefix(n)}${n.san}`); - const path = pathParts.join('/'); + const path = pathParts.join('/').replace(/[+!#]/g,'') ?? ''; if (pathParts.length > 30 || !path || path.length > 255) show(''); + if (!path) show(''); else if (cache.has(path)) show(cache.get(path)!); else if ( Array.from({ length: pathParts.length }, (_, i) => -i - 1) From d36722c02447b2c02bf8d3a694e42caf10deb300 Mon Sep 17 00:00:00 2001 From: Enrique Betancourt Date: Fri, 19 Nov 2021 02:31:20 -0600 Subject: [PATCH 069/144] including ? character --- ui/analyse/src/wiki.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index 9a4310d0f9..c35d3b29b9 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -31,7 +31,7 @@ export default function wikiTheory(): WikiTheory { return debounce( async (nodes: Tree.Node[]) => { const pathParts = nodes.slice(1).map(n => `${plyPrefix(n)}${n.san}`); - const path = pathParts.join('/').replace(/[+!#]/g,'') ?? ''; + const path = pathParts.join('/').replace(/[+!#?]/g,'') ?? ''; if (pathParts.length > 30 || !path || path.length > 255) show(''); if (!path) show(''); else if (cache.has(path)) show(cache.get(path)!); From edbf998c60559cec0ae9c744a21d738322526a95 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 19 Nov 2021 09:45:57 +0100 Subject: [PATCH 070/144] use Option.exists - after #10134 --- modules/plan/src/main/PlanApi.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/plan/src/main/PlanApi.scala b/modules/plan/src/main/PlanApi.scala index 152a94aa69..512e29ce5b 100644 --- a/modules/plan/src/main/PlanApi.scala +++ b/modules/plan/src/main/PlanApi.scala @@ -284,7 +284,7 @@ final class PlanApi( def gift(from: User, to: User, money: Money): Funit = for { toPatronOpt <- userPatron(to) - isLifetime <- fuccess(~toPatronOpt.map(_.isLifetime)) >>| (pricingApi isLifetime money) + isLifetime <- fuccess(toPatronOpt.exists(_.isLifetime)) >>| (pricingApi isLifetime money) _ <- patronColl.update .one( $id(to.id), @@ -296,7 +296,7 @@ final class PlanApi( ), upsert = true ) - newTo = to.mapPlan(p => if (~toPatronOpt.map(_.canLevelUp)) p.incMonths else p.enable) + newTo = to.mapPlan(p => if (toPatronOpt.exists(_.canLevelUp)) p.incMonths else p.enable) _ <- setDbUserPlan(newTo) } yield { notifier.onGift(from, newTo, isLifetime) From a0c13389ce79fa559e194f04b6a9025e676a5e0c Mon Sep 17 00:00:00 2001 From: Giacomo Lorenzetti Date: Fri, 19 Nov 2021 11:25:44 +0100 Subject: [PATCH 071/144] Show games in perfstat from player's perspective --- app/views/user/perfStat.scala | 2 +- modules/perfStat/src/main/PerfStat.scala | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/app/views/user/perfStat.scala b/app/views/user/perfStat.scala index 62441a7591..6fbf6d80b2 100644 --- a/app/views/user/perfStat.scala +++ b/app/views/user/perfStat.scala @@ -271,7 +271,7 @@ object perfStat { results.results map { r => tr( td(userIdLink(r.opId.value.some, withOnline = false), " (", r.opInt, ")"), - td(a(cls := "glpt", href := routes.Round.watcher(r.gameId, "white"))(absClientDateTime(r.at))) + td(a(cls := "glpt", href := routes.Round.watcher(r.gameId, r.color))(absClientDateTime(r.at))) ) } ) diff --git a/modules/perfStat/src/main/PerfStat.scala b/modules/perfStat/src/main/PerfStat.scala index c212662932..799cc30372 100644 --- a/modules/perfStat/src/main/PerfStat.scala +++ b/modules/perfStat/src/main/PerfStat.scala @@ -196,7 +196,7 @@ object RatingAt { } orElse cur } -case class Result(opInt: Int, opId: UserId, at: DateTime, gameId: String) +case class Result(opInt: Int, opId: UserId, at: DateTime, gameId: String, color: String) case class Results(results: List[Result]) extends AnyVal { def agg(pov: Pov, comp: Int) = @@ -210,7 +210,8 @@ case class Results(results: List[Result]) extends AnyVal { opInt, UserId(~pov.opponent.userId), pov.game.movedAt, - pov.gameId + pov.gameId, + if (pov.player.color == chess.White) "white" else "black" ) :: results, Results.nb, Ordering.by[Result, Int](_.opInt * comp) From ba54e1f27fb687ad635d842c2de0716488433da3 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 19 Nov 2021 11:30:55 +0100 Subject: [PATCH 072/144] cache lastPlayedPlaying game ID for longer --- modules/game/src/main/Cached.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/game/src/main/Cached.scala b/modules/game/src/main/Cached.scala index 5e9a45bf8c..ad2b9888e1 100644 --- a/modules/game/src/main/Cached.scala +++ b/modules/game/src/main/Cached.scala @@ -24,7 +24,7 @@ final class Cached( private val lastPlayedPlayingIdCache: LoadingCache[User.ID, Fu[Option[Game.ID]]] = CacheApi.scaffeineNoScheduler - .expireAfterWrite(5 seconds) + .expireAfterWrite(11 seconds) .build(gameRepo.lastPlayedPlayingId) lila.common.Bus.subscribeFun("startGame") { case lila.game.actorApi.StartGame(game) => From 5a7aff0eb223a2aba1623a31b8f6d2cd9a3bae73 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 19 Nov 2021 11:32:15 +0100 Subject: [PATCH 073/144] add withGameIds flag to /api/users/status endpoint --- app/controllers/Api.scala | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/app/controllers/Api.scala b/app/controllers/Api.scala index 49b1e96f7b..50009ca937 100644 --- a/app/controllers/Api.scala +++ b/app/controllers/Api.scala @@ -10,6 +10,7 @@ import lila.api.{ Context, GameApiV2 } import lila.app._ import lila.common.config.{ MaxPerPage, MaxPerSecond } import lila.common.{ HTTPRequest, IpAddress } +import lila.common.LightUser final class Api( env: Env, @@ -74,17 +75,20 @@ final class Api( def usersStatus = ApiRequest { req => val ids = get("ids", req).??(_.split(',').take(100).toList map lila.user.User.normalize) - env.user.lightUserApi asyncMany ids dmap (_.flatten) map { users => + env.user.lightUserApi asyncMany ids dmap (_.flatten) flatMap { users => val streamingIds = env.streamer.liveStreamApi.userIds - toApiResult { - users.map { u => - lila.common.LightUser.lightUserWrites - .writes(u) - .add("online" -> env.socket.isOnline(u.id)) - .add("playing" -> env.round.playing(u.id)) - .add("streaming" -> streamingIds(u.id)) + def toJson(u: LightUser) = + lila.common.LightUser.lightUserWrites + .writes(u) + .add("online" -> env.socket.isOnline(u.id)) + .add("playing" -> env.round.playing(u.id)) + .add("streaming" -> streamingIds(u.id)) + if (getBool("withGameIds", req)) users.map { u => + (env.round.playing(u.id) ?? env.game.cached.lastPlayedPlayingId(u.id)) map { gameId => + toJson(u).add("playingId", gameId) } - } + }.sequenceFu map toApiResult + else fuccess(toApiResult(users map toJson)) } } From 067dc38087ea564bf6669706ca564329eb22d480 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 19 Nov 2021 11:38:18 +0100 Subject: [PATCH 074/144] yarn format --- ui/analyse/src/wiki.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index c35d3b29b9..151de1d3d6 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -31,7 +31,7 @@ export default function wikiTheory(): WikiTheory { return debounce( async (nodes: Tree.Node[]) => { const pathParts = nodes.slice(1).map(n => `${plyPrefix(n)}${n.san}`); - const path = pathParts.join('/').replace(/[+!#?]/g,'') ?? ''; + const path = pathParts.join('/').replace(/[+!#?]/g, '') ?? ''; if (pathParts.length > 30 || !path || path.length > 255) show(''); if (!path) show(''); else if (cache.has(path)) show(cache.get(path)!); From 0e80050dbf5eaecb82793a93b5ac744d1e8d681b Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 20 Nov 2021 06:10:31 +0100 Subject: [PATCH 075/144] Remove duplicate null check - after #10140 --- ui/analyse/src/wiki.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index 151de1d3d6..b818a90673 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -33,7 +33,6 @@ export default function wikiTheory(): WikiTheory { const pathParts = nodes.slice(1).map(n => `${plyPrefix(n)}${n.san}`); const path = pathParts.join('/').replace(/[+!#?]/g, '') ?? ''; if (pathParts.length > 30 || !path || path.length > 255) show(''); - if (!path) show(''); else if (cache.has(path)) show(cache.get(path)!); else if ( Array.from({ length: pathParts.length }, (_, i) => -i - 1) From 850f7f0324658fc465cc93f7da5a4a8b135f7a72 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 20 Nov 2021 07:32:25 +0100 Subject: [PATCH 076/144] Fix fishnet book player ply in FromPosition --- modules/fishnet/src/main/FishnetPlayer.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/fishnet/src/main/FishnetPlayer.scala b/modules/fishnet/src/main/FishnetPlayer.scala index 348ba36f73..7c921ef8f4 100644 --- a/modules/fishnet/src/main/FishnetPlayer.scala +++ b/modules/fishnet/src/main/FishnetPlayer.scala @@ -26,7 +26,7 @@ final class FishnetPlayer( openingBook(game, level) flatMap { case Some(move) => fuccess { - Bus.publish(Tell(game.id, FishnetPlay(move, game.turns)), "roundSocket") + Bus.publish(Tell(game.id, FishnetPlay(move, game.playedTurns)), "roundSocket") } case None => makeWork(game, level) addEffect redis.request void } From 14828e847807d0f325323a4d8508bb1b9d3ded25 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sat, 20 Nov 2021 09:51:26 +0100 Subject: [PATCH 077/144] use proper Color type and reuse json handlers --- app/views/user/perfStat.scala | 6 +++++- modules/activity/src/main/JsonView.scala | 1 - modules/api/src/main/GameApi.scala | 2 +- modules/api/src/main/GameApiV2.scala | 2 +- modules/bot/src/main/BotJsonView.scala | 1 + modules/challenge/src/main/JsonView.scala | 5 +++-- modules/common/src/main/Json.scala | 9 ++++++++- modules/fishnet/src/main/FishnetOpeningBook.scala | 2 +- modules/game/src/main/Event.scala | 1 + modules/game/src/main/JsonView.scala | 10 +--------- modules/perfStat/src/main/JsonView.scala | 1 + modules/perfStat/src/main/PerfStat.scala | 5 +++-- modules/round/src/main/JsonView.scala | 1 + modules/simul/src/main/JsonView.scala | 5 +---- modules/study/src/main/JsonView.scala | 9 --------- modules/study/src/main/StudyMultiBoard.scala | 2 ++ 16 files changed, 30 insertions(+), 32 deletions(-) diff --git a/app/views/user/perfStat.scala b/app/views/user/perfStat.scala index 6fbf6d80b2..aa59f156e5 100644 --- a/app/views/user/perfStat.scala +++ b/app/views/user/perfStat.scala @@ -271,7 +271,11 @@ object perfStat { results.results map { r => tr( td(userIdLink(r.opId.value.some, withOnline = false), " (", r.opInt, ")"), - td(a(cls := "glpt", href := routes.Round.watcher(r.gameId, r.color))(absClientDateTime(r.at))) + td( + a(cls := "glpt", href := routes.Round.watcher(r.gameId, r.color.name))( + absClientDateTime(r.at) + ) + ) ) } ) diff --git a/modules/activity/src/main/JsonView.scala b/modules/activity/src/main/JsonView.scala index 50595d060f..e8c151cd5f 100644 --- a/modules/activity/src/main/JsonView.scala +++ b/modules/activity/src/main/JsonView.scala @@ -6,7 +6,6 @@ import play.api.libs.json._ import lila.common.Iso import lila.common.Json._ -import lila.game.JsonView.colorWrites import lila.game.LightPov import lila.rating.PerfType import lila.simul.Simul diff --git a/modules/api/src/main/GameApi.scala b/modules/api/src/main/GameApi.scala index 3226de6426..c254507791 100644 --- a/modules/api/src/main/GameApi.scala +++ b/modules/api/src/main/GameApi.scala @@ -8,7 +8,7 @@ import reactivemongo.api.ReadPreference import lila.analyse.{ JsonView => analysisJson, Analysis } import lila.common.config._ -import lila.common.Json.jodaWrites +import lila.common.Json._ import lila.common.paginator.{ Paginator, PaginatorJson } import lila.db.dsl._ import lila.db.paginator.Adapter diff --git a/modules/api/src/main/GameApiV2.scala b/modules/api/src/main/GameApiV2.scala index 43693d81d8..6f038fd463 100644 --- a/modules/api/src/main/GameApiV2.scala +++ b/modules/api/src/main/GameApiV2.scala @@ -9,7 +9,7 @@ import scala.concurrent.duration._ import lila.analyse.{ JsonView => analysisJson, Analysis } import lila.common.config.MaxPerSecond -import lila.common.Json.jodaWrites +import lila.common.Json._ import lila.common.{ HTTPRequest, LightUser } import lila.db.dsl._ import lila.game.JsonView._ diff --git a/modules/bot/src/main/BotJsonView.scala b/modules/bot/src/main/BotJsonView.scala index 2f42fbf4ef..bc80dc65ca 100644 --- a/modules/bot/src/main/BotJsonView.scala +++ b/modules/bot/src/main/BotJsonView.scala @@ -3,6 +3,7 @@ package lila.bot import play.api.i18n.Lang import play.api.libs.json._ +import lila.common.Json._ import lila.common.Json.jodaWrites import lila.game.JsonView._ import lila.game.{ Game, GameRepo, Pov } diff --git a/modules/challenge/src/main/JsonView.scala b/modules/challenge/src/main/JsonView.scala index 3401b188d3..814ccf5ef3 100644 --- a/modules/challenge/src/main/JsonView.scala +++ b/modules/challenge/src/main/JsonView.scala @@ -1,8 +1,10 @@ package lila.challenge -import play.api.libs.json._ import play.api.i18n.Lang +import play.api.libs.json._ +import lila.common.Json._ +import lila.game.JsonView._ import lila.i18n.{ I18nKeys => trans } import lila.socket.Socket.SocketVersion import lila.socket.UserLagCache @@ -13,7 +15,6 @@ final class JsonView( isOnline: lila.socket.IsOnline ) { - import lila.game.JsonView._ import Challenge._ implicit private val RegisteredWrites = OWrites[Challenger.Registered] { r => diff --git a/modules/common/src/main/Json.scala b/modules/common/src/main/Json.scala index 98167b21ce..41d0027c74 100644 --- a/modules/common/src/main/Json.scala +++ b/modules/common/src/main/Json.scala @@ -41,9 +41,16 @@ object Json { JsNumber(time.getMillis) } + implicit val colorWrites: Writes[chess.Color] = Writes { c => + JsString(c.name) + } + implicit val fenFormat: Format[FEN] = stringIsoFormat[FEN](Iso.fenIso) - implicit val uciReader: Reads[Uci] = Reads.of[String] flatMapResult { str => + implicit val uciReads: Reads[Uci] = Reads.of[String] flatMapResult { str => JsResult.fromTry(Uci(str) toTry s"Invalid UCI: $str") } + implicit val uciWrites: Writes[Uci] = Writes { u => + JsString(u.uci) + } } diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index 172af35134..cd7a39b1b3 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -10,7 +10,7 @@ import play.api.libs.ws.StandaloneWSClient import scala.concurrent.ExecutionContext import scala.concurrent.duration._ -import lila.common.Json.uciReader +import lila.common.Json._ import lila.common.ThreadLocalRandom import lila.game.Game import lila.memo.SettingStore diff --git a/modules/game/src/main/Event.scala b/modules/game/src/main/Event.scala index 9c97ed0551..365c956ca8 100644 --- a/modules/game/src/main/Event.scala +++ b/modules/game/src/main/Event.scala @@ -17,6 +17,7 @@ import chess.{ import JsonView._ import lila.chat.{ PlayerLine, UserLine } import lila.common.ApiVersion +import lila.common.Json._ sealed trait Event { def typ: String diff --git a/modules/game/src/main/JsonView.scala b/modules/game/src/main/JsonView.scala index ac32290eef..a20f80564c 100644 --- a/modules/game/src/main/JsonView.scala +++ b/modules/game/src/main/JsonView.scala @@ -5,7 +5,7 @@ import play.api.libs.json._ import chess.format.{ FEN, Forsyth } import chess.variant.Crazyhouse import chess.{ Clock, Color } -import lila.common.Json.jodaWrites +import lila.common.Json._ final class JsonView(rematches: Rematches) { @@ -143,12 +143,4 @@ object JsonView { implicit val sourceWriter: Writes[Source] = Writes { s => JsString(s.name) } - - implicit val colorWrites: Writes[Color] = Writes { c => - JsString(c.name) - } - - implicit val fenWrites: Writes[FEN] = Writes { f => - JsString(f.value) - } } diff --git a/modules/perfStat/src/main/JsonView.scala b/modules/perfStat/src/main/JsonView.scala index a321865bb7..c273eb4bfe 100644 --- a/modules/perfStat/src/main/JsonView.scala +++ b/modules/perfStat/src/main/JsonView.scala @@ -5,6 +5,7 @@ import org.joda.time.format.ISODateTimeFormat import play.api.i18n.Lang import play.api.libs.json._ +import lila.common.Json.{ jodaWrites => _, _ } import lila.common.LightUser import lila.rating.{ Glicko, Perf, PerfType } import lila.user.User diff --git a/modules/perfStat/src/main/PerfStat.scala b/modules/perfStat/src/main/PerfStat.scala index 799cc30372..6cf9cdea14 100644 --- a/modules/perfStat/src/main/PerfStat.scala +++ b/modules/perfStat/src/main/PerfStat.scala @@ -1,5 +1,6 @@ package lila.perfStat +import chess.Color import org.joda.time.{ DateTime, Period } import lila.common.Heapsort @@ -196,7 +197,7 @@ object RatingAt { } orElse cur } -case class Result(opInt: Int, opId: UserId, at: DateTime, gameId: String, color: String) +case class Result(opInt: Int, opId: UserId, at: DateTime, gameId: String, color: Color) case class Results(results: List[Result]) extends AnyVal { def agg(pov: Pov, comp: Int) = @@ -211,7 +212,7 @@ case class Results(results: List[Result]) extends AnyVal { UserId(~pov.opponent.userId), pov.game.movedAt, pov.gameId, - if (pov.player.color == chess.White) "white" else "black" + pov.player.color ) :: results, Results.nb, Ordering.by[Result, Int](_.opInt * comp) diff --git a/modules/round/src/main/JsonView.scala b/modules/round/src/main/JsonView.scala index 688ab39124..adf47dbd87 100644 --- a/modules/round/src/main/JsonView.scala +++ b/modules/round/src/main/JsonView.scala @@ -7,6 +7,7 @@ import play.api.libs.json._ import scala.math import lila.common.ApiVersion +import lila.common.Json._ import lila.game.JsonView._ import lila.game.{ Pov, Game, Player => GamePlayer } import lila.pref.Pref diff --git a/modules/simul/src/main/JsonView.scala b/modules/simul/src/main/JsonView.scala index 9c45b4be6e..f2904d0327 100644 --- a/modules/simul/src/main/JsonView.scala +++ b/modules/simul/src/main/JsonView.scala @@ -3,6 +3,7 @@ package lila.simul import play.api.libs.json._ import lila.common.LightUser +import lila.common.Json._ import lila.game.{ Game, GameRepo } import lila.user.User @@ -12,10 +13,6 @@ final class JsonView( proxyRepo: lila.round.GameProxyRepo )(implicit ec: scala.concurrent.ExecutionContext) { - implicit private val colorWriter: Writes[chess.Color] = Writes { c => - JsString(c.name) - } - implicit private val simulTeamWriter = Json.writes[SimulTeam] private def fetchGames(simul: Simul) = diff --git a/modules/study/src/main/JsonView.scala b/modules/study/src/main/JsonView.scala index 7494ab5238..d62f78c687 100644 --- a/modules/study/src/main/JsonView.scala +++ b/modules/study/src/main/JsonView.scala @@ -127,21 +127,12 @@ object JsonView { implicit val chapterIdWrites: Writes[Chapter.Id] = stringIsoWriter(Chapter.idIso) implicit val chapterNameWrites: Writes[Chapter.Name] = stringIsoWriter(Chapter.nameIso) - implicit private[study] val uciWrites: Writes[Uci] = Writes[Uci] { u => - JsString(u.uci) - } implicit private val posReader: Reads[Pos] = Reads[Pos] { v => (v.asOpt[String] flatMap Pos.fromKey).fold[JsResult[Pos]](JsError(Nil))(JsSuccess(_)) } implicit private[study] val pathWrites: Writes[Path] = Writes[Path] { p => JsString(p.toString) } - implicit private[study] val colorWriter: Writes[chess.Color] = Writes[chess.Color] { c => - JsString(c.name) - } - implicit private[study] val fenWriter: Writes[FEN] = Writes[FEN] { f => - JsString(f.value) - } implicit private[study] val sriWriter: Writes[Sri] = Writes[Sri] { sri => JsString(sri.value) } diff --git a/modules/study/src/main/StudyMultiBoard.scala b/modules/study/src/main/StudyMultiBoard.scala index 72b1b1539a..8ca57ea3c3 100644 --- a/modules/study/src/main/StudyMultiBoard.scala +++ b/modules/study/src/main/StudyMultiBoard.scala @@ -106,6 +106,8 @@ final class StudyMultiBoard( private object handlers { + import lila.common.Json._ + implicit val previewPlayerWriter: Writes[ChapterPreview.Player] = Writes[ChapterPreview.Player] { p => Json .obj("name" -> p.name) From 27cdc271ecbccbe7272ddb170e5195cb8c3c9351 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 20 Nov 2021 16:55:44 +0100 Subject: [PATCH 078/144] master db updated internal source: master-update-2021-11-20.pgn deterministic ids: niklasf/lila-openingexplorer@021dc59a7b8f77 --- ui/analyse/src/explorer/explorerView.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/analyse/src/explorer/explorerView.ts b/ui/analyse/src/explorer/explorerView.ts index b95ee6ab04..47ba17d236 100644 --- a/ui/analyse/src/explorer/explorerView.ts +++ b/ui/analyse/src/explorer/explorerView.ts @@ -358,7 +358,7 @@ const explorerTitle = (explorer: ExplorerCtrl) => { nodes ); const playerName = explorer.config.data.playerName.value(); - const masterDbExplanation = explorer.root.trans('masterDbExplanation', 2200, '1952', '2020'), + const masterDbExplanation = explorer.root.trans('masterDbExplanation', 2200, '1952', '2021'), lichessDbExplanation = 'Rated games sampled from all Lichess players'; return h('div.explorer-title', [ db == 'masters' From bc7b9c8952cd43ded33eed5ccce5e7ed92af93cd Mon Sep 17 00:00:00 2001 From: Albert Ford Date: Sat, 20 Nov 2021 09:14:46 -0800 Subject: [PATCH 079/144] Don't animate spinner motion according to prefer-reduced-motion --- ui/common/css/component/_loader.scss | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/ui/common/css/component/_loader.scss b/ui/common/css/component/_loader.scss index dd2a3f6d73..d1dd3b51e0 100644 --- a/ui/common/css/component/_loader.scss +++ b/ui/common/css/component/_loader.scss @@ -78,6 +78,12 @@ $total_len: $len1 + $len2 + $len3; animation-name: mask3; } + @media (prefers-reduced-motion: reduce) { + path { + animation: none !important; + } + } + g { animation: spinner-color 11s steps(1) infinite; stroke-dasharray: $total_len $total_len; From 5584a2de6aaf6a54dc7cc893c47348f439fd8d61 Mon Sep 17 00:00:00 2001 From: Sabrina Tseng Date: Sat, 20 Nov 2021 19:50:08 -0800 Subject: [PATCH 080/144] Hide ratings when importing game to existing study if option selected --- app/controllers/Study.scala | 3 ++- modules/study/src/main/ChapterMaker.scala | 9 ++++---- modules/study/src/main/StudyApi.scala | 25 ++++++++++++++++------- modules/study/src/main/StudySocket.scala | 2 +- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/app/controllers/Study.scala b/app/controllers/Study.scala index cf5e21e83d..4c496a9127 100644 --- a/app/controllers/Study.scala +++ b/app/controllers/Study.scala @@ -328,7 +328,8 @@ final class Study( env.study.api.importPgns( StudyModel.Id(id), data.toChapterDatas, - sticky = data.sticky + sticky = data.sticky, + ctx.pref.showRatings )(Who(me.id, lila.socket.Socket.Sri(sri))) ) } diff --git a/modules/study/src/main/ChapterMaker.scala b/modules/study/src/main/ChapterMaker.scala index d2ca3e969a..248f214af3 100644 --- a/modules/study/src/main/ChapterMaker.scala +++ b/modules/study/src/main/ChapterMaker.scala @@ -19,14 +19,14 @@ final private class ChapterMaker( import ChapterMaker._ - def apply(study: Study, data: Data, order: Int, userId: User.ID): Fu[Chapter] = + def apply(study: Study, data: Data, order: Int, userId: User.ID, withRatings: Boolean): Fu[Chapter] = data.game.??(parseGame) flatMap { case None => data.game ?? pgnFetch.fromUrl flatMap { case Some(pgn) => fromFenOrPgnOrBlank(study, data.copy(pgn = pgn.some), order, userId) case _ => fromFenOrPgnOrBlank(study, data, order, userId) } - case Some(game) => fromGame(study, game, data, order, userId) + case Some(game) => fromGame(study, game, data, order, userId, withRatings) } map { (c: Chapter) => if (c.name.value.isEmpty) c.copy(name = Chapter defaultName order) else c } @@ -125,14 +125,15 @@ final private class ChapterMaker( data: Data, order: Int, userId: User.ID, + withRatings: Boolean, initialFen: Option[FEN] = None ): Fu[Chapter] = for { root <- game2root(game, initialFen) - tags <- pgnDump.tags(game, initialFen, none, withOpening = true, withRating = true) + tags <- pgnDump.tags(game, initialFen, none, withOpening = true, withRatings) name <- { if (data.isDefaultName) - Namer.gameVsText(game, withRatings = false)(lightUser.async) dmap Chapter.Name.apply + Namer.gameVsText(game, withRatings)(lightUser.async) dmap Chapter.Name.apply else fuccess(data.name) } _ = notifyChat(study, game, userId) diff --git a/modules/study/src/main/StudyApi.scala b/modules/study/src/main/StudyApi.scala index df05b134c2..7c5a143722 100644 --- a/modules/study/src/main/StudyApi.scala +++ b/modules/study/src/main/StudyApi.scala @@ -132,7 +132,8 @@ final class StudyApi( addChapter( studyId = study.id, data = data.form.toChapterData, - sticky = study.settings.sticky + sticky = study.settings.sticky, + withRatings )(Who(user.id, Sri(""))) >> byIdWithLastChapter(studyId) case _ => fuccess(none) } orElse importGame(data.copy(form = data.form.copy(asStr = none)), user, withRatings) @@ -587,11 +588,13 @@ final class StudyApi( } } - def addChapter(studyId: Study.Id, data: ChapterMaker.Data, sticky: Boolean)(who: Who): Funit = + def addChapter(studyId: Study.Id, data: ChapterMaker.Data, sticky: Boolean, withRatings: Boolean)( + who: Who + ): Funit = data.manyGames match { case Some(datas) => lila.common.Future.applySequentially(datas) { data => - addChapter(studyId, data, sticky)(who) + addChapter(studyId, data, sticky, withRatings)(who) } case _ => sequenceStudy(studyId) { study => @@ -605,7 +608,7 @@ final class StudyApi( } } >> chapterRepo.nextOrderByStudy(study.id) flatMap { order => - chapterMaker(study, data, order, who.u) flatMap { chapter => + chapterMaker(study, data, order, who.u, withRatings) flatMap { chapter => doAddChapter(study, chapter, sticky, who) } addFailureEffect { case ChapterMaker.ValidationException(error) => @@ -624,9 +627,11 @@ final class StudyApi( studyRepo.updateSomeFields(study) >>- indexStudy(study) } - def importPgns(studyId: Study.Id, datas: List[ChapterMaker.Data], sticky: Boolean)(who: Who) = + def importPgns(studyId: Study.Id, datas: List[ChapterMaker.Data], sticky: Boolean, withRatings: Boolean)( + who: Who + ) = lila.common.Future.applySequentially(datas) { data => - addChapter(studyId, data, sticky)(who) + addChapter(studyId, data, sticky, withRatings)(who) } def doAddChapter(study: Study, chapter: Chapter, sticky: Boolean, who: Who) = @@ -731,7 +736,13 @@ final class StudyApi( chapterRepo.orderedMetadataByStudy(studyId).flatMap { chaps => // deleting the only chapter? Automatically create an empty one if (chaps.sizeIs < 2) { - chapterMaker(study, ChapterMaker.Data(Chapter.Name("Chapter 1")), 1, who.u) flatMap { c => + chapterMaker( + study, + ChapterMaker.Data(Chapter.Name("Chapter 1")), + 1, + who.u, + withRatings = true + ) flatMap { c => doAddChapter(study, c, sticky = true, who) >> doSetChapter(study, c.id, who) } } // deleting the current chapter? Automatically move to another one diff --git a/modules/study/src/main/StudySocket.scala b/modules/study/src/main/StudySocket.scala index 2fe9263b94..8056ecb14a 100644 --- a/modules/study/src/main/StudySocket.scala +++ b/modules/study/src/main/StudySocket.scala @@ -124,7 +124,7 @@ final private class StudySocket( case "addChapter" => reading[ChapterMaker.Data](o) { data => val sticky = o.obj("d").flatMap(_.boolean("sticky")) | true - who foreach api.addChapter(studyId, data, sticky = sticky) + who foreach api.addChapter(studyId, data, sticky = sticky, withRatings = true) } case "setChapter" => o.get[Chapter.Id]("d") foreach { chapterId => From e9547c31a46382353b2696736e5ad6cabb954443 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 21 Nov 2021 09:50:23 +0100 Subject: [PATCH 081/144] remove unused server-side board colors --- modules/pref/src/main/Theme.scala | 23 +++-------------------- 1 file changed, 3 insertions(+), 20 deletions(-) diff --git a/modules/pref/src/main/Theme.scala b/modules/pref/src/main/Theme.scala index a66d06c7fe..961d2abdcf 100644 --- a/modules/pref/src/main/Theme.scala +++ b/modules/pref/src/main/Theme.scala @@ -1,13 +1,10 @@ package lila.pref -sealed class Theme private[pref] (val name: String, val colors: Theme.HexColors) { +sealed class Theme private[pref] (val name: String) { override def toString = name def cssClass = name - - def light = colors._1 - def dark = colors._2 } sealed trait ThemeObject { @@ -27,20 +24,6 @@ sealed trait ThemeObject { object Theme extends ThemeObject { - case class HexColor(value: String) extends AnyVal with StringValue - type HexColors = (HexColor, HexColor) - - private[pref] val defaultHexColors = (HexColor("b0b0b0"), HexColor("909090")) - - private val colors: Map[String, HexColors] = Map( - "blue" -> (HexColor("dee3e6") -> HexColor("8ca2ad")), - "brown" -> (HexColor("f0d9b5") -> HexColor("b58863")), - "green" -> (HexColor("ffffdd") -> HexColor("86a666")), - "purple" -> (HexColor("9f90b0") -> HexColor("7d4a8d")), - "ic" -> (HexColor("ececec") -> HexColor("c1c18e")), - "horsey" -> (HexColor("f1d9b6") -> HexColor("8e6547")) - ) - val all = List( "blue", "blue2", @@ -68,7 +51,7 @@ object Theme extends ThemeObject { "ic", "horsey" ) map { name => - new Theme(name, colors.getOrElse(name, defaultHexColors)) + new Theme(name) } lazy val default = allByName get "brown" err "Can't find default theme D:" @@ -97,7 +80,7 @@ object Theme3d extends ThemeObject { "Jade", "Woodi" ) map { name => - new Theme(name, Theme.defaultHexColors) + new Theme(name) } lazy val default = allByName get "Woodi" err "Can't find default theme D:" From a261b09e0c0c682f0cf275525387a38047dc08db Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 21 Nov 2021 10:06:34 +0100 Subject: [PATCH 082/144] remove now unused function --- app/templating/AssetHelper.scala | 2 -- 1 file changed, 2 deletions(-) diff --git a/app/templating/AssetHelper.scala b/app/templating/AssetHelper.scala index ec5b3637dc..ba42102fce 100644 --- a/app/templating/AssetHelper.scala +++ b/app/templating/AssetHelper.scala @@ -26,8 +26,6 @@ trait AssetHelper { self: I18nHelper with SecurityHelper => def cdnUrl(path: String) = s"$assetBaseUrl$path" - def dbImageUrl(path: String) = s"$assetBaseUrl/image/$path" - def cssTag(name: String)(implicit ctx: Context): Frag = cssTagWithTheme(name, ctx.currentBg) From 5e26e6b706182b45aaae2a2fdeb403559cb50802 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 21 Nov 2021 10:17:13 +0100 Subject: [PATCH 083/144] preload board images to maybe fix the mobile browser bug where it randomly forgets to show the board --- app/views/base/layout.scala | 26 ++++---- modules/pref/src/main/Theme.scala | 98 +++++++++++++++---------------- 2 files changed, 63 insertions(+), 61 deletions(-) diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index 42bcb638c4..b0c5ef779f 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -41,16 +41,21 @@ object layout { import bits._ private val noTranslate = raw("""""") - private def fontPreload(implicit ctx: Context) = - raw { - s"""""" + - !ctx.pref.pieceNotationIsLetter ?? - s"""""" - } + + private def preload(href: String, as: String, tpe: Option[String] = None) = + raw(s""" s"""type="$t" """)}crossorigin>""") + + private def fontPreload(implicit ctx: Context) = frag( + preload(assetUrl(s"font/lichess.woff2"), "font", "font/woff2".some), + !ctx.pref.pieceNotationIsLetter option + preload(assetUrl(s"font/lichess.chess.woff2"), "font", "font/woff2".some) + ) + private def boardPreload(implicit ctx: Context) = frag( + preload(assetUrl(s"images/board/${ctx.currentTheme.file}"), "image"), + ctx.pref.is3d option + preload(s"images/staunton/board/${ctx.currentTheme3d.file}", "image") + ) + private val manifests = raw( """""" ) @@ -248,6 +253,7 @@ object layout { ) }, fontPreload, + boardPreload, manifests, jsLicense ), diff --git a/modules/pref/src/main/Theme.scala b/modules/pref/src/main/Theme.scala index 961d2abdcf..c4ebe4b211 100644 --- a/modules/pref/src/main/Theme.scala +++ b/modules/pref/src/main/Theme.scala @@ -1,6 +1,6 @@ package lila.pref -sealed class Theme private[pref] (val name: String) { +sealed class Theme private[pref] (val name: String, val file: String) { override def toString = name @@ -25,34 +25,32 @@ sealed trait ThemeObject { object Theme extends ThemeObject { val all = List( - "blue", - "blue2", - "blue3", - "blue-marble", - "canvas", - "wood", - "wood2", - "wood3", - "wood4", - "maple", - "maple2", - "brown", - "leather", - "green", - "marble", - "green-plastic", - "grey", - "metal", - "olive", - "newspaper", - "purple", - "purple-diag", - "pink", - "ic", - "horsey" - ) map { name => - new Theme(name) - } + new Theme("blue", "svg/blue.svg"), + new Theme("blue2", "blue2.jpg"), + new Theme("blue3", "blue3.jpg"), + new Theme("blue-marble", "blue-marble.jpg"), + new Theme("canvas", "canvas2.jpg"), + new Theme("wood", "wood.jpg"), + new Theme("wood2", "wood2.jpg"), + new Theme("wood3", "wood3.jpg"), + new Theme("wood4", "wood4.jpg"), + new Theme("maple", "maple.jpg"), + new Theme("maple2", "maple2.jpg"), + new Theme("brown", "svg/brown.svg"), + new Theme("leather", "leather.jpg"), + new Theme("green", "svg/green.svg"), + new Theme("marble", "marble.jpg"), + new Theme("green-plastic", "green-plastic.png"), + new Theme("grey", "grey.jpg"), + new Theme("metal", "metal.jpg"), + new Theme("olive", "olive.jpg"), + new Theme("newspaper", "newspaper.png"), + new Theme("purple", "svg/purple.svg"), + new Theme("purple-diag", "purple-diag.png"), + new Theme("pink", "pink-pyramid.png"), + new Theme("ic", "svg/ic.svg"), + new Theme("horsey", "horsey.jpg") + ) lazy val default = allByName get "brown" err "Can't find default theme D:" } @@ -60,28 +58,26 @@ object Theme extends ThemeObject { object Theme3d extends ThemeObject { val all = List( - "Black-White-Aluminium", - "Brushed-Aluminium", - "China-Blue", - "China-Green", - "China-Grey", - "China-Scarlet", - "China-Yellow", - "Classic-Blue", - "Gold-Silver", - "Green-Glass", - "Light-Wood", - "Power-Coated", - "Purple-Black", - "Rosewood", - "Wood-Glass", - "Marble", - "Wax", - "Jade", - "Woodi" - ) map { name => - new Theme(name) - } + new Theme("Black-White-Aluminium", "Black-White-Aluminium.png"), + new Theme("Brushed-Aluminium", "Brushed-Aluminium.png"), + new Theme("China-Blue", "China-Blue.png"), + new Theme("China-Green", "China-Green.png"), + new Theme("China-Grey", "China-Grey.png"), + new Theme("China-Scarlet", "China-Scarlet.png"), + new Theme("China-Yellow", "China-Yellow.png"), + new Theme("Classic-Blue", "Classic-Blue.png"), + new Theme("Gold-Silver", "Gold-Silver.png"), + new Theme("Green-Glass", "Green-Glass.png"), + new Theme("Light-Wood", "Light-Wood.png"), + new Theme("Power-Coated", "Power-Coated.png"), + new Theme("Purple-Black", "Purple-Black.png"), + new Theme("Rosewood", "Rosewood.png"), + new Theme("Wood-Glass", "Wood-Glass.png"), + new Theme("Marble", "Marble.png"), + new Theme("Wax", "Wax.png"), + new Theme("Jade", "Jade.png"), + new Theme("Woodi", "Woodi.png") + ) lazy val default = allByName get "Woodi" err "Can't find default theme D:" } From 5edc8a6a05ea29836a06f7328ce7214e28b25353 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 22 Nov 2021 08:33:49 +0100 Subject: [PATCH 084/144] use external piece svg assets and preload them experimental, see if that eliminates rare cases of invisible pieces --- app/Env.scala | 5 +++++ app/controllers/Dev.scala | 3 ++- app/templating/AssetHelper.scala | 3 ++- app/views/base/layout.scala | 11 ++++++++++- ui/dasher/src/piece.ts | 2 +- 5 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/Env.scala b/app/Env.scala index 5aef82d4a8..368d6ea553 100644 --- a/app/Env.scala +++ b/app/Env.scala @@ -127,6 +127,11 @@ final class Env( default = 200, text = "Opening explorer games per second".some ) + lazy val pieceImageExternal = memo.settingStore[Boolean]( + "pieceImageExternal ", + default = false, + text = "Use external piece images".some + ) lazy val preloader = wire[mashup.Preload] lazy val socialInfo = wire[mashup.UserInfo.SocialApi] diff --git a/app/controllers/Dev.scala b/app/controllers/Dev.scala index ad91309cdb..2f6d4460b0 100644 --- a/app/controllers/Dev.scala +++ b/app/controllers/Dev.scala @@ -26,7 +26,8 @@ final class Dev(env: Env) extends LilaController(env) { env.fishnet.openingBookDepth, env.noDelaySecretSetting, env.featuredTeamsSetting, - env.prizeTournamentMakers + env.prizeTournamentMakers, + env.pieceImageExternal ) def settings = diff --git a/app/templating/AssetHelper.scala b/app/templating/AssetHelper.scala index ba42102fce..1e97f83cb3 100644 --- a/app/templating/AssetHelper.scala +++ b/app/templating/AssetHelper.scala @@ -22,7 +22,8 @@ trait AssetHelper { self: I18nHelper with SecurityHelper => def assetVersion = AssetVersion.current - def assetUrl(path: String): String = s"$assetBaseUrl/assets/_$assetVersion/$path" + def assetUrl(path: String): String = s"$assetBaseUrl/assets/_$assetVersion/$path" + def staticAssetUrl(path: String): String = s"$assetBaseUrl/assets/$path" def cdnUrl(path: String) = s"$assetBaseUrl$path" diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index b0c5ef779f..2b7e8a142d 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -34,7 +34,7 @@ object layout { def pieceSprite(ps: lila.pref.PieceSet): Frag = link( id := "piece-sprite", - href := assetUrl(s"piece-css/$ps.css"), + href := assetUrl(s"piece-css/$ps.${env.pieceImageExternal.get() ?? "external."}css"), rel := "stylesheet" ) } @@ -55,6 +55,14 @@ object layout { ctx.pref.is3d option preload(s"images/staunton/board/${ctx.currentTheme3d.file}", "image") ) + private def piecesPreload(implicit ctx: Context) = + env.pieceImageExternal.get() option raw { + (for { + c <- List('w', 'b') + p <- List('K', 'Q', 'R', 'B', 'N', 'P') + href = staticAssetUrl(s"piece/${ctx.currentPieceSet.name}/$c$p.svg") + } yield s"""""").mkString + } private val manifests = raw( """""" @@ -254,6 +262,7 @@ object layout { }, fontPreload, boardPreload, + piecesPreload, manifests, jsLicense ), diff --git a/ui/dasher/src/piece.ts b/ui/dasher/src/piece.ts index dc8c3452fe..a5cd0ded92 100644 --- a/ui/dasher/src/piece.ts +++ b/ui/dasher/src/piece.ts @@ -93,6 +93,6 @@ function applyPiece(t: Piece, list: Piece[], is3d: boolean) { $('body').removeClass(list.join(' ')).addClass(t); } else { const sprite = document.getElementById('piece-sprite') as HTMLLinkElement; - sprite.href = sprite.href.replace(/\w+\.css/, t + '.css'); + sprite.href = sprite.href.replace(/\w+(\.external|)\.css/, t + '$1.css'); } } From 0688a1b716f3acd6a97f92ffe36904966b6aa591 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 22 Nov 2021 10:01:09 +0100 Subject: [PATCH 085/144] New Crowdin updates (#10137) * New translations: site.xml (Afrikaans) * New translations: site.xml (Afrikaans) * New translations: site.xml (Afrikaans) * New translations: preferences.xml (Korean) * New translations: puzzle.xml (Korean) * New translations: ublog.xml (Korean) * New translations: streamer.xml (Luxembourgish) * New translations: site.xml (Afrikaans) * New translations: storm.xml (Italian) * New translations: site.xml (Afrikaans) * New translations: emails.xml (Ossetian) * New translations: emails.xml (Ossetian) * New translations: emails.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Persian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: site.xml (Ossetian) * New translations: emails.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: site.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: site.xml (Kazakh) * New translations: site.xml (Telugu) * New translations: site.xml (Kazakh) * New translations: site.xml (Telugu) * New translations: arena.xml (Kazakh) * New translations: faq.xml (Ossetian) * New translations: site.xml (Telugu) * New translations: arena.xml (Kazakh) * New translations: arena.xml (Kazakh) * New translations: emails.xml (Telugu) * New translations: site.xml (Telugu) * New translations: study.xml (Kazakh) * New translations: site.xml (Telugu) * New translations: study.xml (Kazakh) * New translations: site.xml (Telugu) * New translations: site.xml (Telugu) * New translations: faq.xml (Ossetian) * New translations: site.xml (Telugu) * New translations: faq.xml (Ossetian) * New translations: site.xml (Telugu) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: site.xml (Croatian) * New translations: study.xml (Croatian) * New translations: study.xml (Croatian) * New translations: faq.xml (Ossetian) * New translations: site.xml (Ossetian) * New translations: faq.xml (Ossetian) * New translations: site.xml (German) * New translations: site.xml (German) * New translations: faq.xml (Ossetian) * New translations: site.xml (Odia) * New translations: site.xml (Odia) * New translations: learn.xml (Odia) * New translations: storm.xml (Odia) * New translations: arena.xml (Odia) * New translations: coordinates.xml (Odia) * New translations: site.xml (Malay) * New translations: site.xml (Malay) --- translation/dest/arena/kk-KZ.xml | 12 +++ translation/dest/arena/or-IN.xml | 4 +- translation/dest/coordinates/or-IN.xml | 5 +- translation/dest/emails/os-SE.xml | 20 ++--- translation/dest/emails/te-IN.xml | 2 +- translation/dest/faq/fa-IR.xml | 1 + translation/dest/faq/os-SE.xml | 61 +++++++++++--- translation/dest/learn/or-IN.xml | 1 + translation/dest/preferences/ko-KR.xml | 1 + translation/dest/puzzle/ko-KR.xml | 3 +- translation/dest/site/af-ZA.xml | 34 ++++++++ translation/dest/site/de-DE.xml | 4 +- translation/dest/site/hr-HR.xml | 2 + translation/dest/site/kk-KZ.xml | 10 +++ translation/dest/site/ms-MY.xml | 6 +- translation/dest/site/or-IN.xml | 2 + translation/dest/site/os-SE.xml | 12 +-- translation/dest/site/te-IN.xml | 105 ++++++++++++++++++++++++- translation/dest/storm/it-IT.xml | 2 + translation/dest/storm/or-IN.xml | 1 + translation/dest/streamer/lb-LU.xml | 2 + translation/dest/study/hr-HR.xml | 14 +++- translation/dest/study/kk-KZ.xml | 30 +++++++ translation/dest/ublog/ko-KR.xml | 11 ++- 24 files changed, 304 insertions(+), 41 deletions(-) diff --git a/translation/dest/arena/kk-KZ.xml b/translation/dest/arena/kk-KZ.xml index 574deb3344..ed34ba9db7 100644 --- a/translation/dest/arena/kk-KZ.xml +++ b/translation/dest/arena/kk-KZ.xml @@ -41,4 +41,16 @@ Адамдар қосылсын десеңіз, осы URL-мен бөлісіңіз: %s Тепе-теңдік тізбегі: Ойыншының Алаңда қатарынан тепе-теңдіктері болса, алғашқы тепе-теңдік немесе %s жүрістен кейін орын алған тепе-теңдіктер ұпай береді. Тепе-теңдік тізбегін жеңіліс пен тепе-теңдікпен емес, тек жеңіспен ғана тоқтатуға болады. Алаң журналы + + Топты қарау + Барлық %s топты қарау + + Жаңа Топтық Күрес + Басталудың ерекше күні + Сіздің жергілікті уақыт белдеуіңіз. Бұл \"Жарысқа дейінгі уақыт\" баптауына әсер етеді + Берсерк-ті рұқсат ету + Қосымша ұпай жинау үшін ойыншыларға уақытты жартылай қыстартуды рұқсат ету + Ойыншыларға чат бөлмесінде сөйлесуді рұқсат ету + Алаң тізбектері + Қатарынан болған 2 жеңіс 2 ұпайдың орнына 4 ұпай тұрады. diff --git a/translation/dest/arena/or-IN.xml b/translation/dest/arena/or-IN.xml index 3ea04e700d..ce023a84fd 100644 --- a/translation/dest/arena/or-IN.xml +++ b/translation/dest/arena/or-IN.xml @@ -1,2 +1,4 @@ - + + ସ୍କୋରଗୁଡ଼ିକ କିପରି ଗଣନା କରାଯାଏ? + diff --git a/translation/dest/coordinates/or-IN.xml b/translation/dest/coordinates/or-IN.xml index 3ea04e700d..88cdffeb23 100644 --- a/translation/dest/coordinates/or-IN.xml +++ b/translation/dest/coordinates/or-IN.xml @@ -1,2 +1,5 @@ - + + ଧଳା ଭାବରେ ହାରାହାରି ସ୍କୋର୍: %s + କଳା ଭାବରେ ହାରାହାରି ସ୍କୋର୍: %s + diff --git a/translation/dest/emails/os-SE.xml b/translation/dest/emails/os-SE.xml index 7d21d92042..84607506b1 100644 --- a/translation/dest/emails/os-SE.xml +++ b/translation/dest/emails/os-SE.xml @@ -1,22 +1,22 @@ Сфидар кæн дæ lichess.org аккаунт, %s - Дæ Lichess аккаунт цæмæй скусын кæнай, æрæлхъив дæнцæгыл: + Дæ Lichess аккаунт цæмæй скусын кæнай, нылхъив дæнцæгыл: Дæхи Lichess-ы кæд регистраци нæ кодтай, уæд ацы фыстæг ницæмæ дар. Раппар дæ lichess.org пароль, %s - Мах райстам бафæрст дæ аккаунты паролы раппæрыныл. - Ды кæд уыцы бафæрст скодтай, уæд æрæлхъив уыцы дæнцæгыл. Кæд нæ, уæд ницæмæ дар уыцы фыстаг. - Сфидар кæн дæ ног пост, %s - Ды бафарстай дæ посты ивынад. - Цæмæй дæ пост сфидар кæнай, æрæлхъив уыцы дæнцæгыл: + Мах райстам бафарст дæ аккаунты пароль раппарын. + Ды кæд ахæм бафарст кодтай, уæд нылхъив дæнцæгыл дæлдæр. Кæд нæ, уæд ницæмæ дар ацы фыстаг. + Сфидар кæн дæ ног посты адрис, %s + Ды бафарстай дæ посты адрис баивын. + Цæмæй дæ посты адрис сфидар кæнай, нылхъив дæнцæгыл дæлдæр: Æгас цу lichess.org-мæ, %s Æнтыстджынæй дæ lichess.org аккаунт сарæзтай. -Мæнæ ам ис дæ профилы фарс: %1$s. Сфæлындын æй дæ бон у %2$s. +Мæнæ ам ис дæ профилы фарс: %1$s. Сфæлындын æй дæ бон у ам: %2$s. -Хъæлдзаг у, æмæ дæ фигурæтæ ныхмæлæууæджы къаролмæ цæмæй сæ фæндаг æдзухдæр ссарой! +Бантысæд дын, цæмæй дæ фигурæтæ ныхмæлæууæджы къаролмæ сæ фæндаг æдзухдæр ссарой! Бацу lichess.org-мæ, %s (Æлхъивын нæ кусы? Бафæлвар æй дæ браузермæ бавæрын!) - Уый у сервисы фыстæг, дæ %s спайдакæнынмæ баст кæй у, ахæм. - Махимæ цæмæй хи сбæттын, %s пайда кæн. + Ай у службæйон фыстæг, дæ %s спайдакæнынмæ баст кæй у, ахæм. + Махимæ цæмæй бастдзинад скæнын, %s пайда кæн. diff --git a/translation/dest/emails/te-IN.xml b/translation/dest/emails/te-IN.xml index 1d29f0a6f8..032e47a95e 100644 --- a/translation/dest/emails/te-IN.xml +++ b/translation/dest/emails/te-IN.xml @@ -1,7 +1,7 @@ మీ lichess.org ఖాతా %s, ధ్రువపరచండి - మీ Lichess ఖాతాను నిద్రలేపి, వాడడం మొదలుపెట్టడానికి ఈ లంకెను నొక్కండి: + మీ Lichess ఖాతాను వాడడం మొదలుపెట్టడానికి ఈ లంకెను నొక్కండి: ఒకవేళ మీరు lichess లో చేరియుండక పొతే ఈ సందేశాన్ని పట్టించుకోనవసరం లేదు. మీ lichess రహస్యపదాన్ని మార్చండి, %s మీ ఖాతాకు ప్రస్తుతమున్న రహస్యపదాన్ని తుడిచిపెట్టి, కొత్త రహస్యపదాన్ని సృష్టించమని అభ్యర్థన వచ్చింది. diff --git a/translation/dest/faq/fa-IR.xml b/translation/dest/faq/fa-IR.xml index 775f517af4..9e4a89085b 100644 --- a/translation/dest/faq/fa-IR.xml +++ b/translation/dest/faq/fa-IR.xml @@ -15,6 +15,7 @@ بازی منصفانه اگر حریف شما به صورت مداوم بازی را ترک کند، از بازی محروم می شود این به این معنی است که به صورت موقت اجازه بازی کردن را نخواهد داشت. این امر در حساب کاربری فرد نمایش داده نمی شود. اگر این رفتار ادامه پیدا کند، مدت زمان محرومیت از بازی افزایش می یابد و انجام این کار در مدت زمان طولانی ممکن است منجر به مسدود شدن حساب کاربر شود. چگونه می‌توانم یک مدیر باشم؟ + گیم پلی معرفی و مقدمه خوب موقعیت‌ها پیکربندی نمایید diff --git a/translation/dest/faq/os-SE.xml b/translation/dest/faq/os-SE.xml index 1d45be186d..4a8932c291 100644 --- a/translation/dest/faq/os-SE.xml +++ b/translation/dest/faq/os-SE.xml @@ -3,27 +3,64 @@ АрФар Арæх Фарстатæ Lichess-æн йæ ном цæмæн у \"Lichess\"? - \"Lichess\" у \"live/light/libre\" æмæ \"chess\" — удæгас, рог æмæ сæрибар шахмæттæ. Дзурын хъæуы афтæ: %1$s. + \"Lichess\" у \"live/light/libre\" æмæ \"chess\"-ы комбинаци — удæгас, рог æмæ сæрибар шахмæттæ. Дзурын хъæуы афтæ: %1$s. личец - Байхъус специалист æй куыд дзуры. - Удæгас уый тыххæй, æмæ ды хъазыс æмæ кæсыс хъазтытæ реалон рæстæджы цыфæнды афоны; рог æмæ сæрибар уымæн, æмæ Lichess æргом æмæ быронæй сыгъдæг у, иннæ æргъыл сайтытæм нæ кæсгæйæ. - Уый дæр хауы Lichess-ы райдайæн кодмæ, %1$s-мæ, кæй нысан кæны li[chess in sca]la, уымæн æмæ Lichess у фыст %2$s-йæ, æнцонæмбарон программон æвзагæй. + Байхъус, специалист æй куыд дзуры. + Удæгас, уымæн æмæ дæ бон у хъазын æмæ кæсын хъæзтытæ реалон рæстæджы цыфæнды афоны; рог æмæ сæрибар, уымæн æмæ Lichess у æргом æмæ дзы нæй проприетарон быроны, иннæ коммерцион проектты хуызæн. + Уый дæр хауы Lichess-ы райдайæн кодмæ, %1$s-мæ, кæй нысан кæны li[chess in sca]la, уымæн æмæ Lichess сæйрагдæр фыст у %2$s-йæ, æнцонæмбарæн программон æвзагæй. Куыд баххуыс кæнон Lichess-æн? - Lichess цæры хъазджыты снывондытæй æмæ волонтёрты куыстæй. - Дæ бон у базонын %1$s, уый (æмæ %2$s дæр). Кæд дæ фæнды баххуысын Lichess-æн дæ рæстæгæй æмæ арæхстытæй, уæд дзы ис %3$s. + Lichess цæры хъазджыты нывондытæй æмæ волонтёрты куыстæй. + Дæ бон у базонын, %1$s (æмæ %2$s дæр). Кæд дæ фæнды баххуыс кæнын Lichess-æн дæ рæстæгæй æмæ арæхстытæй, уæд дзы ис %3$s. аххуысгæнæгæй куыд суын нæ хардзыты хыгъдлæвæрд æндæр амалтæ баххуыс кæнын - Ис дзы æндæр Lichess-вебсайтытæ? - О, Lichess ныфс радта æндæр æргом сайтытæм, кæй пайда кæнынц нæ %1$s, %2$s æмæ %3$s. + Ис дзы æндæр сайтытæ Lichess-æй бындурыл? + О, æндæр æргом сайтытæ Lichess-æй удцырын райстой æмæ пайда кæнынц нæ %1$s, %2$s æмæ %3$s. Рæстаг Хъазт - Сайæгой мæ кæд рамбулдзæн, уæд мын мæ рейтинг фæстæмæ ратдзыстут? - Хъазты арæзт - Нæмыг, блиц æмæ æндæр сахаты цæстдардтæ куыд рабæрæг конд ысты? + Сайæгой мæ кæд рамбылдта, уæд мын мæ рейтинг фæстæмæ ратдзыстут? + Сайæгойы кæд раргом кодтам, уæд, иу минутæйы фæстæ, йæ 40 ахæм рейтингимæ хъæзтытæ, фæстаг 3 боны кæм рамбылдта, уыдзысты равзард. Ды ацы хъæзтыты йæ ныхмæлæууæг кæд уыдтæ, æмæ дæхион рейтинг æууæнккаг кæд у, уæд дæ рейтинг дын фæстæмæ ратдзыстæм. Баххæст уыдзæн æрæнгонд дæ иуылстыр рейтингæй æмæ æнæраст хъæзтыты фæстæ рейтингы ивындзинæдтæй. + (Зæгъæм, дæ рейтинг ацы хъæзтыты фæстæ кæд тынг фæфылдæр, уæд, чи зоны, ницы дын баххæст кæндзыстæм, кæннод йæ хай æрмæст ратдзыстæм.) Цыфæндыйæ дæр дæ баххæст 150 гæбæлтæй нæ ахисдзæн. + Хъазджытæн, хъазтæй кæй рацæуынц æнæ хи дæттгæйæ, цы вæййы? + Дæ ныхмæлæууæг йæ хъазт кæд арæх фæуромы/фæуадзы, уæд йын хъæзтытæм бацæуæн рæстæгмæ æхгæд уыдзæн, æмæ йæ уæлдæй ничи фендзæн уый. Кæд уый фæстæ йæ митæ нæ бауромдзæн, уæд йын ногæй сыхгæндзыстæм хъæстытæм бацæуæн, фæлæ фылдæр æмгъуыдмæ. Æппын фæстаг, йæ аккаунт æххæстæй сыхгæндзыстæм. + Ис дзы мæ бон модераторæй суын? + Мах модераторы бынатмæ курдиаттæ нæ исæм. Кæд фендзыстæм, исчи, махмæ гæсгæ, уыдзæн хорз модератор, уæд нæхæдæг йемæ бастдзинад саразæм. + Ис дзы хицæндзинад фыстæгтæ фысгæйæ хъазтæй хуымæтæг хъазты \'хсæн? + Lichess-ы сæйраг хицæндзинад фыстæгтæ фысгæ хъазы уагæвæрдты у райдайæнты чиныг. Компьютеры программæты пайдакæнæн иваргонд у æмæ расайдзæн аккаунты æхгæнæн. ICCF-мæ нæкæсгæйæ, Lichess компьютеры программæты аххуыс нæ уадзы. + Хъазты æрфыст + Нæмыг, блиц æмæ æндæр хъазы хуызтæ сахаты цæстдардмæ гæсгæ куыд рабæрæг конд ысты? + Lichess сахаты цæстдард баст у æввахс хъазы дæргъцы = %1$s +Зæъæм, æввахс 5+3 хъазы дæргъц у 5 × 60 + 40 × 3 = 420 сикъунды. + (райдайæн рæстæг) + 40 × (уæлæмхасæн) < %1$s с = %2$s ≥ %1$s с = %2$s - Цавæр варианттæ мæ бон у хъазын Lichess-ы? + Цавæр шахмæтты хуызтæ мæ бон у хъазын Lichess-ы? Lichess-мæ ис классикон шахмæттæ æмæ %1$s. + 8 шахмæтты хуызты + «Рæстæмбис сантипешкæты сæфт» (ACPL) та ци у? + Сантипешкæ у барынады иуæг, кæй пайда у æвдисын уæлидзинад шахмæтты. Сантипешкæ у ¹/₁₀₀ пешкæйы аргъы. 100 сантипешкæты æмбæрц у 1 пешкæмæ. Ацы нысаниуæгтæ ницы давынц хъазты, фæлæ сты пайдаджын компьютерон шахмæтты æмæ позицийы аргъкæнынады компьютерон анализы. + +Фыццаг хаххы комьютеры цыд нысан кæны нулон сантипешкæты сафт, æмæ алы æндæр цыд фæкæны позицийы фæфыддæр, кæй бæрæг у сантипешкæты сафтæй. Цас фыддæр цыд у, уыйас фылдæр сафты нысаниуæг. + +Ацы нысаниуæг у хъазты бæрцы æвдисæн. Цас каддæр у сантипешкæты сæфт цыды рæстæмбисæй, уыйас хъаруджындæр у хъазт. + +Lichess компьютерон анализы пайда кæны Stockfish. + Æмбылд рæстæгæй, хæрæгсаст æмæ æрмæджы хъуаг + Фылдæр хатт, хъазæджы рæстæг куы фæуы, уæд йын фембылд банымайæм. Фæлæ хъазт хæрæгсастæй фæуы, ныхмæлæууæгмæ кæд нæй фадаты мат нывварын дарддæр хъазгæйæ (%1$s). + +Хатгай зын вæййы уынаффæ хæдархайгæ рахæссын (æнæбары цыдты сери, фидар). Æмткæй райсгæйæ мах райсæм фарс хъазæджы, кæмæн ма баззадис рæстæг. + +Хъуыдыйы дар, ныхмæлæууæгмæ кæд ис фигурæ кæнæ пешкæ, йæ бон кæмæн у къаролæн блокадæ сывæрын, уæд мат нывварын фадат ис иу бæхæй дæр, пылæй дæр. + FIDE-йы уагæвæрдтæ %s + Цæмæн пешкæйæн байст ис, цæвгæгонд быдырыл кæд нырид ахизыд, уæд? (ацæуæны байст) + Уый у цыд, зындгонд номæй «ацæуæны байст». Уацхъуыд уырыссаг Википедийы дæтты %1$s. + +Он описан в разделе 3.7 (d), %2$s: + +«Пешка, занимающая поле на той же горизонтали и на соседней вертикали с пешкой соперника, которая только что продвинулась на два поля в один ход от своей первоначальной позиции, может взять эту продвинутую пешку соперника, как если бы последний ее ход был только на одно поле. Это взятие возможно только ходом, следующим за этим продвижением, и называется взятием «на проходе».» + + Посмотрите %3$s и попрактикуйтесь. + хорз райхæлд + Æртывæр позицийы фæлхатын Ис дзы гæнæн мын раттын Lichess Мастеры (LM) хуындад? Нæй. Уый у каджын хуындад, æнæофициалон, æмæ æрмæст вæййы Lichess-ы. diff --git a/translation/dest/learn/or-IN.xml b/translation/dest/learn/or-IN.xml index 3dea0673f7..7f3b6d6b5b 100644 --- a/translation/dest/learn/or-IN.xml +++ b/translation/dest/learn/or-IN.xml @@ -23,6 +23,7 @@ ସୈନିକ = 1 ପ୍ରହେଳିକା ଆପଣଙ୍କ କୌଶଳ ଅଭ୍ୟାସ କରନ୍ତୁ + ଆପଣଙ୍କ ସ୍କୋର୍ ପ୍ରହେଳିକା ବିଫଳ ହେଲା! ପୁନଃଚେଷ୍ଟା କରନ୍ତୁ diff --git a/translation/dest/preferences/ko-KR.xml b/translation/dest/preferences/ko-KR.xml index 11e92654ff..2b4e72fde5 100644 --- a/translation/dest/preferences/ko-KR.xml +++ b/translation/dest/preferences/ko-KR.xml @@ -12,6 +12,7 @@ 체스 말 기호 글자 (K, Q, R, B, N) 젠 모드 + 플레이어 레이팅 보기 보드 크기 재조정 핸들 보이기 초기 상태에서만 눈을 가리고 체스 (기물이 보이지 않음) diff --git a/translation/dest/puzzle/ko-KR.xml b/translation/dest/puzzle/ko-KR.xml index 1f847f15e8..cc62a951d1 100644 --- a/translation/dest/puzzle/ko-KR.xml +++ b/translation/dest/puzzle/ko-KR.xml @@ -13,6 +13,7 @@ 특수 행마 이 퍼즐이 괜찮았나요? 다음 퍼즐을 위해 투표해주세요! + 당신의 퍼즐 레이팅은 바뀌지 않을 것입니다. 퍼즐은 경쟁이 아니라는 걸 기억하세요. 레이팅은 당신의 현재 스킬에 맞는 퍼즐을 선택하도록 돕습니다. 백의 최고의 수를 찾아보세요. 흑의 최고의 수를 찾아보세요. 개인화된 퍼즐을 위해선: @@ -73,7 +74,7 @@ %s 다시 풀기 - %s 해결함 + %s 해결함 표시할 것이 없습니다. 먼저 퍼즐을 플레이하세요! 실력을 높이기 위해 훈련하세요! 이 테마에서 최고의 성적을 냈습니다 diff --git a/translation/dest/site/af-ZA.xml b/translation/dest/site/af-ZA.xml index b99993bd71..08b6b24e8a 100644 --- a/translation/dest/site/af-ZA.xml +++ b/translation/dest/site/af-ZA.xml @@ -95,6 +95,9 @@ %s opening ontdekker Oorwinning gekelder deur 50-skuifreël Verloor voorkom deur 50-skuifreël + Wen of 50 skuiwe deur vorige fout + Verloor of 50 skuiwe deur vorige fout + Wen/verloor slegs gewaarborg as aanbevole tafelbasis lyn gevolg is sedert die laaste vat of pion skuif, weens moontlike afronding van DTZ waardes in Syzygy tafelbasisse. Gereed! Voer PGN in Skrap @@ -114,6 +117,18 @@ Flater Fout Onakkuraatheid + + %s flater + %s flaters + + + %s fout + %s foute + + + %s onakuraatheid + %s onakuraathede + Skuif tye Keer bord om Drie-malige herhaling @@ -124,6 +139,8 @@ %s speler %s spelers + Gelykop deur wedersydse ooreenkoms + Vyftig skuiwe sonder vordering Lopende spelle Sien al %s spelle @@ -353,6 +370,8 @@ rekenaar analise, kletskamer en deelbare URL te kry. Volg Besig om te volg Ontvolg + Volg %s + Ontvolg %s Blokeer Geblok Ontblok @@ -415,6 +434,8 @@ rekenaar analise, kletskamer en deelbare URL te kry. Bord redigeerder Pak die bord Gewilde openinge + Eindspel posisies + Skaak960 begin posisie: %s Begin posisie Maak bord skoon Laai posisie @@ -429,6 +450,7 @@ rekenaar analise, kletskamer en deelbare URL te kry. Noem naam Van Biografie + Land of vlag Dankie! Sosiale media skakels Binnelyn notasie @@ -447,6 +469,8 @@ rekenaar analise, kletskamer en deelbare URL te kry. Toernooi wenners Naam Beskrywing + Private beskrywing + Teks wat slegs spanlede sal sien. Indien gestel vervang dit die publieke beskrywing vir spanlede. Nee Ja Help: @@ -467,6 +491,7 @@ rekenaar analise, kletskamer en deelbare URL te kry. Kul Beledig Boelie + Gradering manipulasie Ander Plak skakel na die spel(le) en verduidelik wat skort met die lid se gedrag. Moenie net sê hulle kroek nie, maar verduidelik hoe daardie gevolgtrekking bereik is. Jou verslag sal vinniger geantwoord word as dit in Engels geskryf is. Verskaf asseblief ten minste een skakel na \'n spel waar hulle gekroek het. @@ -532,7 +557,16 @@ rekenaar analise, kletskamer en deelbare URL te kry. Gereedskap Inkrement Dié veld word vereis + Hierdie epos adres is ongeldig + Hierdie e-pos adres is nie aanvaarbaar nie. Maak seker dis reg en probeer weer. + E-pos adres is ongeldig of reeds geneem + Hierdie is reeds jou e-pos adres + Moet ten minste %s karakters lank wees + Moet op die meeste %s karakters lank wees + Moet minstens %s wees + Moet meestens %s wees Indien gradering ± %s is + Slegs bestaande gesprekke Slegs vriende Kieslys Kastelering diff --git a/translation/dest/site/de-DE.xml b/translation/dest/site/de-DE.xml index b107ff47d2..4c336213ae 100644 --- a/translation/dest/site/de-DE.xml +++ b/translation/dest/site/de-DE.xml @@ -650,7 +650,7 @@ Importiertes Spiel herunterladen Matchverlauf Du kannst auch auf dem Brett scrollen, um die Partie durchzugehen. - Fahre über Computervarianten, um eine Vorschau auf sie zu erhalten. + Bewege den Mauszeiger über Computervarianten, um eine Vorschau zu erhalten. Drücke Shift+Mausklick oder Rechtsklick, um Kreise und Pfeile auf dem Brett zu zeichnen. Erlaube anderen Spielern dich zu kontaktieren Benachrichtigungen erhalten, wenn du im Forum erwähnt wirst @@ -819,7 +819,7 @@ Du bist nun Teammitglied. Du bist \"%1$s\" beigetreten. Jemand, den du gemeldet hast, wurde gebannt - Herzlichen Glückwunsch, du hast gewonnen! + Glückwunsch, du hast gewonnen! Partie gegen %1$s %1$s gegen %2$s Jemand hat dein Trainerprofil bewertet. diff --git a/translation/dest/site/hr-HR.xml b/translation/dest/site/hr-HR.xml index 954008eab4..8aa4292961 100644 --- a/translation/dest/site/hr-HR.xml +++ b/translation/dest/site/hr-HR.xml @@ -865,6 +865,8 @@ računalnu analizu, chat partije i URL za dijeljenje. [Klikni za prikaz e-mail adrese] Preuzmi Dobrodošli! + Postavke za trenera + Postavke za strimera Otkaži turnir Opis turnira Samo za članove tima diff --git a/translation/dest/site/kk-KZ.xml b/translation/dest/site/kk-KZ.xml index 002a749a27..e384fb7d47 100644 --- a/translation/dest/site/kk-KZ.xml +++ b/translation/dest/site/kk-KZ.xml @@ -368,6 +368,8 @@ Серік болу Серіксіз Серік болмау + %s серік болу + %s серік болмау Бұғаттау Бұғатталған Бұғаттан шығару @@ -553,6 +555,14 @@ Құралдар Қосылғыш Бұл жолды толтырыңыз + Бұл пошта мекенжайы қате + Бұл пошта мекенжайы қабылданбайды. Оны тексеріп, қайта көріңіз. + Пошта мекенжайы қате немесе бос емес + Бұл сіздің бұрыңғы поштаңыз + Кемінде %s таңба болу керек + Ең көбі %s таңба болу керек + Кемінде %s болу керек + Ең көбі %s болу керек Егер рейтингі ± %s болса Тек бар әңгімелесуден Достар ғана diff --git a/translation/dest/site/ms-MY.xml b/translation/dest/site/ms-MY.xml index b05258a63d..1f669455ea 100644 --- a/translation/dest/site/ms-MY.xml +++ b/translation/dest/site/ms-MY.xml @@ -51,6 +51,7 @@ Minta analisis komputer Analisis komputer Analisis komputer ada + Analisis komputer ditutup Analisis papan Depth %s Menggunakan server analisis @@ -84,6 +85,7 @@ Mate dalam %s separuh-pergerakan + DTZ50\" yang dibundar, mengikut jumlah pegerakkan separuh sehingga sebuah bidak dimakan atau pegerakkan pion seterusnya Tiada perlawanan dijumpai Mungki masukkan lebih banyak perlawanan daripada pilihan menu? Membuka explorer @@ -138,7 +140,7 @@ Senarai pemain Senarai rakan Senarai perbualan - Hari ini + Hari ini Semalam Minit bagi setiap bahagian Variasi @@ -350,7 +352,7 @@ Buah putih menang Menang Hitam Seri - Posisi permulaan + Posisi permulaan Pautan media sosial Pemain aktif Berjaya diff --git a/translation/dest/site/or-IN.xml b/translation/dest/site/or-IN.xml index 7d7e006231..6d78d9f01a 100644 --- a/translation/dest/site/or-IN.xml +++ b/translation/dest/site/or-IN.xml @@ -237,6 +237,7 @@ %sଟି ଭାଷାରେ ଉପଲବ୍ଧ! ବେନାମୀ + ଆପଣଙ୍କ ସ୍କୋର୍: %s ଭାଷା ହାଲୁକା ଗାଢ଼ @@ -249,6 +250,7 @@ ଆମେ ସମସ୍ତଙ୍କ ପାଇଁ ଏକ ସୁଖଦ ଚେସ୍ ଅଭିଜ୍ଞତା ପ୍ରଦାନ କରିବାକୁ ଲକ୍ଷ୍ୟ ରଖିଛୁ। ଏହାକୁ କିପରି ଏଡ଼ାଇ ହେବ? ପଢ଼ିବା ପାଇଁ ଧନ୍ୟବାଦ! + ଆଜୀବନ ସ୍କୋର୍ ମୁଁ ସହମତ ଯେ ମୁଁ ସମସ୍ତ Lichess ନୀତି ଅନୁସରଣ କରିବି। ସମ୍ପାଦନା ଶାସ୍ତ୍ରୀୟ diff --git a/translation/dest/site/os-SE.xml b/translation/dest/site/os-SE.xml index fe14c46d09..464ca20e1c 100644 --- a/translation/dest/site/os-SE.xml +++ b/translation/dest/site/os-SE.xml @@ -122,7 +122,7 @@ Рæстæг цыдæн Фæйнæг разилын - Æртæхатты позицийы фæлхатын + Æртывæр позицийы фæлхатын Æрдомын хæрæгсаст Бахъарын хæрæгсаст Хæрæгсаст @@ -152,7 +152,7 @@ Хъазджытæ Æмбалтæ Ныхæстæ - Абон + Абон Знон Минутты партийæн Вариант @@ -420,9 +420,9 @@ Фæйнæджы редактор Æрæвæрын позици Адæмы уарзон райдайæнтæ - Райдайæн позици + Райдайæн позици Фæйнæг асыгъдæг кæнын - Позици бавгæнын + Позици бавгæнын Æхгæд Фехъусын кæнын %s-ы тыххæй модератортæн Профилы дзагдзинад: %s @@ -440,7 +440,7 @@ Раздæр Lichess TV-йы Онлайн хъазджытæ Разæнгард хъазджытæ - Хъус бадар, ацы хъазт у рейтингимæ, фæлæ нæй йын рæстæджы цæстдарды! + Хъус бадар, ацы хъазт у рейтингимæ, фæлæ нæй йын рæстæджы цæстдарды! Рауади %s парти @@ -509,7 +509,7 @@ Сындæг хъæзтыты Æдзух Никуы - %1$s сфæнд кодта %2$s-ы хайад исын + %1$s сфæнд кодта %2$s-ы хайад исын Уæлахиз Дæрæн %1$s %2$s-йы ныхмæ %3$s-ы diff --git a/translation/dest/site/te-IN.xml b/translation/dest/site/te-IN.xml index 4b879d8a8f..30535ec188 100644 --- a/translation/dest/site/te-IN.xml +++ b/translation/dest/site/te-IN.xml @@ -147,7 +147,7 @@ ఆటగాళ్లు స్నేహితులు సంభాషణలు - ఈరోజు + ఈరోజు నిన్న గడువు రకం @@ -323,6 +323,8 @@ Translation results సగటు రేటింగ్ స్థానం ఫిల్టర్ గేమ్‌లు + సేవ్ + లీడర్‌బోర్డు గ్రాఫ్ %s నిమిషం కన్నా తక్కువ @@ -367,10 +369,107 @@ Translation results బోర్డు ఎడిటర్ బోర్డు సెట్ చేయండి జనాదరణ పొందిన ఓపెనింగ్స్ - ప్రారంభ స్థానం + ప్రారంభ స్థానం క్లియర్ బోర్డు - స్థానం లోడ్ + స్థానం లోడ్ ప్రైవేట్ మోడరేటర్లకు %s ని నివేదించండి ప్రొఫైల్ పూర్తయింది:%s + ప్రొఫైల్ + ప్రొఫైల్ మార్చాలి + మీ దేశం లేదా జెండా + ధన్యవాదాలు! + సోషల్ మీడియా లింక్‌లు + Inline notation + గతంలో Lichess TVలో + ఆన్‌లైన్ ప్లేయర్‌లు + Active ఆటగాళ్ళు + జాగ్రత్త, గేమ్ రేట్ చేయబడింది కానీ గడియారం లేదు! + విజయం + + %s ఆటలు ఆడుతున్నారు + %s ఆటలు ఆడుతున్నారు + + కదిలించిన తర్వాత దానంతట అదే తదుపరి గేమ్‌కు వెళ్లాలి + ఆటో స్విచ్ + పజిల్స్ + టోర్నమెంట్ విజేతలు + పేరు + అతడు/ఆమె చేసిన తప్పేంటి + ప్రైవేట్ సమాచారం + టీమ్ మెంబర్స్ మాత్రమే చూడగలిగే సమాచారం. సెట్ చేస్తే, బృంద సభ్యుల కోసం పబ్లిక్ సమాచారాన్ని భర్తీ చేస్తుంది. + లేదు + అవును + సహాయం: + కొత్త అంశాన్ని సృష్టించండి + అంశాలు + పోస్ట్‌లు + చివరి పోస్ట్ + వీక్షకులు + సమాధానాలు + ఈ అంశానికి సమాధానం ఇవ్వండి + సమాధానం + సందేశం + కొత్త అంశాన్ని సృష్టించండి + రిపోర్ట్ యూజర్ + వినియోగదారుడు + కారణం + మీరు ఎందుకు రిపోర్టు చేస్తున్నారు? + మోసం + అవమానం + ఎగతాళి చేయండి + రేటింగ్ మానిప్యులేషన్ + ఇతర + గేమ్/గేమ్‌ల లింక్‌ను పంపండి మరియు ఈ వినియోగదారు ప్రవర్తనలో తప్పు ఏమిటో వివరించండి. \"వారు మోసం చేసారు\" అని చెప్పకండి, కానీ మీరు ఈ నిర్ణయానికి ఎలా వచ్చారో మాకు చెప్పండి. మీ నివేదికను ఆంగ్లంలో వ్రాస్తే వేగంగా ప్రాసెస్ చేయబడుతుంది. + దయచేసి కనీసం మోసం చేసిన ఆట ఒక లింక్‌నైనౌ అందించండి. + %s చే + ఈ అంశం ఇప్పుడు మూసివేయబడింది. + బ్లాగ్ + గమనిక + మీ ప్రైవేట్ గమనికలను ఇక్కడ టైప్ చేయండి + వినియోగదారు పేరు లేదా పాస్‌వర్డ్ చెల్లదు + తప్పు పాస్‌వర్డ్ + చెల్లని ప్రమాణీకరణ కోడ్ + నాకు ఒక లింక్ ఇమెయిల్ చేయండి + ప్రస్తుత పాస్వర్డ్ + కొత్త పాస్వర్డ్ + కొత్త పాస్వర్డ్ (మళ్లీ టైప్ చేయండి) + కొత్త పాస్‌వర్డ్‌లు ఒకేలా లేవు + పాస్వర్డ్ బలం + గడియారం ప్రారంభ సమయం + గడియారం పెంపు + పరికరాలు + ఈ సమాచారం అవసరం + కాస్లింగ్ + తెలుపు O-O + నలుపు O-O + + %s ఫోరమ్ పోస్ట్ + %s ఫోరమ్ పోస్ట్‌లు + + ఆడుతూ గడిపిన సమయం: %s + ఆటలు చూడండి + టీవీలో ప్రదర్శించబడిన సమయం: %s + వీక్షించండి + వీడియో లైబ్రరీ + స్ట్రీమర్లు + మొబైల్ యాప్ + Webmasters + మా గురించి + %s గురించి + %1$s అనేది ఉచితమైన (%2$s), స్వేచ్ఛమైన, ప్రకటనలు లేని, ఓపెన్ సోర్స్ చెస్ సర్వర్. + నిజంగా + సహకరించండి + సేవా నిబంధనలు + సోర్స్ కోడ్ + Simultaneous exhibitions + హోస్ట్ + హోస్ట్ రంగు: %s + కొత్తగా సృష్టించబడిన simuls + కొత్త simul చేయండి + Simul కనుబడలేదు + ఈ simultaneous exhibition లేదు. + Simul హోమ్‌పేజీకి తిరిగి వెళ్ళు + Simuls ఒకే ఆటగాడు ఒకేసారి అనేక మంది ఆటగాళ్లను ఎదుర్కొంటాడు. + 50 మంది ప్రత్యర్థులలో, ఫిషర్ 47 గేమ్‌లు గెలిచాడు, 2 డ్రా చేసుకున్నాడు మరియు 1 ఓడిపోయాడు. diff --git a/translation/dest/storm/it-IT.xml b/translation/dest/storm/it-IT.xml index 66e236db85..2888fbf1b1 100644 --- a/translation/dest/storm/it-IT.xml +++ b/translation/dest/storm/it-IT.xml @@ -56,4 +56,6 @@ Questo mese Sempre Clicca per ricaricare + Questa serie è scaduta! + Questa serie è stata aperta in un\'altra scheda! diff --git a/translation/dest/storm/or-IN.xml b/translation/dest/storm/or-IN.xml index 27379a1835..71c684aad3 100644 --- a/translation/dest/storm/or-IN.xml +++ b/translation/dest/storm/or-IN.xml @@ -4,6 +4,7 @@ 1 ରନ୍ %s ରନ୍ + ସ୍କୋର୍ ସମୟ ଏକ ନୂତନ ଖେଳ ସୃଷ୍ଟି କରନ୍ତୁ ବାଦ୍ ଦିଅ diff --git a/translation/dest/streamer/lb-LU.xml b/translation/dest/streamer/lb-LU.xml index b656902365..6169aa9a20 100644 --- a/translation/dest/streamer/lb-LU.xml +++ b/translation/dest/streamer/lb-LU.xml @@ -21,8 +21,10 @@ Lies eis %s fir Fairplay fir jiddereen wärend denge Streams ze garantéieren. Streaming Fairplay FAQ Virdeeler fir Streaming mat engem Stéchwuert + Twitch Benotzernumm oder URL Iwwerschrëft Laang Beschreiwung + %s Streamer Bild Däin Bild änneren/läschen E Bild héichlueden Maximal Gréisst: %s diff --git a/translation/dest/study/hr-HR.xml b/translation/dest/study/hr-HR.xml index 9541dc2742..614e75d015 100644 --- a/translation/dest/study/hr-HR.xml +++ b/translation/dest/study/hr-HR.xml @@ -43,7 +43,7 @@ Postao si gledatelj PGN oznake Sviđa mi se - Ne sviđa mi se + Ne sviđa mi se Nova oznaka Komentiraj ovu poziciju Komentiraj ovaj potez @@ -131,4 +131,16 @@ Dali želite obrisati povijest razgovora? Nakon ovoga nema povratka! Izbriši studiju Gdje želiš to studirati? + Dobar potez + Greška + Briljantan potez + Gruba greška + Zanimljiv potez + Sumnjiv potez + Jedini potez + Nov potez + Razvoj + Inicijativa + Napad + Protunapad diff --git a/translation/dest/study/kk-KZ.xml b/translation/dest/study/kk-KZ.xml index 5f3ddfafb9..31d2c0ea5b 100644 --- a/translation/dest/study/kk-KZ.xml +++ b/translation/dest/study/kk-KZ.xml @@ -15,6 +15,7 @@ Құрылған күні (ескіден) Жақында құрылған Ең танымалдары + Әліппе ретімен Жаңа бөлім құру %s бөлім @@ -40,6 +41,7 @@ Сіз енді көрерменсіз PGN тэгтері Ұнату + Ұнатпаймын Жаңа тэг Осы тақта күйі туралы пікір қалдыру Осы жүріс туралы пікір қалдыру @@ -125,5 +127,33 @@ Чатты өшіру Зерттеудің чат тарихын өшіресіз бе? Кері жол жоқ! Зерттеуді жою + Бүкіл зерттеуді жоясыз ба? Қайтар жол жоқ. Растау үшін зерттеу атауын жазыңыз: %s Бұл күйдің зерттеуін қай жерде бастайсыз? + Жақсы жүріс + Қате + Әдемі жүріс + Өрескел қателік + Қызық жүріс + Күмәнді жүріс + Жалғыз жүріс + Цугцванг + Күйлері шамалас + Күйі анық емес + Ақ сәл күштірек + Қара сәл күштірек + Ақтың жағдайы жақсы + Қараның жағдайы жақсы + Ақ жеңеді + Қара жеңеді + Жаңашылдық + Дамыту + Белсенді + Шабуыл + Қарсы шабуыл + Уақыт қаупі + Өтеумен + Бір оймен + Келесі бөлім + Алдыңғы бөлім + Зерттеу әрекеттері diff --git a/translation/dest/ublog/ko-KR.xml b/translation/dest/ublog/ko-KR.xml index 3ea04e700d..030dcaf8a9 100644 --- a/translation/dest/ublog/ko-KR.xml +++ b/translation/dest/ublog/ko-KR.xml @@ -1,2 +1,11 @@ - + + %{%s} 님의 블로그 + 새 글 쓰기 + 제목 + 본문 + 임시 보관 글 + 게시된 글 + 이 블로그에는 아직 글이 없습니다. + 임시저장된 글이 없습니다. + From 4d9a60c2c065a82708d4eddce477a523c9fe643a Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Tue, 23 Nov 2021 07:41:14 +0100 Subject: [PATCH 086/144] New Crowdin updates (#10155) * New translations: site.xml (Bengali) * New translations: site.xml (Bengali) * New translations: puzzleTheme.xml (English, United States) * New translations: puzzle.xml (English, United States) * New translations: tourname.xml (English, United States) * New translations: site.xml (English, United States) * New translations: ublog.xml (English, United States) --- translation/dest/puzzle/en-US.xml | 2 +- translation/dest/puzzleTheme/en-US.xml | 6 +++--- translation/dest/site/bn-BD.xml | 19 +++++++++++++------ translation/dest/site/en-US.xml | 16 ++++++++-------- translation/dest/tourname/en-US.xml | 6 +++--- translation/dest/ublog/en-US.xml | 4 ++-- 6 files changed, 30 insertions(+), 23 deletions(-) diff --git a/translation/dest/puzzle/en-US.xml b/translation/dest/puzzle/en-US.xml index a0e353ca70..cf50c6b8e9 100644 --- a/translation/dest/puzzle/en-US.xml +++ b/translation/dest/puzzle/en-US.xml @@ -79,7 +79,7 @@ Play rapid and classical games to increase your chances of having a puzzle of yo %s to replay %s to replay - %s solved + %s solved Nothing to show, go play some puzzles first! Train these to optimize your progress! You perform the best in these themes diff --git a/translation/dest/puzzleTheme/en-US.xml b/translation/dest/puzzleTheme/en-US.xml index 8b6e2382ec..307363b078 100644 --- a/translation/dest/puzzleTheme/en-US.xml +++ b/translation/dest/puzzleTheme/en-US.xml @@ -51,7 +51,7 @@ A move where the moved piece attacks two opponent pieces at once. Hanging piece A tactic involving an opponent piece being undefended or insufficiently defended and free to capture. - Rook mate + Hook mate Checkmate with a rook, knight, and pawn along with one enemy pawn to limit the enemy king\'s escape. Interference Moving a piece between two opponent pieces to leave one or both opponent pieces undefended, such as a knight on a defended square between two rooks. @@ -107,7 +107,7 @@ A motif involving a high value piece being attacked, moving out the way, and allowing a lower value piece behind it to be captured or attacked, the inverse of a pin. Smothered mate A checkmate delivered by a knight in which the mated king is unable to move because it is surrounded (or smothered) by its own pieces. - Super GrandMaster games + Super GM games Puzzles from games played by the best players in the world. Trapped piece A piece is unable to escape capture as it has limited moves. @@ -121,7 +121,7 @@ The opponent is limited in the moves they can make, and all moves worsen their position. Healthy mix A bit of everything. You don\'t know what to expect, so you remain ready for anything! Just like in real games. - Player Games + Player games Lookup puzzles generated from your games, or from another player\'s games. These puzzles are in the public domain, and can be downloaded from %s. diff --git a/translation/dest/site/bn-BD.xml b/translation/dest/site/bn-BD.xml index 624697ab0d..13654c45e2 100644 --- a/translation/dest/site/bn-BD.xml +++ b/translation/dest/site/bn-BD.xml @@ -47,9 +47,12 @@ কালো হার মেনে নিলেন সাদা খেলাটি ছেড়ে চলে গেছেন কালো খেলাটি ছেড়ে চলে গেছেন + সাদা চাল দিল না + কালো চাল দিল না কম্পিউটার বিশ্লেষণের জন্য অবেদন করুন কম্পিউটারের বিশ্লেষণ কম্পিউটার বিশ্লেষণ পাওয়া যায় + কম্পিউটার বিশ্লেষণ নিষ্ক্রিয় করা আছে বিশ্লেষণ বোর্ড গভীরতা %s সার্ভারের বিশ্লেষণ ব্যবহার করা হচ্ছে @@ -153,7 +156,7 @@ খেলোয়াড় বন্ধুরা বার্তাগুলি - আজ + আজ গতকাল প্রতিটি পাশের জন্য বরাদ্দ মিনিট বিকল্প @@ -360,6 +363,8 @@ অনুসরণ অনুসরণ করছেন আর অনুসরণ নয় + %sকে অননুসরণ করুন + %sকে অননুসরণ করুন বাধা দিন বাধাগ্রস্ত বাধা উঠিয়ে নিন @@ -422,9 +427,9 @@ বোর্ড সম্পাদক বোর্ড সেট করুন জনপ্রিয় চমক - শুরুর অবস্থান + শুরুর অবস্থান বোর্ড খালি করুন - অবস্থান পুনঃস্থাপন + অবস্থান পুনঃস্থাপন ব্যক্তিগত মডারেটরদের কাছে %s এর নামে রিপোর্ট করুন প্রোফাইল সম্পন্ন: %s @@ -443,7 +448,7 @@ এর আগে Lichess TV- তে যা হয়েছে যে সমস্ত খেলোয়ার অনলাইন আছে সক্রিয় খেলোয়াড়গণ - সাবধান, এই খেলাটি মূল্যায়িত তবে কোনো সময়সীমা নেই! + সাবধান, এই খেলাটি মূল্যায়িত তবে কোনো সময়সীমা নেই! সফল্য হয়েছে %s খেলা চলছে @@ -512,7 +517,7 @@ খেলায় ধীর গতি সব সময় কখনই না - %1$s করা আছে মধ্যে %2$s + %1$s করা আছে মধ্যে %2$s বিজয় হার %1$s বানাম %2$s মধ্যে %3$s @@ -543,6 +548,8 @@ সরঞ্জামসমূহ মুনাফা এই ঘরটি পূরন করা আবশ্যক + ইমেইল ঠিকানাটি বৈধ নয় + ইমেইল ঠিকানাটি গ্রহণযোগ্য নয়। দয়া করে পুনরায় চেক করুন, অত‍ঃপর চেষ্টা করুন। যদি অনুপাত হয় ± %s বিদ্যমান কথোপকথন শুধু বন্ধুরা @@ -790,7 +797,7 @@ আপনি এখন ফোরামে পোষ্ট করতে পারবেন না. আগে কিছু গেম খেলুন! সাবস্ক্রাইব আনসাবস্ক্রাইব - \"%1$s\" এ আপনাকে উল্লেখ করেছেন। + \"%1$s\" এ আপনাকে উল্লেখ করেছেন। %1$s , \"%2$s\" এ আপনার নাম উল্লেখ করেছেন। \"%1$s\" এ আপনাকে আমন্ত্রণ জানিয়েছেন। %1$s, আপনাকে \"%2$s\" এ আমন্ত্রণ জানিয়েছেন। diff --git a/translation/dest/site/en-US.xml b/translation/dest/site/en-US.xml index 4881239dfc..08b4390a76 100644 --- a/translation/dest/site/en-US.xml +++ b/translation/dest/site/en-US.xml @@ -163,7 +163,7 @@ Players Friends Conversations - Today + Today Yesterday Minutes per side Variant @@ -436,9 +436,9 @@ computer analysis, game chat and shareable URL. Popular openings Endgame positions Chess960 start position: %s - Starting position + Starting position Clear board - Load position + Load position Private Report %s to moderators Profile completion: %s @@ -457,7 +457,7 @@ computer analysis, game chat and shareable URL. Previously on Lichess TV Online players Active players - Beware, the game is rated but has no clock! + Beware, the game is rated but has no clock! Success %s game in play @@ -526,7 +526,7 @@ computer analysis, game chat and shareable URL. On slow games Always Never - %1$s competes in %2$s + %1$s competes in %2$s Victory Defeat %1$s vs %2$s in %3$s @@ -561,10 +561,10 @@ computer analysis, game chat and shareable URL. This email address is not acceptable. Please double-check it, and try again. Email address invalid or already taken This is already your email address - Must be at least %s characters long + Must be at least %s characters long Must be at most %s characters long Must be greater or equal to %s - Must be at most %s + Must be at most %s If rating is ± %s Only existing conversations Only friends @@ -813,7 +813,7 @@ computer analysis, game chat and shareable URL. You can\'t post in the forums yet. Play some games! Subscribe Unsubscribe - mentioned you in \"%1$s\". + mentioned you in \"%1$s\". %1$s mentioned you in \"%2$s\". invited you to \"%1$s\". %1$s invited you to \"%2$s\". diff --git a/translation/dest/tourname/en-US.xml b/translation/dest/tourname/en-US.xml index 962c23c52d..78db850b6f 100644 --- a/translation/dest/tourname/en-US.xml +++ b/translation/dest/tourname/en-US.xml @@ -1,7 +1,7 @@ - Hourly Rapid Arena - Hourly Rapid Arena + Hourly Rapid Arena + Hourly Rapid Hourly %s Arena Hourly %s Daily Rapid Arena @@ -14,7 +14,7 @@ Eastern Rapid Eastern Classical Arena Eastern Classical - Elite %s Arena + Eastern %s Arena Eastern %s Weekly Rapid Arena Weekly Rapid diff --git a/translation/dest/ublog/en-US.xml b/translation/dest/ublog/en-US.xml index 45e7301b86..74034e2810 100644 --- a/translation/dest/ublog/en-US.xml +++ b/translation/dest/ublog/en-US.xml @@ -31,6 +31,6 @@ View all %s posts Upload an image for your post - Image alternative text - Image credit + Image alternative text + Image credit From b404824b3f2927e048063b32523d852e6f82d89e Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Tue, 23 Nov 2021 07:50:15 +0100 Subject: [PATCH 087/144] delete broken translation --- translation/dest/ublog/ko-KR.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/translation/dest/ublog/ko-KR.xml b/translation/dest/ublog/ko-KR.xml index 030dcaf8a9..99fea0d130 100644 --- a/translation/dest/ublog/ko-KR.xml +++ b/translation/dest/ublog/ko-KR.xml @@ -1,6 +1,5 @@ - %{%s} 님의 블로그 새 글 쓰기 제목 본문 From 8fbf30ef5904704aee12df1331b1b393b376bb29 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Tue, 23 Nov 2021 08:31:36 +0100 Subject: [PATCH 088/144] fix AI play outofbook detection --- modules/fishnet/src/main/FishnetOpeningBook.scala | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index cd7a39b1b3..4bfbe6082f 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -44,11 +44,9 @@ final private class FishnetOpeningBook( case res => for { data <- res.body[JsValue].validate[Response](responseReader).asOpt + _ = if (data.moves.isEmpty) outOfBook.put(game.id) move <- data.randomPonderedMove - } yield { - if (data.moves.isEmpty) outOfBook.put(game.id) - move.uci - } + } yield move.uci } .monTry { res => _.fishnet From 1964b739199a855f2dd101bf994363d4def45eee Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Tue, 23 Nov 2021 09:11:08 +0100 Subject: [PATCH 089/144] Revert "Show games in perfstat from player's perspective" This reverts commit a0c13389ce79fa559e194f04b6a9025e676a5e0c. use pov=username instead --- app/views/user/perfStat.scala | 14 ++++++++------ modules/perfStat/src/main/PerfStat.scala | 6 ++---- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/app/views/user/perfStat.scala b/app/views/user/perfStat.scala index aa59f156e5..a16eddfb47 100644 --- a/app/views/user/perfStat.scala +++ b/app/views/user/perfStat.scala @@ -62,7 +62,7 @@ object perfStat { counter(stat.count), highlow(stat), resultStreak(stat.resultStreak), - result(stat), + result(stat, user), playStreakNb(stat.playStreak), playStreakTime(stat.playStreak) ) @@ -259,7 +259,9 @@ object perfStat { resultStreakSide(streak.loss, losingStreak(), "red") ) - private def resultTable(results: lila.perfStat.Results, title: Frag)(implicit lang: Lang): Frag = + private def resultTable(results: lila.perfStat.Results, title: Frag, user: User)(implicit + lang: Lang + ): Frag = div( table( thead( @@ -272,7 +274,7 @@ object perfStat { tr( td(userIdLink(r.opId.value.some, withOnline = false), " (", r.opInt, ")"), td( - a(cls := "glpt", href := routes.Round.watcher(r.gameId, r.color.name))( + a(cls := "glpt", href := s"${routes.Round.watcher(r.gameId, "white")}?pov=${user.username}")( absClientDateTime(r.at) ) ) @@ -282,10 +284,10 @@ object perfStat { ) ) - private def result(stat: PerfStat)(implicit lang: Lang): Frag = + private def result(stat: PerfStat, user: User)(implicit lang: Lang): Frag = st.section(cls := "result split")( - resultTable(stat.bestWins, bestRated()), - resultTable(stat.worstLosses, worstRated()) + resultTable(stat.bestWins, bestRated(), user), + resultTable(stat.worstLosses, worstRated(), user) ) private def playStreakNbStreak(s: lila.perfStat.Streak, title: Frag => Frag)(implicit lang: Lang): Frag = diff --git a/modules/perfStat/src/main/PerfStat.scala b/modules/perfStat/src/main/PerfStat.scala index 6cf9cdea14..c212662932 100644 --- a/modules/perfStat/src/main/PerfStat.scala +++ b/modules/perfStat/src/main/PerfStat.scala @@ -1,6 +1,5 @@ package lila.perfStat -import chess.Color import org.joda.time.{ DateTime, Period } import lila.common.Heapsort @@ -197,7 +196,7 @@ object RatingAt { } orElse cur } -case class Result(opInt: Int, opId: UserId, at: DateTime, gameId: String, color: Color) +case class Result(opInt: Int, opId: UserId, at: DateTime, gameId: String) case class Results(results: List[Result]) extends AnyVal { def agg(pov: Pov, comp: Int) = @@ -211,8 +210,7 @@ case class Results(results: List[Result]) extends AnyVal { opInt, UserId(~pov.opponent.userId), pov.game.movedAt, - pov.gameId, - pov.player.color + pov.gameId ) :: results, Results.nb, Ordering.by[Result, Int](_.opInt * comp) From 9ef8924fa42407b5eb721527370591c988e6610f Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Tue, 23 Nov 2021 09:24:35 +0100 Subject: [PATCH 090/144] preload images without crossorigin --- app/views/base/layout.scala | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index 2b7e8a142d..85ae19538b 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -42,18 +42,20 @@ object layout { private val noTranslate = raw("""""") - private def preload(href: String, as: String, tpe: Option[String] = None) = - raw(s""" s"""type="$t" """)}crossorigin>""") + private def preload(href: String, as: String, crossorigin: Boolean, tpe: Option[String] = None) = + raw(s""" + s"""type="$t" """ + )}${crossorigin ?? "crossorigin"}>""") private def fontPreload(implicit ctx: Context) = frag( - preload(assetUrl(s"font/lichess.woff2"), "font", "font/woff2".some), + preload(assetUrl(s"font/lichess.woff2"), "font", crossorigin = true, "font/woff2".some), !ctx.pref.pieceNotationIsLetter option - preload(assetUrl(s"font/lichess.chess.woff2"), "font", "font/woff2".some) + preload(assetUrl(s"font/lichess.chess.woff2"), "font", crossorigin = true, "font/woff2".some) ) private def boardPreload(implicit ctx: Context) = frag( - preload(assetUrl(s"images/board/${ctx.currentTheme.file}"), "image"), + preload(assetUrl(s"images/board/${ctx.currentTheme.file}"), "image", crossorigin = false), ctx.pref.is3d option - preload(s"images/staunton/board/${ctx.currentTheme3d.file}", "image") + preload(s"images/staunton/board/${ctx.currentTheme3d.file}", "image", crossorigin = false) ) private def piecesPreload(implicit ctx: Context) = env.pieceImageExternal.get() option raw { @@ -61,7 +63,7 @@ object layout { c <- List('w', 'b') p <- List('K', 'Q', 'R', 'B', 'N', 'P') href = staticAssetUrl(s"piece/${ctx.currentPieceSet.name}/$c$p.svg") - } yield s"""""").mkString + } yield s"""""").mkString } private val manifests = raw( From 30e6aa705fcc992e48998ecd64e3842bbe707672 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 24 Nov 2021 08:12:58 +0100 Subject: [PATCH 091/144] AI play book: only decent moves --- .../fishnet/src/main/FishnetOpeningBook.scala | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index 4bfbe6082f..13efcf8590 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -2,7 +2,7 @@ package lila.fishnet import chess.format.Forsyth import chess.format.Uci -import chess.Speed +import chess.{ Color, Speed } import com.softwaremill.tagging._ import play.api.libs.json._ import play.api.libs.ws.JsonBodyReadables._ @@ -45,7 +45,7 @@ final private class FishnetOpeningBook( for { data <- res.body[JsValue].validate[Response](responseReader).asOpt _ = if (data.moves.isEmpty) outOfBook.put(game.id) - move <- data.randomPonderedMove + move <- data randomPonderedMove game.turnColor } yield move.uci } .monTry { res => @@ -66,11 +66,13 @@ object FishnetOpeningBook { trait Depth case class Response(moves: List[Move]) { - def randomPonderedMove: Option[Move] = { - val sum = moves.map(_.nb).sum - val novelty = 1 - val rng = ThreadLocalRandom.nextInt(sum + novelty) - moves + + def randomPonderedMove(turn: Color): Option[Move] = { + val decentMoves = moves.filter(_.lossRatioFor(turn) < 0.66) + val sum = decentMoves.map(_.nb).sum + val novelty = 1 + val rng = ThreadLocalRandom.nextInt(sum + novelty) + decentMoves .foldLeft((none[Move], 0)) { case ((found, it), next) => val nextIt = it + next.nb (found orElse (nextIt > rng).option(next), nextIt) @@ -80,7 +82,8 @@ object FishnetOpeningBook { } case class Move(uci: Uci, white: Int, draws: Int, black: Int) { - def nb = white + draws + black + def nb = white + draws + black + def lossRatioFor(color: Color): Float = (nb > 0) ?? color.fold(black, white).toFloat / nb } implicit val moveReader = Json.reads[Move] From b063b94f9fb8e027e9c01c385162dafd9bf3260a Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 24 Nov 2021 09:03:25 +0100 Subject: [PATCH 092/144] Don't abort unlimited AI games after 6 hours --- modules/game/src/main/Game.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/game/src/main/Game.scala b/modules/game/src/main/Game.scala index 5dbfe52efc..4bf24b8b44 100644 --- a/modules/game/src/main/Game.scala +++ b/modules/game/src/main/Game.scala @@ -555,7 +555,7 @@ case class Game( def abandoned = (status <= Status.Started) && { movedAt isBefore { - if (hasAi && !hasCorrespondenceClock) Game.aiAbandonedDate + if (hasAi && hasClock) Game.aiAbandonedDate else Game.abandonedDate } } From 92130afe8fb565d84ad88bc62748e8c1a53c1498 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 24 Nov 2021 13:10:56 +0100 Subject: [PATCH 093/144] allow low fishnet levels to play bad book moves --- .../fishnet/src/main/FishnetOpeningBook.scala | 20 ++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index 13efcf8590..b63ddb214f 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -45,7 +45,7 @@ final private class FishnetOpeningBook( for { data <- res.body[JsValue].validate[Response](responseReader).asOpt _ = if (data.moves.isEmpty) outOfBook.put(game.id) - move <- data randomPonderedMove game.turnColor + move <- data randomPonderedMove(game.turnColor, level) } yield move.uci } .monTry { res => @@ -67,14 +67,13 @@ object FishnetOpeningBook { case class Response(moves: List[Move]) { - def randomPonderedMove(turn: Color): Option[Move] = { - val decentMoves = moves.filter(_.lossRatioFor(turn) < 0.66) - val sum = decentMoves.map(_.nb).sum - val novelty = 1 + def randomPonderedMove(turn: Color, level: Int): Option[Move] = { + val sum = moves.map(_.score(turn, level)).sum + val novelty = 14 val rng = ThreadLocalRandom.nextInt(sum + novelty) - decentMoves + moves .foldLeft((none[Move], 0)) { case ((found, it), next) => - val nextIt = it + next.nb + val nextIt = it + next.score(turn, level) (found orElse (nextIt > rng).option(next), nextIt) } ._1 @@ -82,8 +81,11 @@ object FishnetOpeningBook { } case class Move(uci: Uci, white: Int, draws: Int, black: Int) { - def nb = white + draws + black - def lossRatioFor(color: Color): Float = (nb > 0) ?? color.fold(black, white).toFloat / nb + def score(turn: Color, level: Int) = + // interpolate: real frequency at lvl 1, expectation value at lvl 8 + 14 * turn.fold(white, black) + + (15 - level) * draws + + (16 - 2 * level) * turn.fold(black, white) } implicit val moveReader = Json.reads[Move] From f666b76658d9466ca48195b41106b6fa36ff7c3a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Wed, 24 Nov 2021 13:17:52 +0100 Subject: [PATCH 094/144] more frequently ignore book --- modules/fishnet/src/main/FishnetOpeningBook.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index b63ddb214f..80afbfd65d 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -69,7 +69,7 @@ object FishnetOpeningBook { def randomPonderedMove(turn: Color, level: Int): Option[Move] = { val sum = moves.map(_.score(turn, level)).sum - val novelty = 14 + val novelty = 5 * 14 // score of 5 winning games val rng = ThreadLocalRandom.nextInt(sum + novelty) moves .foldLeft((none[Move], 0)) { case ((found, it), next) => From cc79e29cbf34f2753e2fadec5c006c134409d961 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Wed, 24 Nov 2021 15:59:53 +0100 Subject: [PATCH 095/144] wiki: Fix length limit check --- ui/analyse/src/wiki.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/analyse/src/wiki.ts b/ui/analyse/src/wiki.ts index b818a90673..e39c660cb5 100644 --- a/ui/analyse/src/wiki.ts +++ b/ui/analyse/src/wiki.ts @@ -32,7 +32,7 @@ export default function wikiTheory(): WikiTheory { async (nodes: Tree.Node[]) => { const pathParts = nodes.slice(1).map(n => `${plyPrefix(n)}${n.san}`); const path = pathParts.join('/').replace(/[+!#?]/g, '') ?? ''; - if (pathParts.length > 30 || !path || path.length > 255) show(''); + if (pathParts.length > 30 || !path || path.length > 255 - 21) show(''); else if (cache.has(path)) show(cache.get(path)!); else if ( Array.from({ length: pathParts.length }, (_, i) => -i - 1) From d22d1fd0b1c1c2afd4bf1d4278106b31125eb6c8 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Wed, 24 Nov 2021 16:09:00 +0100 Subject: [PATCH 096/144] handle usernames in Round.watcher pov query parameter --- app/controllers/Round.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/controllers/Round.scala b/app/controllers/Round.scala index 4c660e1085..5102d54314 100644 --- a/app/controllers/Round.scala +++ b/app/controllers/Round.scala @@ -131,7 +131,7 @@ final class Round( Open { implicit ctx => proxyPov(gameId, color) flatMap { case Some(pov) => - get("pov") match { + get("pov").map(UserModel.normalize) match { case Some(requestedPov) => (pov.player.userId, pov.opponent.userId) match { case (Some(_), Some(opponent)) if opponent == requestedPov => From 270f54f69125e7d2f7831cdcd82a93efef4220c2 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 25 Nov 2021 09:47:01 +0100 Subject: [PATCH 097/144] add appeals to mod queue stats --- modules/mod/src/main/ModQueueStats.scala | 42 +++++++++++++----------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/modules/mod/src/main/ModQueueStats.scala b/modules/mod/src/main/ModQueueStats.scala index 4b65053074..78bb497f41 100644 --- a/modules/mod/src/main/ModQueueStats.scala +++ b/modules/mod/src/main/ModQueueStats.scala @@ -51,11 +51,10 @@ final class ModQueueStats( data <- doc.getAsOpt[List[Bdoc]]("data") } yield date -> { for { - entry <- data - nb <- entry.int("nb") - roomStr <- entry.string("room") - room <- Room.byKey get roomStr - score <- entry.int("score") + entry <- data + nb <- entry.int("nb") + room <- entry.string("room") + score <- entry.int("score") } yield (room, score, nb) } } @@ -66,20 +65,25 @@ final class ModQueueStats( "common" -> Json.obj( "xaxis" -> days.map(_._1.getMillis) ), - "rooms" -> Room.all.map { room => - Json.obj( - "name" -> room.name, - "series" -> scores.collect { - case score if score > 20 || room == Room.Boost => - Json.obj( - "name" -> score, - "data" -> days.map(~_._2.collectFirst { - case (r, s, nb) if r == room && s == score => nb - }) - ) - } - ) - } + "rooms" -> Room.all + .map { room => + room.key -> room.name + } + .appended { ("appeal", "Appeal") } + .map { case (roomKey, roomName) => + Json.obj( + "name" -> roomName, + "series" -> scores.collect { + case score if score > 20 || roomKey == Room.Boost.key => + Json.obj( + "name" -> score, + "data" -> days.map(~_._2.collectFirst { + case (r, s, nb) if r == roomKey && s == score => nb + }) + ) + } + ) + } ) ) } From 6789676ca569baa11e6cab3b0d01e95ee4903432 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Thu, 25 Nov 2021 18:54:02 +0100 Subject: [PATCH 098/144] Update specs2-core to 4.13.1 --- project/Dependencies.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 4f5be86ade..0b46f01838 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -21,7 +21,7 @@ object Dependencies { val autoconfig = "io.methvin.play" %% "autoconfig-macros" % "0.3.2" % "provided" val scalatest = "org.scalatest" %% "scalatest" % "3.1.0" % Test val uaparser = "org.uaparser" %% "uap-scala" % "0.13.0" - val specs2 = "org.specs2" %% "specs2-core" % "4.13.0" % Test + val specs2 = "org.specs2" %% "specs2-core" % "4.13.1" % Test val apacheText = "org.apache.commons" % "commons-text" % "1.9" val bloomFilter = "com.github.alexandrnikitin" %% "bloom-filter" % "0.13.1" From c8701601ab5ca50e42af3d836bdcaca04639f0ba Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Thu, 25 Nov 2021 19:10:27 +0100 Subject: [PATCH 099/144] add crawler UA --- modules/common/src/main/HTTPRequest.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/common/src/main/HTTPRequest.scala b/modules/common/src/main/HTTPRequest.scala index d5a5361fb6..af04590827 100644 --- a/modules/common/src/main/HTTPRequest.scala +++ b/modules/common/src/main/HTTPRequest.scala @@ -58,7 +58,7 @@ object HTTPRequest { def sid(req: RequestHeader): Option[String] = req.session get LilaCookie.sessionId val isCrawler = UaMatcher { - """(?i)googlebot|googlebot-mobile|googlebot-image|mediapartners-google|bingbot|slurp|java|wget|curl|commons-httpclient|python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|adidxbot|blekkobot|teoma|ia_archiver|gingercrawler|webmon|httrack|webcrawler|fast-webcrawler|fastenterprisecrawler|convera|biglotron|grub\.org|usinenouvellecrawler|antibot|netresearchserver|speedy|fluffy|jyxobot|bibnum\.bnf|findlink|exabot|gigabot|msrbot|seekbot|ngbot|panscient|yacybot|aisearchbot|ioi|ips-agent|tagoobot|mj12bot|dotbot|woriobot|yanga|buzzbot|mlbot|purebot|lingueebot|yandex\.com/bots|""" + + """(?i)googlebot|googlebot-mobile|googlebot-image|mediapartners-google|bingbot|slurp|java|wget|curl|python-requests|commons-httpclient|python-urllib|libwww|httpunit|nutch|phpcrawl|msnbot|adidxbot|blekkobot|teoma|ia_archiver|gingercrawler|webmon|httrack|webcrawler|fast-webcrawler|fastenterprisecrawler|convera|biglotron|grub\.org|usinenouvellecrawler|antibot|netresearchserver|speedy|fluffy|jyxobot|bibnum\.bnf|findlink|exabot|gigabot|msrbot|seekbot|ngbot|panscient|yacybot|aisearchbot|ioi|ips-agent|tagoobot|mj12bot|dotbot|woriobot|yanga|buzzbot|mlbot|purebot|lingueebot|yandex\.com/bots|""" + """voyager|cyberpatrol|voilabot|baiduspider|citeseerxbot|spbot|twengabot|postrank|turnitinbot|scribdbot|page2rss|sitebot|linkdex|ezooms|dotbot|mail\.ru|discobot|zombie\.js|heritrix|findthatfile|europarchive\.org|nerdbynature\.bot|sistrixcrawler|ahrefsbot|aboundex|domaincrawler|wbsearchbot|summify|ccbot|edisterbot|seznambot|ec2linkfinder|gslfbot|aihitbot|intelium_bot|yeti|retrevopageanalyzer|lb-spider|sogou|lssbot|careerbot|wotbox|wocbot|ichiro|duckduckbot|lssrocketcrawler|drupact|webcompanycrawler|acoonbot|openindexspider|gnamgnamspider|web-archive-net\.com\.bot|backlinkcrawler|""" + """coccoc|integromedb|contentcrawlerspider|toplistbot|seokicks-robot|it2media-domain-crawler|ip-web-crawler\.com|siteexplorer\.info|elisabot|proximic|changedetection|blexbot|arabot|wesee:search|niki-bot|crystalsemanticsbot|rogerbot|360spider|psbot|interfaxscanbot|lipperheyseoservice|ccmetadatascaper|g00g1e\.net|grapeshotcrawler|urlappendbot|brainobot|fr-crawler|binlar|simplecrawler|simplecrawler|livelapbot|twitterbot|cxensebot|smtbot|facebookexternalhit|daumoa|sputnikimagebot|visionutils|yisouspider|parsijoobot|mediatoolkit\.com|semrushbot""" } From 246b871237657e260cdaefcd8bce32c87d8b2d84 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Fri, 26 Nov 2021 15:27:29 +0100 Subject: [PATCH 100/144] Fix /api/bot/online route (move it before /api/bot/*cmd) --- conf/routes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/conf/routes b/conf/routes index 3f72505716..2fc8fe6e00 100644 --- a/conf/routes +++ b/conf/routes @@ -707,10 +707,10 @@ GET /api/user/:name/games controllers.Api.userGames(name: String) # Bot API GET /api/bot/game/stream/:id controllers.PlayApi.botGameStream(id: String) POST /api/bot/game/:id/move/:uci controllers.PlayApi.botMove(id: String, uci: String, offeringDraw: Option[Boolean] ?= None) +GET /api/bot/online controllers.PlayApi.botOnlineApi POST /api/bot/*cmd controllers.PlayApi.botCommand(cmd: String) GET /api/bot/*cmd controllers.PlayApi.botCommandGet(cmd: String) GET /player/bots controllers.PlayApi.botOnline -GET /api/bot/online controllers.PlayApi.botOnlineApi # Board API GET /api/board/game/stream/:id controllers.PlayApi.boardGameStream(id: String) From ea256b836bb62ffa6d96a75bbfe2f50a0721cb8c Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 26 Nov 2021 17:22:54 +0100 Subject: [PATCH 101/144] upper user limit for ceval --- ui/analyse/src/evalCache.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/analyse/src/evalCache.ts b/ui/analyse/src/evalCache.ts index e38e9a3ef6..289e2b3c13 100644 --- a/ui/analyse/src/evalCache.ts +++ b/ui/analyse/src/evalCache.ts @@ -73,7 +73,7 @@ function toCeval(e: Tree.ServerEval): Tree.ClientEval { export function make(opts: EvalCacheOpts): EvalCache { let fetchedByFen: Dictionary = {}; const upgradable = prop(false); - lichess.pubsub.on('socket.in.crowd', d => upgradable(d.nb > 2)); + lichess.pubsub.on('socket.in.crowd', d => upgradable(d.nb > 2 && d.nb < 99999)); return { onCeval: throttle(500, function () { const node = opts.getNode(), From 87ef26b85675da003089dc22a17b58c0ab2efa03 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 26 Nov 2021 17:31:17 +0100 Subject: [PATCH 102/144] remove spaces from settings keys --- app/Env.scala | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/app/Env.scala b/app/Env.scala index 368d6ea553..03cfff51e2 100644 --- a/app/Env.scala +++ b/app/Env.scala @@ -117,18 +117,18 @@ final class Env( text = "Team IDs that always get their tournaments visible on /tournament. Separated by commas.".some ) lazy val prizeTournamentMakers = memo.settingStore[UserIds]( - "prizeTournamentMakers ", + "prizeTournamentMakers", default = UserIds(Nil), text = "User IDs who can make prize tournaments (arena & swiss) without a warning. Separated by commas.".some ) lazy val apiExplorerGamesPerSecond = memo.settingStore[Int]( - "apiExplorerGamesPerSecond ", - default = 200, + "apiExplorerGamesPerSecond", + default = 300, text = "Opening explorer games per second".some ) lazy val pieceImageExternal = memo.settingStore[Boolean]( - "pieceImageExternal ", + "pieceImageExternal", default = false, text = "Use external piece images".some ) From af4efb40b3c3d216dc86d09586ac67be91310eca Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 26 Nov 2021 17:37:45 +0100 Subject: [PATCH 103/144] cloud eval live setting --- app/controllers/Dev.scala | 3 ++- modules/evalCache/src/main/Env.scala | 7 ++++++ .../evalCache/src/main/EvalCacheUpgrade.scala | 22 ++++++++++--------- 3 files changed, 21 insertions(+), 11 deletions(-) diff --git a/app/controllers/Dev.scala b/app/controllers/Dev.scala index 2f6d4460b0..fff8b67fe9 100644 --- a/app/controllers/Dev.scala +++ b/app/controllers/Dev.scala @@ -27,7 +27,8 @@ final class Dev(env: Env) extends LilaController(env) { env.noDelaySecretSetting, env.featuredTeamsSetting, env.prizeTournamentMakers, - env.pieceImageExternal + env.pieceImageExternal, + env.evalCache.enable ) def settings = diff --git a/modules/evalCache/src/main/Env.scala b/modules/evalCache/src/main/Env.scala index 6ea164e8f4..97c5df1c7f 100644 --- a/modules/evalCache/src/main/Env.scala +++ b/modules/evalCache/src/main/Env.scala @@ -16,6 +16,7 @@ final class Env( userRepo: lila.user.UserRepo, yoloDb: lila.db.AsyncDb @@ lila.db.YoloDb, cacheApi: lila.memo.CacheApi, + settingStore: lila.memo.SettingStore.Builder, scheduler: akka.actor.Scheduler )(implicit ec: scala.concurrent.ExecutionContext, @@ -27,6 +28,12 @@ final class Env( private lazy val truster = wire[EvalCacheTruster] + lazy val enable = settingStore[Boolean]( + "useCeval", + default = true, + text = "Enable cloud eval (disable in case of server trouble)".some + ) + private lazy val upgrade = wire[EvalCacheUpgrade] lazy val api: EvalCacheApi = wire[EvalCacheApi] diff --git a/modules/evalCache/src/main/EvalCacheUpgrade.scala b/modules/evalCache/src/main/EvalCacheUpgrade.scala index 1b3066d623..ce31bb558b 100644 --- a/modules/evalCache/src/main/EvalCacheUpgrade.scala +++ b/modules/evalCache/src/main/EvalCacheUpgrade.scala @@ -9,12 +9,13 @@ import lila.socket.Socket import lila.memo.ExpireCallbackMemo import scala.collection.mutable +import lila.memo.SettingStore /* Upgrades the user's eval when a better one becomes available, * by remembering the last evalGet of each socket member, * and listening to new evals stored. */ -final private class EvalCacheUpgrade(scheduler: akka.actor.Scheduler)(implicit +final private class EvalCacheUpgrade(setting: SettingStore[Boolean], scheduler: akka.actor.Scheduler)(implicit ec: scala.concurrent.ExecutionContext, mode: play.api.Mode ) { @@ -26,17 +27,18 @@ final private class EvalCacheUpgrade(scheduler: akka.actor.Scheduler)(implicit private val upgradeMon = lila.mon.evalCache.upgrade - def register(sri: Socket.Sri, variant: Variant, fen: FEN, multiPv: Int, path: String)(push: Push): Unit = { - members get sri.value foreach { wm => - unregisterEval(wm.setupId, sri) + def register(sri: Socket.Sri, variant: Variant, fen: FEN, multiPv: Int, path: String)(push: Push): Unit = + if (setting.get()) { + members get sri.value foreach { wm => + unregisterEval(wm.setupId, sri) + } + val setupId = makeSetupId(variant, fen, multiPv) + members += (sri.value -> WatchingMember(push, setupId, path)) + evals += (setupId -> (~evals.get(setupId) + sri.value)) + expirableSris put sri.value } - val setupId = makeSetupId(variant, fen, multiPv) - members += (sri.value -> WatchingMember(push, setupId, path)) - evals += (setupId -> (~evals.get(setupId) + sri.value)) - expirableSris put sri.value - } - def onEval(input: EvalCacheEntry.Input, sri: Socket.Sri): Unit = { + def onEval(input: EvalCacheEntry.Input, sri: Socket.Sri): Unit = if (setting.get()) { (1 to input.eval.multiPv) flatMap { multiPv => evals get makeSetupId(input.id.variant, input.fen, multiPv) } foreach { sris => From e00eca51120e82cde701e376f98b59aa76a6520c Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 26 Nov 2021 18:59:09 +0100 Subject: [PATCH 104/144] don't GC new users twice --- .../security/src/main/GarbageCollector.scala | 40 +++++++++++-------- 1 file changed, 23 insertions(+), 17 deletions(-) diff --git a/modules/security/src/main/GarbageCollector.scala b/modules/security/src/main/GarbageCollector.scala index 53a5dbf53e..77cb76123a 100644 --- a/modules/security/src/main/GarbageCollector.scala +++ b/modules/security/src/main/GarbageCollector.scala @@ -97,25 +97,31 @@ final class GarbageCollector( private def isBadAccount(user: User) = user.lameOrTrollOrAlt - private def collect(user: User, email: EmailAddress, msg: => String): Funit = - justOnce(user.id) ?? { - val armed = isArmed() - val wait = (30 + ThreadLocalRandom.nextInt(300)).seconds - val message = - s"Will dispose of @${user.username} in $wait. Email: ${email.value}. $msg${!armed ?? " [SIMULATION]"}" - logger.info(message) - noteApi.lichessWrite(user, s"Garbage collected because of $msg") - irc.garbageCollector(message) >>- { - if (armed) { - doInitialSb(user) - system.scheduler - .scheduleOnce(wait) { - doCollect(user) - } - .unit + private def collect(user: User, email: EmailAddress, msg: => String): Funit = justOnce(user.id) ?? { + hasBeenCollectedBefore(user) flatMap { + case true => funit + case _ => + val armed = isArmed() + val wait = (30 + ThreadLocalRandom.nextInt(300)).seconds + val message = + s"Will dispose of @${user.username} in $wait. Email: ${email.value}. $msg${!armed ?? " [SIMULATION]"}" + logger.info(message) + noteApi.lichessWrite(user, s"Garbage collected because of $msg") + irc.garbageCollector(message) >>- { + if (armed) { + doInitialSb(user) + system.scheduler + .scheduleOnce(wait) { + doCollect(user) + } + .unit + } } - } } + } + + private def hasBeenCollectedBefore(user: User): Fu[Boolean] = + noteApi.byUserForMod(user.id).map(_.exists(_.text startsWith "Garbage collected")) private def doInitialSb(user: User): Unit = Bus.publish( From e6b19993eeac7f9f1821c44eb2cb77fb57a03cf7 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sat, 27 Nov 2021 07:51:37 +0100 Subject: [PATCH 105/144] New Crowdin updates (#10161) * New translations: site.xml (Armenian) * New translations: site.xml (Gujarati) * New translations: site.xml (Afrikaans) * New translations: site.xml (Afrikaans) * New translations: site.xml (Afrikaans) * New translations: site.xml (Afrikaans) * New translations: site.xml (Afrikaans) * New translations: site.xml (German, Switzerland) * New translations: site.xml (German, Switzerland) * New translations: study.xml (German, Switzerland) * New translations: patron.xml (German, Switzerland) * New translations: preferences.xml (German, Switzerland) * New translations: storm.xml (German, Switzerland) * New translations: ublog.xml (German, Switzerland) * New translations: storm.xml (German, Switzerland) * New translations: ublog.xml (German, Switzerland) * New translations: streamer.xml (German, Switzerland) * New translations: swiss.xml (German, Switzerland) * New translations: site.xml (Sinhala) * New translations: emails.xml (Afar) --- translation/dest/emails/aa-ER.xml | 6 ++- translation/dest/patron/de-CH.xml | 2 +- translation/dest/preferences/de-CH.xml | 1 + translation/dest/site/af-ZA.xml | 65 ++++++++++++++++++++++++-- translation/dest/site/de-CH.xml | 34 +++++++------- translation/dest/site/gu-IN.xml | 10 ++++ translation/dest/site/hy-AM.xml | 12 +++-- translation/dest/site/si-LK.xml | 3 +- translation/dest/storm/de-CH.xml | 6 +++ translation/dest/streamer/de-CH.xml | 2 +- translation/dest/study/de-CH.xml | 1 + translation/dest/swiss/de-CH.xml | 10 ++-- translation/dest/ublog/de-CH.xml | 10 ++-- 13 files changed, 122 insertions(+), 40 deletions(-) diff --git a/translation/dest/emails/aa-ER.xml b/translation/dest/emails/aa-ER.xml index 3ea04e700d..9607eff19d 100644 --- a/translation/dest/emails/aa-ER.xml +++ b/translation/dest/emails/aa-ER.xml @@ -1,2 +1,6 @@ - + + Bevestig jou lichess.org-rekening, % + Klik op die skakel om jou Lichess-rekening te aktiveer. + As jy nie by Lichess geregistreer het nie, kan jy hierdie boodskap veilig ignoreer. + diff --git a/translation/dest/patron/de-CH.xml b/translation/dest/patron/de-CH.xml index 9925eb7045..19838f1ad1 100644 --- a/translation/dest/patron/de-CH.xml +++ b/translation/dest/patron/de-CH.xml @@ -1,7 +1,7 @@ Schpändä - Spende als %s + Spändä als %s Lichess Patroon Kostenloses Konto Wird Lichess Patroon diff --git a/translation/dest/preferences/de-CH.xml b/translation/dest/preferences/de-CH.xml index effdce4b3c..fa46da7c00 100644 --- a/translation/dest/preferences/de-CH.xml +++ b/translation/dest/preferences/de-CH.xml @@ -12,6 +12,7 @@ Schachfiguräsymbool Buächschtabä (K, Q, R, B, N) Zän Modus + Wertigszahl vo Spieler ahzeigä Regler zum Ändärä vodä Brättgrössi aazeigä Nur idä Aafangsschtellig Blindschach (unsichtbari Figurä) diff --git a/translation/dest/site/af-ZA.xml b/translation/dest/site/af-ZA.xml index 08b6b24e8a..e5acce2d21 100644 --- a/translation/dest/site/af-ZA.xml +++ b/translation/dest/site/af-ZA.xml @@ -56,7 +56,7 @@ Ontledingsbord Diepte %s Die gebruik van bediener analise - Enjin is besig om the laai ... + Enjin laai tans ... Wolkanalise Gaan dieper Toon bedreiging @@ -70,7 +70,7 @@ Variant verloor Variant wen Onvoldoende materiaal - Pion skuif + Pionskuif Buiting Maak toe Besig om te wen @@ -294,7 +294,7 @@ Boekmerk die spel Toernooi Toernooie - Toernooi punte + Toernooipunte %s toernooipunt %s toernooipunte @@ -351,7 +351,7 @@ Plak \'n wedstryd PGN om dit deursoekbaar te herspeel, rekenaar analise, kletskamer en deelbare URL te kry. - %s het spel ingevoer + %s ingevoerde spel %s het spelle ingetrek Hierdie is \'n skaak CAPTCHA. @@ -392,7 +392,7 @@ rekenaar analise, kletskamer en deelbare URL te kry. Lys Grafiek - Minder as %s minute + Minder as %s minuut Minder as %s minute Vereiste. @@ -653,6 +653,7 @@ rekenaar analise, kletskamer en deelbare URL te kry. Jy kan ook blaai oor die bord om te beweeg in die spel. Druk shift+klik of regs-kliek om sirkels en pyle te trek op die bord. Laat ander spelers boodskappe aan jou stuur + Laat weet wanneer in die forum genoem word Deel jou data oor Skaak Insigte Met niemand Met vriende @@ -663,6 +664,7 @@ rekenaar analise, kletskamer en deelbare URL te kry. Skakel kindermodus aan Skakel kindermodus aan Sekuriteit + Sessies alle sessies herroep Speel skaak oral So gratis soos Lichess @@ -810,4 +812,57 @@ rekenaar analise, kletskamer en deelbare URL te kry. U kan nog nie in die forums plaas nie. Speel \'n paar speletjies! Skryf in Beëindig inskrywing + het jou genoem in \"%1$s\". + %1$s het jou genoem in \"%2$s\". + het jou genooi na \"%1$s\". + %1$s het jou genooi na \"%2$s\". + Jy is nou deel van die span. + Jy het by \"%1$s\" aangesluit. + Iemand wat jy aangemeld het, is verban + Baie geluk, jy het gewen! + Spel teen %1$s + %1$s teen %2$s + Iemand het jou afrigtersprofiel resenseer. + Nuwe hangende resensie + Jy het teen iemand verloor wat nie die Lichess bepalings nagekom het nie + Terugbetaling:%1$s %2$s graderingspunte. + Tyd is byna op! + [Klik om e-posadres te beskou] + Laai af + Welkom! + Lichess is \'n liefdadigheidsorganisasie en heeltemal gratis/libre oopbron sagteware. +Alle bestuurskostes, ontwikkeling en inhoud word heeltemal gefinansier deur lede bydraes. + Afrigter bestuurder + Uitsender bestuurder + Kanseleer die toernooi + Toernooi beskrywing + Enige iets spesiaal wat jy aan die deelnemers wil vertel? Probeer dit kort hou. \"Markdown\" skakels is beskikbaar: [name](https://url) + Spelle is gegradeer +en beïnvloed speler graderings + Slegs lede van span + Geen beperking + Minimum gegradeerde spelle + Minimum gradering + Maksimum weeklikse gradering + Slegs getitelde spelers + Benodig \'n amptelike titel om by die toernooi aan te sluit + Plak \'n geldige FEN om elke spel van \'n sekere posisie te begin. +Dit werk slegs met standaard spelle, nie met variante nie. +Jy kan die %s gebruik om \'n FEN posisie te genereer, en dit dan hier plak. +Laat dit leeg om elke spel vanaf die normale posisie te begin. + Kanseleer die simul + Gasheer kleur vir elke spel + Verwagte begin tyd + Vertoon op %s + Wys jou simul aan almal op %s. Deaktiveer vir private simuls. + Simul beskrywing + Enige iets wat jy vir die deelnemers wil vertel? + %s is beskikbaar vir meer ingewikkelde sintaks. + Plak \'n spel URL of a studie hoofstuk URL om dit in te bed. + In you eie plaaslike tydsone + Toernooi klets + Geen klets + Slegs spanleiers + Slegs spanlede + Speel die beste rekenaarskuif diff --git a/translation/dest/site/de-CH.xml b/translation/dest/site/de-CH.xml index d9a84d03f6..d0a5f6ac7b 100644 --- a/translation/dest/site/de-CH.xml +++ b/translation/dest/site/de-CH.xml @@ -87,7 +87,7 @@ Matt i %s Halbzug Matt i %s Halbzüg - DTZ50\'\' mit Rundung, basierend auf der Anzahl der Halbzüge bis zum nächsten Schlag- oder Bauernzug + DTZ50\'\' mit Runde, basierend uf dä Ahzahl Halbzüüg bis zum nächstä Schlag- oder Puurezug Käs Schpiil gfundä Villicht söted meh Schpiil vom Iischteligs Menu iibezogä werdä? Eröffnigsdatäbank @@ -95,9 +95,9 @@ %s Eröffnigsbuäch Sieg dur d 50-Züg-Reglä verhinderet Verluscht dur d 50-Züg-Reglä verhinderät - Sieg oder 50 Züge durch vorherigen Fehler + Siig odär 50 Züüg dur en vorherigä Fählär Verlust odär 50 Züüg dur en vorherigä Fählär - Gewinn/Verlust ist nur garantiert, falls die empfohlene Datenbank-Zugfolge seit dem letzten Schlag- oder Bauernzug befolgt wurde, da möglicherweise DTZ-Werte in den Syzygy-Datenbanken gerundet sind. + Gwünn/Verlust isch nur garantiert, falls diä empfohlänä Datebank-Zugfolge sit äm letztä Schlag- oder Puurezug befolgt worde sind, da möglicherwiis DTZ-Wärt ih de Syzygy-Datebank grundet sind. Färtig! PGN importierä Löschä @@ -163,7 +163,7 @@ Schpiller/-innä Fründä Konvärsazion - Hüt + Hüt Geschter Minutä pro Schpiller/-in Variantä @@ -435,9 +435,9 @@ Beliäbti Eröffnigä Ändschpiilpositionä Fischer Random Aafangsschtellig: %s - Aafangsposition + Aafangsposition Brätt löschä - Schtellig ladä + Schtellig ladä Privaat Mäld %s dä Moderatorä Profil-Vollschtändigkeit: %s @@ -456,7 +456,7 @@ Zletscht uf Lichess TV Schpiller/-innä online Aktivi Schpiller/-innä - Obacht, das Schpiil isch gwärtät, hät aber keis Ziitlimit! + Obacht, das Schpiil isch gwärtät, hät aber keis Ziitlimit! Jawoll %s laufändi Partii @@ -525,7 +525,7 @@ Bi langsamä Partiiä Schtändig Niä - %1$s nimmt bi %2$s teil + %1$s nimmt bi %2$s teil Siig Niderlag %1$s gäg %2$s in %3$s @@ -556,14 +556,14 @@ Wärchzüüg Inkremänt Das Fäld isch Pflicht - Diese E-Mail-Adresse ist ungültig - Diese E-Mail-Adresse ist nicht akzeptabel. Bitte überprüfe sie und versuche es erneut. - E-Mail-Adresse ungültig oder bereits vergeben - Dies ist bereits deine E-Mail Adresse - Mindestlänge ist %s - Maximallänge ist %s - Muss mindestens %s sein - Darf höchstens %s sein + Diä E-Mail-Adrässä isch ungültig + Diä E-Mail-Adrässe wird nöd akzeptiärt. Bitte überprüäf si und probiär dänn nomol. + Diä E-Mail-Adrässe isch invalid oder scho vergäh + Das isch scho dini E-Mail-Adrässä + Mues mindestens %s Zeichä lang sii + Mues höchstens %s Zeichä lang sii + Mues mindestens %s sii + Mues höchstens %s sii Nur, wänn d\'Wärtigszahl ± %s isch Nur bestehändi Konversazionä Nur Fründä @@ -812,7 +812,7 @@ Du chasch nonig idä Forä schriibä. Schpill zersch äs paar Partiiä! Abonniärä Deabonniärä - hät dich in \"%1$s\" ärwähnt. + hät dich in \"%1$s\" ärwähnt. %1$s hät dich in \"%2$s\" ärwähnt. hät dich zu \"%1$s\" iigladä. %1$s hät dich zu \"%2$s\" iigladä. diff --git a/translation/dest/site/gu-IN.xml b/translation/dest/site/gu-IN.xml index fbd4b8990f..998bee2144 100644 --- a/translation/dest/site/gu-IN.xml +++ b/translation/dest/site/gu-IN.xml @@ -24,6 +24,9 @@ તમે સફેદ ટુકડાઓ સાથે રમસો તમે કાળા ટુકડાઓ સાથે રમસો તમારો વારો! + આ રમત સમાપ્તિની સ્થિતિ છે, જો રમત ચાલુ હોય ત્યારે કોઈ ખેલાડી છેતરપિંડી કરે છે. રમત તરત જ સમાપ્ત થાય છે. + + અનુવાદ નિષ્ક્રિય અવાજમાં હોવો જોઈએ કારણ કે ખેલાડીઓ અને દર્શકો બંને તેને જુએ છે. કૃપા કરીને શક્ય તેટલું ટૂંકું રાખો. મધ્યમાં રાજા ત્રણ ચેક રેસ પુરી થઇ ગઇ @@ -86,12 +89,17 @@ મેટ %s ચાલ માં મેટ %s ચાલ માં + DTZ50\'\' રાઉન્ડિંગ સાથે, આગામી કેપ્ચર અથવા પ્યાદાની ચાલ સુધી અર્ધ-ચાલની સંખ્યાના આધારે ડેટાબેઝમાં આવી કોઈ રમત નથી પસંદગી મેનૂમાંથી વધુ રમતો શામેલ કરીને જોવો? રમત પ્રારંભ સંશોધક + ઓપનિંગ/એન્ડગેમ એક્સપ્લોરર %s રમત પ્રારંભ સંશોધક જીતને 50-ચાલના નિયમ દ્વારા અટકાવવામાં આવી છે 50-ચાલના નિયમ દ્વારા હાર અટકાવવામાં આવી છે + અગાઉની ભૂલથી જીત અથવા 50 ચાલ + અગાઉની ભૂલથી નુકશાન અથવા 50 ચાલ + Syzygy ટેબલબેસેસમાં DTZ મૂલ્યોના સંભવિત રાઉન્ડિંગને કારણે, છેલ્લી કેપ્ચર અથવા પ્યાદાની ચાલથી ભલામણ કરેલ ટેબલબેઝ લાઇનને અનુસરવામાં આવે તો જ જીત/હારની ખાતરી આપવામાં આવે છે. બધું સેટ છે! PGN આયાત કરો કાઢી નાખો @@ -125,6 +133,8 @@ ડ્રો માગણું ડ્રો પ્રસ્તાવો ડ્રો + પરસ્પર કરાર દ્વારા દોરો + પ્રગતિ વિના પચાસ ચાલ વર્તમાન રમતો %s રમત diff --git a/translation/dest/site/hy-AM.xml b/translation/dest/site/hy-AM.xml index fc1ca4ab05..76df9da0b0 100644 --- a/translation/dest/site/hy-AM.xml +++ b/translation/dest/site/hy-AM.xml @@ -158,7 +158,7 @@ Խաղացողներ Ընկերներ Հաղորդագրություններ - Այսօր + Այսօր Երեկ Րոպեներ ամեն կողմի համար Տարբերակ @@ -318,6 +318,8 @@ Դուք պետք է խաղաք ևս %s վարկանիշային խաղ Ձեր վարկանիշը %s-ում դեռևս վավեր չէ + Ձեր ամենշաբաթյա վարկանիշը %1$sում (%2$s) չափազանց բարձր է + Ձեր վարկանիշը %1$sին (%2$s) մասնակցելու համար բավարար չէ Վարկանիշը ≥ %1$s %2$s-ում Վարկանիշը ≤ %1$s %2$s-ում Դուք պետք է լինեք %s ակումբի անդամ @@ -426,9 +428,9 @@ Հանրաճանաչ սկզբնախաղեր Վերջնախաղային դիրքեր Շախմատ 960-ի սկզբնական դիրք՝ %s - Սկզբնական դիրք + Սկզբնական դիրք Մաքրել տախտակը - Բեռնել դիրք + Բեռնել դիրք Անձնական Տեղեկացնել %s մոդերատորներին Մասնակցային էջը լրացված է %s-ով @@ -447,7 +449,7 @@ Նախորդները Lichess TV-ով Առցանց խաղացողներ Ակտիվ խաղացողներ - Զգուշացում. խաղը վարկանիշային է, սակայն առանց ժամանակի + Զգուշացում. խաղը վարկանիշային է, սակայն առանց ժամանակի Հաջողված է Ընթանում է %s խաղ @@ -512,7 +514,7 @@ Դանդաղ խաղերում Միշտ Երբեք - %1$s որոշել է մասնակցել %2$s-ին + %1$s որոշել է մասնակցել %2$s-ին Հաղթանակ Պարտություն %1$s-ն ընդդեմ %2$s-ի %3$s-ում diff --git a/translation/dest/site/si-LK.xml b/translation/dest/site/si-LK.xml index 5d06a806d3..ef712e568f 100644 --- a/translation/dest/site/si-LK.xml +++ b/translation/dest/site/si-LK.xml @@ -87,6 +87,7 @@ ඇදීම් %sකින් චෙක්මේට් ඇදීම් වාර %sකින් චෙක්මේට් + DTZ50\'\' with rounding, based on number of half-moves until next capture or pawn move සෙල්ලමක් හමුවුන්නෑ මනාප ලැයිස්තුවේ තවත් සෙල්ලම් තිබිය හැක? පටන්ගැනීම් ගවේෂකය @@ -151,7 +152,7 @@ ක්‍රීඩකයන් යහළුවන් සංවාද - අද + අද ඊයේ එක් පැත්තකට මිනිත්තු වර්ගය diff --git a/translation/dest/storm/de-CH.xml b/translation/dest/storm/de-CH.xml index b351a947f8..dc8a95dc2a 100644 --- a/translation/dest/storm/de-CH.xml +++ b/translation/dest/storm/de-CH.xml @@ -52,4 +52,10 @@ Überschpring dä Zug, zum dini Kombination byzbhaltä! Du chasch das nur eimol pro Ränä machä. Fählgschlagi Ufgoobä Langsami Ufgoobä + Die Wuche + De Monät + Insgesamt + Klick, zum neu lade + De Durchgang isch abgeloffen! + De Durchgang isch imene andere Fenster göffnet worde! diff --git a/translation/dest/streamer/de-CH.xml b/translation/dest/streamer/de-CH.xml index 88cf0e4fb4..2c1ac39724 100644 --- a/translation/dest/streamer/de-CH.xml +++ b/translation/dest/streamer/de-CH.xml @@ -18,7 +18,7 @@ Füäg s\'Stichwort \"lichess.org\" i dim Schtriimtitäl ii und verwänd d\'Kategorie \"Chess\", wänn du uf Lichess schtriimsch. Entfärn s\'Stichwort, wänn du grad nöd Lichess-bezogä schtriimsch. Lichess wird automatisch din Schtriim ärkänä und folgändi Zuäsatzdiänscht aktiviärä: - Lies unsere %s, um Fairplay für alle während deines Streams zu gewährleisten. + Liis eusi %s, um Fairplay für alli während dim Schtriim z\'gwährleistä. Schtriimer Fairplay FAQ Vorteil fürs Schtriimä mitämä Schlüssälwort Bechum äs flammänds Schtriimer-Symbol uf dim Lichess-Profil. diff --git a/translation/dest/study/de-CH.xml b/translation/dest/study/de-CH.xml index a0240603dc..5e05585522 100644 --- a/translation/dest/study/de-CH.xml +++ b/translation/dest/study/de-CH.xml @@ -41,6 +41,7 @@ Du bist jetzt Zuschauer PGN Tags Gefällt mir + Gfallt mer nüme Neuer Tag Kommentiere diese Stellung Kommentiere diesen Zug diff --git a/translation/dest/swiss/de-CH.xml b/translation/dest/swiss/de-CH.xml index 676e31d477..88093f791f 100644 --- a/translation/dest/swiss/de-CH.xml +++ b/translation/dest/swiss/de-CH.xml @@ -46,7 +46,7 @@ Addiere die Punkte aller Gegner, die der Spieler besiegt, und die Hälfte der Pu Wiä wärdäd Paarigä entschidä? Niderländischs Syschtem FIDE-Handbuäch - Mit dem %1$s, implementiert von %2$s, in Übereinstimmung mit den %3$s. + Mit dem %1$s, implementiert von %2$s, in Übereinstimmung mit dem %3$s. Was passiert, wenn das Turnier mehr Runden als Spieler hat? Wenn alle möglichen Paarungen gespielt wurden, wird das Turnier beendet und ein Sieger erklärt. Wieso isch es uf Teams beschränkt? @@ -75,15 +75,17 @@ Am nähesten an ein online Rundenturnier kommt, ein Turnier nach Schweizer Syste Schtartät grad Vergliich Längi vom Turniär - Vordefinierte Dauer in Minuten + Vordefinierti Duuer ih Minutä Vordefinierte maximale Rundenanzahl, aber unbekannte Dauer Aazahl Partiiä So viele wie in der zugewiesenen Dauer gespielt werden können Im Voraus entschieden, für alle Spieler gleich Paarigssüschtem - Jeder verfügbare Gegner mit ähnlichem Rang - Beste Paarung, basierend auf Punkten und Feinwertungen + Jede verfügbari Gägner mit ähnlichem Rang + Besti Paarig, basierend uf Pünkt und Feinwertungen Warteziit für d Paarig + Schnäll: wartet nöd uf all Spieler + Langsam: wartet uuf all Spieler Idäntischi Paarig Möglich, abär nöd hindäränand Verbottä diff --git a/translation/dest/ublog/de-CH.xml b/translation/dest/ublog/de-CH.xml index 56befb4257..fe56ae8e06 100644 --- a/translation/dest/ublog/de-CH.xml +++ b/translation/dest/ublog/de-CH.xml @@ -10,17 +10,17 @@ Entwürf Veröffentlicht Veröffentlich i dim Blog - Wenn aktiviert, wird der Beitrag in deinem Blog aufgelistet. Wenn nicht, wird er privat sein, in deinen Entwürfen + Wänn aktiviert wird de Biitrag ih diim Blog uufgelistet. Wenn nöd wird er ih diine Entwürf privat sii - Hat einen Blog-Beitrag veröffentlicht - Hat %s Blog-Beiträge veröffentlicht + Hät en Blog-Biitrag veröffentlicht + Hät %s Blog-Biiträg veröffentlicht Eimal aagluägt %s-mal aagluägt - %1$s hat %2$s veröffentlicht - Dieser Beitrag wurde veröffentlicht + %1$s hät %2$s veröffentlicht + De Biitrag isch veröffentlicht worde Das isch än Entwurf Meh Blog-Posts vo %s No kei Posts i dem Blog. From f0235c8d3b7a74c73b7dbeb63b89844ab846b550 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sat, 27 Nov 2021 07:54:51 +0100 Subject: [PATCH 106/144] only send study likes to liking SRI to reduce messages on busy broadcasts --- modules/study/src/main/StudySocket.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/study/src/main/StudySocket.scala b/modules/study/src/main/StudySocket.scala index 8056ecb14a..554ebf256e 100644 --- a/modules/study/src/main/StudySocket.scala +++ b/modules/study/src/main/StudySocket.scala @@ -304,7 +304,8 @@ final private class StudySocket( "w" -> who ) ) - def setLiking(liking: Study.Liking, who: Who) = notify("liking", Json.obj("l" -> liking, "w" -> who)) + def setLiking(liking: Study.Liking, who: Who) = + notifySri(who.sri, "liking", Json.obj("l" -> liking, "w" -> who)) def setShapes(pos: Position.Ref, shapes: Shapes, who: Who) = version( "shapes", From 0390dfd8992df9a0451a306f607dd29f26ed2800 Mon Sep 17 00:00:00 2001 From: kraktus Date: Sat, 27 Nov 2021 19:33:08 +0100 Subject: [PATCH 107/144] Switch zulip stream for moderator blog actions --- modules/mod/src/main/ModlogApi.scala | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/mod/src/main/ModlogApi.scala b/modules/mod/src/main/ModlogApi.scala index caa6ed94e8..f37f001fd8 100644 --- a/modules/mod/src/main/ModlogApi.scala +++ b/modules/mod/src/main/ModlogApi.scala @@ -321,7 +321,8 @@ final class ModlogApi(repo: ModlogRepo, userRepo: UserRepo, ircApi: IrcApi)(impl case M.engine | M.unengine | M.booster | M.unbooster | M.reopenAccount | M.unalt => Some(IrcApi.ModDomain.Hunt) case M.troll | M.untroll | M.chatTimeout | M.closeTopic | M.openTopic | M.disableTeam | - M.enableTeam | M.setKidMode | M.deletePost | M.postAsAnonMod | M.editAsAnonMod => + M.enableTeam | M.setKidMode | M.deletePost | M.postAsAnonMod | M.editAsAnonMod | M.blogTier | + M.blogPostEdit => Some(IrcApi.ModDomain.Comm) case _ => Some(IrcApi.ModDomain.Other) } From 6541db2c4b0ad24fb9ea327f4b221be1ba16d812 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 28 Nov 2021 11:13:52 +0100 Subject: [PATCH 108/144] ceval setting can disable client put --- modules/evalCache/src/main/EvalCacheApi.scala | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/modules/evalCache/src/main/EvalCacheApi.scala b/modules/evalCache/src/main/EvalCacheApi.scala index 0041cd0259..2299c46415 100644 --- a/modules/evalCache/src/main/EvalCacheApi.scala +++ b/modules/evalCache/src/main/EvalCacheApi.scala @@ -9,13 +9,16 @@ import scala.concurrent.duration._ import lila.db.AsyncCollFailingSilently import lila.db.dsl._ import lila.memo.CacheApi._ +import lila.memo.SettingStore import lila.socket.Socket +import lila.user.User final class EvalCacheApi( coll: AsyncCollFailingSilently, truster: EvalCacheTruster, upgrade: EvalCacheUpgrade, - cacheApi: lila.memo.CacheApi + cacheApi: lila.memo.CacheApi, + setting: SettingStore[Boolean] )(implicit ec: scala.concurrent.ExecutionContext) { import EvalCacheEntry._ @@ -36,7 +39,7 @@ final class EvalCacheApi( def put(trustedUser: TrustedUser, candidate: Input.Candidate, sri: Socket.Sri): Funit = candidate.input ?? { put(trustedUser, _, sri) } - def shouldPut = truster shouldPut _ + def shouldPut(user: User) = setting.get() && truster.shouldPut(user) def getSinglePvEval(variant: Variant, fen: FEN): Fu[Option[Eval]] = getEval( From fa4ecb5a7690adbbc2f4613d5a84becf114a99f3 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 28 Nov 2021 15:36:17 +0100 Subject: [PATCH 109/144] fix relay round sync log rotation --- modules/relay/src/main/SyncLog.scala | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/modules/relay/src/main/SyncLog.scala b/modules/relay/src/main/SyncLog.scala index e7b0924a3f..c4057236c0 100644 --- a/modules/relay/src/main/SyncLog.scala +++ b/modules/relay/src/main/SyncLog.scala @@ -14,7 +14,10 @@ case class SyncLog(events: Vector[SyncLog.Event]) extends AnyVal { def add(event: SyncLog.Event) = copy( - events = events.take(SyncLog.historySize - 1) :+ event + events = { + if (events.sizeIs > SyncLog.historySize) events drop 1 + else events + } :+ event ) } From ece947fd207cd66ef3cead0b328208d35d6f5513 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 28 Nov 2021 18:30:37 +0100 Subject: [PATCH 110/144] only propagate cloud eval when depth increases this should reduce the number of redis messages greatly. Before that, you could get cevals for pvs greater than yours, even if your depth is already higher. --- .../evalCache/src/main/EvalCacheUpgrade.scala | 26 ++++++++++++------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/modules/evalCache/src/main/EvalCacheUpgrade.scala b/modules/evalCache/src/main/EvalCacheUpgrade.scala index ce31bb558b..d0cd72800b 100644 --- a/modules/evalCache/src/main/EvalCacheUpgrade.scala +++ b/modules/evalCache/src/main/EvalCacheUpgrade.scala @@ -22,8 +22,8 @@ final private class EvalCacheUpgrade(setting: SettingStore[Boolean], scheduler: import EvalCacheUpgrade._ private val members = mutable.AnyRefMap.empty[SriString, WatchingMember] - private val evals = mutable.AnyRefMap.empty[SetupId, Set[SriString]] - private val expirableSris = new ExpireCallbackMemo(20 minutes, sri => unregister(Socket.Sri(sri))) + private val evals = mutable.AnyRefMap.empty[SetupId, EvalState] + private val expirableSris = new ExpireCallbackMemo(10 minutes, sri => unregister(Socket.Sri(sri))) private val upgradeMon = lila.mon.evalCache.upgrade @@ -34,15 +34,19 @@ final private class EvalCacheUpgrade(setting: SettingStore[Boolean], scheduler: } val setupId = makeSetupId(variant, fen, multiPv) members += (sri.value -> WatchingMember(push, setupId, path)) - evals += (setupId -> (~evals.get(setupId) + sri.value)) + evals += (setupId -> evals.get(setupId).fold(EvalState(Set(sri.value), 0))(_ addSri sri)) expirableSris put sri.value } def onEval(input: EvalCacheEntry.Input, sri: Socket.Sri): Unit = if (setting.get()) { (1 to input.eval.multiPv) flatMap { multiPv => - evals get makeSetupId(input.id.variant, input.fen, multiPv) - } foreach { sris => - val wms = sris.withFilter(sri.value !=) flatMap members.get + val setupId = makeSetupId(input.id.variant, input.fen, multiPv) + evals get setupId map (setupId -> _) + } filter { + _._2.depth < input.eval.depth + } foreach { case (setupId, eval) => + evals += (setupId -> eval.copy(depth = input.eval.depth)) + val wms = eval.sris.withFilter(sri.value !=) flatMap members.get if (wms.nonEmpty) { val json = JsonHandlers.writeEval(input.eval, input.fen) wms foreach { wm => @@ -61,10 +65,10 @@ final private class EvalCacheUpgrade(setting: SettingStore[Boolean], scheduler: } private def unregisterEval(setupId: SetupId, sri: Socket.Sri): Unit = - evals get setupId foreach { sris => - val newSris = sris - sri.value + evals get setupId foreach { eval => + val newSris = eval.sris - sri.value if (newSris.isEmpty) evals -= setupId - else evals += (setupId -> newSris) + else evals += (setupId -> eval.copy(sris = newSris)) } scheduler.scheduleWithFixedDelay(1 minute, 1 minute) { () => @@ -80,6 +84,10 @@ private object EvalCacheUpgrade { private type SetupId = String private type Push = JsObject => Unit + private case class EvalState(sris: Set[SriString], depth: Int) { + def addSri(sri: Socket.Sri) = copy(sris = sris + sri.value) + } + private def makeSetupId(variant: Variant, fen: FEN, multiPv: Int): SetupId = s"${variant.id}${EvalCacheEntry.SmallFen.make(variant, fen).value}^$multiPv" From 80523a222401114143d06d1f65dd5b5459e84c61 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 28 Nov 2021 18:40:28 +0100 Subject: [PATCH 111/144] only run client evalPut when local depth > cloud depth --- ui/analyse/src/evalCache.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/ui/analyse/src/evalCache.ts b/ui/analyse/src/evalCache.ts index 289e2b3c13..4e599c4969 100644 --- a/ui/analyse/src/evalCache.ts +++ b/ui/analyse/src/evalCache.ts @@ -75,10 +75,11 @@ export function make(opts: EvalCacheOpts): EvalCache { const upgradable = prop(false); lichess.pubsub.on('socket.in.crowd', d => upgradable(d.nb > 2 && d.nb < 99999)); return { - onCeval: throttle(500, function () { + onCeval: throttle(500, () => { const node = opts.getNode(), ev = node.ceval; - if (ev && !ev.cloud && node.fen in fetchedByFen && qualityCheck(ev) && opts.canPut()) { + const fetched = fetchedByFen[node.fen]; + if (ev && !ev.cloud && fetched && ev.depth > fetched.depth && qualityCheck(ev) && opts.canPut()) { opts.send('evalPut', toPutData(opts.variant, ev)); } }), From f36466aa7d44c718830d1c3b9669f45e9610beb0 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 28 Nov 2021 18:41:59 +0100 Subject: [PATCH 112/144] New Crowdin updates (#10176) * New translations: site.xml (Dutch) * New translations: emails.xml (Afar) * New translations: site.xml (Belarusian) * New translations: site.xml (Belarusian) * New translations: storm.xml (Belarusian) * New translations: coordinates.xml (Belarusian) * New translations: learn.xml (Belarusian) * New translations: learn.xml (Belarusian) * New translations: learn.xml (Belarusian) * New translations: site.xml (Kazakh) * New translations: site.xml (Kazakh) * New translations: challenge.xml (Kazakh) * New translations: site.xml (Kazakh) * New translations: preferences.xml (Kazakh) * New translations: site.xml (Kazakh) * New translations: faq.xml (Kazakh) * New translations: site.xml (Kazakh) * New translations: challenge.xml (Kazakh) * New translations: faq.xml (Kazakh) * New translations: study.xml (Hebrew) * New translations: study.xml (Hebrew) * New translations: class.xml (Hebrew) * New translations: contact.xml (Hebrew) * New translations: contact.xml (Hebrew) * New translations: broadcast.xml (Hebrew) --- translation/dest/broadcast/he-IL.xml | 5 +++++ translation/dest/challenge/kk-KZ.xml | 4 ++-- translation/dest/class/he-IL.xml | 2 ++ translation/dest/contact/he-IL.xml | 2 ++ translation/dest/coordinates/be-BY.xml | 4 ++-- translation/dest/emails/aa-ER.xml | 2 +- translation/dest/faq/kk-KZ.xml | 8 ++++---- translation/dest/learn/be-BY.xml | 17 ++++++++--------- translation/dest/preferences/kk-KZ.xml | 2 +- translation/dest/site/be-BY.xml | 6 +++--- translation/dest/site/kk-KZ.xml | 20 ++++++++++---------- translation/dest/site/nl-NL.xml | 20 ++++++++++---------- translation/dest/storm/be-BY.xml | 6 +++--- translation/dest/study/he-IL.xml | 9 +++++++++ 14 files changed, 62 insertions(+), 45 deletions(-) diff --git a/translation/dest/broadcast/he-IL.xml b/translation/dest/broadcast/he-IL.xml index 81318e367a..0fd30c35a8 100644 --- a/translation/dest/broadcast/he-IL.xml +++ b/translation/dest/broadcast/he-IL.xml @@ -1,9 +1,14 @@ שידורים + משדר משחקים חיים + משדר חי חדש + הוסף סבב כרגע בקרוב שהושלמו + שם סבב + מספר סבב שם הטורניר תיאור הטורניר בקצרה תיאור מלא של הטורניר diff --git a/translation/dest/challenge/kk-KZ.xml b/translation/dest/challenge/kk-KZ.xml index ca617282e9..3ab08c2a13 100644 --- a/translation/dest/challenge/kk-KZ.xml +++ b/translation/dest/challenge/kk-KZ.xml @@ -17,8 +17,8 @@ Бұл уақыт қалпы мен үшін тым баяу, шапшаңдау ойынға шақыруды жіберіңізші. Мен бұндай уақыт қалпындағы ойындарға шақыруды қабылдамаймын. Оның орнына маған бағаланатын шақыруды жіберіңізші. - Оның орнына маған қалыпты шақыруды жіберіңізші. - Мен қазір шахмат түріне негізделген шақыруларды қабылдамаймын. + Оның орнына маған жай шақыруды жіберіңізші. + Мен қазір классикалық емес шақыруларды қабылдамаймын. Мен қазір осы шахмат түрін ойнағым келмейді. Мен роботтардан шақыруларды қабылдамаймын. Мен тек роботтардан келген шақыруларды қабылдаймын. diff --git a/translation/dest/class/he-IL.xml b/translation/dest/class/he-IL.xml index 5789d8a75b..92adcd4fe8 100644 --- a/translation/dest/class/he-IL.xml +++ b/translation/dest/class/he-IL.xml @@ -71,6 +71,8 @@ כתובת דוא\"ל אמיתית וייחודית של התלמיד. אנו נשלח אליו דוא\"ל לאישור, עם קישור לשחרור החשבון. סגור את החשבון סגור את חשבון התלמיד לצמיתות. + התלמיד לעולם לא יוכל עוד להשתמש בחשבון זה. סגירת החשבון היא סופית. אנא וודא שהתלמיד מבין ומסכים. + אולי תרצה במקום לתת לתלמיד שליטה על החשבון, כך שיוכל להמשיך להשתמש בו. מורים מורה diff --git a/translation/dest/contact/he-IL.xml b/translation/dest/contact/he-IL.xml index 44ea6963ab..d17b7182c1 100644 --- a/translation/dest/contact/he-IL.xml +++ b/translation/dest/contact/he-IL.xml @@ -27,6 +27,7 @@ אני רוצה לנקות את ההיסטוריה או הדירוג שלי לא ניתן לנקות את היסטוריית המשחק, היסטוריית הפאזלים, או הדירוגים שלך. אני רוצה לדווח על שחקן + כדי לדווח על שחקן, השתמש בטופס הדיווח אתה יכול גם להגיע לדף זה על ידי לחיצה על כפתור הדוח %s בדף הפרופיל. אל תדווחו על שחקנים בפורום. אל תשלחו לנו אימיילים כדי לדווח. @@ -51,6 +52,7 @@ אפשרי לבצע מט רק אם פרש או רץ, אם ליריב יש יותר ממלך על הלוח. נקודות מד כושר לא הוענקו בדוק אם שיחקת משחק מדורג. משחק לא מדורג לא משפיע על מד הכושר. + בנסיבות מסויימות כאשר משחק משוחק אל מול חשבון של בוט, משחק מנוקד עשוי שלא להעניק נקודות אם יקבע שהשחקן מנצל את הבוט לצבירת ניקוד. שגיאה בדף אם התמודדת עם שגיאה בדף, אתה יכול לדווח על זה: ערער על איסור או על הגבלה בIP diff --git a/translation/dest/coordinates/be-BY.xml b/translation/dest/coordinates/be-BY.xml index d6121f2f79..0983f6560b 100644 --- a/translation/dest/coordinates/be-BY.xml +++ b/translation/dest/coordinates/be-BY.xml @@ -2,8 +2,8 @@ Каардынаты Трэніроўка па каардынатах - Сярэдні лік за белых: %s - Сярэдні лік за чорных: %s + Сярэдні вынік за белых: %s + Сярэдні вынік за чорных: %s Ведаць каардынаты шахматнай дошкі - вельмі важны шахматны навык: Большасць шахматных задач та заданняў выкарыстоўваць шахматнаю натацыю. Гэта спрошчвае гутарку з сябрамі-шахматыстамі, бо вы абодва разумееце \"шахматную мову\". diff --git a/translation/dest/emails/aa-ER.xml b/translation/dest/emails/aa-ER.xml index 9607eff19d..a79101e711 100644 --- a/translation/dest/emails/aa-ER.xml +++ b/translation/dest/emails/aa-ER.xml @@ -1,6 +1,6 @@ - Bevestig jou lichess.org-rekening, % + Bevestig jou lichess.org-rekening, %s Klik op die skakel om jou Lichess-rekening te aktiveer. As jy nie by Lichess geregistreer het nie, kan jy hierdie boodskap veilig ignoreer. diff --git a/translation/dest/faq/kk-KZ.xml b/translation/dest/faq/kk-KZ.xml index 89406270b0..a1ccc5688f 100644 --- a/translation/dest/faq/kk-KZ.xml +++ b/translation/dest/faq/kk-KZ.xml @@ -33,9 +33,9 @@ (бастапқы уақыт) + 40 × (қосылғыш) < %1$sс = %2$s ≥ %1$s с = %2$s - Личесте қандай шахмат түрлерін ойнасам болады? - Личесте әдеттегі шахмат пен %1$s бар. - 8 шахмат түрлері + Личесте шахматтың қай түрлерін ойнасам болады? + Личесте классикалық әрі %1$s бар. + шахматтың 8 түрі Қателіктердің орташа мәні (ACPL – average centipawn loss) деген не? Қателіктердің орташа мәнінің бірлігі сентипоун (\"centi\" - жүзден бір бөлік, \"pawn\" - сарбаз). Бұл шама ойыншының жетістік дәрежесін көрсетеді. Ойын кезінде бұл шама соншалықты маңызды емес, бірақ компьютер үшін ойын күйін бағалауда орны зор. @@ -115,7 +115,7 @@ Мықтылар қатары белгіленген рейтингте кем дегенде 30 бағаланатын ойын ойнау, кейінгі аптада белгіленген рейтингте бағаланатын ойынға қатысу, - рейтинг ауытқуы стандарт шахматта %1$s-тен кем, басқа түрлерінде %2$s-тен кем болу керек, + рейтинг ауытқуы классикалық шахматта %1$s-тен кем, басқа түрлерінде %2$s-тен кем болу керек, белгіленген рейтингтің үздік 10 қатарында болу. Екінші талап – өз тіркелгілерін пайдаланбайтын ойыншылар Мықтылар қатарынан шығады. Рейтинг басқа FIDE, USCF пен ICC сияқты ұйымдармен салыстырғанда неге жоғарылау болып тұр? diff --git a/translation/dest/learn/be-BY.xml b/translation/dest/learn/be-BY.xml index b7a7719b13..7eb925d277 100644 --- a/translation/dest/learn/be-BY.xml +++ b/translation/dest/learn/be-BY.xml @@ -11,7 +11,7 @@ Ладдзя Рухаецца па простых лініях Ладдзя — гэта цяжкая фігура. Ці гатовы вы кіраваць ёй? - Пстрыкніце на ладдзю і давядзіце яе да зоркі! + Націсніце на ладдзю і давядзіце яе да зорачкі! Збярыце ўсе зоркі! Чым менш хадоў вы зробіце, тым больш балаў атрымаеце! @@ -35,7 +35,7 @@ І апошні! Вы навучыліся хадзіць каралём! Конь - Ён ходзіць літарай \"Г\" + Xодзіць літарай \"Г\" Асвоіць каня не так проста, бо ён — хітрая фігура. Коні маюць мудрагелісты спосаб пераскокваць! Коні могуць скакаць праз перашкоды! @@ -124,9 +124,9 @@ Абараніце свайго караля і выведзіце ў бой ладдзю! Перасуньце свайго караля на дзве клеткі да ладдзі каралеўскага фланга! Перасуньце свайго караля на дзве клеткі да ладдзі ферзевага фланга! - Конь у дарозе! Зрабіце ход канём, потым зрабіце ракіроўку. - Кароткая ракіроўка! Спачатку вам трэба развіць фігуры. - Доўгая ракіроўка! Спачатку вам трэба развіць фігуры. + Конь перашкаджае! Пахадзіце канём, потым зрабіце ракіроўку. + Кароткая ракіроўка! Спачатку вам трэба вывесці фігуры. + Доўгая ракіроўка! Спачатку вам трэба вывесці фігуры. Вы не можаце рабіць ракіроўку, калі кароль ці ладдзя, якія ўдзельнічаюць у ракіроўцы, рабілі ход. @@ -151,7 +151,7 @@ Гульня з\'яўляецца нічыйнай Калі гульцу не пастаўлены шах і няма магчымасці зрабіць ход, гэта пат. Гульня сканчаецца: без пераможцаў і пераможаных. Пат чорным: -- Чорныя не могуць схадзіць. +- Чорныя не могуць пахадзіць. - Кароль не пад шахам. Віншуем! Лепш атрымаць пат, чым мат! Прасунуты этап @@ -167,12 +167,11 @@ Атакуйце фігуры з больш высокай каштоўнасцю! Ферзь > Слон Вазьміце найбольш каштоўную фігуру! -Не разменьвайце больше каштоўную фігуру на менш каштоўную. +Не разменьвайце больш каштоўную фігуру на менш каштоўную. Вазьміце найбольш каштоўную фігуру! Пераканайцеся, што ход магчымы! - Атакуйце фігуры -з больш высокай каштоўнасцю! + Вазьміце найкаштоўнейшую фігуру! Віншуем! Вы ведаеце каштоўнасць фігур! Ферзь = 9 пешак Ладдзя= 5 пешак diff --git a/translation/dest/preferences/kk-KZ.xml b/translation/dest/preferences/kk-KZ.xml index bebcd229fa..725cef5318 100644 --- a/translation/dest/preferences/kk-KZ.xml +++ b/translation/dest/preferences/kk-KZ.xml @@ -28,7 +28,7 @@ Екі әдіспен де Алдын-ала жүру (қарсыластың жүру кезегі кезінде жүру) Жүрісті қайтару (қарсыластың келісімі қажет) - Қалыпты ойындарда ғана + Жай ойындарда ғана Уәзірге автоматты түрде айналдыру Алдын-ала жүрісте Үш реттік қайталану болса, автоматты түрде тепе-теңдік жариялау diff --git a/translation/dest/site/be-BY.xml b/translation/dest/site/be-BY.xml index 6d35a47d61..686ac67991 100644 --- a/translation/dest/site/be-BY.xml +++ b/translation/dest/site/be-BY.xml @@ -757,7 +757,7 @@ Хуткі старт Лобі Ананімны гулец - Ваш вынік: %s + Ваш лік: %s Мова Фон Светлы @@ -856,8 +856,8 @@ Мы просім прабачэння за часовыя нязручнасці, і жадаем вам вельмі добрых гульняў на lichess.org. Дзякуй за ўвагу! - Рахунак за ўсё время - Рахунак у бягучым матчы + Лік за ўвесь час + Лік бягучага матчу Я згаджаюся, што я ніколі не скарыстаюся старонняй дапамогай у сваіх гульнях (ад шахматных праграм, кніг, баз даных або іншых гульцоў). Я згаджаюся, што я заўсёды адносіцца з павагай да другіх гульцоў. Я згаджаюся, што я не буду ствараць некалькі ўліковых запісаў. diff --git a/translation/dest/site/kk-KZ.xml b/translation/dest/site/kk-KZ.xml index e384fb7d47..d1fb657997 100644 --- a/translation/dest/site/kk-KZ.xml +++ b/translation/dest/site/kk-KZ.xml @@ -231,9 +231,9 @@ Классикалық Шектеусіз Түрі - Қалыпты + Жай Бағаланатын - Қалыпты + Кәдімгі Бағаланатын Бұл ойын бағаланады Қайта ойнау @@ -517,7 +517,7 @@ Дыбыстар Жоқ Тез - Қалыпты + Орташа Баяу Тақта бетінде Тақта сыртында @@ -601,10 +601,10 @@ Фишер 50 қарсыластың 47-сін жеңіп, 2-мен тең ойнап, 1-нен жеңілді. Бұл түсінік шынайы өмірден алынған: ойында бас ойыншы болады, ол тақталарды аралап, бір-бір жүрістен жасап отырады. Көпшілікке қарсы ойын басталысымен, әр ойыншы ақ тастар иесі – бас ойыншымен жарысады. Барлық ішкі ойындар аяқталғанда, жалпы ойын да аяқталады. - Көпшілікке қарсы ойын – әрқашанда қалыпты. Қайта ойнау, жүрісті қайтару, уақыт қосу дегендер болмайды. + Көпшілікке қарсы ойын – әрқашанда жай ойын. Қайта ойнау, жүрісті қайтару, уақыт қосу дегендер болмайды. Құру Сіз көпшілікке қарсы ойынды құрғанда, бір мезгілде бірнеше қарсыласпен ойнайсыз. - Егер сіз бірнеше шахмат түрін таңдасаңыз, әр ойыншы соның арасынан өзіне лайық біреуін таңдайды. + Егер сіз бірнеше шахмат түрін таңдасаңыз, әр ойыншы соның арасынан біреуін таңдайды. Фишер Сағатын баптау. Қарсыластарыңыз көп болса, уақыт та көп қажет. Көпшілікке қарсы ойынды жеңілдету үшін өзіңізге қосымша уақыт қоса аласыз. Бас ойыншыға қосымша уақыт @@ -710,7 +710,7 @@ Бапкерлер Қате PGN Қате FEN - Қалыпты + Басқа Хабарлама Рейтинг: %s @@ -792,10 +792,10 @@ Рапид Классикалық Лезде ойындар: 30 секундтан кем - Аса жылдам ойындар: 3 минуттан кем + Аса жылдам ойындар: 3 минуттан аз Жылдам ойындар: 3-тен 8 минутке дейін Рапид ойындары: 8-ден 25 минутке дейін - Классикалық ойындар: 25 минут және одан артық + Классикалық ойындар: 25 минут әрі одан көп Хат алмасып ойнау: бір жүріске бір не бірнеше күн Шахмат тактикасын үйретуші Ескерту @@ -846,8 +846,8 @@ Тек атаққа ие ойыншылар Жарысқа қосу үшін ресми атағын талап ету Әр ойынды нақты бір күйден бастау үшін FEN еңгізіңіз. -Бұл тек қалыпты ойындарда ғана жұмыс істейді, басқа шахмат түрлерінде емес. -Күйдің FEN-ін құру үшін %s қолданыңыз, кейін осында қойыңыз. +Бұл тек классикалық ойындарда ғана жұмыс істейді, басқа шахмат түрлерінде емес. +Күйдің FEN-ін құру үшін %s қолданыңыз, кейін осында еңгізіңіз. Ойынды кәдімгі бастапқы күйден бастау үшін бос қалдыра салыңыз. Көпшілікке қарсы ойынды болдырмау Әрқашан бас ойыншы түсінен тәуелді diff --git a/translation/dest/site/nl-NL.xml b/translation/dest/site/nl-NL.xml index 5fa6a74dfa..7fed709167 100644 --- a/translation/dest/site/nl-NL.xml +++ b/translation/dest/site/nl-NL.xml @@ -163,7 +163,7 @@ Spelers Vrienden Discussies - Vandaag + Vandaag Gisteren Minuten per speler Variant @@ -435,9 +435,9 @@ Populaire openingen Eindspel posities Beginstelling Chess960: %s - Beginstelling + Beginstelling Maak het bord leeg - Laad een stelling + Laad een stelling Privé Rapporteer %s aan de toezichthouders Voltooiing profiel: %s @@ -456,7 +456,7 @@ Voorheen op Lichess TV Online spelers Actieve spelers - Let op, de partij is met rating maar heeft geen klok! + Let op, de partij is met rating maar heeft geen klok! Succes %s partij bezig @@ -525,7 +525,7 @@ Alleen bij langzame partijen Altijd Nooit - %1$s neemt deel aan %2$s + %1$s neemt deel aan %2$s Overwinning Nederlaag %1$s vs. %2$s in %3$s @@ -560,10 +560,10 @@ Dit e-mailadres is niet acceptabel. Controleer het nogmaals en probeer het opnieuw. E-mailadres ongeldig of al in gebruik Dit is al uw e-mailadres - Minimumlengte is %s tekens - Maximumlengte is %s tekens - Moet groter dan of gelijk zijn aan %s - Moet kleiner dan of gelijk zijn aan %s + Minimumlengte is %s tekens + Maximumlengte is %s tekens + Moet groter dan of gelijk zijn aan %s + Moet kleiner dan of gelijk zijn aan %s Indien de rating ± %s is Alleen bestaande gesprekken Alleen vrienden @@ -812,7 +812,7 @@ Je kunt nog geen post in de forums plaatsen. Speel eerst een paar partijen! Abonneren Abonnement opzeggen - heeft je genoemd in \"%1$s\". + heeft je genoemd in \"%1$s\". %1$s heeft je genoemd in \"%2$s\". heeft je uitgenodigd voor \"%1$s\". %1$s heeft je uitgenodigd voor \"%2$s\". diff --git a/translation/dest/storm/be-BY.xml b/translation/dest/storm/be-BY.xml index 07647d99e7..34f917c268 100644 --- a/translation/dest/storm/be-BY.xml +++ b/translation/dest/storm/be-BY.xml @@ -4,11 +4,11 @@ Вы гуляеце белымі фігурамі ва ўсіх задачах Вы гуляеце чорнымі фігурамі ва ўсіх задачах задач вырашана - Новы дзенны рэкорд! + Новы дзённы рэкорд! Новы тыднёвы рэкорд! Новы месячны рэкорд! - Новы рэкорд за ўсе часы! - Папярэні рэкорд быў %s + Новы рэкорд за ўвесь час! + Папярэдні рэкорд %s Гуляць яшчэ раз 1 спроба diff --git a/translation/dest/study/he-IL.xml b/translation/dest/study/he-IL.xml index 1dae4d9c9b..c80f978003 100644 --- a/translation/dest/study/he-IL.xml +++ b/translation/dest/study/he-IL.xml @@ -134,6 +134,7 @@ נקה צ\'אט למחוק את ההיסטוריה של הצ\'אט של המחקר? אין דרך חזרה! מחק מחקר + האם למחוק את כל הלימוד? אין דרך חזרה! הקלד את שם הלימוד לאישור: %s איפה אתה מעוניין לחקור זאת? מסע טוב טעות @@ -144,6 +145,7 @@ המסע היחיד כפאי עמדה מאוזנת + עמדה לא ברורה יתרון קל ללבן יתרון קל לשחור לבן יותר טוב @@ -152,6 +154,13 @@ שחור מנצח חידוש פיתוח + יוזמה התקפה + משחק נגד + מצוקת זמן + עם פיצוי + עם הרעיון פרק הבא + הפרק הקודם + פעולות לימוד From d82591c9c15888b7f0a2df9267fa7ba1c6501126 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Sun, 28 Nov 2021 19:08:15 +0100 Subject: [PATCH 113/144] fix ceval.evalPut first eval --- ui/analyse/src/evalCache.ts | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/ui/analyse/src/evalCache.ts b/ui/analyse/src/evalCache.ts index 4e599c4969..29e5ceb2e3 100644 --- a/ui/analyse/src/evalCache.ts +++ b/ui/analyse/src/evalCache.ts @@ -79,7 +79,14 @@ export function make(opts: EvalCacheOpts): EvalCache { const node = opts.getNode(), ev = node.ceval; const fetched = fetchedByFen[node.fen]; - if (ev && !ev.cloud && fetched && ev.depth > fetched.depth && qualityCheck(ev) && opts.canPut()) { + if ( + ev && + !ev.cloud && + node.fen in fetchedByFen && + (!fetched || fetched.depth < ev.depth) && + qualityCheck(ev) && + opts.canPut() + ) { opts.send('evalPut', toPutData(opts.variant, ev)); } }), From 3f23193601b43a947b66c5495fb970e5846a8ab1 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 29 Nov 2021 07:49:18 +0100 Subject: [PATCH 114/144] New Crowdin updates (#10181) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) * New translations: site.xml (Sorani (Kurdish)) --- translation/dest/site/ckb-IR.xml | 50 ++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/translation/dest/site/ckb-IR.xml b/translation/dest/site/ckb-IR.xml index 81efc5518a..fbaceff27b 100644 --- a/translation/dest/site/ckb-IR.xml +++ b/translation/dest/site/ckb-IR.xml @@ -7,6 +7,7 @@ چاوەڕوانی لایەینی بەرامبەر چاوەڕوانی ڕازیبوونی لایەنی بەرامبەر سەرەی تۆیە + %1$s ئاست %2$s پلە هێز چەسپاندنی چاتەکە @@ -23,6 +24,7 @@ یاری بە پارچە سپیەکان دەکەیت یاری بە پارچە ڕەشەکان دەکەیت سەرەی تۆیە! + دەرکەوت کە فێڵ کراوە پاشا لە ناوەڕاست سێ چێککردن (هێرش کردنە سەر پاشای بەرامبەر) کۆتایی هات @@ -41,20 +43,28 @@ رەش خۆی بەدەستەوەدا سپی وازیهێنا لە یاریکردن ڕەش وازیهێنا لە یاریکردن + سپی جووڵەی نەکرد + ڕەش جووڵەی نەکرد داواکردنی شیکاری کۆمپیوتەری شیکاری کۆمپیوتەری شیکاری کۆمپیوتەری بەردەستە + شیکاری کۆمپیوتەری ناچاڵاک کرا شیکاری تەختەی شەترەنج قوڵایی%s بەکارھێنانی شیکاری ڕاژەکاری بزوێنەرەکە لە باری هەڵگرتندایه... + شیکاری ئۆنڵاین زیاتر پیشاندانی مەترسی له شوێنی گەڕانی ناوخۆیی + هەڵسەنگاندنی خۆجێیی بگۆڕە + برەوپێدانی جۆراوجۆر کردن بە رستەی سەرەکی سڕینەوە + هێزی جۆراوجۆر جوڵان دۆرانی ناچاری + براوەی هەمەجۆری یەکسانبوون بەهۆی نەمانی جوڵە بۆ کش جوڵەی سەرباز گرتن @@ -68,16 +78,56 @@ پۆلێنکردنی تێکرا: %s دوایین یاریەکانت بەرزترین یاریەکان + دوو ملیۆن یاری سەر بۆرد لە لایەن %1$s+ FIDE-یاریزانانی هەڵسەنگنراو، کە خەڵکی %2$s بۆ %3$s + DTZ50\'\' لەگەڵ نزیکخستنەوەیان، بە پێی ژمارەی نیوەی جووڵەکان تا زاڵبوونێکی دیکە یان جوڵەی سەرباز هیچ یارییەک نەدۆزرایەوە ئایا دەتەویت جۆری تر دیاری بکەیت? + دۆزەرەوەی کردنەوەکان + دۆزەرەوەی کردنەوەکان\کۆتاییەکانی یاری + %s دۆزەرەوەی کردنەوەکان + براوە دیاری ناکرێت بە یاسای-50 جووڵە + دۆڕاو دیاری ناکرێت بە یاسای-50 جووڵە + براوە یان 50 جووڵە بەهۆی هەڵەی پێشوو + دۆڕاو یان 50 جووڵە بەهۆی هەڵەی پێشوو + براوە\دۆڕاو کاتێک دەستەبەر دەکرێت کە ئەوەی لە لایەن خشتەی سەر تەختەوە پێشنیار دەکرێت جێبەجێبکرێت، ئەوەش لە دواین زاڵبوون یان جوڵەی سەرباز دەێت، لەبەر ئەگەری نزیکخستنەوەی نرخی DTZ لە خشتەی سەر تەختای Syzygy. ئامادەیە! سکرین شوت PGN سڕینەوە دڵنیای کە دەتەوێت بیسڕیەوە? سەیرکردنەوەی یاریەکە وەک خۆی + بەهۆی CPL + خوێندنی کراوە + چالاککراوە + باشترین جووڵە بە تیر دیار بکە + پێوانەی هەڵسەنگاندن + زیادکردنی هێڵەکان + CPUs + Memory + شیکاریکردنی بێ کۆتا + لابردنی دورترین سنوور، بەمەش کۆمپیوتەرەکەت سەلامەتتر دەبێت + هەڵەی گەورە + هەڵە + ناتەواو + + %s هەڵەی گەورە + %s هەڵە گەورەکان + + + %s هەڵە + %s هەڵەکان + + + %s نا تەواو + %s ناتەواوەکان + + ژمارەی جوڵەکان + هەڵگێڕانەوەی تەختە + سێبارە کردنەوە + داوای یەکسانی بکە پێشکەشکردنی یەکسانبوون یەکسان + بە رەزاماندی هەردولا یەکسان بوون دەرچوون چوونە ناوە From 06e1261dab02c8da3278f456ebb0ffc92a46e965 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 29 Nov 2021 11:33:28 +0100 Subject: [PATCH 115/144] code golf --- modules/game/src/main/Captcher.scala | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/modules/game/src/main/Captcher.scala b/modules/game/src/main/Captcher.scala index 9bca056886..86c7c90d43 100644 --- a/modules/game/src/main/Captcher.scala +++ b/modules/game/src/main/Captcher.scala @@ -57,11 +57,7 @@ final private class Captcher(gameRepo: GameRepo)(implicit ec: scala.concurrent.E challenges.find(_.gameId == id) private def createFromDb: Fu[Option[Captcha]] = - findCheckmateInDb(10) flatMap { - _.fold(findCheckmateInDb(1))(g => fuccess(g.some)) - } flatMap { - _ ?? fromGame - } + findCheckmateInDb(10) orElse findCheckmateInDb(1) flatMap { _ ?? fromGame } private def findCheckmateInDb(distribution: Int): Fu[Option[Game]] = gameRepo findRandomStandardCheckmate distribution From 9a7e6bf716bdcc38a68ae1ce5d5f981f176c5b66 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 29 Nov 2021 11:39:28 +0100 Subject: [PATCH 116/144] remove Option.ifNone --- modules/common/src/main/base/PimpedUtils.scala | 2 -- modules/game/src/main/Captcher.scala | 5 ++--- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/modules/common/src/main/base/PimpedUtils.scala b/modules/common/src/main/base/PimpedUtils.scala index 7a86a93ad5..d42bb5d286 100644 --- a/modules/common/src/main/base/PimpedUtils.scala +++ b/modules/common/src/main/base/PimpedUtils.scala @@ -24,8 +24,6 @@ final class PimpedOption[A](private val self: Option[A]) extends AnyVal { def err(message: => String): A = self.getOrElse(sys.error(message)) - def ifNone(n: => Unit): Unit = if (self.isEmpty) n - def has(a: A) = self contains a } diff --git a/modules/game/src/main/Captcher.scala b/modules/game/src/main/Captcher.scala index 86c7c90d43..c3d56c9e81 100644 --- a/modules/game/src/main/Captcher.scala +++ b/modules/game/src/main/Captcher.scala @@ -47,11 +47,10 @@ final private class Captcher(gameRepo: GameRepo)(implicit ec: scala.concurrent.E private val capacity = 256 private var challenges = NonEmptyList.one(Captcha.default) - private def add(c: Captcha): Unit = { - find(c.gameId) ifNone { + private def add(c: Captcha): Unit = + if (find(c.gameId).isEmpty) { challenges = NonEmptyList(c, challenges.toList take capacity) } - } private def find(id: String): Option[Captcha] = challenges.find(_.gameId == id) From 435cfba2ada7f6aba2b1250ee8430340f14da536 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 30 Nov 2021 13:00:39 +0100 Subject: [PATCH 117/144] Fix analysis board material on 3D boards --- ui/analyse/css/_player-clock.scss | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ui/analyse/css/_player-clock.scss b/ui/analyse/css/_player-clock.scss index bcef13b290..b095074075 100644 --- a/ui/analyse/css/_player-clock.scss +++ b/ui/analyse/css/_player-clock.scss @@ -14,6 +14,10 @@ $clock-height: 20px; &.bottom { top: var(--cg-height, 100%); z-index: 1; // over the board coords + + .is3d & { + top: calc(var(--cg-height, 100%) + 15px); + } } /* Where to put them in col1 layout? It moves the entire board and controls down for little benefit */ From d39bb31483a3726867a6887c1b34881bf5335493 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 30 Nov 2021 13:53:12 +0100 Subject: [PATCH 118/144] Fix bottom outer coords color on 3D boards --- ui/common/css/vendor/chessground/_coords.scss | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/ui/common/css/vendor/chessground/_coords.scss b/ui/common/css/vendor/chessground/_coords.scss index 0c6916eaa6..8d3a5a676f 100644 --- a/ui/common/css/vendor/chessground/_coords.scss +++ b/ui/common/css/vendor/chessground/_coords.scss @@ -89,8 +89,11 @@ coords { text-align: center; } - coord { - color: $c-font-page !important; + &.ranks, + .is2d &.files { + coord { + color: $c-font-page !important; + } } } } From 004dbceab81d90547f8b752a398fdf1d5e8e8265 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Tue, 30 Nov 2021 21:44:14 +0100 Subject: [PATCH 119/144] Fix overflow in fishnet opening book --- modules/common/src/main/Random.scala | 2 ++ .../fishnet/src/main/FishnetOpeningBook.scala | 16 ++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/modules/common/src/main/Random.scala b/modules/common/src/main/Random.scala index fd6fffb48a..e03c899056 100644 --- a/modules/common/src/main/Random.scala +++ b/modules/common/src/main/Random.scala @@ -47,6 +47,8 @@ abstract class Random { object ThreadLocalRandom extends Random { override def current = java.util.concurrent.ThreadLocalRandom.current + + def nextLong(n: Long): Long = current.nextLong(n) } object SecureRandom extends Random { diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index 80afbfd65d..d141ec7589 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -69,10 +69,10 @@ object FishnetOpeningBook { def randomPonderedMove(turn: Color, level: Int): Option[Move] = { val sum = moves.map(_.score(turn, level)).sum - val novelty = 5 * 14 // score of 5 winning games - val rng = ThreadLocalRandom.nextInt(sum + novelty) + val novelty = 5L * 14 // score of 5 winning games + val rng = ThreadLocalRandom.nextLong(sum + novelty) moves - .foldLeft((none[Move], 0)) { case ((found, it), next) => + .foldLeft((none[Move], 0L)) { case ((found, it), next) => val nextIt = it + next.score(turn, level) (found orElse (nextIt > rng).option(next), nextIt) } @@ -80,12 +80,12 @@ object FishnetOpeningBook { } } - case class Move(uci: Uci, white: Int, draws: Int, black: Int) { - def score(turn: Color, level: Int) = + case class Move(uci: Uci, white: Long, draws: Long, black: Long) { + def score(turn: Color, level: Int): Long = // interpolate: real frequency at lvl 1, expectation value at lvl 8 - 14 * turn.fold(white, black) + - (15 - level) * draws + - (16 - 2 * level) * turn.fold(black, white) + 14L * turn.fold(white, black) + + (15L - level) * draws + + (16L - 2 * level) * turn.fold(black, white) } implicit val moveReader = Json.reads[Move] From 0fd1d268edd088d779c356e22d62c7409d6e2f8e Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 4 Dec 2021 03:47:10 +0100 Subject: [PATCH 120/144] scalafmt --- modules/fishnet/src/main/FishnetOpeningBook.scala | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/modules/fishnet/src/main/FishnetOpeningBook.scala b/modules/fishnet/src/main/FishnetOpeningBook.scala index d141ec7589..48c15494cd 100644 --- a/modules/fishnet/src/main/FishnetOpeningBook.scala +++ b/modules/fishnet/src/main/FishnetOpeningBook.scala @@ -45,7 +45,7 @@ final private class FishnetOpeningBook( for { data <- res.body[JsValue].validate[Response](responseReader).asOpt _ = if (data.moves.isEmpty) outOfBook.put(game.id) - move <- data randomPonderedMove(game.turnColor, level) + move <- data randomPonderedMove (game.turnColor, level) } yield move.uci } .monTry { res => @@ -68,9 +68,9 @@ object FishnetOpeningBook { case class Response(moves: List[Move]) { def randomPonderedMove(turn: Color, level: Int): Option[Move] = { - val sum = moves.map(_.score(turn, level)).sum - val novelty = 5L * 14 // score of 5 winning games - val rng = ThreadLocalRandom.nextLong(sum + novelty) + val sum = moves.map(_.score(turn, level)).sum + val novelty = 5L * 14 // score of 5 winning games + val rng = ThreadLocalRandom.nextLong(sum + novelty) moves .foldLeft((none[Move], 0L)) { case ((found, it), next) => val nextIt = it + next.score(turn, level) @@ -84,8 +84,8 @@ object FishnetOpeningBook { def score(turn: Color, level: Int): Long = // interpolate: real frequency at lvl 1, expectation value at lvl 8 14L * turn.fold(white, black) + - (15L - level) * draws + - (16L - 2 * level) * turn.fold(black, white) + (15L - level) * draws + + (16L - 2 * level) * turn.fold(black, white) } implicit val moveReader = Json.reads[Move] From 1503257f91712e33ee75d13506da400962939ab9 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 4 Dec 2021 03:59:03 +0100 Subject: [PATCH 121/144] i18n: Fix /account/personal-data download button translation --- app/views/account/bits.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/account/bits.scala b/app/views/account/bits.scala index ce40637e2e..c95f002205 100644 --- a/app/views/account/bits.scala +++ b/app/views/account/bits.scala @@ -17,7 +17,7 @@ object bits { div(cls := "personal-data__header")( p("Here is all personal information Lichess has about ", userLink(u)), a(cls := "button", href := s"${routes.Account.data}?user=${u.id}&text=1", downloadAttr)( - trans.downloadRaw() + trans.download() ) ) ) From b24229ac36ba34864920150ec1bb9b2be19878f6 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 4 Dec 2021 04:36:09 +0100 Subject: [PATCH 122/144] Remove unreachable duplicate condition --- modules/study/src/main/StudyInvite.scala | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/study/src/main/StudyInvite.scala b/modules/study/src/main/StudyInvite.scala index 7c2a21e313..31415c2dcd 100644 --- a/modules/study/src/main/StudyInvite.scala +++ b/modules/study/src/main/StudyInvite.scala @@ -62,7 +62,6 @@ final private class StudyInvite( else if (inviter.roles has "ROLE_COACH") 20 else if (inviter.hasTitle) 20 else if (inviter.perfs.bestRating >= 2000) 50 - else if (invited.hasTitle) 200 else 100 _ <- shouldNotify ?? notifyRateLimit(inviter.id, rateLimitCost) { val notificationContent = InvitedToStudy( From 546c8e23271a52cc1e9fdd974bd545606d2ee76b Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 4 Dec 2021 04:48:10 +0100 Subject: [PATCH 123/144] Allow StudyAdmins to invite Lichess --- modules/study/src/main/StudyInvite.scala | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/study/src/main/StudyInvite.scala b/modules/study/src/main/StudyInvite.scala index 31415c2dcd..e67bfc8e34 100644 --- a/modules/study/src/main/StudyInvite.scala +++ b/modules/study/src/main/StudyInvite.scala @@ -40,7 +40,9 @@ final private class StudyInvite( invited <- userRepo .named(invitedUsername) - .map(_.filterNot(_.id == User.lichessId)) orFail "No such invited" + .map( + _.filterNot(_.id == User.lichessId && !Granter(_.StudyAdmin)(inviter)) + ) orFail "No such invited" _ <- study.members.contains(invited) ?? fufail[Unit]("Already a member") relation <- relationApi.fetchRelation(invited.id, byUserId) _ <- relation.has(Block) ?? fufail[Unit]("This user does not want to join") From 790afe139ded6fcbd79654bbbfe23c87d50b77d3 Mon Sep 17 00:00:00 2001 From: Thomas Daniels Date: Mon, 6 Dec 2021 21:04:28 +0100 Subject: [PATCH 124/144] Add AWC 2021 trophy --- public/images/trophy/atomicwc21.png | Bin 0 -> 12628 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 public/images/trophy/atomicwc21.png diff --git a/public/images/trophy/atomicwc21.png b/public/images/trophy/atomicwc21.png new file mode 100644 index 0000000000000000000000000000000000000000..805c4c81842846d62c12d6cfd422847d97965a4a GIT binary patch literal 12628 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 Date: Sat, 4 Dec 2021 05:38:16 +0100 Subject: [PATCH 125/144] Update sbt-scalafmt to 2.4.5 --- project/plugins.sbt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/plugins.sbt b/project/plugins.sbt index 3832063891..7ff9c64724 100644 --- a/project/plugins.sbt +++ b/project/plugins.sbt @@ -3,5 +3,5 @@ resolvers += Resolver.url( url("https://raw.githubusercontent.com/ornicar/lila-maven/master") )(Resolver.ivyStylePatterns) addSbtPlugin("com.typesafe.play" % "sbt-plugin" % "2.8.8-lila_1.8") -addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.4") +addSbtPlugin("org.scalameta" % "sbt-scalafmt" % "2.4.5") addSbtPlugin("ch.epfl.scala" % "sbt-bloop" % "1.4.11") From d9b7ba6139bb64efdd19e4e576d91e00c341f097 Mon Sep 17 00:00:00 2001 From: Benedikt Werner <1benediktwerner@gmail.com> Date: Sat, 4 Dec 2021 03:47:15 +0100 Subject: [PATCH 126/144] Use 'Cross-Origin-Embedder-Policy: credentialless' in Chrome 96+ On pages embedding Stockfish (i.e. using SharedArrayBuffer). To allow custom backgrounds from non-CORS pages. --- app/controllers/LilaController.scala | 5 +++-- modules/common/src/main/HTTPRequest.scala | 1 + 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/controllers/LilaController.scala b/app/controllers/LilaController.scala index c2d3f5a409..3d3a0a6e8c 100644 --- a/app/controllers/LilaController.scala +++ b/app/controllers/LilaController.scala @@ -65,10 +65,11 @@ abstract private[controllers] class LilaController(val env: Env) implicit def reqConfig(implicit req: RequestHeader) = ui.EmbedConfig(req) def reqLang(implicit req: RequestHeader) = I18nLangPicker(req) - protected def EnableSharedArrayBuffer(res: Result): Result = + protected def EnableSharedArrayBuffer(res: Result)(implicit req: RequestHeader): Result = res.withHeaders( "Cross-Origin-Opener-Policy" -> "same-origin", - "Cross-Origin-Embedder-Policy" -> "require-corp" + "Cross-Origin-Embedder-Policy" -> (if (HTTPRequest isChrome96OrMore req) "credentialless" + else "require-corp") ) protected def NoCache(res: Result): Result = diff --git a/modules/common/src/main/HTTPRequest.scala b/modules/common/src/main/HTTPRequest.scala index af04590827..a270b82423 100644 --- a/modules/common/src/main/HTTPRequest.scala +++ b/modules/common/src/main/HTTPRequest.scala @@ -45,6 +45,7 @@ object HTTPRequest { private def uaContains(req: RequestHeader, str: String) = userAgent(req).exists(_ contains str) def isChrome(req: RequestHeader) = uaContains(req, "Chrome/") + val isChrome96OrMore = UaMatcher("""Chrome/(?:\d{3,}|9[6-9])""") def origin(req: RequestHeader): Option[String] = req.headers get HeaderNames.ORIGIN From 9ddd0a763aaf847ead513f261cc11823eb831ae1 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Fri, 3 Dec 2021 10:06:59 +0100 Subject: [PATCH 127/144] Update scalafmt-core to 3.2.1 --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index cd36a01e4d..1ae23d88e5 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = "2.7.5" +version = "3.2.1" align.preset = more maxColumn = 110 spaces.inImportCurlyBraces = true From f5463f8686d9218be0287689923675c93ae5c816 Mon Sep 17 00:00:00 2001 From: Gian-Carlo Pascutto Date: Wed, 1 Dec 2021 15:30:24 +0100 Subject: [PATCH 128/144] Improve maximum hash table memory estimation. Some browsers don't report navigator.deviceMemory to keep fingerprintability to a minimum. Currently, lila assumes that such a machine provides at most 256MB RAM, thus setting a 32MB max hashtable limit. This was a reasonable starting point when Stockfish was pure JavaScript in a single thread, but with up 16 core WASM support this limitation is a bit more painful. Modify the memory estimation: if the browser is modern enough to have full WASM/SharedArrayBuffer support, and thus run the parallel WASM versions, assume the machine has at least 2GB of RAM. If it doesn't, assume 512MB. This gives a maximum hashtable of 256MB and 64MB, respectively. The assumed minimum RAM values roughly correspond to Firefox's official system requirements for the 32-bit and 64-bit version. Although that doesn't necessarily correspond to WASM support, it's a reasonable starting point for a guess. Note that guessing wrong isn't harmful, it just gives the user the option to shoot themselves in the foot, and even that is limited because we still only use 1/8th of the guessed system RAM. --- ui/ceval/src/ctrl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/ceval/src/ctrl.ts b/ui/ceval/src/ctrl.ts index b33d94ecdc..3a02f1d441 100644 --- a/ui/ceval/src/ctrl.ts +++ b/ui/ceval/src/ctrl.ts @@ -117,7 +117,8 @@ export default function (opts: CevalOpts): CevalCtrl { Math.min(Math.ceil((navigator.hardwareConcurrency || 1) / 4), maxThreads) ); - const maxHashSize = Math.min(((navigator.deviceMemory || 0.25) * 1024) / 8, growableSharedMem ? 1024 : 16); + const estimatedMinMemory = (technology == 'hce' || technology == 'nnue') ? 2.0 : 0.5; + const maxHashSize = Math.min(((navigator.deviceMemory || estimatedMinMemory) * 1024) / 8, growableSharedMem ? 1024 : 16); const hashSize = storedProp(storageKey('ceval.hash-size'), 16); const multiPv = storedProp(storageKey('ceval.multipv'), opts.multiPvDefault || 1); From e323a8595be464aac46b8fb1478d19a51f67b328 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Mon, 6 Dec 2021 23:31:26 +0100 Subject: [PATCH 129/144] Revert "Update scalafmt-core to 3.2.1" This reverts commit 9ddd0a763aaf847ead513f261cc11823eb831ae1. --- .scalafmt.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index 1ae23d88e5..cd36a01e4d 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -1,4 +1,4 @@ -version = "3.2.1" +version = "2.7.5" align.preset = more maxColumn = 110 spaces.inImportCurlyBraces = true From 744abd80cc700be43e8061456d3e60b0192b88c9 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Mon, 6 Dec 2021 22:59:39 +0100 Subject: [PATCH 130/144] scalafmt and prettier --- .scalafmt.conf | 1 + app/controllers/LilaController.scala | 2 +- ui/ceval/src/ctrl.ts | 7 +++++-- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/.scalafmt.conf b/.scalafmt.conf index cd36a01e4d..055fff1659 100644 --- a/.scalafmt.conf +++ b/.scalafmt.conf @@ -4,3 +4,4 @@ maxColumn = 110 spaces.inImportCurlyBraces = true rewrite.rules = [SortImports, RedundantParens, SortModifiers] rewrite.redundantBraces.stringInterpolation = true +runner.dialect = scala213 diff --git a/app/controllers/LilaController.scala b/app/controllers/LilaController.scala index 3d3a0a6e8c..d0a2f8d1b5 100644 --- a/app/controllers/LilaController.scala +++ b/app/controllers/LilaController.scala @@ -67,7 +67,7 @@ abstract private[controllers] class LilaController(val env: Env) protected def EnableSharedArrayBuffer(res: Result)(implicit req: RequestHeader): Result = res.withHeaders( - "Cross-Origin-Opener-Policy" -> "same-origin", + "Cross-Origin-Opener-Policy" -> "same-origin", "Cross-Origin-Embedder-Policy" -> (if (HTTPRequest isChrome96OrMore req) "credentialless" else "require-corp") ) diff --git a/ui/ceval/src/ctrl.ts b/ui/ceval/src/ctrl.ts index 3a02f1d441..925b505a87 100644 --- a/ui/ceval/src/ctrl.ts +++ b/ui/ceval/src/ctrl.ts @@ -117,8 +117,11 @@ export default function (opts: CevalOpts): CevalCtrl { Math.min(Math.ceil((navigator.hardwareConcurrency || 1) / 4), maxThreads) ); - const estimatedMinMemory = (technology == 'hce' || technology == 'nnue') ? 2.0 : 0.5; - const maxHashSize = Math.min(((navigator.deviceMemory || estimatedMinMemory) * 1024) / 8, growableSharedMem ? 1024 : 16); + const estimatedMinMemory = technology == 'hce' || technology == 'nnue' ? 2.0 : 0.5; + const maxHashSize = Math.min( + ((navigator.deviceMemory || estimatedMinMemory) * 1024) / 8, + growableSharedMem ? 1024 : 16 + ); const hashSize = storedProp(storageKey('ceval.hash-size'), 16); const multiPv = storedProp(storageKey('ceval.multipv'), opts.multiPvDefault || 1); From ece789bbf85da06b56da28e99711a0575e5bce0e Mon Sep 17 00:00:00 2001 From: Carlton McFarlane Date: Tue, 7 Dec 2021 14:49:06 +0100 Subject: [PATCH 131/144] Update mobile screenshot (#10204) * Update screenshot and styles on mobile page * Add IntelliJ files to .gitignore * Add improved mobile image. Reinstate inline styles in mobile.scala template --- .gitignore | 3 +++ app/views/mobile.scala | 10 +++++----- public/images/mobile/lichesstv-mobile.png | Bin 0 -> 74970 bytes ui/site/css/_mobile.scss | 6 +++++- 4 files changed, 13 insertions(+), 6 deletions(-) create mode 100644 public/images/mobile/lichesstv-mobile.png diff --git a/.gitignore b/.gitignore index d13065b4f4..604ea84582 100644 --- a/.gitignore +++ b/.gitignore @@ -48,3 +48,6 @@ RUNNING_PID # VSCode auto-generated files .vscode/ + +# IntelliJ auto-generated files +.idea diff --git a/app/views/mobile.scala b/app/views/mobile.scala index 039c6805d6..6b4620651d 100644 --- a/app/views/mobile.scala +++ b/app/views/mobile.scala @@ -47,11 +47,11 @@ object mobile { ), div(cls := "right-side")( img( - cls := "nexus5-playing", - width := "268", - height := "513", - src := assetUrl("images/mobile/nexus5-playing.png"), - alt := "Lichess mobile on nexus 5" + width := "437", + height := "883", + cls := "mobile-playing", + src := assetUrl("images/mobile/lichesstv-mobile.png"), + alt := "Lichess TV on mobile" ), img( cls := "qrcode", diff --git a/public/images/mobile/lichesstv-mobile.png b/public/images/mobile/lichesstv-mobile.png new file mode 100644 index 0000000000000000000000000000000000000000..c1fd4bc8442d2a05004817324fb04a9fcefd1573 GIT binary patch literal 74970 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

e^6sdwmmOS@Y;*)>7kkI>F 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 literal 0 HcmV?d00001 diff --git a/ui/site/css/_mobile.scss b/ui/site/css/_mobile.scss index 4dec9a9275..c8a30632f6 100644 --- a/ui/site/css/_mobile.scss +++ b/ui/site/css/_mobile.scss @@ -40,9 +40,13 @@ } .right-side { - flex: 0 0 264px; + flex: 0 0 33%; text-align: center; + .mobile-playing { + width: 100%; + } + .qrcode { margin-top: 20px; } From 73ac7fb08c94d6d877ec097ecb508f17896029b0 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 7 Dec 2021 14:54:14 +0100 Subject: [PATCH 132/144] compress lichesstv-mobile.png --- public/images/mobile/lichesstv-mobile.png | Bin 74970 -> 58387 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/public/images/mobile/lichesstv-mobile.png b/public/images/mobile/lichesstv-mobile.png index c1fd4bc8442d2a05004817324fb04a9fcefd1573..1a47c68f2940c1f3dc231c9899ce6024fd12e092 100644 GIT binary patch literal 58387 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%0A{0e!JVnrthk6FA7>k;N^OpW;kLky%&YSi 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 literal 74970 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

e^6sdwmmOS@Y;*)>7kkI>F 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 From aa2273d2fdb098d6f24a69e29c71f04dbca4059a Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 7 Dec 2021 22:57:03 +0100 Subject: [PATCH 133/144] use scalatags styles only explicitly fixes accidental inline styles that should have been attributes. thanks @bigsee --- app/ui/scalatags.scala | 9 +++++---- app/views/base/layout.scala | 2 +- app/views/base/notFound.scala | 4 ++-- app/views/coach/picture.scala | 4 ++-- app/views/coach/show.scala | 4 ++-- app/views/mobile.scala | 8 ++++---- app/views/site/dailyPuzzleSlackApp.scala | 4 ++-- app/views/user/show/otherTrophies.scala | 2 +- 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/app/ui/scalatags.scala b/app/ui/scalatags.scala index 73d7c3b85b..db018d3444 100644 --- a/app/ui/scalatags.scala +++ b/app/ui/scalatags.scala @@ -99,15 +99,16 @@ trait ScalatagsPrefix { // what to import in a pure scalatags template trait ScalatagsTemplate - extends Styles - with ScalatagsBundle + extends ScalatagsBundle with ScalatagsAttrs with ScalatagsExtensions with ScalatagsSnippets with ScalatagsPrefix { - val trans = lila.i18n.I18nKeys - def main = scalatags.Text.tags2.main + val trans = lila.i18n.I18nKeys + def main = scalatags.Text.tags2.main + def cssWidth = scalatags.Text.styles.width + def cssHeight = scalatags.Text.styles.height /* Convert play URLs to scalatags attributes with toString */ implicit val playCallAttr = genericAttr[play.api.mvc.Call] diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index 85ae19538b..24feb6feb5 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -244,7 +244,7 @@ object layout { content := openGraph.fold(trans.siteDescription.txt())(o => o.description), name := "description" ), - link(rel := "mask-icon", href := assetUrl("logo/lichess.svg"), color := "black"), + link(rel := "mask-icon", href := assetUrl("logo/lichess.svg"), "color".attr := "black"), favicons, !robots option raw(""""""), noTranslate, diff --git a/app/views/base/notFound.scala b/app/views/base/notFound.scala index 459e53ba7c..0725f2900e 100644 --- a/app/views/base/notFound.scala +++ b/app/views/base/notFound.scala @@ -32,8 +32,8 @@ object notFound { iframe( src := assetUrl(s"vendor/ChessPursuit/bin-release/index.html"), st.frameborder := 0, - width := 400, - height := 500 + widthA := 400, + heightA := 500 ), p(cls := "credits")( a(href := "https://github.com/Saturnyn/ChessPursuit")("ChessPursuit"), diff --git a/app/views/coach/picture.scala b/app/views/coach/picture.scala index 083f2bec66..019b38ab77 100644 --- a/app/views/coach/picture.scala +++ b/app/views/coach/picture.scala @@ -46,8 +46,8 @@ object picture { img( widthA := Coach.imageSize, heightA := Coach.imageSize, - width := cssSize, - height := cssSize, + cssWidth := cssSize, + cssHeight := cssSize, cls := "picture", src := url(c.coach), alt := s"${c.user.titleUsername} Lichess coach picture" diff --git a/app/views/coach/show.scala b/app/views/coach/show.scala index 5f544a59fd..f1dc4f3b46 100644 --- a/app/views/coach/show.scala +++ b/app/views/coach/show.scala @@ -97,8 +97,8 @@ object show { div(cls := "list")( profile.youtubeUrls.map { url => iframe( - width := "256", - height := "192", + widthA := "256", + heightA := "192", src := url.value, attr("frameborder") := "0", frame.allowfullscreen diff --git a/app/views/mobile.scala b/app/views/mobile.scala index 6b4620651d..a340e42239 100644 --- a/app/views/mobile.scala +++ b/app/views/mobile.scala @@ -47,16 +47,16 @@ object mobile { ), div(cls := "right-side")( img( - width := "437", - height := "883", + widthA := "437", + heightA := "883", cls := "mobile-playing", src := assetUrl("images/mobile/lichesstv-mobile.png"), alt := "Lichess TV on mobile" ), img( cls := "qrcode", - width := "200", - height := "200", + widthA := "200", + heightA := "200", src := assetUrl("images/mobile/dynamic-qrcode.png"), alt := "Download QR code" ) diff --git a/app/views/site/dailyPuzzleSlackApp.scala b/app/views/site/dailyPuzzleSlackApp.scala index d0543af4a8..0a1d0406bd 100644 --- a/app/views/site/dailyPuzzleSlackApp.scala +++ b/app/views/site/dailyPuzzleSlackApp.scala @@ -25,8 +25,8 @@ object dailyPuzzleSlackApp { )( img( alt := "Add to Slack", - height := 40, - width := 139, + heightA := 40, + widthA := 139, src := assetUrl("images/add-to-slack.png") ) ), diff --git a/app/views/user/show/otherTrophies.scala b/app/views/user/show/otherTrophies.scala index 0c1411e6dc..55cce07d48 100644 --- a/app/views/user/show/otherTrophies.scala +++ b/app/views/user/show/otherTrophies.scala @@ -46,7 +46,7 @@ object otherTrophies { ariaTitle(t.kind.name), style := "width: 65px; margin: 0 3px!important;" )( - img(src := assetUrl(s"images/trophy/${t.kind._id}.png"), width := 65, height := 80) + img(src := assetUrl(s"images/trophy/${t.kind._id}.png"), cssWidth := 65, cssHeight := 80) ) }, info.trophies.filter(_.kind.klass.has("icon3d")).sorted.map { trophy => From 3fc388278c8b690bd7e1fff199619d1de3febf27 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 7 Dec 2021 23:04:58 +0100 Subject: [PATCH 134/144] fixup color attr --- app/views/base/layout.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/views/base/layout.scala b/app/views/base/layout.scala index 24feb6feb5..d081e54861 100644 --- a/app/views/base/layout.scala +++ b/app/views/base/layout.scala @@ -244,7 +244,7 @@ object layout { content := openGraph.fold(trans.siteDescription.txt())(o => o.description), name := "description" ), - link(rel := "mask-icon", href := assetUrl("logo/lichess.svg"), "color".attr := "black"), + link(rel := "mask-icon", href := assetUrl("logo/lichess.svg"), attr("color") := "black"), favicons, !robots option raw(""""""), noTranslate, From cfc3534de794277bb332e85de0b50c73e17ef93d Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Tue, 7 Dec 2021 23:10:06 +0100 Subject: [PATCH 135/144] fixup mobile image height --- ui/site/css/_mobile.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/ui/site/css/_mobile.scss b/ui/site/css/_mobile.scss index c8a30632f6..68bedd58fc 100644 --- a/ui/site/css/_mobile.scss +++ b/ui/site/css/_mobile.scss @@ -45,6 +45,7 @@ .mobile-playing { width: 100%; + height: auto; } .qrcode { From 6e99139e5004e2078b5e30a4123814cff9ad44f4 Mon Sep 17 00:00:00 2001 From: kraktus Date: Thu, 9 Dec 2021 11:20:01 +0100 Subject: [PATCH 136/144] Swiss round page is not an infiniteScroll page fix https://github.com/ornicar/lila/issues/10209 Could not test --- app/views/swiss/show.scala | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/app/views/swiss/show.scala b/app/views/swiss/show.scala index 97ad4d06bc..41f7a5a688 100644 --- a/app/views/swiss/show.scala +++ b/app/views/swiss/show.scala @@ -83,8 +83,7 @@ object show { def round(s: Swiss, r: SwissRound.Number, pairings: Paginator[SwissPairing])(implicit ctx: Context) = views.html.base.layout( title = s"${fullName(s)} • Round $r/${s.round}", - moreCss = cssTag("swiss.show"), - moreJs = infiniteScrollTag + moreCss = cssTag("swiss.show") ) { val pager = views.html.base.bits .pagination(p => routes.Swiss.round(s.id.value, p).url, r.value, s.round.value, showPost = true) @@ -95,7 +94,6 @@ object show { ), pager(cls := "pagination--top"), table(cls := "slist slist-pad")( - tbody(cls := "infinite-scroll")( pairings.currentPageResults map { p => tr(cls := "paginated")( td(a(href := routes.Round.watcher(p.gameId, "white"), cls := "glpt")(s"#${p.gameId}")), @@ -104,9 +102,7 @@ object show { td(p strResultOf chess.Black), td(userIdLink(p.black.some)) ) - }, - pagerNextTable(pairings, p => routes.Swiss.round(s.id.value, r.value).url) - ) + } ), pager(cls := "pagination--bottom") ) From f6621d2812be1c6db3de18f6a520ce5d3a236a13 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Fri, 10 Dec 2021 08:03:46 +0100 Subject: [PATCH 137/144] New Crowdin updates (#10182) * New translations: ublog.xml (Czech) * New translations: emails.xml (Kazakh) * New translations: emails.xml (Kyrgyz) * New translations: site.xml (Vietnamese) * New translations: site.xml (Vietnamese) * New translations: swiss.xml (German, Switzerland) * New translations: site.xml (Kazakh) * New translations: puzzleTheme.xml (Kazakh) * New translations: ublog.xml (Catalan) * New translations: ublog.xml (Catalan) * New translations: site.xml (Belarusian) * New translations: preferences.xml (Indonesian) * New translations: team.xml (Indonesian) * New translations: swiss.xml (Indonesian) * New translations: preferences.xml (Arabic) * New translations: site.xml (German) * New translations: site.xml (Breton) * New translations: preferences.xml (Vietnamese) * New translations: puzzleTheme.xml (Persian) * New translations: puzzleTheme.xml (Persian) * New translations: puzzleTheme.xml (Persian) * New translations: puzzleTheme.xml (Persian) * New translations: ublog.xml (Korean) * New translations: site.xml (Sardinian) * New translations: site.xml (Sardinian) * New translations: contact.xml (Kazakh) * New translations: preferences.xml (Kazakh) * New translations: faq.xml (Kazakh) * New translations: preferences.xml (Kazakh) * New translations: preferences.xml (Kazakh) * New translations: faq.xml (Kazakh) * New translations: site.xml (Gujarati) * New translations: learn.xml (French) * New translations: swiss.xml (French) * New translations: activity.xml (Polish) * New translations: site.xml (Hindi) * New translations: emails.xml (Belarusian) * New translations: emails.xml (Belarusian) --- translation/dest/activity/pl-PL.xml | 4 ++-- translation/dest/contact/kk-KZ.xml | 2 +- translation/dest/emails/be-BY.xml | 18 +++++++++--------- translation/dest/emails/kk-KZ.xml | 2 +- translation/dest/emails/ky-KG.xml | 4 +++- translation/dest/faq/kk-KZ.xml | 2 +- translation/dest/learn/fr-FR.xml | 2 +- translation/dest/preferences/ar-SA.xml | 1 + translation/dest/preferences/id-ID.xml | 1 + translation/dest/preferences/kk-KZ.xml | 3 ++- translation/dest/preferences/vi-VN.xml | 1 + translation/dest/puzzleTheme/fa-IR.xml | 21 +++++++++++++++++++++ translation/dest/puzzleTheme/kk-KZ.xml | 2 +- translation/dest/site/be-BY.xml | 2 +- translation/dest/site/br-FR.xml | 10 +++++----- translation/dest/site/de-DE.xml | 2 +- translation/dest/site/gu-IN.xml | 1 + translation/dest/site/hi-IN.xml | 2 +- translation/dest/site/kk-KZ.xml | 2 +- translation/dest/site/sc-IT.xml | 16 +++++++++++++++- translation/dest/site/vi-VN.xml | 3 +++ translation/dest/swiss/de-CH.xml | 2 +- translation/dest/swiss/fr-FR.xml | 6 +++--- translation/dest/swiss/id-ID.xml | 5 +++++ translation/dest/team/id-ID.xml | 3 +++ translation/dest/ublog/ca-ES.xml | 16 ++++++++++++++++ translation/dest/ublog/cs-CZ.xml | 1 + translation/dest/ublog/ko-KR.xml | 2 ++ 28 files changed, 104 insertions(+), 32 deletions(-) diff --git a/translation/dest/activity/pl-PL.xml b/translation/dest/activity/pl-PL.xml index 0484ddf9cc..3b555e870f 100644 --- a/translation/dest/activity/pl-PL.xml +++ b/translation/dest/activity/pl-PL.xml @@ -3,8 +3,8 @@ Aktywność Udostępnił stream na żywo - Wsparcie lichess.org przez miesiąc jako %2$s - Wsparcie lichess.org przez %1$s miesiące jako %2$s + Wspomógł lichess.org przez miesiąc jako %2$s + Wspiera Lichess przez %1$s miesiące jako %2$s Wsparcie lichess.org przez %1$s miesięcy jako %2$s Wsparcie lichess.org przez %1$s miesięcy jako %2$s diff --git a/translation/dest/contact/kk-KZ.xml b/translation/dest/contact/kk-KZ.xml index e6b60417ad..7664061d15 100644 --- a/translation/dest/contact/kk-KZ.xml +++ b/translation/dest/contact/kk-KZ.xml @@ -51,7 +51,7 @@ FIDE Шахмат Заңының §6.9 сәйкес, егер ережеге сай кез-келген жүрістермен матқа жету мүмкінкіндігі бар болса, онда ойын қорытындысы тепе-тең деп саналмайды Егер қарсыласта патшадан басқа тағы тас болса, оған матты жалғыз ат немесе пілмен ғана қоюға болады. Ешбір рейтинг ұпайы қосылмады - Бағаланатын ойын екенін тексеріңіз. Қалыпты ойындар ойыншының рейтингіне әсер етпейді. + Бағаланатын ойын екенін тексеріңіз. Жай ойындар ойыншының рейтингіне әсер етпейді. Ақаулық парақшасы Ақаулық парақшасына тап болсаңыз, осында хабарлауыңызға болады: Шектеуге не IP шектеуге қарсылық шағымы diff --git a/translation/dest/emails/be-BY.xml b/translation/dest/emails/be-BY.xml index 7a5255109c..b8de0546e2 100644 --- a/translation/dest/emails/be-BY.xml +++ b/translation/dest/emails/be-BY.xml @@ -2,21 +2,21 @@ %s, пацвердзіце ваш уліковы запіс на lichess.org Націсніце на спасылку, каб актываваць уліковы запіс Lichess: - Калі вы не рэгістраваліся на Lichess, проста праігнаруйце гэты ліст. - %s, вы скідваеце ваш пароль на lichess.org - Мы атрымалі запыт на скасаванне пароля да вашага ўліковага запісу. + Калі вы не рэгістраваліся на Lichess, проста праігнаруйце гэта паведамленне. + %s, вы скідаеце ваш пароль на lichess.org + Мы атрымалі запыт на скіданне пароля да вашага ўліковага запісу. Калі запыт выкананы вамі, націсніце на спасылку ніжэй. Калі не, праігнаруйце гэты ліст. %s, пацвердзіце новы адрас пошты Мы атрымалі запыт на змену вашага адраса электроннай пошты. - Доўжачы пацверджанні наяўнасці доступу да гэтай пошты, калі ласка, націсніце на спасылку ніжэй: + Для пацвярджэння таго, што ў вас ёсць доступ да адраса электроннай пошты, калі ласка, націсніце спасылку ніжэй: Вітаем вас на lichess.org, %s - Вы паспяхова зарэгістравалі свой акаўнт на https://lichess.org. + Вы паспяхова зарэгістравалі свой уліковы запіс на https://lichess.org. -Вось спасылка на Ваш профіль: %1$s. Вы можаце наладзіць яго тут %2$s. +Вось спасылка на ваш профіль: %1$s. Вы можаце наладзіць яго тут %2$s. -Поспехаў, і ды знойдуць Вашы фігуры шлях да караля суперніка! +Поспехаў, і ды знойдуць Вашы фігуры шлях да караля саперніка! Уваход на lichess.org, %s - (Клік па спасылцы не працуе? Паспрабуйце ўставіць у ваш браўзер!) - Гэта службовы ліст ад %s. + (Націск на спасылку не працуе? Паспрабуйце ўставіць у ваш браўзер!) + Гэта службовы ліст, які звязаны з вашым выкарыстаннем %s. Каб звязацца з намі, калі ласка, выкарыстоўвайце %s. diff --git a/translation/dest/emails/kk-KZ.xml b/translation/dest/emails/kk-KZ.xml index 6485c264e4..739a87cd5a 100644 --- a/translation/dest/emails/kk-KZ.xml +++ b/translation/dest/emails/kk-KZ.xml @@ -6,7 +6,7 @@ %s, lichess.org құпиясөзін қайта орнатыңыз Бізге тіркелгіңіздің кұпиясөзін қайта орнату туралы тапсырыс келді. Егер бұл тапсырысты жіберген сіз болсаңыз, төмендегі сілтемеге басыңыз. Кері жағдайда осы хатты назарсыз қалдыра салыңыз. - %s, жаңа поштаңызды растаңыз + Жаңа поштаңызды растаңыз Сіз поштаңызды ауыстыруға тапсырыс жіберген едіңіз. Сол поштаға кіруге рұқсатыңыз бар екенін растау үшін төмендегі сілтемеге басыңыз: %s, Личес-ке қош келдіңіз diff --git a/translation/dest/emails/ky-KG.xml b/translation/dest/emails/ky-KG.xml index 3ea04e700d..52e9f564fe 100644 --- a/translation/dest/emails/ky-KG.xml +++ b/translation/dest/emails/ky-KG.xml @@ -1,2 +1,4 @@ - + + (Башсаныз иштебей жатыр бы? Анда башка браузер мен кириниз) + diff --git a/translation/dest/faq/kk-KZ.xml b/translation/dest/faq/kk-KZ.xml index a1ccc5688f..3ecfca3adb 100644 --- a/translation/dest/faq/kk-KZ.xml +++ b/translation/dest/faq/kk-KZ.xml @@ -123,7 +123,7 @@ Рейтингті салыстырмалы шама деп ойлаған дұрыс: ойыншылар арасында рейтинг айырмашылығына қарап, кім жеңетінін, тең ойнайтынын не жеңілетінін бағалауға болады. \"Менің рейтингім осынша\" дегеннің ешбір мәні жоқ, тек басқа ойыншымен салыстырғаннан пайдасы болар. Рейтингті ойын кезінде қалай жасырсам болады? - %1$s-ге кіріп Zen қалпын қосыңыз немесе ойын кезінде %2$s басыңыз. + %1$s-ге кіріп Оқшау көріністі қосыңыз немесе ойын кезінде %2$s басыңыз. баптауларды көрсету Желінің ажырауы/кідірісі кесірінен ойын тоқталды. Рейтинг ұпайларын қайтара аласыз ба? Өкінішке орай, бұл ажырау не кідіріс кімнің жағынан болғанына қарамастан, біз осы жайтта ұпайды қайтара алмаймыз. Біздің жағымыздан туындаған ажырау мен кідіріс – мүлдем сирек болатын жағдай. Онымен қатар, Личес жүйесінің қайта қосылуынан сіз уақыт жоғалтсаңыз, әділсіз шығын болмағандай біз ойынды үзіп тастаймыз. diff --git a/translation/dest/learn/fr-FR.xml b/translation/dest/learn/fr-FR.xml index 6ecc924c2a..044bf257b8 100644 --- a/translation/dest/learn/fr-FR.xml +++ b/translation/dest/learn/fr-FR.xml @@ -185,7 +185,7 @@ Fou = 3 Cavalier = 3 Pion = 1 Échec en deux coups - Donnez un échec en deux coups + Mettez échec en deux coups Trouvez la bonne combinaison en deux coups qui met le Roi de l\'adversaire en échec ! Menacez le Roi adverse en deux coups ! diff --git a/translation/dest/preferences/ar-SA.xml b/translation/dest/preferences/ar-SA.xml index 471302a527..3aed58d867 100644 --- a/translation/dest/preferences/ar-SA.xml +++ b/translation/dest/preferences/ar-SA.xml @@ -12,6 +12,7 @@ رمز قطعة الشطرنج حروف (K, Q, R, B, N) وضع التأمل + إظهار تقييمات اللاعب أظهر زر تعديل حجم الرقعة خلال الوضع المبدئي فقط (معصوب العينين (إخفاء القطع diff --git a/translation/dest/preferences/id-ID.xml b/translation/dest/preferences/id-ID.xml index 1832005ca1..8707e938fc 100644 --- a/translation/dest/preferences/id-ID.xml +++ b/translation/dest/preferences/id-ID.xml @@ -12,6 +12,7 @@ Simbol bidak catur Huruf (K, Q, R, B, N) Moda Zen + Munculkan rating pemain Tampilkan pengubah ukuran papan catur Hanya di posisi awal Catur buta (buah catur disembunyikan) diff --git a/translation/dest/preferences/kk-KZ.xml b/translation/dest/preferences/kk-KZ.xml index 725cef5318..85cf551514 100644 --- a/translation/dest/preferences/kk-KZ.xml +++ b/translation/dest/preferences/kk-KZ.xml @@ -11,7 +11,8 @@ Жүрісті жазу тәртібі Тас таңбасы Әріптер (K, Q, R, B, N) - Zen қалпы + Оқшау көрініс + Ойыншылардың рейтингін көрсету Тақтаны үлкейту тұтқасын көрсету Тек бастапқы күйде ғана Бүркемелі шахмат (көрінбейтін тастар) diff --git a/translation/dest/preferences/vi-VN.xml b/translation/dest/preferences/vi-VN.xml index 0db6e5698d..ce506d36e8 100644 --- a/translation/dest/preferences/vi-VN.xml +++ b/translation/dest/preferences/vi-VN.xml @@ -12,6 +12,7 @@ Biểu tượng quân cờ Ký tự (K, Q, R, B, N) Chế độ Zen + Hiện xếp hạng của người chơi Hiện nút thay đổi kích cỡ Chỉ ở vị trí khởi tạo Cờ tưởng (quân cờ vô hình) diff --git a/translation/dest/puzzleTheme/fa-IR.xml b/translation/dest/puzzleTheme/fa-IR.xml index f891024bb1..df97d18378 100644 --- a/translation/dest/puzzleTheme/fa-IR.xml +++ b/translation/dest/puzzleTheme/fa-IR.xml @@ -1,17 +1,28 @@ پیاده پیش رفته + یکی از پیاده های شما در زمین حریف پیشروی کرده است. شاید خطر ارتقا پیاده وجود داشته باشد. برتری مات آناستازیا + یک اسب و یک رخ به همدیگر کمک میکنند تا شاه حریف را بین گوشه های زمین و یک مهره از حریف زندانی کنند. مات عربی یک اسب و یک رخ برای به دام انداختن شاه حریف در گوشه صفحه همکاری می کنند. حمله به خانه f2 یا f7 + حمله ای که در آن روی پیاده های f2 و f7 تمرکز می شود، مانند دفاع دو اسب. جلب کردن مات عرض آخر به دام انداختن شاه حریف در عرض اولیه خود زمانی که با مهره های خودی به دام افتاده است. آخر بازی فیل + یک آخر بازی تنها با فیل ها و پیاده ها. + مات Boden + دو فیل به حالت ضربدری که به گوشه زمین حمله می کنند یک شاه که راهش با مهره های خودش سد شده را مات می کنند. قلعه رفتن شاه خود را ایمن کنید و رخ خود را برای حمله مستقر کنید. + مهره دفاع کننده را بگیرید + گرفتن یک مهره که برای دفاع از یک مهره دیگر حیاتی است، اجازه می دهد مهره ای که اکنون بدون دفاع است در حرکت بعدی گرفته شود. + مات با دو فیل + دو فیل که همزمان به گوشه های مجاور حمله می کنند، شاهی که راهش با مهره های خودش سد شده را مات می کنند. + وزیری که شاه مجاور خودش را که تنها دو خانه ای که برای فرارش باقی مانده توسط مهره های خودش سد شده، مات می کند. برابری حمله به جناه شاه حمله به شاه حریف زمانی که در جناه شاه قلعه رفته است. @@ -19,6 +30,7 @@ حرکت تدافعی منحرف کردن حمله برخاستی + حرکت دادن یک مهره (مانند اسب)، که قبلا جلوی حمله مهره خودمان به یکی از مهره های دور حریف (مانند رخ)، از جلوی راه مهره حمله کننده. کیش دوگانه کیش دادن به حریف با دو مهره به صورت هم زمان در نتیجهء یک حمله برخاستی که در آن هم مهره برخاست کننده و هم مهره پشت سر آن به شاه حریف حمله می کنند. آخر بازی @@ -29,9 +41,18 @@ حرکتی که در آن مهره ای که حرکت می کند دو مهره حریف را به صورت همزمان مورد حمله قرار می دهد. مهره بی دفاع تاکتیکی که در آن مهره های حریف برای گرفتن، بدون دفاع یا با دفاع ناکافی است. + مات Hook + مات با یک رخ، اسب و یک پیاده در برابر یک پیاده حریف برای محدود کردن راه های فرار شاه دشمن. + چنگال + حرکت دادن یک مهره و حمله کردن به دو مهره هم زمان که بتوان یکی از مهره ها را گرفت، مانند حمله یک اسب به به دو تا رخ بصورت هم زمان. + آخر بازی اسب + یک آخر بازی تنها با اسب ها و پیاده ها. معمای طولانی + سه حرکت برای پیروزی. بازی‌های اساتید + پازل های برگرفته شده از بازی هایی که توسط بازی کنان دارای عنوان انجام شده است. بازیهای استاد در مقابل استاد + پازل های برگرفته شده از بازی های بین دو بازی کن دارای عنوان. کیش و مات بازی را با سبک خاصی ببرید. مات در یک حرکت diff --git a/translation/dest/puzzleTheme/kk-KZ.xml b/translation/dest/puzzleTheme/kk-KZ.xml index f0e9c0ec01..b3bcb52bb4 100644 --- a/translation/dest/puzzleTheme/kk-KZ.xml +++ b/translation/dest/puzzleTheme/kk-KZ.xml @@ -2,7 +2,7 @@ Әккі сарбаз Айналуға ұмтылған не айналу қаупін төндіретін сарбаз – тактиканың өзегі. - Артықшылық + Басымдылық Шешуші артықшылық беретін сәтті жіберіп алмаңыз. (200-600 сп) Анастасия маты Ат пен тура не уәзірдің бірлесіп, қарсылас патшасын тақта шеті мен өз тасының арасында ұстап алуы. diff --git a/translation/dest/site/be-BY.xml b/translation/dest/site/be-BY.xml index 686ac67991..1c42e6686f 100644 --- a/translation/dest/site/be-BY.xml +++ b/translation/dest/site/be-BY.xml @@ -551,7 +551,7 @@ Махінацыі з рэйтынгам Іншае Пакіньце ніжэй спасылку на гульню (ці гульні) і патлумачце, што вас непакоіць у паводзінах гэтага карыстальніка. Не пішыце нешта кшталту «ён чмут!» – патлумачце, як вы прыйшлі да гэтага выніку. Мы хутчэй разбярэмся ў сітуацыі, калі вы напішаце нам па-англійску. - Калі ласка, дадайце спасылку сама менш на адну гульню, дзе, на вашу думку, былі парушаны правілы. + Калі ласка, дадайце спасылку хаця б на адну гульню, дзе былі парушаны правілы. аўтар: %s Тэма закрытая Блог diff --git a/translation/dest/site/br-FR.xml b/translation/dest/site/br-FR.xml index 862737901b..9968e1cad7 100644 --- a/translation/dest/site/br-FR.xml +++ b/translation/dest/site/br-FR.xml @@ -159,7 +159,7 @@ C\'hoarierien Mignoned Kaozeadennoù - Hiziv + Hiziv Dec\'h Munutennoù evit pep c\'hoarier Doare @@ -494,9 +494,9 @@ Kargit ul lec\'hiadur Digoradurioù brudet Plegennoù an dibenn-partienn - Plegenn orin + Plegenn orin Tennañ pep tra - Kargañ ul lec\'hiadur + Kargañ ul lec\'hiadur Prevez Diskleriañ %s d\'an habaskaerien Feur leuniañ ar profil: %s @@ -514,7 +514,7 @@ N\'eus ket pell zo war Lichess TV C\'hoarierien enlinenn C\'hoarierien oberiat - Diwallit, renket eo ar c\'hrogad met n\'eus horolaj ebet! + Diwallit, renket eo ar c\'hrogad met n\'eus horolaj ebet! Deuet oc\'h a-benn %s c\'hrogad o ren bremañ @@ -582,7 +582,7 @@ E-pad ar c\'hrogadoù difonn Bepred Morse - %1$s a gemer perzh e-barzh %2$s + %1$s a gemer perzh e-barzh %2$s Trec\'h Lamm %1$s a-enep %2$s e %3$s diff --git a/translation/dest/site/de-DE.xml b/translation/dest/site/de-DE.xml index 4c336213ae..1b9dd78921 100644 --- a/translation/dest/site/de-DE.xml +++ b/translation/dest/site/de-DE.xml @@ -59,7 +59,7 @@ Engine wird geladen ... Cloud-Analyse Tiefe erhöhen - Bedrohungen zeigen + Drohungen anzeigen im lokalen Browser Lokale Auswertung an-/ausschalten Variante aufwerten diff --git a/translation/dest/site/gu-IN.xml b/translation/dest/site/gu-IN.xml index 998bee2144..71faf0bd49 100644 --- a/translation/dest/site/gu-IN.xml +++ b/translation/dest/site/gu-IN.xml @@ -195,6 +195,7 @@ રદ કરો સફેદની સમય મર્યાદા પૂરી કાળાની સમય મર્યાદા પૂરી + ડ્રો ઓફર મોકલી સ્વીકારો નકારો અત્યારે રમાય છે diff --git a/translation/dest/site/hi-IN.xml b/translation/dest/site/hi-IN.xml index 0061f6e301..7e2d03a8be 100644 --- a/translation/dest/site/hi-IN.xml +++ b/translation/dest/site/hi-IN.xml @@ -472,7 +472,7 @@ हाँ सहायता एक नया विषय बनाएँ - विषयों की संख्या + विषय पोस्ट की संख्या आख़िरी पोस्ट देखे जाने की संख्या diff --git a/translation/dest/site/kk-KZ.xml b/translation/dest/site/kk-KZ.xml index d1fb657997..063f69bec9 100644 --- a/translation/dest/site/kk-KZ.xml +++ b/translation/dest/site/kk-KZ.xml @@ -750,7 +750,7 @@ Қайта жасау Ақтардың қателерін қарау Қаралардың қателерін қарау - Артықшылық + Басымдылық %s секунд %s секунд diff --git a/translation/dest/site/sc-IT.xml b/translation/dest/site/sc-IT.xml index 3ea04e700d..812c6eb280 100644 --- a/translation/dest/site/sc-IT.xml +++ b/translation/dest/site/sc-IT.xml @@ -1,2 +1,16 @@ - + + Gioga con unu amigu + Gioga chin su elaboradore + Para inbidai calicunu dona issu custu URL + As perdiu + Ispettende unu enemigu + Ispettende + A tue + %1$s difficultade %2$s + Difficultade + Fortza + Chat + Arrendidi + Scaccu maccu + diff --git a/translation/dest/site/vi-VN.xml b/translation/dest/site/vi-VN.xml index 2eb94c3df7..6b2655ce9a 100644 --- a/translation/dest/site/vi-VN.xml +++ b/translation/dest/site/vi-VN.xml @@ -46,6 +46,8 @@ Đen chịu thua Trắng đã thoát khỏi ván cờ Đen đã thoát khỏi ván cờ + Bên trắng không đi quân + Bên đen không đi quân Yêu cầu máy tính phân tích Máy tính phân tích Máy tính phân tích có sẵn @@ -83,6 +85,7 @@ Chiếu hết trong %s nước + DTZ50\"\" được làm tròn, dựa vào số nước đi quân cho tới nước bắt quân hoặc đi tốt tiếp theo Không có ván đấu nào trùng khớp Bạn có muốn thêm nhiều ván đấu hơn từ mục lục? Sách khai cuộc diff --git a/translation/dest/swiss/de-CH.xml b/translation/dest/swiss/de-CH.xml index 88093f791f..1960411642 100644 --- a/translation/dest/swiss/de-CH.xml +++ b/translation/dest/swiss/de-CH.xml @@ -38,7 +38,7 @@ Wänn sölläd Turniär noch Schwiizer Syschtem anschtatt Arena-Turniär verwändät wärdä? Imänä Turniär mit Schwiiizer System, spiiläd alli glich viil partie und jede spielt nur einisch gege jede andär. Ds chöönt ä guäti Waal si für Cklubs und offiziäli Turniär. Wiä wärdäd Pünkt berächnät? - Ein Sieg ist einen Punkt wert, ein Remis einen halben Punkt, und eine Niederlage null Punkte. Wenn ein Spieler während einer Runde keinem anderen Spieler zugeteilt werden kann, erhält er einen Punkt. + En Sieg isch ein Punkt wärt, es Remi zwei, und wenn verlürsch bechumsch keine. Wenn du kein Gänger hesch, bechumsch eifoch en Punkt. Wie wärdäd Tie-Breaks berächnät? Sonneborn-Berger-Wärtig Mit dem %s. diff --git a/translation/dest/swiss/fr-FR.xml b/translation/dest/swiss/fr-FR.xml index a815c3a06d..141d924bb6 100644 --- a/translation/dest/swiss/fr-FR.xml +++ b/translation/dest/swiss/fr-FR.xml @@ -19,7 +19,7 @@ %s secondes entre les rondes - %s minute entre les rondes + %s minutes entre les rondes %s minutes entre les rondes Commence dans @@ -87,8 +87,8 @@ Pourquoi? Parce qu\'il n\'existe pas de façon équitable de résoudre le probl N\'importe quel adversaire disponible avec un classement similaire Meilleur appariement selon le nombre de points et le départage Temps d\'attente pour l\'appariement - Rapide : n\'attend pas tous les joueurs - Lent : attend tous les joueurs + Rapide : n\'attend pas tous les joueurs + Lent : attend tous les joueurs Appariement identique Possible, mais pas de façon consécutive Interdit diff --git a/translation/dest/swiss/id-ID.xml b/translation/dest/swiss/id-ID.xml index f163b3563f..ba011035d5 100644 --- a/translation/dest/swiss/id-ID.xml +++ b/translation/dest/swiss/id-ID.xml @@ -22,10 +22,15 @@ Permainan yang sedang berlangsung + Tanggal mulai turnamen Jumlah babak Sebuah babak yang ganjil mengizinkan keseimbangan warna yang optimal. + Jarak antar ronde Turnamen Swiss baru penilaian Sonneborn–Berger + Dapatkah pemain telat bergabung? + Ya, sampai lebih dari setengah putaran dimulai; misalnya dalam 11 ronde swiss, pemain dapat bergabung sebelum ronde 6 dimulai dan dalam 12 ronde, sebelum ronde 7 dimulai. +Peserta yang terlambat mendapatkan satu \"bye\", bahkan jika mereka melewatkan beberapa ronde. Sedang berlangsung Segera dimulai Perbandingan diff --git a/translation/dest/team/id-ID.xml b/translation/dest/team/id-ID.xml index 8b736bdcd8..38f6915a2c 100644 --- a/translation/dest/team/id-ID.xml +++ b/translation/dest/team/id-ID.xml @@ -6,6 +6,7 @@ %s anggota Semua tim + Tim yang dipimpin Tim baru Tim saya Tidak ada tim yang ditemukan @@ -24,6 +25,7 @@ Permintaan bergabung Anda akan ditinjau oleh ketua tim. Permintaan bergabung Anda telah ditinjau oleh ketua tim. + Permintaan bergabung Anda telah ditolak oleh ketua tim. Berlangganan pesan tim Pertarungan Tim Pertarungan antara bebrapa tim, setiap pemain mencetak poin untuk timnya @@ -47,4 +49,5 @@ Pemain yang tidak suka menerima pesan anda dapat meninggalkan tim. Tim ini sudah ada. Turnamen selanjutnya Turnamen yang telah selesai + Permintaan yang Ditolak diff --git a/translation/dest/ublog/ca-ES.xml b/translation/dest/ublog/ca-ES.xml index 8425623a99..326eba9676 100644 --- a/translation/dest/ublog/ca-ES.xml +++ b/translation/dest/ublog/ca-ES.xml @@ -11,10 +11,26 @@ Publicat Publica al teu blog Si es marca l\'entrada es publicarà al teu blog. Sinó, serà privada i només es mostrarà en les publicacions en esborrany + + Ha publicat una entrada nova + Ha publicat %s entrades noves + + + Una visualització + %s visualitzacions + + %1$s ha publicat %2$s Aquesta entrada està publicada Això és un esborrany + Més publicacions de %s + Encara no hi ha publicacions en aquest blog. Cap esborrany a mostrar. Les últimes publicacions en el blog + + Veure una publicació + Veure les %s publicacions + Puja una imatge de per la teva entrada + Text alternatiu de la imatge Crèdits de la imatge diff --git a/translation/dest/ublog/cs-CZ.xml b/translation/dest/ublog/cs-CZ.xml index b8088f6cee..6f354d3216 100644 --- a/translation/dest/ublog/cs-CZ.xml +++ b/translation/dest/ublog/cs-CZ.xml @@ -1,5 +1,6 @@ + Blog hráče %s Nový příspěvek Upravit svůj příspěvek na blogu Uložit koncept diff --git a/translation/dest/ublog/ko-KR.xml b/translation/dest/ublog/ko-KR.xml index 99fea0d130..bcb373c436 100644 --- a/translation/dest/ublog/ko-KR.xml +++ b/translation/dest/ublog/ko-KR.xml @@ -1,10 +1,12 @@ + %s의 블로그 새 글 쓰기 제목 본문 임시 보관 글 게시된 글 + 더 많은 %s의 글 이 블로그에는 아직 글이 없습니다. 임시저장된 글이 없습니다. From 731eec89a731598dffe7c83501c4b19806944ed7 Mon Sep 17 00:00:00 2001 From: Scala Steward Date: Fri, 10 Dec 2021 20:24:42 +0100 Subject: [PATCH 138/144] Update sbt to 1.5.6 --- project/build.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/project/build.properties b/project/build.properties index 10fd9eee04..bb3a9b7dc6 100644 --- a/project/build.properties +++ b/project/build.properties @@ -1 +1 @@ -sbt.version=1.5.5 +sbt.version=1.5.6 From 4ee19711c9e397f17a5a4671930292f629b3ef12 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Dec 2021 16:40:37 +0100 Subject: [PATCH 139/144] fix analysis of long games is always partial (fixes #10223) --- ui/analyse/src/ctrl.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ui/analyse/src/ctrl.ts b/ui/analyse/src/ctrl.ts index 7dcb8386c6..fc220cdb85 100644 --- a/ui/analyse/src/ctrl.ts +++ b/ui/analyse/src/ctrl.ts @@ -794,7 +794,8 @@ export default class AnalyseCtrl { this.tree.merge(data.tree); if (!this.showComputer()) this.tree.removeComputerVariations(); this.data.analysis = data.analysis; - if (data.analysis) data.analysis.partial = !!treeOps.findInMainline(data.tree, n => !n.eval && !!n.children.length); + if (data.analysis) + data.analysis.partial = !!treeOps.findInMainline(data.tree, n => !n.eval && !!n.children.length && n.ply <= 200); if (data.division) this.data.game.division = data.division; if (this.retro) this.retro.onMergeAnalysisData(); if (this.study) this.study.serverEval.onMergeAnalysisData(); From f1c3aff3f0773a0fbd703be45abe062111c4f993 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Dec 2021 16:44:16 +0100 Subject: [PATCH 140/144] increase fishnet maxPlies (closes #10199, #3961) --- modules/fishnet/src/main/Analyser.scala | 2 +- ui/analyse/src/ctrl.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/fishnet/src/main/Analyser.scala b/modules/fishnet/src/main/Analyser.scala index 68d5d40212..c9e0f98cc8 100644 --- a/modules/fishnet/src/main/Analyser.scala +++ b/modules/fishnet/src/main/Analyser.scala @@ -18,7 +18,7 @@ final class Analyser( system: akka.actor.ActorSystem ) { - val maxPlies = 200 + val maxPlies = 300 private val workQueue = new lila.hub.AsyncActorSequencer(maxSize = 256, timeout = 5 seconds, "fishnetAnalyser") diff --git a/ui/analyse/src/ctrl.ts b/ui/analyse/src/ctrl.ts index fc220cdb85..89e0d76c72 100644 --- a/ui/analyse/src/ctrl.ts +++ b/ui/analyse/src/ctrl.ts @@ -795,7 +795,7 @@ export default class AnalyseCtrl { if (!this.showComputer()) this.tree.removeComputerVariations(); this.data.analysis = data.analysis; if (data.analysis) - data.analysis.partial = !!treeOps.findInMainline(data.tree, n => !n.eval && !!n.children.length && n.ply <= 200); + data.analysis.partial = !!treeOps.findInMainline(data.tree, n => !n.eval && !!n.children.length && n.ply <= 300); if (data.division) this.data.game.division = data.division; if (this.retro) this.retro.onMergeAnalysisData(); if (this.study) this.study.serverEval.onMergeAnalysisData(); From 8e6463299de181c47b425a4c6e65b297ee8e0b94 Mon Sep 17 00:00:00 2001 From: Niklas Fiekas Date: Sat, 11 Dec 2021 18:31:21 +0100 Subject: [PATCH 141/144] update reactivemongo to 1.0.8 --- project/Dependencies.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 0b46f01838..0e68d44091 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -43,12 +43,12 @@ object Dependencies { } object reactivemongo { - val version = "1.0.7" + val version = "1.0.8" val driver = "org.reactivemongo" %% "reactivemongo" % version val stream = "org.reactivemongo" %% "reactivemongo-akkastream" % version val epoll = "org.reactivemongo" % "reactivemongo-shaded-native" % s"$version-linux-x86-64" - val kamon = "org.reactivemongo" %% "reactivemongo-kamon" % "1.0.7" + val kamon = "org.reactivemongo" %% "reactivemongo-kamon" % "1.0.8" def bundle = Seq(driver, stream) } From 4df390e8177a4e62d74be749b0ed8dc65691e989 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 10 Dec 2021 11:13:33 +0100 Subject: [PATCH 142/144] Open user notes by default --- modules/irc/src/main/IrcApi.scala | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/irc/src/main/IrcApi.scala b/modules/irc/src/main/IrcApi.scala index d84eccebed..ebd55db9ce 100644 --- a/modules/irc/src/main/IrcApi.scala +++ b/modules/irc/src/main/IrcApi.scala @@ -212,7 +212,7 @@ object IrcApi { private object markdown { def link(url: String, name: String) = s"[$name]($url)" def lichessLink(path: String, name: String) = s"[$name](https://lichess.org$path)" - def userLink(name: String): String = lichessLink(s"/@/$name?mod", name) + def userLink(name: String): String = lichessLink(s"/@/$name?mod¬es", name) def userLink(user: User): String = userLink(user.username) def modLink(name: String): String = lichessLink(s"/@/$name", name) def modLink(user: User): String = modLink(user.username) From 02183705359118c9c5532c86174f510e32131220 Mon Sep 17 00:00:00 2001 From: kraktus Date: Fri, 10 Dec 2021 11:16:35 +0100 Subject: [PATCH 143/144] Simplify some `modLink` calls --- modules/irc/src/main/IrcApi.scala | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/modules/irc/src/main/IrcApi.scala b/modules/irc/src/main/IrcApi.scala index ebd55db9ce..4a3a312ce2 100644 --- a/modules/irc/src/main/IrcApi.scala +++ b/modules/irc/src/main/IrcApi.scala @@ -38,7 +38,7 @@ final class IrcApi( ) case Some(note) => zulip.sendAndGetLink(stream, "/" + user.username)( - s"${markdown.modLink(mod.user.username)} :pepenote: **${markdown + s"${markdown.modLink(mod.user)} :pepenote: **${markdown .userLink(user.username)}** (${markdown.userNotesLink(user.username)}):\n" + markdown.linkifyUsers(note.text take 2000) ) @@ -70,7 +70,7 @@ final class IrcApi( def commlog(mod: Holder, user: User, reportBy: Option[User.ID]): Funit = zulip(_.mod.adminLog, "private comms checks")({ val finalS = if (user.username endsWith "s") "" else "s" - s"**${markdown modLink mod.user.username}** checked out **${markdown userLink user.username}**'$finalS communications " + s"**${markdown modLink mod.user}** checked out **${markdown userLink user.username}**'$finalS communications " } + reportBy.filter(mod.id !=).fold("spontaneously") { by => s"while investigating a report created by ${markdown.userLink(by)}" }) From 2a34b6a3f0d6621ef0caa962d09c1148dcae715b Mon Sep 17 00:00:00 2001 From: Maxwell Rosenberg Date: Wed, 8 Dec 2021 21:22:42 -0500 Subject: [PATCH 144/144] hide rating range in lobby pools if showRatings disabled --- ui/lobby/src/view/pools.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/lobby/src/view/pools.ts b/ui/lobby/src/view/pools.ts index a2235e41d9..99d55f63fc 100644 --- a/ui/lobby/src/view/pools.ts +++ b/ui/lobby/src/view/pools.ts @@ -38,7 +38,7 @@ export function render(ctrl: LobbyController) { }, [ h('div.clock', pool.lim + '+' + pool.inc), - active && member!.range ? renderRange(member!.range!) : h('div.perf', pool.perf), + active && member!.range && ctrl.opts.showRatings ? renderRange(member!.range!) : h('div.perf', pool.perf), active ? spinner() : null, ] );