From 88fe7cdd95a166164cee0cbd6d75e267631b9c96 Mon Sep 17 00:00:00 2001 From: "jakub.salavec" Date: Mon, 14 Apr 2025 16:49:20 +0200 Subject: [PATCH] Added - timers for deck-building stage, registration stage and pairing delay in playing stage --- .../src/main/web/js/gemp-022/hallUi.js | 16 ++++++++++++---- .../lotro/hall/HallCommunicationChannel.java | 8 +++++++- .../gempukku/lotro/hall/HallInfoVisitor.java | 3 ++- .../lotro/tournament/BaseTournament.java | 9 +++++---- .../lotro/tournament/SealedTournament.java | 17 +++++++++++++++++ .../lotro/tournament/SoloDraftTournament.java | 17 +++++++++++++++++ .../tournament/SoloTableDraftTournament.java | 17 +++++++++++++++++ .../lotro/tournament/TableDraftTournament.java | 17 +++++++++++++++++ .../gempukku/lotro/tournament/Tournament.java | 1 + .../lotro/tournament/TournamentService.java | 8 +++++++- 10 files changed, 102 insertions(+), 11 deletions(-) diff --git a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/hallUi.js b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/hallUi.js index bac6cbdd1..4c91bc3b2 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/hallUi.js +++ b/gemp-lotr/gemp-lotr-async/src/main/web/js/gemp-022/hallUi.js @@ -813,9 +813,13 @@ var GempLotrHallUI = Class.extend({ } rowhtml += "" + tournament.getAttribute("name") + "" + - "" + tournament.getAttribute("system") + "" + - "" + tournament.getAttribute("stage") + "" + - "" + tournament.getAttribute("round") + "" + + "" + tournament.getAttribute("system") + ""; + if (tournament.hasAttribute("timeRemaining")) { + rowhtml += "" + tournament.getAttribute("stage") + " - " + tournament.getAttribute("timeRemaining") + ""; + } else { + rowhtml += "" + tournament.getAttribute("stage") + ""; + } + rowhtml += "" + tournament.getAttribute("round") + "" + "
" + tournament.getAttribute("playerCount") + "
" + ""; @@ -843,7 +847,11 @@ var GempLotrHallUI = Class.extend({ var tablesRow = $(""); tablesRow.append("" + tournament.getAttribute("format") + ""); tablesRow.append(" Tournament - " + displayType + " - " + tournament.getAttribute("name") + ""); - tablesRow.append("" + tournament.getAttribute("stage") + ""); + if (tournament.hasAttribute("timeRemaining")) { + tablesRow.append("" + tournament.getAttribute("stage") + " - " + tournament.getAttribute("timeRemaining") + ""); + } else { + tablesRow.append("" + tournament.getAttribute("stage") + ""); + } tablesRow.append("" + tournament.getAttribute("playerList") + ""); var actionsFieldClone = actionsField.clone(true); tablesRow.append(actionsFieldClone); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallCommunicationChannel.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallCommunicationChannel.java index 2a6d7898c..0ab1d1ba8 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallCommunicationChannel.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallCommunicationChannel.java @@ -1,11 +1,13 @@ package com.gempukku.lotro.hall; +import com.gempukku.lotro.common.DateUtils; import com.gempukku.lotro.game.Player; import com.gempukku.polling.LongPollableResource; import com.gempukku.polling.WaitingRequest; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.mutable.MutableObject; +import java.time.Duration; import java.util.*; public class HallCommunicationChannel implements LongPollableResource { @@ -128,7 +130,8 @@ public class HallCommunicationChannel implements LongPollableResource { @Override public void visitTournament(String tournamentKey, String collectionName, String formatName, String tournamentName, String type, String pairingDescription, - String tournamentStage, int round, int playerCount, String playerList, boolean playerInCompetition, boolean abandoned, boolean joinable) { + String tournamentStage, int round, int playerCount, String playerList, boolean playerInCompetition, boolean abandoned, boolean joinable, + long secsRemaining) { Map props = new HashMap<>(); props.put("collection", collectionName); props.put("format", formatName); @@ -142,6 +145,9 @@ public class HallCommunicationChannel implements LongPollableResource { props.put("signedUp", String.valueOf(playerInCompetition)); props.put("abandoned", String.valueOf(abandoned)); props.put("joinable", String.valueOf(joinable)); + if (secsRemaining >= 0) { + props.put("timeRemaining", DateUtils.HumanDuration(Duration.ofSeconds(secsRemaining))); + } tournamentsOnServer.put(tournamentKey, props); } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallInfoVisitor.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallInfoVisitor.java index 6e49cbe33..a7abe541a 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallInfoVisitor.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallInfoVisitor.java @@ -18,7 +18,8 @@ public interface HallInfoVisitor { int readyCheckSecsRemaining, boolean confirmedReadyCheck); public void visitTournament(String tournamentKey, String collectionName, String formatName, String tournamentName, String type, - String pairingDescription, String tournamentStage, int round, int playerCount, String playerList, boolean playerInCompetition, boolean abandoned, boolean joinable); + String pairingDescription, String tournamentStage, int round, int playerCount, String playerList, boolean playerInCompetition, boolean abandoned, boolean joinable, + long secsRemaining); public void runningPlayerGame(String gameId); } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/BaseTournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/BaseTournament.java index 7bbf74025..2ceab395f 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/BaseTournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/BaseTournament.java @@ -7,25 +7,21 @@ import com.gempukku.lotro.common.DateUtils; import com.gempukku.lotro.competitive.ModifiedMedianStandingsProducer; import com.gempukku.lotro.competitive.PlayerStanding; import com.gempukku.lotro.db.vo.CollectionType; -import com.gempukku.lotro.draft.Draft; import com.gempukku.lotro.draft2.SoloDraftDefinitions; import com.gempukku.lotro.draft3.TableDraftDefinitions; import com.gempukku.lotro.game.CardCollection; import com.gempukku.lotro.game.CardNotFoundException; import com.gempukku.lotro.game.formats.LotroFormatLibrary; import com.gempukku.lotro.hall.TableHolder; -import com.gempukku.lotro.logic.actions.AbstractCostToEffectAction; import com.gempukku.lotro.logic.vo.LotroDeck; import com.gempukku.lotro.packs.ProductLibrary; import com.gempukku.lotro.tournament.action.BroadcastAction; import com.gempukku.lotro.tournament.action.CreateGameAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.time.DurationFormatUtils; import org.apache.commons.text.StringEscapeUtils; import java.time.Duration; -import java.time.ZonedDateTime; import java.util.*; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReadWriteLock; @@ -519,4 +515,9 @@ public abstract class BaseTournament implements Tournament { } return _tournamentReport; } + + @Override + public long getSecondsRemaining() throws IllegalStateException{ + throw new IllegalStateException(); + } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SealedTournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SealedTournament.java index 29f827c12..8f318ef20 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SealedTournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SealedTournament.java @@ -16,11 +16,14 @@ import com.gempukku.lotro.tournament.action.BroadcastAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction; import org.apache.commons.lang3.StringUtils; +import java.time.Duration; +import java.time.ZonedDateTime; import java.util.*; public class SealedTournament extends BaseTournament implements Tournament { private SealedTournamentInfo _sealedInfo; + private ZonedDateTime nextRoundStart = null; public SealedTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, TableHolder tables, String tournamentId) { @@ -201,6 +204,7 @@ public class SealedTournament extends BaseTournament implements Tournament { if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) { result.add(finishTournament(collectionsManager)); } else { + nextRoundStart = DateUtils.Now().plus(PairingDelayTime); if(getCurrentRound() == 0) { result.add(new BroadcastAction("Deck registration for tournament " + getTournamentName() + " has closed. Round " @@ -247,4 +251,17 @@ public class SealedTournament extends BaseTournament implements Tournament { getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF) && (maximumPlayers > activePlayers.size() || maximumPlayers < 0); } + + @Override + public long getSecondsRemaining() throws IllegalStateException { + if (getTournamentStage() == Stage.DECK_BUILDING) { + return Duration.between(DateUtils.Now(), _sealedInfo.DeckbuildingDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.DECK_REGISTRATION) { + return Duration.between(DateUtils.Now(), _sealedInfo.RegistrationDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.PLAYING_GAMES && nextRoundStart != null && DateUtils.Now().isBefore(nextRoundStart)) { + return Duration.between(DateUtils.Now(), nextRoundStart).getSeconds(); + } else { + throw new IllegalStateException(); + } + } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloDraftTournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloDraftTournament.java index 438e70a77..f4b567aef 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloDraftTournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloDraftTournament.java @@ -18,12 +18,15 @@ import com.gempukku.lotro.tournament.action.BroadcastAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction; import org.apache.commons.lang3.StringUtils; +import java.time.Duration; +import java.time.ZonedDateTime; import java.util.*; public class SoloDraftTournament extends BaseTournament implements Tournament { private static final int HIGH_ENOUGH_PRIME_NUMBER = 8963; private SoloDraftTournamentInfo _soloDraftInfo; + private ZonedDateTime nextRoundStart = null; public SoloDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, TableHolder tables, String tournamentId) { @@ -196,6 +199,7 @@ public class SoloDraftTournament extends BaseTournament implements Tournament { if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) { result.add(finishTournament(collectionsManager)); } else { + nextRoundStart = DateUtils.Now().plus(PairingDelayTime); if(getCurrentRound() == 0) { result.add(new BroadcastAction("Deck registration for tournament " + getTournamentName() + " has closed. Round " @@ -262,4 +266,17 @@ public class SoloDraftTournament extends BaseTournament implements Tournament { getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF) && (maximumPlayers > activePlayers.size() || maximumPlayers < 0); } + + @Override + public long getSecondsRemaining() throws IllegalStateException { + if (getTournamentStage() == Stage.DECK_BUILDING) { + return Duration.between(DateUtils.Now(), _soloDraftInfo.DeckbuildingDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.DECK_REGISTRATION) { + return Duration.between(DateUtils.Now(), _soloDraftInfo.RegistrationDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.PLAYING_GAMES && nextRoundStart != null && DateUtils.Now().isBefore(nextRoundStart)) { + return Duration.between(DateUtils.Now(), nextRoundStart).getSeconds(); + } else { + throw new IllegalStateException(); + } + } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloTableDraftTournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloTableDraftTournament.java index 01a77415e..bc1498fbd 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloTableDraftTournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/SoloTableDraftTournament.java @@ -16,12 +16,15 @@ import com.gempukku.lotro.tournament.action.BroadcastAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction; import org.apache.commons.lang3.StringUtils; +import java.time.Duration; +import java.time.ZonedDateTime; import java.util.*; public class SoloTableDraftTournament extends BaseTournament implements Tournament { private SoloTableDraftTournamentInfo soloTableDraftInfo; private Map tables = null; + private ZonedDateTime nextRoundStart = null; public SoloTableDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, @@ -176,6 +179,7 @@ public class SoloTableDraftTournament extends BaseTournament implements Tourname if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) { result.add(finishTournament(collectionsManager)); } else { + nextRoundStart = DateUtils.Now().plus(PairingDelayTime); if(getCurrentRound() == 0) { result.add(new BroadcastAction("Deck registration for tournament " + getTournamentName() + " has closed. Round " @@ -245,4 +249,17 @@ public class SoloTableDraftTournament extends BaseTournament implements Tourname getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF) && (maximumPlayers > activePlayers.size() || maximumPlayers < 0); } + + @Override + public long getSecondsRemaining() throws IllegalStateException { + if (getTournamentStage() == Stage.DECK_BUILDING) { + return Duration.between(DateUtils.Now(), soloTableDraftInfo.deckbuildingDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.DECK_REGISTRATION) { + return Duration.between(DateUtils.Now(), soloTableDraftInfo.registrationDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.PLAYING_GAMES && nextRoundStart != null && DateUtils.Now().isBefore(nextRoundStart)) { + return Duration.between(DateUtils.Now(), nextRoundStart).getSeconds(); + } else { + throw new IllegalStateException(); + } + } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TableDraftTournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TableDraftTournament.java index 281eea745..d90935837 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TableDraftTournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TableDraftTournament.java @@ -18,6 +18,8 @@ import com.gempukku.lotro.tournament.action.BroadcastAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction; import org.apache.commons.lang3.StringUtils; +import java.time.Duration; +import java.time.ZonedDateTime; import java.util.*; public class TableDraftTournament extends BaseTournament implements Tournament { @@ -25,6 +27,7 @@ public class TableDraftTournament extends BaseTournament implements Tournament { private TableDraftTournamentInfo tableDraftInfo; private TableDraft table = null; private final ChatServer chatServer; + private ZonedDateTime nextRoundStart = null; public TableDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, @@ -201,6 +204,7 @@ public class TableDraftTournament extends BaseTournament implements Tournament { if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) { result.add(finishTournament(collectionsManager)); } else { + nextRoundStart = DateUtils.Now().plus(PairingDelayTime); if(getCurrentRound() == 0) { result.add(new BroadcastAction("Deck registration for tournament " + getTournamentName() + " has closed. Round " @@ -262,4 +266,17 @@ public class TableDraftTournament extends BaseTournament implements Tournament { public boolean isJoinable() { return false; // cannot join draft in progress } + + @Override + public long getSecondsRemaining() throws IllegalStateException { + if (getTournamentStage() == Stage.DECK_BUILDING) { + return Duration.between(DateUtils.Now(), tableDraftInfo.deckbuildingDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.DECK_REGISTRATION) { + return Duration.between(DateUtils.Now(), tableDraftInfo.registrationDeadline).getSeconds(); + } else if (getTournamentStage() == Stage.PLAYING_GAMES && nextRoundStart != null && DateUtils.Now().isBefore(nextRoundStart)) { + return Duration.between(DateUtils.Now(), nextRoundStart).getSeconds(); + } else { + throw new IllegalStateException(); + } + } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java index 3ed4b703f..a4a559a95 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/Tournament.java @@ -206,4 +206,5 @@ public interface Tournament { TournamentInfo getInfo(); boolean isJoinable(); + long getSecondsRemaining() throws IllegalStateException; } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TournamentService.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TournamentService.java index 732f12b46..8cb288799 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TournamentService.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/TournamentService.java @@ -264,11 +264,17 @@ public class TournamentService { for (var entry : _activeTournaments.entrySet()) { var tourneyID = entry.getKey(); var tournament = entry.getValue(); + long secsRemaining = -1; + try { + secsRemaining = tournament.getSecondsRemaining(); + } catch (IllegalStateException ignore) { + + } visitor.visitTournament(tourneyID, tournament.getCollectionType().getFullName(), formatLibrary.getFormat(tournament.getFormatCode()).getName(), tournament.getTournamentName(), tournament.getInfo().Parameters().type.toString(), tournament.getPlayOffSystem(), tournament.getTournamentStage().getHumanReadable(), tournament.getCurrentRound(), tournament.getPlayersInCompetitionCount(), tournament.getPlayerList(), tournament.isPlayerInCompetition(player.getName()), tournament.isPlayerAbandoned(player.getName()), - tournament.isJoinable()); + tournament.isJoinable(), secsRemaining); } }