Merge branch 'master' into yarn
This commit is contained in:
commit
f8eea774eb
|
@ -49,6 +49,10 @@ jobs:
|
|||
- stage: test
|
||||
language: node_js
|
||||
node_js: "4"
|
||||
addons:
|
||||
apt:
|
||||
packages:
|
||||
- parallel
|
||||
before_install: yarn global add gulp-cli
|
||||
script: ./ui/build prod
|
||||
after_success:
|
||||
|
|
|
@ -34,7 +34,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
|
|||
val speedAndClock =
|
||||
if (game.imported) "imported"
|
||||
else game.clock.fold(chess.Speed.Correspondence.name) { c =>
|
||||
s"${chess.Speed(c.config).name} (${c.show})"
|
||||
s"${chess.Speed(c.config).name} (${c.config.show})"
|
||||
}
|
||||
val mode = game.mode.name
|
||||
val variant = if (game.variant == chess.variant.FromPosition) "position setup chess"
|
||||
|
@ -174,7 +174,7 @@ trait GameHelper { self: I18nHelper with UserHelper with AiHelper with StringHel
|
|||
private def gameTitle(game: Game, color: Color): String = {
|
||||
val u1 = playerText(game player color, withRating = true)
|
||||
val u2 = playerText(game opponent color, withRating = true)
|
||||
val clock = game.clock ?? { c => " • " + c.show }
|
||||
val clock = game.clock ?? { c => " • " + c.config.show }
|
||||
val variant = game.variant.exotic ?? s" • ${game.variant.name}"
|
||||
s"$u1 vs $u2$clock$variant"
|
||||
}
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
@game.variant.name.toUpperCase
|
||||
}
|
||||
} else {
|
||||
@game.clock.map(_.show).getOrElse {
|
||||
@game.clock.map(_.config.show).getOrElse {
|
||||
@game.daysPerTurn.map { days =>
|
||||
<span data-hint="@trans.correspondence()" class="hint--top">@{(days == 1).fold(trans.oneDay(), trans.nbDays(days))}</span>
|
||||
}.getOrElse {
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
@g.variant.name.toUpperCase
|
||||
}
|
||||
} else {
|
||||
@g.clock.map(_.show).getOrElse {
|
||||
@g.clock.map(_.config.show).getOrElse {
|
||||
@g.daysPerTurn.map { days =>
|
||||
<span data-hint="@trans.correspondence()" class="hint--top">@if(days == 1) {@trans.oneDay()} else {@trans.nbDays(days)}</span>
|
||||
}.getOrElse {
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
@setup.select(form("timeMode"), translatedTimeModeChoices)
|
||||
</div>
|
||||
<div class="time_choice slider">
|
||||
@trans.minutesPerSide(): <span>@(chess.Clock.showLimit(~form("time").value.map(x => (x.toDouble * 60).toInt)))</span>
|
||||
@trans.minutesPerSide(): <span>@(chess.Clock.Config(~form("time").value.map(x => (x.toDouble * 60).toInt), 0).limitString)</span>
|
||||
@setup.input(form("time"))
|
||||
</div>
|
||||
<div class="increment_choice slider">
|
||||
|
|
|
@ -31,7 +31,7 @@
|
|||
<div class="game_legend">
|
||||
@playerText(pov.opponent, withRating = true)
|
||||
@pov.game.clock.map { c =>
|
||||
• @c.show
|
||||
• @c.config.show
|
||||
}
|
||||
</div>
|
||||
}
|
||||
|
|
|
@ -1 +1 @@
|
|||
Subproject commit 5ab69132aaa4203f6280a896a03ec187e566c753
|
||||
Subproject commit 03014d9b3a2d4d861492c3dbe95f72bf71c156fc
|
|
@ -79,60 +79,58 @@ object BinaryFormat {
|
|||
}.take(turns).map(Centis.apply)(breakOut)
|
||||
}
|
||||
|
||||
case class clock(since: DateTime) {
|
||||
|
||||
case class clock(start: Timestamp) {
|
||||
def write(clock: Clock): ByteArray = {
|
||||
Array(writeClockLimit(clock.limitSeconds), clock.incrementSeconds.toByte) ++
|
||||
writeSignedInt24(clock.whiteTime.centis) ++
|
||||
writeSignedInt24(clock.blackTime.centis) ++
|
||||
writeTimer(clock.timerOption.fold(0l)(_.value / 10l))
|
||||
clock.timerOption.fold(Array.empty[Byte])(writeTimer)
|
||||
}
|
||||
|
||||
def read(ba: ByteArray, whiteBerserk: Boolean, blackBerserk: Boolean): Color => Clock = color => ba.value map toInt match {
|
||||
case Array(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12) =>
|
||||
readTimer(b9, b10, b11, b12) match {
|
||||
case 0 => PausedClock(
|
||||
config = Clock.Config(readClockLimit(b1), b2),
|
||||
color = color,
|
||||
whiteTime = Centis(readSignedInt24(b3, b4, b5)),
|
||||
blackTime = Centis(readSignedInt24(b6, b7, b8)),
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk
|
||||
)
|
||||
case timer => RunningClock(
|
||||
config = Clock.Config(readClockLimit(b1), b2),
|
||||
color = color,
|
||||
whiteTime = Centis(readSignedInt24(b3, b4, b5)),
|
||||
blackTime = Centis(readSignedInt24(b6, b7, b8)),
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk,
|
||||
timer = Timestamp(timer * 10l)
|
||||
)
|
||||
def read(ba: ByteArray, whiteBerserk: Boolean, blackBerserk: Boolean): Color => Clock = color => {
|
||||
val ia = ba.value map toInt
|
||||
|
||||
// ba.size might be greater than 12 with 5 bytes timers
|
||||
// ba.size might be 8 if there was no timer.
|
||||
// #TODO remove 5 byte timer case! But fix the DB first!
|
||||
val timer = {
|
||||
if (ia.size == 12) readTimer(readInt(ia(8), ia(9), ia(10), ia(11)))
|
||||
else None
|
||||
}
|
||||
|
||||
ia match {
|
||||
case Array(b1, b2, b3, b4, b5, b6, b7, b8, _*) => {
|
||||
val config = Clock.Config(readClockLimit(b1), b2)
|
||||
val whiteTime = Centis(readSignedInt24(b3, b4, b5))
|
||||
val blackTime = Centis(readSignedInt24(b6, b7, b8))
|
||||
timer.fold[Clock](
|
||||
PausedClock(
|
||||
config = config,
|
||||
color = color,
|
||||
whiteTime = whiteTime,
|
||||
blackTime = blackTime,
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk
|
||||
)
|
||||
)(t =>
|
||||
RunningClock(
|
||||
config = config,
|
||||
color = color,
|
||||
whiteTime = whiteTime,
|
||||
blackTime = blackTime,
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk,
|
||||
timer = t
|
||||
))
|
||||
}
|
||||
// compatibility with 5 bytes timers
|
||||
// #TODO remove me! But fix the DB first!
|
||||
case Array(b1, b2, b3, b4, b5, b6, b7, b8, b9, b10, b11, b12, _) =>
|
||||
PausedClock(
|
||||
config = Clock.Config(readClockLimit(b1), b2),
|
||||
color = color,
|
||||
whiteTime = Centis(readSignedInt24(b3, b4, b5)),
|
||||
blackTime = Centis(readSignedInt24(b6, b7, b8)),
|
||||
whiteBerserk = whiteBerserk,
|
||||
blackBerserk = blackBerserk
|
||||
)
|
||||
case _ => sys error s"BinaryFormat.clock.read invalid bytes: ${ba.showBytes}"
|
||||
case _ => sys error s"BinaryFormat.clock.read invalid bytes: ${ba.showBytes}"
|
||||
}
|
||||
}
|
||||
|
||||
private def decay = (since.getMillis / 10) - 10
|
||||
private def writeTimer(timer: Timestamp) = writeInt((timer - start).centis)
|
||||
|
||||
private def writeTimer(long: Long) = {
|
||||
writeInt(math.max(0l, long - decay).toInt)
|
||||
}
|
||||
|
||||
private def readTimer(b1: Int, b2: Int, b3: Int, b4: Int) = {
|
||||
val l = readInt(b1, b2, b3, b4)
|
||||
if (l == 0) 0 else l + decay
|
||||
}
|
||||
private def readTimer(l: Int) =
|
||||
if (l != 0) Some(start + Centis(l)) else None
|
||||
|
||||
private def writeClockLimit(limit: Int): Byte = {
|
||||
// The database expects a byte for a limit, and this is limit / 60.
|
||||
|
@ -149,6 +147,10 @@ object BinaryFormat {
|
|||
}
|
||||
}
|
||||
|
||||
object clock {
|
||||
def apply(start: DateTime) = new clock(Timestamp(start.getMillis))
|
||||
}
|
||||
|
||||
object castleLastMoveTime {
|
||||
|
||||
def write(clmt: CastleLastMoveTime): ByteArray = {
|
||||
|
|
|
@ -220,7 +220,7 @@ case class Game(
|
|||
binaryMoveTimes.?? { t =>
|
||||
BinaryFormat.moveTime.read(t, playedTurns)
|
||||
} :+ {
|
||||
(Centis(nowCentis - movedAt.getCentis) - ~lag) nonNeg
|
||||
Centis(nowCentis - movedAt.getCentis) nonNeg
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -365,7 +365,7 @@ case class Game(
|
|||
|
||||
def goBerserk(color: Color) =
|
||||
clock.ifTrue(berserkable && !player(color).berserk).map { c =>
|
||||
val newClock = c berserk color
|
||||
val newClock = c goBerserk color
|
||||
Progress(this, copy(
|
||||
clock = Some(newClock),
|
||||
clockHistory = clockHistory.map(history => {
|
||||
|
@ -456,7 +456,7 @@ case class Game(
|
|||
}
|
||||
|
||||
private def outoftimeCorrespondence: Boolean =
|
||||
playableCorrespondenceClock ?? { _ outoftime player.color }
|
||||
playableCorrespondenceClock ?? { _ outoftime turnColor }
|
||||
|
||||
def isCorrespondence = speed == chess.Speed.Correspondence
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.game
|
|||
|
||||
import scala.concurrent.duration._
|
||||
|
||||
import chess.{ Centis, Clock }
|
||||
import chess.{ Centis, Clock, White, Black }
|
||||
import org.specs2.mutable._
|
||||
import org.specs2.specification._
|
||||
|
||||
|
@ -12,57 +12,82 @@ class BinaryClockTest extends Specification {
|
|||
|
||||
val _0_ = "00000000"
|
||||
val since = org.joda.time.DateTime.now.minusHours(1)
|
||||
def write(c: Clock): List[String] =
|
||||
(BinaryFormat.clock(since) write c).showBytes.split(',').toList
|
||||
def read(bytes: List[String]): Clock =
|
||||
(BinaryFormat.clock(since).read(ByteArray.parseBytes(bytes), false, false))(chess.White)
|
||||
def isomorphism(c: Clock): Clock =
|
||||
(BinaryFormat.clock(since).read(BinaryFormat.clock(since) write c, false, false))(chess.White)
|
||||
def writeBytes(c: Clock) = BinaryFormat.clock(since) write c
|
||||
def readBytes(bytes: ByteArray, berserk: Boolean = false): Clock =
|
||||
(BinaryFormat.clock(since).read(bytes, berserk, false))(White)
|
||||
def isomorphism(c: Clock): Clock = readBytes(writeBytes(c))
|
||||
|
||||
def write(c: Clock): List[String] = writeBytes(c).showBytes.split(',').toList
|
||||
def read(bytes: List[String]) = readBytes(ByteArray.parseBytes(bytes))
|
||||
|
||||
"binary Clock" should {
|
||||
val clock = Clock(120, 2)
|
||||
val bits22 = List("00000010", "00000010")
|
||||
"write" in {
|
||||
write(clock) must_== {
|
||||
bits22 ::: List.fill(10)(_0_)
|
||||
bits22 ::: List.fill(6)(_0_)
|
||||
}
|
||||
write(clock.giveTime(chess.White, Centis(3))) must_== {
|
||||
bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(7)(_0_)
|
||||
write(clock.giveTime(White, Centis(3))) must_== {
|
||||
bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(3)(_0_)
|
||||
}
|
||||
write(clock.giveTime(chess.White, Centis(-3))) must_== {
|
||||
bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(7)(_0_)
|
||||
write(clock.giveTime(White, Centis(-3))) must_== {
|
||||
bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(3)(_0_)
|
||||
}
|
||||
write(Clock(0, 3)) must_== {
|
||||
List("00000000", "00000011", "10000000", "00000001", "00101100", "10000000", "00000001", "00101100") ::: List.fill(4)(_0_)
|
||||
List("00000000", "00000011", "10000000", "00000001", "00101100", "10000000", "00000001", "00101100")
|
||||
}
|
||||
}
|
||||
"read" in {
|
||||
read(bits22 ::: List.fill(11)(_0_)) must_== {
|
||||
clock
|
||||
"with timer" in {
|
||||
read(bits22 ::: List.fill(11)(_0_)) must_== {
|
||||
clock
|
||||
}
|
||||
read(bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(3))
|
||||
}
|
||||
read(bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(-3))
|
||||
}
|
||||
}
|
||||
read(bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(chess.White, Centis(3))
|
||||
}
|
||||
read(bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(8)(_0_)) must_== {
|
||||
clock.giveTime(chess.White, Centis(-3))
|
||||
"without timer bytes" in {
|
||||
read(bits22 ::: List.fill(7)(_0_)) must_== {
|
||||
clock
|
||||
}
|
||||
read(bits22 ::: List("10000000", "00000000", "00000011") ::: List.fill(4)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(3))
|
||||
}
|
||||
read(bits22 ::: List("00000000", "00000000", "00000011") ::: List.fill(4)(_0_)) must_== {
|
||||
clock.giveTime(White, Centis(-3))
|
||||
}
|
||||
}
|
||||
}
|
||||
"isomorphism" in {
|
||||
|
||||
isomorphism(clock) must_== clock
|
||||
|
||||
val c2 = clock.giveTime(chess.White, Centis.ofSeconds(15))
|
||||
val c2 = clock.giveTime(White, Centis.ofSeconds(15))
|
||||
isomorphism(c2) must_== c2
|
||||
|
||||
val c3 = clock.giveTime(chess.Black, Centis.ofSeconds(5))
|
||||
isomorphism(c3) must_== c3
|
||||
|
||||
val c4 = clock.start
|
||||
isomorphism(c4).timerOption.get.value must beCloseTo(c4.timerOption.get.value, 10)
|
||||
isomorphism(c4).timerOption.get.value must beCloseTo(c4.timer.value, 10)
|
||||
|
||||
Clock(120, 60) |> { c =>
|
||||
isomorphism(c) must_== c
|
||||
}
|
||||
|
||||
"with berserk" in {
|
||||
val b1 = clock.goBerserk(White)
|
||||
readBytes(writeBytes(b1), true) must_== b1
|
||||
|
||||
val b2 = clock.giveTime(White, Centis(15)).goBerserk(White)
|
||||
readBytes(writeBytes(b2), true) must_== b2
|
||||
|
||||
val b3 = Clock(60, 2).goBerserk(White)
|
||||
readBytes(writeBytes(b3), true) must_== b3
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -31,7 +31,7 @@ private final class GameJson(
|
|||
tree = TreeBuilder(game, plies)
|
||||
} yield Json.obj(
|
||||
"id" -> game.id,
|
||||
"clock" -> game.clock.map(_.show),
|
||||
"clock" -> game.clock.map(_.config.show),
|
||||
"perf" -> Json.obj(
|
||||
"icon" -> perfType.iconChar.toString,
|
||||
"name" -> perfType.name
|
||||
|
|
|
@ -355,7 +355,7 @@ object JsonView {
|
|||
"increment" -> c.incrementSeconds,
|
||||
"white" -> c.remainingTime(Color.White).toSeconds,
|
||||
"black" -> c.remainingTime(Color.Black).toSeconds,
|
||||
"emerg" -> c.emergTime
|
||||
"emerg" -> c.config.emergSeconds
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ package lila.round
|
|||
|
||||
import chess.format.Forsyth
|
||||
import chess.variant._
|
||||
import chess.{ Game => ChessGame, Board, Color => ChessColor, Castles }
|
||||
import chess.{ Clock, Game => ChessGame, Board, Color => ChessColor, Castles }
|
||||
import ChessColor.{ White, Black }
|
||||
|
||||
import lila.game.{ GameRepo, Game, Event, Progress, Pov, Source, AnonCookie, PerfPicker }
|
||||
|
@ -77,7 +77,7 @@ private[round] final class Rematcher(
|
|||
board = Board(pieces, variant = pov.game.variant).withCastles {
|
||||
situation.fold(Castles.init)(_.situation.board.history.castles)
|
||||
},
|
||||
clock = pov.game.clock map (_.reset),
|
||||
clock = pov.game.clock map { c => Clock(c.config) },
|
||||
turns = situation ?? (_.turns),
|
||||
startedAtTurn = situation ?? (_.turns)
|
||||
),
|
||||
|
|
|
@ -45,7 +45,7 @@ object DataForm {
|
|||
val clockTimesPrivate: Seq[Double] = clockTimes ++ (10d to 30d by 5d) ++ (40d to 60d by 10d)
|
||||
val clockTimeDefault = 2d
|
||||
private def formatLimit(l: Double) =
|
||||
chess.Clock.showLimit(l * 60 toInt) + {
|
||||
chess.Clock.Config(l * 60 toInt, 0).limitString + {
|
||||
if (l <= 1) " minute" else " minutes"
|
||||
}
|
||||
val clockTimeChoices = optionsDouble(clockTimes, formatLimit)
|
||||
|
|
30
ui/build
30
ui/build
|
@ -1,16 +1,15 @@
|
|||
#!/bin/sh -e
|
||||
. bin/lilarc
|
||||
|
||||
#!/bin/bash -ea
|
||||
target=${1-dev}
|
||||
|
||||
mkdir -p public/compiled
|
||||
|
||||
ts_apps="common chess ceval game tree"
|
||||
ts_apps1="common chess"
|
||||
ts_apps2="ceval game tree"
|
||||
apps="site chat2 challenge2 notify2 learn insight editor puzzle round2 analyse lobby tournament tournamentSchedule simul perfStat dasher"
|
||||
|
||||
prll_sh=${PRLLSH-/etc/profile.d/prll.sh}
|
||||
|
||||
build_ts() {
|
||||
echo "build_ts" "$@"
|
||||
set -ex
|
||||
cd ui/$1
|
||||
rm -rf node_modules/types
|
||||
rm -rf node_modules/common
|
||||
|
@ -21,8 +20,9 @@ build_ts() {
|
|||
}
|
||||
|
||||
build() {
|
||||
echo "build" "$@"
|
||||
set -ex
|
||||
app=$1
|
||||
echo "Building $app"
|
||||
cd ui/$app
|
||||
rm -rf node_modules/types
|
||||
rm -rf node_modules/common
|
||||
|
@ -35,13 +35,15 @@ build() {
|
|||
cd -
|
||||
}
|
||||
|
||||
if [ -f $prll_sh ]; then # parallel execution!
|
||||
export PRLL_NRJOBS="${PRLL_NRJOBS-10}"
|
||||
echo "Building up to $PRLL_NRJOBS in parallel!"
|
||||
. $prll_sh
|
||||
prll build_ts $ts_apps
|
||||
prll build $apps
|
||||
if command -v parallel >/dev/null; then # parallel execution!
|
||||
if [ -z "$P_OPTS" -a ! -e ~/.parallel/config ]; then
|
||||
P_OPTS="-j+4"
|
||||
[ "$TRAVIS" = "true" ] || P_OPTS+=" --bar"
|
||||
fi
|
||||
parallel --gnu $P_OPTS build_ts ::: $ts_apps1
|
||||
parallel --gnu $P_OPTS build_ts ::: $ts_apps2
|
||||
parallel --gnu $P_OPTS build ::: $apps
|
||||
else # sequential execution
|
||||
for app in $ts_apps; do build_ts $app; done
|
||||
for app in $ts_apps1 $ts_apps2; do build_ts $app; done
|
||||
for app in $apps; do build $app; done
|
||||
fi
|
||||
|
|
|
@ -126,8 +126,14 @@ function renderLine(ctrl: Ctrl, line: Line) {
|
|||
]);
|
||||
var userNode = thunk('a', line.u, userLink, [line.u]);
|
||||
|
||||
return h('li', ctrl.moderation ? [
|
||||
lineAction(() => ctrl.moderation && line.u && ctrl.moderation.open(line.u.split(' ')[0])),
|
||||
return h('li', {
|
||||
hook: ctrl.moderation ? bind('click', (e: Event) => {
|
||||
const target = e.target as HTMLElement;
|
||||
if (ctrl.moderation && target.classList.contains('mod'))
|
||||
ctrl.moderation.open((target.getAttribute('data-username') as string).split(' ')[0]);
|
||||
}) : {}
|
||||
}, ctrl.moderation ? [
|
||||
line.u ? lineAction(line.u) : null,
|
||||
userNode,
|
||||
textNode
|
||||
] : [userNode, textNode]);
|
||||
|
|
|
@ -52,11 +52,11 @@ export function moderationCtrl(opts: ModerationOpts): ModerationCtrl {
|
|||
};
|
||||
}
|
||||
|
||||
export function lineAction(onClick: (e: Event) => void) {
|
||||
export function lineAction(username: string) {
|
||||
return h('i.mod', {
|
||||
hook: bind('click', onClick),
|
||||
attrs: {
|
||||
'data-icon': '',
|
||||
'data-username': username,
|
||||
title: 'Moderation'
|
||||
}
|
||||
});
|
||||
|
@ -65,10 +65,12 @@ export function lineAction(onClick: (e: Event) => void) {
|
|||
export function moderationView(ctrl?: ModerationCtrl): VNode[] | undefined {
|
||||
if (!ctrl) return;
|
||||
if (ctrl.loading()) return [h('div.loading', spinner())];
|
||||
var data = ctrl.data();
|
||||
const data = ctrl.data();
|
||||
if (!data) return;
|
||||
return [
|
||||
h('div.top', [
|
||||
h('div.top', {
|
||||
key: 'mod-' + data.id,
|
||||
}, [
|
||||
h('span.text', {
|
||||
attrs: {'data-icon': '' },
|
||||
}, [userLink(data.username)]),
|
||||
|
|
|
@ -3,9 +3,7 @@ var makeAckable = require('./ackable');
|
|||
// versioned events, acks, retries, resync
|
||||
lichess.StrongSocket = function(url, version, settings) {
|
||||
|
||||
var now = function() {
|
||||
return Date.now();
|
||||
};
|
||||
var now = Date.now;
|
||||
|
||||
var settings = $.extend(true, {}, lichess.StrongSocket.defaults, settings);
|
||||
var url = url;
|
||||
|
|
Loading…
Reference in a new issue