diff --git a/Mage.Client/src/main/java/mage/client/MageFrame.java b/Mage.Client/src/main/java/mage/client/MageFrame.java index 9cf9cfae903..0f712e10116 100644 --- a/Mage.Client/src/main/java/mage/client/MageFrame.java +++ b/Mage.Client/src/main/java/mage/client/MageFrame.java @@ -657,7 +657,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { for (JInternalFrame window : desktopPane.getAllFramesInLayer(JLayeredPane.DEFAULT_LAYER)) { if (window instanceof DraftPane) { DraftPane draftPane = (DraftPane) window; - draftPane.hideDraft(); + draftPane.removeDraft(); } } } @@ -997,12 +997,21 @@ public class MageFrame extends javax.swing.JFrame implements MageClient { } if (window instanceof DraftPane) { DraftPane draftPane = (DraftPane) window; - draftPane.hideDraft(); + draftPane.removeDraft(); } if (window instanceof TournamentPane) { TournamentPane tournamentPane = (TournamentPane) window; - tournamentPane.hideTournament(); + tournamentPane.removeTournament(); } + // close & remove sideboarding or construction pane if open + if (window instanceof DeckEditorPane) { + DeckEditorPane deckEditorPane = (DeckEditorPane) window; + if (deckEditorPane.getDeckEditorMode().equals(DeckEditorMode.Limited) + || deckEditorPane.getDeckEditorMode().equals(DeckEditorMode.Sideboard)){ + deckEditorPane.removeFrame(); + } + } + } } diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java index 1443d21e6df..f306e7a1d49 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPane.java @@ -88,6 +88,9 @@ public class DeckEditorPane extends MagePane { this.repaint(); } + public DeckEditorMode getDeckEditorMode() { + return this.deckEditorPanel1.getDeckEditorMode(); + } /** This method is called from within the constructor to * initialize the form. diff --git a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java index 2ffb1c31a80..bb5f585f1d2 100644 --- a/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java +++ b/Mage.Client/src/main/java/mage/client/deckeditor/DeckEditorPanel.java @@ -329,6 +329,10 @@ public class DeckEditorPanel extends javax.swing.JPanel { } } + public DeckEditorMode getDeckEditorMode() { + return mode; + } + private BigCard getBigCard() { return this.bigCard; } diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPane.java b/Mage.Client/src/main/java/mage/client/draft/DraftPane.java index 057ec0df0ee..28c6cf061ec 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPane.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPane.java @@ -73,7 +73,7 @@ public class DraftPane extends MagePane { this.draftPanel1.showDraft(draftId); } - public void hideDraft() { + public void removeDraft() { draftPanel1.cleanUp(); this.removeFrame(); } diff --git a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java index be580c5e864..eefad217463 100644 --- a/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java +++ b/Mage.Client/src/main/java/mage/client/draft/DraftPanel.java @@ -175,7 +175,7 @@ public class DraftPanel extends javax.swing.JPanel { c = c.getParent(); } if (c != null) { - ((DraftPane)c).hideDraft(); + ((DraftPane)c).removeDraft(); } } diff --git a/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java b/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java index b833a3a0ff2..516ff1b99f0 100644 --- a/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java +++ b/Mage.Client/src/main/java/mage/client/tournament/TournamentPane.java @@ -54,7 +54,7 @@ public class TournamentPane extends MagePane { this.repaint(); } - public void hideTournament() { + public void removeTournament() { tournamentPanel.cleanUp(); removeFrame(); } diff --git a/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java b/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java index 10149f4720d..f3669cdad59 100644 --- a/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java +++ b/Mage.Client/src/main/java/mage/client/tournament/TournamentPanel.java @@ -186,7 +186,7 @@ public class TournamentPanel extends javax.swing.JPanel { c = c.getParent(); } if (c != null) { - ((TournamentPane)c).hideTournament(); + ((TournamentPane)c).removeTournament(); } } @@ -209,6 +209,9 @@ public class TournamentPanel extends javax.swing.JPanel { firstInitDone = true; } + if (txtEndTime == null) { + return; + } if (txtEndTime.getText().equals("running...") && tournament.getEndTime() != null) { txtEndTime.setText(df.format(tournament.getEndTime())); } @@ -608,14 +611,16 @@ class UpdateTournamentTask extends SwingWorker { protected Void doInBackground() throws Exception { while (!isCancelled()) { this.publish(session.getTournament(tournamentId)); - Thread.sleep(1000); + Thread.sleep(2000); } return null; } @Override protected void process(List view) { - panel.update(view.get(0)); + if (view != null) { // if user disconnects, view can be null for a short time + panel.update(view.get(0)); + } } @Override diff --git a/Mage.Common/src/mage/interfaces/MageServer.java b/Mage.Common/src/mage/interfaces/MageServer.java index 2e2a4bf17c0..e9f38575c48 100644 --- a/Mage.Common/src/mage/interfaces/MageServer.java +++ b/Mage.Common/src/mage/interfaces/MageServer.java @@ -50,7 +50,8 @@ public interface MageServer { // connection methods boolean registerClient(String userName, String sessionId, MageVersion version) throws MageException; boolean registerAdmin(String password, String sessionId, MageVersion version) throws MageException; - void deregisterClient(String sessionId) throws MageException; +// Not used +// void deregisterClient(String sessionId) throws MageException; // update methods List getMissingExpansionData(List codes); diff --git a/Mage.Common/src/mage/view/TournamentPlayerView.java b/Mage.Common/src/mage/view/TournamentPlayerView.java index 658dc172452..940bb4300b9 100644 --- a/Mage.Common/src/mage/view/TournamentPlayerView.java +++ b/Mage.Common/src/mage/view/TournamentPlayerView.java @@ -48,9 +48,11 @@ public class TournamentPlayerView implements Serializable, Comparable{ TournamentPlayerView(TournamentPlayer player) { this.name = player.getPlayer().getName(); StringBuilder sb = new StringBuilder(player.getState().toString()); - if (!player.getStateInfo().isEmpty()) { - sb.append(" (").append(player.getStateInfo()).append(")"); + String stateInfo = player.getStateInfo(); + if (!stateInfo.isEmpty()) { + sb.append(" (").append(stateInfo).append(")"); } + sb.append(player.getDisconnectInfo()); this.state = sb.toString(); this.points = player.getPoints(); this.results = player.getResults(); diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index 9bab124dba5..0943c9dc282 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -109,6 +109,7 @@ public class MageServerImpl implements MageServer { LogServiceImpl.instance.log(LogKeys.KEY_WRONG_VERSION, userName, version.toString(), Main.getVersion().toString(), sessionId); throw new MageVersionException(version, Main.getVersion()); } + logger.info(new StringBuilder("RegisterClient - userName: ").append(userName).append(" sessionId = ").append(sessionId)); return SessionManager.getInstance().registerUser(sessionId, userName); } catch (Exception ex) { if (ex instanceof MageVersionException) { @@ -333,16 +334,16 @@ public class MageServerImpl implements MageServer { return SessionManager.getInstance().extendUserSession(sessionId); } - @Override - public void deregisterClient(final String sessionId) throws MageException { - execute("deregisterClient", sessionId, new Action() { - @Override - public void execute() { - SessionManager.getInstance().disconnect(sessionId, true); - logger.debug("Client deregistered ..."); - } - }); - } +// @Override +// public void deregisterClient(final String sessionId) throws MageException { +// execute("deregisterClient", sessionId, new Action() { +// @Override +// public void execute() { +// SessionManager.getInstance().disconnect(sessionId, true); +// logger.debug("Client deregistered ..."); +// } +// }); +// } @Override public void startMatch(final String sessionId, final UUID roomId, final UUID tableId) throws MageException { diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 60573ddb908..ea98ea46aa8 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -37,6 +37,7 @@ import mage.interfaces.callback.ClientCallback; import mage.players.net.UserData; import mage.players.net.UserGroup; import mage.server.util.ConfigSettings; +import mage.view.ChatMessage; import mage.view.UserDataView; import org.apache.log4j.Logger; import org.jboss.remoting.callback.AsynchInvokerCallbackHandler; @@ -88,6 +89,8 @@ public class Session { user = UserManager.getInstance().findUser(userName); if (user.getHost().equals(host)) { if (user.getSessionId().isEmpty()) { + // TODO Send Chat message to tables (user is not registered yet) + // ChatManager.getInstance().broadcast([CHAT ID TABLES], "has reconnected", ChatMessage.MessageColor.GREEN); logger.info("Reconnecting session for " + userName); } else { //throw new MageException("This machine is already connected"); @@ -124,7 +127,10 @@ public class Session { userData = new UserData(UserGroup.PLAYER, userDataView.getAvatarId(), userDataView.isShowAbilityPickerForced()); user.setUserData(userData); } else { - userData.setAvatarId(userDataView.getAvatarId()); + if (userDataView.getAvatarId() == 51) { // Update special avatar if first avatar is selected + updateAvatar(userName, userData); + } + userData.setAvatarId(userDataView.getAvatarId()); userData.setShowAbilityPickerForced(userDataView.isShowAbilityPickerForced()); } return true; diff --git a/Mage.Server/src/main/java/mage/server/SessionManager.java b/Mage.Server/src/main/java/mage/server/SessionManager.java index 3e75b2e819d..340d11ad558 100644 --- a/Mage.Server/src/main/java/mage/server/SessionManager.java +++ b/Mage.Server/src/main/java/mage/server/SessionManager.java @@ -111,7 +111,7 @@ public class SessionManager { LogServiceImpl.instance.log(LogKeys.KEY_SESSION_DISCONNECTED, sessionId); } } else { - logger.info("could not find session with id " + sessionId); + logger.info("disconnect: could not find session with id " + sessionId); } } diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 6d288909310..466b6d3f0d3 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -42,7 +42,6 @@ import mage.cards.decks.InvalidDeckException; import mage.constants.RangeOfInfluence; import mage.constants.TableState; import mage.game.GameException; -import mage.game.GameOptions; import mage.game.Seat; import mage.game.Table; import mage.game.draft.Draft; @@ -56,7 +55,6 @@ import mage.game.tournament.Tournament; import mage.game.tournament.TournamentOptions; import mage.game.tournament.TournamentPlayer; import mage.players.Player; -import mage.server.challenge.ChallengeManager; import mage.server.draft.DraftManager; import mage.server.game.DeckValidatorFactory; import mage.server.game.GameFactory; @@ -306,7 +304,7 @@ public class TableController { } else { TournamentManager.getInstance().submitDeck(tournament.getId(), playerId, deck); - UserManager.getInstance().getUser(userId).removeConstructing(table.getId()); + UserManager.getInstance().getUser(userId).removeConstructing(playerId); } } @@ -474,6 +472,14 @@ public class TableController { if (!match.getPlayer(entry.getValue()).hasQuit()) { User user = UserManager.getInstance().getUser(entry.getKey()); if (user != null) { + if (!user.isConnected()) { + // if the user is not connected but exits, the user is currently disconnected. So it's neccessary + // to join the user to the game here, so he can join the game, if he reconnects in time. + // remove a existing constructing for the player if it exists + user.removeConstructing(match.getPlayer(entry.getValue()).getPlayer().getId()); + GameManager.getInstance().joinGame(match.getGame().getId(), user.getId()); + } + logger.info(new StringBuilder("User ").append(user.getName()).append(" game started - matchId ").append(match.getId()).append(" userId: ").append(user.getId())); user.gameStarted(match.getGame().getId(), entry.getValue()); if (creator == null) { @@ -486,10 +492,8 @@ public class TableController { } } else { - TableManager.getInstance().removeTable(table.getId()); - GameManager.getInstance().removeGame(match.getGame().getId()); logger.warn("Unable to find player " + entry.getKey()); - break; + match.getPlayer(entry.getValue()).setQuit(true); } } } @@ -658,7 +662,9 @@ public class TableController { user.showUserMessage("Match info", sb.toString()); } // remove table from user - table manager holds table for display of finished matches - user.removeTable(entry.getValue()); + if (!table.isTournamentSubTable()) { + user.removeTable(entry.getValue()); + } } } } @@ -735,4 +741,29 @@ public class TableController { return match; } + public boolean isMatchTableStillValid() { + // check only normal match table + if (!table.isTournament() && !table.isTournamentSubTable()) { + int humanPlayers = 0; + int validHumanPlayers = 0; + if (match == null) { + return false; + } + for(Map.Entry userPlayerEntry: userPlayerMap.entrySet()) { + MatchPlayer matchPlayer = match.getPlayer(userPlayerEntry.getValue()); + if (matchPlayer.getPlayer().isHuman()) { + humanPlayers++; + if (!matchPlayer.hasQuit()) { + User user = UserManager.getInstance().getUser(userPlayerEntry.getKey()); + if (user != null) { + validHumanPlayers++; + } + } + } + } + // if at least 2 human players are valid (multiplayer) or all human players are valid the table is valid + return validHumanPlayers >= 2 || validHumanPlayers == humanPlayers; + } + return true; + } } diff --git a/Mage.Server/src/main/java/mage/server/TableManager.java b/Mage.Server/src/main/java/mage/server/TableManager.java index f4e703c7daa..eab24d6634c 100644 --- a/Mage.Server/src/main/java/mage/server/TableManager.java +++ b/Mage.Server/src/main/java/mage/server/TableManager.java @@ -47,6 +47,7 @@ import mage.game.match.MatchPlayer; import mage.game.tournament.Tournament; import mage.game.tournament.TournamentOptions; import mage.players.Player; +import mage.server.game.GameManager; import mage.server.game.GamesRoomManager; import org.apache.log4j.Logger; @@ -175,6 +176,20 @@ public class TableManager { } } + // remove user from all sub tables of a tournament + public void userQuitTournamentSubTables(UUID tournamentId, UUID userId) { + for (TableController controller: controllers.values()) { + if (controller.getTable().isTournamentSubTable() && controller.getTable().getTournament().getId().equals(tournamentId)) { + Match match = controller.getTable().getMatch(); + if (match != null) { + if (match.getGame() != null) { + GameManager.getInstance().quitMatch(match.getGame().getId(), userId); + } + } + } + } + } + public boolean isTableOwner(UUID tableId, UUID userId) { if (controllers.containsKey(tableId)) { return controllers.get(tableId).isOwner(userId); @@ -341,20 +356,10 @@ public class TableManager { logger.warn("Table expired: id = " + table.getId() + ", created_by=" + table.getControllerName() + ". Removing..."); toRemove.add(table.getId()); } - // remove immediately non tournament tables with no human players + // remove tables not valid anymore else if (!table.isTournament()) { - boolean canBeRemoved = true; - for (MatchPlayer matchPlayer :table.getMatch().getPlayers()) { - Player player = matchPlayer.getPlayer(); - if (player != null && player.isHuman() && !player.hasLeft()) { - canBeRemoved = false; - } - // tournament sub tables may not be removed as long the tournament is not finished - if(table.isTournamentSubTable() && table.getTournament().getEndTime() == null) { - canBeRemoved = false; - } - } - if (canBeRemoved) { + TableController tableController = getController(table.getId()); + if (!tableController.isMatchTableStillValid()) { logger.warn("Table with no active human player: id = " + table.getId() + ", created_by=" + table.getControllerName() + ". Removing..."); toRemove.add(table.getId()); } diff --git a/Mage.Server/src/main/java/mage/server/User.java b/Mage.Server/src/main/java/mage/server/User.java index d6c76779bf2..315a0a1e61e 100644 --- a/Mage.Server/src/main/java/mage/server/User.java +++ b/Mage.Server/src/main/java/mage/server/User.java @@ -34,6 +34,7 @@ import java.util.Map; import java.util.Map.Entry; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; import mage.cards.decks.Deck; import mage.game.Table; import mage.interfaces.callback.ClientCallback; @@ -42,6 +43,7 @@ import mage.server.draft.DraftSession; import mage.server.game.GameManager; import mage.server.game.GameSession; import mage.server.tournament.TournamentSession; +import mage.server.util.SystemUtil; import mage.view.TableClientMessage; import org.apache.log4j.Logger; @@ -133,6 +135,13 @@ public class User { return userState == UserState.Connected || userState == UserState.Reconnected; } + public String getDisconnectDuration() { + long secondsDisconnected = SystemUtil.getDateDiff(lastActivity, new Date(), TimeUnit.SECONDS); + int minutes = (int) secondsDisconnected / 60; + int seconds = (int) secondsDisconnected % 60; + return new StringBuilder(Integer.toString(minutes)).append(":").append(seconds > 9 ? seconds: "0" + Integer.toString(seconds)).toString(); + } + public Date getConnectionTime() { return connectionTime; } @@ -246,7 +255,7 @@ public class User { } for (Entry entry: constructing.entrySet()) { - entry.getValue().construct(0); + entry.getValue().construct(0); // TODO: Check if this is correct } for (Entry entry: sideboarding.entrySet()) { TableController controller = TableManager.getInstance().getController(entry.getKey()); @@ -323,10 +332,15 @@ public class User { } public String getGameInfo() { - StringBuilder sb = new StringBuilder(); + + String disconnectInfo = ""; + if (!isConnected()) { + disconnectInfo = new StringBuilder(" (discon. ").append(getDisconnectDuration()).append(")").toString(); + } int draft = 0, match = 0, sideboard = 0, tournament = 0, construct = 0; - for (Table table : tables.values()) { + for (Map.Entry tableEntry : tables.entrySet()) { + Table table = tableEntry.getValue(); if (table.isTournament()) { switch (table.getState()) { case CONSTRUCTING: @@ -339,6 +353,11 @@ public class User { tournament++; break; } + if (!isConnected()) { + table.getTournament().getPlayer(tableEntry.getKey()).setDisconnectInfo(disconnectInfo); + } else { + table.getTournament().getPlayer(tableEntry.getKey()).setDisconnectInfo(""); + } } else { switch (table.getState()) { case SIDEBOARDING: @@ -365,6 +384,7 @@ public class User { if (tournament > 0) { sb.append("TP: ").append(tournament).append(" "); } + sb.append(disconnectInfo); return sb.toString(); } diff --git a/Mage.Server/src/main/java/mage/server/UserManager.java b/Mage.Server/src/main/java/mage/server/UserManager.java index b6416a9f6da..f6c39cec3c6 100644 --- a/Mage.Server/src/main/java/mage/server/UserManager.java +++ b/Mage.Server/src/main/java/mage/server/UserManager.java @@ -102,13 +102,13 @@ public class UserManager { public void disconnect(UUID userId, User.DisconnectReason reason) { if (userId != null) { - ChatManager.getInstance().removeUser(userId, reason); if (users.containsKey(userId)) { User user = users.get(userId); - logger.debug(new StringBuilder("User ").append(user.getName()).append(" has lost connection userId:").append(userId)); - users.get(userId).setSessionId(""); + user.setSessionId(""); // Session will be set again with new id if user reconnects ChatManager.getInstance().broadcast(userId, "has lost connection", MessageColor.BLACK); + logger.info(new StringBuilder("User ").append(user.getName()).append(" has lost connection userId:").append(userId)); } + ChatManager.getInstance().removeUser(userId, reason); } } @@ -143,6 +143,9 @@ public class UserManager { return false; } + /** + * Is the connection lost for more than 3 minutes, the user will be removed (within 3 minutes he can reconnect) + */ private void checkExpired() { Calendar expired = Calendar.getInstance(); expired.add(Calendar.MINUTE, -3) ; diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index d77e16adc41..e6b9a5078e7 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -112,9 +112,6 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { List users = new ArrayList<>(); for (User user : UserManager.getInstance().getUsers()) { StringBuilder sb = new StringBuilder(user.getGameInfo()); - if (!user.isConnected()) { - sb.append(" (discon.)"); - } users.add(new UsersView(user.getName(), user.getInfo(), sb.toString())); } Collections.sort(users, new UserNameSorter()); diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java index 234b2b823b2..faf20f30440 100644 --- a/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java +++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentController.java @@ -53,6 +53,7 @@ import mage.server.UserManager; import mage.server.draft.DraftController; import mage.server.draft.DraftManager; import mage.server.draft.DraftSession; +import mage.server.game.GameManager; import mage.server.game.GamesRoomManager; import mage.server.util.ThreadExecutor; import mage.view.ChatMessage.MessageColor; @@ -91,6 +92,9 @@ public class TournamentController { @Override public void event(TableEvent event) { switch (event.getEventType()) { + case CHECK_STATE_PLAYERS: + checkPlayersState(); + break; case INFO: ChatManager.getInstance().broadcast(chatId, "", event.getMessage(), MessageColor.BLACK, true, MessageType.STATUS); logger.debug(tournament.getId() + " " + event.getMessage()); @@ -214,8 +218,8 @@ public class TournamentController { table.setTournament(tournament); TournamentPlayer player1 = pair.getPlayer1(); TournamentPlayer player2 = pair.getPlayer2(); - tableManager.addPlayer(getPlayerSessionId(player1.getPlayer().getId()), table.getId(), player1.getPlayer(), player1.getPlayerType(), player1.getDeck()); - tableManager.addPlayer(getPlayerSessionId(player2.getPlayer().getId()), table.getId(), player2.getPlayer(), player2.getPlayerType(), player2.getDeck()); + tableManager.addPlayer(getPlayerUserId(player1.getPlayer().getId()), table.getId(), player1.getPlayer(), player1.getPlayerType(), player1.getDeck()); + tableManager.addPlayer(getPlayerUserId(player2.getPlayer().getId()), table.getId(), player2.getPlayer(), player2.getPlayerType(), player2.getDeck()); tableManager.startTournamentSubMatch(null, table.getId()); pair.setMatch(tableManager.getMatch(table.getId())); pair.setTableId(table.getId()); @@ -242,7 +246,7 @@ public class TournamentController { if (tournamentSessions.containsKey(playerId)) { TournamentSession tournamentSession = tournamentSessions.get(playerId); tournamentSession.construct(timeout); - UserManager.getInstance().getUser(getPlayerSessionId(playerId)).addConstructing(playerId, tournamentSession); + UserManager.getInstance().getUser(getPlayerUserId(playerId)).addConstructing(playerId, tournamentSession); TournamentPlayer player = tournament.getPlayer(playerId); player.setState(TournamentPlayerState.CONSTRUCTING); } @@ -285,6 +289,8 @@ public class TournamentController { String info; if (tournament.isDoneConstructing()) { info = new StringBuilder("during round ").append(tournament.getRounds().size()).toString(); + // quit active matches of that tournament + TableManager.getInstance().userQuitTournamentSubTables(tournament.getId(), userId); } else { if (tPlayer.getState().equals(TournamentPlayerState.DRAFTING)) { info = "during Draft phase"; @@ -344,7 +350,7 @@ public class TournamentController { return false; } - private UUID getPlayerSessionId(UUID playerId) { + private UUID getPlayerUserId(UUID playerId) { for (Entry entry: userPlayerMap.entrySet()) { if (entry.getValue().equals(playerId)) { return entry.getKey(); @@ -365,4 +371,28 @@ public class TournamentController { public boolean isAbort() { return tournament.isAbort(); } + + public boolean isPlayerAlive(UUID playerId) { + if (tournamentSessions.containsKey(playerId)) { + return tournamentSessions.get(playerId).isKilled(); + } + return false; + } + + private void checkPlayersState() { + for (TournamentPlayer tournamentPlayer: tournament.getPlayers()) { + if (!tournamentPlayer.getEliminated()) { + if (tournamentSessions.containsKey(tournamentPlayer.getPlayer().getId())) { + if (tournamentSessions.get(tournamentPlayer.getPlayer().getId()).isKilled()) { + tournamentPlayer.setEliminated(); + tournamentPlayer.setStateInfo("disconnected"); + } + } else { + tournamentPlayer.setEliminated(); + tournamentPlayer.setStateInfo("no tournament session"); + } + } + } + + } } diff --git a/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java b/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java index 9ea8b93a610..31053b1f169 100644 --- a/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java +++ b/Mage.Server/src/main/java/mage/server/tournament/TournamentSession.java @@ -39,7 +39,7 @@ import mage.interfaces.callback.ClientCallback; import mage.server.User; import mage.server.UserManager; import mage.server.util.ThreadExecutor; -import mage.view.*; +import mage.view.TournamentView; import org.apache.log4j.Logger; /** @@ -123,6 +123,10 @@ public class TournamentSession { killed = true; } + public boolean isKilled() { + return killed; + } + private synchronized void setupTimeout(int seconds) { if (futureTimeout != null && !futureTimeout.isDone()) { return; diff --git a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java index 661453db0cf..0d4964eb290 100644 --- a/Mage.Server/src/main/java/mage/server/util/SystemUtil.java +++ b/Mage.Server/src/main/java/mage/server/util/SystemUtil.java @@ -8,11 +8,13 @@ import mage.game.Game; import mage.players.Player; import java.io.File; +import java.util.Date; import java.util.HashSet; import java.util.List; import java.util.Random; import java.util.Scanner; import java.util.Set; +import java.util.concurrent.TimeUnit; import java.util.regex.Matcher; import java.util.regex.Pattern; @@ -164,4 +166,17 @@ public class SystemUtil { System.out.println(sanitize("anPlsdf123 ") + "|"); System.out.println(sanitize("anPlsdf123\r\n") + "|"); } + + /** + * Get a diff between two dates + * + * @param date1 the oldest date + * @param date2 the newest date + * @param timeUnit the unit in which you want the diff + * @return the diff value, in the provided unit + */ + public static long getDateDiff(Date date1, Date date2, TimeUnit timeUnit) { + long diffInMillies = date2.getTime() - date1.getTime(); + return timeUnit.convert(diffInMillies, TimeUnit.MILLISECONDS); + } } diff --git a/Mage.Sets/src/mage/sets/visions/NaturalOrder.java b/Mage.Sets/src/mage/sets/visions/NaturalOrder.java index aa9a9ebedc6..c71cc195c00 100644 --- a/Mage.Sets/src/mage/sets/visions/NaturalOrder.java +++ b/Mage.Sets/src/mage/sets/visions/NaturalOrder.java @@ -47,7 +47,7 @@ import mage.target.common.TargetControlledCreaturePermanent; public class NaturalOrder extends CardImpl { private static final FilterControlledCreaturePermanent filter = new FilterControlledCreaturePermanent("a green creature"); - private static final FilterCard filterCard = new FilterCard("a green creature card"); + private static final FilterCard filterCard = new FilterCard("green creature card"); static { filter.add(new ColorPredicate(ObjectColor.GREEN)); diff --git a/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java b/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java index ec9c7427d99..7a4e7cfa9ee 100644 --- a/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java +++ b/Mage.Tests/src/test/java/org/mage/test/load/LoadCallbackClient.java @@ -34,44 +34,62 @@ public class LoadCallbackClient implements CallbackClient { controlCount = 0; log.info(callback.getMethod()); callback.setData(CompressUtil.decompress(callback.getData())); - if (callback.getMethod().equals("startGame")) { - TableClientMessage message = (TableClientMessage) callback.getData(); - gameId = message.getGameId(); - playerId = message.getPlayerId(); - session.joinGame(message.getGameId()); - startControlThread(); - } else if (callback.getMethod().equals("gameInform")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - log.info("Inform: " + message.getMessage()); - gameView = message.getGameView(); - } else if (callback.getMethod().equals("gameInit")) { - - } else if (callback.getMethod().equals("gameTarget")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - log.info("Target: " + message.getMessage()); - if (message.getMessage().equals("Select a starting player")) { - session.sendPlayerUUID(gameId, playerId); - } else if (message.getMessage().equals("Select a card to discard")) { - log.info("hand size: " + gameView.getHand().size()); - SimpleCardView card = gameView.getHand().values().iterator().next(); - session.sendPlayerUUID(gameId, card.getId()); - } - } else if (callback.getMethod().equals("gameAsk")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - log.info("Ask: " + message.getMessage()); - if (message.getMessage().equals("Do you want to take a mulligan?")) { - session.sendPlayerBoolean(gameId, false); - } - } else if (callback.getMethod().equals("gameSelect")) { - GameClientMessage message = (GameClientMessage) callback.getData(); - log.info("Select: " + message.getMessage()); - if (LoadPhaseManager.getInstance().isSkip(message.getGameView(), message.getMessage(), playerId)) { - log.info("Skipped: " + message.getMessage()); - session.sendPlayerBoolean(gameId, false); - } - } else if (callback.getMethod().equals("gameOver")) { - log.info("Game over"); - gameOver = true; + switch (callback.getMethod()) { + case "startGame": + { + TableClientMessage message = (TableClientMessage) callback.getData(); + gameId = message.getGameId(); + playerId = message.getPlayerId(); + session.joinGame(message.getGameId()); + startControlThread(); + break; + } + case "gameInform": + { + GameClientMessage message = (GameClientMessage) callback.getData(); + log.info("Inform: " + message.getMessage()); + gameView = message.getGameView(); + break; + } + case "gameInit": + break; + case "gameTarget": + { + GameClientMessage message = (GameClientMessage) callback.getData(); + log.info("Target: " + message.getMessage()); + switch (message.getMessage()) { + case "Select a starting player": + session.sendPlayerUUID(gameId, playerId); + break; + case "Select a card to discard": + log.info("hand size: " + gameView.getHand().size()); + SimpleCardView card = gameView.getHand().values().iterator().next(); + session.sendPlayerUUID(gameId, card.getId()); + break; + } + break; + } + case "gameAsk": + { + GameClientMessage message = (GameClientMessage) callback.getData(); + log.info("Ask: " + message.getMessage()); + if (message.getMessage().equals("Do you want to take a mulligan?")) { + session.sendPlayerBoolean(gameId, false); + } break; + } + case "gameSelect": + { + GameClientMessage message = (GameClientMessage) callback.getData(); + log.info("Select: " + message.getMessage()); + if (LoadPhaseManager.getInstance().isSkip(message.getGameView(), message.getMessage(), playerId)) { + log.info("Skipped: " + message.getMessage()); + session.sendPlayerBoolean(gameId, false); + } break; + } + case "gameOver": + log.info("Game over"); + gameOver = true; + break; } } diff --git a/Mage/src/mage/game/events/TableEvent.java b/Mage/src/mage/game/events/TableEvent.java index eaf2568a0c9..f6409b770d9 100644 --- a/Mage/src/mage/game/events/TableEvent.java +++ b/Mage/src/mage/game/events/TableEvent.java @@ -46,7 +46,7 @@ public class TableEvent extends EventObject implements ExternalEvent, Serializab public enum EventType { UPDATE, INFO, STATUS, START_DRAFT, START_MATCH, SIDEBOARD, CONSTRUCT, SUBMIT_DECK, END, END_GAME_INFO, ERROR, - INIT_TIMER, RESUME_TIMER, PAUSE_TIMER + INIT_TIMER, RESUME_TIMER, PAUSE_TIMER, CHECK_STATE_PLAYERS } private Game game; diff --git a/Mage/src/mage/game/tournament/Round.java b/Mage/src/mage/game/tournament/Round.java index cffe60001ab..77c20d2096c 100644 --- a/Mage/src/mage/game/tournament/Round.java +++ b/Mage/src/mage/game/tournament/Round.java @@ -72,7 +72,7 @@ public class Round { public boolean isRoundOver() { boolean roundIsOver = true; for (TournamentPairing pair: pairs) { - if (!pair.getMatch().isMatchOver()) { + if (pair.getMatch() != null && !pair.getMatch().isMatchOver()) { roundIsOver = false; } else { if (!pair.isAlreadyPublished()) { diff --git a/Mage/src/mage/game/tournament/TournamentImpl.java b/Mage/src/mage/game/tournament/TournamentImpl.java index c8fad5e3a05..2a40e1efb51 100644 --- a/Mage/src/mage/game/tournament/TournamentImpl.java +++ b/Mage/src/mage/game/tournament/TournamentImpl.java @@ -199,6 +199,7 @@ public abstract class TournamentImpl implements Tournament { } protected void playRound(Round round) { + for (TournamentPairing pair: round.getPairs()) { playMatch(pair); } @@ -240,7 +241,7 @@ public abstract class TournamentImpl implements Tournament { UUID player1Id = pair.getPlayer1().getPlayer().getId(); UUID player2Id = pair.getPlayer2().getPlayer().getId(); Match match = pair.getMatch(); - if (match.isMatchOver()) { + if (match != null && match.isMatchOver()) { if (round.getRoundNumber() == rounds.size()) { if (players.get(player1Id).getState().equals(TournamentPlayerState.DUELING)) { players.get(player1Id).setState(TournamentPlayerState.WAITING); @@ -259,10 +260,10 @@ public abstract class TournamentImpl implements Tournament { sb2.append("-").append(match.getPlayer(player1Id).getWins()).append(") "); players.get(player1Id).setResults(sb1.toString()); players.get(player2Id).setResults(sb2.toString()); - if (match.getPlayer(player1Id).getWins() > match.getPlayer(player2Id).getWins()) { + if (match.getPlayer(player2Id).hasQuit() || match.getPlayer(player1Id).getWins() > match.getPlayer(player2Id).getWins()) { int points = players.get(player1Id).getPoints(); players.get(player1Id).setPoints(points + 3); - } else if (match.getPlayer(player1Id).getWins() < match.getPlayer(player2Id).getWins()) { + } else if (match.getPlayer(player1Id).hasQuit() || match.getPlayer(player1Id).getWins() < match.getPlayer(player2Id).getWins()) { int points = players.get(player2Id).getPoints(); players.get(player2Id).setPoints(points + 3); } else { diff --git a/Mage/src/mage/game/tournament/TournamentPlayer.java b/Mage/src/mage/game/tournament/TournamentPlayer.java index 56a1e1629cb..a052e6a589e 100644 --- a/Mage/src/mage/game/tournament/TournamentPlayer.java +++ b/Mage/src/mage/game/tournament/TournamentPlayer.java @@ -42,10 +42,11 @@ public class TournamentPlayer { protected int points; protected String playerType; protected TournamentPlayerState state; - protected String stateInfo = ""; + protected String stateInfo; + protected String disconnectInfo; protected Player player; protected Deck deck; - protected String results = ""; + protected String results; protected boolean eliminated = false; protected boolean quit = false; protected boolean doneConstructing; @@ -55,6 +56,10 @@ public class TournamentPlayer { this.player = player; this.playerType = playerType; this.state = TournamentPlayerState.JOINED; + this.stateInfo = ""; + this.disconnectInfo = ""; + this.results = ""; + } public Player getPlayer() { @@ -151,6 +156,14 @@ public class TournamentPlayer { this.stateInfo = stateInfo; } + public String getDisconnectInfo() { + return disconnectInfo; + } + + public void setDisconnectInfo(String disconnectInfo) { + this.disconnectInfo = disconnectInfo; + } + public boolean hasQuit() { return quit; } diff --git a/Mage/src/mage/game/tournament/TournamentSingleElimination.java b/Mage/src/mage/game/tournament/TournamentSingleElimination.java index 7217003101f..328d9c4ce1b 100644 --- a/Mage/src/mage/game/tournament/TournamentSingleElimination.java +++ b/Mage/src/mage/game/tournament/TournamentSingleElimination.java @@ -30,6 +30,7 @@ package mage.game.tournament; import java.util.Map; import java.util.UUID; +import mage.game.events.TableEvent; /** * @@ -43,15 +44,15 @@ public abstract class TournamentSingleElimination extends TournamentImpl { @Override protected void runTournament() { - for (Map.Entry entry: players.entrySet()) { if (entry.getValue().getPlayer().autoLoseGame()) { entry.getValue().setEliminated(); entry.getValue().setResults("Auto Eliminated"); } - } - + } while (this.getActivePlayers().size() > 1) { + // check if some player got killed / disconnected meanwhile and update their state + tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS); Round round = createRoundRandom(); playRound(round); eliminatePlayers(round); diff --git a/Mage/src/mage/game/tournament/TournamentSwiss.java b/Mage/src/mage/game/tournament/TournamentSwiss.java index 73ed33c36f3..cc647770035 100644 --- a/Mage/src/mage/game/tournament/TournamentSwiss.java +++ b/Mage/src/mage/game/tournament/TournamentSwiss.java @@ -33,6 +33,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; import mage.constants.TournamentPlayerState; +import mage.game.events.TableEvent; /** * @@ -45,7 +46,7 @@ public abstract class TournamentSwiss extends TournamentImpl { } @Override - protected void runTournament() { + protected void runTournament() { for (Map.Entry entry: players.entrySet()) { if (entry.getValue().getPlayer().autoLoseGame()) { entry.getValue().setEliminated(); @@ -54,6 +55,8 @@ public abstract class TournamentSwiss extends TournamentImpl { } while (this.getActivePlayers().size() > 1 && this.getNumberRounds() > this.getRounds().size()) { + // check if some player got killed / disconnected meanwhile and update their state + tableEventSource.fireTableEvent(TableEvent.EventType.CHECK_STATE_PLAYERS); // Swiss pairing Round round = createRoundSwiss(); playRound(round); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index d11fc47299f..67c3c47ef74 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -337,7 +337,7 @@ public abstract class PlayerImpl> implements Player, Ser this.wins = false; this.loses = false; this.left = false; - // quittet won't be reset because the player stays quit + this.quit = false; this.passed = false; this.passedTurn = false; this.passedAllTurns = false; @@ -1469,6 +1469,7 @@ public abstract class PlayerImpl> implements Player, Ser @Override public void quit(Game game) { + quit = true; this.concede(game); }