diff --git a/.gitignore b/.gitignore index c8e97c13349..227cae92d1f 100644 --- a/.gitignore +++ b/.gitignore @@ -82,6 +82,7 @@ Mage.Server.Plugins/Mage.Draft.8PlayerBooster/target *.classpath *.iml +hs_err*.log /submitted /Mage.Server/config/ai.please.cast.this.txt diff --git a/Mage.Client/pom.xml b/Mage.Client/pom.xml index cb841e6e280..52ff1498269 100644 --- a/Mage.Client/pom.xml +++ b/Mage.Client/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 org.mage diff --git a/Mage.Client/serverlist.txt b/Mage.Client/serverlist.txt index db223b403ff..a4379a04bbd 100644 --- a/Mage.Client/serverlist.txt +++ b/Mage.Client/serverlist.txt @@ -1,6 +1,6 @@ XMage.de 1 (Europe/Germany) fast :xmage.de:17171 woogerworks (North America/USA) :xmage.woogerworks.info:17171 -XMage.info 1 (Europe/France) new network code -> see forum :176.31.186.181:17171 +XMage Testserver (Europe/France) 1.4.8v0 :176.31.186.181:17171 XMage BR (South America/Brazil) :ec2-54-233-67-0.sa-east-1.compute.amazonaws.com:17171 Seedds Server (Asia) :115.29.203.80:17171 localhost -> connect to your local server (must be started):localhost:17171 diff --git a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java index 9840c82582b..2f307ea2f78 100644 --- a/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java +++ b/Mage.Client/src/main/java/mage/client/dialog/TableWaitingDialog.java @@ -26,7 +26,7 @@ * or implied, of BetaSteward_at_googlemail.com. */ -/* + /* * TableWaitingDialog.java * * Created on Dec 16, 2009, 10:27:44 AM @@ -68,7 +68,7 @@ public class TableWaitingDialog extends MageDialog { private Session session; private final TableWaitModel tableWaitModel; private UpdateSeatsTask updateTask; - private static final int[] defaultColumnsWidth = {20, 50, 100, 100}; + private static final int[] defaultColumnsWidth = {20, 50, 100, 100, 100}; /** * Creates new form TableWaitingDialog @@ -268,10 +268,8 @@ public class TableWaitingDialog extends MageDialog { if (session.startMatch(roomId, tableId)) { closeDialog(); } - } else { - if (session.startTournament(roomId, tableId)) { - closeDialog(); - } + } else if (session.startTournament(roomId, tableId)) { + closeDialog(); } }//GEN-LAST:event_btnStartActionPerformed @@ -319,7 +317,7 @@ public class TableWaitingDialog extends MageDialog { class TableWaitModel extends AbstractTableModel { - private final String[] columnNames = new String[]{"Seat", "Loc", "Player Name", "Player Type"}; + private final String[] columnNames = new String[]{"Seat", "Loc", "Player Name", "Player Type", "History"}; private SeatView[] seats = new SeatView[0]; public void loadData(TableView table) { @@ -353,6 +351,8 @@ class TableWaitModel extends AbstractTableModel { return seats[arg0].getPlayerName(); case 3: return seats[arg0].getPlayerType(); + case 4: + return seats[arg0].getHistory(); } } return ""; diff --git a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java index c94ed0dfa14..01e872dc5a7 100644 --- a/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java +++ b/Mage.Client/src/main/java/mage/client/game/PlayerPanelExt.java @@ -26,7 +26,7 @@ * or implied, of BetaSteward_at_googlemail.com. */ -/* + /* * PlayerPanel.java * * Created on Nov 18, 2009, 3:01:31 PM @@ -288,7 +288,8 @@ public class PlayerPanelExt extends javax.swing.JPanel { basicTooltipText = "Name: " + player.getName() + "
Country: " + countryname + "
Deck hash code: " + player.getDeckHashCode() - + "
Wins: " + player.getWins() + " of " + player.getWinsNeeded() + " (to win the match)"; + + "
This match wins: " + player.getWins() + " of " + player.getWinsNeeded() + " (to win the match)" + + (player.getUserData() == null ? "" : "
History: " + player.getUserData().getHistory()); } // Extend tooltip StringBuilder tooltipText = new StringBuilder(basicTooltipText); diff --git a/Mage.Client/src/main/java/mage/client/table/PlayersChatPanel.java b/Mage.Client/src/main/java/mage/client/table/PlayersChatPanel.java index 0aa6e20af9c..795a8618cdf 100644 --- a/Mage.Client/src/main/java/mage/client/table/PlayersChatPanel.java +++ b/Mage.Client/src/main/java/mage/client/table/PlayersChatPanel.java @@ -26,7 +26,7 @@ * or implied, of BetaSteward_at_googlemail.com. */ -/* + /* * ChatPanel.java * * Created on 15-Dec-2009, 11:04:31 PM @@ -61,7 +61,7 @@ public class PlayersChatPanel extends javax.swing.JPanel { private final List players = new ArrayList<>(); private final UserTableModel userTableModel; - private static final int[] defaultColumnsWidth = {20, 100, 100, 100, 80, 80}; + private static final int[] DEFAULT_COLUMNS_WIDTH = {20, 100, 100, 80, 80}; /* @@ -78,7 +78,7 @@ public class PlayersChatPanel extends javax.swing.JPanel { jTablePlayers.setForeground(Color.white); jTablePlayers.setRowSorter(new MageTableRowSorter(userTableModel)); - TableUtil.setColumnWidthAndOrder(jTablePlayers, defaultColumnsWidth, KEY_USERS_COLUMNS_WIDTH, KEY_USERS_COLUMNS_ORDER); + TableUtil.setColumnWidthAndOrder(jTablePlayers, DEFAULT_COLUMNS_WIDTH, KEY_USERS_COLUMNS_WIDTH, KEY_USERS_COLUMNS_ORDER); jTablePlayers.setDefaultRenderer(Icon.class, new CountryCellRenderer()); jScrollPaneTalk.setSystemMessagesPane(colorPaneSystem); @@ -118,7 +118,7 @@ public class PlayersChatPanel extends javax.swing.JPanel { class UserTableModel extends AbstractTableModel { - private final String[] columnNames = new String[]{"Loc", "Players", "History", "Info", "Games", "Connection"}; + private final String[] columnNames = new String[]{"Loc", "Players", "History", "Games", "Connection"}; private UsersView[] players = new UsersView[0]; public void loadData(Collection roomUserInfoList) throws MageRemoteException { @@ -128,7 +128,7 @@ public class PlayersChatPanel extends javax.swing.JPanel { TableColumnModel tcm = th.getColumnModel(); tcm.getColumn(jTablePlayers.convertColumnIndexToView(1)).setHeaderValue("Players (" + this.players.length + ")"); - tcm.getColumn(jTablePlayers.convertColumnIndexToView(4)).setHeaderValue( + tcm.getColumn(jTablePlayers.convertColumnIndexToView(3)).setHeaderValue( "Games " + roomUserInfo.getNumberActiveGames() + (roomUserInfo.getNumberActiveGames() != roomUserInfo.getNumberGameThreads() ? " (T:" + roomUserInfo.getNumberGameThreads() : " (") + " limit: " + roomUserInfo.getNumberMaxGames() + ")"); @@ -156,10 +156,8 @@ public class PlayersChatPanel extends javax.swing.JPanel { case 2: return players[arg0].getHistory(); case 3: - return players[arg0].getInfoState(); - case 4: return players[arg0].getInfoGames(); - case 5: + case 4: return players[arg0].getInfoPing(); } return ""; 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 42b67cf7a57..32462dfa601 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,12 +1,10 @@ /** * 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; @@ -21,25 +19,26 @@ import org.mage.plugins.card.dl.beans.properties.Property; import org.mage.plugins.card.dl.lm.AbstractLaternaBean; import org.mage.plugins.card.utils.CardImageUtils; - /** * The class DownloadJob. - * + * * @version V0.0 25.08.2010 * @author Clemens Koza */ public class DownloadJob extends AbstractLaternaBean { + public static enum State { + NEW, WORKING, FINISHED, ABORTED; } - private final String name; - private final Source source; - private final Destination destination; - private final Property state = properties.property("state", State.NEW); - private final Property message = properties.property("message"); - private final Property error = properties.property("error"); - private final BoundedRangeModel progress = new DefaultBoundedRangeModel(); + private final String name; + private final Source source; + private final Destination destination; + private final Property state = properties.property("state", State.NEW); + private final Property message = properties.property("message"); + private final Property error = properties.property("error"); + private final BoundedRangeModel progress = new DefaultBoundedRangeModel(); public DownloadJob(String name, Source source, Destination destination) { this.name = name; @@ -48,7 +47,9 @@ public class DownloadJob extends AbstractLaternaBean { } /** - * Sets the job's state. If the state is {@link State#ABORTED}, it instead sets the error to "ABORTED" + * Sets the job's state. If the state is {@link State#ABORTED}, it instead + * sets the error to "ABORTED" + * * @param state */ public void setState(State state) { @@ -60,8 +61,9 @@ public class DownloadJob extends AbstractLaternaBean { } /** - * Sets the job's state to {@link State#ABORTED} and the error message to the given message. Logs a warning - * with the given message. + * Sets the job's state to {@link State#ABORTED} and the error message to + * the given message. Logs a warning with the given message. + * * @param message */ public void setError(String message) { @@ -69,8 +71,9 @@ public class DownloadJob extends AbstractLaternaBean { } /** - * Sets the job's state to {@link State#ABORTED} and the error to the given exception. Logs a warning with the - * given exception. + * Sets the job's state to {@link State#ABORTED} and the error to the given + * exception. Logs a warning with the given exception. + * * @param error */ public void setError(Exception error) { @@ -78,14 +81,15 @@ public class DownloadJob extends AbstractLaternaBean { } /** - * Sets the job's state to {@link State#ABORTED} and the error to the given exception. Logs a warning with the - * given message and exception. + * Sets the job's state to {@link State#ABORTED} and the error to the given + * exception. Logs a warning with the given message and exception. + * * @param message * @param error */ public void setError(String message, Exception error) { if (message == null) { - + message = "Download of " + this.getName() + "from " + this.getSource().toString() + " caused error: " + error.toString(); } // log.warn(message, error); @@ -97,6 +101,7 @@ public class DownloadJob extends AbstractLaternaBean { /** * Sets the job's message. + * * @param message */ public void setMessage(String message) { @@ -119,7 +124,6 @@ public class DownloadJob extends AbstractLaternaBean { return message.getValue(); } - public String getName() { return name; } @@ -163,9 +167,9 @@ public class DownloadJob extends AbstractLaternaBean { @Override public String toString() { - return proxy != null ? proxy.type().toString()+" " :"" + url; + return proxy != null ? proxy.type().toString() + " " : "" + url; } - + }; } @@ -189,11 +193,11 @@ public class DownloadJob extends AbstractLaternaBean { public int length() throws IOException { return getConnection().getContentLength(); } - + @Override public String toString() { - return proxy != null ? proxy.type().toString()+" " :"" + url; - } + return proxy != null ? proxy.type().toString() + " " : "" + url; + } }; } @@ -213,6 +217,14 @@ public class DownloadJob extends AbstractLaternaBean { return new FileOutputStream(file); } + @Override + public boolean isValid() throws IOException { + if (file.isFile()) { + return file.length() > 0; + } + return false; + } + @Override public boolean exists() { return file.isFile(); @@ -228,16 +240,20 @@ public class DownloadJob extends AbstractLaternaBean { } public interface Source { + InputStream open() throws IOException; int length() throws IOException; } public interface Destination { + OutputStream open() throws IOException; boolean exists() throws IOException; + boolean isValid() throws IOException; + void delete() throws IOException; } } 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 1ff78368e1f..ca5160a71db 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,9 +1,8 @@ /** * Downloader.java - * + * * Created on 25.08.2010 */ - package org.mage.plugins.card.dl; import java.io.BufferedInputStream; @@ -29,10 +28,9 @@ import org.mage.plugins.card.dl.DownloadJob.Source; import org.mage.plugins.card.dl.DownloadJob.State; import org.mage.plugins.card.dl.lm.AbstractLaternaBean; - /** * The class Downloader. - * + * * @version V0.0 25.08.2010 * @author Clemens Koza */ @@ -40,16 +38,16 @@ public class Downloader extends AbstractLaternaBean implements Disposable { private static final Logger logger = Logger.getLogger(Downloader.class); - private final List jobs = properties.list("jobs"); + private final List jobs = properties.list("jobs"); private final Channel channel = new MemoryChannel<>(); - private final ExecutorService pool = Executors.newCachedThreadPool(); - private final List fibers = new ArrayList<>(); + private final ExecutorService pool = Executors.newCachedThreadPool(); + private final List fibers = new ArrayList<>(); public Downloader() { PoolFiberFactory f = new PoolFiberFactory(pool); //subscribe multiple fibers for parallel execution - for(int i = 0, numThreads = 10; i < numThreads; i++) { + for (int i = 0, numThreads = 10; i < numThreads; i++) { Fiber fiber = f.create(); fiber.start(); fibers.add(fiber); @@ -59,15 +57,15 @@ public class Downloader extends AbstractLaternaBean implements Disposable { @Override public void dispose() { - for(DownloadJob j:getJobs()) { - switch(j.getState()) { + for (DownloadJob j : getJobs()) { + switch (j.getState()) { case NEW: case WORKING: j.setState(State.ABORTED); } } - for(Fiber f:fibers) { + for (Fiber f : fibers) { f.dispose(); } pool.shutdown(); @@ -84,10 +82,10 @@ public class Downloader extends AbstractLaternaBean implements Disposable { } public void add(DownloadJob job) { - if(job.getState() == State.WORKING) { + if (job.getState() == State.WORKING) { throw new IllegalArgumentException("Job already running"); } - if(job.getState() == State.FINISHED) { + if (job.getState() == State.FINISHED) { throw new IllegalArgumentException("Job already finished"); } job.setState(State.NEW); @@ -100,15 +98,17 @@ public class Downloader extends AbstractLaternaBean implements Disposable { } /** - * Performs the download job: Transfers data from {@link Source} to {@link Destination} and updates the - * download job's state to reflect the progress. + * Performs the download job: Transfers data from {@link Source} to + * {@link Destination} and updates the download job's state to reflect the + * progress. */ private class DownloadCallback implements Callback { + @Override public void onMessage(DownloadJob job) { //the job won't be processed by multiple threads - synchronized(job) { - if(job.getState() != State.NEW) { + synchronized (job) { + if (job.getState() != State.NEW) { return; } job.setState(State.WORKING); @@ -118,10 +118,17 @@ public class Downloader extends AbstractLaternaBean implements Disposable { Destination dst = job.getDestination(); BoundedRangeModel progress = job.getProgress(); - if(dst.exists()) { + if (dst.isValid()) { progress.setMaximum(1); progress.setValue(1); } else { + if (dst.exists()) { + try { + dst.delete(); + } catch (IOException ex1) { + logger.warn("While deleting not valid file", ex1); + } + } progress.setMaximum(src.length()); InputStream is = new BufferedInputStream(src.open()); try { @@ -129,45 +136,45 @@ 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;) { - if(job.getState() == State.ABORTED) { + for (int len; (len = is.read(buf)) != -1;) { + if (job.getState() == State.ABORTED) { throw new IOException("Job was aborted"); } progress.setValue(total += len); os.write(buf, 0, len); } - } catch(IOException ex) { + } catch (IOException ex) { try { dst.delete(); - } catch(IOException ex1) { + } catch (IOException ex1) { logger.warn("While deleting", ex1); } throw ex; } finally { try { os.close(); - } catch(IOException ex) { + } catch (IOException ex) { logger.warn("While closing", ex); } } } finally { try { is.close(); - } catch(IOException ex) { + } catch (IOException ex) { logger.warn("While closing", ex); } } } job.setState(State.FINISHED); - } catch(ConnectException ex) { + } catch (ConnectException ex) { String message; if (ex.getMessage() != null) { message = ex.getMessage(); } else { message = "Unknown error"; } - logger.warn("Error resource download " + job.getName() +" from "+ job.getSource().toString() + ": " + message); - } catch(IOException ex) { + logger.warn("Error resource download " + job.getName() + " from " + job.getSource().toString() + ": " + message); + } catch (IOException ex) { job.setError(ex); } } diff --git a/Mage.Common/pom.xml b/Mage.Common/pom.xml index fa29a8edbf5..bacf94c9a8c 100644 --- a/Mage.Common/pom.xml +++ b/Mage.Common/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 mage-common diff --git a/Mage.Common/src/mage/utils/MageVersion.java b/Mage.Common/src/mage/utils/MageVersion.java index c409c179799..e5acc0701b2 100644 --- a/Mage.Common/src/mage/utils/MageVersion.java +++ b/Mage.Common/src/mage/utils/MageVersion.java @@ -40,8 +40,8 @@ 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 = 7; - public final static String MAGE_VERSION_MINOR_PATCH = "v1"; + public final static int MAGE_VERSION_PATCH = 8; + public final static String MAGE_VERSION_MINOR_PATCH = "v0"; public final static String MAGE_VERSION_INFO = ""; private final int major; diff --git a/Mage.Common/src/mage/view/SeatView.java b/Mage.Common/src/mage/view/SeatView.java index d812dadebd6..49d53db0917 100644 --- a/Mage.Common/src/mage/view/SeatView.java +++ b/Mage.Common/src/mage/view/SeatView.java @@ -44,6 +44,7 @@ public class SeatView implements Serializable { private UUID playerId; private final String playerName; private final String playerType; + private final String history; public SeatView(Seat seat) { if (seat.getPlayer() != null) { @@ -51,13 +52,16 @@ public class SeatView implements Serializable { this.playerName = seat.getPlayer().getName(); if (seat.getPlayer().getUserData() == null) { this.flagName = UserData.getDefaultFlagName(); + this.history = ""; } else { this.flagName = seat.getPlayer().getUserData().getFlagName(); + this.history = seat.getPlayer().getUserData().getHistory(); } } else { // Empty seat this.playerName = ""; this.flagName = ""; + this.history = ""; } this.playerType = seat.getPlayerType(); } @@ -78,4 +82,8 @@ public class SeatView implements Serializable { return flagName; } + public String getHistory() { + return history; + } + } diff --git a/Mage.Common/src/mage/view/UsersView.java b/Mage.Common/src/mage/view/UsersView.java index 6e9d649461b..40a39fc41e8 100644 --- a/Mage.Common/src/mage/view/UsersView.java +++ b/Mage.Common/src/mage/view/UsersView.java @@ -24,7 +24,7 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ + */ package mage.view; import java.io.Serializable; @@ -40,15 +40,13 @@ public class UsersView implements Serializable { private final String flagName; private final String userName; private final String history; - private final String infoState; private final String infoGames; private final String infoPing; - public UsersView(String flagName, String userName, String history, String infoState, String infoGames, String infoPing) { + public UsersView(String flagName, String userName, String history, String infoGames, String infoPing) { this.flagName = flagName; this.history = history; this.userName = userName; - this.infoState = infoState; this.infoGames = infoGames; this.infoPing = infoPing; } @@ -65,10 +63,6 @@ public class UsersView implements Serializable { return history; } - public String getInfoState() { - return infoState; - } - public String getInfoGames() { return infoGames; } diff --git a/Mage.Plugins/Mage.Counter.Plugin/pom.xml b/Mage.Plugins/Mage.Counter.Plugin/pom.xml index 79cf14afd4b..3f49e74f486 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.7 + 1.4.8 mage-counter-plugin diff --git a/Mage.Plugins/pom.xml b/Mage.Plugins/pom.xml index a840fe9b784..7e08821960b 100644 --- a/Mage.Plugins/pom.xml +++ b/Mage.Plugins/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 mage-plugins diff --git a/Mage.Server.Console/pom.xml b/Mage.Server.Console/pom.xml index 68d434ffc0d..38af4642cfd 100644 --- a/Mage.Server.Console/pom.xml +++ b/Mage.Server.Console/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 org.mage diff --git a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml index d153187b1a7..a115b6be9f8 100644 --- a/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml +++ b/Mage.Server.Plugins/Mage.Deck.Constructed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-deck-constructed diff --git a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml index dda300f0f17..d750b4d3853 100644 --- a/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml +++ b/Mage.Server.Plugins/Mage.Deck.Limited/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-deck-limited diff --git a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml index 7c6895b1879..7d9a8e7549d 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CommanderDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-game-commanderduel diff --git a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml index 35f9bb38f06..35ca14148ac 100644 --- a/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.CommanderFreeForAll/pom.xml @@ -6,7 +6,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-game-commanderfreeforall diff --git a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml index 620ef0c2c1b..48a762df5d8 100644 --- a/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.FreeForAll/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-game-freeforall diff --git a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml index f7227cb7084..60d8c59dd06 100644 --- a/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.MomirDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-game-momirduel diff --git a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml index 01be0a566b7..45fd9cc0cd9 100644 --- a/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.TinyLeadersDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-game-tinyleadersduel diff --git a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml index b8c5c5a6693..f0fef0763f8 100644 --- a/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml +++ b/Mage.Server.Plugins/Mage.Game.TwoPlayerDuel/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-game-twoplayerduel diff --git a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml index 3fa32de395f..3f199c86b60 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI.DraftBot/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-player-ai-draftbot diff --git a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml index 4a2601cde07..33ed3edb207 100644 --- a/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI.MA/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-player-ai-ma diff --git a/Mage.Server.Plugins/Mage.Player.AI/pom.xml b/Mage.Server.Plugins/Mage.Player.AI/pom.xml index 84682eb0381..ef1eec05151 100644 --- a/Mage.Server.Plugins/Mage.Player.AI/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AI/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-player-ai diff --git a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml index e4a3c7bd26b..d6b9f5c2391 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AIMCTS/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-player-ai-mcts diff --git a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml index ba11e38a376..94f05621aad 100644 --- a/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.AIMinimax/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-player-aiminimax diff --git a/Mage.Server.Plugins/Mage.Player.Human/pom.xml b/Mage.Server.Plugins/Mage.Player.Human/pom.xml index c7e3d8b2eae..e26cbefa9ce 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/pom.xml +++ b/Mage.Server.Plugins/Mage.Player.Human/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-player-human diff --git a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java index 48895f9e727..9dc9d7ff025 100644 --- a/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java +++ b/Mage.Server.Plugins/Mage.Player.Human/src/mage/player/human/HumanPlayer.java @@ -358,15 +358,13 @@ public class HumanPlayer extends PlayerImpl { } } } - } else { - if (target.canTarget(response.getUUID(), game)) { - if (target.getTargets().contains(response.getUUID())) { // if already included remove it with - target.remove(response.getUUID()); - } else { - target.addTarget(response.getUUID(), null, game); - if (target.doneChosing()) { - return true; - } + } else if (target.canTarget(response.getUUID(), game)) { + if (target.getTargets().contains(response.getUUID())) { // if already included remove it with + target.remove(response.getUUID()); + } else { + target.addTarget(response.getUUID(), null, game); + if (target.doneChosing()) { + return true; } } } @@ -530,12 +528,10 @@ public class HumanPlayer extends PlayerImpl { if (response.getUUID() != null) { if (target.getTargets().contains(response.getUUID())) { // if already included remove it target.remove(response.getUUID()); - } else { - if (target.canTarget(response.getUUID(), cards, game)) { - target.addTarget(response.getUUID(), source, game); - if (target.doneChosing()) { - return true; - } + } else if (target.canTarget(response.getUUID(), cards, game)) { + target.addTarget(response.getUUID(), source, game); + if (target.doneChosing()) { + return true; } } } else { @@ -1065,10 +1061,8 @@ public class HumanPlayer extends PlayerImpl { // does not block yet and can block or can block more attackers if (filter.match(blocker, null, playerId, game)) { selectCombatGroup(defendingPlayerId, blocker.getId(), game); - } else { - if (filterBlock.match(blocker, null, playerId, game) && game.getStack().isEmpty()) { - removeBlocker = true; - } + } else if (filterBlock.match(blocker, null, playerId, game) && game.getStack().isEmpty()) { + removeBlocker = true; } if (removeBlocker) { @@ -1546,4 +1540,9 @@ public class HumanPlayer extends PlayerImpl { pass(game); return true; } + + @Override + public String getHistory() { + return "no available"; + } } diff --git a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml index ce3e964160c..8c2ffcdb398 100644 --- a/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.BoosterDraft/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-tournament-boosterdraft diff --git a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml index 350b1794bea..351a99f9712 100644 --- a/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.Constructed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-tournament-constructed diff --git a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml index c7fa4c17de6..a722cc73ee8 100644 --- a/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml +++ b/Mage.Server.Plugins/Mage.Tournament.Sealed/pom.xml @@ -7,7 +7,7 @@ org.mage mage-server-plugins - 1.4.7 + 1.4.8 mage-tournament-sealed diff --git a/Mage.Server.Plugins/pom.xml b/Mage.Server.Plugins/pom.xml index 878b46fda1a..d72e100c2ff 100644 --- a/Mage.Server.Plugins/pom.xml +++ b/Mage.Server.Plugins/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 mage-server-plugins diff --git a/Mage.Server/config/config.xml b/Mage.Server/config/config.xml index 4d22d719179..18c0364e689 100644 --- a/Mage.Server/config/config.xml +++ b/Mage.Server/config/config.xml @@ -19,6 +19,17 @@ userNamePattern - pattern for user name validity check maxAiOpponents - number of allowed AI opponents on the server saveGameActivated - allow game save and replay options (not working correctly yet) + authenticationActivated - "true" = user have to register to signon "false" = user need not to register + * mail configs only needed if authentication is activated: + * if mailUser = "" mailgun is used otherwise nativ mail server on the system + googleAccount - not supported currently + mailgunApiKey - key from the mailgun domain e.g. = "key-12121111..." + mailgunDomain - domain for the mailgun message sending + mailSmtpHost - hostname to send the mail + mailSmtpPort - port to send the mail + mailUser - username used to send the mail + mailPassword - passworf of the used user to send the mail + mailFromAddress - sender address --> diff --git a/Mage.Server/pom.xml b/Mage.Server/pom.xml index e3151182514..67862424c03 100644 --- a/Mage.Server/pom.xml +++ b/Mage.Server/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 mage-server diff --git a/Mage.Server/release/config/config.xml b/Mage.Server/release/config/config.xml index cb90395505a..f69d7f28afb 100644 --- a/Mage.Server/release/config/config.xml +++ b/Mage.Server/release/config/config.xml @@ -1,6 +1,36 @@ + diff --git a/Mage.Server/src/main/java/mage/server/ChatManager.java b/Mage.Server/src/main/java/mage/server/ChatManager.java index ce1b6f5e091..8fbc80dd99c 100644 --- a/Mage.Server/src/main/java/mage/server/ChatManager.java +++ b/Mage.Server/src/main/java/mage/server/ChatManager.java @@ -24,8 +24,7 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.server; import java.util.ArrayList; @@ -44,14 +43,15 @@ import org.apache.log4j.Logger; public class ChatManager { private static final Logger logger = Logger.getLogger(ChatManager.class); - + private static final ChatManager INSTANCE = new ChatManager(); public static ChatManager getInstance() { return INSTANCE; } - private ChatManager() {} + private ChatManager() { + } private final ConcurrentHashMap chatSessions = new ConcurrentHashMap<>(); @@ -66,16 +66,16 @@ public class ChatManager { if (chatSession != null) { chatSession.join(userId); } else { - logger.trace("Chat to join not found - chatId: " + chatId +" userId: " + userId); - } - + logger.trace("Chat to join not found - chatId: " + chatId + " userId: " + userId); + } + } public void leaveChat(UUID chatId, UUID userId) { ChatSession chatSession = chatSessions.get(chatId); if (chatSession != null && chatSession.hasUser(userId)) { chatSession.kill(userId, DisconnectReason.CleaningUp); - } + } } public void destroyChatSession(UUID chatId) { @@ -88,7 +88,7 @@ public class ChatManager { logger.trace("Chat removed - chatId: " + chatId); } else { logger.trace("Chat to destroy does not exist - chatId: " + chatId); - } + } } } } @@ -119,63 +119,56 @@ public class ChatManager { } } - private boolean performUserCommand(User user, String message, UUID chatId) { String command = message.substring(1).trim().toUpperCase(Locale.ENGLISH); - if (command.equals("I") || command.equals("INFO")) { - user.setInfo(""); - chatSessions.get(chatId).broadcastInfoToUser(user,message); - return true; - } - if (command.startsWith("I ") || command.startsWith("INFO ")) { - user.setInfo(message.substring(command.startsWith("I ") ? 3 : 6)); - chatSessions.get(chatId).broadcastInfoToUser(user,message); + if (command.startsWith("H ") || command.startsWith("HISTORY ")) { + message = UserManager.getInstance().getUserHistory(message.substring(command.startsWith("H ") ? 3 : 9)); + chatSessions.get(chatId).broadcastInfoToUser(user, message); return true; } if (command.startsWith("W ") || command.startsWith("WHISPER ")) { - String rest = message.substring(command.startsWith("W ")? 3 : 9); + String rest = message.substring(command.startsWith("W ") ? 3 : 9); int first = rest.indexOf(" "); if (first > 1) { - String userToName = rest.substring(0,first); + String userToName = rest.substring(0, first); rest = rest.substring(first + 1).trim(); User userTo = UserManager.getInstance().getUserByName(userToName); if (userTo != null) { if (!chatSessions.get(chatId).broadcastWhisperToUser(user, userTo, rest)) { message += new StringBuilder("
User ").append(userToName).append(" not found").toString(); - chatSessions.get(chatId).broadcastInfoToUser(user,message); + chatSessions.get(chatId).broadcastInfoToUser(user, message); } } else { message += new StringBuilder("
User ").append(userToName).append(" not found").toString(); - chatSessions.get(chatId).broadcastInfoToUser(user,message); + chatSessions.get(chatId).broadcastInfoToUser(user, message); } return true; } } if (command.equals("L") || command.equals("LIST")) { message += new StringBuilder("
List of commands:") - .append("
\\info [text] - set a info text to your player") - .append("
\\list - Show a list of commands") - .append("
\\whisper [player name] [text] - whisper to the player with the given name").toString(); - chatSessions.get(chatId).broadcastInfoToUser(user,message); + .append("
\\history or \\h [username] - shows the history of a player") + .append("
\\list or \\l - Show a list of commands") + .append("
\\whisper or \\w [player name] [text] - whisper to the player with the given name").toString(); + chatSessions.get(chatId).broadcastInfoToUser(user, message); return true; } return false; } - - /** - * - * use mainly for announcing that a user connection was lost or that a user has reconnected - * + * + * use mainly for announcing that a user connection was lost or that a user + * has reconnected + * * @param userId * @param message - * @param color + * @param color */ public void broadcast(UUID userId, String message, MessageColor color) { User user = UserManager.getInstance().getUser(userId); if (user != null) { - for (ChatSession chat: chatSessions.values()) { + for (ChatSession chat : chatSessions.values()) { if (chat.hasUser(userId)) { chat.broadcast(user.getName(), message, color); } @@ -186,16 +179,16 @@ public class ChatManager { public void sendReconnectMessage(UUID userId) { User user = UserManager.getInstance().getUser(userId); if (user != null) { - for (ChatSession chat: chatSessions.values()) { + for (ChatSession chat : chatSessions.values()) { if (chat.hasUser(userId)) { chat.broadcast(null, user.getName() + " has reconnected", MessageColor.BLUE, true, MessageType.STATUS); - } - } + } + } } } - + public void removeUser(UUID userId, DisconnectReason reason) { - for (ChatSession chatSession: chatSessions.values()) { + for (ChatSession chatSession : chatSessions.values()) { if (chatSession.hasUser(userId)) { chatSession.kill(userId, reason); } diff --git a/Mage.Server/src/main/java/mage/server/MageServerImpl.java b/Mage.Server/src/main/java/mage/server/MageServerImpl.java index b2a7553eb91..6489473a794 100644 --- a/Mage.Server/src/main/java/mage/server/MageServerImpl.java +++ b/Mage.Server/src/main/java/mage/server/MageServerImpl.java @@ -99,7 +99,7 @@ public class MageServerImpl implements MageServer { private static final Logger logger = Logger.getLogger(MageServerImpl.class); private static final ExecutorService callExecutor = ThreadExecutor.getInstance().getCallExecutor(); private static final SecureRandom RANDOM = new SecureRandom(); - + private final String adminPassword; private final boolean testMode; private final LinkedHashMap activeAuthTokens = new LinkedHashMap() { @@ -140,9 +140,16 @@ public class MageServerImpl implements MageServer { } String authToken = generateAuthToken(); activeAuthTokens.put(email, authToken); - if (!MailgunClient.sendMessage(email, "XMage Password Reset Auth Token", - "Use this auth token to reset your password: " + authToken + "\n" + - "It's valid until the next server restart.")) { + String subject = "XMage Password Reset Auth Token"; + String text = "Use this auth token to reset your password: " + authToken + "\n" + + "It's valid until the next server restart."; + boolean success; + if (!ConfigSettings.getInstance().getMailUser().isEmpty()) { + success = MailClient.sendMessage(email, subject, text); + } else { + success = MailgunClient.sendMessage(email, subject, text); + } + if (!success) { sendErrorMessageToClient(sessionId, "There was an error inside the server while emailing an auth token"); return false; } diff --git a/Mage.Server/src/main/java/mage/server/MailClient.java b/Mage.Server/src/main/java/mage/server/MailClient.java new file mode 100644 index 00000000000..9c7ef614589 --- /dev/null +++ b/Mage.Server/src/main/java/mage/server/MailClient.java @@ -0,0 +1,53 @@ +package mage.server; + +import java.util.Properties; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.Session; +import javax.mail.Transport; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import mage.server.util.ConfigSettings; +import org.apache.log4j.Logger; + +public class MailClient { + + private static final Logger logger = Logger.getLogger(Main.class); + + public static boolean sendMessage(String email, String subject, String text) { + if (email.length() == 0) { + logger.info("Email is not sent because the address is empty"); + return false; + } + ConfigSettings config = ConfigSettings.getInstance(); + + Properties properties = System.getProperties(); + properties.setProperty("mail.smtps.host", config.getMailSmtpHost()); + properties.setProperty("mail.smtps.port", config.getMailSmtpPort()); + properties.setProperty("mail.smtps.auth", "true"); + properties.setProperty("mail.user", config.getMailUser()); + properties.setProperty("mail.password", config.getMailPassword()); + + Session session = Session.getDefaultInstance(properties); + + try{ + MimeMessage message = new MimeMessage(session); + message.setFrom(new InternetAddress(config.getMailFromAddress())); + message.addRecipient(Message.RecipientType.TO, new InternetAddress(email)); + message.setSubject(subject); + message.setText(text); + + Transport trnsport; + trnsport = session.getTransport("smtps"); + trnsport.connect(null, properties.getProperty("mail.password")); + message.saveChanges(); + trnsport.sendMessage(message, message.getAllRecipients()); + trnsport.close(); + + return true; + }catch (MessagingException ex) { + logger.error("Error sending message to " + email, ex); + } + return false; + } +} diff --git a/Mage.Server/src/main/java/mage/server/Main.java b/Mage.Server/src/main/java/mage/server/Main.java index 34a8d29a1fd..54c2a2b477c 100644 --- a/Mage.Server/src/main/java/mage/server/Main.java +++ b/Mage.Server/src/main/java/mage/server/Main.java @@ -32,20 +32,10 @@ import java.io.FilenameFilter; import java.net.InetAddress; import java.net.MalformedURLException; import java.util.HashMap; -import java.util.List; import java.util.Map; -import java.util.concurrent.Executors; -import java.util.concurrent.ScheduledExecutorService; -import java.util.concurrent.TimeUnit; import javax.management.MBeanServer; import mage.cards.repository.CardScanner; import mage.game.match.MatchType; -import mage.game.result.ResultProtos.MatchPlayerProto; -import mage.game.result.ResultProtos.MatchProto; -import mage.game.result.ResultProtos.TableProto; -import mage.game.result.ResultProtos.TourneyPlayerProto; -import mage.game.result.ResultProtos.TourneyProto; -import mage.game.result.ResultProtos.UserStatsProto; import mage.game.tournament.TournamentType; import mage.interfaces.MageServer; import mage.remote.Connection; @@ -53,9 +43,6 @@ import mage.server.draft.CubeFactory; import mage.server.game.DeckValidatorFactory; import mage.server.game.GameFactory; import mage.server.game.PlayerFactory; -import mage.server.record.TableRecord; -import mage.server.record.TableRecordRepository; -import mage.server.record.UserStats; import mage.server.record.UserStatsRepository; import mage.server.tournament.TournamentFactory; import mage.server.util.ConfigSettings; @@ -102,8 +89,6 @@ public class Main { protected static boolean testMode; protected static boolean fastDbMode; - private static final ScheduledExecutorService updateUserStatsTaskExecutor = Executors.newSingleThreadScheduledExecutor(); - /** * @param args the command line arguments */ @@ -132,6 +117,10 @@ public class Main { } logger.info("Done."); + logger.info("Updating user stats DB..."); + UserStatsRepository.instance.updateUserStats(); + logger.info("Done."); + deleteSavedGames(); ConfigSettings config = ConfigSettings.getInstance(); for (GamePlugin plugin : config.getGameTypes()) { @@ -167,7 +156,12 @@ public class Main { logger.info("Config - auth. activated : " + (config.isAuthenticationActivated() ? "true" : "false")); logger.info("Config - mailgun api key : " + config.getMailgunApiKey()); logger.info("Config - mailgun domain : " + config.getMailgunDomain()); - //logger.info("Config - google account : " + config.getGoogleAccount()); + logger.info("Config - mail smtp Host : " + config.getMailSmtpHost()); + logger.info("Config - mail smtpPort : " + config.getMailSmtpPort()); + logger.info("Config - mail user : " + config.getMailUser()); + logger.info("Config - mail passw. len.: " + config.getMailPassword().length()); + logger.info("Config - mail from addre.: " + config.getMailFromAddress()); + logger.info("Config - google account : " + config.getGoogleAccount()); Connection connection = new Connection("&maxPoolSize=" + config.getMaxPoolSize()); connection.setHost(config.getServerAddress()); @@ -190,13 +184,6 @@ public class Main { } catch (Exception ex) { logger.fatal("Failed to start server - " + connection.toString(), ex); } - - updateUserStatsTaskExecutor.scheduleAtFixedRate(new Runnable() { - @Override - public void run() { - updateUserStats(); - } - }, 60, 60, TimeUnit.SECONDS); } static void initStatistics() { @@ -391,78 +378,4 @@ public class Main { public static boolean isTestMode() { return testMode; } - - private static void updateUserStats() { - long latestEndTimeMs = UserStatsRepository.instance.getLatestEndTimeMs(); - List records = TableRecordRepository.instance.getAfter(latestEndTimeMs); - for (TableRecord record : records) { - TableProto table = record.getProto(); - if (table.getControllerName().equals("System")) { - // This is a sub table within a tournament, so it's already handled by the main - // tournament table. - continue; - } - if (table.hasMatch()) { - MatchProto match = table.getMatch(); - for (MatchPlayerProto player : match.getPlayersList()) { - UserStats userStats = UserStatsRepository.instance.getUser(player.getName()); - UserStatsProto proto = userStats != null ? userStats.getProto() : - UserStatsProto.newBuilder().setName(player.getName()).build(); - UserStatsProto.Builder builder = UserStatsProto.newBuilder(proto) - .setMatches(proto.getMatches() + 1); - switch (player.getQuit()) { - case IDLE_TIMEOUT: - builder.setMatchesIdleTimeout(proto.getMatchesIdleTimeout() + 1); - break; - case TIMER_TIMEOUT: - builder.setMatchesTimerTimeout(proto.getMatchesTimerTimeout() + 1); - break; - case QUIT: - builder.setMatchesQuit(proto.getMatchesQuit() + 1); - break; - } - if (userStats == null) { - UserStatsRepository.instance.add(new UserStats(builder.build(), table.getEndTimeMs())); - } else { - UserStatsRepository.instance.update(new UserStats(builder.build(), table.getEndTimeMs())); - } - // UserStats for this player is updated, so refresh it. - User user = UserManager.getInstance().getUserByName(player.getName()); - if (user != null) { - user.resetUserStats(); - } - } - } else if (table.hasTourney()) { - TourneyProto tourney = table.getTourney(); - for (TourneyPlayerProto player : tourney.getPlayersList()) { - UserStats userStats = UserStatsRepository.instance.getUser(player.getName()); - UserStatsProto proto = userStats != null ? userStats.getProto() : - UserStatsProto.newBuilder().setName(player.getName()).build(); - UserStatsProto.Builder builder = UserStatsProto.newBuilder(proto) - .setTourneys(proto.getTourneys() + 1); - switch (player.getQuit()) { - case DURING_ROUND: - builder.setTourneysQuitDuringRound(proto.getTourneysQuitDuringRound() + 1); - break; - case DURING_DRAFTING: - builder.setTourneysQuitDuringDrafting(proto.getTourneysQuitDuringDrafting() + 1); - break; - case DURING_CONSTRUCTION: - builder.setTourneysQuitDuringConstruction(proto.getTourneysQuitDuringConstruction() + 1); - break; - } - if (userStats == null) { - UserStatsRepository.instance.add(new UserStats(builder.build(), table.getEndTimeMs())); - } else { - UserStatsRepository.instance.update(new UserStats(builder.build(), table.getEndTimeMs())); - } - // UserStats for this player is updated, so refresh it. - User user = UserManager.getInstance().getUserByName(player.getName()); - if (user != null) { - user.resetUserStats(); - } - } - } - } - } } diff --git a/Mage.Server/src/main/java/mage/server/Session.java b/Mage.Server/src/main/java/mage/server/Session.java index 69c8ad42fa0..88dccf49126 100644 --- a/Mage.Server/src/main/java/mage/server/Session.java +++ b/Mage.Server/src/main/java/mage/server/Session.java @@ -99,8 +99,15 @@ public class Session { return returnMessage; } AuthorizedUserRepository.instance.add(userName, password, email); - if (MailgunClient.sendMessage(email, "XMage Registration Completed", - "You are successfully registered as " + userName + ".")) { + String subject = "XMage Registration Completed"; + String text = "You are successfully registered as " + userName + "."; + boolean success; + if (!ConfigSettings.getInstance().getMailUser().isEmpty()) { + success = MailClient.sendMessage(email, subject, text); + } else { + success = MailgunClient.sendMessage(email, subject, text); + } + if (success) { logger.info("Sent a registration confirmation email to " + email + " for " + userName); } else { logger.error("Failed sending a registration confirmation email to " + email + " for " + userName); diff --git a/Mage.Server/src/main/java/mage/server/User.java b/Mage.Server/src/main/java/mage/server/User.java index d8ef96b5c37..b6dad40c541 100644 --- a/Mage.Server/src/main/java/mage/server/User.java +++ b/Mage.Server/src/main/java/mage/server/User.java @@ -40,6 +40,7 @@ import java.util.concurrent.TimeUnit; import mage.cards.decks.Deck; import mage.constants.ManaType; import mage.game.Table; +import mage.game.result.ResultProtos; import mage.game.tournament.TournamentPlayer; import mage.interfaces.callback.ClientCallback; import mage.players.net.UserData; @@ -61,7 +62,7 @@ import org.apache.log4j.Logger; */ public class User { - private static final Logger logger = Logger.getLogger(User.class); + private static final Logger LOGGER = Logger.getLogger(User.class); public enum UserState { @@ -81,7 +82,6 @@ public class User { private final Map sideboarding; private final List watchedGames; private String sessionId; - private String info = ""; private String pingInfo = ""; private Date lastActivity; private UserState userState; @@ -106,6 +106,7 @@ public class User { this.watchedGames = new ArrayList<>(); this.tablesToDelete = new ArrayList<>(); this.sessionId = ""; + this.userStats = null; } public String getName() { @@ -129,15 +130,15 @@ public class User { if (sessionId.isEmpty()) { userState = UserState.Disconnected; lostConnection(); - logger.trace("USER - lost connection: " + userName + " id: " + userId); + LOGGER.trace("USER - lost connection: " + userName + " id: " + userId); } else if (userState == UserState.Created) { userState = UserState.Connected; - logger.trace("USER - created: " + userName + " id: " + userId); + LOGGER.trace("USER - created: " + userName + " id: " + userId); } else { userState = UserState.Reconnected; reconnect(); - logger.trace("USER - reconnected: " + userName + " id: " + userId); + LOGGER.trace("USER - reconnected: " + userName + " id: " + userId); } } @@ -273,12 +274,13 @@ public class User { public boolean isExpired(Date expired) { if (lastActivity.before(expired)) { - logger.trace(userName + " is expired!"); + LOGGER.trace(userName + " is expired!"); userState = UserState.Expired; return true; } - logger.trace(new StringBuilder("isExpired: User ").append(userName).append(" lastActivity: ").append(lastActivity).append(" expired: ").append(expired).toString()); - return false; /*userState == UserState.Disconnected && */ + LOGGER.trace(new StringBuilder("isExpired: User ").append(userName).append(" lastActivity: ").append(lastActivity).append(" expired: ").append(expired).toString()); + return false; + /*userState == UserState.Disconnected && */ } @@ -360,35 +362,35 @@ public class User { } public void remove(DisconnectReason reason) { - logger.trace("REMOVE " + getName() + " Draft sessions " + draftSessions.size()); + LOGGER.trace("REMOVE " + getName() + " Draft sessions " + draftSessions.size()); for (DraftSession draftSession : draftSessions.values()) { draftSession.setKilled(); } draftSessions.clear(); - logger.trace("REMOVE " + getName() + " Tournament sessions " + userTournaments.size()); + LOGGER.trace("REMOVE " + getName() + " Tournament sessions " + userTournaments.size()); for (UUID tournamentId : userTournaments.values()) { TournamentManager.getInstance().quit(tournamentId, getId()); } userTournaments.clear(); - logger.trace("REMOVE " + getName() + " Tables " + tables.size()); + LOGGER.trace("REMOVE " + getName() + " Tables " + tables.size()); for (Entry entry : tables.entrySet()) { - logger.debug("-- leave tableId: " + entry.getValue().getId()); + LOGGER.debug("-- leave tableId: " + entry.getValue().getId()); TableManager.getInstance().leaveTable(userId, entry.getValue().getId()); } tables.clear(); - logger.trace("REMOVE " + getName() + " Game sessions: " + gameSessions.size()); + LOGGER.trace("REMOVE " + getName() + " Game sessions: " + gameSessions.size()); for (GameSessionPlayer gameSessionPlayer : gameSessions.values()) { - logger.debug("-- kill game session of gameId: " + gameSessionPlayer.getGameId()); + LOGGER.debug("-- kill game session of gameId: " + gameSessionPlayer.getGameId()); GameManager.getInstance().quitMatch(gameSessionPlayer.getGameId(), userId); gameSessionPlayer.quitGame(); } gameSessions.clear(); - logger.trace("REMOVE " + getName() + " watched Games " + watchedGames.size()); + LOGGER.trace("REMOVE " + getName() + " watched Games " + watchedGames.size()); for (UUID gameId : watchedGames) { GameManager.getInstance().stopWatching(gameId, userId); } watchedGames.clear(); - logger.trace("REMOVE " + getName() + " Chats "); + LOGGER.trace("REMOVE " + getName() + " Chats "); ChatManager.getInstance().removeUser(userId, reason); } @@ -397,6 +399,12 @@ public class User { this.userData.update(userData); } else { this.userData = userData; + this.userStats = UserStatsRepository.instance.getUser(this.userName); + if (userStats != null) { + this.userData.setHistory(userStatsToString(userStats.getProto())); + } else { + this.userData.setHistory(""); + } } } @@ -446,11 +454,11 @@ public class User { } } else { // can happen if tournamet has just ended - logger.debug(getName() + " tournament player missing - tableId:" + table.getId(), null); + LOGGER.debug(getName() + " tournament player missing - tableId:" + table.getId(), null); tablesToDelete.add(tableEntry.getKey()); } } else { - logger.error(getName() + " tournament key missing - tableId: " + table.getId(), null); + LOGGER.error(getName() + " tournament key missing - tableId: " + table.getId(), null); } } else { switch (table.getState()) { @@ -500,14 +508,6 @@ public class User { return sb.toString(); } - public String getInfo() { - return info; - } - - public void setInfo(String Info) { - this.info = Info; - } - public void addGameWatchInfo(UUID gameId) { watchedGames.add(gameId); } @@ -540,5 +540,84 @@ public class User { // resetUserStats loads UserStats from DB. public void resetUserStats() { this.userStats = UserStatsRepository.instance.getUser(this.userName); + if (userData != null) { + userData.setHistory(userStatsToString(userStats.getProto())); + } } + + public String getHistory() { + if (userData != null) { + return userData.getHistory(); + } + return ""; + } + + public static String userStatsToString(ResultProtos.UserStatsProto proto) { + List builders = new ArrayList<>(); + if (proto.getMatches() > 0) { + StringBuilder builder = new StringBuilder(); + builder.append("Matches:"); + builder.append(proto.getMatches()); + List quit = new ArrayList<>(); + if (proto.getMatchesIdleTimeout() > 0) { + quit.add("I:" + Integer.toString(proto.getMatchesIdleTimeout())); + } + if (proto.getMatchesTimerTimeout() > 0) { + quit.add("T:" + Integer.toString(proto.getMatchesTimerTimeout())); + } + if (proto.getMatchesQuit() > 0) { + quit.add("Q:" + Integer.toString(proto.getMatchesQuit())); + } + if (quit.size() > 0) { + builder.append(" ("); + joinStrings(builder, quit, " "); + builder.append(")"); + } + builders.add(builder); + } + if (proto.getTourneys() > 0) { + StringBuilder builder = new StringBuilder(); + builder.append("Tourneys:"); + builder.append(proto.getTourneys()); + List quit = new ArrayList<>(); + if (proto.getTourneysQuitDuringDrafting() > 0) { + quit.add("D:" + Integer.toString(proto.getTourneysQuitDuringDrafting())); + } + if (proto.getTourneysQuitDuringConstruction() > 0) { + quit.add("C:" + Integer.toString(proto.getTourneysQuitDuringConstruction())); + } + if (proto.getTourneysQuitDuringRound() > 0) { + quit.add("R:" + Integer.toString(proto.getTourneysQuitDuringRound())); + } + if (quit.size() > 0) { + builder.append(" ("); + joinStrings(builder, quit, " "); + builder.append(")"); + } + builders.add(builder); + } + return joinBuilders(builders); + } + + private static String joinBuilders(List builders) { + if (builders.isEmpty()) { + return null; + } + StringBuilder builder = builders.get(0); + for (int i = 1; i < builders.size(); ++i) { + builder.append(" "); + builder.append(builders.get(i)); + } + return builder.toString(); + } + + private static void joinStrings(StringBuilder joined, List strings, String separator) { + for (int i = 0; i < strings.size(); ++i) { + if (i > 0) { + joined.append(separator); + } + joined.append(strings.get(i)); + } + } + } diff --git a/Mage.Server/src/main/java/mage/server/UserManager.java b/Mage.Server/src/main/java/mage/server/UserManager.java index 3306a6ddba1..7b1b875869d 100644 --- a/Mage.Server/src/main/java/mage/server/UserManager.java +++ b/Mage.Server/src/main/java/mage/server/UserManager.java @@ -1,16 +1,16 @@ /* * Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved. - * + * * Redistribution and use in source and binary forms, with or without modification, are * permitted provided that the following conditions are met: - * + * * 1. Redistributions of source code must retain the above copyright notice, this list of * conditions and the following disclaimer. - * + * * 2. Redistributions in binary form must reproduce the above copyright notice, this list * of conditions and the following disclaimer in the documentation and/or other materials * provided with the distribution. - * + * * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR @@ -20,7 +20,7 @@ * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * + * * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. @@ -38,6 +38,8 @@ import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import mage.server.User.UserState; +import mage.server.record.UserStats; +import mage.server.record.UserStatsRepository; import mage.server.util.ThreadExecutor; import org.apache.log4j.Logger; @@ -45,7 +47,7 @@ import org.apache.log4j.Logger; * * manages users - if a user is disconnected and 10 minutes have passed with no * activity the user is removed - * + * * @author BetaSteward_at_googlemail.com */ public class UserManager { @@ -56,7 +58,7 @@ public class UserManager { private final ConcurrentHashMap users = new ConcurrentHashMap<>(); private final ConcurrentHashMap usersByName = new ConcurrentHashMap<>(); - + private static final ExecutorService callExecutor = ThreadExecutor.getInstance().getCallExecutor(); private static final UserManager INSTANCE = new UserManager(); @@ -64,8 +66,8 @@ public class UserManager { public static UserManager getInstance() { return INSTANCE; } - - private UserManager() { + + private UserManager() { expireExecutor.scheduleAtFixedRate(new Runnable() { @Override public void run() { @@ -113,7 +115,7 @@ public class UserManager { public void disconnect(UUID userId, DisconnectReason reason) { if (userId != null) { User user = users.get(userId); - if (user != null) { + if (user != null) { user.setSessionId(""); // Session will be set again with new id if user reconnects } ChatManager.getInstance().removeUser(userId, reason); @@ -123,44 +125,44 @@ public class UserManager { public boolean isAdmin(UUID userId) { if (userId != null) { User user = users.get(userId); - if (user != null) { + if (user != null) { return user.getName().equals("Admin"); } } return false; } - public void removeUser(final UUID userId, final DisconnectReason reason) { + public void removeUser(final UUID userId, final DisconnectReason reason) { if (userId != null) { final User user = users.get(userId); if (user != null) { callExecutor.execute( - new Runnable() { - @Override - public void run() { - try { - logger.info("USER REMOVE - " + user.getName() + " (" + reason.toString() + ") userId: " + userId); - user.remove(reason); - logger.debug("USER REMOVE END - " + user.getName()); - } catch (Exception ex) { - handleException(ex); - } finally { - users.remove(userId); - usersByName.remove(user.getName()); - } + new Runnable() { + @Override + public void run() { + try { + logger.info("USER REMOVE - " + user.getName() + " (" + reason.toString() + ") userId: " + userId); + user.remove(reason); + logger.debug("USER REMOVE END - " + user.getName()); + } catch (Exception ex) { + handleException(ex); + } finally { + users.remove(userId); + usersByName.remove(user.getName()); } } + } ); } else { logger.warn("Trying to remove userId: " + userId + " - but it does not exist."); } - } + } } public boolean extendUserSession(UUID userId, String pingInfo) { if (userId != null) { User user = users.get(userId); - if (user != null) { + if (user != null) { user.updateLastActivity(pingInfo); return true; } @@ -169,7 +171,8 @@ public class UserManager { } /** - * Is the connection lost for more than 3 minutes, the user will be removed (within 3 minutes the user can reconnect) + * Is the connection lost for more than 3 minutes, the user will be removed + * (within 3 minutes the user can reconnect) */ private void checkExpired() { Calendar calendar = Calendar.getInstance(); @@ -185,13 +188,39 @@ public class UserManager { public void handleException(Exception ex) { if (ex != null) { - logger.fatal("User manager exception " + (ex.getMessage() == null ? "null":ex.getMessage())); + logger.fatal("User manager exception " + (ex.getMessage() == null ? "null" : ex.getMessage())); if (ex.getCause() != null) { - logger.debug("- Cause: " + (ex.getCause().getMessage() == null ? "null":ex.getCause().getMessage())); + logger.debug("- Cause: " + (ex.getCause().getMessage() == null ? "null" : ex.getCause().getMessage())); } ex.printStackTrace(); - }else { + } else { logger.fatal("User manager exception - null"); } } + + public String getUserHistory(String userName) { + User user = getUserByName(userName); + if (user == null) { + UserStats userStats = UserStatsRepository.instance.getUser(userName); + if (userStats == null) { + return "User " + userName + " not found"; + } + return User.userStatsToString(userStats.getProto()); + } + return "History of user " + userName + ": " + user.getUserData().getHistory(); + } + + public void updateUserHistory() { + callExecutor.execute(new Runnable() { + @Override + public void run() { + for (String updatedUser : UserStatsRepository.instance.updateUserStats()) { + User user = getUserByName(updatedUser); + if (user != null) { + user.resetUserStats(); + } + } + } + }); + } } diff --git a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java index 27d0888704a..30b005e19c2 100644 --- a/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java +++ b/Mage.Server/src/main/java/mage/server/game/GamesRoomImpl.java @@ -43,13 +43,11 @@ import mage.constants.TableState; import mage.game.GameException; import mage.game.Table; import mage.game.match.MatchOptions; -import mage.game.result.ResultProtos.UserStatsProto; import mage.game.tournament.TournamentOptions; import mage.server.RoomImpl; import mage.server.TableManager; import mage.server.User; import mage.server.UserManager; -import mage.server.record.UserStats; import mage.server.tournament.TournamentManager; import mage.server.util.ConfigSettings; import mage.server.util.ThreadExecutor; @@ -93,74 +91,6 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { return tableView; } - private static void joinStrings(StringBuilder joined, List strings, String separator) { - for (int i = 0; i < strings.size(); ++i) { - if (i > 0) { - joined.append(separator); - } - joined.append(strings.get(i)); - } - } - - private static String joinBuilders(List builders) { - if (builders.isEmpty()) { - return null; - } - StringBuilder builder = builders.get(0); - for (int i = 1; i < builders.size(); ++i) { - builder.append(" "); - builder.append(builders.get(i)); - } - return builder.toString(); - } - - private static String userStatsToString(UserStatsProto proto) { - List builders = new ArrayList<>(); - if (proto.getMatches() > 0) { - StringBuilder builder = new StringBuilder(); - builder.append("Matches:"); - builder.append(proto.getMatches()); - List quit = new ArrayList<>(); - if (proto.getMatchesIdleTimeout() > 0) { - quit.add("I:" + Integer.toString(proto.getMatchesIdleTimeout())); - } - if (proto.getMatchesTimerTimeout() > 0) { - quit.add("T:" + Integer.toString(proto.getMatchesTimerTimeout())); - } - if (proto.getMatchesQuit() > 0) { - quit.add("Q:" + Integer.toString(proto.getMatchesQuit())); - } - if (quit.size() > 0) { - builder.append(" ("); - joinStrings(builder, quit, " "); - builder.append(")"); - } - builders.add(builder); - } - if (proto.getTourneys() > 0) { - StringBuilder builder = new StringBuilder(); - builder.append("Tourneys:"); - builder.append(proto.getTourneys()); - List quit = new ArrayList<>(); - if (proto.getTourneysQuitDuringDrafting() > 0) { - quit.add("D:" + Integer.toString(proto.getTourneysQuitDuringDrafting())); - } - if (proto.getTourneysQuitDuringConstruction() > 0) { - quit.add("C:" + Integer.toString(proto.getTourneysQuitDuringConstruction())); - } - if (proto.getTourneysQuitDuringRound() > 0) { - quit.add("R:" + Integer.toString(proto.getTourneysQuitDuringRound())); - } - if (quit.size() > 0) { - builder.append(" ("); - joinStrings(builder, quit, " "); - builder.append(")"); - } - builders.add(builder); - } - return joinBuilders(builders); - } - private void update() { ArrayList tableList = new ArrayList<>(); ArrayList matchList = new ArrayList<>(); @@ -183,20 +113,14 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable { matchView = matchList; List users = new ArrayList<>(); for (User user : UserManager.getInstance().getUsers()) { - String history = null; - UserStats stats = user.getUserStats(); - if (stats != null) { - history = userStatsToString(stats.getProto()); - } try { - users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), history, user.getInfo(), user.getGameInfo(), user.getPingInfo())); + users.add(new UsersView(user.getUserData().getFlagName(), user.getName(), user.getHistory(), user.getGameInfo(), user.getPingInfo())); } catch (Exception ex) { logger.fatal("User update exception: " + user.getName() + " - " + ex.toString(), ex); users.add(new UsersView( (user.getUserData() != null && user.getUserData().getFlagName() != null) ? user.getUserData().getFlagName() : "world", user.getName() != null ? user.getName() : "", - history != null ? history : "", - user.getInfo() != null ? user.getInfo() : "", + user.getHistory() != null ? user.getHistory() : "", "[exception]", user.getPingInfo() != null ? user.getPingInfo() : "")); } diff --git a/Mage.Server/src/main/java/mage/server/record/TableRecorderImpl.java b/Mage.Server/src/main/java/mage/server/record/TableRecorderImpl.java index b4d16688edf..2bafdff9c1c 100644 --- a/Mage.Server/src/main/java/mage/server/record/TableRecorderImpl.java +++ b/Mage.Server/src/main/java/mage/server/record/TableRecorderImpl.java @@ -3,6 +3,7 @@ package mage.server.record; import mage.game.Table; import mage.game.Table.TableRecorder; import mage.game.result.ResultProtos.TableProto; +import mage.server.UserManager; import org.apache.log4j.Logger; public class TableRecorderImpl implements TableRecorder { @@ -17,5 +18,6 @@ public class TableRecorderImpl implements TableRecorder { public void record(Table table) { TableProto proto = table.toProto(); TableRecordRepository.instance.add(new TableRecord(proto, proto.getEndTimeMs())); + UserManager.getInstance().updateUserHistory(); } } diff --git a/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java b/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java index de43a7c76bc..26348de66a3 100644 --- a/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java +++ b/Mage.Server/src/main/java/mage/server/record/UserStatsRepository.java @@ -9,8 +9,11 @@ import com.j256.ormlite.support.DatabaseConnection; import com.j256.ormlite.table.TableUtils; import java.io.File; import java.sql.SQLException; +import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import mage.cards.repository.RepositoryUtil; +import mage.game.result.ResultProtos; import org.apache.log4j.Logger; public enum UserStatsRepository { @@ -98,6 +101,79 @@ public enum UserStatsRepository { return 0; } + // updateUserStats reads tables finished after the last DB update and reflects it to the DB. + // It returns the list of user names that are upated. + public List updateUserStats() { + HashSet updatedUsers = new HashSet(); + // Lock the DB so that no other updateUserStats runs at the same time. + synchronized(this) { + long latestEndTimeMs = this.getLatestEndTimeMs(); + List records = TableRecordRepository.instance.getAfter(latestEndTimeMs); + for (TableRecord record : records) { + ResultProtos.TableProto table = record.getProto(); + if (table.getControllerName().equals("System")) { + // This is a sub table within a tournament, so it's already handled by the main + // tournament table. + continue; + } + if (table.hasMatch()) { + ResultProtos.MatchProto match = table.getMatch(); + for (ResultProtos.MatchPlayerProto player : match.getPlayersList()) { + UserStats userStats = this.getUser(player.getName()); + ResultProtos.UserStatsProto proto = userStats != null ? userStats.getProto() + : ResultProtos.UserStatsProto.newBuilder().setName(player.getName()).build(); + ResultProtos.UserStatsProto.Builder builder = ResultProtos.UserStatsProto.newBuilder(proto) + .setMatches(proto.getMatches() + 1); + switch (player.getQuit()) { + case IDLE_TIMEOUT: + builder.setMatchesIdleTimeout(proto.getMatchesIdleTimeout() + 1); + break; + case TIMER_TIMEOUT: + builder.setMatchesTimerTimeout(proto.getMatchesTimerTimeout() + 1); + break; + case QUIT: + builder.setMatchesQuit(proto.getMatchesQuit() + 1); + break; + } + if (userStats == null) { + this.add(new UserStats(builder.build(), table.getEndTimeMs())); + } else { + this.update(new UserStats(builder.build(), table.getEndTimeMs())); + } + updatedUsers.add(player.getName()); + } + } else if (table.hasTourney()) { + ResultProtos.TourneyProto tourney = table.getTourney(); + for (ResultProtos.TourneyPlayerProto player : tourney.getPlayersList()) { + UserStats userStats = this.getUser(player.getName()); + ResultProtos.UserStatsProto proto = userStats != null ? userStats.getProto() + : ResultProtos.UserStatsProto.newBuilder().setName(player.getName()).build(); + ResultProtos.UserStatsProto.Builder builder = ResultProtos.UserStatsProto.newBuilder(proto) + .setTourneys(proto.getTourneys() + 1); + switch (player.getQuit()) { + case DURING_ROUND: + builder.setTourneysQuitDuringRound(proto.getTourneysQuitDuringRound() + 1); + break; + case DURING_DRAFTING: + builder.setTourneysQuitDuringDrafting(proto.getTourneysQuitDuringDrafting() + 1); + break; + case DURING_CONSTRUCTION: + builder.setTourneysQuitDuringConstruction(proto.getTourneysQuitDuringConstruction() + 1); + break; + } + if (userStats == null) { + this.add(new UserStats(builder.build(), table.getEndTimeMs())); + } else { + this.update(new UserStats(builder.build(), table.getEndTimeMs())); + } + updatedUsers.add(player.getName()); + } + } + } + } + return new ArrayList(updatedUsers); + } + public void closeDB() { try { if (dao != null && dao.getConnectionSource() != null) { diff --git a/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java b/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java index 4cda0d91958..d472b0a8cea 100644 --- a/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java +++ b/Mage.Server/src/main/java/mage/server/util/ConfigSettings.java @@ -147,6 +147,26 @@ public class ConfigSettings { return config.getServer().getMailgunDomain(); } + public String getMailSmtpHost() { + return config.getServer().getMailSmtpHost(); + } + + public String getMailSmtpPort() { + return config.getServer().getMailSmtpPort(); + } + + public String getMailUser() { + return config.getServer().getMailUser(); + } + + public String getMailPassword() { + return config.getServer().getMailPassword(); + } + + public String getMailFromAddress() { + return config.getServer().getMailFromAddress(); + } + public List getPlayerTypes() { return config.getPlayerTypes().getPlayerType(); } diff --git a/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd b/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd index 1c25007dd23..0a82ec9bae5 100644 --- a/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd +++ b/Mage.Server/src/main/xml-resources/jaxb/Config/Config.xsd @@ -38,6 +38,11 @@ + + + + + diff --git a/Mage.Sets/pom.xml b/Mage.Sets/pom.xml index 95b969006f5..4050b0efede 100644 --- a/Mage.Sets/pom.xml +++ b/Mage.Sets/pom.xml @@ -7,7 +7,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 org.mage diff --git a/Mage.Sets/src/mage/sets/apocalypse/ConsumeStrength.java b/Mage.Sets/src/mage/sets/apocalypse/ConsumeStrength.java index 9c3a352436b..c583f19ca4c 100644 --- a/Mage.Sets/src/mage/sets/apocalypse/ConsumeStrength.java +++ b/Mage.Sets/src/mage/sets/apocalypse/ConsumeStrength.java @@ -37,6 +37,8 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SubLayer; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -54,8 +56,17 @@ public class ConsumeStrength extends CardImpl { // Target creature gets +2/+2 until end of turn. Another target creature gets -2/-2 until end of turn. this.getSpellAbility().addEffect(new ConsumeStrengthEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature to get +2/+2"); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature to get -2/-2"); + filter2.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); } public ConsumeStrength(final ConsumeStrength card) { @@ -91,7 +102,7 @@ class ConsumeStrengthEffect extends ContinuousEffectImpl { permanent.addPower(2); permanent.addToughness(2); } - permanent = game.getPermanent(source.getTargets().get(0).getTargets().get(1)); + permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent != null) { permanent.addPower(-2); permanent.addToughness(-2); diff --git a/Mage.Sets/src/mage/sets/eventide/DreamFracture.java b/Mage.Sets/src/mage/sets/eventide/DreamFracture.java index 2b4de1ba5eb..2641de664bb 100644 --- a/Mage.Sets/src/mage/sets/eventide/DreamFracture.java +++ b/Mage.Sets/src/mage/sets/eventide/DreamFracture.java @@ -49,7 +49,6 @@ public class DreamFracture extends CardImpl { super(ownerId, 19, "Dream Fracture", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{1}{U}{U}"); this.expansionSetCode = "EVE"; - // Counter target spell. Its controller draws a card. this.getSpellAbility().addEffect(new DreamFractureEffect()); this.getSpellAbility().addTarget(new TargetSpell()); @@ -73,7 +72,7 @@ class DreamFractureEffect extends OneShotEffect { public DreamFractureEffect() { super(Outcome.Neutral); - this.staticText = "Counter target spell. Its controller draws a card"; + this.staticText = "Counter target spell. Its controller draws a card"; } public DreamFractureEffect(final DreamFractureEffect effect) { @@ -102,4 +101,4 @@ class DreamFractureEffect extends OneShotEffect { } return countered; } -} \ No newline at end of file +} diff --git a/Mage.Sets/src/mage/sets/fifthedition/LibraryOfLeng.java b/Mage.Sets/src/mage/sets/fifthedition/LibraryOfLeng.java new file mode 100644 index 00000000000..a6dc94c1875 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fifthedition/LibraryOfLeng.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.fifthedition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class LibraryOfLeng extends mage.sets.limitedbeta.LibraryOfLeng { + + public LibraryOfLeng(UUID ownerId) { + super(ownerId); + this.cardNumber = 387; + this.expansionSetCode = "5ED"; + } + + public LibraryOfLeng(final LibraryOfLeng card) { + super(card); + } + + @Override + public LibraryOfLeng copy() { + return new LibraryOfLeng(this); + } +} diff --git a/Mage.Sets/src/mage/sets/fourthedition/LibraryOfLeng.java b/Mage.Sets/src/mage/sets/fourthedition/LibraryOfLeng.java new file mode 100644 index 00000000000..5cd8b91f3b2 --- /dev/null +++ b/Mage.Sets/src/mage/sets/fourthedition/LibraryOfLeng.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.fourthedition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class LibraryOfLeng extends mage.sets.limitedbeta.LibraryOfLeng { + + public LibraryOfLeng(UUID ownerId) { + super(ownerId); + this.cardNumber = 351; + this.expansionSetCode = "4ED"; + } + + public LibraryOfLeng(final LibraryOfLeng card) { + super(card); + } + + @Override + public LibraryOfLeng copy() { + return new LibraryOfLeng(this); + } +} diff --git a/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java b/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java index 9bed435422f..bef3060278d 100644 --- a/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java +++ b/Mage.Sets/src/mage/sets/gatecrash/Bioshift.java @@ -56,7 +56,7 @@ public class Bioshift extends CardImpl { // Move any number of +1/+1 counters from target creature onto another target creature with the same controller. getSpellAbility().addEffect(new MoveCounterFromTargetToTargetEffect()); getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (you take counters from)"))); - getSpellAbility().addTarget(new BioshiftSecondTargetPermanent()); + getSpellAbility().addTarget(new BioshiftSecondTargetCreaturePermanent()); } @@ -113,14 +113,15 @@ class MoveCounterFromTargetToTargetEffect extends OneShotEffect { } } -class BioshiftSecondTargetPermanent extends TargetPermanent { +class BioshiftSecondTargetCreaturePermanent extends TargetCreaturePermanent { - BioshiftSecondTargetPermanent() { - super(); - this.filter = new FilterCreaturePermanent("another target creature with the same controller (counters go to)"); + private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("another target creature with the same controller (counters go to)"); + + BioshiftSecondTargetCreaturePermanent() { + super(filter); } - BioshiftSecondTargetPermanent(final BioshiftSecondTargetPermanent target) { + BioshiftSecondTargetCreaturePermanent(final BioshiftSecondTargetCreaturePermanent target) { super(target); } @@ -137,7 +138,7 @@ class BioshiftSecondTargetPermanent extends TargetPermanent { } @Override - public BioshiftSecondTargetPermanent copy() { - return new BioshiftSecondTargetPermanent(this); + public BioshiftSecondTargetCreaturePermanent copy() { + return new BioshiftSecondTargetCreaturePermanent(this); } } diff --git a/Mage.Sets/src/mage/sets/guildpact/Schismotivate.java b/Mage.Sets/src/mage/sets/guildpact/Schismotivate.java index 4a1b7ac9ecb..ab92114de1d 100644 --- a/Mage.Sets/src/mage/sets/guildpact/Schismotivate.java +++ b/Mage.Sets/src/mage/sets/guildpact/Schismotivate.java @@ -37,6 +37,8 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SubLayer; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -53,7 +55,17 @@ public class Schismotivate extends CardImpl { // Target creature gets +4/+0 until end of turn. Another target creature gets -4/-0 until end of turn. this.getSpellAbility().addEffect(new SchismotivateEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); + + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature (gets +4/+0 until end of turn)"); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature (gets -4/-0 until end of turn)"); + filter2.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); } public Schismotivate(final Schismotivate card) { @@ -88,7 +100,7 @@ class SchismotivateEffect extends ContinuousEffectImpl { if (permanent != null) { permanent.addPower(4); } - permanent = game.getPermanent(source.getTargets().get(0).getTargets().get(1)); + permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent != null) { permanent.addPower(-4); } diff --git a/Mage.Sets/src/mage/sets/legions/ElvishSoultiller.java b/Mage.Sets/src/mage/sets/legions/ElvishSoultiller.java new file mode 100644 index 00000000000..014632be1d1 --- /dev/null +++ b/Mage.Sets/src/mage/sets/legions/ElvishSoultiller.java @@ -0,0 +1,121 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.legions; + +import java.util.UUID; +import mage.MageInt; +import mage.MageObject; +import mage.abilities.Ability; +import mage.abilities.common.DiesTriggeredAbility; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.cards.repository.CardRepository; +import mage.choices.Choice; +import mage.choices.ChoiceImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.filter.common.FilterCreatureCard; +import mage.filter.predicate.mageobject.SubtypePredicate; +import mage.game.Game; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class ElvishSoultiller extends CardImpl { + + public ElvishSoultiller(UUID ownerId) { + super(ownerId, 124, "Elvish Soultiller", Rarity.RARE, new CardType[]{CardType.CREATURE}, "{3}{G}{G}"); + this.expansionSetCode = "LGN"; + this.subtype.add("Elf"); + this.subtype.add("Mutant"); + this.power = new MageInt(5); + this.toughness = new MageInt(4); + + // When Elvish Soultiller dies, choose a creature type. Shuffle all creature cards of that type from your graveyard into your library. + addAbility(new DiesTriggeredAbility(new ElvishSoultillerEffect())); + + } + + public ElvishSoultiller(final ElvishSoultiller card) { + super(card); + } + + @Override + public ElvishSoultiller copy() { + return new ElvishSoultiller(this); + } +} + +class ElvishSoultillerEffect extends OneShotEffect { + + public ElvishSoultillerEffect() { + super(Outcome.Benefit); + this.staticText = "choose a creature type. Shuffle all creature cards of that type from your graveyard into your library"; + } + + public ElvishSoultillerEffect(final ElvishSoultillerEffect effect) { + super(effect); + } + + @Override + public ElvishSoultillerEffect copy() { + return new ElvishSoultillerEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Player controller = game.getPlayer(source.getControllerId()); + MageObject mageObject = game.getObject(source.getSourceId()); + if (controller != null && mageObject != null) { + Choice typeChoice = new ChoiceImpl(true); + typeChoice.setMessage("Choose creature type"); + typeChoice.setChoices(CardRepository.instance.getCreatureTypes()); + while (!controller.choose(outcome, typeChoice, game)) { + if (!controller.canRespond()) { + return false; + } + } + if (!game.isSimulation()) { + game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice()); + } + Cards cardsToLibrary = new CardsImpl(); + FilterCreatureCard filter = new FilterCreatureCard(); + filter.add(new SubtypePredicate(typeChoice.getChoice())); + cardsToLibrary.addAll(controller.getGraveyard().getCards(filter, source.getSourceId(), source.getControllerId(), game)); + controller.putCardsOnTopOfLibrary(cardsToLibrary, game, source, false); + controller.shuffleLibrary(game); + return true; + } + return false; + } +} diff --git a/Mage.Sets/src/mage/sets/limitedalpha/LibraryOfLeng.java b/Mage.Sets/src/mage/sets/limitedalpha/LibraryOfLeng.java new file mode 100644 index 00000000000..c929823f598 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedalpha/LibraryOfLeng.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.limitedalpha; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class LibraryOfLeng extends mage.sets.limitedbeta.LibraryOfLeng { + + public LibraryOfLeng(UUID ownerId) { + super(ownerId); + this.cardNumber = 257; + this.expansionSetCode = "LEA"; + } + + public LibraryOfLeng(final LibraryOfLeng card) { + super(card); + } + + @Override + public LibraryOfLeng copy() { + return new LibraryOfLeng(this); + } +} diff --git a/Mage.Sets/src/mage/sets/limitedbeta/LibraryOfLeng.java b/Mage.Sets/src/mage/sets/limitedbeta/LibraryOfLeng.java new file mode 100644 index 00000000000..fc1b1df94a3 --- /dev/null +++ b/Mage.Sets/src/mage/sets/limitedbeta/LibraryOfLeng.java @@ -0,0 +1,149 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.limitedbeta; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.common.SimpleStaticAbility; +import mage.abilities.effects.Effect; +import mage.abilities.effects.ReplacementEffectImpl; +import mage.abilities.effects.common.continuous.MaximumHandSizeControllerEffect; +import mage.cards.Card; +import mage.cards.CardImpl; +import mage.cards.Cards; +import mage.cards.CardsImpl; +import mage.constants.CardType; +import mage.constants.Duration; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.constants.Zone; +import mage.game.Game; +import mage.game.events.GameEvent; +import mage.game.events.GameEvent.EventType; +import mage.game.events.ZoneChangeEvent; +import mage.players.Player; + +/** + * + * @author LevelX2 + */ +public class LibraryOfLeng extends CardImpl { + + public LibraryOfLeng(UUID ownerId) { + super(ownerId, 259, "Library of Leng", Rarity.UNCOMMON, new CardType[]{CardType.ARTIFACT}, "{1}"); + this.expansionSetCode = "LEB"; + + // You have no maximum hand size. + Effect effect = new MaximumHandSizeControllerEffect(Integer.MAX_VALUE, Duration.WhileOnBattlefield, MaximumHandSizeControllerEffect.HandSizeModification.SET); + addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, effect)); + + // If an effect causes you to discard a card, discard it, but you may put it on top of your library instead of into your graveyard. + addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new LibraryOfLengEffect())); + + } + + public LibraryOfLeng(final LibraryOfLeng card) { + super(card); + } + + @Override + public LibraryOfLeng copy() { + return new LibraryOfLeng(this); + } +} + +class LibraryOfLengEffect extends ReplacementEffectImpl { + + private UUID cardId; + private int zoneChangeCounter; + + public LibraryOfLengEffect() { + super(Duration.WhileOnBattlefield, Outcome.Benefit); + staticText = "If an effect causes you to discard a card, discard it, but you may put it on top of your library instead of into your graveyard"; + } + + public LibraryOfLengEffect(final LibraryOfLengEffect effect) { + super(effect); + this.cardId = effect.cardId; + this.zoneChangeCounter = effect.zoneChangeCounter; + } + + @Override + public LibraryOfLengEffect copy() { + return new LibraryOfLengEffect(this); + } + + @Override + public boolean checksEventType(GameEvent event, Game game) { + return event.getType().equals(EventType.DISCARD_CARD) + || event.getType().equals(EventType.ZONE_CHANGE); + } + + @Override + public boolean applies(GameEvent event, Ability source, Game game) { + if (event.getType().equals(EventType.DISCARD_CARD)) { + return event.getPlayerId().equals(source.getControllerId()); + } + if (event.getType().equals(EventType.ZONE_CHANGE)) { + if (event.getTargetId().equals(cardId) && game.getState().getZoneChangeCounter(event.getTargetId()) == zoneChangeCounter) { + if (((ZoneChangeEvent) event).getFromZone().equals(Zone.HAND) && ((ZoneChangeEvent) event).getToZone().equals(Zone.GRAVEYARD)) { + return true; + } + } + } + return false; + } + + @Override + public boolean replaceEvent(GameEvent event, Ability source, Game game) { + if (event.getType().equals(EventType.DISCARD_CARD)) { + // only save card info + Card card = game.getCard(event.getTargetId()); + if (card != null) { + cardId = card.getId(); + zoneChangeCounter = game.getState().getZoneChangeCounter(cardId); + } + return false; + } + if (event.getType().equals(EventType.ZONE_CHANGE)) { + Player controller = game.getPlayer(source.getControllerId()); + Card card = game.getCard(event.getTargetId()); + if (controller != null && card != null) { + cardId = null; + zoneChangeCounter = 0; + if (controller.chooseUse(outcome, "Put " + card.getIdName() + " on top of your library instead?", source, game)) { + Cards cardsToLibrary = new CardsImpl(card); + controller.putCardsOnTopOfLibrary(cardsToLibrary, game, source, false); + return true; + } + } + } + return false; + } + +} diff --git a/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java b/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java index d6af2a5a2ab..9d646d73863 100644 --- a/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java +++ b/Mage.Sets/src/mage/sets/magic2012/AdaptiveAutomaton.java @@ -50,7 +50,6 @@ import mage.filter.predicate.permanent.AnotherPredicate; import mage.filter.predicate.permanent.ControllerPredicate; import mage.game.Game; import mage.game.permanent.Permanent; -import mage.players.Player; /** * @author nantuko @@ -87,8 +86,8 @@ public class AdaptiveAutomaton extends CardImpl { } } - class AdaptiveAutomatonAddSubtypeEffect extends ContinuousEffectImpl { + public AdaptiveAutomatonAddSubtypeEffect() { super(Duration.WhileOnBattlefield, Layer.TypeChangingEffects_4, SubLayer.NA, Outcome.Benefit); staticText = "{this} is the chosen type in addition to its other types"; diff --git a/Mage.Sets/src/mage/sets/masterseditioniv/LibraryOfLeng.java b/Mage.Sets/src/mage/sets/masterseditioniv/LibraryOfLeng.java new file mode 100644 index 00000000000..32b45b5106c --- /dev/null +++ b/Mage.Sets/src/mage/sets/masterseditioniv/LibraryOfLeng.java @@ -0,0 +1,54 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.masterseditioniv; + +import java.util.UUID; +import mage.constants.Rarity; + +/** + * + * @author LevelX2 + */ +public class LibraryOfLeng extends mage.sets.limitedbeta.LibraryOfLeng { + + public LibraryOfLeng(UUID ownerId) { + super(ownerId); + this.cardNumber = 211; + this.expansionSetCode = "ME4"; + this.rarity = Rarity.COMMON; + } + + public LibraryOfLeng(final LibraryOfLeng card) { + super(card); + } + + @Override + public LibraryOfLeng copy() { + return new LibraryOfLeng(this); + } +} diff --git a/Mage.Sets/src/mage/sets/modernmasters/IncrementalGrowth.java b/Mage.Sets/src/mage/sets/modernmasters/IncrementalGrowth.java index 3eccc7bed57..0f9c606abad 100644 --- a/Mage.Sets/src/mage/sets/modernmasters/IncrementalGrowth.java +++ b/Mage.Sets/src/mage/sets/modernmasters/IncrementalGrowth.java @@ -35,6 +35,8 @@ import mage.constants.CardType; import mage.constants.Outcome; import mage.constants.Rarity; import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; @@ -53,8 +55,23 @@ public class IncrementalGrowth extends CardImpl { // Put a +1/+1 counter on target creature, two +1/+1 counters on another target creature, and three +1/+1 counters on a third target creature. this.getSpellAbility().addEffect(new IncrementalGrowthEffect()); - Target target = new TargetCreaturePermanent(3,3); - this.getSpellAbility().addTarget(target); + + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature (gets a +1/+1 counter)"); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature (gets two +1/+1 counter)"); + filter2.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); + + FilterCreaturePermanent filter3 = new FilterCreaturePermanent("another creature (gets three +1/+1 counters)"); + filter3.add(new AnotherTargetPredicate(3)); + TargetCreaturePermanent target3 = new TargetCreaturePermanent(filter3); + target3.setTargetTag(3); + this.getSpellAbility().addTarget(target3); } public IncrementalGrowth(final IncrementalGrowth card) { @@ -86,9 +103,9 @@ class IncrementalGrowthEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int i = 0; - for (UUID targetId : getTargetPointer().getTargets(game, source)) { + for (Target target : source.getTargets()) { i++; - Permanent creature = game.getPermanent(targetId); + Permanent creature = game.getPermanent(target.getFirstTarget()); if (creature != null) { creature.addCounters(CounterType.P1P1.createInstance(i), game); } diff --git a/Mage.Sets/src/mage/sets/newphyrexia/LeechingBite.java b/Mage.Sets/src/mage/sets/newphyrexia/LeechingBite.java index 54869297d22..eb760b652e1 100644 --- a/Mage.Sets/src/mage/sets/newphyrexia/LeechingBite.java +++ b/Mage.Sets/src/mage/sets/newphyrexia/LeechingBite.java @@ -28,15 +28,20 @@ package mage.sets.newphyrexia; import java.util.UUID; -import mage.abilities.effects.Effect; -import mage.abilities.effects.common.continuous.BoostTargetEffect; +import mage.abilities.Ability; +import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; import mage.constants.CardType; import mage.constants.Duration; +import mage.constants.Layer; +import mage.constants.Outcome; import mage.constants.Rarity; +import mage.constants.SubLayer; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; +import mage.game.Game; +import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; -import mage.target.targetpointer.SecondTargetPointer; /** * @@ -48,16 +53,20 @@ public class LeechingBite extends CardImpl { super(ownerId, 113, "Leeching Bite", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{G}"); this.expansionSetCode = "NPH"; + // Target creature gets +1/+1 until end of turn. Another target creature gets -1/-1 until end of turn. - Effect effect = new BoostTargetEffect(1, 1, Duration.EndOfTurn); - effect.setText("Target creature gets +1/+1 until end of turn"); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (getting the +1/+1 counter)"))); - effect = new BoostTargetEffect(-1, -1, Duration.EndOfTurn); - effect.setText("Another target creature gets -1/-1 until end of turn"); - effect.setTargetPointer(new SecondTargetPointer()); - this.getSpellAbility().addEffect(effect); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(new FilterCreaturePermanent("creature (getting the -1/-1 counter)"))); + this.getSpellAbility().addEffect(new LeechingBiteEffect()); + + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature to get +1/+1"); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature to get -1/-1"); + filter2.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); } public LeechingBite(final LeechingBite card) { @@ -69,3 +78,35 @@ public class LeechingBite extends CardImpl { return new LeechingBite(this); } } + +class LeechingBiteEffect extends ContinuousEffectImpl { + + public LeechingBiteEffect() { + super(Duration.EndOfTurn, Layer.PTChangingEffects_7, SubLayer.ModifyPT_7c, Outcome.BoostCreature); + this.staticText = "Target creature gets +1/+1 until end of turn. Another target creature gets -1/-1 until end of turn"; + } + + public LeechingBiteEffect(final LeechingBiteEffect effect) { + super(effect); + } + + @Override + public LeechingBiteEffect copy() { + return new LeechingBiteEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + Permanent permanent = game.getPermanent(source.getFirstTarget()); + if (permanent != null) { + permanent.addPower(1); + permanent.addToughness(1); + } + permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); + if (permanent != null) { + permanent.addPower(-1); + permanent.addToughness(-1); + } + return true; + } +} diff --git a/Mage.Sets/src/mage/sets/planarchaos/DismalFailure.java b/Mage.Sets/src/mage/sets/planarchaos/DismalFailure.java new file mode 100644 index 00000000000..a6a9a3ab524 --- /dev/null +++ b/Mage.Sets/src/mage/sets/planarchaos/DismalFailure.java @@ -0,0 +1,99 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.planarchaos; + +import java.util.UUID; +import mage.abilities.Ability; +import mage.abilities.effects.OneShotEffect; +import mage.cards.CardImpl; +import mage.constants.CardType; +import mage.constants.Outcome; +import mage.constants.Rarity; +import mage.game.Game; +import mage.players.Player; +import mage.target.TargetSpell; + +/** + * + * @author LevelX2 + */ +public class DismalFailure extends CardImpl { + + public DismalFailure(UUID ownerId) { + super(ownerId, 39, "Dismal Failure", Rarity.UNCOMMON, new CardType[]{CardType.INSTANT}, "{2}{U}{U}"); + this.expansionSetCode = "PLC"; + + // Counter target spell. Its controller discards a card. + this.getSpellAbility().addEffect(new DismalFailureEffect()); + this.getSpellAbility().addTarget(new TargetSpell()); + } + + public DismalFailure(final DismalFailure card) { + super(card); + } + + @Override + public DismalFailure copy() { + return new DismalFailure(this); + } +} + +class DismalFailureEffect extends OneShotEffect { + + public DismalFailureEffect() { + super(Outcome.Neutral); + this.staticText = "Counter target spell. Its controller discards a card"; + } + + public DismalFailureEffect(final DismalFailureEffect effect) { + super(effect); + } + + @Override + public DismalFailureEffect copy() { + return new DismalFailureEffect(this); + } + + @Override + public boolean apply(Game game, Ability source) { + UUID targetId = source.getFirstTarget(); + Player controller = null; + boolean countered = false; + if (targetId != null) { + controller = game.getPlayer(game.getControllerId(targetId)); + } + if (targetId != null + && game.getStack().counter(targetId, source.getSourceId(), game)) { + countered = true; + } + if (controller != null) { + controller.discard(1, false, source, game); + } + return countered; + } +} diff --git a/Mage.Sets/src/mage/sets/prophecy/StealStrength.java b/Mage.Sets/src/mage/sets/prophecy/StealStrength.java index c0dc42b53d9..514b0001b8c 100644 --- a/Mage.Sets/src/mage/sets/prophecy/StealStrength.java +++ b/Mage.Sets/src/mage/sets/prophecy/StealStrength.java @@ -37,6 +37,8 @@ import mage.constants.Layer; import mage.constants.Outcome; import mage.constants.Rarity; import mage.constants.SubLayer; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -53,7 +55,17 @@ public class StealStrength extends CardImpl { // Target creature gets +1/+1 until end of turn. Another target creature gets -1/-1 until end of turn. this.getSpellAbility().addEffect(new StealStrengthEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); + + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature (gets +1/+1 until end of turn)"); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature (gets -1/-1 until end of turn)"); + filter2.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); } public StealStrength(final StealStrength card) { @@ -89,7 +101,7 @@ class StealStrengthEffect extends ContinuousEffectImpl { permanent.addPower(1); permanent.addToughness(1); } - permanent = game.getPermanent(source.getTargets().get(0).getTargets().get(1)); + permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent != null) { permanent.addPower(-1); permanent.addToughness(-1); diff --git a/Mage.Sets/src/mage/sets/returntoravnica/RitesOfReaping.java b/Mage.Sets/src/mage/sets/returntoravnica/RitesOfReaping.java index b51d0f6adc1..07494f51551 100644 --- a/Mage.Sets/src/mage/sets/returntoravnica/RitesOfReaping.java +++ b/Mage.Sets/src/mage/sets/returntoravnica/RitesOfReaping.java @@ -37,6 +37,8 @@ import mage.constants.SubLayer; import mage.abilities.Ability; import mage.abilities.effects.ContinuousEffectImpl; import mage.cards.CardImpl; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -54,7 +56,17 @@ public class RitesOfReaping extends CardImpl { // Target creature gets +3/+3 until end of turn. Another target creature gets -3/-3 until end of turn. this.getSpellAbility().addEffect(new RitesOfReapingEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(2)); + + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature (gets +3/+3 until end of turn)"); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature (gets -3/-3 until end of turn)"); + filter2.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); } public RitesOfReaping(final RitesOfReaping card) { @@ -90,7 +102,7 @@ class RitesOfReapingEffect extends ContinuousEffectImpl { permanent.addPower(3); permanent.addToughness(3); } - permanent = game.getPermanent(source.getTargets().get(0).getTargets().get(1)); + permanent = game.getPermanent(source.getTargets().get(1).getFirstTarget()); if (permanent != null) { permanent.addPower(-3); permanent.addToughness(-3); diff --git a/Mage.Sets/src/mage/sets/revisededition/LibraryOfLeng.java b/Mage.Sets/src/mage/sets/revisededition/LibraryOfLeng.java new file mode 100644 index 00000000000..ea198f03c13 --- /dev/null +++ b/Mage.Sets/src/mage/sets/revisededition/LibraryOfLeng.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.revisededition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class LibraryOfLeng extends mage.sets.limitedbeta.LibraryOfLeng { + + public LibraryOfLeng(UUID ownerId) { + super(ownerId); + this.cardNumber = 261; + this.expansionSetCode = "3ED"; + } + + public LibraryOfLeng(final LibraryOfLeng card) { + super(card); + } + + @Override + public LibraryOfLeng copy() { + return new LibraryOfLeng(this); + } +} diff --git a/Mage.Sets/src/mage/sets/shadowmoor/CragganwickCremator.java b/Mage.Sets/src/mage/sets/shadowmoor/CragganwickCremator.java index 1a18f370549..7cadbaed388 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/CragganwickCremator.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/CragganwickCremator.java @@ -31,9 +31,7 @@ import java.util.UUID; import mage.MageInt; import mage.abilities.Ability; import mage.abilities.common.EntersBattlefieldTriggeredAbility; -import mage.abilities.effects.Effect; import mage.abilities.effects.OneShotEffect; -import mage.abilities.effects.common.discard.DiscardTargetEffect; import mage.cards.Card; import mage.cards.CardImpl; import mage.constants.CardType; @@ -42,7 +40,6 @@ import mage.constants.Rarity; import mage.game.Game; import mage.players.Player; import mage.target.TargetPlayer; -import mage.target.targetpointer.FixedTarget; /** * @@ -94,14 +91,17 @@ class CragganwickCrematorEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { - Player you = game.getPlayer(source.getControllerId()); - Player targetedPlayer = game.getPlayer(source.getFirstTarget()); - if (you != null && targetedPlayer != null) { - Card discardedCard = targetedPlayer.discardOne(true, source, game); + Player controller = game.getPlayer(source.getControllerId()); + + if (controller != null) { + Card discardedCard = controller.discardOne(true, source, game); if (discardedCard != null && discardedCard.getCardType().contains(CardType.CREATURE)) { - int damage = discardedCard.getPower().getValue(); - targetedPlayer.damage(damage, source.getSourceId(), game, false, true); + Player targetedPlayer = game.getPlayer(source.getFirstTarget()); + if (targetedPlayer != null) { + int damage = discardedCard.getPower().getValue(); + targetedPlayer.damage(damage, source.getSourceId(), game, false, true); + } } return true; } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/FateTransfer.java b/Mage.Sets/src/mage/sets/shadowmoor/FateTransfer.java index 811237a3b48..6cc21583286 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/FateTransfer.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/FateTransfer.java @@ -36,6 +36,7 @@ import mage.constants.Outcome; import mage.constants.Rarity; import mage.counters.Counter; import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetCreaturePermanent; @@ -47,7 +48,7 @@ import mage.target.common.TargetCreaturePermanent; public class FateTransfer extends CardImpl { private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("target creature to move all counters from"); - private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("target creature to move all counters to"); + private static final FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another target creature to move all counters to"); public FateTransfer(UUID ownerId) { super(ownerId, 161, "Fate Transfer", Rarity.COMMON, new CardType[]{CardType.INSTANT}, "{1}{U/B}"); @@ -55,8 +56,15 @@ public class FateTransfer extends CardImpl { // Move all counters from target creature onto another target creature. this.getSpellAbility().addEffect(new FateTransferEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter)); - this.getSpellAbility().addTarget(new TargetCreaturePermanent(filter2)); + + TargetCreaturePermanent fromTarget = new TargetCreaturePermanent(filter); + fromTarget.setTargetTag(1); + this.getSpellAbility().addTarget(fromTarget); + + TargetCreaturePermanent toTarget = new TargetCreaturePermanent(filter2); + filter2.add(new AnotherTargetPredicate(2)); + toTarget.setTargetTag(2); + this.getSpellAbility().addTarget(toTarget); } diff --git a/Mage.Sets/src/mage/sets/shadowmoor/IncrementalBlight.java b/Mage.Sets/src/mage/sets/shadowmoor/IncrementalBlight.java index 52e1c7100a8..b2641294305 100644 --- a/Mage.Sets/src/mage/sets/shadowmoor/IncrementalBlight.java +++ b/Mage.Sets/src/mage/sets/shadowmoor/IncrementalBlight.java @@ -35,6 +35,8 @@ import mage.abilities.Ability; import mage.abilities.effects.OneShotEffect; import mage.cards.CardImpl; import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.Target; @@ -53,8 +55,23 @@ public class IncrementalBlight extends CardImpl { // Put a -1/-1 counter on target creature, two -1/-1 counters on another target creature, and three -1/-1 counters on a third target creature. this.getSpellAbility().addEffect(new IncrementalBlightEffect()); - Target target = new TargetCreaturePermanent(3,3); - this.getSpellAbility().addTarget(target); + + FilterCreaturePermanent filter1 = new FilterCreaturePermanent("creature (gets a -1/-1 counter)"); + TargetCreaturePermanent target1 = new TargetCreaturePermanent(filter1); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + + FilterCreaturePermanent filter2 = new FilterCreaturePermanent("another creature (gets two -1/-1 counters)"); + filter2.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter2); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); + + FilterCreaturePermanent filter3 = new FilterCreaturePermanent("another creature (gets three -1/-1 counters)"); + filter3.add(new AnotherTargetPredicate(3)); + TargetCreaturePermanent target3 = new TargetCreaturePermanent(filter3); + target3.setTargetTag(3); + this.getSpellAbility().addTarget(target3); } public IncrementalBlight(final IncrementalBlight card) { @@ -85,9 +102,9 @@ class IncrementalBlightEffect extends OneShotEffect { @Override public boolean apply(Game game, Ability source) { int i = 0; - for (UUID targetId : getTargetPointer().getTargets(game, source)) { + for (Target target : source.getTargets()) { i++; - Permanent creature = game.getPermanent(targetId); + Permanent creature = game.getPermanent(target.getFirstTarget()); if (creature != null) { creature.addCounters(CounterType.M1M1.createInstance(i), game); } diff --git a/Mage.Sets/src/mage/sets/shardsofalara/RealmRazer.java b/Mage.Sets/src/mage/sets/shardsofalara/RealmRazer.java index 0f7291ab53e..7119873a085 100644 --- a/Mage.Sets/src/mage/sets/shardsofalara/RealmRazer.java +++ b/Mage.Sets/src/mage/sets/shardsofalara/RealmRazer.java @@ -124,7 +124,7 @@ class RealmRazerEffect extends OneShotEffect { if (controller != null) { ExileZone exZone = game.getExile().getExileZone(source.getSourceId()); if (exZone != null) { - return controller.moveCards(exZone.getCards(game), Zone.BATTLEFIELD, source, game, true, false, false, null); + return controller.moveCards(exZone.getCards(game), Zone.BATTLEFIELD, source, game, true, false, true, null); } return true; } diff --git a/Mage.Sets/src/mage/sets/unlimitededition/LibraryOfLeng.java b/Mage.Sets/src/mage/sets/unlimitededition/LibraryOfLeng.java new file mode 100644 index 00000000000..0db34db7436 --- /dev/null +++ b/Mage.Sets/src/mage/sets/unlimitededition/LibraryOfLeng.java @@ -0,0 +1,52 @@ +/* + * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without modification, are + * permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, this list of + * conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright notice, this list + * of conditions and the following disclaimer in the documentation and/or other materials + * provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are those of the + * authors and should not be interpreted as representing official policies, either expressed + * or implied, of BetaSteward_at_googlemail.com. + */ +package mage.sets.unlimitededition; + +import java.util.UUID; + +/** + * + * @author LevelX2 + */ +public class LibraryOfLeng extends mage.sets.limitedbeta.LibraryOfLeng { + + public LibraryOfLeng(UUID ownerId) { + super(ownerId); + this.cardNumber = 258; + this.expansionSetCode = "2ED"; + } + + public LibraryOfLeng(final LibraryOfLeng card) { + super(card); + } + + @Override + public LibraryOfLeng copy() { + return new LibraryOfLeng(this); + } +} diff --git a/Mage.Sets/src/mage/sets/worldwake/FeralContest.java b/Mage.Sets/src/mage/sets/worldwake/FeralContest.java index b519cab6c9c..2ff3ec2b193 100644 --- a/Mage.Sets/src/mage/sets/worldwake/FeralContest.java +++ b/Mage.Sets/src/mage/sets/worldwake/FeralContest.java @@ -37,6 +37,8 @@ import mage.abilities.effects.common.counter.AddCountersTargetEffect; import mage.cards.CardImpl; import mage.constants.Duration; import mage.counters.CounterType; +import mage.filter.common.FilterCreaturePermanent; +import mage.filter.predicate.mageobject.AnotherTargetPredicate; import mage.game.Game; import mage.game.permanent.Permanent; import mage.target.common.TargetControlledCreaturePermanent; @@ -54,10 +56,18 @@ public class FeralContest extends CardImpl { // Put a +1/+1 counter on target creature you control. this.getSpellAbility().addEffect(new AddCountersTargetEffect(CounterType.P1P1.createInstance())); - this.getSpellAbility().addTarget(new TargetControlledCreaturePermanent()); + + TargetControlledCreaturePermanent target1 = new TargetControlledCreaturePermanent(); + target1.setTargetTag(1); + this.getSpellAbility().addTarget(target1); + // Another target creature blocks it this turn if able. this.getSpellAbility().addEffect(new FeralContestEffect()); - this.getSpellAbility().addTarget(new TargetCreaturePermanent()); + FilterCreaturePermanent filter = new FilterCreaturePermanent("another creature (must block this turn)"); + filter.add(new AnotherTargetPredicate(2)); + TargetCreaturePermanent target2 = new TargetCreaturePermanent(filter); + target2.setTargetTag(2); + this.getSpellAbility().addTarget(target2); } public FeralContest(final FeralContest card) { diff --git a/Mage.Stats/pom.xml b/Mage.Stats/pom.xml index 63c9fa979b0..8393d25c828 100644 --- a/Mage.Stats/pom.xml +++ b/Mage.Stats/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 org.mage diff --git a/Mage.Tests/pom.xml b/Mage.Tests/pom.xml index 6714da9223a..279c225f293 100644 --- a/Mage.Tests/pom.xml +++ b/Mage.Tests/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 mage-tests diff --git a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java index 280c227ac81..a4e73e15331 100644 --- a/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java +++ b/Mage.Tests/src/test/java/org/mage/test/player/TestPlayer.java @@ -2077,4 +2077,8 @@ public class TestPlayer implements Player { return AIPlayer; } + public String getHistory() { + return computerPlayer.getHistory(); + } + } diff --git a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java index bcfa18d0f54..ecc5c3df051 100644 --- a/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java +++ b/Mage.Tests/src/test/java/org/mage/test/serverside/base/MageTestBase.java @@ -1,9 +1,19 @@ package org.mage.test.serverside.base; -import mage.constants.PhaseStep; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FilenameFilter; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Scanner; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import mage.cards.Card; import mage.cards.repository.CardInfo; import mage.cards.repository.CardRepository; +import mage.constants.PhaseStep; import mage.constants.RangeOfInfluence; import mage.constants.Zone; import mage.game.Game; @@ -25,19 +35,13 @@ import org.junit.BeforeClass; import org.mage.test.player.RandomPlayer; import org.mage.test.player.TestPlayer; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FilenameFilter; -import java.util.*; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - /** * Base class for all tests. * * @author ayratn */ public abstract class MageTestBase { + protected static Logger logger = Logger.getLogger(MageTestBase.class); public static PluginClassLoader classLoader = new PluginClassLoader(); @@ -46,17 +50,17 @@ public abstract class MageTestBase { protected Pattern pattern = Pattern.compile("([a-zA-Z]*):([\\w]*):([a-zA-Z ,\\-.!'\\d]*):([\\d]*)(:\\{tapped\\})?"); - protected List handCardsA = new ArrayList(); - protected List handCardsB = new ArrayList(); - protected List battlefieldCardsA = new ArrayList(); - protected List battlefieldCardsB = new ArrayList(); - protected List graveyardCardsA = new ArrayList(); - protected List graveyardCardsB = new ArrayList(); - protected List libraryCardsA = new ArrayList(); - protected List libraryCardsB = new ArrayList(); + protected List handCardsA = new ArrayList<>(); + protected List handCardsB = new ArrayList<>(); + protected List battlefieldCardsA = new ArrayList<>(); + protected List battlefieldCardsB = new ArrayList<>(); + protected List graveyardCardsA = new ArrayList<>(); + protected List graveyardCardsB = new ArrayList<>(); + protected List libraryCardsA = new ArrayList<>(); + protected List libraryCardsB = new ArrayList<>(); - protected Map commandsA = new HashMap(); - protected Map commandsB = new HashMap(); + protected Map commandsA = new HashMap<>(); + protected Map commandsB = new HashMap<>(); protected TestPlayer playerA; protected TestPlayer playerB; @@ -67,8 +71,7 @@ public abstract class MageTestBase { protected static Game currentGame = null; /** - * Player thats starts the game first. - * By default, it is ComputerA. + * Player thats starts the game first. By default, it is ComputerA. */ protected static Player activePlayer = null; @@ -77,6 +80,7 @@ public abstract class MageTestBase { protected PhaseStep stopAtStep = PhaseStep.UNTAP; protected enum ParserState { + INIT, OPTIONS, EXPECTED @@ -85,18 +89,13 @@ public abstract class MageTestBase { protected ParserState parserState; /** - * Expected results of the test. - * Read from test case in {@link String} based format: + * Expected results of the test. Read from test case in {@link String} based + * format: *

- * Example: - * turn:1 - * result:won:ComputerA - * life:ComputerA:20 - * life:ComputerB:0 - * battlefield:ComputerB:Tine Shrike:0 - * graveyard:ComputerB:Tine Shrike:1 + * Example: turn:1 result:won:ComputerA life:ComputerA:20 life:ComputerB:0 + * battlefield:ComputerB:Tine Shrike:0 graveyard:ComputerB:Tine Shrike:1 */ - protected List expectedResults = new ArrayList(); + protected List expectedResults = new ArrayList<>(); protected static final String TESTS_PATH = "tests" + File.separator; @@ -163,8 +162,9 @@ public abstract class MageTestBase { private static void deleteSavedGames() { File directory = new File("saved/"); - if (!directory.exists()) + if (!directory.exists()) { directory.mkdirs(); + } File[] files = directory.listFiles( new FilenameFilter() { @Override @@ -185,7 +185,9 @@ public abstract class MageTestBase { try { while (scanner.hasNextLine()) { String line = scanner.nextLine().trim(); - if (line == null || line.isEmpty() || line.startsWith("#")) continue; + if (line == null || line.isEmpty() || line.startsWith("#")) { + continue; + } if (line.startsWith("$include")) { includeFrom(line); continue; diff --git a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java index d076ed3265c..18288af786a 100644 --- a/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java +++ b/Mage.Tests/src/test/java/org/mage/test/stub/PlayerStub.java @@ -1237,4 +1237,10 @@ public class PlayerStub implements Player { public boolean addTargets(Ability ability, Game game) { return false; } + + @Override + public String getHistory() { + return ""; + } + } diff --git a/Mage.Updater/pom.xml b/Mage.Updater/pom.xml index 2e452c1d49f..cc2e47e1b97 100644 --- a/Mage.Updater/pom.xml +++ b/Mage.Updater/pom.xml @@ -5,7 +5,7 @@ mage-root org.mage - 1.4.7 + 1.4.8 4.0.0 diff --git a/Mage/pom.xml b/Mage/pom.xml index d81c9655d9b..30c0b461c1e 100644 --- a/Mage/pom.xml +++ b/Mage/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 mage diff --git a/Mage/src/main/java/mage/cards/repository/CardRepository.java b/Mage/src/main/java/mage/cards/repository/CardRepository.java index 855a16527ef..cc140503ba2 100644 --- a/Mage/src/main/java/mage/cards/repository/CardRepository.java +++ b/Mage/src/main/java/mage/cards/repository/CardRepository.java @@ -63,7 +63,7 @@ public enum CardRepository { // raise this if db structure was changed private static final long CARD_DB_VERSION = 43; // raise this if new cards were added to the server - private static final long CARD_CONTENT_VERSION = 44; + private static final long CARD_CONTENT_VERSION = 45; private final Random random = new Random(); private Dao cardDao; diff --git a/Mage/src/main/java/mage/game/Seat.java b/Mage/src/main/java/mage/game/Seat.java index 751a18d3932..f9a3325ac23 100644 --- a/Mage/src/main/java/mage/game/Seat.java +++ b/Mage/src/main/java/mage/game/Seat.java @@ -24,8 +24,7 @@ * The views and conclusions contained in the software and documentation are those of the * authors and should not be interpreted as representing official policies, either expressed * or implied, of BetaSteward_at_googlemail.com. -*/ - + */ package mage.game; import java.io.Serializable; @@ -38,7 +37,6 @@ import mage.players.Player; public class Seat implements Serializable { // private static final Logger logger = Logger.getLogger(Seat.class); - private String playerType; private Player player; diff --git a/Mage/src/main/java/mage/game/events/GameEvent.java b/Mage/src/main/java/mage/game/events/GameEvent.java index 95b1a318e67..46cce3480e9 100644 --- a/Mage/src/main/java/mage/game/events/GameEvent.java +++ b/Mage/src/main/java/mage/game/events/GameEvent.java @@ -91,6 +91,7 @@ public class GameEvent implements Serializable { DRAW_CARD, DREW_CARD, MIRACLE_CARD_REVEALED, MADNESS_CARD_EXILED, + DISCARD_CARD, DISCARDED_CARD, CYCLE_CARD, CYCLED_CARD, CLASH, CLASHED, diff --git a/Mage/src/main/java/mage/players/Player.java b/Mage/src/main/java/mage/players/Player.java index 654f503e0c8..9bad10ada90 100644 --- a/Mage/src/main/java/mage/players/Player.java +++ b/Mage/src/main/java/mage/players/Player.java @@ -824,4 +824,6 @@ public interface Player extends MageItem, Copyable { * @return */ boolean addTargets(Ability ability, Game game); + + String getHistory(); } diff --git a/Mage/src/main/java/mage/players/PlayerImpl.java b/Mage/src/main/java/mage/players/PlayerImpl.java index 07ac9fb6e8f..301900b70cf 100644 --- a/Mage/src/main/java/mage/players/PlayerImpl.java +++ b/Mage/src/main/java/mage/players/PlayerImpl.java @@ -92,6 +92,14 @@ import mage.constants.ManaType; import mage.constants.Outcome; import mage.constants.PhaseStep; import mage.constants.PlayerAction; +import static mage.constants.PlayerAction.PASS_PRIORITY_CANCEL_ALL_ACTIONS; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_MY_NEXT_TURN; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_NEXT_MAIN_PHASE; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_NEXT_TURN; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_STACK_RESOLVED; +import static mage.constants.PlayerAction.PASS_PRIORITY_UNTIL_TURN_END_STEP; +import static mage.constants.PlayerAction.PERMISSION_REQUESTS_ALLOWED_OFF; +import static mage.constants.PlayerAction.PERMISSION_REQUESTS_ALLOWED_ON; import mage.constants.RangeOfInfluence; import mage.constants.SpellAbilityType; import mage.constants.TimingRule; @@ -516,31 +524,29 @@ public abstract class PlayerImpl implements Player, Serializable { inRange.add(player.getId()); } } + } else if ((range.getRange() * 2) + 1 >= game.getPlayers().size()) { + for (Player player : game.getPlayers().values()) { + if (!player.hasLeft()) { + inRange.add(player.getId()); + } + } } else { - if ((range.getRange() * 2) + 1 >= game.getPlayers().size()) { - for (Player player : game.getPlayers().values()) { - if (!player.hasLeft()) { - inRange.add(player.getId()); - } + inRange.add(playerId); + PlayerList players = game.getState().getPlayerList(playerId); + for (int i = 0; i < range.getRange(); i++) { + Player player = players.getNext(game); + while (player.hasLeft()) { + player = players.getNext(game); } - } else { - inRange.add(playerId); - PlayerList players = game.getState().getPlayerList(playerId); - for (int i = 0; i < range.getRange(); i++) { - Player player = players.getNext(game); - while (player.hasLeft()) { - player = players.getNext(game); - } - inRange.add(player.getId()); - } - players = game.getState().getPlayerList(playerId); - for (int i = 0; i < range.getRange(); i++) { - Player player = players.getPrevious(game); - while (player.hasLeft()) { - player = players.getPrevious(game); - } - inRange.add(player.getId()); + inRange.add(player.getId()); + } + players = game.getState().getPlayerList(playerId); + for (int i = 0; i < range.getRange(); i++) { + Player player = players.getPrevious(game); + while (player.hasLeft()) { + player = players.getPrevious(game); } + inRange.add(player.getId()); } } } @@ -760,7 +766,8 @@ public abstract class PlayerImpl implements Player, Serializable { about the discarded card, that cost payment is illegal; the game returns to the moment before the cost was paid (see rule 717, "Handling Illegal Actions"). */ - if (card != null) { + if (card != null + && !game.replaceEvent(GameEvent.getEvent(GameEvent.EventType.DISCARD_CARD, card.getId(), source == null ? null : source.getSourceId(), playerId), source)) { // write info to game log first so game log infos from triggered or replacement effects follow in the game log if (!game.isSimulation()) { game.informPlayers(getLogName() + " discards " + card.getLogName()); @@ -2089,7 +2096,8 @@ public abstract class PlayerImpl implements Player, Serializable { break; } } - if (!opponentInGame || // if no more opponent is in game the wins event may no longer be replaced + if (!opponentInGame + || // if no more opponent is in game the wins event may no longer be replaced !game.replaceEvent(new GameEvent(GameEvent.EventType.WINS, null, null, playerId))) { logger.debug("player won -> start: " + this.getName()); if (!this.loses) { @@ -2169,10 +2177,8 @@ public abstract class PlayerImpl implements Player, Serializable { if (blocker != null && group != null && group.canBlock(blocker, game)) { group.addBlocker(blockerId, playerId, game); game.getCombat().addBlockingGroup(blockerId, attackerId, playerId, game); - } else { - if (this.isHuman() && !game.isSimulation()) { - game.informPlayer(this, "You can't block this creature."); - } + } else if (this.isHuman() && !game.isSimulation()) { + game.informPlayer(this, "You can't block this creature."); } } @@ -2793,14 +2799,12 @@ public abstract class PlayerImpl implements Player, Serializable { } if (targetNum < option.getTargets().size() - 2) { addTargetOptions(options, newOption, targetNum + 1, game); + } else if (option.getChoices().size() > 0) { + addChoiceOptions(options, newOption, 0, game); + } else if (option.getCosts().getTargets().size() > 0) { + addCostTargetOptions(options, newOption, 0, game); } else { - if (option.getChoices().size() > 0) { - addChoiceOptions(options, newOption, 0, game); - } else if (option.getCosts().getTargets().size() > 0) { - addCostTargetOptions(options, newOption, 0, game); - } else { - options.add(newOption); - } + options.add(newOption); } } } @@ -2811,12 +2815,10 @@ public abstract class PlayerImpl implements Player, Serializable { newOption.getChoices().get(choiceNum).setChoice(choice); if (choiceNum < option.getChoices().size() - 1) { addChoiceOptions(options, newOption, choiceNum + 1, game); + } else if (option.getCosts().getTargets().size() > 0) { + addCostTargetOptions(options, newOption, 0, game); } else { - if (option.getCosts().getTargets().size() > 0) { - addCostTargetOptions(options, newOption, 0, game); - } else { - options.add(newOption); - } + options.add(newOption); } } } @@ -3503,4 +3505,9 @@ public abstract class PlayerImpl implements Player, Serializable { return true; } + @Override + public String getHistory() { + return "no available"; + } + } diff --git a/Mage/src/main/java/mage/players/net/UserData.java b/Mage/src/main/java/mage/players/net/UserData.java index c2c3b2a3bb4..bca9f231700 100644 --- a/Mage/src/main/java/mage/players/net/UserData.java +++ b/Mage/src/main/java/mage/players/net/UserData.java @@ -23,6 +23,8 @@ public class UserData implements Serializable { protected boolean passPriorityActivation; protected boolean autoOrderTrigger; + protected String history; + public UserData(UserGroup userGroup, int avatarId, boolean showAbilityPickerForced, boolean allowRequestShowHandCards, boolean confirmEmptyManaPool, UserSkipPrioritySteps userSkipPrioritySteps, String flagName, boolean askMoveToGraveOrder, boolean manaPoolAutomatic, boolean manaPoolAutomaticRestricted, @@ -40,6 +42,7 @@ public class UserData implements Serializable { this.passPriorityCast = passPriorityCast; this.passPriorityActivation = passPriorityActivation; this.autoOrderTrigger = autoOrderTrigger; + this.history = ""; } public void update(UserData userData) { @@ -166,7 +169,16 @@ public class UserData implements Serializable { this.autoOrderTrigger = autoOrderTrigger; } + public void setHistory(String history) { + this.history = history; + } + + public String getHistory() { + return history; + } + public static String getDefaultFlagName() { return "world.png"; } + } diff --git a/Utils/release/getting_implemented_cards.txt b/Utils/release/getting_implemented_cards.txt index 8a4b8ad0f8e..06cf52b409a 100644 --- a/Utils/release/getting_implemented_cards.txt +++ b/Utils/release/getting_implemented_cards.txt @@ -54,6 +54,9 @@ git log 2ad15bbd48d5ae34b0cb5d709895d406b977d104..head --diff-filter=A --name-st since 1.4.7v0 git log 8b37d0b989ba19f0dfccc81db66f5a21cc71fb94..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt +since 1.4.8v0 +git log 804f9e7fc2b481f7f784943409f558a671088372..head --diff-filter=A --name-status | sed -ne "s/^A[^u]Mage.Sets\/src\/mage\/sets\///p" | sort > added_cards.txt + 3. Copy added_cards.txt to trunk\Utils folder 4. Run script: > perl extract_in_wiki_format.perl diff --git a/hs_err_pid4700.log b/hs_err_pid4700.log deleted file mode 100644 index e8d083da24a..00000000000 --- a/hs_err_pid4700.log +++ /dev/null @@ -1,141 +0,0 @@ -# -# There is insufficient memory for the Java Runtime Environment to continue. -# Native memory allocation (malloc) failed to allocate 1048576 bytes for AllocateHeap -# Possible reasons: -# The system is out of physical RAM or swap space -# In 32 bit mode, the process size limit was hit -# Possible solutions: -# Reduce memory load on the system -# Increase physical memory or swap space -# Check if swap backing store is full -# Use 64 bit Java on a 64 bit OS -# Decrease Java heap size (-Xmx/-Xms) -# Decrease number of Java threads -# Decrease Java thread stack sizes (-Xss) -# Set larger code cache with -XX:ReservedCodeCacheSize= -# This output file may be truncated or incomplete. -# -# Out of Memory Error (memory/allocation.inline.hpp:61), pid=4700, tid=4912 -# -# JRE version: (7.0_71-b14) (build ) -# Java VM: Java HotSpot(TM) 64-Bit Server VM (24.71-b01 mixed mode windows-amd64 compressed oops) -# Failed to write core dump. Minidumps are not enabled by default on client versions of Windows -# - ---------------- T H R E A D --------------- - -Current thread (0x000000000223f000): JavaThread "Unknown thread" [_thread_in_vm, id=4912, stack(0x00000000025b0000,0x00000000026b0000)] - -Stack: [0x00000000025b0000,0x00000000026b0000] -[error occurred during error reporting (printing stack bounds), id 0xc0000005] - - ---------------- P R O C E S S --------------- - -Java Threads: ( => current thread ) - -Other Threads: - -=>0x000000000223f000 (exited) JavaThread "Unknown thread" [_thread_in_vm, id=4912, stack(0x00000000025b0000,0x00000000026b0000)] - -VM state:not at safepoint (normal execution) - -VM Mutex/Monitor currently owned by a thread: None - -Heap - PSYoungGen total 612352K, used 10506K [0x00000007d5500000, 0x0000000800000000, 0x0000000800000000) - eden space 525312K, 2% used [0x00000007d5500000,0x00000007d5f42948,0x00000007f5600000) - from space 87040K, 0% used [0x00000007fab00000,0x00000007fab00000,0x0000000800000000) - to space 87040K, 0% used [0x00000007f5600000,0x00000007f5600000,0x00000007fab00000) - ParOldGen total 1398272K, used 0K [0x000000077ff80000, 0x00000007d5500000, 0x00000007d5500000) - object space 1398272K, 0% used [0x000000077ff80000,0x000000077ff80000,0x00000007d5500000) - PSPermGen total 21504K, used 694K [0x000000077ad80000, 0x000000077c280000, 0x000000077ff80000) - object space 21504K, 3% used [0x000000077ad80000,0x000000077ae2d9f8,0x000000077c280000) - -Card table byte_map: [0x00000000056b0000,0x0000000005ae0000] byte_map_base: 0x0000000001ad9400 - -Polling page: 0x0000000000240000 - -Code Cache [0x00000000026b0000, 0x0000000002920000, 0x00000000056b0000) - total_blobs=37 nmethods=0 adapters=20 free_code_cache=48890Kb largest_free_block=50063104 - -Compilation events (0 events): -No events - -GC Heap History (0 events): -No events - -Deoptimization events (0 events): -No events - -Internal exceptions (0 events): -No events - -Events (10 events): -Event: 0.029 loading class 0x00000000022bed40 -Event: 0.029 loading class 0x00000000022bed40 done -Event: 0.029 loading class 0x00000000022bed90 -Event: 0.029 loading class 0x00000000022bed90 done -Event: 0.029 loading class 0x00000000022bede0 -Event: 0.029 loading class 0x00000000022bede0 done -Event: 0.030 loading class 0x00000000022bfb40 -Event: 0.030 loading class 0x00000000022bfb40 done -Event: 0.030 loading class 0x00000000022bf760 -Event: 0.030 loading class 0x00000000022bf760 done - - -Dynamic libraries: -0x000000013f320000 - 0x000000013f353000 C:\Program Files\Java\jdk1.7.0_71\bin\java.exe -0x00000000770c0000 - 0x0000000077269000 C:\Windows\SYSTEM32\ntdll.dll -0x0000000076ea0000 - 0x0000000076fbf000 C:\Windows\system32\kernel32.dll -0x000007fefd170000 - 0x000007fefd1dc000 C:\Windows\system32\KERNELBASE.dll -0x000007fefd7c0000 - 0x000007fefd89b000 C:\Windows\system32\ADVAPI32.dll -0x000007feff300000 - 0x000007feff39f000 C:\Windows\system32\msvcrt.dll -0x000007fefe7e0000 - 0x000007fefe7ff000 C:\Windows\SYSTEM32\sechost.dll -0x000007fefd8a0000 - 0x000007fefd9cd000 C:\Windows\system32\RPCRT4.dll -0x0000000076fc0000 - 0x00000000770ba000 C:\Windows\system32\USER32.dll -0x000007fefd230000 - 0x000007fefd297000 C:\Windows\system32\GDI32.dll -0x000007fefef50000 - 0x000007fefef5e000 C:\Windows\system32\LPK.dll -0x000007feff230000 - 0x000007feff2f9000 C:\Windows\system32\USP10.dll -0x000007fefb490000 - 0x000007fefb684000 C:\Windows\WinSxS\amd64_microsoft.windows.common-controls_6595b64144ccf1df_6.0.7601.18837_none_fa3b1e3d17594757\COMCTL32.dll -0x000007fefe760000 - 0x000007fefe7d1000 C:\Windows\system32\SHLWAPI.dll -0x000007feff3a0000 - 0x000007feff3ce000 C:\Windows\system32\IMM32.DLL -0x000007fefe9f0000 - 0x000007fefeaf9000 C:\Windows\system32\MSCTF.dll -0x000000006ab40000 - 0x000000006ac12000 C:\Program Files\Java\jdk1.7.0_71\jre\bin\msvcr100.dll -0x0000000067b10000 - 0x00000000682e3000 C:\Program Files\Java\jdk1.7.0_71\jre\bin\server\jvm.dll -0x000007fefa970000 - 0x000007fefa979000 C:\Windows\system32\WSOCK32.dll -0x000007fefd690000 - 0x000007fefd6dd000 C:\Windows\system32\WS2_32.dll -0x000007fefe9e0000 - 0x000007fefe9e8000 C:\Windows\system32\NSI.dll -0x000007fefaf60000 - 0x000007fefaf9b000 C:\Windows\system32\WINMM.dll -0x0000000077290000 - 0x0000000077297000 C:\Windows\system32\PSAPI.DLL -0x000000006f510000 - 0x000000006f51f000 C:\Program Files\Java\jdk1.7.0_71\jre\bin\verify.dll -0x000000006c820000 - 0x000000006c848000 C:\Program Files\Java\jdk1.7.0_71\jre\bin\java.dll -0x000000006c860000 - 0x000000006c875000 C:\Program Files\Java\jdk1.7.0_71\jre\bin\zip.dll - -VM Arguments: -jvm_args: -Xms2048m -Xmx2048m -Dclassworlds.conf=C:\Program Files\NetBeans 8.0.2\java\maven\bin\m2.conf -Dmaven.home=C:\Program Files\NetBeans 8.0.2\java\maven -java_command: org.codehaus.plexus.classworlds.launcher.Launcher -Dmaven.ext.class.path=C:\Users\fireshoes\AppData\Roaming\NetBeans\8.0.2\maven-nblib\netbeans-eventspy.jar -Dfile.encoding=UTF-8 clean install -Launcher Type: SUN_STANDARD - -Environment Variables: -JAVA_HOME=C:\Program Files\Java\jdk1.7.0_71 -PATH=c:\Program FIles\Apache Software Foundation\apache-maven-3.2.3\bin;C:\ProgramData\Oracle\Java\javapath;C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMD APP\bin\x86;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Program Files (x86)\Common Files\Microsoft Shared\Windows Live;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\Windows Live\Shared;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;C:\Program FIles\Java\jdk1.7.0_71\bin;C:\Strawberry\c\bin;C:\Strawberry\perl\site\bin;C:\Strawberry\perl\bin;C:\Program Files (x86)\Git\cmd;C:\apache-maven-3.3.9-src\apache-maven-3.3.9\apache-maven\src -USERNAME=fireshoes -OS=Windows_NT -PROCESSOR_IDENTIFIER=AMD64 Family 18 Model 1 Stepping 0, AuthenticAMD - - - ---------------- S Y S T E M --------------- - -OS: Windows 7 , 64 bit Build 7601 Service Pack 1 - -CPU:total 4 (4 cores per cpu, 1 threads per core) family 18 model 1 stepping 0, cmov, cx8, fxsr, mmx, sse, sse2, sse3, popcnt, mmxext, 3dnowpref, lzcnt, sse4a, tsc, tscinvbit, tscinv - -Memory: 4k page, physical 3559120k(796500k free), swap 7967852k(857396k free) - -vm_info: Java HotSpot(TM) 64-Bit Server VM (24.71-b01) for windows-amd64 JRE (1.7.0_71-b14), built on Sep 26 2014 16:16:12 by "java_re" with unknown MS VC++:1600 - -time: Wed Jan 20 09:21:37 2016 -elapsed time: 0 seconds - diff --git a/pom.xml b/pom.xml index a42d4eac758..5dbc875485c 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ org.mage mage-root - 1.4.7 + 1.4.8 pom Mage Root Mage Root POM @@ -83,7 +83,7 @@ - 1.4.7 + 1.4.8 UTF-8