Added - timers for deck-building stage, registration stage and pairing delay in playing stage

This commit is contained in:
jakub.salavec
2025-04-14 16:49:20 +02:00
parent 5dbd81dfc3
commit 88fe7cdd95
10 changed files with 102 additions and 11 deletions

View File

@@ -813,9 +813,13 @@ var GempLotrHallUI = Class.extend({
} }
rowhtml += "<td>" + tournament.getAttribute("name") + "</td>" + rowhtml += "<td>" + tournament.getAttribute("name") + "</td>" +
"<td>" + tournament.getAttribute("system") + "</td>" + "<td>" + tournament.getAttribute("system") + "</td>";
"<td>" + tournament.getAttribute("stage") + "</td>" + if (tournament.hasAttribute("timeRemaining")) {
"<td>" + tournament.getAttribute("round") + "</td>" + rowhtml += "<td>" + tournament.getAttribute("stage") + " - " + tournament.getAttribute("timeRemaining") + "</td>";
} else {
rowhtml += "<td>" + tournament.getAttribute("stage") + "</td>";
}
rowhtml += "<td>" + tournament.getAttribute("round") + "</td>" +
"<td><div class='prizeHint' title='Competing Players' value='" + tournament.getAttribute("playerList") + "<br><br>* = abandoned'>" + tournament.getAttribute("playerCount") + "</div></td>" + "<td><div class='prizeHint' title='Competing Players' value='" + tournament.getAttribute("playerList") + "<br><br>* = abandoned'>" + tournament.getAttribute("playerCount") + "</div></td>" +
"</tr>"; "</tr>";
@@ -843,7 +847,11 @@ var GempLotrHallUI = Class.extend({
var tablesRow = $("<tr class='table" + id + "'></tr>"); var tablesRow = $("<tr class='table" + id + "'></tr>");
tablesRow.append("<td>" + tournament.getAttribute("format") + "</td>"); tablesRow.append("<td>" + tournament.getAttribute("format") + "</td>");
tablesRow.append("<td> Tournament - " + displayType + " - " + tournament.getAttribute("name") + "</td>"); tablesRow.append("<td> Tournament - " + displayType + " - " + tournament.getAttribute("name") + "</td>");
if (tournament.hasAttribute("timeRemaining")) {
tablesRow.append("<td>" + tournament.getAttribute("stage") + " - " + tournament.getAttribute("timeRemaining") + "</td>");
} else {
tablesRow.append("<td>" + tournament.getAttribute("stage") + "</td>"); tablesRow.append("<td>" + tournament.getAttribute("stage") + "</td>");
}
tablesRow.append("<td>" + tournament.getAttribute("playerList") + "</td>"); tablesRow.append("<td>" + tournament.getAttribute("playerList") + "</td>");
var actionsFieldClone = actionsField.clone(true); var actionsFieldClone = actionsField.clone(true);
tablesRow.append(actionsFieldClone); tablesRow.append(actionsFieldClone);

View File

@@ -1,11 +1,13 @@
package com.gempukku.lotro.hall; package com.gempukku.lotro.hall;
import com.gempukku.lotro.common.DateUtils;
import com.gempukku.lotro.game.Player; import com.gempukku.lotro.game.Player;
import com.gempukku.polling.LongPollableResource; import com.gempukku.polling.LongPollableResource;
import com.gempukku.polling.WaitingRequest; import com.gempukku.polling.WaitingRequest;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.mutable.MutableObject; import org.apache.commons.lang3.mutable.MutableObject;
import java.time.Duration;
import java.util.*; import java.util.*;
public class HallCommunicationChannel implements LongPollableResource { public class HallCommunicationChannel implements LongPollableResource {
@@ -128,7 +130,8 @@ public class HallCommunicationChannel implements LongPollableResource {
@Override @Override
public void visitTournament(String tournamentKey, String collectionName, String formatName, String tournamentName, String type, String pairingDescription, 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<String, String> props = new HashMap<>(); Map<String, String> props = new HashMap<>();
props.put("collection", collectionName); props.put("collection", collectionName);
props.put("format", formatName); props.put("format", formatName);
@@ -142,6 +145,9 @@ public class HallCommunicationChannel implements LongPollableResource {
props.put("signedUp", String.valueOf(playerInCompetition)); props.put("signedUp", String.valueOf(playerInCompetition));
props.put("abandoned", String.valueOf(abandoned)); props.put("abandoned", String.valueOf(abandoned));
props.put("joinable", String.valueOf(joinable)); props.put("joinable", String.valueOf(joinable));
if (secsRemaining >= 0) {
props.put("timeRemaining", DateUtils.HumanDuration(Duration.ofSeconds(secsRemaining)));
}
tournamentsOnServer.put(tournamentKey, props); tournamentsOnServer.put(tournamentKey, props);
} }

View File

@@ -18,7 +18,8 @@ public interface HallInfoVisitor {
int readyCheckSecsRemaining, boolean confirmedReadyCheck); int readyCheckSecsRemaining, boolean confirmedReadyCheck);
public void visitTournament(String tournamentKey, String collectionName, String formatName, String tournamentName, String type, 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); public void runningPlayerGame(String gameId);
} }

View File

@@ -7,25 +7,21 @@ import com.gempukku.lotro.common.DateUtils;
import com.gempukku.lotro.competitive.ModifiedMedianStandingsProducer; import com.gempukku.lotro.competitive.ModifiedMedianStandingsProducer;
import com.gempukku.lotro.competitive.PlayerStanding; import com.gempukku.lotro.competitive.PlayerStanding;
import com.gempukku.lotro.db.vo.CollectionType; import com.gempukku.lotro.db.vo.CollectionType;
import com.gempukku.lotro.draft.Draft;
import com.gempukku.lotro.draft2.SoloDraftDefinitions; import com.gempukku.lotro.draft2.SoloDraftDefinitions;
import com.gempukku.lotro.draft3.TableDraftDefinitions; import com.gempukku.lotro.draft3.TableDraftDefinitions;
import com.gempukku.lotro.game.CardCollection; import com.gempukku.lotro.game.CardCollection;
import com.gempukku.lotro.game.CardNotFoundException; import com.gempukku.lotro.game.CardNotFoundException;
import com.gempukku.lotro.game.formats.LotroFormatLibrary; import com.gempukku.lotro.game.formats.LotroFormatLibrary;
import com.gempukku.lotro.hall.TableHolder; import com.gempukku.lotro.hall.TableHolder;
import com.gempukku.lotro.logic.actions.AbstractCostToEffectAction;
import com.gempukku.lotro.logic.vo.LotroDeck; import com.gempukku.lotro.logic.vo.LotroDeck;
import com.gempukku.lotro.packs.ProductLibrary; import com.gempukku.lotro.packs.ProductLibrary;
import com.gempukku.lotro.tournament.action.BroadcastAction; import com.gempukku.lotro.tournament.action.BroadcastAction;
import com.gempukku.lotro.tournament.action.CreateGameAction; import com.gempukku.lotro.tournament.action.CreateGameAction;
import com.gempukku.lotro.tournament.action.TournamentProcessAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.DurationFormatUtils;
import org.apache.commons.text.StringEscapeUtils; import org.apache.commons.text.StringEscapeUtils;
import java.time.Duration; import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock; import java.util.concurrent.locks.ReadWriteLock;
@@ -519,4 +515,9 @@ public abstract class BaseTournament implements Tournament {
} }
return _tournamentReport; return _tournamentReport;
} }
@Override
public long getSecondsRemaining() throws IllegalStateException{
throw new IllegalStateException();
}
} }

