Added timestamps to all recorded replay events. Added a readout of the exact decks used to the end of replays.
This commit is contained in:
@@ -3,6 +3,7 @@ package com.gempukku.lotro.game.state;
|
||||
import com.gempukku.lotro.common.Zone;
|
||||
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
|
||||
import com.gempukku.lotro.logic.timing.GameStats;
|
||||
import com.gempukku.lotro.logic.vo.LotroDeck;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
@@ -14,6 +15,7 @@ public class EventSerializer {
|
||||
public Node serializeEvent(Document doc, GameEvent gameEvent) {
|
||||
Element eventElem = doc.createElement("ge");
|
||||
eventElem.setAttribute("type", gameEvent.getType().getCode());
|
||||
eventElem.setAttribute("timestamp", gameEvent.getTimestamp().toString());
|
||||
|
||||
if (gameEvent.getBlueprintId() != null)
|
||||
eventElem.setAttribute("blueprintId", gameEvent.getBlueprintId());
|
||||
@@ -45,6 +47,8 @@ public class EventSerializer {
|
||||
serializeGameStats(doc, eventElem, gameEvent.getGameStats());
|
||||
if (gameEvent.getAwaitingDecision() != null)
|
||||
serializeDecision(doc, eventElem, gameEvent.getAwaitingDecision());
|
||||
if (gameEvent.getDecks() != null)
|
||||
serializeDecks(doc, eventElem, gameEvent.getDecks());
|
||||
|
||||
return eventElem;
|
||||
}
|
||||
@@ -74,6 +78,23 @@ public class EventSerializer {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
private void serializeDecks(Document document, Element eventElem, Map<String, LotroDeck> decks) {
|
||||
for(var pair : decks.entrySet()) {
|
||||
String player = pair.getKey();
|
||||
var deck = pair.getValue();
|
||||
|
||||
var deckElement = document.createElement("deckReadout");
|
||||
deckElement.setAttribute("playerId", player);
|
||||
deckElement.setAttribute("name", deck.getDeckName());
|
||||
deckElement.setAttribute("rb", deck.getRingBearer());
|
||||
deckElement.setAttribute("ring", deck.getRing());
|
||||
deckElement.setAttribute("sites", String.join(",", deck.getSites()));
|
||||
deckElement.setAttribute("deck", String.join(",", deck.getAdventureCards()));
|
||||
|
||||
eventElem.appendChild(deckElement);
|
||||
}
|
||||
}
|
||||
|
||||
private void serializeDecision(Document doc, Element eventElem, AwaitingDecision decision) {
|
||||
eventElem.setAttribute("id", String.valueOf(decision.getAwaitingDecisionId()));
|
||||
eventElem.setAttribute("decisionType", decision.getDecisionType().name());
|
||||
|
||||
@@ -3,14 +3,16 @@ package com.gempukku.lotro.game.state;
|
||||
import com.gempukku.lotro.common.Token;
|
||||
import com.gempukku.lotro.communication.GameStateListener;
|
||||
import com.gempukku.lotro.game.PhysicalCard;
|
||||
import static com.gempukku.lotro.game.state.GameEvent.Type.*;
|
||||
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
|
||||
import com.gempukku.lotro.logic.timing.GameStats;
|
||||
import com.gempukku.lotro.logic.vo.LotroDeck;
|
||||
import com.gempukku.polling.LongPollableResource;
|
||||
import com.gempukku.polling.WaitingRequest;
|
||||
|
||||
import java.util.*;
|
||||
|
||||
import static com.gempukku.lotro.game.state.GameEvent.Type.*;
|
||||
|
||||
public class GameCommunicationChannel implements GameStateListener, LongPollableResource {
|
||||
private List<GameEvent> _events = Collections.synchronizedList(new LinkedList<>());
|
||||
private final String _self;
|
||||
@@ -186,6 +188,10 @@ public class GameCommunicationChannel implements GameStateListener, LongPollable
|
||||
appendEvent(new GameEvent(DECISION).awaitingDecision(decision).participantId(playerId));
|
||||
}
|
||||
|
||||
public void deckReadout(Map<String, LotroDeck> decks) {
|
||||
appendEvent(new GameEvent(DECK_READOUT).decks(decks));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sendWarning(String playerId, String warning) {
|
||||
if (playerId.equals(_self))
|
||||
|
||||
@@ -6,8 +6,12 @@ import com.gempukku.lotro.common.Zone;
|
||||
import com.gempukku.lotro.game.PhysicalCard;
|
||||
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
|
||||
import com.gempukku.lotro.logic.timing.GameStats;
|
||||
import com.gempukku.lotro.logic.vo.LotroDeck;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class GameEvent {
|
||||
public enum Type {
|
||||
@@ -19,6 +23,7 @@ public class GameEvent {
|
||||
ADD_TOKENS("AT"), REMOVE_TOKENS("RT"),
|
||||
SEND_MESSAGE("M"), SEND_WARNING("W"),
|
||||
GAME_STATS("GS"),
|
||||
CHAT_MESSAGE("CM"), DECK_READOUT("DR"),
|
||||
CARD_AFFECTED_BY_CARD("CAC"), SHOW_CARD_ON_SCREEN("EP"), FLASH_CARD_IN_PLAY("CA"), DECISION("D");
|
||||
|
||||
private final String code;
|
||||
@@ -47,13 +52,18 @@ public class GameEvent {
|
||||
private Integer _count;
|
||||
private Token _token;
|
||||
private int[] _otherCardIds;
|
||||
private Map<String, LotroDeck> _decks;
|
||||
private GameStats _gameStats;
|
||||
private AwaitingDecision _awaitingDecision;
|
||||
private LocalDateTime _timestamp;
|
||||
|
||||
public GameEvent(Type type) {
|
||||
_type = type;
|
||||
_timestamp = LocalDateTime.now(ZoneOffset.UTC);
|
||||
}
|
||||
|
||||
public LocalDateTime getTimestamp() { return _timestamp; }
|
||||
|
||||
public Integer getIndex() {
|
||||
return _index;
|
||||
}
|
||||
@@ -130,6 +140,15 @@ public class GameEvent {
|
||||
return this;
|
||||
}
|
||||
|
||||
public Map<String, LotroDeck> getDecks() {
|
||||
return _decks;
|
||||
}
|
||||
|
||||
public GameEvent decks(Map<String, LotroDeck> decks) {
|
||||
_decks = decks;
|
||||
return this;
|
||||
}
|
||||
|
||||
public String getParticipantId() {
|
||||
return _participantId;
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import com.gempukku.lotro.common.AppConfig;
|
||||
import com.gempukku.lotro.game.state.EventSerializer;
|
||||
import com.gempukku.lotro.game.state.GameCommunicationChannel;
|
||||
import com.gempukku.lotro.game.state.GameEvent;
|
||||
import com.gempukku.lotro.logic.vo.LotroDeck;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
|
||||
@@ -49,18 +50,24 @@ public class GameRecorder {
|
||||
return new InflaterInputStream(new FileInputStream(file));
|
||||
}
|
||||
|
||||
public GameRecordingInProgress recordGame(LotroGameMediator lotroGame, LotroFormat format, final String tournament, final Map<String, String> deckNames) {
|
||||
public GameRecordingInProgress recordGame(LotroGameMediator lotroGame, LotroFormat format, final String tournament, final Map<String, LotroDeck> decks) {
|
||||
final Date startData = new Date();
|
||||
final Map<String, GameCommunicationChannel> recordingChannels = new HashMap<>();
|
||||
for (String playerId : lotroGame.getPlayersPlaying()) {
|
||||
GameCommunicationChannel recordChannel = new GameCommunicationChannel(playerId, 0);
|
||||
var recordChannel = new GameCommunicationChannel(playerId, 0);
|
||||
lotroGame.addGameStateListener(playerId, recordChannel);
|
||||
recordingChannels.put(playerId, recordChannel);
|
||||
}
|
||||
|
||||
return (winner, winReason, loser, loseReason) -> {
|
||||
Map<String, String> playerRecordingId = saveRecordedChannels(recordingChannels);
|
||||
_gameHistoryService.addGameHistory(winner, loser, winReason, loseReason, playerRecordingId.get(winner), playerRecordingId.get(loser), format.getName(), tournament, deckNames.get(winner), deckNames.get(loser), startData, new Date());
|
||||
for(var comm : recordingChannels.values()) {
|
||||
comm.deckReadout(decks);
|
||||
}
|
||||
|
||||
var playerRecordingId = saveRecordedChannels(recordingChannels);
|
||||
_gameHistoryService.addGameHistory(winner, loser, winReason, loseReason,
|
||||
playerRecordingId.get(winner), playerRecordingId.get(loser), format.getName(),
|
||||
tournament, decks.get(winner).getDeckName(), decks.get(loser).getDeckName(), startData, new Date());
|
||||
|
||||
if(format.isPlaytest())
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ public class LotroGameMediator {
|
||||
private final Map<String, Integer> _playerClocks = new HashMap<>();
|
||||
private final Map<String, Long> _decisionQuerySentTimes = new HashMap<>();
|
||||
private final Set<String> _playersPlaying = new HashSet<>();
|
||||
private final Map<String, LotroDeck> _playerDecks = new HashMap<>();
|
||||
|
||||
private final String _gameId;
|
||||
private final int _maxSecondsForGamePerPlayer;
|
||||
@@ -54,17 +55,15 @@ public class LotroGameMediator {
|
||||
if (participants.length < 1)
|
||||
throw new IllegalArgumentException("Game can't have less than one participant");
|
||||
|
||||
Map<String, LotroDeck> decks = new HashMap<>();
|
||||
|
||||
for (LotroGameParticipant participant : participants) {
|
||||
String participantId = participant.getPlayerId();
|
||||
decks.put(participantId, participant.getDeck());
|
||||
_playerDecks.put(participantId, participant.getDeck());
|
||||
_playerClocks.put(participantId, 0);
|
||||
_playersPlaying.add(participantId);
|
||||
}
|
||||
|
||||
_userFeedback = new DefaultUserFeedback();
|
||||
_lotroGame = new DefaultLotroGame(lotroFormat, decks, _userFeedback, library);
|
||||
_lotroGame = new DefaultLotroGame(lotroFormat, _playerDecks, _userFeedback, library);
|
||||
_userFeedback.setGame(_lotroGame);
|
||||
}
|
||||
|
||||
@@ -191,27 +190,27 @@ public class LotroGameMediator {
|
||||
PhysicalCard target = card.getAttachedTo();
|
||||
int twilightCost = _lotroGame.getModifiersQuerying().getTwilightCost(_lotroGame, card, target, 0, false);
|
||||
sb.append("<br><b>Twilight cost:</b> " + twilightCost);
|
||||
} catch (UnsupportedOperationException exp) {
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
try {
|
||||
int strength = _lotroGame.getModifiersQuerying().getStrength(_lotroGame, card);
|
||||
sb.append("<br><b>Strength:</b> " + strength);
|
||||
} catch (UnsupportedOperationException exp) {
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
try {
|
||||
int vitality = _lotroGame.getModifiersQuerying().getVitality(_lotroGame, card);
|
||||
sb.append("<br><b>Vitality:</b> " + vitality);
|
||||
} catch (UnsupportedOperationException exp) {
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
try {
|
||||
int resistance = _lotroGame.getModifiersQuerying().getResistance(_lotroGame, card);
|
||||
sb.append("<br><b>Resistance:</b> " + resistance);
|
||||
} catch (UnsupportedOperationException exp) {
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
try {
|
||||
int siteNumber = _lotroGame.getModifiersQuerying().getMinionSiteNumber(_lotroGame, card);
|
||||
sb.append("<br><b>Site number:</b> " + siteNumber);
|
||||
} catch (UnsupportedOperationException exp) {
|
||||
} catch (UnsupportedOperationException ignored) {
|
||||
}
|
||||
|
||||
StringBuilder keywords = new StringBuilder();
|
||||
@@ -354,6 +353,12 @@ public class LotroGameMediator {
|
||||
}
|
||||
}
|
||||
|
||||
public void readoutParticipantDecks() {
|
||||
for(var comms : _communicationChannels.values()) {
|
||||
comms.deckReadout(_playerDecks);
|
||||
}
|
||||
}
|
||||
|
||||
public GameCommunicationChannel getCommunicationChannel(Player player, int channelNumber) throws PrivateInformationException, SubscriptionConflictException, SubscriptionExpiredException {
|
||||
String playerName = player.getName();
|
||||
if (!player.hasType(Player.Type.ADMIN) && !_allowSpectators && !_playersPlaying.contains(playerName))
|
||||
|
||||
@@ -104,46 +104,47 @@ public class LotroServer extends AbstractServer {
|
||||
gameSettings.getMaxSecondsPerPlayer(), gameSettings.getMaxSecondsPerDecision(),
|
||||
gameSettings.getLeague() != null || !gameSettings.isCompetitive(), !gameSettings.isCompetitive(), gameSettings.isPrivateGame());
|
||||
lotroGameMediator.addGameResultListener(
|
||||
new GameResultListener() {
|
||||
@Override
|
||||
public void gameFinished(String winnerPlayerId, String winReason, Map<String, String> loserPlayerIdsWithReasons) {
|
||||
_finishedGamesTime.put(gameId, new Date());
|
||||
}
|
||||
new GameResultListener() {
|
||||
@Override
|
||||
public void gameFinished(String winnerPlayerId, String winReason, Map<String, String> loserPlayerIdsWithReasons) {
|
||||
_finishedGamesTime.put(gameId, new Date());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gameCancelled() {
|
||||
_finishedGamesTime.put(gameId, new Date());
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void gameCancelled() {
|
||||
_finishedGamesTime.put(gameId, new Date());
|
||||
}
|
||||
});
|
||||
lotroGameMediator.sendMessageToPlayers("You're starting a game of " + gameSettings.getLotroFormat().getName());
|
||||
|
||||
StringBuilder players = new StringBuilder();
|
||||
Map<String, String> deckNames = new HashMap<>();
|
||||
Map<String, LotroDeck> decks = new HashMap<>();
|
||||
for (LotroGameParticipant participant : participants) {
|
||||
deckNames.put(participant.getPlayerId(), participant.getDeck().getDeckName());
|
||||
if (players.length() > 0)
|
||||
players.append(", ");
|
||||
players.append(participant.getPlayerId());
|
||||
decks.put(participant.getPlayerId(), participant.getDeck());
|
||||
}
|
||||
|
||||
lotroGameMediator.sendMessageToPlayers("Players in the game are: " + players.toString());
|
||||
|
||||
final GameRecorder.GameRecordingInProgress gameRecordingInProgress = _gameRecorder.recordGame(lotroGameMediator, gameSettings.getLotroFormat(), tournamentName, deckNames);
|
||||
final var gameRecordingInProgress = _gameRecorder.recordGame(lotroGameMediator, gameSettings.getLotroFormat(), tournamentName, decks);
|
||||
lotroGameMediator.addGameResultListener(
|
||||
new GameResultListener() {
|
||||
@Override
|
||||
public void gameFinished(String winnerPlayerId, String winReason, Map<String, String> loserPlayerIdsWithReasons) {
|
||||
final Map.Entry<String, String> loserEntry = loserPlayerIdsWithReasons.entrySet().iterator().next();
|
||||
new GameResultListener() {
|
||||
@Override
|
||||
public void gameFinished(String winnerPlayerId, String winReason, Map<String, String> loserPlayerIdsWithReasons) {
|
||||
final var loserEntry = loserPlayerIdsWithReasons.entrySet().iterator().next();
|
||||
|
||||
|
||||
gameRecordingInProgress.finishRecording(winnerPlayerId, winReason, loserEntry.getKey(), loserEntry.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gameCancelled() {
|
||||
gameRecordingInProgress.finishRecording(participants[0].getPlayerId(), "Game cancelled due to error", participants[1].getPlayerId(), "Game cancelled due to error");
|
||||
}
|
||||
//potentially this is where to kick off any "reveal deck" events
|
||||
//lotroGameMediator.readoutParticipantDecks();
|
||||
gameRecordingInProgress.finishRecording(winnerPlayerId, winReason, loserEntry.getKey(), loserEntry.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gameCancelled() {
|
||||
gameRecordingInProgress.finishRecording(participants[0].getPlayerId(), "Game cancelled due to error", participants[1].getPlayerId(), "Game cancelled due to error");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
_runningGames.put(gameId, lotroGameMediator);
|
||||
|
||||
Reference in New Issue
Block a user