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 index bc20fc413..71fe0ffb3 100644 --- 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 @@ -12,7 +12,8 @@ import com.gempukku.lotro.league.LeagueService; import com.gempukku.lotro.logic.vo.LotroDeck; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.ReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; public class HallServer extends AbstractServer { private ChatServer _chatServer; @@ -24,20 +25,23 @@ public class HallServer extends AbstractServer { private Map _supportedFormats = new LinkedHashMap(); // TODO Reading/writing from/to these maps is done in multiple threads - private Map _awaitingTables = new ConcurrentHashMap(); - private Map _runningTables = new ConcurrentHashMap(); - private Map _runningTableFormatNames = new ConcurrentHashMap(); - private Map _runningTableTournamentNames = new ConcurrentHashMap(); + private Map _awaitingTables = new LinkedHashMap(); + private Map _runningTables = new LinkedHashMap(); + + private Map _runningTableFormatNames = new HashMap(); + private Map _runningTableTournamentNames = new HashMap(); private int _nextTableId = 1; private final int _playerInactivityPeriod = 1000 * 10; // 10 seconds - private Map _lastVisitedPlayers = Collections.synchronizedMap(new LinkedHashMap()); + private Map _lastVisitedPlayers = new HashMap(); private String _motd; private CollectionType _allCardsCollectionType = new CollectionType("default", "All cards"); + private ReadWriteLock _hallTablesLock = new ReentrantReadWriteLock(false); + public HallServer(LotroServer lotroServer, ChatServer chatServer, LeagueService leagueService, LotroCardBlueprintLibrary library, CollectionDAO collectionDao, boolean test) { _lotroServer = lotroServer; _chatServer = chatServer; @@ -58,8 +62,6 @@ public class HallServer extends AbstractServer { addFormat("c_war_block", new WarOfTheRingBlockFormat(library, true)); addFormat("open", new OpenFormat(library)); addFormat("expanded", new ExpandedFormat(library)); - -// addFormat("whatever", "Format for testing", "default", new FreeFormat(library)); } public String getMOTD() { @@ -89,34 +91,107 @@ public class HallServer extends AbstractServer { /** * @return If table created, otherwise false (if the user already is sitting at a table or playing). */ - public synchronized void createNewTable(String type, Player player, String deckName) throws HallException { - League league = null; - LeagueSerie leagueSerie = null; - CollectionType collectionType = _allCardsCollectionType; - LotroFormat format = _supportedFormats.get(type); + public void createNewTable(String type, Player player, String deckName) throws HallException { + _hallTablesLock.writeLock().lock(); + try { + League league = null; + LeagueSerie leagueSerie = null; + CollectionType collectionType = _allCardsCollectionType; + LotroFormat format = _supportedFormats.get(type); - if (format == null) { - // Maybe it's a league format? - league = _leagueService.getLeagueByType(type); - if (league != null) { - leagueSerie = _leagueService.getCurrentLeagueSerie(league); - if (leagueSerie == null) - throw new HallException("There is no ongoing serie for that league"); - format = _supportedFormats.get(leagueSerie.getFormat()); - collectionType = league.getCollectionType(); + if (format == null) { + // Maybe it's a league format? + league = _leagueService.getLeagueByType(type); + if (league != null) { + leagueSerie = _leagueService.getCurrentLeagueSerie(league); + if (leagueSerie == null) + throw new HallException("There is no ongoing serie for that league"); + format = _supportedFormats.get(leagueSerie.getFormat()); + collectionType = league.getCollectionType(); + } } + // It's not a normal format and also not a league one + if (format == null) + throw new HallException("This format is not supported: " + type); + + LotroDeck lotroDeck = validateUserAndDeck(format, player, deckName, collectionType); + + String tableId = String.valueOf(_nextTableId++); + AwaitingTable table = new AwaitingTable(format, collectionType, league, leagueSerie); + _awaitingTables.put(tableId, table); + + joinTableInternal(tableId, player.getName(), table, deckName, lotroDeck); + } finally { + _hallTablesLock.writeLock().unlock(); } - // It's not a normal format and also not a league one - if (format == null) - throw new HallException("This format is not supported: " + type); + } - LotroDeck lotroDeck = validateUserAndDeck(format, player, deckName, collectionType); + /** + * @return If table joined, otherwise false (if the user already is sitting at a table or playing). + */ + public boolean joinTableAsPlayer(String tableId, Player player, String deckName) throws HallException { + _hallTablesLock.writeLock().lock(); + try { + AwaitingTable awaitingTable = _awaitingTables.get(tableId); + if (awaitingTable == null) + throw new HallException("Table is already taken or was removed"); - String tableId = String.valueOf(_nextTableId++); - AwaitingTable table = new AwaitingTable(format, collectionType, league, leagueSerie); - _awaitingTables.put(tableId, table); + LotroDeck lotroDeck = validateUserAndDeck(awaitingTable.getLotroFormat(), player, deckName, awaitingTable.getCollectionType()); - joinTableInternal(tableId, player.getName(), table, deckName, lotroDeck); + joinTableInternal(tableId, player.getName(), awaitingTable, deckName, lotroDeck); + + return true; + } finally { + _hallTablesLock.writeLock().unlock(); + } + } + + public void leaveAwaitingTables(Player player) { + _hallTablesLock.writeLock().lock(); + try { + Map copy = new HashMap(_awaitingTables); + for (Map.Entry table : copy.entrySet()) { + if (table.getValue().hasPlayer(player.getName())) { + boolean empty = table.getValue().removePlayer(player.getName()); + if (empty) + _awaitingTables.remove(table.getKey()); + } + } + } finally { + _hallTablesLock.writeLock().unlock(); + } + } + + public void processTables(Player player, HallInfoVisitor visitor) { + _hallTablesLock.readLock().lock(); + try { + _lastVisitedPlayers.put(player, System.currentTimeMillis()); + visitor.playerIsWaiting(isPlayerWaiting(player.getName())); + + for (Map.Entry tableInformation : _awaitingTables.entrySet()) { + final AwaitingTable table = tableInformation.getValue(); + + visitor.visitTable(tableInformation.getKey(), null, "Waiting", table.getLotroFormat().getName(), getTournamentName(table), table.getPlayerNames(), null); + } + + for (Map.Entry runningGame : _runningTables.entrySet()) { + LotroGameMediator lotroGameMediator = _lotroServer.getGameById(runningGame.getValue()); + if (lotroGameMediator != null) + visitor.visitTable(runningGame.getKey(), runningGame.getValue(), lotroGameMediator.getGameStatus(), _runningTableFormatNames.get(runningGame.getKey()), _runningTableTournamentNames.get(runningGame.getKey()), lotroGameMediator.getPlayersPlaying(), lotroGameMediator.getWinner()); + } + + String playerTable = getNonFinishedPlayerTable(player.getName()); + if (playerTable != null) { + String gameId = _runningTables.get(playerTable); + if (gameId != null) { + LotroGameMediator lotroGameMediator = _lotroServer.getGameById(gameId); + if (lotroGameMediator != null && !lotroGameMediator.getGameStatus().equals("Finished")) + visitor.runningPlayerGame(gameId); + } + } + } finally { + _hallTablesLock.readLock().unlock(); + } } private LotroDeck validateUserAndDeck(LotroFormat format, Player player, String deckName, CollectionType collectionType) throws HallException { @@ -151,27 +226,6 @@ public class HallServer extends AbstractServer { return lotroDeck; } - /** - * @return If table joined, otherwise false (if the user already is sitting at a table or playing). - */ - public synchronized boolean joinTableAsPlayer(String tableId, Player player, String deckName) throws HallException { - AwaitingTable awaitingTable = _awaitingTables.get(tableId); - if (awaitingTable == null) - throw new HallException("Table is already taken or was removed"); - - LotroDeck lotroDeck = validateUserAndDeck(awaitingTable.getLotroFormat(), player, deckName, awaitingTable.getCollectionType()); - - joinTableInternal(tableId, player.getName(), awaitingTable, deckName, lotroDeck); - - return true; - } - - private void joinTableInternal(String tableId, String playerId, AwaitingTable awaitingTable, String deckName, LotroDeck lotroDeck) { - boolean tableFull = awaitingTable.addPlayer(new LotroGameParticipant(playerId, deckName, lotroDeck)); - if (tableFull) - createGame(tableId, awaitingTable); - } - private String getTournamentName(AwaitingTable table) { String tournamentName = "Casual"; final League league = table.getLeague(); @@ -195,15 +249,10 @@ public class HallServer extends AbstractServer { _awaitingTables.remove(tableId); } - public synchronized void leaveAwaitingTables(Player player) { - Map copy = new HashMap(_awaitingTables); - for (Map.Entry table : copy.entrySet()) { - if (table.getValue().hasPlayer(player.getName())) { - boolean empty = table.getValue().removePlayer(player.getName()); - if (empty) - _awaitingTables.remove(table.getKey()); - } - } + private void joinTableInternal(String tableId, String playerId, AwaitingTable awaitingTable, String deckName, LotroDeck lotroDeck) { + boolean tableFull = awaitingTable.addPlayer(new LotroGameParticipant(playerId, deckName, lotroDeck)); + if (tableFull) + createGame(tableId, awaitingTable); } private boolean isPlayerWaiting(String playerId) { @@ -213,35 +262,6 @@ public class HallServer extends AbstractServer { return false; } - public void processTables(Player player, HallInfoVisitor visitor) { - _lastVisitedPlayers.put(player, System.currentTimeMillis()); - visitor.playerIsWaiting(isPlayerWaiting(player.getName())); - - Map copy = new HashMap(_awaitingTables); - for (Map.Entry tableInformation : copy.entrySet()) { - final AwaitingTable table = tableInformation.getValue(); - - visitor.visitTable(tableInformation.getKey(), null, "Waiting", table.getLotroFormat().getName(), getTournamentName(table), table.getPlayerNames(), null); - } - - Map runningCopy = new LinkedHashMap(_runningTables); - for (Map.Entry runningGame : runningCopy.entrySet()) { - LotroGameMediator lotroGameMediator = _lotroServer.getGameById(runningGame.getValue()); - if (lotroGameMediator != null) - visitor.visitTable(runningGame.getKey(), runningGame.getValue(), lotroGameMediator.getGameStatus(), _runningTableFormatNames.get(runningGame.getKey()), _runningTableTournamentNames.get(runningGame.getKey()), lotroGameMediator.getPlayersPlaying(), lotroGameMediator.getWinner()); - } - - String playerTable = getNonFinishedPlayerTable(player.getName()); - if (playerTable != null) { - String gameId = _runningTables.get(playerTable); - if (gameId != null) { - LotroGameMediator lotroGameMediator = _lotroServer.getGameById(gameId); - if (lotroGameMediator != null && !lotroGameMediator.getGameStatus().equals("Finished")) - visitor.runningPlayerGame(gameId); - } - } - } - private String getNonFinishedPlayerTable(String playerId) { for (Map.Entry table : _awaitingTables.entrySet()) { if (table.getValue().hasPlayer(playerId)) @@ -274,24 +294,29 @@ public class HallServer extends AbstractServer { @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()); - _runningTableFormatNames.remove(runningTable.getKey()); - _runningTableTournamentNames.remove(runningTable.getKey()); + _hallTablesLock.writeLock().lock(); + try { + // Remove finished games + HashMap copy = new HashMap(_runningTables); + for (Map.Entry runningTable : copy.entrySet()) { + if (_lotroServer.getGameById(runningTable.getValue()) == null) { + _runningTables.remove(runningTable.getKey()); + _runningTableFormatNames.remove(runningTable.getKey()); + _runningTableTournamentNames.remove(runningTable.getKey()); + } } - } - long currentTime = System.currentTimeMillis(); - Map visitCopy = new LinkedHashMap(_lastVisitedPlayers); - for (Map.Entry lastVisitedPlayer : visitCopy.entrySet()) { - if (currentTime > lastVisitedPlayer.getValue() + _playerInactivityPeriod) { - Player player = lastVisitedPlayer.getKey(); - _lastVisitedPlayers.remove(player); - leaveAwaitingTables(player); + long currentTime = System.currentTimeMillis(); + Map visitCopy = new LinkedHashMap(_lastVisitedPlayers); + for (Map.Entry lastVisitedPlayer : visitCopy.entrySet()) { + if (currentTime > lastVisitedPlayer.getValue() + _playerInactivityPeriod) { + Player player = lastVisitedPlayer.getKey(); + _lastVisitedPlayers.remove(player); + leaveAwaitingTables(player); + } } + } finally { + _hallTablesLock.writeLock().unlock(); } } }