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 cf2472f5132..6e263f0751e 100644 --- a/Mage.Client/src/main/java/mage/client/components/MageUI.java +++ b/Mage.Client/src/main/java/mage/client/components/MageUI.java @@ -34,7 +34,7 @@ public class MageUI { super.afterExecute(r, t); // catch errors in popup threads (example: card popup over cards or chat/log messages) - t = ThreadUtils.findRealException(r, t); + t = ThreadUtils.findRunnableException(r, t); 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/dialog/ConnectDialog.java b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java index ed643e1c675..cc9154a678f 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/ConnectDialog.java @@ -11,6 +11,7 @@ import mage.client.util.gui.countryBox.CountryItemEditor; import mage.client.util.sets.ConstructedFormats; import mage.remote.Connection; import mage.utils.StreamUtils; +import mage.utils.ThreadUtils; import org.apache.log4j.Logger; import javax.swing.*; @@ -61,6 +62,7 @@ public class ConnectDialog extends MageDialog { } public void showDialog() { + this.lblStatus.setText(""); String serverAddress = MagePreferences.getServerAddressWithDefault(ClientDefaultSettings.serverName); this.txtServer.setText(serverAddress); this.txtPort.setText(Integer.toString(MagePreferences.getServerPortWithDefault(ClientDefaultSettings.port))); @@ -83,6 +85,7 @@ public class ConnectDialog extends MageDialog { } private void saveSettings() { + ThreadUtils.sleep(3000); String serverAddress = txtServer.getText().trim(); MagePreferences.setServerAddress(serverAddress); MagePreferences.setServerPort(Integer.parseInt(txtPort.getText().trim())); @@ -678,8 +681,10 @@ public class ConnectDialog extends MageDialog { @Override protected Boolean doInBackground() throws Exception { - lblStatus.setText("Connecting..."); - setConnectButtonsState(false); + SwingUtilities.invokeLater(() -> { + lblStatus.setText("Connecting..."); + setConnectButtonsState(false); + }); result = MageFrame.connect(connection); lastConnectError = SessionHandler.getLastConnectError(); return result; @@ -688,32 +693,44 @@ public class ConnectDialog extends MageDialog { @Override protected void done() { try { - get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS); - if (result) { - lblStatus.setText(""); - connected(); - MageFrame.getInstance().prepareAndShowTablesPane(); - } else { - lblStatus.setText("Could not connect: " + lastConnectError); - } + get(CONNECTION_TIMEOUT_MS, TimeUnit.MILLISECONDS); // catch exceptions + + SwingUtilities.invokeLater(() -> { + if (result) { + lblStatus.setText("Connected"); + + // for ux: after connection client can load additional resources and data, + // so the connection dialog will be visible all that time + SwingUtilities.invokeLater(() -> { + doAfterConnected(); + MageFrame.getInstance().prepareAndShowTablesPane(); + btnConnect.setEnabled(true); + }); + } else { + lblStatus.setText("Could not connect: " + lastConnectError); + } + }); } catch (InterruptedException | ExecutionException ex) { - logger.fatal("Update Players Task error", ex); + logger.fatal("Connection: can't load data from server", ex); } catch (CancellationException ex) { logger.info("Connect: canceled"); lblStatus.setText("Connect was canceled"); } catch (TimeoutException ex) { - logger.fatal("Connection timeout: ", ex); + logger.fatal("Connection: timeout", ex); } finally { - MageFrame.stopConnecting(); - setConnectButtonsState(true); + if (!result) { + MageFrame.stopConnecting(); + SwingUtilities.invokeLater(() -> { + setConnectButtonsState(true); + }); + } } } } - private void connected() { + private void doAfterConnected() { this.saveSettings(); this.hideDialog(); - ConstructedFormats.ensureLists(); } private void keyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_keyTyped 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 25f0390fea5..45df3ad77d6 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 @@ -41,7 +41,7 @@ public class LinePool { @Override protected void afterExecute(Runnable r, Throwable t) { super.afterExecute(r, t); - t = ThreadUtils.findRealException(r, t); + t = ThreadUtils.findRunnableException(r, t); 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.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java index 91ac15a4fb2..a3a597da16b 100644 --- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java +++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java @@ -18,6 +18,7 @@ import mage.interfaces.callback.ClientCallback; import mage.players.PlayerType; import mage.players.net.UserData; import mage.utils.CompressUtil; +import mage.utils.ThreadUtils; import mage.view.*; import org.apache.log4j.Logger; import org.jboss.remoting.*; @@ -118,10 +119,13 @@ public class SessionImpl implements Session { } private void showMessageToUser(String message) { + if (message == null) { + message = "Unknown error, look at logs for details"; + } if (message.contains("free port for use")) { message += " (try to close and restart a client app)"; } - client.showMessage("Remote task error. " + message); + client.showMessage("Remote task error: " + message); } private boolean doRemoteWorkAndHandleErrors(boolean closeConnectionOnFinish, boolean mustWaitServerMessageOnFail, @@ -180,17 +184,18 @@ public class SessionImpl implements Session { logger.warn("Connect: wrong versions"); connectStop(false); if (!canceled) { - showMessageToUser(ex.getMessage()); + showMessageToUser(ex.toString()); } } catch (CannotConnectException ex) { if (!canceled) { handleCannotConnectException(ex); } } catch (Throwable t) { + Throwable ex = ThreadUtils.findRootException(t); logger.fatal("Connect: FAIL", t); connectStop(false); if (!canceled) { - showMessageToUser(t.getMessage()); + showMessageToUser(ex.toString()); } } finally { lastRemotingTask = null; @@ -470,6 +475,8 @@ public class SessionImpl implements Session { private void handleCannotConnectException(CannotConnectException ex) { logger.warn("Cannot connect", ex); + + // try to find a known error Throwable t = ex.getCause(); String message = ""; while (t != null) { @@ -493,6 +500,7 @@ public class SessionImpl implements Session { t = t.getCause(); } client.showMessage("Unable connect to server. " + message); + setLastError(message); if (logger.isTraceEnabled()) { logger.trace("StackTrace", t); } diff --git a/Mage.Common/src/main/java/mage/utils/ThreadUtils.java b/Mage.Common/src/main/java/mage/utils/ThreadUtils.java index a4359e08f97..24241ab8ef1 100644 --- a/Mage.Common/src/main/java/mage/utils/ThreadUtils.java +++ b/Mage.Common/src/main/java/mage/utils/ThreadUtils.java @@ -1,5 +1,7 @@ package mage.utils; +import com.google.common.base.Throwables; + import java.util.concurrent.CancellationException; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -31,7 +33,7 @@ public final class ThreadUtils { * Find real exception object after thread task completed. Can be used in afterExecute * */ - public static Throwable findRealException(Runnable r, Throwable t) { + public static Throwable findRunnableException(Runnable r, Throwable t) { // executer.submit - return exception in result // executer.execute - return exception in t if (t == null && r instanceof Future) { @@ -47,4 +49,8 @@ public final class ThreadUtils { } return t; } + + public static Throwable findRootException(Throwable t) { + return Throwables.getRootCause(t); + } } 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 e6c5c747421..8f9899b10c0 100644 --- a/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java +++ b/Mage.Server/src/main/java/mage/server/util/ThreadExecutorImpl.java @@ -64,7 +64,7 @@ public class ThreadExecutorImpl implements ThreadExecutor { super.afterExecute(r, t); // catch errors in CALL threads (from client commands) - t = ThreadUtils.findRealException(r, t); + t = ThreadUtils.findRunnableException(r, t); if (t != null && !(t instanceof CancellationException)) { logger.error("Catch unhandled error in CALL thread: " + t.getMessage(), t); } @@ -83,7 +83,7 @@ public class ThreadExecutorImpl implements ThreadExecutor { super.afterExecute(r, t); // catch errors in GAME threads (from game processing) - t = ThreadUtils.findRealException(r, t); + t = ThreadUtils.findRunnableException(r, t); 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);