Merge pull request #50 from magefree/master

merge
This commit is contained in:
theelk801 2017-09-01 21:25:20 -04:00 committed by GitHub
commit bda6658d79
29 changed files with 376 additions and 293 deletions

View file

@ -27,6 +27,10 @@
*/ */
package mage.client.remote; package mage.client.remote;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.UUID;
import javax.swing.*;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.client.MageFrame; import mage.client.MageFrame;
import mage.client.SessionHandler; import mage.client.SessionHandler;
@ -48,11 +52,6 @@ import mage.view.*;
import mage.view.ChatMessage.MessageType; import mage.view.ChatMessage.MessageType;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import javax.swing.*;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.UUID;
/** /**
* *
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
@ -325,11 +324,11 @@ public class CallbackClientImpl implements CallbackClient {
break; break;
} }
case VIEW_LIMITED_DECK: { case VIEW_LIMITED_DECK: {
TableClientMessage message = (TableClientMessage) callback.getData(); TableClientMessage message = (TableClientMessage) callback.getData();
DeckView deckView = message.getDeck(); DeckView deckView = message.getDeck();
Deck deck = DeckUtil.construct(deckView); Deck deck = DeckUtil.construct(deckView);
viewLimitedDeck(deck, message.getTableId(), message.getTime()); viewLimitedDeck(deck, message.getTableId(), message.getTime());
break; break;
} }
case CONSTRUCT: { case CONSTRUCT: {
TableClientMessage message = (TableClientMessage) callback.getData(); TableClientMessage message = (TableClientMessage) callback.getData();
@ -356,14 +355,6 @@ public class CallbackClientImpl implements CallbackClient {
} }
break; break;
} }
case DRAFT_INFORM: // if (callback.getMessageId() > messageId) {
{
DraftClientMessage message = (DraftClientMessage) callback.getData();
}
// } else {
// logger.warn("message out of sequence - ignoring");
// }
break;
case DRAFT_INIT: { case DRAFT_INIT: {
DraftClientMessage message = (DraftClientMessage) callback.getData(); DraftClientMessage message = (DraftClientMessage) callback.getData();
DraftPanel panel = MageFrame.getDraft(callback.getObjectId()); DraftPanel panel = MageFrame.getDraft(callback.getObjectId());

View file

@ -30,18 +30,27 @@ public enum ClientCallbackMethod {
GAME_UPDATE("gameUpdate"), GAME_UPDATE("gameUpdate"),
DRAFT_OVER("draftOver"), DRAFT_OVER("draftOver"),
REPLAY_DONE("replayDone"), REPLAY_DONE("replayDone"),
USER_REQUEST_DIALOG("userRequestDialog"), USER_REQUEST_DIALOG("userRequestDialog"),
REPLAY_UPDATE("replayUpdate"), REPLAY_UPDATE("replayUpdate"),
REPLAY_INIT("replayInit"), REPLAY_INIT("replayInit"),
END_GAME_INFO("endGameInfo"), END_GAME_INFO("endGameInfo"),
GAME_TARGET("gameTarget"), GAME_TARGET("gameTarget"),
GAME_CHOOSE_ABILITY("gameChooseAbility"), GAME_CHOOSE_ABILITY("gameChooseAbility"),
GAME_CHOOSE_PILE("gameChoosePile"), GAME_CHOOSE_PILE("gameChoosePile"),
GAME_CHOOSE_CHOICE("gameChooseChoice"), GAME_ASK("gameAsk"), GAME_SELECT("gameSelect"), GAME_PLAY_MANA("gamePlayMana"), GAME_PLAY_XMANA("gamePlayXMana"), GAME_GET_AMOUNT("gameSelectAmount"), DRAFT_INIT("draftInit"), DRAFT_INFORM("draftInform"), DRAFT_PICK("draftPick"), DRAFT_UPDATE("draftUpdate"); GAME_CHOOSE_CHOICE("gameChooseChoice"),
GAME_ASK("gameAsk"),
GAME_SELECT("gameSelect"),
GAME_PLAY_MANA("gamePlayMana"),
GAME_PLAY_XMANA("gamePlayXMana"),
GAME_GET_AMOUNT("gameSelectAmount"),
DRAFT_INIT("draftInit"),
// DRAFT_INFORM("draftInform"),
DRAFT_PICK("draftPick"),
DRAFT_UPDATE("draftUpdate");
String value; String value;
ClientCallbackMethod(String value){ ClientCallbackMethod(String value) {
this.value = value; this.value = value;
} }
} }

View file

@ -35,7 +35,6 @@ import java.util.concurrent.TimeUnit;
import javax.swing.*; import javax.swing.*;
import mage.MageException; import mage.MageException;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.cards.decks.InvalidDeckException;
import mage.cards.repository.CardInfo; import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository; import mage.cards.repository.CardRepository;
import mage.cards.repository.ExpansionInfo; import mage.cards.repository.ExpansionInfo;
@ -691,8 +690,6 @@ public class SessionImpl implements Session {
} }
return server.joinTable(sessionId, roomId, tableId, playerName, playerType, skill, deckList, password); return server.joinTable(sessionId, roomId, tableId, playerName, playerType, skill, deckList, password);
} }
} catch (InvalidDeckException iex) {
handleInvalidDeckException(iex);
} catch (GameException ex) { } catch (GameException ex) {
handleGameException(ex); handleGameException(ex);
} catch (MageException ex) { } catch (MageException ex) {
@ -1547,11 +1544,6 @@ public class SessionImpl implements Session {
client.showError(ex.getMessage()); client.showError(ex.getMessage());
} }
private void handleInvalidDeckException(InvalidDeckException iex) {
logger.warn(iex.getMessage() + '\n' + iex.getInvalid());
client.showError(iex.getMessage() + '\n' + iex.getInvalid());
}
private void handleGameException(GameException ex) { private void handleGameException(GameException ex) {
logger.warn(ex.getMessage()); logger.warn(ex.getMessage());
client.showError(ex.getMessage()); client.showError(ex.getMessage());

View file

@ -834,10 +834,23 @@ public class HumanPlayer extends PlayerImpl {
return !controllingPlayer.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType()); return !controllingPlayer.getUserData().getUserSkipPrioritySteps().getOpponentTurn().isPhaseStepSet(game.getStep().getType());
} }
} catch (NullPointerException ex) { } catch (NullPointerException ex) {
String isNull = controllingPlayer.getUserData() == null ? "null" : "not null"; if (controllingPlayer.getUserData() != null) {
logger.error("null pointer exception UserData = " + isNull); if (controllingPlayer.getUserData().getUserSkipPrioritySteps() != null) {
if (game.getStep() != null) {
if (game.getStep().getType() == null) {
logger.error("game.getStep().getType() == null");
}
} else {
logger.error("game.getStep() == null");
}
} else {
logger.error("UserData.getUserSkipPrioritySteps == null");
}
} else {
logger.error("UserData == null");
}
} }
return true; return false;
} }
@Override @Override
@ -1223,7 +1236,9 @@ public class HumanPlayer extends PlayerImpl {
FilterCreatureForCombatBlock filter = filterCreatureForCombatBlock.copy(); FilterCreatureForCombatBlock filter = filterCreatureForCombatBlock.copy();
filter.add(new ControllerIdPredicate(defendingPlayerId)); filter.add(new ControllerIdPredicate(defendingPlayerId));
if (game.getBattlefield().count(filter, null, playerId, game) == 0 if (game.getBattlefield().count(filter, null, playerId, game) == 0
&& !getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareBlockerIfNoneAvailable()) { && !getControllingPlayersUserData(game)
.getUserSkipPrioritySteps()
.isStopOnDeclareBlockerIfNoneAvailable()) {
return; return;
} }
while (!abort) { while (!abort) {

View file

@ -374,11 +374,9 @@ public class Session {
call.setMessageId(messageId++); call.setMessageId(messageId++);
callbackHandler.handleCallbackOneway(new Callback(call)); callbackHandler.handleCallbackOneway(new Callback(call));
} catch (HandleCallbackException ex) { } catch (HandleCallbackException ex) {
// ex.printStackTrace();
UserManager.instance.getUser(userId).ifPresent(user -> { UserManager.instance.getUser(userId).ifPresent(user -> {
logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId); user.setUserState(User.UserState.Disconnected);
logger.warn(" - method: " + call.getMethod()); logger.warn("SESSION CALLBACK EXCEPTION - " + user.getName() + " userId " + userId + " - cause: " + getBasicCause(ex).toString());
logger.warn(" - cause: " + getBasicCause(ex).toString());
logger.trace("Stack trace:", ex); logger.trace("Stack trace:", ex);
SessionManager.instance.disconnect(sessionId, LostConnection); SessionManager.instance.disconnect(sessionId, LostConnection);
}); });

View file

@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit;
import mage.MageException; import mage.MageException;
import mage.cards.decks.Deck; import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.cards.decks.InvalidDeckException;
import mage.constants.RangeOfInfluence; import mage.constants.RangeOfInfluence;
import mage.constants.TableState; import mage.constants.TableState;
import mage.game.*; import mage.game.*;
@ -414,7 +413,17 @@ public class TableController {
} }
} }
if (!Main.isTestMode() && !table.getValidator().validate(deck)) { if (!Main.isTestMode() && !table.getValidator().validate(deck)) {
throw new InvalidDeckException("Invalid deck for this format", table.getValidator().getInvalid()); Optional<User> _user = UserManager.instance.getUser(userId);
if (!_user.isPresent()) {
return false;
}
StringBuilder sb = new StringBuilder("Invalid deck for the selected ").append(table.getValidator().getName()).append(" format. \n\n");
for (Map.Entry<String, String> entry : table.getValidator().getInvalid().entrySet()) {
sb.append(entry.getKey()).append(": ").append(entry.getValue()).append('\n');
}
sb.append("\n\nAdd enough cards and try again!");
_user.get().showUserMessage("Submit deck", sb.toString());
return false;
} }
submitDeck(userId, playerId, deck); submitDeck(userId, playerId, deck);
return true; return true;

View file

@ -35,6 +35,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import mage.MageException; import mage.MageException;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.constants.TableState; import mage.constants.TableState;
@ -66,7 +69,10 @@ public enum TableManager {
private static final DateFormat formatter = new SimpleDateFormat("HH:mm:ss"); private static final DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
private final ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<>();
private final ReadWriteLock controllersLock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<>();
private final ReadWriteLock tablesLock = new ReentrantReadWriteLock();
/** /**
* Defines how often checking process should be run on server. * Defines how often checking process should be run on server.
@ -88,25 +94,45 @@ public enum TableManager {
public Table createTable(UUID roomId, UUID userId, MatchOptions options) { public Table createTable(UUID roomId, UUID userId, MatchOptions options) {
TableController tableController = new TableController(roomId, userId, options); TableController tableController = new TableController(roomId, userId, options);
controllers.put(tableController.getTable().getId(), tableController); putControllers(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable()); putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable(); return tableController.getTable();
} }
public Table createTable(UUID roomId, MatchOptions options) { public Table createTable(UUID roomId, MatchOptions options) {
TableController tableController = new TableController(roomId, null, options); TableController tableController = new TableController(roomId, null, options);
controllers.put(tableController.getTable().getId(), tableController); putControllers(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable()); putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable(); return tableController.getTable();
} }
public Table createTournamentTable(UUID roomId, UUID userId, TournamentOptions options) { public Table createTournamentTable(UUID roomId, UUID userId, TournamentOptions options) {
TableController tableController = new TableController(roomId, userId, options); TableController tableController = new TableController(roomId, userId, options);
controllers.put(tableController.getTable().getId(), tableController); putControllers(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable()); putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable(); return tableController.getTable();
} }
private void putTables(UUID tableId, Table table) {
final Lock w = tablesLock.writeLock();
w.lock();
try {
tables.put(tableId, table);
} finally {
w.unlock();
}
}
private void putControllers(UUID controllerId, TableController tableController) {
final Lock w = controllersLock.writeLock();
w.lock();
try {
controllers.put(controllerId, tableController);
} finally {
w.unlock();
}
}
public Table getTable(UUID tableId) { public Table getTable(UUID tableId) {
return tables.get(tableId); return tables.get(tableId);
} }
@ -119,7 +145,27 @@ public enum TableManager {
} }
public Collection<Table> getTables() { public Collection<Table> getTables() {
return tables.values(); Collection<Table> newTables = new ArrayList<>();
final Lock r = tablesLock.readLock();
r.lock();
try {
newTables.addAll(tables.values());
} finally {
r.unlock();
}
return newTables;
}
public Collection<TableController> getControllers() {
Collection<TableController> newControllers = new ArrayList<>();
final Lock r = controllersLock.readLock();
r.lock();
try {
newControllers.addAll(controllers.values());
} finally {
r.unlock();
}
return newControllers;
} }
public Optional<TableController> getController(UUID tableId) { public Optional<TableController> getController(UUID tableId) {
@ -164,7 +210,7 @@ public enum TableManager {
// removeUserFromAllTablesAndChat user from all tournament sub tables // removeUserFromAllTablesAndChat user from all tournament sub tables
public void userQuitTournamentSubTables(UUID userId) { public void userQuitTournamentSubTables(UUID userId) {
for (TableController controller : controllers.values()) { for (TableController controller : getControllers()) {
if (controller.getTable() != null) { if (controller.getTable() != null) {
if (controller.getTable().isTournamentSubTable()) { if (controller.getTable().isTournamentSubTable()) {
controller.leaveTable(userId); controller.leaveTable(userId);
@ -177,7 +223,7 @@ public enum TableManager {
// removeUserFromAllTablesAndChat user from all sub tables of a tournament // removeUserFromAllTablesAndChat user from all sub tables of a tournament
public void userQuitTournamentSubTables(UUID tournamentId, UUID userId) { public void userQuitTournamentSubTables(UUID tournamentId, UUID userId) {
for (TableController controller : controllers.values()) { for (TableController controller : getControllers()) {
if (controller.getTable().isTournamentSubTable() && controller.getTable().getTournament().getId().equals(tournamentId)) { if (controller.getTable().isTournamentSubTable() && controller.getTable().getTournament().getId().equals(tournamentId)) {
if (controller.hasPlayer(userId)) { if (controller.hasPlayer(userId)) {
controller.leaveTable(userId); controller.leaveTable(userId);
@ -268,12 +314,6 @@ public enum TableManager {
return false; return false;
} }
// public boolean replayTable(UUID userId, UUID tableId) {
// if (controllers.containsKey(tableId)) {
// return controllers.get(tableId).replayTable(userId);
// }
// return false;
// }
public void endGame(UUID tableId) { public void endGame(UUID tableId) {
if (controllers.containsKey(tableId)) { if (controllers.containsKey(tableId)) {
if (controllers.get(tableId).endGameAndStartNextGame()) { if (controllers.get(tableId).endGameAndStartNextGame()) {
@ -321,11 +361,24 @@ public enum TableManager {
public void removeTable(UUID tableId) { public void removeTable(UUID tableId) {
TableController tableController = controllers.get(tableId); TableController tableController = controllers.get(tableId);
if (tableController != null) { if (tableController != null) {
controllers.remove(tableId); Lock w = controllersLock.writeLock();
w.lock();
try {
controllers.remove(tableId);
} finally {
w.unlock();
}
tableController.cleanUp(); // deletes the table chat and references to users tableController.cleanUp(); // deletes the table chat and references to users
Table table = tables.get(tableId); Table table = tables.get(tableId);
tables.remove(tableId); w = tablesLock.writeLock();
w.lock();
try {
tables.remove(tableId);
} finally {
w.unlock();
}
Match match = table.getMatch(); Match match = table.getMatch();
Game game = null; Game game = null;
if (match != null) { if (match != null) {
@ -383,8 +436,7 @@ public enum TableManager {
debugServerState(); debugServerState();
} }
logger.debug("TABLE HEALTH CHECK"); logger.debug("TABLE HEALTH CHECK");
List<Table> tableCopy = new ArrayList<>(tables.values()); for (Table table : getTables()) {
for (Table table : tableCopy) {
try { try {
if (table.getState() != TableState.FINISHED if (table.getState() != TableState.FINISHED
&& ((System.currentTimeMillis() - table.getStartTime().getTime()) / 1000) > 30) { // removeUserFromAllTablesAndChat only if table started longer than 30 seconds ago && ((System.currentTimeMillis() - table.getStartTime().getTime()) / 1000) > 30) { // removeUserFromAllTablesAndChat only if table started longer than 30 seconds ago

View file

@ -159,15 +159,15 @@ public class User {
public void setSessionId(String sessionId) { public void setSessionId(String sessionId) {
this.sessionId = sessionId; this.sessionId = sessionId;
if (sessionId.isEmpty()) { if (sessionId.isEmpty()) {
userState = UserState.Disconnected; setUserState(UserState.Disconnected);
lostConnection(); lostConnection();
logger.trace("USER - lost connection: " + userName + " id: " + userId); logger.trace("USER - lost connection: " + userName + " id: " + userId);
} else if (userState == UserState.Created) { } else if (userState == UserState.Created) {
userState = UserState.Connected; setUserState(UserState.Connected);
logger.trace("USER - created: " + userName + " id: " + userId); logger.trace("USER - created: " + userName + " id: " + userId);
} else { } else {
userState = UserState.Connected; setUserState(UserState.Connected);
reconnect(); reconnect();
logger.trace("USER - reconnected: " + userName + " id: " + userId); logger.trace("USER - reconnected: " + userName + " id: " + userId);
} }
@ -339,7 +339,7 @@ public class User {
} }
lastActivity = new Date(); lastActivity = new Date();
if (userState == UserState.Disconnected) { // this can happen if user reconnects very fast after disconnect if (userState == UserState.Disconnected) { // this can happen if user reconnects very fast after disconnect
userState = UserState.Connected; setUserState(UserState.Connected);
} }
} }

View file

@ -24,10 +24,16 @@
* The views and conclusions contained in the software and documentation are those of the * 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 * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.server.draft; package mage.server.draft;
import java.rmi.RemoteException;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import mage.game.draft.Draft; import mage.game.draft.Draft;
import mage.interfaces.callback.ClientCallback; import mage.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod; import mage.interfaces.callback.ClientCallbackMethod;
@ -39,14 +45,6 @@ import mage.view.DraftPickView;
import mage.view.DraftView; import mage.view.DraftView;
import org.apache.log4j.Logger; import org.apache.log4j.Logger;
import java.rmi.RemoteException;
import java.util.Optional;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/** /**
* @author BetaSteward_at_googlemail.com * @author BetaSteward_at_googlemail.com
*/ */
@ -89,21 +87,10 @@ public class DraftSession {
UserManager.instance UserManager.instance
.getUser(userId). .getUser(userId).
ifPresent(user -> user.fireCallback( ifPresent(user -> user.fireCallback(
new ClientCallback(ClientCallbackMethod.DRAFT_UPDATE, draft.getId(), getDraftView()))); new ClientCallback(ClientCallbackMethod.DRAFT_UPDATE, draft.getId(), getDraftView())));
} }
} }
// not used
//
public void inform(final String message) {
if (!killed) {
UserManager.instance
.getUser(userId)
.ifPresent(user -> user.fireCallback(new ClientCallback(ClientCallbackMethod.DRAFT_INFORM, draft.getId(), new DraftClientMessage(getDraftView(), message))));
}
}
public void draftOver() { public void draftOver() {
if (!killed) { if (!killed) {
UserManager.instance UserManager.instance

View file

@ -31,6 +31,9 @@ import java.io.*;
import java.util.*; import java.util.*;
import java.util.Map.Entry; import java.util.Map.Entry;
import java.util.concurrent.*; import java.util.concurrent.*;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.zip.GZIPOutputStream; import java.util.zip.GZIPOutputStream;
import mage.MageException; import mage.MageException;
import mage.abilities.Ability; import mage.abilities.Ability;
@ -80,7 +83,11 @@ public class GameController implements GameCallback {
protected static final ScheduledExecutorService timeoutIdleExecutor = ThreadExecutor.instance.getTimeoutIdleExecutor(); protected static final ScheduledExecutorService timeoutIdleExecutor = ThreadExecutor.instance.getTimeoutIdleExecutor();
private final ConcurrentHashMap<UUID, GameSessionPlayer> gameSessions = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, GameSessionPlayer> gameSessions = new ConcurrentHashMap<>();
private final ReadWriteLock gameSessionsLock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, GameSessionWatcher> watchers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, GameSessionWatcher> watchers = new ConcurrentHashMap<>();
private final ReadWriteLock gameWatchersLock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, PriorityTimer> timers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, PriorityTimer> timers = new ConcurrentHashMap<>();
private final ConcurrentHashMap<UUID, UUID> userPlayerMap; private final ConcurrentHashMap<UUID, UUID> userPlayerMap;
@ -114,7 +121,7 @@ public class GameController implements GameCallback {
public void cleanUp() { public void cleanUp() {
cancelTimeout(); cancelTimeout();
for (GameSessionPlayer gameSessionPlayer : gameSessions.values()) { for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
gameSessionPlayer.cleanUp(); gameSessionPlayer.cleanUp();
} }
ChatManager.instance.destroyChatSession(chatId); ChatManager.instance.destroyChatSession(chatId);
@ -301,7 +308,13 @@ public class GameController implements GameCallback {
String joinType; String joinType;
if (gameSession == null) { if (gameSession == null) {
gameSession = new GameSessionPlayer(game, userId, playerId); gameSession = new GameSessionPlayer(game, userId, playerId);
gameSessions.put(playerId, gameSession); final Lock w = gameSessionsLock.writeLock();
w.lock();
try {
gameSessions.put(playerId, gameSession);
} finally {
w.unlock();
}
joinType = "joined"; joinType = "joined";
} else { } else {
joinType = "rejoined"; joinType = "rejoined";
@ -314,8 +327,8 @@ public class GameController implements GameCallback {
private synchronized void startGame() { private synchronized void startGame() {
if (gameFuture == null) { if (gameFuture == null) {
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
entry.getValue().init(); gameSessionPlayer.init();
} }
GameWorker worker = new GameWorker(game, choosingPlayerId, this); GameWorker worker = new GameWorker(game, choosingPlayerId, this);
@ -413,7 +426,13 @@ public class GameController implements GameCallback {
} }
UserManager.instance.getUser(userId).ifPresent(user -> { UserManager.instance.getUser(userId).ifPresent(user -> {
GameSessionWatcher gameWatcher = new GameSessionWatcher(userId, game, false); GameSessionWatcher gameWatcher = new GameSessionWatcher(userId, game, false);
watchers.put(userId, gameWatcher); final Lock w = gameWatchersLock.writeLock();
w.lock();
try {
watchers.put(userId, gameWatcher);
} finally {
w.unlock();
}
gameWatcher.init(); gameWatcher.init();
user.addGameWatchInfo(game.getId()); user.addGameWatchInfo(game.getId());
ChatManager.instance.broadcast(chatId, user.getName(), " has started watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null); ChatManager.instance.broadcast(chatId, user.getName(), " has started watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null);
@ -422,7 +441,13 @@ public class GameController implements GameCallback {
} }
public void stopWatching(UUID userId) { public void stopWatching(UUID userId) {
watchers.remove(userId); final Lock w = gameWatchersLock.writeLock();
w.lock();
try {
watchers.remove(userId);
} finally {
w.unlock();
}
UserManager.instance.getUser(userId).ifPresent(user -> { UserManager.instance.getUser(userId).ifPresent(user -> {
ChatManager.instance.broadcast(chatId, user.getName(), " has stopped watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null); ChatManager.instance.broadcast(chatId, user.getName(), " has stopped watching", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null);
}); });
@ -673,11 +698,11 @@ public class GameController implements GameCallback {
} }
public void endGame(final String message) throws MageException { public void endGame(final String message) throws MageException {
for (final GameSessionPlayer gameSession : gameSessions.values()) { for (final GameSessionPlayer gameSession : getGameSessions()) {
gameSession.gameOver(message); gameSession.gameOver(message);
gameSession.removeGame(); gameSession.removeGame();
} }
for (final GameSessionWatcher gameWatcher : watchers.values()) { for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
gameWatcher.gameOver(message); gameWatcher.gameOver(message);
} }
TableManager.instance.endGame(tableId); TableManager.instance.endGame(tableId);
@ -722,10 +747,10 @@ public class GameController implements GameCallback {
} }
} }
} }
for (final GameSessionPlayer gameSession : gameSessions.values()) { for (final GameSessionPlayer gameSession : getGameSessions()) {
gameSession.update(); gameSession.update();
} }
for (final GameSessionWatcher gameWatcher : watchers.values()) { for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
gameWatcher.update(); gameWatcher.update();
} }
} }
@ -734,12 +759,12 @@ public class GameController implements GameCallback {
Table table = TableManager.instance.getTable(tableId); Table table = TableManager.instance.getTable(tableId);
if (table != null) { if (table != null) {
if (table.getMatch() != null) { if (table.getMatch() != null) {
for (final GameSessionPlayer gameSession : gameSessions.values()) { for (final GameSessionPlayer gameSession : getGameSessions()) {
gameSession.endGameInfo(table); gameSession.endGameInfo(table);
} }
// TODO: inform watchers about game end and who won
} }
} }
// TODO: inform watchers about game end and who won
} }
private synchronized void ask(UUID playerId, final String question, final Map<String, Serializable> options) throws MageException { private synchronized void ask(UUID playerId, final String question, final Map<String, Serializable> options) throws MageException {
@ -814,12 +839,12 @@ public class GameController implements GameCallback {
message.append(game.getStep().getType().toString()).append(" - "); message.append(game.getStep().getType().toString()).append(" - ");
} }
message.append("Waiting for ").append(game.getPlayer(playerId).getLogName()); message.append("Waiting for ").append(game.getPlayer(playerId).getLogName());
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
if (!entry.getKey().equals(playerId)) { if (!entry.getKey().equals(playerId)) {
entry.getValue().inform(message.toString()); entry.getValue().inform(message.toString());
} }
} }
for (final GameSessionWatcher watcher : watchers.values()) { for (final GameSessionWatcher watcher : getGameSessionWatchers()) {
watcher.inform(message.toString()); watcher.inform(message.toString());
} }
} }
@ -834,14 +859,13 @@ public class GameController implements GameCallback {
return; return;
} }
final String message = new StringBuilder(game.getStep().getType().toString()).append(" - Waiting for ").append(controller.getName()).toString(); final String message = new StringBuilder(game.getStep().getType().toString()).append(" - Waiting for ").append(controller.getName()).toString();
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
boolean skip = players.stream().anyMatch(playerId -> entry.getKey().equals(playerId)); boolean skip = players.stream().anyMatch(playerId -> entry.getKey().equals(playerId));
if (!skip) { if (!skip) {
entry.getValue().inform(message); entry.getValue().inform(message);
} }
} }
for (final GameSessionWatcher watcher : watchers.values()) { for (final GameSessionWatcher watcher : getGameSessionWatchers()) {
watcher.inform(message); watcher.inform(message);
} }
} }
@ -858,7 +882,7 @@ public class GameController implements GameCallback {
for (StackTraceElement e : ex.getStackTrace()) { for (StackTraceElement e : ex.getStackTrace()) {
sb.append(e.toString()).append('\n'); sb.append(e.toString()).append('\n');
} }
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) { for (final Entry<UUID, GameSessionPlayer> entry : getGameSessionsMap().entrySet()) {
entry.getValue().gameError(sb.toString()); entry.getValue().gameError(sb.toString());
} }
} }
@ -995,6 +1019,42 @@ public class GameController implements GameCallback {
void execute(UUID player); void execute(UUID player);
} }
private Map<UUID, GameSessionPlayer> getGameSessionsMap() {
Map<UUID, GameSessionPlayer> newGameSessionsMap = new HashMap<>();
final Lock r = gameSessionsLock.readLock();
r.lock();
try {
newGameSessionsMap.putAll(gameSessions);
} finally {
r.unlock();
}
return newGameSessionsMap;
}
private List<GameSessionPlayer> getGameSessions() {
List<GameSessionPlayer> newGameSessions = new ArrayList<>();
final Lock r = gameSessionsLock.readLock();
r.lock();
try {
newGameSessions.addAll(gameSessions.values());
} finally {
r.unlock();
}
return newGameSessions;
}
private List<GameSessionWatcher> getGameSessionWatchers() {
List<GameSessionWatcher> newGameSessionWatchers = new ArrayList<>();
final Lock r = gameSessionsLock.readLock();
r.lock();
try {
newGameSessionWatchers.addAll(watchers.values());
} finally {
r.unlock();
}
return newGameSessionWatchers;
}
private GameSessionPlayer getGameSession(UUID playerId) { private GameSessionPlayer getGameSession(UUID playerId) {
if (!timers.isEmpty()) { if (!timers.isEmpty()) {
Player player = game.getState().getPlayer(playerId); Player player = game.getState().getPlayer(playerId);

View file

@ -24,13 +24,17 @@
* The views and conclusions contained in the software and documentation are those of the * 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 * authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com. * or implied, of BetaSteward_at_googlemail.com.
*/ */
package mage.server.game; package mage.server.game;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional; import java.util.Optional;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import mage.cards.decks.DeckCardLists; import mage.cards.decks.DeckCardLists;
import mage.constants.ManaType; import mage.constants.ManaType;
import mage.constants.PlayerAction; import mage.constants.PlayerAction;
@ -46,10 +50,17 @@ public enum GameManager {
instance; instance;
private final ConcurrentHashMap<UUID, GameController> gameControllers = new ConcurrentHashMap<>(); private final ConcurrentHashMap<UUID, GameController> gameControllers = new ConcurrentHashMap<>();
private final ReadWriteLock gameControllersLock = new ReentrantReadWriteLock();
public UUID createGameSession(Game game, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) { public UUID createGameSession(Game game, ConcurrentHashMap<UUID, UUID> userPlayerMap, UUID tableId, UUID choosingPlayerId, GameOptions gameOptions) {
GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId, gameOptions); GameController gameController = new GameController(game, userPlayerMap, tableId, choosingPlayerId, gameOptions);
gameControllers.put(game.getId(), gameController); final Lock w = gameControllersLock.writeLock();
w.lock();
try {
gameControllers.put(game.getId(), gameController);
} finally {
w.unlock();
}
return gameController.getSessionId(); return gameController.getSessionId();
} }
@ -109,10 +120,10 @@ public enum GameManager {
gameController.quitMatch(userId); gameController.quitMatch(userId);
} }
} }
public void sendPlayerAction(PlayerAction playerAction, UUID gameId, UUID userId, Object data) { public void sendPlayerAction(PlayerAction playerAction, UUID gameId, UUID userId, Object data) {
GameController gameController = gameControllers.get(gameId); GameController gameController = gameControllers.get(gameId);
if (gameController != null) { if (gameController != null) {
gameController.sendPlayerAction(playerAction, userId, data); gameController.sendPlayerAction(playerAction, userId, data);
} }
} }
@ -151,7 +162,13 @@ public enum GameManager {
GameController gameController = gameControllers.get(gameId); GameController gameController = gameControllers.get(gameId);
if (gameController != null) { if (gameController != null) {
gameController.cleanUp(); gameController.cleanUp();
gameControllers.remove(gameId); final Lock w = gameControllersLock.writeLock();
w.lock();
try {
gameControllers.remove(gameId);
} finally {
w.unlock();
}
} }
} }
@ -174,8 +191,16 @@ public enum GameManager {
public int getNumberActiveGames() { public int getNumberActiveGames() {
return gameControllers.size(); return gameControllers.size();
} }
public ConcurrentHashMap<UUID, GameController> getGameController() { public Map<UUID, GameController> getGameController() {
return gameControllers; Map<UUID, GameController> newControllers = new HashMap<>();
final Lock r = gameControllersLock.readLock();
r.lock();
try {
newControllers.putAll(gameControllers);
} finally {
r.unlock();
}
return newControllers;
} }
} }

