Adding game history.

This commit is contained in:
marcins78@gmail.com
2011-10-26 15:39:37 +00:00
parent afd800d46c
commit 26c5732e67
10 changed files with 428 additions and 121 deletions

View File

@@ -36,7 +36,6 @@ public class DefaultLotroGame implements LotroGame {
private Set<String> _allPlayers;
private String _winnerPlayerId;
private String _winReason;
private Map<String, String> _losers = new HashMap<String, String>();
private Set<GameResultListener> _gameResultListeners = new HashSet<GameResultListener>();
@@ -107,15 +106,25 @@ public class DefaultLotroGame implements LotroGame {
@Override
public void playerWon(String playerId, String reason) {
_winnerPlayerId = playerId;
_winReason = reason;
// Any remaining players have lost
Set<String> losers = new HashSet<String>(_allPlayers);
losers.removeAll(losers);
losers.remove(playerId);
for (String loser : losers)
_losers.put(loser, "Other player won");
gameWon(playerId, reason);
}
private void gameWon(String winner, String reason) {
_winnerPlayerId = winner;
if (_gameState != null)
_gameState.sendMessage(_winnerPlayerId + " is the winner due to: " + reason);
for (GameResultListener gameResultListener : _gameResultListeners) {
Set<String> losers = new HashSet<String>(_allPlayers);
losers.remove(_winnerPlayerId);
gameResultListener.gameFinished(_winnerPlayerId, losers, reason);
}
for (GameResultListener gameResultListener : _gameResultListeners)
gameResultListener.gameFinished(_winnerPlayerId, reason, _losers);
}
@Override
@@ -129,7 +138,7 @@ public class DefaultLotroGame implements LotroGame {
if (_losers.size() + 1 == _allPlayers.size()) {
List<String> allPlayers = new LinkedList<String>(_allPlayers);
allPlayers.removeAll(_losers.keySet());
playerWon(allPlayers.get(0), "Last remaining player in game");
gameWon(allPlayers.get(0), "Last remaining player in game");
}
}
}

View File

@@ -1,7 +1,7 @@
package com.gempukku.lotro.logic.timing;
import java.util.Set;
import java.util.Map;
public interface GameResultListener {
public void gameFinished(String winnerPlayerId, Set<String> loserPlayerIds, String reason);
public void gameFinished(String winnerPlayerId, String winReason, Map<String, String> loserPlayerIdsWithReasons);
}

View File

@@ -41,7 +41,7 @@ public class RegroupGameProcess implements GameProcess {
public void process() {
if (_game.getGameState().getCurrentSiteNumber() == 9
&& _game.getModifiersQuerying().hasFlagActive(ModifierFlag.WIN_CHECK_AFTER_SHADOW_RECONCILE)) {
_game.playerWon(_game.getGameState().getCurrentPlayerId(), "Surviving to Regroup phase on site 9");
_game.playerWon(_game.getGameState().getCurrentPlayerId(), "Surviving to Shadow Reconcile on site 9");
}
}

View File

@@ -0,0 +1,116 @@
package com.gempukku.lotro.db;
import com.gempukku.lotro.db.vo.GameHistoryEntry;
import com.gempukku.lotro.db.vo.Player;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
public class GameHistoryDAO {
private DbAccess _dbAccess;
public GameHistoryDAO(DbAccess dbAccess) {
_dbAccess = dbAccess;
}
public void addGameHistory(String winner, String loser, String winReason, String loseReason, String winRecordingId, String loseRecordingId, Date startDate, Date endDate) {
try {
Connection connection = _dbAccess.getDataSource().getConnection();
try {
PreparedStatement statement = connection.prepareStatement("insert into game_history (winner, loser, win_reason, lose_reason, win_recording_id, lose_recording_id, start_date, end_date) values (?,?,?,?,?,?,?,?)");
try {
statement.setString(1, winner);
statement.setString(2, loser);
statement.setString(3, winReason);
statement.setString(4, loseReason);
statement.setString(5, winRecordingId);
statement.setString(6, loseRecordingId);
statement.setLong(7, startDate.getTime());
statement.setLong(8, endDate.getTime());
statement.execute();
} finally {
statement.close();
}
} finally {
connection.close();
}
} catch (SQLException exp) {
throw new RuntimeException("Unable to get count of player games", exp);
}
}
public List<GameHistoryEntry> getGameHistoryForPlayer(Player player, int start, int count) {
try {
Connection connection = _dbAccess.getDataSource().getConnection();
try {
PreparedStatement statement = connection.prepareStatement("select winner, loser, win_reason, lose_reason, win_recording_id, lose_recording_id, start_date, end_date from game_history where winner=? or loser=? order by end_date deck limit ?, ?");
try {
statement.setString(1, player.getName());
statement.setString(2, player.getName());
statement.setInt(3, start);
statement.setInt(4, count);
ResultSet rs = statement.executeQuery();
try {
List<GameHistoryEntry> result = new LinkedList<GameHistoryEntry>();
while (rs.next()) {
String winner = rs.getString(1);
String loser = rs.getString(2);
String winReason = rs.getString(3);
String loseReason = rs.getString(4);
String winRecordingId = rs.getString(5);
String loseRecordingId = rs.getString(6);
Date startDate = new Date(rs.getLong(7));
Date endDate = new Date(rs.getLong(8));
GameHistoryEntry entry = new GameHistoryEntry(winner, winReason, winRecordingId, loser, loseReason, loseRecordingId, startDate, endDate);
result.add(entry);
}
return result;
} finally {
rs.close();
}
} finally {
statement.close();
}
} finally {
connection.close();
}
} catch (SQLException exp) {
throw new RuntimeException("Unable to get count of player games", exp);
}
}
public int getGameHistoryForPlayerCount(Player player) {
try {
Connection connection = _dbAccess.getDataSource().getConnection();
try {
PreparedStatement statement = connection.prepareStatement("select count(*) from game_history where winner=? or loser=?");
try {
statement.setString(1, player.getName());
statement.setString(2, player.getName());
ResultSet rs = statement.executeQuery();
try {
if (rs.next())
return rs.getInt(1);
else
return -1;
} finally {
rs.close();
}
} finally {
statement.close();
}
} finally {
connection.close();
}
} catch (SQLException exp) {
throw new RuntimeException("Unable to get count of player games", exp);
}
}
}

View File

@@ -0,0 +1,60 @@
package com.gempukku.lotro.db.vo;
import java.util.Date;
public class GameHistoryEntry {
private String _winner;
private String _loser;
private String _winReason;
private String _loseReason;
private String _winnerRecording;
private String _loserRecording;
private Date _startTime;
private Date _endTime;
public GameHistoryEntry(String winner, String winReason, String winnerRecording, String loser, String loseReason, String loserRecording, Date startTime, Date endTime) {
_winner = winner;
_winReason = winReason;
_winnerRecording = winnerRecording;
_loser = loser;
_loseReason = loseReason;
_loserRecording = loserRecording;
_startTime = startTime;
_endTime = endTime;
}
public String getLoser() {
return _loser;
}
public String getLoseReason() {
return _loseReason;
}
public String getLoserRecording() {
return _loserRecording;
}
public String getWinner() {
return _winner;
}
public String getWinnerRecording() {
return _winnerRecording;
}
public String getWinReason() {
return _winReason;
}
public Date getEndTime() {
return _endTime;
}
public Date getStartTime() {
return _startTime;
}
}

View File

@@ -0,0 +1,123 @@
package com.gempukku.lotro.game;
import com.gempukku.lotro.db.GameHistoryDAO;
import com.gempukku.lotro.game.state.EventSerializer;
import com.gempukku.lotro.game.state.GameEvent;
import com.gempukku.lotro.game.state.GatheringParticipantCommunicationChannel;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
public class GameRecorder {
private static String _possibleChars = "abcdefghijklmnopqrstuvwxyz0123456789";
private static int _charsCount = _possibleChars.length();
private GameHistoryDAO _gameHistoryDao;
public GameRecorder(GameHistoryDAO gameHistoryDao) {
_gameHistoryDao = gameHistoryDao;
}
private String randomUid() {
int length = 16;
char[] chars = new char[length];
Random rnd = new Random();
for (int i = 0; i < length; i++)
chars[i] = _possibleChars.charAt(rnd.nextInt(_charsCount));
return new String(chars);
}
public InputStream getRecordedGame(String playerId, String gameId) throws IOException {
final File file = getRecordingFile(playerId, gameId);
if (!file.exists() || !file.isFile())
return null;
return new FileInputStream(file);
}
public GameRecordingInProgress recordGame(LotroGameMediator lotroGame) {
final Date startData = new Date();
final Map<String, GatheringParticipantCommunicationChannel> recordingChannels = new HashMap<String, GatheringParticipantCommunicationChannel>();
for (String playerId : lotroGame.getPlayersPlaying()) {
GatheringParticipantCommunicationChannel recordChannel = new GatheringParticipantCommunicationChannel(playerId);
lotroGame.addGameStateListener(playerId, recordChannel);
recordingChannels.put(playerId, recordChannel);
}
return new GameRecordingInProgress() {
@Override
public void finishRecording(String winner, String winReason, String loser, String loseReason) {
Map<String, String> playerRecordingId = saveRecordedChannels(recordingChannels);
_gameHistoryDao.addGameHistory(winner, loser, winReason, loseReason, playerRecordingId.get(winner), playerRecordingId.get(loser), startData, new Date());
}
};
}
public interface GameRecordingInProgress {
public void finishRecording(String winner, String winReason, String loser, String loseReason);
}
private File getRecordingFile(String playerId, String gameId) {
File gameReplayFolder = new File("i:\\gemp-lotr\\replay");
File playerReplayFolder = new File(gameReplayFolder, playerId);
return new File(playerReplayFolder, gameId + ".xml");
}
private Map<String, String> saveRecordedChannels(Map<String, GatheringParticipantCommunicationChannel> gameProgress) {
Map<String, String> result = new HashMap<String, String>();
for (Map.Entry<String, GatheringParticipantCommunicationChannel> playerRecordings : gameProgress.entrySet()) {
String playerId = playerRecordings.getKey();
File replayFile;
String gameRecordingId;
do {
gameRecordingId = randomUid();
replayFile = getRecordingFile(playerId, gameRecordingId);
} while (replayFile.exists());
replayFile.getParentFile().mkdirs();
final List<GameEvent> gameEvents = playerRecordings.getValue().consumeGameEvents();
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element gameReplay = doc.createElement("gameReplay");
EventSerializer serializer = new EventSerializer();
for (GameEvent gameEvent : gameEvents) {
gameReplay.appendChild(serializer.serializeEvent(doc, gameEvent));
}
doc.appendChild(gameReplay);
// Prepare the DOM document for writing
Source source = new DOMSource(doc);
// Prepare the output file
Result streamResult = new StreamResult(replayFile);
// Write the DOM document to the file
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.transform(source, streamResult);
} catch (Exception exp) {
}
result.put(playerId, gameRecordingId);
}
return result;
}
}

View File

@@ -1,85 +0,0 @@
package com.gempukku.lotro.game;
import com.gempukku.lotro.game.state.EventSerializer;
import com.gempukku.lotro.game.state.GameEvent;
import com.gempukku.lotro.game.state.GatheringParticipantCommunicationChannel;
import com.gempukku.lotro.logic.timing.GameResultListener;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.Result;
import javax.xml.transform.Source;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
public class GameRecording implements GameResultListener {
private Map<String, GatheringParticipantCommunicationChannel> _gameProgress;
private static String _possibleChars = "abcdefghijklmnopqrstuvwxyz0123456789";
private static int _charsCount = _possibleChars.length();
public GameRecording(Map<String, GatheringParticipantCommunicationChannel> gameProgress) {
_gameProgress = gameProgress;
}
private String randomUid() {
int length = 16;
char[] chars = new char[length];
Random rnd = new Random();
for (int i = 0; i < length; i++)
chars[i] = _possibleChars.charAt(rnd.nextInt(_charsCount));
return new String(chars);
}
@Override
public void gameFinished(String winnerPlayerId, Set<String> loserPlayerIds, String reason) {
File gameReplayFolder = new File("i:\\gemp-lotr\\replay");
gameReplayFolder.mkdirs();
for (Map.Entry<String, GatheringParticipantCommunicationChannel> playerRecordings : _gameProgress.entrySet()) {
String playerId = playerRecordings.getKey();
File playerReplayFolder = new File(gameReplayFolder, playerId);
playerReplayFolder.mkdir();
File replayFile;
do {
replayFile = new File(playerReplayFolder, randomUid() + ".xml");
} while (replayFile.exists());
final List<GameEvent> gameEvents = playerRecordings.getValue().consumeGameEvents();
try {
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element gameReplay = doc.createElement("gameReplay");
EventSerializer serializer = new EventSerializer();
for (GameEvent gameEvent : gameEvents) {
gameReplay.appendChild(serializer.serializeEvent(doc, gameEvent));
}
doc.appendChild(gameReplay);
// Prepare the DOM document for writing
Source source = new DOMSource(doc);
// Prepare the output file
Result result = new StreamResult(replayFile);
// Write the DOM document to the file
Transformer xformer = TransformerFactory.newInstance().newTransformer();
xformer.transform(source, result);
} catch (Exception exp) {
}
}
}
}

View File

@@ -3,6 +3,7 @@ package com.gempukku.lotro.game;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.common.Phase;
import com.gempukku.lotro.common.Zone;
import com.gempukku.lotro.communication.GameStateListener;
import com.gempukku.lotro.game.state.GameEvent;
import com.gempukku.lotro.game.state.GatheringParticipantCommunicationChannel;
import com.gempukku.lotro.logic.decisions.AwaitingDecision;
@@ -46,16 +47,14 @@ public class LotroGameMediator {
}
_lotroGame = new DefaultLotroGame(lotroFormat, decks, _userFeedback, library);
}
final HashMap<String, GatheringParticipantCommunicationChannel> recordingChannels = new HashMap<String, GatheringParticipantCommunicationChannel>();
for (String playerId : _playersPlaying) {
final GatheringParticipantCommunicationChannel playerRecording = new GatheringParticipantCommunicationChannel(playerId);
_lotroGame.addGameStateListener(playerId, playerRecording);
recordingChannels.put(playerId, playerRecording);
}
public void addGameStateListener(String playerId, GameStateListener listener) {
_lotroGame.addGameStateListener(playerId, listener);
}
_lotroGame.addGameResultListener(
new GameRecording(recordingChannels));
public void removeGameStateListener(GameStateListener listener) {
_lotroGame.removeGameStateListener(listener);
}
public void addGameResultListener(GameResultListener listener) {

View File

@@ -7,12 +7,16 @@ import com.gempukku.lotro.common.CardType;
import com.gempukku.lotro.common.Keyword;
import com.gempukku.lotro.db.DbAccess;
import com.gempukku.lotro.db.DeckDAO;
import com.gempukku.lotro.db.GameHistoryDAO;
import com.gempukku.lotro.db.PlayerDAO;
import com.gempukku.lotro.db.vo.GameHistoryEntry;
import com.gempukku.lotro.db.vo.Player;
import com.gempukku.lotro.logic.timing.GameResultListener;
import com.gempukku.lotro.logic.vo.LotroDeck;
import org.apache.log4j.Logger;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
@@ -30,8 +34,10 @@ public class LotroServer extends AbstractServer {
private PlayerDAO _playerDao;
private DeckDAO _deckDao;
private GameHistoryDAO _gameHistoryDao;
private DefaultCardCollection _defaultCollection;
private ChatServer _chatServer;
private GameRecorder _gameRecorder;
public LotroServer(DbAccess dbAccess, LotroCardBlueprintLibrary library, ChatServer chatServer, boolean test) {
_lotroCardBlueprintLibrary = library;
@@ -55,6 +61,22 @@ public class LotroServer extends AbstractServer {
_playerDao = new PlayerDAO(dbAccess);
_deckDao = new DeckDAO(dbAccess);
_gameHistoryDao = new GameHistoryDAO(dbAccess);
_gameRecorder = new GameRecorder(_gameHistoryDao);
}
public InputStream getGameRecording(String playerId, String gameId) throws IOException {
return _gameRecorder.getRecordedGame(playerId, gameId);
}
public List<GameHistoryEntry> getPlayerGameHistory(Player player, int start, int count) {
return _gameHistoryDao.getGameHistoryForPlayer(player, start, count);
}
public int getPlayerGameHistoryRecordCount(Player player) {
return _gameHistoryDao.getGameHistoryForPlayerCount(player);
}
public LotroCardBlueprintLibrary getLotroCardBlueprintLibrary() {
@@ -112,13 +134,24 @@ public class LotroServer extends AbstractServer {
lotroGameMediator.addGameResultListener(
new GameResultListener() {
@Override
public void gameFinished(String winnerPlayerId, Set<String> loserPlayerIds, String reason) {
log.debug("Game finished, winner is - " + winnerPlayerId + " due to: " + reason);
public void gameFinished(String winnerPlayerId, String winReason, Map<String, String> loserPlayerIdsWithReasons) {
log.debug("Game finished, winner is - " + winnerPlayerId + " due to: " + winReason);
synchronized (_finishedGamesTime) {
_finishedGamesTime.put(gameId, new Date());
}
}
});
final GameRecorder.GameRecordingInProgress gameRecordingInProgress = _gameRecorder.recordGame(lotroGameMediator);
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();
gameRecordingInProgress.finishRecording(winnerPlayerId, winReason, loserEntry.getKey(), loserEntry.getValue());
}
}
);
_runningGames.put(gameId, lotroGameMediator);
_nextGameId++;

View File

@@ -8,6 +8,7 @@ import com.gempukku.lotro.db.CollectionDAO;
import com.gempukku.lotro.db.DbAccess;
import com.gempukku.lotro.db.DeckDAO;
import com.gempukku.lotro.db.PlayerDAO;
import com.gempukku.lotro.db.vo.GameHistoryEntry;
import com.gempukku.lotro.db.vo.League;
import com.gempukku.lotro.db.vo.Player;
import com.gempukku.lotro.game.*;
@@ -35,7 +36,9 @@ import javax.ws.rs.core.StreamingOutput;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.*;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;
@Singleton
@@ -125,36 +128,87 @@ public class ServerResource {
@Produces(MediaType.APPLICATION_XML)
public StreamingOutput getReplay(
@PathParam("replayId") String replayId) throws ParserConfigurationException {
File gameReplayFolder = new File("i:\\gemp-lotr\\replay");
if (!replayId.contains("$"))
sendError(Response.Status.NOT_FOUND);
if (replayId.contains("."))
sendError(Response.Status.NOT_FOUND);
String[] split = replayId.split("\\$");
final String[] split = replayId.split("\\$");
if (split.length != 2)
sendError(Response.Status.NOT_FOUND);
final File replayFile = new File(new File(gameReplayFolder, split[0]), split[1] + ".xml");
if (!replayFile.exists() || !replayFile.isFile())
sendError(Response.Status.NOT_FOUND);
return new StreamingOutput() {
@Override
public void write(OutputStream outputStream) throws IOException, WebApplicationException {
InputStream is = new FileInputStream(replayFile);
final InputStream recordedGame = _lotroServer.getGameRecording(split[0], split[1]);
if (recordedGame == null)
sendError(Response.Status.NOT_FOUND);
try {
byte[] bytes = new byte[1024];
int count;
while ((count = is.read(bytes)) != -1)
while ((count = recordedGame.read(bytes)) != -1)
outputStream.write(bytes, 0, count);
} finally {
is.close();
recordedGame.close();
}
}
};
}
@Path("/gameHistory/{playerId}")
@GET
@Produces(MediaType.APPLICATION_XML)
public Document getGameHistory(
@PathParam("playerId") String playerId,
@QueryParam("start") int start,
@QueryParam("count") int count,
@QueryParam("participantId") String participantId,
@Context HttpServletRequest request) throws ParserConfigurationException {
if (!_test)
participantId = getLoggedUser(request);
if (start < 0 || count < 1 || count > 100)
sendError(Response.Status.BAD_REQUEST);
PlayerDAO playerDao = _lotroServer.getPlayerDao();
Player player = playerDao.getPlayer(participantId);
if (player == null)
sendError(Response.Status.UNAUTHORIZED);
final List<GameHistoryEntry> playerGameHistory = _lotroServer.getPlayerGameHistory(player, start, count);
int recordCount = _lotroServer.getPlayerGameHistoryRecordCount(player);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document doc = documentBuilder.newDocument();
Element gameHistory = doc.createElement("gameHistory");
gameHistory.setAttribute("count", String.valueOf(recordCount));
gameHistory.setAttribute("playerId", participantId);
for (GameHistoryEntry gameHistoryEntry : playerGameHistory) {
Element historyEntry = doc.createElement("historyEntry");
historyEntry.setAttribute("winner", gameHistoryEntry.getWinner());
historyEntry.setAttribute("loser", gameHistoryEntry.getLoser());
historyEntry.setAttribute("winReason", gameHistoryEntry.getWinReason());
historyEntry.setAttribute("loseReason", gameHistoryEntry.getLoseReason());
if (gameHistoryEntry.getWinner().equals(participantId))
historyEntry.setAttribute("gameRecordingId", gameHistoryEntry.getWinnerRecording());
else if (gameHistoryEntry.getLoser().equals(participantId))
historyEntry.setAttribute("gameRecordingId", gameHistoryEntry.getLoserRecording());
historyEntry.setAttribute("startTime", String.valueOf(gameHistoryEntry.getStartTime().getTime()));
historyEntry.setAttribute("endTime", String.valueOf(gameHistoryEntry.getEndTime().getTime()));
gameHistory.appendChild(historyEntry);
}
doc.appendChild(gameHistory);
return doc;
}
@Path("/game/{gameId}")
@GET
@Produces(MediaType.APPLICATION_XML)
@@ -843,9 +897,7 @@ public class ServerResource {
return loggedUser;
}
private void sendError(Response.Status status) {
WebApplicationException webApplicationException = new WebApplicationException(status);
// _logger.debug("Sending error to user: " + status.getStatusCode(), webApplicationException);
throw webApplicationException;
private void sendError(Response.Status status) throws WebApplicationException {
throw new WebApplicationException(status);
}
}