mirror of
https://github.com/correl/mage.git
synced 2024-12-25 03:00:15 +00:00
Merge pull request #6131 from magefree/game_freezes_and_lost_priorities
Server stability and freezes improves
This commit is contained in:
commit
3b1433989a
24 changed files with 491 additions and 337 deletions
|
@ -26,6 +26,7 @@ import mage.client.plugins.impl.Plugins;
|
|||
import mage.client.preference.MagePreferences;
|
||||
import mage.client.remote.CallbackClientImpl;
|
||||
import mage.client.table.TablesPane;
|
||||
import mage.client.table.TablesPanel;
|
||||
import mage.client.tournament.TournamentPane;
|
||||
import mage.client.util.*;
|
||||
import mage.client.util.audio.MusicPlayer;
|
||||
|
@ -261,7 +262,7 @@ public class MageFrame extends javax.swing.JFrame implements MageClient {
|
|||
desktopPane.add(errorDialog, JLayeredPane.MODAL_LAYER);
|
||||
UI.addComponent(MageComponents.DESKTOP_PANE, desktopPane);
|
||||
|
||||
PING_TASK_EXECUTOR.scheduleAtFixedRate(() -> SessionHandler.ping(), 60, 60, TimeUnit.SECONDS);
|
||||
PING_TASK_EXECUTOR.scheduleAtFixedRate(() -> SessionHandler.ping(), TablesPanel.PING_SERVER_SECS, TablesPanel.PING_SERVER_SECS, TimeUnit.SECONDS);
|
||||
|
||||
updateMemUsageTask = new UpdateMemUsageTask(jMemUsageLabel);
|
||||
|
||||
|
|
|
@ -18,7 +18,6 @@ import mage.interfaces.callback.CallbackClient;
|
|||
import mage.interfaces.callback.ClientCallback;
|
||||
import mage.remote.ActionData;
|
||||
import mage.remote.Session;
|
||||
import mage.utils.CompressUtil;
|
||||
import mage.view.*;
|
||||
import mage.view.ChatMessage.MessageType;
|
||||
import org.apache.log4j.Logger;
|
||||
|
@ -44,8 +43,8 @@ public class CallbackClientImpl implements CallbackClient {
|
|||
|
||||
@Override
|
||||
public synchronized void processCallback(final ClientCallback callback) {
|
||||
callback.decompressData();
|
||||
SaveObjectUtil.saveObject(callback.getData(), callback.getMethod().toString());
|
||||
callback.setData(CompressUtil.decompress(callback.getData()));
|
||||
SwingUtilities.invokeLater(() -> {
|
||||
try {
|
||||
logger.debug(callback.getMessageId() + " -- " + callback.getMethod());
|
||||
|
|
|
@ -64,6 +64,9 @@ public class TablesPanel extends javax.swing.JPanel {
|
|||
private static final Logger LOGGER = Logger.getLogger(TablesPanel.class);
|
||||
private static final int[] DEFAULT_COLUMNS_WIDTH = {35, 150, 100, 50, 120, 180, 80, 120, 80, 60, 40, 40, 60};
|
||||
|
||||
// ping timeout (warning, must be less than SERVER_TIMEOUTS_USER_INFORM_OPPONENTS_ABOUT_DISCONNECT_AFTER_SECS)
|
||||
public static final int PING_SERVER_SECS = 20;
|
||||
|
||||
// refresh timeouts for data downloads from server
|
||||
public static final int REFRESH_ACTIVE_TABLES_SECS = 5;
|
||||
public static final int REFRESH_FINISHED_TABLES_SECS = 30;
|
||||
|
|
|
@ -1,12 +1,12 @@
|
|||
|
||||
|
||||
package mage.interfaces.callback;
|
||||
|
||||
import mage.remote.traffic.ZippedObject;
|
||||
import mage.utils.CompressUtil;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author BetaSteward_at_googlemail.com
|
||||
*/
|
||||
public class ClientCallback implements Serializable {
|
||||
|
@ -16,12 +16,14 @@ public class ClientCallback implements Serializable {
|
|||
private ClientCallbackMethod method;
|
||||
private int messageId;
|
||||
|
||||
public ClientCallback() {}
|
||||
|
||||
public ClientCallback(ClientCallbackMethod method, UUID objectId, Object data) {
|
||||
this(method, objectId, data, true);
|
||||
}
|
||||
|
||||
public ClientCallback(ClientCallbackMethod method, UUID objectId, Object data, boolean useCompress) {
|
||||
this.method = method;
|
||||
this.objectId = objectId;
|
||||
this.data = data;
|
||||
this.setData(data, useCompress);
|
||||
}
|
||||
|
||||
public ClientCallback(ClientCallbackMethod method, UUID objectId) {
|
||||
|
@ -42,13 +44,28 @@ public class ClientCallback implements Serializable {
|
|||
}
|
||||
|
||||
public Object getData() {
|
||||
if (this.data instanceof ZippedObject) {
|
||||
throw new IllegalStateException("Client data must be decompressed first");
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public void setData(Object data) {
|
||||
public void setData(Object data, boolean useCompress) {
|
||||
if (!useCompress || data == null || data instanceof ZippedObject) {
|
||||
this.data = data;
|
||||
} else {
|
||||
this.data = CompressUtil.compress(data);
|
||||
}
|
||||
|
||||
this.data = data;
|
||||
}
|
||||
|
||||
public void decompressData() {
|
||||
if (this.data instanceof ZippedObject) {
|
||||
this.data = CompressUtil.decompress(this.data);
|
||||
}
|
||||
}
|
||||
|
||||
public ClientCallbackMethod getMethod() {
|
||||
return method;
|
||||
}
|
||||
|
|
|
@ -14,7 +14,7 @@ public final class CompressUtil {
|
|||
* Defines should data be compressed or not. True by default. Read from
|
||||
* system property:
|
||||
*/
|
||||
private static boolean compressData = true;
|
||||
private static boolean compressData;
|
||||
|
||||
/**
|
||||
* Defines the system property name to disable any compressing.
|
||||
|
|
|
@ -67,7 +67,7 @@ public class ConsoleFrame extends javax.swing.JFrame implements MageClient {
|
|||
logger.fatal("", ex);
|
||||
}
|
||||
|
||||
pingTaskExecutor.scheduleAtFixedRate(() -> session.ping(), 60, 60, TimeUnit.SECONDS);
|
||||
pingTaskExecutor.scheduleAtFixedRate(() -> session.ping(), 20, 20, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
public boolean connect(Connection connection) {
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -5,6 +5,7 @@ import mage.cards.repository.CardRepository;
|
|||
import mage.server.exceptions.UserNotFoundException;
|
||||
import mage.server.game.GameController;
|
||||
import mage.server.game.GameManager;
|
||||
import mage.server.game.GamesRoomManager;
|
||||
import mage.server.util.SystemUtil;
|
||||
import mage.view.ChatMessage.MessageColor;
|
||||
import mage.view.ChatMessage.MessageType;
|
||||
|
@ -316,12 +317,16 @@ public enum ChatManager {
|
|||
}
|
||||
|
||||
public void sendLostConnectionMessage(UUID userId, DisconnectReason reason) {
|
||||
UserManager.instance.getUser(userId).ifPresent(user -> sendMessageToUserChats(userId, user.getName() + " " + reason.getMessage()));
|
||||
}
|
||||
|
||||
public void sendMessageToUserChats(UUID userId, String message) {
|
||||
UserManager.instance.getUser(userId).ifPresent(user
|
||||
-> getChatSessions()
|
||||
.stream()
|
||||
.filter(chat -> !chat.getChatId().equals(GamesRoomManager.instance.getMainChatId())) // ignore main lobby
|
||||
.filter(chat -> chat.hasUser(userId))
|
||||
.forEach(chatSession -> chatSession.broadcast(null, user.getName() + reason.getMessage(), MessageColor.BLUE, true, MessageType.STATUS, null)));
|
||||
|
||||
.forEach(chatSession -> chatSession.broadcast(null, message, MessageColor.BLUE, true, MessageType.STATUS, null)));
|
||||
}
|
||||
|
||||
public void removeUser(UUID userId, DisconnectReason reason) {
|
||||
|
|
|
@ -22,6 +22,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock;
|
|||
public enum UserManager {
|
||||
instance;
|
||||
|
||||
private static final int SERVER_TIMEOUTS_USER_INFORM_OPPONENTS_ABOUT_DISCONNECT_AFTER_SECS = 30; // send to chat info about disconnection troubles, must be more than ping timeout
|
||||
private static final int SERVER_TIMEOUTS_USER_DISCONNECT_FROM_SERVER_AFTER_SECS = 3 * 60; // removes from all games and chats too (can be seen in users list with disconnected status)
|
||||
private static final int SERVER_TIMEOUTS_USER_REMOVE_FROM_SERVER_AFTER_SECS = 8 * 60; // removes from users list
|
||||
|
||||
|
@ -145,6 +146,22 @@ public enum UserManager {
|
|||
}
|
||||
}
|
||||
|
||||
public void informUserOpponents(final UUID userId, final String message) {
|
||||
if (userId != null) {
|
||||
getUser(userId).ifPresent(user
|
||||
-> USER_EXECUTOR.execute(
|
||||
() -> {
|
||||
try {
|
||||
logger.info("INFORM OPPONENTS by " + user.getName() + ": " + message);
|
||||
ChatManager.instance.sendMessageToUserChats(user.getId(), message);
|
||||
} catch (Exception ex) {
|
||||
handleException(ex);
|
||||
}
|
||||
}
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
public boolean extendUserSession(UUID userId, String pingInfo) {
|
||||
if (userId != null) {
|
||||
User user = users.get(userId);
|
||||
|
@ -163,6 +180,8 @@ public enum UserManager {
|
|||
*/
|
||||
private void checkExpired() {
|
||||
try {
|
||||
Calendar calendarInform = Calendar.getInstance();
|
||||
calendarInform.add(Calendar.SECOND, -1 * SERVER_TIMEOUTS_USER_INFORM_OPPONENTS_ABOUT_DISCONNECT_AFTER_SECS);
|
||||
Calendar calendarExp = Calendar.getInstance();
|
||||
calendarExp.add(Calendar.SECOND, -1 * SERVER_TIMEOUTS_USER_DISCONNECT_FROM_SERVER_AFTER_SECS);
|
||||
Calendar calendarRemove = Calendar.getInstance();
|
||||
|
@ -179,6 +198,12 @@ public enum UserManager {
|
|||
}
|
||||
for (User user : userList) {
|
||||
try {
|
||||
if (user.getUserState() != UserState.Offline
|
||||
&& user.isExpired(calendarInform.getTime())) {
|
||||
long secsInfo = (Calendar.getInstance().getTimeInMillis() - user.getLastActivity().getTime()) / 1000;
|
||||
informUserOpponents(user.getId(), user.getName() + " got connection problem for " + secsInfo + " secs");
|
||||
}
|
||||
|
||||
if (user.getUserState() == UserState.Offline) {
|
||||
if (user.isExpired(calendarRemove.getTime())) {
|
||||
// removes from users list
|
||||
|
|
|
@ -50,7 +50,7 @@ import java.util.zip.GZIPOutputStream;
|
|||
public class GameController implements GameCallback {
|
||||
|
||||
private static final int GAME_TIMEOUTS_CHECK_JOINING_STATUS_EVERY_SECS = 15; // checks and inform players about joining status
|
||||
private static final int GAME_TIMEOUTS_CANCEL_PLAYER_GAME_JOINING_AFTER_INACTIVE_SECS = 4 * 60; // leave player from game if it don't join and inactive on server
|
||||
private static final int GAME_TIMEOUTS_CANCEL_PLAYER_GAME_JOINING_AFTER_INACTIVE_SECS = 2 * 60; // leave player from game if it don't join and inactive on server
|
||||
|
||||
private static final ExecutorService gameExecutor = ThreadExecutor.instance.getGameExecutor();
|
||||
private static final Logger logger = Logger.getLogger(GameController.class);
|
||||
|
@ -336,7 +336,10 @@ public class GameController implements GameCallback {
|
|||
GameManager.instance.joinGame(game.getId(), user.getId());
|
||||
logger.debug("Player " + player.getName() + " (disconnected) has joined gameId: " + game.getId());
|
||||
}
|
||||
ChatManager.instance.broadcast(chatId, player.getName(), user.getPingInfo() + " is pending to join the game", MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null);
|
||||
ChatManager.instance.broadcast(chatId, player.getName(), user.getPingInfo()
|
||||
+ " is pending to join the game (waiting " + user.getSecondsDisconnected() + " of "
|
||||
+ GAME_TIMEOUTS_CANCEL_PLAYER_GAME_JOINING_AFTER_INACTIVE_SECS + " secs)",
|
||||
MessageColor.BLUE, true, ChatMessage.MessageType.STATUS, null);
|
||||
if (user.getSecondsDisconnected() > GAME_TIMEOUTS_CANCEL_PLAYER_GAME_JOINING_AFTER_INACTIVE_SECS) {
|
||||
// TODO: 2019.04.22 - if user playing another game on server but not joining (that's the reason?), then that's check will never trigger
|
||||
// Cancel player join possibility lately after 4 minutes
|
||||
|
@ -1201,6 +1204,7 @@ public class GameController implements GameCallback {
|
|||
if (activePlayer != null && activePlayer.hasLeft()) {
|
||||
sb.append("<br>Found disconnected player! Concede...");
|
||||
activePlayer.concede(game);
|
||||
activePlayer.leave(); // abort any wait response actions
|
||||
|
||||
Phase currentPhase = game.getPhase();
|
||||
if (currentPhase != null) {
|
||||
|
@ -1221,6 +1225,7 @@ public class GameController implements GameCallback {
|
|||
Player p = game.getPlayer(state.getChoosingPlayerId());
|
||||
if (p != null) {
|
||||
p.concede(game);
|
||||
p.leave(); // abort any wait response actions
|
||||
}
|
||||
Phase currentPhase = game.getPhase();
|
||||
if (currentPhase != null && !fixedAlready) {
|
||||
|
@ -1235,14 +1240,14 @@ public class GameController implements GameCallback {
|
|||
}
|
||||
|
||||
// fix lost priority
|
||||
Player p = game.getPlayer(state.getPriorityPlayerId());
|
||||
sb.append("<br>Checking priority player: " + getName(game.getPlayer(state.getPriorityPlayerId())));
|
||||
if (state.getPriorityPlayerId() != null) {
|
||||
if (game.getPlayer(state.getPriorityPlayerId()).hasLeft()) {
|
||||
if (p != null) {
|
||||
if (p.hasLeft()) {
|
||||
sb.append("<br>Found disconnected player! Concede...");
|
||||
Player p = game.getPlayer(state.getPriorityPlayerId());
|
||||
if (p != null) {
|
||||
p.concede(game);
|
||||
}
|
||||
p.concede(game);
|
||||
p.leave(); // abort any wait response actions
|
||||
|
||||
Phase currentPhase = game.getPhase();
|
||||
if (currentPhase != null && !fixedAlready) {
|
||||
currentPhase.getStep().skipStep(game, state.getActivePlayerId());
|
||||
|
@ -1250,7 +1255,6 @@ public class GameController implements GameCallback {
|
|||
sb.append("<br>Forcibly passing the phase!");
|
||||
}
|
||||
}
|
||||
sb.append(game.getPlayer(state.getPriorityPlayerId()).getName());
|
||||
sb.append("</font>");
|
||||
}
|
||||
|
||||
|
@ -1272,6 +1276,8 @@ public class GameController implements GameCallback {
|
|||
sb.append("Not using future Timeout!");
|
||||
}
|
||||
sb.append("</font>");
|
||||
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
package mage.server.game;
|
||||
|
||||
import mage.game.Game;
|
||||
|
@ -101,7 +99,6 @@ public class GameSessionWatcher {
|
|||
GameView gameView = new GameView(game.getState(), game, null, userId);
|
||||
processWatchedHands(userId, gameView);
|
||||
return gameView;
|
||||
|
||||
}
|
||||
|
||||
protected void processWatchedHands(UUID userId, GameView gameView) {
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
|
||||
|
||||
package mage.server.game;
|
||||
|
||||
import org.apache.log4j.Logger;
|
||||
|
@ -16,12 +14,14 @@ public enum GamesRoomManager {
|
|||
|
||||
private final ConcurrentHashMap<UUID, GamesRoom> rooms = new ConcurrentHashMap<>();
|
||||
private final UUID mainRoomId;
|
||||
private final UUID mainChatId;
|
||||
private static final Logger logger = Logger.getLogger(GamesRoomManager.class);
|
||||
|
||||
|
||||
GamesRoomManager() {
|
||||
GamesRoom mainRoom = new GamesRoomImpl();
|
||||
mainRoomId = mainRoom.getRoomId();
|
||||
mainChatId = mainRoom.getChatId();
|
||||
rooms.put(mainRoomId, mainRoom);
|
||||
}
|
||||
|
||||
|
@ -35,8 +35,12 @@ public enum GamesRoomManager {
|
|||
return mainRoomId;
|
||||
}
|
||||
|
||||
public UUID getMainChatId() {
|
||||
return mainChatId;
|
||||
}
|
||||
|
||||
public Optional<GamesRoom> getRoom(UUID roomId) {
|
||||
if(rooms.containsKey(roomId)) {
|
||||
if (rooms.containsKey(roomId)) {
|
||||
return Optional.of(rooms.get(roomId));
|
||||
}
|
||||
logger.error("room not found : " + roomId);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package mage.cards.d;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
|
@ -60,10 +61,10 @@ class DeathbellowWarCryTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
return card != null
|
||||
&& filter.match(card, game)
|
||||
&& filter.match(card, playerId, game)
|
||||
&& this
|
||||
.getTargets()
|
||||
.stream()
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.g;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.*;
|
||||
|
@ -15,14 +13,15 @@ import mage.target.TargetCard;
|
|||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class GiftsUngiven extends CardImpl {
|
||||
|
||||
public GiftsUngiven(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{3}{U}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{3}{U}");
|
||||
|
||||
// Search your library for up to four cards with different names and reveal them. Target opponent chooses two of those cards. Put the chosen cards into your graveyard and the rest into your hand. Then shuffle your library.
|
||||
this.getSpellAbility().addEffect(new GiftsUngivenEffect());
|
||||
|
@ -115,7 +114,7 @@ class GiftsUngivenTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card != null) {
|
||||
for (UUID targetId : this.getTargets()) {
|
||||
|
@ -124,7 +123,7 @@ class GiftsUngivenTarget extends TargetCardInLibrary {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return filter.match(card, game);
|
||||
return filter.match(card, playerId, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -88,8 +88,8 @@ class TargetCardInLibrarySharingLandType extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
if (super.canTarget(id, cards, game)) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
if (super.canTarget(playerId, id, source, cards, game)) {
|
||||
if (!getTargets().isEmpty()) {
|
||||
// check if new target shares a Land Type
|
||||
SubTypeList landTypes = new SubTypeList();
|
||||
|
|
|
@ -1,15 +1,8 @@
|
|||
|
||||
package mage.cards.n;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
|
@ -21,8 +14,10 @@ import mage.players.Player;
|
|||
import mage.target.TargetCard;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public final class NissasEncouragement extends CardImpl {
|
||||
|
@ -89,8 +84,8 @@ class NissasEncouragementEffect extends OneShotEffect {
|
|||
foundCards.put("Brambleweft Behemoth", 0);
|
||||
foundCards.put("Nissa, Genesis Mage", 0);
|
||||
Cards cards = new CardsImpl();
|
||||
|
||||
if (!target.getTargets().isEmpty()) {
|
||||
|
||||
if (!target.getTargets().isEmpty()) {
|
||||
for (UUID cardId : target.getTargets()) {
|
||||
Card card = player.getLibrary().remove(cardId, game);
|
||||
|
||||
|
@ -120,7 +115,7 @@ class NissasEncouragementEffect extends OneShotEffect {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!cards.isEmpty()) {
|
||||
player.revealCards(sourceCard.getIdName(), cards, game);
|
||||
player.moveCards(cards, Zone.HAND, source, game);
|
||||
|
@ -149,7 +144,7 @@ class NissasEncouragementTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card != null) {
|
||||
for (UUID targetId : this.getTargets()) {
|
||||
|
@ -158,7 +153,7 @@ class NissasEncouragementTarget extends TargetCardInLibrary {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return filter.match(card, game);
|
||||
return filter.match(card, playerId, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,31 +1,35 @@
|
|||
|
||||
package mage.cards.o;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.filter.FilterCard;
|
||||
import mage.filter.predicate.Predicates;
|
||||
import mage.filter.predicate.mageobject.SubtypePredicate;
|
||||
import mage.game.Game;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author escplan9 (Derek Monturo - dmontur1 at gmail dot com)
|
||||
*/
|
||||
public final class OpenTheArmory extends CardImpl {
|
||||
|
||||
private static final FilterCard auraOrEquipmentTarget = new FilterCard("Aura or Equipment card");
|
||||
|
||||
static {
|
||||
auraOrEquipmentTarget.add(Predicates.or(
|
||||
new SubtypePredicate(SubType.EQUIPMENT),
|
||||
new SubtypePredicate(SubType.AURA)));
|
||||
}
|
||||
|
||||
public OpenTheArmory(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{1}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{1}{W}");
|
||||
|
||||
// Search your library for an Aura or Equipment card, reveal it, and put it into your hand. Then shuffle your library.
|
||||
this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new OpenTheArmoryTarget(), true, true));
|
||||
this.getSpellAbility().addEffect(new SearchLibraryPutInHandEffect(new TargetCardInLibrary(1, 1, auraOrEquipmentTarget), true, true));
|
||||
}
|
||||
|
||||
public OpenTheArmory(final OpenTheArmory card) {
|
||||
|
@ -36,36 +40,4 @@ public final class OpenTheArmory extends CardImpl {
|
|||
public OpenTheArmory copy() {
|
||||
return new OpenTheArmory(this);
|
||||
}
|
||||
}
|
||||
|
||||
class OpenTheArmoryTarget extends TargetCardInLibrary {
|
||||
|
||||
private static final FilterCard auraOrEquipmentTarget = new FilterCard("Aura or Equipment card");
|
||||
static {
|
||||
auraOrEquipmentTarget.add(Predicates.or(
|
||||
new SubtypePredicate(SubType.EQUIPMENT),
|
||||
new SubtypePredicate(SubType.AURA)));
|
||||
}
|
||||
|
||||
public OpenTheArmoryTarget() {
|
||||
super(1, 1, auraOrEquipmentTarget.copy());
|
||||
}
|
||||
|
||||
public OpenTheArmoryTarget(final OpenTheArmoryTarget target) {
|
||||
super(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OpenTheArmoryTarget copy() {
|
||||
return new OpenTheArmoryTarget(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card != null) {
|
||||
return auraOrEquipmentTarget.match(card, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,9 @@
|
|||
|
||||
package mage.cards.r;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import mage.MageObject;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
|
@ -23,14 +16,16 @@ import mage.target.TargetCard;
|
|||
import mage.target.common.TargetCardInLibrary;
|
||||
import mage.target.common.TargetOpponent;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author North
|
||||
*/
|
||||
public final class RealmsUncharted extends CardImpl {
|
||||
|
||||
public RealmsUncharted(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.INSTANT},"{2}{G}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.INSTANT}, "{2}{G}");
|
||||
|
||||
// Search your library for four land cards with different names and reveal them. An opponent chooses two of those cards. Put the chosen cards into your graveyard and the rest into your hand. Then shuffle your library.
|
||||
this.getSpellAbility().addEffect(new RealmsUnchartedEffect());
|
||||
|
@ -128,7 +123,7 @@ class RealmsUnchartedTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card != null) {
|
||||
for (UUID targetId : this.getTargets()) {
|
||||
|
@ -137,7 +132,7 @@ class RealmsUnchartedTarget extends TargetCardInLibrary {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return filter.match(card, game);
|
||||
return filter.match(card, playerId, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
|
||||
package mage.cards.s;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.DelayedTriggeredAbility;
|
||||
import mage.abilities.LoyaltyAbility;
|
||||
|
@ -9,20 +7,16 @@ import mage.abilities.common.PlaneswalkerEntersWithLoyaltyCountersAbility;
|
|||
import mage.abilities.common.delayed.AtTheBeginOfNextEndStepDelayedTriggeredAbility;
|
||||
import mage.abilities.effects.Effect;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||
import mage.abilities.effects.common.DamagePlayersEffect;
|
||||
import mage.abilities.effects.common.ExileTargetEffect;
|
||||
import mage.abilities.effects.common.CreateTokenCopyTargetEffect;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInPlayEffect;
|
||||
import mage.abilities.effects.keyword.ScryEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.SubType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.SuperType;
|
||||
import mage.constants.TargetController;
|
||||
import mage.constants.*;
|
||||
import mage.filter.StaticFilters;
|
||||
import mage.filter.common.FilterArtifactCard;
|
||||
import mage.game.Game;
|
||||
|
@ -31,14 +25,15 @@ import mage.target.common.TargetCardInLibrary;
|
|||
import mage.target.common.TargetControlledPermanent;
|
||||
import mage.target.targetpointer.FixedTarget;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author emerald000
|
||||
*/
|
||||
public final class SaheeliRai extends CardImpl {
|
||||
|
||||
public SaheeliRai(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.PLANESWALKER},"{1}{U}{R}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.PLANESWALKER}, "{1}{U}{R}");
|
||||
this.addSuperType(SuperType.LEGENDARY);
|
||||
this.subtype.add(SubType.SAHEELI);
|
||||
|
||||
|
@ -121,7 +116,7 @@ class SaheeliRaiTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card != null) {
|
||||
for (UUID targetId : this.getTargets()) {
|
||||
|
@ -130,7 +125,7 @@ class SaheeliRaiTarget extends TargetCardInLibrary {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return filter.match(card, game);
|
||||
return filter.match(card, playerId, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package mage.cards.s;
|
||||
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
|
@ -55,15 +56,20 @@ class SharedSummonsTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card == null || !card.isCreature()) {
|
||||
return false;
|
||||
}
|
||||
return !this
|
||||
|
||||
if (!filter.match(card, playerId, game)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this
|
||||
.getTargets()
|
||||
.stream()
|
||||
.map(uuid -> game.getCard(uuid))
|
||||
.anyMatch(c -> c != null && c.getName().equals(card.getName()));
|
||||
.map(game::getCard)
|
||||
.noneMatch(c -> c != null && c.getName().equals(card.getName()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
|
||||
package mage.cards.t;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.common.search.SearchLibraryPutInHandEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
|
@ -14,14 +13,15 @@ import mage.filter.predicate.mageobject.SubtypePredicate;
|
|||
import mage.game.Game;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author LevelX2
|
||||
*/
|
||||
public final class ThreeDreams extends CardImpl {
|
||||
|
||||
public ThreeDreams(UUID ownerId, CardSetInfo setInfo) {
|
||||
super(ownerId,setInfo,new CardType[]{CardType.SORCERY},"{4}{W}");
|
||||
super(ownerId, setInfo, new CardType[]{CardType.SORCERY}, "{4}{W}");
|
||||
|
||||
|
||||
// Search your library for up to three Aura cards with different names, reveal them, and put them into your hand. Then shuffle your library.
|
||||
|
@ -41,6 +41,7 @@ public final class ThreeDreams extends CardImpl {
|
|||
class ThreeDreamsTarget extends TargetCardInLibrary {
|
||||
|
||||
private static final FilterCard aurafilter = new FilterCard("Aura cards with different names");
|
||||
|
||||
static {
|
||||
aurafilter.add(new SubtypePredicate(SubType.AURA));
|
||||
}
|
||||
|
@ -59,7 +60,7 @@ class ThreeDreamsTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card != null) {
|
||||
// check if card with that name was selected before
|
||||
|
@ -69,7 +70,7 @@ class ThreeDreamsTarget extends TargetCardInLibrary {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return filter.match(card, game);
|
||||
return filter.match(card, playerId, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
|
||||
package mage.cards.u;
|
||||
|
||||
import java.util.UUID;
|
||||
import mage.abilities.Ability;
|
||||
import mage.abilities.effects.OneShotEffect;
|
||||
import mage.cards.Card;
|
||||
import mage.cards.CardImpl;
|
||||
import mage.cards.CardSetInfo;
|
||||
import mage.cards.Cards;
|
||||
import mage.cards.CardsImpl;
|
||||
import mage.cards.*;
|
||||
import mage.constants.CardType;
|
||||
import mage.constants.Outcome;
|
||||
import mage.constants.Zone;
|
||||
|
@ -19,8 +13,9 @@ import mage.game.Game;
|
|||
import mage.players.Player;
|
||||
import mage.target.common.TargetCardInLibrary;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author spjspj
|
||||
*/
|
||||
public final class UncageTheMenagerie extends CardImpl {
|
||||
|
@ -115,7 +110,7 @@ class UncageTheMenagerieTarget extends TargetCardInLibrary {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
Card card = cards.get(id, game);
|
||||
if (card != null) {
|
||||
for (UUID targetId : this.getTargets()) {
|
||||
|
@ -124,11 +119,12 @@ class UncageTheMenagerieTarget extends TargetCardInLibrary {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(card.isCreature() && card.getConvertedManaCost() == xValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filter.match(card, game);
|
||||
return filter.match(card, playerId, game);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@ import mage.constants.PlayerAction;
|
|||
import mage.interfaces.callback.CallbackClient;
|
||||
import mage.interfaces.callback.ClientCallback;
|
||||
import mage.remote.Session;
|
||||
import mage.utils.CompressUtil;
|
||||
import mage.view.*;
|
||||
import org.apache.log4j.Logger;
|
||||
|
||||
|
@ -36,9 +35,18 @@ public class LoadCallbackClient implements CallbackClient {
|
|||
|
||||
@Override
|
||||
public void processCallback(ClientCallback callback) {
|
||||
callback.decompressData();
|
||||
controlCount = 0;
|
||||
callback.setData(CompressUtil.decompress(callback.getData()));
|
||||
log.info(getLogStartInfo() + "callback: " + callback.getMethod());
|
||||
|
||||
// ignore bloaded logs
|
||||
switch (callback.getMethod()) {
|
||||
case CHATMESSAGE:
|
||||
case GAME_INFORM:
|
||||
case GAME_UPDATE:
|
||||
break;
|
||||
default:
|
||||
log.info(getLogStartInfo() + "callback: " + callback.getMethod());
|
||||
}
|
||||
|
||||
switch (callback.getMethod()) {
|
||||
|
||||
|
@ -69,7 +77,7 @@ public class LoadCallbackClient implements CallbackClient {
|
|||
case GAME_INFORM_PERSONAL: {
|
||||
GameClientMessage message = (GameClientMessage) callback.getData();
|
||||
gameView = message.getGameView();
|
||||
log.info(getLogStartInfo() + "Inform: " + message.getMessage());
|
||||
//log.info(getLogStartInfo() + "Inform: " + message.getMessage());
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -208,8 +208,8 @@ public class TargetCard extends TargetObject {
|
|||
&& getFilter() != null && getFilter().match(card, playerId, game);
|
||||
}
|
||||
|
||||
public boolean canTarget(UUID id, Cards cards, Game game) {
|
||||
return cards.contains(id) && canTarget(id, game);
|
||||
public boolean canTarget(UUID playerId, UUID id, Ability source, Cards cards, Game game) {
|
||||
return cards.contains(id) && canTarget(playerId, id, source, game);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in a new issue