View file

@ -55,8 +55,7 @@ import mage.watchers.common.CardsAmountDrawnThisTurnWatcher;
public class ArchmageAscension extends CardImpl { public class ArchmageAscension extends CardImpl {
public ArchmageAscension(UUID ownerId, CardSetInfo setInfo) { public ArchmageAscension(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ENCHANTMENT},"{2}{U}"); super(ownerId, setInfo, new CardType[]{CardType.ENCHANTMENT}, "{2}{U}");
// At the beginning of each end step, if you drew two or more cards this turn, you may put a quest counter on Archmage Ascension. // At the beginning of each end step, if you drew two or more cards this turn, you may put a quest counter on Archmage Ascension.
this.addAbility(new ArchmageAscensionTriggeredAbility(), new CardsAmountDrawnThisTurnWatcher()); this.addAbility(new ArchmageAscensionTriggeredAbility(), new CardsAmountDrawnThisTurnWatcher());
@ -64,7 +63,7 @@ public class ArchmageAscension extends CardImpl {
// As long as Archmage Ascension has six or more quest counters on it, if you would draw a card, // As long as Archmage Ascension has six or more quest counters on it, if you would draw a card,
// you may instead search your library for a card, put that card into your hand, then shuffle your library. // you may instead search your library for a card, put that card into your hand, then shuffle your library.
this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArchmageAscensionReplacementEffect())); this.addAbility(new SimpleStaticAbility(Zone.BATTLEFIELD, new ArchmageAscensionReplacementEffect()));
} }
public ArchmageAscension(final ArchmageAscension card) { public ArchmageAscension(final ArchmageAscension card) {
@ -91,17 +90,17 @@ class ArchmageAscensionTriggeredAbility extends TriggeredAbilityImpl {
public ArchmageAscensionTriggeredAbility copy() { public ArchmageAscensionTriggeredAbility copy() {
return new ArchmageAscensionTriggeredAbility(this); return new ArchmageAscensionTriggeredAbility(this);
} }
@Override @Override
public boolean checkEventType(GameEvent event, Game game) { public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE; return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE;
} }
@Override @Override
public boolean checkTrigger(GameEvent event, Game game) { public boolean checkTrigger(GameEvent event, Game game) {
Permanent archmage = game.getPermanent(super.getSourceId()); Permanent archmage = game.getPermanent(super.getSourceId());
CardsAmountDrawnThisTurnWatcher watcher = CardsAmountDrawnThisTurnWatcher watcher
(CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.class.getSimpleName()); = (CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.class.getSimpleName());
return archmage != null && watcher != null && watcher.getAmountCardsDrawn(this.getControllerId()) >= 2; return archmage != null && watcher != null && watcher.getAmountCardsDrawn(this.getControllerId()) >= 2;
} }
@ -115,8 +114,8 @@ class ArchmageAscensionReplacementEffect extends ReplacementEffectImpl {
public ArchmageAscensionReplacementEffect() { public ArchmageAscensionReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit); super(Duration.WhileOnBattlefield, Outcome.Benefit);
staticText = "As long as {this} has six or more quest counters on it, if you would draw a card, " + staticText = "As long as {this} has six or more quest counters on it, if you would draw a card, "
"you may instead search your library for a card, put that card into your hand, then shuffle your library"; + "you may instead search your library for a card, put that card into your hand, then shuffle your library";
} }
public ArchmageAscensionReplacementEffect(final ArchmageAscensionReplacementEffect effect) { public ArchmageAscensionReplacementEffect(final ArchmageAscensionReplacementEffect effect) {
@ -141,19 +140,19 @@ class ArchmageAscensionReplacementEffect extends ReplacementEffectImpl {
if (player.searchLibrary(target, game)) { if (player.searchLibrary(target, game)) {
Card card = game.getCard(target.getFirstTarget()); Card card = game.getCard(target.getFirstTarget());
if (card != null) { if (card != null) {
card.moveToZone(Zone.HAND, id, game, false); card.moveToZone(Zone.HAND, source.getSourceId(), game, false);
player.shuffleLibrary(source, game); player.shuffleLibrary(source, game);
} }
} }
} }
return true; return true;
} }
@Override @Override
public boolean checksEventType(GameEvent event, Game game) { public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DRAW_CARD; return event.getType() == GameEvent.EventType.DRAW_CARD;
} }
@Override @Override
public boolean applies(GameEvent event, Ability source, Game game) { public boolean applies(GameEvent event, Ability source, Game game) {
Permanent archmage = game.getPermanent(source.getSourceId()); Permanent archmage = game.getPermanent(source.getSourceId());

View file

@ -82,7 +82,7 @@ public class CurseOfTheCabal extends CardImpl {
} }
} }
class CurseOfTheCabalSacrificeEffect extends OneShotEffect{ class CurseOfTheCabalSacrificeEffect extends OneShotEffect {
private static final FilterControlledPermanent FILTER = new FilterControlledPermanent(); // ggf filter.FilterPermanent private static final FilterControlledPermanent FILTER = new FilterControlledPermanent(); // ggf filter.FilterPermanent
@ -103,10 +103,11 @@ class CurseOfTheCabalSacrificeEffect extends OneShotEffect{
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player targetPlayer = game.getPlayer(source.getFirstTarget()); Player targetPlayer = game.getPlayer(source.getFirstTarget());
if(targetPlayer != null) { if (targetPlayer != null) {
int amount = game.getBattlefield().countAll(FILTER, targetPlayer.getId(), game) / 2; int amount = game.getBattlefield().countAll(FILTER, targetPlayer.getId(), game) / 2;
if(amount < 1) if (amount < 1) {
return true; return true;
}
Target target = new TargetControlledPermanent(amount, amount, FILTER, true); Target target = new TargetControlledPermanent(amount, amount, FILTER, true);
if (target.canChoose(targetPlayer.getId(), game)) { if (target.canChoose(targetPlayer.getId(), game)) {
while (!target.isChosen() && target.canChoose(targetPlayer.getId(), game) && targetPlayer.canRespond()) { while (!target.isChosen() && target.canChoose(targetPlayer.getId(), game) && targetPlayer.canRespond()) {
@ -129,9 +130,9 @@ class CurseOfTheCabalTriggeredAbility extends ConditionalTriggeredAbility {
public CurseOfTheCabalTriggeredAbility() { public CurseOfTheCabalTriggeredAbility() {
super(new BeginningOfUpkeepTriggeredAbility( super(new BeginningOfUpkeepTriggeredAbility(
Zone.EXILED, new CurseOfTheCabalTriggeredAbilityConditionalDelay(), Zone.EXILED, new CurseOfTheCabalTriggeredAbilityConditionalDelay(),
TargetController.ANY, false, true TargetController.ANY, false, true
), ),
SuspendedCondition.instance, SuspendedCondition.instance,
"At the beginning of each player's upkeep, if {this} is suspended, that player may sacrifice a permanent. If he or she does, put two time counters on {this}." "At the beginning of each player's upkeep, if {this} is suspended, that player may sacrifice a permanent. If he or she does, put two time counters on {this}."
); );
@ -149,21 +150,22 @@ class CurseOfTheCabalTriggeredAbility extends ConditionalTriggeredAbility {
} }
} }
class CurseOfTheCabalTriggeredAbilityConditionalDelay extends AddCountersSourceEffect{ class CurseOfTheCabalTriggeredAbilityConditionalDelay extends AddCountersSourceEffect {
public CurseOfTheCabalTriggeredAbilityConditionalDelay(){ public CurseOfTheCabalTriggeredAbilityConditionalDelay() {
super(CounterType.TIME.createInstance(), new StaticValue(2), false, true); super(CounterType.TIME.createInstance(), new StaticValue(2), false, true);
} }
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
UUID id = game.getActivePlayerId(); UUID activePlayerId = game.getActivePlayerId();
Player target = game.getPlayer(id); Player target = game.getPlayer(activePlayerId);
Cost cost = new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledPermanent())); Cost cost = new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledPermanent()));
if(target == null) if (target == null) {
return false; return false;
if (cost.canPay(source, source.getSourceId(), id, game) }
if (cost.canPay(source, source.getSourceId(), activePlayerId, game)
&& target.chooseUse(Outcome.Sacrifice, "Sacrifice a permanent to delay Curse of the Cabal?", source, game) && target.chooseUse(Outcome.Sacrifice, "Sacrifice a permanent to delay Curse of the Cabal?", source, game)
&& cost.pay(source, game, source.getSourceId(), id, true, null)) { && cost.pay(source, game, source.getSourceId(), activePlayerId, true, null)) {
return super.apply(game, source); return super.apply(game, source);
} }
return true; return true;

View file

@ -50,7 +50,7 @@ import mage.target.common.TargetCreaturePermanent;
public class DeadReckoning extends CardImpl { public class DeadReckoning extends CardImpl {
public DeadReckoning(UUID ownerId, CardSetInfo setInfo) { public DeadReckoning(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{B}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{B}{B}");
// You may put target creature card from your graveyard on top of your library. If you do, Dead Reckoning deals damage equal to that card's power to target creature. // You may put target creature card from your graveyard on top of your library. If you do, Dead Reckoning deals damage equal to that card's power to target creature.
this.getSpellAbility().addEffect(new DeadReckoningEffect()); this.getSpellAbility().addEffect(new DeadReckoningEffect());
@ -96,7 +96,7 @@ class DeadReckoningEffect extends OneShotEffect {
&& you.choose(Outcome.Damage, target2, source.getSourceId(), game)) { && you.choose(Outcome.Damage, target2, source.getSourceId(), game)) {
Card creatureInGraveyard = game.getCard(target1.getFirstTarget()); Card creatureInGraveyard = game.getCard(target1.getFirstTarget());
if (creatureInGraveyard != null) { if (creatureInGraveyard != null) {
if (creatureInGraveyard.moveToZone(Zone.LIBRARY, id, game, true)) { if (creatureInGraveyard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, true)) {
int power = creatureInGraveyard.getPower().getValue(); int power = creatureInGraveyard.getPower().getValue();
Permanent creature = game.getPermanent(target2.getFirstTarget()); Permanent creature = game.getPermanent(target2.getFirstTarget());
if (creature != null) { if (creature != null) {

View file

@ -1,4 +1,4 @@
/* /*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved. * Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without modification, are * Redistribution and use in source and binary forms, with or without modification, are
@ -27,7 +27,7 @@
*/ */
package mage.cards.d; package mage.cards.d;
import java.util.UUID; import java.util.UUID;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.common.CycleTriggeredAbility; import mage.abilities.common.CycleTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl; import mage.abilities.costs.mana.ManaCostsImpl;
@ -54,14 +54,14 @@ import mage.players.Player;
public class DecreeOfAnnihilation extends CardImpl { public class DecreeOfAnnihilation extends CardImpl {
public DecreeOfAnnihilation(UUID ownerId, CardSetInfo setInfo) { public DecreeOfAnnihilation(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{8}{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{8}{R}{R}");
// Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands. // Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands.
this.getSpellAbility().addEffect(new DecreeOfAnnihilationEffect()); this.getSpellAbility().addEffect(new DecreeOfAnnihilationEffect());
// Cycling {5}{R}{R} // Cycling {5}{R}{R}
this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{R}{R}"))); this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{R}{R}")));
// When you cycle Decree of Annihilation, destroy all lands. // When you cycle Decree of Annihilation, destroy all lands.
Ability ability = new CycleTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_LANDS), false); Ability ability = new CycleTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_LANDS), false);
this.addAbility(ability); this.addAbility(ability);
@ -78,16 +78,16 @@ public class DecreeOfAnnihilation extends CardImpl {
} }
class DecreeOfAnnihilationEffect extends OneShotEffect { class DecreeOfAnnihilationEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent(""); private static final FilterPermanent filter = new FilterPermanent("");
static { static {
filter.add(Predicates.or( filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT), new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.CREATURE), new CardTypePredicate(CardType.CREATURE),
new CardTypePredicate(CardType.LAND))); new CardTypePredicate(CardType.LAND)));
} }
public DecreeOfAnnihilationEffect() { public DecreeOfAnnihilationEffect() {
super(Outcome.Detriment); super(Outcome.Detriment);
staticText = "Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands"; staticText = "Exile all artifacts, creatures, and lands from the battlefield, all cards from all graveyards, and all cards from all hands";
@ -105,7 +105,7 @@ class DecreeOfAnnihilationEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
permanent.moveToExile(id, "all artifacts, creatures, and land", id, game); permanent.moveToExile(null, "", source.getSourceId(), game);
} }
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player = game.getPlayer(playerId); Player player = game.getPlayer(playerId);

View file

@ -151,19 +151,19 @@ class ElsewhereFlaskContinuousEffect extends ContinuousEffectImpl {
if (sublayer == SubLayer.NA) { if (sublayer == SubLayer.NA) {
land.getAbilities().clear(); land.getAbilities().clear();
if (choice.equals("Forest")) { if (choice.equals("Forest")) {
land.addAbility(new GreenManaAbility(), id, game); land.addAbility(new GreenManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Plains")) { if (choice.equals("Plains")) {
land.addAbility(new WhiteManaAbility(), id, game); land.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Mountain")) { if (choice.equals("Mountain")) {
land.addAbility(new RedManaAbility(), id, game); land.addAbility(new RedManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Island")) { if (choice.equals("Island")) {
land.addAbility(new BlueManaAbility(), id, game); land.addAbility(new BlueManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Swamp")) { if (choice.equals("Swamp")) {
land.addAbility(new BlackManaAbility(), id, game); land.addAbility(new BlackManaAbility(), source.getSourceId(), game);
} }
} }
break; break;

View file

@ -28,6 +28,7 @@
package mage.cards.e; package mage.cards.e;
import java.util.UUID; import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.cards.Card; import mage.cards.Card;
@ -50,8 +51,7 @@ import mage.target.common.TargetCreatureOrPlayer;
public class ExplosiveRevelation extends CardImpl { public class ExplosiveRevelation extends CardImpl {
public ExplosiveRevelation(UUID ownerId, CardSetInfo setInfo) { public ExplosiveRevelation(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{3}{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{3}{R}{R}");
// Choose target creature or player. Reveal cards from the top of your library until you reveal a nonland card. Explosive Revelation deals damage equal to that card's converted mana cost to that creature or player. Put the nonland card into your hand and the rest on the bottom of your library in any order. // Choose target creature or player. Reveal cards from the top of your library until you reveal a nonland card. Explosive Revelation deals damage equal to that card's converted mana cost to that creature or player. Put the nonland card into your hand and the rest on the bottom of your library in any order.
this.getSpellAbility().addEffect(new ExplosiveRevelationEffect()); this.getSpellAbility().addEffect(new ExplosiveRevelationEffect());
@ -86,42 +86,46 @@ class ExplosiveRevelationEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId()); Player controller = game.getPlayer(source.getControllerId());
if (player != null && player.getLibrary().hasCards()) { MageObject sourceObject = source.getSourceObject(game);
CardsImpl cards = new CardsImpl(); if (controller != null && sourceObject != null) {
Library library = player.getLibrary(); if (controller.getLibrary().hasCards()) {
Card card = null;
do { CardsImpl cards = new CardsImpl();
card = library.removeFromTop(game); Library library = controller.getLibrary();
if (card != null) { Card card = null;
cards.add(card); do {
card = library.removeFromTop(game);
if (card != null) {
cards.add(card);
}
} while (library.hasCards() && card != null && card.isLand());
// reveal cards
if (!cards.isEmpty()) {
controller.revealCards(sourceObject.getIdName(), cards, game);
} }
} while (library.hasCards() && card != null && card.isLand()); // the nonland card
// reveal cards int damage = card.getConvertedManaCost();
if (!cards.isEmpty()) { // assign damage to target
player.revealCards("Explosive Revelation", cards, game); for (UUID targetId : targetPointer.getTargets(game, source)) {
} Permanent targetedCreature = game.getPermanent(targetId);
// the nonland card if (targetedCreature != null) {
int damage = card.getConvertedManaCost(); targetedCreature.damage(damage, source.getSourceId(), game, false, true);
// assign damage to target } else {
for (UUID targetId: targetPointer.getTargets(game, source)) { Player targetedPlayer = game.getPlayer(targetId);
Permanent targetedCreature = game.getPermanent(targetId); if (targetedPlayer != null) {
if (targetedCreature != null) { targetedPlayer.damage(damage, source.getSourceId(), game, false, true);
targetedCreature.damage(damage, source.getSourceId(), game, false, true); }
}
else {
Player targetedPlayer = game.getPlayer(targetId);
if (targetedPlayer != null) {
targetedPlayer.damage(damage, source.getSourceId(), game, false, true);
} }
} }
// move nonland card to hand
card.moveToZone(Zone.HAND, source.getSourceId(), game, true);
// remove nonland card from revealed card list
cards.remove(card);
// put the rest of the cards on the bottom of the library in any order
return controller.putCardsOnBottomOfLibrary(cards, game, source, true);
} }
// move nonland card to hand return true;
card.moveToZone(Zone.HAND, id, game, true);
// remove nonland card from revealed card list
cards.remove(card);
// put the rest of the cards on the bottom of the library in any order
return player.putCardsOnBottomOfLibrary(cards, game, source, true);
} }
return false; return false;
} }

View file

@ -28,6 +28,7 @@
package mage.cards.i; package mage.cards.i;
import java.util.UUID; import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.StormAbility; import mage.abilities.keyword.StormAbility;
@ -49,8 +50,7 @@ import mage.target.TargetPlayer;
public class IgniteMemories extends CardImpl { public class IgniteMemories extends CardImpl {
public IgniteMemories(UUID ownerId, CardSetInfo setInfo) { public IgniteMemories(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{R}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{R}");
// Target player reveals a card at random from his or her hand. Ignite Memories deals damage to that player equal to that card's converted mana cost. // Target player reveals a card at random from his or her hand. Ignite Memories deals damage to that player equal to that card's converted mana cost.
this.getSpellAbility().addTarget(new TargetPlayer()); this.getSpellAbility().addTarget(new TargetPlayer());
@ -69,7 +69,6 @@ public class IgniteMemories extends CardImpl {
} }
} }
class IgniteMemoriesEffect extends OneShotEffect { class IgniteMemoriesEffect extends OneShotEffect {
public IgniteMemoriesEffect() { public IgniteMemoriesEffect() {
@ -83,13 +82,17 @@ class IgniteMemoriesEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source)); Player controller = game.getPlayer(targetPointer.getFirst(game, source));
if (player != null && !player.getHand().isEmpty()) { MageObject sourceObject = source.getSourceObject(game);
Cards revealed = new CardsImpl(); if (controller != null && sourceObject != null) {
Card card = player.getHand().getRandom(game); if (!controller.getHand().isEmpty()) {
revealed.add(card); Cards revealed = new CardsImpl();
player.revealCards("Ignite Memories", revealed, game); Card card = controller.getHand().getRandom(game);
player.damage(card.getConvertedManaCost(), id, game, false, true); revealed.add(card);
controller.revealCards(sourceObject.getIdName(), revealed, game);
controller.damage(card.getConvertedManaCost(), source.getSourceId(), game, false, true);
}
return true; return true;
} }
return false; return false;
@ -100,4 +103,4 @@ class IgniteMemoriesEffect extends OneShotEffect {
return new IgniteMemoriesEffect(this); return new IgniteMemoriesEffect(this);
} }
} }

View file

@ -96,7 +96,7 @@ class MeglonothEffect extends OneShotEffect {
Permanent meglonoth = game.getPermanent(source.getSourceId()); Permanent meglonoth = game.getPermanent(source.getSourceId());
Permanent blocked = game.getPermanent(targetPointer.getFirst(game, source)); Permanent blocked = game.getPermanent(targetPointer.getFirst(game, source));
if (blocked != null && meglonoth != null) { if (blocked != null && meglonoth != null) {
game.getPlayer(blocked.getControllerId()).damage(meglonoth.getPower().getValue(), id, game, false, true); game.getPlayer(blocked.getControllerId()).damage(meglonoth.getPower().getValue(), source.getSourceId(), game, false, true);
return true; return true;
} }
return false; return false;

View file

@ -49,8 +49,7 @@ import mage.target.common.TargetOpponent;
public class PerishTheThought extends CardImpl { public class PerishTheThought extends CardImpl {
public PerishTheThought(UUID ownerId, CardSetInfo setInfo) { public PerishTheThought(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{2}{B}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{2}{B}");
// Target opponent reveals his or her hand. You choose a card from it. That player shuffles that card into his or her library. // Target opponent reveals his or her hand. You choose a card from it. That player shuffles that card into his or her library.
this.getSpellAbility().addEffect(new PerishTheThoughtEffect()); this.getSpellAbility().addEffect(new PerishTheThoughtEffect());
@ -68,9 +67,9 @@ public class PerishTheThought extends CardImpl {
} }
class PerishTheThoughtEffect extends OneShotEffect { class PerishTheThoughtEffect extends OneShotEffect {
private static final FilterCard filter = new FilterCard("card in target opponent's hand"); private static final FilterCard filter = new FilterCard("card in target opponent's hand");
public PerishTheThoughtEffect() { public PerishTheThoughtEffect() {
super(Outcome.Neutral); super(Outcome.Neutral);
this.staticText = "Target opponent reveals his or her hand. You choose a card from it. That player shuffles that card into his or her library"; this.staticText = "Target opponent reveals his or her hand. You choose a card from it. That player shuffles that card into his or her library";
@ -99,7 +98,7 @@ class PerishTheThoughtEffect extends OneShotEffect {
Card chosenCard = targetOpponent.getHand().get(target.getFirstTarget(), game); Card chosenCard = targetOpponent.getHand().get(target.getFirstTarget(), game);
if (chosenCard != null) { if (chosenCard != null) {
if (targetOpponent != null) { if (targetOpponent != null) {
chosenCard.moveToZone(Zone.LIBRARY, id, game, false); chosenCard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
targetOpponent.shuffleLibrary(source, game); targetOpponent.shuffleLibrary(source, game);
} }
} }

View file

@ -52,7 +52,7 @@ import mage.target.common.TargetCreatureOrPlayer;
public class RazorBoomerang extends CardImpl { public class RazorBoomerang extends CardImpl {
public RazorBoomerang(UUID ownerId, CardSetInfo setInfo) { public RazorBoomerang(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
this.subtype.add("Equipment"); this.subtype.add("Equipment");
// Equipped creature has "{tap}, Unattach Razor Boomerang: Razor Boomerang deals 1 damage to target creature or player. Return Razor Boomerang to its owner's hand." // Equipped creature has "{tap}, Unattach Razor Boomerang: Razor Boomerang deals 1 damage to target creature or player. Return Razor Boomerang to its owner's hand."
@ -105,7 +105,7 @@ class RazorBoomerangEffect extends OneShotEffect {
} }
Permanent razor = game.getPermanent(attachmentid); Permanent razor = game.getPermanent(attachmentid);
if (razor != null) { if (razor != null) {
razor.moveToZone(Zone.HAND, id, game, true); razor.moveToZone(Zone.HAND, source.getSourceId(), game, true);
} }
return true; return true;
} }

View file

@ -64,13 +64,14 @@ import mage.util.CardUtil;
public class ShellOfTheLastKappa extends CardImpl { public class ShellOfTheLastKappa extends CardImpl {
private static final FilterSpell filter = new FilterSpell("instant or sorcery spell that targets you"); private static final FilterSpell filter = new FilterSpell("instant or sorcery spell that targets you");
static { static {
filter.add(new TargetYouPredicate()); filter.add(new TargetYouPredicate());
filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), new CardTypePredicate(CardType.SORCERY))); filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), new CardTypePredicate(CardType.SORCERY)));
} }
public ShellOfTheLastKappa(UUID ownerId, CardSetInfo setInfo) { public ShellOfTheLastKappa(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.ARTIFACT},"{3}"); super(ownerId, setInfo, new CardType[]{CardType.ARTIFACT}, "{3}");
addSuperType(SuperType.LEGENDARY); addSuperType(SuperType.LEGENDARY);
// {3}, {tap}: Exile target instant or sorcery spell that targets you. // {3}, {tap}: Exile target instant or sorcery spell that targets you.
@ -117,14 +118,14 @@ class ShellOfTheLastKappaEffect extends OneShotEffect {
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source)); Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) { if (spell != null) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId()); Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent == null) { if (sourcePermanent == null) {
sourcePermanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD); sourcePermanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
} }
if (sourcePermanent != null) { if (sourcePermanent != null) {
game.getStack().counter(spell.getId(), source.getSourceId(), game); game.getStack().counter(spell.getId(), source.getSourceId(), game);
Card card = spell.getCard(); Card card = spell.getCard();
card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourcePermanent.getName(), id, game); card.moveToExile(CardUtil.getCardExileZoneId(game, source), sourcePermanent.getName(), source.getSourceId(), game);
} }
} }
return false; return false;
@ -164,7 +165,6 @@ class ShellOfTheLastKappaCastEffect extends OneShotEffect {
} }
} }
class TargetYouPredicate implements ObjectPlayerPredicate<ObjectPlayer<StackObject>> { class TargetYouPredicate implements ObjectPlayerPredicate<ObjectPlayer<StackObject>> {
@Override @Override

View file

@ -49,10 +49,9 @@ import mage.players.Player;
public class SuddenDemise extends CardImpl { public class SuddenDemise extends CardImpl {
public SuddenDemise(UUID ownerId, CardSetInfo setInfo) { public SuddenDemise(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{X}{R}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{X}{R}");
// Choose a color. Sudden Demise deals X damage to each creature of the chosen color.
// Choose a color. Sudden Demise deals X damage to each creature of the chosen color.
this.getSpellAbility().addEffect(new SuddenDemiseDamageEffect()); this.getSpellAbility().addEffect(new SuddenDemiseDamageEffect());
} }
@ -71,7 +70,7 @@ class SuddenDemiseDamageEffect extends OneShotEffect {
public SuddenDemiseDamageEffect() { public SuddenDemiseDamageEffect() {
super(Outcome.Damage); super(Outcome.Damage);
this.staticText = "Choose a color. Sudden Demise deals X damage to each creature of the chosen color"; this.staticText = "Choose a color. {this} deals X damage to each creature of the chosen color";
} }
public SuddenDemiseDamageEffect(final SuddenDemiseDamageEffect effect) { public SuddenDemiseDamageEffect(final SuddenDemiseDamageEffect effect) {
@ -93,7 +92,7 @@ class SuddenDemiseDamageEffect extends OneShotEffect {
final int damage = source.getManaCostsToPay().getX(); final int damage = source.getManaCostsToPay().getX();
FilterPermanent filter = new FilterCreaturePermanent(); FilterPermanent filter = new FilterCreaturePermanent();
filter.add(new ColorPredicate(choice.getColor())); filter.add(new ColorPredicate(choice.getColor()));
for (Permanent permanent:game.getBattlefield().getActivePermanents(filter, source.getControllerId(), id, game)) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
permanent.damage(damage, source.getSourceId(), game, false, true); permanent.damage(damage, source.getSourceId(), game, false, true);
} }
return true; return true;

View file

@ -160,19 +160,19 @@ class TerraformerContinuousEffect extends ContinuousEffectImpl {
if (sublayer == SubLayer.NA) { if (sublayer == SubLayer.NA) {
land.getAbilities().clear(); land.getAbilities().clear();
if (choice.equals("Forest")) { if (choice.equals("Forest")) {
land.addAbility(new GreenManaAbility(), id, game); land.addAbility(new GreenManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Plains")) { if (choice.equals("Plains")) {
land.addAbility(new WhiteManaAbility(), id, game); land.addAbility(new WhiteManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Mountain")) { if (choice.equals("Mountain")) {
land.addAbility(new RedManaAbility(), id, game); land.addAbility(new RedManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Island")) { if (choice.equals("Island")) {
land.addAbility(new BlueManaAbility(), id, game); land.addAbility(new BlueManaAbility(), source.getSourceId(), game);
} }
if (choice.equals("Swamp")) { if (choice.equals("Swamp")) {
land.addAbility(new BlackManaAbility(), id, game); land.addAbility(new BlackManaAbility(), source.getSourceId(), game);
} }
} }
break; break;

View file

@ -47,8 +47,7 @@ import mage.players.Player;
public class Worldfire extends CardImpl { public class Worldfire extends CardImpl {
public Worldfire(UUID ownerId, CardSetInfo setInfo) { public Worldfire(UUID ownerId, CardSetInfo setInfo) {
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{6}{R}{R}{R}"); super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{6}{R}{R}{R}");
// Exile all permanents. Exile all cards from all hands and graveyards. Each player's life total becomes 1. // Exile all permanents. Exile all cards from all hands and graveyards. Each player's life total becomes 1.
this.getSpellAbility().addEffect(new WorldfireEffect()); this.getSpellAbility().addEffect(new WorldfireEffect());
@ -65,9 +64,9 @@ public class Worldfire extends CardImpl {
} }
class WorldfireEffect extends OneShotEffect { class WorldfireEffect extends OneShotEffect {
private static FilterPermanent filter = new FilterPermanent(); private static FilterPermanent filter = new FilterPermanent();
public WorldfireEffect() { public WorldfireEffect() {
super(Outcome.Detriment); super(Outcome.Detriment);
staticText = "Exile all permanents. Exile all cards from all hands and graveyards. Each player's life total becomes 1"; staticText = "Exile all permanents. Exile all cards from all hands and graveyards. Each player's life total becomes 1";
@ -85,7 +84,7 @@ class WorldfireEffect extends OneShotEffect {
@Override @Override
public boolean apply(Game game, Ability source) { public boolean apply(Game game, Ability source) {
for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) { for (Permanent permanent : game.getBattlefield().getActivePermanents(filter, source.getControllerId(), source.getSourceId(), game)) {
permanent.moveToExile(id, "all permanents", id, game); permanent.moveToExile(null, "", source.getSourceId(), game);
} }
for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) { for (UUID playerId : game.getState().getPlayersInRange(source.getControllerId(), game)) {
Player player = game.getPlayer(playerId); Player player = game.getPlayer(playerId);

View file

@ -81,9 +81,9 @@ public class MageObjectReference implements Comparable<MageObjectReference>, Ser
if (game.getPlayerList().contains(sourceId)) { if (game.getPlayerList().contains(sourceId)) {
this.zoneChangeCounter = 0; this.zoneChangeCounter = 0;
} else { } else {
logger.error("The provided sourceId is not connected to an object in the game id:" + sourceId); logger.error("The provided sourceId is not connected to an object in the game id: " + sourceId);
for (StackObject stackObject : game.getStack()) { for (StackObject stackObject : game.getStack()) {
logger.error("StackObject: " + stackObject.getId() + " sourceId" + stackObject.getSourceId() + " name" + stackObject.getName()); logger.error("StackObject: " + stackObject.getId() + " sourceId " + stackObject.getSourceId() + " name " + stackObject.getName());
} }
mageObject = game.getLastKnownInformation(sourceId, Zone.STACK); mageObject = game.getLastKnownInformation(sourceId, Zone.STACK);
if (mageObject != null) { if (mageObject != null) {

View file

@ -27,6 +27,8 @@
*/ */
package mage.abilities.effects.common; package mage.abilities.effects.common;
import java.util.LinkedHashSet;
import java.util.stream.Collectors;
import mage.MageObject; import mage.MageObject;
import mage.abilities.Ability; import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect; import mage.abilities.effects.OneShotEffect;
@ -39,9 +41,6 @@ import mage.game.permanent.Permanent;
import mage.players.Player; import mage.players.Player;
import mage.util.CardUtil; import mage.util.CardUtil;
import java.util.LinkedHashSet;
import java.util.stream.Collectors;
/** /**
* @author LevelX2 * @author LevelX2
*/ */
@ -72,6 +71,9 @@ public class ChooseCreatureTypeEffect extends OneShotEffect {
return false; return false;
} }
} }
if (typeChoice.getChoice() == null) {
return false;
}
if (!game.isSimulation()) { if (!game.isSimulation()) {
game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice()); game.informPlayers(mageObject.getName() + ": " + controller.getLogName() + " has chosen " + typeChoice.getChoice());
} }

View file

@ -1,61 +0,0 @@
/*
* Copyright 2011 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
* permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice, this list of
* conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright notice, this list
* of conditions and the following disclaimer in the documentation and/or other materials
* provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY BetaSteward_at_googlemail.com ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
* FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL BetaSteward_at_googlemail.com OR
* 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.decks;
import java.util.Map;
import java.util.Map.Entry;
import mage.game.GameException;
/**
*
* @author BetaSteward_at_googlemail.com
*/
public class InvalidDeckException extends GameException {
Map<String, String> invalid;
public InvalidDeckException (String message, Map<String, String> invalid) {
super(message);
this.invalid = invalid;
}
public Map<String, String> getInvalid() {
return invalid;
}
@Override
public String getMessage() {
StringBuilder sb = new StringBuilder();
sb.append(super.getMessage()).append('\n');
for (Entry<String, String> entry: invalid.entrySet()) {
sb.append(entry.getKey()).append(' ').append(entry.getValue()).append('\n');
}
return sb.toString();
}
}

View file

@ -2959,7 +2959,7 @@ public abstract class PlayerImpl implements Player, Serializable {
} }
public UserData getControllingPlayersUserData(Game game) { public UserData getControllingPlayersUserData(Game game) {
if (isGameUnderControl()) { if (!isGameUnderControl()) {
Player player = game.getPlayer(getTurnControlledBy()); Player player = game.getPlayer(getTurnControlledBy());
if (player.isHuman()) { if (player.isHuman()) {
return player.getUserData(); return player.getUserData();
@ -2969,8 +2969,7 @@ public abstract class PlayerImpl implements Player, Serializable {
} }
@Override @Override
public void setUserData(UserData userData public void setUserData(UserData userData) {
) {
this.userData = userData; this.userData = userData;
getManaPool().setAutoPayment(userData.isManaPoolAutomatic()); getManaPool().setAutoPayment(userData.isManaPoolAutomatic());
getManaPool().setAutoPaymentRestricted(userData.isManaPoolAutomaticRestricted()); getManaPool().setAutoPaymentRestricted(userData.isManaPoolAutomaticRestricted());