Fixed some bugs in table handling. Fixed that matches were not set to finished yet, if players left match during sideboarding phase.

This commit is contained in:
LevelX2 2014-02-07 02:04:14 +01:00
parent df9c200753
commit 40c25fae34
25 changed files with 274 additions and 140 deletions

View file

@ -189,8 +189,7 @@ public class TablesPanel extends javax.swing.JPanel {
} }
} else if (action.equals("Replay")) { } else if (action.equals("Replay")) {
logger.info("Replaying game " + gameId); logger.info("Replaying game " + gameId);
// no replay because of memory leaks session.replayGame(gameId);
// session.replayGame(gameId);
} }
} }
}; };
@ -201,8 +200,8 @@ public class TablesPanel extends javax.swing.JPanel {
public void actionPerformed(ActionEvent e) public void actionPerformed(ActionEvent e)
{ {
int modelRow = Integer.valueOf( e.getActionCommand() ); int modelRow = Integer.valueOf( e.getActionCommand() );
if (matchesModel.getValueAt(modelRow, MatchesTableModel.ACTION_COLUMN) instanceof List) { if (matchesModel.getValueAt(modelRow, MatchesTableModel.GAMES_LIST_COLUMN) instanceof List) {
List<UUID> games = (List<UUID>)matchesModel.getValueAt(modelRow, MatchesTableModel.ACTION_COLUMN); List<UUID> games = (List<UUID>)matchesModel.getValueAt(modelRow, MatchesTableModel.GAMES_LIST_COLUMN);
if (games.size() == 1) { if (games.size() == 1) {
session.replayGame(games.get(0)); session.replayGame(games.get(0));
} }
@ -769,19 +768,16 @@ class TableTableModel extends AbstractTableModel {
@Override @Override
public boolean isCellEditable(int rowIndex, int columnIndex) { public boolean isCellEditable(int rowIndex, int columnIndex) {
if (columnIndex != ACTION_COLUMN) { return columnIndex == ACTION_COLUMN;
return false;
}
return true;
} }
} }
class UpdateTablesTask extends SwingWorker<Void, Collection<TableView>> { class UpdateTablesTask extends SwingWorker<Void, Collection<TableView>> {
private Session session; private final Session session;
private UUID roomId; private final UUID roomId;
private TablesPanel panel; private final TablesPanel panel;
private static final Logger logger = Logger.getLogger(UpdateTablesTask.class); private static final Logger logger = Logger.getLogger(UpdateTablesTask.class);
@ -830,9 +826,9 @@ class UpdateTablesTask extends SwingWorker<Void, Collection<TableView>> {
class UpdatePlayersTask extends SwingWorker<Void, Collection<UsersView>> { class UpdatePlayersTask extends SwingWorker<Void, Collection<UsersView>> {
private Session session; private final Session session;
private UUID roomId; private final UUID roomId;
private ChatPanel chat; private final ChatPanel chat;
private static final Logger logger = Logger.getLogger(UpdatePlayersTask.class); private static final Logger logger = Logger.getLogger(UpdatePlayersTask.class);
@ -872,8 +868,8 @@ class UpdatePlayersTask extends SwingWorker<Void, Collection<UsersView>> {
class MatchesTableModel extends AbstractTableModel { class MatchesTableModel extends AbstractTableModel {
public static final int ACTION_COLUMN = 7; // column the action is located (starting with 0) public static final int ACTION_COLUMN = 7; // column the action is located (starting with 0)
public static final int GAMES_LIST_COLUMN = 8;
private String[] columnNames = new String[]{"Match Name", "Game Type", "Deck Type", "Players", "Result", "Start Time", "End Time","Action"}; private final String[] columnNames = new String[]{"Match Name", "Game Type", "Deck Type", "Players", "Result", "Start Time", "End Time","Action"};
private MatchView[] matches = new MatchView[0]; private MatchView[] matches = new MatchView[0];
private static final DateFormat timeFormatter = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT); private static final DateFormat timeFormatter = SimpleDateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
@ -914,7 +910,12 @@ class MatchesTableModel extends AbstractTableModel {
return ""; return "";
} }
case 7: case 7:
return "None"; if (matches[arg0].isReplayAvailable()) {
return "Replay";
} else {
return "None";
}
case 8: case 8:
return matches[arg0].getGames(); return matches[arg0].getGames();
} }
@ -939,19 +940,16 @@ class MatchesTableModel extends AbstractTableModel {
@Override @Override
public boolean isCellEditable(int rowIndex, int columnIndex) { public boolean isCellEditable(int rowIndex, int columnIndex) {
if (columnIndex != ACTION_COLUMN) { return columnIndex == ACTION_COLUMN;
return false;
}
return true;
} }
} }
class UpdateMatchesTask extends SwingWorker<Void, Collection<MatchView>> { class UpdateMatchesTask extends SwingWorker<Void, Collection<MatchView>> {
private Session session; private final Session session;
private UUID roomId; private final UUID roomId;
private TablesPanel panel; private final TablesPanel panel;
private static final Logger logger = Logger.getLogger(UpdateTablesTask.class); private static final Logger logger = Logger.getLogger(UpdateTablesTask.class);
@ -1013,7 +1011,7 @@ class GameChooser extends JPopupMenu {
private class GameChooserAction extends AbstractAction { private class GameChooserAction extends AbstractAction {
private UUID id; private final UUID id;
public GameChooserAction(UUID id, String choice) { public GameChooserAction(UUID id, String choice) {
this.id = id; this.id = id;

View file

@ -87,9 +87,10 @@ public class SessionImpl implements Session {
private static final Logger logger = Logger.getLogger(SessionImpl.class); private static final Logger logger = Logger.getLogger(SessionImpl.class);
private final MageClient client;
private String sessionId; private String sessionId;
private MageServer server; private MageServer server;
private MageClient client;
private Client callbackClient; private Client callbackClient;
private CallbackHandler callbackHandler; private CallbackHandler callbackHandler;
private ServerState serverState; private ServerState serverState;
@ -828,6 +829,7 @@ public class SessionImpl implements Session {
try { try {
if (isConnected()) { if (isConnected()) {
server.removeTable(sessionId, roomId, tableId); server.removeTable(sessionId, roomId, tableId);
return true; return true;
} }
} catch (MageException ex) { } catch (MageException ex) {
@ -1275,8 +1277,8 @@ public class SessionImpl implements Session {
} }
class MageAuthenticator extends Authenticator { class MageAuthenticator extends Authenticator {
private String username; private final String username;
private String password; private final String password;
public MageAuthenticator(String username, String password) { public MageAuthenticator(String username, String password) {
this.username = username; this.username = username;

View file

@ -44,17 +44,18 @@ import mage.game.tournament.TournamentPlayer;
*/ */
public class MatchView implements Serializable { public class MatchView implements Serializable {
private UUID matchId; private final UUID matchId;
private String matchName; private final String matchName;
private String gameType; private String gameType;
private String deckType; private final String deckType;
private List<UUID> games = new ArrayList<UUID>(); private final List<UUID> games = new ArrayList<UUID>();
private String result; private final String result;
private String players; private final String players;
private Date startTime; private final Date startTime;
private Date endTime; private final Date endTime;
private final Boolean replayAvailable;
public MatchView(Match match) { public MatchView(Match match) {
this.matchId = match.getId(); this.matchId = match.getId();
@ -79,6 +80,7 @@ public class MatchView implements Serializable {
result = sb2.substring(0, sb2.length() - 2); result = sb2.substring(0, sb2.length() - 2);
this.startTime = match.getStartTime(); this.startTime = match.getStartTime();
this.endTime = match.getEndTime(); this.endTime = match.getEndTime();
this.replayAvailable = match.isReplayAvailable();
} }
// used for tournaments // used for tournaments
@ -107,6 +109,7 @@ public class MatchView implements Serializable {
this.result = sb2.toString(); this.result = sb2.toString();
this.startTime = table.getTournament().getStartTime(); this.startTime = table.getTournament().getStartTime();
this.endTime = table.getTournament().getEndTime(); this.endTime = table.getTournament().getEndTime();
this.replayAvailable = false;
} }
public UUID getMatchId() { public UUID getMatchId() {
@ -144,4 +147,13 @@ public class MatchView implements Serializable {
public Date getEndTime() { public Date getEndTime() {
return endTime; return endTime;
} }
public String getMatchName() {
return matchName;
}
public Boolean isReplayAvailable() {
return replayAvailable;
}
} }

View file

@ -10,6 +10,7 @@
maxUserNameLength="14" maxUserNameLength="14"
userNamePattern="[^a-z0-9_]" userNamePattern="[^a-z0-9_]"
maxAiOpponents="3" maxAiOpponents="3"
saveGameActivated="false"
/> />
<playerTypes> <playerTypes>
<playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/> <playerType name="Human" jar="mage-player-human.jar" className="mage.player.human.HumanPlayer"/>

View file

@ -1,7 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Config.xsd"> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Config.xsd">
<server serverAddress="0.0.0.0" serverName="mage-server" port="17171" maxGameThreads="10" maxSecondsIdle="600" minUserNameLength="3" maxUserNameLength="14" userNamePattern="[^a-z0-9_]"/> <server serverAddress="0.0.0.0" serverName="mage-server" port="17171"
maxGameThreads="10"
maxSecondsIdle="600"
minUserNameLength="3"
maxUserNameLength="14"
userNamePattern="[^a-z0-9_]"
maxAiOpponents="15"
saveGameActivated="false"
/>
<playerTypes> <playerTypes>
<playerType name="Human" jar="mage-player-human-${project.version}.jar" className="mage.player.human.HumanPlayer"/> <playerType name="Human" jar="mage-player-human-${project.version}.jar" className="mage.player.human.HumanPlayer"/>
<playerType name="Computer - mad" jar="mage-player-ai-ma-${project.version}.jar" className="mage.player.ai.ComputerPlayer7"/> <playerType name="Computer - mad" jar="mage-player-ai-ma-${project.version}.jar" className="mage.player.ai.ComputerPlayer7"/>

View file

@ -89,10 +89,10 @@ import org.apache.log4j.Logger;
public class MageServerImpl implements MageServer { public class MageServerImpl implements MageServer {
private static final Logger logger = Logger.getLogger(MageServerImpl.class); private static final Logger logger = Logger.getLogger(MageServerImpl.class);
private static ExecutorService callExecutor = ThreadExecutor.getInstance().getCallExecutor(); private static final ExecutorService callExecutor = ThreadExecutor.getInstance().getCallExecutor();
private String password; private final String password;
private boolean testMode; private final boolean testMode;
public MageServerImpl(String password, boolean testMode) { public MageServerImpl(String password, boolean testMode) {
this.password = password; this.password = password;
@ -187,7 +187,9 @@ public class MageServerImpl implements MageServer {
logger.debug("Tournament table " + table.getTableId() + " created"); logger.debug("Tournament table " + table.getTableId() + " created");
LogServiceImpl.instance.log(LogKeys.KEY_TOURNAMENT_TABLE_CREATED, sessionId, userId.toString(), table.getTableId().toString()); LogServiceImpl.instance.log(LogKeys.KEY_TOURNAMENT_TABLE_CREATED, sessionId, userId.toString(), table.getTableId().toString());
return table; return table;
} catch (Exception ex) { } catch (NumberFormatException ex) {
handleException(ex);
} catch (MageException ex) {
handleException(ex); handleException(ex);
} }
return null; return null;

View file

@ -32,6 +32,7 @@ import java.io.File;
import java.io.FilenameFilter; import java.io.FilenameFilter;
import java.io.IOException; import java.io.IOException;
import java.net.InetAddress; import java.net.InetAddress;
import java.net.MalformedURLException;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import javax.management.MBeanServer; import javax.management.MBeanServer;
@ -76,12 +77,13 @@ import org.w3c.dom.Element;
*/ */
public class Main { public class Main {
private static Logger logger = Logger.getLogger(Main.class); private static final Logger logger = Logger.getLogger(Main.class);
private static final MageVersion version = new MageVersion(1, 3, 0, "dev2014-02-03");
private static final String testModeArg = "-testMode="; private static final String testModeArg = "-testMode=";
private static final String adminPasswordArg = "-adminPassword="; private static final String adminPasswordArg = "-adminPassword=";
private static final String pluginFolder = "plugins"; private static final String pluginFolder = "plugins";
private static MageVersion version = new MageVersion(1, 3, 0, "");
public static PluginClassLoader classLoader = new PluginClassLoader(); public static PluginClassLoader classLoader = new PluginClassLoader();
public static TransporterServer server; public static TransporterServer server;
@ -250,10 +252,9 @@ public class Main {
@Override @Override
public void removeListener(InvokerCallbackHandler callbackHandler) { public void removeListener(InvokerCallbackHandler callbackHandler) {
logger.fatal("removeListener called"); ServerInvokerCallbackHandler handler = (ServerInvokerCallbackHandler) callbackHandler;
// ServerInvokerCallbackHandler handler = (ServerInvokerCallbackHandler) callbackHandler; String sessionId = handler.getClientSessionId();
// String sessionId = handler.getCallbackClient().getSessionId(); SessionManager.getInstance().disconnect(sessionId, true);
// SessionManager.getInstance().disconnect(sessionId);
} }
} }
@ -264,8 +265,8 @@ public class Main {
logger.debug("Loading plugin: " + plugin.getClassName()); logger.debug("Loading plugin: " + plugin.getClassName());
return Class.forName(plugin.getClassName(), true, classLoader); return Class.forName(plugin.getClassName(), true, classLoader);
} catch (ClassNotFoundException ex) { } catch (ClassNotFoundException ex) {
logger.warn(new StringBuilder("Plugin not Found: ").append(plugin.getClassName()).append(" - ").append(plugin.getJar()).append(" - check plugin folder")); logger.warn(new StringBuilder("Plugin not Found: ").append(plugin.getClassName()).append(" - ").append(plugin.getJar()).append(" - check plugin folder"), ex);
} catch (Exception ex) { } catch (MalformedURLException ex) {
logger.fatal("Error loading plugin " + plugin.getJar(), ex); logger.fatal("Error loading plugin " + plugin.getJar(), ex);
} }
return null; return null;
@ -277,8 +278,12 @@ public class Main {
logger.debug("Loading game type: " + plugin.getClassName()); logger.debug("Loading game type: " + plugin.getClassName());
return (MatchType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance(); return (MatchType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance();
} catch (ClassNotFoundException ex) { } catch (ClassNotFoundException ex) {
logger.warn("Game type not found:" + plugin.getJar() + " - check plugin folder"); logger.warn("Game type not found:" + plugin.getJar() + " - check plugin folder", ex);
} catch (Exception ex) { } catch (IllegalAccessException ex) {
logger.fatal("Error loading game type " + plugin.getJar(), ex);
} catch (InstantiationException ex) {
logger.fatal("Error loading game type " + plugin.getJar(), ex);
} catch (MalformedURLException ex) {
logger.fatal("Error loading game type " + plugin.getJar(), ex); logger.fatal("Error loading game type " + plugin.getJar(), ex);
} }
return null; return null;
@ -290,8 +295,12 @@ public class Main {
logger.debug("Loading tournament type: " + plugin.getClassName()); logger.debug("Loading tournament type: " + plugin.getClassName());
return (TournamentType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance(); return (TournamentType) Class.forName(plugin.getTypeName(), true, classLoader).newInstance();
} catch (ClassNotFoundException ex) { } catch (ClassNotFoundException ex) {
logger.warn("Tournament type not found:" + plugin.getName() + " / "+ plugin.getJar() + " - check plugin folder"); logger.warn("Tournament type not found:" + plugin.getName() + " / "+ plugin.getJar() + " - check plugin folder", ex);
} catch (Exception ex) { } catch (IllegalAccessException ex) {
logger.fatal("Error loading game type " + plugin.getJar(), ex);
} catch (InstantiationException ex) {
logger.fatal("Error loading game type " + plugin.getJar(), ex);
} catch (MalformedURLException ex) {
logger.fatal("Error loading game type " + plugin.getJar(), ex); logger.fatal("Error loading game type " + plugin.getJar(), ex);
} }
return null; return null;

View file

@ -67,6 +67,7 @@ import mage.server.services.LogKeys;
import mage.server.services.impl.LogServiceImpl; import mage.server.services.impl.LogServiceImpl;
import mage.server.tournament.TournamentFactory; import mage.server.tournament.TournamentFactory;
import mage.server.tournament.TournamentManager; import mage.server.tournament.TournamentManager;
import mage.server.util.ConfigSettings;
import mage.server.util.ServerMessagesUtil; import mage.server.util.ServerMessagesUtil;
import mage.server.util.ThreadExecutor; import mage.server.util.ThreadExecutor;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
@ -80,15 +81,16 @@ public class TableController {
private static final Logger logger = Logger.getLogger(TableController.class); private static final Logger logger = Logger.getLogger(TableController.class);
private UUID userId; private final UUID userId;
private UUID chatId; private final UUID chatId;
private String controllerName; private final String controllerName;
private Table table; private final Table table;
private final ConcurrentHashMap<UUID, UUID> userPlayerMap = new ConcurrentHashMap<UUID, UUID>();
private Match match; private Match match;
private MatchOptions options; private MatchOptions options;
private Tournament tournament; private Tournament tournament;
private ConcurrentHashMap<UUID, UUID> userPlayerMap = new ConcurrentHashMap<UUID, UUID>();
private ScheduledFuture<?> futureTimeout; private ScheduledFuture<?> futureTimeout;
protected static ScheduledExecutorService timeoutExecutor = ThreadExecutor.getInstance().getTimeoutExecutor(); protected static ScheduledExecutorService timeoutExecutor = ThreadExecutor.getInstance().getTimeoutExecutor();
@ -237,10 +239,10 @@ public class TableController {
} }
match.addPlayer(player, deck); match.addPlayer(player, deck);
table.joinTable(player, seat); table.joinTable(player, seat);
user.addTable(player.getId(), table); logger.debug("player joined " + player.getId() + " " + player.getName());
logger.debug("player joined " + player.getId());
//only inform human players and add them to sessionPlayerMap //only inform human players and add them to sessionPlayerMap
if (seat.getPlayer().isHuman()) { if (seat.getPlayer().isHuman()) {
user.addTable(player.getId(), table);
user.joinedTable(table.getRoomId(), table.getId(), false); user.joinedTable(table.getRoomId(), table.getId(), false);
userPlayerMap.put(userId, player.getId()); userPlayerMap.put(userId, player.getId());
} }
@ -383,6 +385,14 @@ public class TableController {
TableManager.getInstance().userQuitTournamentSubTables(userId); TableManager.getInstance().userQuitTournamentSubTables(userId);
TournamentManager.getInstance().quit(tournament.getId(), userId); TournamentManager.getInstance().quit(tournament.getId(), userId);
} else { } else {
MatchPlayer matchPlayer = match.getPlayer(playerId);
if (matchPlayer != null) {
if (table.getState().equals(TableState.SIDEBOARDING)) {
// submit deck to finish sideboarding and trigger match start / end
matchPlayer.submitDeck(matchPlayer.getDeck());
}
matchPlayer.setQuit(true);
}
match.leave(playerId); match.leave(playerId);
} }
} }
@ -547,30 +557,6 @@ public class TableController {
} }
} }
private void sendMatchEndInfo(UUID playerId) {
for (Entry<UUID, UUID> entry: userPlayerMap.entrySet()) {
if (entry.getValue().equals(playerId)) {
User user = UserManager.getInstance().getUser(entry.getKey());
if (user != null) {
StringBuilder sb = new StringBuilder();
if (table.isTournamentSubTable()) {
sb.append("Your tournament match of round ");
sb.append(table.getTournament().getRounds().size());
sb.append(" is over. ");
} else {
sb.append("Match [").append(match.getName()).append("] is over. ");
}
if(match.getPlayers().size() > 2) {
sb.append("All your opponents have lost or quit the match.");
} else {
sb.append("Your opponent has quit the match.");
}
user.showUserMessage("Match info", sb.toString());
}
break;
}
}
}
public int getRemainingTime() { public int getRemainingTime() {
return (int) futureTimeout.getDelay(TimeUnit.SECONDS); return (int) futureTimeout.getDelay(TimeUnit.SECONDS);
} }
@ -596,10 +582,11 @@ public class TableController {
UUID choosingPlayerId = match.getChooser(); UUID choosingPlayerId = match.getChooser();
match.endGame(); match.endGame();
table.endGame(); table.endGame();
// Saving of games caused memory leaks - so save is deactivated if (ConfigSettings.getInstance().isSaveGameActivated() && !match.getGame().isSimulation()) {
// if (!match.getGame().isSimulation()) { if (GameManager.getInstance().saveGame(match.getGame().getId())) {
// GameManager.getInstance().saveGame(match.getGame().getId()); match.setReplayAvailable(true);
// } }
}
GameManager.getInstance().removeGame(match.getGame().getId()); GameManager.getInstance().removeGame(match.getGame().getId());
try { try {
if (!match.isMatchOver()) { if (!match.isMatchOver()) {
@ -610,23 +597,56 @@ public class TableController {
if (!match.isMatchOver()) { if (!match.isMatchOver()) {
startGame(choosingPlayerId); startGame(choosingPlayerId);
} else { } else {
this.matchEnd();
if (!ConfigSettings.getInstance().isSaveGameActivated() || match.getGame().isSimulation()) {
match.getGames().clear();
}
table.endGame(); table.endGame();
// opponent(s) left during sideboarding
for (MatchPlayer mPlayer :match.getPlayers()) {
if(!mPlayer.hasQuit()) {
this.sendMatchEndInfo(mPlayer.getPlayer().getId());
}
}
} }
} }
else { else {
match.getGames().clear(); // if match has only one game
this.matchEnd();
if (!ConfigSettings.getInstance().isSaveGameActivated() || match.getGame().isSimulation()) {
match.getGames().clear();
}
table.endGame();
} }
} catch (GameException ex) { } catch (GameException ex) {
logger.fatal(null, ex); logger.fatal(null, ex);
} }
} }
private void matchEnd() {
for (Entry<UUID, UUID> entry: userPlayerMap.entrySet()) {
MatchPlayer matchPlayer = match.getPlayer(entry.getValue());
// opponent(s) left during sideboarding
if(!matchPlayer.hasQuit()) {
User user = UserManager.getInstance().getUser(entry.getKey());
if (user != null) {
if (table.getState().equals(TableState.SIDEBOARDING)) {
StringBuilder sb = new StringBuilder();
if (table.isTournamentSubTable()) {
sb.append("Your tournament match of round ");
sb.append(table.getTournament().getRounds().size());
sb.append(" is over. ");
} else {
sb.append("Match [").append(match.getName()).append("] is over. ");
}
if (match.getPlayers().size() > 2) {
sb.append("All your opponents have lost or quit the match.");
} else {
sb.append("Your opponent has quit the match.");
}
user.showUserMessage("Match info", sb.toString());
}
// remove table from user - table manager holds table for display of finished matches
user.removeTable(entry.getValue());
}
}
}
}
private synchronized void setupTimeout(int seconds) { private synchronized void setupTimeout(int seconds) {
cancelTimeout(); cancelTimeout();
if (seconds > 0) { if (seconds > 0) {

View file

@ -62,8 +62,8 @@ public class TableManager {
private static final TableManager INSTANCE = new TableManager(); private static final TableManager INSTANCE = new TableManager();
private static final Logger logger = Logger.getLogger(TableManager.class); private static final Logger logger = Logger.getLogger(TableManager.class);
private ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<UUID, TableController>(); private final ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<UUID, TableController>();
private ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<UUID, Table>(); private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<UUID, Table>();
/** /**
* Defines how often checking process should be run on server. * Defines how often checking process should be run on server.
@ -184,6 +184,7 @@ public class TableManager {
public boolean removeTable(UUID userId, UUID tableId) { public boolean removeTable(UUID userId, UUID tableId) {
if (isTableOwner(tableId, userId) || UserManager.getInstance().isAdmin(userId)) { if (isTableOwner(tableId, userId) || UserManager.getInstance().isAdmin(userId)) {
leaveTable(userId, tableId);
removeTable(tableId); removeTable(tableId);
return true; return true;
} }
@ -192,13 +193,14 @@ public class TableManager {
public void leaveTable(UUID userId, UUID tableId) { public void leaveTable(UUID userId, UUID tableId) {
if (controllers.containsKey(tableId)) { if (controllers.containsKey(tableId)) {
controllers.get(tableId).leaveTable(userId);
// table not started yet and user is the owner, remove the table // table not started yet and user is the owner, remove the table
if (isTableOwner(tableId, userId)) { if (isTableOwner(tableId, userId)
if (getTable(tableId).getState().equals(TableState.WAITING) && (getTable(tableId).getState().equals(TableState.WAITING)
|| getTable(tableId).getState().equals(TableState.STARTING)) { || getTable(tableId).getState().equals(TableState.STARTING))) {
removeTable(tableId); removeTable(tableId);
}
} else {
controllers.get(tableId).leaveTable(userId);
} }
} }
} }

View file

@ -28,7 +28,6 @@
package mage.server; package mage.server;
import java.util.Date; import java.util.Date;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -36,6 +35,7 @@ import java.util.Map.Entry;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.constants.TableState;
import mage.game.Table; import mage.game.Table;
import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallback;
import mage.players.net.UserData; import mage.players.net.UserData;
@ -324,15 +324,47 @@ public class User {
} }
public String getGameInfo() { public String getGameInfo() {
StringBuilder sb = new StringBuilder();
if (gameSessions.size() > 0) { StringBuilder sb = new StringBuilder();
sb.append("G: ").append(gameSessions.size()); int draft = 0, match = 0, sideboard = 0, tournament = 0, construct = 0;
} for (Table table : tables.values()) {
if (tournamentSessions.size() > 0) { if (table.isTournament()) {
if (sb.length() > 0) { switch (table.getState()) {
sb.append(" "); case CONSTRUCTING:
construct++;
break;
case DRAFTING:
draft++;
break;
case DUELING:
tournament++;
break;
}
} else {
switch (table.getState()) {
case SIDEBOARDING:
sideboard++;
break;
case DUELING:
match++;
break;
}
} }
sb.append("T: ").append(tournamentSessions.size()); }
if (match > 0) {
sb.append("MP: ").append(match).append(" ");
}
if (sideboard > 0) {
sb.append("MS: ").append(sideboard).append(" ");
}
if (draft > 0) {
sb.append("TD: ").append(draft).append(" ");
}
if (construct > 0) {
sb.append("TC: ").append(construct).append(" ");
}
if (tournament > 0) {
sb.append("TP: ").append(tournament).append(" ");
} }
return sb.toString(); return sb.toString();
} }

View file

@ -64,7 +64,7 @@ public class UserManager {
}, 60, 60, TimeUnit.SECONDS); }, 60, 60, TimeUnit.SECONDS);
} }
private ConcurrentHashMap<UUID, User> users = new ConcurrentHashMap<UUID, User>(); private final ConcurrentHashMap<UUID, User> users = new ConcurrentHashMap<UUID, User>();
public User createUser(String userName, String host) { public User createUser(String userName, String host) {
if (findUser(userName) != null) { if (findUser(userName) != null) {
@ -95,8 +95,8 @@ public class UserManager {
public boolean connectToSession(String sessionId, UUID userId) { public boolean connectToSession(String sessionId, UUID userId) {
if (users.containsKey(userId)) { if (users.containsKey(userId)) {
users.get(userId).setSessionId(sessionId); users.get(userId).setSessionId(sessionId);
return true; return true;
} }
return false; return false;
} }

View file

@ -694,7 +694,7 @@ public class GameController implements GameCallback {
} }
} }
public void saveGame() { public boolean saveGame() {
try { try {
OutputStream file = new FileOutputStream("saved/" + game.getId().toString() + ".game"); OutputStream file = new FileOutputStream("saved/" + game.getId().toString() + ".game");
OutputStream buffer = new BufferedOutputStream(file); OutputStream buffer = new BufferedOutputStream(file);
@ -707,10 +707,12 @@ public class GameController implements GameCallback {
output.close(); output.close();
} }
logger.debug("Saved game:" + game.getId()); logger.debug("Saved game:" + game.getId());
return true;
} }
catch(IOException ex) { catch(IOException ex) {
logger.fatal("Cannot save game.", ex); logger.fatal("Cannot save game.", ex);
} }
return false;
} }
/** /**

View file

@ -180,10 +180,11 @@ public class GameManager {
gameControllers.remove(gameId); gameControllers.remove(gameId);
} }
public void saveGame(UUID gameId) { public boolean saveGame(UUID gameId) {
if (gameControllers.containsKey(gameId)) { if (gameControllers.containsKey(gameId)) {
gameControllers.get(gameId).saveGame(); return gameControllers.get(gameId).saveGame();
} }
return false;
} }
public GameView getGameView(UUID gameId, UUID userId, UUID playerId) { public GameView getGameView(UUID gameId, UUID userId, UUID playerId) {

View file

@ -35,11 +35,15 @@ import java.io.InputStream;
import java.io.ObjectInput; import java.io.ObjectInput;
import java.util.UUID; import java.util.UUID;
import java.util.zip.GZIPInputStream; import java.util.zip.GZIPInputStream;
import mage.game.*; import mage.game.Game;
import mage.game.GameState;
import mage.game.GameStates;
import mage.server.Main; import mage.server.Main;
import mage.util.CopierObjectInputStream; import mage.util.CopierObjectInputStream;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -48,8 +52,8 @@ public class GameReplay {
private static final Logger logger = Logger.getLogger(GameReplay.class); private static final Logger logger = Logger.getLogger(GameReplay.class);
private GameStates savedGame; private final GameStates savedGame;
private Game game; private final Game game;
private int stateIndex; private int stateIndex;
public GameReplay(UUID gameId) { public GameReplay(UUID gameId) {

View file

@ -57,8 +57,8 @@ import mage.view.SimpleCardsView;
*/ */
public class GameSession extends GameWatcher { public class GameSession extends GameWatcher {
private UUID playerId; private final UUID playerId;
private boolean useTimeout; private final boolean useTimeout;
private ScheduledFuture<?> futureTimeout; private ScheduledFuture<?> futureTimeout;
protected static ScheduledExecutorService timeoutExecutor = ThreadExecutor.getInstance().getTimeoutExecutor(); protected static ScheduledExecutorService timeoutExecutor = ThreadExecutor.getInstance().getTimeoutExecutor();

View file

@ -33,6 +33,7 @@ import org.apache.log4j.Logger;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import mage.MageException;
/** /**
* *
@ -42,9 +43,9 @@ public class GameWorker implements Callable {
private static final Logger logger = Logger.getLogger(GameWorker.class); private static final Logger logger = Logger.getLogger(GameWorker.class);
private GameCallback result; private final GameCallback result;
private Game game; private final Game game;
private UUID choosingPlayerId; private final UUID choosingPlayerId;
public GameWorker(Game game, UUID choosingPlayerId, GameCallback result) { public GameWorker(Game game, UUID choosingPlayerId, GameCallback result) {
this.game = game; this.game = game;
@ -58,7 +59,7 @@ public class GameWorker implements Callable {
game.start(choosingPlayerId); game.start(choosingPlayerId);
game.fireUpdatePlayersEvent(); game.fireUpdatePlayersEvent();
result.gameResult(game.getWinner()); result.gameResult(game.getWinner());
} catch (Exception ex) { } catch (MageException ex) {
logger.fatal("GameWorker error ", ex); logger.fatal("GameWorker error ", ex);
} }
return null; return null;

View file

@ -45,14 +45,12 @@ public class ReplayManager {
private ReplayManager() {} private ReplayManager() {}
private ConcurrentHashMap<String, ReplaySession> replaySessions = new ConcurrentHashMap<String, ReplaySession>(); private final ConcurrentHashMap<String, ReplaySession> replaySessions = new ConcurrentHashMap<String, ReplaySession>();
public void replayGame(UUID gameId, UUID userId) { public void replayGame(UUID gameId, UUID userId) {
if (1 == 2) { // deactivated because replay causes memor leaks ReplaySession replaySession = new ReplaySession(gameId, userId);
ReplaySession replaySession = new ReplaySession(gameId, userId); replaySessions.put(gameId.toString() + userId.toString(), replaySession);
replaySessions.put(gameId.toString() + userId.toString(), replaySession); UserManager.getInstance().getUser(userId).replayGame(gameId);
UserManager.getInstance().getUser(userId).replayGame(gameId);
}
} }
public void startReplay(UUID gameId, UUID userId) { public void startReplay(UUID gameId, UUID userId) {

View file

@ -51,11 +51,19 @@ public class Config {
remoteServer = p.getProperty("remote-server"); remoteServer = p.getProperty("remote-server");
maxGameThreads = Integer.parseInt(p.getProperty("max-game-threads")); maxGameThreads = Integer.parseInt(p.getProperty("max-game-threads"));
maxSecondsIdle = Integer.parseInt(p.getProperty("max-seconds-idle")); maxSecondsIdle = Integer.parseInt(p.getProperty("max-seconds-idle"));
minUserNameLength = Integer.parseInt(p.getProperty("minUserNameLength"));
maxUserNameLength = Integer.parseInt(p.getProperty("maxUserNameLength"));
userNamePattern = p.getProperty("userNamePattern");
saveGameActivated = Boolean.parseBoolean(p.getProperty("saveGameActivated"));
} }
public static final String remoteServer; public static final String remoteServer;
public static final int port; public static final int port;
public static final int maxGameThreads; public static final int maxGameThreads;
public static final int maxSecondsIdle; public static final int maxSecondsIdle;
public static final int minUserNameLength;
public static final int maxUserNameLength;
public static final String userNamePattern;
public static final boolean saveGameActivated;
} }

View file

@ -24,6 +24,8 @@
<xs:attribute name="minUserNameLength" type="xs:positiveInteger" use="required"/> <xs:attribute name="minUserNameLength" type="xs:positiveInteger" use="required"/>
<xs:attribute name="maxUserNameLength" type="xs:positiveInteger" use="required"/> <xs:attribute name="maxUserNameLength" type="xs:positiveInteger" use="required"/>
<xs:attribute name="userNamePattern" type="xs:string" use="required"/> <xs:attribute name="userNamePattern" type="xs:string" use="required"/>
<xs:attribute name="maxAiOpponents" type="xs:string" use="optional"/>
<xs:attribute name="saveGameActivated" type="xs:boolean" use="optional"/>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>

View file

@ -99,6 +99,10 @@ public class ConfigSettings {
return config.getServer().getMaxAiOpponents(); return config.getServer().getMaxAiOpponents();
} }
public Boolean isSaveGameActivated() {
return config.getServer().isSaveGameActivated();
}
public List<Plugin> getPlayerTypes() { public List<Plugin> getPlayerTypes() {
return config.getPlayerTypes().getPlayerType(); return config.getPlayerTypes().getPlayerType();
} }

View file

@ -1,7 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Config.xsd"> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../Config.xsd">
<server serverAddress="0.0.0.0" serverName="mage-server" port="17171" maxGameThreads="10" maxSecondsIdle="600" minUserNameLength="3" maxUserNameLength="14" userNamePattern="[^a-z0-9_]"/> <server serverAddress="0.0.0.0" serverName="mage-server" port="17171" maxGameThreads="10"
maxSecondsIdle="600"
minUserNameLength="3"
maxUserNameLength="14"
userNamePattern="[^a-z0-9_]"
saveGameActivated="true"
maxAiOpponents="15"
saveGameActivated="false"
/>
<playerTypes> <playerTypes>
<playerType name="Human" jar="Mage.Player.Human.jar" className="mage.player.human.HumanPlayer"/> <playerType name="Human" jar="Mage.Player.Human.jar" className="mage.player.human.HumanPlayer"/>
<playerType name="Computer - default" jar="Mage.Player.AI.jar" className="mage.player.ai.ComputerPlayer"/> <playerType name="Computer - default" jar="Mage.Player.AI.jar" className="mage.player.ai.ComputerPlayer"/>

View file

@ -22,10 +22,11 @@
<xs:attribute name="port" type="xs:positiveInteger" use="required"/> <xs:attribute name="port" type="xs:positiveInteger" use="required"/>
<xs:attribute name="maxGameThreads" type="xs:positiveInteger" use="required"/> <xs:attribute name="maxGameThreads" type="xs:positiveInteger" use="required"/>
<xs:attribute name="maxSecondsIdle" type="xs:positiveInteger" use="required"/> <xs:attribute name="maxSecondsIdle" type="xs:positiveInteger" use="required"/>
<xs:attribute name="minUserNameLength" type="xs:positiveInteger" use="required"/> <xs:attribute name="minUserNameLength" type="xs:positiveInteger" use="required"/>
<xs:attribute name="maxUserNameLength" type="xs:positiveInteger" use="required"/> <xs:attribute name="maxUserNameLength" type="xs:positiveInteger" use="required"/>
<xs:attribute name="userNamePattern" type="xs:string" use="required"/> <xs:attribute name="userNamePattern" type="xs:string" use="required"/>
<xs:attribute name="maxAiOpponents" type="xs:string" use="optional"/> <xs:attribute name="maxAiOpponents" type="xs:string" use="optional"/>
<xs:attribute name="saveGameActivated" type="xs:boolean" use="optional"/>
</xs:complexType> </xs:complexType>
</xs:element> </xs:element>

View file

@ -76,4 +76,11 @@ public interface Match {
// match times // match times
Date getStartTime(); Date getStartTime();
Date getEndTime(); Date getEndTime();
/**
* Can the games of the match be replayed
*
* @return
*/
boolean isReplayAvailable();
void setReplayAvailable(boolean replayAvailable);
} }

View file

@ -60,9 +60,12 @@ public abstract class MatchImpl implements Match {
protected Date startTime; protected Date startTime;
protected Date endTime; protected Date endTime;
protected boolean replayAvailable;
public MatchImpl(MatchOptions options) { public MatchImpl(MatchOptions options) {
this.options = options; this.options = options;
startTime = new Date(); startTime = new Date();
replayAvailable = false;
} }
@Override @Override
@ -316,4 +319,12 @@ public abstract class MatchImpl implements Match {
return null; return null;
} }
@Override
public boolean isReplayAvailable() {
return replayAvailable;
}
public void setReplayAvailable(boolean replayAvailable) {
this.replayAvailable = replayAvailable;
}
} }

View file

@ -1,12 +1,13 @@
package mage.util.functions; package mage.util.functions;
import java.io.Serializable;
import mage.game.Game; import mage.game.Game;
import mage.game.permanent.Permanent; import mage.game.permanent.Permanent;
/** /**
* @author noxx * @author noxx
*/ */
public abstract class ApplyToPermanent { public abstract class ApplyToPermanent implements Serializable {
public abstract Boolean apply(Game game, Permanent permanent); public abstract Boolean apply(Game game, Permanent permanent);
} }