");
buffer.append(card.getDisplayName());
if (card.isGameObject()) {
- buffer.append(" [").append(card.getId().toString().substring(0, 3)).append(']');
+ buffer.append(" [").append(card.getId().toString(), 0, 3).append(']');
}
buffer.append(" | ").append(textLine).append("");
}
diff --git a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java
index 93efaaafba..37d050fc46 100644
--- a/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java
+++ b/Mage.Client/src/main/java/mage/client/util/sets/ConstructedFormats.java
@@ -1,15 +1,13 @@
package mage.client.util.sets;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
import mage.cards.repository.ExpansionInfo;
import mage.cards.repository.ExpansionRepository;
+import mage.cards.repository.RepositoryEvent;
import mage.constants.SetType;
import mage.deck.Standard;
+import mage.game.events.Listener;
+
+import java.util.*;
/**
* Utility class for constructed formats (expansions and other editions).
@@ -18,17 +16,42 @@ import mage.deck.Standard;
*/
public final class ConstructedFormats {
- public static final String ALL = "- All Sets";
+ public static final String ALL_SETS = "- All Sets";
public static final String STANDARD = "- Standard";
public static final String EXTENDED = "- Extended";
public static final String FRONTIER = "- Frontier";
public static final String MODERN = "- Modern";
public static final String VINTAGE_LEGACY = "- Vintage / Legacy";
+ public static final String JOKE = "- Joke Sets";
public static final String CUSTOM = "- Custom";
public static final Standard STANDARD_CARDS = new Standard();
+ // Attention -Month is 0 Based so Feb = 1 for example. //
+ private static final Date extendedDate = new GregorianCalendar(2009, 7, 20).getTime();
+ private static final Date frontierDate = new GregorianCalendar(2014, 6, 17).getTime();
+ private static final Date modernDate = new GregorianCalendar(2003, 6, 20).getTime();
+
+ // for all sets just return empty list
+ private static final List all = new ArrayList<>();
+
private static final Map> underlyingSetCodesPerFormat = new HashMap<>();
private static final List formats = new ArrayList<>();
+ private static final Listener setsDbListener;
+
+ static {
+ buildLists();
+
+ // auto-update sets list on changes
+ setsDbListener = new Listener() {
+ @Override
+ public void event(RepositoryEvent event) {
+ if (event.getEventType().equals(RepositoryEvent.RepositoryEventType.DB_UPDATED)) {
+ buildLists();
+ }
+ }
+ };
+ ExpansionRepository.instance.subscribe(setsDbListener);
+ }
private ConstructedFormats() {
}
@@ -42,7 +65,7 @@ public final class ConstructedFormats {
}
public static List getSetsByFormat(final String format) {
- if (!format.equals(ALL)) {
+ if (!format.equals(ALL_SETS)) {
return underlyingSetCodesPerFormat.get(format);
}
return all;
@@ -55,46 +78,69 @@ public final class ConstructedFormats {
}
}
- private static void buildLists() {
+ public static void buildLists() {
underlyingSetCodesPerFormat.put(STANDARD, new ArrayList<>());
underlyingSetCodesPerFormat.put(EXTENDED, new ArrayList<>());
underlyingSetCodesPerFormat.put(FRONTIER, new ArrayList<>());
underlyingSetCodesPerFormat.put(MODERN, new ArrayList<>());
underlyingSetCodesPerFormat.put(VINTAGE_LEGACY, new ArrayList<>());
+ underlyingSetCodesPerFormat.put(JOKE, new ArrayList<>());
underlyingSetCodesPerFormat.put(CUSTOM, new ArrayList<>());
final Map expansionInfo = new HashMap<>();
formats.clear(); // prevent NPE on sorting if this is not the first try
+
+ // Because this is also called in Netbeans Design view, but the object does not exist in that case,
+ // we have to return here to prevent exception in design view. (Does not hurt at design time)
+ if (!ExpansionRepository.instance.instanceInitialized) {
+ return;
+ }
+
+ // build formats list for deck validators
for (ExpansionInfo set : ExpansionRepository.instance.getAll()) {
expansionInfo.put(set.getName(), set);
formats.add(set.getName());
+ // full list
underlyingSetCodesPerFormat.put(set.getName(), new ArrayList<>());
underlyingSetCodesPerFormat.get(set.getName()).add(set.getCode());
- // create the play formats
- if (set.getType() == SetType.CUSTOM_SET) {
+ // custom
+ if (set.getType().isCustomSet()) {
underlyingSetCodesPerFormat.get(CUSTOM).add(set.getCode());
continue;
}
- underlyingSetCodesPerFormat.get(VINTAGE_LEGACY).add(set.getCode());
- if (set.getType() == SetType.CORE || set.getType() == SetType.EXPANSION || set.getType() == SetType.SUPPLEMENTAL_STANDARD_LEGAL) {
- if (STANDARD_CARDS.getSetCodes().contains(set.getCode())) {
- underlyingSetCodesPerFormat.get(STANDARD).add(set.getCode());
- }
- if (set.getType() != SetType.SUPPLEMENTAL_STANDARD_LEGAL) {
- if (set.getReleaseDate().after(extendedDate) && (set.getType() == SetType.EXPANSION || set.getType() == SetType.CORE)) {
- underlyingSetCodesPerFormat.get(EXTENDED).add(set.getCode());
- }
- if (set.getReleaseDate().after(frontierDate) && (set.getType() == SetType.EXPANSION || set.getType() == SetType.CORE)) {
- underlyingSetCodesPerFormat.get(FRONTIER).add(set.getCode());
- }
- if (set.getReleaseDate().after(modernDate) && (set.getType() == SetType.EXPANSION || set.getType() == SetType.CORE)) {
- underlyingSetCodesPerFormat.get(MODERN).add(set.getCode());
- }
- }
+
+ // joke
+ if (set.getType().isJokeSet()) {
+ underlyingSetCodesPerFormat.get(JOKE).add(set.getCode());
+ continue;
}
- // Create the Block formats
+ // vintage/legacy (any set, TODO: even ?custom set?)
+ underlyingSetCodesPerFormat.get(VINTAGE_LEGACY).add(set.getCode());
+
+ // standard (dependent on current date)
+ if (STANDARD_CARDS.getSetCodes().contains(set.getCode())) {
+ underlyingSetCodesPerFormat.get(STANDARD).add(set.getCode());
+ }
+
+ // extended
+ if (set.getType().isStandardLegal() && set.getReleaseDate().after(extendedDate)) {
+ underlyingSetCodesPerFormat.get(EXTENDED).add(set.getCode());
+ }
+
+ // frontier
+ if (set.getType().isStandardLegal() && set.getReleaseDate().after(frontierDate)) {
+ underlyingSetCodesPerFormat.get(FRONTIER).add(set.getCode());
+ }
+
+ // modern
+ if (set.getType().isModernLegal() && set.getReleaseDate().after(modernDate)) {
+ underlyingSetCodesPerFormat.get(MODERN).add(set.getCode());
+ }
+
+ // BLOCKS formats
+
if (set.getType() == SetType.EXPANSION && set.getBlockName() != null) {
String blockDisplayName = getBlockDisplayName(set.getBlockName());
underlyingSetCodesPerFormat.computeIfAbsent(blockDisplayName, k -> new ArrayList<>());
@@ -109,7 +155,6 @@ public final class ConstructedFormats {
if (expansionInfo.get(blockDisplayName).getReleaseDate().after(set.getReleaseDate())) {
expansionInfo.put(blockDisplayName, set);
}
-
}
if (set.getType() == SetType.SUPPLEMENTAL && set.getBlockName() != null) {
@@ -200,30 +245,20 @@ public final class ConstructedFormats {
}
return expansionInfo1.getType().compareTo(expansionInfo2.getType());
});
+
if (!formats.isEmpty()) {
formats.add(0, CUSTOM);
+ formats.add(0, JOKE);
formats.add(0, VINTAGE_LEGACY);
formats.add(0, MODERN);
- formats.add(0, EXTENDED);
formats.add(0, FRONTIER);
+ formats.add(0, EXTENDED);
formats.add(0, STANDARD);
-
}
- formats.add(0, ALL);
+ formats.add(0, ALL_SETS);
}
private static String getBlockDisplayName(String blockName) {
return "* " + blockName + " Block";
}
- // Attention -Month is 0 Based so Feb = 1 for example.
- private static final Date extendedDate = new GregorianCalendar(2009, 7, 20).getTime();
- private static final Date frontierDate = new GregorianCalendar(2014, 6, 17).getTime();
- private static final Date modernDate = new GregorianCalendar(2003, 6, 20).getTime();
-
- // for all sets just return empty list
- private static final List all = new ArrayList<>();
-
- static {
- buildLists();
- }
}
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java
index 34a44b66e2..d5d679aa7b 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/Animation.java
@@ -125,16 +125,16 @@ public abstract class Animation {
@Override
protected void update(float percentage) {
if (tapped) {
- panel.tappedAngle = CardPanel.TAPPED_ANGLE * percentage;
+ panel.setTappedAngle(CardPanel.TAPPED_ANGLE * percentage);
// reverse movement if untapping
if (!panel.isTapped()) {
- panel.tappedAngle = CardPanel.TAPPED_ANGLE - panel.tappedAngle;
+ panel.setTappedAngle(CardPanel.TAPPED_ANGLE - panel.getTappedAngle());
}
}
if (flipped) {
- panel.flippedAngle = CardPanel.FLIPPED_ANGLE * percentage;
+ panel.setFlippedAngle(CardPanel.FLIPPED_ANGLE * percentage);
if (!panel.isFlipped()) {
- panel.flippedAngle = CardPanel.FLIPPED_ANGLE - panel.flippedAngle;
+ panel.setFlippedAngle(CardPanel.FLIPPED_ANGLE - panel.getFlippedAngle());
}
}
panel.repaint();
@@ -143,10 +143,10 @@ public abstract class Animation {
@Override
protected void end() {
if (tapped) {
- panel.tappedAngle = panel.isTapped() ? CardPanel.TAPPED_ANGLE : 0;
+ panel.setTappedAngle(panel.isTapped() ? CardPanel.TAPPED_ANGLE : 0);
}
if (flipped) {
- panel.flippedAngle = panel.isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
+ panel.setFlippedAngle(panel.isFlipped() ? CardPanel.FLIPPED_ANGLE : 0);
}
parent.onEndAnimation();
parent.repaint();
@@ -334,7 +334,7 @@ public abstract class Animation {
currentX = Math.min(currentX, layeredPane.getWidth() - currentWidth);
int currentY = Math.max(0, centerY - Math.round(currentHeight / 2f));
currentY = Math.min(currentY, layeredPane.getHeight() - currentHeight);
- animationPanel.tappedAngle = overPanel.tappedAngle * percentage;
+ animationPanel.setTappedAngle(overPanel.getTappedAngle() * percentage);
animationPanel.setCardBounds(currentX, currentY, currentWidth, currentHeight);
}
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java
index 9c670224dd..b39cf150b9 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanel.java
@@ -48,14 +48,14 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
private static final float ROT_CENTER_TO_TOP_CORNER = 1.0295630140987000315797369464196f;
private static final float ROT_CENTER_TO_BOTTOM_CORNER = 0.7071067811865475244008443621048f;
- public CardView gameCard;
- public CardView updateCard;
+ private CardView gameCard;
+ private CardView updateCard;
// for two faced cards
- public CardView temporary;
+ private CardView temporary;
- public double tappedAngle = 0;
- public double flippedAngle = 0;
+ private double tappedAngle = 0;
+ private double flippedAngle = 0;
private final List links = new ArrayList<>();
@@ -99,14 +99,14 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
public CardPanel(CardView newGameCard, UUID gameId, final boolean loadImage, ActionCallback callback, final boolean foil, Dimension dimension) {
// Store away params
- this.gameCard = newGameCard;
+ this.setGameCard(newGameCard);
this.callback = callback;
this.gameId = gameId;
// Gather info about the card
- this.isPermanent = this.gameCard instanceof PermanentView && !this.gameCard.inViewerOnly();
+ this.isPermanent = this.getGameCard() instanceof PermanentView && !this.getGameCard().inViewerOnly();
if (isPermanent) {
- this.hasSickness = ((PermanentView) this.gameCard).hasSummoningSickness();
+ this.hasSickness = ((PermanentView) this.getGameCard()).hasSummoningSickness();
}
// Set to requested size
@@ -120,7 +120,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
add(buttonPanel);
// Both card rendering implementations have a transform button
- if (this.gameCard.canTransform()) {
+ if (this.getGameCard().canTransform()) {
// Create the day night button
dayNightButton = new JButton("");
dayNightButton.setSize(32, 32);
@@ -142,12 +142,12 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
}
// Both card rendering implementations have a view copy source button
- if (this.gameCard instanceof PermanentView) {
+ if (this.getGameCard() instanceof PermanentView) {
// Create the show source button
showCopySourceButton = new JButton("");
showCopySourceButton.setSize(32, 32);
showCopySourceButton.setToolTipText("This permanent is copying a target. To see original card, push this button or turn mouse wheel down while hovering with the mouse pointer over the permanent.");
- showCopySourceButton.setVisible(((PermanentView) this.gameCard).isCopy());
+ showCopySourceButton.setVisible(((PermanentView) this.getGameCard()).isCopy());
showCopySourceButton.setIcon(new ImageIcon(ImageManagerImpl.instance.getCopyInformIconImage()));
showCopySourceButton.addActionListener(e -> {
ActionCallback callback1 = Plugins.instance.getActionCallback();
@@ -174,8 +174,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
tooltipText.setText(getText(cardType, newGameCard));
// Animation setup
- tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
- flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
+ setTappedAngle(isTapped() ? CardPanel.TAPPED_ANGLE : 0);
+ setFlippedAngle(isFlipped() ? CardPanel.FLIPPED_ANGLE : 0);
}
@Override
@@ -197,7 +197,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
public final void initialDraw() {
// Kick off
- if (gameCard.isTransformed()) {
+ if (getGameCard().isTransformed()) {
// this calls updateImage
toggleTransformed();
} else {
@@ -325,10 +325,10 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
g2d.translate(edgeOffset * (1 - transformAngle), 0);
g2d.scale(transformAngle, 1);
}
- if (tappedAngle + flippedAngle > 0) {
+ if (getTappedAngle() + getFlippedAngle() > 0) {
g2d = (Graphics2D) g2d.create();
float edgeOffset = cardWidth / 2f;
- double angle = tappedAngle + (Math.abs(flippedAngle - FLIPPED_ANGLE) < 0.001 ? 0 : flippedAngle);
+ double angle = getTappedAngle() + (Math.abs(getFlippedAngle() - FLIPPED_ANGLE) < 0.001 ? 0 : getFlippedAngle());
g2d.rotate(angle, cardXOffset + edgeOffset, cardYOffset + cardHeight - edgeOffset);
}
super.paint(g2d);
@@ -347,7 +347,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public String toString() {
- return gameCard.toString();
+ return getGameCard().toString();
}
@Override
@@ -433,7 +433,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
}
public final CardView getCard() {
- return this.gameCard;
+ return this.getGameCard();
}
@Override
@@ -457,7 +457,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public final boolean isTapped() {
if (isPermanent) {
- return ((PermanentView) gameCard).isTapped();
+ return ((PermanentView) getGameCard()).isTapped();
}
return false;
}
@@ -465,7 +465,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public final boolean isFlipped() {
if (isPermanent) {
- return ((PermanentView) gameCard).isFlipped();
+ return ((PermanentView) getGameCard()).isFlipped();
}
return false;
}
@@ -473,7 +473,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public final boolean isTransformed() {
if (isPermanent) {
- if (gameCard.isTransformed()) {
+ if (getGameCard().isTransformed()) {
return !this.transformed;
} else {
return this.transformed;
@@ -503,7 +503,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
*/
@Override
public void update(CardView card) {
- this.updateCard = card;
+ this.setUpdateCard(card);
// Animation update
if (isPermanent && (card instanceof PermanentView)) {
@@ -527,11 +527,11 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
// Update art?
boolean mustUpdateArt
- = (!gameCard.getName().equals(card.getName()))
- || (gameCard.isFaceDown() != card.isFaceDown());
+ = (!getGameCard().getName().equals(card.getName()))
+ || (getGameCard().isFaceDown() != card.isFaceDown());
// Set the new card
- this.gameCard = card;
+ this.setGameCard(card);
// Update tooltip text
String cardType = getType(card);
@@ -580,7 +580,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public CardView getOriginal() {
- return this.gameCard;
+ return this.getGameCard();
}
@Override
@@ -589,7 +589,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public void mouseEntered(MouseEvent e) {
- if (gameCard.hideInfo()) {
+ if (getGameCard().hideInfo()) {
return;
}
if (!tooltipShowing) {
@@ -607,22 +607,22 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public void mouseDragged(MouseEvent e) {
- data.component = this;
+ data.setComponent(this);
callback.mouseDragged(e, data);
}
@Override
public void mouseMoved(MouseEvent e) {
- if (gameCard.hideInfo()) {
+ if (getGameCard().hideInfo()) {
return;
}
- data.component = this;
+ data.setComponent(this);
callback.mouseMoved(e, data);
}
@Override
public void mouseExited(MouseEvent e) {
- if (gameCard.hideInfo()) {
+ if (getGameCard().hideInfo()) {
return;
}
@@ -630,9 +630,9 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
synchronized (this) {
if (tooltipShowing) {
tooltipShowing = false;
- data.component = this;
- data.card = this.gameCard;
- data.popupText = tooltipText;
+ data.setComponent(this);
+ data.setCard(this.getGameCard());
+ data.setPopupText(tooltipText);
callback.mouseExited(e, data);
}
}
@@ -641,9 +641,9 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public void mousePressed(MouseEvent e) {
- data.component = this;
- data.card = this.gameCard;
- data.gameId = this.gameId;
+ data.setComponent(this);
+ data.setCard(this.getGameCard());
+ data.setGameId(this.gameId);
callback.mousePressed(e, data);
}
@@ -658,13 +658,13 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
* @return
*/
private TransferData getTransferDataForMouseEntered() {
- data.component = this;
- data.card = this.gameCard;
- data.popupText = tooltipText;
- data.gameId = this.gameId;
- data.locationOnScreen = data.component.getLocationOnScreen(); // we need this for popup
- data.popupOffsetX = isTapped() ? cardHeight + cardXOffset + POPUP_X_GAP : cardWidth + cardXOffset + POPUP_X_GAP;
- data.popupOffsetY = 40;
+ data.setComponent(this);
+ data.setCard(this.getGameCard());
+ data.setPopupText(tooltipText);
+ data.setGameId(this.gameId);
+ data.setLocationOnScreen(data.getComponent().getLocationOnScreen()); // we need this for popup
+ data.setPopupOffsetX(isTapped() ? cardHeight + cardXOffset + POPUP_X_GAP : cardWidth + cardXOffset + POPUP_X_GAP);
+ data.setPopupOffsetY(40);
return data;
}
@@ -734,7 +734,7 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
@Override
public PermanentView getOriginalPermanent() {
if (isPermanent) {
- return (PermanentView) this.gameCard;
+ return (PermanentView) this.getGameCard();
}
throw new IllegalStateException("Is not permanent.");
}
@@ -757,13 +757,13 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
BufferedImage night = ImageManagerImpl.instance.getNightImage();
dayNightButton.setIcon(new ImageIcon(night));
}
- if (this.gameCard.getSecondCardFace() == null) {
+ if (this.getGameCard().getSecondCardFace() == null) {
LOGGER.error("no second side for card to transform!");
return;
}
if (!isPermanent) { // use only for custom transformation (when pressing day-night button)
- this.temporary = this.gameCard;
- update(this.gameCard.getSecondCardFace());
+ this.setTemporary(this.getGameCard());
+ update(this.getGameCard().getSecondCardFace());
}
} else {
if (dayNightButton != null) { // if transformbable card is copied, button can be null
@@ -771,22 +771,22 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
dayNightButton.setIcon(new ImageIcon(day));
}
if (!isPermanent) { // use only for custom transformation (when pressing day-night button)
- update(this.temporary);
- this.temporary = null;
+ update(this.getTemporary());
+ this.setTemporary(null);
}
}
- String temp = this.gameCard.getAlternateName();
- this.gameCard.setAlternateName(this.gameCard.getOriginalName());
- this.gameCard.setOriginalName(temp);
+ String temp = this.getGameCard().getAlternateName();
+ this.getGameCard().setAlternateName(this.getGameCard().getOriginalName());
+ this.getGameCard().setOriginalName(temp);
updateArtImage();
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
- if (gameCard.hideInfo()) {
+ if (getGameCard().hideInfo()) {
return;
}
- data.component = this;
+ data.setComponent(this);
callback.mouseWheelMoved(e, data);
}
@@ -800,8 +800,8 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
// this update removes the isChoosable mark from targetCardsInLibrary
// so only done for permanents because it's needed to redraw counters in different size, if window size was changed
// no perfect solution yet (maybe also other not wanted effects for PermanentView objects)
- if ((updateCard instanceof PermanentView)) {
- update(updateCard);
+ if ((getUpdateCard() instanceof PermanentView)) {
+ update(getUpdateCard());
}
}
@@ -836,4 +836,43 @@ public abstract class CardPanel extends MagePermanent implements MouseListener,
this.popupMenu = popupMenu;
}
+ public CardView getGameCard() {
+ return gameCard;
+ }
+
+ public void setGameCard(CardView gameCard) {
+ this.gameCard = gameCard;
+ }
+
+ public CardView getUpdateCard() {
+ return updateCard;
+ }
+
+ public void setUpdateCard(CardView updateCard) {
+ this.updateCard = updateCard;
+ }
+
+ public CardView getTemporary() {
+ return temporary;
+ }
+
+ public void setTemporary(CardView temporary) {
+ this.temporary = temporary;
+ }
+
+ public double getTappedAngle() {
+ return tappedAngle;
+ }
+
+ public void setTappedAngle(double tappedAngle) {
+ this.tappedAngle = tappedAngle;
+ }
+
+ public double getFlippedAngle() {
+ return flippedAngle;
+ }
+
+ public void setFlippedAngle(double flippedAngle) {
+ this.flippedAngle = flippedAngle;
+ }
}
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java
index d7812ba2aa..a237318a17 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelComponentImpl.java
@@ -1,11 +1,11 @@
package org.mage.card.arcane;
-import com.google.common.base.Function;
-import com.google.common.collect.MapMaker;
import mage.cards.action.ActionCallback;
+import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.ImageCaches;
import mage.client.util.ImageHelper;
+import mage.client.util.SoftValuesLoadingCache;
import mage.components.ImagePanel;
import mage.components.ImagePanelStyle;
import mage.constants.AbilityType;
@@ -17,12 +17,10 @@ import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
-import mage.client.constants.Constants;
import javax.swing.*;
import java.awt.*;
import java.awt.image.BufferedImage;
-import java.util.Map;
import java.util.StringTokenizer;
import java.util.UUID;
@@ -52,12 +50,12 @@ public class CardPanelComponentImpl extends CardPanel {
private static final int CARD_MAX_SIZE_FOR_ICONS = 200;
public final ScaledImagePanel imagePanel;
- public ImagePanel overlayPanel;
+ private ImagePanel overlayPanel;
- public JPanel iconPanel;
+ private JPanel iconPanel;
private JButton typeButton;
- public JPanel counterPanel;
+ private JPanel counterPanel;
private JLabel loyaltyCounterLabel;
private JLabel plusCounterLabel;
private JLabel otherCounterLabel;
@@ -78,7 +76,31 @@ public class CardPanelComponentImpl extends CardPanel {
private boolean displayTitleAnyway;
private boolean displayFullImagePath;
- private final static Map IMAGE_CACHE;
+ private final static SoftValuesLoadingCache IMAGE_CACHE;
+
+ public ImagePanel getOverlayPanel() {
+ return overlayPanel;
+ }
+
+ public void setOverlayPanel(ImagePanel overlayPanel) {
+ this.overlayPanel = overlayPanel;
+ }
+
+ public JPanel getIconPanel() {
+ return iconPanel;
+ }
+
+ public void setIconPanel(JPanel iconPanel) {
+ this.iconPanel = iconPanel;
+ }
+
+ public JPanel getCounterPanel() {
+ return counterPanel;
+ }
+
+ public void setCounterPanel(JPanel counterPanel) {
+ this.counterPanel = counterPanel;
+ }
static class Key {
@@ -93,8 +115,9 @@ public class CardPanelComponentImpl extends CardPanel {
final boolean isChoosable;
final boolean isPlayable;
final boolean canAttack;
+ final boolean canBlock;
- public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack) {
+ public Key(int width, int height, int cardWidth, int cardHeight, int cardXOffset, int cardYOffset, boolean hasImage, boolean isSelected, boolean isChoosable, boolean isPlayable, boolean canAttack, boolean canBlock) {
this.width = width;
this.height = height;
this.cardWidth = cardWidth;
@@ -106,6 +129,7 @@ public class CardPanelComponentImpl extends CardPanel {
this.isChoosable = isChoosable;
this.isPlayable = isPlayable;
this.canAttack = canAttack;
+ this.canBlock = canBlock;
}
@Override
@@ -122,6 +146,7 @@ public class CardPanelComponentImpl extends CardPanel {
hash = 19 * hash + (this.isChoosable ? 1 : 0);
hash = 19 * hash + (this.isPlayable ? 1 : 0);
hash = 19 * hash + (this.canAttack ? 1 : 0);
+ hash = 19 * hash + (this.canBlock ? 1 : 0);
return hash;
}
@@ -170,28 +195,28 @@ public class CardPanelComponentImpl extends CardPanel {
if (this.canAttack != other.canAttack) {
return false;
}
- return true;
+ return this.canBlock == other.canBlock;
}
}
static {
- IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap((Function) key -> createImage(key)));
+ IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(CardPanelComponentImpl::createImage));
}
- static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage){
+ static private boolean canShowCardIcons(int cardFullWidth, boolean cardHasImage) {
// cards without images show icons and text always
// TODO: apply "card names on card" setting to icon too?
// TODO: fix card min-max size to hide (compare to settings size, not direct 60 and 200)
return ((cardFullWidth > 60) && (cardFullWidth < 200)) || (!cardHasImage);
}
- private static class CardSizes{
+ private static class CardSizes {
Rectangle rectFull;
Rectangle rectSelection;
Rectangle rectBorder;
Rectangle rectCard;
- CardSizes(int offsetX, int offsetY, int fullWidth, int fullHeight){
+ CardSizes(int offsetX, int offsetY, int fullWidth, int fullHeight) {
int realBorderSizeX = Math.round(fullWidth * BLACK_BORDER_SIZE);
int realBorderSizeY = Math.round(fullWidth * BLACK_BORDER_SIZE);
@@ -213,27 +238,27 @@ public class CardPanelComponentImpl extends CardPanel {
// Counter panel
if (!newGameCard.isAbility()) {
// panel to show counters on the card
- counterPanel = new JPanel();
- counterPanel.setLayout(null);
- counterPanel.setOpaque(false);
- add(counterPanel);
+ setCounterPanel(new JPanel());
+ getCounterPanel().setLayout(null);
+ getCounterPanel().setOpaque(false);
+ add(getCounterPanel());
plusCounterLabel = new JLabel("");
plusCounterLabel.setToolTipText("+1/+1");
- counterPanel.add(plusCounterLabel);
+ getCounterPanel().add(plusCounterLabel);
minusCounterLabel = new JLabel("");
minusCounterLabel.setToolTipText("-1/-1");
- counterPanel.add(minusCounterLabel);
+ getCounterPanel().add(minusCounterLabel);
loyaltyCounterLabel = new JLabel("");
loyaltyCounterLabel.setToolTipText("loyalty");
- counterPanel.add(loyaltyCounterLabel);
+ getCounterPanel().add(loyaltyCounterLabel);
otherCounterLabel = new JLabel("");
- counterPanel.add(otherCounterLabel);
+ getCounterPanel().add(otherCounterLabel);
- counterPanel.setVisible(false);
+ getCounterPanel().setVisible(false);
}
// Ability icon
@@ -246,7 +271,7 @@ public class CardPanelComponentImpl extends CardPanel {
}
// Token icon
- if (this.gameCard.isToken()) {
+ if (this.getGameCard().isToken()) {
setTypeIcon(ImageManagerImpl.instance.getTokenIconImage(), "Token Permanent");
}
@@ -255,7 +280,7 @@ public class CardPanelComponentImpl extends CardPanel {
// Title Text
titleText = new GlowText();
- setText(gameCard);
+ setText(getGameCard());
// int fontSize = (int) cardHeight / 11;
// titleText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
titleText.setForeground(Color.white);
@@ -271,10 +296,10 @@ public class CardPanelComponentImpl extends CardPanel {
// PT Text
ptText = new GlowText();
- if (gameCard.isCreature()) {
- ptText.setText(gameCard.getPower() + '/' + gameCard.getToughness());
- } else if (gameCard.isPlanesWalker()) {
- ptText.setText(gameCard.getLoyalty());
+ if (getGameCard().isCreature()) {
+ ptText.setText(getGameCard().getPower() + '/' + getGameCard().getToughness());
+ } else if (getGameCard().isPlanesWalker()) {
+ ptText.setText(getGameCard().getLoyalty());
}
// ptText.setFont(getFont().deriveFont(Font.BOLD, fontSize));
ptText.setForeground(Color.white);
@@ -283,9 +308,9 @@ public class CardPanelComponentImpl extends CardPanel {
// Sickness overlay
BufferedImage sickness = ImageManagerImpl.instance.getSicknessImage();
- overlayPanel = new ImagePanel(sickness, ImagePanelStyle.SCALED);
- overlayPanel.setOpaque(false);
- add(overlayPanel);
+ setOverlayPanel(new ImagePanel(sickness, ImagePanelStyle.SCALED));
+ getOverlayPanel().setOpaque(false);
+ add(getOverlayPanel());
// Imagel panel
imagePanel = new ScaledImagePanel();
@@ -301,27 +326,27 @@ public class CardPanelComponentImpl extends CardPanel {
}
private void setTypeIcon(BufferedImage bufferedImage, String toolTipText) {
- iconPanel = new JPanel();
- iconPanel.setLayout(null);
- iconPanel.setOpaque(false);
- add(iconPanel);
+ setIconPanel(new JPanel());
+ getIconPanel().setLayout(null);
+ getIconPanel().setOpaque(false);
+ add(getIconPanel());
typeButton = new JButton("");
typeButton.setLocation(2, 2);
typeButton.setSize(25, 25);
- iconPanel.setVisible(true);
+ getIconPanel().setVisible(true);
typeButton.setIcon(new ImageIcon(bufferedImage));
if (toolTipText != null) {
typeButton.setToolTipText(toolTipText);
}
- iconPanel.add(typeButton);
+ getIconPanel().add(typeButton);
}
@Override
public void cleanUp() {
super.cleanUp();
- this.counterPanel = null;
+ this.setCounterPanel(null);
}
private void setText(CardView card) {
@@ -380,9 +405,10 @@ public class CardPanelComponentImpl extends CardPanel {
}
g2d.drawImage(
- IMAGE_CACHE.get(
+ IMAGE_CACHE.getOrThrow(
new Key(getWidth(), getHeight(), getCardWidth(), getCardHeight(), getCardXOffset(), getCardYOffset(),
- hasImage, isSelected(), isChoosable(), gameCard.isPlayable(), gameCard.isCanAttack())),
+ hasImage, isSelected(), isChoosable(), getGameCard().isPlayable(), getGameCard().isCanAttack(),
+ getGameCard().isCanBlock())),
0, 0, null);
g2d.dispose();
}
@@ -418,6 +444,12 @@ public class CardPanelComponentImpl extends CardPanel {
g2d.fillRoundRect(sizes.rectSelection.x, sizes.rectSelection.y, sizes.rectSelection.width, sizes.rectSelection.height, cornerSizeSelection, cornerSizeSelection);
}
+ // draw attack or block border (?inner part of selection?)
+ if (key.canAttack || key.canBlock) {
+ g2d.setColor(new Color(255, 50, 50, 230));
+ g2d.fillRoundRect(sizes.rectSelection.x + 1, sizes.rectSelection.y + 1, sizes.rectSelection.width - 2, sizes.rectSelection.height - 2, cornerSizeSelection, cornerSizeSelection);
+ }
+
// draw empty card with border
if (!key.hasImage) {
// gray 1 px border
@@ -428,12 +460,6 @@ public class CardPanelComponentImpl extends CardPanel {
g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder);
}
- // draw attack border (inner part of selection)
- if (key.canAttack) {
- g2d.setColor(new Color(0, 0, 255, 230));
- g2d.fillRoundRect(sizes.rectBorder.x + 1, sizes.rectBorder.y + 1, sizes.rectBorder.width - 2, sizes.rectBorder.height - 2, cornerSizeBorder, cornerSizeBorder);
- }
-
// draw real card by component (see imagePanel and other layout's items)
//TODO:uncomment
@@ -477,7 +503,7 @@ public class CardPanelComponentImpl extends CardPanel {
int symbolMarginX = 2; // 2 px between icons
- String manaCost = ManaSymbols.getStringManaCost(gameCard.getManaCost());
+ String manaCost = ManaSymbols.getStringManaCost(getGameCard().getManaCost());
int manaWidth = getManaWidth(manaCost, symbolMarginX);
// right top corner with margin (sizes from any sample card, length from black border to mana icon)
@@ -497,7 +523,7 @@ public class CardPanelComponentImpl extends CardPanel {
StringTokenizer tok = new StringTokenizer(manaCost, " ");
while (tok.hasMoreTokens()) {
tok.nextToken();
- if(width != 0) {
+ if (width != 0) {
width += symbolMarginX;
}
width += getSymbolWidth();
@@ -521,32 +547,32 @@ public class CardPanelComponentImpl extends CardPanel {
imagePanel.setLocation(realCardSize.x, realCardSize.y);
imagePanel.setSize(realCardSize.width, realCardSize.height);
- if (hasSickness() && gameCard.isCreature() && isPermanent()) {
- overlayPanel.setLocation(realCardSize.x, realCardSize.y);
- overlayPanel.setSize(realCardSize.width, realCardSize.height);
+ if (hasSickness() && getGameCard().isCreature() && isPermanent()) {
+ getOverlayPanel().setLocation(realCardSize.x, realCardSize.y);
+ getOverlayPanel().setSize(realCardSize.width, realCardSize.height);
} else {
- overlayPanel.setVisible(false);
+ getOverlayPanel().setVisible(false);
}
- if (iconPanel != null) {
- iconPanel.setLocation(realCardSize.x, realCardSize.y);
- iconPanel.setSize(realCardSize.width, realCardSize.height);
+ if (getIconPanel() != null) {
+ getIconPanel().setLocation(realCardSize.x, realCardSize.y);
+ getIconPanel().setSize(realCardSize.width, realCardSize.height);
}
- if (counterPanel != null) {
- counterPanel.setLocation(realCardSize.x, realCardSize.y);
- counterPanel.setSize(realCardSize.width, realCardSize.height);
+ if (getCounterPanel() != null) {
+ getCounterPanel().setLocation(realCardSize.x, realCardSize.y);
+ getCounterPanel().setSize(realCardSize.width, realCardSize.height);
int size = cardWidth > WIDTH_LIMIT ? 40 : 20;
- minusCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size * 2);
+ minusCounterLabel.setLocation(getCounterPanel().getWidth() - size, getCounterPanel().getHeight() - size * 2);
minusCounterLabel.setSize(size, size);
- plusCounterLabel.setLocation(5, counterPanel.getHeight() - size * 2);
+ plusCounterLabel.setLocation(5, getCounterPanel().getHeight() - size * 2);
plusCounterLabel.setSize(size, size);
- loyaltyCounterLabel.setLocation(counterPanel.getWidth() - size, counterPanel.getHeight() - size);
+ loyaltyCounterLabel.setLocation(getCounterPanel().getWidth() - size, getCounterPanel().getHeight() - size);
loyaltyCounterLabel.setSize(size, size);
- otherCounterLabel.setLocation(5, counterPanel.getHeight() - size);
+ otherCounterLabel.setLocation(5, getCounterPanel().getHeight() - size);
otherCounterLabel.setSize(size, size);
}
@@ -601,7 +627,7 @@ public class CardPanelComponentImpl extends CardPanel {
@Override
public String toString() {
- return gameCard.toString();
+ return getGameCard().toString();
}
@Override
@@ -635,8 +661,8 @@ public class CardPanelComponentImpl extends CardPanel {
@Override
public void updateArtImage() {
- tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
- flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
+ setTappedAngle(isTapped() ? CardPanel.TAPPED_ANGLE : 0);
+ setFlippedAngle(isFlipped() ? CardPanel.FLIPPED_ANGLE : 0);
//final CardView gameCard = this.gameCard;
final int stamp = ++updateArtImageStamp;
@@ -644,20 +670,20 @@ public class CardPanelComponentImpl extends CardPanel {
Util.threadPool.submit(() -> {
try {
final BufferedImage srcImage;
- if (gameCard.isFaceDown()) {
+ if (getGameCard().isFaceDown()) {
srcImage = getFaceDownImage();
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
- srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
+ srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
} else {
- srcImage = ImageCache.getThumbnail(gameCard);
+ srcImage = ImageCache.getThumbnail(getGameCard());
}
if (srcImage == null) {
- setFullPath(ImageCache.getFilePath(gameCard, getCardWidth()));
+ setFullPath(ImageCache.getFilePath(getGameCard(), getCardWidth()));
}
UI.invokeLater(() -> {
if (stamp == updateArtImageStamp) {
hasImage = srcImage != null;
- setText(gameCard);
+ setText(getGameCard());
setImage(srcImage);
}
});
@@ -671,12 +697,12 @@ public class CardPanelComponentImpl extends CardPanel {
private BufferedImage getFaceDownImage() {
if (isPermanent()) {
- if (((PermanentView) gameCard).isMorphed()) {
+ if (((PermanentView) getGameCard()).isMorphed()) {
return ImageCache.getMorphImage();
} else {
return ImageCache.getManifestImage();
}
- } else if (this.gameCard instanceof StackAbilityView) {
+ } else if (this.getGameCard() instanceof StackAbilityView) {
return ImageCache.getMorphImage();
} else {
return ImageCache.getCardbackImage();
@@ -686,7 +712,7 @@ public class CardPanelComponentImpl extends CardPanel {
@Override
public void showCardTitle() {
displayTitleAnyway = true;
- setText(gameCard);
+ setText(getGameCard());
}
@Override
@@ -708,13 +734,13 @@ public class CardPanelComponentImpl extends CardPanel {
// Summoning Sickness overlay
if (hasSickness() && card.isCreature() && isPermanent()) {
- overlayPanel.setVisible(true);
+ getOverlayPanel().setVisible(true);
} else {
- overlayPanel.setVisible(false);
+ getOverlayPanel().setVisible(false);
}
// Update counters panel
- if (counterPanel != null) {
+ if (getCounterPanel() != null) {
updateCounters(card);
}
@@ -773,13 +799,13 @@ public class CardPanelComponentImpl extends CardPanel {
}
}
- counterPanel.setVisible(true);
+ getCounterPanel().setVisible(true);
} else {
plusCounterLabel.setVisible(false);
minusCounterLabel.setVisible(false);
loyaltyCounterLabel.setVisible(false);
otherCounterLabel.setVisible(false);
- counterPanel.setVisible(false);
+ getCounterPanel().setVisible(false);
}
}
@@ -805,10 +831,10 @@ public class CardPanelComponentImpl extends CardPanel {
@Override
public Image getImage() {
if (this.hasImage) {
- if (gameCard.isFaceDown()) {
+ if (getGameCard().isFaceDown()) {
return getFaceDownImage();
} else {
- return ImageCache.getImageOriginal(gameCard);
+ return ImageCache.getImageOriginal(getGameCard());
}
}
return null;
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java
index 10182f6654..cf62e7cb43 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardPanelRenderImpl.java
@@ -1,7 +1,9 @@
package org.mage.card.arcane;
-import com.google.common.collect.MapMaker;
+import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import mage.cards.action.ActionCallback;
+import mage.client.constants.Constants;
import mage.constants.CardType;
import mage.constants.SubType;
import mage.constants.SuperType;
@@ -12,12 +14,11 @@ import mage.view.StackAbilityView;
import org.apache.log4j.Logger;
import org.jdesktop.swingx.graphics.GraphicsUtilities;
import org.mage.plugins.card.images.ImageCache;
-import mage.client.constants.Constants;
import java.awt.*;
import java.awt.image.BufferedImage;
-import java.util.Map;
import java.util.UUID;
+import java.util.concurrent.ExecutionException;
public class CardPanelRenderImpl extends CardPanel {
@@ -104,9 +105,7 @@ public class CardPanelRenderImpl extends CardPanel {
// are the same for a and b
return false;
}
- if (aa.getDamage() != bb.getDamage()) {
- return false;
- }
+ return aa.getDamage() == bb.getDamage();
}
return true;
}
@@ -140,6 +139,7 @@ public class CardPanelRenderImpl extends CardPanel {
sb.append((char) (isChoosable ? 1 : 0));
sb.append((char) (this.view.isPlayable() ? 1 : 0));
sb.append((char) (this.view.isCanAttack() ? 1 : 0));
+ sb.append((char) (this.view.isCanBlock() ? 1 : 0));
sb.append((char) (this.view.isFaceDown() ? 1 : 0));
sb.append((char) this.view.getFrameStyle().ordinal());
if (this.view instanceof PermanentView) {
@@ -215,7 +215,7 @@ public class CardPanelRenderImpl extends CardPanel {
}
// Map of generated images
- private final static Map IMAGE_CACHE = new MapMaker().softValues().makeMap();
+ private final static Cache IMAGE_CACHE = CacheBuilder.newBuilder().softValues().build();
// The art image for the card, loaded in from the disk
private BufferedImage artImage;
@@ -236,7 +236,7 @@ public class CardPanelRenderImpl extends CardPanel {
super(newGameCard, gameId, loadImage, callback, foil, dimension);
// Renderer
- cardRenderer = cardRendererFactory.create(gameCard, isTransformed());
+ cardRenderer = cardRendererFactory.create(getGameCard(), isTransformed());
// Draw the parts
initialDraw();
@@ -262,10 +262,14 @@ public class CardPanelRenderImpl extends CardPanel {
if (cardImage == null) {
// Try to get card image from cache based on our card characteristics
ImageKey key
- = new ImageKey(gameCard, artImage,
- getCardWidth(), getCardHeight(),
- isChoosable(), isSelected());
- cardImage = IMAGE_CACHE.computeIfAbsent(key, k -> renderCard());
+ = new ImageKey(getGameCard(), artImage,
+ getCardWidth(), getCardHeight(),
+ isChoosable(), isSelected());
+ try {
+ cardImage = IMAGE_CACHE.get(key, this::renderCard);
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
// No cached copy exists? Render one and cache it
}
@@ -317,8 +321,8 @@ public class CardPanelRenderImpl extends CardPanel {
cardRenderer.setFaceArtImage(null);
// Stop animation
- tappedAngle = isTapped() ? CardPanel.TAPPED_ANGLE : 0;
- flippedAngle = isFlipped() ? CardPanel.FLIPPED_ANGLE : 0;
+ setTappedAngle(isTapped() ? CardPanel.TAPPED_ANGLE : 0);
+ setFlippedAngle(isFlipped() ? CardPanel.FLIPPED_ANGLE : 0);
// Schedule a repaint
repaint();
@@ -333,16 +337,16 @@ public class CardPanelRenderImpl extends CardPanel {
try {
final BufferedImage srcImage;
final BufferedImage faceArtSrcImage;
- if (gameCard.isFaceDown()) {
+ if (getGameCard().isFaceDown()) {
// Nothing to do
srcImage = null;
faceArtSrcImage = null;
} else if (getCardWidth() > Constants.THUMBNAIL_SIZE_FULL.width) {
- srcImage = ImageCache.getImage(gameCard, getCardWidth(), getCardHeight());
- faceArtSrcImage = ImageCache.getFaceImage(gameCard, getCardWidth(), getCardHeight());
+ srcImage = ImageCache.getImage(getGameCard(), getCardWidth(), getCardHeight());
+ faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight());
} else {
- srcImage = ImageCache.getThumbnail(gameCard);
- faceArtSrcImage = ImageCache.getFaceImage(gameCard, getCardWidth(), getCardHeight());
+ srcImage = ImageCache.getThumbnail(getGameCard());
+ faceArtSrcImage = ImageCache.getFaceImage(getGameCard(), getCardWidth(), getCardHeight());
}
UI.invokeLater(() -> {
@@ -375,7 +379,7 @@ public class CardPanelRenderImpl extends CardPanel {
// Update renderer
cardImage = null;
- cardRenderer = cardRendererFactory.create(gameCard, isTransformed());
+ cardRenderer = cardRendererFactory.create(getGameCard(), isTransformed());
cardRenderer.setArtImage(artImage);
cardRenderer.setFaceArtImage(faceArtImage);
@@ -398,12 +402,12 @@ public class CardPanelRenderImpl extends CardPanel {
private BufferedImage getFaceDownImage() {
if (isPermanent()) {
- if (((PermanentView) gameCard).isMorphed()) {
+ if (((PermanentView) getGameCard()).isMorphed()) {
return ImageCache.getMorphImage();
} else {
return ImageCache.getManifestImage();
}
- } else if (this.gameCard instanceof StackAbilityView) {
+ } else if (this.getGameCard() instanceof StackAbilityView) {
return ImageCache.getMorphImage();
} else {
return ImageCache.getCardbackImage();
@@ -433,10 +437,10 @@ public class CardPanelRenderImpl extends CardPanel {
@Override
public Image getImage() {
if (artImage != null) {
- if (gameCard.isFaceDown()) {
+ if (getGameCard().isFaceDown()) {
return getFaceDownImage();
} else {
- return ImageCache.getImageOriginal(gameCard);
+ return ImageCache.getImageOriginal(getGameCard());
}
}
return null;
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java
index a042e9665c..eed6403067 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRenderer.java
@@ -5,6 +5,7 @@
*/
package org.mage.card.arcane;
+import mage.abilities.hint.HintUtils;
import mage.cards.ArtRect;
import mage.client.dialog.PreferencesDialog;
import mage.constants.AbilityType;
@@ -137,12 +138,18 @@ public abstract class CardRenderer {
}
protected void parseRules(List stringRules, ArrayList keywords, ArrayList rules) {
- // Translate the textbox text
+ // Translate the textbox text and remove card hints
for (String rule : stringRules) {
+ // remove all card hints
+ if (rule.equals(HintUtils.HINT_START_MARK)) {
+ break;
+ }
+
// Kill reminder text
if (PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_RENDERING_REMINDER_TEXT, "false").equals("false")) {
rule = CardRendererUtils.killReminderText(rule).trim();
}
+
if (!rule.isEmpty()) {
TextboxRule tbRule = TextboxRuleParser.parse(cardView, rule);
if (tbRule.type == TextboxRuleType.SIMPLE_KEYWORD) {
@@ -388,7 +395,7 @@ public abstract class CardRenderer {
// Return the width of the drawn symbol
protected int drawExpansionSymbol(Graphics2D g, int x, int y, int w, int h) {
// Draw the expansion symbol
- Image setSymbol = ManaSymbols.getSetSymbolImage(cardView.getExpansionSetCode(), cardView.getRarity().getCode());
+ Image setSymbol = ManaSymbols.getSetSymbolImage(cardView.getExpansionSetCode(), cardView.getRarity());
int setSymbolWidth;
if (setSymbol == null) {
// Don't draw anything when we don't have a set symbol
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java
index 3702f176cb..f32c08e972 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/CardRendererUtils.java
@@ -1,8 +1,3 @@
-/*
- * To change this license header, choose License Headers in Project Properties.
- * To change this template file, choose Tools | Templates
- * and open the template in the editor.
- */
package org.mage.card.arcane;
import java.awt.*;
@@ -63,26 +58,26 @@ public final class CardRendererUtils {
int plus_b = (int) ((255 - b) / 2);
return new Color(r + plus_r,
- g + plus_g,
- b + plus_b,
- alpha);
+ g + plus_g,
+ b + plus_b,
+ alpha);
}
-
+
public static Color abitdarker(Color c) {
int r = c.getRed();
int g = c.getGreen();
int b = c.getBlue();
int alpha = c.getAlpha();
- int plus_r = (int) (Math.min (255 - r, r) / 2);
- int plus_g = (int) (Math.min (255 - g, g) / 2);
- int plus_b = (int) (Math.min (255 - b, b) / 2);
+ int plus_r = (int) (Math.min(255 - r, r) / 2);
+ int plus_g = (int) (Math.min(255 - g, g) / 2);
+ int plus_b = (int) (Math.min(255 - b, b) / 2);
return new Color(r - plus_r,
- g - plus_g,
- b - plus_b,
- alpha);
- }
+ g - plus_g,
+ b - plus_b,
+ alpha);
+ }
// Draw a rounded box with a 2-pixel border
// Used on various card parts.
@@ -192,4 +187,12 @@ public final class CardRendererUtils {
.replaceAll("", "")
.replaceAll("", "");
}
+
+ public static Color copyColor(Color color) {
+ if (color != null) {
+ return new Color(color.getRed(), color.getGreen(), color.getBlue(), color.getAlpha());
+ } else {
+ return null;
+ }
+ }
}
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java
index bc7ac1617d..8d95a11ac0 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/GlowText.java
@@ -1,7 +1,5 @@
package org.mage.card.arcane;
-import com.google.common.base.Function;
-import com.google.common.collect.MapMaker;
import java.awt.*;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
@@ -15,10 +13,18 @@ import java.text.BreakIterator;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
+
import javax.swing.*;
-import mage.client.util.ImageCaches;
+
import org.jdesktop.swingx.graphics.GraphicsUtilities;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+
+import mage.client.util.ImageCaches;
+import mage.client.util.SoftValuesLoadingCache;
+
public class GlowText extends JLabel {
private static final long serialVersionUID = 1827677946939348001L;
@@ -28,9 +34,9 @@ public class GlowText extends JLabel {
private Color glowColor;
private boolean wrap;
private int lineCount = 0;
- private final static Map IMAGE_CACHE;
+ private static final SoftValuesLoadingCache IMAGE_CACHE;
- private final static class Key {
+ private static final class Key {
final int width;
final int height;
@@ -125,7 +131,7 @@ public class GlowText extends JLabel {
}
static {
- IMAGE_CACHE = ImageCaches.register(new MapMaker().softValues().makeComputingMap((Function) GlowText::createImage));
+ IMAGE_CACHE = ImageCaches.register(SoftValuesLoadingCache.from(GlowText::createGlowImage));
}
public void setGlow(Color glowColor, int size, float intensity) {
@@ -152,10 +158,10 @@ public class GlowText extends JLabel {
return;
}
- g.drawImage(IMAGE_CACHE.get(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)), 0, 0, null);
+ g.drawImage(IMAGE_CACHE.getOrThrow(new Key(getWidth(), getHeight(), getText(), getFont(), getForeground(), glowSize, glowIntensity, glowColor, wrap)), 0, 0, null);
}
- private static BufferedImage createImage(Key key) {
+ private static BufferedImage createGlowImage(Key key) {
Dimension size = new Dimension(key.width, key.height);
BufferedImage image = GraphicsUtilities.createCompatibleTranslucentImage(size.width, size.height);
Graphics2D g2d = image.createGraphics();
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
index 1f972175eb..75d361cbf7 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbols.java
@@ -1,43 +1,6 @@
package org.mage.card.arcane;
-import java.awt.Color;
-import java.awt.Dimension;
-import java.awt.Font;
-import java.awt.Graphics;
-import java.awt.Graphics2D;
-import java.awt.Image;
-import java.awt.Rectangle;
-import java.awt.RenderingHints;
-import java.awt.Toolkit;
-import java.awt.image.BufferedImage;
-import java.awt.image.FilteredImageSource;
-import java.awt.image.ImageProducer;
-import java.awt.image.RGBImageFilter;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.nio.file.FileSystems;
-import java.nio.file.FileVisitResult;
-import java.nio.file.Files;
-import java.nio.file.Path;
-import java.nio.file.PathMatcher;
-import java.nio.file.Paths;
-import java.nio.file.SimpleFileVisitor;
-import java.nio.file.StandardCopyOption;
-import java.nio.file.attribute.BasicFileAttributes;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.StringTokenizer;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicBoolean;
-import java.util.regex.Pattern;
-import java.util.stream.IntStream;
-import javax.imageio.ImageIO;
-import javax.swing.*;
-
+import mage.abilities.hint.HintUtils;
import mage.cards.repository.ExpansionRepository;
import mage.client.MageFrame;
import mage.client.constants.Constants;
@@ -46,8 +9,10 @@ import mage.client.constants.Constants.ResourceSymbolSize;
import mage.client.util.GUISizeHelper;
import mage.client.util.ImageHelper;
import mage.client.util.gui.BufferedImageBuilder;
+import mage.client.util.gui.GuiDisplayUtil;
+import mage.constants.Rarity;
import mage.utils.StreamUtils;
-import org.apache.batik.dom.svg.SVGDOMImplementation;
+import org.apache.batik.anim.dom.SVGDOMImplementation;
import org.apache.batik.transcoder.TranscoderException;
import org.apache.batik.transcoder.TranscoderInput;
import org.apache.batik.transcoder.TranscoderOutput;
@@ -57,6 +22,26 @@ import org.apache.batik.util.SVGConstants;
import org.apache.log4j.Logger;
import org.mage.plugins.card.utils.CardImageUtils;
+import javax.imageio.ImageIO;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.awt.image.FilteredImageSource;
+import java.awt.image.ImageProducer;
+import java.awt.image.RGBImageFilter;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.nio.file.*;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.List;
+import java.util.*;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.regex.Pattern;
+import java.util.stream.IntStream;
+
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
public final class ManaSymbols {
@@ -64,10 +49,10 @@ public final class ManaSymbols {
private static final Logger LOGGER = Logger.getLogger(ManaSymbols.class);
private static final Map> manaImages = new HashMap<>();
- private static final Map> setImages = new ConcurrentHashMap<>();
+ private static final Map> setImages = new ConcurrentHashMap<>();
- private static final HashSet onlyMythics = new HashSet<>();
- private static final HashSet withoutSymbols = new HashSet<>();
+ private static final Set onlyMythics = new HashSet<>();
+ private static final Set withoutSymbols = new HashSet<>();
static {
onlyMythics.add("DRB");
@@ -86,11 +71,13 @@ public final class ManaSymbols {
private static final Map setImagesExist = new HashMap<>();
private static final Pattern REPLACE_SYMBOLS_PATTERN = Pattern.compile("\\{([^}/]*)/?([^}]*)\\}");
- private static final String[] symbols = new String[]{"0", "1", "10", "11", "12", "15", "16", "2", "3", "4", "5", "6", "7", "8", "9",
+ private static final String[] symbols = new String[]{
+ "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
+ "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20",
"B", "BG", "BR", "BP", "2B",
"G", "GU", "GW", "GP", "2G",
"R", "RG", "RW", "RP", "2R",
- "S", "T",
+ "S", "T", "Q",
"U", "UB", "UR", "UP", "2U",
"W", "WB", "WU", "WP", "2W",
"X", "C", "E"};
@@ -186,19 +173,19 @@ public final class ManaSymbols {
continue;
}
- String[] codes;
+ Set codes;
if (onlyMythics.contains(set)) {
- codes = new String[]{"M"};
+ codes = EnumSet.of(Rarity.MYTHIC);
} else {
- codes = new String[]{"C", "U", "R", "M"};
+ codes = EnumSet.of(Rarity.COMMON, Rarity.UNCOMMON, Rarity.RARE, Rarity.MYTHIC);
}
- Map rarityImages = new HashMap<>();
+ Map rarityImages = new EnumMap<>(Rarity.class);
setImages.put(set, rarityImages);
// load medium size
- for (String rarityCode : codes) {
- File file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM) + set + '-' + rarityCode + ".jpg");
+ for (Rarity rarityCode : codes) {
+ File file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM) + set + '-' + rarityCode.getCode() + ".jpg");
try {
Image image = UI.getImageIcon(file.getAbsolutePath()).getImage();
int width = image.getWidth(null);
@@ -223,7 +210,7 @@ public final class ManaSymbols {
file.mkdirs();
}
String pathRoot = getResourceSetsPath(ResourceSetSize.SMALL) + set;
- for (String code : codes) {
+ for (Rarity code : codes) {
File newFile = new File(pathRoot + '-' + code + ".png");
if (!(MageFrame.isSkipSmallSymbolGenerationForExisting() && newFile.exists())) {// skip if option enabled and file already exists
file = new File(getResourceSetsPath(ResourceSetSize.MEDIUM) + set + '-' + code + ".png");
@@ -784,7 +771,18 @@ public final class ManaSymbols {
" ";
+ Integer width = setImagesExist.get(set).width * factor;
+ Integer height = setImagesExist.get(set).height * factor;
+ return " ";
} else {
return set;
}
}
public static Image getSetSymbolImage(String set) {
- return getSetSymbolImage(set, "C");
+ return getSetSymbolImage(set, Rarity.COMMON);
}
- public static Image getSetSymbolImage(String set, String rarity) {
- Map rarityImages = setImages.get(set);
+ public static Image getSetSymbolImage(String set, Rarity rarity) {
+ Map rarityImages = setImages.get(set);
if (rarityImages != null) {
return rarityImages.get(rarity);
} else {
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java
index 46a0e3f1c2..bd9731ec6a 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/ManaSymbolsCellRenderer.java
@@ -9,10 +9,13 @@ import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.StringTokenizer;
+/**
+ * @author JayDi85
+ */
public final class ManaSymbolsCellRenderer extends DefaultTableCellRenderer {
// base panel to render
- private JPanel manaPanel = new JPanel();
+ private JPanel renderPanel = new JPanel();
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
@@ -20,47 +23,47 @@ public final class ManaSymbolsCellRenderer extends DefaultTableCellRenderer {
// get table text cell settings
DefaultTableCellRenderer baseRenderer = (DefaultTableCellRenderer) table.getDefaultRenderer(String.class);
- JLabel baseLabel = (JLabel)baseRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
+ JLabel baseComp = (JLabel) baseRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
// apply settings to mana panel from parent
- manaPanel.setOpaque(baseLabel.isOpaque());
- manaPanel.setForeground(baseLabel.getForeground());
- manaPanel.setBackground(baseLabel.getBackground());
+ renderPanel.setOpaque(baseComp.isOpaque());
+ renderPanel.setForeground(CardRendererUtils.copyColor(baseComp.getForeground()));
+ renderPanel.setBackground(CardRendererUtils.copyColor(baseComp.getBackground()));
+ renderPanel.setBorder(baseComp.getBorder());
// icons size with margin
int symbolWidth = GUISizeHelper.symbolTableSize;
int symbolHorizontalMargin = 2;
// create each mana symbol as child label
- String manaCost = (String)value;
- manaPanel.removeAll();
- manaPanel.setLayout(new BoxLayout(manaPanel, BoxLayout.X_AXIS));
- if(manaCost != null){
+ String manaCost = (String) value;
+ renderPanel.removeAll();
+ renderPanel.setLayout(new BoxLayout(renderPanel, BoxLayout.X_AXIS));
+ if (manaCost != null) {
StringTokenizer tok = new StringTokenizer(manaCost, " ");
while (tok.hasMoreTokens()) {
String symbol = tok.nextToken();
JLabel symbolLabel = new JLabel();
//symbolLabel.setBorder(new LineBorder(new Color(150, 150, 150))); // debug
- symbolLabel.setBorder(new EmptyBorder(0, symbolHorizontalMargin,0, 0));
+ symbolLabel.setBorder(new EmptyBorder(0, symbolHorizontalMargin, 0, 0));
BufferedImage image = ManaSymbols.getSizedManaSymbol(symbol, symbolWidth);
- if (image != null){
+ if (image != null) {
// icon
symbolLabel.setIcon(new ImageIcon(image));
- }else
- {
+ } else {
// text
symbolLabel.setText("{" + symbol + "}");
- symbolLabel.setOpaque(baseLabel.isOpaque());
- symbolLabel.setForeground(baseLabel.getForeground());
- symbolLabel.setBackground(baseLabel.getBackground());
+ symbolLabel.setOpaque(baseComp.isOpaque());
+ symbolLabel.setForeground(baseComp.getForeground());
+ symbolLabel.setBackground(baseComp.getBackground());
}
- manaPanel.add(symbolLabel);
+ renderPanel.add(symbolLabel);
}
}
- return manaPanel;
+ return renderPanel;
}
}
diff --git a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java
index 4aea652c0b..fbdac1c3fa 100644
--- a/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java
+++ b/Mage.Client/src/main/java/org/mage/card/arcane/ModernCardRenderer.java
@@ -5,24 +5,6 @@
*/
package org.mage.card.arcane;
-import java.awt.*;
-import java.awt.font.*;
-import java.awt.geom.Arc2D;
-import java.awt.geom.Area;
-import java.awt.geom.Path2D;
-import java.awt.geom.Rectangle2D;
-import java.awt.geom.RoundRectangle2D;
-import java.awt.image.BufferedImage;
-import java.io.IOException;
-import java.io.InputStream;
-import java.net.URL;
-import java.text.AttributedCharacterIterator;
-import java.text.AttributedString;
-import java.text.CharacterIterator;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import javax.swing.*;
import mage.ObjectColor;
import mage.cards.ArtRect;
import mage.cards.FrameStyle;
@@ -34,6 +16,22 @@ import mage.util.SubTypeList;
import mage.view.CardView;
import mage.view.PermanentView;
import org.apache.log4j.Logger;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.font.*;
+import java.awt.geom.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.text.AttributedCharacterIterator;
+import java.text.AttributedString;
+import java.text.CharacterIterator;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol;
@@ -56,14 +54,15 @@ import static org.mage.card.arcane.ManaSymbols.getSizedManaSymbol;
render.draw(g, cardWidth, cardHeight);
}
*/
+
/**
* @author stravant@gmail.com
- *
+ *
* Base rendering class for new border cards
*/
public class ModernCardRenderer extends CardRenderer {
- private final static Logger LOGGER = Logger.getLogger(ModernCardRenderer.class);
+ private static final Logger LOGGER = Logger.getLogger(ModernCardRenderer.class);
///////////////////////////////////////////////////////////////////////////
// Textures for modern frame cards
@@ -98,6 +97,7 @@ public class ModernCardRenderer extends CardRenderer {
}
return new Font("Arial", Font.PLAIN, 1);
}
+
public static final Font BASE_BELEREN_FONT = loadFont("beleren-bold");
public static final Paint BG_TEXTURE_WHITE = loadBackgroundTexture("white");
@@ -275,7 +275,9 @@ public class ModernCardRenderer extends CardRenderer {
} else if (cardView.isPlayable()) {
borderColor = new Color(153, 102, 204, 200);
} else if (cardView.isCanAttack()) {
- borderColor = new Color(0, 0, 255, 230);
+ borderColor = new Color(255, 50, 50, 230);
+ } else if (cardView.isCanBlock()) {
+ borderColor = new Color(255, 50, 50, 230);
} else {
borderColor = Color.BLACK;
}
@@ -480,7 +482,7 @@ public class ModernCardRenderer extends CardRenderer {
g.setPaint(borderPaint);
if (cardView.getFrameStyle() == FrameStyle.KLD_INVENTION) {
- g.drawImage(FRAME_INVENTION, 0, 0, cardWidth, cardHeight, null);
+ g.drawImage(FRAME_INVENTION, 3, 3, cardWidth - 6, cardHeight - 6, null);
g.drawRect(
totalContentInset, typeLineY,
contentWidth - 1, cardHeight - borderWidth * 3 - typeLineY - 1);
@@ -659,7 +661,7 @@ public class ModernCardRenderer extends CardRenderer {
}
public void drawZendikarCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
- Color boxColor, Paint paint) {
+ Color boxColor, Paint paint) {
BufferedImage artToUse = faceArtImage;
boolean hadToUseFullArt = false;
@@ -707,8 +709,8 @@ public class ModernCardRenderer extends CardRenderer {
}
public void drawBFZCurvedFace(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
- int topxdelta, int endydelta,
- Color boxColor, Paint paint) {
+ int topxdelta, int endydelta,
+ Color boxColor, Paint paint) {
BufferedImage artToUse = faceArtImage;
boolean hadToUseFullArt = false;
if (faceArtImage == null) {
@@ -764,10 +766,10 @@ public class ModernCardRenderer extends CardRenderer {
}
public void drawUSTCurves(Graphics2D g2, BufferedImage image, int x, int y, int x2, int y2,
- int topxdelta, int endydelta,
- Color boxColor, Paint paint) {
+ int topxdelta, int endydelta,
+ Color boxColor, Paint paint) {
BufferedImage artToUse = artImage;
-
+
int srcW = x2;
int srcH = y2;
if (artToUse != null) {
@@ -999,23 +1001,23 @@ public class ModernCardRenderer extends CardRenderer {
Polygon symbol = new Polygon(
new int[]{
- x + w / 2,
- (int) (x + w * 0.9),
- x + w,
- (int) (x + w * 0.6),
- x + w / 2,
- (int) (x + w * 0.4),
- x,
- (int) (x + w * 0.1),},
+ x + w / 2,
+ (int) (x + w * 0.9),
+ x + w,
+ (int) (x + w * 0.6),
+ x + w / 2,
+ (int) (x + w * 0.4),
+ x,
+ (int) (x + w * 0.1),},
new int[]{
- y + h,
- (int) (y + 0.8 * h),
- y,
- (int) (y - 0.2 * h),
- y,
- (int) (y - 0.2 * h),
- y,
- (int) (y + 0.8 * h),},
+ y + h,
+ (int) (y + 0.8 * h),
+ y,
+ (int) (y - 0.2 * h),
+ y,
+ (int) (y - 0.2 * h),
+ y,
+ (int) (y + 0.8 * h),},
8);
// Draw + stroke
@@ -1124,7 +1126,7 @@ public class ModernCardRenderer extends CardRenderer {
drawBasicManaTextbox(g, x, y, w, h, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol());
return;
} else // Big circle in the middle for Zendikar lands
- if (allRules.size() == 1) {
+ if (allRules.size() == 1) {
// Size of mana symbol = 9/4 * h, 3/4h above line
if (allRules.get(0) instanceof TextboxBasicManaRule) {
drawBasicManaSymbol(g, x + w / 2 - 9 * h / 8 + 1, y - 3 * h / 4, 9 * h / 4, 9 * h / 4, ((TextboxBasicManaRule) allRules.get(0)).getBasicManaSymbol());
@@ -1278,45 +1280,45 @@ public class ModernCardRenderer extends CardRenderer {
if (loyaltyRule.loyaltyChange < 0 || loyaltyRule.loyaltyChange == TextboxLoyaltyRule.MINUS_X) {
symbol = new Polygon(
new int[]{
- symbolX,
- symbolX + symbolWidth,
- symbolX + symbolWidth,
- symbolX + symbolWidth / 2,
- symbolX,},
+ symbolX,
+ symbolX + symbolWidth,
+ symbolX + symbolWidth,
+ symbolX + symbolWidth / 2,
+ symbolX,},
new int[]{
- symbolY,
- symbolY,
- symbolY + symbolHeight - 3,
- symbolY + symbolHeight + 3,
- symbolY + symbolHeight - 3,},
+ symbolY,
+ symbolY,
+ symbolY + symbolHeight - 3,
+ symbolY + symbolHeight + 3,
+ symbolY + symbolHeight - 3,},
5);
} else if (loyaltyRule.loyaltyChange > 0) {
symbol = new Polygon(
new int[]{
- symbolX,
- symbolX + symbolWidth / 2,
- symbolX + symbolWidth,
- symbolX + symbolWidth,
- symbolX,},
+ symbolX,
+ symbolX + symbolWidth / 2,
+ symbolX + symbolWidth,
+ symbolX + symbolWidth,
+ symbolX,},
new int[]{
- symbolY + 3,
- symbolY - 3,
- symbolY + 3,
- symbolY + symbolHeight,
- symbolY + symbolHeight,},
+ symbolY + 3,
+ symbolY - 3,
+ symbolY + 3,
+ symbolY + symbolHeight,
+ symbolY + symbolHeight,},
5);
} else {
symbol = new Polygon(
new int[]{
- symbolX,
- symbolX + symbolWidth,
- symbolX + symbolWidth,
- symbolX,},
+ symbolX,
+ symbolX + symbolWidth,
+ symbolX + symbolWidth,
+ symbolX,},
new int[]{
- symbolY,
- symbolY,
- symbolY + symbolHeight,
- symbolY + symbolHeight,},
+ symbolY,
+ symbolY,
+ symbolY + symbolHeight,
+ symbolY + symbolHeight,},
4);
}
g.setColor(new Color(0, 0, 0, 128));
@@ -1610,13 +1612,13 @@ public class ModernCardRenderer extends CardRenderer {
Color[] translatedColors;
if (types.contains(CardType.LAND)) {
translatedColors = new Color[]{
- getLandTextboxColor(twoColors.get(0)),
- getLandTextboxColor(twoColors.get(1))
+ getLandTextboxColor(twoColors.get(0)),
+ getLandTextboxColor(twoColors.get(1))
};
} else {
translatedColors = new Color[]{
- getTextboxColor(twoColors.get(0)),
- getTextboxColor(twoColors.get(1))
+ getTextboxColor(twoColors.get(0)),
+ getTextboxColor(twoColors.get(1))
};
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java
index c8a403dc3d..4e690bf1a5 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/CardPluginImpl.java
@@ -1,26 +1,9 @@
package org.mage.plugins.card;
-import java.awt.BorderLayout;
-import java.awt.Dimension;
-import java.awt.Frame;
-import java.awt.Rectangle;
-import java.awt.event.WindowAdapter;
-import java.awt.event.WindowEvent;
-import java.awt.image.BufferedImage;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-import java.util.concurrent.TimeUnit;
-import javax.swing.JComponent;
-import javax.swing.JDialog;
-import javax.swing.JLayeredPane;
import mage.cards.MagePermanent;
import mage.cards.action.ActionCallback;
import mage.client.dialog.PreferencesDialog;
import mage.client.util.GUISizeHelper;
-import mage.constants.Rarity;
import mage.interfaces.plugin.CardPlugin;
import mage.view.CardView;
import mage.view.CounterView;
@@ -41,14 +24,21 @@ import org.mage.plugins.card.dl.sources.ScryfallSymbolsSource;
import org.mage.plugins.card.images.ImageCache;
import org.mage.plugins.card.info.CardInfoPaneImpl;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.awt.image.BufferedImage;
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.util.List;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+
/**
* {@link CardPlugin} implementation.
*
- * @author nantuko
- * @version 0.1 01.11.2010 Mage permanents. Sorting card layout.
- * @version 0.6 17.07.2011 #sortPermanents got option to display non-land
- * permanents in one pile
- * @version 0.7 29.07.2011 face down cards support
+ * @author nantuko, JayDi85
*/
@PluginImplementation
@Author(name = "nantuko")
@@ -584,6 +574,10 @@ public class CardPluginImpl implements CardPlugin {
}
}
+ private void symbolsOnFinish() {
+
+ }
+
/**
* Download various symbols (mana, tap, set).
*
@@ -591,23 +585,25 @@ public class CardPluginImpl implements CardPlugin {
*/
@Override
public void downloadSymbols(String imagesDir) {
- final DownloadGui g = new DownloadGui(new Downloader());
+ final Downloader downloader = new Downloader();
+ final DownloadGui downloadGui = new DownloadGui(downloader);
- Iterable it;
+ LOGGER.info("Symbols download prepare...");
+ Iterable jobs;
- it = new GathererSymbols();
- for (DownloadJob job : it) {
- g.getDownloader().add(job);
+ jobs = new GathererSymbols();
+ for (DownloadJob job : jobs) {
+ downloader.add(job);
}
- it = new GathererSets();
- for (DownloadJob job : it) {
- g.getDownloader().add(job);
+ jobs = new GathererSets();
+ for (DownloadJob job : jobs) {
+ downloader.add(job);
}
- it = new ScryfallSymbolsSource();
- for (DownloadJob job : it) {
- g.getDownloader().add(job);
+ jobs = new ScryfallSymbolsSource();
+ for (DownloadJob job : jobs) {
+ downloader.add(job);
}
/*
@@ -615,26 +611,56 @@ public class CardPluginImpl implements CardPlugin {
for (DownloadJob job : it) {
g.getDownloader().add(job);
}
- */
- it = new DirectLinksForDownload();
- for (DownloadJob job : it) {
- g.getDownloader().add(job);
+ */
+
+ jobs = new DirectLinksForDownload();
+ for (DownloadJob job : jobs) {
+ downloader.add(job);
}
- JDialog d = new JDialog((Frame) null, "Download symbols", false);
- d.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
- d.addWindowListener(new WindowAdapter() {
+ LOGGER.info("Symbols download needs " + downloader.getJobs().size() + " files");
+
+ // download GUI dialog
+ JDialog dialog = new JDialog((Frame) null, "Download symbols", false);
+ dialog.setDefaultCloseOperation(JDialog.DISPOSE_ON_CLOSE);
+ dialog.addWindowListener(new WindowAdapter() {
@Override
public void windowClosing(WindowEvent e) {
- g.getDownloader().dispose();
- ManaSymbols.loadImages();
- // TODO: check reload process after download (icons do not update)
+ // user force to close window/downloader
+ downloader.cleanup();
}
});
- d.setLayout(new BorderLayout());
- d.add(g);
- d.pack();
- d.setVisible(true);
+ dialog.setLayout(new BorderLayout());
+ dialog.add(downloadGui);
+ dialog.pack();
+ dialog.setVisible(true);
+
+ // downloader controller thread
+ SwingWorker worker = new SwingWorker() {
+ @Override
+ protected Void doInBackground() throws Exception {
+ downloader.publishAllJobs();
+ downloader.waitFinished();
+ downloader.cleanup();
+ return null;
+ }
+ };
+ // downloader finisher
+ worker.addPropertyChangeListener(new PropertyChangeListener() {
+ @Override
+ public void propertyChange(PropertyChangeEvent evt) {
+ if (evt.getPropertyName().equals("state")) {
+ if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
+ // all done, can close dialog and refresh symbols for UI
+ LOGGER.info("Symbols download finished");
+ dialog.dispose();
+ ManaSymbols.loadImages();
+ ImageCache.clearCache();
+ }
+ }
+ }
+ });
+ worker.execute();
}
@Override
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadGui.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadGui.java
index e3ff2ca179..3ae0601657 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadGui.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadGui.java
@@ -1,113 +1,105 @@
-/**
- * DownloadGui.java
- *
- * Created on 25.08.2010
- */
-
package org.mage.plugins.card.dl;
+import org.mage.plugins.card.dl.DownloadJob.State;
-import java.awt.BorderLayout;
-import java.awt.Dimension;
+import javax.swing.*;
+import java.awt.*;
import java.beans.IndexedPropertyChangeEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.HashMap;
import java.util.Map;
-import javax.swing.BorderFactory;
-import javax.swing.BoundedRangeModel;
-import javax.swing.BoxLayout;
-import javax.swing.DefaultBoundedRangeModel;
-import javax.swing.JButton;
-import javax.swing.JPanel;
-import javax.swing.JProgressBar;
-import javax.swing.JScrollPane;
-
-import org.mage.plugins.card.dl.DownloadJob.State;
-
/**
- * The class DownloadGui.
- *
- * @version V0.0 25.08.2010
+ * Downloader GUI to control and show progress
+ *
* @author Clemens Koza
*/
public class DownloadGui extends JPanel {
- private static final long serialVersionUID = -7346572382493844327L;
+ private static final long serialVersionUID = -7346572382493844327L;
- private final Downloader d;
- private final DownloadListener l = new DownloadListener();
- private final BoundedRangeModel model = new DefaultBoundedRangeModel(0, 0, 0, 0);
- private final JProgressBar progress = new JProgressBar(model);
+ private final Downloader downloader;
+ private final DownloadListener listener = new DownloadListener();
+ private final BoundedRangeModel progressModel = new DefaultBoundedRangeModel(0, 0, 0, 0);
+ private final JProgressBar progressBar = new JProgressBar(progressModel);
- private final Map progresses = new HashMap<>();
- private final JPanel panel = new JPanel();
+ private final Map jobPanels = new HashMap<>();
+ private final JPanel basicPanel = new JPanel();
public DownloadGui(Downloader downloader) {
super(new BorderLayout());
- this.d = downloader;
- downloader.addPropertyChangeListener(l);
+ this.downloader = downloader;
+ downloader.addPropertyChangeListener(listener);
JPanel p = new JPanel(new BorderLayout());
p.setBorder(BorderFactory.createTitledBorder("Progress:"));
- p.add(progress);
- JButton b = new JButton("X");
- b.addActionListener(e -> {
- d.dispose();
+ p.add(progressBar);
+ JButton closeButton = new JButton("X");
+ closeButton.addActionListener(e -> {
+ this.downloader.cleanup();
});
- p.add(b, BorderLayout.EAST);
+ p.add(closeButton, BorderLayout.EAST);
add(p, BorderLayout.NORTH);
- panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
- JScrollPane pane = new JScrollPane(panel);
+ basicPanel.setLayout(new BoxLayout(basicPanel, BoxLayout.Y_AXIS));
+ JScrollPane pane = new JScrollPane(basicPanel);
pane.setPreferredSize(new Dimension(500, 300));
add(pane);
- for(int i = 0; i < downloader.getJobs().size(); i++) {
+ for (int i = 0; i < downloader.getJobs().size(); i++) {
addJob(i, downloader.getJobs().get(i));
}
}
public Downloader getDownloader() {
- return d;
+ return downloader;
}
private class DownloadListener implements PropertyChangeListener {
+
@Override
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
- if(evt.getSource() instanceof DownloadJob) {
- DownloadPanel p = progresses.get(evt.getSource());
+ if (evt.getSource() instanceof DownloadJob) {
+ // one job changes
+ DownloadPanel panel = jobPanels.get(evt.getSource());
switch (name) {
case "state":
- if(evt.getOldValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
+ if (evt.getOldValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
+ // started
changeProgress(-1, 0);
- } if(evt.getNewValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
- changeProgress(+1, 0);
- } if(p != null) {
- p.setVisible(p.getJob().getState() != State.FINISHED);
- p.revalidate();
- } break;
+ }
+ if (evt.getNewValue() == State.FINISHED || evt.getOldValue() == State.ABORTED) {
+ // finished
+ changeProgress(+1, 0);
+ }
+ if (panel != null) {
+ panel.setVisible(panel.getJob().getState() != State.FINISHED);
+ panel.revalidate();
+ }
+ break;
case "message":
- if(p != null) {
- JProgressBar bar = p.getBar();
- String message = p.getJob().getMessage();
+ if (panel != null) {
+ JProgressBar bar = panel.getBar();
+ String message = panel.getJob().getMessage();
bar.setStringPainted(message != null);
bar.setString(message);
- } break;
+ }
+ break;
}
- } else if(evt.getSource() == d) {
- if("jobs".equals(name)) {
+ } else if (evt.getSource() == downloader) {
+ // all jobs changes (add/delete)
+ if ("jobs".equals(name)) {
IndexedPropertyChangeEvent ev = (IndexedPropertyChangeEvent) evt;
int index = ev.getIndex();
DownloadJob oldValue = (DownloadJob) ev.getOldValue();
- if(oldValue != null) {
+ if (oldValue != null) {
removeJob(index, oldValue);
}
DownloadJob newValue = (DownloadJob) ev.getNewValue();
- if(newValue != null) {
+ if (newValue != null) {
addJob(index, newValue);
}
}
@@ -116,39 +108,39 @@ public class DownloadGui extends JPanel {
}
private synchronized void addJob(int index, DownloadJob job) {
- job.addPropertyChangeListener(l);
+ job.addPropertyChangeListener(listener);
changeProgress(0, +1);
DownloadPanel p = new DownloadPanel(job);
- progresses.put(job, p);
- panel.add(p, index);
- panel.revalidate();
+ jobPanels.put(job, p);
+ basicPanel.add(p, index);
+ basicPanel.revalidate();
}
private synchronized void removeJob(int index, DownloadJob job) {
- assert progresses.get(job) == panel.getComponent(index);
- job.removePropertyChangeListener(l);
+ assert jobPanels.get(job) == basicPanel.getComponent(index);
+ job.removePropertyChangeListener(listener);
changeProgress(0, -1);
- progresses.remove(job);
- panel.remove(index);
- panel.revalidate();
+ jobPanels.remove(job);
+ basicPanel.remove(index);
+ basicPanel.revalidate();
}
private synchronized void changeProgress(int progress, int total) {
- progress += model.getValue();
- total += model.getMaximum();
- model.setMaximum(total);
- model.setValue(progress);
- this.progress.setStringPainted(true);
- this.progress.setString(progress + "/" + total);
+ progress += progressModel.getValue();
+ total += progressModel.getMaximum();
+ progressModel.setMaximum(total);
+ progressModel.setValue(progress);
+ this.progressBar.setStringPainted(true);
+ this.progressBar.setString(progress + "/" + total);
}
private class DownloadPanel extends JPanel {
private static final long serialVersionUID = 1187986738303477168L;
- private final DownloadJob job;
- private final JProgressBar bar;
+ private final DownloadJob job;
+ private final JProgressBar bar;
- public DownloadPanel(DownloadJob job) {
+ DownloadPanel(DownloadJob job) {
super(new BorderLayout());
this.job = job;
@@ -156,7 +148,7 @@ public class DownloadGui extends JPanel {
add(bar = new JProgressBar(job.getProgress()));
JButton b = new JButton("X");
b.addActionListener(e -> {
- switch(this.job.getState()) {
+ switch (this.job.getState()) {
case NEW:
case PREPARING:
case WORKING:
@@ -165,7 +157,7 @@ public class DownloadGui extends JPanel {
});
add(b, BorderLayout.EAST);
- if(job.getState() == State.FINISHED | job.getState() == State.ABORTED) {
+ if (job.getState() == State.FINISHED | job.getState() == State.ABORTED) {
changeProgress(+1, 0);
}
setVisible(job.getState() != State.FINISHED);
@@ -177,15 +169,13 @@ public class DownloadGui extends JPanel {
Dimension d = getPreferredSize();
d.width = Integer.MAX_VALUE;
setMaximumSize(d);
-// d.width = 500;
-// setMinimumSize(d);
}
- public DownloadJob getJob() {
+ DownloadJob getJob() {
return job;
}
- public JProgressBar getBar() {
+ JProgressBar getBar() {
return bar;
}
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java
index c39fb6d163..72c99422ab 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/DownloadJob.java
@@ -1,28 +1,18 @@
-/**
- * DownloadJob.java
- *
- * Created on 25.08.2010
- */
package org.mage.plugins.card.dl;
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.Proxy;
-import java.net.URL;
-import java.net.URLConnection;
-import javax.swing.BoundedRangeModel;
-import javax.swing.DefaultBoundedRangeModel;
import org.mage.plugins.card.dl.beans.properties.Property;
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
import org.mage.plugins.card.utils.CardImageUtils;
+import javax.swing.*;
+import java.io.*;
+import java.net.Proxy;
+import java.net.URL;
+import java.net.URLConnection;
+
/**
- * The class DownloadJob.
+ * Downloader job to download one resource
*
- * @version V0.0 25.08.2010
* @author Clemens Koza, JayDi85
*/
public class DownloadJob extends AbstractLaternaBean {
@@ -88,11 +78,8 @@ public class DownloadJob extends AbstractLaternaBean {
*/
public void setError(String message, Exception error) {
if (message == null) {
-
- message = "Download of " + name + "from " + source.toString() + " caused error: " + error.toString();
+ message = "Download of " + name + " from " + source.toString() + " caused error: " + error.toString();
}
-// log.warn(message, error);
- log.warn(message);
this.state.setValue(State.ABORTED);
this.error.setValue(error);
this.message.setValue(message);
@@ -116,7 +103,7 @@ public class DownloadJob extends AbstractLaternaBean {
return;
}
- // change to working state on good prepare call
+ // can continue
this.state.setValue(State.WORKING);
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/Downloader.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/Downloader.java
index 497ee62d62..815083b901 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/Downloader.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/Downloader.java
@@ -1,26 +1,9 @@
-/**
- * Downloader.java
- *
- * Created on 25.08.2010
- */
package org.mage.plugins.card.dl;
-import java.io.BufferedInputStream;
-import java.io.BufferedOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-import java.net.ConnectException;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import javax.swing.BoundedRangeModel;
import org.apache.log4j.Logger;
import org.jetlang.channels.Channel;
import org.jetlang.channels.MemoryChannel;
import org.jetlang.core.Callback;
-import org.jetlang.core.Disposable;
import org.jetlang.fibers.Fiber;
import org.jetlang.fibers.PoolFiberFactory;
import org.mage.plugins.card.dl.DownloadJob.Destination;
@@ -28,41 +11,56 @@ import org.mage.plugins.card.dl.DownloadJob.Source;
import org.mage.plugins.card.dl.DownloadJob.State;
import org.mage.plugins.card.dl.lm.AbstractLaternaBean;
+import javax.swing.*;
+import java.io.*;
+import java.net.ConnectException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
/**
- * The class Downloader.
+ * Downloader
*
- * @version V0.0 25.08.2010
- * @author Clemens Koza
+ * @author Clemens Koza, JayDi85
*/
-public class Downloader extends AbstractLaternaBean implements Disposable {
+public class Downloader extends AbstractLaternaBean {
private static final Logger logger = Logger.getLogger(Downloader.class);
private final List jobs = properties.list("jobs");
- private final Channel channel = new MemoryChannel<>();
+ private final Channel jobsQueue = new MemoryChannel<>();
+ private CountDownLatch worksCount = null;
private final ExecutorService pool = Executors.newCachedThreadPool();
private final List fibers = new ArrayList<>();
public Downloader() {
+ // prepare 10 threads and start to waiting new download jobs from queue
PoolFiberFactory f = new PoolFiberFactory(pool);
- //subscribe multiple fibers for parallel execution
for (int i = 0, numThreads = 10; i < numThreads; i++) {
Fiber fiber = f.create();
fiber.start();
fibers.add(fiber);
- channel.subscribe(fiber, new DownloadCallback());
+ jobsQueue.subscribe(fiber, new DownloadCallback());
}
}
- @Override
- public void dispose() {
+ public void cleanup() {
+ // close all threads and jobs
for (DownloadJob j : jobs) {
switch (j.getState()) {
case NEW:
case PREPARING:
case WORKING:
j.setState(State.ABORTED);
+ break;
+ case ABORTED:
+ case FINISHED:
+ // don't change state
+ break;
}
}
@@ -70,16 +68,10 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
f.dispose();
}
pool.shutdown();
- }
- /**
- *
- * @throws Throwable
- */
- @Override
- protected void finalize() throws Throwable {
- dispose();
- super.finalize();
+ while (worksCount.getCount() != 0) {
+ worksCount.countDown();
+ }
}
public void add(DownloadJob job) {
@@ -94,7 +86,27 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
}
job.setState(State.NEW);
jobs.add(job);
- channel.publish(job);
+ }
+
+ public void publishAllJobs() {
+ worksCount = new CountDownLatch(jobs.size());
+ for (DownloadJob job : jobs) {
+ jobsQueue.publish(job);
+ }
+ }
+
+ public void waitFinished() {
+ try {
+ while (worksCount.getCount() != 0) {
+ worksCount.await(60, TimeUnit.SECONDS);
+
+ if (worksCount.getCount() != 0) {
+ logger.warn("Symbols download too long...");
+ }
+ }
+ } catch (InterruptedException e) {
+ logger.error("Need to stop symbols download...");
+ }
}
public List getJobs() {
@@ -111,20 +123,23 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
@Override
public void onMessage(DownloadJob job) {
- // start to work
- // the job won't be processed by multiple threads
+ // each 10 threads gets same jobs, but take to work only one NEW
synchronized (job) {
- if (job.getState() != State.NEW) {
- return;
- }
-
- job.doPrepareAndStartWork();
-
- if (job.getState() != State.WORKING) {
+ if (job.getState() == State.NEW) {
+ // take new job
+ job.doPrepareAndStartWork();
+ if (job.getState() != State.WORKING) {
+ logger.warn("Can't prepare symbols download job: " + job.getName());
+ worksCount.countDown();
+ return;
+ }
+ } else {
+ // skip job (other thread takes it)
return;
}
}
+ // real work for new job
// download and save data
try {
Source src = job.getSource();
@@ -132,9 +147,11 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
BoundedRangeModel progress = job.getProgress();
if (dst.isValid()) {
+ // already done
progress.setMaximum(1);
progress.setValue(1);
} else {
+ // downloading
if (dst.exists()) {
try {
dst.delete();
@@ -149,7 +166,7 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
try {
byte[] buf = new byte[8 * 1024];
int total = 0;
- for (int len; (len = is.read(buf)) != -1;) {
+ for (int len; (len = is.read(buf)) != -1; ) {
if (job.getState() == State.ABORTED) {
throw new IOException("Job was aborted");
}
@@ -193,6 +210,8 @@ public class Downloader extends AbstractLaternaBean implements Disposable {
logger.warn("Error resource download " + job.getName() + " from " + job.getSource().toString() + ": " + message);
} catch (IOException ex) {
job.setError(ex);
+ } finally {
+ worksCount.countDown();
}
}
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java
index be462e1829..224a1b036e 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/lm/AbstractLaternaBean.java
@@ -1,6 +1,6 @@
/**
* AbstractLaternaBean.java
- *
+ *
* Created on 25.08.2010
*/
@@ -16,12 +16,12 @@ import org.mage.plugins.card.dl.beans.properties.bound.BoundProperties;
/**
* The class AbstractLaternaBean.
- *
- * @version V0.0 25.08.2010
+ *
* @author Clemens Koza
+ * @version V0.0 25.08.2010
*/
public class AbstractLaternaBean extends AbstractBoundBean {
protected static final Logger log = Logger.getLogger(AbstractLaternaBean.class);
- protected final Properties properties = new BoundProperties(s);
- protected EventListenerList listeners = new EventListenerList();
+ protected final Properties properties = new BoundProperties(s);
+ protected EventListenerList listeners = new EventListenerList();
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/AltMtgOnlTokensImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/AltMtgOnlTokensImageSource.java
index df290687c1..f8216028ba 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/AltMtgOnlTokensImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/AltMtgOnlTokensImageSource.java
@@ -1,4 +1,3 @@
-
package org.mage.plugins.card.dl.sources;
import org.apache.log4j.Logger;
@@ -59,7 +58,7 @@ public enum AltMtgOnlTokensImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
return null;
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageSource.java
index f2d0fabf75..758f38881e 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CardImageSource.java
@@ -1,15 +1,16 @@
package org.mage.plugins.card.dl.sources;
-import java.util.ArrayList;
+import mage.client.util.CardLanguage;
import org.mage.plugins.card.images.CardDownloadData;
+import java.util.ArrayList;
+
/**
- *
- * @author North
+ * @author North, JayDi85
*/
public interface CardImageSource {
- CardImageUrls generateURL(CardDownloadData card) throws Exception;
+ CardImageUrls generateCardUrl(CardDownloadData card) throws Exception;
CardImageUrls generateTokenUrl(CardDownloadData card) throws Exception;
@@ -31,17 +32,32 @@ public interface CardImageSource {
return false;
}
+ default boolean isLanguagesSupport() {
+ return false;
+ }
+
+ default void setCurrentLanguage(CardLanguage cardLanguage) {
+ }
+
+ default CardLanguage getCurrentLanguage() {
+ return CardLanguage.ENGLISH;
+ }
+
void doPause(String httpImageUrl);
default ArrayList getSupportedSets() {
- return null;
+ return new ArrayList<>();
}
default boolean isSetSupportedComplete(String setCode) {
return true;
}
- default boolean isImageProvided(String setCode, String cardName) {
+ default boolean isCardImageProvided(String setCode, String cardName) {
+ return false;
+ }
+
+ default boolean isTokenImageProvided(String setCode, String cardName, Integer tokenNumber) {
return false;
}
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CopyPasteImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CopyPasteImageSource.java
index 0fc6561b97..766208bdf1 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CopyPasteImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/CopyPasteImageSource.java
@@ -1,6 +1,10 @@
package org.mage.plugins.card.dl.sources;
-import java.awt.Toolkit;
+import mage.cards.Sets;
+import org.mage.plugins.card.images.CardDownloadData;
+
+import javax.swing.*;
+import java.awt.*;
import java.awt.datatransfer.Clipboard;
import java.awt.datatransfer.StringSelection;
import java.io.IOException;
@@ -10,12 +14,8 @@ import java.util.LinkedHashSet;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import javax.swing.JOptionPane;
-import mage.cards.Sets;
-import org.mage.plugins.card.images.CardDownloadData;
/**
- *
* @author spjspj
*/
public enum CopyPasteImageSource implements CardImageSource {
@@ -74,7 +74,7 @@ public enum CopyPasteImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
if (singleLinks == null) {
setupLinks();
}
@@ -198,7 +198,7 @@ public enum CopyPasteImageSource implements CardImageSource {
@Override
public CardImageUrls generateTokenUrl(CardDownloadData card) throws IOException {
try {
- return generateURL(card);
+ return generateCardUrl(card);
} catch (Exception ex) {
}
return null;
@@ -239,7 +239,7 @@ public enum CopyPasteImageSource implements CardImageSource {
}
@Override
- public boolean isImageProvided(String setCode, String cardName) {
+ public boolean isCardImageProvided(String setCode, String cardName) {
missingCards.add(setCode + "/" + cardName);
if (singleLinks != null) {
@@ -248,6 +248,11 @@ public enum CopyPasteImageSource implements CardImageSource {
return false;
}
+ @Override
+ public boolean isTokenImageProvided(String setCode, String cardName, Integer tokenNumber) {
+ return false;
+ }
+
@Override
public boolean isSetSupportedComplete(String setCode) {
return false;
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java
index 8d3e12236c..e38898760f 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/GrabbagImageSource.java
@@ -1,6 +1,8 @@
-
package org.mage.plugins.card.dl.sources;
+import org.apache.log4j.Logger;
+import org.mage.plugins.card.images.CardDownloadData;
+
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
@@ -9,9 +11,6 @@ import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
-import org.apache.log4j.Logger;
-import org.mage.plugins.card.images.CardDownloadData;
-
/**
* @author spjspj
*/
@@ -48,7 +47,7 @@ public enum GrabbagImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
if (singleLinks == null) {
setupLinks();
}
@@ -450,7 +449,7 @@ public enum GrabbagImageSource implements CardImageSource {
@Override
public CardImageUrls generateTokenUrl(CardDownloadData card) throws IOException {
try {
- return generateURL(card);
+ return generateCardUrl(card);
} catch (Exception ex) {
java.util.logging.Logger.getLogger(GrabbagImageSource.class.getName()).log(Level.SEVERE, null, ex);
}
@@ -501,7 +500,7 @@ public enum GrabbagImageSource implements CardImageSource {
}
@Override
- public boolean isImageProvided(String setCode, String cardName) {
+ public boolean isCardImageProvided(String setCode, String cardName) {
if (singleLinks == null) {
setupLinks();
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java
deleted file mode 100644
index dfee0c9a46..0000000000
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagicCardsImageSource.java
+++ /dev/null
@@ -1,439 +0,0 @@
-package org.mage.plugins.card.dl.sources;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
-import mage.client.dialog.PreferencesDialog;
-import org.mage.plugins.card.images.CardDownloadData;
-import org.mage.plugins.card.utils.CardImageUtils;
-
-/**
- * @author North
- */
-public enum MagicCardsImageSource implements CardImageSource {
-
- instance;
-
- private static final Set supportedSets = new LinkedHashSet() {
- {
- // add("PTC"); // Prerelease Events
- add("LEA");
- add("LEB");
- add("2ED");
- add("ARN");
- add("ATQ");
- add("3ED");
- add("LEG");
- add("DRK");
- add("FEM");
- add("4ED");
- add("ICE");
- add("CHR");
- add("HML");
- add("ALL");
- add("MIR");
- add("VIS");
- add("5ED");
- add("POR");
- add("WTH");
- add("TMP");
- add("STH");
- add("EXO");
- add("P02");
- add("UGL");
- add("USG");
- add("DD3DVD");
- add("DD3EVG");
- add("DD3GVL");
- add("DD3JVC");
-
- add("ULG");
- add("6ED");
- add("UDS");
- add("PTK");
- add("S99");
- add("MMQ");
- // add("BRB");Battle Royale Box Set
- add("NEM");
- add("S00");
- add("PCY");
- add("INV");
- // add("BTD"); // Beatdown Boxset
- add("PLS");
- add("7ED");
- add("APC");
- add("ODY");
- // add("DKM"); // Deckmasters 2001
- add("TOR");
- add("JUD");
- add("ONS");
- add("LGN");
- add("SCG");
- add("8ED");
- add("MRD");
- add("DST");
- add("5DN");
- add("CHK");
- add("UNH");
- add("BOK");
- add("SOK");
- add("9ED");
- add("RAV");
- add("GPT");
- add("DIS");
- add("CSP");
- add("TSP");
- add("TSB");
- add("PLC");
- add("FUT");
- add("10E");
- add("MED");
- add("LRW");
- add("EVG");
- add("MOR");
- add("SHM");
- add("EVE");
- add("DRB");
- add("ME2");
- add("ALA");
- add("DD2");
- add("CON");
- add("DDC");
- add("ARB");
- add("M10");
- // add("TD0"); // Magic Online Deck Series
- add("V09");
- add("HOP");
- add("ME3");
- add("ZEN");
- add("DDD");
- add("H09");
- add("WWK");
- add("DDE");
- add("ROE");
- add("DPA");
- add("ARC");
- add("M11");
- add("V10");
- add("DDF");
- add("SOM");
- // add("TD0"); // Commander Theme Decks
- add("PD2");
- add("ME4");
- add("MBS");
- add("DDG");
- add("NPH");
- add("CMD");
- add("M12");
- add("V11");
- add("DDH");
- add("ISD");
- add("PD3");
- add("DKA");
- add("DDI");
- add("AVR");
- add("PC2");
- add("M13");
- add("V12");
- add("DDJ");
- add("RTR");
- add("CM1");
- // add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
- add("GTC");
- add("DDK");
- add("DGM");
- add("MMA");
- add("M14");
- add("V13");
- add("DDL");
- add("THS");
- add("C13");
- add("BNG");
- add("DDM");
- add("JOU");
- // add("MD1"); // Modern Event Deck
- add("CNS");
- add("VMA");
- add("M15");
- add("V14");
- add("DDN");
- add("KTK");
- add("C14");
- // add("DD3"); // Duel Decks Anthology
- add("FRF");
- add("DDO");
- add("DTK");
- add("TPR");
- add("MM2");
- add("ORI");
- add("V15");
- add("DDP");
- add("BFZ");
- add("EXP");
- add("C15");
- // add("PZ1"); // Legendary Cube
- add("OGW");
- add("DDQ");
- add("W16");
- add("SOI");
- add("EMA");
- add("EMN");
- add("V16");
- add("CN2");
- add("DDR");
- add("KLD");
- add("MPS");
- // add("PZ2"); // Treasure Chests
- add("C16");
- add("PCA");
- add("AER");
- add("MM3");
- add("DDS");
- add("W17");
- add("AKH");
- add("MPS");
- add("CMA");
- add("E01");
- add("HOU");
- add("C17");
- add("XLN");
- add("DDT");
- add("DDU");
- add("IMA");
- add("E02");
- add("V17");
- add("UST");
- add("RIX");
- add("A25");
- add("DOM");
-// add("CM2");
-// add("M19");
- }
- };
-
- private static final Map setNameTokenReplacement = new HashMap() {
- {
- put("10E", "tenth-edition");
- put("AER", "aether-revolt");
- put("AKH", "amonkhet");
- put("ALA", "shards-of-alara");
- put("ANB", "archenemy-nicol-bolas");
- put("APAC", "asia-pacific-land-program");
- put("APC", "player-rewards-2001");
- put("ARB", "alara-reborn");
- put("ARC", "archenemy");
- put("ARENA", "arena-league");
- put("AVR", "avacyn-restored");
- put("BFZ", "battle-for-zendikar");
- put("BNG", "born-of-the-gods");
- put("C13", "commander-2013-edition");
- put("C14", "commander-2014");
- put("C15", "commander-2015");
- put("C16", "commander-2016");
- put("CLASH", "clash-pack");
- put("CMA", "commander-anthology");
- put("CMA", "commanders-arsenal");
- put("CMD", "commander");
- put("CN2", "conspiracy-take-the-crown");
- put("CNS", "conspiracy");
- put("CON", "conflux");
- put("CP", "champs");
- put("CSP", "coldsnap");
- put("DD2", "duel-decks-jace-vs-chandra");
- put("DD3DVD", "duel-decks-anthology-divine-vs-demonic");
- put("DD3EVG", "duel-decks-anthology-elves-vs-goblins");
- put("DD3GVL", "duel-decks-anthology-garruk-vs-liliana");
- put("DD3JVC", "duel-decks-anthology-jace-vs-chandra");
- put("DDC", "duel-decks-divine-vs-demonic");
- put("DDD", "duel-decks-garruk-vs-liliana");
- put("DDE", "duel-decks-phyrexia-vs-the-coalition");
- put("DDF", "duel-decks-elspeth-vs-tezzeret");
- put("DDG", "duel-decks-knights-vs-dragons");
- put("DDH", "duel-decks-ajani-vs-nicol-bolas");
- put("DDI", "duel-decks-venser-vs-koth");
- put("DDJ", "duel-decks-izzet-vs-golgari");
- put("DDK", "duel-decks-sorin-vs-tibalt");
- put("DDL", "duel-decks-heroes-vs-monsters");
- put("DDM", "duel-decks-jace-vs-vraska");
- put("DDN", "duel-decks-speed-vs-cunning");
- put("DDO", "duel-decks-elspeth-vs-kiora");
- put("DDP", "duel-decks-zendikar-vs-eldrazi");
- put("DDQ", "duel-decks-blessed-vs-cursed");
- put("DDR", "duel-decks-nissa-vs-ob-nixilis");
- put("DDS", "duel-decks-mind-vs-might");
- put("DDT", "duel-decks-merfolk-vs-goblin");
- put("DDU", "duel-decks-elves-vs-inventors");
- put("DGM", "dragons-maze");
- put("DKA", "dark-ascension");
- put("DRB", "from-the-vault-dragons");
- put("DTK", "dragons-of-tarkir");
- put("EMA", "eternal-masters");
- put("EMN", "eldritch-moon");
- put("EURO", "european-land-program");
- put("EVE", "eventide");
- put("EVG", "duel-decks-elves-vs-goblins");
- put("EXP", "zendikar-expeditions");
- put("FNMP", "friday-night-magic");
- put("FRF", "fate-reforged");
- put("GPX", "grand-prix");
- put("GRC", "wpngateway");
- put("GTC", "gatecrash");
- put("HOP", "planechase");
- put("HOU", "hour-of-devastation");
- put("INV", "player-rewards-2001");
- put("ISD", "innistrad");
- put("JOU", "journey-into-nyx");
- put("JR", "judge-gift-program");
- put("KLD", "kaladesh");
- put("KTK", "khans-of-tarkir");
- put("LRW", "lorwyn");
- put("M10", "magic-2010");
- put("M11", "magic-2011");
- put("M12", "magic-2012");
- put("M13", "magic-2013");
- put("M14", "magic-2014");
- put("M15", "magic-2015");
- put("MBP", "media-inserts");
- put("MBS", "mirrodin-besieged");
- put("MGDC", "magic-game-day-cards");
- put("MLP", "launch-party");
- put("MM2", "modern-masters-2015");
- put("MM3", "modern-masters-2017");
- put("MMA", "modern-masters");
- put("MOR", "morningtide");
- put("MPRP", "magic-player-rewards");
- put("MPS", "masterpiece-series");
- put("NPH", "new-phyrexia");
- put("ODY", "player-rewards-2002");
- put("OGW", "oath-of-the-gatewatch");
- put("ORG", "oath-of-the-gatewatch");
- put("ORI", "magic-origins");
- put("PC2", "planechase-2012-edition");
- put("PO2", "portal-second-age");
- put("PLS", "player-rewards-2001");
- put("POR", "portal");
- put("PTC", "prerelease-events");
- put("PTK", "portal-three-kingdoms");
- put("ROE", "rise-of-the-eldrazi");
- put("RTR", "return-to-ravnica");
- put("SHM", "shadowmoor");
- put("SOI", "shadows-over-innistrad");
- put("SOM", "scars-of-mirrodin");
- put("SUS", "super-series");
- put("THS", "theros");
- put("TPR", "tempest-remastered");
- put("UGIN", "ugins-fate");
- put("V09", "from-the-vault-exiled");
- put("V10", "from-the-vault-relics");
- put("V11", "from-the-vault-legends");
- put("V12", "from-the-vault-realms");
- put("V13", "from-the-vault-twenty");
- put("V14", "from-the-vault-annihilation");
- put("V15", "from-the-vault-angels");
- put("V16", "from-the-vault-lore");
- put("VMA", "vintage-masters");
- put("W16", "welcome-deck-2016");
- put("W17", "welcome-deck-2017");
- put("WMCQ", "world-magic-cup-qualifier");
- put("WWK", "worldwake");
- put("ZEN", "zendikar");
- }
-
- private static final long serialVersionUID = 1L;
- };
-
- @Override
- public String getSourceName() {
- return "magiccards.info";
- }
-
- @Override
- public String getNextHttpImageUrl() {
- return null;
- }
-
- @Override
- public String getFileForHttpImage(String httpImageUrl) {
- return null;
- }
-
- @Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
- String collectorId = card.getCollectorId();
- String cardSet = card.getSet();
- if (collectorId == null || cardSet == null) {
- throw new Exception("Wrong parameters for image: collector id: " + collectorId + ",card set: " + cardSet);
- }
- String set = CardImageUtils.updateSet(cardSet, true);
-
- String preferedLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
-
- StringBuilder url = new StringBuilder("http://magiccards.info/scans/").append(preferedLanguage).append('/');
- url.append(set.toLowerCase(Locale.ENGLISH)).append('/').append(collectorId);
-
- if (card.isTwoFacedCard()) {
- url.append(card.isSecondSide() ? "b" : "a");
- }
- if (card.isSplitCard()) {
- url.append('a');
- }
- if (card.isFlipCard()) {
- if (card.isFlippedSide()) { // download rotated by 180 degree image
- url.append('b');
- } else {
- url.append('a');
- }
- }
- url.append(".jpg");
-
- return new CardImageUrls(url.toString());
- }
-
- @Override
- public CardImageUrls generateTokenUrl(CardDownloadData card) {
- String name = card.getName();
- // add type to name if it's not 0
- if (card.getType() > 0) {
- name = name + ' ' + card.getType();
- }
- name = name.replaceAll(" ", "-").replace(",", "").toLowerCase(Locale.ENGLISH);
- String set = "not-supported-set";
- if (setNameTokenReplacement.containsKey(card.getSet())) {
- set = setNameTokenReplacement.get(card.getSet());
- } else {
- set += '-' + card.getSet();
- }
- return new CardImageUrls("http://magiccards.info/extras/token/" + set + '/' + name + ".jpg");
- }
-
- @Override
- public float getAverageSize() {
- return 70.0f;
- }
-
- @Override
- public int getTotalImages() {
- return -1;
- }
-
- @Override
- public boolean isTokenSource() {
- return true;
- }
-
- @Override
- public ArrayList getSupportedSets() {
- ArrayList supportedSetsCopy = new ArrayList<>();
- supportedSetsCopy.addAll(supportedSets);
- return supportedSetsCopy;
- }
-
- @Override
- public void doPause(String httpImageUrl) {
- }
-
-}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java
index c2ddfcc987..4bd2c49433 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MagidexImageSource.java
@@ -1,16 +1,10 @@
-
package org.mage.plugins.card.dl.sources;
-import java.net.URI;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-
import org.mage.plugins.card.images.CardDownloadData;
+import java.net.URI;
+import java.util.*;
+
/**
* @author Pete Rossi
*/
@@ -234,7 +228,7 @@ public enum MagidexImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
String cardDownloadName = card.getDownloadName().toLowerCase(Locale.ENGLISH);
String cardSet = card.getSet();
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java
index f5104a2226..7c8a368579 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgImageSource.java
@@ -1,10 +1,9 @@
-
package org.mage.plugins.card.dl.sources;
-import java.util.Locale;
-
import org.mage.plugins.card.images.CardDownloadData;
+import java.util.Locale;
+
/**
* Site was shutdown by wizards Feb. 2015
*
@@ -30,7 +29,7 @@ public enum MtgImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java
index 9d82cbb425..f43baec92c 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MtgOnlTokensImageSource.java
@@ -1,12 +1,11 @@
-
package org.mage.plugins.card.dl.sources;
-import java.io.IOException;
-import java.util.HashMap;
-
import org.apache.log4j.Logger;
import org.mage.plugins.card.images.CardDownloadData;
+import java.io.IOException;
+import java.util.HashMap;
+
/**
* @author spjspj
*/
@@ -59,7 +58,7 @@ public enum MtgOnlTokensImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
return null;
}
@@ -352,8 +351,12 @@ public enum MtgOnlTokensImageSource implements CardImageSource {
}
@Override
- public boolean isImageProvided(String setCode, String cardName) {
- return true;
+ public boolean isCardImageProvided(String setCode, String cardName) {
+ return false;
}
+ @Override
+ public boolean isTokenImageProvided(String setCode, String cardName, Integer tokenNumber) {
+ return true;
+ }
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java
index 86f80a6fea..1c0ecaf857 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/MythicspoilerComSource.java
@@ -1,22 +1,5 @@
package org.mage.plugins.card.dl.sources;
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.net.HttpURLConnection;
-import java.net.InetSocketAddress;
-import java.net.Proxy;
-import java.net.URL;
-import java.net.URLEncoder;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.prefs.Preferences;
import mage.client.MageFrame;
import mage.remote.Connection;
import mage.remote.Connection.ProxyType;
@@ -26,6 +9,13 @@ import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.mage.plugins.card.images.CardDownloadData;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.net.*;
+import java.util.*;
+import java.util.prefs.Preferences;
+
/**
* @author LevelX2
*/
@@ -330,7 +320,7 @@ public enum MythicspoilerComSource implements CardImageSource {
}
private Map getSetLinksFromPage(String cardSet, Set aliasesStart, Preferences prefs,
- ProxyType proxyType, String baseUrl, String pageUrl) throws IOException {
+ ProxyType proxyType, String baseUrl, String pageUrl) throws IOException {
Map pageLinks = new HashMap<>();
String urlDocument;
@@ -392,7 +382,7 @@ public enum MythicspoilerComSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java
index 7f2bc5825f..133bebf5b0 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSource.java
@@ -1,305 +1,113 @@
package org.mage.plugins.card.dl.sources;
-import mage.client.dialog.PreferencesDialog;
+import mage.client.util.CardLanguage;
import org.mage.plugins.card.images.CardDownloadData;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
/**
- * @author Quercitron, JayDi85
+ * @author JayDi85
*/
public enum ScryfallImageSource implements CardImageSource {
instance;
- private final Set supportedSets;
- private final Map languageAliases;
+ private final Map languageAliases;
+ private CardLanguage currentLanguage = CardLanguage.ENGLISH; // working language
ScryfallImageSource() {
+ // LANGUAGES
// https://scryfall.com/docs/api/languages
languageAliases = new HashMap<>();
- languageAliases.put("en", "en");
- languageAliases.put("es", "es");
- languageAliases.put("jp", "ja");
- languageAliases.put("it", "it");
- languageAliases.put("fr", "fr");
- languageAliases.put("cn", "zhs"); // Simplified Chinese
- languageAliases.put("de", "de");
- languageAliases.put("ko", "ko");
- languageAliases.put("pt", "pt");
- languageAliases.put("ru", "ru");
-
- supportedSets = new LinkedHashSet<>();
- // supportedSets.add("PTC"); //
- supportedSets.add("LEA");
- supportedSets.add("LEB");
- supportedSets.add("2ED");
- supportedSets.add("ARN");
- supportedSets.add("ATQ");
- supportedSets.add("3ED");
- supportedSets.add("LEG");
- supportedSets.add("DRK");
- supportedSets.add("FEM");
- supportedSets.add("4ED");
- supportedSets.add("ICE");
- supportedSets.add("CHR");
- supportedSets.add("HML");
- supportedSets.add("ALL");
- supportedSets.add("MIR");
- supportedSets.add("VIS");
- supportedSets.add("5ED");
- supportedSets.add("POR");
- supportedSets.add("WTH");
- supportedSets.add("TMP");
- supportedSets.add("STH");
- supportedSets.add("EXO");
- supportedSets.add("P02");
- supportedSets.add("UGL");
- supportedSets.add("USG");
- supportedSets.add("DD3DVD");
- supportedSets.add("DD3EVG");
- supportedSets.add("DD3GVL");
- supportedSets.add("DD3JVC");
-
- supportedSets.add("ULG");
- supportedSets.add("6ED");
- supportedSets.add("UDS");
- supportedSets.add("PTK");
- supportedSets.add("S99");
- supportedSets.add("MMQ");
- // supportedSets.add("BRB");Battle Royale Box Set
- supportedSets.add("NEM");
- supportedSets.add("S00");
- supportedSets.add("PCY");
- supportedSets.add("INV");
- // supportedSets.add("BTD"); // Beatdown Boxset
- supportedSets.add("PLS");
- supportedSets.add("7ED");
- supportedSets.add("APC");
- supportedSets.add("ODY");
- // supportedSets.add("DKM"); // Deckmasters 2001
- supportedSets.add("TOR");
- supportedSets.add("JUD");
- supportedSets.add("ONS");
- supportedSets.add("LGN");
- supportedSets.add("SCG");
- supportedSets.add("8ED");
- supportedSets.add("MRD");
- supportedSets.add("DST");
- supportedSets.add("5DN");
- supportedSets.add("CHK");
- supportedSets.add("UNH");
- supportedSets.add("BOK");
- supportedSets.add("SOK");
- supportedSets.add("9ED");
- supportedSets.add("RAV");
- supportedSets.add("GPT");
- supportedSets.add("DIS");
- supportedSets.add("CSP");
- supportedSets.add("TSP");
- supportedSets.add("TSB");
- supportedSets.add("PLC");
- supportedSets.add("FUT");
- supportedSets.add("10E");
- supportedSets.add("MED");
- supportedSets.add("LRW");
- supportedSets.add("EVG");
- supportedSets.add("MOR");
- supportedSets.add("SHM");
- supportedSets.add("EVE");
- supportedSets.add("DRB");
- supportedSets.add("ME2");
- supportedSets.add("ALA");
- supportedSets.add("DD2");
- supportedSets.add("CON");
- supportedSets.add("DDC");
- supportedSets.add("ARB");
- supportedSets.add("M10");
- // supportedSets.add("TD0"); // Magic Online Deck Series
- supportedSets.add("V09");
- supportedSets.add("HOP");
- supportedSets.add("ME3");
- supportedSets.add("ZEN");
- supportedSets.add("DDD");
- supportedSets.add("H09");
- supportedSets.add("WWK");
- supportedSets.add("DDE");
- supportedSets.add("ROE");
- // duels of the planewalkers:
- supportedSets.add("DPA");
- supportedSets.add("DPAP");
- //
- supportedSets.add("ARC");
- supportedSets.add("M11");
- supportedSets.add("V10");
- supportedSets.add("DDF");
- supportedSets.add("SOM");
- // supportedSets.add("TD0"); // Commander Theme Decks
- supportedSets.add("PD2");
- supportedSets.add("ME4");
- supportedSets.add("MBS");
- supportedSets.add("DDG");
- supportedSets.add("NPH");
- supportedSets.add("CMD");
- supportedSets.add("M12");
- supportedSets.add("V11");
- supportedSets.add("DDH");
- supportedSets.add("ISD");
- supportedSets.add("PD3");
- supportedSets.add("DKA");
- supportedSets.add("DDI");
- supportedSets.add("AVR");
- supportedSets.add("PC2");
- supportedSets.add("M13");
- supportedSets.add("V12");
- supportedSets.add("DDJ");
- supportedSets.add("RTR");
- supportedSets.add("CM1");
- // supportedSets.add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
- supportedSets.add("GTC");
- supportedSets.add("DDK");
- supportedSets.add("DGM");
- supportedSets.add("MMA");
- supportedSets.add("M14");
- supportedSets.add("V13");
- supportedSets.add("DDL");
- supportedSets.add("THS");
- supportedSets.add("C13");
- supportedSets.add("BNG");
- supportedSets.add("DDM");
- supportedSets.add("JOU");
- // supportedSets.add("MD1"); // Modern Event Deck
- supportedSets.add("CNS");
- supportedSets.add("VMA");
- supportedSets.add("M15");
- supportedSets.add("V14");
- supportedSets.add("DDN");
- supportedSets.add("KTK");
- supportedSets.add("C14");
- // supportedSets.add("DD3"); // Duel Decks Anthology
- supportedSets.add("FRF");
- supportedSets.add("DDO");
- supportedSets.add("DTK");
- supportedSets.add("TPR");
- supportedSets.add("MM2");
- supportedSets.add("ORI");
- supportedSets.add("V15");
- supportedSets.add("DDP");
- supportedSets.add("BFZ");
- supportedSets.add("EXP");
- supportedSets.add("C15");
- // supportedSets.add("PZ1"); // Legendary Cube
- supportedSets.add("OGW");
- supportedSets.add("DDQ");
- supportedSets.add("W16");
- supportedSets.add("SOI");
- supportedSets.add("EMA");
- supportedSets.add("EMN");
- supportedSets.add("V16");
- supportedSets.add("CN2");
- supportedSets.add("DDR");
- supportedSets.add("KLD");
- supportedSets.add("MPS");
- // supportedSets.add("PZ2");
- supportedSets.add("C16");
- supportedSets.add("PCA");
- supportedSets.add("AER");
- supportedSets.add("MM3");
- supportedSets.add("DDS");
- supportedSets.add("W17");
- supportedSets.add("AKH");
- supportedSets.add("CMA");
- supportedSets.add("E01");
- supportedSets.add("HOU");
- supportedSets.add("C17");
- supportedSets.add("XLN");
- supportedSets.add("DDT");
- supportedSets.add("IMA");
- supportedSets.add("E02");
- supportedSets.add("V17");
- supportedSets.add("UST");
- supportedSets.add("DDU");
- supportedSets.add("RIX");
- supportedSets.add("WMCQ");
- supportedSets.add("PPRO");
- supportedSets.add("A25");
- supportedSets.add("DOM");
- supportedSets.add("BBD");
- supportedSets.add("C18");
- supportedSets.add("CM2");
- supportedSets.add("M19");
- supportedSets.add("GS1");
- supportedSets.add("GRN");
- supportedSets.add("GK1");
- //
- supportedSets.add("EURO");
- supportedSets.add("GPX");
- supportedSets.add("ATH");
- supportedSets.add("GRC");
+ languageAliases.put(CardLanguage.ENGLISH, "en");
+ languageAliases.put(CardLanguage.SPANISH, "es");
+ languageAliases.put(CardLanguage.FRENCH, "fr");
+ languageAliases.put(CardLanguage.GERMAN, "de");
+ languageAliases.put(CardLanguage.ITALIAN, "it");
+ languageAliases.put(CardLanguage.PORTUGUESE, "pt");
+ languageAliases.put(CardLanguage.JAPANESE, "ja");
+ languageAliases.put(CardLanguage.KOREAN, "ko");
+ languageAliases.put(CardLanguage.RUSSIAN, "ru");
+ languageAliases.put(CardLanguage.CHINES_SIMPLE, "zhs");
+ languageAliases.put(CardLanguage.CHINES_TRADITION, "zht");
}
- @Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
-
- String preferredLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
- String defaultCode = "en";
- String localizedCode = languageAliases.getOrDefault(preferredLanguage, defaultCode);
+ private CardImageUrls innerGenerateURL(CardDownloadData card, boolean isToken) {
+ String defaultCode = CardLanguage.ENGLISH.getCode();
+ String localizedCode = languageAliases.getOrDefault(this.getCurrentLanguage(), defaultCode);
// loc example: https://api.scryfall.com/cards/xln/121/ru?format=image
- // TODO: do not use API at all? It's can help with scryfall request limits (1 request instead 2)
+ // WARNING, some cards haven't direct images and uses random GUID:
+ // As example: Raging Ravine - https://scryfall.com/card/uma/249/raging-ravine
+ // https://img.scryfall.com/cards/large/front/5/4/54f41726-e0bb-4154-a2db-4b68b50f5032.jpg
String baseUrl = null;
String alternativeUrl = null;
- // direct links to images (non localization)
- if (baseUrl == null) {
+ // TOKENS TRY
- // set/card/number
- String linkCode1 = card.getSet() + "/" + card.getName() + "/" + card.getCollectorId();
- if (directDownloadLinks.containsKey(linkCode1)) {
- baseUrl = directDownloadLinks.get(linkCode1);
- alternativeUrl = null;
- }
-
- // set/card
- String linkCode2 = card.getSet() + "/" + card.getName();
- if (directDownloadLinks.containsKey(linkCode2)) {
- baseUrl = directDownloadLinks.get(linkCode2);
- alternativeUrl = null;
- }
+ // tokens support only direct links
+ if (baseUrl == null && isToken) {
+ baseUrl = ScryfallImageSupportTokens.findTokenLink(card.getSet(), card.getName(), card.getType());
+ alternativeUrl = null;
}
- // special card number like "103a" already compatible
+ // CARDS TRY
+
+ // direct links to images (non localization)
+ if (baseUrl == null) {
+ baseUrl = ScryfallImageSupportCards.findDirectDownloadLink(card.getSet(), card.getName(), card.getCollectorId());
+ alternativeUrl = null;
+ }
+
+ // special card number like "103a" and "U123" already compatible
if (baseUrl == null && card.isCollectorIdWithStr()) {
- baseUrl = "https://img.scryfall.com/cards/large/" + localizedCode + "/" + formatSetName(card.getSet()) + "/"
- + card.getCollectorId() + ".jpg";
- alternativeUrl = "https://img.scryfall.com/cards/large/" + defaultCode + "/" + formatSetName(card.getSet()) + "/"
- + card.getCollectorId() + ".jpg";
+ // WARNING, after 2018 it's not compatible and some new sets have GUID files instead card numbers
+ // TODO: replace card number links to API calls (need test with lands, alternative images and double faces), replace not working images by direct links
+
+ if (card.getCollectorId().startsWith("U") || card.getCollectorIdAsInt() == -1) {
+ // fix for Ultimate Box Topper (PUMA) and Mythic Edition (MED) -- need to use API
+ // ignored and go to API call at the end
+ } else {
+ baseUrl = "https://img.scryfall.com/cards/large/" + localizedCode + "/" + formatSetName(card.getSet(), isToken) + "/"
+ + card.getCollectorId() + ".jpg";
+ alternativeUrl = "https://img.scryfall.com/cards/large/" + defaultCode + "/" + formatSetName(card.getSet(), isToken) + "/"
+ + card.getCollectorId() + ".jpg";
+ }
}
// double faced cards do not supports by API (need direct link for img)
// example: https://img.scryfall.com/cards/large/en/xln/173b.jpg
if (baseUrl == null && card.isTwoFacedCard()) {
- baseUrl = "https://img.scryfall.com/cards/large/" + localizedCode + "/" + formatSetName(card.getSet()) + "/"
+ baseUrl = "https://img.scryfall.com/cards/large/" + localizedCode + "/" + formatSetName(card.getSet(), isToken) + "/"
+ card.getCollectorId() + (card.isSecondSide() ? "b" : "a") + ".jpg";
- alternativeUrl = "https://img.scryfall.com/cards/large/" + defaultCode + "/" + formatSetName(card.getSet()) + "/"
+ alternativeUrl = "https://img.scryfall.com/cards/large/" + defaultCode + "/" + formatSetName(card.getSet(), isToken) + "/"
+ card.getCollectorId() + (card.isSecondSide() ? "b" : "a") + ".jpg";
}
// basic cards by api call (redirect to img link)
// example: https://api.scryfall.com/cards/xln/121/en?format=image
if (baseUrl == null) {
- baseUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
+ baseUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
+ card.getCollectorId() + "/" + localizedCode + "?format=image";
- alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet()) + "/"
+ alternativeUrl = "https://api.scryfall.com/cards/" + formatSetName(card.getSet(), isToken) + "/"
+ card.getCollectorId() + "/" + defaultCode + "?format=image";
}
return new CardImageUrls(baseUrl, alternativeUrl);
}
+ @Override
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
+ return innerGenerateURL(card, false);
+
+ }
+
@Override
public CardImageUrls generateTokenUrl(CardDownloadData card) throws Exception {
- return null;
+ return innerGenerateURL(card, true);
}
@Override
@@ -329,7 +137,22 @@ public enum ScryfallImageSource implements CardImageSource {
@Override
public boolean isTokenSource() {
- return false;
+ return true;
+ }
+
+ @Override
+ public boolean isLanguagesSupport() {
+ return true;
+ }
+
+ @Override
+ public void setCurrentLanguage(CardLanguage cardLanguage) {
+ this.currentLanguage = cardLanguage;
+ }
+
+ @Override
+ public CardLanguage getCurrentLanguage() {
+ return currentLanguage;
}
@Override
@@ -337,131 +160,41 @@ public enum ScryfallImageSource implements CardImageSource {
}
- private String formatSetName(String setName) {
- if (setNameReplacement.containsKey(setName)) {
- setName = setNameReplacement.get(setName);
+ private String formatSetName(String setName, boolean isToken) {
+ if (isToken) {
+ // token uses direct link download, not set
+ return setName.toLowerCase(Locale.ENGLISH);
+ } else {
+ return ScryfallImageSupportCards.findScryfallSetCode(setName);
}
- return setName.toLowerCase(Locale.ENGLISH);
}
- private static final Map setNameReplacement = new HashMap() {
- {
- put("DD3GVL", "gvl");
- put("DD3JVC", "jvc");
- put("DD3DVD", "dvd");
- put("DD3EVG", "evg");
- put("MPS-AKH", "mp2");
- put("MBP", "pmei");
- put("WMCQ", "pwcq");
- put("EURO", "pelp");
- put("GPX", "pgpx");
- }
- };
-
- private static final Map directDownloadLinks = new HashMap() {
- {
- // direct links to download images for special cards
-
- // Duels of the Planeswalkers Promos -- xmage uses one set (DPAP), but scryfall store it by years
- // 2009 - https://scryfall.com/sets/pdtp
- put("DPAP/Garruk Wildspeaker", "https://img.scryfall.com/cards/large/en/pdtp/1.jpg");
- // 2010 - https://scryfall.com/sets/pdp10
- put("DPAP/Liliana Vess", "https://img.scryfall.com/cards/large/en/pdp10/1.jpg");
- put("DPAP/Nissa Revane", "https://img.scryfall.com/cards/large/en/pdp10/2.jpg");
- // 2011 - https://scryfall.com/sets/pdp11
- put("DPAP/Frost Titan", "https://img.scryfall.com/cards/large/en/pdp11/1.jpg");
- put("DPAP/Grave Titan", "https://img.scryfall.com/cards/large/en/pdp11/2.jpg");
- put("DPAP/Inferno Titan", "https://img.scryfall.com/cards/large/en/pdp11/3.jpg");
- // 2012 - https://scryfall.com/sets/pdp12
- put("DPAP/Primordial Hydra", "https://img.scryfall.com/cards/large/en/pdp12/1.jpg");
- put("DPAP/Serra Avatar", "https://img.scryfall.com/cards/large/en/pdp12/2.jpg");
- put("DPAP/Vampire Nocturnus", "https://img.scryfall.com/cards/large/en/pdp12/3.jpg");
- // 2013 - https://scryfall.com/sets/pdp13
- put("DPAP/Bonescythe Sliver", "https://img.scryfall.com/cards/large/en/pdp13/1.jpg");
- put("DPAP/Ogre Battledriver", "https://img.scryfall.com/cards/large/en/pdp13/2.jpg");
- put("DPAP/Scavenging Ooze", "https://img.scryfall.com/cards/large/en/pdp13/3.jpg");
- // 2014 - https://scryfall.com/sets/pdp14
- put("DPAP/Soul of Ravnica", "https://img.scryfall.com/cards/large/en/pdp14/1.jpg");
- put("DPAP/Soul of Zendikar", "https://img.scryfall.com/cards/large/en/pdp14/2.jpg");
-
- // Gateway Promos -- xmage uses one set (GRC), but scryfall store it by years
- // 2006 - https://scryfall.com/sets/pgtw
- put("GRC/Fiery Temper", "https://img.scryfall.com/cards/large/en/pgtw/3.jpg");
- put("GRC/Icatian Javelineers", "https://img.scryfall.com/cards/large/en/pgtw/2.jpg");
- put("GRC/Wood Elves", "https://img.scryfall.com/cards/large/en/pgtw/1.jpg");
- // 2007 - https://scryfall.com/sets/pg07
- put("GRC/Boomerang", "https://img.scryfall.com/cards/large/en/pg07/4.jpg");
- put("GRC/Calciderm", "https://img.scryfall.com/cards/large/en/pg07/5.jpg");
- put("GRC/Dauntless Dourbark", "https://img.scryfall.com/cards/large/en/pg07/12.jpg");
- put("GRC/Llanowar Elves", "https://img.scryfall.com/cards/large/en/pg07/9.jpg");
- put("GRC/Mind Stone", "https://img.scryfall.com/cards/large/en/pg07/11.jpg");
- put("GRC/Mogg Fanatic", "https://img.scryfall.com/cards/large/en/pg07/10.jpg");
- put("GRC/Reckless Wurm", "https://img.scryfall.com/cards/large/en/pg07/6.jpg");
- put("GRC/Yixlid Jailer", "https://img.scryfall.com/cards/large/en/pg07/7.jpg");
- put("GRC/Zoetic Cavern", "https://img.scryfall.com/cards/large/en/pg07/8.jpg");
- // 2008a - https://scryfall.com/sets/pg08
- put("GRC/Boggart Ram-Gang", "https://img.scryfall.com/cards/large/en/pg08/17.jpg");
- put("GRC/Cenn's Tactician", "https://img.scryfall.com/cards/large/en/pg08/14.jpg");
- put("GRC/Duergar Hedge-Mage", "https://img.scryfall.com/cards/large/en/pg08/19.jpg");
- put("GRC/Gravedigger", "https://img.scryfall.com/cards/large/en/pg08/16.jpg");
- put("GRC/Lava Axe", "https://img.scryfall.com/cards/large/en/pg08/13.jpg");
- put("GRC/Oona's Blackguard", "https://img.scryfall.com/cards/large/en/pg08/15.jpg");
- put("GRC/Selkie Hedge-Mage", "https://img.scryfall.com/cards/large/en/pg08/20.jpg");
- put("GRC/Wilt-Leaf Cavaliers", "https://img.scryfall.com/cards/large/en/pg08/18.jpg");
-
- // Wizards Play Network Promos -- xmage uses one set (GRC), but scryfall store it by years
- // 2008b - https://scryfall.com/sets/pwpn
- put("GRC/Sprouting Thrinax", "https://img.scryfall.com/cards/large/en/pwpn/21.jpg");
- put("GRC/Woolly Thoctar", "https://img.scryfall.com/cards/large/en/pwpn/22.jpg");
- // 2009 - https://scryfall.com/sets/pwp09
- put("GRC/Hellspark Elemental", "https://img.scryfall.com/cards/large/en/pwp09/25.jpg");
- put("GRC/Kor Duelist", "https://img.scryfall.com/cards/large/en/pwp09/32.jpg");
- put("GRC/Marisi's Twinclaws", "https://img.scryfall.com/cards/large/en/pwp09/26.jpg");
- put("GRC/Mind Control", "https://img.scryfall.com/cards/large/en/pwp09/30.jpg");
- put("GRC/Path to Exile", "https://img.scryfall.com/cards/large/en/pwp09/24.jpg");
- put("GRC/Rise from the Grave", "https://img.scryfall.com/cards/large/en/pwp09/31.jpg");
- put("GRC/Slave of Bolas", "https://img.scryfall.com/cards/large/en/pwp09/27.jpg");
- put("GRC/Vampire Nighthawk", "https://img.scryfall.com/cards/large/en/pwp09/33.jpg");
- // 2010 - https://scryfall.com/sets/pwp10
- put("GRC/Kor Firewalker", "https://img.scryfall.com/cards/large/en/pwp10/36.jpg");
- put("GRC/Leatherback Baloth", "https://img.scryfall.com/cards/large/en/pwp10/37.jpg");
- put("GRC/Syphon Mind", "https://img.scryfall.com/cards/large/en/pwp10/40.jpg");
- put("GRC/Pathrazer of Ulamog", "https://img.scryfall.com/cards/large/en/pwp10/46.jpg");
- put("GRC/Curse of Wizardry", "https://img.scryfall.com/cards/large/en/pwp10/47.jpg");
- put("GRC/Fling/50", "https://img.scryfall.com/cards/large/en/pwp10/50.jpg"); // same card but different year
- put("GRC/Sylvan Ranger/51", "https://img.scryfall.com/cards/large/en/pwp10/51.jpg"); // same card but different year
- put("GRC/Plague Stinger", "https://img.scryfall.com/cards/large/en/pwp10/59.jpg");
- put("GRC/Golem's Heart", "https://img.scryfall.com/cards/large/en/pwp10/60.jpg");
- put("GRC/Skinrender", "https://img.scryfall.com/cards/large/en/pwp10/63.jpg");
- // 2011 - https://scryfall.com/sets/pwp11
- put("GRC/Auramancer", "https://img.scryfall.com/cards/large/en/pwp11/77.jpg");
- put("GRC/Bloodcrazed Neonate", "https://img.scryfall.com/cards/large/en/pwp11/83.jpg");
- put("GRC/Boneyard Wurm", "https://img.scryfall.com/cards/large/en/pwp11/84.jpg");
- put("GRC/Circle of Flame", "https://img.scryfall.com/cards/large/en/pwp11/78.jpg");
- put("GRC/Curse of the Bloody Tome", "https://img.scryfall.com/cards/large/en/pwp11/80.jpg");
- put("GRC/Fling/69", "https://img.scryfall.com/cards/large/en/pwp11/69.jpg"); // same card but different year
- put("GRC/Master's Call", "https://img.scryfall.com/cards/large/en/pwp11/64.jpg");
- put("GRC/Maul Splicer", "https://img.scryfall.com/cards/large/en/pwp11/72.jpg");
- put("GRC/Plague Myr", "https://img.scryfall.com/cards/large/en/pwp11/65.jpg");
- put("GRC/Shrine of Burning Rage", "https://img.scryfall.com/cards/large/en/pwp11/73.jpg");
- put("GRC/Signal Pest", "https://img.scryfall.com/cards/large/en/pwp11/66.jpg");
- put("GRC/Sylvan Ranger/70", "https://img.scryfall.com/cards/large/en/pwp11/70.jpg"); // same card but different year
- put("GRC/Tormented Soul", "https://img.scryfall.com/cards/large/en/pwp11/76.jpg");
- put("GRC/Vault Skirge", "https://img.scryfall.com/cards/large/en/pwp11/71.jpg");
- // 2012 - https://scryfall.com/sets/pwp12
- put("GRC/Curse of Thirst", "https://img.scryfall.com/cards/large/en/pwp12/81.jpg");
- put("GRC/Gather the Townsfolk", "https://img.scryfall.com/cards/large/en/pwp12/79.jpg");
- put("GRC/Nearheath Stalker", "https://img.scryfall.com/cards/large/en/pwp12/82.jpg");
-
- // TODO: remove Grand Prix fix after scryfall fix image's link (that's link must be work: https://img.scryfall.com/cards/large/en/pgpx/2016b.jpg )
- put("GPX/Sword of Feast and Famine", "https://img.scryfall.com/cards/large/en/pgpx/1%E2%98%85.jpg");
- }
- };
-
@Override
public ArrayList getSupportedSets() {
ArrayList supportedSetsCopy = new ArrayList<>();
- supportedSetsCopy.addAll(supportedSets);
+
+ // cards
+ supportedSetsCopy.addAll(ScryfallImageSupportCards.getSupportedSets());
+
+ // tokens
+ for (String code : ScryfallImageSupportTokens.getSupportedSets().keySet()) {
+ if (!supportedSetsCopy.contains(code)) {
+ supportedSetsCopy.add(code);
+ }
+ }
+
return supportedSetsCopy;
}
+
+ @Override
+ public boolean isCardImageProvided(String setCode, String cardName) {
+ // all cards from set
+ return ScryfallImageSupportCards.getSupportedSets().contains(setCode);
+ }
+
+ @Override
+ public boolean isTokenImageProvided(String setCode, String cardName, Integer tokenNumber) {
+ // only direct tokens from set
+ return ScryfallImageSupportTokens.findTokenLink(setCode, cardName, tokenNumber) != null;
+ }
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
new file mode 100644
index 0000000000..2f847fc3ee
--- /dev/null
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportCards.java
@@ -0,0 +1,395 @@
+package org.mage.plugins.card.dl.sources;
+
+import com.google.common.collect.ImmutableMap;
+import org.tritonus.share.ArraySet;
+
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * @author JayDi85
+ */
+public class ScryfallImageSupportCards {
+
+ private static final Map xmageSetsToScryfall = ImmutableMap.builder().put("DD3GVL", "gvl").
+ put("DD3JVC", "jvc").
+ put("DD3DVD", "dvd").
+ put("DD3EVG", "evg").
+ put("MPS-AKH", "mp2").
+ put("MBP", "pmei").
+ put("WMCQ", "pwcq").
+ put("EURO", "pelp").
+ put("GPX", "pgpx").
+ put("MED", "me1").
+ put("MEDM", "med").build();
+
+
+
+ private static final Set supportedSets = new ArraySet() {
+ {
+ // xmage set codes
+ // add("PTC"); //
+ add("LEA");
+ add("LEB");
+ add("2ED");
+ add("ARN");
+ add("ATQ");
+ add("3ED");
+ add("LEG");
+ add("DRK");
+ add("FEM");
+ add("4ED");
+ add("ICE");
+ add("CHR");
+ add("HML");
+ add("ALL");
+ add("MIR");
+ add("VIS");
+ add("5ED");
+ add("POR");
+ add("WTH");
+ add("TMP");
+ add("STH");
+ add("EXO");
+ add("P02");
+ add("UGL");
+ add("USG");
+ add("DD3DVD");
+ add("DD3EVG");
+ add("DD3GVL");
+ add("DD3JVC");
+
+ add("ULG");
+ add("6ED");
+ add("UDS");
+ add("PTK");
+ add("S99");
+ add("MMQ");
+ // add("BRB");Battle Royale Box Set
+ add("NEM");
+ add("S00");
+ add("PCY");
+ add("INV");
+ // add("BTD"); // Beatdown Boxset
+ add("PLS");
+ add("7ED");
+ add("APC");
+ add("ODY");
+ // add("DKM"); // Deckmasters 2001
+ add("TOR");
+ add("JUD");
+ add("ONS");
+ add("LGN");
+ add("SCG");
+ add("8ED");
+ add("MRD");
+ add("DST");
+ add("5DN");
+ add("CHK");
+ add("UNH");
+ add("BOK");
+ add("SOK");
+ add("9ED");
+ add("RAV");
+ add("GPT");
+ add("DIS");
+ add("CSP");
+ add("TSP");
+ add("TSB");
+ add("PLC");
+ add("FUT");
+ add("10E");
+ add("MED");
+ add("LRW");
+ add("EVG");
+ add("MOR");
+ add("SHM");
+ add("EVE");
+ add("DRB");
+ add("ME2");
+ add("ALA");
+ add("DD2");
+ add("CON");
+ add("DDC");
+ add("ARB");
+ add("M10");
+ // add("TD0"); // Magic Online Deck Series
+ add("V09");
+ add("HOP");
+ add("ME3");
+ add("ZEN");
+ add("DDD");
+ add("H09");
+ add("WWK");
+ add("DDE");
+ add("ROE");
+ // duels of the planewalkers:
+ add("DPA");
+ add("DPAP");
+ //
+ add("ARC");
+ add("M11");
+ add("V10");
+ add("DDF");
+ add("SOM");
+ // add("TD0"); // Commander Theme Decks
+ add("PD2");
+ add("ME4");
+ add("MBS");
+ add("DDG");
+ add("NPH");
+ add("CMD");
+ add("M12");
+ add("V11");
+ add("DDH");
+ add("ISD");
+ add("PD3");
+ add("DKA");
+ add("DDI");
+ add("AVR");
+ add("PC2");
+ add("M13");
+ add("V12");
+ add("DDJ");
+ add("RTR");
+ add("CM1");
+ // add("TD2"); // Duel Decks: Mirrodin Pure vs. New Phyrexia
+ add("GTC");
+ add("DDK");
+ add("DGM");
+ add("MMA");
+ add("M14");
+ add("V13");
+ add("DDL");
+ add("THS");
+ add("C13");
+ add("BNG");
+ add("DDM");
+ add("JOU");
+ // add("MD1"); // Modern Event Deck
+ add("CNS");
+ add("VMA");
+ add("M15");
+ add("V14");
+ add("DDN");
+ add("KTK");
+ add("C14");
+ // add("DD3"); // Duel Decks Anthology
+ add("FRF");
+ add("DDO");
+ add("DTK");
+ add("TPR");
+ add("MM2");
+ add("ORI");
+ add("V15");
+ add("DDP");
+ add("BFZ");
+ add("EXP");
+ add("C15");
+ // add("PZ1"); // Legendary Cube
+ add("OGW");
+ add("DDQ");
+ add("W16");
+ add("SOI");
+ add("EMA");
+ add("EMN");
+ add("V16");
+ add("CN2");
+ add("DDR");
+ add("KLD");
+ add("MPS");
+ // add("PZ2");
+ add("C16");
+ add("PCA");
+ add("AER");
+ add("MM3");
+ add("DDS");
+ add("W17");
+ add("AKH");
+ add("CMA");
+ add("E01");
+ add("HOU");
+ add("C17");
+ add("XLN");
+ add("DDT");
+ add("IMA");
+ add("E02");
+ add("V17");
+ add("UST");
+ add("DDU");
+ add("RIX");
+ add("WMCQ");
+ add("PPRO");
+ add("A25");
+ add("DOM");
+ add("BBD");
+ add("C18");
+ add("CM2");
+ add("M19");
+ add("GS1");
+ add("GRN");
+ add("GK1");
+ add("GNT");
+ add("UMA");
+ add("PUMA");
+ add("RNA");
+ add("MEDM");
+ add("GK2");
+ add("MH1");
+ add("WAR");
+ //
+ add("EURO");
+ add("GPX");
+ add("ATH");
+ add("GRC");
+ add("ANA");
+ }
+ };
+
+ private static final Map directDownloadLinks = new HashMap() {
+ {
+ // xmage card -> direct or api link:
+ // examples:
+ // direct example: https://img.scryfall.com/cards/large/en/trix/6.jpg
+ // api example: https://api.scryfall.com/cards/trix/6/en?format=image
+ // api example: https://api.scryfall.com/cards/trix/6?format=image
+ // api format is primary
+ //
+ // code form for one card:
+ // set/card_name
+ //
+ // code form for same name cards (alternative images):
+ // set/card_name/card_number
+ // set/card_name/card_number
+
+ // Duels of the Planeswalkers Promos -- xmage uses one set (DPAP), but scryfall store it by years
+ // 2009 - https://scryfall.com/sets/pdtp
+ put("DPAP/Garruk Wildspeaker", "https://img.scryfall.com/cards/large/en/pdtp/1.jpg");
+ // 2010 - https://scryfall.com/sets/pdp10
+ put("DPAP/Liliana Vess", "https://img.scryfall.com/cards/large/en/pdp10/1.jpg");
+ put("DPAP/Nissa Revane", "https://img.scryfall.com/cards/large/en/pdp10/2.jpg");
+ // 2011 - https://scryfall.com/sets/pdp11
+ put("DPAP/Frost Titan", "https://img.scryfall.com/cards/large/en/pdp11/1.jpg");
+ put("DPAP/Grave Titan", "https://img.scryfall.com/cards/large/en/pdp11/2.jpg");
+ put("DPAP/Inferno Titan", "https://img.scryfall.com/cards/large/en/pdp11/3.jpg");
+ // 2012 - https://scryfall.com/sets/pdp12
+ put("DPAP/Primordial Hydra", "https://img.scryfall.com/cards/large/en/pdp12/1.jpg");
+ put("DPAP/Serra Avatar", "https://img.scryfall.com/cards/large/en/pdp12/2.jpg");
+ put("DPAP/Vampire Nocturnus", "https://img.scryfall.com/cards/large/en/pdp12/3.jpg");
+ // 2013 - https://scryfall.com/sets/pdp13
+ put("DPAP/Bonescythe Sliver", "https://img.scryfall.com/cards/large/en/pdp13/1.jpg");
+ put("DPAP/Ogre Battledriver", "https://img.scryfall.com/cards/large/en/pdp13/2.jpg");
+ put("DPAP/Scavenging Ooze", "https://img.scryfall.com/cards/large/en/pdp13/3.jpg");
+ // 2014 - https://scryfall.com/sets/pdp14
+ put("DPAP/Soul of Ravnica", "https://img.scryfall.com/cards/large/en/pdp14/1.jpg");
+ put("DPAP/Soul of Zendikar", "https://img.scryfall.com/cards/large/en/pdp14/2.jpg");
+
+ // Gateway Promos -- xmage uses one set (GRC), but scryfall store it by years
+ // 2006 - https://scryfall.com/sets/pgtw
+ put("GRC/Fiery Temper", "https://img.scryfall.com/cards/large/en/pgtw/3.jpg");
+ put("GRC/Icatian Javelineers", "https://img.scryfall.com/cards/large/en/pgtw/2.jpg");
+ put("GRC/Wood Elves", "https://img.scryfall.com/cards/large/en/pgtw/1.jpg");
+ // 2007 - https://scryfall.com/sets/pg07
+ put("GRC/Boomerang", "https://img.scryfall.com/cards/large/en/pg07/4.jpg");
+ put("GRC/Calciderm", "https://img.scryfall.com/cards/large/en/pg07/5.jpg");
+ put("GRC/Dauntless Dourbark", "https://img.scryfall.com/cards/large/en/pg07/12.jpg");
+ put("GRC/Llanowar Elves", "https://img.scryfall.com/cards/large/en/pg07/9.jpg");
+ put("GRC/Mind Stone", "https://img.scryfall.com/cards/large/en/pg07/11.jpg");
+ put("GRC/Mogg Fanatic", "https://img.scryfall.com/cards/large/en/pg07/10.jpg");
+ put("GRC/Reckless Wurm", "https://img.scryfall.com/cards/large/en/pg07/6.jpg");
+ put("GRC/Yixlid Jailer", "https://img.scryfall.com/cards/large/en/pg07/7.jpg");
+ put("GRC/Zoetic Cavern", "https://img.scryfall.com/cards/large/en/pg07/8.jpg");
+ // 2008a - https://scryfall.com/sets/pg08
+ put("GRC/Boggart Ram-Gang", "https://img.scryfall.com/cards/large/en/pg08/17.jpg");
+ put("GRC/Cenn's Tactician", "https://img.scryfall.com/cards/large/en/pg08/14.jpg");
+ put("GRC/Duergar Hedge-Mage", "https://img.scryfall.com/cards/large/en/pg08/19.jpg");
+ put("GRC/Gravedigger", "https://img.scryfall.com/cards/large/en/pg08/16.jpg");
+ put("GRC/Lava Axe", "https://img.scryfall.com/cards/large/en/pg08/13.jpg");
+ put("GRC/Oona's Blackguard", "https://img.scryfall.com/cards/large/en/pg08/15.jpg");
+ put("GRC/Selkie Hedge-Mage", "https://img.scryfall.com/cards/large/en/pg08/20.jpg");
+ put("GRC/Wilt-Leaf Cavaliers", "https://img.scryfall.com/cards/large/en/pg08/18.jpg");
+
+ // Wizards Play Network Promos -- xmage uses one set (GRC), but scryfall store it by years
+ // 2008b - https://scryfall.com/sets/pwpn
+ put("GRC/Sprouting Thrinax", "https://img.scryfall.com/cards/large/en/pwpn/21.jpg");
+ put("GRC/Woolly Thoctar", "https://img.scryfall.com/cards/large/en/pwpn/22.jpg");
+ // 2009 - https://scryfall.com/sets/pwp09
+ put("GRC/Hellspark Elemental", "https://img.scryfall.com/cards/large/en/pwp09/25.jpg");
+ put("GRC/Kor Duelist", "https://img.scryfall.com/cards/large/en/pwp09/32.jpg");
+ put("GRC/Marisi's Twinclaws", "https://img.scryfall.com/cards/large/en/pwp09/26.jpg");
+ put("GRC/Mind Control", "https://img.scryfall.com/cards/large/en/pwp09/30.jpg");
+ put("GRC/Path to Exile", "https://img.scryfall.com/cards/large/en/pwp09/24.jpg");
+ put("GRC/Rise from the Grave", "https://img.scryfall.com/cards/large/en/pwp09/31.jpg");
+ put("GRC/Slave of Bolas", "https://img.scryfall.com/cards/large/en/pwp09/27.jpg");
+ put("GRC/Vampire Nighthawk", "https://img.scryfall.com/cards/large/en/pwp09/33.jpg");
+ // 2010 - https://scryfall.com/sets/pwp10
+ put("GRC/Kor Firewalker", "https://img.scryfall.com/cards/large/en/pwp10/36.jpg");
+ put("GRC/Leatherback Baloth", "https://img.scryfall.com/cards/large/en/pwp10/37.jpg");
+ put("GRC/Syphon Mind", "https://img.scryfall.com/cards/large/en/pwp10/40.jpg");
+ put("GRC/Pathrazer of Ulamog", "https://img.scryfall.com/cards/large/en/pwp10/46.jpg");
+ put("GRC/Curse of Wizardry", "https://img.scryfall.com/cards/large/en/pwp10/47.jpg");
+ put("GRC/Fling/50", "https://img.scryfall.com/cards/large/en/pwp10/50.jpg"); // same card but different year
+ put("GRC/Sylvan Ranger/51", "https://img.scryfall.com/cards/large/en/pwp10/51.jpg"); // same card but different year
+ put("GRC/Plague Stinger", "https://img.scryfall.com/cards/large/en/pwp10/59.jpg");
+ put("GRC/Golem's Heart", "https://img.scryfall.com/cards/large/en/pwp10/60.jpg");
+ put("GRC/Skinrender", "https://img.scryfall.com/cards/large/en/pwp10/63.jpg");
+ // 2011 - https://scryfall.com/sets/pwp11
+ put("GRC/Auramancer", "https://img.scryfall.com/cards/large/en/pwp11/77.jpg");
+ put("GRC/Bloodcrazed Neonate", "https://img.scryfall.com/cards/large/en/pwp11/83.jpg");
+ put("GRC/Boneyard Wurm", "https://img.scryfall.com/cards/large/en/pwp11/84.jpg");
+ put("GRC/Circle of Flame", "https://img.scryfall.com/cards/large/en/pwp11/78.jpg");
+ put("GRC/Curse of the Bloody Tome", "https://img.scryfall.com/cards/large/en/pwp11/80.jpg");
+ put("GRC/Fling/69", "https://img.scryfall.com/cards/large/en/pwp11/69.jpg"); // same card but different year
+ put("GRC/Master's Call", "https://img.scryfall.com/cards/large/en/pwp11/64.jpg");
+ put("GRC/Maul Splicer", "https://img.scryfall.com/cards/large/en/pwp11/72.jpg");
+ put("GRC/Plague Myr", "https://img.scryfall.com/cards/large/en/pwp11/65.jpg");
+ put("GRC/Shrine of Burning Rage", "https://img.scryfall.com/cards/large/en/pwp11/73.jpg");
+ put("GRC/Signal Pest", "https://img.scryfall.com/cards/large/en/pwp11/66.jpg");
+ put("GRC/Sylvan Ranger/70", "https://img.scryfall.com/cards/large/en/pwp11/70.jpg"); // same card but different year
+ put("GRC/Tormented Soul", "https://img.scryfall.com/cards/large/en/pwp11/76.jpg");
+ put("GRC/Vault Skirge", "https://img.scryfall.com/cards/large/en/pwp11/71.jpg");
+ // 2012 - https://scryfall.com/sets/pwp12
+ put("GRC/Curse of Thirst", "https://img.scryfall.com/cards/large/en/pwp12/81.jpg");
+ put("GRC/Gather the Townsfolk", "https://img.scryfall.com/cards/large/en/pwp12/79.jpg");
+ put("GRC/Nearheath Stalker", "https://img.scryfall.com/cards/large/en/pwp12/82.jpg");
+
+ // TODO: remove Grand Prix fix after scryfall fix image's link (that's link must be work: https://img.scryfall.com/cards/large/en/pgpx/2016b.jpg )
+ put("GPX/Sword of Feast and Famine", "https://img.scryfall.com/cards/large/en/pgpx/1%E2%98%85.jpg");
+
+ // TODO: remove after scryfall add lands to RNA (that's link must works: https://api.scryfall.com/cards/rna/262/en?format=image)
+ put("RNA/Plains", "https://api.scryfall.com/cards/grn/260/en?format=image");
+ put("RNA/Island", "https://api.scryfall.com/cards/grn/261/en?format=image");
+ put("RNA/Swamp", "https://api.scryfall.com/cards/grn/262/en?format=image");
+ put("RNA/Mountain", "https://api.scryfall.com/cards/grn/263/en?format=image");
+ put("RNA/Forest", "https://api.scryfall.com/cards/grn/264/en?format=image");
+ }
+ };
+
+ public static String findScryfallSetCode(String xmageCode) {
+ return xmageSetsToScryfall.getOrDefault(xmageCode, xmageCode).toLowerCase(Locale.ENGLISH);
+ }
+
+ public static Set getSupportedSets() {
+ return supportedSets;
+ }
+
+ public static String findDirectDownloadLink(String setCode, String cardName, String cardNumber) {
+
+ // set/card/number
+ String linkCode1 = setCode + "/" + cardName + "/" + cardNumber;
+ if (directDownloadLinks.containsKey(linkCode1)) {
+ return directDownloadLinks.get(linkCode1);
+ }
+
+ // set/card
+ String linkCode2 = setCode + "/" + cardName;
+ if (directDownloadLinks.containsKey(linkCode2)) {
+ return directDownloadLinks.get(linkCode2);
+ }
+
+ // default
+ return null;
+ }
+}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
new file mode 100644
index 0000000000..6be07dc523
--- /dev/null
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallImageSupportTokens.java
@@ -0,0 +1,92 @@
+package org.mage.plugins.card.dl.sources;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author JayDi85
+ */
+public class ScryfallImageSupportTokens {
+
+ private static final Map supportedSets = new HashMap<>();
+
+ private static final Map supportedCards = new HashMap() {
+ {
+ // xmage token -> direct or api link:
+ // examples:
+ // direct example: https://img.scryfall.com/cards/large/en/trix/6.jpg
+ // api example: https://api.scryfall.com/cards/trix/6/en?format=image
+ // api example: https://api.scryfall.com/cards/trix/6?format=image
+ // api format is primary
+ //
+ // code form for one token:
+ // set/token_name
+ //
+ // code form for same name tokens (alternative images):
+ // set/token_name/1
+ // set/token_name/2
+
+ // RIX
+ put("RIX/City's Blessing", "https://api.scryfall.com/cards/trix/6/en?format=image"); // TODO: missing from tokens data
+ put("RIX/Elemental/1", "https://api.scryfall.com/cards/trix/1/en?format=image");
+ put("RIX/Elemental/2", "https://api.scryfall.com/cards/trix/2/en?format=image");
+ put("RIX/Golem", "https://api.scryfall.com/cards/trix/4/en?format=image");
+ put("RIX/Emblem Huatli, Radiant Champion", "https://api.scryfall.com/cards/trix/5/en?format=image");
+ put("RIX/Saproling", "https://api.scryfall.com/cards/trix/3/en?format=image");
+
+ // RNA
+ put("RNA/Beast", "https://api.scryfall.com/cards/trna/8/en?format=image");
+ put("RNA/Centaur", "https://api.scryfall.com/cards/trna/5/en?format=image");
+ put("RNA/Emblem Domri, Chaos Bringer", "https://api.scryfall.com/cards/trna/13/en?format=image");
+ put("RNA/Frog Lizard", "https://api.scryfall.com/cards/trna/6/en?format=image");
+ put("RNA/Goblin", "https://api.scryfall.com/cards/trna/4/en?format=image");
+ put("RNA/Human", "https://api.scryfall.com/cards/trna/1/en?format=image");
+ put("RNA/Illusion", "https://api.scryfall.com/cards/trna/2/en?format=image");
+ put("RNA/Ooze", "https://api.scryfall.com/cards/trna/7/en?format=image");
+ put("RNA/Sphinx", "https://api.scryfall.com/cards/trna/9/en?format=image");
+ put("RNA/Spirit", "https://api.scryfall.com/cards/trna/10/en?format=image");
+ put("RNA/Thopter", "https://api.scryfall.com/cards/trna/11/en?format=image");
+ put("RNA/Treasure", "https://api.scryfall.com/cards/trna/12/en?format=image");
+ put("RNA/Zombie", "https://api.scryfall.com/cards/trna/3/en?format=image");
+
+ // WAR
+ put("WAR/Angel", "https://api.scryfall.com/cards/twar/2/en?format=image");
+ put("WAR/Assassin", "https://api.scryfall.com/cards/twar/6/en?format=image");
+ put("WAR/Citizen", "https://api.scryfall.com/cards/twar/16/en?format=image");
+ put("WAR/Devil", "https://api.scryfall.com/cards/twar/12/en?format=image");
+ put("WAR/Dragon", "https://api.scryfall.com/cards/twar/13/en?format=image");
+ put("WAR/Goblin", "https://api.scryfall.com/cards/twar/14/en?format=image");
+ put("WAR/Emblem Nissa, Who Shakes the World", "https://api.scryfall.com/cards/twar/19/en?format=image");
+ put("WAR/Servo", "https://api.scryfall.com/cards/twar/18/en?format=image");
+ put("WAR/Soldier", "https://api.scryfall.com/cards/twar/3/en?format=image");
+ put("WAR/Spirit", "https://api.scryfall.com/cards/twar/1/en?format=image");
+ put("WAR/Voja, Friend to Elves", "https://api.scryfall.com/cards/twar/17/en?format=image");
+ put("WAR/Wall", "https://api.scryfall.com/cards/twar/4/en?format=image");
+ put("WAR/Wizard", "https://api.scryfall.com/cards/twar/5/en?format=image");
+ put("WAR/Wolf", "https://api.scryfall.com/cards/twar/15/en?format=image");
+ put("WAR/Zombie Army/1", "https://api.scryfall.com/cards/twar/10/en?format=image");
+ put("WAR/Zombie Army/2", "https://api.scryfall.com/cards/twar/8/en?format=image");
+ put("WAR/Zombie Army/3", "https://api.scryfall.com/cards/twar/9/en?format=image");
+ put("WAR/Zombie Warrior", "https://api.scryfall.com/cards/twar/11/en?format=image");
+ put("WAR/Zombie", "https://api.scryfall.com/cards/twar/7/en?format=image");
+
+ // generate supported sets
+ supportedSets.clear();
+ for (String cardName : this.keySet()) {
+ String[] s = cardName.split("\\/");
+ if (s.length > 1) {
+ supportedSets.putIfAbsent(s[0], s[0]);
+ }
+ }
+ }
+ };
+
+ public static Map getSupportedSets() {
+ return supportedSets;
+ }
+
+ public static String findTokenLink(String setCode, String tokenName, Integer tokenNumber) {
+ String search = setCode + "/" + tokenName + (!tokenNumber.equals(0) ? "/" + tokenNumber : "");
+ return supportedCards.getOrDefault(search, null);
+ }
+}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java
index 483bb27e86..714310c5a8 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/ScryfallSymbolsSource.java
@@ -70,10 +70,7 @@ public class ScryfallSymbolsSource implements Iterable {
}
// gen symbols list
- ArrayList allMageSymbols = new ArrayList<>();
- for (int i = 0; i < SYMBOLS_LIST.length; i++) {
- allMageSymbols.add(SYMBOLS_LIST[i]);
- }
+ List allMageSymbols = new ArrayList<>(Arrays.asList(SYMBOLS_LIST));
for (Integer i = SYMBOLS_NUMBER_START; i <= SYMBOLS_NUMBER_END; i++) {
allMageSymbols.add(String.valueOf(SYMBOLS_NUMBER_START + i));
}
@@ -111,21 +108,17 @@ public class ScryfallSymbolsSource implements Iterable {
if (destFile.exists() && (destFile.length() > 0)) {
continue;
}
- FileOutputStream stream = null;
- try {
+ try(FileOutputStream stream = new FileOutputStream(destFile)) {
// base64 transform
String data64 = foundedData.get(searchCode);
Base64.Decoder dec = Base64.getDecoder();
byte[] fileData = dec.decode(data64);
- stream = new FileOutputStream(destFile);
stream.write(fileData);
LOGGER.info("New svg symbol downloaded: " + needCode);
} catch (Exception e) {
LOGGER.error("Can't decode svg icon and save to file: " + destFile.getPath() + ", reason: " + e.getMessage());
- } finally {
- StreamUtils.closeQuietly(stream);
}
}
}
@@ -166,7 +159,7 @@ public class ScryfallSymbolsSource implements Iterable {
org.jsoup.nodes.Document doc = CardImageUtils.downloadHtmlDocument(CSS_SOURCE_URL);
org.jsoup.select.Elements cssList = doc.select(CSS_SOURCE_SELECTOR);
if (cssList.size() == 1) {
- this.cssUrl = cssList.first().attr("href").toString();
+ this.cssUrl = cssList.first().attr("href");
}
if (this.cssUrl.isEmpty()) {
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/TokensMtgImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/TokensMtgImageSource.java
index da76bfc8b6..5ac33a0b0a 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/TokensMtgImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/TokensMtgImageSource.java
@@ -1,6 +1,11 @@
-
package org.mage.plugins.card.dl.sources;
+import mage.constants.SubType;
+import org.apache.log4j.Logger;
+import org.mage.plugins.card.images.CardDownloadData;
+import org.mage.plugins.card.images.DownloadPicturesService;
+import org.mage.plugins.card.utils.CardImageUtils;
+
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
@@ -8,21 +13,9 @@ import java.io.InputStreamReader;
import java.net.Proxy;
import java.net.URL;
import java.net.URLConnection;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.logging.Level;
-import mage.constants.SubType;
-import org.apache.log4j.Logger;
-import org.mage.plugins.card.images.CardDownloadData;
-import org.mage.plugins.card.images.DownloadPictures;
-import org.mage.plugins.card.utils.CardImageUtils;
-
/**
* @author Quercitron
*/
@@ -32,7 +25,7 @@ public enum TokensMtgImageSource implements CardImageSource {
private static final Logger logger = Logger.getLogger(TokensMtgImageSource.class);
// [[EXP/Name, TokenData>
- private HashMap> tokensData;
+ private HashMap> tokensData;
private static final Set supportedSets = new LinkedHashSet();
private final Object tokensDataSync = new Object();
@@ -58,7 +51,7 @@ public enum TokensMtgImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
return null;
}
@@ -71,7 +64,7 @@ public enum TokensMtgImageSource implements CardImageSource {
private String getEmblemName(String originalName) {
- for (SubType subType : SubType.getPlaneswalkerTypes(true)) {
+ for (SubType subType : SubType.getPlaneswalkerTypes()) {
if (originalName.toLowerCase(Locale.ENGLISH).contains(subType.toString().toLowerCase(Locale.ENGLISH))) {
return subType.getDescription() + " Emblem";
}
@@ -96,7 +89,7 @@ public enum TokensMtgImageSource implements CardImageSource {
}
// Image URL contains token number
- // e.g. http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010
+ // e.g. https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg -- token number 010
// We don't know these numbers, but we can take them from a file
// with tokens information that can be downloaded from the site.
if (tokensData.isEmpty()) {
@@ -122,7 +115,7 @@ public enum TokensMtgImageSource implements CardImageSource {
tokenData = list.get(card.getType() - 1);
}
- String url = "http://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
+ String url = "https://tokens.mtg.onl/tokens/" + tokenData.getExpansionSetCode().trim() + '_'
+ tokenData.getNumber().trim() + '-' + tokenData.getName().trim() + ".jpg";
url = url.replace(' ', '-');
return new CardImageUrls(url);
@@ -160,7 +153,12 @@ public enum TokensMtgImageSource implements CardImageSource {
}
@Override
- public boolean isImageProvided(String setCode, String cardName) {
+ public boolean isCardImageProvided(String setCode, String cardName) {
+ return false;
+ }
+
+ @Override
+ public boolean isTokenImageProvided(String setCode, String cardName, Integer tokenNumber) {
String searchName = cardName;
if (cardName.toLowerCase(Locale.ENGLISH).contains("emblem")) {
searchName = getEmblemName(cardName);
@@ -179,10 +177,10 @@ public enum TokensMtgImageSource implements CardImageSource {
return false;
}
- private HashMap> getTokensData() throws IOException {
+ private HashMap> getTokensData() throws IOException {
synchronized (tokensDataSync) {
if (tokensData == null) {
- DownloadPictures.getInstance().updateAndViewMessage("Creating token data...");
+ DownloadPicturesService.getInstance().updateAndViewMessage("Find tokens data...");
tokensData = new HashMap<>();
// get tokens data from resource file
@@ -190,7 +188,7 @@ public enum TokensMtgImageSource implements CardImageSource {
List fileTokensData = parseTokensData(inputStream);
for (TokenData tokenData : fileTokensData) {
String key = tokenData.getExpansionSetCode() + "/" + tokenData.getName();
- ArrayList list = tokensData.get(key);
+ List list = tokensData.get(key);
if (list == null) {
list = new ArrayList<>();
tokensData.put(key, list);
@@ -215,7 +213,7 @@ public enum TokensMtgImageSource implements CardImageSource {
// logger.info("TOK: " + siteData.getExpansionSetCode() + "/" + siteData.getName());
String key = siteData.getExpansionSetCode() + "/" + siteData.getName();
supportedSets.add(siteData.getExpansionSetCode());
- ArrayList list = tokensData.get(key);
+ List list = tokensData.get(key);
if (list == null) {
list = new ArrayList<>();
tokensData.put(key, list);
@@ -233,10 +231,10 @@ public enum TokensMtgImageSource implements CardImageSource {
}
}
}
- DownloadPictures.getInstance().updateAndViewMessage("");
+ DownloadPicturesService.getInstance().updateAndViewMessage("");
} catch (Exception ex) {
logger.warn("Failed to get tokens description from tokens.mtg.onl", ex);
- DownloadPictures.getInstance().updateAndViewMessage(ex.getMessage());
+ DownloadPicturesService.getInstance().updateAndViewMessage(ex.getMessage());
}
}
}
@@ -251,7 +249,7 @@ public enum TokensMtgImageSource implements CardImageSource {
BufferedReader reader = new BufferedReader(inputReader)) {
// we have to specify encoding to read special comma
- reader.readLine(); // skip header
+ String header = reader.readLine(); // skip header
String line = reader.readLine();
// states
// 0 - wait set name
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java
index 5d39a943f6..0d434ec1c0 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/dl/sources/WizardCardsImageSource.java
@@ -1,22 +1,10 @@
package org.mage.plugins.card.dl.sources;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.LinkedHashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
-import mage.client.dialog.PreferencesDialog;
+import mage.client.util.CardLanguage;
import org.apache.log4j.Logger;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
@@ -24,6 +12,12 @@ import org.jsoup.select.Elements;
import org.mage.plugins.card.images.CardDownloadData;
import org.mage.plugins.card.utils.CardImageUtils;
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+
/**
* @author North
*/
@@ -34,9 +28,10 @@ public enum WizardCardsImageSource implements CardImageSource {
private static final Logger logger = Logger.getLogger(WizardCardsImageSource.class);
private final Map setsAliases;
- private final Map languageAliases;
+ private final Map languageAliases;
private final Map> sets;
private final Set supportedSets;
+ private CardLanguage currentLanguage = CardLanguage.ENGLISH; // working language
@Override
public String getSourceName() {
@@ -44,6 +39,20 @@ public enum WizardCardsImageSource implements CardImageSource {
}
WizardCardsImageSource() {
+
+ languageAliases = new EnumMap<>(CardLanguage.class);
+ languageAliases.put(CardLanguage.ENGLISH, "English");
+ languageAliases.put(CardLanguage.SPANISH, "Spanish");
+ languageAliases.put(CardLanguage.FRENCH, "French");
+ languageAliases.put(CardLanguage.GERMAN, "German");
+ languageAliases.put(CardLanguage.ITALIAN, "Italian");
+ languageAliases.put(CardLanguage.PORTUGUESE, "Portuguese (Brazil)");
+ languageAliases.put(CardLanguage.JAPANESE, "Japanese");
+ languageAliases.put(CardLanguage.KOREAN, "Korean");
+ languageAliases.put(CardLanguage.RUSSIAN, "Russian");
+ languageAliases.put(CardLanguage.CHINES_SIMPLE, "Chinese Simplified");
+ languageAliases.put(CardLanguage.CHINES_TRADITION, "Chinese Traditional ");
+
supportedSets = new LinkedHashSet<>();
// supportedSets.add("PTC"); // Prerelease Events
supportedSets.add("LEA");
@@ -430,18 +439,6 @@ public enum WizardCardsImageSource implements CardImageSource {
setsAliases.put("WTH", "Weatherlight");
setsAliases.put("WWK", "Worldwake");
setsAliases.put("ZEN", "Zendikar");
-
- languageAliases = new HashMap<>();
- languageAliases.put("en", "English");
- languageAliases.put("es", "Spanish");
- languageAliases.put("jp", "Japanese");
- languageAliases.put("it", "Italian");
- languageAliases.put("fr", "French");
- languageAliases.put("cn", "Chinese Simplified");
- languageAliases.put("de", "German");
- languageAliases.put("ko", "Korean");
- languageAliases.put("pt", "Portuguese (Brazil)");
- languageAliases.put("ru", "Russian");
}
@Override
@@ -455,7 +452,7 @@ public enum WizardCardsImageSource implements CardImageSource {
}
@Override
- public CardImageUrls generateURL(CardDownloadData card) throws Exception {
+ public CardImageUrls generateCardUrl(CardDownloadData card) throws Exception {
String collectorId = card.getCollectorId();
String cardSet = card.getSet();
if (collectorId == null || cardSet == null) {
@@ -517,7 +514,7 @@ public enum WizardCardsImageSource implements CardImageSource {
if (setNames == null) {
setNames = Sets.getInstance().get(cardSet).getName();
}
- String preferredLanguage = PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_PREF_LANGUAGE, "en");
+
for (String setName : setNames.split("\\^")) {
// String URLSetName = URLEncoder.encode(setName, "UTF-8");
String URLSetName = setName.replaceAll(" ", "%20");
@@ -547,15 +544,15 @@ public enum WizardCardsImageSource implements CardImageSource {
getLandVariations(setLinks, cardSet, multiverseId, cardName);
} else {
String numberChar = "";
- int pos1 = cardName.indexOf("(");
+ int pos1 = cardName.indexOf('(');
if (pos1 > 0) {
- int pos2 = cardName.indexOf("(", pos1 + 1);
+ int pos2 = cardName.indexOf('(', pos1 + 1);
if (pos2 > 0) {
numberChar = cardName.substring(pos2 + 1, pos2 + 2);
cardName = cardName.substring(0, pos1);
}
}
- Integer preferredMultiverseId = getLocalizedMultiverseId(preferredLanguage, multiverseId);
+ Integer preferredMultiverseId = getLocalizedMultiverseId(getCurrentLanguage(), multiverseId);
setLinks.put(cardName.toLowerCase(Locale.ENGLISH) + numberChar, generateLink(preferredMultiverseId));
}
}
@@ -617,8 +614,8 @@ public enum WizardCardsImageSource implements CardImageSource {
return "/Handlers/Image.ashx?multiverseid=" + landMultiverseId + "&type=card";
}
- private int getLocalizedMultiverseId(String preferredLanguage, Integer multiverseId) throws IOException {
- if (preferredLanguage.equals("en")) {
+ private int getLocalizedMultiverseId(CardLanguage preferredLanguage, Integer multiverseId) throws IOException {
+ if (preferredLanguage.equals(CardLanguage.ENGLISH)) {
return multiverseId;
}
@@ -682,43 +679,6 @@ public enum WizardCardsImageSource implements CardImageSource {
return 60.0f;
}
- // private final class GetImageLinkTask implements Runnable {
-//
-// private int multiverseId;
-// private String cardName;
-// private String preferredLanguage;
-// private LinkedHashMap setLinks;
-//
-// public GetImageLinkTask(int multiverseId, String cardName, String preferredLanguage, LinkedHashMap setLinks) {
-// try {
-// this.multiverseId = multiverseId;
-// this.cardName = cardName;
-// this.preferredLanguage = preferredLanguage;
-// this.setLinks = setLinks;
-// } catch (Exception ex) {
-// logger.error(ex.getMessage());
-// logger.error("multiverseId: " + multiverseId);
-// logger.error("cardName: " + cardName);
-// logger.error("preferredLanguage: " + preferredLanguage);
-// logger.error("setLinks: " + setLinks.toString());
-// }
-// }
-//
-// @Override
-// public void run() {
-// try {
-// if (cardName.equals("Forest") || cardName.equals("Swamp") || cardName.equals("Mountain") || cardName.equals("Island") || cardName.equals("Plains")) {
-// setLinks.putAll(getLandVariations(multiverseId, cardName));
-// } else {
-// Integer preferredMultiverseId = getLocalizedMultiverseId(preferredLanguage, multiverseId);
-// setLinks.put(cardName.toLowerCase(Locale.ENGLISH), generateLink(preferredMultiverseId));
-// }
-// } catch (IOException | NumberFormatException ex) {
-// logger.error("Exception when parsing the wizards page: " + ex.getMessage());
-// }
-// }
-//
-// }
@Override
public int getTotalImages() {
return -1;
@@ -729,6 +689,21 @@ public enum WizardCardsImageSource implements CardImageSource {
return false;
}
+ @Override
+ public boolean isLanguagesSupport() {
+ return true;
+ }
+
+ @Override
+ public void setCurrentLanguage(CardLanguage cardLanguage) {
+ this.currentLanguage = cardLanguage;
+ }
+
+ @Override
+ public CardLanguage getCurrentLanguage() {
+ return currentLanguage;
+ }
+
@Override
public void doPause(String httpImageUrl) {
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java
similarity index 55%
rename from Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java
rename to Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java
index 7be47db2d1..2ffc11bad1 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPictures.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/DownloadPicturesService.java
@@ -1,93 +1,82 @@
package org.mage.plugins.card.images;
-import java.awt.*;
-import java.awt.event.ItemEvent;
-import java.io.*;
-import java.net.*;
-import java.nio.file.AccessDeniedException;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Locale;
-import java.util.Set;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
-import javax.swing.*;
-
import mage.cards.ExpansionSet;
import mage.cards.Sets;
import mage.cards.repository.CardCriteria;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
import mage.client.MageFrame;
+import mage.client.dialog.DownloadImagesDialog;
import mage.client.dialog.PreferencesDialog;
+import mage.client.util.CardLanguage;
import mage.client.util.sets.ConstructedFormats;
import mage.remote.Connection;
-import mage.util.StreamUtils;
import net.java.truevfs.access.TFile;
import net.java.truevfs.access.TFileOutputStream;
import net.java.truevfs.access.TVFS;
import net.java.truevfs.kernel.spec.FsSyncException;
import org.apache.log4j.Logger;
import org.mage.plugins.card.dl.sources.*;
-import org.mage.plugins.card.properties.SettingsManager;
import org.mage.plugins.card.utils.CardImageUtils;
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ItemEvent;
+import java.io.*;
+import java.net.*;
+import java.nio.file.AccessDeniedException;
+import java.util.List;
+import java.util.*;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
import static org.mage.plugins.card.utils.CardImageUtils.getImagesDir;
-public class DownloadPictures extends DefaultBoundedRangeModel implements Runnable {
+/**
+ * @author JayDi85
+ */
+public class DownloadPicturesService extends DefaultBoundedRangeModel implements Runnable {
- // don't forget to remove new sets from ignore.urls to download (propeties file in resources)
- private static DownloadPictures instance;
+ // don't forget to remove new sets from ignore.urls to download (properties file in resources)
+ private static DownloadPicturesService instance;
+ private static final Logger logger = Logger.getLogger(DownloadPicturesService.class);
- private static final Logger logger = Logger.getLogger(DownloadPictures.class);
+ private static final String ALL_IMAGES = "- ALL images from selected source (can be slow)";
+ private static final String ALL_MODERN_IMAGES = "- MODERN images (can be slow)";
+ private static final String ALL_STANDARD_IMAGES = "- STANDARD images";
+ private static final String ALL_TOKENS = "- TOKEN images";
- public static final String ALL_IMAGES = "- ALL images from selected source (CAN BE VERY SLOW)";
- public static final String ALL_STANDARD_IMAGES = "- Only images from STANDARD sets";
- public static final String ALL_TOKENS = "- Only token images from selected source";
+ private static final int MAX_ERRORS_COUNT_BEFORE_CANCEL = 50;
- private JDialog dialog;
- private final JProgressBar bar;
- private final JOptionPane dlg;
- private boolean cancel;
- private final JButton closeButton;
- private final JButton startDownloadButton;
+ private DownloadImagesDialog uiDialog;
+ private boolean needCancel;
+ private int errorCount;
private int cardIndex;
- private List allCardsMissingImage;
- private List cardsToDownload;
- private int missingCards = 0;
- private int missingTokens = 0;
+ private List cardsAll;
+ private List cardsMissing;
+ private List cardsDownloadQueue;
+ private int missingCardsCount = 0;
+ private int missingTokensCount = 0;
- List selectedSetCodes = new ArrayList<>();
-
- private final JComboBox jComboBoxServer;
- private final JLabel jLabelMessage;
- private final JLabel jLabelAllMissing;
- private final JLabel jLabelServer;
-
- private final JComboBox jComboBoxSet;
- private final JLabel jLabelSet;
+ private List selectedSets = new ArrayList<>();
+ private static CardImageSource selectedSource;
private final Object sync = new Object();
-
- private static CardImageSource cardImageSource;
-
private Proxy p = Proxy.NO_PROXY;
enum DownloadSources {
- WIZARDS("1. wizards.com - low quality CARDS, multi-language, can be SLOW", WizardCardsImageSource.instance),
+ WIZARDS("1. wizards.com - low quality CARDS, multi-language, slow download", WizardCardsImageSource.instance),
TOKENS("2. tokens.mtg.onl - high quality TOKENS", TokensMtgImageSource.instance),
- SCRYFALL("3. scryfall.com - high quality CARDS, multi-language", ScryfallImageSource.instance),
+ SCRYFALL("3. scryfall.com - high quality CARDS and TOKENS, multi-language", ScryfallImageSource.instance),
MAGIDEX("4. magidex.com - high quality CARDS", MagidexImageSource.instance),
GRAB_BAG("5. GrabBag - STAR WARS cards and tokens", GrabbagImageSource.instance),
MYTHICSPOILER("6. mythicspoiler.com", MythicspoilerComSource.instance),
ALTERNATIVE("7. alternative.mtg.onl", AltMtgOnlTokensImageSource.instance),
COPYPASTE("8. Copy and Paste Image URLs", CopyPasteImageSource.instance);
// MTG_ONL("mtg.onl", MtgOnlTokensImageSource.instance), Not working correctly yet
- // MAGICCARDS("magiccards.info", MagicCardsImageSource.instance)
private final String text;
private final CardImageSource source;
@@ -108,7 +97,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
- public static DownloadPictures getInstance() {
+ public static DownloadPicturesService getInstance() {
return instance;
}
@@ -117,245 +106,252 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
public static void startDownload() {
+ // load images info in background task
+ instance = new DownloadPicturesService(MageFrame.getInstance());
+ new Thread(new LoadMissingCardDataNew(instance)).start();
- /*
- * if (cards == null || cards.isEmpty()) {
- * JOptionPane.showMessageDialog(null,
- * "All card pictures have been downloaded."); return; }
- */
- instance = new DownloadPictures(MageFrame.getInstance());
- Thread t1 = new Thread(new LoadMissingCardData(instance));
- t1.start();
- instance.getDlg().setVisible(true);
- instance.getDlg().dispose();
- instance.cancel = true;
+ // show dialog
+ instance.setNeedCancel(false);
+ instance.resetErrorCount();
+ instance.uiDialog.showDialog();
+ instance.uiDialog.dispose();
+ instance.setNeedCancel(true);
}
- public JDialog getDlg() {
- return dialog;
+ private boolean getNeedCancel() {
+ return this.needCancel || (this.errorCount > MAX_ERRORS_COUNT_BEFORE_CANCEL);
}
- public void setCancel(boolean cancel) {
- this.cancel = cancel;
+ private void setNeedCancel(boolean needCancel) {
+ this.needCancel = needCancel;
}
- static int WIDTH = 400;
+ private void incErrorCount() {
+ this.errorCount = this.errorCount + 1;
+ }
- public DownloadPictures(JFrame frame) {
+ private void resetErrorCount() {
+ this.errorCount = 0;
+ }
- cardsToDownload = new ArrayList<>();
+ public DownloadPicturesService(JFrame frame) {
+ // init service and dialog
+ cardsAll = Collections.synchronizedList(new ArrayList<>());
+ cardsMissing = Collections.synchronizedList(new ArrayList<>());
+ cardsDownloadQueue = Collections.synchronizedList(new ArrayList<>());
+ uiDialog = new DownloadImagesDialog();
- JPanel p0 = new JPanel();
- p0.setLayout(new BoxLayout(p0, BoxLayout.Y_AXIS));
+ // MESSAGE
+ uiDialog.setGlobalInfo("Initializing image download...");
- p0.add(Box.createVerticalStrut(5));
-
- jLabelMessage = new JLabel();
- jLabelMessage.setAlignmentX(Component.CENTER_ALIGNMENT);
- jLabelMessage.setText("Initializing image download...");
- p0.add(jLabelMessage);
- p0.add(Box.createVerticalStrut(5));
-
- jLabelAllMissing = new JLabel();
-
- jLabelAllMissing.setAlignmentX(Component.LEFT_ALIGNMENT);
- // jLabelAllMissing.setText("Computing number of missing images...");
- p0.add(jLabelAllMissing);
- p0.add(Box.createVerticalStrut(5));
-
- jLabelServer = new JLabel();
- jLabelServer.setText("Please select image source:");
- jLabelServer.setAlignmentX(Component.LEFT_ALIGNMENT);
- jLabelServer.setVisible(false);
- p0.add(jLabelServer);
-
- p0.add(Box.createVerticalStrut(5));
-
- jComboBoxServer = new JComboBox();
- jComboBoxServer.setModel(new DefaultComboBoxModel(DownloadSources.values()));
- jComboBoxServer.setAlignmentX(Component.LEFT_ALIGNMENT);
- jComboBoxServer.setAlignmentY(Component.LEFT_ALIGNMENT);
- jComboBoxServer.addItemListener((ItemEvent event) -> {
+ // SOURCES - scryfall is default source
+ uiDialog.getSourcesCombo().setModel(new DefaultComboBoxModel(DownloadSources.values()));
+ uiDialog.getSourcesCombo().setSelectedItem(DownloadSources.SCRYFALL);
+ selectedSource = ScryfallImageSource.instance;
+ uiDialog.getSourcesCombo().addItemListener((ItemEvent event) -> {
if (event.getStateChange() == ItemEvent.SELECTED) {
- comboBoxServerItemSelected(event);
- }
- });
- Dimension d = jComboBoxServer.getPreferredSize();
- d.width = WIDTH;
- jComboBoxServer.setPreferredSize(d);
- p0.add(jComboBoxServer);
- jComboBoxServer.setVisible(false);
-
- // set the first source as default
- cardImageSource = WizardCardsImageSource.instance;
-
- p0.add(Box.createVerticalStrut(5));
-
- // Set selection ---------------------------------
- jLabelSet = new JLabel();
- jLabelSet.setText("Please select sets to download the images for:");
- jLabelSet.setAlignmentX(Component.LEFT_ALIGNMENT);
- jLabelSet.setVisible(false);
- p0.add(jLabelSet);
-
- jComboBoxSet = new JComboBox();
-// jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
- jComboBoxSet.setAlignmentX(Component.LEFT_ALIGNMENT);
- jComboBoxSet.addItemListener((ItemEvent event) -> {
- if (event.getStateChange() == ItemEvent.SELECTED) {
- comboBoxSetItemSelected(event);
+ comboboxSourceSelected(event);
}
});
- p0.add(jComboBoxSet);
- jComboBoxSet.setVisible(false);
+ // LANGUAGES
+ uiDialog.getLaunguagesCombo().setModel(new DefaultComboBoxModel(CardLanguage.values()));
+ uiDialog.getLaunguagesCombo().setSelectedItem(PreferencesDialog.getPrefImagesLanguage());
+ reloadLanguagesForSelectedSource();
- p0.add(Box.createVerticalStrut(5));
+ // REDOWNLOAD
+ uiDialog.getRedownloadCheckbox().setSelected(false);
+ uiDialog.getRedownloadCheckbox().addItemListener(this::checkboxRedowloadChanged);
- // Start
- startDownloadButton = new JButton("Start download");
- startDownloadButton.addActionListener(e -> {
- new Thread(DownloadPictures.this).start();
- startDownloadButton.setEnabled(false);
+
+ // SETS (fills after source and language select)
+ //uiDialog.getSetsCombo().setModel(new DefaultComboBoxModel(DownloadSources.values()));
+ uiDialog.getSetsCombo().addItemListener((ItemEvent event) -> {
+ if (event.getStateChange() == ItemEvent.SELECTED) {
+ comboboxSetSelected(event);
+ }
});
- p0.add(Box.createVerticalStrut(5));
- // Progress
- bar = new JProgressBar(this);
- p0.add(bar);
- bar.setStringPainted(true);
+ // BUTTON START
+ uiDialog.getStartButton().addActionListener(e -> {
+ // selected language setup
+ if (selectedSource != null) {
+ if (selectedSource.isLanguagesSupport()) {
+ selectedSource.setCurrentLanguage((CardLanguage) uiDialog.getLaunguagesCombo().getSelectedItem());
+ }
+ }
- d = bar.getPreferredSize();
- d.width = WIDTH;
- bar.setPreferredSize(d);
- bar.setVisible(false);
+ // run
+ uiDialog.enableActionControls(false);
+ uiDialog.getStartButton().setEnabled(false);
+ new Thread(DownloadPicturesService.this).start();
+ });
- // JOptionPane
- Object[] options = {startDownloadButton, closeButton = new JButton("Cancel")};
- startDownloadButton.setVisible(false);
- closeButton.addActionListener(e -> dialog.setVisible(false));
- closeButton.setVisible(false);
+ // BUTTON CANCEL (dialog and loading)
+ uiDialog.getCancelButton().addActionListener(e -> uiDialog.setVisible(false));
+ uiDialog.getStopButton().addActionListener(e -> uiDialog.setVisible(false));
- dlg = new JOptionPane(p0, JOptionPane.PLAIN_MESSAGE, JOptionPane.DEFAULT_OPTION, null, options, options[1]);
- dialog = this.dlg.createDialog(frame, "Downloading images");
+ // PROGRESS BAR
+ uiDialog.getProgressBar().setValue(0);
}
- public void setAllMissingCards() {
- updateAndViewMessage("Get all available cards from the repository...");
- List cards = CardRepository.instance.findCards(new CardCriteria());
- updateAndViewMessage("Check which images are missing ...");
- this.allCardsMissingImage = getNeededCards(cards);
- updateAndViewMessage("Check which images the current source is providing ...");
- jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
+ public void findMissingCards() {
+ updateAndViewMessage("Loading...");
+ this.cardsAll.clear();
+ this.cardsMissing.clear();
+ this.cardsDownloadQueue.clear();
- updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
+ updateAndViewMessage("Loading cards list...");
+ this.cardsAll = Collections.synchronizedList(CardRepository.instance.findCards(new CardCriteria()));
- jComboBoxServer.setVisible(true);
- jLabelServer.setVisible(true);
- jComboBoxSet.setVisible(true);
- jLabelSet.setVisible(true);
- bar.setVisible(true);
- startDownloadButton.setVisible(true);
- closeButton.setVisible(true);
+ updateAndViewMessage("Finding missing images...");
+ this.cardsMissing = prepareMissingCards(this.cardsAll, uiDialog.getRedownloadCheckbox().isSelected());
+ updateAndViewMessage("Finding available sets from selected source...");
+ this.uiDialog.getSetsCombo().setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
+ reloadCardsToDownload(this.uiDialog.getSetsCombo().getSelectedItem().toString());
+
+ this.uiDialog.showDownloadControls(true);
updateAndViewMessage("");
}
- private void comboBoxServerItemSelected(ItemEvent evt) {
- if (jComboBoxServer.isEnabled()) {
- cardImageSource = ((DownloadSources) evt.getItem()).getSource();
- // update the available sets / token comboBox
- jComboBoxSet.setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
- updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
+ private void reloadLanguagesForSelectedSource() {
+ this.uiDialog.showLanguagesSupport(selectedSource != null && selectedSource.isLanguagesSupport());
+ }
+
+ private void reloadSetsForSelectedSource() {
+ // update the available sets / token combobox
+ Object oldSelection = this.uiDialog.getSetsCombo().getSelectedItem();
+ this.uiDialog.getSetsCombo().setModel(new DefaultComboBoxModel<>(getSetsForCurrentImageSource()));
+ if (oldSelection != null) {
+ this.uiDialog.getSetsCombo().setSelectedItem(oldSelection);
+ }
+ reloadCardsToDownload(this.uiDialog.getSetsCombo().getSelectedItem().toString());
+ }
+
+ private void comboboxSourceSelected(ItemEvent evt) {
+ if (this.uiDialog.getSourcesCombo().isEnabled()) {
+ selectedSource = ((DownloadSources) evt.getItem()).getSource();
+ reloadSetsForSelectedSource();
+ reloadLanguagesForSelectedSource();
}
}
public void updateAndViewMessage(String text) {
- jLabelMessage.setText(text);
- if (dialog != null) {
- dialog.pack();
- dialog.validate();
- dialog.repaint();
+ this.uiDialog.setGlobalInfo(text);
+
+ // auto-size on empty message (on complete)
+ if (text.isEmpty()) {
+ this.uiDialog.showDownloadControls(true);
}
}
+ private String getSetNameWithYear(ExpansionSet exp) {
+ Calendar cal = Calendar.getInstance();
+ cal.setTime(exp.getReleaseDate());
+ String year = String.valueOf(cal.get(Calendar.YEAR));
+
+ return exp.getName() + " (" + exp.getCode() + ", " + year + ")";
+
+ /*
+ if (!exp.getName().contains(year)) {
+ return exp.getName() + " (" + year + ")";
+ } else {
+ return exp.getName();
+ }
+ */
+ }
+
+ private ExpansionSet findSetByNameWithYear(String name) {
+ return Sets.getInstance().values().stream()
+ .filter(exp -> getSetNameWithYear(exp).equals(name))
+ .findFirst()
+ .orElse(null);
+ }
+
private Object[] getSetsForCurrentImageSource() {
// Set the available sets to the combo box
- ArrayList supportedSets = cardImageSource.getSupportedSets();
+ ArrayList supportedSets = selectedSource.getSupportedSets();
List setNames = new ArrayList<>();
- if (supportedSets != null) {
- setNames.add(ALL_IMAGES);
- setNames.add(ALL_STANDARD_IMAGES);
- }
- if (cardImageSource.isTokenSource()) {
+
+ // multiple sets selection
+ setNames.add(ALL_IMAGES);
+ setNames.add(ALL_MODERN_IMAGES);
+ setNames.add(ALL_STANDARD_IMAGES);
+ if (selectedSource.isTokenSource()) {
setNames.add(ALL_TOKENS);
}
- if (supportedSets != null) {
- for (String setCode : supportedSets) {
- ExpansionSet expansionSet = Sets.findSet(setCode);
- if (expansionSet != null) {
- setNames.add(expansionSet.getName());
- } else {
- logger.warn("Source: " + cardImageSource.getSourceName() + ": Expansion set for code " + setCode + " not found in xmage sets!");
- }
- }
- }
+ // single set selection
+ Collection dbSets = Sets.getInstance().values();
+ Collection comboSets = dbSets.stream()
+ .filter(exp -> supportedSets.contains(exp.getCode()))
+ .sorted(Comparator.comparing(ExpansionSet::getReleaseDate).reversed())
+ .map(this::getSetNameWithYear)
+ .collect(Collectors.toList());
+ setNames.addAll(comboSets);
+
if (setNames.isEmpty()) {
- logger.error("Source " + cardImageSource.getSourceName() + " creates no selectable items.");
- setNames.add("not avalable");
+ logger.error("Source " + selectedSource.getSourceName() + " creates no selectable items.");
+ setNames.add("not available");
}
return setNames.toArray(new String[0]);
}
- private void updateCardsToDownload(String itemText) {
- selectedSetCodes.clear();
- switch (itemText) {
+ private void reloadCardsToDownload(String selectedItem) {
+ // find selected sets
+ selectedSets.clear();
+ List formatSets;
+ List sourceSets = selectedSource.getSupportedSets();
+ switch (selectedItem) {
+
case ALL_IMAGES:
- if (cardImageSource.getSupportedSets() == null) {
- selectedSetCodes = cardImageSource.getSupportedSets();
- } else {
- selectedSetCodes.addAll(cardImageSource.getSupportedSets());
- }
+ selectedSets.addAll(selectedSource.getSupportedSets());
break;
+
case ALL_STANDARD_IMAGES:
- List standardSets = ConstructedFormats.getSetsByFormat(ConstructedFormats.STANDARD);
- for (String setCode : cardImageSource.getSupportedSets()) {
- if (standardSets.contains(setCode)) {
- selectedSetCodes.add(setCode);
- } else {
- logger.debug("Set code " + setCode + " from download source " + cardImageSource.getSourceName());
- }
- }
+ formatSets = ConstructedFormats.getSetsByFormat(ConstructedFormats.STANDARD);
+ formatSets.stream()
+ .filter(sourceSets::contains)
+ .forEachOrdered(selectedSets::add);
break;
+
+ case ALL_MODERN_IMAGES:
+ formatSets = ConstructedFormats.getSetsByFormat(ConstructedFormats.MODERN);
+ formatSets.stream()
+ .filter(sourceSets::contains)
+ .forEachOrdered(selectedSets::add);
+ break;
+
case ALL_TOKENS:
break;
+
default:
- int nonSetEntries = 0;
- if (cardImageSource.getSupportedSets() != null) {
- nonSetEntries = 2;
+ // selects one set
+ ExpansionSet selectedExp = findSetByNameWithYear(selectedItem);
+ if (selectedExp != null) {
+ selectedSets.add(selectedExp.getCode());
}
- if (cardImageSource.isTokenSource()) {
- nonSetEntries++;
- }
- selectedSetCodes.add(cardImageSource.getSupportedSets().get(jComboBoxSet.getSelectedIndex() - nonSetEntries));
+ break;
}
- cardsToDownload.clear();
+
+ // find missing cards to download
+ cardsDownloadQueue.clear();
int numberTokenImagesAvailable = 0;
int numberCardImagesAvailable = 0;
- for (CardDownloadData data : allCardsMissingImage) {
+ for (CardDownloadData data : cardsMissing) {
if (data.isToken()) {
- if (cardImageSource.isTokenSource() && cardImageSource.isImageProvided(data.getSet(), data.getName())) {
+ if (selectedSource.isTokenSource() && selectedSource.isTokenImageProvided(data.getSet(), data.getName(), data.getType())) {
numberTokenImagesAvailable++;
- cardsToDownload.add(data);
+ cardsDownloadQueue.add(data);
} else {
//logger.warn("Source do not support token (set " + data.getSet() + ", token " + data.getName() + ")");
}
} else {
- if (selectedSetCodes != null && selectedSetCodes.contains(data.getSet())) {
- if (cardImageSource.isSetSupportedComplete(data.getSet()) || cardImageSource.isImageProvided(data.getSet(), data.getName())) {
+ if (selectedSets != null && selectedSets.contains(data.getSet())) {
+ if (selectedSource.isSetSupportedComplete(data.getSet()) || selectedSource.isCardImageProvided(data.getSet(), data.getName())) {
numberCardImagesAvailable++;
- cardsToDownload.add(data);
+ cardsDownloadQueue.add(data);
}
}
}
@@ -363,24 +359,40 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
updateProgressText(numberCardImagesAvailable, numberTokenImagesAvailable);
}
- private void comboBoxSetItemSelected(ItemEvent event) {
+ private void comboboxSetSelected(ItemEvent event) {
// Update the cards to download related to the selected set
- updateCardsToDownload(event.getItem().toString());
+ reloadCardsToDownload(event.getItem().toString());
+ }
+
+ private void checkboxRedowloadChanged(ItemEvent event) {
+ MageFrame.getDesktop().setCursor(new Cursor(Cursor.WAIT_CURSOR));
+ try {
+ this.cardsMissing.clear();
+ this.cardsMissing = prepareMissingCards(this.cardsAll, uiDialog.getRedownloadCheckbox().isSelected());
+ reloadCardsToDownload(uiDialog.getSetsCombo().getSelectedItem().toString());
+ } finally {
+ MageFrame.getDesktop().setCursor(new Cursor(Cursor.DEFAULT_CURSOR));
+ }
}
private void updateProgressText(int cardCount, int tokenCount) {
- missingTokens = 0;
- for (CardDownloadData card : allCardsMissingImage) {
+ missingTokensCount = 0;
+ for (CardDownloadData card : cardsMissing) {
if (card.isToken()) {
- missingTokens++;
+ missingTokensCount++;
}
}
- missingCards = allCardsMissingImage.size() - missingTokens;
- jLabelAllMissing.setText("Missing: " + missingCards + " card images / " + missingTokens + " token images");
+ missingCardsCount = cardsMissing.size() - missingTokensCount;
+
+ uiDialog.setCurrentInfo("Missing: " + missingCardsCount + " card images / " + missingTokensCount + " token images");
int imageSum = cardCount + tokenCount;
- float mb = (imageSum * cardImageSource.getAverageSize()) / 1024;
- bar.setString(String.format(cardIndex == imageSum ? "%d of %d (%d cards/%d tokens) image downloads finished! Please close!"
- : "%d of %d (%d cards/%d tokens) image downloads finished! Please wait! [%.1f Mb]", 0, imageSum, cardCount, tokenCount, mb));
+ float mb = (imageSum * selectedSource.getAverageSize()) / 1024;
+ uiDialog.getProgressBar().setString(String.format(
+ cardIndex == imageSum
+ ? "%d of %d (%d cards/%d tokens) image downloads finished! Please close!"
+ : "%d of %d (%d cards/%d tokens) image downloads finished! Please wait! [%.1f Mb]",
+ 0, imageSum, cardCount, tokenCount, mb
+ ));
}
private static String createDownloadName(CardInfo card) {
@@ -388,16 +400,9 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
return className.substring(className.lastIndexOf('.') + 1);
}
- private static List getNeededCards(List allCards) {
+ private static List prepareMissingCards(List allCards, boolean redownloadMode) {
- /**
- * read all card names and urls
- */
- HashSet ignoreUrls = SettingsManager.getIntance().getIgnoreUrls();
-
- /**
- * get filter for Standard Type 2 cards
- */
+ // get filter for Standard Type 2 cards
Set type2SetsFilter = new HashSet<>();
List constructedFormats = ConstructedFormats.getSetsByFormat(ConstructedFormats.STANDARD);
if (constructedFormats != null && !constructedFormats.isEmpty()) {
@@ -406,11 +411,11 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
logger.warn("No formats defined. Try connecting to a server first!");
}
+ // prepare checking list
List allCardsUrls = Collections.synchronizedList(new ArrayList<>());
try {
allCards.parallelStream().forEach(card -> {
- if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()
- && !ignoreUrls.contains(card.getSetCode())) {
+ if (!card.getCardNumber().isEmpty() && !"0".equals(card.getCardNumber()) && !card.getSetCode().isEmpty()) {
String cardName = card.getName();
boolean isType2 = type2SetsFilter.contains(card.getSetCode());
CardDownloadData url = new CardDownloadData(cardName, card.getSetCode(), card.getCardNumber(), card.usesVariousArt(), 0, "", "", false, card.isDoubleFaced(), card.isNightCard());
@@ -455,32 +460,33 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
logger.info("Card was not selected: " + card.getName());
}
});
+
allCardsUrls.addAll(getTokenCardUrls());
} catch (Exception e) {
logger.error(e);
}
- /**
- * check to see which cards we already have
- */
+ // find missing files
List cardsToDownload = Collections.synchronizedList(new ArrayList<>());
allCardsUrls.parallelStream().forEach(card -> {
- File file = new TFile(CardImageUtils.buildImagePathToCard(card));
- logger.debug(card.getName() + " (is_token=" + card.isToken() + "). Image is here:" + file.getAbsolutePath() + " (exists=" + file.exists() + ')');
- if (!file.exists()) {
- logger.debug("Missing: " + file.getAbsolutePath());
- // logger.info("Missing image: " + (card.isToken() ? "TOKEN " : "CARD ") + card.getSet() + "/" + card.getName() + " type: " + card.getType());
+ if (redownloadMode) {
+ // need all cards
cardsToDownload.add(card);
+ } else {
+ // need missing cards
+ File file = new TFile(CardImageUtils.buildImagePathToCard(card));
+ if (!file.exists()) {
+ cardsToDownload.add(card);
+ }
}
});
- return new ArrayList<>(cardsToDownload);
+ return Collections.synchronizedList(new ArrayList<>(cardsToDownload));
}
public static ArrayList getTokenCardUrls() throws RuntimeException {
ArrayList list = new ArrayList<>();
- InputStream in = DownloadPictures.class
- .getClassLoader().getResourceAsStream("card-pictures-tok.txt");
+ InputStream in = DownloadPicturesService.class.getClassLoader().getResourceAsStream("card-pictures-tok.txt");
if (in == null) {
logger.error("resources input stream is null");
@@ -498,7 +504,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
if (params.length >= 5) {
int type = 0;
if (params[4] != null && !params[4].isEmpty()) {
- type = Integer.parseInt(params[4].trim());
+ type = Integer.parseInt(params[4].trim()); // token number for same names
}
String fileName = "";
if (params.length > 5 && params[5] != null && !params[5].isEmpty()) {
@@ -545,8 +551,18 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
} catch (Exception ex) {
logger.error(ex);
- throw new RuntimeException("DownloadPictures : readFile() error");
+ throw new RuntimeException("DownloadPicturesService : readFile() error");
}
+
+ // TODO: delete and move to copy-pate images download mode
+ /*
+ for (CardDownloadData card : list) {
+ if (card.isToken()) {
+ System.out.println(card.getSet() + "/" + card.getName() + (!card.getType().equals(0) ? "/" + card.getType() : ""));
+ }
+ }
+ */
+
return list;
}
@@ -581,62 +597,60 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
Integer port = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_PROXY_PORT, "80"));
p = new Proxy(type, new InetSocketAddress(address, port));
} catch (Exception ex) {
- throw new RuntimeException("Gui_DownloadPictures : error 1 - " + ex);
+ throw new RuntimeException("Gui_DownloadPicturesService : error 1 - " + ex);
}
}
if (p != null) {
- HashSet ignoreUrls = SettingsManager.getIntance().getIgnoreUrls();
-
- update(0, cardsToDownload.size());
- logger.info("Started download of " + cardsToDownload.size() + " images from source: " + cardImageSource.getSourceName());
+ update(0, cardsDownloadQueue.size());
+ logger.info("Started download of " + cardsDownloadQueue.size() + " images"
+ + " from source: " + selectedSource.getSourceName()
+ + ", language: " + selectedSource.getCurrentLanguage().getCode());
int numberOfThreads = Integer.parseInt(PreferencesDialog.getCachedValue(PreferencesDialog.KEY_CARD_IMAGES_THREADS, "10"));
ExecutorService executor = Executors.newFixedThreadPool(numberOfThreads);
- for (int i = 0; i < cardsToDownload.size() && !cancel; i++) {
+ for (int i = 0; i < cardsDownloadQueue.size() && !this.getNeedCancel(); i++) {
try {
-
- CardDownloadData card = cardsToDownload.get(i);
+ CardDownloadData card = cardsDownloadQueue.get(i);
logger.debug("Downloading image: " + card.getName() + " (" + card.getSet() + ')');
CardImageUrls urls;
-
- if (ignoreUrls.contains(card.getSet()) || card.isToken()) {
+ if (card.isToken()) {
if (!"0".equals(card.getCollectorId())) {
continue;
}
- urls = cardImageSource.generateTokenUrl(card);
+ urls = selectedSource.generateTokenUrl(card);
} else {
- urls = cardImageSource.generateURL(card);
+ urls = selectedSource.generateCardUrl(card);
}
if (urls == null) {
- String imageRef = cardImageSource.getNextHttpImageUrl();
- String fileName = cardImageSource.getFileForHttpImage(imageRef);
+ String imageRef = selectedSource.getNextHttpImageUrl();
+ String fileName = selectedSource.getFileForHttpImage(imageRef);
if (imageRef != null && fileName != null) {
- imageRef = cardImageSource.getSourceName() + imageRef;
+ imageRef = selectedSource.getSourceName() + imageRef;
try {
- card.setToken(cardImageSource.isTokenSource());
- Runnable task = new DownloadTask(card, imageRef, fileName, cardImageSource.getTotalImages());
+ card.setToken(selectedSource.isTokenSource());
+ Runnable task = new DownloadTask(card, imageRef, fileName, selectedSource.getTotalImages());
executor.execute(task);
} catch (Exception ex) {
}
- } else if (cardImageSource.getTotalImages() == -1) {
- logger.info("Image not available on " + cardImageSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')');
+ } else if (selectedSource.getTotalImages() == -1) {
+ logger.info("Image not available on " + selectedSource.getSourceName() + ": " + card.getName() + " (" + card.getSet() + ')');
synchronized (sync) {
- update(cardIndex + 1, cardsToDownload.size());
+ update(cardIndex + 1, cardsDownloadQueue.size());
}
}
} else {
- Runnable task = new DownloadTask(card, urls, cardsToDownload.size());
+ Runnable task = new DownloadTask(card, urls, cardsDownloadQueue.size());
executor.execute(task);
}
-
} catch (Exception ex) {
logger.error(ex, ex);
}
}
+
executor.shutdown();
while (!executor.isTerminated()) {
try {
@@ -645,6 +659,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
}
}
+
try {
TVFS.umount();
} catch (FsSyncException e) {
@@ -653,11 +668,15 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
} finally {
//
}
- closeButton.setText("Close");
- updateCardsToDownload(jComboBoxSet.getSelectedItem().toString());
+
+ // stop
+ reloadCardsToDownload(uiDialog.getSetsCombo().getSelectedItem().toString());
+
+ // reset images cache
+ ImageCache.clearCache();
}
- static String convertStreamToString(java.io.InputStream is) {
+ static String convertStreamToString(InputStream is) {
java.util.Scanner s = new java.util.Scanner(is).useDelimiter("\\A");
return s.hasNext() ? s.next() : "";
}
@@ -670,10 +689,6 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
private final String actualFilename;
private final boolean useSpecifiedPaths;
- DownloadTask(CardDownloadData card, String baseUrl, int count) {
- this(card, new CardImageUrls(baseUrl, null), count);
- }
-
DownloadTask(CardDownloadData card, CardImageUrls urls, int count) {
this.card = card;
this.urls = urls;
@@ -692,7 +707,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
@Override
public void run() {
- if (cancel) {
+ if (DownloadPicturesService.getInstance().getNeedCancel()) {
synchronized (sync) {
update(cardIndex + 1, count);
}
@@ -736,20 +751,23 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
// FILE already exists (in zip or in dir)
+ // don't use, images can be re-downloaded
+ /*
if (destFile.exists()) {
synchronized (sync) {
update(cardIndex + 1, count);
}
return;
}
+ */
- // zip can't be read
+ // check zip access
TFile testArchive = destFile.getTopLevelArchive();
if (testArchive != null && testArchive.exists()) {
try {
testArchive.list();
} catch (Exception e) {
- logger.error("Error reading archive, may be it was corrapted. Try to delete it: " + testArchive.toString());
+ logger.error("Error reading archive, it's can be corrupted. Try to delete it: " + testArchive.toString());
synchronized (sync) {
update(cardIndex + 1, count);
@@ -773,53 +791,63 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
URL url = new URL(currentUrl);
// on download cancel need to stop
- if (cancel) {
+ if (DownloadPicturesService.getInstance().getNeedCancel()) {
return;
}
// download
- cardImageSource.doPause(url.getPath());
+ selectedSource.doPause(url.getPath());
+
httpConn = url.openConnection(p);
- httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
- httpConn.connect();
- int responseCode = ((HttpURLConnection) httpConn).getResponseCode();
+ if (httpConn != null) {
- // check result
- if (responseCode != 200) {
- // show errors only on full fail (all urls were not work)
- errorsList.add("Image download for " + card.getName()
- + (!card.getDownloadName().equals(card.getName()) ? " downloadname: " + card.getDownloadName() : "")
- + " (" + card.getSet() + ") failed - responseCode: " + responseCode + " url: " + url.toString());
-
- if (logger.isDebugEnabled()) {
- // Shows the returned html from the request to the web server
- logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
+ httpConn.setRequestProperty("User-Agent", "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.4; en-US; rv:1.9.2.2) Gecko/20100316 Firefox/3.6.2");
+ try {
+ httpConn.connect();
+ } catch (SocketException e) {
+ incErrorCount();
+ errorsList.add("Wrong image URL or java app is not allowed to use network. Check your firewall or proxy settings. Error: " + e.getMessage() + ". Image URL: " + url.toString());
+ break;
+ } catch (UnknownHostException e) {
+ incErrorCount();
+ errorsList.add("Unknown site. Check your DNS settings. Error: " + e.getMessage() + ". Image URL: " + url.toString());
+ break;
}
+ int responseCode = ((HttpURLConnection) httpConn).getResponseCode();
- // go to next try
- continue;
- } else {
- // all fine
- isDownloadOK = true;
- break;
+ // check result
+ if (responseCode != 200) {
+ // show errors only on full fail (all urls were not work)
+ errorsList.add("Image download for " + card.getName()
+ + (!card.getDownloadName().equals(card.getName()) ? " downloadname: " + card.getDownloadName() : "")
+ + " (" + card.getSet() + ") failed - responseCode: " + responseCode + " url: " + url.toString());
+
+ if (logger.isDebugEnabled()) {
+ // Shows the returned html from the request to the web server
+ logger.debug("Returned HTML ERROR:\n" + convertStreamToString(((HttpURLConnection) httpConn).getErrorStream()));
+ }
+
+ // go to next try
+ continue;
+ } else {
+ // all fine
+ isDownloadOK = true;
+ break;
+ }
}
}
// can save result
if (isDownloadOK & httpConn != null) {
// save data to temp
- OutputStream out = null;
- OutputStream tfileout = null;
- InputStream in = null;
- try {
- in = new BufferedInputStream(httpConn.getInputStream());
- tfileout = new TFileOutputStream(fileTempImage);
- out = new BufferedOutputStream(tfileout);
+ try (InputStream in = new BufferedInputStream(httpConn.getInputStream());
+ OutputStream tfileout = new TFileOutputStream(fileTempImage);
+ OutputStream out = new BufferedOutputStream(tfileout)) {
byte[] buf = new byte[1024];
int len;
while ((len = in.read(buf)) != -1) {
// user cancelled
- if (cancel) {
+ if (DownloadPicturesService.getInstance().getNeedCancel()) {
// stop download, save current state and exit
TFile archive = destFile.getTopLevelArchive();
///* not need to unmout/close - it's auto action
@@ -840,13 +868,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
out.write(buf, 0, len);
}
- } finally {
- StreamUtils.closeQuietly(in);
- StreamUtils.closeQuietly(out);
- StreamUtils.closeQuietly(tfileout);
}
-
-
// TODO: add two faces card correction? (WTF)
// SAVE final data
if (fileTempImage.exists()) {
@@ -869,9 +891,11 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
} catch (AccessDeniedException e) {
+ incErrorCount();
logger.error("Can't access to files: " + card.getName() + "(" + card.getSet() + "). Try rebooting your system to remove the file lock.");
} catch (Exception e) {
- logger.error(e.getMessage(), e);
+ incErrorCount();
+ logger.error("Unknown error: " + e.getMessage(), e);
} finally {
}
@@ -881,34 +905,38 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
}
- private void update(int card, int count) {
- this.cardIndex = card;
+ private void update(int lastCardIndex, int needDownloadCount) {
+ this.cardIndex = lastCardIndex;
- if (cardIndex < count) {
- float mb = ((count - card) * cardImageSource.getAverageSize()) / 1024;
- bar.setString(String.format("%d of %d image downloads finished! Please wait! [%.1f Mb]",
- card, count, mb));
+ if (cardIndex < needDownloadCount) {
+ // downloading
+ float mb = ((needDownloadCount - lastCardIndex) * selectedSource.getAverageSize()) / 1024;
+ uiDialog.getProgressBar().setString(String.format("%d of %d image downloads finished! Please wait! [%.1f Mb]",
+ lastCardIndex, needDownloadCount, mb));
} else {
- List remainingCards = Collections.synchronizedList(new ArrayList<>());
- DownloadPictures.this.allCardsMissingImage.parallelStream().forEach(cardDownloadData -> {
+ // finished
+ List downloadedCards = Collections.synchronizedList(new ArrayList<>());
+ DownloadPicturesService.this.cardsMissing.parallelStream().forEach(cardDownloadData -> {
TFile file = new TFile(CardImageUtils.buildImagePathToCard(cardDownloadData));
- if (!file.exists()) {
- remainingCards.add(cardDownloadData);
+ if (file.exists()) {
+ downloadedCards.add(cardDownloadData);
}
});
- // remove the cards not downloaded to get the siccessfull downloaded cards
- DownloadPictures.this.cardsToDownload.removeAll(remainingCards);
- DownloadPictures.this.allCardsMissingImage.removeAll(DownloadPictures.this.cardsToDownload);
+ // remove all downloaded cards, missing must be remains
+ this.cardsDownloadQueue.removeAll(downloadedCards);
+ this.cardsMissing.removeAll(downloadedCards);
- count = remainingCards.size();
-
- if (count == 0) {
- bar.setString("0 images remaining! Please close!");
+ if (this.cardsDownloadQueue.isEmpty()) {
+ // stop download
+ uiDialog.getProgressBar().setString("0 images remaining. Please close.");
} else {
-// bar.setString(String.format("%d cards remaining! Please choose another source!", count));
- startDownloadButton.setEnabled(true);
+ // try download again
}
+
+ this.uiDialog.getRedownloadCheckbox().setSelected(false);
+ uiDialog.enableActionControls(true);
+ uiDialog.getStartButton().setEnabled(true);
}
}
@@ -916,22 +944,20 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
}
-class LoadMissingCardData implements Runnable {
+class LoadMissingCardDataNew implements Runnable {
- private static DownloadPictures downloadPictures;
+ private static DownloadPicturesService downloadPicturesService;
- public LoadMissingCardData(DownloadPictures downloadPictures) {
- LoadMissingCardData.downloadPictures = downloadPictures;
+ public LoadMissingCardDataNew(DownloadPicturesService downloadPicturesService) {
+ LoadMissingCardDataNew.downloadPicturesService = downloadPicturesService;
}
@Override
public void run() {
- downloadPictures.setAllMissingCards();
+ downloadPicturesService.findMissingCards();
}
public static void main() {
-
- (new Thread(new LoadMissingCardData(downloadPictures))).start();
+ (new Thread(new LoadMissingCardDataNew(downloadPicturesService))).start();
}
-
}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
index ffe80ecdf5..92927b0509 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/images/ImageCache.java
@@ -1,35 +1,42 @@
package org.mage.plugins.card.images;
-import com.google.common.base.Function;
-import com.google.common.collect.ComputationException;
-import com.google.common.collect.MapMaker;
import java.awt.*;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
-import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+
+import javax.annotation.Nullable;
import javax.imageio.ImageIO;
+
+import org.apache.log4j.Logger;
+import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
+import org.mage.plugins.card.utils.CardImageUtils;
+
+import com.google.common.base.Function;
+import com.google.common.cache.CacheBuilder;
+import com.google.common.cache.CacheLoader;
+import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ComputationException;
+
import mage.client.constants.Constants;
import mage.client.dialog.PreferencesDialog;
+import mage.client.util.SoftValuesLoadingCache;
import mage.client.util.TransformedImageCache;
import mage.view.CardView;
import net.java.truevfs.access.TFile;
import net.java.truevfs.access.TFileInputStream;
import net.java.truevfs.access.TFileOutputStream;
-import org.apache.log4j.Logger;
-import org.mage.plugins.card.dl.sources.DirectLinksForDownload;
-import org.mage.plugins.card.utils.CardImageUtils;
/**
* This class stores ALL card images in a cache with soft values. this means
* that the images may be garbage collected when they are not needed any more,
* but will be kept as long as possible.
- *
+ *
* Key format: "[cardname]#[setname]#[type]#[collectorID]#[param]"
- *
+ *
* where param is:
*
* - size of image
@@ -44,8 +51,8 @@ public final class ImageCache {
private static final Logger LOGGER = Logger.getLogger(ImageCache.class);
- private static final Map IMAGE_CACHE;
- private static final Map FACE_IMAGE_CACHE;
+ private static final SoftValuesLoadingCache IMAGE_CACHE;
+ private static final SoftValuesLoadingCache FACE_IMAGE_CACHE;
/**
* Common pattern for keys. Format: "##"
@@ -54,11 +61,10 @@ public final class ImageCache {
static {
// softValues() = Specifies that each value (not key) stored in the map should be wrapped in a SoftReference (by default, strong references are used). Softly-referenced objects will be garbage-collected in a globally least-recently-used manner, in response to memory demand.
- IMAGE_CACHE = new MapMaker().softValues().makeComputingMap(new Function() {
+ IMAGE_CACHE = SoftValuesLoadingCache.from(new Function() {
@Override
public BufferedImage apply(String key) {
try {
-
boolean usesVariousArt = false;
if (key.matches(".*#usesVariousArt.*")) {
usesVariousArt = true;
@@ -178,56 +184,46 @@ public final class ImageCache {
}
});
- FACE_IMAGE_CACHE = new MapMaker().softValues().makeComputingMap(new Function() {
- @Override
- public BufferedImage apply(String key) {
- try {
+ FACE_IMAGE_CACHE = SoftValuesLoadingCache.from(key -> {
+ try {
+ Matcher m = KEY_PATTERN.matcher(key);
- Matcher m = KEY_PATTERN.matcher(key);
+ if (m.matches()) {
+ String name = m.group(1);
+ String set = m.group(2);
+ //Integer artid = Integer.parseInt(m.group(2));
- if (m.matches()) {
- String name = m.group(1);
- String set = m.group(2);
- //Integer artid = Integer.parseInt(m.group(2));
+ String path;
+ path = CardImageUtils.generateFaceImagePath(name, set);
- String path;
- path = CardImageUtils.generateFaceImagePath(name, set);
-
- if (path == null) {
- return null;
- }
- TFile file = getTFile(path);
- if (file == null) {
- return null;
- }
-
- BufferedImage image = loadImage(file);
- return image;
- } else {
- throw new RuntimeException(
- "Requested face image doesn't fit the requirement for key (##: " + key);
+ if (path == null) {
+ return null;
}
- } catch (Exception ex) {
- if (ex instanceof ComputationException) {
- throw (ComputationException) ex;
- } else {
- throw new ComputationException(ex);
+ TFile file = getTFile(path);
+ if (file == null) {
+ return null;
}
- }
- }
- public BufferedImage makeThumbnailByFile(String key, TFile file, String thumbnailPath) {
- BufferedImage image = loadImage(file);
- image = getWizardsCard(image);
- if (image == null) {
- return null;
+ BufferedImage image = loadImage(file);
+ return image;
+ } else {
+ throw new RuntimeException(
+ "Requested face image doesn't fit the requirement for key (##: " + key);
+ }
+ } catch (Exception ex) {
+ if (ex instanceof ComputationException) {
+ throw (ComputationException) ex;
+ } else {
+ throw new ComputationException(ex);
}
- LOGGER.debug("creating thumbnail for " + key);
- return makeThumbnail(image, thumbnailPath);
}
});
}
+ public static void clearCache() {
+ IMAGE_CACHE.invalidateAll();
+ }
+
public static String getFilePath(CardView card, int width) {
String key = getKey(card, card.getName(), Integer.toString(width));
boolean usesVariousArt = false;
@@ -389,7 +385,7 @@ public final class ImageCache {
return getImage(getKey(card, card.getName(), ""));
}
-// public static BufferedImage getImageFaceOriginal(CardView card) {
+ // public static BufferedImage getImageFaceOriginal(CardView card) {
// return getFaceImage(getFaceKey(card, card.getName(), card.getExpansionSetCode()));
// }
public static BufferedImage getImageOriginalAlternateName(CardView card) {
@@ -401,14 +397,9 @@ public final class ImageCache {
*/
private static BufferedImage getImage(String key) {
try {
- return IMAGE_CACHE.get(key);
- } catch (NullPointerException ex) {
- // unfortunately NullOutputException, thrown when apply() returns
- // null, is not public
- // NullOutputException is a subclass of NullPointerException
- // legitimate, happens when a card has no image
- return null;
+ return IMAGE_CACHE.getOrNull(key);
} catch (ComputationException ex) {
+ // too low memory
if (ex.getCause() instanceof NullPointerException) {
return null;
}
@@ -422,13 +413,7 @@ public final class ImageCache {
*/
private static BufferedImage getFaceImage(String key) {
try {
- return FACE_IMAGE_CACHE.get(key);
- } catch (NullPointerException ex) {
- // unfortunately NullOutputException, thrown when apply() returns
- // null, is not public
- // NullOutputException is a subclass of NullPointerException
- // legitimate, happens when a card has no image
- return null;
+ return FACE_IMAGE_CACHE.getOrNull(key);
} catch (ComputationException ex) {
if (ex.getCause() instanceof NullPointerException) {
return null;
@@ -443,7 +428,7 @@ public final class ImageCache {
* the cache.
*/
private static BufferedImage tryGetImage(String key) {
- return IMAGE_CACHE.containsKey(key) ? IMAGE_CACHE.get(key) : null;
+ return IMAGE_CACHE.peekIfPresent(key);
}
/**
@@ -471,6 +456,7 @@ public final class ImageCache {
// return alternateName + "#" + card.getExpansionSetCode() + "#" +card.getType()+ "#" + card.getCardNumber() + "#"
// + (card.getTokenSetCode() == null ? "":card.getTokenSetCode());
// }
+
/**
* Load image from file
*
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java b/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java
index aabbc302d4..1ec644ec77 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/info/CardInfoPaneImpl.java
@@ -57,7 +57,7 @@ public class CardInfoPaneImpl extends JEditorPane implements CardInfoPane {
SwingUtilities.invokeLater(() -> {
TextLines textLines = GuiDisplayUtil.getTextLinesfromCardView(card);
StringBuilder buffer = GuiDisplayUtil.getRulefromCardView(card, textLines);
- resizeTooltipIfNeeded(container, textLines.basicTextLength, textLines.lines.size());
+ resizeTooltipIfNeeded(container, textLines.getBasicTextLength(), textLines.getLines().size());
setText(buffer.toString());
setCaretPosition(0);
});
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/properties/SettingsManager.java b/Mage.Client/src/main/java/org/mage/plugins/card/properties/SettingsManager.java
deleted file mode 100644
index c7a6b87d62..0000000000
--- a/Mage.Client/src/main/java/org/mage/plugins/card/properties/SettingsManager.java
+++ /dev/null
@@ -1,76 +0,0 @@
-package org.mage.plugins.card.properties;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Properties;
-import mage.client.constants.Constants;
-
-public class SettingsManager {
-
- private static SettingsManager settingsManager = null;
-
- public static synchronized SettingsManager getIntance() {
- if (settingsManager == null) {
- settingsManager = new SettingsManager();
- }
- return settingsManager;
- }
-
- private SettingsManager() {
- loadImageProperties();
- }
-
- public void reloadImageProperties() {
- loadImageProperties();
- }
-
- private void loadImageProperties() {
- imageUrlProperties = new Properties();
- try {
- InputStream is = SettingsManager.class.getClassLoader().getResourceAsStream(Constants.IO.IMAGE_PROPERTIES_FILE);
- if (is == null) {
- throw new RuntimeException("Couldn't load " + Constants.IO.IMAGE_PROPERTIES_FILE);
- }
- imageUrlProperties.load(is);
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
-
- public String getSetNameReplacement(String setName) {
- String result = setName;
- if (imageUrlProperties != null) {
- result = imageUrlProperties.getProperty(setName, setName);
- }
- return result;
- }
-
- public HashSet getIgnoreUrls() {
- HashSet ignoreUrls = new HashSet<>();
- if (imageUrlProperties != null) {
- String result = imageUrlProperties.getProperty("ignore.urls");
- if (result != null) {
- String[] ignore = result.split(",");
- ignoreUrls.addAll(Arrays.asList(ignore));
- }
- }
- return ignoreUrls;
- }
-
- public ArrayList getTokenLookupOrder() {
- ArrayList order = new ArrayList<>();
- if (imageUrlProperties != null) {
- String result = imageUrlProperties.getProperty("token.lookup.order");
- if (result != null) {
- String[] sets = result.split(",");
- order.addAll(Arrays.asList(sets));
- }
- }
- return order;
- }
-
- private Properties imageUrlProperties;
-}
diff --git a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java
index fb7c42aa09..c7dae39355 100644
--- a/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java
+++ b/Mage.Client/src/main/java/org/mage/plugins/card/utils/CardImageUtils.java
@@ -1,5 +1,16 @@
package org.mage.plugins.card.utils;
+import mage.client.MageFrame;
+import mage.client.constants.Constants;
+import mage.client.dialog.PreferencesDialog;
+import mage.remote.Connection;
+import mage.remote.Connection.ProxyType;
+import net.java.truevfs.access.TFile;
+import org.apache.log4j.Logger;
+import org.jsoup.Jsoup;
+import org.jsoup.nodes.Document;
+import org.mage.plugins.card.images.CardDownloadData;
+
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
@@ -12,18 +23,6 @@ import java.util.HashMap;
import java.util.Locale;
import java.util.prefs.Preferences;
-import mage.client.MageFrame;
-import mage.client.constants.Constants;
-import mage.client.dialog.PreferencesDialog;
-import mage.remote.Connection;
-import mage.remote.Connection.ProxyType;
-import net.java.truevfs.access.TFile;
-import org.apache.log4j.Logger;
-import org.jsoup.Jsoup;
-import org.jsoup.nodes.Document;
-import org.mage.plugins.card.images.CardDownloadData;
-import org.mage.plugins.card.properties.SettingsManager;
-
public final class CardImageUtils {
private static final HashMap pathCache = new HashMap<>();
@@ -53,7 +52,7 @@ public final class CardImageUtils {
return filePath;
}
- log.warn("Token image file not found. Set: " + card.getSet() + " Token Set Code: " + card.getTokenSetCode() + " Name: " + card.getName() + " File path: " + filePath);
+ log.warn("Token image file not found. Set: " + card.getSet() + " Token Set Code: " + card.getTokenSetCode() + " Name: " + card.getName() + " File path: " + getTokenImagePath(card));
} else {
log.warn("Trying to get token path for non token card. Set: " + card.getSet() + " Set Code: " + card.getTokenSetCode() + " Name: " + card.getName());
}
@@ -85,20 +84,6 @@ public final class CardImageUtils {
}
}
return filename;
-
-// makes no longer sense
-// file = new TFile(filename);
-// if (!file.exists()) {
-// CardDownloadData updated = new CardDownloadData(card);
-// updated.setName(card.getName() + " 1");
-// filename = buildImagePathToCard(updated);
-// file = new TFile(filename);
-// if (!file.exists()) {
-// updated = new CardDownloadData(card);
-// updated.setName(card.getName() + " 2");
-// filename = buildImagePathToCard(updated);
-// }
-// }
}
private static String searchForCardImage(CardDownloadData card) {
@@ -112,30 +97,9 @@ public final class CardImageUtils {
pathCache.put(card, path);
return path;
}
-
-// for (String set : SettingsManager.getIntance().getTokenLookupOrder()) {
-// c.setSet(set);
-// path = getTokenImagePath(c);
-// file = new TFile(path);
-// if (file.exists()) {
-// pathCache.put(card, path);
-// return path;
-// }
-// }
return generateTokenDescriptorImagePath(card);
}
- public static String updateSet(String cardSet, boolean forUrl) {
- String set = cardSet.toLowerCase(Locale.ENGLISH);
- if (set.equals("con")) {
- set = "cfx";
- }
- if (forUrl) {
- set = SettingsManager.getIntance().getSetNameReplacement(set);
- }
- return set;
- }
-
public static String prepareCardNameForFile(String cardName) {
return cardName.replace(":", "").replace("\"", "").replace("//", "-");
}
@@ -162,6 +126,15 @@ public final class CardImageUtils {
return path;
}
+ public static String fixSetNameForWindows(String set) {
+ // windows can't create con folders
+ if (set.equals("CON") || set.equals("con")) {
+ return "COX";
+ } else {
+ return set;
+ }
+ }
+
public static String buildImagePathToTokens() {
String imagesPath = getImagesDir() + File.separator;
@@ -182,7 +155,7 @@ public final class CardImageUtils {
throw new IllegalArgumentException("Card " + card.getName() + " have empty set.");
}
- String set = updateSet(card.getSet(), false).toUpperCase(Locale.ENGLISH); // TODO: research auto-replace... old code?
+ String set = card.getSet().toUpperCase(Locale.ENGLISH);
if (card.isToken()) {
return buildImagePathToSetAsToken(set);
@@ -195,14 +168,14 @@ public final class CardImageUtils {
String imagesPath = getImagesDir() + File.separator;
if (PreferencesDialog.isSaveImagesToZip()) {
- return imagesPath + set + ".zip" + File.separator + set + File.separator;
+ return imagesPath + fixSetNameForWindows(set) + ".zip" + File.separator + fixSetNameForWindows(set) + File.separator;
} else {
- return imagesPath + set + File.separator;
+ return imagesPath + fixSetNameForWindows(set) + File.separator;
}
}
private static String buildImagePathToSetAsToken(String set) {
- return buildImagePathToTokens() + set + File.separator;
+ return buildImagePathToTokens() + fixSetNameForWindows(set) + File.separator;
}
public static String buildImagePathToCard(CardDownloadData card) {
@@ -211,7 +184,7 @@ public final class CardImageUtils {
String prefixType = "";
if (card.getType() != 0) {
- prefixType = " " + Integer.toString(card.getType());
+ prefixType = " " + card.getType();
}
String cardName = card.getFileName();
@@ -228,6 +201,7 @@ public final class CardImageUtils {
finalFileName = cardName + prefixType + ".full.jpg";
}
+ /* 2019-01-12: no needs in name corrections, all files must be same and auto-downloaded
// if image file exists, correct name (for case sensitive systems)
// use TFile for zips
TFile dirFile = new TFile(setPath);
@@ -246,12 +220,13 @@ public final class CardImageUtils {
} catch (Exception ex) {
log.error("Can't read card name from file, may be it broken: " + setPath);
}
+ */
return setPath + finalFileName;
}
public static String generateFaceImagePath(String cardname, String set) {
- return getImagesDir() + File.separator + "FACE" + File.separator + set + File.separator + prepareCardNameForFile(cardname) + ".jpg";
+ return getImagesDir() + File.separator + "FACE" + File.separator + fixSetNameForWindows(set) + File.separator + prepareCardNameForFile(cardname) + ".jpg";
}
public static String generateTokenDescriptorImagePath(CardDownloadData card) {
diff --git a/Mage.Client/src/main/resources/META-INF/MANIFEST.MF b/Mage.Client/src/main/resources/META-INF/MANIFEST.MF
deleted file mode 100644
index 342098cf3f..0000000000
--- a/Mage.Client/src/main/resources/META-INF/MANIFEST.MF
+++ /dev/null
@@ -1,3 +0,0 @@
-Manifest-Version: 1.0
-X-COMMENT: Main-Class will be added automatically by build
-SplashScreen-Image: splash.jpg
diff --git a/Mage.Client/src/main/resources/buttons/copy_128.png b/Mage.Client/src/main/resources/buttons/copy_128.png
new file mode 100644
index 0000000000..cb3442c04c
Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/copy_128.png differ
diff --git a/Mage.Client/src/main/resources/buttons/copy_24.png b/Mage.Client/src/main/resources/buttons/copy_24.png
new file mode 100644
index 0000000000..9a2920c9a1
Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/copy_24.png differ
diff --git a/Mage.Client/src/main/resources/buttons/copy_32.png b/Mage.Client/src/main/resources/buttons/copy_32.png
new file mode 100644
index 0000000000..9e8be51fac
Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/copy_32.png differ
diff --git a/Mage.Client/src/main/resources/buttons/paste_128.png b/Mage.Client/src/main/resources/buttons/paste_128.png
new file mode 100644
index 0000000000..45f8a68c34
Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/paste_128.png differ
diff --git a/Mage.Client/src/main/resources/buttons/paste_24.png b/Mage.Client/src/main/resources/buttons/paste_24.png
new file mode 100644
index 0000000000..12ba4b619a
Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/paste_24.png differ
diff --git a/Mage.Client/src/main/resources/buttons/paste_32.png b/Mage.Client/src/main/resources/buttons/paste_32.png
new file mode 100644
index 0000000000..aa72ebba80
Binary files /dev/null and b/Mage.Client/src/main/resources/buttons/paste_32.png differ
diff --git a/Mage.Client/src/main/resources/card-pictures-tok.txt b/Mage.Client/src/main/resources/card-pictures-tok.txt
index 0598855512..98099ba71d 100644
--- a/Mage.Client/src/main/resources/card-pictures-tok.txt
+++ b/Mage.Client/src/main/resources/card-pictures-tok.txt
@@ -86,6 +86,8 @@
|Generate|EMBLEM:MMA|Elspeth, Knight Errant||Emblem Elspeth|ElspethKnightErrantEmblem|
|Generate|EMBLEM:SWS|Obi-Wan Kenobi||Emblem Obi-Wan Kenobi|ObiWanKenobiEmblem|
|Generate|EMBLEM:RIX|Huatli, Radiant Champion||Emblem Huatli|HuatliRadiantChampionEmblem|
+|Generate|EMBLEM:RNA|Domri, Chaos Bringer||Emblem Domri|DomriChaosBringerEmblem|
+|Generate|EMBLEM:WAR|Nissa, Who Shakes the World||Emblem Nissa|NissaWhoShakesTheWorldEmblem|
|Generate|PLANE:PCA|Plane - Academy At Tolaria West|||AcademyAtTolariaWestPlane|
|Generate|PLANE:PCA|Plane - Agyrem|||AgyremPlane|
|Generate|PLANE:PCA|Plane - Akoum|||AkoumPlane|
@@ -105,6 +107,8 @@
|Generate|PLANE:PCA|Plane - Truga Jungle|||TrugaJunglePlane|
|Generate|PLANE:PCA|Plane - Turri Island|||TurriIslandPlane|
|Generate|PLANE:PCA|Plane - Undercity Reaches|||UndercityReachesPlane|
+|Generate|TOK:ANA|Goblin|||GoblinToken|
+|Generate|TOK:ANA|Spirit|||SpiritWhiteToken|
|Generate|TOK:PCA|Eldrazi|||EldraziAnnihilatorToken|
|Generate|TOK:10E|Ape|||PongifyApeToken|
|Generate|TOK:10E|Dragon|||DragonToken2|
@@ -185,6 +189,7 @@
|Generate|TOK:ALL|Graveborn|||SekKuarDeathkeeperGravebornToken|
|Generate|TOK:ALL|Hippo|||HippoToken|
|Generate|TOK:ALL|Soldier|||SoldierToken|
+|Generate|TOK:ALL|Starfish|||StarfishToken|
|Generate|TOK:ALL|Zombie|||ZombieToken|
|Generate|TOK:APC|Angel|||HauntedAngelToken|
|Generate|TOK:APC|Cat|||PenumbraBobcatToken|
@@ -1182,3 +1187,39 @@
|Generate|TOK:ZEN|Vampire||
|Generate|TOK:ZEN|Wolf|||WolfToken|
|Generate|TOK:ZEN|Zombie Giant|||QuestForTheGravelordZombieToken|
+|Generate|TOK:RNA|Beast|||RedGreenBeastToken|
+|Generate|TOK:RNA|Centaur|||CentaurToken|
+|Generate|TOK:RNA|Frog Lizard|||FrogLizardToken|
+|Generate|TOK:RNA|Goblin|||GoblinToken|
+|Generate|TOK:RNA|Human|||HumanToken|
+|Generate|TOK:RNA|Illusion|||MesmerizingBenthidToken|
+|Generate|TOK:RNA|Ooze|||BiogenicOozeToken|
+|Generate|TOK:RNA|Sphinx|||WardenSphinxToken|
+|Generate|TOK:RNA|Spirit|||SpiritWhiteToken|
+|Generate|TOK:RNA|Thopter|||ThopterToken|
+|Generate|TOK:RNA|Treasure|||TreasureToken|
+|Generate|TOK:RNA|Zombie|||ZombieToken|
+|Generate|TOK:WAR|Angel|||AngelVigilanceToken|
+|Generate|TOK:WAR|Assassin|||AssassinToken2|
+|Generate|TOK:WAR|Devil|||DevilToken|
+|Generate|TOK:WAR|Dragon|||DragonToken|
+|Generate|TOK:WAR|Goblin|||GoblinToken|
+|Generate|TOK:WAR|Servo|||ServoToken|
+|Generate|TOK:WAR|Soldier|||SoldierVigilanceToken|
+|Generate|TOK:WAR|Spirit|||UginTheIneffableToken|
+|Generate|TOK:WAR|Voja, Friend to Elves|||VojaFriendToElvesToken|
+|Generate|TOK:WAR|Wall|||TeyoToken|
+|Generate|TOK:WAR|Wizard|||WizardToken|
+|Generate|TOK:WAR|Wolf|||WolfToken|
+|Generate|TOK:WAR|Zombie|||ZombieToken|
+|Generate|TOK:WAR|Zombie Warrior|||GodEternalOketraToken|
+|Generate|TOK:WAR|Zombie Army|1||ZombieArmyToken|
+|Generate|TOK:WAR|Zombie Army|2||ZombieArmyToken|
+|Generate|TOK:WAR|Zombie Army|3||ZombieArmyToken|
+
+
+
+
+
+
+
diff --git a/Mage.Client/src/main/resources/hint/bad.png b/Mage.Client/src/main/resources/hint/bad.png
new file mode 100644
index 0000000000..963626b1e0
Binary files /dev/null and b/Mage.Client/src/main/resources/hint/bad.png differ
diff --git a/Mage.Client/src/main/resources/hint/bad_old.png b/Mage.Client/src/main/resources/hint/bad_old.png
new file mode 100644
index 0000000000..6b4497d5ef
Binary files /dev/null and b/Mage.Client/src/main/resources/hint/bad_old.png differ
diff --git a/Mage.Client/src/main/resources/hint/good.png b/Mage.Client/src/main/resources/hint/good.png
new file mode 100644
index 0000000000..14a87bd4a3
Binary files /dev/null and b/Mage.Client/src/main/resources/hint/good.png differ
diff --git a/Mage.Client/src/main/resources/hint/good_old.png b/Mage.Client/src/main/resources/hint/good_old.png
new file mode 100644
index 0000000000..3b7a6907ee
Binary files /dev/null and b/Mage.Client/src/main/resources/hint/good_old.png differ
diff --git a/Mage.Client/src/main/resources/hint/restrict.png b/Mage.Client/src/main/resources/hint/restrict.png
new file mode 100644
index 0000000000..a977004e62
Binary files /dev/null and b/Mage.Client/src/main/resources/hint/restrict.png differ
diff --git a/Mage.Client/src/main/resources/image.url.properties b/Mage.Client/src/main/resources/image.url.properties
deleted file mode 100644
index d3ab400892..0000000000
--- a/Mage.Client/src/main/resources/image.url.properties
+++ /dev/null
@@ -1,78 +0,0 @@
-tsp=ts
-tor=tr
-mor=mt
-ody=od
-lrw=lw
-plc=pc
-gpt=gp
-inv=in
-ons=on
-scg=sc
-jud=ju
-mmq=mm
-pls=ps
-mrd=mi
-mir=mr
-tst=ts
-usg=us
-apc=ap
-nem=ne
-dis=di
-vis=vi
-9ed=9e
-8ed=8e
-7ed=7e
-4ed=4e
-tsb=tsts
-ulg=ul
-5ed=5e
-6ed=6e
-btd=bd
-sth=sh
-por=po
-s99=st
-lgn=le
-ice=ia
-csp=cs
-tmp=tp
-s00=st2k
-dst=ds
-pcy=pr
-uds=ud
-exo=ex
-lea=al
-hop=pch
-chr=ch
-arn=an
-wth=wl
-leb=be
-2ed=un
-3ed=rv
-brb=br
-atq=aq
-fem=fe
-leg=lg
-hml=hl
-all=ai
-drk=dk
-ptk=p3k
-gur=guru
-ddc=dvd
-dd2=jvc
-ddd=gvl
-unh=uh
-dde=pvc
-v09=fve
-v10=fvr
-v11=fvl
-drb=fvd
-h09=pds
-ugl=ug
-dd3dvd=ddadvd
-dd3evg=ddaevg
-dd3gvl=ddagvl
-dd3jvc=ddajvc
-# Remove setname as soon as the images can be downloaded
-ignore.urls=TOK,H17
-# sets ordered by release time (newest goes first)
-token.lookup.order=C18,M19,A25,DOM,E02,RIX,UST,XLN,IMA,H17,C17,V17,E01,DDT,CMA,HOU,MM3,DDS,AKH,DD3DVD,DD3EVG,DD3GVL,DD3JVC,H09,AER,PCA,C16,V16,MPS,KLD,DDR,CN2,EMN,EMA,SOI,DDQ,CP,CMA,ARENA,SUS,APAC,EURO,UGIN,C15,OGW,EXP,DDP,BFZ,DRB,V09,V10,V11,V12,V13,V14,V15,TPR,MPRP,DD3,DDO,ORI,MM2,PTC,DTK,FRF,KTK,M15,VMA,CNS,JOU,BNG,THS,DDL,M14,MMA,DGM,GTC,RTR,M13,AVR,DDI,DKA,ISD,M12,NPH,MBS,SOM,M11,ROE,DDE,WWK,ZEN,M10,GVL,ARB,DVD,CFX,JVC,ALA,EVE,SHM,EVG,MOR,LRW,10E,CLS,CHK,GRC
\ No newline at end of file
diff --git a/Mage.Client/src/main/resources/info/yellow_star_16.png b/Mage.Client/src/main/resources/info/yellow_star_16.png
new file mode 100644
index 0000000000..849916980a
Binary files /dev/null and b/Mage.Client/src/main/resources/info/yellow_star_16.png differ
diff --git a/Mage.Client/src/main/resources/info/yellow_star_24.png b/Mage.Client/src/main/resources/info/yellow_star_24.png
new file mode 100644
index 0000000000..41d21f49db
Binary files /dev/null and b/Mage.Client/src/main/resources/info/yellow_star_24.png differ
diff --git a/Mage.Client/src/main/resources/info/yellow_star_32.png b/Mage.Client/src/main/resources/info/yellow_star_32.png
new file mode 100644
index 0000000000..029025a9a9
Binary files /dev/null and b/Mage.Client/src/main/resources/info/yellow_star_32.png differ
diff --git a/Mage.Client/src/test/java/mage/client/game/MultiConnectTest.java b/Mage.Client/src/test/java/mage/client/game/MultiConnectTest.java
index 1974187c4d..ddcc9b34a7 100644
--- a/Mage.Client/src/test/java/mage/client/game/MultiConnectTest.java
+++ b/Mage.Client/src/test/java/mage/client/game/MultiConnectTest.java
@@ -1,8 +1,5 @@
package mage.client.game;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
-import javax.swing.*;
import mage.client.components.MageUI;
import mage.interfaces.MageClient;
import mage.interfaces.callback.ClientCallback;
@@ -13,6 +10,10 @@ import mage.utils.MageVersion;
import org.apache.log4j.Logger;
import org.junit.Ignore;
+import javax.swing.*;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
/**
* Test for emulating the connection from multi mage clients.
*
@@ -30,7 +31,7 @@ public class MultiConnectTest {
private static final CountDownLatch latch = new CountDownLatch(USER_CONNECT_COUNT);
- private static final MageVersion version = new MageVersion(MageVersion.MAGE_VERSION_MAJOR, MageVersion.MAGE_VERSION_MINOR, MageVersion.MAGE_VERSION_PATCH, MageVersion.MAGE_VERSION_MINOR_PATCH, MageVersion.MAGE_VERSION_INFO);
+ private static final MageVersion version = new MageVersion(MultiConnectTest.class);
private static volatile int connected;
@@ -70,7 +71,7 @@ public class MultiConnectTest {
}
@Override
- public void disconnected(boolean errorCall) {
+ public void disconnected(boolean askToReconnect) {
logger.info("disconnected");
}
diff --git a/Mage.Client/src/test/java/mage/client/game/TokensMtgImageSourceTest.java b/Mage.Client/src/test/java/mage/client/game/TokensMtgImageSourceTest.java
index 16f4e618f1..6ec7139d3e 100644
--- a/Mage.Client/src/test/java/mage/client/game/TokensMtgImageSourceTest.java
+++ b/Mage.Client/src/test/java/mage/client/game/TokensMtgImageSourceTest.java
@@ -19,15 +19,15 @@ public class TokensMtgImageSourceTest {
CardImageSource imageSource = TokensMtgImageSource.instance;
CardImageUrls url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 1, "ORI", ""));
- Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
+ Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_010-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Thopter", "ORI", "0", false, 2, "ORI", ""));
- Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
+ Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_011-Thopter.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Ashaya, the Awoken World", "ORI", "0", false, 0, "ORI", ""));
- Assert.assertEquals("http://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
+ Assert.assertEquals("https://tokens.mtg.onl/tokens/ORI_007-Ashaya,-the-Awoken-World.jpg", url.baseUrl);
url = imageSource.generateTokenUrl(new CardDownloadData("Emblem Gideon, Ally of Zendikar", "BFZ", "0", false, 0, null, ""));
- Assert.assertEquals("http://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
+ Assert.assertEquals("https://tokens.mtg.onl/tokens/BFZ_012-Gideon-Emblem.jpg", url.baseUrl);
}
}
diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml
index a139eab960..b82542869a 100644
--- a/Mage.Common/pom.xml
+++ b/Mage.Common/pom.xml
@@ -7,7 +7,7 @@
org.mage
mage-root
- 1.4.31
+ 1.4.35
mage-common
@@ -34,12 +34,12 @@
org.jboss
jboss-common-core
- 2.2.22.GA
+ 2.5.0.Final
jboss
jboss-serialization
- 1.0.3.GA
+ 4.2.2.GA
concurrent
@@ -54,7 +54,7 @@
com.google.code.gson
gson
- 2.8.2
+ 2.8.5
diff --git a/Mage.Common/src/main/java/mage/cards/CardDimensions.java b/Mage.Common/src/main/java/mage/cards/CardDimensions.java
index f989042f25..79c2e20aa8 100644
--- a/Mage.Common/src/main/java/mage/cards/CardDimensions.java
+++ b/Mage.Common/src/main/java/mage/cards/CardDimensions.java
@@ -9,19 +9,19 @@ import static mage.constants.Constants.*;
*/
public class CardDimensions {
- public int frameHeight;
- public int frameWidth;
- public int symbolHeight;
- public int symbolWidth;
- public int contentXOffset;
- public int nameYOffset;
- public int typeYOffset;
- public int textYOffset;
- public int textWidth;
- public int textHeight;
- public int powBoxTextTop;
- public int powBoxTextLeft;
- public int nameFontSize;
+ private final int frameHeight;
+ private final int frameWidth;
+ private final int symbolHeight;
+ private final int symbolWidth;
+ private final int contentXOffset;
+ private final int nameYOffset;
+ private final int typeYOffset;
+ private final int textYOffset;
+ private final int textWidth;
+ private final int textHeight;
+ private final int powBoxTextTop;
+ private final int powBoxTextLeft;
+ private final int nameFontSize;
public CardDimensions(double scaleFactor) {
frameHeight = (int) (FRAME_MAX_HEIGHT * scaleFactor);
@@ -39,4 +39,55 @@ public class CardDimensions {
nameFontSize = Math.max(9, (int) (NAME_FONT_MAX_SIZE * scaleFactor));
}
+ public int getFrameHeight() {
+ return frameHeight;
+ }
+
+ public int getFrameWidth() {
+ return frameWidth;
+ }
+
+ public int getSymbolHeight() {
+ return symbolHeight;
+ }
+
+ public int getSymbolWidth() {
+ return symbolWidth;
+ }
+
+ public int getContentXOffset() {
+ return contentXOffset;
+ }
+
+ public int getNameYOffset() {
+ return nameYOffset;
+ }
+
+ public int getTypeYOffset() {
+ return typeYOffset;
+ }
+
+ public int getTextYOffset() {
+ return textYOffset;
+ }
+
+ public int getTextWidth() {
+ return textWidth;
+ }
+
+ public int getTextHeight() {
+ return textHeight;
+ }
+
+ public int getPowBoxTextTop() {
+ return powBoxTextTop;
+ }
+
+ public int getPowBoxTextLeft() {
+ return powBoxTextLeft;
+ }
+
+ public int getNameFontSize() {
+ return nameFontSize;
+ }
}
diff --git a/Mage.Common/src/main/java/mage/cards/action/TransferData.java b/Mage.Common/src/main/java/mage/cards/action/TransferData.java
index 11228aa5ea..815adb8893 100644
--- a/Mage.Common/src/main/java/mage/cards/action/TransferData.java
+++ b/Mage.Common/src/main/java/mage/cards/action/TransferData.java
@@ -7,11 +7,67 @@ import mage.cards.TextPopup;
import mage.view.CardView;
public class TransferData {
- public Component component;
- public TextPopup popupText;
- public Point locationOnScreen;
- public int popupOffsetX;
- public int popupOffsetY;
- public UUID gameId;
- public CardView card;
+ private Component component;
+ private TextPopup popupText;
+ private Point locationOnScreen;
+ private int popupOffsetX;
+ private int popupOffsetY;
+ private UUID gameId;
+ private CardView card;
+
+ public Component getComponent() {
+ return component;
+ }
+
+ public void setComponent(Component component) {
+ this.component = component;
+ }
+
+ public TextPopup getPopupText() {
+ return popupText;
+ }
+
+ public void setPopupText(TextPopup popupText) {
+ this.popupText = popupText;
+ }
+
+ public Point getLocationOnScreen() {
+ return locationOnScreen;
+ }
+
+ public void setLocationOnScreen(Point locationOnScreen) {
+ this.locationOnScreen = locationOnScreen;
+ }
+
+ public int getPopupOffsetX() {
+ return popupOffsetX;
+ }
+
+ public void setPopupOffsetX(int popupOffsetX) {
+ this.popupOffsetX = popupOffsetX;
+ }
+
+ public int getPopupOffsetY() {
+ return popupOffsetY;
+ }
+
+ public void setPopupOffsetY(int popupOffsetY) {
+ this.popupOffsetY = popupOffsetY;
+ }
+
+ public UUID getGameId() {
+ return gameId;
+ }
+
+ public void setGameId(UUID gameId) {
+ this.gameId = gameId;
+ }
+
+ public CardView getCard() {
+ return card;
+ }
+
+ public void setCard(CardView card) {
+ this.card = card;
+ }
}
diff --git a/Mage.Common/src/main/java/mage/constants/Constants.java b/Mage.Common/src/main/java/mage/constants/Constants.java
index dbcc030101..400b9df9ef 100644
--- a/Mage.Common/src/main/java/mage/constants/Constants.java
+++ b/Mage.Common/src/main/java/mage/constants/Constants.java
@@ -1,8 +1,6 @@
-
package mage.constants;
/**
- *
* @author BetaSteward_at_googlemail.com
*/
public final class Constants {
@@ -50,19 +48,14 @@ public final class Constants {
*/
public static final int PRIORITY_TIME_SEC = 1200;
-
-
public enum Option {
-
;
-
public static final String POSSIBLE_ATTACKERS = "possibleAttackers";
+ public static final String POSSIBLE_BLOCKERS = "possibleBlockers";
public static final String SPECIAL_BUTTON = "specialButton";
// used to control automatic answers of optional effects
public static final String ORIGINAL_ID = "originalId";
public static final String SECOND_MESSAGE = "secondMessage";
public static final String HINT_TEXT = "hintText";
-
}
-
}
diff --git a/Mage.Common/src/main/java/mage/db/model/Feedback.java b/Mage.Common/src/main/java/mage/db/model/Feedback.java
index bbc2f52c00..735569d66e 100644
--- a/Mage.Common/src/main/java/mage/db/model/Feedback.java
+++ b/Mage.Common/src/main/java/mage/db/model/Feedback.java
@@ -1,5 +1,6 @@
package mage.db.model;
+import com.j256.ormlite.field.DataType;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import java.util.Date;
@@ -23,7 +24,7 @@ public class Feedback {
private String email;
@DatabaseField
private String host;
- @DatabaseField(columnName = "created_dt")
+ @DatabaseField(columnName = "created_dt", dataType = DataType.DATE_STRING, format = "yyyy-MM-dd HH:mm:ss")
private Date createdDate;
@DatabaseField
diff --git a/Mage.Common/src/main/java/mage/db/model/Log.java b/Mage.Common/src/main/java/mage/db/model/Log.java
index ff11495f1d..b80fadbea8 100644
--- a/Mage.Common/src/main/java/mage/db/model/Log.java
+++ b/Mage.Common/src/main/java/mage/db/model/Log.java
@@ -1,5 +1,6 @@
package mage.db.model;
+import com.j256.ormlite.field.DataType;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
@@ -15,7 +16,7 @@ public class Log {
@DatabaseField
private String key;
- @DatabaseField(columnName = "created_dt")
+ @DatabaseField(columnName = "created_dt", dataType = DataType.DATE_STRING, format = "yyyy-MM-dd HH:mm:ss")
private Date createdDate;
@DatabaseField
private String arg0;
diff --git a/Mage.Common/src/main/java/mage/interfaces/MageClient.java b/Mage.Common/src/main/java/mage/interfaces/MageClient.java
index 09c079152f..e1612c3f72 100644
--- a/Mage.Common/src/main/java/mage/interfaces/MageClient.java
+++ b/Mage.Common/src/main/java/mage/interfaces/MageClient.java
@@ -1,11 +1,9 @@
-
package mage.interfaces;
import mage.interfaces.callback.CallbackClient;
import mage.utils.MageVersion;
/**
- *
* @author BetaSteward_at_googlemail.com
*/
public interface MageClient extends CallbackClient {
@@ -14,7 +12,7 @@ public interface MageClient extends CallbackClient {
void connected(String message);
- void disconnected(boolean errorCall);
+ void disconnected(boolean askToReconnect);
void showMessage(String message);
diff --git a/Mage.Common/src/main/java/mage/remote/MageVersionException.java b/Mage.Common/src/main/java/mage/remote/MageVersionException.java
index b5f317be3e..d67093874f 100644
--- a/Mage.Common/src/main/java/mage/remote/MageVersionException.java
+++ b/Mage.Common/src/main/java/mage/remote/MageVersionException.java
@@ -1,11 +1,9 @@
-
package mage.remote;
import mage.MageException;
import mage.utils.MageVersion;
/**
- *
* @author BetaSteward_at_googlemail.com
*/
public class MageVersionException extends MageException {
@@ -13,7 +11,11 @@ public class MageVersionException extends MageException {
private final MageVersion serverVersion;
public MageVersionException(MageVersion clientVersion, MageVersion serverVersion) {
- super("Wrong client version " + clientVersion + ", expecting version " + serverVersion + ". \r\n\r\nPlease download needed version from http://XMage.de or http://www.slightlymagic.net/forum/viewforum.php?f=70");
+ super("Wrong client version."
+ + " Your version: " + clientVersion
+ + " Server version: " + serverVersion
+ + " Release app download: http://xmage.de"
+ + " BETA app download: http://xmage.today");
this.serverVersion = serverVersion;
}
diff --git a/Mage.Common/src/main/java/mage/remote/SessionImpl.java b/Mage.Common/src/main/java/mage/remote/SessionImpl.java
index 91878a9797..71f982f69f 100644
--- a/Mage.Common/src/main/java/mage/remote/SessionImpl.java
+++ b/Mage.Common/src/main/java/mage/remote/SessionImpl.java
@@ -1,15 +1,5 @@
-
package mage.remote;
-import java.io.BufferedWriter;
-import java.io.File;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.io.PrintWriter;
-import java.lang.reflect.UndeclaredThrowableException;
-import java.net.*;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
import mage.MageException;
import mage.cards.decks.DeckCardLists;
import mage.cards.repository.CardInfo;
@@ -38,8 +28,16 @@ import org.jboss.remoting.transport.bisocket.Bisocket;
import org.jboss.remoting.transport.socket.SocketWrapper;
import org.jboss.remoting.transporter.TransporterClient;
+import javax.swing.*;
+import java.io.*;
+import java.lang.reflect.UndeclaredThrowableException;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.CancellationException;
+import java.util.concurrent.TimeUnit;
+
/**
- * @author BetaSteward_at_googlemail.com
+ * @author BetaSteward_at_googlemail.com, JayDi85
*/
public class SessionImpl implements Session {
@@ -58,13 +56,15 @@ public class SessionImpl implements Session {
private ServerState serverState;
private SessionState sessionState = SessionState.DISCONNECTED;
private Connection connection;
- private final static int PING_CYCLES = 10;
+ private RemotingTask lastRemotingTask = null;
+ private static final int PING_CYCLES = 10;
private final LinkedList pingTime = new LinkedList<>();
private String pingInfo = "";
private static boolean debugMode = false;
private boolean canceled = false;
private boolean jsonLogActive = false;
+ private String lastError = "";
static {
debugMode = System.getProperty("debug.mage") != null;
@@ -79,99 +79,129 @@ public class SessionImpl implements Session {
return sessionId;
}
- // RemotingTask encapsulates a task which is involved with some JBoss Remoting. This is
- // intended to be used with handleRemotingTaskExceptions for sharing the common exception
- // handling.
- public interface RemotingTask {
+ // RemotingTask - do server side works in background and return result, can be canceled at any time
+ public abstract class RemotingTask {
- public boolean run() throws Throwable;
+ SwingWorker worker = null;
+ Throwable lastError = null;
+
+ abstract public boolean work() throws Throwable;
+
+ boolean doWork() throws Throwable {
+ worker = new SwingWorker() {
+ @Override
+ protected Boolean doInBackground() {
+ try {
+ return work();
+ } catch (Throwable t) {
+ lastError = t;
+ return false;
+ }
+ }
+ };
+ worker.execute();
+
+ boolean res = worker.get();
+ if (lastError != null) {
+ throw lastError;
+ }
+ return res;
+ }
+
+ public void cancel() {
+ if (worker != null) {
+ worker.cancel(true);
+ }
+ }
}
- // handleRemotingTaskExceptions runs the given task and handles exceptions appropriately. This
- // way we can share the common exception handling.
- private boolean handleRemotingTaskExceptions(RemotingTask remoting) {
+ private void showMessageToUser(String message) {
+ client.showMessage("Remote task error. " + message);
+ }
+
+ private boolean doRemoteWorkAndHandleErrors(RemotingTask remoting) {
+ // execute remote task and wait result, can be canceled
+ lastRemotingTask = remoting;
try {
- return remoting.run();
+ return remoting.doWork();
+ } catch (InterruptedException | CancellationException t) {
+ // was canceled by user, nothing to show
} catch (MalformedURLException ex) {
- logger.fatal("", ex);
- client.showMessage("Unable to connect to server. " + ex.getMessage());
+ logger.fatal("Connect: wrong server address", ex);
+ showMessageToUser(ex.getMessage());
} catch (UndeclaredThrowableException ex) {
String addMessage = "";
Throwable cause = ex.getCause();
if (cause instanceof InvocationFailureException) {
InvocationFailureException exep = (InvocationFailureException) cause;
if (exep.getCause() instanceof IOException) {
- if (exep.getCause().getMessage().startsWith("Field hash null is not available on current")) {
- addMessage = "Probabaly the server version is not compatible to the client. ";
+ if ((exep.getCause().getMessage() != null) && (exep.getCause().getMessage().startsWith("Field hash null is not available on current")
+ || exep.getCause().getMessage().endsWith("end of file"))) {
+ addMessage = "Probably the server version is not compatible with the client. ";
}
+ } else {
+ logger.error("Connect: unknown server error", exep.getCause());
}
} else if (cause instanceof NoSuchMethodException) {
// NoSuchMethodException is thrown on an invocation of an unknow JBoss remoting
// method, so it's likely to be because of a version incompatibility.
addMessage = "The following method is not available in the server, probably the "
- + "server version is not compatible to the client: " + cause.getMessage();
+ + "server version is not compatible with the client: " + cause.getMessage();
}
if (addMessage.isEmpty()) {
- logger.fatal("", ex);
+ logger.fatal("Connect: unknown error", ex);
}
- client.showMessage("Unable to connect to server. " + addMessage + (ex.getMessage() != null ? ex.getMessage() : ""));
+ showMessageToUser(addMessage + (ex.getMessage() != null ? ex.getMessage() : ""));
} catch (IOException ex) {
- logger.fatal("", ex);
+ logger.fatal("Connect: unknown IO error", ex);
String addMessage = "";
if (ex.getMessage() != null && ex.getMessage().startsWith("Unable to perform invocation")) {
addMessage = "Maybe the server version is not compatible. ";
}
- client.showMessage("Unable to connect to server. " + addMessage + ex.getMessage() != null ? ex.getMessage() : "");
+ showMessageToUser(addMessage + (ex.getMessage() != null ? ex.getMessage() : ""));
} catch (MageVersionException ex) {
- if (!canceled) {
- client.showMessage("Unable to connect to server. " + ex.getMessage());
- }
+ logger.warn("Connect: wrong versions");
disconnect(false);
+ if (!canceled) {
+ showMessageToUser(ex.getMessage());
+ }
} catch (CannotConnectException ex) {
if (!canceled) {
handleCannotConnectException(ex);
}
} catch (Throwable t) {
- logger.fatal("Unable to connect to server - ", t);
+ logger.fatal("Connect: FAIL", t);
+ disconnect(false);
if (!canceled) {
- disconnect(false);
- StringBuilder sb = new StringBuilder();
- sb.append("Unable to connect to server.\n");
- for (StackTraceElement element : t.getStackTrace()) {
- sb.append(element.toString()).append('\n');
- }
- client.showMessage(sb.toString());
+ showMessageToUser(t.getMessage());
}
+ } finally {
+ lastRemotingTask = null;
}
return false;
}
@Override
public synchronized boolean register(final Connection connection) {
- return establishJBossRemotingConnection(connection) && handleRemotingTaskExceptions(new RemotingTask() {
+ return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
@Override
- public boolean run() throws Throwable {
- logger.info("Trying to register as " + getUserName() + " to XMAGE server at " + connection.getHost() + ':' + connection.getPort());
- boolean registerResult = server.registerUser(sessionId, connection.getUsername(),
- connection.getPassword(), connection.getEmail());
- if (registerResult) {
- logger.info("Registered as " + getUserName() + " to MAGE server at " + connection.getHost() + ':' + connection.getPort());
- }
- return registerResult;
+ public boolean work() throws Throwable {
+ logger.info("Registration: username " + getUserName() + " for email " + getEmail());
+ boolean result = server.registerUser(sessionId, connection.getUsername(), connection.getPassword(), connection.getEmail());
+ logger.info("Registration: " + (result ? "DONE, check your email for new password" : "FAIL"));
+ return result;
}
});
}
@Override
public synchronized boolean emailAuthToken(final Connection connection) {
- return establishJBossRemotingConnection(connection) && handleRemotingTaskExceptions(new RemotingTask() {
+ return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
@Override
- public boolean run() throws Throwable {
- logger.info("Trying to ask for an auth token to " + getEmail() + " to XMAGE server at " + connection.getHost() + ':' + connection.getPort());
+ public boolean work() throws Throwable {
+ logger.info("Auth request: requesting auth token for username " + getUserName() + " to email " + getEmail());
boolean result = server.emailAuthToken(sessionId, connection.getEmail());
- if (result) {
- logger.info("An auth token is emailed to " + getEmail() + " from MAGE server at " + connection.getHost() + ':' + connection.getPort());
- }
+ logger.info("Auth request: " + (result ? "DONE, check your email for auth token" : "FAIL"));
return result;
}
});
@@ -179,14 +209,12 @@ public class SessionImpl implements Session {
@Override
public synchronized boolean resetPassword(final Connection connection) {
- return establishJBossRemotingConnection(connection) && handleRemotingTaskExceptions(new RemotingTask() {
+ return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
@Override
- public boolean run() throws Throwable {
- logger.info("Trying reset the password in XMAGE server at " + connection.getHost() + ':' + connection.getPort());
+ public boolean work() throws Throwable {
+ logger.info("Password reset: reseting password for username " + getUserName());
boolean result = server.resetPassword(sessionId, connection.getEmail(), connection.getAuthToken(), connection.getPassword());
- if (result) {
- logger.info("Password is successfully reset in MAGE server at " + connection.getHost() + ':' + connection.getPort());
- }
+ logger.info("Password reset: " + (result ? "DONE, check your email for new password" : "FAIL"));
return result;
}
});
@@ -194,58 +222,71 @@ public class SessionImpl implements Session {
@Override
public synchronized boolean connect(final Connection connection) {
- return establishJBossRemotingConnection(connection)
- && handleRemotingTaskExceptions(new RemotingTask() {
- @Override
- public boolean run() throws Throwable {
- logger.info("Trying to log-in as " + getUserName() + " to XMAGE server at " + connection.getHost() + ':' + connection.getPort());
- boolean registerResult;
- if (connection.getAdminPassword() == null) {
- // for backward compatibility. don't remove twice call - first one does nothing but for version checking
- registerResult = server.connectUser(connection.getUsername(), connection.getPassword(), sessionId, client.getVersion(), connection.getUserIdStr());
- if (registerResult) {
- server.setUserData(connection.getUsername(), sessionId, connection.getUserData(), client.getVersion().toString(), connection.getUserIdStr());
- }
- } else {
- registerResult = server.connectAdmin(connection.getAdminPassword(), sessionId, client.getVersion());
- }
- if (registerResult) {
- serverState = server.getServerState();
- if (!connection.getUsername().equals("Admin")) {
- updateDatabase(connection.isForceDBComparison(), serverState);
- }
- logger.info("Logged-in as " + getUserName() + " to MAGE server at " + connection.getHost() + ':' + connection.getPort());
- client.connected(getUserName() + '@' + connection.getHost() + ':' + connection.getPort() + ' ');
- return true;
- }
- disconnect(false);
- return false;
+ return doRemoteConnection(connection) && doRemoteWorkAndHandleErrors(new RemotingTask() {
+ @Override
+ public boolean work() throws Throwable {
+ setLastError("");
+ logger.info("Logging: as username " + getUserName() + " to server " + connection.getHost() + ':' + connection.getPort());
+ boolean result;
+
+ if (connection.getAdminPassword() == null) {
+ // for backward compatibility. don't remove twice call - first one does nothing but for version checking
+ result = server.connectUser(connection.getUsername(), connection.getPassword(), sessionId, client.getVersion(), connection.getUserIdStr());
+ } else {
+ result = server.connectAdmin(connection.getAdminPassword(), sessionId, client.getVersion());
+ }
+
+ if (result) {
+ serverState = server.getServerState();
+
+ // client side check for incompatible versions
+ if (client.getVersion().compareTo(serverState.getVersion()) != 0) {
+ throw new MageVersionException(client.getVersion(), serverState.getVersion());
}
- });
+
+ if (!connection.getUsername().equals("Admin")) {
+ server.setUserData(connection.getUsername(), sessionId, connection.getUserData(), client.getVersion().toString(), connection.getUserIdStr());
+ updateDatabase(connection.isForceDBComparison(), serverState);
+ }
+
+ logger.info("Logging: DONE");
+ client.connected(getUserName() + '@' + connection.getHost() + ':' + connection.getPort() + ' ');
+ return true;
+ }
+
+ logger.info("Logging: FAIL");
+ disconnect(false);
+ return false;
+ }
+ });
}
@Override
public Optional getServerHostname() {
- return isConnected() ? Optional.of(connection.getHost()) : Optional.empty();
+ return isConnected() ? Optional.of(connection.getHost()) : Optional.empty();
}
@Override
public boolean stopConnecting() {
canceled = true;
+ if (lastRemotingTask != null) {
+ lastRemotingTask.cancel();
+ }
return true;
}
- private boolean establishJBossRemotingConnection(final Connection connection) {
+ private boolean doRemoteConnection(final Connection connection) {
+ // connect to server and setup all data, can be canceled
if (isConnected()) {
disconnect(true);
}
this.connection = connection;
this.canceled = false;
sessionState = SessionState.CONNECTING;
- boolean result = handleRemotingTaskExceptions(new RemotingTask() {
+ lastRemotingTask = new RemotingTask() {
@Override
- public boolean run() throws Throwable {
- logger.info("Trying to connect to XMAGE server at " + connection.getHost() + ':' + connection.getPort());
+ public boolean work() throws Throwable {
+ logger.info("Connect: connecting to server " + connection.getHost() + ':' + connection.getPort());
System.setProperty("http.nonProxyHosts", "code.google.com");
System.setProperty("socksNonProxyHosts", "code.google.com");
@@ -256,6 +297,9 @@ public class SessionImpl implements Session {
System.clearProperty("http.proxyHost");
System.clearProperty("http.proxyPort");
+ if (connection.getProxyType() != Connection.ProxyType.NONE) {
+ logger.info("Connect: using proxy " + connection.getProxyHost() + ":" + connection.getProxyPort());
+ }
switch (connection.getProxyType()) {
case SOCKS:
System.setProperty("socksProxyHost", connection.getProxyHost());
@@ -383,38 +427,46 @@ public class SessionImpl implements Session {
sessionId = callbackClient.getSessionId();
sessionState = SessionState.CONNECTED;
- logger.info("Connected to MAGE server at " + connection.getHost() + ':' + connection.getPort());
+ logger.info("Connect: DONE");
return true;
}
- });
+ };
+
+ boolean result;
+ try {
+ result = doRemoteWorkAndHandleErrors(lastRemotingTask);
+ } finally {
+ lastRemotingTask = null;
+ }
+
if (result) {
return true;
+ } else {
+ disconnect(false);
+ return false;
}
- disconnect(false);
- return false;
}
private void updateDatabase(boolean forceDBComparison, ServerState serverState) {
- long cardDBVersion = CardRepository.instance.getContentVersionFromDB();
- if (forceDBComparison || serverState.getCardsContentVersion() > cardDBVersion) {
- List classNames = CardRepository.instance.getClassNames();
- List cards = server.getMissingCardsData(classNames);
- CardRepository.instance.addCards(cards);
- CardRepository.instance.setContentVersion(serverState.getCardsContentVersion());
- logger.info("Updating client cards DB - existing cards: " + classNames.size() + " new cards: " + cards.size()
- + " content versions - server: " + serverState.getCardsContentVersion() + " client: " + cardDBVersion);
- }
+ // download NEW cards/sets, but do not download data fixes (it's an old and rare feature from old clients, e.g. one client for different servers with different cards)
+ // use case: server gets new minor version with new cards, old client can get that cards too without donwload new version
+ // sets
long expansionDBVersion = ExpansionRepository.instance.getContentVersionFromDB();
if (forceDBComparison || serverState.getExpansionsContentVersion() > expansionDBVersion) {
List setCodes = ExpansionRepository.instance.getSetCodes();
List expansions = server.getMissingExpansionData(setCodes);
- for (ExpansionInfo expansion : expansions) {
- ExpansionRepository.instance.add(expansion);
- }
- ExpansionRepository.instance.setContentVersion(serverState.getExpansionsContentVersion());
- logger.info("Updating client expansions DB - existing sets: " + setCodes.size() + " new sets: " + expansions.size()
- + " content versions - server: " + serverState.getExpansionsContentVersion() + " client: " + expansionDBVersion);
+ logger.info("DB: updating sets... Found new: " + expansions.size());
+ ExpansionRepository.instance.saveSets(expansions, null, serverState.getExpansionsContentVersion());
+ }
+
+ // cards
+ long cardDBVersion = CardRepository.instance.getContentVersionFromDB();
+ if (forceDBComparison || serverState.getCardsContentVersion() > cardDBVersion) {
+ List classNames = CardRepository.instance.getClassNames();
+ List cards = server.getMissingCardsData(classNames);
+ logger.info("DB: updating cards... Found new: " + cards.size());
+ CardRepository.instance.saveCards(cards, serverState.getCardsContentVersion());
}
}
@@ -442,7 +494,7 @@ public class SessionImpl implements Session {
t = t.getCause();
}
- client.showMessage("Unable to connect to server. " + message);
+ client.showMessage("Unable connect to server. " + message);
if (logger.isTraceEnabled()) {
logger.trace("StackTrace", t);
}
@@ -450,12 +502,12 @@ public class SessionImpl implements Session {
/**
* @param askForReconnect - true = connection was lost because of error and
- * ask the user if he want to try to reconnect
+ * ask the user if he want to try to reconnect
*/
@Override
public synchronized void disconnect(boolean askForReconnect) {
if (isConnected()) {
- logger.info("DISCONNECT (still connected)");
+ logger.info("Disconnecting...");
sessionState = SessionState.DISCONNECTING;
}
if (connection == null || sessionState == SessionState.DISCONNECTED) {
@@ -463,18 +515,20 @@ public class SessionImpl implements Session {
}
try {
- callbackClient.removeListener(callbackHandler);
- callbackClient.disconnect();
+ if (callbackClient.isConnected()) {
+ callbackClient.removeListener(callbackHandler);
+ callbackClient.disconnect();
+ }
TransporterClient.destroyTransporterClient(server);
} catch (Throwable ex) {
- logger.fatal("Error disconnecting ...", ex);
+ logger.fatal("Disconnecting FAIL", ex);
}
if (sessionState == SessionState.DISCONNECTING || sessionState == SessionState.CONNECTING) {
sessionState = SessionState.DISCONNECTED;
- logger.info("Disconnected ... ");
+ logger.info("Disconnecting DONE");
if (askForReconnect) {
- client.showError("Network error. You have been disconnected from " + connection.getHost());
+ client.showError("Network error. You have been disconnected from " + connection.getHost());
}
client.disconnected(askForReconnect); // MageFrame with check to reconnect
pingTime.clear();
@@ -483,7 +537,6 @@ public class SessionImpl implements Session {
@Override
public synchronized void reconnect(Throwable throwable) {
- logger.info("RECONNECT - Connected: " + isConnected());
client.disconnected(true);
}
@@ -514,7 +567,7 @@ public class SessionImpl implements Session {
@Override
public void handleConnectionException(Throwable throwable, Client client) {
- logger.info("connection to server lost - " + throwable.getMessage(), throwable);
+ logger.info("Connect: lost connection to server.", throwable);
reconnect(throwable);
}
}
@@ -1530,10 +1583,24 @@ public class SessionImpl implements Session {
}
private void handleThrowable(Throwable t) {
- logger.fatal("Communication error", t);
- // Probably this can cause hanging the client under certain circumstances as the disconnect method is synchronized
- // so check if it's needed
- // disconnect(true);
+
+ // ignore interrupted exceptions -- it's connection problem or user's close
+ if (t instanceof InterruptedException) {
+ //logger.error("Connection error: was interrupted", t);
+ Thread.currentThread().interrupt();
+ return;
+ }
+
+ if (t instanceof RuntimeException) {
+ RuntimeException re = (RuntimeException) t;
+ if (t.getCause() instanceof InterruptedException) {
+ //logger.error("Connection error: was interrupted by runtime exception", t.getCause());
+ Thread.currentThread().interrupt();
+ return;
+ }
+ }
+
+ logger.fatal("Connection error: other", t);
}
private void handleMageException(MageException ex) {
@@ -1580,7 +1647,7 @@ public class SessionImpl implements Session {
@Override
public boolean ping() {
try {
- if (isConnected()) {
+ if (isConnected() && sessionId != null) {
long startTime = System.nanoTime();
if (!server.ping(sessionId, pingInfo)) {
logger.error("Ping failed: " + this.getUserName() + " Session: " + sessionId + " to MAGE server at " + connection.getHost() + ':' + connection.getPort());
@@ -1628,6 +1695,15 @@ public class SessionImpl implements Session {
this.jsonLogActive = jsonLogActive;
}
+ private void setLastError(String error) {
+ lastError = error;
+ }
+
+ @Override
+ public String getLastError() {
+ return lastError;
+ }
+
}
class MageAuthenticator extends Authenticator {
diff --git a/Mage.Common/src/main/java/mage/remote/interfaces/Connect.java b/Mage.Common/src/main/java/mage/remote/interfaces/Connect.java
index 747f5e63c5..4795578ffc 100644
--- a/Mage.Common/src/main/java/mage/remote/interfaces/Connect.java
+++ b/Mage.Common/src/main/java/mage/remote/interfaces/Connect.java
@@ -1,4 +1,3 @@
-
package mage.remote.interfaces;
import mage.remote.Connection;
@@ -37,10 +36,12 @@ public interface Connect {
boolean muteUserChat(String userName, long durationMinute);
boolean setActivation(String userName, boolean active);
-
+
boolean toggleActivation(String userName);
boolean lockUser(String userName, long durationMinute);
String getSessionId();
+
+ String getLastError();
}
diff --git a/Mage.Common/src/main/java/mage/utils/CardUtil.java b/Mage.Common/src/main/java/mage/utils/CardColorUtil.java
similarity index 98%
rename from Mage.Common/src/main/java/mage/utils/CardUtil.java
rename to Mage.Common/src/main/java/mage/utils/CardColorUtil.java
index ccd928f988..8556bfd698 100644
--- a/Mage.Common/src/main/java/mage/utils/CardUtil.java
+++ b/Mage.Common/src/main/java/mage/utils/CardColorUtil.java
@@ -13,7 +13,7 @@ import java.util.List;
* @version 0.1 02.11.2010
* @author nantuko
*/
-public final class CardUtil {
+public final class CardColorUtil {
private static final String regexBlack = ".*\\x7b.{0,2}B.{0,2}\\x7d.*";
private static final String regexBlue = ".*\\x7b.{0,2}U.{0,2}\\x7d.*";
diff --git a/Mage.Common/src/main/java/mage/utils/DeckBuilder.java b/Mage.Common/src/main/java/mage/utils/DeckBuilder.java
index 74615c9bff..6d599f935c 100644
--- a/Mage.Common/src/main/java/mage/utils/DeckBuilder.java
+++ b/Mage.Common/src/main/java/mage/utils/DeckBuilder.java
@@ -164,10 +164,7 @@ public final class DeckBuilder {
}
}
if (count > 0) {
- Integer typeCount = colorCount.get(symbol);
- if (typeCount == null) {
- typeCount = 0;
- }
+ Integer typeCount = colorCount.getOrDefault(symbol, 0);
typeCount += 1;
colorCount.put(symbol, typeCount);
}
@@ -243,9 +240,9 @@ public final class DeckBuilder {
int type;
if (card.isCreature()) {
type = 10;
- } else if (card.getSubtype(null).contains(SubType.EQUIPMENT)) {
+ } else if (card.hasSubtype(SubType.EQUIPMENT, null)) {
type = 8;
- } else if (card.getSubtype(null).contains(SubType.AURA)) {
+ } else if (card.hasSubtype(SubType.AURA, null)) {
type = 5;
} else if (card.isInstant()) {
type = 7;
@@ -283,10 +280,7 @@ public final class DeckBuilder {
multicolor += 1;
colors.add(symbol);
}
- Integer typeCount = singleCount.get(symbol);
- if (typeCount == null) {
- typeCount = 0;
- }
+ Integer typeCount = singleCount.getOrDefault(symbol, 0);
typeCount += 1;
singleCount.put(symbol, typeCount);
maxSingleCount = Math.max(maxSingleCount, typeCount);
diff --git a/Mage.Common/src/main/java/mage/utils/MageVersion.java b/Mage.Common/src/main/java/mage/utils/MageVersion.java
index 4a0feb9728..d2283709cf 100644
--- a/Mage.Common/src/main/java/mage/utils/MageVersion.java
+++ b/Mage.Common/src/main/java/mage/utils/MageVersion.java
@@ -1,35 +1,43 @@
package mage.utils;
+import mage.util.JarVersion;
+
import java.io.Serializable;
/**
- *
* @author BetaSteward_at_googlemail.com
*/
public class MageVersion implements Serializable, Comparable {
- /**
- *
- */
- public final static int MAGE_VERSION_MAJOR = 1;
- public final static int MAGE_VERSION_MINOR = 4;
- public final static int MAGE_VERSION_PATCH = 31;
- public final static String MAGE_VERSION_MINOR_PATCH = "V4";
- public final static String MAGE_VERSION_INFO = "";
+ public static final int MAGE_VERSION_MAJOR = 1;
+ public static final int MAGE_VERSION_MINOR = 4;
+ public static final int MAGE_VERSION_PATCH = 35;
+ public static final String MAGE_EDITION_INFO = ""; // set "-beta" for 1.4.32-betaV0
+ public static final String MAGE_VERSION_MINOR_PATCH = "V1"; // default
+ // strict mode
+ private static final boolean MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME = false; // set true on uncompatible github changes, set false after new major release (after MAGE_VERSION_PATCH changes)
+ public static final boolean MAGE_VERSION_SHOW_BUILD_TIME = true;
private final int major;
private final int minor;
private final int patch;
private final String minorPatch; // doesn't matter for compatibility
+ private final String buildTime;
+ private String editionInfo;
- private String info = "";
+ public MageVersion(Class sourceClass) {
+ this(MAGE_VERSION_MAJOR, MAGE_VERSION_MINOR, MAGE_VERSION_PATCH, MAGE_VERSION_MINOR_PATCH, MAGE_EDITION_INFO, sourceClass);
+ }
- public MageVersion(int major, int minor, int patch, String minorPatch, String info) {
+ public MageVersion(int major, int minor, int patch, String minorPatch, String editionInfo, Class sourceClass) {
this.major = major;
this.minor = minor;
this.patch = patch;
this.minorPatch = minorPatch;
- this.info = info;
+ this.editionInfo = editionInfo;
+
+ // build time
+ this.buildTime = JarVersion.getBuildTime(sourceClass);
}
public int getMajor() {
@@ -48,9 +56,18 @@ public class MageVersion implements Serializable, Comparable {
return minorPatch;
}
+ public String toString(boolean showBuildTime) {
+ // 1.4.32-betaV0 (build: time)
+ String res = major + "." + minor + '.' + patch + editionInfo + minorPatch;
+ if (showBuildTime && !this.buildTime.isEmpty()) {
+ res += " (build: " + this.buildTime + ")";
+ }
+ return res;
+ }
+
@Override
public String toString() {
- return major + "." + minor + '.' + patch + info + minorPatch;
+ return toString(MAGE_VERSION_SHOW_BUILD_TIME);
}
@Override
@@ -64,7 +81,9 @@ public class MageVersion implements Serializable, Comparable {
if (patch != o.patch) {
return patch - o.patch;
}
- return info.compareTo(o.info);
+ if (MAGE_VERSION_MINOR_PATCH_MUST_BE_SAME && !minorPatch.equals(o.minorPatch)) {
+ return minorPatch.compareTo(o.minorPatch);
+ }
+ return editionInfo.compareTo(o.editionInfo);
}
-
}
diff --git a/Mage.Common/src/main/java/mage/utils/properties/PropertiesUtil.java b/Mage.Common/src/main/java/mage/utils/properties/PropertiesUtil.java
index 0a220e12fd..17fe10e05b 100644
--- a/Mage.Common/src/main/java/mage/utils/properties/PropertiesUtil.java
+++ b/Mage.Common/src/main/java/mage/utils/properties/PropertiesUtil.java
@@ -21,7 +21,11 @@ public final class PropertiesUtil {
static {
try (InputStream in = PropertiesUtil.class.getResourceAsStream("/xmage.properties")) {
- properties.load(in);
+ if(in != null) {
+ properties.load(in);
+ } else {
+ logger.warn("No xmage.properties were found");
+ }
} catch (FileNotFoundException fnfe) {
logger.warn("No xmage.properties were found on classpath");
} catch (IOException e) {
diff --git a/Mage.Common/src/main/java/mage/view/CardView.java b/Mage.Common/src/main/java/mage/view/CardView.java
index cdd9b22e66..8505589a47 100644
--- a/Mage.Common/src/main/java/mage/view/CardView.java
+++ b/Mage.Common/src/main/java/mage/view/CardView.java
@@ -1,9 +1,6 @@
-
package mage.view;
import com.google.gson.annotations.Expose;
-import java.util.*;
-import java.util.stream.Collectors;
import mage.MageObject;
import mage.ObjectColor;
import mage.abilities.Abilities;
@@ -29,6 +26,9 @@ import mage.target.Target;
import mage.target.Targets;
import mage.util.SubTypeList;
+import java.util.*;
+import java.util.stream.Collectors;
+
/**
* @author BetaSteward_at_googlemail.com
*/
@@ -108,8 +108,11 @@ public class CardView extends SimpleCardView {
protected boolean isChoosable;
protected boolean selected;
protected boolean canAttack;
+ protected boolean canBlock;
protected boolean inViewerOnly;
+ protected Card originalCard = null;
+
public CardView(Card card) {
this(card, null, false);
}
@@ -126,6 +129,7 @@ public class CardView extends SimpleCardView {
public CardView(CardView cardView) {
super(cardView.id, cardView.expansionSetCode, cardView.cardNumber, cardView.usesVariousArt, cardView.tokenSetCode, cardView.gameObject, cardView.tokenDescriptor);
+ this.originalCard = cardView.originalCard;
this.id = UUID.randomUUID();
this.parentId = cardView.parentId;
@@ -198,15 +202,17 @@ public class CardView extends SimpleCardView {
this.isChoosable = cardView.isChoosable;
this.selected = cardView.selected;
this.canAttack = cardView.canAttack;
+ this.canBlock = cardView.canBlock;
this.inViewerOnly = cardView.inViewerOnly;
+ this.originalCard = cardView.originalCard.copy();
}
/**
* @param card
* @param game
* @param controlled is the card view created for the card controller - used
- * for morph / face down cards to know which player may see information for
- * the card
+ * for morph / face down cards to know which player may see information for
+ * the card
*/
public CardView(Card card, Game game, boolean controlled) {
this(card, game, controlled, false, false);
@@ -232,15 +238,17 @@ public class CardView extends SimpleCardView {
/**
* @param card
* @param game
- * @param controlled is the card view created for the card controller - used
- * for morph / face down cards to know which player may see information for
- * the card
+ * @param controlled is the card view created for the card controller - used
+ * for morph / face down cards to know which player may see information for
+ * the card
* @param showFaceDownCard if true and the card is not on the battlefield,
- * also a face down card is shown in the view, face down cards will be shown
- * @param storeZone if true the card zone will be set in the zone attribute.
+ * also a face down card is shown in the view, face down cards will be shown
+ * @param storeZone if true the card zone will be set in the zone attribute.
*/
public CardView(Card card, Game game, boolean controlled, boolean showFaceDownCard, boolean storeZone) {
super(card.getId(), card.getExpansionSetCode(), card.getCardNumber(), card.getUsesVariousArt(), card.getTokenSetCode(), game != null, card.getTokenDescriptor());
+ this.originalCard = card;
+
// no information available for face down cards as long it's not a controlled face down morph card
// TODO: Better handle this in Framework (but currently I'm not sure how to do it there) LevelX2
boolean showFaceUp = true;
@@ -279,7 +287,7 @@ public class CardView extends SimpleCardView {
this.power = Integer.toString(card.getPower().getValue());
this.toughness = Integer.toString(card.getToughness().getValue());
this.cardTypes = card.getCardType();
- this.faceDown = ((Permanent) card).isFaceDown(game);
+ this.faceDown = card.isFaceDown(game);
} else {
// this.hideInfo = true;
return;
@@ -289,9 +297,9 @@ public class CardView extends SimpleCardView {
SplitCard splitCard = null;
if (card.isSplitCard()) {
splitCard = (SplitCard) card;
- rotate = (((SplitCard) card).getSpellAbility().getSpellAbilityType()) != SpellAbilityType.SPLIT_AFTERMATH;
+ rotate = (card.getSpellAbility().getSpellAbilityType()) != SpellAbilityType.SPLIT_AFTERMATH;
} else if (card instanceof Spell) {
- switch (((Spell) card).getSpellAbility().getSpellAbilityType()) {
+ switch (card.getSpellAbility().getSpellAbilityType()) {
case SPLIT_FUSED:
splitCard = (SplitCard) ((Spell) card).getCard();
rotate = true;
@@ -383,12 +391,12 @@ public class CardView extends SimpleCardView {
this.cardNumber = ((PermanentToken) card).getToken().getOriginalCardNumber();
} else {
// a created token
- this.expansionSetCode = ((PermanentToken) card).getExpansionSetCode();
- this.tokenDescriptor = ((PermanentToken) card).getTokenDescriptor();
+ this.expansionSetCode = card.getExpansionSetCode();
+ this.tokenDescriptor = card.getTokenDescriptor();
}
//
// set code und card number for token copies to get the image
- this.rules = ((PermanentToken) card).getRules(game);
+ this.rules = card.getRules(game);
this.type = ((PermanentToken) card).getToken().getTokenType();
} else {
this.rarity = card.getRarity();
@@ -460,10 +468,14 @@ public class CardView extends SimpleCardView {
// Get starting loyalty
this.startingLoyalty = "" + card.getStartingLoyalty();
+
+
}
public CardView(MageObject object) {
super(object.getId(), "", "0", false, "", true, "");
+ this.originalCard = null;
+
this.name = object.getName();
this.displayName = object.getName();
if (object instanceof Permanent) {
@@ -984,6 +996,14 @@ public class CardView extends SimpleCardView {
this.canAttack = canAttack;
}
+ public boolean isCanBlock() {
+ return canBlock;
+ }
+
+ public void setCanBlock(boolean canBlock) {
+ this.canBlock = canBlock;
+ }
+
public boolean isCreature() {
return cardTypes.contains(CardType.CREATURE);
}
@@ -1046,4 +1066,8 @@ public class CardView extends SimpleCardView {
public boolean inViewerOnly() {
return inViewerOnly;
}
+
+ public Card getOriginalCard() {
+ return this.originalCard;
+ }
}
diff --git a/Mage.Common/src/main/java/mage/view/DraftClientMessage.java b/Mage.Common/src/main/java/mage/view/DraftClientMessage.java
index 33bf75f212..dfaaf2bc22 100644
--- a/Mage.Common/src/main/java/mage/view/DraftClientMessage.java
+++ b/Mage.Common/src/main/java/mage/view/DraftClientMessage.java
@@ -13,21 +13,12 @@ public class DraftClientMessage implements Serializable {
private DraftView draftView;
private DraftPickView draftPickView;
- private String message;
- public DraftClientMessage(DraftView draftView) {
+ public DraftClientMessage(DraftView draftView, DraftPickView draftPickView) {
this.draftView = draftView;
- }
-
- public DraftClientMessage(DraftPickView draftPickView) {
this.draftPickView = draftPickView;
}
- public DraftClientMessage(DraftView draftView, String message) {
- this.message = message;
- this.draftView = draftView;
- }
-
public DraftPickView getDraftPickView() {
return draftPickView;
}
diff --git a/Mage.Common/src/main/java/mage/view/DraftView.java b/Mage.Common/src/main/java/mage/view/DraftView.java
index 241c6a840f..b1f2b960a4 100644
--- a/Mage.Common/src/main/java/mage/view/DraftView.java
+++ b/Mage.Common/src/main/java/mage/view/DraftView.java
@@ -7,6 +7,7 @@ import java.util.ArrayList;
import java.util.List;
import mage.cards.ExpansionSet;
import mage.game.draft.Draft;
+import mage.game.draft.DraftCube;
import mage.game.draft.DraftPlayer;
/**
@@ -17,6 +18,7 @@ public class DraftView implements Serializable {
private static final long serialVersionUID = 1L;
private final List sets = new ArrayList<>();
+ private final List setCodes = new ArrayList<>();
private final int boosterNum;
private final int cardNum;
private final List players = new ArrayList<>();
@@ -24,11 +26,14 @@ public class DraftView implements Serializable {
public DraftView(Draft draft) {
if (draft.getDraftCube() != null) {
for (int i = 0; i < draft.getNumberBoosters(); i++) {
- sets.add(draft.getDraftCube().getName());
+ DraftCube cube = draft.getDraftCube();
+ sets.add(cube.getName());
+ setCodes.add(cube.getCode());
}
} else {
for (ExpansionSet set: draft.getSets()) {
sets.add(set.getName());
+ setCodes.add(set.getCode());
}
}
this.boosterNum = draft.getBoosterNum();
@@ -42,6 +47,10 @@ public class DraftView implements Serializable {
return sets;
}
+ public List getSetCodes() {
+ return setCodes;
+ }
+
public List getPlayers() {
return players;
}
diff --git a/Mage.Common/src/main/java/mage/view/GameView.java b/Mage.Common/src/main/java/mage/view/GameView.java
index ffc10011df..f0466f6950 100644
--- a/Mage.Common/src/main/java/mage/view/GameView.java
+++ b/Mage.Common/src/main/java/mage/view/GameView.java
@@ -1,14 +1,7 @@
-
package mage.view;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.UUID;
import mage.MageObject;
import mage.abilities.costs.Cost;
import mage.cards.Card;
@@ -32,8 +25,10 @@ import mage.players.Player;
import mage.watchers.common.CastSpellLastTurnWatcher;
import org.apache.log4j.Logger;
+import java.io.Serializable;
+import java.util.*;
+
/**
- *
* @author BetaSteward_at_googlemail.com
*/
public class GameView implements Serializable {
@@ -90,7 +85,7 @@ public class GameView implements Serializable {
if (object != null) {
if (object instanceof Permanent) {
boolean controlled = ((Permanent) object).getControllerId().equals(createdForPlayerId);
- stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, ((Permanent) object).getName(), new CardView(((Permanent) object), game, controlled, false, false)));
+ stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, object.getName(), new CardView(((Permanent) object), game, controlled, false, false)));
} else {
stack.put(stackObject.getId(), new StackAbilityView(game, (StackAbility) stackObject, card.getName(), new CardView(card, game, false, false, false)));
}
@@ -109,14 +104,14 @@ public class GameView implements Serializable {
} else if (object instanceof Emblem) {
CardView cardView = new CardView(new EmblemView((Emblem) object));
// Card sourceCard = (Card) ((Emblem) object).getSourceObject();
- ((StackAbility) stackObject).setName(((Emblem) object).getName());
+ stackObject.setName(object.getName());
// ((StackAbility) stackObject).setExpansionSetCode(sourceCard.getExpansionSetCode());
stack.put(stackObject.getId(),
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView));
checkPaid(stackObject.getId(), ((StackAbility) stackObject));
} else if (object instanceof Plane) {
CardView cardView = new CardView(new PlaneView((Plane) object));
- ((StackAbility) stackObject).setName(((Plane) object).getName());
+ stackObject.setName(object.getName());
stack.put(stackObject.getId(),
new StackAbilityView(game, (StackAbility) stackObject, object.getName(), cardView));
checkPaid(stackObject.getId(), ((StackAbility) stackObject));
@@ -131,7 +126,7 @@ public class GameView implements Serializable {
} else if (object instanceof StackAbility) {
StackAbility stackAbility = ((StackAbility) object);
stackAbility.newId();
- stack.put(stackObject.getId(), new CardView(((StackAbility) stackObject)));
+ stack.put(stackObject.getId(), new CardView(stackObject));
checkPaid(stackObject.getId(), ((StackAbility) stackObject));
} else {
LOGGER.fatal("Object can't be cast to StackAbility: " + object.getName() + ' ' + object.toString() + ' ' + object.getClass().toString());
@@ -182,7 +177,7 @@ public class GameView implements Serializable {
this.special = false;
}
- CastSpellLastTurnWatcher watcher = (CastSpellLastTurnWatcher) game.getState().getWatchers().get(CastSpellLastTurnWatcher.class.getSimpleName());
+ CastSpellLastTurnWatcher watcher = game.getState().getWatcher(CastSpellLastTurnWatcher.class);
if (watcher != null) {
spellsCastCurrentTurn = watcher.getAmountOfSpellsAllPlayersCastOnCurrentTurn();
} else {
diff --git a/Mage.Common/src/main/java/mage/view/TableView.java b/Mage.Common/src/main/java/mage/view/TableView.java
index c3d748c344..efd42d3239 100644
--- a/Mage.Common/src/main/java/mage/view/TableView.java
+++ b/Mage.Common/src/main/java/mage/view/TableView.java
@@ -1,11 +1,5 @@
-
package mage.view;
-import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.UUID;
import mage.constants.SkillLevel;
import mage.constants.TableState;
import mage.game.Game;
@@ -15,6 +9,12 @@ import mage.game.draft.Draft;
import mage.game.match.MatchPlayer;
import mage.game.tournament.TournamentPlayer;
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.UUID;
+
/**
* @author BetaSteward_at_googlemail.com
*/
@@ -32,10 +32,12 @@ public class TableView implements Serializable {
private TableState tableState;
private final SkillLevel skillLevel;
private final String tableStateText;
+ private final String seatsInfo;
private boolean isTournament;
private List seats = new ArrayList<>();
private List games = new ArrayList<>();
private final String quitRatio;
+ private final String minimumRating;
private final boolean limited;
private final boolean rated;
private final boolean passworded;
@@ -45,10 +47,6 @@ public class TableView implements Serializable {
this.tableId = table.getId();
this.gameType = table.getGameType();
this.tableName = table.getName();
- String tableNameInfo = null;
- if (tableName != null && !tableName.isEmpty()) {
- tableNameInfo = " [" + table.getName() + ']';
- }
this.controllerName = table.getControllerName();
this.tableState = table.getState();
if (table.getState() == TableState.WAITING
@@ -68,8 +66,9 @@ public class TableView implements Serializable {
}
if (!table.isTournament()) {
// MATCH
+ seatsInfo = "" + table.getMatch().getPlayers().size() + '/' + table.getSeats().length;
if (table.getState() == TableState.WAITING || table.getState() == TableState.READY_TO_START) {
- tableStateText = table.getState().toString() + " (" + table.getMatch().getPlayers().size() + '/' + table.getSeats().length + ')';
+ tableStateText = table.getState().toString() + " (" + seatsInfo + ')';
} else {
tableStateText = table.getState().toString();
}
@@ -93,7 +92,7 @@ public class TableView implements Serializable {
sbScore.append(" Draws: ").append(table.getMatch().getDraws());
}
this.controllerName += sb.toString();
- this.deckType = table.getDeckType() + (tableNameInfo != null ? tableNameInfo : "");
+ this.deckType = table.getDeckType();
StringBuilder addInfo = new StringBuilder();
if (table.getMatch().getGames().isEmpty()) {
addInfo.append("Wins:").append(table.getMatch().getWinsNeeded());
@@ -111,6 +110,7 @@ public class TableView implements Serializable {
this.additionalInfo = addInfo.toString();
this.skillLevel = table.getMatch().getOptions().getSkillLevel();
this.quitRatio = Integer.toString(table.getMatch().getOptions().getQuitRatio());
+ this.minimumRating = Integer.toString(table.getMatch().getOptions().getMinimumRating());
this.limited = table.getMatch().getOptions().isLimited();
this.rated = table.getMatch().getOptions().isRated();
this.passworded = !table.getMatch().getOptions().getPassword().isEmpty();
@@ -127,10 +127,11 @@ public class TableView implements Serializable {
}
}
this.controllerName += sb1.toString();
+ this.seatsInfo = "" + table.getTournament().getPlayers().size() + "/" + table.getNumberOfSeats();
StringBuilder infoText = new StringBuilder();
StringBuilder stateText = new StringBuilder(table.getState().toString());
infoText.append("Wins:").append(table.getTournament().getOptions().getMatchOptions().getWinsNeeded());
- infoText.append(" Seats: ").append(table.getTournament().getPlayers().size()).append('/').append(table.getNumberOfSeats());
+ infoText.append(" Seats: ").append(this.seatsInfo);
switch (table.getState()) {
case WAITING:
stateText.append(" (").append(table.getTournament().getPlayers().size()).append('/').append(table.getNumberOfSeats()).append(')');
@@ -156,9 +157,10 @@ public class TableView implements Serializable {
}
this.additionalInfo = infoText.toString();
this.tableStateText = stateText.toString();
- this.deckType = table.getDeckType() + ' ' + table.getTournament().getBoosterInfo() + (tableNameInfo != null ? tableNameInfo : "");
+ this.deckType = table.getDeckType() + ' ' + table.getTournament().getBoosterInfo();
this.skillLevel = table.getTournament().getOptions().getMatchOptions().getSkillLevel();
this.quitRatio = Integer.toString(table.getTournament().getOptions().getQuitRatio());
+ this.minimumRating = Integer.toString(table.getTournament().getOptions().getMinimumRating());
this.limited = table.getTournament().getOptions().getMatchOptions().isLimited();
this.rated = table.getTournament().getOptions().getMatchOptions().isRated();
this.passworded = !table.getTournament().getOptions().getPassword().isEmpty();
@@ -177,11 +179,11 @@ public class TableView implements Serializable {
public String getControllerName() {
return controllerName;
}
-
+
public boolean getSpectatorsAllowed() {
return spectatorsAllowed;
}
-
+
public String getGameType() {
return gameType;
@@ -207,6 +209,10 @@ public class TableView implements Serializable {
return games;
}
+ public String getSeatsInfo() {
+ return seatsInfo;
+ }
+
public boolean isTournament() {
return this.isTournament;
}
@@ -227,6 +233,10 @@ public class TableView implements Serializable {
return quitRatio;
}
+ public String getMinimumRating() {
+ return minimumRating;
+ }
+
public boolean isLimited() {
return limited;
}
diff --git a/Mage.Common/src/main/java/mage/view/TournamentGameView.java b/Mage.Common/src/main/java/mage/view/TournamentGameView.java
index c50662f21e..e2ca098709 100644
--- a/Mage.Common/src/main/java/mage/view/TournamentGameView.java
+++ b/Mage.Common/src/main/java/mage/view/TournamentGameView.java
@@ -43,7 +43,7 @@ public class TournamentGameView implements Serializable {
String duelingTime = "";
if (game.hasEnded()) {
- if (game.getEndTime() != null) {
+ if (game.getEndTime() != null && game.getStartTime() != null) {
duelingTime = " (" + DateFormat.getDuration((game.getEndTime().getTime() - game.getStartTime().getTime())/1000) + ')';
}
this.state = "Finished" + duelingTime;
diff --git a/Mage.Common/src/main/java/mage/view/UserRequestMessage.java b/Mage.Common/src/main/java/mage/view/UserRequestMessage.java
index 71c582b229..51cb913d0e 100644
--- a/Mage.Common/src/main/java/mage/view/UserRequestMessage.java
+++ b/Mage.Common/src/main/java/mage/view/UserRequestMessage.java
@@ -1,19 +1,18 @@
-
package mage.view;
+import mage.constants.PlayerAction;
+
import java.io.Serializable;
import java.util.UUID;
-import mage.constants.PlayerAction;
/**
- *
* @author LevelX2
*/
public class UserRequestMessage implements Serializable {
private static final long serialVersionUID = 1L;
- private final String titel;
+ private final String title;
private final String message;
private UUID relatedUserId;
private String relatedUserName;
@@ -32,8 +31,8 @@ public class UserRequestMessage implements Serializable {
private String button3Text;
private PlayerAction button3Action;
- public UserRequestMessage(String titel, String message) {
- this.titel = titel;
+ public UserRequestMessage(String title, String message) {
+ this.title = title;
this.message = message;
this.button1Action = null;
this.button2Action = null;
@@ -68,8 +67,8 @@ public class UserRequestMessage implements Serializable {
this.button3Action = buttonAction;
}
- public String getTitel() {
- return titel;
+ public String getTitle() {
+ return title;
}
public static long getSerialVersionUID() {
diff --git a/Mage.Plugins/Mage.Counter.Plugin/pom.xml b/Mage.Plugins/Mage.Counter.Plugin/pom.xml
index a8af62ad9d..576fd8497d 100644
--- a/Mage.Plugins/Mage.Counter.Plugin/pom.xml
+++ b/Mage.Plugins/Mage.Counter.Plugin/pom.xml
@@ -7,7 +7,7 @@
org.mage
mage-plugins
- 1.4.31
+ 1.4.35
mage-counter-plugin
@@ -35,8 +35,8 @@
org.apache.maven.plugins
maven-compiler-plugin
- 1.7
- 1.7
+ 1.8
+ 1.8
diff --git a/Mage.Plugins/pom.xml b/Mage.Plugins/pom.xml
index 19abee5bda..98b9b80e55 100644
--- a/Mage.Plugins/pom.xml
+++ b/Mage.Plugins/pom.xml
@@ -7,7 +7,7 @@
org.mage
mage-root
- 1.4.31
+ 1.4.35
mage-plugins
diff --git a/Mage.Server.Console/pom.xml b/Mage.Server.Console/pom.xml
index a48379e45c..ac1e109629 100644
--- a/Mage.Server.Console/pom.xml
+++ b/Mage.Server.Console/pom.xml
@@ -6,10 +6,9 @@
org.mage
mage-root
- 1.4.31
+ 1.4.35
- org.mage
mage.server.console
jar
Mage Server Console
@@ -53,7 +52,6 @@
maven-jar-plugin
- ${manifest.file}
|