View File

@@ -16,11 +16,14 @@ import com.gempukku.lotro.tournament.action.BroadcastAction;
import com.gempukku.lotro.tournament.action.TournamentProcessAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
public class SealedTournament extends BaseTournament implements Tournament { public class SealedTournament extends BaseTournament implements Tournament {
private SealedTournamentInfo _sealedInfo; private SealedTournamentInfo _sealedInfo;
private ZonedDateTime nextRoundStart = null;
public SealedTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, public SealedTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary,
LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, TableHolder tables, String tournamentId) { 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)) { if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) {
result.add(finishTournament(collectionsManager)); result.add(finishTournament(collectionsManager));
} else { } else {
nextRoundStart = DateUtils.Now().plus(PairingDelayTime);
if(getCurrentRound() == 0) { if(getCurrentRound() == 0) {
result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName() result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName()
+ "</b> has closed. Round " + "</b> has closed. Round "
@@ -247,4 +251,17 @@ public class SealedTournament extends BaseTournament implements Tournament {
getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF) getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF)
&& (maximumPlayers > activePlayers.size() || maximumPlayers < 0); && (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();
}
}
} }

View File

@@ -18,12 +18,15 @@ import com.gempukku.lotro.tournament.action.BroadcastAction;
import com.gempukku.lotro.tournament.action.TournamentProcessAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
public class SoloDraftTournament extends BaseTournament implements Tournament { public class SoloDraftTournament extends BaseTournament implements Tournament {
private static final int HIGH_ENOUGH_PRIME_NUMBER = 8963; private static final int HIGH_ENOUGH_PRIME_NUMBER = 8963;
private SoloDraftTournamentInfo _soloDraftInfo; private SoloDraftTournamentInfo _soloDraftInfo;
private ZonedDateTime nextRoundStart = null;
public SoloDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, public SoloDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary,
LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, TableHolder tables, String tournamentId) { 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)) { if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) {
result.add(finishTournament(collectionsManager)); result.add(finishTournament(collectionsManager));
} else { } else {
nextRoundStart = DateUtils.Now().plus(PairingDelayTime);
if(getCurrentRound() == 0) { if(getCurrentRound() == 0) {
result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName() result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName()
+ "</b> has closed. Round " + "</b> has closed. Round "
@@ -262,4 +266,17 @@ public class SoloDraftTournament extends BaseTournament implements Tournament {
getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF) getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF)
&& (maximumPlayers > activePlayers.size() || maximumPlayers < 0); && (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();
}
}
} }

