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;
import java.awt.event.KeyEvent;
import java.util.List;
import java.util.UUID;
import javax.swing.*;
import mage.cards.decks.Deck;
import mage.client.MageFrame;
import mage.client.SessionHandler;
@ -48,11 +52,6 @@ import mage.view.*;
import mage.view.ChatMessage.MessageType;
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
@ -325,11 +324,11 @@ public class CallbackClientImpl implements CallbackClient {
break;
}
case VIEW_LIMITED_DECK: {
TableClientMessage message = (TableClientMessage) callback.getData();
DeckView deckView = message.getDeck();
Deck deck = DeckUtil.construct(deckView);
viewLimitedDeck(deck, message.getTableId(), message.getTime());
break;
TableClientMessage message = (TableClientMessage) callback.getData();
DeckView deckView = message.getDeck();
Deck deck = DeckUtil.construct(deckView);
viewLimitedDeck(deck, message.getTableId(), message.getTime());
break;
}
case CONSTRUCT: {
TableClientMessage message = (TableClientMessage) callback.getData();
@ -356,14 +355,6 @@ public class CallbackClientImpl implements CallbackClient {
}
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: {
DraftClientMessage message = (DraftClientMessage) callback.getData();
DraftPanel panel = MageFrame.getDraft(callback.getObjectId());

View file

@ -30,18 +30,27 @@ public enum ClientCallbackMethod {
GAME_UPDATE("gameUpdate"),
DRAFT_OVER("draftOver"),
REPLAY_DONE("replayDone"),
USER_REQUEST_DIALOG("userRequestDialog"),
USER_REQUEST_DIALOG("userRequestDialog"),
REPLAY_UPDATE("replayUpdate"),
REPLAY_INIT("replayInit"),
END_GAME_INFO("endGameInfo"),
GAME_TARGET("gameTarget"),
GAME_CHOOSE_ABILITY("gameChooseAbility"),
GAME_CHOOSE_ABILITY("gameChooseAbility"),
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;
ClientCallbackMethod(String value){
ClientCallbackMethod(String value) {
this.value = value;
}
}

View file

@ -35,7 +35,6 @@ import java.util.concurrent.TimeUnit;
import javax.swing.*;
import mage.MageException;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.InvalidDeckException;
import mage.cards.repository.CardInfo;
import mage.cards.repository.CardRepository;
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);
}
} catch (InvalidDeckException iex) {
handleInvalidDeckException(iex);
} catch (GameException ex) {
handleGameException(ex);
} catch (MageException ex) {
@ -1547,11 +1544,6 @@ public class SessionImpl implements Session {
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) {
logger.warn(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());
}
} catch (NullPointerException ex) {
String isNull = controllingPlayer.getUserData() == null ? "null" : "not null";
logger.error("null pointer exception UserData = " + isNull);
if (controllingPlayer.getUserData() != null) {
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
@ -1223,7 +1236,9 @@ public class HumanPlayer extends PlayerImpl {
FilterCreatureForCombatBlock filter = filterCreatureForCombatBlock.copy();
filter.add(new ControllerIdPredicate(defendingPlayerId));
if (game.getBattlefield().count(filter, null, playerId, game) == 0
&& !getControllingPlayersUserData(game).getUserSkipPrioritySteps().isStopOnDeclareBlockerIfNoneAvailable()) {
&& !getControllingPlayersUserData(game)
.getUserSkipPrioritySteps()
.isStopOnDeclareBlockerIfNoneAvailable()) {
return;
}
while (!abort) {

View file

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

View file

@ -38,7 +38,6 @@ import java.util.concurrent.TimeUnit;
import mage.MageException;
import mage.cards.decks.Deck;
import mage.cards.decks.DeckCardLists;
import mage.cards.decks.InvalidDeckException;
import mage.constants.RangeOfInfluence;
import mage.constants.TableState;
import mage.game.*;
@ -414,7 +413,17 @@ public class TableController {
}
}
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);
return true;

View file

@ -35,6 +35,9 @@ import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
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.cards.decks.DeckCardLists;
import mage.constants.TableState;
@ -66,7 +69,10 @@ public enum TableManager {
private static final DateFormat formatter = new SimpleDateFormat("HH:mm:ss");
private final ConcurrentHashMap<UUID, TableController> controllers = new ConcurrentHashMap<>();
private final ReadWriteLock controllersLock = new ReentrantReadWriteLock();
private final ConcurrentHashMap<UUID, Table> tables = new ConcurrentHashMap<>();
private final ReadWriteLock tablesLock = new ReentrantReadWriteLock();
/**
* 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) {
TableController tableController = new TableController(roomId, userId, options);
controllers.put(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable());
putControllers(tableController.getTable().getId(), tableController);
putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable();
}
public Table createTable(UUID roomId, MatchOptions options) {
TableController tableController = new TableController(roomId, null, options);
controllers.put(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable());
putControllers(tableController.getTable().getId(), tableController);
putTables(tableController.getTable().getId(), tableController.getTable());
return tableController.getTable();
}
public Table createTournamentTable(UUID roomId, UUID userId, TournamentOptions options) {
TableController tableController = new TableController(roomId, userId, options);
controllers.put(tableController.getTable().getId(), tableController);
tables.put(tableController.getTable().getId(), tableController.getTable());
putControllers(tableController.getTable().getId(), tableController);
putTables(tableController.getTable().getId(), 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) {
return tables.get(tableId);
}
@ -119,7 +145,27 @@ public enum TableManager {
}
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) {
@ -164,7 +210,7 @@ public enum TableManager {
// removeUserFromAllTablesAndChat user from all tournament sub tables
public void userQuitTournamentSubTables(UUID userId) {
for (TableController controller : controllers.values()) {
for (TableController controller : getControllers()) {
if (controller.getTable() != null) {
if (controller.getTable().isTournamentSubTable()) {
controller.leaveTable(userId);
@ -177,7 +223,7 @@ public enum TableManager {
// removeUserFromAllTablesAndChat user from all sub tables of a tournament
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.hasPlayer(userId)) {
controller.leaveTable(userId);
@ -268,12 +314,6 @@ public enum TableManager {
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) {
if (controllers.containsKey(tableId)) {
if (controllers.get(tableId).endGameAndStartNextGame()) {
@ -321,11 +361,24 @@ public enum TableManager {
public void removeTable(UUID tableId) {
TableController tableController = controllers.get(tableId);
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
Table table = tables.get(tableId);
tables.remove(tableId);
w = tablesLock.writeLock();
w.lock();
try {
tables.remove(tableId);
} finally {
w.unlock();
}
Match match = table.getMatch();
Game game = null;
if (match != null) {
@ -383,8 +436,7 @@ public enum TableManager {
debugServerState();
}
logger.debug("TABLE HEALTH CHECK");
List<Table> tableCopy = new ArrayList<>(tables.values());
for (Table table : tableCopy) {
for (Table table : getTables()) {
try {
if (table.getState() != TableState.FINISHED
&& ((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) {
this.sessionId = sessionId;
if (sessionId.isEmpty()) {
userState = UserState.Disconnected;
setUserState(UserState.Disconnected);
lostConnection();
logger.trace("USER - lost connection: " + userName + " id: " + userId);
} else if (userState == UserState.Created) {
userState = UserState.Connected;
setUserState(UserState.Connected);
logger.trace("USER - created: " + userName + " id: " + userId);
} else {
userState = UserState.Connected;
setUserState(UserState.Connected);
reconnect();
logger.trace("USER - reconnected: " + userName + " id: " + userId);
}
@ -339,7 +339,7 @@ public class User {
}
lastActivity = new Date();
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
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
*/
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.interfaces.callback.ClientCallback;
import mage.interfaces.callback.ClientCallbackMethod;
@ -39,14 +45,6 @@ import mage.view.DraftPickView;
import mage.view.DraftView;
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
*/
@ -89,21 +87,10 @@ public class DraftSession {
UserManager.instance
.getUser(userId).
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() {
if (!killed) {
UserManager.instance

View file

@ -31,6 +31,9 @@ import java.io.*;
import java.util.*;
import java.util.Map.Entry;
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 mage.MageException;
import mage.abilities.Ability;
@ -80,7 +83,11 @@ public class GameController implements GameCallback {
protected static final ScheduledExecutorService timeoutIdleExecutor = ThreadExecutor.instance.getTimeoutIdleExecutor();
private final ConcurrentHashMap<UUID, GameSessionPlayer> gameSessions = new ConcurrentHashMap<>();
private final ReadWriteLock gameSessionsLock = new ReentrantReadWriteLock();
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, UUID> userPlayerMap;
@ -114,7 +121,7 @@ public class GameController implements GameCallback {
public void cleanUp() {
cancelTimeout();
for (GameSessionPlayer gameSessionPlayer : gameSessions.values()) {
for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
gameSessionPlayer.cleanUp();
}
ChatManager.instance.destroyChatSession(chatId);
@ -301,7 +308,13 @@ public class GameController implements GameCallback {
String joinType;
if (gameSession == null) {
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";
} else {
joinType = "rejoined";
@ -314,8 +327,8 @@ public class GameController implements GameCallback {
private synchronized void startGame() {
if (gameFuture == null) {
for (final Entry<UUID, GameSessionPlayer> entry : gameSessions.entrySet()) {
entry.getValue().init();
for (GameSessionPlayer gameSessionPlayer : getGameSessions()) {
gameSessionPlayer.init();
}
GameWorker worker = new GameWorker(game, choosingPlayerId, this);
@ -413,7 +426,13 @@ public class GameController implements GameCallback {
}
UserManager.instance.getUser(userId).ifPresent(user -> {
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();
user.addGameWatchInfo(game.getId());
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) {
watchers.remove(userId);
final Lock w = gameWatchersLock.writeLock();
w.lock();
try {
watchers.remove(userId);
} finally {
w.unlock();
}
UserManager.instance.getUser(userId).ifPresent(user -> {
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 {
for (final GameSessionPlayer gameSession : gameSessions.values()) {
for (final GameSessionPlayer gameSession : getGameSessions()) {
gameSession.gameOver(message);
gameSession.removeGame();
}
for (final GameSessionWatcher gameWatcher : watchers.values()) {
for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
gameWatcher.gameOver(message);
}
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();
}
for (final GameSessionWatcher gameWatcher : watchers.values()) {
for (final GameSessionWatcher gameWatcher : getGameSessionWatchers()) {
gameWatcher.update();
}
}
@ -734,12 +759,12 @@ public class GameController implements GameCallback {
Table table = TableManager.instance.getTable(tableId);
if (table != null) {
if (table.getMatch() != null) {
for (final GameSessionPlayer gameSession : gameSessions.values()) {
for (final GameSessionPlayer gameSession : getGameSessions()) {
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 {
@ -814,12 +839,12 @@ public class GameController implements GameCallback {
message.append(game.getStep().getType().toString()).append(" - ");
}
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)) {
entry.getValue().inform(message.toString());
}
}
for (final GameSessionWatcher watcher : watchers.values()) {
for (final GameSessionWatcher watcher : getGameSessionWatchers()) {
watcher.inform(message.toString());
}
}
@ -834,14 +859,13 @@ public class GameController implements GameCallback {
return;
}
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));
if (!skip) {
entry.getValue().inform(message);
}
}
for (final GameSessionWatcher watcher : watchers.values()) {
for (final GameSessionWatcher watcher : getGameSessionWatchers()) {
watcher.inform(message);
}
}
@ -858,7 +882,7 @@ public class GameController implements GameCallback {
for (StackTraceElement e : ex.getStackTrace()) {
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());
}
}
@ -995,6 +1019,42 @@ public class GameController implements GameCallback {
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) {
if (!timers.isEmpty()) {
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
* authors and should not be interpreted as representing official policies, either expressed
* or implied, of BetaSteward_at_googlemail.com.
*/
*/
package mage.server.game;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
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.constants.ManaType;
import mage.constants.PlayerAction;
@ -46,10 +50,17 @@ public enum GameManager {
instance;
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) {
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();
}
@ -109,10 +120,10 @@ public enum GameManager {
gameController.quitMatch(userId);
}
}
public void sendPlayerAction(PlayerAction playerAction, UUID gameId, UUID userId, Object data) {
GameController gameController = gameControllers.get(gameId);
if (gameController != null) {
if (gameController != null) {
gameController.sendPlayerAction(playerAction, userId, data);
}
}
@ -151,7 +162,13 @@ public enum GameManager {
GameController gameController = gameControllers.get(gameId);
if (gameController != null) {
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() {
return gameControllers.size();
}
public ConcurrentHashMap<UUID, GameController> getGameController() {
return gameControllers;
public Map<UUID, GameController> getGameController() {
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 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.
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,
// 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()));
}
public ArchmageAscension(final ArchmageAscension card) {
@ -91,17 +90,17 @@ class ArchmageAscensionTriggeredAbility extends TriggeredAbilityImpl {
public ArchmageAscensionTriggeredAbility copy() {
return new ArchmageAscensionTriggeredAbility(this);
}
@Override
public boolean checkEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.END_TURN_STEP_PRE;
}
@Override
public boolean checkTrigger(GameEvent event, Game game) {
Permanent archmage = game.getPermanent(super.getSourceId());
CardsAmountDrawnThisTurnWatcher watcher =
(CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.class.getSimpleName());
CardsAmountDrawnThisTurnWatcher watcher
= (CardsAmountDrawnThisTurnWatcher) game.getState().getWatchers().get(CardsAmountDrawnThisTurnWatcher.class.getSimpleName());
return archmage != null && watcher != null && watcher.getAmountCardsDrawn(this.getControllerId()) >= 2;
}
@ -115,8 +114,8 @@ class ArchmageAscensionReplacementEffect extends ReplacementEffectImpl {
public ArchmageAscensionReplacementEffect() {
super(Duration.WhileOnBattlefield, Outcome.Benefit);
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";
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";
}
public ArchmageAscensionReplacementEffect(final ArchmageAscensionReplacementEffect effect) {
@ -141,19 +140,19 @@ class ArchmageAscensionReplacementEffect extends ReplacementEffectImpl {
if (player.searchLibrary(target, game)) {
Card card = game.getCard(target.getFirstTarget());
if (card != null) {
card.moveToZone(Zone.HAND, id, game, false);
card.moveToZone(Zone.HAND, source.getSourceId(), game, false);
player.shuffleLibrary(source, game);
}
}
}
return true;
}
@Override
public boolean checksEventType(GameEvent event, Game game) {
return event.getType() == GameEvent.EventType.DRAW_CARD;
}
@Override
public boolean applies(GameEvent event, Ability source, Game game) {
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
@ -103,10 +103,11 @@ class CurseOfTheCabalSacrificeEffect extends OneShotEffect{
@Override
public boolean apply(Game game, Ability source) {
Player targetPlayer = game.getPlayer(source.getFirstTarget());
if(targetPlayer != null) {
if (targetPlayer != null) {
int amount = game.getBattlefield().countAll(FILTER, targetPlayer.getId(), game) / 2;
if(amount < 1)
if (amount < 1) {
return true;
}
Target target = new TargetControlledPermanent(amount, amount, FILTER, true);
if (target.canChoose(targetPlayer.getId(), game)) {
while (!target.isChosen() && target.canChoose(targetPlayer.getId(), game) && targetPlayer.canRespond()) {
@ -129,9 +130,9 @@ class CurseOfTheCabalTriggeredAbility extends ConditionalTriggeredAbility {
public CurseOfTheCabalTriggeredAbility() {
super(new BeginningOfUpkeepTriggeredAbility(
Zone.EXILED, new CurseOfTheCabalTriggeredAbilityConditionalDelay(),
TargetController.ANY, false, true
),
Zone.EXILED, new CurseOfTheCabalTriggeredAbilityConditionalDelay(),
TargetController.ANY, false, true
),
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}."
);
@ -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);
}
public boolean apply(Game game, Ability source) {
UUID id = game.getActivePlayerId();
Player target = game.getPlayer(id);
UUID activePlayerId = game.getActivePlayerId();
Player target = game.getPlayer(activePlayerId);
Cost cost = new SacrificeTargetCost(new TargetControlledPermanent(new FilterControlledPermanent()));
if(target == null)
if (target == null) {
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)
&& cost.pay(source, game, source.getSourceId(), id, true, null)) {
&& cost.pay(source, game, source.getSourceId(), activePlayerId, true, null)) {
return super.apply(game, source);
}
return true;

View file

@ -50,7 +50,7 @@ import mage.target.common.TargetCreaturePermanent;
public class DeadReckoning extends CardImpl {
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.
this.getSpellAbility().addEffect(new DeadReckoningEffect());
@ -96,7 +96,7 @@ class DeadReckoningEffect extends OneShotEffect {
&& you.choose(Outcome.Damage, target2, source.getSourceId(), game)) {
Card creatureInGraveyard = game.getCard(target1.getFirstTarget());
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();
Permanent creature = game.getPermanent(target2.getFirstTarget());
if (creature != null) {

View file

@ -1,4 +1,4 @@
/*
/*
* Copyright 2010 BetaSteward_at_googlemail.com. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are
@ -27,7 +27,7 @@
*/
package mage.cards.d;
import java.util.UUID;
import java.util.UUID;
import mage.abilities.Ability;
import mage.abilities.common.CycleTriggeredAbility;
import mage.abilities.costs.mana.ManaCostsImpl;
@ -54,14 +54,14 @@ import mage.players.Player;
public class DecreeOfAnnihilation extends CardImpl {
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.
this.getSpellAbility().addEffect(new DecreeOfAnnihilationEffect());
// Cycling {5}{R}{R}
this.addAbility(new CyclingAbility(new ManaCostsImpl("{5}{R}{R}")));
// When you cycle Decree of Annihilation, destroy all lands.
Ability ability = new CycleTriggeredAbility(new DestroyAllEffect(StaticFilters.FILTER_LANDS), false);
this.addAbility(ability);
@ -78,16 +78,16 @@ public class DecreeOfAnnihilation extends CardImpl {
}
class DecreeOfAnnihilationEffect extends OneShotEffect {
private static final FilterPermanent filter = new FilterPermanent("");
static {
filter.add(Predicates.or(
new CardTypePredicate(CardType.ARTIFACT),
new CardTypePredicate(CardType.CREATURE),
new CardTypePredicate(CardType.LAND)));
}
public DecreeOfAnnihilationEffect() {
super(Outcome.Detriment);
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
public boolean apply(Game game, Ability source) {
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)) {
Player player = game.getPlayer(playerId);

View file

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

View file

@ -28,6 +28,7 @@
package mage.cards.e;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.cards.Card;
@ -50,8 +51,7 @@ import mage.target.common.TargetCreatureOrPlayer;
public class ExplosiveRevelation extends CardImpl {
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.
this.getSpellAbility().addEffect(new ExplosiveRevelationEffect());
@ -86,42 +86,46 @@ class ExplosiveRevelationEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(source.getControllerId());
if (player != null && player.getLibrary().hasCards()) {
CardsImpl cards = new CardsImpl();
Library library = player.getLibrary();
Card card = null;
do {
card = library.removeFromTop(game);
if (card != null) {
cards.add(card);
Player controller = game.getPlayer(source.getControllerId());
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
if (controller.getLibrary().hasCards()) {
CardsImpl cards = new CardsImpl();
Library library = controller.getLibrary();
Card card = null;
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());
// reveal cards
if (!cards.isEmpty()) {
player.revealCards("Explosive Revelation", cards, game);
}
// the nonland card
int damage = card.getConvertedManaCost();
// assign damage to target
for (UUID targetId: targetPointer.getTargets(game, source)) {
Permanent targetedCreature = game.getPermanent(targetId);
if (targetedCreature != null) {
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);
// the nonland card
int damage = card.getConvertedManaCost();
// assign damage to target
for (UUID targetId : targetPointer.getTargets(game, source)) {
Permanent targetedCreature = game.getPermanent(targetId);
if (targetedCreature != null) {
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
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 true;
}
return false;
}

View file

@ -28,6 +28,7 @@
package mage.cards.i;
import java.util.UUID;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
import mage.abilities.keyword.StormAbility;
@ -49,8 +50,7 @@ import mage.target.TargetPlayer;
public class IgniteMemories extends CardImpl {
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.
this.getSpellAbility().addTarget(new TargetPlayer());
@ -69,7 +69,6 @@ public class IgniteMemories extends CardImpl {
}
}
class IgniteMemoriesEffect extends OneShotEffect {
public IgniteMemoriesEffect() {
@ -83,13 +82,17 @@ class IgniteMemoriesEffect extends OneShotEffect {
@Override
public boolean apply(Game game, Ability source) {
Player player = game.getPlayer(targetPointer.getFirst(game, source));
if (player != null && !player.getHand().isEmpty()) {
Cards revealed = new CardsImpl();
Card card = player.getHand().getRandom(game);
revealed.add(card);
player.revealCards("Ignite Memories", revealed, game);
player.damage(card.getConvertedManaCost(), id, game, false, true);
Player controller = game.getPlayer(targetPointer.getFirst(game, source));
MageObject sourceObject = source.getSourceObject(game);
if (controller != null && sourceObject != null) {
if (!controller.getHand().isEmpty()) {
Cards revealed = new CardsImpl();
Card card = controller.getHand().getRandom(game);
revealed.add(card);
controller.revealCards(sourceObject.getIdName(), revealed, game);
controller.damage(card.getConvertedManaCost(), source.getSourceId(), game, false, true);
}
return true;
}
return false;
@ -100,4 +103,4 @@ class IgniteMemoriesEffect extends OneShotEffect {
return new IgniteMemoriesEffect(this);
}
}
}

View file

@ -96,7 +96,7 @@ class MeglonothEffect extends OneShotEffect {
Permanent meglonoth = game.getPermanent(source.getSourceId());
Permanent blocked = game.getPermanent(targetPointer.getFirst(game, source));
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 false;

View file

@ -49,8 +49,7 @@ import mage.target.common.TargetOpponent;
public class PerishTheThought extends CardImpl {
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.
this.getSpellAbility().addEffect(new PerishTheThoughtEffect());
@ -68,9 +67,9 @@ public class PerishTheThought extends CardImpl {
}
class PerishTheThoughtEffect extends OneShotEffect {
private static final FilterCard filter = new FilterCard("card in target opponent's hand");
public PerishTheThoughtEffect() {
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";
@ -99,7 +98,7 @@ class PerishTheThoughtEffect extends OneShotEffect {
Card chosenCard = targetOpponent.getHand().get(target.getFirstTarget(), game);
if (chosenCard != null) {
if (targetOpponent != null) {
chosenCard.moveToZone(Zone.LIBRARY, id, game, false);
chosenCard.moveToZone(Zone.LIBRARY, source.getSourceId(), game, false);
targetOpponent.shuffleLibrary(source, game);
}
}

View file

@ -52,7 +52,7 @@ import mage.target.common.TargetCreatureOrPlayer;
public class RazorBoomerang extends CardImpl {
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");
// 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);
if (razor != null) {
razor.moveToZone(Zone.HAND, id, game, true);
razor.moveToZone(Zone.HAND, source.getSourceId(), game, true);
}
return true;
}

View file

@ -64,13 +64,14 @@ import mage.util.CardUtil;
public class ShellOfTheLastKappa extends CardImpl {
private static final FilterSpell filter = new FilterSpell("instant or sorcery spell that targets you");
static {
filter.add(new TargetYouPredicate());
filter.add(Predicates.or(new CardTypePredicate(CardType.INSTANT), new CardTypePredicate(CardType.SORCERY)));
}
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);
// {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) {
Spell spell = game.getStack().getSpell(targetPointer.getFirst(game, source));
if (spell != null) {
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
Permanent sourcePermanent = game.getPermanent(source.getSourceId());
if (sourcePermanent == null) {
sourcePermanent = (Permanent) game.getLastKnownInformation(source.getSourceId(), Zone.BATTLEFIELD);
}
if (sourcePermanent != null) {
game.getStack().counter(spell.getId(), source.getSourceId(), game);
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;
@ -164,7 +165,6 @@ class ShellOfTheLastKappaCastEffect extends OneShotEffect {
}
}
class TargetYouPredicate implements ObjectPlayerPredicate<ObjectPlayer<StackObject>> {
@Override

View file

@ -49,10 +49,9 @@ import mage.players.Player;
public class SuddenDemise extends CardImpl {
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());
}
@ -71,7 +70,7 @@ class SuddenDemiseDamageEffect extends OneShotEffect {
public SuddenDemiseDamageEffect() {
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) {
@ -93,7 +92,7 @@ class SuddenDemiseDamageEffect extends OneShotEffect {
final int damage = source.getManaCostsToPay().getX();
FilterPermanent filter = new FilterCreaturePermanent();
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);
}
return true;

View file

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

View file

@ -47,8 +47,7 @@ import mage.players.Player;
public class Worldfire extends CardImpl {
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.
this.getSpellAbility().addEffect(new WorldfireEffect());
@ -65,9 +64,9 @@ public class Worldfire extends CardImpl {
}
class WorldfireEffect extends OneShotEffect {
private static FilterPermanent filter = new FilterPermanent();
public WorldfireEffect() {
super(Outcome.Detriment);
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
public boolean apply(Game game, Ability source) {
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)) {
Player player = game.getPlayer(playerId);

View file

@ -81,9 +81,9 @@ public class MageObjectReference implements Comparable<MageObjectReference>, Ser
if (game.getPlayerList().contains(sourceId)) {
this.zoneChangeCounter = 0;
} 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()) {
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);
if (mageObject != null) {

View file

@ -27,6 +27,8 @@
*/
package mage.abilities.effects.common;
import java.util.LinkedHashSet;
import java.util.stream.Collectors;
import mage.MageObject;
import mage.abilities.Ability;
import mage.abilities.effects.OneShotEffect;
@ -39,9 +41,6 @@ import mage.game.permanent.Permanent;
import mage.players.Player;
import mage.util.CardUtil;
import java.util.LinkedHashSet;
import java.util.stream.Collectors;
/**
* @author LevelX2
*/
@ -72,6 +71,9 @@ public class ChooseCreatureTypeEffect extends OneShotEffect {
return false;
}
}
if (typeChoice.getChoice() == null) {
return false;
}
if (!game.isSimulation()) {
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) {
if (isGameUnderControl()) {
if (!isGameUnderControl()) {
Player player = game.getPlayer(getTurnControlledBy());
if (player.isHuman()) {
return player.getUserData();
@ -2969,8 +2969,7 @@ public abstract class PlayerImpl implements Player, Serializable {
}
@Override
public void setUserData(UserData userData
) {
public void setUserData(UserData userData) {
this.userData = userData;
getManaPool().setAutoPayment(userData.isManaPoolAutomatic());
getManaPool().setAutoPaymentRestricted(userData.isManaPoolAutomaticRestricted());