diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ReplayRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ReplayRequestHandler.java index 5b74e18af..abc87b6ef 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ReplayRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/ReplayRequestHandler.java @@ -2,23 +2,36 @@ package com.gempukku.lotro.async.handler; import com.gempukku.lotro.async.HttpProcessingException; import com.gempukku.lotro.async.ResponseWriter; -import com.gempukku.lotro.game.GameRecorder; +import com.gempukku.lotro.collection.DeckRenderer; +import com.gempukku.lotro.common.JSONDefs; +import com.gempukku.lotro.game.*; +import com.gempukku.lotro.game.formats.LotroFormatLibrary; +import com.gempukku.lotro.hall.GameTimer; +import com.gempukku.lotro.logic.vo.LotroDeck; +import com.gempukku.lotro.tournament.TournamentService; +import com.gempukku.util.JsonUtils; import io.netty.handler.codec.http.HttpMethod; import io.netty.handler.codec.http.HttpRequest; +import io.netty.handler.codec.http.multipart.HttpPostRequestDecoder; import io.netty.util.AsciiString; import org.apache.log4j.Logger; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.lang.module.FindException; import java.lang.reflect.Type; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.stream.Stream; import static io.netty.handler.codec.http.HttpHeaderNames.CONTENT_TYPE; public class ReplayRequestHandler extends LotroServerRequestHandler implements UriRequestHandler { private final GameRecorder _gameRecorder; + private final DeckRenderer _deckRenderer; private static final Logger _log = Logger.getLogger(ReplayRequestHandler.class); @@ -26,43 +39,84 @@ public class ReplayRequestHandler extends LotroServerRequestHandler implements U super(context); _gameRecorder = extractObject(context, GameRecorder.class); + _deckRenderer = new DeckRenderer(extractObject(context, LotroCardBlueprintLibrary.class), + extractObject(context, LotroFormatLibrary.class), new SortAndFilterCards()); } @Override public void handleRequest(String uri, HttpRequest request, Map context, ResponseWriter responseWriter, String remoteIp) throws Exception { - if (uri.startsWith("/") && request.method() == HttpMethod.GET) { - String replayId = uri.substring(1); - - if (!replayId.contains("$")) - throw new HttpProcessingException(404); - if (replayId.contains(".")) - throw new HttpProcessingException(404); - - final String[] split = replayId.split("\\$"); - if (split.length != 2) - throw new HttpProcessingException(404); - - ByteArrayOutputStream baos = new ByteArrayOutputStream(); - - final InputStream recordedGame = _gameRecorder.getRecordedGame(split[0], split[1]); - try (recordedGame) { - if (recordedGame == null) - throw new HttpProcessingException(404); - byte[] bytes = new byte[1024]; - int count; - while ((count = recordedGame.read(bytes)) != -1) - baos.write(bytes, 0, count); - } catch (IOException exp) { - _log.error("Error 404 response for " + request.uri(), exp); - throw new HttpProcessingException(404); - } - - Map headers = new HashMap<>(); - headers.put(CONTENT_TYPE, "application/html; charset=UTF-8"); - - responseWriter.writeByteResponse(baos.toByteArray(), headers); + if (uri.startsWith("/summarydecks")) { + int length = "/summarydecks/".length(); + ServeSummaryDecks(uri.substring(length, uri.length()), request, responseWriter); + } + else if (uri.startsWith("/") && request.method() == HttpMethod.GET) { + ServeReplayXML(uri.substring(1), request, responseWriter); } else { throw new HttpProcessingException(404); } } + + private void ServeSummaryDecks(String replayId, HttpRequest request, ResponseWriter responseWriter) throws HttpProcessingException { + validateAdmin(request); + + try { + var summaryFile = _gameRecorder.getRecordedSummary(replayId); + String json = new String(summaryFile.readAllBytes()); + var summary = JsonUtils.Convert(json, ReplayMetadata.class); + + var decks = _deckRenderer.ExtractDecksFromReplaySummary(summary); + + var fragments = new ArrayList(); + for(var deck : decks) { + fragments.add(_deckRenderer.convertDeckToForumFragment(deck, deck.getNotes())); + } + + responseWriter.writeHtmlResponse(_deckRenderer.AddDeckReadoutHeaderAndFooter(fragments)); + } + catch(IOException ex) { + throw new HttpProcessingException(404); + } catch (CardNotFoundException e) { + throw new HttpProcessingException(500); + } + + + } + + private void ServeReplayXML(String replayId, HttpRequest request, ResponseWriter responseWriter) throws Exception { + if (!replayId.contains("$")) + throw new HttpProcessingException(404); + if (replayId.contains(".")) + throw new HttpProcessingException(404); + + final String[] split = replayId.split("\\$"); + if (split.length != 2) + throw new HttpProcessingException(404); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + + final InputStream recordedGame = _gameRecorder.getRecordedGame(split[0], split[1]); + try (recordedGame) { + if (recordedGame == null) + throw new HttpProcessingException(404); + byte[] bytes = new byte[1024]; + int count; + while ((count = recordedGame.read(bytes)) != -1) + baos.write(bytes, 0, count); + } catch (IOException exp) { + _log.error("Error 404 response for " + request.uri(), exp); + throw new HttpProcessingException(404); + } + + Map headers = new HashMap<>(); + headers.put(CONTENT_TYPE, "application/html; charset=UTF-8"); + + responseWriter.writeByteResponse(baos.toByteArray(), headers); + } + + private void validateAdmin(HttpRequest request) throws HttpProcessingException { + Player player = getResourceOwnerSafely(request, null); + + if (!player.hasType(Player.Type.ADMIN)) + throw new HttpProcessingException(403); + } } diff --git a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TournamentRequestHandler.java b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TournamentRequestHandler.java index 5ab532564..65ad23ebe 100644 --- a/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TournamentRequestHandler.java +++ b/gemp-lotr/gemp-lotr-async/src/main/java/com/gempukku/lotro/async/handler/TournamentRequestHandler.java @@ -2,6 +2,7 @@ package com.gempukku.lotro.async.handler; import com.gempukku.lotro.async.HttpProcessingException; import com.gempukku.lotro.async.ResponseWriter; +import com.gempukku.lotro.collection.DeckRenderer; import com.gempukku.lotro.competitive.PlayerStanding; import com.gempukku.lotro.game.CardCollection; import com.gempukku.lotro.game.DefaultCardCollection; @@ -36,6 +37,7 @@ public class TournamentRequestHandler extends LotroServerRequestHandler implemen private final LotroFormatLibrary _formatLibrary; private final LotroCardBlueprintLibrary _library; private final SortAndFilterCards _sortAndFilterCards; + private final DeckRenderer _deckRenderer; private static final Logger _log = Logger.getLogger(TournamentRequestHandler.class); @@ -46,6 +48,8 @@ public class TournamentRequestHandler extends LotroServerRequestHandler implemen _formatLibrary = extractObject(context, LotroFormatLibrary.class); _library = extractObject(context, LotroCardBlueprintLibrary.class); _sortAndFilterCards = new SortAndFilterCards(); + + _deckRenderer = new DeckRenderer(_library, _formatLibrary, _sortAndFilterCards); } @Override @@ -117,41 +121,9 @@ public class TournamentRequestHandler extends LotroServerRequestHandler implemen if (deck == null) throw new HttpProcessingException(404); - StringBuilder result = new StringBuilder(); - result.append(""); - result.append("

" + StringEscapeUtils.escapeHtml(deck.getDeckName()) + "

"); - result.append("

by " + playerName + "

"); - String ringBearer = deck.getRingBearer(); - if (ringBearer != null) - result.append("Ring-bearer: " + GameUtils.getFullName(_library.getLotroCardBlueprint(ringBearer)) + "
"); - String ring = deck.getRing(); - if (ring != null) - result.append("Ring: " + GameUtils.getFullName(_library.getLotroCardBlueprint(ring)) + "
"); + String fragment = _deckRenderer.convertDeckToHTMLFragment(deck, playerName); - DefaultCardCollection deckCards = new DefaultCardCollection(); - for (String card : deck.getAdventureCards()) - deckCards.addItem(_library.getBaseBlueprintId(card), 1); - for (String site : deck.getSites()) - deckCards.addItem(_library.getBaseBlueprintId(site), 1); - - result.append("
"); - result.append("Adventure deck:
"); - for (CardCollection.Item item : _sortAndFilterCards.process("cardType:SITE sort:siteNumber,twilight", deckCards.getAll(), _library, _formatLibrary)) - result.append(GameUtils.getFullName(_library.getLotroCardBlueprint(item.getBlueprintId())) + "
"); - - result.append("
"); - result.append("Free Peoples Draw Deck:
"); - for (CardCollection.Item item : _sortAndFilterCards.process("side:FREE_PEOPLE sort:cardType,culture,name", deckCards.getAll(), _library, _formatLibrary)) - result.append(item.getCount() + "x " + GameUtils.getFullName(_library.getLotroCardBlueprint(item.getBlueprintId())) + "
"); - - result.append("
"); - result.append("Shadow Draw Deck:
"); - for (CardCollection.Item item : _sortAndFilterCards.process("side:SHADOW sort:cardType,culture,name", deckCards.getAll(), _library, _formatLibrary)) - result.append(item.getCount() + "x " + GameUtils.getFullName(_library.getLotroCardBlueprint(item.getBlueprintId())) + "
"); - - result.append(""); - - responseWriter.writeHtmlResponse(result.toString()); + responseWriter.writeHtmlResponse(_deckRenderer.AddDeckReadoutHeaderAndFooter(fragment)); } private void getTournamentReport(HttpRequest request, String tournamentId, String playerName, ResponseWriter responseWriter) throws Exception { @@ -162,7 +134,7 @@ public class TournamentRequestHandler extends LotroServerRequestHandler implemen if (tournament.getTournamentStage() != Tournament.Stage.FINISHED) throw new HttpProcessingException(403); - var result = tournament.produceReport(_library, _formatLibrary, _sortAndFilterCards); + var result = tournament.produceReport(_deckRenderer); responseWriter.writeHtmlResponse(result); } diff --git a/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/lotro/common/Names.java b/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/lotro/common/Names.java index 3a0ce07b5..ec2d3787b 100644 --- a/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/lotro/common/Names.java +++ b/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/lotro/common/Names.java @@ -30,4 +30,13 @@ public class Names { .replaceAll("_", "") .toLowerCase(); } + + public static String SanitizeDisplayName(String name) { + return Normalizer.normalize(name, Normalizer.Form.NFD) + .replaceAll("’", "'") + .replaceAll("‘", "'") + .replaceAll("”", "\"") + .replaceAll("“", "\"") + .replaceAll("\\p{M}", ""); + } } diff --git a/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/util/JsonUtils.java b/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/util/JsonUtils.java index 0e3edac9a..a5c667858 100644 --- a/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/util/JsonUtils.java +++ b/gemp-lotr/gemp-lotr-common/src/main/java/com/gempukku/util/JsonUtils.java @@ -28,6 +28,11 @@ public final class JsonUtils { return JSON.parseObject(json, clazz); } + public static T Convert(String jsonText, Class clazz) throws IOException { + String json = JsonValue.readHjson(jsonText).toString(); + return JSON.parseObject(json, clazz); + } + public static List ConvertArray(Reader reader, Class clazz) throws IOException { final String json = ReadJson(reader); try { diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/collection/DeckRenderer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/collection/DeckRenderer.java new file mode 100644 index 000000000..530914b29 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/collection/DeckRenderer.java @@ -0,0 +1,294 @@ +package com.gempukku.lotro.collection; + +import com.gempukku.lotro.common.Names; +import com.gempukku.lotro.competitive.PlayerStanding; +import com.gempukku.lotro.game.*; +import com.gempukku.lotro.game.formats.LotroFormatLibrary; +import com.gempukku.lotro.logic.GameUtils; +import com.gempukku.lotro.logic.vo.LotroDeck; +import org.apache.commons.lang.StringEscapeUtils; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +public class DeckRenderer { + + private final LotroCardBlueprintLibrary _bpLibrary; + private final LotroFormatLibrary _formatLibrary; + private final SortAndFilterCards _cardFilter; + + public DeckRenderer(LotroCardBlueprintLibrary library, LotroFormatLibrary formatLibrary, SortAndFilterCards cardfilter) { + _bpLibrary = library; + _cardFilter = cardfilter; + _formatLibrary = formatLibrary; + } + + + public List ExtractDecksFromReplaySummary(ReplayMetadata summary) { + var decks = new ArrayList(); + for(var deck : summary.Decks.values()) { + decks.add(ConvertMetadataDeckToLotroDeck(deck)); + } + return decks; + } + + public LotroDeck ConvertMetadataDeckToLotroDeck(ReplayMetadata.DeckMetadata recordedDeck) { + var deck = new LotroDeck(recordedDeck.DeckName); + deck.setNotes(recordedDeck.Owner); + deck.setTargetFormat(recordedDeck.TargetFormat); + deck.setRing(recordedDeck.Ring); + deck.setRingBearer(recordedDeck.RingBearer); + for(var site : recordedDeck.AdventureDeck) { + deck.addSite(site); + } + for(var card : recordedDeck.DrawDeck) { + deck.addCard(card); + } + return deck; + } + + public String AddDeckReadoutHeaderAndFooter(String fragment) { + return AddDeckReadoutHeaderAndFooter(Collections.singletonList(fragment)); + } + + public String AddDeckReadoutHeaderAndFooter(List fragments) { + String preamble = """ + + + """; + String divider = "
[hr]

"; + String postamble = ""; + return preamble + String.join(divider, fragments) + postamble; + + } + + public String convertDeckToHTMLFragment(LotroDeck deck, String author) throws CardNotFoundException { + return convertDeckToHTMLFragment(deck, author, new ArrayList<>()); + } + + public String convertDeckToHTMLFragment(LotroDeck deck, String author, List> replays) throws CardNotFoundException { + + if (deck == null) + return null; + + StringBuilder result = new StringBuilder(); + result.append("
") + .append("

").append(StringEscapeUtils.escapeHtml(deck.getDeckName())).append("

") + .append("

Format: ").append(StringEscapeUtils.escapeHtml(deck.getTargetFormat())).append("

"); + + if(author != null) { + result.append("

Author: ").append(StringEscapeUtils.escapeHtml(author)).append("

"); + } + + if(replays != null && replays.size() > 0) { + result.append("

Round Replays:

"); + List rounds = new ArrayList<>(); + + for(var replay : replays) { + if(replay.getValue().isBlank()) { + rounds.add(replay.getKey()); + } + else { + rounds.add("" + replay.getKey() + ""); + } + } + + result.append(String.join(" • ", rounds)); + } + + String ringBearer = deck.getRingBearer(); + if (ringBearer != null) { + result.append("Ring-bearer: ").append(generateCardTooltip(ringBearer)).append("
"); + } + String ring = deck.getRing(); + if (ring != null) { + result.append("Ring: ").append(generateCardTooltip(ring)).append("
"); + } + + DefaultCardCollection deckCards = new DefaultCardCollection(); + for (String card : deck.getAdventureCards()) { + deckCards.addItem(_bpLibrary.getBaseBlueprintId(card), 1); + } + for (String site : deck.getSites()) { + deckCards.addItem(_bpLibrary.getBaseBlueprintId(site), 1); + } + + result.append("
"); + result.append("Adventure deck:
"); + for (CardCollection.Item item : _cardFilter.process("cardType:SITE sort:siteNumber,twilight", deckCards.getAll(), + _bpLibrary, _formatLibrary)) { + result.append(generateCardTooltip(item)).append("
"); + } + + result.append("
"); + result.append("Free Peoples Draw Deck:
"); + for (CardCollection.Item item : _cardFilter.process("side:FREE_PEOPLE sort:cardType,culture,name", deckCards.getAll(), + _bpLibrary, _formatLibrary)) { + result.append(item.getCount()).append("x ").append(generateCardTooltip(item)).append("
"); + } + + result.append("
"); + result.append("Shadow Draw Deck:
"); + for (CardCollection.Item item : _cardFilter.process("side:SHADOW sort:cardType,culture,name", deckCards.getAll(), + _bpLibrary, _formatLibrary)) { + result.append(item.getCount()).append("x ").append(generateCardTooltip(item)).append("
"); + } + + result.append("

Notes


").append(deck.getNotes().replace("\n", "
")); + + result.append("
"); + + return result.toString(); + } + + public String convertDeckToForumFragment(LotroDeck deck, String author) throws CardNotFoundException { + return convertDeckToForumFragment(deck, author, new ArrayList<>()); + } + + public String convertDeckToForumFragment(LotroDeck deck, String author, List> replays) throws CardNotFoundException { + + if (deck == null) + return null; + + StringBuilder result = new StringBuilder(); + result.append("
") + .append("

").append(StringEscapeUtils.escapeHtml(deck.getDeckName())).append("

") + .append("

Format: ").append(StringEscapeUtils.escapeHtml(deck.getTargetFormat())).append("

"); + + if(author != null) { + result.append("

Author: ").append(StringEscapeUtils.escapeHtml(author)).append("

"); + } + + if(replays != null && replays.size() > 0) { + result.append("

[b]Round Replays: [/b]

"); + List rounds = new ArrayList<>(); + + for(var replay : replays) { + if(replay.getValue().isBlank()) { + rounds.add(replay.getKey()); + } + else { + rounds.add("[url=" + replay.getValue() + "]" + replay.getKey() + "[/url]"); + } + } + + result.append(String.join(" • ", rounds)); + } + + String ringBearer = deck.getRingBearer(); + if (ringBearer != null) { + result.append("[b]Ring-bearer:[/b] ").append(generateCardTooltip(ringBearer)).append("
"); + } + String ring = deck.getRing(); + if (ring != null) { + result.append("[b]Ring:[/b] ").append(generateCardTooltip(ring)).append("
"); + } + + DefaultCardCollection deckCards = new DefaultCardCollection(); + for (String card : deck.getAdventureCards()) { + deckCards.addItem(_bpLibrary.getBaseBlueprintId(card), 1); + } + for (String site : deck.getSites()) { + deckCards.addItem(_bpLibrary.getBaseBlueprintId(site), 1); + } + + result.append("
"); + result.append("[b]Adventure deck:[/b]
"); + for (CardCollection.Item item : _cardFilter.process("cardType:SITE sort:siteNumber,twilight", deckCards.getAll(), + _bpLibrary, _formatLibrary)) { + result.append(generateCardTooltip(item)).append("
"); + } + + result.append("
"); + result.append("[b]Free Peoples Draw Deck:[/b]
"); + for (CardCollection.Item item : _cardFilter.process("side:FREE_PEOPLE sort:cardType,culture,name", deckCards.getAll(), + _bpLibrary, _formatLibrary)) { + result.append(item.getCount()).append("x ").append(generateCardTooltip(item)).append("
"); + } + + result.append("
"); + result.append("[b]Shadow Draw Deck:[/b]
"); + for (CardCollection.Item item : _cardFilter.process("side:SHADOW sort:cardType,culture,name", deckCards.getAll(), + _bpLibrary, _formatLibrary)) { + result.append(item.getCount()).append("x ").append(generateCardTooltip(item)).append("
"); + } + + result.append("
"); + + return result.toString(); + } + + private String generateCardTooltip(CardCollection.Item item) throws CardNotFoundException { + return generateCardTooltip(_bpLibrary.getLotroCardBlueprint(item.getBlueprintId()), item.getBlueprintId()); + } + + private String generateCardTooltip(String bpid) throws CardNotFoundException { + return generateCardTooltip(_bpLibrary.getLotroCardBlueprint(bpid), bpid); + } + + private String generateCardTooltip(LotroCardBlueprint bp, String bpid) throws CardNotFoundException { + String[] parts = bpid.split("_"); + int setnum = Integer.parseInt(parts[0]); + String set = String.format("%02d", setnum); + String subset = "S"; + int version = 0; + if(setnum >= 50 && setnum <= 69) { + setnum -= 50; + set = String.format("%02d", setnum); + subset = "E"; + version = 1; + } + else if(setnum >= 70 && setnum <= 89) { + setnum -= 70; + set = String.format("%02d", setnum); + subset = "E"; + version = 1; + } + else if(setnum >= 100 && setnum <= 149) { + setnum -= 100; + set = "V" + setnum; + } + int cardnum = Integer.parseInt(parts[1].replace("*", "").replace("T", "")); + + String id = "LOTR-EN" + set + subset + String.format("%03d", cardnum) + "." + String.format("%01d", version); + String displayName = Names.SanitizeDisplayName(GameUtils.getFullName(bp)); + if(subset.equals("E")) { + displayName += " (Errata)"; + } + String result = "" + displayName + + ""; + + return result; + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameRecorder.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameRecorder.java index d6a37bb25..e96080ad6 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameRecorder.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameRecorder.java @@ -272,4 +272,18 @@ public class GameRecorder { return new InflaterInputStream(new FileInputStream(recordingFile)); } + + public InputStream getRecordedSummary(String recordId) throws IOException { + var history = _gameHistoryService.getGameHistory(recordId); + + if(history == null) + return null; + + File recordingFile = getSummaryFile(history); + + if (recordingFile == null || !recordingFile.exists() || !recordingFile.isFile()) + return null; + + return new FileInputStream(recordingFile); + } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ReplayMetadata.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ReplayMetadata.java index 52adaf80b..84e1b6526 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ReplayMetadata.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ReplayMetadata.java @@ -43,6 +43,9 @@ public class ReplayMetadata { public HashSet PlayedCards = new HashSet<>(); + public ReplayMetadata() { + + } public ReplayMetadata(DBDefs.GameHistory game, Map decks) { GameReplayInfo = game; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java index de7bceba3..3723c088a 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/tournament/DefaultTournament.java @@ -2,6 +2,7 @@ package com.gempukku.lotro.tournament; import com.gempukku.lotro.DateUtils; import com.gempukku.lotro.collection.CollectionsManager; +import com.gempukku.lotro.collection.DeckRenderer; import com.gempukku.lotro.competitive.BestOfOneStandingsProducer; import com.gempukku.lotro.competitive.PlayerStanding; import com.gempukku.lotro.db.vo.CollectionType; @@ -464,8 +465,12 @@ public class DefaultTournament implements Tournament { } } + private Map.Entry createEntry(String label, String url) { + return new AbstractMap.SimpleEntry<>(label, url); + } + @Override - public String produceReport(LotroCardBlueprintLibrary bpLibrary, LotroFormatLibrary formatLibrary, SortAndFilterCards cardFilter) throws CardNotFoundException { + public String produceReport(DeckRenderer renderer) throws CardNotFoundException { if(_tournamentReport != null) return _tournamentReport; @@ -495,8 +500,8 @@ public class DefaultTournament implements Tournament { } } - StringBuilder result = new StringBuilder(); - result.append("") + StringBuilder summary = new StringBuilder(); + summary.append("") .append("

").append(StringEscapeUtils.escapeHtml(_tournamentName)).append("

") .append("
    ") .append("
  • Format: ").append(_format).append("
  • ") @@ -506,19 +511,20 @@ public class DefaultTournament implements Tournament { .append("
  • End: ").append(DateUtils.FormatStandardDateTime(end)).append("
  • ") .append("



"); + var decks = new ArrayList(); + decks.add(summary.toString()); + for(var standing : getCurrentStandings()) { var playerName = standing.playerName(); - result.append("

by ").append(playerName).append("


") - .append("

Round Replays

"); - var rounds = new ArrayList(); + var rounds = new ArrayList>(); 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("[bye]"); + rounds.add(createEntry("[bye]", "")); continue; } @@ -527,7 +533,7 @@ public class DefaultTournament implements Tournament { .findFirst().orElse(null); if(match == null) { - rounds.add("[dropped]"); + rounds.add(createEntry("[dropped]", "")); continue; } @@ -541,67 +547,20 @@ public class DefaultTournament implements Tournament { replayId = game.lose_recording_id; } - rounds.add("Round " + i + ""); + String label = "Round " + i; + String url = "https://play.lotrtcgpc.net/gemp-lotr/game.html?replayId=" + + playerName.replace("_", "%5F") + "$" + replayId; + + rounds.add(createEntry(label, url)); } - result.append(String.join(" • ", rounds)); - - result.append("

Deck

"); - LotroDeck deck = _tournamentService.getPlayerDeck(_tournamentId, playerName, _format); - String ringBearer = deck.getRingBearer(); - if (ringBearer != null) { - result.append("Ring-bearer: ") - .append(GameUtils.getFullName(bpLibrary.getLotroCardBlueprint(ringBearer))) - .append("
"); - } + var fragment = renderer.convertDeckToForumFragment(deck, playerName, rounds); + decks.add(fragment); - String ring = deck.getRing(); - if (ring != null) { - result.append("Ring: ") - .append(GameUtils.getFullName(bpLibrary.getLotroCardBlueprint(ring))) - .append("
"); - } - - DefaultCardCollection deckCards = new DefaultCardCollection(); - for (String card : deck.getAdventureCards()) { - deckCards.addItem(bpLibrary.getBaseBlueprintId(card), 1); - } - for (String site : deck.getSites()) { - deckCards.addItem(bpLibrary.getBaseBlueprintId(site), 1); - } - - result.append("
"); - result.append("Adventure deck:
"); - for (CardCollection.Item item : cardFilter.process("cardType:SITE sort:siteNumber,twilight", deckCards.getAll(), bpLibrary, formatLibrary)) { - result.append(GameUtils.getFullName(bpLibrary.getLotroCardBlueprint(item.getBlueprintId()))) - .append("
"); - } - - result.append("
"); - result.append("Free Peoples Draw Deck:
"); - for (CardCollection.Item item : cardFilter.process("side:FREE_PEOPLE sort:cardType,culture,name", deckCards.getAll(), bpLibrary, formatLibrary)) { - result.append(item.getCount()).append("x ") - .append(GameUtils.getFullName(bpLibrary.getLotroCardBlueprint(item.getBlueprintId()))) - .append("
"); - } - - result.append("
"); - result.append("Shadow Draw Deck:
"); - for (CardCollection.Item item : cardFilter.process("side:SHADOW sort:cardType,culture,name", deckCards.getAll(), bpLibrary, formatLibrary)) { - result.append(item.getCount()).append("x ") - .append(GameUtils.getFullName(bpLibrary.getLotroCardBlueprint(item.getBlueprintId()))) - .append("
"); - } - - result.append("


"); } - result.append(""); - - return result.toString(); + return renderer.AddDeckReadoutHeaderAndFooter(decks); } } 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 6b8164389..2c8c00965 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 @@ -1,6 +1,7 @@ package com.gempukku.lotro.tournament; import com.gempukku.lotro.collection.CollectionsManager; +import com.gempukku.lotro.collection.DeckRenderer; import com.gempukku.lotro.competitive.PlayerStanding; import com.gempukku.lotro.db.vo.CollectionType; import com.gempukku.lotro.draft.Draft; @@ -92,5 +93,5 @@ public interface Tournament { public boolean isPlayerInCompetition(String player); - public String produceReport(LotroCardBlueprintLibrary bpLibrary, LotroFormatLibrary formatLibrary, SortAndFilterCards cardFilter) throws CardNotFoundException; + public String produceReport(DeckRenderer renderer) throws CardNotFoundException; }