View File

@@ -16,12 +16,15 @@ import com.gempukku.lotro.tournament.action.BroadcastAction;
import com.gempukku.lotro.tournament.action.TournamentProcessAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
public class SoloTableDraftTournament extends BaseTournament implements Tournament { public class SoloTableDraftTournament extends BaseTournament implements Tournament {
private SoloTableDraftTournamentInfo soloTableDraftInfo; private SoloTableDraftTournamentInfo soloTableDraftInfo;
private Map<String, TableDraft> tables = null; private Map<String, TableDraft> tables = null;
private ZonedDateTime nextRoundStart = null;
public SoloTableDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, public SoloTableDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary,
LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions,
@@ -176,6 +179,7 @@ public class SoloTableDraftTournament extends BaseTournament implements Tourname
if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) { if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) {
result.add(finishTournament(collectionsManager)); result.add(finishTournament(collectionsManager));
} else { } else {
nextRoundStart = DateUtils.Now().plus(PairingDelayTime);
if(getCurrentRound() == 0) { if(getCurrentRound() == 0) {
result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName() result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName()
+ "</b> has closed. Round " + "</b> has closed. Round "
@@ -245,4 +249,17 @@ public class SoloTableDraftTournament extends BaseTournament implements Tourname
getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF) getTournamentStage() == Tournament.Stage.PAUSED || getTournamentStage() == Tournament.Stage.AWAITING_KICKOFF)
&& (maximumPlayers > activePlayers.size() || maximumPlayers < 0); && (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();
}
}
} }

