Cleaned up tournament code

This commit is contained in:
MarcinSc
2024-08-28 11:33:27 +07:00
parent 0bda261da8
commit 55644ef77f
8 changed files with 140 additions and 152 deletions

View File

@@ -747,7 +747,6 @@ public class HallServer extends AbstractServer {
private int _tickCounter = 60;
public void cleanup(boolean forceRefresh) throws SQLException, IOException {
_hallDataAccessLock.writeLock().lock();
try {
// Remove finished games
@@ -869,6 +868,7 @@ public class HallServer extends AbstractServer {
public ManualGameSpawner GetManualGameSpawner(Tournament tournament, LotroFormat format, GameTimer timer, String description) {
return new ManualGameSpawner(tournament, format, timer, description);
}
public class ManualGameSpawner implements TournamentCallback {
private final Tournament _tournament;
private final GameSettings _settings;
@@ -891,7 +891,6 @@ public class HallServer extends AbstractServer {
_hallDataAccessLock.writeLock().lock();
try {
if (!_shutdown) {
final GameTable gameTable = tableHolder.setupTournamentTable(_settings, participants);
final LotroGameMediator mediator = createGameMediator(participants,
_notifyHallListeners, _tournament.getTournamentName(), _settings);

View File

@@ -17,12 +17,13 @@ import org.apache.commons.lang3.StringUtils;
import java.time.ZonedDateTime;
import java.util.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class DefaultTournament implements Tournament {
// 10 minutes
public static int DeckBuildTime = 10 * 60 * 1000;
public static final int DeckBuildTime = 10 * 60 * 1000;
public static long PairingDelayTime = 1000 * 60 * 1;
private final PairingMechanism _pairingMechanism;
@@ -46,7 +47,10 @@ public class DefaultTournament implements Tournament {
private final TournamentService _tournamentService;
private final ReadWriteLock _lock = new ReentrantReadWriteLock();
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock readLock = lock.readLock();
private final Lock writeLock = lock.writeLock();
private TournamentTask _nextTask;
private long _deckBuildStartTime;
@@ -54,14 +58,11 @@ public class DefaultTournament implements Tournament {
private List<PlayerStanding> _currentStandings;
private final boolean _manualKickoff;
private boolean _hasKickedOff;
private String _tournamentReport;
public DefaultTournament(TournamentService tournamentService, String tournamentId,
String tournamentName, String format, CollectionType collectionType,
int tournamentRound, Stage tournamentStage, boolean manualKickoff,
int tournamentRound, Stage tournamentStage,
PairingMechanism pairingMechanism, TournamentPrizes tournamentPrizes,
CollectionsManager collectionsManager, ProductLibrary productLibrary, DraftPack draftPack) {
_tournamentService = tournamentService;
@@ -71,7 +72,6 @@ public class DefaultTournament implements Tournament {
_collectionType = collectionType;
_tournamentRound = tournamentRound;
_tournamentStage = tournamentStage;
_manualKickoff = manualKickoff;
_pairingMechanism = pairingMechanism;
_tournamentPrizes = tournamentPrizes;
@@ -104,8 +104,6 @@ public class DefaultTournament implements Tournament {
_players);
} else if (_tournamentStage == Stage.DECK_BUILDING) {
_deckBuildStartTime = System.currentTimeMillis();
} else if (_tournamentStage == Stage.AWAITING_KICKOFF || _tournamentStage == Stage.PAUSED) {
} else if (_tournamentStage == Stage.FINISHED) {
_finishedTournamentMatches.addAll(_tournamentService.getMatches(_tournamentId));
}
@@ -114,14 +112,10 @@ public class DefaultTournament implements Tournament {
public DefaultTournament(TournamentService tournamentService, CollectionsManager collectionsManager,
ProductLibrary productLibrary, DraftPack draftPack, TournamentInfo info) {
this(tournamentService, info.tournamentId(), info.name(), info.format(), info.collectionType(),
info.round(), info.tournamentStage(), info.manualKickoff(), info.pairingMechanism(), info.prizesScheme(),
info.round(), info.tournamentStage(), info.pairingMechanism(), info.prizesScheme(),
collectionsManager, productLibrary, draftPack);
}
public DefaultTournament( TournamentService tournamentService, TournamentInfo info) {
this(tournamentService, null, null, null, info);
}
protected void regeneratePlayerList() {
_playerList = "";
@@ -144,7 +138,8 @@ public class DefaultTournament implements Tournament {
}
public void setWaitForPairingsTime(long waitForPairingsTime) {
// Used for test only
protected void setWaitForPairingsTime(long waitForPairingsTime) {
PairingDelayTime = waitForPairingsTime;
}
@@ -155,7 +150,12 @@ public class DefaultTournament implements Tournament {
@Override
public int getPlayersInCompetitionCount() {
return _players.size() - _droppedPlayers.size();
readLock.lock();
try {
return _players.size() - _droppedPlayers.size();
} finally {
readLock.unlock();
}
}
@Override
@@ -195,17 +195,17 @@ public class DefaultTournament implements Tournament {
@Override
public boolean isPlayerInCompetition(String player) {
_lock.readLock().lock();
readLock.lock();
try {
return _tournamentStage != Stage.FINISHED && _players.contains(player) && !_droppedPlayers.contains(player);
} finally {
_lock.readLock().unlock();
readLock.unlock();
}
}
@Override
public void reportGameFinished(String winner, String loser) {
_lock.writeLock().lock();
writeLock.lock();
try {
if (_tournamentStage == Stage.PLAYING_GAMES && _currentlyPlayingPlayers.contains(winner)
&& _currentlyPlayingPlayers.contains(loser)) {
@@ -221,51 +221,53 @@ public class DefaultTournament implements Tournament {
_currentStandings = null;
}
} finally {
_lock.writeLock().unlock();
writeLock.unlock();
}
}
@Override
public void playerSubmittedDeck(String player, LotroDeck deck) {
_lock.writeLock().lock();
writeLock.lock();
try {
if (_tournamentStage == Stage.DECK_BUILDING && _players.contains(player)) {
_tournamentService.setPlayerDeck(_tournamentId, player, deck);
_playerDecks.put(player, deck);
}
} finally {
_lock.writeLock().unlock();
writeLock.unlock();
}
}
@Override
public LotroDeck getPlayerDeck(String player) {
_lock.readLock().lock();
readLock.lock();
try {
return _playerDecks.get(player);
} finally {
_lock.readLock().unlock();
readLock.unlock();
}
}
@Override
public Draft getDraft() {
return _draft;
}
@Override
public void playerChosenCard(String playerName, String cardId) {
_lock.writeLock().lock();
writeLock.lock();
try {
if (_tournamentStage == Stage.DRAFT) {
_draft.playerChosenCard(playerName, cardId);
}
} finally {
_lock.writeLock().unlock();
writeLock.unlock();
}
}
@Override
public boolean dropPlayer(String player) {
_lock.writeLock().lock();
writeLock.lock();
try {
if (_currentlyPlayingPlayers.contains(player))
return false;
@@ -281,13 +283,13 @@ public class DefaultTournament implements Tournament {
regeneratePlayerList();
return true;
} finally {
_lock.writeLock().unlock();
writeLock.unlock();
}
}
@Override
public boolean advanceTournament(TournamentCallback tournamentCallback, CollectionsManager collectionsManager) {
_lock.writeLock().lock();
writeLock.lock();
try {
boolean result = false;
if (_nextTask == null) {
@@ -310,14 +312,7 @@ public class DefaultTournament implements Tournament {
result = true;
}
}
if (_tournamentStage == Stage.AWAITING_KICKOFF || _tournamentStage == Stage.PAUSED) {
}
else if (_tournamentStage == Stage.PREPARING) {
_tournamentStage = Stage.PLAYING_GAMES;
_tournamentService.updateTournamentStage(_tournamentId, _tournamentStage);
}
else if (_tournamentStage == Stage.PLAYING_GAMES) {
if (_tournamentStage == Stage.PLAYING_GAMES) {
if (_currentlyPlayingPlayers.isEmpty()) {
if (_pairingMechanism.isFinished(_tournamentRound, _players, _droppedPlayers)) {
finishTournament(tournamentCallback, collectionsManager);
@@ -337,7 +332,7 @@ public class DefaultTournament implements Tournament {
}
return result;
} finally {
_lock.writeLock().unlock();
writeLock.unlock();
}
}
@@ -347,12 +342,12 @@ public class DefaultTournament implements Tournament {
if (result != null)
return result;
_lock.readLock().lock();
readLock.lock();
try {
_currentStandings = ModifiedMedianStandingsProducer.produceStandings(_players, _finishedTournamentMatches, 1, 0, _playerByes);
return _currentStandings;
} finally {
_lock.readLock().unlock();
readLock.unlock();
}
}
@@ -467,97 +462,101 @@ public class DefaultTournament implements Tournament {
@Override
public String produceReport(DeckRenderer renderer) throws CardNotFoundException {
if(_tournamentReport != null)
return _tournamentReport;
readLock.lock();
try {
if (_tournamentReport == null) {
ZonedDateTime tournamentStart = null;
ZonedDateTime tournamentEnd = null;
ZonedDateTime start = null;
ZonedDateTime end = null;
var games = _tournamentService.getGames(_tournamentName);
var games = _tournamentService.getGames(_tournamentName);
for (var match : _finishedTournamentMatches) {
var game = games.stream()
.filter((x) -> x.winner.equals(match.getWinner()) && x.loser.equals(match.getLoser()))
.findFirst()
.orElse(null);
for(var match : _finishedTournamentMatches) {
var game = games.stream()
.filter((x) -> x.winner.equals(match.getWinner()) && x.loser.equals(match.getLoser()))
.findFirst()
.orElse(null);
if (game == null)
continue;
if(game == null)
continue;
var gameStart = game.GetUTCStartDate();
var gameEnd = game.GetUTCEndDate();
var gameStart = game.GetUTCStartDate();
var gameEnd = game.GetUTCEndDate();
if (tournamentStart == null || gameStart.isBefore(tournamentStart)) {
tournamentStart = gameStart;
}
if(start == null || gameStart.isBefore(start)) {
start = gameStart;
}
if(end == null || gameEnd.isAfter(end)) {
end = gameEnd;
if (tournamentEnd == null || gameEnd.isAfter(tournamentEnd)) {
tournamentEnd = gameEnd;
}
}
StringBuilder summary = new StringBuilder();
summary
.append("<h1>").append(StringEscapeUtils.escapeHtml3(_tournamentName)).append("</h1>")
.append("<ul>")
.append("<li>Format: ").append(_format).append("</li>")
.append("<li>Collection: ").append(_collectionType.getFullName()).append("</li>")
.append("<li>Total Rounds: ").append(_tournamentRound).append("</li>")
.append("<li>Start: ").append(DateUtils.FormatDateTime(tournamentStart)).append("</li>")
.append("<li>End: ").append(DateUtils.FormatDateTime(tournamentEnd)).append("</li>")
.append("</ul><br/><br/><hr>");
var sections = new ArrayList<String>();
sections.add(summary.toString());
for (var standing : getCurrentStandings()) {
var playerName = standing.playerName;
var rounds = new ArrayList<Map.Entry<String, String>>();
var playerRounds = _finishedTournamentMatches.stream()
.filter((x) -> x.getPlayerOne().equals(playerName) || x.getPlayerTwo().equals(playerName))
.toList();
for (int i = 1; i <= _tournamentRound; i++) {
if (_playerByes.containsKey(playerName) && _playerByes.get(playerName) == i) {
rounds.add(createEntry("[bye]", ""));
continue;
}
int currentRound = i;
var match = playerRounds.stream().filter(x -> x.getRound() == currentRound)
.findFirst().orElse(null);
if (match == null) {
rounds.add(createEntry("[dropped]", ""));
continue;
}
var game = games.stream().filter((x) -> x.winner.equals(match.getWinner()) && x.loser.equals(match.getLoser()))
.findFirst()
.orElse(null);
if (game == null)
continue;
String replayId = game.win_recording_id;
if (match.getLoser().equals(playerName)) {
replayId = game.lose_recording_id;
}
String label = "Round " + i;
String url = "https://play.lotrtcgpc.net/gemp-lotr/game.html?replayId=" +
playerName.replace("_", "%5F") + "$" + replayId;
rounds.add(createEntry(label, url));
}
LotroDeck deck = _tournamentService.getPlayerDeck(_tournamentId, playerName, _format);
var fragment = renderer.convertDeckToForumFragment(deck, playerName, rounds);
sections.add(fragment);
}
_tournamentReport = renderer.AddDeckReadoutHeaderAndFooter(sections);
}
} finally {
readLock.unlock();
}
StringBuilder summary = new StringBuilder();
summary.append("<html><body>")
.append("<h1>").append(StringEscapeUtils.escapeHtml3(_tournamentName)).append("</h1>")
.append("<ul>")
.append("<li>Format: ").append(_format).append("</li>")
.append("<li>Collection: ").append(_collectionType.getFullName()).append("</li>")
.append("<li>Total Rounds: ").append(_tournamentRound).append("</li>")
.append("<li>Start: ").append(DateUtils.FormatDateTime(start)).append("</li>")
.append("<li>End: ").append(DateUtils.FormatDateTime(end)).append("</li>")
.append("</ul><br/><br/><hr>");
var decks = new ArrayList<String>();
decks.add(summary.toString());
for(var standing : getCurrentStandings()) {
var playerName = standing.playerName;
var rounds = new ArrayList<Map.Entry<String, String>>();
var playerRounds = _finishedTournamentMatches.stream()
.filter((x) -> x.getPlayerOne().equals(playerName) || x.getPlayerTwo().equals(playerName))
.toList();
for(int i = 1; i <= _tournamentRound; i++) {
if(_playerByes.containsKey(playerName) && _playerByes.get(playerName) == i) {
rounds.add(createEntry("[bye]", ""));
continue;
}
int currentRound = i;
var match = playerRounds.stream().filter(x -> x.getRound() == currentRound)
.findFirst().orElse(null);
if(match == null) {
rounds.add(createEntry("[dropped]", ""));
continue;
}
var game = games.stream().filter((x) -> x.winner.equals(match.getWinner()) && x.loser.equals(match.getLoser()))
.findFirst()
.orElse(null);
if(game == null)
continue;
String replayId = game.win_recording_id;
if(match.getLoser().equals(playerName)) {
replayId = game.lose_recording_id;
}
String label = "Round " + i;
String url = "https://play.lotrtcgpc.net/gemp-lotr/game.html?replayId=" +
playerName.replace("_", "%5F") + "$" + replayId;
rounds.add(createEntry(label, url));
}
LotroDeck deck = _tournamentService.getPlayerDeck(_tournamentId, playerName, _format);
var fragment = renderer.convertDeckToForumFragment(deck, playerName, rounds);
decks.add(fragment);
}
return renderer.AddDeckReadoutHeaderAndFooter(decks);
return _tournamentReport;
}
}

View File

@@ -46,7 +46,7 @@ public class ImmediateRecurringQueue extends AbstractTournamentQueue implements
}
var info = new TournamentInfo(tournamentId, null, tournamentName, _format, ZonedDateTime.now(),
_collectionType, Tournament.Stage.PLAYING_GAMES, 0, false,
_collectionType, Tournament.Stage.PLAYING_GAMES, 0,
Tournament.getPairingMechanism("singleElimination"), _tournamentPrizes);
var tournament = _tournamentService.addTournament(info);

View File

@@ -71,7 +71,7 @@ public class RecurringScheduledQueue extends AbstractTournamentQueue implements
_tournamentService.addPlayer(tournamentId, player, _playerDecks.get(player));
var info = new TournamentInfo(tournamentId, null, tournamentName, _format, ZonedDateTime.now(),
_collectionType, Tournament.Stage.PLAYING_GAMES, 0, false,
_collectionType, Tournament.Stage.PLAYING_GAMES, 0,
_pairingMechanism, _tournamentPrizes);
var tournament = _tournamentService.addTournament(info);

View File

@@ -35,12 +35,7 @@ public class ScheduledTournamentQueue extends AbstractTournamentQueue implements
_startCondition = "at " + _startTime.format(DateUtils.DateTimeFormat);
_minimumPlayers = info.minimum_players;
if(info.manual_kickoff) {
_stage = Tournament.Stage.AWAITING_KICKOFF;
}
else {
_stage = Tournament.Stage.PLAYING_GAMES;
}
_stage = Tournament.Stage.PLAYING_GAMES;
}
@Override
@@ -63,12 +58,11 @@ public class ScheduledTournamentQueue extends AbstractTournamentQueue implements
var now = ZonedDateTime.now();
if (now.isAfter(_startTime)) {
if (_players.size() >= _minimumPlayers) {
for (String player : _players)
_tournamentService.addPlayer(_scheduledTournamentId, player, _playerDecks.get(player));
var info = new TournamentInfo(_scheduledTournamentId, null, _tournamentName, _format, ZonedDateTime.now(),
_collectionType, _stage, 0, false,
_collectionType, _stage, 0,
_pairingMechanism, _tournamentPrizes);
var tournament = _tournamentService.addTournament(info);
@@ -87,7 +81,7 @@ public class ScheduledTournamentQueue extends AbstractTournamentQueue implements
@Override
public boolean isJoinable() {
var window = _signupTimeBeforeStart;
if(_scheduledTournamentId.toLowerCase().contains("wc")) {
if (_scheduledTournamentId.toLowerCase().contains("wc")) {
window = _wcSignupTimeBeforeStart;
}
return ZonedDateTime.now().isAfter(_startTime.minus(window));

View File

@@ -18,9 +18,6 @@ public interface Tournament {
public enum Stage {
DRAFT("Drafting"),
DECK_BUILDING("Deck building"),
AWAITING_KICKOFF("Awaiting kickoff"),
PAUSED("Paused"),
PREPARING("Preparing"),
PLAYING_GAMES("Playing games"),
FINISHED("Finished");

View File

@@ -10,21 +10,21 @@ import java.util.List;
public record TournamentInfo(String tournamentId, String draftType, String name, String format,
ZonedDateTime startDate, CollectionType collectionType,
Tournament.Stage tournamentStage, int round, boolean manualKickoff,
Tournament.Stage tournamentStage, int round,
PairingMechanism pairingMechanism, TournamentPrizes prizesScheme
) {
) {
public TournamentInfo(ProductLibrary library, DBDefs.Tournament info) {
this(info.tournament_id, info.draft_type, info.name, info.format, info.GetUTCStartDate(),
CollectionType.parseCollectionCode(info.collection), Tournament.Stage.parseStage(info.stage),
info.round, info.manual_kickoff, Tournament.getPairingMechanism(info.pairing),
info.round, Tournament.getPairingMechanism(info.pairing),
Tournament.getTournamentPrizes(library, info.prizes));
}
public TournamentInfo(ProductLibrary library, DBDefs.ScheduledTournament info) {
public TournamentInfo(ProductLibrary library, DBDefs.ScheduledTournament info) {
this(info.tournament_id, null, info.name, info.format, info.GetUTCStartDate(),
CollectionType.ALL_CARDS, info.manual_kickoff ? Tournament.Stage.AWAITING_KICKOFF : Tournament.Stage.PLAYING_GAMES,
0, info.manual_kickoff, Tournament.getPairingMechanism(info.playoff),
CollectionType.ALL_CARDS, Tournament.Stage.PLAYING_GAMES,
0, Tournament.getPairingMechanism(info.playoff),
Tournament.getTournamentPrizes(library, info.prizes));
}
@@ -38,24 +38,23 @@ public record TournamentInfo(String tournamentId, String draftType, String name,
tourney.collection = this.collectionType.getCode();
tourney.stage = this.tournamentStage.getHumanReadable();
tourney.round = this.round;
tourney.manual_kickoff = this.manualKickoff;
tourney.pairing = this.pairingMechanism.getRegistryRepresentation();
tourney.prizes = this.prizesScheme.getRegistryRepresentation();
return tourney;
}
public static List<TournamentInfo> fromDB(ProductLibrary library, List<DBDefs.Tournament> dbinfos) {
public static List<TournamentInfo> fromDB(ProductLibrary library, List<DBDefs.Tournament> dbinfos) {
List<TournamentInfo> infos = new ArrayList<>();
for(var result : dbinfos) {
for (var result : dbinfos) {
infos.add(new TournamentInfo(library, result));
}
return infos;
}
public static List<TournamentInfo> fromSDB(ProductLibrary library, List<DBDefs.ScheduledTournament> dbinfos) {
public static List<TournamentInfo> fromSDB(ProductLibrary library, List<DBDefs.ScheduledTournament> dbinfos) {
List<TournamentInfo> infos = new ArrayList<>();
for(var result : dbinfos) {
for (var result : dbinfos) {
infos.add(new TournamentInfo(library, result));
}
return infos;

View File

@@ -47,7 +47,7 @@ public class DefaultTournamentTest extends AbstractAtTest {
CollectionsManager collectionsManager = Mockito.mock(CollectionsManager.class);
var tournament = new DefaultTournament(tournamentService, tournamentId, "Name", "format",
CollectionType.ALL_CARDS, 0, Tournament.Stage.PLAYING_GAMES, false, pairingMechanism,
CollectionType.ALL_CARDS, 0, Tournament.Stage.PLAYING_GAMES, pairingMechanism,
new SingleEliminationOnDemandPrizes(_cardLibrary, "onDemand"),null, null, null);
tournament.setWaitForPairingsTime(_waitForPairingsTime);