parent
5da1d4575e
commit
14904c0776
|
@ -71,6 +71,7 @@ object racer {
|
|||
s.raceComplete,
|
||||
s.spectating,
|
||||
s.joinTheRace,
|
||||
s.startTheRace,
|
||||
s.yourRankX,
|
||||
s.waitForRematch,
|
||||
s.nextRace,
|
||||
|
|
|
@ -2128,6 +2128,7 @@ val `waitingForMorePlayers` = new I18nKey("storm:waitingForMorePlayers")
|
|||
val `raceComplete` = new I18nKey("storm:raceComplete")
|
||||
val `spectating` = new I18nKey("storm:spectating")
|
||||
val `joinTheRace` = new I18nKey("storm:joinTheRace")
|
||||
val `startTheRace` = new I18nKey("storm:startTheRace")
|
||||
val `yourRankX` = new I18nKey("storm:yourRankX")
|
||||
val `waitForRematch` = new I18nKey("storm:waitForRematch")
|
||||
val `nextRace` = new I18nKey("storm:nextRace")
|
||||
|
|
|
@ -39,7 +39,7 @@ final class RacerApi(colls: RacerColls, selector: StormSelector, userRepo: UserR
|
|||
.make(
|
||||
owner = player,
|
||||
puzzles = puzzles.grouped(2).flatMap(_.headOption).toList,
|
||||
countdownSeconds = 10
|
||||
countdownSeconds = 5
|
||||
)
|
||||
store.put(race.id, race)
|
||||
lila.mon.racer.race(lobby = race.isLobby).increment()
|
||||
|
@ -69,18 +69,23 @@ final class RacerApi(colls: RacerColls, selector: StormSelector, userRepo: UserR
|
|||
|
||||
def join(id: RacerRace.Id, player: RacerPlayer.Id): Option[RacerRace] =
|
||||
get(id).flatMap(_ join player) map { r =>
|
||||
val race = start(r) | r
|
||||
val race = (r.isLobby ?? doStart(r)) | r
|
||||
saveAndPublish(race)
|
||||
race
|
||||
}
|
||||
|
||||
private def start(race: RacerRace): Option[RacerRace] = race.startCountdown.map { starting =>
|
||||
system.scheduler.scheduleOnce(RacerRace.duration.seconds + race.countdownSeconds.seconds + 50.millis) {
|
||||
finish(race.id)
|
||||
}
|
||||
starting
|
||||
private[racer] def manualStart(race: RacerRace): Unit = !race.isLobby ?? {
|
||||
doStart(race) foreach saveAndPublish
|
||||
}
|
||||
|
||||
private def doStart(race: RacerRace): Option[RacerRace] =
|
||||
race.startCountdown.map { starting =>
|
||||
system.scheduler.scheduleOnce(RacerRace.duration.seconds + race.countdownSeconds.seconds + 50.millis) {
|
||||
finish(race.id)
|
||||
}
|
||||
starting
|
||||
}
|
||||
|
||||
private def finish(id: RacerRace.Id): Unit =
|
||||
get(id) foreach { race =>
|
||||
lila.mon.racer.players(lobby = race.isLobby).record(race.players.size)
|
||||
|
|
|
@ -27,7 +27,8 @@ final class RacerJson(stormJson: StormJson, sign: StormSign, lightUserSync: Ligh
|
|||
.add("lobby", race.isLobby),
|
||||
"player" -> player,
|
||||
"puzzles" -> race.puzzles
|
||||
) ++ state(race)
|
||||
)
|
||||
.add("owner", race.owner == player.id) ++ state(race)
|
||||
|
||||
// socket updates
|
||||
def state(race: RacerRace) = Json
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package lila.racer
|
||||
|
||||
|
||||
import lila.room.RoomSocket.{ Protocol => RP, _ }
|
||||
import lila.socket.RemoteSocket.{ Protocol => P, _ }
|
||||
import play.api.libs.json.{ JsObject, Json }
|
||||
|
@ -28,6 +29,12 @@ final private class RacerSocket(
|
|||
api.join(raceId, playerId).unit
|
||||
case Protocol.In.PlayerScore(raceId, playerId, score) =>
|
||||
api.registerPlayerScore(raceId, playerId, score)
|
||||
case Protocol.In.RaceStart(raceId, playerId) =>
|
||||
api
|
||||
.get(raceId)
|
||||
.filter(_.startsAt.isEmpty)
|
||||
.filter(_.owner == playerId)
|
||||
.foreach(api.manualStart)
|
||||
}
|
||||
|
||||
remoteSocketApi.subscribe("racer-in", Protocol.In.reader)(
|
||||
|
@ -45,6 +52,7 @@ object RacerSocket {
|
|||
|
||||
case class PlayerJoin(race: RacerRace.Id, player: RacerPlayer.Id) extends P.In
|
||||
case class PlayerScore(race: RacerRace.Id, player: RacerPlayer.Id, score: Int) extends P.In
|
||||
case class RaceStart(race: RacerRace.Id, player: RacerPlayer.Id) extends P.In
|
||||
|
||||
val reader: P.In.Reader = raw => raceReader(raw) orElse RP.In.reader(raw)
|
||||
|
||||
|
@ -58,6 +66,10 @@ object RacerSocket {
|
|||
raw.get(3) { case Array(raceId, playerId, scoreStr) =>
|
||||
scoreStr.toIntOption map { PlayerScore(RacerRace.Id(raceId), RacerPlayer.Id(playerId), _) }
|
||||
}
|
||||
case "racer/start" =>
|
||||
raw.get(2) { case Array(raceId, playerId) =>
|
||||
RaceStart(RacerRace.Id(raceId), RacerPlayer.Id(playerId)).some
|
||||
}
|
||||
case _ => none
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@
|
|||
<string name="raceComplete">Race complete!</string>
|
||||
<string name="spectating">Spectating</string>
|
||||
<string name="joinTheRace">Join the race!</string>
|
||||
<string name="startTheRace">Start the race</string>
|
||||
<string name="yourRankX">Your rank: %s</string>
|
||||
<string name="waitForRematch">Wait for rematch</string>
|
||||
<string name="nextRace">Next race</string>
|
||||
|
|
|
@ -54,6 +54,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
.puz-side__start {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.puz-clock {
|
||||
flex-flow: row wrap;
|
||||
justify-content: space-between;
|
||||
|
|
|
@ -95,6 +95,8 @@ export default class StormCtrl {
|
|||
|
||||
isRacing = () => this.status() == 'racing';
|
||||
|
||||
isOwner = () => this.data.owner;
|
||||
|
||||
myScore = (): number | undefined => {
|
||||
const p = this.data.players.find(p => p.name == this.data.player.name);
|
||||
return p?.score;
|
||||
|
@ -104,6 +106,10 @@ export default class StormCtrl {
|
|||
if (!this.isPlayer()) this.socketSend('racerJoin');
|
||||
});
|
||||
|
||||
start = throttle(1000, () => {
|
||||
if (this.isOwner()) this.socketSend('racerStart');
|
||||
});
|
||||
|
||||
countdownSeconds = (): number | undefined =>
|
||||
this.status() == 'pre' && this.vm.startsAt && this.vm.startsAt > new Date()
|
||||
? Math.min(9, Math.ceil((this.vm.startsAt.getTime() - Date.now()) / 1000))
|
||||
|
|
|
@ -5,6 +5,8 @@ export type RaceStatus = 'pre' | 'racing' | 'post';
|
|||
|
||||
export type WithGround = <A>(f: (g: CgApi) => A) => A | false;
|
||||
|
||||
export type PlayerId = string;
|
||||
|
||||
export interface RacerOpts {
|
||||
data: RacerData;
|
||||
pref: RacerPrefs;
|
||||
|
@ -22,6 +24,7 @@ export interface RacerData extends UpdatableData {
|
|||
race: Race;
|
||||
puzzles: Puzzle[];
|
||||
player: Player;
|
||||
owner?: boolean;
|
||||
}
|
||||
|
||||
export interface Race {
|
||||
|
|
|
@ -45,7 +45,7 @@ const selectScreen = (ctrl: RacerCtrl): MaybeVNodes => {
|
|||
: [
|
||||
waitingToStart(noarg),
|
||||
h('div.racer__pre__message', [
|
||||
ctrl.raceFull() ? undefined : ctrl.isPlayer() ? renderLink(ctrl) : renderJoin(ctrl),
|
||||
...(ctrl.raceFull() ? [] : ctrl.isPlayer() ? [renderLink(ctrl), renderStart(ctrl)] : [renderJoin(ctrl)]),
|
||||
povMsg,
|
||||
]),
|
||||
comboZone(ctrl),
|
||||
|
@ -150,6 +150,26 @@ const renderLink = (ctrl: RacerCtrl) =>
|
|||
]),
|
||||
]);
|
||||
|
||||
const renderStart = (ctrl: RacerCtrl) =>
|
||||
ctrl.isOwner() && !ctrl.vm.startsAt
|
||||
? h(
|
||||
'div.puz-side__start',
|
||||
h(
|
||||
'button.button.button-fat',
|
||||
{
|
||||
class: {
|
||||
disabled: ctrl.players().length < 2,
|
||||
},
|
||||
hook: bind('click', ctrl.start),
|
||||
attrs: {
|
||||
disabled: ctrl.players().length < 2,
|
||||
},
|
||||
},
|
||||
ctrl.trans.noarg('startTheRace')
|
||||
)
|
||||
)
|
||||
: null;
|
||||
|
||||
const renderJoin = (ctrl: RacerCtrl) =>
|
||||
h(
|
||||
'div.puz-side__join',
|
||||
|
|
Loading…
Reference in New Issue