mirror of
https://github.com/correl/mage.git
synced 2025-01-12 19:25:44 +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,11 +44,26 @@ 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() {
|
||||
|
|
|
@ -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
|
||||
sb.append("<br>Checking priority player: " + getName(game.getPlayer(state.getPriorityPlayerId())));
|
||||
if (state.getPriorityPlayerId() != null) {
|
||||
if (game.getPlayer(state.getPriorityPlayerId()).hasLeft()) {
|
||||
sb.append("<br>Found disconnected player! Concede...");
|
||||
Player p = game.getPlayer(state.getPriorityPlayerId());
|
||||
sb.append("<br>Checking priority player: " + getName(game.getPlayer(state.getPriorityPlayerId())));
|
||||
if (p != null) {
|
||||
if (p.hasLeft()) {
|
||||
sb.append("<br>Found disconnected player! Concede...");
|
||||
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,6 +35,10 @@ public enum GamesRoomManager {
|
|||
return mainRoomId;
|
||||
}
|
||||
|
||||
public UUID getMainChatId() {
|
||||
return mainChatId;
|
||||
}
|
||||
|
||||
public Optional<GamesRoom> getRoom(UUID roomId) {
|
||||
if (rooms.containsKey(roomId)) {
|
||||
return Optional.of(rooms.get(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,8 +13,9 @@ 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 {
|
||||
|
@ -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 {
|
||||
|
@ -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}");
|
||||
|
||||
// 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) {
|
||||
|
@ -37,35 +41,3 @@ public final class OpenTheArmory extends CardImpl {
|
|||
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,8 +16,10 @@ 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 {
|
||||
|
@ -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,8 +25,9 @@ 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 {
|
||||
|
@ -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,8 +13,9 @@ 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 {
|
||||
|
@ -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()));
|
||||
|
||||
// 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