From c3c77e3974166a102620fabb87dc0b9daa8d87b2 Mon Sep 17 00:00:00 2001 From: LevelX2 Date: Sun, 7 Sep 2014 02:28:47 +0200 Subject: [PATCH] * Server - Some changes to user / match / table handling. --- .../java/mage/client/table/TablesPanel.java | 20 ++++---- Mage.Common/src/mage/remote/SessionImpl.java | 23 +++++++++ .../main/java/mage/server/ChatManager.java | 38 ++++++++------ .../main/java/mage/server/ChatSession.java | 13 ++++- .../main/java/mage/server/MageServerImpl.java | 7 +++ .../main/java/mage/server/SessionManager.java | 2 +- .../java/mage/server/TableController.java | 40 +++++++++++---- .../main/java/mage/server/TableManager.java | 49 ++++++++++++------- .../main/java/mage/server/UserManager.java | 2 +- .../java/mage/server/game/GamesRoomImpl.java | 6 +-- .../abilities/effects/ContinuousEffects.java | 17 ++++--- Mage/src/mage/game/Table.java | 9 ++++ Mage/src/mage/players/PlayerImpl.java | 8 +-- 13 files changed, 164 insertions(+), 70 deletions(-) diff --git a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java index 774b39d574..8537cc77dc 100644 --- a/Mage.Client/src/main/java/mage/client/table/TablesPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/TablesPanel.java @@ -747,12 +747,12 @@ class TableTableModel extends AbstractTableModel { case 9: switch (tables[arg0].getTableState()) { - case WAITING: - String owner = tables[arg0].getControllerName(); - if (session != null && owner.equals(session.getUserName())) { - return "Remove"; - } - return "Join"; +// case WAITING: +// String owner = tables[arg0].getControllerName(); +// if (session != null && owner.equals(session.getUserName())) { +// return "Remove"; +// } +// return "Join"; case CONSTRUCTING: case DRAFTING: if (tables[arg0].isTournament()) { @@ -762,10 +762,10 @@ class TableTableModel extends AbstractTableModel { if (tables[arg0].isTournament()) { return "Show"; } else { - owner = tables[arg0].getControllerName(); - if (session != null && owner.equals(session.getUserName())) { - return "Remove"; - } +// String owner = tables[arg0].getControllerName(); +// if (session != null && owner.equals(session.getUserName())) { +// return "Remove"; +// } return "Watch"; } default: diff --git a/Mage.Common/src/mage/remote/SessionImpl.java b/Mage.Common/src/mage/remote/SessionImpl.java index a0d2d07c8c..afbd044caa 100644 --- a/Mage.Common/src/mage/remote/SessionImpl.java +++ b/Mage.Common/src/mage/remote/SessionImpl.java @@ -218,6 +218,24 @@ public class SessionImpl implements Session { */ clientMetadata.put("numberOfCallRetries", "1"); + + /** + * I'll explain the meaning of "secondaryBindPort" and "secondaryConnectPort", and maybe that will help. + * The Remoting bisocket transport creates two ServerSockets on the server. The "primary" ServerSocket is used to create + * connections used for ordinary invocations, e.g., a request to create a JMS consumer, and the "secondary" ServerSocket + * is used to create "control" connections for internal Remoting messages. The port for the primary ServerSocket is configured + * by the "serverBindPort" parameter, and the port for the secondary ServerSocket is, by default, chosen randomly. + * The "secondaryBindPort" parameter can be used to assign a specific port to the secondary ServerSocket. Now, if there is a + * translating firewall between the client and server, the client should be given the value of the port that is translated + * to the actual binding port of the secondary ServerSocket. + * For example, your configuration will tell the secondary ServerSocket to bind to port 14000, and it will tell the client to + * connect to port 14001. It assumes that there is a firewall which will translate 14001 to 14000. Apparently, that's not happening. + */ + // secondaryBindPort - the port to which the secondary server socket is to be bound. By default, an arbitrary port is selected. + + // secondaryConnectPort - the port clients are to use to connect to the secondary server socket. + // By default, the value of secondaryBindPort is used. secondaryConnectPort is useful if the server is behind a translating firewall. + // Indicated the max number of threads used within oneway thread pool. clientMetadata.put(Client.MAX_NUM_ONEWAY_THREADS, "10"); clientMetadata.put(Remoting.USE_CLIENT_CONNECTION_IDENTITY, "true"); @@ -913,6 +931,11 @@ public class SessionImpl implements Session { return false; } + /** + * Remove table - called from admin console + * @param tableId + * @return + */ @Override public boolean removeTable(UUID tableId) { try { diff --git a/Mage.Server/src/main/java/mage/server/ChatManager.java b/Mage.Server/src/main/java/mage/server/ChatManager.java index f6bfdd6337..1d7bd7c955 100644 --- a/Mage.Server/src/main/java/mage/server/ChatManager.java +++ b/Mage.Server/src/main/java/mage/server/ChatManager.java @@ -72,8 +72,9 @@ public class ChatManager { } public void leaveChat(UUID chatId, UUID userId) { - if (chatSessions.containsKey(chatId)) { - chatSessions.get(chatId).kill(userId, DisconnectReason.CleaningUp); + ChatSession chatSession = chatSessions.get(chatId); + if (chatSession != null && chatSession.hasUser(userId)) { + chatSession.kill(userId, DisconnectReason.CleaningUp); } } @@ -106,30 +107,33 @@ public class ChatManager { } public void broadcast(UUID chatId, String userName, String message, MessageColor color, boolean withTime, MessageType messageType, SoundToPlay soundToPlay) { - if (message.startsWith("\\")) { - User user = UserManager.getInstance().findUser(userName); - if (user != null && performUserCommand(user, message, chatId)) { - return; + ChatSession chatSession = chatSessions.get(chatId); + if (chatSession != null) { + if (message.startsWith("\\") || message.startsWith("/")) { + User user = UserManager.getInstance().findUser(userName); + if (user != null && performUserCommand(user, message, chatId)) { + return; + } } + chatSession.broadcast(userName, message, color, withTime, messageType, soundToPlay); } - chatSessions.get(chatId).broadcast(userName, message, color, withTime, messageType, soundToPlay); } private boolean performUserCommand(User user, String message, UUID chatId) { - String command = message.trim().toUpperCase(Locale.ENGLISH); - if (command.equals("\\I") || command.equals("\\INFO")) { + String command = message.substring(1).trim().toUpperCase(Locale.ENGLISH); + if (command.equals("I") || command.equals("INFO")) { user.setInfo(""); chatSessions.get(chatId).broadcastInfoToUser(user,message); return true; } - if (command.startsWith("\\I ") || command.startsWith("\\INFO ")) { - user.setInfo(message.substring(command.startsWith("\\I ") ? 3 : 6)); + if (command.startsWith("I ") || command.startsWith("INFO ")) { + user.setInfo(message.substring(command.startsWith("I ") ? 3 : 6)); chatSessions.get(chatId).broadcastInfoToUser(user,message); return true; } - if (command.startsWith("\\W ") || command.startsWith("\\WHISPER ") || command.startsWith("/W ") || command.startsWith("/WHISPER ")) { - String rest = message.substring(command.startsWith("\\W ") || command.startsWith("/W ")? 3 : 9); + if (command.startsWith("W ") || command.startsWith("WHISPER ")) { + String rest = message.substring(command.startsWith("W ")? 3 : 9); int first = rest.indexOf(" "); if (first > 1) { String userToName = rest.substring(0,first); @@ -147,7 +151,7 @@ public class ChatManager { return true; } } - if (command.equals("\\L") || command.equals("\\LIST")) { + if (command.equals("L") || command.equals("LIST")) { message += new StringBuilder("\nList of commands:") .append("\n\\info - set a info text to your player") .append("\n\\list - Show a list of commands") @@ -191,8 +195,10 @@ public class ChatManager { } public void removeUser(UUID userId, DisconnectReason reason) { - for (ChatSession chat: chatSessions.values()) { - chat.kill(userId, reason); + for (ChatSession chatSession: chatSessions.values()) { + if (chatSession.hasUser(userId)) { + chatSession.kill(userId, reason); + } } } diff --git a/Mage.Server/src/main/java/mage/server/ChatSession.java b/Mage.Server/src/main/java/mage/server/ChatSession.java index e866fb4f6f..7051942c19 100644 --- a/Mage.Server/src/main/java/mage/server/ChatSession.java +++ b/Mage.Server/src/main/java/mage/server/ChatSession.java @@ -31,6 +31,7 @@ package mage.server; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Date; +import java.util.HashSet; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; import mage.interfaces.callback.ClientCallback; @@ -52,6 +53,7 @@ public class ChatSession { private final Date createTime; private final String info; private final DateFormat timeFormatter = SimpleDateFormat.getTimeInstance(SimpleDateFormat.SHORT); + private final HashSet clientsToRemove = new HashSet<>(); public ChatSession(String info) { chatId = UUID.randomUUID(); @@ -148,7 +150,8 @@ public class ChatSession { } public void broadcast(String userName, String message, MessageColor color, boolean withTime, MessageType messageType, SoundToPlay soundToPlay) { - if (!message.isEmpty()) { + if (!message.isEmpty()) { + boolean remove = false; final String msg = message; final String time = (withTime ? timeFormatter.format(new Date()):""); final String username = userName; @@ -160,8 +163,16 @@ public class ChatSession { } else { logger.error("User not found but connected to chat - userId: " + userId + " chatId: " + chatId); + clientsToRemove.add(userId); + remove = true; } } + if (remove) { + for (UUID userIdToRemove: clientsToRemove) { + clients.remove(userIdToRemove); + } + clientsToRemove.clear(); + } } } diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index 8e5fc15cdb..ccd474d269 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -1016,6 +1016,13 @@ public class MageServerImpl implements MageServer { }); } + /** + * Admin console - Remove table + * + * @param sessionId + * @param tableId + * @throws MageException + */ @Override public void removeTable(final String sessionId, final UUID tableId) throws MageException { execute("removeTable", sessionId, new Action() { diff --git a/Mage.Server/src/main/java/mage/server/SessionManager.java b/Mage.Server/src/main/java/mage/server/SessionManager.java index 604984d1d4..d3199830d6 100644 --- a/Mage.Server/src/main/java/mage/server/SessionManager.java +++ b/Mage.Server/src/main/java/mage/server/SessionManager.java @@ -59,7 +59,7 @@ public class SessionManager { if (session != null && session.getUserId() != null && UserManager.getInstance().getUser(session.getUserId()) == null) { logger.error("User for session " + sessionId + " with userId " + session.getUserId() + " is missing. Session removed."); // can happen if user from same host signs in multiple time with multiple clients, after he disconnects with one client - disconnect(sessionId, DisconnectReason.LostConnection); + disconnect(sessionId, DisconnectReason.ConnectingOtherInstance); return null; } return session; diff --git a/Mage.Server/src/main/java/mage/server/TableController.java b/Mage.Server/src/main/java/mage/server/TableController.java index 6258a363e8..7746cec845 100644 --- a/Mage.Server/src/main/java/mage/server/TableController.java +++ b/Mage.Server/src/main/java/mage/server/TableController.java @@ -372,6 +372,13 @@ public class TableController { return player; } + public void leaveTableAll() { + for (UUID leavingUserId: userPlayerMap.keySet()) { + leaveTable(leavingUserId); + } + closeTable(); + } + public synchronized void leaveTable(UUID userId) { if (table == null) { logger.error("No table object - userId: " + userId); @@ -413,14 +420,23 @@ public class TableController { TournamentManager.getInstance().quit(tournament.getId(), userId); } 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()); + if (matchPlayer != null && !match.hasEnded() && !matchPlayer.hasQuit()) { + Game game = match.getGame(); + if (game != null && !game.hasEnded()){ + Player player = match.getPlayer(playerId).getPlayer(); + if (player != null && player.isInGame()) { + GameManager.getInstance().quitMatch(game.getId(), userId); + } + } else { + if (table.getState().equals(TableState.SIDEBOARDING)) { + if (!matchPlayer.isDoneSideboarding()) { + // submit deck to finish sideboarding and trigger match start / end + matchPlayer.submitDeck(matchPlayer.getDeck()); + } + } + match.leave(playerId); } - matchPlayer.setQuit(true); } - match.leave(playerId); } } } else { @@ -829,11 +845,11 @@ public class TableController { continue; } if (matchPlayer.getPlayer().isHuman()) { - humanPlayers++; - User user = UserManager.getInstance().getUser(userPlayerEntry.getKey()); + humanPlayers++; if (!matchPlayer.hasQuit()) { + User user = UserManager.getInstance().getUser(userPlayerEntry.getKey()); if (user == null) { - logger.debug("- Active user of match is missing:"); + logger.debug("- Active user of match is missing: " + matchPlayer.getName()); logger.debug("-- matchId:" + match.getId()); logger.debug("-- userId:" + userPlayerEntry.getKey()); logger.debug("-- playerId:" + userPlayerEntry.getValue()); @@ -869,10 +885,14 @@ public class TableController { } break; case STARTING: - if (!getTable().getState().equals(TableState.READY_TO_START)){ + if (!getTable().getState().equals(TableState.READY_TO_START)) { // tournament is not ready, can't start return false; } + if (!table.allSeatsAreOccupied()) { + logger.debug("Not alle Seats are occupied: stop start tableId:" + table.getId()); + return false; + } break; } getTable().setState(newTableState); diff --git a/Mage.Server/src/main/java/mage/server/TableManager.java b/Mage.Server/src/main/java/mage/server/TableManager.java index fc2a9b6a77..a8c858d015 100644 --- a/Mage.Server/src/main/java/mage/server/TableManager.java +++ b/Mage.Server/src/main/java/mage/server/TableManager.java @@ -31,8 +31,8 @@ package mage.server; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; -import java.util.Date; import java.util.List; import java.util.Map.Entry; import java.util.UUID; @@ -155,7 +155,13 @@ public class TableManager { if (controllers.containsKey(tableId)) { return controllers.get(tableId).submitDeck(userId, deckList); } - return false; + User user = UserManager.getInstance().getUser(userId); + if (user != null) { + user.removeSideboarding(tableId); + user.showUserMessage("Submit deck", "Table no longer active"); + } + // return true so the panel closes + return true; } public void updateDeck(UUID userId, UUID tableId, DeckCardLists deckList) throws MageException { @@ -201,10 +207,11 @@ public class TableManager { } public boolean removeTable(UUID userId, UUID tableId) { - if (isTableOwner(tableId, userId) || UserManager.getInstance().isAdmin(userId)) { - leaveTable(userId, tableId); + if (UserManager.getInstance().isAdmin(userId)) { + logger.debug("Table remove request - userId: " + userId + " tableId: " + tableId); TableController tableController = controllers.get(tableId); if (tableController != null) { + tableController.leaveTableAll(); ChatManager.getInstance().destroyChatSession(tableController.getChatId()); removeTable(tableId); } @@ -372,22 +379,30 @@ public class TableManager { } logger.debug("TABLE HEALTH CHECK"); List toRemove = new ArrayList<>(); - for (Table table : tables.values()) { - if (!table.getState().equals(TableState.FINISHED)) { - // remove tables and games not valid anymore - logger.debug(table.getId() + " [" + table.getName()+ "] " + formatter.format(table.getStartTime()) +" (" + table.getState().toString() + ") " + (table.isTournament() ? "- Tournament":"")); - TableController tableController = getController(table.getId()); - if (tableController != null) { - if (table.isTournament()) { - if (!tableController.isTournamentStillValid()) { - toRemove.add(table.getId()); - } - } else { - if (!tableController.isMatchTableStillValid()) { - toRemove.add(table.getId()); + + ArrayList tableCopy = new ArrayList<>(); + tableCopy.addAll(tables.values()); + for (Table table : tableCopy) { + try { + if (!table.getState().equals(TableState.FINISHED)) { + // remove tables and games not valid anymore + logger.debug(table.getId() + " [" + table.getName()+ "] " + formatter.format(table.getStartTime()) +" (" + table.getState().toString() + ") " + (table.isTournament() ? "- Tournament":"")); + TableController tableController = getController(table.getId()); + if (tableController != null) { + if (table.isTournament()) { + if (!tableController.isTournamentStillValid()) { + toRemove.add(table.getId()); + } + } else { + if (!tableController.isMatchTableStillValid()) { + toRemove.add(table.getId()); + } } } } + } catch (Exception ex) { + logger.debug("Table Health check error tableId: " + table.getId()); + logger.debug(Arrays.toString(ex.getStackTrace())); } } for (UUID tableId : toRemove) { diff --git a/Mage.Server/src/main/java/mage/server/UserManager.java b/Mage.Server/src/main/java/mage/server/UserManager.java index 97bfc042a7..dad2fc575f 100644 --- a/Mage.Server/src/main/java/mage/server/UserManager.java +++ b/Mage.Server/src/main/java/mage/server/UserManager.java @@ -193,7 +193,7 @@ public class UserManager { public void handleException(Exception ex) { if (ex != null) { - logger.fatal("User manager exception" + (ex.getMessage() == null ? "null":ex.getMessage())); + logger.fatal("User manager exception " + (ex.getMessage() == null ? "null":ex.getMessage())); if (ex.getCause() != null) { logger.debug("- Cause: " + (ex.getCause().getMessage() == null ? "null":ex.getCause().getMessage())); } 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 3ed422018a..b6de8b227a 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -219,15 +219,15 @@ class TableListSorter implements Comparator
{ @Override public int compare(Table one, Table two) { // priority 1 - Not started yet - if (one.getState().equals(TableState.READY_TO_START) || one.getState().equals(TableState.WAITING)) { - if (two.getState().equals(TableState.READY_TO_START) || two.getState().equals(TableState.WAITING)) { + if (one.getState().equals(TableState.READY_TO_START) || one.getState().equals(TableState.WAITING) || one.getState().equals(TableState.STARTING)) { + if (two.getState().equals(TableState.READY_TO_START) || two.getState().equals(TableState.WAITING) || two.getState().equals(TableState.STARTING)) { return two.getCreateTime().compareTo(one.getCreateTime()); } else { return -1; // one has higher priority } } // priority 2 - Not finished yet -> Sorted by time started - if (two.getState().equals(TableState.READY_TO_START) || two.getState().equals(TableState.WAITING)) { + if (two.getState().equals(TableState.READY_TO_START) || two.getState().equals(TableState.WAITING) || two.getState().equals(TableState.STARTING)) { return 1; // two has higher priority } else if (one.getEndTime() == null) { if (two.getEndTime() == null) { diff --git a/Mage/src/mage/abilities/effects/ContinuousEffects.java b/Mage/src/mage/abilities/effects/ContinuousEffects.java index a20ab2b87a..ffc82a32a4 100644 --- a/Mage/src/mage/abilities/effects/ContinuousEffects.java +++ b/Mage/src/mage/abilities/effects/ContinuousEffects.java @@ -983,15 +983,18 @@ public class ContinuousEffects implements Serializable { public List getReplacementEffectsTexts(HashMap> rEffects, Game game) { List texts = new ArrayList<>(); for (Map.Entry> entry : rEffects.entrySet()) { - for (Ability ability :entry.getValue()) { - MageObject object = game.getObject(ability.getSourceId()); - if (object != null) { - texts.add(ability.getRule(object.getLogName())); - } else { - texts.add(entry.getKey().getText(null)); + if (entry.getValue() != null) { + for (Ability ability :entry.getValue()) { + MageObject object = game.getObject(ability.getSourceId()); + if (object != null) { + texts.add(ability.getRule(object.getLogName())); + } else { + texts.add(entry.getKey().getText(null)); + } } + } else { + logger.error("Replacement effect without ability: " + entry.getKey().toString()); } - } return texts; } diff --git a/Mage/src/mage/game/Table.java b/Mage/src/mage/game/Table.java index bdb51be33f..249b318d7e 100644 --- a/Mage/src/mage/game/Table.java +++ b/Mage/src/mage/game/Table.java @@ -203,6 +203,15 @@ public class Table implements Serializable { return null; } + public boolean allSeatsAreOccupied() { + for (int i = 0; i < numSeats; i++ ) { + if (seats[i].getPlayer() == null) { + return false; + } + } + return true; + } + public void leaveNotStartedTable(UUID playerId) { for (int i = 0; i < numSeats; i++ ) { Player player = seats[i].getPlayer(); diff --git a/Mage/src/mage/players/PlayerImpl.java b/Mage/src/mage/players/PlayerImpl.java index 241595f7f2..9eedcb4c8e 100644 --- a/Mage/src/mage/players/PlayerImpl.java +++ b/Mage/src/mage/players/PlayerImpl.java @@ -1648,8 +1648,8 @@ public abstract class PlayerImpl implements Player, Serializable { @Override public void lost(Game game) { - logger.debug(this.getName() + " has lost gameId: " + game.getId()); - if (canLose(game)) { + if (canLose(game)) { + logger.debug(this.getName() + " has lost gameId: " + game.getId()); //20100423 - 603.9 if (!this.wins) { this.loses = true; @@ -1672,9 +1672,9 @@ public abstract class PlayerImpl implements Player, Serializable { } @Override - public void won(Game game) { - logger.debug("player won -> start: " + this.getName()); + public void won(Game game) { if (!game.replaceEvent(new GameEvent(GameEvent.EventType.WINS, null, null, playerId))) { + logger.debug("player won -> start: " + this.getName()); if (!this.loses) { //20130501 - 800.7, 801.16 // all opponents in range loose the game