View File

@@ -18,6 +18,8 @@ import com.gempukku.lotro.tournament.action.BroadcastAction;
import com.gempukku.lotro.tournament.action.TournamentProcessAction; import com.gempukku.lotro.tournament.action.TournamentProcessAction;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.util.*; import java.util.*;
public class TableDraftTournament extends BaseTournament implements Tournament { public class TableDraftTournament extends BaseTournament implements Tournament {
@@ -25,6 +27,7 @@ public class TableDraftTournament extends BaseTournament implements Tournament {
private TableDraftTournamentInfo tableDraftInfo; private TableDraftTournamentInfo tableDraftInfo;
private TableDraft table = null; private TableDraft table = null;
private final ChatServer chatServer; private final ChatServer chatServer;
private ZonedDateTime nextRoundStart = null;
public TableDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary, public TableDraftTournament(TournamentService tournamentService, CollectionsManager collectionsManager, ProductLibrary productLibrary,
LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions, LotroFormatLibrary formatLibrary, SoloDraftDefinitions soloDraftDefinitions, TableDraftDefinitions tableDraftDefinitions,
@@ -201,6 +204,7 @@ public class TableDraftTournament extends BaseTournament implements Tournament {
if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) { if (_tournamentInfo.PairingMechanism.isFinished(getCurrentRound(), _players, _droppedPlayers)) {
result.add(finishTournament(collectionsManager)); result.add(finishTournament(collectionsManager));
} else { } else {
nextRoundStart = DateUtils.Now().plus(PairingDelayTime);
if(getCurrentRound() == 0) { if(getCurrentRound() == 0) {
result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName() result.add(new BroadcastAction("Deck registration for tournament <b>" + getTournamentName()
+ "</b> has closed. Round " + "</b> has closed. Round "
@@ -262,4 +266,17 @@ public class TableDraftTournament extends BaseTournament implements Tournament {
public boolean isJoinable() { public boolean isJoinable() {
return false; // cannot join draft in progress 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();
}
}
} }

View File

@@ -206,4 +206,5 @@ public interface Tournament {
TournamentInfo getInfo(); TournamentInfo getInfo();
boolean isJoinable(); boolean isJoinable();
long getSecondsRemaining() throws IllegalStateException;
} }

View File

@@ -264,11 +264,17 @@ public class TournamentService {
for (var entry : _activeTournaments.entrySet()) { for (var entry : _activeTournaments.entrySet()) {
var tourneyID = entry.getKey(); var tourneyID = entry.getKey();
var tournament = entry.getValue(); var tournament = entry.getValue();
long secsRemaining = -1;
try {
secsRemaining = tournament.getSecondsRemaining();
} catch (IllegalStateException ignore) {
}
visitor.visitTournament(tourneyID, tournament.getCollectionType().getFullName(), visitor.visitTournament(tourneyID, tournament.getCollectionType().getFullName(),
formatLibrary.getFormat(tournament.getFormatCode()).getName(), tournament.getTournamentName(), tournament.getInfo().Parameters().type.toString(), tournament.getPlayOffSystem(), formatLibrary.getFormat(tournament.getFormatCode()).getName(), tournament.getTournamentName(), tournament.getInfo().Parameters().type.toString(), tournament.getPlayOffSystem(),
tournament.getTournamentStage().getHumanReadable(), tournament.getTournamentStage().getHumanReadable(),
tournament.getCurrentRound(), tournament.getPlayersInCompetitionCount(), tournament.getPlayerList(), tournament.isPlayerInCompetition(player.getName()), tournament.isPlayerAbandoned(player.getName()), tournament.getCurrentRound(), tournament.getPlayersInCompetitionCount(), tournament.getPlayerList(), tournament.isPlayerInCompetition(player.getName()), tournament.isPlayerAbandoned(player.getName()),
tournament.isJoinable()); tournament.isJoinable(), secsRemaining);
} }
} }