Timing out stale communication channels (to preserve memory) and forfeit for users who are not responding to a decision for a long time.

This commit is contained in:
marcins78@gmail.com
2011-09-13 15:31:28 +00:00
parent 6bc95e8167
commit 53f3c10bc4
5 changed files with 71 additions and 25 deletions

View File

@@ -118,6 +118,10 @@ public class GameState {
sendStateToPlayer(playerId);
}
public void removeGameStateListener(String playerId, GameStateListener gameStateListener) {
_gameStateListeners.remove(playerId);
}
private Collection<GameStateListener> getPrivateGameStateListeners(PhysicalCard physicalCard) {
String owner = physicalCard.getOwner();
GameStateListener result = _gameStateListeners.get(owner);

View File

@@ -152,4 +152,11 @@ public class DefaultLotroGame implements LotroGame {
else
_gameStateListeners.put(playerId, gameStateListener);
}
public void removeGameStateListener(String playerId, GameStateListener gameStateListener) {
if (_gameState != null)
_gameState.removeGameStateListener(playerId, gameStateListener);
else
_gameStateListeners.remove(playerId);
}
}

View File

@@ -5,6 +5,7 @@ import com.gempukku.lotro.common.Token;
import com.gempukku.lotro.communication.GameStateListener;
import com.gempukku.lotro.game.PhysicalCard;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
@@ -13,6 +14,7 @@ import static com.gempukku.lotro.GameEvent.Type.*;
public class GatheringParticipantCommunicationChannel implements GameStateListener {
private List<GameEvent> _events = new LinkedList<GameEvent>();
private String _self;
private Date _lastConsumed;
public GatheringParticipantCommunicationChannel(String self) {
_self = self;
@@ -105,6 +107,11 @@ public class GatheringParticipantCommunicationChannel implements GameStateListen
public List<GameEvent> consumeGameEvents() {
List<GameEvent> result = _events;
_events = new LinkedList<GameEvent>();
_lastConsumed = new Date();
return result;
}
public Date getLastConsumed() {
return _lastConsumed;
}
}

View File

@@ -20,7 +20,11 @@ public class LotroGameMediator {
private DefaultLotroGame _lotroGame;
private Map<String, Integer> _playerClocks = new HashMap<String, Integer>();
private Map<String, Long> _decisionQuerySentTimes = new HashMap<String, Long>();
private final int _maxSecondsPerPlayer = 60 * 30; // 30 minutes
private final int _maxSecondsForGamePerPlayer = 60 * 30; // 30 minutes
private final int _channelInactivityTimeoutPeriod = 30; // 30 seconds
private final int _playerDecisionTimeoutPeriod = 60 * 5; // 5 minutes
public LotroGameMediator(LotroFormat lotroFormat, LotroGameParticipant[] participants, LotroCardBlueprintLibrary library, GameResultListener gameResultListener) {
if (participants.length < 1)
@@ -76,6 +80,27 @@ public class LotroGameMediator {
startClocksForUsersPendingDecision();
}
public synchronized void cleanup() {
long currentTime = System.currentTimeMillis();
Map<String, GatheringParticipantCommunicationChannel> channelsCopy = new HashMap<String, GatheringParticipantCommunicationChannel>(_communicationChannels);
for (Map.Entry<String, GatheringParticipantCommunicationChannel> playerChannels : channelsCopy.entrySet()) {
String playerId = playerChannels.getKey();
// Channel is stale (user no longer connected to game, to save memory, we remove the channel
// User can always reconnect and establish a new channel
GatheringParticipantCommunicationChannel channel = playerChannels.getValue();
_lotroGame.removeGameStateListener(playerId, channel);
if (currentTime > channel.getLastConsumed().getTime() + _channelInactivityTimeoutPeriod)
_communicationChannels.remove(playerId);
}
for (Map.Entry<String, Long> playerDecision : _decisionQuerySentTimes.entrySet()) {
String playerId = playerDecision.getKey();
long decisionSent = playerDecision.getValue();
if (currentTime > decisionSent + _playerDecisionTimeoutPeriod)
_lotroGame.playerLost(playerId);
}
}
public synchronized void playerAnswered(String lotroGameParticipant, int decisionId, String answer) {
AwaitingDecision awaitingDecision = _userFeedback.getAwaitingDecision(lotroGameParticipant);
if (awaitingDecision != null) {
@@ -99,28 +124,6 @@ public class LotroGameMediator {
}
}
private void startClocksForUsersPendingDecision() {
long currentTime = System.currentTimeMillis();
Set<String> 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)
@@ -136,7 +139,7 @@ public class LotroGameMediator {
Map<String, Integer> secondsLeft = new HashMap<String, Integer>();
for (Map.Entry<String, Integer> playerClock : _playerClocks.entrySet()) {
String player = playerClock.getKey();
secondsLeft.put(player, _maxSecondsPerPlayer - playerClock.getValue() - getCurrentUserPendingTime(player));
secondsLeft.put(player, _maxSecondsForGamePerPlayer - playerClock.getValue() - getCurrentUserPendingTime(player));
}
visitor.visitClock(secondsLeft);
}
@@ -160,8 +163,30 @@ public class LotroGameMediator {
Map<String, Integer> secondsLeft = new HashMap<String, Integer>();
for (Map.Entry<String, Integer> playerClock : _playerClocks.entrySet()) {
String player = playerClock.getKey();
secondsLeft.put(player, _maxSecondsPerPlayer - playerClock.getValue() - getCurrentUserPendingTime(player));
secondsLeft.put(player, _maxSecondsForGamePerPlayer - playerClock.getValue() - getCurrentUserPendingTime(player));
}
visitor.visitClock(secondsLeft);
}
private void startClocksForUsersPendingDecision() {
long currentTime = System.currentTimeMillis();
Set<String> 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);
}
}

View File

@@ -103,6 +103,9 @@ public class LotroServer {
}
}
}
for (LotroGameMediator lotroGameMediator : _runningGames.values())
lotroGameMediator.cleanup();
}
private String getChatRoomName(String gameId) {