diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TableDraftRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TableDraftRequestHandler.java index 38f208b3c..d2c727d94 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TableDraftRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TableDraftRequestHandler.java @@ -218,101 +218,6 @@ public class TableDraftRequestHandler extends LotroServerRequestHandler implemen } private void writeXml(ResponseWriter responseWriter, DraftPlayer draftPlayer) throws ParserConfigurationException { - DraftDocBuilder draftDocBuilder = new DraftDocBuilder(); - responseWriter.writeXmlResponse(draftDocBuilder.getDocument(draftPlayer)); - } - - private class DraftDocBuilder{ - DraftPlayer draftPlayer; - - public Document getDocument(DraftPlayer draftPlayer) throws ParserConfigurationException { - this.draftPlayer = draftPlayer; - - DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); - DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); - - Document doc = documentBuilder.newDocument(); - - Element rootElement = doc.createElement("draftStatus"); - doc.appendChild(rootElement); - - appendPlayersInfo(doc, rootElement); - - if (draftPlayer.getCollection() != null) { - appendPickedCards(doc, rootElement); - } - - if (draftPlayer.getCardsToPickFrom() != null && !draftPlayer.getCardsToPickFrom().isEmpty()) { - appendAvailablePicks(doc, rootElement); - } - - try { - int timeRemaining = draftPlayer.getSecondsRemainingForPick(); - appendTimeRemaining(doc, rootElement, timeRemaining); - } catch (IllegalStateException ignore) { - // No timer - } - - appendRoundsInfo(doc, rootElement); - - return doc; - } - - private void appendPlayersInfo(Document doc, Element rootElement) { - TableDraft.TableStatus tableStatus = draftPlayer.getTableStatus(); - - for (TableDraft.PlayerStatus playerStatus : tableStatus.getPlayerStatuses()) { - Element playerElement = doc.createElement("player"); - playerElement.setAttribute("name", playerStatus.getName()); - playerElement.setAttribute("hasChosen", String.valueOf(playerStatus.hasChosenCard())); - rootElement.appendChild(playerElement); - } - - Element pickOrder = doc.createElement("pickOrderAscending"); - pickOrder.setAttribute("value", String.valueOf(tableStatus.isPickOrderAscending())); - rootElement.appendChild(pickOrder); - } - - - private void appendTimeRemaining(Document doc, Element rootElement, int timeRemaining) { - Element time = doc.createElement("timeRemaining"); - time.setAttribute("value", "" + timeRemaining); - rootElement.appendChild(time); - } - - private void appendRoundsInfo(Document doc, Element rootElement) { - Element time = doc.createElement("roundsInfo"); - time.setAttribute("currentRound", "" + draftPlayer.getTableStatus().getCurrentRound()); - time.setAttribute("roundsTotal", "" + draftPlayer.getTableStatus().getRoundsTotal()); - rootElement.appendChild(time); - } - - private void appendPickedCards(Document doc, Element rootElement) { - SortAndFilterCards sortAndFilterCards = new SortAndFilterCards(); - List pickedCards = sortAndFilterCards.process( - "sort:cardType,culture,name", - draftPlayer.getCollection().getAll(), - _cardLibrary, - _formatLibrary - ); - for (CardCollection.Item item : pickedCards) { - Element pickedCard = doc.createElement("pickedCard"); - pickedCard.setAttribute("blueprintId", item.getBlueprintId()); - pickedCard.setAttribute("count", String.valueOf(item.getCount())); - rootElement.appendChild(pickedCard); - } - } - - private void appendAvailablePicks(Document doc, Element rootElement) { - for (String availableChoice : draftPlayer.getCardsToPickFrom()) { - Element availablePick = doc.createElement("availablePick"); - availablePick.setAttribute("id", availableChoice); - availablePick.setAttribute("blueprintId", availableChoice); - if (draftPlayer.getChosenCard() != null && draftPlayer.getChosenCard().equals(availableChoice)) { - availablePick.setAttribute("chosen", "true"); - } - rootElement.appendChild(availablePick); - } - } + responseWriter.writeXmlResponse(draftPlayer.getDocument(_cardLibrary, _formatLibrary)); } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/DraftPlayer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/DraftPlayer.java index 58306a148..672c83c80 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/DraftPlayer.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/DraftPlayer.java @@ -1,7 +1,11 @@ package com.gempukku.lotro.draft3; import com.gempukku.lotro.game.CardCollection; +import com.gempukku.lotro.game.LotroCardBlueprintLibrary; +import com.gempukku.lotro.game.formats.LotroFormatLibrary; +import org.w3c.dom.Document; +import javax.xml.parsers.ParserConfigurationException; import java.util.List; import java.util.Objects; @@ -38,14 +42,14 @@ public class DraftPlayer { return table.getChosenCard(this); } - public TableDraft.TableStatus getTableStatus() { - return table.getTableStatus(); - } - public boolean draftFinished() { return table.isFinished(); } + public Document getDocument(LotroCardBlueprintLibrary cardLibrary, LotroFormatLibrary formatLibrary) throws ParserConfigurationException { + return table.getDocument(this, cardLibrary, formatLibrary); + } + @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraft.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraft.java index 32c4cbba0..6f9869574 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraft.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraft.java @@ -1,7 +1,11 @@ package com.gempukku.lotro.draft3; import com.gempukku.lotro.game.CardCollection; +import com.gempukku.lotro.game.LotroCardBlueprintLibrary; +import com.gempukku.lotro.game.formats.LotroFormatLibrary; +import org.w3c.dom.Document; +import javax.xml.parsers.ParserConfigurationException; import java.util.List; public interface TableDraft { @@ -15,53 +19,5 @@ public interface TableDraft { boolean isFinished(); int getSecondsRemainingForPick() throws IllegalStateException; CardCollection getPickedCards(DraftPlayer draftPlayer); - TableStatus getTableStatus(); - - class PlayerStatus { - private final String name; - private final boolean hasChosenCard; - - public PlayerStatus(String name, boolean hasChosenCard) { - this.name = name; - this.hasChosenCard = hasChosenCard; - } - - public String getName() { - return name; - } - - public boolean hasChosenCard() { - return hasChosenCard; - } - } - - class TableStatus { - private final List playerStatuses; - private final boolean pickOrderAscending; - private final int currentRound; - private final int roundsTotal; - - public TableStatus(List playerStatuses, boolean pickOrderAscending, int currentRound, int roundsTotal) { - this.playerStatuses = playerStatuses; - this.pickOrderAscending = pickOrderAscending; - this.currentRound = currentRound; - this.roundsTotal = roundsTotal; - } - - public List getPlayerStatuses() { - return playerStatuses; - } - - public boolean isPickOrderAscending() { - return pickOrderAscending; - } - - public int getCurrentRound() { - return currentRound; - } - - public int getRoundsTotal() { - return roundsTotal; - } - } + Document getDocument(DraftPlayer draftPlayer, LotroCardBlueprintLibrary cardLibrary, LotroFormatLibrary formatLibrary) throws ParserConfigurationException; } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraftClassic.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraftClassic.java index 410af65f6..4f36e3ba9 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraftClassic.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/draft3/TableDraftClassic.java @@ -5,10 +5,14 @@ import com.gempukku.lotro.db.vo.CollectionType; import com.gempukku.lotro.draft3.bot.DraftBot; import com.gempukku.lotro.draft3.bot.WeightDraftBot; import com.gempukku.lotro.draft3.timer.DraftTimer; -import com.gempukku.lotro.game.CardCollection; -import com.gempukku.lotro.game.DefaultCardCollection; -import com.gempukku.lotro.game.MutableCardCollection; +import com.gempukku.lotro.game.*; +import com.gempukku.lotro.game.formats.LotroFormatLibrary; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; import java.io.IOException; import java.sql.SQLException; import java.util.*; @@ -16,12 +20,18 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class TableDraftClassic implements TableDraft{ - //TODO thread locks private static final int MISSED_CARDS_ALLOWED = 4; + private final ReadWriteLock lock = new ReentrantReadWriteLock(true); + private final Lock readLock = lock.readLock(); + private final Lock writeLock = lock.writeLock(); + private final StartingCollectionProducer startingCollectionProducer; private final BoosterProducer boosterProducer; private final int maxPlayers; @@ -71,48 +81,63 @@ public class TableDraftClassic implements TableDraft{ @Override public List getCardsToPickFrom(DraftPlayer draftPlayer) { - if (!assignedBoosters.containsKey(draftPlayer)) { - return List.of(); + readLock.lock(); + try { + if (!assignedBoosters.containsKey(draftPlayer)) { + return List.of(); + } + return assignedBoosters.get(draftPlayer).getCardsInPack(); + } finally { + readLock.unlock(); } - return assignedBoosters.get(draftPlayer).getCardsInPack(); } @Override public String getChosenCard(DraftPlayer draftPlayer) { - return chosenCards.get(draftPlayer); + readLock.lock(); + try { + return chosenCards.get(draftPlayer); + } finally { + readLock.unlock(); + } } @Override public void advanceDraft() { - if (isFinished()) { - return; - } + writeLock.lock(); + try { + if (isFinished()) { + return; + } - if (currentRound == 0) { - startDraft(); - startTimer(); - makeBotsDeclare(); - } else if (haveAllPlayersDeclared()) { - pickDeclaredCards(); - if (areBoostersEmpty()) { - if (currentRound < rounds) { - openNextBoosters(); + if (currentRound == 0) { + startDraft(); + startTimer(); + makeBotsDeclare(); + } else if (haveAllPlayersDeclared()) { + pickDeclaredCards(); + if (areBoostersEmpty()) { + if (currentRound < rounds) { + openNextBoosters(); + } else { + // Draft finished, shutdown timer scheduler + scheduler.shutdown(); + return; + } } else { - // Draft finished, shutdown timer scheduler - scheduler.shutdown(); - return; + passBoosters(); } + startTimer(); + makeBotsDeclare(); + makeAfkersDeclare(); } else { - passBoosters(); - } - startTimer(); - makeBotsDeclare(); - makeAfkersDeclare(); - } else { - if (choiceTimePassed()) { - forcePlayerDeclares(); - advanceDraft(); + if (choiceTimePassed()) { + forcePlayerDeclares(); + advanceDraft(); + } } + } finally { + writeLock.unlock(); } } @@ -277,11 +302,16 @@ public class TableDraftClassic implements TableDraft{ @Override public int getSecondsRemainingForPick() throws IllegalStateException { - if (draftTimer == null || timerStartTime == 0) { - throw new IllegalStateException("No timer set."); + readLock.lock(); + try { + if (draftTimer == null || timerStartTime == 0) { + throw new IllegalStateException("No timer set."); + } + int elapsedTime = (int) ((System.currentTimeMillis() - timerStartTime) / 1000); // Convert to seconds + return timerDuration - elapsedTime; + } finally { + readLock.unlock(); } - int elapsedTime = (int) ((System.currentTimeMillis() - timerStartTime) / 1000); // Convert to seconds - return timerDuration - elapsedTime; } private void forcePlayerDeclares() { @@ -314,42 +344,62 @@ public class TableDraftClassic implements TableDraft{ @Override public void chooseCard(DraftPlayer who, String what) { - // Is player at this table - if (!players.contains(who)) { - return; - } + writeLock.lock(); + try { + // Is player at this table + if (!players.contains(who)) { + return; + } - // Is chosen card in his booster - if (!assignedBoosters.get(who).getCardsInPack().contains(what)) { - return; - } + // Is chosen card in his booster + if (!assignedBoosters.get(who).getCardsInPack().contains(what)) { + return; + } - chosenCards.put(who, what); - missedPicksInARow.put(who, 0); + chosenCards.put(who, what); + missedPicksInARow.put(who, 0); - //Check if all players chose - if (!(who instanceof DraftBot)) { - advanceDraft(); + //Check if all players chose + if (!(who instanceof DraftBot)) { + advanceDraft(); + } + } finally { + writeLock.unlock(); } } @Override public boolean isFinished() { - return areBoostersEmpty() && currentRound == rounds; + readLock.lock(); + try { + return areBoostersEmpty() && currentRound == rounds; + } finally { + readLock.unlock(); + } } @Override public DraftPlayer getPlayer(String name) { - return players.stream().filter(draftPlayer -> draftPlayer.getName().equals(name)).findFirst().orElse(null); + readLock.lock(); + try { + return players.stream().filter(draftPlayer -> draftPlayer.getName().equals(name)).findFirst().orElse(null); + } finally { + readLock.unlock(); + } } @Override public DraftPlayer registerPlayer(String name) { - // Only before draft starts - if (currentRound != 0 || name == null) { - return null; + writeLock.lock(); + try { + // Only before draft starts + if (currentRound != 0 || name == null) { + return null; + } + return registerPlayer(name, false); + } finally { + writeLock.unlock(); } - return registerPlayer(name, false); } private DraftPlayer registerPlayer(String name, boolean bot) { @@ -369,37 +419,131 @@ public class TableDraftClassic implements TableDraft{ @Override public boolean removePlayer(String name) { - // Only before draft starts - if (currentRound != 0) { - return false; - } - - DraftPlayer toRemove = null; - for (DraftPlayer player : players) { - if (player.getName().equals(name)) { - toRemove = player; + writeLock.lock(); + try { + // Only before draft starts + if (currentRound != 0) { + return false; } + + DraftPlayer toRemove = null; + for (DraftPlayer player : players) { + if (player.getName().equals(name)) { + toRemove = player; + } + } + if (players.remove(toRemove)) { + missedPicksInARow.remove(toRemove); + return true; + } + return false; + } finally { + writeLock.unlock(); } - if (players.remove(toRemove)) { - missedPicksInARow.remove(toRemove); - return true; - } - return false; } @Override public CardCollection getPickedCards(DraftPlayer draftPlayer) { - if (draftPlayer instanceof DraftBot) { - return botCollections.get(((DraftBot) draftPlayer)); - } + readLock.lock(); + try { + if (draftPlayer instanceof DraftBot) { + return botCollections.get(((DraftBot) draftPlayer)); + } - return collectionsManager.getPlayerCollection(draftPlayer.getName(), collectionType.getCode()); + return collectionsManager.getPlayerCollection(draftPlayer.getName(), collectionType.getCode()); + } finally { + readLock.unlock(); + } } @Override - public TableStatus getTableStatus() { - List statuses = new ArrayList<>(); - players.forEach(draftPlayer -> statuses.add(new PlayerStatus(draftPlayer.getName(), chosenCards.containsKey(draftPlayer)))); - return new TableStatus(statuses, currentRound % 2 == 1, currentRound, rounds); // Alternate pick order with each booster + public Document getDocument(DraftPlayer draftPlayer, LotroCardBlueprintLibrary cardLibrary, LotroFormatLibrary formatLibrary) throws ParserConfigurationException { + readLock.lock(); + try { + DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); + DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); + + Document doc = documentBuilder.newDocument(); + + Element rootElement = doc.createElement("draftStatus"); + doc.appendChild(rootElement); + + appendPlayersInfo(doc, rootElement); + appendPickedCards(doc, rootElement, getPickedCards(draftPlayer), cardLibrary, formatLibrary); + appendAvailablePicks(doc, rootElement, getCardsToPickFrom(draftPlayer), getChosenCard(draftPlayer)); + try { + appendTimeRemaining(doc, rootElement, getSecondsRemainingForPick()); + } catch (IllegalStateException ignore) { + // No timer + } + appendRoundsInfo(doc, rootElement); + + return doc; + } finally { + readLock.unlock(); + } + } + + private void appendPlayersInfo(Document doc, Element rootElement) { + players.forEach(draftPlayer -> { + Element playerElement = doc.createElement("player"); + playerElement.setAttribute("name", draftPlayer.getName()); + playerElement.setAttribute("hasChosen", String.valueOf(chosenCards.containsKey(draftPlayer))); + rootElement.appendChild(playerElement); + }); + + Element pickOrder = doc.createElement("pickOrderAscending"); + pickOrder.setAttribute("value", String.valueOf(currentRound % 2 == 1)); // Alternate pick order with each booster + rootElement.appendChild(pickOrder); + } + + + private void appendTimeRemaining(Document doc, Element rootElement, int timeRemaining) { + Element time = doc.createElement("timeRemaining"); + time.setAttribute("value", "" + timeRemaining); + rootElement.appendChild(time); + } + + private void appendRoundsInfo(Document doc, Element rootElement) { + Element time = doc.createElement("roundsInfo"); + time.setAttribute("currentRound", "" + currentRound); + time.setAttribute("roundsTotal", "" + rounds); + rootElement.appendChild(time); + } + + private void appendPickedCards(Document doc, Element rootElement, CardCollection toAppend, LotroCardBlueprintLibrary cardLibrary, LotroFormatLibrary formatLibrary) { + if (toAppend == null) { + return; + } + + SortAndFilterCards sortAndFilterCards = new SortAndFilterCards(); + List pickedCards = sortAndFilterCards.process( + "sort:cardType,culture,name", + toAppend.getAll(), + cardLibrary, + formatLibrary + ); + for (CardCollection.Item item : pickedCards) { + Element pickedCard = doc.createElement("pickedCard"); + pickedCard.setAttribute("blueprintId", item.getBlueprintId()); + pickedCard.setAttribute("count", String.valueOf(item.getCount())); + rootElement.appendChild(pickedCard); + } + } + + private void appendAvailablePicks(Document doc, Element rootElement, List availablePicks, String chosenCard) { + if (availablePicks == null || availablePicks.isEmpty()) { + return; + } + + for (String availableChoice : availablePicks) { + Element availablePick = doc.createElement("availablePick"); + availablePick.setAttribute("id", availableChoice); + availablePick.setAttribute("blueprintId", availableChoice); + if (chosenCard != null && chosenCard.equals(availableChoice)) { + availablePick.setAttribute("chosen", "true"); + } + rootElement.appendChild(availablePick); + } } }