diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/DefaultUserFeedback.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/DefaultUserFeedback.java index 24a7ae3e0..abc8e8e61 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/DefaultUserFeedback.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/DefaultUserFeedback.java @@ -5,6 +5,7 @@ import com.gempukku.lotro.logic.decisions.AwaitingDecision; import java.util.HashMap; import java.util.Map; +import java.util.Set; public class DefaultUserFeedback implements UserFeedback { private Map _awaitingDecisionMap = new HashMap(); @@ -36,4 +37,8 @@ public class DefaultUserFeedback implements UserFeedback { public void sendWarning(String playerId, String warning) { _warnings.put(playerId, warning); } + + public Set getUsersPendingDecision() { + return _awaitingDecisionMap.keySet(); + } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameMediator.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameMediator.java index cece50c68..0261bc57a 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameMediator.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameMediator.java @@ -18,6 +18,9 @@ public class LotroGameMediator { private Map _communicationChannels = new HashMap(); private DefaultUserFeedback _userFeedback = new DefaultUserFeedback(); private DefaultLotroGame _lotroGame; + private Map _playerClocks = new HashMap(); + private Map _decisionQuerySentTimes = new HashMap(); + private final int _maxSecondsPerPlayer = 60 * 30; // 30 minutes public LotroGameMediator(LotroFormat lotroFormat, LotroGameParticipant[] participants, LotroCardBlueprintLibrary library, GameResultListener gameResultListener) { if (participants.length < 1) @@ -31,6 +34,7 @@ public class LotroGameMediator { String participantId = participant.getPlayerId(); participantSet.add(participantId); decks.put(participantId, participant.getDeck()); + _playerClocks.put(participantId, 0); } _lotroGame = new DefaultLotroGame(decks, _userFeedback, gameResultListener, library); } @@ -78,7 +82,13 @@ public class LotroGameMediator { try { _userFeedback.participantDecided(lotroGameParticipant); awaitingDecision.decisionMade(answer); + + // Decision successfully made, add the time to user clock + addTimeSpentOnDecisionToUserClock(lotroGameParticipant); + _lotroGame.carryOutPendingActionsUntilDecisionNeeded(); + startClocksForUsersPendingDecision(); + } catch (DecisionResultInvalidException decisionResultInvalidException) { // Participant provided wrong answer - send a warning message, and ask again for the same decision // _userCommunication.sendWarning(lotroGameParticipant, decisionResultInvalidException.getWarningMessage()); @@ -88,6 +98,28 @@ public class LotroGameMediator { } } + private void startClocksForUsersPendingDecision() { + long currentTime = System.currentTimeMillis(); + Set users = _userFeedback.getUsersPendingDecision(); + for (String user : users) + _decisionQuerySentTimes.put(user, currentTime); + } + + private void addTimeSpentOnDecisionToUserClock(String participantId) { + long queryTime = _decisionQuerySentTimes.remove(participantId); + long currentTime = System.currentTimeMillis(); + long diffSec = (currentTime - queryTime) / 1000; + _playerClocks.put(participantId, _playerClocks.get(participantId) + (int) diffSec); + } + + private int getCurrentUserPendingTime(String participantId) { + if (!_decisionQuerySentTimes.containsKey(participantId)) + return 0; + long queryTime = _decisionQuerySentTimes.get(participantId); + long currentTime = System.currentTimeMillis(); + return (int) ((currentTime - queryTime) / 1000); + } + public synchronized void processCommunicationChannel(String participantId, ParticipantCommunicationVisitor visitor) { GatheringParticipantCommunicationChannel communicationChannel = _communicationChannels.get(participantId); if (communicationChannel != null) @@ -99,6 +131,13 @@ public class LotroGameMediator { AwaitingDecision awaitingDecision = _userFeedback.getAwaitingDecision(participantId); if (awaitingDecision != null) visitor.visitAwaitingDecision(_lotroGame.getActionStack().getTopmostAction(), awaitingDecision); + + Map secondsLeft = new HashMap(); + for (Map.Entry playerClock : _playerClocks.entrySet()) { + String player = playerClock.getKey(); + secondsLeft.put(player, _maxSecondsPerPlayer - playerClock.getValue() - getCurrentUserPendingTime(player)); + } + visitor.visitClock(secondsLeft); } public synchronized void singupUserForGame(String participantId, ParticipantCommunicationVisitor visitor) { @@ -116,5 +155,10 @@ public class LotroGameMediator { AwaitingDecision awaitingDecision = _userFeedback.getAwaitingDecision(participantId); if (awaitingDecision != null) visitor.visitAwaitingDecision(_lotroGame.getActionStack().getTopmostAction(), awaitingDecision); + + Map secondsLeft = new HashMap(); + for (Map.Entry playerClock : _playerClocks.entrySet()) + secondsLeft.put(playerClock.getKey(), _maxSecondsPerPlayer - playerClock.getValue()); + visitor.visitClock(secondsLeft); } } diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroServer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroServer.java index 5c6644eb2..450c83339 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroServer.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroServer.java @@ -14,13 +14,17 @@ import com.gempukku.lotro.logic.vo.LotroDeck; import org.apache.log4j.Logger; import java.util.*; +import java.util.concurrent.ConcurrentHashMap; public class LotroServer { private static final Logger log = Logger.getLogger(LotroServer.class); private LotroCardBlueprintLibrary _lotroCardBlueprintLibrary = new LotroCardBlueprintLibrary(); - private Map _runningGames = new HashMap(); + private Map _runningGames = new ConcurrentHashMap(); + + private final Map _finishedGamesTime = new LinkedHashMap(); + private final long _timeToGameDeath = 1000 * 60 * 5; // 5 minutes private boolean _started; private CleaningTask _cleaningTask; @@ -83,21 +87,41 @@ public class LotroServer { } public void cleanup() { - // TODO + long currentTime = System.currentTimeMillis(); + + synchronized (_finishedGamesTime) { + LinkedHashMap copy = new LinkedHashMap(_finishedGamesTime); + for (Map.Entry finishedGame : copy.entrySet()) { + if (finishedGame.getValue().getTime() > currentTime + _timeToGameDeath) { + String gameId = finishedGame.getKey(); + _runningGames.remove(gameId); + _chatServer.destroyChatRoom(getChatRoomName(gameId)); + _finishedGamesTime.remove(gameId); + } else { + break; + } + } + } + } + + private String getChatRoomName(String gameId) { + return "Game" + gameId; } public synchronized String createNewGame(LotroFormat lotroFormat, LotroGameParticipant[] participants) { if (participants.length < 2) throw new IllegalArgumentException("There has to be at least two players"); - String gameId = String.valueOf(_nextGameId); - final String chatRoomName = "Game" + gameId; + final String gameId = String.valueOf(_nextGameId); + String chatRoomName = getChatRoomName(gameId); _chatServer.createChatRoom(chatRoomName); LotroGameMediator lotroGameMediator = new LotroGameMediator(lotroFormat, participants, _lotroCardBlueprintLibrary, new GameResultListener() { @Override public void gameFinished(String winnerPlayerId, Set loserPlayerIds) { - _chatServer.destroyChatRoom(chatRoomName); + synchronized (_finishedGamesTime) { + _finishedGamesTime.put(gameId, new Date()); + } } }); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ParticipantCommunicationVisitor.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ParticipantCommunicationVisitor.java index 4e808e7f1..31697a846 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ParticipantCommunicationVisitor.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/ParticipantCommunicationVisitor.java @@ -4,7 +4,11 @@ import com.gempukku.lotro.GameEvent; import com.gempukku.lotro.logic.decisions.AwaitingDecision; import com.gempukku.lotro.logic.timing.Action; +import java.util.Map; + public interface ParticipantCommunicationVisitor { + public void visitClock(Map secondsLeft); + public void visitWarning(String warning); public void visitAwaitingDecision(Action currentAction, AwaitingDecision awaitingDecision); diff --git a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java index 4078cf962..a6d3ec1a8 100644 --- a/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java +++ b/gemp-lotr/gemp-lotr-web/src/main/java/com/gempukku/lotro/server/ServerResource.java @@ -420,6 +420,23 @@ public class ServerResource { public void visitAwaitingDecision(Action currentAction, AwaitingDecision awaitingDecision) { _element.appendChild(serializeDecision(_doc, currentAction, awaitingDecision)); } + + @Override + public void visitClock(Map secondsLeft) { + _element.appendChild(serializeClocks(_doc, secondsLeft)); + } + } + + private Node serializeClocks(Document doc, Map secondsLeft) { + Element clocks = doc.createElement("clocks"); + for (Map.Entry userClock : secondsLeft.entrySet()) { + Element clock = doc.createElement("clock"); + clock.setAttribute("participantId", userClock.getKey()); + clock.appendChild(doc.createTextNode(userClock.getValue().toString())); + clocks.appendChild(clock); + } + + return clocks; } private Node serializeDecision(Document doc, Action currentAction, AwaitingDecision decision) {