mirror of
https://github.com/magefree/mage.git
synced 2025-12-20 10:40:06 -08:00
GUI: added changeable card popup mode for chats/logs by card name clicks (alternative to mouse wheel from game cards);
other: fixed duplicated chat popups in game, added miss error logs from popup related code, added additional checks for good code usage;
This commit is contained in:
parent
785f6973b9
commit
4500b79008
12 changed files with 129 additions and 70 deletions
|
|
@ -9,8 +9,10 @@ import mage.client.dialog.PreferencesDialog;
|
||||||
import mage.client.plugins.adapters.MageActionCallback;
|
import mage.client.plugins.adapters.MageActionCallback;
|
||||||
import mage.client.plugins.impl.Plugins;
|
import mage.client.plugins.impl.Plugins;
|
||||||
import mage.client.util.ClientDefaultSettings;
|
import mage.client.util.ClientDefaultSettings;
|
||||||
|
import mage.utils.SystemUtil;
|
||||||
import mage.view.CardView;
|
import mage.view.CardView;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.util.UUID;
|
import java.util.UUID;
|
||||||
|
|
||||||
|
|
@ -24,26 +26,28 @@ import java.util.UUID;
|
||||||
* - call "onMouseEntered" to prepare;
|
* - call "onMouseEntered" to prepare;
|
||||||
* - call "onMouseMoved" to draw;
|
* - call "onMouseMoved" to draw;
|
||||||
* - call "onMouseExited" to hide;
|
* - call "onMouseExited" to hide;
|
||||||
|
* - call "onMouseWheel" to switch between text and image modes;
|
||||||
* <p>
|
* <p>
|
||||||
* Hints:
|
* Hints:
|
||||||
* - for game GUI: you must init with gameId (otherwise you can't see it)
|
* - for game GUI: you must init with gameId (otherwise you can't see it)
|
||||||
* - for non-game GUI: no needs in gameId or bigCard (bigCard is a panel with card image)
|
* - for non-game GUI: no needs in gameId or bigCard (bigCard is a panel with card image)
|
||||||
* - if you want to show card immediately then use init + onMouseEntered + onMouseMoved
|
* - if you want to show card immediately then use init + onMouseEntered + onMouseMoved
|
||||||
|
* - if you want to switch text/image mode then use onMouseWheel
|
||||||
* <p>
|
* <p>
|
||||||
* Auto-location modes:
|
* Auto-location modes:
|
||||||
* - default: popup container will be put inside parent container
|
* - default: popup container will be put inside parent container (example: popup over cards in game or deck editor)
|
||||||
* - near mouse: popup container will be put near mouse position (example: popup over chat messages)
|
* - near mouse: popup container will be put near mouse position (example: popup over chat messages or game logs)
|
||||||
*
|
*
|
||||||
* @author JayDi85
|
* @author JayDi85
|
||||||
*/
|
*/
|
||||||
public class VirtualCardInfo {
|
public class VirtualCardInfo {
|
||||||
|
|
||||||
CardView cardView;
|
CardView cardView;
|
||||||
MageCard cardComponent;
|
MageCard cardComponent;
|
||||||
BigCard bigCard;
|
BigCard bigCard;
|
||||||
MageActionCallback actionCallback;
|
MageActionCallback actionCallback;
|
||||||
TransferData data = new TransferData();
|
TransferData data = new TransferData();
|
||||||
Dimension cardDimension = null;
|
Dimension cardDimension = null;
|
||||||
|
int viewMode = 1; // workaround to simulate mouse wheel for switch card view mode (text/image styles)
|
||||||
|
|
||||||
public VirtualCardInfo() {
|
public VirtualCardInfo() {
|
||||||
super();
|
super();
|
||||||
|
|
@ -103,6 +107,8 @@ public class VirtualCardInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean prepared() {
|
public boolean prepared() {
|
||||||
|
SystemUtil.ensureRunInGUISwingThread();
|
||||||
|
|
||||||
return this.cardView != null
|
return this.cardView != null
|
||||||
&& this.cardComponent != null
|
&& this.cardComponent != null
|
||||||
&& this.actionCallback != null;
|
&& this.actionCallback != null;
|
||||||
|
|
@ -117,10 +123,6 @@ public class VirtualCardInfo {
|
||||||
data.setLocationOnScreen(newPoint);
|
data.setLocationOnScreen(newPoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onMouseEntered() {
|
|
||||||
onMouseMoved(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void onMouseEntered(Point newLocation) {
|
public void onMouseEntered(Point newLocation) {
|
||||||
if (!prepared()) {
|
if (!prepared()) {
|
||||||
return;
|
return;
|
||||||
|
|
@ -145,6 +147,19 @@ public class VirtualCardInfo {
|
||||||
this.actionCallback.mouseMoved(null, this.data);
|
this.actionCallback.mouseMoved(null, this.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void onMouseWheel(Point newLocation) {
|
||||||
|
if (!prepared()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newLocation != null) {
|
||||||
|
updateLocation(newLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.viewMode = -this.viewMode; // simulate diff mouse wheel moves
|
||||||
|
this.actionCallback.mouseWheelMoved(this.viewMode, this.data);
|
||||||
|
}
|
||||||
|
|
||||||
public void onMouseExited() {
|
public void onMouseExited() {
|
||||||
if (!prepared()) {
|
if (!prepared()) {
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -415,10 +415,6 @@ public class ChatPanelBasic extends javax.swing.JPanel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void enableHyperlinks() {
|
|
||||||
txtConversation.enableHyperlinksAndCardPopups();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void txtMessageKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtMessageKeyTyped
|
private void txtMessageKeyTyped(java.awt.event.KeyEvent evt) {//GEN-FIRST:event_txtMessageKeyTyped
|
||||||
handleKeyTyped(evt);
|
handleKeyTyped(evt);
|
||||||
}//GEN-LAST:event_txtMessageKeyTyped
|
}//GEN-LAST:event_txtMessageKeyTyped
|
||||||
|
|
|
||||||
|
|
@ -21,10 +21,7 @@ import javax.swing.text.html.HTMLEditorKit;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseAdapter;
|
import java.awt.event.MouseAdapter;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.util.Enumeration;
|
import java.util.*;
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.UUID;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GUI: chats with html and hints popup support for objects
|
* GUI: chats with html and hints popup support for objects
|
||||||
|
|
@ -51,7 +48,10 @@ public class ColorPane extends JEditorPane {
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addHyperlinkHandlers() {
|
private void addHyperlinkHandlers() {
|
||||||
addHyperlinkListener(e -> ThreadUtils.threadPool2.submit(() -> {
|
if (Arrays.stream(getHyperlinkListeners()).findAny().isPresent()) {
|
||||||
|
throw new IllegalStateException("Wrong code usage: popup links support enabled already");
|
||||||
|
}
|
||||||
|
addHyperlinkListener(e -> ThreadUtils.threadPoolPopups.submit(() -> {
|
||||||
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_DELAY, 300) == 0) {
|
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_SHOW_TOOLTIPS_DELAY, 300) == 0) {
|
||||||
// if disabled
|
// if disabled
|
||||||
return;
|
return;
|
||||||
|
|
@ -111,13 +111,23 @@ public class ColorPane extends JEditorPane {
|
||||||
cardInfo.init(cardView, this.bigCard, this.gameId);
|
cardInfo.init(cardView, this.bigCard, this.gameId);
|
||||||
cardInfo.setTooltipDelay(CHAT_TOOLTIP_DELAY_MS);
|
cardInfo.setTooltipDelay(CHAT_TOOLTIP_DELAY_MS);
|
||||||
cardInfo.setPopupAutoLocationMode(TransferData.PopupAutoLocationMode.PUT_NEAR_MOUSE_POSITION);
|
cardInfo.setPopupAutoLocationMode(TransferData.PopupAutoLocationMode.PUT_NEAR_MOUSE_POSITION);
|
||||||
cardInfo.onMouseEntered(MouseInfo.getPointerInfo().getLocation());
|
SwingUtilities.invokeLater(() -> {
|
||||||
cardInfo.onMouseMoved(MouseInfo.getPointerInfo().getLocation());
|
cardInfo.onMouseEntered(MouseInfo.getPointerInfo().getLocation());
|
||||||
|
cardInfo.onMouseMoved(MouseInfo.getPointerInfo().getLocation());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.getEventType() == EventType.ACTIVATED) {
|
||||||
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
cardInfo.onMouseWheel(MouseInfo.getPointerInfo().getLocation());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (e.getEventType() == EventType.EXITED) {
|
if (e.getEventType() == EventType.EXITED) {
|
||||||
cardInfo.onMouseExited();
|
SwingUtilities.invokeLater(() -> {
|
||||||
|
cardInfo.onMouseExited();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1951,7 +1951,6 @@ public final class GamePanel extends javax.swing.JPanel {
|
||||||
gameChatPanel.setConnectedChat(userChatPanel);
|
gameChatPanel.setConnectedChat(userChatPanel);
|
||||||
gameChatPanel.disableInput();
|
gameChatPanel.disableInput();
|
||||||
gameChatPanel.setMinimumSize(new java.awt.Dimension(100, 48));
|
gameChatPanel.setMinimumSize(new java.awt.Dimension(100, 48));
|
||||||
gameChatPanel.enableHyperlinks();
|
|
||||||
jSplitPane2 = new javax.swing.JSplitPane();
|
jSplitPane2 = new javax.swing.JSplitPane();
|
||||||
handContainer = new HandPanel();
|
handContainer = new HandPanel();
|
||||||
handCards = new HashMap<>();
|
handCards = new HashMap<>();
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,6 @@ import org.mage.plugins.card.images.ImageCache;
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseWheelEvent;
|
|
||||||
import java.awt.image.BufferedImage;
|
import java.awt.image.BufferedImage;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
@ -188,7 +187,7 @@ public class MageActionCallback implements ActionCallback {
|
||||||
private void showCardHintPopup(final TransferData data, final Component parentComponent, final Point parentPoint) {
|
private void showCardHintPopup(final TransferData data, final Component parentComponent, final Point parentPoint) {
|
||||||
MageCard cardPanel = data.getComponent().getTopPanelRef();
|
MageCard cardPanel = data.getComponent().getTopPanelRef();
|
||||||
|
|
||||||
ThreadUtils.threadPool2.submit(new Runnable() {
|
ThreadUtils.threadPoolPopups.submit(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
ThreadUtils.sleep(tooltipDelay);
|
ThreadUtils.sleep(tooltipDelay);
|
||||||
|
|
@ -482,7 +481,7 @@ public class MageActionCallback implements ActionCallback {
|
||||||
cancelHidingEnlagedCard();
|
cancelHidingEnlagedCard();
|
||||||
Component parentComponent = SwingUtilities.getRoot(cardPanel);
|
Component parentComponent = SwingUtilities.getRoot(cardPanel);
|
||||||
if (parentComponent == null) {
|
if (parentComponent == null) {
|
||||||
// virtual card (example: show card popup in non cards panel like PickChoiceDialog)
|
// virtual card (example: show card popup in non cards panel like PickChoiceDialog or chat )
|
||||||
parentComponent = MageFrame.getDesktop();
|
parentComponent = MageFrame.getDesktop();
|
||||||
}
|
}
|
||||||
Point parentPoint = parentComponent.getLocationOnScreen();
|
Point parentPoint = parentComponent.getLocationOnScreen();
|
||||||
|
|
@ -571,8 +570,7 @@ public class MageActionCallback implements ActionCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseWheelMoved(MouseWheelEvent e, TransferData data) {
|
public void mouseWheelMoved(int mouseWheelRotation, TransferData data) {
|
||||||
int notches = e.getWheelRotation();
|
|
||||||
if (enlargedWindowState != EnlargedWindowState.CLOSED) {
|
if (enlargedWindowState != EnlargedWindowState.CLOSED) {
|
||||||
// same move direction will be ignored, opposite direction closes the enlarged window
|
// same move direction will be ignored, opposite direction closes the enlarged window
|
||||||
if (enlargeredViewOpened != null && new Date().getTime() - enlargeredViewOpened.getTime() > 1000) {
|
if (enlargeredViewOpened != null && new Date().getTime() - enlargeredViewOpened.getTime() > 1000) {
|
||||||
|
|
@ -580,17 +578,18 @@ public class MageActionCallback implements ActionCallback {
|
||||||
hideEnlargedCard();
|
hideEnlargedCard();
|
||||||
handleMouseMoveOverNewCard(data);
|
handleMouseMoveOverNewCard(data);
|
||||||
} else if (enlargeMode == EnlargeMode.NORMAL) {
|
} else if (enlargeMode == EnlargeMode.NORMAL) {
|
||||||
if (notches > 0) {
|
if (mouseWheelRotation > 0) {
|
||||||
hideEnlargedCard();
|
hideEnlargedCard();
|
||||||
handleMouseMoveOverNewCard(data);
|
handleMouseMoveOverNewCard(data);
|
||||||
}
|
}
|
||||||
} else if (notches < 0) {
|
} else if (mouseWheelRotation < 0) {
|
||||||
hideEnlargedCard();
|
hideEnlargedCard();
|
||||||
handleMouseMoveOverNewCard(data);
|
handleMouseMoveOverNewCard(data);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (notches < 0) {
|
|
||||||
|
if (mouseWheelRotation < 0) {
|
||||||
// move up - show normal image
|
// move up - show normal image
|
||||||
enlargeCard(EnlargeMode.NORMAL);
|
enlargeCard(EnlargeMode.NORMAL);
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -648,7 +647,7 @@ public class MageActionCallback implements ActionCallback {
|
||||||
private void displayEnlargedCard(final CardView cardView, final TransferData data) {
|
private void displayEnlargedCard(final CardView cardView, final TransferData data) {
|
||||||
MageCard cardPanel = data.getComponent().getTopPanelRef();
|
MageCard cardPanel = data.getComponent().getTopPanelRef();
|
||||||
|
|
||||||
ThreadUtils.threadPool3.submit(() -> {
|
ThreadUtils.threadPoolPopups.submit(() -> {
|
||||||
if (cardView == null) {
|
if (cardView == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -677,12 +676,19 @@ public class MageActionCallback implements ActionCallback {
|
||||||
final Component popupContainer = MageFrame.getUI().getComponent(mageComponentCardPreviewContainer);
|
final Component popupContainer = MageFrame.getUI().getComponent(mageComponentCardPreviewContainer);
|
||||||
Component cardPreviewPane = MageFrame.getUI().getComponent(mageComponentCardPreviewPane);
|
Component cardPreviewPane = MageFrame.getUI().getComponent(mageComponentCardPreviewPane);
|
||||||
Component parentComponent = SwingUtilities.getRoot(cardPanel);
|
Component parentComponent = SwingUtilities.getRoot(cardPanel);
|
||||||
|
if (parentComponent == null) {
|
||||||
|
// virtual card (example: show card popup in non cards panel like PickChoiceDialog or chat )
|
||||||
|
parentComponent = MageFrame.getDesktop();
|
||||||
|
}
|
||||||
if (cardPreviewPane != null && parentComponent != null) {
|
if (cardPreviewPane != null && parentComponent != null) {
|
||||||
Point parentPoint = parentComponent.getLocationOnScreen();
|
Point parentPoint = parentComponent.getLocationOnScreen();
|
||||||
if (DebugUtil.GUI_POPUP_CONTAINER_DRAW_DEBUG_BORDER) {
|
if (DebugUtil.GUI_POPUP_CONTAINER_DRAW_DEBUG_BORDER) {
|
||||||
((JComponent) cardPreviewPane).setBorder(BorderFactory.createLineBorder(Color.green));
|
((JComponent) cardPreviewPane).setBorder(BorderFactory.createLineBorder(Color.green));
|
||||||
}
|
}
|
||||||
data.setLocationOnScreen(cardPanel.getCardLocationOnScreen().getCardPoint());
|
if (data.getLocationOnScreen() == null) {
|
||||||
|
// in virtual mode you can't get here cause cardPanel hidden
|
||||||
|
data.setLocationOnScreen(cardPanel.getCardLocationOnScreen().getCardPoint());
|
||||||
|
}
|
||||||
|
|
||||||
Point location = preparePopupContainerLocation(popupContainer, cardPreviewPane, data, parentPoint, parentComponent);
|
Point location = preparePopupContainerLocation(popupContainer, cardPreviewPane, data, parentPoint, parentComponent);
|
||||||
popupContainer.setLocation(location);
|
popupContainer.setLocation(location);
|
||||||
|
|
|
||||||
|
|
@ -106,7 +106,7 @@ public class LinePool {
|
||||||
}
|
}
|
||||||
logLineStats();
|
logLineStats();
|
||||||
}
|
}
|
||||||
ThreadUtils.threadPool.submit(() -> {
|
ThreadUtils.threadPoolSounds.submit(() -> {
|
||||||
synchronized (LinePool.this) {
|
synchronized (LinePool.this) {
|
||||||
try {
|
try {
|
||||||
if (!line.isOpen()) {
|
if (!line.isOpen()) {
|
||||||
|
|
|
||||||
|
|
@ -744,7 +744,7 @@ public abstract class CardPanel extends MagePermanent implements ComponentListen
|
||||||
data.setComponent(this);
|
data.setComponent(this);
|
||||||
data.setCard(this.getGameCard());
|
data.setCard(this.getGameCard());
|
||||||
data.setGameId(this.gameId);
|
data.setGameId(this.gameId);
|
||||||
callback.mouseWheelMoved(e, data);
|
callback.mouseWheelMoved(e.getWheelRotation(), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ package mage.cards.action;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.event.MouseEvent;
|
import java.awt.event.MouseEvent;
|
||||||
import java.awt.event.MouseWheelEvent;
|
|
||||||
|
|
||||||
public interface ActionCallback {
|
public interface ActionCallback {
|
||||||
|
|
||||||
|
|
@ -20,7 +19,7 @@ public interface ActionCallback {
|
||||||
|
|
||||||
void mouseExited(MouseEvent e, TransferData data);
|
void mouseExited(MouseEvent e, TransferData data);
|
||||||
|
|
||||||
void mouseWheelMoved(MouseWheelEvent e, TransferData data);
|
void mouseWheelMoved(int mouseWheelRotation, TransferData data);
|
||||||
|
|
||||||
void hideOpenComponents();
|
void hideOpenComponents();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -32,7 +32,7 @@ public class EmptyCallback implements ActionCallback {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void mouseWheelMoved(MouseWheelEvent e, TransferData data) {
|
public void mouseWheelMoved(int mouseWheelRotation, TransferData data) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ import mage.util.CardUtil;
|
||||||
import mage.util.MultiAmountMessage;
|
import mage.util.MultiAmountMessage;
|
||||||
import mage.util.RandomUtil;
|
import mage.util.RandomUtil;
|
||||||
|
|
||||||
|
import javax.swing.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
|
@ -917,4 +918,11 @@ public final class SystemUtil {
|
||||||
throw new IllegalArgumentException("Wrong code usage: client commands code must run in CALL threads, but used in " + name, new Throwable());
|
throw new IllegalArgumentException("Wrong code usage: client commands code must run in CALL threads, but used in " + name, new Throwable());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void ensureRunInGUISwingThread() {
|
||||||
|
if (!SwingUtilities.isEventDispatchThread()) {
|
||||||
|
// hot-to fix: run GUI changeable code by SwingUtilities.invokeLater(() -> {xxx})
|
||||||
|
throw new IllegalArgumentException("Wrong code usage: GUI related code must run in SWING thread by SwingUtilities.invokeLater", new Throwable());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,66 +1,65 @@
|
||||||
package mage.utils;
|
package mage.utils;
|
||||||
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import org.apache.log4j.Logger;
|
||||||
import java.util.concurrent.ThreadFactory;
|
|
||||||
import java.util.concurrent.ThreadPoolExecutor;
|
import java.util.concurrent.*;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Util method to work with threads.
|
* Util method to work with threads.
|
||||||
*
|
*
|
||||||
* @author ayrat
|
* @author ayrat
|
||||||
*/
|
*/
|
||||||
@SuppressWarnings("unchecked")
|
|
||||||
public final class ThreadUtils {
|
public final class ThreadUtils {
|
||||||
|
|
||||||
public static final ThreadPoolExecutor threadPool;
|
private static final Logger logger = Logger.getLogger(ThreadUtils.class);
|
||||||
public static final ThreadPoolExecutor threadPool2;
|
|
||||||
public static final ThreadPoolExecutor threadPool3;
|
public static final ThreadPoolExecutor threadPoolSounds;
|
||||||
|
public static final ThreadPoolExecutor threadPoolPopups;
|
||||||
private static int threadCount;
|
private static int threadCount;
|
||||||
|
|
||||||
static {
|
static {
|
||||||
/**
|
threadPoolSounds = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadFactory() {
|
||||||
* used in CardInfoPaneImpl
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
threadPool = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadFactory() {
|
|
||||||
@Override
|
@Override
|
||||||
public Thread newThread(Runnable runnable) {
|
public Thread newThread(Runnable runnable) {
|
||||||
threadCount++;
|
threadCount++;
|
||||||
Thread thread = new Thread(runnable, "Util" + threadCount);
|
Thread thread = new Thread(runnable, "SOUND-" + threadCount);
|
||||||
thread.setDaemon(true);
|
thread.setDaemon(true);
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
});
|
}) {
|
||||||
threadPool.prestartAllCoreThreads();
|
@Override
|
||||||
|
protected void afterExecute(Runnable r, Throwable t) {
|
||||||
|
super.afterExecute(r, t);
|
||||||
|
t = findRealException(r, t);
|
||||||
|
if (t != null) {
|
||||||
|
// TODO: show sound errors in client logs?
|
||||||
|
//logger.error("Catch unhandled error in SOUND thread: " + t.getMessage(), t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
threadPoolSounds.prestartAllCoreThreads();
|
||||||
|
|
||||||
/**
|
threadPoolPopups = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadFactory() {
|
||||||
* Used for MageActionCallback
|
|
||||||
*/
|
|
||||||
threadPool2 = new ThreadPoolExecutor(4, 4, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadFactory() {
|
|
||||||
@Override
|
@Override
|
||||||
public Thread newThread(Runnable runnable) {
|
public Thread newThread(Runnable runnable) {
|
||||||
threadCount++;
|
threadCount++;
|
||||||
Thread thread = new Thread(runnable, "TP2" + threadCount);
|
Thread thread = new Thread(runnable, "POPUP-" + threadCount);
|
||||||
thread.setDaemon(true);
|
thread.setDaemon(true);
|
||||||
return thread;
|
return thread;
|
||||||
}
|
}
|
||||||
});
|
}) {
|
||||||
threadPool2.prestartAllCoreThreads();
|
@Override
|
||||||
/**
|
protected void afterExecute(Runnable r, Throwable t) {
|
||||||
* Used for Enlarged view
|
super.afterExecute(r, t);
|
||||||
*/
|
|
||||||
|
|
||||||
threadPool3 = new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue(), new ThreadFactory() {
|
// catch errors in popup threads (example: card popup over cards or chat/log messages)
|
||||||
@Override
|
t = findRealException(r, t);
|
||||||
public Thread newThread(Runnable runnable) {
|
if (t != null) {
|
||||||
threadCount++;
|
logger.error("Catch unhandled error in POPUP thread: " + t.getMessage(), t);
|
||||||
Thread thread = new Thread(runnable, "EV" + threadCount);
|
}
|
||||||
thread.setDaemon(true);
|
|
||||||
return thread;
|
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
threadPool3.prestartAllCoreThreads();
|
threadPoolPopups.prestartAllCoreThreads();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void sleep(int millis) {
|
public static void sleep(int millis) {
|
||||||
|
|
@ -78,4 +77,28 @@ public final class ThreadUtils {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find real exception object after thread task completed. Can be used in afterExecute
|
||||||
|
*
|
||||||
|
* @param r
|
||||||
|
* @param t
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Throwable findRealException(Runnable r, Throwable t) {
|
||||||
|
// executer.submit - return exception in result
|
||||||
|
// executer.execute - return exception in t
|
||||||
|
if (t == null && r instanceof Future<?>) {
|
||||||
|
try {
|
||||||
|
Object result = ((Future<?>) r).get();
|
||||||
|
} catch (CancellationException ce) {
|
||||||
|
t = ce;
|
||||||
|
} catch (ExecutionException ee) {
|
||||||
|
t = ee.getCause();
|
||||||
|
} catch (InterruptedException ie) {
|
||||||
|
Thread.currentThread().interrupt(); // ignore/reset
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return t;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ package mage.server.util;
|
||||||
|
|
||||||
import mage.server.managers.ConfigSettings;
|
import mage.server.managers.ConfigSettings;
|
||||||
import mage.server.managers.ThreadExecutor;
|
import mage.server.managers.ThreadExecutor;
|
||||||
|
import mage.utils.ThreadUtils;
|
||||||
import org.apache.log4j.Logger;
|
import org.apache.log4j.Logger;
|
||||||
|
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
|
|
@ -63,6 +64,7 @@ public class ThreadExecutorImpl implements ThreadExecutor {
|
||||||
super.afterExecute(r, t);
|
super.afterExecute(r, t);
|
||||||
|
|
||||||
// catch errors in CALL threads (from client commands)
|
// catch errors in CALL threads (from client commands)
|
||||||
|
t = ThreadUtils.findRealException(r, t);
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
logger.error("Catch unhandled error in CALL thread: " + t.getMessage(), t);
|
logger.error("Catch unhandled error in CALL thread: " + t.getMessage(), t);
|
||||||
}
|
}
|
||||||
|
|
@ -81,6 +83,7 @@ public class ThreadExecutorImpl implements ThreadExecutor {
|
||||||
super.afterExecute(r, t);
|
super.afterExecute(r, t);
|
||||||
|
|
||||||
// catch errors in GAME threads (from game processing)
|
// catch errors in GAME threads (from game processing)
|
||||||
|
t = ThreadUtils.findRealException(r, t);
|
||||||
if (t != null) {
|
if (t != null) {
|
||||||
// it's impossible to brake game thread in normal use case, so each bad use case must be researched
|
// 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);
|
logger.error("Catch unhandled error in GAME thread: " + t.getMessage(), t);
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue