This commit is contained in:
ingmargoudt 2017-03-20 10:06:53 +01:00
commit 992954eb12
47 changed files with 1586 additions and 506 deletions

View file

@ -1,9 +1,7 @@
package mage.client;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.*;
import mage.cards.decks.DeckCardLists;
import mage.client.chat.LocalCommands;
import mage.constants.ManaType;
@ -129,7 +127,7 @@ public final class SessionHandler {
return session.isTableOwner(roomId, tableId);
}
public static UUID getTableChatId(UUID tableId) {
public static Optional<UUID> getTableChatId(UUID tableId) {
return session.getTableChatId(tableId);
}
@ -141,7 +139,7 @@ public final class SessionHandler {
return session.startMatch(roomId, tableId);
}
public static UUID getGameChatId(UUID gameId) {
public static Optional<UUID> getGameChatId(UUID gameId) {
return session.getGameChatId(gameId);
}
@ -161,7 +159,7 @@ public final class SessionHandler {
return session.joinTournament(tournamentId);
}
public static UUID getTournamentChatId(UUID tournamentId) {
public static Optional<UUID> getTournamentChatId(UUID tournamentId) {
return session.getTournamentChatId(tournamentId);
}
@ -255,7 +253,7 @@ public final class SessionHandler {
session.sendCardMark(draftId, id);
}
public static UUID getRoomChatId(UUID roomId) {
public static Optional<UUID> getRoomChatId(UUID roomId) {
return session.getRoomChatId(roomId);
}
@ -264,7 +262,7 @@ public final class SessionHandler {
return session.getRoomUsers(roomId);
} catch (MageRemoteException e) {
e.printStackTrace();
return null;
return Collections.emptyList();
}
}
@ -273,7 +271,7 @@ public final class SessionHandler {
return session.getFinishedMatches(roomId);
} catch (MageRemoteException e) {
e.printStackTrace();
return null;
return new ArrayList<>();
}
}
@ -290,7 +288,7 @@ public final class SessionHandler {
return session.getTables(roomId);
} catch (MageRemoteException e) {
e.printStackTrace();
return null;
return new ArrayList<>();
}
}
@ -318,7 +316,7 @@ public final class SessionHandler {
return session.sendPlayerManaType(gameId, playerId, data);
}
public static TableView getTable(UUID roomId, UUID tableId) {
public static Optional<TableView> getTable(UUID roomId, UUID tableId) {
return session.getTable(roomId, tableId);
}

View file

@ -2984,10 +2984,10 @@ public class PreferencesDialog extends javax.swing.JDialog {
if (args.length > 0) {
String param1 = args[0];
if (param1.equals(OPEN_CONNECTION_TAB)) {
param = 4;
param = 6;
}
if (param1.equals(OPEN_PHASES_TAB)) {
param = 1;
param = 2;
}
}
final int openedTab = param;

View file

@ -35,19 +35,23 @@ package mage.client.dialog;
import java.awt.Dimension;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
import javax.swing.Icon;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.chat.ChatPanelBasic;
import mage.client.components.MageComponents;
import mage.client.components.tray.MageTray;
import static mage.client.dialog.PreferencesDialog.KEY_TABLE_WAITING_COLUMNS_ORDER;
import static mage.client.dialog.PreferencesDialog.KEY_TABLE_WAITING_COLUMNS_WIDTH;
import mage.client.util.GUISizeHelper;
import mage.client.util.audio.AudioManager;
import mage.client.util.gui.TableUtil;
@ -58,7 +62,6 @@ import mage.view.TableView;
import org.apache.log4j.Logger;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class TableWaitingDialog extends MageDialog {
@ -159,9 +162,9 @@ public class TableWaitingDialog extends MageDialog {
this.btnMoveDown.setVisible(false);
this.btnMoveUp.setVisible(false);
}
UUID chatId = SessionHandler.getTableChatId(tableId);
if (chatId != null) {
this.chatPanel.connect(chatId);
Optional<UUID> chatId = SessionHandler.getTableChatId(tableId);
if (chatId.isPresent()) {
this.chatPanel.connect(chatId.get());
updateTask.execute();
this.setModal(false);
this.setLocation(100, 100);
@ -416,8 +419,15 @@ class UpdateSeatsTask extends SwingWorker<Void, TableView> {
@Override
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
this.publish(SessionHandler.getTable(roomId, tableId));
SessionHandler.getTable(roomId, tableId).ifPresent(tableView -> {
this.publish(tableView);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
return null;
}

View file

@ -80,6 +80,7 @@ import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;
import javax.swing.plaf.basic.BasicSplitPaneDivider;
import javax.swing.plaf.basic.BasicSplitPaneUI;
import mage.cards.Card;
import mage.cards.action.ActionCallback;
import mage.choices.Choice;
@ -99,7 +100,9 @@ import mage.client.dialog.PickChoiceDialog;
import mage.client.dialog.PickNumberDialog;
import mage.client.dialog.PickPileDialog;
import mage.client.dialog.PreferencesDialog;
import static mage.client.dialog.PreferencesDialog.*;
import mage.client.dialog.ShowCardsDialog;
import mage.client.game.FeedbackPanel.FeedbackMode;
import mage.client.plugins.adapters.MageActionCallback;
@ -116,11 +119,13 @@ import mage.constants.Constants;
import mage.constants.EnlargeMode;
import mage.constants.PhaseStep;
import mage.constants.PlayerAction;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_ABILITY_FIRST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_ABILITY_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_FIRST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_NAME_LAST;
import static mage.constants.PlayerAction.TRIGGER_AUTO_ORDER_RESET_ALL;
import mage.constants.Zone;
import mage.game.events.PlayerQueryEvent;
import mage.view.AbilityPickerView;
@ -139,7 +144,6 @@ import org.mage.card.arcane.CardPanel;
import org.mage.plugins.card.utils.impl.ImageManagerImpl;
/**
*
* @author BetaSteward_at_googlemail.com, nantuko8
*/
public final class GamePanel extends javax.swing.JPanel {
@ -194,6 +198,7 @@ public final class GamePanel extends javax.swing.JPanel {
TRIGGER_ORDER
}
// CardView popupMenu was invoked last
private CardView cardViewPopupMenu;
@ -497,7 +502,7 @@ public final class GamePanel extends javax.swing.JPanel {
this.pnlReplay.setVisible(false);
this.gameChatPanel.clear();
this.gameChatPanel.connect(SessionHandler.getGameChatId(gameId));
SessionHandler.getGameChatId(gameId).ifPresent(uuid -> this.gameChatPanel.connect(uuid));
if (!SessionHandler.joinGame(gameId)) {
removeGame();
} else {
@ -529,7 +534,8 @@ public final class GamePanel extends javax.swing.JPanel {
this.pnlReplay.setVisible(false);
this.gameChatPanel.clear();
this.gameChatPanel.connect(SessionHandler.getGameChatId(gameId));
SessionHandler.getGameChatId(gameId).ifPresent(uuid ->
this.gameChatPanel.connect(uuid));
if (!SessionHandler.watchGame(gameId)) {
removeGame();
}

View file

@ -480,7 +480,7 @@ public class TablesPanel extends javax.swing.JPanel {
if (SessionHandler.getSession() != null) {
btnQuickStart.setVisible(SessionHandler.isTestMode());
gameChooser.init();
chatRoomId = SessionHandler.getRoomChatId(roomId);
chatRoomId = SessionHandler.getRoomChatId(roomId).orElse(null);
}
if (newTableDialog == null) {
newTableDialog = new NewTableDialog();
@ -520,7 +520,7 @@ public class TablesPanel extends javax.swing.JPanel {
this.messages = serverMessages;
this.currentMessage = 0;
}
if (serverMessages == null || serverMessages.isEmpty()) {
if (serverMessages.isEmpty()) {
this.jPanelBottom.setVisible(false);
} else {
this.jPanelBottom.setVisible(true);
@ -1447,7 +1447,7 @@ class UpdateTablesTask extends SwingWorker<Void, Collection<TableView>> {
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
Collection<TableView> tables = SessionHandler.getTables(roomId);
if (tables != null) {
if (!tables.isEmpty()) {
this.publish(tables);
}
Thread.sleep(3000);
@ -1633,7 +1633,7 @@ class UpdateMatchesTask extends SwingWorker<Void, Collection<MatchView>> {
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
Collection<MatchView> matches = SessionHandler.getFinishedMatches(roomId);
if (matches != null) {
if (!matches.isEmpty()) {
this.publish(matches);
}
Thread.sleep(10000);

View file

@ -40,6 +40,7 @@ import java.awt.event.ActionEvent;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ExecutionException;
@ -48,6 +49,8 @@ import javax.swing.Action;
import javax.swing.Icon;
import javax.swing.SwingWorker;
import javax.swing.table.AbstractTableModel;
import mage.cards.o.Opt;
import mage.client.MageFrame;
import mage.client.SessionHandler;
import mage.client.chat.ChatPanelBasic;
@ -202,9 +205,9 @@ public class TournamentPanel extends javax.swing.JPanel {
public synchronized void showTournament(UUID tournamentId) {
this.tournamentId = tournamentId;
// MageFrame.addTournament(tournamentId, this);
UUID chatRoomId = SessionHandler.getTournamentChatId(tournamentId);
if (SessionHandler.joinTournament(tournamentId) && chatRoomId != null) {
this.chatPanel1.connect(chatRoomId);
Optional<UUID> chatRoomId = SessionHandler.getTournamentChatId(tournamentId);
if (SessionHandler.joinTournament(tournamentId) && chatRoomId.isPresent()) {
this.chatPanel1.connect(chatRoomId.get());
startTasks();
this.setVisible(true);
this.repaint();

View file

@ -316,6 +316,7 @@ public class DownloadPictures extends DefaultBoundedRangeModel implements Runnab
List<CardDownloadData> cardsToDownload = Collections.synchronizedList(new ArrayList<>());
allCardsUrls.parallelStream().forEach(card -> {
TFile file = new TFile(CardImageUtils.generateImagePath(card));
logger.debug(card.getName() + " (is_token=" + card.isToken() + "). Image is here:" + file.getAbsolutePath() + " (exists=" + file.exists() +')');
if (!file.exists()) {
logger.debug("Missing: " + file.getAbsolutePath());
cardsToDownload.add(card);

View file

@ -28,6 +28,7 @@
package mage.interfaces;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import mage.MageException;
@ -113,7 +114,7 @@ public interface MageServer {
boolean isTableOwner(String sessionId, UUID roomId, UUID tableId) throws MageException;
TableView getTable(UUID roomId, UUID tableId) throws MageException;
Optional<TableView> getTable(UUID roomId, UUID tableId) throws MageException;
List<TableView> getTables(UUID roomId) throws MageException;
@ -124,13 +125,13 @@ public interface MageServer {
void leaveChat(UUID chatId, String sessionId) throws MageException;
UUID getTableChatId(UUID tableId) throws MageException;
Optional<UUID> getTableChatId(UUID tableId) throws MageException;
UUID getGameChatId(UUID gameId) throws MageException;
Optional<UUID> getGameChatId(UUID gameId) throws MageException;
UUID getRoomChatId(UUID roomId) throws MageException;
Optional<UUID> getRoomChatId(UUID roomId) throws MageException;
UUID getTournamentChatId(UUID tournamentId) throws MageException;
Optional<UUID> getTournamentChatId(UUID tournamentId) throws MageException;
//room methods
UUID getMainRoomId() throws MageException;

View file

@ -600,7 +600,7 @@ public class SessionImpl implements Session {
}
@Override
public UUID getRoomChatId(UUID roomId) {
public Optional<UUID> getRoomChatId(UUID roomId) {
try {
if (isConnected()) {
return server.getRoomChatId(roomId);
@ -608,11 +608,11 @@ public class SessionImpl implements Session {
} catch (MageException ex) {
handleMageException(ex);
}
return null;
return Optional.empty();
}
@Override
public UUID getTableChatId(UUID tableId) {
public Optional<UUID> getTableChatId(UUID tableId) {
try {
if (isConnected()) {
return server.getTableChatId(tableId);
@ -620,11 +620,11 @@ public class SessionImpl implements Session {
} catch (MageException ex) {
handleMageException(ex);
}
return null;
return Optional.empty();
}
@Override
public UUID getGameChatId(UUID gameId) {
public Optional<UUID> getGameChatId(UUID gameId) {
try {
if (isConnected()) {
return server.getGameChatId(gameId);
@ -634,11 +634,11 @@ public class SessionImpl implements Session {
} catch (Throwable t) {
handleThrowable(t);
}
return null;
return Optional.empty();
}
@Override
public TableView getTable(UUID roomId, UUID tableId) {
public Optional<TableView> getTable(UUID roomId, UUID tableId) {
try {
if (isConnected()) {
return server.getTable(roomId, tableId);
@ -646,7 +646,7 @@ public class SessionImpl implements Session {
} catch (MageException ex) {
handleMageException(ex);
}
return null;
return Optional.empty();
}
@Override
@ -735,7 +735,7 @@ public class SessionImpl implements Session {
} catch (Throwable t) {
handleThrowable(t);
}
return null;
return new ArrayList<>();
}
@Override
@ -750,7 +750,7 @@ public class SessionImpl implements Session {
} catch (Throwable t) {
handleThrowable(t);
}
return null;
return new ArrayList<>();
}
@Override
@ -765,7 +765,7 @@ public class SessionImpl implements Session {
} catch (Throwable t) {
handleThrowable(t);
}
return null;
return new ArrayList<>();
}
@Override
@ -784,7 +784,7 @@ public class SessionImpl implements Session {
}
@Override
public UUID getTournamentChatId(UUID tournamentId) {
public Optional<UUID> getTournamentChatId(UUID tournamentId) {
try {
if (isConnected()) {
return server.getTournamentChatId(tournamentId);
@ -794,7 +794,7 @@ public class SessionImpl implements Session {
} catch (Throwable t) {
handleThrowable(t);
}
return null;
return Optional.empty();
}
@Override
@ -1420,7 +1420,7 @@ public class SessionImpl implements Session {
} catch (Throwable t) {
handleThrowable(t);
}
return null;
return new ArrayList<>();
}
@Override

View file

@ -27,6 +27,7 @@
*/
package mage.remote.interfaces;
import java.util.Optional;
import java.util.UUID;
/**
@ -34,13 +35,13 @@ import java.util.UUID;
*/
public interface ChatSession {
UUID getRoomChatId(UUID roomId);
Optional<UUID> getRoomChatId(UUID roomId);
UUID getTableChatId(UUID tableId);
Optional<UUID> getTableChatId(UUID tableId);
UUID getGameChatId(UUID gameId);
Optional<UUID> getGameChatId(UUID gameId);
UUID getTournamentChatId(UUID tournamentId);
Optional<UUID> getTournamentChatId(UUID tournamentId);
boolean joinChat(UUID chatId);

View file

@ -34,6 +34,7 @@ import mage.remote.MageRemoteException;
import mage.view.TableView;
import mage.view.TournamentView;
import java.util.Optional;
import java.util.UUID;
/**
@ -71,7 +72,7 @@ public interface PlayerActions {
boolean joinTable(UUID roomId, UUID tableId, String playerName, String playerType, int skill, DeckCardLists deckList, String password);
TableView getTable(UUID roomId, UUID tableId);
Optional<TableView> getTable(UUID roomId, UUID tableId);
TournamentView getTournament(UUID tournamentId) throws MageRemoteException;

View file

@ -29,6 +29,7 @@ package mage.remote.interfaces;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import mage.remote.MageRemoteException;
import mage.view.MatchView;

View file

@ -51,7 +51,6 @@ import static javax.swing.JTable.AUTO_RESIZE_NEXT_COLUMN;
import static javax.swing.JTable.AUTO_RESIZE_OFF;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class ConsolePanel extends javax.swing.JPanel {

View file

@ -211,7 +211,12 @@ public class MageServerImpl implements MageServer {
@Override
public TableView execute() throws MageException {
try {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session to found : " + sessionId);
return null;
}
UUID userId = session.get().getUserId();
Optional<User> _user = UserManager.instance.getUser(userId);
if (!_user.isPresent()) {
logger.error("User for session not found. session = " + sessionId);
@ -247,9 +252,14 @@ public class MageServerImpl implements MageServer {
user.showUserMessage("Create tournament", message);
throw new MageException("No message");
}
TableView table = GamesRoomManager.instance.getRoom(roomId).createTournamentTable(userId, options);
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (!room.isPresent()) {
} else {
TableView table = room.get().createTournamentTable(userId, options);
logger.debug("Tournament table " + table.getTableId() + " created");
return table;
}
} catch (Exception ex) {
handleException(ex);
}
@ -261,8 +271,13 @@ public class MageServerImpl implements MageServer {
@Override
public void removeTable(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
execute("removeTable", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.removeTable(userId, tableId);
}
});
}
@ -271,13 +286,23 @@ public class MageServerImpl implements MageServer {
return executeWithResult("joinTable", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return false;
}
UUID userId = session.get().getUserId();
logger.debug(name + " joins tableId: " + tableId);
if (userId == null) {
logger.fatal("Got no userId from sessionId" + sessionId + " tableId" + tableId);
return false;
}
return GamesRoomManager.instance.getRoom(roomId).joinTable(userId, tableId, name, playerType, skill, deckList, password);
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (!room.isPresent()) {
logger.error("room not found : " + roomId);
return false;
}
return room.get().joinTable(userId, tableId, name, playerType, skill, deckList, password);
}
});
@ -288,7 +313,12 @@ public class MageServerImpl implements MageServer {
return executeWithResult("joinTournamentTable", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return false;
}
UUID userId = session.get().getUserId();
if (logger.isTraceEnabled()) {
Optional<User> user = UserManager.instance.getUser(userId);
user.ifPresent(user1 -> logger.trace("join tourn. tableId: " + tableId + ' ' + name));
@ -297,7 +327,11 @@ public class MageServerImpl implements MageServer {
logger.fatal("Got no userId from sessionId" + sessionId + " tableId" + tableId);
return false;
}
return GamesRoomManager.instance.getRoom(roomId).joinTournamentTable(userId, tableId, name, playerType, skill, deckList, password);
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (room.isPresent()) {
return room.get().joinTournamentTable(userId, tableId, name, playerType, skill, deckList, password);
}
return null;
}
});
@ -308,20 +342,32 @@ public class MageServerImpl implements MageServer {
return executeWithResult("submitDeck", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return false;
} else {
UUID userId = session.get().getUserId();
boolean ret = TableManager.instance.submitDeck(userId, tableId, deckList);
logger.debug("Session " + sessionId + " submitted deck");
return ret;
}
}
});
}
@Override
public void updateDeck(final String sessionId, final UUID tableId, final DeckCardLists deckList) throws MageException, GameException {
execute("updateDeck", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.updateDeck(userId, tableId, deckList);
logger.trace("Session " + sessionId + " updated deck");
}
});
}
@ -329,11 +375,11 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public List<TableView> getTables(UUID roomId) throws MageException {
try {
GamesRoom room = GamesRoomManager.instance.getRoom(roomId);
if (room != null) {
return room.getTables();
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (room.isPresent()) {
return room.get().getTables();
} else {
return null;
return new ArrayList<>();
}
} catch (Exception ex) {
handleException(ex);
@ -345,47 +391,44 @@ public class MageServerImpl implements MageServer {
//FIXME: why no sessionId here???
public List<MatchView> getFinishedMatches(UUID roomId) throws MageException {
try {
GamesRoom room = GamesRoomManager.instance.getRoom(roomId);
if (room != null) {
return room.getFinished();
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (room.isPresent()) {
return room.get().getFinished();
} else {
return null;
return new ArrayList<>();
}
} catch (Exception ex) {
handleException(ex);
}
return null;
return new ArrayList<>();
}
@Override
public List<RoomUsersView> getRoomUsers(UUID roomId) throws MageException {
try {
GamesRoom room = GamesRoomManager.instance.getRoom(roomId);
if (room != null) {
return room.getRoomUsersInfo();
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (room.isPresent()) {
return room.get().getRoomUsersInfo();
} else {
return null;
return new ArrayList<>();
}
} catch (Exception ex) {
handleException(ex);
}
return null;
return new ArrayList<>();
}
@Override
//FIXME: why no sessionId here???
public TableView getTable(UUID roomId, UUID tableId) throws MageException {
public Optional<TableView> getTable(UUID roomId, UUID tableId) throws MageException {
try {
GamesRoom room = GamesRoomManager.instance.getRoom(roomId);
if (room != null) {
return room.getTable(tableId);
} else {
return null;
}
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
return room.flatMap(r -> r.getTable(tableId));
} catch (Exception ex) {
handleException(ex);
}
return null;
return Optional.empty();
}
@Override
@ -405,12 +448,22 @@ public class MageServerImpl implements MageServer {
// }
@Override
public boolean startMatch(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
if (!TableManager.instance.getController(tableId).changeTableStateToStarting()) {
Optional<TableController> controller = TableManager.instance.getController(tableId);
if (!controller.isPresent()) {
logger.error("table not found : " + tableId);
return false;
}
if (!controller.get().changeTableStateToStarting()) {
return false;
}
execute("startMatch", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.startMatch(userId, roomId, tableId);
}
});
return true;
}
@ -427,12 +480,22 @@ public class MageServerImpl implements MageServer {
// }
@Override
public boolean startTournament(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
if (!TableManager.instance.getController(tableId).changeTableStateToStarting()) {
Optional<TableController> controller = TableManager.instance.getController(tableId);
if (!controller.isPresent()) {
logger.error("table not found : " + tableId);
return false;
}
if (!controller.get().changeTableStateToStarting()) {
return false;
}
execute("startTournament", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.startTournament(userId, roomId, tableId);
}
});
return true;
}
@ -463,8 +526,13 @@ public class MageServerImpl implements MageServer {
@Override
public void joinChat(final UUID chatId, final String sessionId, final String userName) throws MageException {
execute("joinChat", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ChatManager.instance.joinChat(chatId, userId);
}
});
}
@ -472,8 +540,13 @@ public class MageServerImpl implements MageServer {
public void leaveChat(final UUID chatId, final String sessionId) throws MageException {
execute("leaveChat", sessionId, () -> {
if (chatId != null) {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ChatManager.instance.leaveChat(chatId, userId);
}
} else {
logger.warn("The chatId is null. sessionId = " + sessionId);
}
@ -493,13 +566,18 @@ public class MageServerImpl implements MageServer {
@Override
//FIXME: why no sessionId here???
public UUID getRoomChatId(UUID roomId) throws MageException {
public Optional<UUID> getRoomChatId(UUID roomId) throws MageException {
try {
return GamesRoomManager.instance.getRoom(roomId).getChatId();
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (!room.isPresent()) {
logger.error("roomId not found : " + roomId);
return Optional.empty();
}
return Optional.of(room.get().getChatId());
} catch (Exception ex) {
handleException(ex);
}
return null;
return Optional.empty();
}
@Override
@ -507,72 +585,114 @@ public class MageServerImpl implements MageServer {
return executeWithResult("isTableOwner", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return false;
} else {
UUID userId = session.get().getUserId();
return TableManager.instance.isTableOwner(tableId, userId);
}
}
});
}
@Override
public void swapSeats(final String sessionId, final UUID roomId, final UUID tableId, final int seatNum1, final int seatNum2) throws MageException {
execute("swapSeats", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.swapSeats(tableId, userId, seatNum1, seatNum2);
}
});
}
@Override
public boolean leaveTable(final String sessionId, final UUID roomId, final UUID tableId) throws MageException {
TableState tableState = TableManager.instance.getController(tableId).getTableState();
Optional<TableController> tableController = TableManager.instance.getController(tableId);
if (tableController.isPresent()) {
TableState tableState = tableController.get().getTableState();
if (tableState != TableState.WAITING && tableState != TableState.READY_TO_START) {
// table was already started, so player can't leave anymore now
return false;
}
execute("leaveTable", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
GamesRoomManager.instance.getRoom(roomId).leaveTable(userId, tableId);
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (!room.isPresent()) {
logger.error("room not found : " + roomId);
} else {
room.get().leaveTable(userId, tableId);
}
}
});
} else {
logger.error("table not found : " + tableId);
}
return true;
}
@Override
//FIXME: why no sessionId here???
public UUID getTableChatId(UUID tableId) throws MageException {
public Optional<UUID> getTableChatId(UUID tableId) throws MageException {
try {
return TableManager.instance.getChatId(tableId);
} catch (Exception ex) {
handleException(ex);
}
return null;
return Optional.empty();
}
@Override
public void joinGame(final UUID gameId, final String sessionId) throws MageException {
execute("joinGame", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
GameManager.instance.joinGame(gameId, userId);
}
});
}
@Override
public void joinDraft(final UUID draftId, final String sessionId) throws MageException {
execute("joinDraft", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
DraftManager.instance.joinDraft(draftId, userId);
}
});
}
@Override
public void joinTournament(final UUID tournamentId, final String sessionId) throws MageException {
execute("joinTournament", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TournamentManager.instance.joinTournament(tournamentId, userId);
}
});
}
@Override
//FIXME: why no sessionId here???
public UUID getGameChatId(UUID gameId) throws MageException {
public Optional<UUID> getGameChatId(UUID gameId) throws MageException {
try {
return GameManager.instance.getChatId(gameId);
} catch (Exception ex) {
@ -583,13 +703,13 @@ public class MageServerImpl implements MageServer {
@Override
//FIXME: why no sessionId here???
public UUID getTournamentChatId(UUID tournamentId) throws MageException {
public Optional<UUID> getTournamentChatId(UUID tournamentId) throws MageException {
try {
return TournamentManager.instance.getChatId(tournamentId);
} catch (Exception ex) {
handleException(ex);
}
return null;
return Optional.empty();
}
@Override
@ -661,11 +781,12 @@ public class MageServerImpl implements MageServer {
@Override
public void sendCardMark(final UUID draftId, final String sessionId, final UUID cardPick) throws MageException {
execute("sendCardMark", sessionId, () -> {
Session session = SessionManager.instance.getSession(sessionId);
if (session != null) {
DraftManager.instance.sendCardMark(draftId, session.getUserId(), cardPick);
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
logger.error("Session not found sessionId: " + sessionId + " draftId:" + draftId);
UUID userId = session.get().getUserId();
DraftManager.instance.sendCardMark(draftId, userId, cardPick);
}
});
}
@ -673,11 +794,13 @@ public class MageServerImpl implements MageServer {
@Override
public void quitMatch(final UUID gameId, final String sessionId) throws MageException {
execute("quitMatch", sessionId, () -> {
Session session = SessionManager.instance.getSession(sessionId);
if (session != null) {
GameManager.instance.quitMatch(gameId, session.getUserId());
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
logger.error("Session not found sessionId: " + sessionId + " gameId:" + gameId);
UUID userId = session.get().getUserId();
GameManager.instance.quitMatch(gameId, userId);
}
});
}
@ -685,11 +808,13 @@ public class MageServerImpl implements MageServer {
@Override
public void quitTournament(final UUID tournamentId, final String sessionId) throws MageException {
execute("quitTournament", sessionId, () -> {
Session session = SessionManager.instance.getSession(sessionId);
if (session != null) {
TournamentManager.instance.quit(tournamentId, session.getUserId());
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
logger.error("Session not found sessionId: " + sessionId + " tournamentId:" + tournamentId);
UUID userId = session.get().getUserId();
TournamentManager.instance.quit(tournamentId, userId);
}
});
}
@ -697,16 +822,17 @@ public class MageServerImpl implements MageServer {
@Override
public void quitDraft(final UUID draftId, final String sessionId) throws MageException {
execute("quitDraft", sessionId, () -> {
Session session = SessionManager.instance.getSession(sessionId);
if (session == null) {
logger.error("Session not found sessionId: " + sessionId + " draftId:" + draftId);
return;
}
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
UUID tableId = DraftManager.instance.getControllerByDraftId(draftId).getTableId();
Table table = TableManager.instance.getTable(tableId);
if (table.isTournament()) {
UUID tournamentId = table.getTournament().getId();
TournamentManager.instance.quit(tournamentId, session.getUserId());
TournamentManager.instance.quit(tournamentId, userId);
}
}
});
}
@ -714,12 +840,13 @@ public class MageServerImpl implements MageServer {
@Override
public void sendPlayerAction(final PlayerAction playerAction, final UUID gameId, final String sessionId, final Object data) throws MageException {
execute("sendPlayerAction", sessionId, () -> {
Session session = SessionManager.instance.getSession(sessionId);
if (session == null) {
logger.error("Session not found sessionId: " + sessionId + " gameId:" + gameId);
return;
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
GameManager.instance.sendPlayerAction(playerAction, gameId, userId, data);
}
GameManager.instance.sendPlayerAction(playerAction, gameId, session.getUserId(), data);
});
}
@ -728,8 +855,18 @@ public class MageServerImpl implements MageServer {
return executeWithResult("setUserData", sessionId, new ActionWithBooleanResult() {
@Override
public Boolean execute() throws MageException {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
return GamesRoomManager.instance.getRoom(roomId).watchTable(userId, tableId);
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return false;
} else {
UUID userId = session.get().getUserId();
if (GamesRoomManager.instance.getRoom(roomId).isPresent()) {
return GamesRoomManager.instance.getRoom(roomId).get().watchTable(userId, tableId);
} else {
return false;
}
}
}
});
}
@ -742,19 +879,29 @@ public class MageServerImpl implements MageServer {
@Override
public void watchGame(final UUID gameId, final String sessionId) throws MageException {
execute("watchGame", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
GameManager.instance.watchGame(gameId, userId);
}
});
}
@Override
public void stopWatching(final UUID gameId, final String sessionId) throws MageException {
execute("stopWatching", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
UserManager.instance.getUser(userId).ifPresent(user -> {
GameManager.instance.stopWatching(gameId, userId);
user.removeGameWatchInfo(gameId);
});
}
});
}
@ -762,48 +909,78 @@ public class MageServerImpl implements MageServer {
@Override
public void replayGame(final UUID gameId, final String sessionId) throws MageException {
execute("replayGame", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.replayGame(gameId, userId);
}
});
}
@Override
public void startReplay(final UUID gameId, final String sessionId) throws MageException {
execute("startReplay", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.startReplay(gameId, userId);
}
});
}
@Override
public void stopReplay(final UUID gameId, final String sessionId) throws MageException {
execute("stopReplay", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.stopReplay(gameId, userId);
}
});
}
@Override
public void nextPlay(final UUID gameId, final String sessionId) throws MageException {
execute("nextPlay", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.nextPlay(gameId, userId);
}
});
}
@Override
public void previousPlay(final UUID gameId, final String sessionId) throws MageException {
execute("previousPlay", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.previousPlay(gameId, userId);
}
});
}
@Override
public void skipForward(final UUID gameId, final String sessionId, final int moves) throws MageException {
execute("skipForward", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
ReplayManager.instance.skipForward(gameId, userId, moves);
}
});
}
@ -832,9 +1009,14 @@ public class MageServerImpl implements MageServer {
public void cheat(final UUID gameId, final String sessionId, final UUID playerId, final DeckCardLists deckList) throws MageException {
execute("cheat", sessionId, () -> {
if (testMode) {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
GameManager.instance.cheat(gameId, userId, playerId, deckList);
}
}
});
}
@ -844,9 +1026,14 @@ public class MageServerImpl implements MageServer {
@Override
public Boolean execute() {
if (testMode) {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
return GameManager.instance.cheat(gameId, userId, playerId, cardName);
}
}
return false;
}
});
@ -957,8 +1144,13 @@ public class MageServerImpl implements MageServer {
@Override
public void removeTable(final String sessionId, final UUID tableId) throws MageException {
execute("removeTable", sessionId, () -> {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
TableManager.instance.removeTable(userId, tableId);
}
});
}
@ -971,8 +1163,13 @@ public class MageServerImpl implements MageServer {
public void sendFeedbackMessage(final String sessionId, final String username, final String title, final String type, final String message, final String email) throws MageException {
if (title != null && message != null) {
execute("sendFeedbackMessage", sessionId, () -> {
String host = SessionManager.instance.getSession(sessionId).getHost();
FeedbackServiceImpl.instance.feedback(username, title, type, message, email, host);
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error(String.format("Session not found: %s", sessionId));
} else {
FeedbackServiceImpl.instance.feedback(username, title, type, message, email, session.get().getHost());
}
});
}
}
@ -1104,10 +1301,16 @@ public class MageServerImpl implements MageServer {
@Override
public GameView execute() throws MageException {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return null;
} else {
UUID userId = session.get().getUserId();
return GameManager.instance.getGameView(gameId, userId, playerId);
}
}
}
private static class MyActionWithBooleanResult extends ActionWithBooleanResult {
private final String sessionId;
@ -1120,10 +1323,16 @@ public class MageServerImpl implements MageServer {
@Override
public Boolean execute() throws MageException {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return false;
} else {
UUID userId = session.get().getUserId();
return TableManager.instance.watchTable(userId, tableId);
}
}
}
private static class DraftPickViewActionWithNullNegativeResult extends ActionWithNullNegativeResult<DraftPickView> {
private final String sessionId;
@ -1140,9 +1349,9 @@ public class MageServerImpl implements MageServer {
@Override
public DraftPickView execute() {
Session session = SessionManager.instance.getSession(sessionId);
if (session != null) {
return DraftManager.instance.sendCardPick(draftId, session.getUserId(), cardPick, hiddenCards);
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (session.isPresent()) {
return DraftManager.instance.sendCardPick(draftId, session.get().getUserId(), cardPick, hiddenCards);
} else {
logger.error("Session not found sessionId: " + sessionId + " draftId:" + draftId);
}
@ -1163,7 +1372,12 @@ public class MageServerImpl implements MageServer {
@Override
public TableView execute() throws MageException {
UUID userId = SessionManager.instance.getSession(sessionId).getUserId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
return null;
}
UUID userId = session.get().getUserId();
Optional<User> _user = UserManager.instance.getUser(userId);
if (!_user.isPresent()) {
logger.error("User for session not found. session = " + sessionId);
@ -1183,13 +1397,19 @@ public class MageServerImpl implements MageServer {
throw new MageException("No message");
}
TableView table = GamesRoomManager.instance.getRoom(roomId).createTable(userId, options);
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(roomId);
if (room.isPresent()) {
TableView table = room.get().createTable(userId, options);
if (logger.isDebugEnabled()) {
logger.debug("TABLE created - tableId: " + table.getTableId() + ' ' + table.getTableName());
logger.debug("- " + user.getName() + " userId: " + user.getId());
logger.debug("- chatId: " + TableManager.instance.getChatId(table.getTableId()));
}
return table;
} else {
return null;
}
}
}
}

View file

@ -263,17 +263,20 @@ public final class Main {
@Override
public void handleConnectionException(Throwable throwable, Client client) {
Session session = SessionManager.instance.getSession(client.getSessionId());
if (session != null) {
String sessionId = client.getSessionId();
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
UUID userId = session.get().getUserId();
StringBuilder sessionInfo = new StringBuilder();
Optional<User> user = UserManager.instance.getUser(session.getUserId());
Optional<User> user = UserManager.instance.getUser(userId);
if (user.isPresent()) {
sessionInfo.append(user.get().getName()).append(" [").append(user.get().getGameInfo()).append(']');
} else {
sessionInfo.append("[user missing] ");
}
sessionInfo.append(" at ").append(session.getHost()).append(" sessionId: ").append(session.getId());
sessionInfo.append(" at ").append(session.get().getHost()).append(" sessionId: ").append(session.get().getId());
if (throwable instanceof ClientDisconnectedException) {
// Seems like the random diconnects from public server land here and should not be handled as explicit disconnects
// So it should be possible to reconnect to server and continue games if DisconnectReason is set to LostConnection
@ -372,7 +375,12 @@ public final class Main {
} else {
host = "localhost";
}
SessionManager.instance.getSession(sessionId).setHost(host);
Optional<Session> session = SessionManager.instance.getSession(sessionId);
if (!session.isPresent()) {
logger.error("Session not found : " + sessionId);
} else {
session.get().setHost(host);
}
return null;
}

View file

@ -29,6 +29,7 @@
package mage.server;
import java.rmi.Remote;
import java.util.Optional;
import java.util.UUID;
/**

View file

@ -27,16 +27,12 @@
*/
package mage.server;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import mage.MageException;
import mage.constants.Constants;
import mage.interfaces.callback.ClientCallback;
import mage.players.net.UserData;
import mage.players.net.UserGroup;
import mage.server.game.GamesRoom;
import mage.server.game.GamesRoomManager;
import mage.server.util.ConfigSettings;
import mage.server.util.SystemUtil;
@ -47,8 +43,13 @@ import org.jboss.remoting.callback.Callback;
import org.jboss.remoting.callback.HandleCallbackException;
import org.jboss.remoting.callback.InvokerCallbackHandler;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class Session {
@ -129,7 +130,7 @@ public class Session {
}
}
static private String validateUserName(String userName) {
private static String validateUserName(String userName) {
if (userName.equals("Admin")) {
return "User name Admin already in use";
}
@ -152,7 +153,7 @@ public class Session {
return null;
}
static private String validatePassword(String password, String userName) {
private static String validatePassword(String password, String userName) {
ConfigSettings config = ConfigSettings.instance;
if (password.length() < config.getMinPasswordLength()) {
return "Password may not be shorter than " + config.getMinPasswordLength() + " characters";
@ -171,7 +172,7 @@ public class Session {
return null;
}
static private String validateEmail(String email) {
private static String validateEmail(String email) {
if (email == null || email.isEmpty()) {
return "Email address cannot be blank";
@ -248,10 +249,12 @@ public class Session {
}
this.userId = user.getId();
if (reconnect) { // must be connected to receive the message
UUID chatId = GamesRoomManager.instance.getRoom(GamesRoomManager.instance.getMainRoomId()).getChatId();
if (chatId != null) {
ChatManager.instance.joinChat(chatId, userId);
Optional<GamesRoom> room = GamesRoomManager.instance.getRoom(GamesRoomManager.instance.getMainRoomId());
if (!room.isPresent()) {
logger.error("main room not found");
return null;
}
ChatManager.instance.joinChat(room.get().getChatId(), userId);
ChatManager.instance.sendReconnectMessage(userId);
}
return null;

View file

@ -49,15 +49,15 @@ public enum SessionManager {
private final ConcurrentHashMap<String, Session> sessions = new ConcurrentHashMap<>();
public Session getSession(@Nonnull String sessionId) {
public Optional<Session> getSession(@Nonnull String sessionId) {
Session session = sessions.get(sessionId);
if (session != null && session.getUserId() != null && UserManager.instance.getUser(session.getUserId()) == null) {
logger.error("User for session " + sessionId + " with userId " + session.getUserId() + " is missing. Session removed.");
// can happen if user from same host signs in multiple time with multiple clients, after he disconnects with one client
disconnect(sessionId, DisconnectReason.ConnectingOtherInstance);
return null;
return Optional.empty();
}
return session;
return Optional.of(session);
}
public void createSession(String sessionId, InvokerCallbackHandler callbackHandler) {
@ -168,26 +168,26 @@ public enum SessionManager {
*/
public void disconnectUser(String sessionId, String userSessionId) {
if (isAdmin(sessionId)) {
User userAdmin;
if ((userAdmin = getUserFromSession(sessionId)) != null) {
User user;
if ((user = getUserFromSession(userSessionId)) != null) {
getUserFromSession(sessionId).ifPresent(admin -> {
Optional<User> u = getUserFromSession(userSessionId);
if (u.isPresent()) {
User user = u.get();
user.showUserMessage("Admin operation", "Your session was disconnected by Admin.");
userAdmin.showUserMessage("Admin action", "User" + user.getName() + " was disconnected.");
admin.showUserMessage("Admin action", "User" + user.getName() + " was disconnected.");
disconnect(userSessionId, DisconnectReason.AdminDisconnect);
} else {
userAdmin.showUserMessage("Admin operation", "User with sessionId " + userSessionId + " could not be found!");
}
admin.showUserMessage("Admin operation", "User with sessionId " + userSessionId + " could not be found!");
}
});
}
}
private User getUserFromSession(String sessionId) {
Session session = getSession(sessionId);
if (session == null) {
return null;
private Optional<User> getUserFromSession(String sessionId) {
Optional<Session> session = getSession(sessionId);
if (!session.isPresent()) {
return Optional.empty();
}
return UserManager.instance.getUser(session.getUserId()).get();
return UserManager.instance.getUser(session.get().getUserId());
}
public void endUserSession(String sessionId, String userSessionId) {

View file

@ -242,7 +242,7 @@ public class TableController {
newTournamentPlayer.setState(oldTournamentPlayer.getState());
newTournamentPlayer.setReplacedTournamentPlayer(oldTournamentPlayer);
DraftManager.instance.getController(table.getId()).replacePlayer(oldPlayer, newPlayer);
DraftManager.instance.getController(table.getId()).ifPresent(controller -> controller.replacePlayer(oldPlayer, newPlayer));
return true;
}

View file

@ -49,6 +49,7 @@ import mage.game.match.Match;
import mage.game.match.MatchOptions;
import mage.game.tournament.Tournament;
import mage.game.tournament.TournamentOptions;
import mage.game.tournament.TournamentPlayer;
import mage.players.Player;
import mage.server.game.GameController;
import mage.server.game.GameManager;
@ -114,19 +115,22 @@ public enum TableManager {
return tables.get(tableId);
}
public Match getMatch(UUID tableId) {
public Optional<Match> getMatch(UUID tableId) {
if (controllers.containsKey(tableId)) {
return controllers.get(tableId).getMatch();
return Optional.of(controllers.get(tableId).getMatch());
}
return null;
return Optional.empty();
}
public Collection<Table> getTables() {
return tables.values();
}
public TableController getController(UUID tableId) {
return controllers.get(tableId);
public Optional<TableController> getController(UUID tableId) {
if (controllers.containsKey(tableId)) {
return Optional.of(controllers.get(tableId));
}
return Optional.empty();
}
public boolean joinTable(UUID userId, UUID tableId, String name, String playerType, int skill, DeckCardLists deckList, String password) throws MageException {
@ -214,11 +218,11 @@ public enum TableManager {
}
}
public UUID getChatId(UUID tableId) {
public Optional<UUID> getChatId(UUID tableId) {
if (controllers.containsKey(tableId)) {
return controllers.get(tableId).getChatId();
return Optional.of(controllers.get(tableId).getChatId());
}
return null;
return Optional.empty();
}
/**
@ -313,9 +317,9 @@ public enum TableManager {
}
}
public void addPlayer(UUID userId, UUID tableId, Player player, String playerType, Deck deck) throws GameException {
public void addPlayer(UUID userId, UUID tableId, TournamentPlayer player) throws GameException {
if (controllers.containsKey(tableId)) {
controllers.get(tableId).addPlayer(userId, player, playerType, deck);
controllers.get(tableId).addPlayer(userId, player.getPlayer(), player.getPlayerType(), player.getDeck());
}
}
@ -352,10 +356,10 @@ public enum TableManager {
Collection<User> users = UserManager.instance.getUsers();
logger.debug("--------User: " + users.size() + " [userId | since | lock | name -----------------------");
for (User user : users) {
Session session = SessionManager.instance.getSession(user.getSessionId());
Optional<Session> session = SessionManager.instance.getSession(user.getSessionId());
String sessionState = "N";
if (session != null) {
if (session.isLocked()) {
if (session.isPresent()) {
if (session.get().isLocked()) {
sessionState = "L";
} else {
sessionState = "+";
@ -390,8 +394,7 @@ public enum TableManager {
if (table.getState() != TableState.FINISHED) {
// remove tables and games not valid anymore
logger.debug(table.getId() + " [" + table.getName() + "] " + formatter.format(table.getStartTime() == null ? table.getCreateTime() : table.getCreateTime()) + " (" + table.getState().toString() + ") " + (table.isTournament() ? "- Tournament" : ""));
TableController tableController = getController(table.getId());
if (tableController != null) {
getController(table.getId()).ifPresent(tableController -> {
if ((table.isTournament() && !tableController.isTournamentStillValid()) ||
(!table.isTournament() && !tableController.isMatchTableStillValid())) {
try {
@ -401,7 +404,7 @@ public enum TableManager {
logger.error(e);
}
}
}
});
}
} catch (Exception ex) {
logger.debug("Table Health check error tableId: " + table.getId());

View file

@ -27,14 +27,8 @@
*/
package mage.server;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
import mage.cards.decks.Deck;
@ -246,10 +240,9 @@ public class User {
public void fireCallback(final ClientCallback call) {
if (isConnected()) {
Session session = SessionManager.instance.getSession(sessionId);
if (session != null) {
session.fireCallback(call);
}
SessionManager.instance.getSession(sessionId).ifPresent(session ->
session.fireCallback(call)
);
}
}
@ -378,8 +371,13 @@ public class User {
entry.getValue().construct(0); // TODO: Check if this is correct
}
for (Entry<UUID, Deck> entry : sideboarding.entrySet()) {
TableController controller = TableManager.instance.getController(entry.getKey());
ccSideboard(entry.getValue(), entry.getKey(), controller.getRemainingTime(), controller.getOptions().isLimited());
Optional<TableController> controller = TableManager.instance.getController(entry.getKey());
if(controller.isPresent()) {
ccSideboard(entry.getValue(), entry.getKey(), controller.get().getRemainingTime(), controller.get().getOptions().isLimited());
}
else{
logger.error("sideboarding id not found : "+entry.getKey());
}
}
ServerMessagesUtil.instance.incReconnects();
logger.trace(userName + " ended reconnect");
@ -784,8 +782,11 @@ public class User {
if (table.getState() == TableState.FINISHED) {
number++;
} else {
TableController tableController = TableManager.instance.getController(table.getId());
if (tableController != null && tableController.isUserStillActive(userId)) {
Optional<TableController> tableController = TableManager.instance.getController(table.getId());
if(!tableController.isPresent()){
logger.error("table not found : "+table.getId());
}
else if (tableController.get().isUserStillActive(userId)) {
number++;
}
}

View file

@ -30,6 +30,7 @@ package mage.server.draft;
import java.io.File;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -129,11 +130,11 @@ public class DraftController {
checkStart();
}
public DraftSession getDraftSession(UUID playerId) {
public Optional<DraftSession> getDraftSession(UUID playerId) {
if (draftSessions.containsKey(playerId)) {
return draftSessions.get(playerId);
return Optional.of(draftSessions.get(playerId));
}
return null;
return Optional.empty();
}
public boolean replacePlayer(Player oldPlayer, Player newPlayer) {

View file

@ -28,6 +28,7 @@
package mage.server.draft;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -87,12 +88,7 @@ public enum DraftManager {
return draftControllers.get(draftId);
}
public DraftController getController(UUID tableId) {
for (DraftController controller: draftControllers.values()) {
if (controller.getTableId().equals(tableId)) {
return controller;
}
}
return null;
public Optional<DraftController> getController(UUID tableId) {
return draftControllers.values().stream().filter(controller -> controller.getTableId().equals(tableId)).findFirst();
}
}

View file

@ -28,6 +28,7 @@
package mage.server.game;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import mage.cards.decks.DeckCardLists;
@ -59,12 +60,12 @@ public enum GameManager {
}
}
public UUID getChatId(UUID gameId) {
public Optional<UUID> getChatId(UUID gameId) {
GameController gameController = gameControllers.get(gameId);
if (gameController != null) {
return gameController.getChatId();
return Optional.of(gameController.getChatId());
}
return null;
return Optional.empty();
}
public void sendPlayerUUID(UUID gameId, UUID userId, UUID data) {

View file

@ -29,6 +29,7 @@
package mage.server.game;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import mage.MageException;
import mage.cards.decks.DeckCardLists;
@ -55,7 +56,7 @@ public interface GamesRoom extends Room {
TableView createTournamentTable(UUID userId, TournamentOptions options);
void removeTable(UUID userId, UUID tableId);
void removeTable(UUID tableId);
TableView getTable(UUID tableId);
Optional<TableView> getTable(UUID tableId);
void leaveTable(UUID userId, UUID tableId);
boolean watchTable(UUID userId, UUID tableId) throws MageException;

View file

@ -28,10 +28,7 @@
package mage.server.game;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@ -180,11 +177,11 @@ public class GamesRoomImpl extends RoomImpl implements GamesRoom, Serializable {
}
@Override
public TableView getTable(UUID tableId) {
public Optional<TableView> getTable(UUID tableId) {
if (tables.containsKey(tableId)) {
return new TableView(tables.get(tableId));
return Optional.of(new TableView(tables.get(tableId)));
}
return null;
return Optional.empty();
}
@Override

View file

@ -28,6 +28,7 @@
package mage.server.game;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -57,8 +58,12 @@ public enum GamesRoomManager {
return mainRoomId;
}
public GamesRoom getRoom(UUID roomId) {
return rooms.get(roomId);
public Optional<GamesRoom> getRoom(UUID roomId) {
if(rooms.containsKey(roomId)) {
return Optional.of(rooms.get(roomId));
}
return Optional.empty();
}
public void removeTable(UUID tableId) {

View file

@ -249,16 +249,21 @@ public class TournamentController {
table.setState(TableState.WAITING);
TournamentPlayer player1 = pair.getPlayer1();
TournamentPlayer player2 = pair.getPlayer2();
tableManager.addPlayer(getPlayerUserId(player1.getPlayer().getId()), table.getId(), player1.getPlayer(), player1.getPlayerType(), player1.getDeck());
tableManager.addPlayer(getPlayerUserId(player2.getPlayer().getId()), table.getId(), player2.getPlayer(), player2.getPlayerType(), player2.getDeck());
Optional<UUID> user1Id = getPlayerUserId(player1.getPlayer().getId());
Optional<UUID> user2Id = getPlayerUserId(player2.getPlayer().getId());
if (user1Id.isPresent() && user2Id.isPresent()) {
tableManager.addPlayer(getPlayerUserId(player1.getPlayer().getId()).get(), table.getId(), player1);
tableManager.addPlayer(getPlayerUserId(player2.getPlayer().getId()).get(), table.getId(), player2);
table.setState(TableState.STARTING);
tableManager.startTournamentSubMatch(null, table.getId());
Match match = tableManager.getMatch(table.getId());
tableManager.getMatch(table.getId()).ifPresent(match -> {
match.setTableId(tableId);
pair.setMatch(match);
pair.setTableId(table.getId());
player1.setState(TournamentPlayerState.DUELING);
player2.setState(TournamentPlayerState.DUELING);
});
}
} catch (GameException ex) {
logger.fatal("TournamentController startMatch error", ex);
}
@ -271,19 +276,21 @@ public class TournamentController {
table.setTournamentSubTable(true);
table.setTournament(tournament);
table.setState(TableState.WAITING);
if (round.getAllPlayers().stream().allMatch(tournamentPlayer -> getPlayerUserId(tournamentPlayer.getPlayer().getId()).isPresent())) {
for (TournamentPlayer player : round.getAllPlayers()) {
tableManager.addPlayer(getPlayerUserId(player.getPlayer().getId()), table.getId(), player.getPlayer(), player.getPlayerType(), player.getDeck());
tableManager.addPlayer(getPlayerUserId(player.getPlayer().getId()).get(), table.getId(), player);
}
table.setState(TableState.STARTING);
tableManager.startTournamentSubMatch(null, table.getId());
Match match = tableManager.getMatch(table.getId());
tableManager.getMatch(table.getId()).ifPresent(match -> {
match.setTableId(tableId);
round.setMatch(match);
round.setTableId(table.getId());
for (TournamentPlayer player : round.getAllPlayers()) {
player.setState(TournamentPlayerState.DUELING);
}
});
}
} catch (GameException ex) {
logger.fatal("TournamentController startMatch error", ex);
}
@ -307,9 +314,13 @@ public class TournamentController {
if (tournamentSessions.containsKey(playerId)) {
TournamentSession tournamentSession = tournamentSessions.get(playerId);
tournamentSession.construct(timeout);
UserManager.instance.getUser(getPlayerUserId(playerId)).get().addConstructing(playerId, tournamentSession);
getPlayerUserId(playerId).ifPresent(userId -> {
UserManager.instance.getUser(userId).ifPresent(user -> {
user.addConstructing(playerId, tournamentSession);
TournamentPlayer player = tournament.getPlayer(playerId);
player.setState(TournamentPlayerState.CONSTRUCTING);
});
});
}
}
@ -385,13 +396,11 @@ public class TournamentController {
if (!checkToReplaceDraftPlayerByAi(userId, tournamentPlayer)) {
this.abortDraftTournament();
} else {
DraftController draftController = DraftManager.instance.getController(tableId);
if (draftController != null) {
DraftSession draftSession = draftController.getDraftSession(playerId);
if (draftSession != null) {
DraftManager.instance.kill(draftSession.getDraftId(), userId);
}
}
DraftManager.instance.getController(tableId).ifPresent(draftController -> {
draftController.getDraftSession(playerId).ifPresent(draftSession ->
DraftManager.instance.kill(draftSession.getDraftId(), userId));
});
}
status = TourneyQuitStatus.DURING_DRAFTING;
} else if (tournamentPlayer.getState() == TournamentPlayerState.CONSTRUCTING) {
@ -419,8 +428,8 @@ public class TournamentController {
// replace player that quits with draft bot
if (humans > 1) {
Optional<User> user = UserManager.instance.getUser(userId);
TableController tableController = TableManager.instance.getController(tableId);
if (tableController != null) {
TableManager.instance.getController(tableId).ifPresent(tableController -> {
String replacePlayerName = "Draftbot";
if (user.isPresent()) {
replacePlayerName = "Draftbot (" + user.get().getName() + ')';
@ -432,19 +441,14 @@ public class TournamentController {
user.get().removeTournament(leavingPlayer.getPlayer().getId());
}
ChatManager.instance.broadcast(chatId, "", leavingPlayer.getPlayer().getLogName() + " was replaced by draftbot", MessageColor.BLACK, true, MessageType.STATUS, null);
}
});
return true;
}
return false;
}
private UUID getPlayerUserId(UUID playerId) {
for (Entry<UUID, UUID> entry : userPlayerMap.entrySet()) {
if (entry.getValue().equals(playerId)) {
return entry.getKey();
}
}
return null;
private Optional<UUID> getPlayerUserId(UUID playerId) {
return userPlayerMap.entrySet().stream().filter(entry -> entry.getValue().equals(playerId)).map(Entry::getKey).findFirst();
}
public TournamentView getTournamentView() {
@ -453,7 +457,7 @@ public class TournamentController {
private void abortDraftTournament() {
tournament.setAbort(true);
DraftManager.instance.getController(tableId).abortDraft();
DraftManager.instance.getController(tableId).ifPresent(DraftController::abortDraft);
}
public boolean isAbort() {

View file

@ -28,6 +28,7 @@
package mage.server.tournament;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
@ -85,9 +86,13 @@ public enum TournamentManager {
return null;
}
public UUID getChatId(UUID tournamentId) {
return controllers.get(tournamentId).getChatId();
public Optional<UUID> getChatId(UUID tournamentId) {
if(controllers.containsKey(tournamentId)) {
return Optional.of(controllers.get(tournamentId).getChatId());
}
return Optional.empty();
}
public void removeTournament(UUID tournamentId) {
TournamentController tournamentController = controllers.get(tournamentId);

View file

@ -0,0 +1,124 @@
/*
* 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.cards.a;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.Effect;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.ExileSpellEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.Card;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.cards.Cards;
import mage.cards.CardsImpl;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.constants.Zone;
import mage.counters.CounterType;
import mage.filter.common.FilterCreatureCard;
import mage.game.Game;
import mage.players.Player;
/**
*
* @author jeffwadsworth
*/
public class AllHallowsEve extends CardImpl {
public AllHallowsEve(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}{B}");
// Exile All Hallow's Eve with two scream counters on it.
this.getSpellAbility().addEffect(ExileSpellEffect.getInstance());
Effect effect = new AddCountersSourceEffect(CounterType.SCREAM.createInstance(), new StaticValue(2), true, true);
effect.setText("with 2 scream counters on it");
this.getSpellAbility().addEffect(effect);
// At the beginning of your upkeep, if All Hallow's Eve is exiled with a scream counter on it, remove a scream counter from it. If there are no more scream counters on it, put it into your graveyard and each player returns all creature cards from his or her graveyard to the battlefield.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(Zone.EXILED, new AllHallowsEveEffect(), TargetController.YOU, false));
}
public AllHallowsEve(final AllHallowsEve card) {
super(card);
}
@Override
public AllHallowsEve copy() {
return new AllHallowsEve(this);
}
}
class AllHallowsEveEffect extends OneShotEffect {
public AllHallowsEveEffect() {
super(Outcome.PutCreatureInPlay);
this.staticText = "if {this} is exiled with a scream counter on it, remove a scream counter from it. If there are no more scream counters on it, put it into your graveyard and each player returns all creature cards from his or her graveyard to the battlefield";
}
public AllHallowsEveEffect(final AllHallowsEveEffect effect) {
super(effect);
}
@Override
public AllHallowsEveEffect copy() {
return new AllHallowsEveEffect(this);
}
@Override
public boolean apply(Game game, Ability source) {
Card allHallowsEve = game.getCard(source.getSourceId());
Player controller = game.getPlayer(source.getControllerId());
if (allHallowsEve != null
&& controller != null
&& game.getExile().getCard(allHallowsEve.getId(), game) != null) {
allHallowsEve.getCounters(game).removeCounter(CounterType.SCREAM, 1);
if (allHallowsEve.getCounters(game).getCount(CounterType.SCREAM) == 0) {
allHallowsEve.moveToZone(Zone.GRAVEYARD, source.getId(), game, false);
Cards creatures = new CardsImpl();
for (Player player : game.getPlayers().values()) {
if (player != null) {
for (Card creatureCard : player.getGraveyard().getCards(new FilterCreatureCard(), game)) {
creatures.add(creatureCard);
}
}
}
for (Card card : creatures.getCards(game)) {
card.putOntoBattlefield(game, Zone.GRAVEYARD, source.getId(), card.getOwnerId());
}
}
return true;
}
return false;
}
}

View file

@ -0,0 +1,124 @@
/*
* 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.cards.l;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.BeginningOfUpkeepTriggeredAbility;
import mage.abilities.common.DiesCreatureTriggeredAbility;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.effects.common.CreateTokenEffect;
import mage.abilities.effects.common.counter.AddCountersSourceEffect;
import mage.cards.CardImpl;
import mage.cards.CardSetInfo;
import mage.constants.CardType;
import mage.constants.Outcome;
import mage.constants.TargetController;
import mage.counters.CounterType;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.predicate.Predicates;
import mage.filter.predicate.permanent.ControllerPredicate;
import mage.filter.predicate.permanent.TokenPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.ElementalToken;
import mage.players.Player;
/**
*
* @author escplan9 - Derek Monturo
*/
public class LightningCoils extends CardImpl {
private static final FilterCreaturePermanent filter = new FilterCreaturePermanent("a nontoken creature you control");
static {
filter.add(new ControllerPredicate(TargetController.YOU));
filter.add(Predicates.not(new TokenPredicate()));
}
public LightningCoils(UUID ownerId, CardSetInfo setInfo) {
super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
// Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils.
this.addAbility(
new DiesCreatureTriggeredAbility(
new AddCountersSourceEffect(CounterType.CHARGE.createInstance(), true),
false, filter));
// At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it
// and put that many 3/1 red Elemental creature tokens with haste onto the battlefield.
// Exile them at the beginning of the next end step.
this.addAbility(new BeginningOfUpkeepTriggeredAbility(new LightningCoilsEffect(), TargetController.YOU, false));
}
public LightningCoils(final LightningCoils card) {
super(card);
}
@Override
public LightningCoils copy() {
return new LightningCoils(this);
}
}
class LightningCoilsEffect extends OneShotEffect {
LightningCoilsEffect() {
super(Outcome.Benefit);
staticText = "if {this} has five or more charge counters on it, remove all of them from it and put that many 3/1 red Elemental creature tokens with haste onto the battlefield. Exile them at the beginning of the next end step.";
}
LightningCoilsEffect(final LightningCoilsEffect effect) {
super(effect);
}
@Override
public boolean apply(Game game, Ability source) {
Player controller = game.getPlayer(source.getControllerId());
Permanent p = game.getPermanent(source.getSourceId());
if (p != null && controller != null) {
int counters = p.getCounters(game).getCount(CounterType.CHARGE);
if (counters >= 5) {
// remove all the counters and create that many tokens
p.removeCounters(CounterType.CHARGE.getName(), p.getCounters(game).getCount(CounterType.CHARGE), game);
CreateTokenEffect effect = new CreateTokenEffect(new ElementalToken("CON", 1, true), counters);
effect.apply(game, source);
// exile those tokens at next end step
effect.exileTokensCreatedAtNextEndStep(game, source);
return true;
}
}
return false;
}
@Override
public LightningCoilsEffect copy() {
return new LightningCoilsEffect(this);
}
}

View file

@ -41,9 +41,11 @@ import mage.constants.CardType;
import mage.constants.Duration;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.filter.Filter;
import mage.filter.FilterPermanent;
import mage.filter.common.FilterCreaturePermanent;
import mage.filter.common.FilterSpiritOrArcaneCard;
import mage.filter.predicate.mageobject.ConvertedManaCostPredicate;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.stack.Spell;
@ -86,6 +88,7 @@ public class SkyfireKirin extends CardImpl {
int cmc = spell.getConvertedManaCost();
ability.getTargets().clear();
FilterPermanent filter = new FilterCreaturePermanent(new StringBuilder("creature with converted mana costs of ").append(cmc).toString());
filter.add(new ConvertedManaCostPredicate(Filter.ComparisonType.Equal, cmc));
Target target = new TargetPermanent(filter);
ability.addTarget(target);
}

View file

@ -60,6 +60,7 @@ public class Legends extends ExpansionSet {
cards.add(new SetCardInfo("Aisling Leprechaun", 87, Rarity.COMMON, mage.cards.a.AislingLeprechaun.class));
cards.add(new SetCardInfo("Akron Legionnaire", 170, Rarity.RARE, mage.cards.a.AkronLegionnaire.class));
cards.add(new SetCardInfo("Alchor's Tomb", 214, Rarity.RARE, mage.cards.a.AlchorsTomb.class));
cards.add(new SetCardInfo("All Hallow's Eve", 2, Rarity.RARE, mage.cards.a.AllHallowsEve.class));
cards.add(new SetCardInfo("Amrou Kithkin", 172, Rarity.COMMON, mage.cards.a.AmrouKithkin.class));
cards.add(new SetCardInfo("Angus Mackenzie", 257, Rarity.RARE, mage.cards.a.AngusMackenzie.class));
cards.add(new SetCardInfo("Arcades Sabboth", 258, Rarity.RARE, mage.cards.a.ArcadesSabboth.class));

View file

@ -56,6 +56,7 @@ public class MastersEditionIII extends ExpansionSet {
this.ratioBoosterMythic = 0;
cards.add(new SetCardInfo("Active Volcano", 85, Rarity.UNCOMMON, mage.cards.a.ActiveVolcano.class));
cards.add(new SetCardInfo("Akron Legionnaire", 1, Rarity.RARE, mage.cards.a.AkronLegionnaire.class));
cards.add(new SetCardInfo("All Hallow's Eve", 57, Rarity.RARE, mage.cards.a.AllHallowsEve.class));
cards.add(new SetCardInfo("Amrou Kithkin", 3, Rarity.COMMON, mage.cards.a.AmrouKithkin.class));
cards.add(new SetCardInfo("Anaba Ancestor", 86, Rarity.COMMON, mage.cards.a.AnabaAncestor.class));
cards.add(new SetCardInfo("Anaba Spirit Crafter", 87, Rarity.COMMON, mage.cards.a.AnabaSpiritCrafter.class));

View file

@ -142,6 +142,7 @@ public class Mirrodin extends ExpansionSet {
cards.add(new SetCardInfo("Leonin Sun Standard", 194, Rarity.RARE, mage.cards.l.LeoninSunStandard.class));
cards.add(new SetCardInfo("Leveler", 195, Rarity.RARE, mage.cards.l.Leveler.class));
cards.add(new SetCardInfo("Lifespark Spellbomb", 197, Rarity.COMMON, mage.cards.l.LifesparkSpellbomb.class));
cards.add(new SetCardInfo("Lightning Coils", 198, Rarity.RARE, mage.cards.l.LightningCoils.class));
cards.add(new SetCardInfo("Lightning Greaves", 199, Rarity.UNCOMMON, mage.cards.l.LightningGreaves.class));
cards.add(new SetCardInfo("Living Hive", 124, Rarity.RARE, mage.cards.l.LivingHive.class));
cards.add(new SetCardInfo("Lodestone Myr", 200, Rarity.RARE, mage.cards.l.LodestoneMyr.class));

View file

@ -103,11 +103,10 @@ public class GainControlTargetEffectTest extends CardTestPlayerBase {
// under control of Shackles even if it's no longer a creature
assertPermanentCount(playerB, "Mutavault", 0);
assertPermanentCount(playerA, "Mutavault", 1);
}
/**
* Steel Golem, once Donate'd to another player does not disable their ability to play creature cards.
* Steel Golem, once donated to another player does not disable their ability to play creature cards.
*/
@Test
public void testDonateSteelGolem() {
@ -134,7 +133,99 @@ public class GainControlTargetEffectTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Steel Golem", 1);
assertPermanentCount(playerB, "Silvercoat Lion", 0);
assertHandCount(playerB, "Silvercoat Lion", 1);
}
/*
Reported bug: Skyfire Kirin was allowed to steal a creature with a different CMC
than the card cast for it. Played a 5 CMC creature and stole a 3 CMC creature.
*/
@Test
public void testSkyfireKirinStealCreatureDifferentCMC()
{
/*
Skyfire Kirin {2}{R}{R}
Legendary Creature - Kirin Spirit 3/3
Flying
Whenever you cast a Spirit or Arcane spell, you may gain control of target creature with that spell's converted mana cost until end of turn.
*/
String sKirin = "Skyfire Kirin";
/*
Ore Gorger {3}{R}{R}
Creature Spirit 3/1
Whenever you cast a Spirit or Arcane spell, you may destroy target nonbasic land.
*/
String oGorger = "Ore Gorger";
/*
Leovold, Emissary of Trest {B}{G}{U}
Legendary Creature Elf Advisor 3/3
Each opponent can't draw more than one card each turn.
Whenever you or a permanent you control becomes the target of a spell or ability an opponent controls, you may draw a card.
*/
String leovold = "Leovold, Emissary of Trest";
addCard(Zone.BATTLEFIELD, playerA, sKirin);
addCard(Zone.HAND, playerA, oGorger);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerB, leovold);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, oGorger);
setChoice(playerA, "Yes"); // opt to use Kirin's ability
addTarget(playerA, leovold); // attempt to target Leovold with Kirin's take control ability
setChoice(playerB, "Yes"); // opt to use Leovold's ability to draw a card when targetted (should not occur)
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, oGorger, 1);
assertPermanentCount(playerA, leovold, 0); // should not have gained control
assertPermanentCount(playerB, leovold, 1); // still under playerB control
assertHandCount(playerB, 0); // leovold ability should not have triggered due to not targetted, so no extra cards
}
/*
Skyfire Kirin should steal be able to steal creatures with same CMC.
*/
@Test
public void testSkyfireKirinStealCreatureSameCMC()
{
/*
Skyfire Kirin {2}{R}{R}
Legendary Creature - Kirin Spirit 3/3
Flying
Whenever you cast a Spirit or Arcane spell, you may gain control of target creature with that spell's converted mana cost until end of turn.
*/
String sKirin = "Skyfire Kirin";
/*
Ore Gorger {3}{R}{R}
Creature Spirit 3/1
Whenever you cast a Spirit or Arcane spell, you may destroy target nonbasic land.
*/
String oGorger = "Ore Gorger";
/*
Angel of Light {4}{W}
Creature Angel (3/3)
Flying, vigilance
*/
String aLight = "Angel of Light"; // 5 cmc creature, so valid to steal with Ore Gorger
addCard(Zone.BATTLEFIELD, playerA, sKirin);
addCard(Zone.HAND, playerA, oGorger);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 5);
addCard(Zone.BATTLEFIELD, playerB, aLight);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, oGorger);
setChoice(playerA, "Yes"); // opt to use Kirin's ability
addTarget(playerA, aLight); // target Angel of Light with Kirin's take control ability
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertPermanentCount(playerA, oGorger, 1);
assertPermanentCount(playerA, aLight, 1); // should have gained control of Angel
assertPermanentCount(playerB, aLight, 0); // Angel no longer under opponent's control
}
}

View file

@ -167,4 +167,87 @@ public class CantAttackTest extends CardTestPlayerBase {
assertPowerToughness(playerB, "Silvercoat Lion", 4, 4);
}
/*
Reported bug: Medomai was able to attack on an extra turn when cheated into play.
*/
@Test
public void testMedomaiShouldNotAttackOnExtraTurns() {
/*
Medomai the Ageless {4}{W}{U}
Legendary Creature Sphinx 4/4
Flying
Whenever Medomai the Ageless deals combat damage to a player, take an extra turn after this one.
Medomai the Ageless can't attack during extra turns.
*/
String medomai = "Medomai the Ageless";
/*
Cauldron Dance {4}{B}{R} Instant
Cast Cauldron Dance only during combat.
Return target creature card from your graveyard to the battlefield. That creature gains haste. Return it to your hand at the beginning of the next end step.
You may put a creature card from your hand onto the battlefield. That creature gains haste. Its controller sacrifices it at the beginning of the next end step.
*/
String cDance = "Cauldron Dance";
String dBlade = "Doom Blade"; // {1}{B} instant destroy target creature
addCard(Zone.BATTLEFIELD, playerA, medomai);
addCard(Zone.HAND, playerA, dBlade);
addCard(Zone.HAND, playerA, cDance);
addCard(Zone.BATTLEFIELD, playerA, "Swamp", 4);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
// attack with Medomai, connect, and destroy him after combat
attack(1, playerA, medomai);
castSpell(1, PhaseStep.POSTCOMBAT_MAIN, playerA, dBlade, medomai);
// next turn granted, return Medomai to field with Cauldron and try to attack again
castSpell(2, PhaseStep.BEGIN_COMBAT, playerA, cDance);
addTarget(playerA, medomai);
attack(2, playerA, medomai);
// medomai should not have been allowed to attack, but returned to hand at beginning of next end step still
setStopAt(2, PhaseStep.END_TURN);
execute();
assertLife(playerB, 16); // one hit from medomai
assertGraveyardCount(playerA, dBlade, 1);
assertGraveyardCount(playerA, cDance, 1);
assertGraveyardCount(playerA, medomai, 0);
assertHandCount(playerA, medomai, 1);
}
@Test
public void basicMedomaiTestForExtraTurn() {
/*
Medomai the Ageless {4}{W}{U}
Legendary Creature Sphinx 4/4
Flying
Whenever Medomai the Ageless deals combat damage to a player, take an extra turn after this one.
Medomai the Ageless can't attack during extra turns.
*/
String medomai = "Medomai the Ageless";
/*
Exquisite Firecraft {1}{R}{R}
Sorcery
Exquisite Firecraft deals 4 damage to target creature or player.
*/
String eFirecraft = "Exquisite Firecraft";
addCard(Zone.BATTLEFIELD, playerA, medomai);
addCard(Zone.HAND, playerA, eFirecraft);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 3);
// attack with medomai, get extra turn, confirm cannot attack again with medomai and can cast sorcery
attack(1, playerA, medomai);
attack(2, playerA, medomai); // should not be allowed to
castSpell(2, PhaseStep.POSTCOMBAT_MAIN, playerA, eFirecraft, playerB);
setStopAt(2, PhaseStep.END_TURN);
execute();
assertLife(playerB, 12); // 1 hit from medomai and firecraft = 8 damage
assertGraveyardCount(playerA, eFirecraft, 1);
assertPermanentCount(playerA, medomai, 1);
}
}

View file

@ -0,0 +1,57 @@
package org.mage.test.cards.single;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author escplan9
*/
public class DivergentTransformationsTest extends CardTestPlayerBase {
/*
* Reported bug: creatures exiled end up in both the exile pile and remain on the battlefield.
* Manual testing unable to reproduce this issue.
*/
@Test
public void testDivergentTransformationsExiles()
{
/*
Divergent Transformations {6}{R}
Instant
Undaunted (This spell costs 1 less to cast for each opponent.)
Exile two target creatures. For each of those creatures, its controller reveals cards from the top of his or her library until he or she reveals a creature card,
puts that card onto the battlefield, then shuffles the rest into his or her library.
*/
String dTransformations = "Divergent Transformations";
String memnite = "Memnite"; // {0} 1/1
String gBears = "Grizzly Bears"; // {1}{G} 2/2
String hGiant = "Hill Giant"; // {3}{R} 3/3
String mFlunkies = "Mogg Flunkies"; // {1}{R} 3/3 cannot attack alone
addCard(Zone.HAND, playerA, dTransformations);
addCard(Zone.BATTLEFIELD, playerA, memnite);
addCard(Zone.BATTLEFIELD, playerB, gBears);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 6); // costs 1 less with Undaunted
addCard(Zone.LIBRARY, playerA, hGiant);
addCard(Zone.LIBRARY, playerB, mFlunkies);
castSpell(1, PhaseStep.PRECOMBAT_MAIN, playerA, dTransformations, memnite + "^" + gBears);
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertGraveyardCount(playerA, dTransformations, 1);
assertTappedCount("Mountain", true, 6);
assertExileCount(playerA, memnite, 1);
assertExileCount(playerA, 1);
assertExileCount(playerB, gBears, 1);
assertExileCount(playerB, 1);
assertPermanentCount(playerA, memnite, 0);
assertPermanentCount(playerB, gBears, 0);
assertPermanentCount(playerA, hGiant, 1); // revealed and brought to battlefield
assertPermanentCount(playerB, mFlunkies, 1);
}
}

View file

@ -0,0 +1,54 @@
package org.mage.test.cards.single.bfz;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
* Created by escplan9
*/
public class EmeriaShepherdTest extends CardTestPlayerBase {
/*
* Reported bug: Emeria Shepherd can't bounce Bruna to the table when a plains enters the battlefield.
*/
@Test
public void emeriaInteractionWithBruna()
{
/*
*
* Emeria Shepherd (5)(W)(W)
Flying 4/4 Creature Angel
Landfall Whenever a land enters the battlefield under your control, you may return target nonland permanent card from your graveyard to your hand.
If that land is a Plains, you may return that nonland permanent card to the battlefield instead.
*/
String emeria = "Emeria Shepherd";
/*
* Bruna, The Fading Light (5)(W)(W)
* Legendary Creature - Angel Horror 5/7
When you cast Bruna, the Fading Light, you may return target Angel or Human creature card from your graveyard to the battlefield.
Flying, vigilance
(Melds with Gisela, the Broken Blade.)
*/
String bruna = "Bruna, the Fading Light";
addCard(Zone.BATTLEFIELD, playerA, emeria);
addCard(Zone.HAND, playerA, "Plains");
addCard(Zone.GRAVEYARD, playerA, bruna);
addCard(Zone.BATTLEFIELD, playerA, "Plains", 7);
playLand(1, PhaseStep.PRECOMBAT_MAIN, playerA, "Plains");
setChoice(playerA, "Yes"); // opt to use Emeria's triggered ability
addTarget(playerA, bruna); // target Bruna in grave
setStopAt(1, PhaseStep.BEGIN_COMBAT);
execute();
assertHandCount(playerA, "Plains", 0);
assertGraveyardCount(playerA, bruna, 0);
assertHandCount(playerA, bruna, 0);
assertPermanentCount(playerA, bruna, 1);
}
}

View file

@ -0,0 +1,99 @@
package org.mage.test.cards.single.mir;
import mage.constants.PhaseStep;
import mage.constants.Zone;
import mage.counters.CounterType;
import org.junit.Test;
import org.mage.test.serverside.base.CardTestPlayerBase;
/**
*
* @author escplan9 (Derek Monturo)
*/
public class LightningCoilsTest extends CardTestPlayerBase {
@Test
public void sacrificeSixCreaturesProducesSixElementals() {
/*
Lightning Coils {3}
Artifact
Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils.
At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it
and create that many 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step.
*/
String lCoils = "Lightning Coils";
/*
Bottle Gnomes {3}
Artifact Creature Gnome
Sacrifice Bottle Gnomes: You gain 3 life.
*/
String bGnomes = "Bottle Gnomes";
int gnomeCount = 6;
addCard(Zone.BATTLEFIELD, playerA, lCoils);
addCard(Zone.BATTLEFIELD, playerA, bGnomes, gnomeCount);
for (int i = 0; i < gnomeCount; i++) // sac Gnomes 6 times
activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice");
setStopAt(3, PhaseStep.PRECOMBAT_MAIN);
execute();
assertGraveyardCount(playerA, bGnomes, gnomeCount);
assertPermanentCount(playerA, lCoils, 1);
assertCounterCount(playerA, lCoils, CounterType.CHARGE, 0);
assertPermanentCount(playerA, "Elemental", gnomeCount);
}
@Test
public void sacrificeSixCreaturesProducesSixElementalsExiledAtEnd() {
/*
Lightning Coils {3}
Artifact
Whenever a nontoken creature you control dies, put a charge counter on Lightning Coils.
At the beginning of your upkeep, if Lightning Coils has five or more charge counters on it, remove all of them from it
and create that many 3/1 red Elemental creature tokens with haste. Exile them at the beginning of the next end step.
*/
String lCoils = "Lightning Coils";
/*
Bottle Gnomes {3}
Artifact Creature Gnome
Sacrifice Bottle Gnomes: You gain 3 life.
*/
String bGnomes = "Bottle Gnomes";
/*
Grand Melee {3}{R}
Enchantment
All creatures attack each turn if able.
All creatures block each turn if able.
*/
String gMelee = "Grand Melee";
int tokenCount = 5;
addCard(Zone.BATTLEFIELD, playerA, lCoils);
addCard(Zone.BATTLEFIELD, playerA, bGnomes, tokenCount);
addCard(Zone.BATTLEFIELD, playerA, "Mountain", 4);
addCard(Zone.HAND, playerA, gMelee);
for (int i = 0; i < tokenCount; i++) // sac Gnomes 5 times
activateAbility(2, PhaseStep.POSTCOMBAT_MAIN, playerA, "Sacrifice");
castSpell(3, PhaseStep.PRECOMBAT_MAIN, playerA, gMelee);
setStopAt(3, PhaseStep.CLEANUP);
execute();
assertPermanentCount(playerA, gMelee, 1);
assertGraveyardCount(playerA, bGnomes, tokenCount);
assertPermanentCount(playerA, lCoils, 1);
assertCounterCount(playerA, lCoils, CounterType.CHARGE, 0);
assertPermanentCount(playerA, "Elemental", 0);
int remainingLife = 20 - (tokenCount * 3); // each elemental does 3 damage
assertLife(playerB, remainingLife);
}
}

View file

@ -332,4 +332,101 @@ public class AttackBlockRestrictionsTest extends CardTestPlayerBase {
assertPermanentCount(playerB, "Storm Crow", 0);
}
/*
* Mogg Flunkies cannot attack alone. Cards like Goblin Assault force all goblins to attack each turn.
* Mogg Flunkies should not be able to attack.
*/
@Test
public void testMustAttackButCannotAttackAlone()
{
/* Mogg Flunkies {1}{R} 3/3
Creature Goblin
Mogg Flunkies can't attack or block alone.
*/
String flunkies = "Mogg Flunkies";
/* Goblin Assault {2}{R}
* Enchantment
At the beginning of your upkeep, create a 1/1 red Goblin creature token with haste.
Goblin creatures attack each turn if able.
*/
String gAssault = "Goblin Assault";
addCard(Zone.BATTLEFIELD, playerA, flunkies);
addCard(Zone.BATTLEFIELD, playerB, gAssault);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertTapped(flunkies, false);
assertLife(playerB, 20);
}
/*
Reported bug: Tromokratis is unable to be blocked.
*/
@Test
public void tromokratisBlockedByAll() {
/*
Tromokratis {5}{U}{U}
Legendary Creature Kraken 8/8
Tromokratis has hexproof unless it's attacking or blocking.
Tromokratis can't be blocked unless all creatures defending player controls block it. (If any creature that player controls doesn't block this creature, it can't be blocked.)
*/
String tromokratis = "Tromokratis";
String gBears = "Grizzly Bears"; // {1}{G} 2/2
String memnite = "Memnite"; // {0} 1/1
addCard(Zone.BATTLEFIELD, playerA, tromokratis);
addCard(Zone.BATTLEFIELD, playerB, gBears);
addCard(Zone.BATTLEFIELD, playerB, memnite);
attack(1, playerA, tromokratis);
block(1, playerB, gBears, tromokratis);
block(1, playerB, memnite, tromokratis);
setStopAt(1, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerB, 20);
assertGraveyardCount(playerB, gBears, 1);
assertGraveyardCount(playerB, memnite, 1);
assertTapped(tromokratis, true);
}
/*
Reported bug: Tromokratis is unable to be blocked.
*/
@Test
public void tromokratisNotBlockedByAll() {
/*
Tromokratis {5}{U}{U}
Legendary Creature Kraken 8/8
Tromokratis has hexproof unless it's attacking or blocking.
Tromokratis can't be blocked unless all creatures defending player controls block it. (If any creature that player controls doesn't block this creature, it can't be blocked.)
*/
String tromokratis = "Tromokratis";
String gBears = "Grizzly Bears"; // {1}{G} 2/2
String memnite = "Memnite"; // {0} 1/1
String hGiant = "Hill Giant"; // {3}{R} 3/3
addCard(Zone.BATTLEFIELD, playerA, tromokratis);
addCard(Zone.BATTLEFIELD, playerB, gBears);
addCard(Zone.BATTLEFIELD, playerB, memnite);
addCard(Zone.BATTLEFIELD, playerB, hGiant);
attack(2, playerB, hGiant); // forces a creature to be tapped so unable to block Tromokratis, which means it cannot be blocked at all
attack(3, playerA, tromokratis);
block(3, playerB, gBears, tromokratis);
block(3, playerB, memnite, tromokratis);
setStopAt(3, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertLife(playerB, 12); // Hill Giant could not block it, so no other creature could block Tromokratis either
assertPermanentCount(playerB, gBears, 1);
assertPermanentCount(playerB, memnite, 1);
assertTapped(tromokratis, true);
assertTapped(hGiant, true);
}
}

View file

@ -37,7 +37,7 @@ import org.mage.test.serverside.base.CardTestPlayerBase;
*
* @author jeffwadsworth
*/
public class WatcherInTheWebMultipleBlocks extends CardTestPlayerBase {
public class CanBlockMultipleCreatures extends CardTestPlayerBase {
// test must be ignored until creature blocking multiple supported by test framework
@Ignore
@ -74,4 +74,46 @@ public class WatcherInTheWebMultipleBlocks extends CardTestPlayerBase {
}
/*
* Reported bug: Night Market Guard was able to block a creature with Menace
*/
@Test
public void testNightMarketGuardShouldNotBlockCreatureWithMenace()
{
/*
Night Market Guard {3} 3/1
Artifact Creature Construct
Night Market Guard can block an additional creature each combat.
*/
String nMarketGuard = "Night Market Guard";
/*
Embraal Bruiser {1}{B}
Creature - Human Warrior
Embraal Bruiser enters the battlefield tapped.
Embraal Bruiser has menace as long as you control an artifact.
*/
String eBruiser = "Embraal Bruiser";
/*
{0} 1/1
* Artifact Creature Construct
*/
String memnite = "Memnite";
addCard(Zone.BATTLEFIELD, playerA, nMarketGuard);
addCard(Zone.BATTLEFIELD, playerB, eBruiser);
addCard(Zone.BATTLEFIELD, playerB, memnite); // only here to grant Embraal Menace
attack(4, playerB, eBruiser);
block(4, playerA, nMarketGuard, eBruiser);
setStopAt(4, PhaseStep.POSTCOMBAT_MAIN);
execute();
assertTapped(eBruiser, true);
assertLife(playerA, 17); // could not block, so 3 damage goes through
assertPermanentCount(playerA, nMarketGuard, 1);
assertPermanentCount(playerB, eBruiser, 1);
}
}

View file

@ -107,7 +107,7 @@ public class LoadTest {
if (!session.joinTable(roomId, table.getTableId(), TEST_USER_NAME + i, "Human", 1, deckList,"")) {
log.error("Error while joining table");
Assert.assertTrue("Error while joining table", false);
Assert.fail("Error while joining table");
return;
}
@ -121,7 +121,7 @@ public class LoadTest {
// connect to the table with the same deck
if (!session2.joinTable(roomId2, table.getTableId(), TEST_USER_NAME_2 + i, "Human", 1, deckList,"")) {
log.error("Error while joining table");
Assert.assertTrue("Error while joining table", false);
Assert.fail("Error while joining table");
return;
}
@ -179,7 +179,7 @@ public class LoadTest {
if (!session.joinTable(roomId, table.getTableId(), TEST_USER_NAME + i, "Human", 1, deckList,"")) {
log.error("Error while joining table");
Assert.assertTrue("Error while joining table", false);
Assert.fail("Error while joining table");
return true;
}
@ -195,7 +195,7 @@ public class LoadTest {
// connect to the table with the same deck
if (!session2.joinTable(roomId2, table.getTableId(), TEST_USER_NAME_2 + i, "Human", 1, deckList,"")) {
log.error("Error while joining table");
Assert.assertTrue("Error while joining table", false);
Assert.fail("Error while joining table");
return true;
}

View file

@ -30,12 +30,16 @@ package mage.abilities.effects.common;
import java.util.ArrayList;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
import mage.abilities.dynamicvalue.DynamicValue;
import mage.abilities.dynamicvalue.common.StaticValue;
import mage.abilities.effects.OneShotEffect;
import mage.constants.Outcome;
import mage.constants.Zone;
import mage.game.Game;
import mage.game.permanent.Permanent;
import mage.game.permanent.token.Token;
import mage.target.targetpointer.FixedTarget;
import mage.util.CardUtil;
/**
@ -109,6 +113,17 @@ public class CreateTokenEffect extends OneShotEffect {
return lastAddedTokenIds;
}
public void exileTokensCreatedAtNextEndStep(Game game, Ability source) {
for (UUID tokenId : this.getLastAddedTokenIds()) {
Permanent tokenPermanent = game.getPermanent(tokenId);
if (tokenPermanent != null) {
ExileTargetEffect exileEffect = new ExileTargetEffect(null, "", Zone.BATTLEFIELD);
exileEffect.setTargetPointer(new FixedTarget(tokenPermanent, game));
game.addDelayedTriggeredAbility(new AtTheBeginOfNextEndStepDelayedTriggeredAbility(exileEffect), source);
}
}
}
private void setText() {
StringBuilder sb = new StringBuilder("create ");
if (amount.toString().equals("1")) {
@ -144,5 +159,4 @@ public class CreateTokenEffect extends OneShotEffect {
sb.append(message);
staticText = sb.toString();
}
}

View file

@ -31,6 +31,7 @@ import java.util.LinkedHashSet;
import java.util.Set;
import mage.Mana;
import mage.abilities.Ability;
import mage.abilities.ActivatedAbility;
import mage.abilities.SpellAbility;
import mage.cards.Card;
import mage.choices.ChoiceImpl;
@ -85,6 +86,12 @@ public class SpellsCostReductionAllEffect extends CostModificationEffectImpl {
@Override
public boolean apply(Game game, Ability source, Ability abilityToModify) {
if (upTo) {
if (abilityToModify instanceof ActivatedAbility) {
if (((ActivatedAbility) abilityToModify).isCheckPlayableMode()) {
CardUtil.reduceCost(abilityToModify, this.amount);
return true;
}
}
Mana mana = abilityToModify.getManaCostsToPay().getMana();
int reduceMax = mana.getGeneric();
if (reduceMax > 2) {

View file

@ -970,6 +970,7 @@ public abstract class PermanentImpl extends CardImpl implements Permanent {
game.informPlayers(player.getLogName() + " sacrificed " + this.getLogName());
}
game.fireEvent(GameEvent.getEvent(EventType.SACRIFICED_PERMANENT, objectId, sourceId, controllerId));
game.checkStateAndTriggered();
return true;
}
return false;

View file

@ -32,6 +32,7 @@ import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import mage.MageInt;
import mage.abilities.keyword.HasteAbility;
import mage.constants.CardType;
/**
@ -59,4 +60,17 @@ public class ElementalToken extends Token {
this.setOriginalExpansionSetCode("CON");
}
public ElementalToken(String setCode, int tokenType, boolean hasHaste) {
super("Elemental", "3/1 red Elemental creature token");
setTokenType(tokenType);
availableImageSetCodes = tokenImageSets;
setOriginalExpansionSetCode(setCode);
cardType.add(CardType.CREATURE);
color.setRed(true);
subtype.add("Elemental");
power = new MageInt(3);
toughness = new MageInt(1);
if (hasHaste) this.addAbility(HasteAbility.getInstance());
}
}