diff --git a/Mage.Client/src/main/java/mage/client/cards/Cards.java b/Mage.Client/src/main/java/mage/client/cards/Cards.java index 31a6df9fe0f..d0c8395ac31 100644 --- a/Mage.Client/src/main/java/mage/client/cards/Cards.java +++ b/Mage.Client/src/main/java/mage/client/cards/Cards.java @@ -20,7 +20,7 @@ import java.util.*; /** - * Panel for stack and hand zones, component for lookAt and reveal windows (CardInfoWindowDialog) + * GUI: panel for stack and hand zones, component for lookAt and reveal windows (CardInfoWindowDialog) * * @author BetaSteward_at_googlemail.com, JayDi85 */ diff --git a/Mage.Client/src/main/java/mage/client/components/MageUI.java b/Mage.Client/src/main/java/mage/client/components/MageUI.java index 19b6d3aba8d..cf2472f5132 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageUI.java +++ b/Mage.Client/src/main/java/mage/client/components/MageUI.java @@ -6,10 +6,7 @@ import org.apache.log4j.Logger; import java.awt.Component; import java.util.EnumMap; import java.util.Map; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import javax.swing.JButton; public class MageUI { @@ -38,7 +35,7 @@ public class MageUI { // catch errors in popup threads (example: card popup over cards or chat/log messages) t = ThreadUtils.findRealException(r, t); - if (t != null) { + if (t != null && !(t instanceof CancellationException)) { logger.error("Catch unhandled error in POPUP thread: " + t.getMessage(), t); } } diff --git a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java index 1ebc5ac7379..1fa20a5568b 100644 --- a/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java +++ b/Mage.Client/src/main/java/mage/client/plugins/adapters/MageActionCallback.java @@ -36,10 +36,7 @@ import java.awt.event.MouseEvent; import java.awt.image.BufferedImage; import java.util.List; import java.util.*; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; /** * Class that handles the callbacks from the card panels to mage to display big @@ -68,11 +65,11 @@ public class MageActionCallback implements ActionCallback { // higher value -> the effect will appear earlier (depends on the card size) public static final int HAND_CARDS_COMPARE_GAP_X = 30; + public static final int HAND_CARDS_MIN_DISTANCE_TO_START_DRAGGING = 20; // do not drag on small distance (click instead) + public static final int GO_DOWN_ON_DRAG_Y_OFFSET = 0; public static final int GO_UP_ON_DRAG_Y_OFFSET = 0; - public static final int MIN_X_OFFSET_REQUIRED = 20; - private Popup tooltipPopup; private BigCard bigCard; @@ -275,24 +272,29 @@ public class MageActionCallback implements ActionCallback { clearDragging(cardPanel); this.startedDragging = false; - if (maxXOffset < MIN_X_OFFSET_REQUIRED) { // we need this for protection from small card movements - // default click simulation // TODO: replace to normal clicked events (change dialogs) - cardPanel.requestFocusInWindow(); - DefaultActionCallback.instance.mouseClicked(data.getGameId(), data.getCard()); - // Closes popup & enlarged view if a card/Permanent is selected - hideTooltipPopup(); + if (isDragging && maxXOffset < HAND_CARDS_MIN_DISTANCE_TO_START_DRAGGING) { + // if user returned card to original place + // outdated code, HAND_CARDS_MIN_DISTANCE_TO_START_DRAGGING already used for wrong drag protection, + // so no needs in additional clicks here + //logger.info("User drag card to original place"); + //simulateCardClick(data); } e.consume(); } else { // default click simulation - cardPanel.requestFocusInWindow(); - DefaultActionCallback.instance.mouseClicked(data.getGameId(), data.getCard()); - // Closes popup & enlarged view if a card/Permanent is selected - hideTooltipPopup(); + simulateCardClick(data); e.consume(); } } + private void simulateCardClick(TransferData data) { + MageCard cardPanel = data.getComponent().getTopPanelRef(); + cardPanel.requestFocusInWindow(); + DefaultActionCallback.instance.mouseClicked(data.getGameId(), data.getCard()); + // closes popup & enlarged view if a card/permanent is selected + hideTooltipPopup(); + } + private void clearDragging(MageCard clearCard) { if (this.startedDragging && prevCardPanel != null && clearCard != null) { // distribute cards between cards container and a drag container @@ -314,7 +316,6 @@ public class MageActionCallback implements ActionCallback { @Override public void mouseMoved(MouseEvent e, TransferData data) { // MouseEvent can be null for custom hints calls, e.g. from choose dialog - if (!Plugins.instance.isCardPluginLoaded()) { return; } @@ -341,14 +342,22 @@ public class MageActionCallback implements ActionCallback { // only allow draging with the left mouse button return; } + + Point mouse = new Point(e.getX(), e.getY()); + SwingUtilities.convertPointToScreen(mouse, data.getComponent()); + if (!isDragging + && Math.abs(mouse.x - initialMousePos.x) < HAND_CARDS_MIN_DISTANCE_TO_START_DRAGGING + && Math.abs(mouse.y - initialMousePos.y) < HAND_CARDS_MIN_DISTANCE_TO_START_DRAGGING) { + // users do clicks while mouse moving, so it's not a drag and must be ignored + return; + } + isDragging = true; prevCardPanel = cardPanel; Point cardPanelLocationOld = cardPanel.getCardLocation().getCardPoint(); - Point mouse = new Point(e.getX(), e.getY()); - SwingUtilities.convertPointToScreen(mouse, data.getComponent()); int xOffset = 0; // starting position - int newX = Math.max(initialCardPos.x + (int) (mouse.getX() - initialMousePos.x) - xOffset, 0); // TODO: fix + int newX = Math.max(initialCardPos.x + (int) (mouse.getX() - initialMousePos.x) - xOffset, 0); cardPanel.setCardBounds( newX, cardPanelLocationOld.y, @@ -432,7 +441,8 @@ public class MageActionCallback implements ActionCallback { private void sortAndAnimateDraggingHandCards(List cards, MageCard source, boolean includeSource) { // special offset, allows to switch with first card - source.setCardLocation(source.getCardLocation().getCardX() - HAND_CARDS_COMPARE_GAP_X, source.getCardLocation().getCardY()); + int draggingOffsetX = 0; // if you need side effect while moving then use HAND_CARDS_COMPARE_GAP_X (but it looks bad) + source.setCardLocation(source.getCardLocation().getCardX() - draggingOffsetX, source.getCardLocation().getCardY()); // sorting card components, so the effect above will be applied too cards.sort(Comparator.comparingInt(cp -> cp.getCardLocation().getCardX())); diff --git a/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java b/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java index f85d95b7933..25f0390fea5 100644 --- a/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java +++ b/Mage.Client/src/main/java/mage/client/util/audio/LinePool.java @@ -7,10 +7,7 @@ import java.util.Queue; import java.util.Set; import java.util.Timer; import java.util.TimerTask; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadFactory; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.*; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioSystem; @@ -45,7 +42,7 @@ public class LinePool { protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); t = ThreadUtils.findRealException(r, t); - if (t != null) { + if (t != null && !(t instanceof CancellationException)) { // TODO: show sound errors in client logs? //logger.error("Catch unhandled error in SOUND thread: " + t.getMessage(), t); } diff --git a/Mage.Server/src/main/java/mage/server/game/GameController.java b/Mage.Server/src/main/java/mage/server/game/GameController.java index a0a563d694c..6bcfca3141a 100644 --- a/Mage.Server/src/main/java/mage/server/game/GameController.java +++ b/Mage.Server/src/main/java/mage/server/game/GameController.java @@ -317,6 +317,7 @@ public class GameController implements GameCallback { } user.get().addGame(playerId, gameSession); logger.debug("Player " + player.getName() + ' ' + playerId + " has " + joinType + " gameId: " + game.getId()); + // TODO: force user update?! managerFactory.chatManager().broadcast(chatId, "", game.getPlayer(playerId).getLogName() + " has " + joinType + " the game", MessageColor.ORANGE, true, game, MessageType.GAME, null); checkStart(); } diff --git a/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java b/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java index 386cd4fa3b1..e6c5c747421 100644 --- a/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java +++ b/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java @@ -65,7 +65,7 @@ public class ThreadExecutorImpl implements ThreadExecutor { // catch errors in CALL threads (from client commands) t = ThreadUtils.findRealException(r, t); - if (t != null) { + if (t != null && !(t instanceof CancellationException)) { logger.error("Catch unhandled error in CALL thread: " + t.getMessage(), t); } } @@ -84,7 +84,7 @@ public class ThreadExecutorImpl implements ThreadExecutor { // catch errors in GAME threads (from game processing) t = ThreadUtils.findRealException(r, t); - if (t != null) { + if (t != null && !(t instanceof CancellationException)) { // it's impossible to brake game thread in normal use case, so each bad use case must be researched logger.error("Catch unhandled error in GAME thread: " + t.getMessage(), t); }