diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/AbstractServer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/AbstractServer.java new file mode 100644 index 000000000..8ec48a4e6 --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/AbstractServer.java @@ -0,0 +1,42 @@ +package com.gempukku.lotro; + +public abstract class AbstractServer { + private boolean _started; + private CleaningTask _cleaningTask; + + public final synchronized void startServer() { + if (!_started) { + _cleaningTask = new CleaningTask(); + new Thread(_cleaningTask).start(); + _started = true; + } + } + + public final synchronized void stopServer() { + if (_started) { + _cleaningTask.stop(); + _started = false; + } + } + + protected abstract void cleanup(); + + private class CleaningTask implements Runnable { + private boolean _running = true; + + public void run() { + while (_running) { + cleanup(); + try { + Thread.sleep(1000); + } catch (InterruptedException e) { + // Ignore + } + } + } + + public void stop() { + _running = false; + } + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatRoomMediator.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatRoomMediator.java index 12ac44f99..784dfc7ec 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatRoomMediator.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatRoomMediator.java @@ -1,6 +1,6 @@ package com.gempukku.lotro.chat; -import com.gempukku.lotro.GatheringChatRoomListener; +import com.gempukku.lotro.game.GatheringChatRoomListener; import java.util.HashMap; import java.util.List; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatServer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatServer.java index 44879ef96..7167cff33 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatServer.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/chat/ChatServer.java @@ -1,14 +1,13 @@ package com.gempukku.lotro.chat; +import com.gempukku.lotro.AbstractServer; + import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -public class ChatServer { +public class ChatServer extends AbstractServer { private Map _chatRooms = new ConcurrentHashMap(); - private boolean _started; - private CleaningTask _cleaningTask; - public void createChatRoom(String name) { _chatRooms.put(name, new ChatRoomMediator()); } @@ -21,42 +20,8 @@ public class ChatServer { _chatRooms.remove(name); } - public synchronized void startServer() { - if (!_started) { - _cleaningTask = new CleaningTask(); - new Thread(_cleaningTask).start(); - _started = true; - } - } - - public synchronized void stopServer() { - if (_started) { - _cleaningTask.stop(); - _started = false; - } - } - - private void cleanup() { + protected void cleanup() { for (ChatRoomMediator chatRoomMediator : _chatRooms.values()) chatRoomMediator.cleanup(); } - - private class CleaningTask implements Runnable { - private boolean _running = true; - - public void run() { - while (_running) { - cleanup(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - // Ignore - } - } - } - - public void stop() { - _running = false; - } - } } 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/game/DefaultUserFeedback.java similarity index 93% rename from gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/DefaultUserFeedback.java rename to gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/DefaultUserFeedback.java index 8978bae49..a312b662e 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/game/DefaultUserFeedback.java @@ -1,4 +1,4 @@ -package com.gempukku.lotro; +package com.gempukku.lotro.game; import com.gempukku.lotro.communication.UserFeedback; import com.gempukku.lotro.logic.decisions.AwaitingDecision; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GameEvent.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameEvent.java similarity index 94% rename from gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GameEvent.java rename to gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameEvent.java index e0eeb24b0..75e7ef2e7 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GameEvent.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GameEvent.java @@ -1,10 +1,9 @@ -package com.gempukku.lotro; +package com.gempukku.lotro.game; import com.gempukku.lotro.common.CardType; import com.gempukku.lotro.common.Phase; import com.gempukku.lotro.common.Token; import com.gempukku.lotro.common.Zone; -import com.gempukku.lotro.game.PhysicalCard; import java.util.List; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GatheringChatRoomListener.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GatheringChatRoomListener.java similarity index 95% rename from gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GatheringChatRoomListener.java rename to gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GatheringChatRoomListener.java index bc8493a2d..ac2562acf 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GatheringChatRoomListener.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GatheringChatRoomListener.java @@ -1,4 +1,4 @@ -package com.gempukku.lotro; +package com.gempukku.lotro.game; import com.gempukku.lotro.chat.ChatMessage; import com.gempukku.lotro.chat.ChatRoomListener; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GatheringParticipantCommunicationChannel.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GatheringParticipantCommunicationChannel.java similarity index 93% rename from gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GatheringParticipantCommunicationChannel.java rename to gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GatheringParticipantCommunicationChannel.java index 9d26e6880..96979a92b 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/GatheringParticipantCommunicationChannel.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/GatheringParticipantCommunicationChannel.java @@ -1,15 +1,15 @@ -package com.gempukku.lotro; +package com.gempukku.lotro.game; -import static com.gempukku.lotro.GameEvent.Type.*; import com.gempukku.lotro.common.Phase; 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; +import static com.gempukku.lotro.game.GameEvent.Type.*; + public class GatheringParticipantCommunicationChannel implements GameStateListener { private List _events = new LinkedList(); private String _self; 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/game/LotroGameMediator.java similarity index 94% rename from gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameMediator.java rename to gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameMediator.java index 4f4b9b6e2..95fd02009 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/game/LotroGameMediator.java @@ -1,10 +1,6 @@ -package com.gempukku.lotro; +package com.gempukku.lotro.game; import com.gempukku.lotro.common.CardType; -import com.gempukku.lotro.game.LotroCardBlueprintLibrary; -import com.gempukku.lotro.game.LotroFormat; -import com.gempukku.lotro.game.ParticipantCommunicationVisitor; -import com.gempukku.lotro.game.PhysicalCard; import com.gempukku.lotro.logic.decisions.AwaitingDecision; import com.gempukku.lotro.logic.decisions.DecisionResultInvalidException; import com.gempukku.lotro.logic.modifiers.Modifier; @@ -12,10 +8,7 @@ import com.gempukku.lotro.logic.timing.DefaultLotroGame; import com.gempukku.lotro.logic.timing.GameResultListener; import com.gempukku.lotro.logic.vo.LotroDeck; -import java.util.Collection; -import java.util.HashMap; -import java.util.Map; -import java.util.Set; +import java.util.*; public class LotroGameMediator { private Map _communicationChannels = new HashMap(); @@ -23,6 +16,7 @@ public class LotroGameMediator { private DefaultLotroGame _lotroGame; private Map _playerClocks = new HashMap(); private Map _decisionQuerySentTimes = new HashMap(); + private Set _playersPlaying = new HashSet(); private final int _maxSecondsForGamePerPlayer = 60 * 30; // 30 minutes private final int _channelInactivityTimeoutPeriod = 1000 * 60 * 5; // 5 minutes @@ -40,10 +34,15 @@ public class LotroGameMediator { String participantId = participant.getPlayerId(); decks.put(participantId, participant.getDeck()); _playerClocks.put(participantId, 0); + _playersPlaying.add(participantId); } _lotroGame = new DefaultLotroGame(decks, _userFeedback, gameResultListener, library); } + public Set getPlayersPlaying() { + return Collections.unmodifiableSet(_playersPlaying); + } + public synchronized String produceCardInfo(String participantId, int cardId) { StringBuilder sb = new StringBuilder(); diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameParticipant.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameParticipant.java similarity index 88% rename from gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameParticipant.java rename to gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameParticipant.java index a9e961d44..aa834b533 100644 --- a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroGameParticipant.java +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroGameParticipant.java @@ -1,4 +1,4 @@ -package com.gempukku.lotro; +package com.gempukku.lotro.game; import com.gempukku.lotro.logic.vo.LotroDeck; 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/game/LotroServer.java similarity index 83% rename from gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/LotroServer.java rename to gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/game/LotroServer.java index 2d23451f8..8265013f8 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/game/LotroServer.java @@ -1,5 +1,6 @@ -package com.gempukku.lotro; +package com.gempukku.lotro.game; +import com.gempukku.lotro.AbstractServer; import com.gempukku.lotro.chat.ChatServer; import com.gempukku.lotro.common.CardType; import com.gempukku.lotro.common.Keyword; @@ -8,7 +9,6 @@ import com.gempukku.lotro.db.DeckDAO; import com.gempukku.lotro.db.PlayerDAO; import com.gempukku.lotro.db.vo.Deck; import com.gempukku.lotro.db.vo.Player; -import com.gempukku.lotro.game.*; import com.gempukku.lotro.logic.timing.GameResultListener; import com.gempukku.lotro.logic.vo.LotroDeck; import org.apache.log4j.Logger; @@ -16,18 +16,17 @@ import org.apache.log4j.Logger; import java.util.*; import java.util.concurrent.ConcurrentHashMap; -public class LotroServer { +public class LotroServer extends AbstractServer { private static final Logger log = Logger.getLogger(LotroServer.class); private LotroCardBlueprintLibrary _lotroCardBlueprintLibrary = new LotroCardBlueprintLibrary(); 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; private int _nextGameId = 1; private PlayerDAO _playerDao; @@ -71,22 +70,7 @@ public class LotroServer { return _defaultCollection; } - public synchronized void startServer() { - if (!_started) { - _cleaningTask = new CleaningTask(); - new Thread(_cleaningTask).start(); - _started = true; - } - } - - public synchronized void stopServer() { - if (_started) { - _cleaningTask.stop(); - _started = false; - } - } - - public void cleanup() { + protected void cleanup() { long currentTime = System.currentTimeMillis(); synchronized (_finishedGamesTime) { @@ -186,23 +170,4 @@ public class LotroServer { public LotroGameMediator getGameById(String gameId) { return _runningGames.get(gameId); } - - private class CleaningTask implements Runnable { - private boolean _running = true; - - public void run() { - while (_running) { - cleanup(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - log.error("Cleaning task interrupted", e); - } - } - } - - public void stop() { - _running = false; - } - } } 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 80be60efb..4bb3d9ec6 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 @@ -1,6 +1,5 @@ package com.gempukku.lotro.game; -import com.gempukku.lotro.GameEvent; import com.gempukku.lotro.logic.decisions.AwaitingDecision; import java.util.Map; diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/AwaitingTable.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/AwaitingTable.java new file mode 100644 index 000000000..745b0880f --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/AwaitingTable.java @@ -0,0 +1,29 @@ +package com.gempukku.lotro.hall; + +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +public class AwaitingTable { + private Set _players = new HashSet(); + + private int _capacity = 2; + + public boolean addPlayer(String playerId) { + _players.add(playerId); + return _players.size() == _capacity; + } + + public boolean removePlayer(String playerId) { + _players.remove(playerId); + return _players.size() == 0; + } + + public boolean hasPlayer(String playerId) { + return _players.contains(playerId); + } + + public Set getPlayers() { + return Collections.unmodifiableSet(_players); + } +} diff --git a/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallServer.java b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallServer.java new file mode 100644 index 000000000..86f14997b --- /dev/null +++ b/gemp-lotr/gemp-lotr-server/src/main/java/com/gempukku/lotro/hall/HallServer.java @@ -0,0 +1,134 @@ +package com.gempukku.lotro.hall; + +import com.gempukku.lotro.AbstractServer; +import com.gempukku.lotro.chat.ChatServer; +import com.gempukku.lotro.game.DefaultLotroFormat; +import com.gempukku.lotro.game.LotroGameMediator; +import com.gempukku.lotro.game.LotroGameParticipant; +import com.gempukku.lotro.game.LotroServer; + +import java.util.*; +import java.util.concurrent.ConcurrentHashMap; + +public class HallServer extends AbstractServer { + private ChatServer _chatServer; + private LotroServer _lotroServer; + + private Map _awaitingTables = new ConcurrentHashMap(); + private Map _runningTables = new ConcurrentHashMap(); + private int _nextTableId = 1; + + private final int _playerInactivityPeriod = 1000 * 10; // 10 seconds + + private Map _lastVisitedPlayers = Collections.synchronizedMap(new LinkedHashMap()); + + public HallServer(LotroServer lotroServer, ChatServer chatServer) { + _lotroServer = lotroServer; + _chatServer = chatServer; + _chatServer.createChatRoom("default"); + } + + /** + * @param playerId + * @return If table created, otherwise false (if the user already is sitting at a table or playing). + */ + public synchronized boolean createNewTable(String playerId) { + if (isPlayerBusy(playerId)) + return false; + + String tableId = String.valueOf(_nextTableId++); + AwaitingTable table = new AwaitingTable(); + table.addPlayer(playerId); + _awaitingTables.put(tableId, table); + + return true; + } + + /** + * @param playerId + * @return If table joined, otherwise false (if the user already is sitting at a table or playing). + */ + public synchronized boolean joinTableAsPlayer(String tableId, String playerId) { + if (isPlayerBusy(playerId)) + return false; + + AwaitingTable awaitingTable = _awaitingTables.get(tableId); + if (awaitingTable == null) + return false; + boolean tableFull = awaitingTable.addPlayer(playerId); + if (tableFull) { + Set players = awaitingTable.getPlayers(); + LotroGameParticipant[] participants = new LotroGameParticipant[players.size()]; + int index = 0; + for (String player : players) + participants[index] = new LotroGameParticipant(player, _lotroServer.getParticipantDeck(player)); + + String gameId = _lotroServer.createNewGame(new DefaultLotroFormat(true), participants); + LotroGameMediator lotroGameMediator = _lotroServer.getGameById(gameId); + lotroGameMediator.startGame(); + _runningTables.put(tableId, gameId); + } + return true; + } + + public synchronized void leaveAwaitingTables(String playerId) { + Map copy = new HashMap(_awaitingTables); + for (Map.Entry table : copy.entrySet()) { + if (table.getValue().hasPlayer(playerId)) { + boolean empty = table.getValue().removePlayer(playerId); + if (empty) + _awaitingTables.remove(table.getKey()); + } + } + } + + public String getPlayerTable(String playerId) { + for (Map.Entry table : _awaitingTables.entrySet()) { + if (table.getValue().hasPlayer(playerId)) + return table.getKey(); + } + + for (Map.Entry runningTable : _runningTables.entrySet()) { + String gameId = runningTable.getValue(); + LotroGameMediator lotroGameMediator = _lotroServer.getGameById(gameId); + if (lotroGameMediator != null) + if (lotroGameMediator.getPlayersPlaying().contains(playerId)) + return runningTable.getKey(); + } + + return null; + } + + private boolean isPlayerBusy(String playerId) { + for (AwaitingTable awaitingTable : _awaitingTables.values()) + if (awaitingTable.hasPlayer(playerId)) + return true; + + for (String gameId : _runningTables.values()) { + LotroGameMediator lotroGameMediator = _lotroServer.getGameById(gameId); + if (lotroGameMediator.getPlayersPlaying().contains(playerId)) + return true; + } + return false; + } + + @Override + protected void cleanup() { + // Remove finished games + HashMap copy = new HashMap(_runningTables); + for (Map.Entry runningTable : copy.entrySet()) { + if (_lotroServer.getGameById(runningTable.getValue()) == null) + _runningTables.remove(runningTable.getKey()); + } + + long currentTime = System.currentTimeMillis(); + Map visitCopy = new LinkedHashMap(_lastVisitedPlayers); + for (Map.Entry lastVisitedPlayer : visitCopy.entrySet()) { + if (currentTime > lastVisitedPlayer.getValue() + _playerInactivityPeriod) { + String playerId = lastVisitedPlayer.getKey(); + _lastVisitedPlayers.remove(playerId); + leaveAwaitingTables(playerId); + } + } + } +} 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 1791205fc..5e56804b3 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 @@ -1,9 +1,5 @@ package com.gempukku.lotro.server; -import com.gempukku.lotro.GameEvent; -import com.gempukku.lotro.LotroGameMediator; -import com.gempukku.lotro.LotroGameParticipant; -import com.gempukku.lotro.LotroServer; import com.gempukku.lotro.chat.ChatMessage; import com.gempukku.lotro.chat.ChatRoomMediator; import com.gempukku.lotro.chat.ChatServer; @@ -11,9 +7,8 @@ import com.gempukku.lotro.db.DeckDAO; import com.gempukku.lotro.db.PlayerDAO; import com.gempukku.lotro.db.vo.Deck; import com.gempukku.lotro.db.vo.Player; -import com.gempukku.lotro.game.CardCollection; -import com.gempukku.lotro.game.DefaultLotroFormat; -import com.gempukku.lotro.game.ParticipantCommunicationVisitor; +import com.gempukku.lotro.game.*; +import com.gempukku.lotro.hall.HallServer; import com.gempukku.lotro.logic.decisions.AwaitingDecision; import com.sun.jersey.spi.resource.Singleton; import org.apache.commons.lang.StringEscapeUtils; @@ -38,6 +33,7 @@ import java.util.Map; public class ServerResource { private static final Logger _logger = Logger.getLogger(ServerResource.class); + private HallServer _hallServer; private LotroServer _lotroServer; private ChatServer _chatServer; @@ -47,10 +43,12 @@ public class ServerResource { try { _chatServer = new ChatServer(); _chatServer.startServer(); - _chatServer.createChatRoom("default"); _lotroServer = new LotroServer(_chatServer); _lotroServer.startServer(); + + _hallServer = new HallServer(_lotroServer, _chatServer); + _hallServer.startServer(); } catch (RuntimeException exp) { _logger.error("Error while creating resource", exp); exp.printStackTrace();