Introducing HallServer (pairing players, etc).

This commit is contained in:
marcins78@gmail.com
2011-09-15 15:00:50 +00:00
parent adaf3a8c41
commit 04548ac341
14 changed files with 236 additions and 106 deletions

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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<String, ChatRoomMediator> _chatRooms = new ConcurrentHashMap<String, ChatRoomMediator>();
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;
}
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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<GameEvent> _events = new LinkedList<GameEvent>();
private String _self;

View File

@@ -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<String, GatheringParticipantCommunicationChannel> _communicationChannels = new HashMap<String, GatheringParticipantCommunicationChannel>();
@@ -23,6 +16,7 @@ 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 Set<String> _playersPlaying = new HashSet<String>();
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<String> getPlayersPlaying() {
return Collections.unmodifiableSet(_playersPlaying);
}
public synchronized String produceCardInfo(String participantId, int cardId) {
StringBuilder sb = new StringBuilder();

View File

@@ -1,4 +1,4 @@
package com.gempukku.lotro;
package com.gempukku.lotro.game;
import com.gempukku.lotro.logic.vo.LotroDeck;

View File

@@ -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<String, LotroGameMediator> _runningGames = new ConcurrentHashMap<String, LotroGameMediator>();
private final Map<String, Date> _finishedGamesTime = new LinkedHashMap<String, Date>();
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;
}
}
}

View File

@@ -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;

View File

@@ -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<String> _players = new HashSet<String>();
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<String> getPlayers() {
return Collections.unmodifiableSet(_players);
}
}

View File

@@ -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<String, AwaitingTable> _awaitingTables = new ConcurrentHashMap<String, AwaitingTable>();
private Map<String, String> _runningTables = new ConcurrentHashMap<String, String>();
private int _nextTableId = 1;
private final int _playerInactivityPeriod = 1000 * 10; // 10 seconds
private Map<String, Long> _lastVisitedPlayers = Collections.synchronizedMap(new LinkedHashMap<String, Long>());
public HallServer(LotroServer lotroServer, ChatServer chatServer) {
_lotroServer = lotroServer;
_chatServer = chatServer;
_chatServer.createChatRoom("default");
}
/**
* @param playerId
* @return If table created, otherwise <code>false</code> (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 <code>false</code> (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<String> 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<String, AwaitingTable> copy = new HashMap<String, AwaitingTable>(_awaitingTables);
for (Map.Entry<String, AwaitingTable> 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<String, AwaitingTable> table : _awaitingTables.entrySet()) {
if (table.getValue().hasPlayer(playerId))
return table.getKey();
}
for (Map.Entry<String, String> 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<String, String> copy = new HashMap<String, String>(_runningTables);
for (Map.Entry<String, String> runningTable : copy.entrySet()) {
if (_lotroServer.getGameById(runningTable.getValue()) == null)
_runningTables.remove(runningTable.getKey());
}
long currentTime = System.currentTimeMillis();
Map<String, Long> visitCopy = new LinkedHashMap<String, Long>(_lastVisitedPlayers);
for (Map.Entry<String, Long> lastVisitedPlayer : visitCopy.entrySet()) {
if (currentTime > lastVisitedPlayer.getValue() + _playerInactivityPeriod) {
String playerId = lastVisitedPlayer.getKey();
_lastVisitedPlayers.remove(playerId);
leaveAwaitingTables(playerId);
}
}
}
}

View File

@@ -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();